[automerger] Use BoringSSL to get random bytes am: 06eaa1c9cf

Change-Id: I726b1c08ccb1d06e972ba2e47c9a6256eeb0b386
diff --git a/CONTRIBUTIONS b/CONTRIBUTIONS
index 76600bc..1dc7547 100644
--- a/CONTRIBUTIONS
+++ b/CONTRIBUTIONS
@@ -140,7 +140,7 @@
 
 Modified BSD license (no advertisement clause):
 
-Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/COPYING b/COPYING
index 7efce0d..945bdc0 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 
diff --git a/README b/README
index 9685f58..43b684c 100644
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 These programs are licensed under the BSD license (the one with
diff --git a/hostapd/Android.mk b/hostapd/Android.mk
index 67ca129..d9c4b97 100644
--- a/hostapd/Android.mk
+++ b/hostapd/Android.mk
@@ -27,6 +27,9 @@
 # Disable unused parameter warnings
 L_CFLAGS += -Wno-unused-parameter
 
+# Disable macro redefined warnings
+L_CFLAGS += -Wno-macro-redefined
+
 # Set Android extended P2P functionality
 L_CFLAGS += -DANDROID_P2P
 
@@ -38,6 +41,9 @@
 L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
 L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/hostapd\"
 
+# Use Android specific directory for hostapd_cli command completion history
+L_CFLAGS += -DCONFIG_HOSTAPD_CLI_HISTORY_DIR=\"/data/misc/wifi\"
+
 # To force sizeof(enum) = 4
 ifeq ($(TARGET_ARCH),arm)
 L_CFLAGS += -mabi=aapcs-linux
@@ -96,6 +102,8 @@
 OBJS += src/ap/ieee802_11_shared.c
 OBJS += src/ap/beacon.c
 OBJS += src/ap/bss_load.c
+OBJS += src/ap/neighbor_db.c
+OBJS += src/ap/rrm.c
 OBJS_d =
 OBJS_p =
 LIBS =
@@ -147,7 +155,6 @@
 
 OBJS += src/eapol_auth/eapol_auth_sm.c
 
-
 ifndef CONFIG_NO_DUMP_STATE
 # define HOSTAPD_DUMP_STATE to include support for dumping internal state
 # through control interface commands (undefine it, if you want to save in
@@ -242,7 +249,7 @@
 endif
 
 ifdef CONFIG_IEEE80211R
-L_CFLAGS += -DCONFIG_IEEE80211R
+L_CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -256,6 +263,13 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_FILS
+L_CFLAGS += -DCONFIG_FILS
+OBJS += src/ap/fils_hlp.c
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 L_CFLAGS += -DCONFIG_WNM
 OBJS += src/ap/wnm_ap.c
@@ -269,6 +283,10 @@
 L_CFLAGS += -DCONFIG_IEEE80211AC
 endif
 
+ifdef CONFIG_IEEE80211AX
+L_CFLAGS += -DCONFIG_IEEE80211AX
+endif
+
 ifdef CONFIG_MBO
 L_CFLAGS += -DCONFIG_MBO
 OBJS += src/ap/mbo_ap.c
@@ -735,6 +753,9 @@
 AESOBJS += src/crypto/aes-cbc.c
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal-dec.c
@@ -821,6 +842,9 @@
 endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/sha384.c
+endif
 OBJS += src/crypto/sha384-prf.c
 endif
 
@@ -895,6 +919,10 @@
 OBJS += src/ap/ieee802_11_vht.c
 endif
 
+ifdef CONFIG_IEEE80211AX
+OBJS += src/ap/ieee802_11_he.c
+endif
+
 ifdef CONFIG_P2P_MANAGER
 L_CFLAGS += -DCONFIG_P2P_MANAGER
 OBJS += src/ap/p2p_hostapd.c
@@ -933,6 +961,10 @@
 L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
 endif
 
+ifdef CONFIG_DEBUG_SYSLOG
+L_CFLAGS += -DCONFIG_DEBUG_SYSLOG
+endif
+
 ifdef CONFIG_DEBUG_LINUX_TRACING
 L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
 endif
@@ -945,7 +977,10 @@
 L_CFLAGS += -DCONFIG_ANDROID_LOG
 endif
 
-OBJS_c = hostapd_cli.c src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
+OBJS_c = hostapd_cli.c
+OBJS_c += src/common/wpa_ctrl.c
+OBJS_c += src/utils/os_$(CONFIG_OS).c
+OBJS_c += src/common/cli.c
 OBJS_c += src/utils/eloop.c
 OBJS_c += src/utils/common.c
 ifdef CONFIG_WPA_TRACE
@@ -963,6 +998,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := hostapd_cli
 LOCAL_MODULE_TAGS := debug
+LOCAL_PROPRIETARY_MODULE := true
 LOCAL_SHARED_LIBRARIES := libc libcutils liblog
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS_c)
@@ -973,6 +1009,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := hostapd
 LOCAL_MODULE_TAGS := optional
+LOCAL_PROPRIETARY_MODULE := true
 ifdef CONFIG_DRIVER_CUSTOM
 LOCAL_STATIC_LIBRARIES := libCustomWifi
 endif
@@ -990,6 +1027,7 @@
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS)
 LOCAL_C_INCLUDES := $(INCLUDES)
+LOCAL_INIT_RC := hostapd.android.rc
 include $(BUILD_EXECUTABLE)
 
 endif # ifeq ($(WPA_BUILD_HOSTAPD),true)
diff --git a/hostapd/ChangeLog b/hostapd/ChangeLog
index af54e1e..d2b669b 100644
--- a/hostapd/ChangeLog
+++ b/hostapd/ChangeLog
@@ -1,5 +1,78 @@
 ChangeLog for hostapd
 
+2016-10-02 - v2.6
+	* fixed EAP-pwd last fragment validation
+	  [http://w1.fi/security/2015-7/] (CVE-2015-5314)
+	* fixed WPS configuration update vulnerability with malformed passphrase
+	  [http://w1.fi/security/2016-1/] (CVE-2016-4476)
+	* extended channel switch support for VHT bandwidth changes
+	* added support for configuring new ANQP-elements with
+	  anqp_elem=<InfoID>:<hexdump of payload>
+	* fixed Suite B 192-bit AKM to use proper PMK length
+	  (note: this makes old releases incompatible with the fixed behavior)
+	* added no_probe_resp_if_max_sta=1 parameter to disable Probe Response
+	  frame sending for not-associated STAs if max_num_sta limit has been
+	  reached
+	* added option (-S as command line argument) to request all interfaces
+	  to be started at the same time
+	* modified rts_threshold and fragm_threshold configuration parameters
+	  to allow -1 to be used to disable RTS/fragmentation
+	* EAP-pwd: added support for Brainpool Elliptic Curves
+	  (with OpenSSL 1.0.2 and newer)
+	* fixed EAPOL reauthentication after FT protocol run
+	* fixed FTIE generation for 4-way handshake after FT protocol run
+	* fixed and improved various FST operations
+	* TLS server
+	  - support SHA384 and SHA512 hashes
+	  - support TLS v1.2 signature algorithm with SHA384 and SHA512
+	  - support PKCS #5 v2.0 PBES2
+	  - support PKCS #5 with PKCS #12 style key decryption
+	  - minimal support for PKCS #12
+	  - support OCSP stapling (including ocsp_multi)
+	* added support for OpenSSL 1.1 API changes
+	  - drop support for OpenSSL 0.9.8
+	  - drop support for OpenSSL 1.0.0
+	* EAP-PEAP: support fast-connect crypto binding
+	* RADIUS
+	  - fix Called-Station-Id to not escape SSID
+	  - add Event-Timestamp to all Accounting-Request packets
+	  - add Acct-Session-Id to Accounting-On/Off
+	  - add Acct-Multi-Session-Id  ton Access-Request packets
+	  - add Service-Type (= Frames)
+	  - allow server to provide PSK instead of passphrase for WPA-PSK
+	    Tunnel_password case
+	  - update full message for interim accounting updates
+	  - add Acct-Delay-Time into Accounting messages
+	  - add require_message_authenticator configuration option to require
+	    CoA/Disconnect-Request packets to be authenticated
+	* started to postpone WNM-Notification frame sending by 100 ms so that
+	  the STA has some more time to configure the key before this frame is
+	  received after the 4-way handshake
+	* VHT: added interoperability workaround for 80+80 and 160 MHz channels
+	* extended VLAN support (per-STA vif, etc.)
+	* fixed PMKID derivation with SAE
+	* nl80211
+	  - added support for full station state operations
+	  - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use
+	    unencrypted EAPOL frames
+	* added initial MBO support; number of extensions to WNM BSS Transition
+	  Management
+	* added initial functionality for location related operations
+	* added assocresp_elements parameter to allow vendor specific elements
+	  to be added into (Re)Association Response frames
+	* improved Public Action frame addressing
+	  - use Address 3 = wildcard BSSID in GAS response if a query from an
+	    unassociated STA used that address
+	  - fix TX status processing for Address 3 = wildcard BSSID
+	  - add gas_address3 configuration parameter to control Address 3
+	    behavior
+	* added command line parameter -i to override interface parameter in
+	  hostapd.conf
+	* added command completion support to hostapd_cli
+	* added passive client taxonomy determination (CONFIG_TAXONOMY=y
+	  compile option and "SIGNATURE <addr>" control interface command)
+	* number of small fixes
+
 2015-09-27 - v2.5
 	* fixed WPS UPnP vulnerability with HTTP chunked transfer encoding
 	  [http://w1.fi/security/2015-2/] (CVE-2015-4141)
diff --git a/hostapd/CleanSpec.mk b/hostapd/CleanSpec.mk
new file mode 100644
index 0000000..653aebb
--- /dev/null
+++ b/hostapd/CleanSpec.mk
@@ -0,0 +1,52 @@
+# -*- mode: makefile -*-
+#  Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(OUT_DIR)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+#
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/hostapd)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/hostapd_cli)
diff --git a/hostapd/Makefile b/hostapd/Makefile
index fa4af82..3160d0d 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -84,8 +84,13 @@
 OBJS += ../src/ap/ieee802_11_shared.o
 OBJS += ../src/ap/beacon.o
 OBJS += ../src/ap/bss_load.o
+OBJS += ../src/ap/neighbor_db.o
+OBJS += ../src/ap/rrm.o
 
-OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o
+OBJS_c = hostapd_cli.o
+OBJS_c += ../src/common/wpa_ctrl.o
+OBJS_c += ../src/utils/os_$(CONFIG_OS).o
+OBJS_c += ../src/common/cli.o
 
 NEED_RC4=y
 NEED_AES=y
@@ -95,6 +100,11 @@
 OBJS += ../src/drivers/drivers.o
 CFLAGS += -DHOSTAPD
 
+ifdef CONFIG_TAXONOMY
+CFLAGS += -DCONFIG_TAXONOMY
+OBJS += ../src/ap/taxonomy.o
+endif
+
 ifdef CONFIG_MODULE_TESTS
 CFLAGS += -DCONFIG_MODULE_TESTS
 OBJS += hapd_module_tests.o
@@ -280,7 +290,7 @@
 endif
 
 ifdef CONFIG_IEEE80211R
-CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -295,6 +305,13 @@
 NEED_AP_MLME=y
 endif
 
+ifdef CONFIG_FILS
+CFLAGS += -DCONFIG_FILS
+OBJS += ../src/ap/fils_hlp.o
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 CFLAGS += -DCONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
@@ -308,6 +325,11 @@
 CFLAGS += -DCONFIG_IEEE80211AC
 endif
 
+ifdef CONFIG_IEEE80211AX
+CFLAGS += -DCONFIG_IEEE80211AX
+OBJS += ../src/ap/ieee802_11_he.o
+endif
+
 ifdef CONFIG_MBO
 CFLAGS += -DCONFIG_MBO
 OBJS += ../src/ap/mbo_ap.o
@@ -767,6 +789,9 @@
 AESOBJS += ../src/crypto/aes-cbc.o
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+endif
 ifdef NEED_AES_DEC
 ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal-dec.o
@@ -855,6 +880,9 @@
 endif
 ifdef NEED_SHA384
 CFLAGS += -DCONFIG_SHA384
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += ../src/crypto/sha384.o
+endif
 OBJS += ../src/crypto/sha384-prf.o
 endif
 
@@ -973,6 +1001,10 @@
 CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
 endif
 
+ifdef CONFIG_DEBUG_SYSLOG
+CFLAGS += -DCONFIG_DEBUG_SYSLOG
+endif
+
 ifdef CONFIG_DEBUG_LINUX_TRACING
 CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
 endif
diff --git a/hostapd/README b/hostapd/README
index 5d5fd36..cb37c8e 100644
--- a/hostapd/README
+++ b/hostapd/README
@@ -2,7 +2,7 @@
 	  Authenticator and RADIUS authentication server
 ================================================================
 
-Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
diff --git a/hostapd/android.config b/hostapd/android.config
index e382c40..1715cc8 100644
--- a/hostapd/android.config
+++ b/hostapd/android.config
@@ -199,3 +199,6 @@
 # These extentions facilitate efficient use of multiple frequency bands
 # available to the AP and the devices that may associate with it.
 #CONFIG_MBO=y
+
+# Include internal line edit mode in hostapd_cli.
+CONFIG_WPA_CLI_EDIT=y
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index c35d5ae..9e95440 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -129,9 +129,6 @@
 	struct mac_acl_entry *newacl;
 	int vlan_id;
 
-	if (!fname)
-		return 0;
-
 	f = fopen(fname, "r");
 	if (!f) {
 		wpa_printf(MSG_ERROR, "MAC list file '%s' not found.", fname);
@@ -208,7 +205,8 @@
 
 	fclose(f);
 
-	qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
+	if (*acl)
+		qsort(*acl, *num, sizeof(**acl), hostapd_acl_comp);
 
 	return 0;
 }
@@ -223,9 +221,6 @@
 	int line = 0, ret = 0, num_methods;
 	struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
 
-	if (!fname)
-		return 0;
-
 	if (os_strncmp(fname, "sqlite:", 7) == 0) {
 #ifdef CONFIG_SQLITE
 		os_free(conf->eap_user_sqlite);
@@ -522,15 +517,10 @@
 	fclose(f);
 
 	if (ret == 0) {
-		user = conf->eap_user;
-		while (user) {
-			struct hostapd_eap_user *prev;
-
-			prev = user;
-			user = user->next;
-			hostapd_config_free_eap_user(prev);
-		}
+		hostapd_config_free_eap_users(conf->eap_user);
 		conf->eap_user = new_user;
+	} else {
+		hostapd_config_free_eap_users(new_user);
 	}
 
 	return ret;
@@ -636,8 +626,7 @@
 }
 
 
-static int hostapd_parse_das_client(struct hostapd_bss_config *bss,
-				    const char *val)
+static int hostapd_parse_das_client(struct hostapd_bss_config *bss, char *val)
 {
 	char *secret;
 
@@ -645,7 +634,7 @@
 	if (secret == NULL)
 		return -1;
 
-	secret++;
+	*secret++ = '\0';
 
 	if (hostapd_parse_ip_addr(val, &bss->radius_das_client_addr))
 		return -1;
@@ -685,12 +674,12 @@
 			val |= WPA_KEY_MGMT_PSK;
 		else if (os_strcmp(start, "WPA-EAP") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		else if (os_strcmp(start, "FT-PSK") == 0)
 			val |= WPA_KEY_MGMT_FT_PSK;
 		else if (os_strcmp(start, "FT-EAP") == 0)
 			val |= WPA_KEY_MGMT_FT_IEEE8021X;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 		else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
 			val |= WPA_KEY_MGMT_PSK_SHA256;
@@ -711,6 +700,18 @@
 		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+		else if (os_strcmp(start, "FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA256;
+		else if (os_strcmp(start, "FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA384;
+#ifdef CONFIG_IEEE80211R_AP
+		else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA256;
+		else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -756,7 +757,25 @@
 {
 	size_t len = os_strlen(val);
 
-	if (keyidx < 0 || keyidx > 3 || wep->key[keyidx] != NULL)
+	if (keyidx < 0 || keyidx > 3)
+		return -1;
+
+	if (len == 0) {
+		int i, set = 0;
+
+		bin_clear_free(wep->key[keyidx], wep->len[keyidx]);
+		wep->key[keyidx] = NULL;
+		wep->len[keyidx] = 0;
+		for (i = 0; i < NUM_WEP_KEYS; i++) {
+			if (wep->key[i])
+				set++;
+		}
+		if (!set)
+			wep->keys_set = 0;
+		return 0;
+	}
+
+	if (wep->key[keyidx] != NULL)
 		return -1;
 
 	if (val[0] == '"') {
@@ -979,7 +998,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static int add_r0kh(struct hostapd_bss_config *bss, char *value)
 {
 	struct ft_remote_r0kh *r0kh;
@@ -1069,7 +1088,7 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 #ifdef CONFIG_IEEE80211N
@@ -1920,31 +1939,6 @@
 #endif /* CONFIG_HS20 */
 
 
-#ifdef CONFIG_WPS_NFC
-static struct wpabuf * hostapd_parse_bin(const char *buf)
-{
-	size_t len;
-	struct wpabuf *ret;
-
-	len = os_strlen(buf);
-	if (len & 0x01)
-		return NULL;
-	len /= 2;
-
-	ret = wpabuf_alloc(len);
-	if (ret == NULL)
-		return NULL;
-
-	if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
-		wpabuf_free(ret);
-		return NULL;
-	}
-
-	return ret;
-}
-#endif /* CONFIG_WPS_NFC */
-
-
 #ifdef CONFIG_ACS
 static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf,
 					      char *pos)
@@ -1987,6 +1981,54 @@
 #endif /* CONFIG_ACS */
 
 
+static int parse_wpabuf_hex(int line, const char *name, struct wpabuf **buf,
+			    const char *val)
+{
+	struct wpabuf *elems;
+
+	if (val[0] == '\0') {
+		wpabuf_free(*buf);
+		*buf = NULL;
+		return 0;
+	}
+
+	elems = wpabuf_parse_bin(val);
+	if (!elems) {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid %s '%s'",
+			   line, name, val);
+		return -1;
+	}
+
+	wpabuf_free(*buf);
+	*buf = elems;
+
+	return 0;
+}
+
+
+#ifdef CONFIG_FILS
+static int parse_fils_realm(struct hostapd_bss_config *bss, const char *val)
+{
+	struct fils_realm *realm;
+	size_t len;
+
+	len = os_strlen(val);
+	realm = os_zalloc(sizeof(*realm) + len + 1);
+	if (!realm)
+		return -1;
+
+	os_memcpy(realm->realm, val, len);
+	if (fils_domain_name_hash(val, realm->hash) < 0) {
+		os_free(realm);
+		return -1;
+	}
+	dl_list_add_tail(&bss->fils_realms, &realm->list);
+
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
 			       struct hostapd_bss_config *bss,
 			       const char *buf, char *pos, int line)
@@ -2002,20 +2044,21 @@
 		os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
 	} else if (os_strcmp(buf, "driver") == 0) {
 		int j;
-		/* clear to get error below if setting is invalid */
-		conf->driver = NULL;
+		const struct wpa_driver_ops *driver = NULL;
+
 		for (j = 0; wpa_drivers[j]; j++) {
 			if (os_strcmp(pos, wpa_drivers[j]->name) == 0) {
-				conf->driver = wpa_drivers[j];
+				driver = wpa_drivers[j];
 				break;
 			}
 		}
-		if (conf->driver == NULL) {
+		if (!driver) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid/unknown driver '%s'",
 				   line, pos);
 			return 1;
 		}
+		conf->driver = driver;
 	} else if (os_strcmp(buf, "driver_params") == 0) {
 		os_free(conf->driver_params);
 		conf->driver_params = os_strdup(pos);
@@ -2059,13 +2102,16 @@
 	} else if (os_strcmp(buf, "utf8_ssid") == 0) {
 		bss->ssid.utf8_ssid = atoi(pos) > 0;
 	} else if (os_strcmp(buf, "macaddr_acl") == 0) {
-		bss->macaddr_acl = atoi(pos);
-		if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
-		    bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
-		    bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+		enum macaddr_acl acl = atoi(pos);
+
+		if (acl != ACCEPT_UNLESS_DENIED &&
+		    acl != DENY_UNLESS_ACCEPTED &&
+		    acl != USE_EXTERNAL_RADIUS_AUTH) {
 			wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d",
-				   line, bss->macaddr_acl);
+				   line, acl);
+			return 1;
 		}
+		bss->macaddr_acl = acl;
 	} else if (os_strcmp(buf, "accept_mac_file") == 0) {
 		if (hostapd_config_read_maclist(pos, &bss->accept_mac,
 						&bss->num_accept_mac)) {
@@ -2101,13 +2147,15 @@
 	} else if (os_strcmp(buf, "ieee8021x") == 0) {
 		bss->ieee802_1x = atoi(pos);
 	} else if (os_strcmp(buf, "eapol_version") == 0) {
-		bss->eapol_version = atoi(pos);
-		if (bss->eapol_version < 1 || bss->eapol_version > 2) {
+		int eapol_version = atoi(pos);
+
+		if (eapol_version < 1 || eapol_version > 2) {
 			wpa_printf(MSG_ERROR,
 				   "Line %d: invalid EAPOL version (%d): '%s'.",
-				   line, bss->eapol_version, pos);
+				   line, eapol_version, pos);
 			return 1;
 		}
+		bss->eapol_version = eapol_version;
 		wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version);
 #ifdef EAP_SERVER
 	} else if (os_strcmp(buf, "eap_authenticator") == 0) {
@@ -2235,24 +2283,25 @@
 		os_free(bss->erp_domain);
 		bss->erp_domain = os_strdup(pos);
 	} else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
-		bss->default_wep_key_len = atoi(pos);
-		if (bss->default_wep_key_len > 13) {
-			wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)",
-				   line,
-				   (unsigned long) bss->default_wep_key_len,
-				   (unsigned long)
-				   bss->default_wep_key_len * 8);
+		int val = atoi(pos);
+
+		if (val < 0 || val > 13) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid WEP key len %d (= %d bits)",
+				   line, val, val * 8);
 			return 1;
 		}
+		bss->default_wep_key_len = val;
 	} else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
-		bss->individual_wep_key_len = atoi(pos);
-		if (bss->individual_wep_key_len < 0 ||
-		    bss->individual_wep_key_len > 13) {
-			wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)",
-				   line, bss->individual_wep_key_len,
-				   bss->individual_wep_key_len * 8);
+		int val = atoi(pos);
+
+		if (val < 0 || val > 13) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid WEP key len %d (= %d bits)",
+				   line, val, val * 8);
 			return 1;
 		}
+		bss->individual_wep_key_len = val;
 	} else if (os_strcmp(buf, "wep_rekey_period") == 0) {
 		bss->wep_rekeying_period = atoi(pos);
 		if (bss->wep_rekeying_period < 0) {
@@ -2411,6 +2460,9 @@
 		bss->radius_das_time_window = atoi(pos);
 	} else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) {
 		bss->radius_das_require_event_timestamp = atoi(pos);
+	} else if (os_strcmp(buf, "radius_das_require_message_authenticator") ==
+		   0) {
+		bss->radius_das_require_message_authenticator = atoi(pos);
 #endif /* CONFIG_NO_RADIUS */
 	} else if (os_strcmp(buf, "auth_algs") == 0) {
 		bss->auth_algs = atoi(pos);
@@ -2437,6 +2489,28 @@
 		bss->wpa_gmk_rekey = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
 		bss->wpa_ptk_rekey = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
+		char *endp;
+		unsigned long val = strtoul(pos, &endp, 0);
+
+		if (*endp || val < 1 || val > (u32) -1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295",
+				   line, val);
+			return 1;
+		}
+		bss->wpa_group_update_count = (u32) val;
+	} else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) {
+		char *endp;
+		unsigned long val = strtoul(pos, &endp, 0);
+
+		if (*endp || val < 1 || val > (u32) -1) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295",
+				   line, val);
+			return 1;
+		}
+		bss->wpa_pairwise_update_count = (u32) val;
 	} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
 		int len = os_strlen(pos);
 		if (len < 8 || len > 63) {
@@ -2519,7 +2593,7 @@
 	} else if (os_strcmp(buf, "peerkey") == 0) {
 		bss->peerkey = atoi(pos);
 #endif /* CONFIG_PEERKEY */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	} else if (os_strcmp(buf, "mobility_domain") == 0) {
 		if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
 		    hexstr2bin(pos, bss->mobility_domain,
@@ -2557,7 +2631,9 @@
 		bss->pmk_r1_push = atoi(pos);
 	} else if (os_strcmp(buf, "ft_over_ds") == 0) {
 		bss->ft_over_ds = atoi(pos);
-#endif /* CONFIG_IEEE80211R */
+	} else if (os_strcmp(buf, "ft_psk_generate_local") == 0) {
+		bss->ft_psk_generate_local = atoi(pos);
+#endif /* CONFIG_IEEE80211R_AP */
 #ifndef CONFIG_NO_CTRL_IFACE
 	} else if (os_strcmp(buf, "ctrl_interface") == 0) {
 		os_free(bss->ctrl_interface);
@@ -2685,12 +2761,14 @@
 		}
 #endif /* CONFIG_ACS */
 	} else if (os_strcmp(buf, "dtim_period") == 0) {
-		bss->dtim_period = atoi(pos);
-		if (bss->dtim_period < 1 || bss->dtim_period > 255) {
+		int val = atoi(pos);
+
+		if (val < 1 || val > 255) {
 			wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d",
-				   line, bss->dtim_period);
+				   line, val);
 			return 1;
 		}
+		bss->dtim_period = val;
 	} else if (os_strcmp(buf, "bss_load_update_period") == 0) {
 		bss->bss_load_update_period = atoi(pos);
 		if (bss->bss_load_update_period < 0 ||
@@ -2739,6 +2817,40 @@
 				   line);
 			return 1;
 		}
+	} else if (os_strcmp(buf, "beacon_rate") == 0) {
+		int val;
+
+		if (os_strncmp(pos, "ht:", 3) == 0) {
+			val = atoi(pos + 3);
+			if (val < 0 || val > 31) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: invalid beacon_rate HT-MCS %d",
+					   line, val);
+				return 1;
+			}
+			conf->rate_type = BEACON_RATE_HT;
+			conf->beacon_rate = val;
+		} else if (os_strncmp(pos, "vht:", 4) == 0) {
+			val = atoi(pos + 4);
+			if (val < 0 || val > 9) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: invalid beacon_rate VHT-MCS %d",
+					   line, val);
+				return 1;
+			}
+			conf->rate_type = BEACON_RATE_VHT;
+			conf->beacon_rate = val;
+		} else {
+			val = atoi(pos);
+			if (val < 10 || val > 10000) {
+				wpa_printf(MSG_ERROR,
+					   "Line %d: invalid legacy beacon_rate %d",
+					   line, val);
+				return 1;
+			}
+			conf->rate_type = BEACON_RATE_LEGACY;
+			conf->beacon_rate = val;
+		}
 	} else if (os_strcmp(buf, "preamble") == 0) {
 		if (atoi(pos))
 			conf->preamble = SHORT_PREAMBLE;
@@ -2893,7 +3005,27 @@
 		conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
 	} else if (os_strcmp(buf, "vendor_vht") == 0) {
 		bss->vendor_vht = atoi(pos);
+	} else if (os_strcmp(buf, "use_sta_nsts") == 0) {
+		bss->use_sta_nsts = atoi(pos);
 #endif /* CONFIG_IEEE80211AC */
+#ifdef CONFIG_IEEE80211AX
+	} else if (os_strcmp(buf, "ieee80211ax") == 0) {
+		conf->ieee80211ax = atoi(pos);
+	} else if (os_strcmp(buf, "he_su_beamformer") == 0) {
+		conf->he_phy_capab.he_su_beamformer = atoi(pos);
+	} else if (os_strcmp(buf, "he_su_beamformee") == 0) {
+		conf->he_phy_capab.he_su_beamformee = atoi(pos);
+	} else if (os_strcmp(buf, "he_mu_beamformer") == 0) {
+		conf->he_phy_capab.he_mu_beamformer = atoi(pos);
+	} else if (os_strcmp(buf, "he_bss_color") == 0) {
+		conf->he_op.he_bss_color = atoi(pos);
+	} else if (os_strcmp(buf, "he_default_pe_duration") == 0) {
+		conf->he_op.he_default_pe_duration = atoi(pos);
+	} else if (os_strcmp(buf, "he_twt_required") == 0) {
+		conf->he_op.he_twt_required = atoi(pos);
+	} else if (os_strcmp(buf, "he_rts_threshold") == 0) {
+		conf->he_op.he_rts_threshold = atoi(pos);
+#endif /* CONFIG_IEEE80211AX */
 	} else if (os_strcmp(buf, "max_listen_interval") == 0) {
 		bss->max_listen_interval = atoi(pos);
 	} else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
@@ -3031,15 +3163,15 @@
 		bss->wps_nfc_pw_from_config = 1;
 	} else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
 		wpabuf_free(bss->wps_nfc_dh_pubkey);
-		bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
+		bss->wps_nfc_dh_pubkey = wpabuf_parse_bin(pos);
 		bss->wps_nfc_pw_from_config = 1;
 	} else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
 		wpabuf_free(bss->wps_nfc_dh_privkey);
-		bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
+		bss->wps_nfc_dh_privkey = wpabuf_parse_bin(pos);
 		bss->wps_nfc_pw_from_config = 1;
 	} else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
 		wpabuf_free(bss->wps_nfc_dev_pw);
-		bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
+		bss->wps_nfc_dev_pw = wpabuf_parse_bin(pos);
 		bss->wps_nfc_pw_from_config = 1;
 #endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
@@ -3206,7 +3338,15 @@
 		if (parse_anqp_elem(bss, pos, line) < 0)
 			return 1;
 	} else if (os_strcmp(buf, "gas_frag_limit") == 0) {
-		bss->gas_frag_limit = atoi(pos);
+		int val = atoi(pos);
+
+		if (val <= 0) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid gas_frag_limit '%s'",
+				   line, pos);
+			return 1;
+		}
+		bss->gas_frag_limit = val;
 	} else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
 		bss->gas_comeback_delay = atoi(pos);
 	} else if (os_strcmp(buf, "qos_map_set") == 0) {
@@ -3346,7 +3486,15 @@
 		WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
 		bss->bss_load_test_set = 1;
 	} else if (os_strcmp(buf, "radio_measurements") == 0) {
-		bss->radio_measurements = atoi(pos);
+		/*
+		 * DEPRECATED: This parameter will be removed in the future.
+		 * Use rrm_neighbor_report instead.
+		 */
+		int val = atoi(pos);
+
+		if (val & BIT(0))
+			bss->radio_measurements[0] |=
+				WLAN_RRM_CAPS_NEIGHBOR_REPORT;
 	} else if (os_strcmp(buf, "own_ie_override") == 0) {
 		struct wpabuf *tmp;
 		size_t len = os_strlen(pos) / 2;
@@ -3367,35 +3515,11 @@
 		bss->own_ie_override = tmp;
 #endif /* CONFIG_TESTING_OPTIONS */
 	} else if (os_strcmp(buf, "vendor_elements") == 0) {
-		struct wpabuf *elems;
-		size_t len = os_strlen(pos);
-		if (len & 0x01) {
-			wpa_printf(MSG_ERROR,
-				   "Line %d: Invalid vendor_elements '%s'",
-				   line, pos);
+		if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
 			return 1;
-		}
-		len /= 2;
-		if (len == 0) {
-			wpabuf_free(bss->vendor_elements);
-			bss->vendor_elements = NULL;
-			return 0;
-		}
-
-		elems = wpabuf_alloc(len);
-		if (elems == NULL)
+	} else if (os_strcmp(buf, "assocresp_elements") == 0) {
+		if (parse_wpabuf_hex(line, buf, &bss->assocresp_elements, pos))
 			return 1;
-
-		if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
-			wpabuf_free(elems);
-			wpa_printf(MSG_ERROR,
-				   "Line %d: Invalid vendor_elements '%s'",
-				   line, pos);
-			return 1;
-		}
-
-		wpabuf_free(bss->vendor_elements);
-		bss->vendor_elements = elems;
 	} else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
 		bss->sae_anti_clogging_threshold = atoi(pos);
 	} else if (os_strcmp(buf, "sae_groups") == 0) {
@@ -3487,6 +3611,68 @@
 	} else if (os_strcmp(buf, "no_auth_if_seen_on") == 0) {
 		os_free(bss->no_auth_if_seen_on);
 		bss->no_auth_if_seen_on = os_strdup(pos);
+	} else if (os_strcmp(buf, "lci") == 0) {
+		wpabuf_free(conf->lci);
+		conf->lci = wpabuf_parse_bin(pos);
+		if (conf->lci && wpabuf_len(conf->lci) == 0) {
+			wpabuf_free(conf->lci);
+			conf->lci = NULL;
+		}
+	} else if (os_strcmp(buf, "civic") == 0) {
+		wpabuf_free(conf->civic);
+		conf->civic = wpabuf_parse_bin(pos);
+		if (conf->civic && wpabuf_len(conf->civic) == 0) {
+			wpabuf_free(conf->civic);
+			conf->civic = NULL;
+		}
+	} else if (os_strcmp(buf, "rrm_neighbor_report") == 0) {
+		if (atoi(pos))
+			bss->radio_measurements[0] |=
+				WLAN_RRM_CAPS_NEIGHBOR_REPORT;
+	} else if (os_strcmp(buf, "rrm_beacon_report") == 0) {
+		if (atoi(pos))
+			bss->radio_measurements[0] |=
+				WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+				WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+				WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+	} else if (os_strcmp(buf, "gas_address3") == 0) {
+		bss->gas_address3 = atoi(pos);
+	} else if (os_strcmp(buf, "stationary_ap") == 0) {
+		conf->stationary_ap = atoi(pos);
+	} else if (os_strcmp(buf, "ftm_responder") == 0) {
+		bss->ftm_responder = atoi(pos);
+	} else if (os_strcmp(buf, "ftm_initiator") == 0) {
+		bss->ftm_initiator = atoi(pos);
+#ifdef CONFIG_FILS
+	} else if (os_strcmp(buf, "fils_cache_id") == 0) {
+		if (hexstr2bin(pos, bss->fils_cache_id, FILS_CACHE_ID_LEN)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid fils_cache_id '%s'",
+				   line, pos);
+			return 1;
+		}
+		bss->fils_cache_id_set = 1;
+	} else if (os_strcmp(buf, "fils_realm") == 0) {
+		if (parse_fils_realm(bss, pos) < 0)
+			return 1;
+	} else if (os_strcmp(buf, "dhcp_server") == 0) {
+		if (hostapd_parse_ip_addr(pos, &bss->dhcp_server)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: invalid IP address '%s'",
+				   line, pos);
+			return 1;
+		}
+	} else if (os_strcmp(buf, "dhcp_rapid_commit_proxy") == 0) {
+		bss->dhcp_rapid_commit_proxy = atoi(pos);
+	} else if (os_strcmp(buf, "fils_hlp_wait_time") == 0) {
+		bss->fils_hlp_wait_time = atoi(pos);
+	} else if (os_strcmp(buf, "dhcp_server_port") == 0) {
+		bss->dhcp_server_port = atoi(pos);
+	} else if (os_strcmp(buf, "dhcp_relay_port") == 0) {
+		bss->dhcp_relay_port = atoi(pos);
+#endif /* CONFIG_FILS */
+	} else if (os_strcmp(buf, "multicast_to_unicast") == 0) {
+		bss->multicast_to_unicast = atoi(pos);
 	} else {
 		wpa_printf(MSG_ERROR,
 			   "Line %d: unknown configuration item '%s'",
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index a87f117..62feaa4 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -25,6 +25,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/module_tests.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ctrl_iface_common.h"
@@ -47,6 +48,8 @@
 #include "ap/wnm_ap.h"
 #include "ap/wpa_auth.h"
 #include "ap/beacon.h"
+#include "ap/neighbor_db.h"
+#include "ap/rrm.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
 #include "fst/fst_ctrl_iface.h"
@@ -1014,14 +1017,16 @@
 		if (ret != 3) {
 			wpa_printf(MSG_DEBUG,
 				   "MBO requires three arguments: mbo=<reason>:<reassoc_delay>:<cell_pref>");
-			return -1;
+			ret = -1;
+			goto fail;
 		}
 
 		if (mbo_reason > MBO_TRANSITION_REASON_PREMIUM_AP) {
 			wpa_printf(MSG_DEBUG,
 				   "Invalid MBO transition reason code %u",
 				   mbo_reason);
-			return -1;
+			ret = -1;
+			goto fail;
 		}
 
 		/* Valid values for Cellular preference are: 0, 1, 255 */
@@ -1029,7 +1034,8 @@
 			wpa_printf(MSG_DEBUG,
 				   "Invalid MBO cellular capability %u",
 				   cell_pref);
-			return -1;
+			ret = -1;
+			goto fail;
 		}
 
 		if (reassoc_delay > 65535 ||
@@ -1037,7 +1043,8 @@
 		     !(req_mode & WNM_BSS_TM_REQ_DISASSOC_IMMINENT))) {
 			wpa_printf(MSG_DEBUG,
 				   "MBO: Assoc retry delay is only valid in disassoc imminent mode");
-			return -1;
+			ret = -1;
+			goto fail;
 		}
 
 		*mbo_pos++ = MBO_ATTR_ID_TRANSITION_REASON;
@@ -1063,6 +1070,9 @@
 				  nei_pos > nei_rep ? nei_rep : NULL,
 				  nei_pos - nei_rep, mbo_len ? mbo : NULL,
 				  mbo_len);
+#ifdef CONFIG_MBO
+fail:
+#endif /* CONFIG_MBO */
 	os_free(url);
 	return ret;
 }
@@ -1093,7 +1103,7 @@
 			return pos - buf;
 		pos += ret;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
 		ret = os_snprintf(pos, end - pos, "FT-PSK ");
 		if (os_snprintf_error(end - pos, ret))
@@ -1114,7 +1124,21 @@
 		pos += ret;
 	}
 #endif /* CONFIG_SAE */
-#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_FILS
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "FT-FILS-SHA384 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
 		ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
@@ -1151,6 +1175,20 @@
 			return pos - buf;
 		pos += ret;
 	}
+#ifdef CONFIG_FILS
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "FILS-SHA256 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+	if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "FILS-SHA384 ");
+		if (os_snprintf_error(end - pos, ret))
+			return pos - buf;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS */
 
 	if (pos > buf && *(pos - 1) == ' ') {
 		*(pos - 1) = '\0';
@@ -1316,14 +1354,6 @@
 		wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
 			   wps_corrupt_pkhash);
 #endif /* CONFIG_WPS_TESTING */
-#ifdef CONFIG_INTERWORKING
-	} else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
-		int val = atoi(value);
-		if (val <= 0)
-			ret = -1;
-		else
-			hapd->gas_frag_limit = val;
-#endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_TESTING_OPTIONS
 	} else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
 		hapd->ext_mgmt_frame_handling = atoi(value);
@@ -1531,6 +1561,76 @@
 }
 
 
+static int hostapd_ctrl_iface_mgmt_rx_process(struct hostapd_data *hapd,
+					      char *cmd)
+{
+	char *pos, *param;
+	size_t len;
+	u8 *buf;
+	int freq = 0, datarate = 0, ssi_signal = 0;
+	union wpa_event_data event;
+
+	if (!hapd->ext_mgmt_frame_handling)
+		return -1;
+
+	/* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */
+
+	wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
+
+	pos = cmd;
+	param = os_strstr(pos, "freq=");
+	if (param) {
+		param += 5;
+		freq = atoi(param);
+	}
+
+	param = os_strstr(pos, " datarate=");
+	if (param) {
+		param += 10;
+		datarate = atoi(param);
+	}
+
+	param = os_strstr(pos, " ssi_signal=");
+	if (param) {
+		param += 12;
+		ssi_signal = atoi(param);
+	}
+
+	param = os_strstr(pos, " frame=");
+	if (param == NULL)
+		return -1;
+	param += 7;
+
+	len = os_strlen(param);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(param, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_mgmt.freq = freq;
+	event.rx_mgmt.frame = buf;
+	event.rx_mgmt.frame_len = len;
+	event.rx_mgmt.ssi_signal = ssi_signal;
+	event.rx_mgmt.datarate = datarate;
+	hapd->ext_mgmt_frame_handling = 0;
+	wpa_supplicant_event(hapd, EVENT_RX_MGMT, &event);
+	hapd->ext_mgmt_frame_handling = 1;
+
+	os_free(buf);
+
+	return 0;
+}
+
+
 static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
 {
 	char *pos;
@@ -1588,8 +1688,8 @@
 #define HWSIM_PACKETLEN 1500
 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
 
-void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
-			  size_t len)
+static void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+				 size_t len)
 {
 	struct hostapd_data *hapd = ctx;
 	const struct ether_header *eth;
@@ -1776,8 +1876,6 @@
 static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_fail_func[256];
-	extern unsigned int wpa_trace_fail_after;
 	char *pos;
 
 	wpa_trace_fail_after = atoi(cmd);
@@ -1801,9 +1899,6 @@
 				       char *buf, size_t buflen)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_fail_func[256];
-	extern unsigned int wpa_trace_fail_after;
-
 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
 			   wpa_trace_fail_func);
 #else /* WPA_TRACE_BFD */
@@ -1815,8 +1910,6 @@
 static int hostapd_ctrl_test_fail(struct hostapd_data *hapd, char *cmd)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_test_fail_func[256];
-	extern unsigned int wpa_trace_test_fail_after;
 	char *pos;
 
 	wpa_trace_test_fail_after = atoi(cmd);
@@ -1840,9 +1933,6 @@
 				 char *buf, size_t buflen)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_test_fail_func[256];
-	extern unsigned int wpa_trace_test_fail_after;
-
 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
 			   wpa_trace_test_fail_func);
 #else /* WPA_TRACE_BFD */
@@ -2047,6 +2137,9 @@
 	struct hostapd_sta_info *info;
 	struct os_reltime now;
 
+	if (!iface->num_sta_seen)
+		return 0;
+
 	sta_track_expire(iface, 0);
 
 	pos = buf;
@@ -2059,8 +2152,9 @@
 		int ret;
 
 		os_reltime_sub(&now, &info->last_seen, &age);
-		ret = os_snprintf(pos, end - pos, MACSTR " %u\n",
-				  MAC2STR(info->addr), (unsigned int) age.sec);
+		ret = os_snprintf(pos, end - pos, MACSTR " %u %d\n",
+				  MAC2STR(info->addr), (unsigned int) age.sec,
+				  info->ssi_signal);
 		if (os_snprintf_error(end - pos, ret))
 			break;
 		pos += ret;
@@ -2071,6 +2165,272 @@
 #endif /* NEED_AP_MLME */
 
 
+static int hostapd_ctrl_iface_req_lci(struct hostapd_data *hapd,
+				      const char *cmd)
+{
+	u8 addr[ETH_ALEN];
+
+	if (hwaddr_aton(cmd, addr)) {
+		wpa_printf(MSG_INFO, "CTRL: REQ_LCI: Invalid MAC address");
+		return -1;
+	}
+
+	return hostapd_send_lci_req(hapd, addr);
+}
+
+
+static int hostapd_ctrl_iface_req_range(struct hostapd_data *hapd, char *cmd)
+{
+	u8 addr[ETH_ALEN];
+	char *token, *context = NULL;
+	int random_interval, min_ap;
+	u8 responders[ETH_ALEN * RRM_RANGE_REQ_MAX_RESPONDERS];
+	unsigned int n_responders;
+
+	token = str_token(cmd, " ", &context);
+	if (!token || hwaddr_aton(token, addr)) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: REQ_RANGE - Bad destination address");
+		return -1;
+	}
+
+	token = str_token(cmd, " ", &context);
+	if (!token)
+		return -1;
+
+	random_interval = atoi(token);
+	if (random_interval < 0 || random_interval > 0xffff)
+		return -1;
+
+	token = str_token(cmd, " ", &context);
+	if (!token)
+		return -1;
+
+	min_ap = atoi(token);
+	if (min_ap <= 0 || min_ap > WLAN_RRM_RANGE_REQ_MAX_MIN_AP)
+		return -1;
+
+	n_responders = 0;
+	while ((token = str_token(cmd, " ", &context))) {
+		if (n_responders == RRM_RANGE_REQ_MAX_RESPONDERS) {
+			wpa_printf(MSG_INFO,
+				   "CTRL: REQ_RANGE: Too many responders");
+			return -1;
+		}
+
+		if (hwaddr_aton(token, responders + n_responders * ETH_ALEN)) {
+			wpa_printf(MSG_INFO,
+				   "CTRL: REQ_RANGE: Bad responder address");
+			return -1;
+		}
+
+		n_responders++;
+	}
+
+	if (!n_responders) {
+		wpa_printf(MSG_INFO,
+			   "CTRL: REQ_RANGE - No FTM responder address");
+		return -1;
+	}
+
+	return hostapd_send_range_req(hapd, addr, random_interval, min_ap,
+				      responders, n_responders);
+}
+
+
+static int hostapd_ctrl_iface_req_beacon(struct hostapd_data *hapd,
+					 const char *cmd, char *reply,
+					 size_t reply_size)
+{
+	u8 addr[ETH_ALEN];
+	const char *pos;
+	struct wpabuf *req;
+	int ret;
+	u8 req_mode = 0;
+
+	if (hwaddr_aton(cmd, addr))
+		return -1;
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+	if (os_strncmp(pos, "req_mode=", 9) == 0) {
+		int val = hex2byte(pos + 9);
+
+		if (val < 0)
+			return -1;
+		req_mode = val;
+		pos += 11;
+		pos = os_strchr(pos, ' ');
+		if (!pos)
+			return -1;
+		pos++;
+	}
+	req = wpabuf_parse_bin(pos);
+	if (!req)
+		return -1;
+
+	ret = hostapd_send_beacon_req(hapd, addr, req_mode, req);
+	wpabuf_free(req);
+	if (ret >= 0)
+		ret = os_snprintf(reply, reply_size, "%d", ret);
+	return ret;
+}
+
+
+static int hostapd_ctrl_iface_set_neighbor(struct hostapd_data *hapd, char *buf)
+{
+	struct wpa_ssid_value ssid;
+	u8 bssid[ETH_ALEN];
+	struct wpabuf *nr, *lci = NULL, *civic = NULL;
+	int stationary = 0;
+	char *tmp;
+	int ret;
+
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: SET_NEIGHBOR: Neighbor report is not enabled");
+		return -1;
+	}
+
+	if (hwaddr_aton(buf, bssid)) {
+		wpa_printf(MSG_ERROR, "CTRL: SET_NEIGHBOR: Bad BSSID");
+		return -1;
+	}
+
+	tmp = os_strstr(buf, "ssid=");
+	if (!tmp || ssid_parse(tmp + 5, &ssid)) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: SET_NEIGHBOR: Bad or missing SSID");
+		return -1;
+	}
+	buf = os_strchr(tmp + 6, tmp[5] == '"' ? '"' : ' ');
+	if (!buf)
+		return -1;
+
+	tmp = os_strstr(buf, "nr=");
+	if (!tmp) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: SET_NEIGHBOR: Missing Neighbor Report element");
+		return -1;
+	}
+
+	buf = os_strchr(tmp, ' ');
+	if (buf)
+		*buf++ = '\0';
+
+	nr = wpabuf_parse_bin(tmp + 3);
+	if (!nr) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: SET_NEIGHBOR: Bad Neighbor Report element");
+		return -1;
+	}
+
+	if (!buf)
+		goto set;
+
+	tmp = os_strstr(buf, "lci=");
+	if (tmp) {
+		buf = os_strchr(tmp, ' ');
+		if (buf)
+			*buf++ = '\0';
+		lci = wpabuf_parse_bin(tmp + 4);
+		if (!lci) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL: SET_NEIGHBOR: Bad LCI subelement");
+			wpabuf_free(nr);
+			return -1;
+		}
+	}
+
+	if (!buf)
+		goto set;
+
+	tmp = os_strstr(buf, "civic=");
+	if (tmp) {
+		buf = os_strchr(tmp, ' ');
+		if (buf)
+			*buf++ = '\0';
+		civic = wpabuf_parse_bin(tmp + 6);
+		if (!civic) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL: SET_NEIGHBOR: Bad civic subelement");
+			wpabuf_free(nr);
+			wpabuf_free(lci);
+			return -1;
+		}
+	}
+
+	if (!buf)
+		goto set;
+
+	if (os_strstr(buf, "stat"))
+		stationary = 1;
+
+set:
+	ret = hostapd_neighbor_set(hapd, bssid, &ssid, nr, lci, civic,
+				   stationary);
+
+	wpabuf_free(nr);
+	wpabuf_free(lci);
+	wpabuf_free(civic);
+
+	return ret;
+}
+
+
+static int hostapd_ctrl_iface_remove_neighbor(struct hostapd_data *hapd,
+					      char *buf)
+{
+	struct wpa_ssid_value ssid;
+	u8 bssid[ETH_ALEN];
+	char *tmp;
+
+	if (hwaddr_aton(buf, bssid)) {
+		wpa_printf(MSG_ERROR, "CTRL: REMOVE_NEIGHBOR: Bad BSSID");
+		return -1;
+	}
+
+	tmp = os_strstr(buf, "ssid=");
+	if (!tmp || ssid_parse(tmp + 5, &ssid)) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: REMOVE_NEIGHBORr: Bad or missing SSID");
+		return -1;
+	}
+
+	return hostapd_neighbor_remove(hapd, bssid, &ssid);
+}
+
+
+static int hostapd_ctrl_driver_flags(struct hostapd_iface *iface, char *buf,
+				     size_t buflen)
+{
+	int ret, i;
+	char *pos, *end;
+
+	ret = os_snprintf(buf, buflen, "%016llX:\n",
+			  (long long unsigned) iface->drv_flags);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+
+	pos = buf + ret;
+	end = buf + buflen;
+
+	for (i = 0; i < 64; i++) {
+		if (iface->drv_flags & (1LLU << i)) {
+			ret = os_snprintf(pos, end - pos, "%s\n",
+					  driver_flag_to_string(1LLU << i));
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
 static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
 					      char *buf, char *reply,
 					      int reply_size,
@@ -2153,6 +2513,14 @@
 	} else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
 		if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
 			reply_len = -1;
+#ifdef CONFIG_TAXONOMY
+	} else if (os_strncmp(buf, "SIGNATURE ", 10) == 0) {
+		reply_len = hostapd_ctrl_iface_signature(hapd, buf + 10,
+							 reply, reply_size);
+#endif /* CONFIG_TAXONOMY */
+	} else if (os_strncmp(buf, "POLL_STA ", 9) == 0) {
+		if (hostapd_ctrl_iface_poll_sta(hapd, buf + 9))
+			reply_len = -1;
 	} else if (os_strcmp(buf, "STOP_AP") == 0) {
 		if (hostapd_ctrl_iface_stop_ap(hapd))
 			reply_len = -1;
@@ -2258,6 +2626,9 @@
 	} else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
 		if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
+		if (hostapd_ctrl_iface_mgmt_rx_process(hapd, buf + 16) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
 		if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
 			reply_len = -1;
@@ -2312,6 +2683,26 @@
 							  reply_size);
 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
 		hostapd_ctrl_iface_pmksa_flush(hapd);
+	} else if (os_strncmp(buf, "SET_NEIGHBOR ", 13) == 0) {
+		if (hostapd_ctrl_iface_set_neighbor(hapd, buf + 13))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "REMOVE_NEIGHBOR ", 16) == 0) {
+		if (hostapd_ctrl_iface_remove_neighbor(hapd, buf + 16))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "REQ_LCI ", 8) == 0) {
+		if (hostapd_ctrl_iface_req_lci(hapd, buf + 8))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "REQ_RANGE ", 10) == 0) {
+		if (hostapd_ctrl_iface_req_range(hapd, buf + 10))
+			reply_len = -1;
+	} else if (os_strncmp(buf, "REQ_BEACON ", 11) == 0) {
+		reply_len = hostapd_ctrl_iface_req_beacon(hapd, buf + 11,
+							  reply, reply_size);
+	} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
+		reply_len = hostapd_ctrl_driver_flags(hapd->iface, reply,
+						      reply_size);
+	} else if (os_strcmp(buf, "TERMINATE") == 0) {
+		eloop_terminate();
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -3142,7 +3533,6 @@
 			reply_len = -1;
 #ifdef CONFIG_MODULE_TESTS
 	} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
-		int hapd_module_tests(void);
 		if (hapd_module_tests() < 0)
 			reply_len = -1;
 #endif /* CONFIG_MODULE_TESTS */
@@ -3242,8 +3632,6 @@
 		}
 	}
 
-	dl_list_init(&interface->global_ctrl_dst);
-	interface->global_ctrl_sock = -1;
 	os_get_random(gcookie, COOKIE_LEN);
 
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
diff --git a/hostapd/defconfig b/hostapd/defconfig
index f7b60e0..9ade580 100644
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -157,6 +157,12 @@
 # IEEE 802.11ac (Very High Throughput) support
 #CONFIG_IEEE80211AC=y
 
+# IEEE 802.11ax HE support
+# Note: This is experimental and work in progress. The definitions are still
+# subject to change and this should not be expected to interoperate with the
+# final IEEE 802.11ax version.
+#CONFIG_IEEE80211AX=y
+
 # Remove debugging code that is printing out debug messages to stdout.
 # This can be used to reduce the size of the hostapd considerably if debugging
 # code is not needed.
@@ -166,6 +172,9 @@
 # Disabled by default.
 #CONFIG_DEBUG_FILE=y
 
+# Send debug messages to syslog instead of stdout
+#CONFIG_DEBUG_SYSLOG=y
+
 # Add support for sending all debug messages (regardless of debug verbosity)
 # to the Linux kernel tracing facility. This helps debug the entire stack by
 # making it easy to record everything happening from the driver up into the
@@ -337,3 +346,18 @@
 # These extentions facilitate efficient use of multiple frequency bands
 # available to the AP and the devices that may associate with it.
 #CONFIG_MBO=y
+
+# Client Taxonomy
+# Has the AP retain the Probe Request and (Re)Association Request frames from
+# a client, from which a signature can be produced which can identify the model
+# of client device like "Nexus 6P" or "iPhone 5s".
+#CONFIG_TAXONOMY=y
+
+# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
+# Note: This is an experimental and not yet complete implementation. This
+# should not be enabled for production use.
+#CONFIG_FILS=y
+
+# Include internal line edit mode in hostapd_cli. This can be used to provide
+# limited command line editing and history support.
+#CONFIG_WPA_CLI_EDIT=y
diff --git a/hostapd/hapd_module_tests.c b/hostapd/hapd_module_tests.c
index f7887eb..a5016f2 100644
--- a/hostapd/hapd_module_tests.c
+++ b/hostapd/hapd_module_tests.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/module_tests.h"
 
 int hapd_module_tests(void)
 {
diff --git a/hostapd/hlr_auc_gw.c b/hostapd/hlr_auc_gw.c
index 2117d34..5caa779 100644
--- a/hostapd/hlr_auc_gw.c
+++ b/hostapd/hlr_auc_gw.c
@@ -1,6 +1,6 @@
 /*
  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -973,7 +973,7 @@
 {
 	printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
 	       "database/authenticator\n"
-	       "Copyright (c) 2005-2016, Jouni Malinen <j@w1.fi>\n"
+	       "Copyright (c) 2005-2017, Jouni Malinen <j@w1.fi>\n"
 	       "\n"
 	       "usage:\n"
 	       "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
diff --git a/hostapd/hostapd.android.rc b/hostapd/hostapd.android.rc
new file mode 100644
index 0000000..672e188
--- /dev/null
+++ b/hostapd/hostapd.android.rc
@@ -0,0 +1,20 @@
+#
+# init.rc fragment for hostapd on Android
+# Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+
+on post-fs-data
+    mkdir /data/misc/wifi/hostapd 0770 wifi wifi
+
+service hostapd /vendor/bin/hostapd \
+        -e /data/misc/wifi/entropy.bin \
+        /data/misc/wifi/hostapd.conf
+    class main
+    user wifi
+    group wifi net_raw net_admin
+    writepid /data/misc/wifi/hostapd.pid
+    disabled
+    oneshot
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index d943a43..c4eebff 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -3,6 +3,8 @@
 
 # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
 # management frames with the Host AP driver); wlan0 with many nl80211 drivers
+# Note: This attribute can be overridden by the values supplied with the '-i'
+# command line parameter.
 interface=wlan0
 
 # In case of atheros and nl80211 driver interfaces, an additional
@@ -225,6 +227,19 @@
 #basic_rates=10 20 55 110
 #basic_rates=60 120 240
 
+# Beacon frame TX rate configuration
+# This sets the TX rate that is used to transmit Beacon frames. If this item is
+# not included, the driver default rate (likely lowest rate) is used.
+# Legacy (CCK/OFDM rates):
+#    beacon_rate=<legacy rate in 100 kbps>
+# HT:
+#    beacon_rate=ht:<HT MCS>
+# VHT:
+#    beacon_rate=vht:<VHT MCS>
+#
+# For example, beacon_rate=10 for 1 Mbps or beacon_rate=60 for 6 Mbps (OFDM).
+#beacon_rate=10
+
 # Short Preamble
 # This parameter can be used to enable optional use of short preamble for
 # frames sent at 2 Mbps, 5.5 Mbps, and 11 Mbps to improve network performance.
@@ -283,9 +298,16 @@
 # one or more elements)
 #vendor_elements=dd0411223301
 
+# Additional vendor specific elements for (Re)Association Response frames
+# This parameter can be used to add additional vendor specific element(s) into
+# the end of the (Re)Association Response frames. The format for these
+# element(s) is a hexdump of the raw information elements (id+len+payload for
+# one or more elements)
+#assocresp_elements=dd0411223301
+
 # TX queue parameters (EDCF / bursting)
 # tx_queue_<queue name>_<param>
-# queues: data0, data1, data2, data3, after_beacon, beacon
+# queues: data0, data1, data2, data3
 #		(data0 is the highest priority queue)
 # parameters:
 #   aifs: AIFS (default 2)
@@ -473,6 +495,22 @@
 # <station count>:<channel utilization>:<available admission capacity>
 #bss_load_test=12:80:20000
 
+# Multicast to unicast conversion
+# Request that the AP will do multicast-to-unicast conversion for ARP, IPv4, and
+# IPv6 frames (possibly within 802.1Q). If enabled, such frames are to be sent
+# to each station separately, with the DA replaced by their own MAC address
+# rather than the group address.
+#
+# Note that this may break certain expectations of the receiver, such as the
+# ability to drop unicast IP packets received within multicast L2 frames, or the
+# ability to not send ICMP destination unreachable messages for packets received
+# in L2 multicast (which is required, but the receiver can't tell the difference
+# if this new option is enabled).
+#
+# This also doesn't implement the 802.11 DMS (directed multicast service).
+#
+#multicast_to_unicast=0
+
 ##### IEEE 802.11n related configuration ######################################
 
 # ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -676,6 +714,54 @@
 #
 #vht_oper_centr_freq_seg1_idx=159
 
+# Workaround to use station's nsts capability in (Re)Association Response frame
+# This may be needed with some deployed devices as an interoperability
+# workaround for beamforming if the AP's capability is greater than the
+# station's capability. This is disabled by default and can be enabled by
+# setting use_sta_nsts=1.
+#use_sta_nsts=0
+
+##### IEEE 802.11ax related configuration #####################################
+
+#ieee80211ax: Whether IEEE 802.11ax (HE) is enabled
+# 0 = disabled (default)
+# 1 = enabled
+#ieee80211ax=1
+
+#he_su_beamformer: HE single user beamformer support
+# 0 = not supported (default)
+# 1 = supported
+#he_su_beamformer=1
+
+#he_su_beamformee: HE single user beamformee support
+# 0 = not supported (default)
+# 1 = supported
+#he_su_beamformee=1
+
+#he_mu_beamformer: HE multiple user beamformer support
+# 0 = not supported (default)
+# 1 = supported
+#he_mu_beamformer=1
+
+# he_bss_color: BSS color
+# 0 = no BSS color (default)
+# unsigned integer = BSS color
+#he_bss_color=0
+
+#he_default_pe_duration: The duration of PE field in an HE PPDU in us
+# Possible values are 0 us (default), 4 us, 8 us, 12 us, and 16 us
+#he_default_pe_duration=0
+
+#he_twt_required: Whether TWT is required
+# 0 = not required (default)
+# 1 = required
+#he_twt_required=0
+
+#he_rts_threshold: Duration of STA transmission
+# 0 = not set (default)
+# unsigned integer = duration in units of 16 us
+#he_rts_threshold=0
+
 ##### IEEE 802.1X-2004 related configuration ##################################
 
 # Require IEEE 802.1X authorization
@@ -1079,6 +1165,9 @@
 #
 # DAS require Event-Timestamp
 #radius_das_require_event_timestamp=1
+#
+# DAS require Message-Authenticator
+#radius_das_require_message_authenticator=1
 
 ##### RADIUS authentication server configuration ##############################
 
@@ -1144,6 +1233,10 @@
 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The
 # entries are separated with a space. WPA-PSK-SHA256 and WPA-EAP-SHA256 can be
 # added to enable SHA256-based stronger algorithms.
+# FILS-SHA256 = Fast Initial Link Setup with SHA256
+# FILS-SHA384 = Fast Initial Link Setup with SHA384
+# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
+# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
 # (dot11RSNAConfigAuthenticationSuitesTable)
 #wpa_key_mgmt=WPA-PSK WPA-EAP
 
@@ -1169,6 +1262,15 @@
 # (dot11RSNAConfigGroupRekeyStrict)
 #wpa_strict_rekey=1
 
+# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is
+#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount)
+# This value should only be increased when stations are constantly
+# deauthenticated during GTK rekeying with the log message
+# "group key handshake failed...".
+# You should consider to also increase wpa_pairwise_update_count then.
+# Range 1..4294967295; default: 4
+#wpa_group_update_count=4
+
 # Time interval for rekeying GMK (master key used internally to generate GTKs
 # (in seconds).
 #wpa_gmk_rekey=86400
@@ -1177,6 +1279,12 @@
 # PTK to mitigate some attacks against TKIP deficiencies.
 #wpa_ptk_rekey=600
 
+# The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way
+# Handshake are retried per 4-Way Handshake attempt.
+# (dot11RSNAConfigPairwiseUpdateCount)
+# Range 1..4294967295; default: 4
+#wpa_pairwise_update_count=4
+
 # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
 # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
 # authentication and key handshake before actually associating with a new AP.
@@ -1254,6 +1362,44 @@
 # http://www.iana.org/assignments/ipsec-registry/ipsec-registry.xml#ipsec-registry-9
 #sae_groups=19 20 21 25 26
 
+# FILS Cache Identifier (16-bit value in hexdump format)
+#fils_cache_id=0011
+
+# FILS Realm Information
+# One or more FILS realms need to be configured when FILS is enabled. This list
+# of realms is used to define which realms (used in keyName-NAI by the client)
+# can be used with FILS shared key authentication for ERP.
+#fils_realm=example.com
+#fils_realm=example.org
+
+# DHCP server for FILS HLP
+# If configured, hostapd will act as a DHCP relay for all FILS HLP requests
+# that include a DHCPDISCOVER message and send them to the specific DHCP
+# server for processing. hostapd will then wait for a response from that server
+# before replying with (Re)Association Response frame that encapsulates this
+# DHCP response. own_ip_addr is used as the local address for the communication
+# with the DHCP server.
+#dhcp_server=127.0.0.1
+
+# DHCP server UDP port
+# Default: 67
+#dhcp_server_port=67
+
+# DHCP relay UDP port on the local device
+# Default: 67; 0 means not to bind any specific port
+#dhcp_relay_port=67
+
+# DHCP rapid commit proxy
+# If set to 1, this enables hostapd to act as a DHCP rapid commit proxy to
+# allow the rapid commit options (two message DHCP exchange) to be used with a
+# server that supports only the four message DHCP exchange. This is disabled by
+# default (= 0) and can be enabled by setting this to 1.
+#dhcp_rapid_commit_proxy=0
+
+# Wait time for FILS HLP (dot11HLPWaitTime) in TUs
+# default: 30 TUs (= 30.72 milliseconds)
+#fils_hlp_wait_time=30
+
 ##### IEEE 802.11r configuration ##############################################
 
 # Mobility Domain identifier (dot11FTMobilityDomainID, MDID)
@@ -1307,6 +1453,14 @@
 # 1 = FT-over-DS enabled (default)
 #ft_over_ds=1
 
+# Whether to generate FT response locally for PSK networks
+# This avoids use of PMK-R1 push/pull from other APs with FT-PSK networks as
+# the required information (PSK and other session data) is already locally
+# available.
+# 0 = disabled (default)
+# 1 = enabled
+#ft_psk_generate_local=0
+
 ##### Neighbor table ##########################################################
 # Maximum number of entries kept in AP table (either for neigbor table or for
 # detecting Overlapping Legacy BSS Condition). The oldest entry will be
@@ -1740,6 +1894,13 @@
 # For example, AP Civic Location ANQP-element with unknown location:
 #anqp_elem=266:000000
 
+# GAS Address 3 behavior
+# 0 = P2P specification (Address3 = AP BSSID) workaround enabled by default
+#     based on GAS request Address3
+# 1 = IEEE 802.11 standard compliant regardless of GAS request Address3
+# 2 = Force non-compliant behavior (Address3 = AP BSSID for all cases)
+#gas_address3=0
+
 # QoS Map Set configuration
 #
 # Comma delimited QoS Map Set in decimal values
@@ -1879,6 +2040,36 @@
 # Transitioning between states).
 #fst_llt=100
 
+##### Radio measurements / location ###########################################
+
+# The content of a LCI measurement subelement
+#lci=<Hexdump of binary data of the LCI report>
+
+# The content of a location civic measurement subelement
+#civic=<Hexdump of binary data of the location civic report>
+
+# Enable neighbor report via radio measurements
+#rrm_neighbor_report=1
+
+# Enable beacon report via radio measurements
+#rrm_beacon_report=1
+
+# Publish fine timing measurement (FTM) responder functionality
+# This parameter only controls publishing via Extended Capabilities element.
+# Actual functionality is managed outside hostapd.
+#ftm_responder=0
+
+# Publish fine timing measurement (FTM) initiator functionality
+# This parameter only controls publishing via Extended Capabilities element.
+# Actual functionality is managed outside hostapd.
+#ftm_initiator=0
+#
+# Stationary AP config indicates that the AP doesn't move hence location data
+# can be considered as always up to date. If configured, LCI data will be sent
+# as a radio measurement even if the request doesn't contain a max age element
+# that allows sending of such data. Default: 0.
+#stationary_ap=0
+
 ##### TESTING OPTIONS #########################################################
 #
 # The options in this section are only available when the build configuration
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index bf86d37..f8d1eda 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -1,6 +1,6 @@
 /*
  * hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,80 +15,13 @@
 #include "utils/eloop.h"
 #include "utils/edit.h"
 #include "common/version.h"
+#include "common/cli.h"
 
 #ifndef CONFIG_NO_CTRL_IFACE
 
 static const char *const hostapd_cli_version =
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors";
-
-
-static const char *const hostapd_cli_license =
-"This software may be distributed under the terms of the BSD license.\n"
-"See README for more details.\n";
-
-static const char *const hostapd_cli_full_license =
-"This software may be distributed under the terms of the BSD license.\n"
-"\n"
-"Redistribution and use in source and binary forms, with or without\n"
-"modification, are permitted provided that the following conditions are\n"
-"met:\n"
-"\n"
-"1. Redistributions of source code must retain the above copyright\n"
-"   notice, this list of conditions and the following disclaimer.\n"
-"\n"
-"2. Redistributions in binary form must reproduce the above copyright\n"
-"   notice, this list of conditions and the following disclaimer in the\n"
-"   documentation and/or other materials provided with the distribution.\n"
-"\n"
-"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
-"   names of its contributors may be used to endorse or promote products\n"
-"   derived from this software without specific prior written permission.\n"
-"\n"
-"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
-"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
-"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
-"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
-"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
-"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
-"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
-"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
-"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
-"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
-"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
-"\n";
-
-static const char *const commands_help =
-"Commands:\n"
-"   mib                  get MIB variables (dot1x, dot11, radius)\n"
-"   sta <addr>           get MIB variables for one station\n"
-"   all_sta              get MIB variables for all stations\n"
-"   new_sta <addr>       add a new station\n"
-"   deauthenticate <addr>  deauthenticate a station\n"
-"   disassociate <addr>  disassociate a station\n"
-#ifdef CONFIG_IEEE80211W
-"   sa_query <addr>      send SA Query to a station\n"
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WPS
-"   wps_pin <uuid> <pin> [timeout] [addr]  add WPS Enrollee PIN\n"
-"   wps_check_pin <PIN>  verify PIN checksum\n"
-"   wps_pbc              indicate button pushed to initiate PBC\n"
-"   wps_cancel           cancel the pending WPS operation\n"
-#ifdef CONFIG_WPS_NFC
-"   wps_nfc_tag_read <hexdump>  report read NFC tag with WPS data\n"
-"   wps_nfc_config_token <WPS/NDEF>  build NFC configuration token\n"
-"   wps_nfc_token <WPS/NDEF/enable/disable>  manager NFC password token\n"
-#endif /* CONFIG_WPS_NFC */
-"   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
-"   wps_config <SSID> <auth> <encr> <key>  configure AP\n"
-"   wps_get_status       show current WPS status\n"
-#endif /* CONFIG_WPS */
-"   get_config           show current configuration\n"
-"   help                 show this usage help\n"
-"   interface [ifname]   show interfaces/select interface\n"
-"   level <debug level>  change debug level\n"
-"   license              show full hostapd_cli license\n"
-"   quit                 exit hostapd_cli\n";
+"Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> and contributors";
 
 static struct wpa_ctrl *ctrl_conn;
 static int hostapd_cli_quit = 0;
@@ -105,6 +38,15 @@
 static const char *action_file = NULL;
 static int ping_interval = 5;
 static int interactive = 0;
+static int event_handler_registered = 0;
+
+static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
+
+static void print_help(FILE *stream, const char *cmd);
+static char ** list_cmd_list(void);
+static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx);
+static void update_stations(struct wpa_ctrl *ctrl);
+static void cli_event(const char *str);
 
 
 static void usage(void)
@@ -129,9 +71,32 @@
 		"   -B           run a daemon in the background\n"
 		"   -i<ifname>   Interface to listen on (default: first "
 		"interface found in the\n"
-		"                socket path)\n\n"
-		"%s",
-		commands_help);
+		"                socket path)\n\n");
+	print_help(stderr, NULL);
+}
+
+
+static void register_event_handler(struct wpa_ctrl *ctrl)
+{
+	if (!ctrl_conn)
+		return;
+	if (interactive) {
+		event_handler_registered =
+			!eloop_register_read_sock(wpa_ctrl_get_fd(ctrl),
+						  hostapd_cli_receive,
+						  NULL, NULL);
+	}
+}
+
+
+static void unregister_event_handler(struct wpa_ctrl *ctrl)
+{
+	if (!ctrl_conn)
+		return;
+	if (interactive && event_handler_registered) {
+		eloop_unregister_read_sock(wpa_ctrl_get_fd(ctrl));
+		event_handler_registered = 0;
+	}
 }
 
 
@@ -174,6 +139,7 @@
 	if (ctrl_conn == NULL)
 		return;
 
+	unregister_event_handler(ctrl_conn);
 	if (hostapd_cli_attached) {
 		wpa_ctrl_detach(ctrl_conn);
 		hostapd_cli_attached = 0;
@@ -183,13 +149,45 @@
 }
 
 
+static int hostapd_cli_reconnect(const char *ifname)
+{
+	char *next_ctrl_ifname;
+
+	hostapd_cli_close_connection();
+
+	if (!ifname)
+		return -1;
+
+	next_ctrl_ifname = os_strdup(ifname);
+	os_free(ctrl_ifname);
+	ctrl_ifname = next_ctrl_ifname;
+	if (!ctrl_ifname)
+		return -1;
+
+	ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+	if (!ctrl_conn)
+		return -1;
+	if (!interactive && !action_file)
+		return 0;
+	if (wpa_ctrl_attach(ctrl_conn) == 0) {
+		hostapd_cli_attached = 1;
+		register_event_handler(ctrl_conn);
+		update_stations(ctrl_conn);
+	} else {
+		printf("Warning: Failed to attach to hostapd.\n");
+	}
+	return 0;
+}
+
+
 static void hostapd_cli_msg_cb(char *msg, size_t len)
 {
+	cli_event(msg);
 	printf("%s\n", msg);
 }
 
 
-static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
 {
 	char buf[4096];
 	size_t len;
@@ -217,42 +215,12 @@
 }
 
 
-static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
 {
 	return _wpa_ctrl_command(ctrl, cmd, 1);
 }
 
 
-static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
-		     char *argv[])
-{
-	int i, res;
-	char *pos, *end;
-
-	pos = buf;
-	end = buf + buflen;
-
-	res = os_snprintf(pos, end - pos, "%s", cmd);
-	if (os_snprintf_error(end - pos, res))
-		goto fail;
-	pos += res;
-
-	for (i = 0; i < argc; i++) {
-		res = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (os_snprintf_error(end - pos, res))
-			goto fail;
-		pos += res;
-	}
-
-	buf[buflen - 1] = '\0';
-	return 0;
-
-fail:
-	printf("Too long command\n");
-	return -1;
-}
-
-
 static int hostapd_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd,
 			   int min_args, int argc, char *argv[])
 {
@@ -352,6 +320,21 @@
 }
 
 
+static char ** hostapd_complete_stations(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
 static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
@@ -402,6 +385,22 @@
 }
 
 
+#ifdef CONFIG_TAXONOMY
+static int hostapd_cli_cmd_signature(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	char buf[64];
+
+	if (argc != 1) {
+		printf("Invalid 'signature' command - exactly one argument, STA address, is required.\n");
+		return -1;
+	}
+	os_snprintf(buf, sizeof(buf), "SIGNATURE %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+#endif /* CONFIG_TAXONOMY */
+
+
 #ifdef CONFIG_IEEE80211W
 static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
@@ -721,8 +720,8 @@
 }
 
 
-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
-				char *addr, size_t addr_len)
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+				char *addr, size_t addr_len, int print)
 {
 	char buf[4096], *pos;
 	size_t len;
@@ -746,7 +745,8 @@
 	buf[len] = '\0';
 	if (memcmp(buf, "FAIL", 4) == 0)
 		return -1;
-	printf("%s", buf);
+	if (print)
+		printf("%s", buf);
 
 	pos = buf;
 	while (*pos != '\0' && *pos != '\n')
@@ -762,27 +762,59 @@
 {
 	char addr[32], cmd[64];
 
-	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
 		return 0;
 	do {
 		snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
-	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
 
 	return -1;
 }
 
 
+static int hostapd_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	char addr[32], cmd[64];
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return 0;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			printf("%s\n", addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+
+	return 0;
+}
+
+
 static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	printf("%s", commands_help);
+	print_help(stdout, argc > 0 ? argv[0] : NULL);
 	return 0;
 }
 
 
+static char ** hostapd_cli_complete_help(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = list_cmd_list();
+		break;
+	}
+
+	return res;
+}
+
+
 static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
-	printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license);
+	printf("%s\n\n%s\n", hostapd_cli_version, cli_full_license);
 	return 0;
 }
 
@@ -893,6 +925,47 @@
 }
 
 
+static void update_stations(struct wpa_ctrl *ctrl)
+{
+	char addr[32], cmd[64];
+
+	if (!ctrl || !interactive)
+		return;
+
+	cli_txt_list_flush(&stations);
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			cli_txt_list_add(&stations, addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+}
+
+
+static void hostapd_cli_get_interfaces(struct wpa_ctrl *ctrl,
+				       struct dl_list *interfaces)
+{
+	struct dirent *dent;
+	DIR *dir;
+
+	if (!ctrl || !interfaces)
+		return;
+	dir = opendir(ctrl_iface_dir);
+	if (dir == NULL)
+		return;
+
+	while ((dent = readdir(dir))) {
+		if (strcmp(dent->d_name, ".") == 0 ||
+		    strcmp(dent->d_name, "..") == 0)
+			continue;
+		cli_txt_list_add(interfaces, dent->d_name);
+	}
+	closedir(dir);
+}
+
+
 static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl)
 {
 	struct dirent *dent;
@@ -923,22 +996,7 @@
 		hostapd_cli_list_interfaces(ctrl);
 		return 0;
 	}
-
-	hostapd_cli_close_connection();
-	os_free(ctrl_ifname);
-	ctrl_ifname = os_strdup(argv[0]);
-	if (ctrl_ifname == NULL)
-		return -1;
-
-	if (hostapd_cli_open_connection(ctrl_ifname)) {
-		printf("Connected to interface '%s.\n", ctrl_ifname);
-		if (wpa_ctrl_attach(ctrl_conn) == 0) {
-			hostapd_cli_attached = 1;
-		} else {
-			printf("Warning: Failed to attach to "
-			       "hostapd.\n");
-		}
-	} else {
+	if (hostapd_cli_reconnect(argv[0]) != 0) {
 		printf("Could not connect to interface '%s' - re-trying\n",
 			ctrl_ifname);
 	}
@@ -946,6 +1004,24 @@
 }
 
 
+static char ** hostapd_complete_interface(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+	DEFINE_DL_LIST(interfaces);
+
+	switch (arg) {
+	case 1:
+		hostapd_cli_get_interfaces(ctrl_conn, &interfaces);
+		res = cli_txt_list_array(&interfaces);
+		cli_txt_list_flush(&interfaces);
+		break;
+	}
+
+	return res;
+}
+
+
 static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -966,6 +1042,44 @@
 }
 
 
+static char ** hostapd_complete_set(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+#ifdef CONFIG_WPS_TESTING
+		"wps_version_number", "wps_testing_dummy_cred",
+		"wps_corrupt_pkhash",
+#endif /* CONFIG_WPS_TESTING */
+#ifdef CONFIG_INTERWORKING
+		"gas_frag_limit",
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_TESTING_OPTIONS
+		"ext_mgmt_frame_handling", "ext_eapol_frame_io",
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_MBO
+		"mbo_assoc_disallow",
+#endif /* CONFIG_MBO */
+		"deny_mac_file", "accept_mac_file",
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+
+	if (arg == 1) {
+		char **res;
+
+		res = os_calloc(num_fields + 1, sizeof(char *));
+		if (!res)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (!res[i])
+				return res;
+		}
+		return res;
+	}
+	return NULL;
+}
+
+
 static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	char cmd[256];
@@ -986,6 +1100,31 @@
 }
 
 
+static char ** hostapd_complete_get(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+		"version", "tls_library",
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+
+	if (arg == 1) {
+		char **res;
+
+		res = os_calloc(num_fields + 1, sizeof(char *));
+		if (!res)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (!res[i])
+				return res;
+		}
+		return res;
+	}
+	return NULL;
+}
+
+
 #ifdef CONFIG_FST
 static int hostapd_cli_cmd_fst(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -1143,71 +1282,260 @@
 }
 
 
+static int hostapd_cli_cmd_set_neighbor(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	char cmd[2048];
+	int res;
+
+	if (argc < 3 || argc > 6) {
+		printf("Invalid set_neighbor command: needs 3-6 arguments\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "SET_NEIGHBOR %s %s %s %s %s %s",
+			  argv[0], argv[1], argv[2], argc >= 4 ? argv[3] : "",
+			  argc >= 5 ? argv[4] : "", argc == 6 ? argv[5] : "");
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long SET_NEIGHBOR command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_remove_neighbor(struct wpa_ctrl *ctrl, int argc,
+					   char *argv[])
+{
+	char cmd[400];
+	int res;
+
+	if (argc != 2) {
+		printf("Invalid remove_neighbor command: needs 2 arguments\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NEIGHBOR %s %s",
+			  argv[0], argv[1]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long REMOVE_NEIGHBOR command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_req_lci(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	char cmd[256];
+	int res;
+
+	if (argc != 1) {
+		printf("Invalid req_lci command - requires destination address\n");
+		return -1;
+	}
+
+	res = os_snprintf(cmd, sizeof(cmd), "REQ_LCI %s", argv[0]);
+	if (os_snprintf_error(sizeof(cmd), res)) {
+		printf("Too long REQ_LCI command.\n");
+		return -1;
+	}
+	return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_req_range(struct wpa_ctrl *ctrl, int argc,
+				     char *argv[])
+{
+	if (argc < 4) {
+		printf("Invalid req_range command: needs at least 4 arguments - dest address, randomization interval, min AP count, and 1 to 16 AP addresses\n");
+		return -1;
+	}
+
+	return hostapd_cli_cmd(ctrl, "REQ_RANGE", 4, argc, argv);
+}
+
+
+static int hostapd_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DRIVER_FLAGS");
+}
+
+
 struct hostapd_cli_cmd {
 	const char *cmd;
 	int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+	char ** (*completion)(const char *str, int pos);
+	const char *usage;
 };
 
 static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
-	{ "ping", hostapd_cli_cmd_ping },
-	{ "mib", hostapd_cli_cmd_mib },
-	{ "relog", hostapd_cli_cmd_relog },
-	{ "status", hostapd_cli_cmd_status },
-	{ "sta", hostapd_cli_cmd_sta },
-	{ "all_sta", hostapd_cli_cmd_all_sta },
-	{ "new_sta", hostapd_cli_cmd_new_sta },
-	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
-	{ "disassociate", hostapd_cli_cmd_disassociate },
+	{ "ping", hostapd_cli_cmd_ping, NULL,
+	  "= pings hostapd" },
+	{ "mib", hostapd_cli_cmd_mib, NULL,
+	  "= get MIB variables (dot1x, dot11, radius)" },
+	{ "relog", hostapd_cli_cmd_relog, NULL,
+	  "= reload/truncate debug log output file" },
+	{ "status", hostapd_cli_cmd_status, NULL,
+	  "= show interface status info" },
+	{ "sta", hostapd_cli_cmd_sta, hostapd_complete_stations,
+	  "<addr> = get MIB variables for one station" },
+	{ "all_sta", hostapd_cli_cmd_all_sta, NULL,
+	   "= get MIB variables for all stations" },
+	{ "list_sta", hostapd_cli_cmd_list_sta, NULL,
+	   "= list all stations" },
+	{ "new_sta", hostapd_cli_cmd_new_sta, NULL,
+	  "<addr> = add a new station" },
+	{ "deauthenticate", hostapd_cli_cmd_deauthenticate,
+	  hostapd_complete_stations,
+	  "<addr> = deauthenticate a station" },
+	{ "disassociate", hostapd_cli_cmd_disassociate,
+	  hostapd_complete_stations,
+	  "<addr> = disassociate a station" },
+#ifdef CONFIG_TAXONOMY
+	{ "signature", hostapd_cli_cmd_signature, hostapd_complete_stations,
+	  "<addr> = get taxonomy signature for a station" },
+#endif /* CONFIG_TAXONOMY */
 #ifdef CONFIG_IEEE80211W
-	{ "sa_query", hostapd_cli_cmd_sa_query },
+	{ "sa_query", hostapd_cli_cmd_sa_query, hostapd_complete_stations,
+	  "<addr> = send SA Query to a station" },
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WPS
-	{ "wps_pin", hostapd_cli_cmd_wps_pin },
-	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
-	{ "wps_pbc", hostapd_cli_cmd_wps_pbc },
-	{ "wps_cancel", hostapd_cli_cmd_wps_cancel },
+	{ "wps_pin", hostapd_cli_cmd_wps_pin, NULL,
+	  "<uuid> <pin> [timeout] [addr] = add WPS Enrollee PIN" },
+	{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin, NULL,
+	  "<PIN> = verify PIN checksum" },
+	{ "wps_pbc", hostapd_cli_cmd_wps_pbc, NULL,
+	  "= indicate button pushed to initiate PBC" },
+	{ "wps_cancel", hostapd_cli_cmd_wps_cancel, NULL,
+	  "= cancel the pending WPS operation" },
 #ifdef CONFIG_WPS_NFC
-	{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read },
-	{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token },
-	{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token },
-	{ "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel },
+	{ "wps_nfc_tag_read", hostapd_cli_cmd_wps_nfc_tag_read, NULL,
+	  "<hexdump> = report read NFC tag with WPS data" },
+	{ "wps_nfc_config_token", hostapd_cli_cmd_wps_nfc_config_token, NULL,
+	  "<WPS/NDEF> = build NFC configuration token" },
+	{ "wps_nfc_token", hostapd_cli_cmd_wps_nfc_token, NULL,
+	  "<WPS/NDEF/enable/disable> = manager NFC password token" },
+	{ "nfc_get_handover_sel", hostapd_cli_cmd_nfc_get_handover_sel, NULL,
+	  NULL },
 #endif /* CONFIG_WPS_NFC */
-	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
-	{ "wps_config", hostapd_cli_cmd_wps_config },
-	{ "wps_get_status", hostapd_cli_cmd_wps_get_status },
+	{ "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin, NULL,
+	  "<cmd> [params..] = enable/disable AP PIN" },
+	{ "wps_config", hostapd_cli_cmd_wps_config, NULL,
+	  "<SSID> <auth> <encr> <key> = configure AP" },
+	{ "wps_get_status", hostapd_cli_cmd_wps_get_status, NULL,
+	  "= show current WPS status" },
 #endif /* CONFIG_WPS */
-	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
-	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
-	{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
-	{ "get_config", hostapd_cli_cmd_get_config },
-	{ "help", hostapd_cli_cmd_help },
-	{ "interface", hostapd_cli_cmd_interface },
+	{ "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent, NULL,
+	  "= send Disassociation Imminent notification" },
+	{ "ess_disassoc", hostapd_cli_cmd_ess_disassoc, NULL,
+	  "= send ESS Dissassociation Imminent notification" },
+	{ "bss_tm_req", hostapd_cli_cmd_bss_tm_req, NULL,
+	  "= send BSS Transition Management Request" },
+	{ "get_config", hostapd_cli_cmd_get_config, NULL,
+	  "= show current configuration" },
+	{ "help", hostapd_cli_cmd_help, hostapd_cli_complete_help,
+	  "= show this usage help" },
+	{ "interface", hostapd_cli_cmd_interface, hostapd_complete_interface,
+	  "[ifname] = show interfaces/select interface" },
 #ifdef CONFIG_FST
-	{ "fst", hostapd_cli_cmd_fst },
+	{ "fst", hostapd_cli_cmd_fst, NULL,
+	  "<params...> = send FST-MANAGER control interface command" },
 #endif /* CONFIG_FST */
-	{ "raw", hostapd_cli_cmd_raw },
-	{ "level", hostapd_cli_cmd_level },
-	{ "license", hostapd_cli_cmd_license },
-	{ "quit", hostapd_cli_cmd_quit },
-	{ "set", hostapd_cli_cmd_set },
-	{ "get", hostapd_cli_cmd_get },
-	{ "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
-	{ "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
-	{ "chan_switch", hostapd_cli_cmd_chan_switch },
-	{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
-	{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
-	{ "vendor", hostapd_cli_cmd_vendor },
-	{ "enable", hostapd_cli_cmd_enable },
-	{ "reload", hostapd_cli_cmd_reload },
-	{ "disable", hostapd_cli_cmd_disable },
-	{ "erp_flush", hostapd_cli_cmd_erp_flush },
-	{ "log_level", hostapd_cli_cmd_log_level },
-	{ "pmksa", hostapd_cli_cmd_pmksa },
-	{ "pmksa_flush", hostapd_cli_cmd_pmksa_flush },
-	{ NULL, NULL }
+	{ "raw", hostapd_cli_cmd_raw, NULL,
+	  "<params..> = send unprocessed command" },
+	{ "level", hostapd_cli_cmd_level, NULL,
+	  "<debug level> = change debug level" },
+	{ "license", hostapd_cli_cmd_license, NULL,
+	  "= show full hostapd_cli license" },
+	{ "quit", hostapd_cli_cmd_quit, NULL,
+	  "= exit hostapd_cli" },
+	{ "set", hostapd_cli_cmd_set, hostapd_complete_set,
+	  "<name> <value> = set runtime variables" },
+	{ "get", hostapd_cli_cmd_get, hostapd_complete_get,
+	  "<name> = get runtime info" },
+	{ "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set, NULL,
+	  "<arg,arg,...> = set QoS Map set element" },
+	{ "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf,
+	  hostapd_complete_stations,
+	  "<addr> = send QoS Map Configure frame" },
+	{ "chan_switch", hostapd_cli_cmd_chan_switch, NULL,
+	  "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]\n"
+	  "  [center_freq2=] [bandwidth=] [blocktx] [ht|vht]\n"
+	  "  = initiate channel switch announcement" },
+	{ "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif, NULL,
+	  "<addr> <url>\n"
+	  "  = send WNM-Notification Subscription Remediation Request" },
+	{ "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req, NULL,
+	  "<addr> <code (0/1)> <Re-auth-Delay(sec)> [url]\n"
+	  "  = send WNM-Notification imminent deauthentication indication" },
+	{ "vendor", hostapd_cli_cmd_vendor, NULL,
+	  "<vendor id> <sub command id> [<hex formatted data>]\n"
+	  "  = send vendor driver command" },
+	{ "enable", hostapd_cli_cmd_enable, NULL,
+	  "= enable hostapd on current interface" },
+	{ "reload", hostapd_cli_cmd_reload, NULL,
+	  "= reload configuration for current interface" },
+	{ "disable", hostapd_cli_cmd_disable, NULL,
+	  "= disable hostapd on current interface" },
+	{ "erp_flush", hostapd_cli_cmd_erp_flush, NULL,
+	  "= drop all ERP keys"},
+	{ "log_level", hostapd_cli_cmd_log_level, NULL,
+	  "[level] = show/change log verbosity level" },
+	{ "pmksa", hostapd_cli_cmd_pmksa, NULL,
+	  " = show PMKSA cache entries" },
+	{ "pmksa_flush", hostapd_cli_cmd_pmksa_flush, NULL,
+	  " = flush PMKSA cache" },
+	{ "set_neighbor", hostapd_cli_cmd_set_neighbor, NULL,
+	  "<addr> <ssid=> <nr=> [lci=] [civic=] [stat]\n"
+	  "  = add AP to neighbor database" },
+	{ "remove_neighbor", hostapd_cli_cmd_remove_neighbor, NULL,
+	  "<addr> <ssid=> = remove AP from neighbor database" },
+	{ "req_lci", hostapd_cli_cmd_req_lci, hostapd_complete_stations,
+	  "<addr> = send LCI request to a station"},
+	{ "req_range", hostapd_cli_cmd_req_range, NULL,
+	  " = send FTM range request"},
+	{ "driver_flags", hostapd_cli_cmd_driver_flags, NULL,
+	  " = show supported driver flags"},
+	{ NULL, NULL, NULL, NULL }
 };
 
 
+/*
+ * Prints command usage, lines are padded with the specified string.
+ */
+static void print_cmd_help(FILE *stream, const struct hostapd_cli_cmd *cmd,
+			   const char *pad)
+{
+	char c;
+	size_t n;
+
+	if (cmd->usage == NULL)
+		return;
+	fprintf(stream, "%s%s ", pad, cmd->cmd);
+	for (n = 0; (c = cmd->usage[n]); n++) {
+		fprintf(stream, "%c", c);
+		if (c == '\n')
+			fprintf(stream, "%s", pad);
+	}
+	fprintf(stream, "\n");
+}
+
+
+static void print_help(FILE *stream, const char *cmd)
+{
+	int n;
+
+	fprintf(stream, "commands:\n");
+	for (n = 0; hostapd_cli_commands[n].cmd; n++) {
+		if (cmd == NULL || str_starts(hostapd_cli_commands[n].cmd, cmd))
+			print_cmd_help(stream, &hostapd_cli_commands[n], "  ");
+	}
+}
+
+
 static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	const struct hostapd_cli_cmd *cmd, *match = NULL;
@@ -1247,6 +1575,34 @@
 }
 
 
+static void cli_event(const char *str)
+{
+	const char *start, *s;
+
+	start = os_strchr(str, '>');
+	if (start == NULL)
+		return;
+
+	start++;
+
+	if (str_starts(start, AP_STA_CONNECTED)) {
+		s = os_strchr(start, ' ');
+		if (s == NULL)
+			return;
+		cli_txt_list_add(&stations, s + 1);
+		return;
+	}
+
+	if (str_starts(start, AP_STA_DISCONNECTED)) {
+		s = os_strchr(start, ' ');
+		if (s == NULL)
+			return;
+		cli_txt_list_del_addr(&stations, s + 1);
+		return;
+	}
+}
+
+
 static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
 				     int action_monitor)
 {
@@ -1261,6 +1617,7 @@
 			if (action_monitor)
 				hostapd_cli_action_process(buf, len);
 			else {
+				cli_event(buf);
 				if (in_read && first)
 					printf("\n");
 				first = 0;
@@ -1274,35 +1631,9 @@
 }
 
 
-#define max_args 10
-
-static int tokenize_cmd(char *cmd, char *argv[])
+static void hostapd_cli_receive(int sock, void *eloop_ctx, void *sock_ctx)
 {
-	char *pos;
-	int argc = 0;
-
-	pos = cmd;
-	for (;;) {
-		while (*pos == ' ')
-			pos++;
-		if (*pos == '\0')
-			break;
-		argv[argc] = pos;
-		argc++;
-		if (argc == max_args)
-			break;
-		if (*pos == '"') {
-			char *pos2 = os_strrchr(pos, '"');
-			if (pos2)
-				pos = pos2 + 1;
-		}
-		while (*pos != '\0' && *pos != ' ')
-			pos++;
-		if (*pos == ' ')
-			*pos++ = '\0';
-	}
-
-	return argc;
+	hostapd_cli_recv_pending(ctrl_conn, 0, 0);
 }
 
 
@@ -1312,18 +1643,8 @@
 		printf("Connection to hostapd lost - trying to reconnect\n");
 		hostapd_cli_close_connection();
 	}
-	if (!ctrl_conn) {
-		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
-		if (ctrl_conn) {
-			printf("Connection to hostapd re-established\n");
-			if (wpa_ctrl_attach(ctrl_conn) == 0) {
-				hostapd_cli_attached = 1;
-			} else {
-				printf("Warning: Failed to attach to "
-				       "hostapd.\n");
-			}
-		}
-	}
+	if (!ctrl_conn && hostapd_cli_reconnect(ctrl_ifname) == 0)
+		printf("Connection to hostapd re-established\n");
 	if (ctrl_conn)
 		hostapd_cli_recv_pending(ctrl_conn, 1, 0);
 	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
@@ -1352,18 +1673,100 @@
 }
 
 
+static char ** list_cmd_list(void)
+{
+	char **res;
+	int i, count;
+
+	count = ARRAY_SIZE(hostapd_cli_commands);
+	res = os_calloc(count + 1, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+
+	for (i = 0; hostapd_cli_commands[i].cmd; i++) {
+		res[i] = os_strdup(hostapd_cli_commands[i].cmd);
+		if (res[i] == NULL)
+			break;
+	}
+
+	return res;
+}
+
+
+static char ** hostapd_cli_cmd_completion(const char *cmd, const char *str,
+				      int pos)
+{
+	int i;
+
+	for (i = 0; hostapd_cli_commands[i].cmd; i++) {
+		if (os_strcasecmp(hostapd_cli_commands[i].cmd, cmd) != 0)
+			continue;
+		if (hostapd_cli_commands[i].completion)
+			return hostapd_cli_commands[i].completion(str, pos);
+		if (!hostapd_cli_commands[i].usage)
+			return NULL;
+		edit_clear_line();
+		printf("\r%s\n", hostapd_cli_commands[i].usage);
+		edit_redraw();
+		break;
+	}
+
+	return NULL;
+}
+
+
+static char ** hostapd_cli_edit_completion_cb(void *ctx, const char *str,
+					      int pos)
+{
+	char **res;
+	const char *end;
+	char *cmd;
+
+	end = os_strchr(str, ' ');
+	if (end == NULL || str + pos < end)
+		return list_cmd_list();
+
+	cmd = os_malloc(pos + 1);
+	if (cmd == NULL)
+		return NULL;
+	os_memcpy(cmd, str, pos);
+	cmd[end - str] = '\0';
+	res = hostapd_cli_cmd_completion(cmd, str, pos);
+	os_free(cmd);
+	return res;
+}
+
+
 static void hostapd_cli_interactive(void)
 {
+	char *hfile = NULL;
+	char *home;
+
 	printf("\nInteractive mode\n\n");
 
+#ifdef CONFIG_HOSTAPD_CLI_HISTORY_DIR
+	home = CONFIG_HOSTAPD_CLI_HISTORY_DIR;
+#else /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
+	home = getenv("HOME");
+#endif /* CONFIG_HOSTAPD_CLI_HISTORY_DIR */
+	if (home) {
+		const char *fname = ".hostapd_cli_history";
+		int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+		hfile = os_malloc(hfile_len);
+		if (hfile)
+			os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
+	}
+
 	eloop_register_signal_terminate(hostapd_cli_eloop_terminate, NULL);
 	edit_init(hostapd_cli_edit_cmd_cb, hostapd_cli_edit_eof_cb,
-		  NULL, NULL, NULL, NULL);
+		  hostapd_cli_edit_completion_cb, NULL, hfile, NULL);
 	eloop_register_timeout(ping_interval, 0, hostapd_cli_ping, NULL, NULL);
 
 	eloop_run();
 
-	edit_deinit(NULL, NULL);
+	cli_txt_list_flush(&stations);
+	edit_deinit(hfile, NULL);
+	os_free(hfile);
 	eloop_cancel_timeout(hostapd_cli_ping, NULL, NULL);
 }
 
@@ -1466,8 +1869,7 @@
 	interactive = (argc == optind) && (action_file == NULL);
 
 	if (interactive) {
-		printf("%s\n\n%s\n\n", hostapd_cli_version,
-		       hostapd_cli_license);
+		printf("%s\n\n%s\n\n", hostapd_cli_version, cli_license);
 	}
 
 	if (eloop_init())
@@ -1491,7 +1893,7 @@
 				closedir(dir);
 			}
 		}
-		ctrl_conn = hostapd_cli_open_connection(ctrl_ifname);
+		hostapd_cli_reconnect(ctrl_ifname);
 		if (ctrl_conn) {
 			if (warning_displayed)
 				printf("Connection established.\n");
@@ -1512,16 +1914,8 @@
 		continue;
 	}
 
-	if (interactive || action_file) {
-		if (wpa_ctrl_attach(ctrl_conn) == 0) {
-			hostapd_cli_attached = 1;
-		} else {
-			printf("Warning: Failed to attach to hostapd.\n");
-			if (action_file)
-				return -1;
-		}
-	}
-
+	if (action_file && !hostapd_cli_attached)
+		return -1;
 	if (daemonize && os_daemonize(pid_file) && eloop_sock_requeue())
 		return -1;
 
@@ -1532,6 +1926,7 @@
 	else
 		wpa_request(ctrl_conn, argc - optind, &argv[optind]);
 
+	unregister_event_handler(ctrl_conn);
 	os_free(ctrl_ifname);
 	eloop_destroy();
 	hostapd_cli_cleanup();
diff --git a/hostapd/main.c b/hostapd/main.c
index 1d9e63e..593267c 100644
--- a/hostapd/main.c
+++ b/hostapd/main.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / main()
- * Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -108,6 +108,10 @@
 			    module_str ? module_str : "",
 			    module_str ? ": " : "", txt);
 
+#ifdef CONFIG_DEBUG_SYSLOG
+	if (wpa_debug_syslog)
+		conf_stdout = 0;
+#endif /* CONFIG_DEBUG_SYSLOG */
 	if ((conf_stdout & module) && level >= conf_stdout_level) {
 		wpa_debug_print_timestamp();
 		wpa_printf(MSG_INFO, "%s", format);
@@ -217,11 +221,20 @@
 		iface->drv_flags = capa.flags;
 		iface->smps_modes = capa.smps_modes;
 		iface->probe_resp_offloads = capa.probe_resp_offloads;
+		/*
+		 * Use default extended capa values from per-radio information
+		 */
 		iface->extended_capa = capa.extended_capa;
 		iface->extended_capa_mask = capa.extended_capa_mask;
 		iface->extended_capa_len = capa.extended_capa_len;
 		iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
 
+		/*
+		 * Override extended capa with per-interface type (AP), if
+		 * available from the driver.
+		 */
+		hostapd_get_ext_capa(iface);
+
 		triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
 		if (triggs && hapd->driver->set_wowlan) {
 			if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
@@ -242,7 +255,7 @@
  * interfaces. No actiual driver operations are started.
  */
 static struct hostapd_iface *
-hostapd_interface_init(struct hapd_interfaces *interfaces,
+hostapd_interface_init(struct hapd_interfaces *interfaces, const char *if_name,
 		       const char *config_fname, int debug)
 {
 	struct hostapd_iface *iface;
@@ -252,6 +265,12 @@
 	iface = hostapd_init(interfaces, config_fname);
 	if (!iface)
 		return NULL;
+
+	if (if_name) {
+		os_strlcpy(iface->conf->bss[0]->iface, if_name,
+			   sizeof(iface->conf->bss[0]->iface));
+	}
+
 	iface->interfaces = interfaces;
 
 	for (k = 0; k < debug; k++) {
@@ -261,7 +280,8 @@
 
 	if (iface->conf->bss[0]->iface[0] == '\0' &&
 	    !hostapd_drv_none(iface->bss[0])) {
-		wpa_printf(MSG_ERROR, "Interface name not specified in %s",
+		wpa_printf(MSG_ERROR,
+			   "Interface name not specified in %s, nor by '-i' parameter",
 			   config_fname);
 		hostapd_interface_deinit_free(iface);
 		return NULL;
@@ -330,6 +350,7 @@
 		wpa_printf(MSG_ERROR, "Failed to initialize event loop");
 		return -1;
 	}
+	interfaces->eloop_initialized = 1;
 
 	random_init(entropy_file);
 
@@ -357,7 +378,7 @@
 }
 
 
-static void hostapd_global_deinit(const char *pid_file)
+static void hostapd_global_deinit(const char *pid_file, int eloop_initialized)
 {
 	int i;
 
@@ -375,7 +396,8 @@
 
 	random_deinit();
 
-	eloop_destroy();
+	if (eloop_initialized)
+		eloop_destroy();
 
 #ifndef CONFIG_NATIVE_WINDOWS
 	closelog();
@@ -433,7 +455,7 @@
 		"hostapd v" VERSION_STR "\n"
 		"User space daemon for IEEE 802.11 AP management,\n"
 		"IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
-		"Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi> "
+		"Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi> "
 		"and contributors\n");
 }
 
@@ -445,7 +467,8 @@
 		"\n"
 		"usage: hostapd [-hdBKtv] [-P <PID file>] [-e <entropy file>] "
 		"\\\n"
-		"         [-g <global ctrl_iface>] [-G <group>] \\\n"
+		"         [-g <global ctrl_iface>] [-G <group>]\\\n"
+		"         [-i <comma-separated list of interface names>]\\\n"
 		"         <configuration file(s)>\n"
 		"\n"
 		"options:\n"
@@ -461,9 +484,13 @@
 		"   -f   log output to debug file instead of stdout\n"
 #endif /* CONFIG_DEBUG_FILE */
 #ifdef CONFIG_DEBUG_LINUX_TRACING
-		"   -T = record to Linux tracing in addition to logging\n"
+		"   -T   record to Linux tracing in addition to logging\n"
 		"        (records all messages regardless of debug verbosity)\n"
 #endif /* CONFIG_DEBUG_LINUX_TRACING */
+		"   -i   list of interface names to use\n"
+#ifdef CONFIG_DEBUG_SYSLOG
+		"   -s   log output to syslog instead of stdout\n"
+#endif /* CONFIG_DEBUG_SYSLOG */
 		"   -S   start all the interfaces synchronously\n"
 		"   -t   include timestamps in some debug messages\n"
 		"   -v   show hostapd version\n");
@@ -527,6 +554,43 @@
 }
 
 
+static int hostapd_get_interface_names(char ***if_names,
+				       size_t *if_names_size,
+				       char *arg)
+{
+	char *if_name, *tmp, **nnames;
+	size_t i;
+
+	if (!arg)
+		return -1;
+	if_name = strtok_r(arg, ",", &tmp);
+
+	while (if_name) {
+		nnames = os_realloc_array(*if_names, 1 + *if_names_size,
+					  sizeof(char *));
+		if (!nnames)
+			goto fail;
+		*if_names = nnames;
+
+		(*if_names)[*if_names_size] = os_strdup(if_name);
+		if (!(*if_names)[*if_names_size])
+			goto fail;
+		(*if_names_size)++;
+		if_name = strtok_r(NULL, ",", &tmp);
+	}
+
+	return 0;
+
+fail:
+	for (i = 0; i < *if_names_size; i++)
+		os_free((*if_names)[i]);
+	os_free(*if_names);
+	*if_names = NULL;
+	*if_names_size = 0;
+	return -1;
+}
+
+
 #ifdef CONFIG_WPS
 static int gen_uuid(const char *txt_addr)
 {
@@ -585,6 +649,8 @@
 	int enable_trace_dbg = 0;
 #endif /* CONFIG_DEBUG_LINUX_TRACING */
 	int start_ifaces_in_sync = 0;
+	char **if_names = NULL;
+	size_t if_names_size = 0;
 
 	if (os_program_init())
 		return -1;
@@ -602,7 +668,7 @@
 	dl_list_init(&interfaces.global_ctrl_dst);
 
 	for (;;) {
-		c = getopt(argc, argv, "b:Bde:f:hKP:STtu:vg:G:");
+		c = getopt(argc, argv, "b:Bde:f:hi:KP:sSTtu:vg:G:");
 		if (c < 0)
 			break;
 		switch (c) {
@@ -659,6 +725,11 @@
 			bss_config = tmp_bss;
 			bss_config[num_bss_configs++] = optarg;
 			break;
+#ifdef CONFIG_DEBUG_SYSLOG
+		case 's':
+			wpa_debug_syslog = 1;
+			break;
+#endif /* CONFIG_DEBUG_SYSLOG */
 		case 'S':
 			start_ifaces_in_sync = 1;
 			break;
@@ -666,6 +737,11 @@
 		case 'u':
 			return gen_uuid(optarg);
 #endif /* CONFIG_WPS */
+		case 'i':
+			if (hostapd_get_interface_names(&if_names,
+							&if_names_size, optarg))
+				goto out;
+			break;
 		default:
 			usage();
 			break;
@@ -682,6 +758,10 @@
 		wpa_debug_open_file(log_file);
 	else
 		wpa_debug_setup_stdout();
+#ifdef CONFIG_DEBUG_SYSLOG
+	if (wpa_debug_syslog)
+		wpa_debug_open_syslog();
+#endif /* CONFIG_DEBUG_SYSLOG */
 #ifdef CONFIG_DEBUG_LINUX_TRACING
 	if (enable_trace_dbg) {
 		int tret = wpa_debug_open_linux_tracing();
@@ -723,7 +803,13 @@
 
 	/* Allocate and parse configuration for full interface files */
 	for (i = 0; i < interfaces.count; i++) {
+		char *if_name = NULL;
+
+		if (i < if_names_size)
+			if_name = if_names[i];
+
 		interfaces.iface[i] = hostapd_interface_init(&interfaces,
+							     if_name,
 							     argv[optind + i],
 							     debug);
 		if (!interfaces.iface[i]) {
@@ -807,16 +893,22 @@
 	}
 	os_free(interfaces.iface);
 
-	eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
-	hostapd_global_deinit(pid_file);
+	if (interfaces.eloop_initialized)
+		eloop_cancel_timeout(hostapd_periodic, &interfaces, NULL);
+	hostapd_global_deinit(pid_file, interfaces.eloop_initialized);
 	os_free(pid_file);
 
+	wpa_debug_close_syslog();
 	if (log_file)
 		wpa_debug_close_file();
 	wpa_debug_close_linux_tracing();
 
 	os_free(bss_config);
 
+	for (i = 0; i < if_names_size; i++)
+		os_free(if_names[i]);
+	os_free(if_names);
+
 	fst_global_deinit();
 
 	os_program_deinit();
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
index c05c57d..d73feb1 100644
--- a/hs20/client/osu_client.c
+++ b/hs20/client/osu_client.c
@@ -2134,7 +2134,7 @@
 	char fname[255];
 	FILE *f;
 	struct osu_data *osu = NULL, *last = NULL;
-	size_t osu_count, i, j;
+	size_t osu_count = 0, i, j;
 	int ret;
 
 	write_summary(ctx, "OSU provider selection");
diff --git a/hs20/server/hs20-osu-server.txt b/hs20/server/hs20-osu-server.txt
index 001d6f2..20774a6 100644
--- a/hs20/server/hs20-osu-server.txt
+++ b/hs20/server/hs20-osu-server.txt
@@ -110,8 +110,8 @@
 ./clean.sh
 rm -fr rootCA"
 old_hostname=myserver.local
-./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" -d $old_hostname \
-   -I "Hotspot 2.0 Intermediate CA - CT" -o $old_hostname-osu-client \
+./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" \
+   -o $old_hostname-osu-client \
    -O $old_hostname-oscp -p lanforge -S $old_hostname \
    -V $old_hostname-osu-revoked \
    -m local -u http://$old_hostname:8888/
diff --git a/hs20/server/spp_server.c b/hs20/server/spp_server.c
index 33e3fa1..51c1d96 100644
--- a/hs20/server/spp_server.c
+++ b/hs20/server/spp_server.c
@@ -1823,10 +1823,8 @@
 	}
 
 	if (strcasecmp(req_reason, "User input completed") == 0) {
-		if (devinfo)
-			db_add_session_devinfo(ctx, session_id, devinfo);
-		if (devdetail)
-			db_add_session_devdetail(ctx, session_id, devdetail);
+		db_add_session_devinfo(ctx, session_id, devinfo);
+		db_add_session_devdetail(ctx, session_id, devdetail);
 		ret = hs20_user_input_complete(ctx, user, realm, dmacc,
 					       session_id);
 		hs20_eventlog_node(ctx, user, realm, session_id,
diff --git a/hs20/server/www/spp.php b/hs20/server/www/spp.php
index dde4434..002d028 100644
--- a/hs20/server/www/spp.php
+++ b/hs20/server/www/spp.php
@@ -96,7 +96,8 @@
   putenv("HS20USER");
 
 putenv("HS20REALM=$realm");
-putenv("HS20POST=$HTTP_RAW_POST_DATA");
+$postdata = file_get_contents("php://input");
+putenv("HS20POST=$postdata");
 $addr = $_SERVER["REMOTE_ADDR"];
 putenv("HS20ADDR=$addr");
 
diff --git a/src/ap/Makefile b/src/ap/Makefile
index 98788fe..1c65bd6 100644
--- a/src/ap/Makefile
+++ b/src/ap/Makefile
@@ -13,9 +13,11 @@
 CFLAGS += -DCONFIG_HS20
 CFLAGS += -DCONFIG_INTERWORKING
 CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R_AP
 CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_WPS
 CFLAGS += -DCONFIG_PROXYARP
+CFLAGS += -DCONFIG_IPV6
 CFLAGS += -DCONFIG_IAPP
 
 LIB_OBJS= \
@@ -43,14 +45,18 @@
 	ieee802_11_shared.o \
 	ieee802_11_vht.o \
 	ieee802_1x.o \
+	neighbor_db.o \
 	ndisc_snoop.o \
 	p2p_hostapd.o \
 	peerkey_auth.o \
 	pmksa_cache_auth.o \
 	preauth_auth.o \
+	rrm.o \
 	sta_info.o \
 	tkip_countermeasures.o \
 	utils.o \
+	vlan.o \
+	vlan_ifconfig.o \
 	vlan_init.o \
 	wmm.o \
 	wnm_ap.o \
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
index 854174e..0aacc3c 100644
--- a/src/ap/accounting.c
+++ b/src/ap/accounting.c
@@ -50,11 +50,6 @@
 		return NULL;
 	}
 
-	if (radius_msg_make_authenticator(msg) < 0) {
-		wpa_printf(MSG_INFO, "Could not make Request Authenticator");
-		goto fail;
-	}
-
 	if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
 				       status_type)) {
 		wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 66b843c..9abcab7 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -13,6 +13,7 @@
 #include "radius/radius_client.h"
 #include "common/ieee802_11_defs.h"
 #include "common/eapol_common.h"
+#include "common/dhcp.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_server/eap.h"
 #include "wpa_auth.h"
@@ -55,6 +56,8 @@
 
 	bss->wpa_group_rekey = 600;
 	bss->wpa_gmk_rekey = 86400;
+	bss->wpa_group_update_count = 4;
+	bss->wpa_pairwise_update_count = 4;
 	bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
 	bss->wpa_pairwise = WPA_CIPHER_TKIP;
 	bss->wpa_group = WPA_CIPHER_TKIP;
@@ -88,13 +91,22 @@
 	/* Set to -1 as defaults depends on HT in setup */
 	bss->wmm_enabled = -1;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	bss->ft_over_ds = 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	bss->radius_das_time_window = 300;
 
 	bss->sae_anti_clogging_threshold = 5;
+
+	bss->gas_frag_limit = 1400;
+
+#ifdef CONFIG_FILS
+	dl_list_init(&bss->fils_realms);
+	bss->fils_hlp_wait_time = 30;
+	bss->dhcp_server_port = DHCP_SERVER_PORT;
+	bss->dhcp_relay_port = DHCP_SERVER_PORT;
+#endif /* CONFIG_FILS */
 }
 
 
@@ -329,13 +341,7 @@
 		ssid->wpa_psk->group = 1;
 	}
 
-	if (ssid->wpa_psk_file) {
-		if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file,
-						&conf->ssid))
-			return -1;
-	}
-
-	return 0;
+	return hostapd_config_read_wpa_psk(ssid->wpa_psk_file, &conf->ssid);
 }
 
 
@@ -384,6 +390,18 @@
 }
 
 
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user)
+{
+	struct hostapd_eap_user *prev_user;
+
+	while (user) {
+		prev_user = user;
+		user = user->next;
+		hostapd_config_free_eap_user(prev_user);
+	}
+}
+
+
 static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
 {
 	int i;
@@ -420,10 +438,22 @@
 }
 
 
+static void hostapd_config_free_fils_realms(struct hostapd_bss_config *conf)
+{
+#ifdef CONFIG_FILS
+	struct fils_realm *realm;
+
+	while ((realm = dl_list_first(&conf->fils_realms, struct fils_realm,
+				      list))) {
+		dl_list_del(&realm->list);
+		os_free(realm);
+	}
+#endif /* CONFIG_FILS */
+}
+
+
 void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
-	struct hostapd_eap_user *user, *prev_user;
-
 	if (conf == NULL)
 		return;
 
@@ -436,12 +466,7 @@
 	os_free(conf->ssid.vlan_tagged_interface);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
-	user = conf->eap_user;
-	while (user) {
-		prev_user = user;
-		user = user->next;
-		hostapd_config_free_eap_user(prev_user);
-	}
+	hostapd_config_free_eap_users(conf->eap_user);
 	os_free(conf->eap_user_sqlite);
 
 	os_free(conf->eap_req_id_text);
@@ -477,7 +502,7 @@
 	hostapd_config_free_vlan(conf);
 	os_free(conf->time_zone);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	{
 		struct ft_remote_r0kh *r0kh, *r0kh_prev;
 		struct ft_remote_r1kh *r1kh, *r1kh_prev;
@@ -498,7 +523,7 @@
 			os_free(r1kh_prev);
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_WPS
 	os_free(conf->wps_pin_requests);
@@ -567,6 +592,7 @@
 #endif /* CONFIG_HS20 */
 
 	wpabuf_free(conf->vendor_elements);
+	wpabuf_free(conf->assocresp_elements);
 
 	os_free(conf->sae_groups);
 
@@ -581,6 +607,8 @@
 	os_free(conf->no_probe_resp_if_seen_on);
 	os_free(conf->no_auth_if_seen_on);
 
+	hostapd_config_free_fils_realms(conf);
+
 	os_free(conf);
 }
 
@@ -606,6 +634,8 @@
 #ifdef CONFIG_ACS
 	os_free(conf->acs_chan_bias);
 #endif /* CONFIG_ACS */
+	wpabuf_free(conf->lci);
+	wpabuf_free(conf->civic);
 
 	os_free(conf);
 }
@@ -799,7 +829,7 @@
 		}
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
 	    (bss->nas_identifier == NULL ||
 	     os_strlen(bss->nas_identifier) < 1 ||
@@ -809,7 +839,7 @@
 			   "string");
 		return -1;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_IEEE80211N
 	if (full_config && conf->ieee80211n &&
@@ -838,6 +868,25 @@
 	}
 #endif /* CONFIG_IEEE80211N */
 
+#ifdef CONFIG_IEEE80211AC
+	if (full_config && conf->ieee80211ac &&
+	    bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+		bss->disable_11ac = 1;
+		wpa_printf(MSG_ERROR,
+			   "VHT (IEEE 802.11ac) with WEP is not allowed, disabling VHT capabilities");
+	}
+
+	if (full_config && conf->ieee80211ac && bss->wpa &&
+	    !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+	    !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+				   WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+	{
+		bss->disable_11ac = 1;
+		wpa_printf(MSG_ERROR,
+			   "VHT (IEEE 802.11ac) with WPA/WPA2 requires CCMP/GCMP to be enabled, disabling VHT capabilities");
+	}
+#endif /* CONFIG_IEEE80211AC */
+
 #ifdef CONFIG_WPS
 	if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
 		wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 2d07c67..fdd5a1a 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -41,6 +41,10 @@
 #define MESH_CONF_SEC_AUTH BIT(1)
 #define MESH_CONF_SEC_AMPE BIT(2)
 	unsigned int security;
+	enum mfp_options ieee80211w;
+	unsigned int pairwise_cipher;
+	unsigned int group_cipher;
+	unsigned int mgmt_group_cipher;
 	int dot11MeshMaxRetries;
 	int dot11MeshRetryTimeout; /* msec */
 	int dot11MeshConfirmTimeout; /* msec */
@@ -220,6 +224,12 @@
 	struct wpabuf *payload;
 };
 
+struct fils_realm {
+	struct dl_list list;
+	u8 hash[2];
+	char realm[];
+};
+
 
 /**
  * struct hostapd_bss_config - Per-BSS configuration
@@ -259,6 +269,7 @@
 	int radius_das_port;
 	unsigned int radius_das_time_window;
 	int radius_das_require_event_timestamp;
+	int radius_das_require_message_authenticator;
 	struct hostapd_ip_addr radius_das_client_addr;
 	u8 *radius_das_shared_secret;
 	size_t radius_das_shared_secret_len;
@@ -282,7 +293,7 @@
 	char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
 					* frames */
 
-	enum {
+	enum macaddr_acl {
 		ACCEPT_UNLESS_DENIED = 0,
 		DENY_UNLESS_ACCEPTED = 1,
 		USE_EXTERNAL_RADIUS_AUTH = 2
@@ -319,12 +330,14 @@
 	int wpa_strict_rekey;
 	int wpa_gmk_rekey;
 	int wpa_ptk_rekey;
+	u32 wpa_group_update_count;
+	u32 wpa_pairwise_update_count;
 	int rsn_pairwise;
 	int rsn_preauth;
 	char *rsn_preauth_interfaces;
 	int peerkey;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	/* IEEE 802.11r - Fast BSS Transition */
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
 	u8 r1_key_holder[FT_R1KH_ID_LEN];
@@ -334,7 +347,8 @@
 	struct ft_remote_r1kh *r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+	int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
 
 	char *ctrl_interface; /* directory for UNIX domain sockets */
 #ifndef CONFIG_NATIVE_WINDOWS
@@ -503,7 +517,8 @@
 	struct dl_list anqp_elem; /* list of struct anqp_element */
 
 	u16 gas_comeback_delay;
-	int gas_frag_limit;
+	size_t gas_frag_limit;
+	int gas_address3;
 
 	u8 qos_map_set[16 + 2 * 21];
 	unsigned int qos_map_set_len;
@@ -557,6 +572,7 @@
 #endif /* CONFIG_RADIUS_TEST */
 
 	struct wpabuf *vendor_elements;
+	struct wpabuf *assocresp_elements;
 
 	unsigned int sae_anti_clogging_threshold;
 	int *sae_groups;
@@ -572,9 +588,10 @@
 #define MESH_ENABLED BIT(0)
 	int mesh;
 
-	int radio_measurements;
+	u8 radio_measurements[RRM_CAPABILITIES_IE_LEN];
 
 	int vendor_vht;
+	int use_sta_nsts;
 
 	char *no_probe_resp_if_seen_on;
 	char *no_auth_if_seen_on;
@@ -584,8 +601,42 @@
 #ifdef CONFIG_MBO
 	int mbo_enabled;
 #endif /* CONFIG_MBO */
+
+	int ftm_responder;
+	int ftm_initiator;
+
+#ifdef CONFIG_FILS
+	u8 fils_cache_id[FILS_CACHE_ID_LEN];
+	int fils_cache_id_set;
+	struct dl_list fils_realms; /* list of struct fils_realm */
+	struct hostapd_ip_addr dhcp_server;
+	int dhcp_rapid_commit_proxy;
+	unsigned int fils_hlp_wait_time;
+	u16 dhcp_server_port;
+	u16 dhcp_relay_port;
+#endif /* CONFIG_FILS */
+
+	int multicast_to_unicast;
 };
 
+/**
+ * struct he_phy_capabilities_info - HE PHY capabilities
+ */
+struct he_phy_capabilities_info {
+	Boolean he_su_beamformer;
+	Boolean he_su_beamformee;
+	Boolean he_mu_beamformer;
+};
+
+/**
+ * struct he_operation - HE operation
+ */
+struct he_operation {
+	u8 he_bss_color;
+	u8 he_default_pe_duration;
+	u8 he_twt_required;
+	u8 he_rts_threshold;
+};
 
 /**
  * struct hostapd_config - Per-radio interface configuration
@@ -609,6 +660,8 @@
 
 	int *supported_rates;
 	int *basic_rates;
+	unsigned int beacon_rate;
+	enum beacon_rate_type rate_type;
 
 	const struct wpa_driver_ops *driver;
 	char *driver_params;
@@ -693,6 +746,16 @@
 	} *acs_chan_bias;
 	unsigned int num_acs_chan_bias;
 #endif /* CONFIG_ACS */
+
+	struct wpabuf *lci;
+	struct wpabuf *civic;
+	int stationary_ap;
+
+	int ieee80211ax;
+#ifdef CONFIG_IEEE80211AX
+	struct he_phy_capabilities_info he_phy_capab;
+	struct he_operation he_op;
+#endif /* CONFIG_IEEE80211AX */
 };
 
 
@@ -700,6 +763,7 @@
 struct hostapd_config * hostapd_config_defaults(void);
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
 void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_free_eap_users(struct hostapd_eap_user *user);
 void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
 void hostapd_config_free_bss(struct hostapd_bss_config *conf);
 void hostapd_config_free(struct hostapd_config *conf);
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index b89f60e..f139465 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -179,6 +179,7 @@
 
 	add_buf(&beacon, hapd->conf->vendor_elements);
 	add_buf(&proberesp, hapd->conf->vendor_elements);
+	add_buf(&assocresp, hapd->conf->assocresp_elements);
 
 	*beacon_ret = beacon;
 	*proberesp_ret = proberesp;
@@ -362,7 +363,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo, u8 vht_opmode, int set)
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+		    int set)
 {
 	struct hostapd_sta_add_params params;
 
@@ -384,6 +386,7 @@
 	params.vht_opmode = vht_opmode;
 	params.flags = hostapd_sta_flags_to_drv(flags);
 	params.qosinfo = qosinfo;
+	params.support_p2p_ps = supp_p2p_ps;
 	params.set = set;
 	return hapd->driver->sta_add(hapd->drv_priv, &params);
 }
@@ -620,7 +623,7 @@
 int hostapd_drv_send_mlme(struct hostapd_data *hapd,
 			  const void *msg, size_t len, int noack)
 {
-	if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+	if (!hapd->driver || !hapd->driver->send_mlme || !hapd->drv_priv)
 		return 0;
 	return hapd->driver->send_mlme(hapd->drv_priv, msg, len, noack, 0,
 				       NULL, 0);
@@ -641,7 +644,7 @@
 int hostapd_drv_sta_deauth(struct hostapd_data *hapd,
 			   const u8 *addr, int reason)
 {
-	if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL)
+	if (!hapd->driver || !hapd->driver->sta_deauth || !hapd->drv_priv)
 		return 0;
 	return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr,
 					reason);
@@ -651,7 +654,7 @@
 int hostapd_drv_sta_disassoc(struct hostapd_data *hapd,
 			     const u8 *addr, int reason)
 {
-	if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL)
+	if (!hapd->driver || !hapd->driver->sta_disassoc || !hapd->drv_priv)
 		return 0;
 	return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr,
 					  reason);
@@ -672,6 +675,36 @@
 			    unsigned int wait, const u8 *dst, const u8 *data,
 			    size_t len)
 {
+	const u8 *bssid;
+	const u8 wildcard_bssid[ETH_ALEN] = {
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+	};
+
+	if (!hapd->driver || !hapd->driver->send_action || !hapd->drv_priv)
+		return 0;
+	bssid = hapd->own_addr;
+	if (!is_multicast_ether_addr(dst) &&
+	    len > 0 && data[0] == WLAN_ACTION_PUBLIC) {
+		struct sta_info *sta;
+
+		/*
+		 * Public Action frames to a STA that is not a member of the BSS
+		 * shall use wildcard BSSID value.
+		 */
+		sta = ap_get_sta(hapd, dst);
+		if (!sta || !(sta->flags & WLAN_STA_ASSOC))
+			bssid = wildcard_bssid;
+	}
+	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
+					 hapd->own_addr, bssid, data, len, 0);
+}
+
+
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+				     unsigned int freq,
+				     unsigned int wait, const u8 *dst,
+				     const u8 *data, size_t len)
+{
 	if (hapd->driver == NULL || hapd->driver->send_action == NULL)
 		return 0;
 	return hapd->driver->send_action(hapd->drv_priv, freq, wait, dst,
@@ -721,7 +754,7 @@
 int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
 			    const u8 *qos_map_set, u8 qos_map_set_len)
 {
-	if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
+	if (!hapd->driver || !hapd->driver->set_qos_map || !hapd->drv_priv)
 		return 0;
 	return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
 					 qos_map_set_len);
@@ -747,6 +780,20 @@
 }
 
 
+void hostapd_get_ext_capa(struct hostapd_iface *iface)
+{
+	struct hostapd_data *hapd = iface->bss[0];
+
+	if (!hapd->driver || !hapd->driver->get_ext_capab)
+		return;
+
+	hapd->driver->get_ext_capab(hapd->drv_priv, WPA_IF_AP_BSS,
+				    &iface->extended_capa,
+				    &iface->extended_capa_mask,
+				    &iface->extended_capa_len);
+}
+
+
 int hostapd_drv_do_acs(struct hostapd_data *hapd)
 {
 	struct drv_acs_params params;
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index 757a706..0bb7954 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -41,7 +41,8 @@
 		    u16 listen_interval,
 		    const struct ieee80211_ht_capabilities *ht_capab,
 		    const struct ieee80211_vht_capabilities *vht_capab,
-		    u32 flags, u8 qosinfo, u8 vht_opmode, int set);
+		    u32 flags, u8 qosinfo, u8 vht_opmode, int supp_p2p_ps,
+		    int set);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
 			     size_t elem_len);
@@ -98,6 +99,10 @@
 int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
 			    unsigned int wait, const u8 *dst, const u8 *data,
 			    size_t len);
+int hostapd_drv_send_action_addr3_ap(struct hostapd_data *hapd,
+				     unsigned int freq,
+				     unsigned int wait, const u8 *dst,
+				     const u8 *data, size_t len);
 int hostapd_add_sta_node(struct hostapd_data *hapd, const u8 *addr,
 			 u16 auth_alg);
 int hostapd_sta_auth(struct hostapd_data *hapd, const u8 *addr,
@@ -123,6 +128,8 @@
 int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
 			    u8 qos_map_set_len);
 
+void hostapd_get_ext_capa(struct hostapd_iface *iface);
+
 static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
 						  int enabled)
 {
@@ -153,7 +160,7 @@
 static inline int hostapd_drv_sta_remove(struct hostapd_data *hapd,
 					 const u8 *addr)
 {
-	if (hapd->driver == NULL || hapd->driver->sta_remove == NULL)
+	if (!hapd->driver || !hapd->driver->sta_remove || !hapd->drv_priv)
 		return 0;
 	return hapd->driver->sta_remove(hapd->drv_priv, addr);
 }
@@ -276,7 +283,7 @@
 static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
 				     size_t buflen)
 {
-	if (hapd->driver == NULL || hapd->driver->status == NULL)
+	if (!hapd->driver || !hapd->driver->status || !hapd->drv_priv)
 		return -1;
 	return hapd->driver->status(hapd->drv_priv, buf, buflen);
 }
@@ -335,7 +342,7 @@
 
 static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
 {
-	if (hapd->driver == NULL || hapd->driver->stop_ap == NULL)
+	if (!hapd->driver || !hapd->driver->stop_ap || !hapd->drv_priv)
 		return 0;
 	return hapd->driver->stop_ap(hapd->drv_priv);
 }
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
index e7308a0..db8a267 100644
--- a/src/ap/ap_mlme.c
+++ b/src/ap/ap_mlme.c
@@ -57,7 +57,11 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-AUTHENTICATE.indication(" MACSTR ", %s)",
 		       MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg));
-	if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK &&
+	    !(sta->flags & WLAN_STA_MFP))
 		mlme_deletekeys_request(hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
@@ -105,7 +109,10 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-ASSOCIATE.indication(" MACSTR ")",
 		       MAC2STR(sta->addr));
-	if (sta->auth_alg != WLAN_AUTH_FT)
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK)
 		mlme_deletekeys_request(hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
@@ -130,7 +137,10 @@
 		       HOSTAPD_LEVEL_DEBUG,
 		       "MLME-REASSOCIATE.indication(" MACSTR ")",
 		       MAC2STR(sta->addr));
-	if (sta->auth_alg != WLAN_AUTH_FT)
+	if (sta->auth_alg != WLAN_AUTH_FT &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK &&
+	    sta->auth_alg != WLAN_AUTH_FILS_SK_PFS &&
+	    sta->auth_alg != WLAN_AUTH_FILS_PK)
 		mlme_deletekeys_request(hapd, sta);
 	ap_sta_clear_disconnect_timeouts(hapd, sta);
 }
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 19bff7b..c6bbda3 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -29,6 +29,7 @@
 #include "beacon.h"
 #include "hs20.h"
 #include "dfs.h"
+#include "taxonomy.h"
 
 
 #ifdef NEED_AP_MLME
@@ -36,18 +37,21 @@
 static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
 					 size_t len)
 {
-	if (!hapd->conf->radio_measurements || len < 2 + 4)
+	size_t i;
+
+	for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+		if (hapd->conf->radio_measurements[i])
+			break;
+	}
+
+	if (i == RRM_CAPABILITIES_IE_LEN || len < 2 + RRM_CAPABILITIES_IE_LEN)
 		return eid;
 
 	*eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
-	*eid++ = 5;
-	*eid++ = (hapd->conf->radio_measurements & BIT(0)) ?
-		WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00;
-	*eid++ = 0x00;
-	*eid++ = 0x00;
-	*eid++ = 0x00;
-	*eid++ = 0x00;
-	return eid;
+	*eid++ = RRM_CAPABILITIES_IE_LEN;
+	os_memcpy(eid, hapd->conf->radio_measurements, RRM_CAPABILITIES_IE_LEN);
+
+	return eid + RRM_CAPABILITIES_IE_LEN;
 }
 
 
@@ -388,6 +392,13 @@
 			2 + sizeof(struct ieee80211_vht_operation);
 	}
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		buflen += 4 + sizeof (struct ieee80211_he_capabilities) +
+			4 + sizeof (struct ieee80211_he_operation);
+	}
+#endif
+
 	buflen += hostapd_mbo_ie_len(hapd);
 
 	resp = os_zalloc(buflen);
@@ -482,15 +493,27 @@
 
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
-		pos = hostapd_eid_vht_capabilities(hapd, pos);
+		pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
 		pos = hostapd_eid_vht_operation(hapd, pos);
 		pos = hostapd_eid_txpower_envelope(hapd, pos);
 		pos = hostapd_eid_wb_chsw_wrapper(hapd, pos);
 	}
+#endif /* CONFIG_IEEE80211AC */
+
+	pos = hostapd_eid_fils_indic(hapd, pos, 0);
+
+#ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		pos = hostapd_eid_vendor_he_capab(hapd, pos);
+		pos = hostapd_eid_vendor_he_operation(hapd, pos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	/* Wi-Fi Alliance WMM */
 	pos = hostapd_eid_wmm(hapd, pos);
 
@@ -596,7 +619,7 @@
 			   MAC2STR(info->addr));
 		dl_list_del(&info->list);
 		iface->num_sta_seen--;
-		os_free(info);
+		sta_track_del(info);
 	}
 }
 
@@ -614,7 +637,7 @@
 }
 
 
-void sta_track_add(struct hostapd_iface *iface, const u8 *addr)
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal)
 {
 	struct hostapd_sta_info *info;
 
@@ -624,13 +647,17 @@
 		dl_list_del(&info->list);
 		dl_list_add_tail(&iface->sta_seen, &info->list);
 		os_get_reltime(&info->last_seen);
+		info->ssi_signal = ssi_signal;
 		return;
 	}
 
 	/* Add a new entry */
 	info = os_zalloc(sizeof(*info));
+	if (info == NULL)
+		return;
 	os_memcpy(info->addr, addr, ETH_ALEN);
 	os_get_reltime(&info->last_seen);
+	info->ssi_signal = ssi_signal;
 
 	if (iface->num_sta_seen >= iface->conf->track_sta_max_num) {
 		/* Expire oldest entry to make room for a new one */
@@ -670,6 +697,23 @@
 }
 
 
+#ifdef CONFIG_TAXONOMY
+void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
+				   struct wpabuf **probe_ie_taxonomy)
+{
+	struct hostapd_sta_info *info;
+
+	info = sta_track_get(iface, addr);
+	if (!info)
+		return;
+
+	wpabuf_free(*probe_ie_taxonomy);
+	*probe_ie_taxonomy = info->probe_ie_taxonomy;
+	info->probe_ie_taxonomy = NULL;
+}
+#endif /* CONFIG_TAXONOMY */
+
+
 void handle_probe_req(struct hostapd_data *hapd,
 		      const struct ieee80211_mgmt *mgmt, size_t len,
 		      int ssi_signal)
@@ -689,7 +733,7 @@
 		return;
 	ie = ((const u8 *) mgmt) + IEEE80211_HDRLEN;
 	if (hapd->iconf->track_sta_max_num)
-		sta_track_add(hapd->iface, mgmt->sa);
+		sta_track_add(hapd->iface, mgmt->sa, ssi_signal);
 	ie_len = len - IEEE80211_HDRLEN;
 
 	for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
@@ -779,6 +823,21 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_TAXONOMY
+	{
+		struct sta_info *sta;
+		struct hostapd_sta_info *info;
+
+		if ((sta = ap_get_sta(hapd, mgmt->sa)) != NULL) {
+			taxonomy_sta_info_probe_req(hapd, sta, ie, ie_len);
+		} else if ((info = sta_track_get(hapd->iface,
+						 mgmt->sa)) != NULL) {
+			taxonomy_hostapd_sta_info_probe_req(hapd, info,
+							    ie, ie_len);
+		}
+	}
+#endif /* CONFIG_TAXONOMY */
+
 	res = ssid_match(hapd, elems.ssid, elems.ssid_len,
 			 elems.ssid_list, elems.ssid_list_len);
 	if (res == NO_SSID_MATCH) {
@@ -947,6 +1006,16 @@
 #endif /* NEED_AP_MLME */
 
 
+void sta_track_del(struct hostapd_sta_info *info)
+{
+#ifdef CONFIG_TAXONOMY
+	wpabuf_free(info->probe_ie_taxonomy);
+	info->probe_ie_taxonomy = NULL;
+#endif /* CONFIG_TAXONOMY */
+	os_free(info);
+}
+
+
 int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 			       struct wpa_driver_ap_params *params)
 {
@@ -985,6 +1054,13 @@
 	}
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		tail_len += 4 + sizeof (struct ieee80211_he_capabilities) +
+			4 + sizeof (struct ieee80211_he_operation);
+	}
+#endif
+
 	tail_len += hostapd_mbo_ie_len(hapd);
 
 	tailpos = tail = os_malloc(tail_len);
@@ -1102,15 +1178,27 @@
 
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
-		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+		tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0);
 		tailpos = hostapd_eid_vht_operation(hapd, tailpos);
 		tailpos = hostapd_eid_txpower_envelope(hapd, tailpos);
 		tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos);
 	}
+#endif /* CONFIG_IEEE80211AC */
+
+	tailpos = hostapd_eid_fils_indic(hapd, tailpos, 0);
+
+#ifdef CONFIG_IEEE80211AC
 	if (hapd->conf->vendor_vht)
 		tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_IEEE80211AX
+	if (hapd->iconf->ieee80211ax) {
+		tailpos = hostapd_eid_vendor_he_capab(hapd, tailpos);
+		tailpos = hostapd_eid_vendor_he_operation(hapd, tailpos);
+	}
+#endif /* CONFIG_IEEE80211AX */
+
 	/* Wi-Fi Alliance WMM */
 	tailpos = hostapd_eid_wmm(hapd, tailpos);
 
@@ -1163,6 +1251,8 @@
 	params->dtim_period = hapd->conf->dtim_period;
 	params->beacon_int = hapd->iconf->beacon_int;
 	params->basic_rates = hapd->iface->basic_rates;
+	params->beacon_rate = hapd->iconf->beacon_rate;
+	params->rate_type = hapd->iconf->rate_type;
 	params->ssid = hapd->conf->ssid.ssid;
 	params->ssid_len = hapd->conf->ssid.ssid_len;
 	if ((hapd->conf->wpa & (WPA_PROTO_WPA | WPA_PROTO_RSN)) ==
@@ -1226,6 +1316,7 @@
 		params->osen = 1;
 	}
 #endif /* CONFIG_HS20 */
+	params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
 	params->pbss = hapd->conf->pbss;
 	return 0;
 }
diff --git a/src/ap/beacon.h b/src/ap/beacon.h
index d98f42e..a26e308 100644
--- a/src/ap/beacon.h
+++ b/src/ap/beacon.h
@@ -21,10 +21,13 @@
 int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 			       struct wpa_driver_ap_params *params);
 void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
-void sta_track_add(struct hostapd_iface *iface, const u8 *addr);
+void sta_track_add(struct hostapd_iface *iface, const u8 *addr, int ssi_signal);
+void sta_track_del(struct hostapd_sta_info *info);
 void sta_track_expire(struct hostapd_iface *iface, int force);
 struct hostapd_data *
 sta_track_seen_on(struct hostapd_iface *iface, const u8 *addr,
 		  const char *ifname);
+void sta_track_claim_taxonomy_info(struct hostapd_iface *iface, const u8 *addr,
+				   struct wpabuf **probe_ie_taxonomy);
 
 #endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 317b238..a760e3a 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -23,6 +23,7 @@
 #include "ctrl_iface_ap.h"
 #include "ap_drv_ops.h"
 #include "mbo_ap.h"
+#include "taxonomy.h"
 
 
 static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
@@ -36,9 +37,9 @@
 		return 0;
 
 	ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
-			  "rx_bytes=%llu\ntx_bytes=%llu\n",
+			  "rx_bytes=%llu\ntx_bytes=%llu\ninactive_msec=%lu\n",
 			  data.rx_packets, data.tx_packets,
-			  data.rx_bytes, data.tx_bytes);
+			  data.rx_bytes, data.tx_bytes, data.inactive_msec);
 	if (os_snprintf_error(buflen, ret))
 		return 0;
 	return ret;
@@ -258,7 +259,7 @@
 	int ret;
 	u8 *pos;
 
-	if (hapd->driver->send_frame == NULL)
+	if (!hapd->drv_priv || !hapd->driver->send_frame)
 		return -1;
 
 	mgmt = os_zalloc(sizeof(*mgmt) + 100);
@@ -269,7 +270,7 @@
 	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
 		" with minor reason code %u (stype=%u (%s))",
 		MAC2STR(addr), minor_reason_code, stype,
-		fc2str(mgmt->frame_control));
+		fc2str(le_to_host16(mgmt->frame_control)));
 
 	os_memcpy(mgmt->da, addr, ETH_ALEN);
 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
@@ -325,7 +326,7 @@
 	if (pos) {
 		struct ieee80211_mgmt mgmt;
 		int encrypt;
-		if (hapd->driver->send_frame == NULL)
+		if (!hapd->drv_priv || !hapd->driver->send_frame)
 			return -1;
 		pos += 6;
 		encrypt = atoi(pos);
@@ -352,7 +353,10 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_deauth(hapd, addr, reason);
+	if (os_strstr(txtaddr, " tx=0"))
+		hostapd_drv_sta_remove(hapd, addr);
+	else
+		hostapd_drv_sta_deauth(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
 		ap_sta_deauthenticate(hapd, sta, reason);
@@ -385,7 +389,7 @@
 	if (pos) {
 		struct ieee80211_mgmt mgmt;
 		int encrypt;
-		if (hapd->driver->send_frame == NULL)
+		if (!hapd->drv_priv || !hapd->driver->send_frame)
 			return -1;
 		pos += 6;
 		encrypt = atoi(pos);
@@ -412,7 +416,10 @@
 	}
 #endif /* CONFIG_P2P_MANAGER */
 
-	hostapd_drv_sta_disassoc(hapd, addr, reason);
+	if (os_strstr(txtaddr, " tx=0"))
+		hostapd_drv_sta_remove(hapd, addr);
+	else
+		hostapd_drv_sta_disassoc(hapd, addr, reason);
 	sta = ap_get_sta(hapd, addr);
 	if (sta)
 		ap_sta_disassociate(hapd, sta, reason);
@@ -423,6 +430,49 @@
 }
 
 
+#ifdef CONFIG_TAXONOMY
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+				 const char *txtaddr,
+				 char *buf, size_t buflen)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE SIGNATURE %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	return retrieve_sta_taxonomy(hapd, sta, buf, buflen);
+}
+#endif /* CONFIG_TAXONOMY */
+
+
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+				const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	struct sta_info *sta;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE POLL_STA %s", txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta)
+		return -1;
+
+	hostapd_drv_poll_client(hapd, hapd->own_addr, addr,
+				sta->flags & WLAN_STA_WMM);
+	return 0;
+}
+
+
 int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
 			      size_t buflen)
 {
@@ -487,20 +537,28 @@
 			  "channel=%u\n"
 			  "secondary_channel=%d\n"
 			  "ieee80211n=%d\n"
-			  "ieee80211ac=%d\n"
-			  "vht_oper_chwidth=%d\n"
-			  "vht_oper_centr_freq_seg0_idx=%d\n"
-			  "vht_oper_centr_freq_seg1_idx=%d\n",
+			  "ieee80211ac=%d\n",
 			  iface->conf->channel,
-			  iface->conf->secondary_channel,
-			  iface->conf->ieee80211n,
-			  iface->conf->ieee80211ac,
-			  iface->conf->vht_oper_chwidth,
-			  iface->conf->vht_oper_centr_freq_seg0_idx,
-			  iface->conf->vht_oper_centr_freq_seg1_idx);
+			  iface->conf->ieee80211n && !hapd->conf->disable_11n ?
+			  iface->conf->secondary_channel : 0,
+			  iface->conf->ieee80211n && !hapd->conf->disable_11n,
+			  iface->conf->ieee80211ac &&
+			  !hapd->conf->disable_11ac);
 	if (os_snprintf_error(buflen - len, ret))
 		return len;
 	len += ret;
+	if (iface->conf->ieee80211ac && !hapd->conf->disable_11ac) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  "vht_oper_chwidth=%d\n"
+				  "vht_oper_centr_freq_seg0_idx=%d\n"
+				  "vht_oper_centr_freq_seg1_idx=%d\n",
+				  iface->conf->vht_oper_chwidth,
+				  iface->conf->vht_oper_centr_freq_seg0_idx,
+				  iface->conf->vht_oper_centr_freq_seg1_idx);
+		if (os_snprintf_error(buflen - len, ret))
+			return len;
+		len += ret;
+	}
 
 	for (i = 0; i < iface->num_bss; i++) {
 		struct hostapd_data *bss = iface->bss[i];
@@ -581,3 +639,60 @@
 {
 	wpa_auth_pmksa_flush(hapd->wpa_auth);
 }
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+				       const u8 *addr, char *buf, size_t len)
+{
+	return wpa_auth_pmksa_list_mesh(hapd->wpa_auth, addr, buf, len);
+}
+
+
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd)
+{
+	u8 spa[ETH_ALEN];
+	u8 pmkid[PMKID_LEN];
+	u8 pmk[PMK_LEN_MAX];
+	char *pos;
+	int expiration;
+
+	/*
+	 * Entry format:
+	 * <BSSID> <PMKID> <PMK> <expiration in seconds>
+	 */
+
+	if (hwaddr_aton(cmd, spa))
+		return NULL;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return NULL;
+	pos++;
+
+	if (hexstr2bin(pos, pmkid, PMKID_LEN) < 0)
+		return NULL;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return NULL;
+	pos++;
+
+	if (hexstr2bin(pos, pmk, PMK_LEN) < 0)
+		return NULL;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		return NULL;
+	pos++;
+
+	if (sscanf(pos, "%d", &expiration) != 1)
+		return NULL;
+
+	return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 3ad622f..3b61cac 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -19,6 +19,11 @@
 				      const char *txtaddr);
 int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
 				    const char *txtaddr);
+int hostapd_ctrl_iface_signature(struct hostapd_data *hapd,
+				 const char *txtaddr,
+				 char *buf, size_t buflen);
+int hostapd_ctrl_iface_poll_sta(struct hostapd_data *hapd,
+				const char *txtaddr);
 int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
 			      size_t buflen);
 int hostapd_parse_csa_settings(const char *pos,
@@ -27,5 +32,8 @@
 int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
 				  size_t len);
 void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
+int hostapd_ctrl_iface_pmksa_list_mesh(struct hostapd_data *hapd,
+				       const u8 *addr, char *buf, size_t len);
+void * hostapd_ctrl_iface_pmksa_create_entry(const u8 *aa, char *cmd);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
index 3a77225..b9a36d7 100644
--- a/src/ap/dhcp_snoop.c
+++ b/src/ap/dhcp_snoop.c
@@ -7,10 +7,9 @@
  */
 
 #include "utils/includes.h"
-#include <netinet/ip.h>
-#include <netinet/udp.h>
 
 #include "utils/common.h"
+#include "common/dhcp.h"
 #include "l2_packet/l2_packet.h"
 #include "hostapd.h"
 #include "sta_info.h"
@@ -18,29 +17,6 @@
 #include "x_snoop.h"
 #include "dhcp_snoop.h"
 
-struct bootp_pkt {
-	struct iphdr iph;
-	struct udphdr udph;
-	u8 op;
-	u8 htype;
-	u8 hlen;
-	u8 hops;
-	be32 xid;
-	be16 secs;
-	be16 flags;
-	be32 client_ip;
-	be32 your_ip;
-	be32 server_ip;
-	be32 relay_ip;
-	u8 hw_addr[16];
-	u8 serv_name[64];
-	u8 boot_file[128];
-	u8 exten[312];
-} STRUCT_PACKED;
-
-#define DHCPACK	5
-static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
-
 
 static const char * ipaddr_str(u32 addr)
 {
@@ -74,24 +50,26 @@
 	if (tot_len > (unsigned int) (len - ETH_HLEN))
 		return;
 
-	if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
+	if (WPA_GET_BE32(b->exten) != DHCP_MAGIC)
 		return;
 
 	/* Parse DHCP options */
 	end = (const u8 *) b + tot_len;
 	pos = &b->exten[4];
-	while (pos < end && *pos != 0xff) {
+	while (pos < end && *pos != DHCP_OPT_END) {
 		const u8 *opt = pos++;
 
-		if (*opt == 0) /* padding */
+		if (*opt == DHCP_OPT_PAD)
 			continue;
 
+		if (pos >= end || 1 + *pos > end - pos)
+			break;
 		pos += *pos + 1;
 		if (pos >= end)
 			break;
 
 		switch (*opt) {
-		case 1:  /* subnet mask */
+		case DHCP_OPT_SUBNET_MASK:
 			if (opt[1] == 4)
 				subnet_mask = WPA_GET_BE32(&opt[2]);
 			if (subnet_mask == 0)
@@ -101,7 +79,7 @@
 				prefixlen--;
 			}
 			break;
-		case 53: /* message type */
+		case DHCP_OPT_MSG_TYPE:
 			if (opt[1])
 				msgtype = opt[2];
 			break;
@@ -121,7 +99,8 @@
 
 		wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
 			   " @ IPv4 address %s/%d",
-			   MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
+			   MAC2STR(sta->addr),
+			   ipaddr_str(be_to_host32(b->your_ip)),
 			   prefixlen);
 
 		if (sta->ipaddr == b->your_ip)
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 3ab5bf3..f69c655 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -45,10 +45,10 @@
 	struct ieee802_11_elems elems;
 	const u8 *ie;
 	size_t ielen;
-#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_IEEE80211W)
 	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
 	u8 *p = buf;
-#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+#endif /* CONFIG_IEEE80211R_AP || CONFIG_IEEE80211W */
 	u16 reason = WLAN_REASON_UNSPECIFIED;
 	u16 status = WLAN_STATUS_SUCCESS;
 	const u8 *p2p_dev_addr = NULL;
@@ -116,8 +116,15 @@
 	}
 	sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS | WLAN_STA_WPS2);
 
-	res = hostapd_check_acl(hapd, addr, NULL);
-	if (res != HOSTAPD_ACL_ACCEPT) {
+	/*
+	 * ACL configurations to the drivers (implementing AP SME and ACL
+	 * offload) without hostapd's knowledge, can result in a disconnection
+	 * though the driver accepts the connection. Skip the hostapd check for
+	 * ACL if the driver supports ACL offload to avoid potentially
+	 * conflicting ACL rules.
+	 */
+	if (hapd->iface->drv_max_acl_mac_addrs == 0 &&
+	    hostapd_check_acl(hapd, addr, NULL) != HOSTAPD_ACL_ACCEPT) {
 		wpa_printf(MSG_INFO, "STA " MACSTR " not allowed to connect",
 			   MAC2STR(addr));
 		reason = WLAN_REASON_UNSPECIFIED;
@@ -286,7 +293,7 @@
 			sta->flags &= ~WLAN_STA_MFP;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			status = wpa_ft_validate_reassoc(sta->wpa_sm, req_ies,
 							 req_ies_len);
@@ -300,7 +307,7 @@
 				goto fail;
 			}
 		}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	} else if (hapd->conf->wps_state) {
 #ifdef CONFIG_WPS
 		struct wpabuf *wps;
@@ -368,7 +375,7 @@
 skip_wpa_check:
 #endif /* CONFIG_WPS */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, buf, sizeof(buf),
 					sta->auth_alg, req_ies, req_ies_len);
 
@@ -376,11 +383,11 @@
 
 	if (sta->auth_alg == WLAN_AUTH_FT)
 		ap_sta_set_authorized(hapd, sta, 1);
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP */
 	/* Keep compiler silent about unused variables */
 	if (status) {
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
 	sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
@@ -407,9 +414,9 @@
 	return 0;
 
 fail:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	hostapd_drv_sta_disassoc(hapd, sta->addr, reason);
 	ap_free_sta(hapd, sta);
 	return -1;
@@ -464,8 +471,7 @@
 		       HOSTAPD_LEVEL_INFO,
 		       "disconnected due to excessive missing ACKs");
 	hostapd_drv_sta_disassoc(hapd, addr, WLAN_REASON_DISASSOC_LOW_ACK);
-	if (sta)
-		ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
+	ap_sta_disassociate(hapd, sta, WLAN_REASON_DISASSOC_LOW_ACK);
 }
 
 
@@ -683,7 +689,7 @@
 
 #ifdef HOSTAPD
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static void hostapd_notify_auth_ft_finish(void *ctx, const u8 *dst,
 					  const u8 *bssid,
 					  u16 auth_transaction, u16 status,
@@ -702,7 +708,7 @@
 
 	hostapd_sta_auth(hapd, dst, auth_transaction, status, ies, ies_len);
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 static void hostapd_notif_auth(struct hostapd_data *hapd,
@@ -723,7 +729,7 @@
 	}
 	sta->flags &= ~WLAN_STA_PREAUTH;
 	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (rx_auth->auth_type == WLAN_AUTH_FT && hapd->wpa_auth) {
 		sta->auth_alg = WLAN_AUTH_FT;
 		if (sta->wpa_sm == NULL)
@@ -741,7 +747,7 @@
 				    hostapd_notify_auth_ft_finish, hapd);
 		return;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 fail:
 	hostapd_sta_auth(hapd, rx_auth->peer, rx_auth->auth_transaction + 1,
 			 status, resp_ies, resp_ies_len);
@@ -774,13 +780,13 @@
 		wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
 		return;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (mgmt->u.action.category == WLAN_ACTION_FT) {
 		const u8 *payload = drv_mgmt->frame + 24 + 1;
 
 		wpa_ft_action_rx(sta->wpa_sm, payload, plen);
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
 		ieee802_11_sa_query_action(
@@ -916,11 +922,24 @@
 			       size_t len, u16 stype, int ok)
 {
 	struct ieee80211_hdr *hdr;
+	struct hostapd_data *orig_hapd = hapd;
 
 	hdr = (struct ieee80211_hdr *) buf;
 	hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
-	if (hapd == NULL || hapd == HAPD_BROADCAST)
+	if (!hapd)
 		return;
+	if (hapd == HAPD_BROADCAST) {
+		if (stype != WLAN_FC_STYPE_ACTION || len <= 25 ||
+		    buf[24] != WLAN_ACTION_PUBLIC)
+			return;
+		hapd = get_hapd_bssid(orig_hapd->iface, hdr->addr2);
+		if (!hapd || hapd == HAPD_BROADCAST)
+			return;
+		/*
+		 * Allow processing of TX status for a Public Action frame that
+		 * used wildcard BBSID.
+		 */
+	}
 	ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
 }
 
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
new file mode 100644
index 0000000..40d9be1
--- /dev/null
+++ b/src/ap/fils_hlp.c
@@ -0,0 +1,636 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/dhcp.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
+#include "fils_hlp.h"
+
+
+static be16 ip_checksum(const void *buf, size_t len)
+{
+	u32 sum = 0;
+	const u16 *pos;
+
+	for (pos = buf; len >= 2; len -= 2)
+		sum += ntohs(*pos++);
+	if (len)
+		sum += ntohs(*pos << 8);
+
+	sum = (sum >> 16) + (sum & 0xffff);
+	sum += sum >> 16;
+	return htons(~sum);
+}
+
+
+static int fils_dhcp_request(struct hostapd_data *hapd, struct sta_info *sta,
+			     struct dhcp_data *dhcpoffer, u8 *dhcpofferend)
+{
+	u8 *pos, *end;
+	struct dhcp_data *dhcp;
+	struct sockaddr_in addr;
+	ssize_t res;
+	const u8 *server_id = NULL;
+
+	if (!sta->hlp_dhcp_discover) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No pending HLP DHCPDISCOVER available");
+		return -1;
+	}
+
+	/* Convert to DHCPREQUEST, remove rapid commit option, replace requested
+	 * IP address option with yiaddr. */
+	pos = wpabuf_mhead(sta->hlp_dhcp_discover);
+	end = pos + wpabuf_len(sta->hlp_dhcp_discover);
+	dhcp = (struct dhcp_data *) pos;
+	pos = (u8 *) (dhcp + 1);
+	pos += 4; /* skip magic */
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_MSG_TYPE:
+			if (olen > 0)
+				*pos = DHCPREQUEST;
+			break;
+		case DHCP_OPT_RAPID_COMMIT:
+		case DHCP_OPT_REQUESTED_IP_ADDRESS:
+		case DHCP_OPT_SERVER_ID:
+			/* Remove option */
+			pos -= 2;
+			os_memmove(pos, pos + 2 + olen, end - pos - 2 - olen);
+			end -= 2 + olen;
+			olen = 0;
+			break;
+		}
+		pos += olen;
+	}
+	if (pos >= end || *pos != DHCP_OPT_END) {
+		wpa_printf(MSG_DEBUG, "FILS: Could not update DHCPDISCOVER");
+		return -1;
+	}
+	sta->hlp_dhcp_discover->used = pos - (u8 *) dhcp;
+
+	/* Copy Server ID option from DHCPOFFER to DHCPREQUEST */
+	pos = (u8 *) (dhcpoffer + 1);
+	end = dhcpofferend;
+	pos += 4; /* skip magic */
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_SERVER_ID:
+			server_id = pos - 2;
+			break;
+		}
+		pos += olen;
+	}
+
+	if (wpabuf_resize(&sta->hlp_dhcp_discover,
+			  6 + 1 + (server_id ? 2 + server_id[1] : 0)))
+		return -1;
+	if (server_id)
+		wpabuf_put_data(sta->hlp_dhcp_discover, server_id,
+				2 + server_id[1]);
+	wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_REQUESTED_IP_ADDRESS);
+	wpabuf_put_u8(sta->hlp_dhcp_discover, 4);
+	wpabuf_put_data(sta->hlp_dhcp_discover, &dhcpoffer->your_ip, 4);
+	wpabuf_put_u8(sta->hlp_dhcp_discover, DHCP_OPT_END);
+
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+	addr.sin_port = htons(hapd->conf->dhcp_server_port);
+	res = sendto(hapd->dhcp_sock, wpabuf_head(sta->hlp_dhcp_discover),
+		     wpabuf_len(sta->hlp_dhcp_discover), 0,
+		     (const struct sockaddr *) &addr, sizeof(addr));
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+			   strerror(errno));
+		return -1;
+	}
+	wpa_printf(MSG_DEBUG,
+		   "FILS: Acting as DHCP rapid commit proxy for %s:%d",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+	sta->fils_dhcp_rapid_commit_proxy = 1;
+	return 0;
+}
+
+
+static void fils_dhcp_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+	struct hostapd_data *hapd = sock_ctx;
+	struct sta_info *sta;
+	u8 buf[1500], *pos, *end, *end_opt = NULL;
+	struct dhcp_data *dhcp;
+	struct sockaddr_in addr;
+	socklen_t addr_len;
+	ssize_t res;
+	u8 msgtype = 0;
+	int rapid_commit = 0;
+	struct iphdr *iph;
+	struct udphdr *udph;
+	struct wpabuf *resp;
+	const u8 *rpos;
+	size_t left, len;
+
+	addr_len = sizeof(addr);
+	res = recvfrom(sd, buf, sizeof(buf), 0,
+		       (struct sockaddr *) &addr, &addr_len);
+	if (res < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: DHCP read failed: %s",
+			   strerror(errno));
+		return;
+	}
+	wpa_printf(MSG_DEBUG, "FILS: DHCP response from server %s:%d (len=%d)",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), (int) res);
+	wpa_hexdump(MSG_MSGDUMP, "FILS: HLP - DHCP server response", buf, res);
+	if ((size_t) res < sizeof(*dhcp))
+		return;
+	dhcp = (struct dhcp_data *) buf;
+	if (dhcp->op != 2)
+		return; /* Not a BOOTREPLY */
+	if (dhcp->relay_ip != hapd->conf->own_ip_addr.u.v4.s_addr) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP - DHCP response to unknown relay address 0x%x",
+			   dhcp->relay_ip);
+		return;
+	}
+	dhcp->relay_ip = 0;
+	pos = (u8 *) (dhcp + 1);
+	end = &buf[res];
+
+	if (end - pos < 4 || WPA_GET_BE32(pos) != DHCP_MAGIC) {
+		wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic in response");
+		return;
+	}
+	pos += 4;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options in response",
+		    pos, end - pos);
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_MSG_TYPE:
+			if (olen > 0)
+				msgtype = pos[0];
+			break;
+		case DHCP_OPT_RAPID_COMMIT:
+			rapid_commit = 1;
+			break;
+		}
+		pos += olen;
+	}
+	if (pos < end && *pos == DHCP_OPT_END)
+		end_opt = pos;
+
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP - DHCP message type %u (rapid_commit=%d hw_addr="
+		   MACSTR ")",
+		   msgtype, rapid_commit, MAC2STR(dhcp->hw_addr));
+
+	sta = ap_get_sta(hapd, dhcp->hw_addr);
+	if (!sta || !sta->fils_pending_assoc_req) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No pending HLP DHCP exchange with hw_addr"
+			   MACSTR, MAC2STR(dhcp->hw_addr));
+		return;
+	}
+
+	if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPOFFER &&
+	    !rapid_commit) {
+		/* Use hostapd to take care of 4-message exchange and convert
+		 * the final DHCPACK to rapid commit version. */
+		if (fils_dhcp_request(hapd, sta, dhcp, end) == 0)
+			return;
+		/* failed, so send the server response as-is */
+	} else if (msgtype != DHCPACK) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No DHCPACK available from the server and cannot do rapid commit proxying");
+	}
+
+	pos = buf;
+	resp = wpabuf_alloc(2 * ETH_ALEN + 6 + 2 +
+			    sizeof(*iph) + sizeof(*udph) + (end - pos) + 2);
+	if (!resp)
+		return;
+	wpabuf_put_data(resp, sta->addr, ETH_ALEN);
+	wpabuf_put_data(resp, hapd->own_addr, ETH_ALEN);
+	wpabuf_put_data(resp, "\xaa\xaa\x03\x00\x00\x00", 6);
+	wpabuf_put_be16(resp, ETH_P_IP);
+	iph = wpabuf_put(resp, sizeof(*iph));
+	iph->version = 4;
+	iph->ihl = sizeof(*iph) / 4;
+	iph->tot_len = htons(sizeof(*iph) + sizeof(*udph) + (end - pos));
+	iph->ttl = 1;
+	iph->saddr = hapd->conf->dhcp_server.u.v4.s_addr;
+	iph->daddr = dhcp->client_ip;
+	iph->check = ip_checksum(iph, sizeof(*iph));
+	udph = wpabuf_put(resp, sizeof(*udph));
+	udph->uh_sport = htons(DHCP_SERVER_PORT);
+	udph->uh_dport = htons(DHCP_CLIENT_PORT);
+	udph->len = htons(sizeof(*udph) + (end - pos));
+	udph->check = htons(0x0000); /* TODO: calculate checksum */
+	if (hapd->conf->dhcp_rapid_commit_proxy && msgtype == DHCPACK &&
+	    !rapid_commit && sta->fils_dhcp_rapid_commit_proxy && end_opt) {
+		/* Add rapid commit option */
+		wpabuf_put_data(resp, pos, end_opt - pos);
+		wpabuf_put_u8(resp, DHCP_OPT_RAPID_COMMIT);
+		wpabuf_put_u8(resp, 0);
+		wpabuf_put_data(resp, end_opt, end - end_opt);
+	} else {
+		wpabuf_put_data(resp, pos, end - pos);
+	}
+	if (wpabuf_resize(&sta->fils_hlp_resp, wpabuf_len(resp) +
+			  2 * wpabuf_len(resp) / 255 + 100)) {
+		wpabuf_free(resp);
+		return;
+	}
+
+	rpos = wpabuf_head(resp);
+	left = wpabuf_len(resp);
+
+	wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXTENSION); /* Element ID */
+	if (left <= 254)
+		len = 1 + left;
+	else
+		len = 255;
+	wpabuf_put_u8(sta->fils_hlp_resp, len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_EXT_FILS_HLP_CONTAINER);
+	/* Destination MAC Address, Source MAC Address, HLP Packet.
+	 * HLP Packet is in MSDU format (i.e., including the LLC/SNAP header
+	 * when LPD is used). */
+	wpabuf_put_data(sta->fils_hlp_resp, rpos, len - 1);
+	rpos += len - 1;
+	left -= len - 1;
+	while (left) {
+		wpabuf_put_u8(sta->fils_hlp_resp, WLAN_EID_FRAGMENT);
+		len = left > 255 ? 255 : left;
+		wpabuf_put_u8(sta->fils_hlp_resp, len);
+		wpabuf_put_data(sta->fils_hlp_resp, rpos, len);
+		rpos += len;
+		left -= len;
+	}
+	wpabuf_free(resp);
+	fils_hlp_finish_assoc(hapd, sta);
+}
+
+
+static int fils_process_hlp_dhcp(struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *msg, size_t len)
+{
+	const struct dhcp_data *dhcp;
+	struct wpabuf *dhcp_buf;
+	struct dhcp_data *dhcp_msg;
+	u8 msgtype = 0;
+	int rapid_commit = 0;
+	const u8 *pos = msg, *end;
+	struct sockaddr_in addr;
+	ssize_t res;
+
+	if (len < sizeof(*dhcp))
+		return 0;
+	dhcp = (const struct dhcp_data *) pos;
+	end = pos + len;
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP request DHCP: op=%u htype=%u hlen=%u hops=%u xid=0x%x",
+		   dhcp->op, dhcp->htype, dhcp->hlen, dhcp->hops,
+		   ntohl(dhcp->xid));
+	pos += sizeof(*dhcp);
+	if (dhcp->op != 1)
+		return 0; /* Not a BOOTREQUEST */
+
+	if (end - pos < 4)
+		return 0;
+	if (WPA_GET_BE32(pos) != DHCP_MAGIC) {
+		wpa_printf(MSG_DEBUG, "FILS: HLP - no DHCP magic");
+		return 0;
+	}
+	pos += 4;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: HLP - DHCP options", pos, end - pos);
+	while (pos < end && *pos != DHCP_OPT_END) {
+		u8 opt, olen;
+
+		opt = *pos++;
+		if (opt == DHCP_OPT_PAD)
+			continue;
+		if (pos >= end)
+			break;
+		olen = *pos++;
+		if (olen > end - pos)
+			break;
+
+		switch (opt) {
+		case DHCP_OPT_MSG_TYPE:
+			if (olen > 0)
+				msgtype = pos[0];
+			break;
+		case DHCP_OPT_RAPID_COMMIT:
+			rapid_commit = 1;
+			break;
+		}
+		pos += olen;
+	}
+
+	wpa_printf(MSG_DEBUG, "FILS: HLP - DHCP message type %u", msgtype);
+	if (msgtype != DHCPDISCOVER)
+		return 0;
+
+	if (hapd->conf->dhcp_server.af != AF_INET ||
+	    hapd->conf->dhcp_server.u.v4.s_addr == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP - no DHCPv4 server configured - drop request");
+		return 0;
+	}
+
+	if (hapd->conf->own_ip_addr.af != AF_INET ||
+	    hapd->conf->own_ip_addr.u.v4.s_addr == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP - no IPv4 own_ip_addr configured - drop request");
+		return 0;
+	}
+
+	if (hapd->dhcp_sock < 0) {
+		int s;
+
+		s = socket(AF_INET, SOCK_DGRAM, 0);
+		if (s < 0) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: Failed to open DHCP socket: %s",
+				   strerror(errno));
+			return 0;
+		}
+
+		if (hapd->conf->dhcp_relay_port) {
+			os_memset(&addr, 0, sizeof(addr));
+			addr.sin_family = AF_INET;
+			addr.sin_addr.s_addr =
+				hapd->conf->own_ip_addr.u.v4.s_addr;
+			addr.sin_port = htons(hapd->conf->dhcp_relay_port);
+			if (bind(s, (struct sockaddr *) &addr, sizeof(addr))) {
+				wpa_printf(MSG_ERROR,
+					   "FILS: Failed to bind DHCP socket: %s",
+					   strerror(errno));
+				close(s);
+				return 0;
+			}
+		}
+		if (eloop_register_sock(s, EVENT_TYPE_READ,
+					fils_dhcp_handler, NULL, hapd)) {
+			close(s);
+			return 0;
+		}
+
+		hapd->dhcp_sock = s;
+	}
+
+	dhcp_buf = wpabuf_alloc(len);
+	if (!dhcp_buf)
+		return 0;
+	dhcp_msg = wpabuf_put(dhcp_buf, len);
+	os_memcpy(dhcp_msg, msg, len);
+	dhcp_msg->relay_ip = hapd->conf->own_ip_addr.u.v4.s_addr;
+	os_memset(&addr, 0, sizeof(addr));
+	addr.sin_family = AF_INET;
+	addr.sin_addr.s_addr = hapd->conf->dhcp_server.u.v4.s_addr;
+	addr.sin_port = htons(hapd->conf->dhcp_server_port);
+	res = sendto(hapd->dhcp_sock, dhcp_msg, len, 0,
+		     (const struct sockaddr *) &addr, sizeof(addr));
+	if (res < 0) {
+		wpa_printf(MSG_ERROR, "FILS: DHCP sendto failed: %s",
+			   strerror(errno));
+		wpabuf_free(dhcp_buf);
+		/* Close the socket to try to recover from error */
+		eloop_unregister_read_sock(hapd->dhcp_sock);
+		close(hapd->dhcp_sock);
+		hapd->dhcp_sock = -1;
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP relayed DHCP request to server %s:%d (rapid_commit=%d)",
+		   inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+		   rapid_commit);
+	if (hapd->conf->dhcp_rapid_commit_proxy && rapid_commit) {
+		/* Store a copy of the DHCPDISCOVER for rapid commit proxying
+		 * purposes if the server does not support the rapid commit
+		 * option. */
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Store DHCPDISCOVER for rapid commit proxy");
+		wpabuf_free(sta->hlp_dhcp_discover);
+		sta->hlp_dhcp_discover = dhcp_buf;
+	} else {
+		wpabuf_free(dhcp_buf);
+	}
+
+	return 1;
+}
+
+
+static int fils_process_hlp_udp(struct hostapd_data *hapd,
+				struct sta_info *sta, const u8 *dst,
+				const u8 *pos, size_t len)
+{
+	const struct iphdr *iph;
+	const struct udphdr *udph;
+	u16 sport, dport, ulen;
+
+	if (len < sizeof(*iph) + sizeof(*udph))
+		return 0;
+	iph = (const struct iphdr *) pos;
+	udph = (const struct udphdr *) (iph + 1);
+	sport = ntohs(udph->uh_sport);
+	dport = ntohs(udph->uh_dport);
+	ulen = ntohs(udph->uh_ulen);
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP request UDP: sport=%u dport=%u ulen=%u sum=0x%x",
+		   sport, dport, ulen, ntohs(udph->uh_sum));
+	/* TODO: Check UDP checksum */
+	if (ulen < sizeof(*udph) || ulen > len - sizeof(*iph))
+		return 0;
+
+	if (dport == DHCP_SERVER_PORT && sport == DHCP_CLIENT_PORT) {
+		return fils_process_hlp_dhcp(hapd, sta, (const u8 *) (udph + 1),
+					     ulen - sizeof(*udph));
+	}
+
+	return 0;
+}
+
+
+static int fils_process_hlp_ip(struct hostapd_data *hapd,
+			       struct sta_info *sta, const u8 *dst,
+			       const u8 *pos, size_t len)
+{
+	const struct iphdr *iph;
+	u16 tot_len;
+
+	if (len < sizeof(*iph))
+		return 0;
+	iph = (const struct iphdr *) pos;
+	if (ip_checksum(iph, sizeof(*iph)) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: HLP request IPv4 packet had invalid header checksum - dropped");
+		return 0;
+	}
+	tot_len = ntohs(iph->tot_len);
+	if (tot_len > len)
+		return 0;
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP request IPv4: saddr=%08x daddr=%08x protocol=%u",
+		   iph->saddr, iph->daddr, iph->protocol);
+	switch (iph->protocol) {
+	case 17:
+		return fils_process_hlp_udp(hapd, sta, dst, pos, len);
+	}
+
+	return 0;
+}
+
+
+static int fils_process_hlp_req(struct hostapd_data *hapd,
+				struct sta_info *sta,
+				const u8 *pos, size_t len)
+{
+	const u8 *pkt, *end;
+
+	wpa_printf(MSG_DEBUG, "FILS: HLP request from " MACSTR " (dst=" MACSTR
+		   " src=" MACSTR " len=%u)",
+		   MAC2STR(sta->addr), MAC2STR(pos), MAC2STR(pos + ETH_ALEN),
+		   (unsigned int) len);
+	if (os_memcmp(sta->addr, pos + ETH_ALEN, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Ignore HLP request with unexpected source address"
+			   MACSTR, MAC2STR(pos + ETH_ALEN));
+		return 0;
+	}
+
+	end = pos + len;
+	pkt = pos + 2 * ETH_ALEN;
+	if (end - pkt >= 6 &&
+	    os_memcmp(pkt, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
+		pkt += 6; /* Remove SNAP/LLC header */
+	wpa_hexdump(MSG_MSGDUMP, "FILS: HLP request packet", pkt, end - pkt);
+
+	if (end - pkt < 2)
+		return 0;
+
+	switch (WPA_GET_BE16(pkt)) {
+	case ETH_P_IP:
+		return fils_process_hlp_ip(hapd, sta, pos, pkt + 2,
+					   end - pkt - 2);
+	}
+
+	return 0;
+}
+
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+		     const u8 *pos, int left)
+{
+	const u8 *end = pos + left;
+	u8 *tmp, *tmp_pos;
+	int ret = 0;
+
+	/* Old DHCPDISCOVER is not needed anymore, if it was still pending */
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+	sta->fils_dhcp_rapid_commit_proxy = 0;
+
+	/* Check if there are any FILS HLP Container elements */
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos)
+			return 0;
+		if (pos[0] == WLAN_EID_EXTENSION &&
+		    pos[1] >= 1 + 2 * ETH_ALEN &&
+		    pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		pos += 2 + pos[1];
+	}
+	if (end - pos < 2)
+		return 0; /* No FILS HLP Container elements */
+
+	tmp = os_malloc(end - pos);
+	if (!tmp)
+		return 0;
+
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos ||
+		    pos[0] != WLAN_EID_EXTENSION ||
+		    pos[1] < 1 + 2 * ETH_ALEN ||
+		    pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		tmp_pos = tmp;
+		os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
+		tmp_pos += pos[1] - 1;
+		pos += 2 + pos[1];
+
+		/* Add possible fragments */
+		while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
+		       2 + pos[1] <= end - pos) {
+			os_memcpy(tmp_pos, pos + 2, pos[1]);
+			tmp_pos += pos[1];
+			pos += 2 + pos[1];
+		}
+
+		if (fils_process_hlp_req(hapd, sta, tmp, tmp_pos - tmp) > 0)
+			ret = 1;
+	}
+
+	os_free(tmp);
+
+	return ret;
+}
+
+
+void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+	if (hapd->dhcp_sock >= 0) {
+		eloop_unregister_read_sock(hapd->dhcp_sock);
+		close(hapd->dhcp_sock);
+		hapd->dhcp_sock = -1;
+	}
+}
diff --git a/src/ap/fils_hlp.h b/src/ap/fils_hlp.h
new file mode 100644
index 0000000..e14a6bf
--- /dev/null
+++ b/src/ap/fils_hlp.h
@@ -0,0 +1,27 @@
+/*
+ * FILS HLP request processing
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FILS_HLP_H
+#define FILS_HLP_H
+
+int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
+		     const u8 *pos, int left);
+
+#ifdef CONFIG_FILS
+
+void fils_hlp_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_FILS */
+
+static inline void fils_hlp_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_FILS */
+
+#endif /* FILS_HLP_H */
diff --git a/src/ap/gas_serv.c b/src/ap/gas_serv.c
index 179dc7a..96cd703 100644
--- a/src/ap/gas_serv.c
+++ b/src/ap/gas_serv.c
@@ -50,9 +50,12 @@
 		sta->flags |= WLAN_STA_GAS;
 		/*
 		 * The default inactivity is 300 seconds. We don't need
-		 * it to be that long.
+		 * it to be that long. Use five second timeout and increase this
+		 * with the comeback_delay for testing cases.
 		 */
-		ap_sta_session_timeout(hapd, sta, 5);
+		ap_sta_session_timeout(hapd, sta,
+				       hapd->conf->gas_comeback_delay / 1024 +
+				       5);
 	} else {
 		ap_sta_replenish_timeout(hapd, sta, 5);
 	}
@@ -255,20 +258,29 @@
 		wpabuf_put_le16(buf, ANQP_DOMAIN_NAME);
 	if (get_anqp_elem(hapd, ANQP_EMERGENCY_ALERT_URI))
 		wpabuf_put_le16(buf, ANQP_EMERGENCY_ALERT_URI);
+	if (get_anqp_elem(hapd, ANQP_TDLS_CAPABILITY))
+		wpabuf_put_le16(buf, ANQP_TDLS_CAPABILITY);
 	if (get_anqp_elem(hapd, ANQP_EMERGENCY_NAI))
 		wpabuf_put_le16(buf, ANQP_EMERGENCY_NAI);
 	if (get_anqp_elem(hapd, ANQP_NEIGHBOR_REPORT))
 		wpabuf_put_le16(buf, ANQP_NEIGHBOR_REPORT);
-	for (id = 273; id < 277; id++) {
-		if (get_anqp_elem(hapd, id))
-			wpabuf_put_le16(buf, id);
-	}
+#ifdef CONFIG_FILS
+	if (!dl_list_empty(&hapd->conf->fils_realms) ||
+	    get_anqp_elem(hapd, ANQP_FILS_REALM_INFO))
+		wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+#endif /* CONFIG_FILS */
+	if (get_anqp_elem(hapd, ANQP_CAG))
+		wpabuf_put_le16(buf, ANQP_CAG);
 	if (get_anqp_elem(hapd, ANQP_VENUE_URL))
 		wpabuf_put_le16(buf, ANQP_VENUE_URL);
 	if (get_anqp_elem(hapd, ANQP_ADVICE_OF_CHARGE))
 		wpabuf_put_le16(buf, ANQP_ADVICE_OF_CHARGE);
 	if (get_anqp_elem(hapd, ANQP_LOCAL_CONTENT))
 		wpabuf_put_le16(buf, ANQP_LOCAL_CONTENT);
+	for (id = 280; id < 300; id++) {
+		if (get_anqp_elem(hapd, id))
+			wpabuf_put_le16(buf, id);
+	}
 #ifdef CONFIG_HS20
 	anqp_add_hs_capab_list(hapd, buf);
 #endif /* CONFIG_HS20 */
@@ -548,6 +560,36 @@
 }
 
 
+#ifdef CONFIG_FILS
+static void anqp_add_fils_realm_info(struct hostapd_data *hapd,
+				     struct wpabuf *buf)
+{
+	size_t count;
+
+	if (anqp_add_override(hapd, buf, ANQP_FILS_REALM_INFO))
+		return;
+
+	count = dl_list_len(&hapd->conf->fils_realms);
+	if (count > 10000)
+		count = 10000;
+	if (count) {
+		struct fils_realm *realm;
+
+		wpabuf_put_le16(buf, ANQP_FILS_REALM_INFO);
+		wpabuf_put_le16(buf, 2 * count);
+
+		dl_list_for_each(realm, &hapd->conf->fils_realms,
+				 struct fils_realm, list) {
+			if (count == 0)
+				break;
+			wpabuf_put_data(buf, realm->hash, 2);
+			count--;
+		}
+	}
+}
+#endif /* CONFIG_FILS */
+
+
 #ifdef CONFIG_HS20
 
 static void anqp_add_operator_friendly_name(struct hostapd_data *hapd,
@@ -649,7 +691,7 @@
 
 	/* OSU Method List */
 	count = wpabuf_put(buf, 1);
-	for (i = 0; p->method_list[i] >= 0; i++)
+	for (i = 0; p->method_list && p->method_list[i] >= 0; i++)
 		wpabuf_put_u8(buf, p->method_list[i]);
 	*count = i;
 
@@ -821,6 +863,10 @@
 		len += 1000;
 	if (request & ANQP_REQ_ICON_REQUEST)
 		len += 65536;
+#ifdef CONFIG_FILS
+	if (request & ANQP_FILS_REALM_INFO)
+		len += 2 * dl_list_len(&hapd->conf->fils_realms);
+#endif /* CONFIG_FILS */
 	len += anqp_get_required_len(hapd, extra_req, num_extra_req);
 
 	buf = wpabuf_alloc(len);
@@ -860,8 +906,15 @@
 	if (request & ANQP_REQ_EMERGENCY_NAI)
 		anqp_add_elem(hapd, buf, ANQP_EMERGENCY_NAI);
 
-	for (i = 0; i < num_extra_req; i++)
+	for (i = 0; i < num_extra_req; i++) {
+#ifdef CONFIG_FILS
+		if (extra_req[i] == ANQP_FILS_REALM_INFO) {
+			anqp_add_fils_realm_info(hapd, buf);
+			continue;
+		}
+#endif /* CONFIG_FILS */
 		anqp_add_elem(hapd, buf, extra_req[i]);
+	}
 
 #ifdef CONFIG_HS20
 	if (request & ANQP_REQ_HS_CAPABILITY_LIST)
@@ -984,6 +1037,13 @@
 			     get_anqp_elem(hapd, info_id) != NULL, qi);
 		break;
 	default:
+#ifdef CONFIG_FILS
+		if (info_id == ANQP_FILS_REALM_INFO &&
+		    !dl_list_empty(&hapd->conf->fils_realms)) {
+			wpa_printf(MSG_DEBUG,
+				   "ANQP: FILS Realm Information (local)");
+		} else
+#endif /* CONFIG_FILS */
 		if (!get_anqp_elem(hapd, info_id)) {
 			wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
 				   info_id);
@@ -1166,7 +1226,8 @@
 
 static void gas_serv_req_local_processing(struct hostapd_data *hapd,
 					  const u8 *sa, u8 dialog_token,
-					  struct anqp_query_info *qi, int prot)
+					  struct anqp_query_info *qi, int prot,
+					  int std_addr3)
 {
 	struct wpabuf *buf, *tx_buf;
 
@@ -1188,7 +1249,7 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (wpabuf_len(buf) > hapd->gas_frag_limit ||
+	if (wpabuf_len(buf) > hapd->conf->gas_frag_limit ||
 	    hapd->conf->gas_comeback_delay) {
 		struct gas_dialog_info *di;
 		u16 comeback_delay = 1;
@@ -1227,15 +1288,22 @@
 		return;
 	if (prot)
 		convert_to_protected_dual(tx_buf);
-	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	if (std_addr3)
+		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+					wpabuf_head(tx_buf),
+					wpabuf_len(tx_buf));
+	else
+		hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+						 wpabuf_head(tx_buf),
+						 wpabuf_len(tx_buf));
 	wpabuf_free(tx_buf);
 }
 
 
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
 					const u8 *sa,
-					const u8 *data, size_t len, int prot)
+					const u8 *data, size_t len, int prot,
+					int std_addr3)
 {
 	const u8 *pos = data;
 	const u8 *end = data + len;
@@ -1287,8 +1355,15 @@
 		wpabuf_put_le16(buf, 0); /* Query Response Length */
 		if (prot)
 			convert_to_protected_dual(buf);
-		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-					wpabuf_head(buf), wpabuf_len(buf));
+		if (std_addr3)
+			hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+						wpabuf_head(buf),
+						wpabuf_len(buf));
+		else
+			hostapd_drv_send_action_addr3_ap(hapd,
+							 hapd->iface->freq, 0,
+							 sa, wpabuf_head(buf),
+							 wpabuf_len(buf));
 		wpabuf_free(buf);
 		return;
 	}
@@ -1338,13 +1413,15 @@
 		pos += elen;
 	}
 
-	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
+	gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot,
+				      std_addr3);
 }
 
 
 static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
 					 const u8 *sa,
-					 const u8 *data, size_t len, int prot)
+					 const u8 *data, size_t len, int prot,
+					 int std_addr3)
 {
 	struct gas_dialog_info *dialog;
 	struct wpabuf *buf, *tx_buf;
@@ -1376,8 +1453,8 @@
 	}
 
 	frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
-	if (frag_len > hapd->gas_frag_limit) {
-		frag_len = hapd->gas_frag_limit;
+	if (frag_len > hapd->conf->gas_frag_limit) {
+		frag_len = hapd->conf->gas_frag_limit;
 		more = 1;
 	}
 	wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: resp frag_len %u",
@@ -1420,8 +1497,14 @@
 send_resp:
 	if (prot)
 		convert_to_protected_dual(tx_buf);
-	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
-				wpabuf_head(tx_buf), wpabuf_len(tx_buf));
+	if (std_addr3)
+		hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
+					wpabuf_head(tx_buf),
+					wpabuf_len(tx_buf));
+	else
+		hostapd_drv_send_action_addr3_ap(hapd, hapd->iface->freq, 0, sa,
+						 wpabuf_head(tx_buf),
+						 wpabuf_len(tx_buf));
 	wpabuf_free(tx_buf);
 }
 
@@ -1432,7 +1515,7 @@
 	struct hostapd_data *hapd = ctx;
 	const struct ieee80211_mgmt *mgmt;
 	const u8 *sa, *data;
-	int prot;
+	int prot, std_addr3;
 
 	mgmt = (const struct ieee80211_mgmt *) buf;
 	if (len < IEEE80211_HDRLEN + 2)
@@ -1447,14 +1530,22 @@
 	 */
 	prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
 	sa = mgmt->sa;
+	if (hapd->conf->gas_address3 == 1)
+		std_addr3 = 1;
+	else if (hapd->conf->gas_address3 == 2)
+		std_addr3 = 0;
+	else
+		std_addr3 = is_broadcast_ether_addr(mgmt->bssid);
 	len -= IEEE80211_HDRLEN + 1;
 	data = buf + IEEE80211_HDRLEN + 1;
 	switch (data[0]) {
 	case WLAN_PA_GAS_INITIAL_REQ:
-		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
+		gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot,
+					    std_addr3);
 		break;
 	case WLAN_PA_GAS_COMEBACK_REQ:
-		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
+		gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot,
+					     std_addr3);
 		break;
 	}
 }
@@ -1464,9 +1555,6 @@
 {
 	hapd->public_action_cb2 = gas_serv_rx_public_action;
 	hapd->public_action_cb2_ctx = hapd;
-	hapd->gas_frag_limit = 1400;
-	if (hapd->conf->gas_frag_limit > 0)
-		hapd->gas_frag_limit = hapd->conf->gas_frag_limit;
 	return 0;
 }
 
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index ee80f4f..cf8a8cb 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -43,6 +43,9 @@
 #include "x_snoop.h"
 #include "dhcp_snoop.h"
 #include "ndisc_snoop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
+#include "fils_hlp.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
@@ -73,6 +76,9 @@
 {
 	struct hostapd_ssid *ssid;
 
+	if (!hapd->started)
+		return;
+
 #ifndef CONFIG_NO_RADIUS
 	radius_client_reconfig(hapd->radius, hapd->conf->radius);
 #endif /* CONFIG_NO_RADIUS */
@@ -204,10 +210,12 @@
 
 
 static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
-					      char *ifname)
+					      const char *ifname)
 {
 	int i;
 
+	if (!ifname || !hapd->drv_priv)
+		return;
 	for (i = 0; i < NUM_WEP_KEYS; i++) {
 		if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
 					0, NULL, 0, NULL, 0)) {
@@ -335,6 +343,9 @@
 	wpabuf_free(hapd->mesh_pending_auth);
 	hapd->mesh_pending_auth = NULL;
 #endif /* CONFIG_MESH */
+
+	hostapd_clean_rrm(hapd);
+	fils_hlp_deinit(hapd);
 }
 
 
@@ -351,8 +362,10 @@
 	wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
 		   hapd->conf->iface);
 	if (hapd->iface->interfaces &&
-	    hapd->iface->interfaces->ctrl_iface_deinit)
+	    hapd->iface->interfaces->ctrl_iface_deinit) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
 		hapd->iface->interfaces->ctrl_iface_deinit(hapd);
+	}
 	hostapd_free_hapd_data(hapd);
 }
 
@@ -368,7 +381,7 @@
 				     list))) {
 		dl_list_del(&info->list);
 		iface->num_sta_seen--;
-		os_free(info);
+		sta_track_del(info);
 	}
 }
 
@@ -950,10 +963,10 @@
 	if (conf->wmm_enabled < 0)
 		conf->wmm_enabled = hapd->iconf->ieee80211n;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (is_zero_ether_addr(conf->r1_key_holder))
 		os_memcpy(conf->r1_key_holder, hapd->own_addr, ETH_ALEN);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_MESH
 	if (hapd->iface->mconf == NULL)
@@ -1037,6 +1050,8 @@
 		das_conf.time_window = conf->radius_das_time_window;
 		das_conf.require_event_timestamp =
 			conf->radius_das_require_event_timestamp;
+		das_conf.require_message_authenticator =
+			conf->radius_das_require_message_authenticator;
 		das_conf.ctx = hapd;
 		das_conf.disconnect = hostapd_das_disconnect;
 		hapd->radius_das = radius_das_init(&das_conf);
@@ -1524,6 +1539,126 @@
 #endif /* CONFIG_FST */
 
 
+#ifdef NEED_AP_MLME
+static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+						    int ht, int vht)
+{
+	if (!ht && !vht)
+		return NR_CHAN_WIDTH_20;
+	if (!hapd->iconf->secondary_channel)
+		return NR_CHAN_WIDTH_20;
+	if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+		return NR_CHAN_WIDTH_40;
+	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+		return NR_CHAN_WIDTH_80;
+	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
+		return NR_CHAN_WIDTH_160;
+	if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
+		return NR_CHAN_WIDTH_80P80;
+	return NR_CHAN_WIDTH_20;
+}
+#endif /* NEED_AP_MLME */
+
+
+static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
+{
+#ifdef NEED_AP_MLME
+	u16 capab = hostapd_own_capab_info(hapd);
+	int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+	int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+	struct wpa_ssid_value ssid;
+	u8 channel, op_class;
+	u8 center_freq1_idx = 0, center_freq2_idx = 0;
+	enum nr_chan_width width;
+	u32 bssid_info;
+	struct wpabuf *nr;
+
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+		return;
+
+	bssid_info = 3; /* AP is reachable */
+	bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
+	bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
+
+	if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
+		bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+
+	bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
+
+	if (hapd->conf->wmm_enabled) {
+		bssid_info |= NEI_REP_BSSID_INFO_QOS;
+
+		if (hapd->conf->wmm_uapsd &&
+		    (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+			bssid_info |= NEI_REP_BSSID_INFO_APSD;
+	}
+
+	if (ht) {
+		bssid_info |= NEI_REP_BSSID_INFO_HT |
+			NEI_REP_BSSID_INFO_DELAYED_BA;
+
+		/* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+		if (vht)
+			bssid_info |= NEI_REP_BSSID_INFO_VHT;
+	}
+
+	/* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+
+	if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+					  hapd->iconf->secondary_channel,
+					  hapd->iconf->vht_oper_chwidth,
+					  &op_class, &channel) ==
+	    NUM_HOSTAPD_MODES)
+		return;
+	width = hostapd_get_nr_chan_width(hapd, ht, vht);
+	if (vht) {
+		center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+		if (width == NR_CHAN_WIDTH_80P80)
+			center_freq2_idx =
+				hapd->iconf->vht_oper_centr_freq_seg1_idx;
+	} else if (ht) {
+		ieee80211_freq_to_chan(hapd->iface->freq +
+				       10 * hapd->iconf->secondary_channel,
+				       &center_freq1_idx);
+	}
+
+	ssid.ssid_len = hapd->conf->ssid.ssid_len;
+	os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+
+	/*
+	 * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
+	 * phy type + wide bandwidth channel subelement.
+	 */
+	nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
+	if (!nr)
+		return;
+
+	wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
+	wpabuf_put_le32(nr, bssid_info);
+	wpabuf_put_u8(nr, op_class);
+	wpabuf_put_u8(nr, channel);
+	wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+
+	/*
+	 * Wide Bandwidth Channel subelement may be needed to allow the
+	 * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
+	 * Figure 9-301.
+	 */
+	wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
+	wpabuf_put_u8(nr, 3);
+	wpabuf_put_u8(nr, width);
+	wpabuf_put_u8(nr, center_freq1_idx);
+	wpabuf_put_u8(nr, center_freq2_idx);
+
+	hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
+			     hapd->iconf->civic, hapd->iconf->stationary_ap);
+
+	wpabuf_free(nr);
+#endif /* NEED_AP_MLME */
+}
+
+
 static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
 						 int err)
 {
@@ -1649,7 +1784,6 @@
 	hostapd_tx_queue_params(iface);
 
 	ap_list_init(iface);
-	dl_list_init(&iface->sta_seen);
 
 	hostapd_set_acl(hapd);
 
@@ -1709,6 +1843,9 @@
 	if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
 		iface->interfaces->terminate_on_error--;
 
+	for (j = 0; j < iface->num_bss; j++)
+		hostapd_set_own_neighbor_report(iface->bss[j]);
+
 	return 0;
 
 fail:
@@ -1867,9 +2004,12 @@
 	hapd->iconf = conf;
 	hapd->conf = bss;
 	hapd->iface = hapd_iface;
-	hapd->driver = hapd->iconf->driver;
+	if (conf)
+		hapd->driver = conf->driver;
 	hapd->ctrl_sock = -1;
 	dl_list_init(&hapd->ctrl_dst);
+	dl_list_init(&hapd->nr_db);
+	hapd->dhcp_sock = -1;
 
 	return hapd;
 }
@@ -1877,6 +2017,8 @@
 
 static void hostapd_bss_deinit(struct hostapd_data *hapd)
 {
+	if (!hapd)
+		return;
 	wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
 		   hapd->conf->iface);
 	hostapd_bss_deinit_no_free(hapd);
@@ -1911,8 +2053,11 @@
 	}
 #endif /* CONFIG_FST */
 
-	for (j = iface->num_bss - 1; j >= 0; j--)
+	for (j = iface->num_bss - 1; j >= 0; j--) {
+		if (!iface->bss)
+			break;
 		hostapd_bss_deinit(iface->bss[j]);
+	}
 }
 
 
@@ -1921,6 +2066,8 @@
 	size_t j;
 	wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
 	for (j = 0; j < iface->num_bss; j++) {
+		if (!iface->bss)
+			break;
 		wpa_printf(MSG_DEBUG, "%s: free hapd %p",
 			   __func__, iface->bss[j]);
 		os_free(iface->bss[j]);
@@ -1929,6 +2076,20 @@
 }
 
 
+struct hostapd_iface * hostapd_alloc_iface(void)
+{
+	struct hostapd_iface *hapd_iface;
+
+	hapd_iface = os_zalloc(sizeof(*hapd_iface));
+	if (!hapd_iface)
+		return NULL;
+
+	dl_list_init(&hapd_iface->sta_seen);
+
+	return hapd_iface;
+}
+
+
 /**
  * hostapd_init - Allocate and initialize per-interface data
  * @config_file: Path to the configuration file
@@ -1946,7 +2107,7 @@
 	struct hostapd_data *hapd;
 	size_t i;
 
-	hapd_iface = os_zalloc(sizeof(*hapd_iface));
+	hapd_iface = hostapd_alloc_iface();
 	if (hapd_iface == NULL)
 		goto fail;
 
@@ -2282,7 +2443,7 @@
 		return NULL;
 	interfaces->iface = iface;
 	hapd_iface = interfaces->iface[interfaces->count] =
-		os_zalloc(sizeof(*hapd_iface));
+		hostapd_alloc_iface();
 	if (hapd_iface == NULL) {
 		wpa_printf(MSG_ERROR, "%s: Failed to allocate memory for "
 			   "the interface", __func__);
@@ -2682,7 +2843,16 @@
 	} else
 		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
 
-	if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
+		if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
+			wpa_printf(MSG_DEBUG,
+				   "%s: %s: canceled wired ap_handle_timer timeout for "
+				   MACSTR,
+				   hapd->conf->iface, __func__,
+				   MAC2STR(sta->addr));
+		}
+	} else if (!(hapd->iface->drv_flags &
+		     WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
 		wpa_printf(MSG_DEBUG,
 			   "%s: %s: reschedule ap_handle_timer timeout for "
 			   MACSTR " (%d seconds - ap_max_inactivity)",
@@ -2721,8 +2891,8 @@
 void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
 {
 	wpa_printf(MSG_INFO, "%s: interface state %s->%s",
-		   iface->conf->bss[0]->iface, hostapd_state_text(iface->state),
-		   hostapd_state_text(s));
+		   iface->conf ? iface->conf->bss[0]->iface : "N/A",
+		   hostapd_state_text(iface->state), hostapd_state_text(s));
 	iface->state = s;
 }
 
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 75a7c04..5ab623d 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -53,6 +53,7 @@
 #ifndef CONFIG_NO_VLAN
 	struct dynamic_iface *vlan_priv;
 #endif /* CONFIG_NO_VLAN */
+	int eloop_initialized;
 };
 
 enum hostapd_chan_status {
@@ -99,6 +100,17 @@
 	u8 peer_addr[ETH_ALEN];
 };
 
+struct hostapd_neighbor_entry {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	struct wpa_ssid_value ssid;
+	struct wpabuf *nr;
+	struct wpabuf *lci;
+	struct wpabuf *civic;
+	/* LCI update time */
+	struct os_time lci_date;
+	int stationary;
+};
 
 /**
  * struct hostapd_data - hostapd per-BSS data structure
@@ -248,9 +260,6 @@
 	int noa_start;
 	int noa_duration;
 #endif /* CONFIG_P2P */
-#ifdef CONFIG_INTERWORKING
-	size_t gas_frag_limit;
-#endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_PROXYARP
 	struct l2_packet_data *sock_dhcp;
 	struct l2_packet_data *sock_ndisc;
@@ -286,6 +295,16 @@
 #ifdef CONFIG_MBO
 	unsigned int mbo_assoc_disallow;
 #endif /* CONFIG_MBO */
+
+	struct dl_list nr_db;
+
+	u8 beacon_req_token;
+	u8 lci_req_token;
+	u8 range_req_token;
+	unsigned int lci_req_active:1;
+	unsigned int range_req_active:1;
+
+	int dhcp_sock; /* UDP socket used with the DHCP server */
 };
 
 
@@ -293,6 +312,10 @@
 	struct dl_list list;
 	u8 addr[ETH_ALEN];
 	struct os_reltime last_seen;
+	int ssi_signal;
+#ifdef CONFIG_TAXONOMY
+	struct wpabuf *probe_ie_taxonomy;
+#endif /* CONFIG_TAXONOMY */
 };
 
 /**
@@ -453,6 +476,7 @@
 int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
 void hostapd_interface_deinit(struct hostapd_iface *iface);
 void hostapd_interface_free(struct hostapd_iface *iface);
+struct hostapd_iface * hostapd_alloc_iface(void);
 struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
 				    const char *config_file);
 struct hostapd_iface *
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 16887ac..2d6cef1 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -329,6 +329,8 @@
 	res = ieee80211n_allowed_ht40_channel_pair(iface);
 	if (!res) {
 		iface->conf->secondary_channel = 0;
+		iface->conf->vht_oper_centr_freq_seg0_idx = 0;
+		iface->conf->vht_oper_centr_freq_seg1_idx = 0;
 		res = 1;
 		wpa_printf(MSG_INFO, "Fallback to 20 MHz");
 	}
@@ -621,41 +623,6 @@
 
 
 #ifdef CONFIG_IEEE80211AC
-
-static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name)
-{
-	u32 req_cap = conf & cap;
-
-	/*
-	 * Make sure we support all requested capabilities.
-	 * NOTE: We assume that 'cap' represents a capability mask,
-	 * not a discrete value.
-	 */
-	if ((hw & req_cap) != req_cap) {
-		wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]",
-			   name);
-		return 0;
-	}
-	return 1;
-}
-
-
-static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
-				     unsigned int shift,
-				     const char *name)
-{
-	u32 hw_max = hw & mask;
-	u32 conf_val = conf & mask;
-
-	if (conf_val > hw_max) {
-		wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
-			   name, conf_val >> shift, hw_max >> shift);
-		return 0;
-	}
-	return 1;
-}
-
-
 static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
 {
 	struct hostapd_hw_modes *mode = iface->current_mode;
@@ -683,45 +650,7 @@
 		}
 	}
 
-#define VHT_CAP_CHECK(cap) \
-	do { \
-		if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
-			return 0; \
-	} while (0)
-
-#define VHT_CAP_CHECK_MAX(cap) \
-	do { \
-		if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
-					       #cap)) \
-			return 0; \
-	} while (0)
-
-	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
-	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
-	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
-	VHT_CAP_CHECK(VHT_CAP_RXLDPC);
-	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
-	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
-	VHT_CAP_CHECK(VHT_CAP_TXSTBC);
-	VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
-	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
-	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
-	VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
-	VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
-	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
-	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
-	VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
-	VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
-	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
-	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
-	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
-	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
-	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
-
-#undef VHT_CAP_CHECK
-#undef VHT_CAP_CHECK_MAX
-
-	return 1;
+	return ieee80211ac_cap_check(hw, conf);
 }
 #endif /* CONFIG_IEEE80211AC */
 
@@ -785,6 +714,8 @@
 			   chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
 	}
 
+	wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode",
+		   channel, primary ? "primary" : "secondary");
 	return 0;
 }
 
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
index 07672ce..2556da3 100644
--- a/src/ap/iapp.c
+++ b/src/ap/iapp.c
@@ -381,6 +381,7 @@
 	struct sockaddr_in *paddr, uaddr;
 	struct iapp_data *iapp;
 	struct ip_mreqn mreq;
+	int reuseaddr = 1;
 
 	iapp = os_zalloc(sizeof(*iapp));
 	if (iapp == NULL)
@@ -443,6 +444,18 @@
 	os_memset(&uaddr, 0, sizeof(uaddr));
 	uaddr.sin_family = AF_INET;
 	uaddr.sin_port = htons(IAPP_UDP_PORT);
+
+	if (setsockopt(iapp->udp_sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
+		       sizeof(reuseaddr)) < 0) {
+		wpa_printf(MSG_INFO,
+			   "iapp_init - setsockopt[UDP,SO_REUSEADDR]: %s",
+			   strerror(errno));
+		/*
+		 * Ignore this and try to continue. This is fine for single
+		 * BSS cases, but may fail if multiple BSSes enable IAPP.
+		 */
+	}
+
 	if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
 		 sizeof(uaddr)) < 0) {
 		wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 45ecbfb..c6234dc 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -43,6 +43,9 @@
 #include "ieee802_11.h"
 #include "dfs.h"
 #include "mbo_ap.h"
+#include "rrm.h"
+#include "taxonomy.h"
+#include "fils_hlp.h"
 
 
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -140,6 +143,7 @@
 	int capab = WLAN_CAPABILITY_ESS;
 	int privacy;
 	int dfs;
+	int i;
 
 	/* Check if any of configured channels require DFS */
 	dfs = hostapd_is_dfs_required(hapd->iface);
@@ -187,8 +191,12 @@
 	    (hapd->iconf->spectrum_mgmt_required || dfs))
 		capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 
-	if (hapd->conf->radio_measurements)
-		capab |= IEEE80211_CAP_RRM;
+	for (i = 0; i < RRM_CAPABILITIES_IE_LEN; i++) {
+		if (hapd->conf->radio_measurements[i]) {
+			capab |= IEEE80211_CAP_RRM;
+			break;
+		}
+	}
 
 	return capab;
 }
@@ -296,7 +304,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
 				  u16 auth_transaction, u16 status,
 				  const u8 *ies, size_t ies_len)
@@ -327,7 +335,7 @@
 	sta->flags |= WLAN_STA_AUTH;
 	mlme_authenticate_indication(hapd, sta);
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 #ifdef CONFIG_SAE
@@ -512,6 +520,9 @@
 	if (sae_check_big_sync(sta))
 		return;
 	sta->sae->sync++;
+	wpa_printf(MSG_DEBUG, "SAE: Auth SAE retransmit timer for " MACSTR
+		   " (sync=%d state=%d)",
+		   MAC2STR(sta->addr), sta->sae->sync, sta->sae->state);
 
 	switch (sta->sae->state) {
 	case SAE_COMMITTED:
@@ -609,7 +620,7 @@
 				 * message now to get alternating sequence of
 				 * Authentication frames between the AP and STA.
 				 * Confirm will be sent in
-				 * Commited -> Confirmed/Accepted transition
+				 * Committed -> Confirmed/Accepted transition
 				 * when receiving Confirm from STA.
 				 */
 			}
@@ -718,6 +729,44 @@
 }
 
 
+static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	struct sae_data *sae = sta->sae;
+	int i, *groups = hapd->conf->sae_groups;
+
+	if (sae->state != SAE_COMMITTED)
+		return;
+
+	wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
+
+	for (i = 0; groups && groups[i] > 0; i++) {
+		if (sae->group == groups[i])
+			break;
+	}
+
+	if (!groups || groups[i] <= 0) {
+		wpa_printf(MSG_DEBUG,
+			   "SAE: Previously selected group not found from the current configuration");
+		return;
+	}
+
+	for (;;) {
+		i++;
+		if (groups[i] <= 0) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: No alternative group enabled");
+			return;
+		}
+
+		if (sae_set_group(sae, groups[i]) < 0)
+			continue;
+
+		break;
+	}
+	wpa_printf(MSG_DEBUG, "SAE: Selected new group: %d", groups[i]);
+}
+
+
 static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
 			    const struct ieee80211_mgmt *mgmt, size_t len,
 			    u16 auth_transaction, u16 status_code)
@@ -805,6 +854,16 @@
 			return;
 		}
 
+		if ((hapd->conf->mesh & MESH_ENABLED) &&
+		    status_code ==
+		    WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
+		    sta->sae->tmp) {
+			wpa_printf(MSG_DEBUG,
+				   "SAE: Peer did not accept our SAE group");
+			sae_pick_next_group(hapd, sta);
+			goto remove_sta;
+		}
+
 		if (status_code != WLAN_STATUS_SUCCESS)
 			goto remove_sta;
 
@@ -922,6 +981,410 @@
 #endif /* CONFIG_SAE */
 
 
+static u16 wpa_res_to_status_code(int res)
+{
+	if (res == WPA_INVALID_GROUP)
+		return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
+	if (res == WPA_INVALID_PAIRWISE)
+		return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
+	if (res == WPA_INVALID_AKMP)
+		return WLAN_STATUS_AKMP_NOT_VALID;
+	if (res == WPA_ALLOC_FAIL)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+#ifdef CONFIG_IEEE80211W
+	if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+	if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+		return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+#endif /* CONFIG_IEEE80211W */
+	if (res == WPA_INVALID_MDIE)
+		return WLAN_STATUS_INVALID_MDIE;
+	if (res != WPA_IE_OK)
+		return WLAN_STATUS_INVALID_IE;
+	return WLAN_STATUS_SUCCESS;
+}
+
+
+#ifdef CONFIG_FILS
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct rsn_pmksa_cache_entry *pmksa,
+				    struct wpabuf *erp_resp,
+				    const u8 *msk, size_t msk_len);
+
+static void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
+			    const struct ieee80211_mgmt *mgmt, size_t len,
+			    u16 auth_transaction, u16 status_code)
+{
+	u16 resp = WLAN_STATUS_SUCCESS;
+	const u8 *pos, *end;
+	struct ieee802_11_elems elems;
+	int res;
+	struct wpa_ie_data rsn;
+	struct rsn_pmksa_cache_entry *pmksa = NULL;
+
+	if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
+		return;
+
+	pos = mgmt->u.auth.variable;
+	end = ((const u8 *) mgmt) + len;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+		    pos, end - pos);
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
+	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	/* RSNE */
+	wpa_hexdump(MSG_DEBUG, "FILS: RSN element",
+		    elems.rsn_ie, elems.rsn_ie_len);
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: No valid RSN element");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	if (!sta->wpa_sm)
+		sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr,
+						NULL);
+	if (!sta->wpa_sm) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to initialize RSN state machine");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+				  elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				  elems.mdie, elems.mdie_len);
+	resp = wpa_res_to_status_code(res);
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	/* TODO: MDE when using FILS+FT */
+	/* TODO: FTE when using FILS+FT */
+
+	if (!elems.fils_nonce) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", elems.fils_nonce,
+		    FILS_NONCE_LEN);
+	os_memcpy(sta->fils_snonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+	/* PMKID List */
+	if (rsn.pmkid && rsn.num_pmkid > 0) {
+		u8 num;
+		const u8 *pmkid;
+
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+			    rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+		pmkid = rsn.pmkid;
+		num = rsn.num_pmkid;
+		while (num) {
+			wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
+			pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr,
+						   pmkid);
+			if (pmksa)
+				break;
+			pmkid += PMKID_LEN;
+			num--;
+		}
+	}
+	if (pmksa && wpa_auth_sta_key_mgmt(sta->wpa_sm) != pmksa->akmp) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Matching PMKSA cache entry has different AKMP (0x%x != 0x%x) - ignore",
+			   wpa_auth_sta_key_mgmt(sta->wpa_sm), pmksa->akmp);
+		pmksa = NULL;
+	}
+	if (pmksa)
+		wpa_printf(MSG_DEBUG, "FILS: Found matching PMKSA cache entry");
+
+	/* FILS Session */
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+		    FILS_SESSION_LEN);
+	os_memcpy(sta->fils_session, elems.fils_session, FILS_SESSION_LEN);
+
+	/* FILS Wrapped Data */
+	if (elems.fils_wrapped_data) {
+		wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+			    elems.fils_wrapped_data,
+			    elems.fils_wrapped_data_len);
+		if (!pmksa) {
+#ifndef CONFIG_NO_RADIUS
+			if (!sta->eapol_sm) {
+				sta->eapol_sm =
+					ieee802_1x_alloc_eapol_sm(hapd, sta);
+			}
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Forward EAP-Identity/Re-auth Start to authentication server");
+			ieee802_1x_encapsulate_radius(
+				hapd, sta, elems.fils_wrapped_data,
+				elems.fils_wrapped_data_len);
+			wpa_printf(MSG_DEBUG,
+				   "FILS: Will send Authentication frame once the response from authentication server is available");
+			sta->flags |= WLAN_STA_PENDING_FILS_ERP;
+			return;
+#else /* CONFIG_NO_RADIUS */
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+#endif /* CONFIG_NO_RADIUS */
+		}
+	}
+
+fail:
+	handle_auth_fils_finish(hapd, sta, resp, pmksa, NULL, NULL, 0);
+}
+
+
+static void handle_auth_fils_finish(struct hostapd_data *hapd,
+				    struct sta_info *sta, u16 resp,
+				    struct rsn_pmksa_cache_entry *pmksa,
+				    struct wpabuf *erp_resp,
+				    const u8 *msk, size_t msk_len)
+{
+	u8 fils_nonce[FILS_NONCE_LEN];
+	size_t ielen;
+	struct wpabuf *data = NULL;
+	const u8 *ie;
+	u8 *ie_buf = NULL;
+	const u8 *pmk = NULL;
+	size_t pmk_len = 0;
+	u8 pmk_buf[PMK_LEN_MAX];
+
+	if (resp != WLAN_STATUS_SUCCESS)
+		goto fail;
+
+	ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen);
+	if (!ie) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	if (pmksa) {
+		/* Add PMKID of the selected PMKSA into RSNE */
+		ie_buf = os_malloc(ielen + 2 + 2 + PMKID_LEN);
+		if (!ie_buf) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		os_memcpy(ie_buf, ie, ielen);
+		if (wpa_insert_pmkid(ie_buf, &ielen, pmksa->pmkid) < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		ie = ie_buf;
+	}
+
+	if (random_get_bytes(fils_nonce, FILS_NONCE_LEN) < 0) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+	wpa_hexdump(MSG_DEBUG, "RSN: Generated FILS Nonce",
+		    fils_nonce, FILS_NONCE_LEN);
+
+	data = wpabuf_alloc(1000 + ielen);
+	if (!data) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto fail;
+	}
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	/* RSNE */
+	wpabuf_put_data(data, ie, ielen);
+
+	/* TODO: MDE when using FILS+FT */
+	/* TODO: FTE when using FILS+FT */
+
+	/* FILS Nonce */
+	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(data, 1 + FILS_NONCE_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(data, WLAN_EID_EXT_FILS_NONCE);
+	wpabuf_put_data(data, fils_nonce, FILS_NONCE_LEN);
+
+	/* FILS Session */
+	wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(data, 1 + FILS_SESSION_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(data, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(data, sta->fils_session, FILS_SESSION_LEN);
+
+	/* FILS Wrapped Data */
+	if (!pmksa && erp_resp) {
+		wpabuf_put_u8(data, WLAN_EID_EXTENSION); /* Element ID */
+		wpabuf_put_u8(data, 1 + wpabuf_len(erp_resp)); /* Length */
+		/* Element ID Extension */
+		wpabuf_put_u8(data, WLAN_EID_EXT_FILS_WRAPPED_DATA);
+		wpabuf_put_buf(data, erp_resp);
+
+		if (fils_rmsk_to_pmk(wpa_auth_sta_key_mgmt(sta->wpa_sm),
+				     msk, msk_len, sta->fils_snonce, fils_nonce,
+				     NULL, 0, pmk_buf, &pmk_len)) {
+			wpa_printf(MSG_DEBUG, "FILS: Failed to derive PMK");
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			wpabuf_free(data);
+			data = NULL;
+			goto fail;
+		}
+		pmk = pmk_buf;
+	} else if (pmksa) {
+		pmk = pmksa->pmk;
+		pmk_len = pmksa->pmk_len;
+	}
+
+	if (!pmk) {
+		wpa_printf(MSG_DEBUG, "FILS: No PMK available");
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		wpabuf_free(data);
+		data = NULL;
+		goto fail;
+	}
+
+	if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
+				 sta->fils_snonce, fils_nonce) < 0) {
+		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		wpabuf_free(data);
+		data = NULL;
+		goto fail;
+	}
+
+fail:
+	send_auth_reply(hapd, sta->addr, hapd->own_addr, WLAN_AUTH_FILS_SK, 2,
+			resp,
+			data ? wpabuf_head(data) : (u8 *) "",
+			data ? wpabuf_len(data) : 0);
+	wpabuf_free(data);
+
+	if (resp == WLAN_STATUS_SUCCESS) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "authentication OK (FILS)");
+		sta->flags |= WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+		sta->auth_alg = WLAN_AUTH_FILS_SK;
+		mlme_authenticate_indication(hapd, sta);
+	}
+
+	os_free(ie_buf);
+}
+
+
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+				 struct sta_info *sta, int success,
+				 struct wpabuf *erp_resp,
+				 const u8 *msk, size_t msk_len)
+{
+	sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
+	handle_auth_fils_finish(hapd, sta, success ? WLAN_STATUS_SUCCESS :
+				WLAN_STATUS_UNSPECIFIED_FAILURE, NULL,
+				erp_resp, msk, msk_len);
+}
+
+#endif /* CONFIG_FILS */
+
+
+static int
+ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
+			   const u8 *msg, size_t len, u32 *session_timeout,
+			   u32 *acct_interim_interval,
+			   struct vlan_description *vlan_id,
+			   struct hostapd_sta_wpa_psk_short **psk,
+			   char **identity, char **radius_cui)
+{
+	int res;
+
+	os_memset(vlan_id, 0, sizeof(*vlan_id));
+	res = hostapd_allowed_address(hapd, addr, msg, len,
+				      session_timeout, acct_interim_interval,
+				      vlan_id, psk, identity, radius_cui);
+
+	if (res == HOSTAPD_ACL_REJECT) {
+		wpa_printf(MSG_INFO,
+			   "Station " MACSTR " not allowed to authenticate",
+			   MAC2STR(addr));
+		return HOSTAPD_ACL_REJECT;
+	}
+
+	if (res == HOSTAPD_ACL_PENDING) {
+		wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
+			   " waiting for an external authentication",
+			   MAC2STR(addr));
+		/* Authentication code will re-send the authentication frame
+		 * after it has received (and cached) information from the
+		 * external source. */
+		return HOSTAPD_ACL_PENDING;
+	}
+
+	return res;
+}
+
+
+static int
+ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
+			   int res, u32 session_timeout,
+			   u32 acct_interim_interval,
+			   struct vlan_description *vlan_id,
+			   struct hostapd_sta_wpa_psk_short **psk,
+			   char **identity, char **radius_cui)
+{
+	if (vlan_id->notempty &&
+	    !hostapd_vlan_valid(hapd->conf->vlan, vlan_id)) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO,
+			       "Invalid VLAN %d%s received from RADIUS server",
+			       vlan_id->untagged,
+			       vlan_id->tagged[0] ? "+" : "");
+		return -1;
+	}
+	if (ap_sta_set_vlan(hapd, sta, vlan_id) < 0)
+		return -1;
+	if (sta->vlan_id)
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
+
+	hostapd_free_psk_list(sta->psk);
+	if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
+		sta->psk = *psk;
+		*psk = NULL;
+	} else {
+		sta->psk = NULL;
+	}
+
+	sta->identity = *identity;
+	*identity = NULL;
+	sta->radius_cui = *radius_cui;
+	*radius_cui = NULL;
+
+	if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
+		sta->acct_interim_interval = acct_interim_interval;
+	if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
+		ap_sta_session_timeout(hapd, sta, session_timeout);
+	else
+		ap_sta_no_session_timeout(hapd, sta);
+
+	return 0;
+}
+
+
 static void handle_auth(struct hostapd_data *hapd,
 			const struct ieee80211_mgmt *mgmt, size_t len)
 {
@@ -940,8 +1403,6 @@
 	char *radius_cui = NULL;
 	u16 seq_ctrl;
 
-	os_memset(&vlan_id, 0, sizeof(vlan_id));
-
 	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
 		wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
 			   (unsigned long) len);
@@ -995,14 +1456,18 @@
 
 	if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) &&
 	       auth_alg == WLAN_AUTH_OPEN) ||
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	      (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
 	       auth_alg == WLAN_AUTH_FT) ||
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
 	      (hapd->conf->wpa && wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
 	       auth_alg == WLAN_AUTH_SAE) ||
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	      (hapd->conf->wpa && wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt) &&
+	       auth_alg == WLAN_AUTH_FILS_SK) ||
+#endif /* CONFIG_FILS */
 	      ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
 	       auth_alg == WLAN_AUTH_SHARED_KEY))) {
 		wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
@@ -1081,29 +1546,19 @@
 		}
 	}
 
-	res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len,
-				      &session_timeout,
-				      &acct_interim_interval, &vlan_id,
-				      &psk, &identity, &radius_cui);
-
+	res = ieee802_11_allowed_address(
+		hapd, mgmt->sa, (const u8 *) mgmt, len, &session_timeout,
+		&acct_interim_interval, &vlan_id, &psk, &identity, &radius_cui);
 	if (res == HOSTAPD_ACL_REJECT) {
-		wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
-			   MAC2STR(mgmt->sa));
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
-	if (res == HOSTAPD_ACL_PENDING) {
-		wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR
-			   " waiting for an external authentication",
-			   MAC2STR(mgmt->sa));
-		/* Authentication code will re-send the authentication frame
-		 * after it has received (and cached) information from the
-		 * external source. */
+	if (res == HOSTAPD_ACL_PENDING)
 		return;
-	}
 
 	sta = ap_get_sta(hapd, mgmt->sa);
 	if (sta) {
+		sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
 		if ((fc & WLAN_FC_RETRY) &&
 		    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
 		    sta->last_seq_ctrl == seq_ctrl &&
@@ -1152,47 +1607,17 @@
 	sta->last_seq_ctrl = seq_ctrl;
 	sta->last_subtype = WLAN_FC_STYPE_AUTH;
 
-	if (vlan_id.notempty &&
-	    !hostapd_vlan_valid(hapd->conf->vlan, &vlan_id)) {
-		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
-			       HOSTAPD_LEVEL_INFO,
-			       "Invalid VLAN %d%s received from RADIUS server",
-			       vlan_id.untagged,
-			       vlan_id.tagged[0] ? "+" : "");
+	res = ieee802_11_set_radius_info(
+		hapd, sta, res, session_timeout, acct_interim_interval,
+		&vlan_id, &psk, &identity, &radius_cui);
+	if (res) {
 		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
 		goto fail;
 	}
-	if (ap_sta_set_vlan(hapd, sta, &vlan_id) < 0) {
-		resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-		goto fail;
-	}
-	if (sta->vlan_id)
-		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
-			       HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id);
-
-	hostapd_free_psk_list(sta->psk);
-	if (hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) {
-		sta->psk = psk;
-		psk = NULL;
-	} else {
-		sta->psk = NULL;
-	}
-
-	sta->identity = identity;
-	identity = NULL;
-	sta->radius_cui = radius_cui;
-	radius_cui = NULL;
 
 	sta->flags &= ~WLAN_STA_PREAUTH;
 	ieee802_1x_notify_pre_auth(sta->eapol_sm, 0);
 
-	if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval)
-		sta->acct_interim_interval = acct_interim_interval;
-	if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
-		ap_sta_session_timeout(hapd, sta, session_timeout);
-	else
-		ap_sta_no_session_timeout(hapd, sta);
-
 	/*
 	 * If the driver supports full AP client state, add a station to the
 	 * driver before sending authentication reply to make sure the driver
@@ -1205,8 +1630,15 @@
 	 *
 	 * In mesh mode, the station was already added to the driver when the
 	 * NEW_PEER_CANDIDATE event is received.
+	 *
+	 * If PMF was negotiated for the existing association, skip this to
+	 * avoid dropping the STA entry and the associated keys. This is needed
+	 * to allow the original connection work until the attempt can complete
+	 * (re)association, so that unprotected Authentication frame cannot be
+	 * used to bypass PMF protection.
 	 */
 	if (FULL_AP_CLIENT_STATE_SUPP(hapd->iface->drv_flags) &&
+	    (!(sta->flags & WLAN_STA_MFP) || !ap_sta_is_authorized(sta)) &&
 	    !(hapd->conf->mesh & MESH_ENABLED) &&
 	    !(sta->added_unassoc)) {
 		/*
@@ -1220,8 +1652,8 @@
 		sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_AUTH |
 				WLAN_STA_AUTHORIZED);
 
-		if (hostapd_sta_add(hapd, sta->addr, 0, 0, 0, 0, 0,
-				    NULL, NULL, sta->flags, 0, 0, 0)) {
+		if (hostapd_sta_add(hapd, sta->addr, 0, 0, NULL, 0, 0,
+				    NULL, NULL, sta->flags, 0, 0, 0, 0)) {
 			hostapd_logger(hapd, sta->addr,
 				       HOSTAPD_MODULE_IEEE80211,
 				       HOSTAPD_LEVEL_NOTICE,
@@ -1258,7 +1690,7 @@
 		}
 		break;
 #endif /* CONFIG_NO_RC4 */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	case WLAN_AUTH_FT:
 		sta->auth_alg = WLAN_AUTH_FT;
 		if (sta->wpa_sm == NULL)
@@ -1277,7 +1709,7 @@
 				    handle_auth_ft_finish, hapd);
 		/* handle_auth_ft_finish() callback will complete auth. */
 		return;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_SAE
 	case WLAN_AUTH_SAE:
 #ifdef CONFIG_MESH
@@ -1299,6 +1731,12 @@
 				status_code);
 		return;
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+	case WLAN_AUTH_FILS_SK:
+		handle_auth_fils(hapd, sta, mgmt, len, auth_transaction,
+				 status_code);
+		return;
+#endif /* CONFIG_FILS */
 	}
 
  fail:
@@ -1328,6 +1766,9 @@
 		return 0;
 	}
 
+	if (TEST_FAIL())
+		return -1;
+
 	for (i = 0; i < AID_WORDS; i++) {
 		if (hapd->sta_aid[i] == (u32) -1)
 			continue;
@@ -1398,6 +1839,11 @@
 static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
 			   struct ieee802_11_elems *elems)
 {
+	/* Supported rates not used in IEEE 802.11ad/DMG */
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD)
+		return WLAN_STATUS_SUCCESS;
+
 	if (!elems->supp_rates) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -1584,24 +2030,7 @@
 		res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
 					  wpa_ie, wpa_ie_len,
 					  elems.mdie, elems.mdie_len);
-		if (res == WPA_INVALID_GROUP)
-			resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
-		else if (res == WPA_INVALID_PAIRWISE)
-			resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
-		else if (res == WPA_INVALID_AKMP)
-			resp = WLAN_STATUS_AKMP_NOT_VALID;
-		else if (res == WPA_ALLOC_FAIL)
-			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-#ifdef CONFIG_IEEE80211W
-		else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
-			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
-		else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
-			resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
-#endif /* CONFIG_IEEE80211W */
-		else if (res == WPA_INVALID_MDIE)
-			resp = WLAN_STATUS_INVALID_MDIE;
-		else if (res != WPA_IE_OK)
-			resp = WLAN_STATUS_INVALID_IE;
+		resp = wpa_res_to_status_code(res);
 		if (resp != WLAN_STATUS_SUCCESS)
 			return resp;
 #ifdef CONFIG_IEEE80211W
@@ -1629,7 +2058,7 @@
 			sta->flags &= ~WLAN_STA_MFP;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		if (sta->auth_alg == WLAN_AUTH_FT) {
 			if (!reassoc) {
 				wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried "
@@ -1644,7 +2073,7 @@
 			if (resp != WLAN_STATUS_SUCCESS)
 				return resp;
 		}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_SAE
 		if (wpa_auth_uses_sae(sta->wpa_sm) &&
@@ -1743,6 +2172,12 @@
 	ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
 				    elems.supp_op_classes_len);
 
+	if ((sta->capability & WLAN_CAPABILITY_RADIO_MEASUREMENT) &&
+	    elems.rrm_enabled &&
+	    elems.rrm_enabled_len >= sizeof(sta->rrm_enabled_capa))
+		os_memcpy(sta->rrm_enabled_capa, elems.rrm_enabled,
+			  sizeof(sta->rrm_enabled_capa));
+
 	return WLAN_STATUS_SUCCESS;
 }
 
@@ -1815,7 +2250,8 @@
 			    sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
 			    sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
 			    sta->flags | WLAN_STA_ASSOC, sta->qosinfo,
-			    sta->vht_opmode, set)) {
+			    sta->vht_opmode, sta->p2p_ie ? 1 : 0,
+			    set)) {
 		hostapd_logger(hapd, sta->addr,
 			       HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_NOTICE,
 			       "Could not %s STA to kernel driver",
@@ -1836,21 +2272,32 @@
 
 
 static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
-			   u16 status_code, int reassoc, const u8 *ies,
-			   size_t ies_len)
+			   const u8 *addr, u16 status_code, int reassoc,
+			   const u8 *ies, size_t ies_len)
 {
 	int send_len;
-	u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+	u8 *buf;
+	size_t buflen;
 	struct ieee80211_mgmt *reply;
 	u8 *p;
+	u16 res = WLAN_STATUS_SUCCESS;
 
-	os_memset(buf, 0, sizeof(buf));
+	buflen = sizeof(struct ieee80211_mgmt) + 1024;
+#ifdef CONFIG_FILS
+	if (sta && sta->fils_hlp_resp)
+		buflen += wpabuf_len(sta->fils_hlp_resp);
+#endif /* CONFIG_FILS */
+	buf = os_zalloc(buflen);
+	if (!buf) {
+		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+		goto done;
+	}
 	reply = (struct ieee80211_mgmt *) buf;
 	reply->frame_control =
 		IEEE80211_FC(WLAN_FC_TYPE_MGMT,
 			     (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
 			      WLAN_FC_STYPE_ASSOC_RESP));
-	os_memcpy(reply->da, sta->addr, ETH_ALEN);
+	os_memcpy(reply->da, addr, ETH_ALEN);
 	os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
 	os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
 
@@ -1859,24 +2306,26 @@
 	reply->u.assoc_resp.capab_info =
 		host_to_le16(hostapd_own_capab_info(hapd));
 	reply->u.assoc_resp.status_code = host_to_le16(status_code);
-	reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
+
+	reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) |
+					       BIT(14) | BIT(15));
 	/* Supported rates */
 	p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
 	/* Extended supported rates */
 	p = hostapd_eid_ext_supp_rates(hapd, p);
 
-#ifdef CONFIG_IEEE80211R
-	if (status_code == WLAN_STATUS_SUCCESS) {
+#ifdef CONFIG_IEEE80211R_AP
+	if (sta && status_code == WLAN_STATUS_SUCCESS) {
 		/* IEEE 802.11r: Mobility Domain Information, Fast BSS
 		 * Transition Information, RSN, [RIC Response] */
 		p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
-						buf + sizeof(buf) - p,
+						buf + buflen - p,
 						sta->auth_alg, ies, ies_len);
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_IEEE80211W
-	if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+	if (sta && status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
 		p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
 #endif /* CONFIG_IEEE80211W */
 
@@ -1887,14 +2336,30 @@
 
 #ifdef CONFIG_IEEE80211AC
 	if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
-		p = hostapd_eid_vht_capabilities(hapd, p);
+		u32 nsts = 0, sta_nsts;
+
+		if (sta && hapd->conf->use_sta_nsts && sta->vht_capabilities) {
+			struct ieee80211_vht_capabilities *capa;
+
+			nsts = (hapd->iface->conf->vht_capab >>
+				VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+			capa = sta->vht_capabilities;
+			sta_nsts = (le_to_host32(capa->vht_capabilities_info) >>
+				    VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+
+			if (nsts < sta_nsts)
+				nsts = 0;
+			else
+				nsts = sta_nsts;
+		}
+		p = hostapd_eid_vht_capabilities(hapd, p, nsts);
 		p = hostapd_eid_vht_operation(hapd, p);
 	}
 #endif /* CONFIG_IEEE80211AC */
 
 	p = hostapd_eid_ext_capab(hapd, p);
 	p = hostapd_eid_bss_max_idle_period(hapd, p);
-	if (sta->qos_map_enabled)
+	if (sta && sta->qos_map_enabled)
 		p = hostapd_eid_qos_map_set(hapd, p);
 
 #ifdef CONFIG_FST
@@ -1906,16 +2371,17 @@
 #endif /* CONFIG_FST */
 
 #ifdef CONFIG_IEEE80211AC
-	if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+	if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
 		p = hostapd_eid_vendor_vht(hapd, p);
 #endif /* CONFIG_IEEE80211AC */
 
-	if (sta->flags & WLAN_STA_WMM)
+	if (sta && (sta->flags & WLAN_STA_WMM))
 		p = hostapd_eid_wmm(hapd, p);
 
 #ifdef CONFIG_WPS
-	if ((sta->flags & WLAN_STA_WPS) ||
-	    ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa)) {
+	if (sta &&
+	    ((sta->flags & WLAN_STA_WPS) ||
+	     ((sta->flags & WLAN_STA_MAYBE_WPS) && hapd->conf->wpa))) {
 		struct wpabuf *wps = wps_build_assoc_resp_ie();
 		if (wps) {
 			os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
@@ -1926,7 +2392,7 @@
 #endif /* CONFIG_WPS */
 
 #ifdef CONFIG_P2P
-	if (sta->p2p_ie && hapd->p2p_group) {
+	if (sta && sta->p2p_ie && hapd->p2p_group) {
 		struct wpabuf *p2p_resp_ie;
 		enum p2p_status_code status;
 		switch (status_code) {
@@ -1955,20 +2421,109 @@
 		p = hostapd_eid_p2p_manage(hapd, p);
 #endif /* CONFIG_P2P_MANAGER */
 
-	p = hostapd_eid_mbo(hapd, p, buf + sizeof(buf) - p);
+	p = hostapd_eid_mbo(hapd, p, buf + buflen - p);
+
+	if (hapd->conf->assocresp_elements &&
+	    (size_t) (buf + buflen - p) >=
+	    wpabuf_len(hapd->conf->assocresp_elements)) {
+		os_memcpy(p, wpabuf_head(hapd->conf->assocresp_elements),
+			  wpabuf_len(hapd->conf->assocresp_elements));
+		p += wpabuf_len(hapd->conf->assocresp_elements);
+	}
 
 	send_len += p - reply->u.assoc_resp.variable;
 
+#ifdef CONFIG_FILS
+	if (sta &&
+	    (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    status_code == WLAN_STATUS_SUCCESS) {
+		struct ieee802_11_elems elems;
+
+		if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+		    ParseFailed || !elems.fils_session) {
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+
+		/* FILS Session */
+		*p++ = WLAN_EID_EXTENSION; /* Element ID */
+		*p++ = 1 + FILS_SESSION_LEN; /* Length */
+		*p++ = WLAN_EID_EXT_FILS_SESSION; /* Element ID Extension */
+		os_memcpy(p, elems.fils_session, FILS_SESSION_LEN);
+		send_len += 2 + 1 + FILS_SESSION_LEN;
+
+		send_len = fils_encrypt_assoc(sta->wpa_sm, buf, send_len,
+					      buflen, sta->fils_hlp_resp);
+		if (send_len < 0) {
+			res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto done;
+		}
+	}
+#endif /* CONFIG_FILS */
+
 	if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
 		wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
 			   strerror(errno));
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+		res = WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
-	return WLAN_STATUS_SUCCESS;
+done:
+	os_free(buf);
+	return res;
 }
 
 
+#ifdef CONFIG_FILS
+
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	u16 reply_res;
+
+	wpa_printf(MSG_DEBUG, "FILS: Finish association with " MACSTR,
+		   MAC2STR(sta->addr));
+	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+	if (!sta->fils_pending_assoc_req)
+		return;
+	reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
+				    sta->fils_pending_assoc_is_reassoc,
+				    sta->fils_pending_assoc_req,
+				    sta->fils_pending_assoc_req_len);
+	os_free(sta->fils_pending_assoc_req);
+	sta->fils_pending_assoc_req = NULL;
+	sta->fils_pending_assoc_req_len = 0;
+	wpabuf_free(sta->fils_hlp_resp);
+	sta->fils_hlp_resp = NULL;
+	wpabuf_free(sta->hlp_dhcp_discover);
+	sta->hlp_dhcp_discover = NULL;
+
+	/*
+	 * Remove the station in case tranmission of a success response fails
+	 * (the STA was added associated to the driver) or if the station was
+	 * previously added unassociated.
+	 */
+	if (reply_res != WLAN_STATUS_SUCCESS || sta->added_unassoc) {
+		hostapd_drv_sta_remove(hapd, sta->addr);
+		sta->added_unassoc = 0;
+	}
+}
+
+
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = eloop_data;
+
+	wpa_printf(MSG_DEBUG,
+		   "FILS: HLP response timeout - continue with association response for "
+		   MACSTR, MAC2STR(sta->addr));
+	fils_hlp_finish_assoc(hapd, sta);
+}
+
+#endif /* CONFIG_FILS */
+
+
 static void handle_assoc(struct hostapd_data *hapd,
 			 const struct ieee80211_mgmt *mgmt, size_t len,
 			 int reassoc)
@@ -1978,6 +2533,13 @@
 	const u8 *pos;
 	int left, i;
 	struct sta_info *sta;
+	u8 *tmp = NULL;
+	struct hostapd_sta_wpa_psk_short *psk = NULL;
+	char *identity = NULL;
+	char *radius_cui = NULL;
+#ifdef CONFIG_FILS
+	int delay_assoc = 0;
+#endif /* CONFIG_FILS */
 
 	if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
 				      sizeof(mgmt->u.assoc_req))) {
@@ -2035,7 +2597,7 @@
 	}
 
 	sta = ap_get_sta(hapd, mgmt->sa);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (sta && sta->auth_alg == WLAN_AUTH_FT &&
 	    (sta->flags & WLAN_STA_AUTH) == 0) {
 		wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate "
@@ -2048,24 +2610,72 @@
 		 */
 		sta->flags |= WLAN_STA_AUTH;
 	} else
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) {
-		hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
-			       HOSTAPD_LEVEL_INFO, "Station tried to "
-			       "associate before authentication "
-			       "(aid=%d flags=0x%x)",
-			       sta ? sta->aid : -1,
-			       sta ? sta->flags : 0);
-		send_deauth(hapd, mgmt->sa,
-			    WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
-		return;
+		if (hapd->iface->current_mode &&
+		    hapd->iface->current_mode->mode ==
+			HOSTAPD_MODE_IEEE80211AD) {
+			int acl_res;
+			u32 session_timeout, acct_interim_interval;
+			struct vlan_description vlan_id;
+
+			acl_res = ieee802_11_allowed_address(
+				hapd, mgmt->sa, (const u8 *) mgmt, len,
+				&session_timeout, &acct_interim_interval,
+				&vlan_id, &psk, &identity, &radius_cui);
+			if (acl_res == HOSTAPD_ACL_REJECT) {
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+			if (acl_res == HOSTAPD_ACL_PENDING)
+				return;
+
+			/* DMG/IEEE 802.11ad does not use authentication.
+			 * Allocate sta entry upon association. */
+			sta = ap_sta_add(hapd, mgmt->sa);
+			if (!sta) {
+				hostapd_logger(hapd, mgmt->sa,
+					       HOSTAPD_MODULE_IEEE80211,
+					       HOSTAPD_LEVEL_INFO,
+					       "Failed to add STA");
+				resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+				goto fail;
+			}
+
+			acl_res = ieee802_11_set_radius_info(
+				hapd, sta, acl_res, session_timeout,
+				acct_interim_interval, &vlan_id, &psk,
+				&identity, &radius_cui);
+			if (acl_res) {
+				resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+				goto fail;
+			}
+
+			hostapd_logger(hapd, sta->addr,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "Skip authentication for DMG/IEEE 802.11ad");
+			sta->flags |= WLAN_STA_AUTH;
+			wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+			sta->auth_alg = WLAN_AUTH_OPEN;
+		} else {
+			hostapd_logger(hapd, mgmt->sa,
+				       HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO,
+				       "Station tried to associate before authentication (aid=%d flags=0x%x)",
+				       sta ? sta->aid : -1,
+				       sta ? sta->flags : 0);
+			send_deauth(hapd, mgmt->sa,
+				    WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+			return;
+		}
 	}
 
 	if ((fc & WLAN_FC_RETRY) &&
 	    sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
 	    sta->last_seq_ctrl == seq_ctrl &&
-	    sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
-	    WLAN_FC_STYPE_ASSOC_REQ) {
+	    sta->last_subtype == (reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+				  WLAN_FC_STYPE_ASSOC_REQ)) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
 			       HOSTAPD_LEVEL_DEBUG,
 			       "Drop repeated association frame seq_ctrl=0x%x",
@@ -2097,6 +2707,36 @@
 	}
 #endif /* CONFIG_MBO */
 
+	/*
+	 * sta->capability is used in check_assoc_ies() for RRM enabled
+	 * capability element.
+	 */
+	sta->capability = capab_info;
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		/* The end of the payload is encrypted. Need to decrypt it
+		 * before parsing. */
+
+		tmp = os_malloc(left);
+		if (!tmp) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		os_memcpy(tmp, pos, left);
+
+		left = fils_decrypt_assoc(sta->wpa_sm, sta->fils_session, mgmt,
+					  len, tmp, left);
+		if (left < 0) {
+			resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+			goto fail;
+		}
+		pos = tmp;
+	}
+#endif /* CONFIG_FILS */
+
 	/* followed by SSID and Supported rates; and HT capabilities if 802.11n
 	 * is used */
 	resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
@@ -2110,7 +2750,6 @@
 		goto fail;
 	}
 
-	sta->capability = capab_info;
 	sta->listen_interval = listen_interval;
 
 	if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
@@ -2180,7 +2819,26 @@
 	 * remove the STA immediately. */
 	sta->timeout_next = STA_NULLFUNC;
 
+#ifdef CONFIG_TAXONOMY
+	taxonomy_sta_info_assoc_req(hapd, sta, pos, left);
+#endif /* CONFIG_TAXONOMY */
+
+	sta->pending_wds_enable = 0;
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		if (fils_process_hlp(hapd, sta, pos, left) > 0)
+			delay_assoc = 1;
+	}
+#endif /* CONFIG_FILS */
+
  fail:
+	os_free(identity);
+	os_free(radius_cui);
+	hostapd_free_psk_list(psk);
+
 	/*
 	 * In case of a successful response, add the station to the driver.
 	 * Otherwise, the kernel may ignore Data frames before we process the
@@ -2199,18 +2857,43 @@
 	 *    issues with processing other non-Data Class 3 frames during this
 	 *    window.
 	 */
-	if (resp == WLAN_STATUS_SUCCESS && add_associated_sta(hapd, sta))
+	if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta))
 		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
 
-	reply_res = send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+#ifdef CONFIG_FILS
+	if (sta) {
+		eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+		os_free(sta->fils_pending_assoc_req);
+		sta->fils_pending_assoc_req = NULL;
+		sta->fils_pending_assoc_req_len = 0;
+		wpabuf_free(sta->fils_hlp_resp);
+		sta->fils_hlp_resp = NULL;
+	}
+	if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS) {
+		sta->fils_pending_assoc_req = tmp;
+		sta->fils_pending_assoc_req_len = left;
+		sta->fils_pending_assoc_is_reassoc = reassoc;
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Waiting for HLP processing before sending (Re)Association Response frame to "
+			   MACSTR, MAC2STR(sta->addr));
+		eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+		eloop_register_timeout(0, hapd->conf->fils_hlp_wait_time * 1024,
+				       fils_hlp_timeout, hapd, sta);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
+	reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos,
+				    left);
+	os_free(tmp);
 
 	/*
 	 * Remove the station in case tranmission of a success response fails
 	 * (the STA was added associated to the driver) or if the station was
 	 * previously added unassociated.
 	 */
-	if ((reply_res != WLAN_STATUS_SUCCESS &&
-	     resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc) {
+	if (sta && ((reply_res != WLAN_STATUS_SUCCESS &&
+		     resp == WLAN_STATUS_SUCCESS) || sta->added_unassoc)) {
 		hostapd_drv_sta_remove(hapd, sta->addr);
 		sta->added_unassoc = 0;
 	}
@@ -2267,6 +2950,17 @@
 
 	mlme_disassociate_indication(
 		hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code));
+
+	/* DMG/IEEE 802.11ad does not use deauthication. Deallocate sta upon
+	 * disassociation. */
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		sta->flags &= ~WLAN_STA_AUTH;
+		wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_DEBUG, "deauthenticated");
+		ap_free_sta(hapd, sta);
+	}
 }
 
 
@@ -2378,7 +3072,7 @@
 	    (sta == NULL || !(sta->flags & WLAN_STA_ASSOC))) {
 		wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
 			   "frame (category=%u) from unassociated STA " MACSTR,
-			   MAC2STR(mgmt->sa), mgmt->u.action.category);
+			   mgmt->u.action.category, MAC2STR(mgmt->sa));
 		return 0;
 	}
 
@@ -2415,14 +3109,14 @@
 	}
 
 	switch (mgmt->u.action.category) {
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	case WLAN_ACTION_FT:
 		if (!sta ||
 		    wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
 				     len - IEEE80211_HDRLEN))
 			break;
 		return 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	case WLAN_ACTION_WMM:
 		hostapd_wmm_action(hapd, mgmt, len);
 		return 1;
@@ -2477,6 +3171,9 @@
 				return 1;
 		}
 		break;
+	case WLAN_ACTION_RADIO_MEASUREMENT:
+		hostapd_handle_radio_measurement(hapd, (const u8 *) mgmt, len);
+		return 1;
 	}
 
 	hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -2484,8 +3181,9 @@
 		       "handle_action - unknown action category %d or invalid "
 		       "frame",
 		       mgmt->u.action.category);
-	if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) &&
-	    !(mgmt->sa[0] & 0x01)) {
+	if (!is_multicast_ether_addr(mgmt->da) &&
+	    !(mgmt->u.action.category & 0x80) &&
+	    !is_multicast_ether_addr(mgmt->sa)) {
 		struct ieee80211_mgmt *resp;
 
 		/*
@@ -2532,7 +3230,6 @@
 		    struct hostapd_frame_info *fi)
 {
 	struct ieee80211_mgmt *mgmt;
-	int broadcast;
 	u16 fc, stype;
 	int ret = 0;
 
@@ -2548,11 +3245,7 @@
 		return 1;
 	}
 
-	broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
-		mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff &&
-		mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff;
-
-	if (!broadcast &&
+	if (!is_broadcast_ether_addr(mgmt->bssid) &&
 #ifdef CONFIG_P2P
 	    /* Invitation responses can be sent with the peer MAC as BSSID */
 	    !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
@@ -2582,7 +3275,7 @@
 	}
 
 	if (hapd->iconf->track_sta_max_num)
-		sta_track_add(hapd->iface, mgmt->sa);
+		sta_track_add(hapd->iface, mgmt->sa, fi->ssi_signal);
 
 	switch (stype) {
 	case WLAN_FC_STYPE_AUTH:
@@ -2756,11 +3449,15 @@
 		new_assoc = 0;
 	sta->flags |= WLAN_STA_ASSOC;
 	sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
-	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
+	if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+	     !hapd->conf->osen) ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK ||
 	    sta->auth_alg == WLAN_AUTH_FT) {
 		/*
-		 * Open, static WEP, or FT protocol; no separate authorization
-		 * step.
+		 * Open, static WEP, FT protocol, or FILS; no separate
+		 * authorization step.
 		 */
 		ap_sta_set_authorized(hapd, sta, 1);
 	}
@@ -2774,16 +3471,6 @@
 	sta->sa_query_timed_out = 0;
 #endif /* CONFIG_IEEE80211W */
 
-	if (sta->flags & WLAN_STA_WDS) {
-		int ret;
-		char ifname_wds[IFNAMSIZ + 1];
-
-		ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
-					  sta->aid, 1);
-		if (!ret)
-			hostapd_set_wds_encryption(hapd, sta, ifname_wds);
-	}
-
 	if (sta->eapol_sm == NULL) {
 		/*
 		 * This STA does not use RADIUS server for EAP authentication,
@@ -2800,6 +3487,27 @@
 
 	hostapd_set_sta_flags(hapd, sta);
 
+	if (!(sta->flags & WLAN_STA_WDS) && sta->pending_wds_enable) {
+		wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for STA "
+			   MACSTR " based on pending request",
+			   MAC2STR(sta->addr));
+		sta->pending_wds_enable = 0;
+		sta->flags |= WLAN_STA_WDS;
+	}
+
+	if (sta->flags & WLAN_STA_WDS) {
+		int ret;
+		char ifname_wds[IFNAMSIZ + 1];
+
+		wpa_printf(MSG_DEBUG, "Reenable 4-address WDS mode for STA "
+			   MACSTR " (aid %u)",
+			   MAC2STR(sta->addr), sta->aid);
+		ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+					  sta->aid, 1);
+		if (!ret)
+			hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+	}
+
 	if (sta->auth_alg == WLAN_AUTH_FT)
 		wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
 	else
@@ -2807,6 +3515,18 @@
 	hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
 	ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
 
+#ifdef CONFIG_FILS
+	if ((sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	     sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	     sta->auth_alg == WLAN_AUTH_FILS_PK) &&
+	    fils_set_tk(sta->wpa_sm) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: TK configuration failed");
+		ap_sta_disconnect(hapd, sta, sta->addr,
+				  WLAN_REASON_UNSPECIFIED);
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	if (sta->pending_eapol_rx) {
 		struct os_reltime now, age;
 
@@ -2833,7 +3553,7 @@
 			     size_t len, int ok)
 {
 	struct sta_info *sta;
-	if (mgmt->da[0] & 0x01)
+	if (is_multicast_ether_addr(mgmt->da))
 		return;
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
@@ -2857,7 +3577,7 @@
 			       size_t len, int ok)
 {
 	struct sta_info *sta;
-	if (mgmt->da[0] & 0x01)
+	if (is_multicast_ether_addr(mgmt->da))
 		return;
 	sta = ap_get_sta(hapd, mgmt->da);
 	if (!sta) {
@@ -2876,6 +3596,35 @@
 }
 
 
+static void handle_action_cb(struct hostapd_data *hapd,
+			     const struct ieee80211_mgmt *mgmt,
+			     size_t len, int ok)
+{
+	struct sta_info *sta;
+	const struct rrm_measurement_report_element *report;
+
+	if (is_multicast_ether_addr(mgmt->da))
+		return;
+	sta = ap_get_sta(hapd, mgmt->da);
+	if (!sta) {
+		wpa_printf(MSG_DEBUG, "handle_action_cb: STA " MACSTR
+			   " not found", MAC2STR(mgmt->da));
+		return;
+	}
+
+	if (len < 24 + 5 + sizeof(*report))
+		return;
+	report = (const struct rrm_measurement_report_element *)
+		&mgmt->u.action.u.rrm.variable[2];
+	if (mgmt->u.action.category == WLAN_ACTION_RADIO_MEASUREMENT &&
+	    mgmt->u.action.u.rrm.action == WLAN_RRM_RADIO_MEASUREMENT_REQUEST &&
+	    report->eid == WLAN_EID_MEASURE_REQUEST &&
+	    report->len >= 3 &&
+	    report->type == MEASURE_TYPE_BEACON)
+		hostapd_rrm_beacon_req_tx_status(hapd, mgmt, len, ok);
+}
+
+
 /**
  * ieee802_11_mgmt_cb - Process management frame TX status callback
  * @hapd: hostapd BSS data structure (the BSS from which the management frame
@@ -2925,6 +3674,7 @@
 		break;
 	case WLAN_FC_STYPE_ACTION:
 		wpa_printf(MSG_DEBUG, "mgmt::action cb ok=%d", ok);
+		handle_action_cb(hapd, mgmt, len, ok);
 		break;
 	default:
 		wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
@@ -3022,6 +3772,8 @@
 	}
 	if (sta == NULL)
 		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_POLL_OK MACSTR,
+		MAC2STR(sta->addr));
 	if (!(sta->flags & WLAN_STA_PENDING_POLL))
 		return;
 
@@ -3037,10 +3789,22 @@
 	struct sta_info *sta;
 
 	sta = ap_get_sta(hapd, src);
-	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+	if (sta &&
+	    ((sta->flags & WLAN_STA_ASSOC) ||
+	     ((sta->flags & WLAN_STA_ASSOC_REQ_OK) && wds))) {
 		if (!hapd->conf->wds_sta)
 			return;
 
+		if ((sta->flags & (WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK)) ==
+		    WLAN_STA_ASSOC_REQ_OK) {
+			wpa_printf(MSG_DEBUG,
+				   "Postpone 4-address WDS mode enabling for STA "
+				   MACSTR " since TX status for AssocResp is not yet known",
+				   MAC2STR(sta->addr));
+			sta->pending_wds_enable = 1;
+			return;
+		}
+
 		if (wds && !(sta->flags & WLAN_STA_WDS)) {
 			int ret;
 			char ifname_wds[IFNAMSIZ + 1];
@@ -3060,7 +3824,7 @@
 
 	wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
 		   MACSTR, MAC2STR(src));
-	if (src[0] & 0x01) {
+	if (is_multicast_ether_addr(src)) {
 		/* Broadcast bit set in SA?! Ignore the frame silently. */
 		return;
 	}
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 71b3b49..ce3abcb 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -50,11 +50,13 @@
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid);
-u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts);
 u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_he_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_he_operation(struct hostapd_data *hapd, u8 *eid);
 
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -135,4 +137,12 @@
 				 const u8 *supp_op_classes,
 				 size_t supp_op_classes_len);
 
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid);
+void ieee802_11_finish_fils_auth(struct hostapd_data *hapd,
+				 struct sta_info *sta, int success,
+				 struct wpabuf *erp_resp,
+				 const u8 *msk, size_t msk_len);
+void fils_hlp_timeout(void *eloop_ctx, void *eloop_data);
+void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta);
+
 #endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 9609152..1e0358c 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -457,7 +457,7 @@
 
 		if (passphraselen < MIN_PASSPHRASE_LEN ||
 		    passphraselen > MAX_PASSPHRASE_LEN + 1)
-			continue;
+			goto free_pass;
 
 		/*
 		 * passphrase does not contain the NULL termination.
@@ -484,6 +484,7 @@
 		}
 skip:
 		os_free(psk);
+free_pass:
 		os_free(passphrase);
 	}
 }
@@ -664,9 +665,11 @@
 
 #ifndef CONFIG_NO_RADIUS
 	hostapd_acl_cache_free(hapd->acl_cache);
+	hapd->acl_cache = NULL;
 #endif /* CONFIG_NO_RADIUS */
 
 	query = hapd->acl_queries;
+	hapd->acl_queries = NULL;
 	while (query) {
 		prev = query;
 		query = query->next;
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
new file mode 100644
index 0000000..7d6a84f
--- /dev/null
+++ b/src/ap/ieee802_11_he.c
@@ -0,0 +1,101 @@
+/*
+ * hostapd / IEEE 802.11ax HE
+ * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+#include "dfs.h"
+
+u8 * hostapd_eid_vendor_he_capab(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_he_capabilities *cap;
+	u8 *pos = eid;
+
+	if (!hapd->iface->current_mode)
+		return eid;
+
+	/* For now, use a vendor specific element since the P802.11ax draft is
+	 * still subject to changes and the contents of this element may change.
+	 * This can be replaced with the actual element once P802.11ax is
+	 * finalized. */
+	/* Vendor HE Capabilities element */
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = 4 /* The Vendor OUI, subtype */ +
+		sizeof(struct ieee80211_he_capabilities);
+
+	WPA_PUT_BE32(pos, (OUI_QCA << 8) | QCA_VENDOR_ELEM_HE_CAPAB);
+	pos += 4;
+	cap = (struct ieee80211_he_capabilities *) pos;
+	os_memset(cap, 0, sizeof(*cap));
+
+	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
+			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
+
+	if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
+		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
+			HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
+
+	if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
+		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
+			HE_PHYCAP_MU_BEAMFORMER_CAPAB;
+
+	pos += sizeof(*cap);
+
+	return pos;
+}
+
+
+u8 * hostapd_eid_vendor_he_operation(struct hostapd_data *hapd, u8 *eid)
+{
+	struct ieee80211_he_operation *oper;
+	u8 *pos = eid;
+
+	if (!hapd->iface->current_mode)
+		return eid;
+
+	/* For now, use a vendor specific element since the P802.11ax draft is
+	 * still subject to changes and the contents of this element may change.
+	 * This can be replaced with the actual element once P802.11ax is
+	 * finalized. */
+	/* Vendor HE Operation element */
+	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
+	*pos++ = 4 /* The Vendor OUI, subtype */ +
+		sizeof(struct ieee80211_he_operation);
+
+	WPA_PUT_BE32(pos, (OUI_QCA << 8) | QCA_VENDOR_ELEM_HE_OPER);
+	pos += 4;
+	oper = (struct ieee80211_he_operation *) pos;
+	os_memset(oper, 0, sizeof(*oper));
+
+	if (hapd->iface->conf->he_op.he_bss_color)
+		oper->he_oper_params |= hapd->iface->conf->he_op.he_bss_color;
+
+	if (hapd->iface->conf->he_op.he_default_pe_duration)
+		oper->he_oper_params |=
+			(hapd->iface->conf->he_op.he_default_pe_duration <<
+			 HE_OPERATION_DFLT_PE_DURATION_OFFSET);
+
+	if (hapd->iface->conf->he_op.he_twt_required)
+		oper->he_oper_params |= HE_OPERATION_TWT_REQUIRED;
+
+	if (hapd->iface->conf->he_op.he_rts_threshold)
+		oper->he_oper_params |=
+			(hapd->iface->conf->he_op.he_rts_threshold <<
+			 HE_OPERATION_RTS_THRESHOLD_OFFSET);
+
+	pos += sizeof(*oper);
+
+	return pos;
+}
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
index 5eb1060..146e447 100644
--- a/src/ap/ieee802_11_ht.c
+++ b/src/ap/ieee802_11_ht.c
@@ -340,8 +340,8 @@
 	 * that did not specify a valid WMM IE in the (Re)Association Request
 	 * frame.
 	 */
-	if (!ht_capab ||
-	    !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) {
+	if (!ht_capab || !(sta->flags & WLAN_STA_WMM) ||
+	    !hapd->iconf->ieee80211n || hapd->conf->disable_11n) {
 		sta->flags &= ~WLAN_STA_HT;
 		os_free(sta->ht_capabilities);
 		sta->ht_capabilities = NULL;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index af858f0..daacf7e 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -218,6 +218,20 @@
 		if (hapd->conf->ssid.utf8_ssid)
 			*pos |= 0x01; /* Bit 48 - UTF-8 SSID */
 		break;
+	case 7: /* Bits 56-63 */
+		break;
+	case 8: /* Bits 64-71 */
+		if (hapd->conf->ftm_responder)
+			*pos |= 0x40; /* Bit 70 - FTM responder */
+		if (hapd->conf->ftm_initiator)
+			*pos |= 0x80; /* Bit 71 - FTM initiator */
+	case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+		if ((hapd->conf->wpa & WPA_PROTO_RSN) &&
+		    wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+			*pos |= 0x01;
+#endif /* CONFIG_FILS */
+		break;
 	}
 }
 
@@ -237,6 +251,9 @@
 		len = 1;
 	if (len < 7 && hapd->conf->ssid.utf8_ssid)
 		len = 7;
+	if (len < 9 &&
+	    (hapd->conf->ftm_initiator || hapd->conf->ftm_responder))
+		len = 9;
 #ifdef CONFIG_WNM
 	if (len < 4)
 		len = 4;
@@ -249,6 +266,11 @@
 	if (hapd->conf->mbo_enabled && len < 6)
 		len = 6;
 #endif /* CONFIG_MBO */
+#ifdef CONFIG_FILS
+	if ((!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	     !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
+		len = 10;
+#endif /* CONFIG_FILS */
 	if (len < hapd->iface->extended_capa_len)
 		len = hapd->iface->extended_capa_len;
 	if (len == 0)
@@ -575,3 +597,63 @@
 	os_memcpy(sta->supp_op_classes + 1, supp_op_classes,
 		  supp_op_classes_len);
 }
+
+
+u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
+{
+	u8 *pos = eid;
+#ifdef CONFIG_FILS
+	u8 *len;
+	u16 fils_info = 0;
+	size_t realms;
+	struct fils_realm *realm;
+
+	if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
+	    !wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt))
+		return pos;
+
+	realms = dl_list_len(&hapd->conf->fils_realms);
+	if (realms > 7)
+		realms = 7; /* 3 bit count field limits this to max 7 */
+
+	*pos++ = WLAN_EID_FILS_INDICATION;
+	len = pos++;
+	/* TODO: B0..B2: Number of Public Key Identifiers */
+	if (hapd->conf->erp_domain) {
+		/* B3..B5: Number of Realm Identifiers */
+		fils_info |= realms << 3;
+	}
+	/* TODO: B6: FILS IP Address Configuration */
+	if (hapd->conf->fils_cache_id_set)
+		fils_info |= BIT(7);
+	if (hessid && !is_zero_ether_addr(hapd->conf->hessid))
+		fils_info |= BIT(8); /* HESSID Included */
+	/* FILS Shared Key Authentication without PFS Supported */
+	fils_info |= BIT(9);
+	/* TODO: B10: FILS Shared Key Authentication with PFS Supported */
+	/* TODO: B11: FILS Public Key Authentication Supported */
+	/* B12..B15: Reserved */
+	WPA_PUT_LE16(pos, fils_info);
+	pos += 2;
+	if (hapd->conf->fils_cache_id_set) {
+		os_memcpy(pos, hapd->conf->fils_cache_id, FILS_CACHE_ID_LEN);
+		pos += FILS_CACHE_ID_LEN;
+	}
+	if (hessid && !is_zero_ether_addr(hapd->conf->hessid)) {
+		os_memcpy(pos, hapd->conf->hessid, ETH_ALEN);
+		pos += ETH_ALEN;
+	}
+
+	dl_list_for_each(realm, &hapd->conf->fils_realms, struct fils_realm,
+			 list) {
+		if (realms == 0)
+			break;
+		realms--;
+		os_memcpy(pos, realm->hash, 2);
+		pos += 2;
+	}
+	*len = pos - len - 1;
+#endif /* CONFIG_FILS */
+
+	return pos;
+}
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 0841898..8d06620 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -20,7 +20,7 @@
 #include "dfs.h"
 
 
-u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
+u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts)
 {
 	struct ieee80211_vht_capabilities *cap;
 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
@@ -50,6 +50,18 @@
 	cap->vht_capabilities_info = host_to_le32(
 		hapd->iface->conf->vht_capab);
 
+	if (nsts != 0) {
+		u32 hapd_nsts;
+
+		hapd_nsts = le_to_host32(cap->vht_capabilities_info);
+		hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7;
+		cap->vht_capabilities_info &=
+			~(host_to_le32(hapd_nsts <<
+				       VHT_CAP_BEAMFORMEE_STS_OFFSET));
+		cap->vht_capabilities_info |=
+			host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET);
+	}
+
 	/* Supported MCS set comes from hw */
 	os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
 
@@ -322,7 +334,7 @@
 {
 	/* Disable VHT caps for STAs associated to no-VHT BSSes. */
 	if (!vht_capab ||
-	    hapd->conf->disable_11ac ||
+	    !hapd->iconf->ieee80211ac || hapd->conf->disable_11ac ||
 	    !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
 		sta->flags &= ~WLAN_STA_VHT;
 		os_free(sta->vht_capabilities);
@@ -398,7 +410,7 @@
 	WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
 	pos += 4;
 	*pos++ = VENDOR_VHT_SUBTYPE;
-	pos = hostapd_eid_vht_capabilities(hapd, pos);
+	pos = hostapd_eid_vht_capabilities(hapd, pos, 0);
 	pos = hostapd_eid_vht_operation(hapd, pos);
 
 	return pos;
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 42b0299..fccdc72 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -31,6 +31,8 @@
 #include "ap_drv_ops.h"
 #include "wps_hostapd.h"
 #include "hs20.h"
+/* FIX: Not really a good thing to require ieee802_11.h here.. (FILS) */
+#include "ieee802_11.h"
 #include "ieee802_1x.h"
 
 
@@ -316,6 +318,7 @@
 	     hdr->code != EAP_CODE_INITIATE))
 		return;
 
+	eap_erp_update_identity(sm->eap, eap, len);
 	identity = eap_get_identity(sm->eap, &identity_len);
 	if (identity == NULL)
 		return;
@@ -414,6 +417,7 @@
 
 	if (!hostapd_config_get_radius_attr(req_attr,
 					    RADIUS_ATTR_NAS_PORT) &&
+	    sta->aid > 0 &&
 	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) {
 		wpa_printf(MSG_ERROR, "Could not add NAS-Port");
 		return -1;
@@ -471,7 +475,7 @@
 		}
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
 	    sta->wpa_sm &&
 	    (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
@@ -484,7 +488,7 @@
 		wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
 		return -1;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm &&
 	    add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
@@ -587,9 +591,9 @@
 }
 
 
-static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
-					  struct sta_info *sta,
-					  const u8 *eap, size_t len)
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+				   struct sta_info *sta,
+				   const u8 *eap, size_t len)
 {
 	struct radius_msg *msg;
 	struct eapol_state_machine *sm = sta->eapol_sm;
@@ -844,7 +848,7 @@
 }
 
 
-static struct eapol_state_machine *
+struct eapol_state_machine *
 ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
 {
 	int flags = 0;
@@ -1153,7 +1157,7 @@
 
 	sta->eapol_sm->eap_if->portEnabled = TRUE;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (sta->auth_alg == WLAN_AUTH_FT) {
 		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_DEBUG,
@@ -1172,7 +1176,29 @@
 		/* TODO: get vlan_id from R0KH using RRB message */
 		return;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+	    sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+	    sta->auth_alg == WLAN_AUTH_FILS_PK) {
+		hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+			       HOSTAPD_LEVEL_DEBUG,
+			       "PMK from FILS - skip IEEE 802.1X/EAP");
+		/* Setup EAPOL state machines to already authenticated state
+		 * because of existing FILS information. */
+		sta->eapol_sm->keyRun = TRUE;
+		sta->eapol_sm->eap_if->eapKeyAvailable = TRUE;
+		sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING;
+		sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS;
+		sta->eapol_sm->authSuccess = TRUE;
+		sta->eapol_sm->authFail = FALSE;
+		sta->eapol_sm->portValid = TRUE;
+		if (sta->eapol_sm->eap)
+			eap_sm_notify_cached(sta->eapol_sm->eap);
+		return;
+	}
+#endif /* CONFIG_FILS */
 
 	pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
 	if (pmksa) {
@@ -1836,6 +1862,19 @@
 	if (override_eapReq)
 		sm->eap_if->aaaEapReq = FALSE;
 
+#ifdef CONFIG_FILS
+#ifdef NEED_AP_MLME
+	if (sta->flags & WLAN_STA_PENDING_FILS_ERP) {
+		/* TODO: Add a PMKSA entry on success? */
+		ieee802_11_finish_fils_auth(
+			hapd, sta, hdr->code == RADIUS_CODE_ACCESS_ACCEPT,
+			sm->eap_if->aaaEapReqData,
+			sm->eap_if->aaaEapKeyData,
+			sm->eap_if->aaaEapKeyDataLen);
+	}
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_FILS */
+
 	eapol_auth_step(sm);
 
 	return RADIUS_RX_QUEUED;
@@ -1923,7 +1962,7 @@
 
 	wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
 		   eapol->default_wep_key_idx);
-		      
+
 	if (ieee802_1x_rekey_broadcast(hapd)) {
 		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
 			       HOSTAPD_LEVEL_WARNING, "failed to generate a "
@@ -2325,6 +2364,16 @@
 		   MAC2STR(sta->addr), xhdr->version, xhdr->type,
 		   be_to_host16(xhdr->length), ack);
 
+#ifdef CONFIG_WPS
+	if (xhdr->type == IEEE802_1X_TYPE_EAP_PACKET && ack &&
+	    (sta->flags & WLAN_STA_WPS) &&
+	    ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta)) {
+		wpa_printf(MSG_DEBUG,
+			   "WPS: Indicate EAP completion on ACK for EAP-Failure");
+		hostapd_wps_eap_completed(hapd);
+	}
+#endif /* CONFIG_WPS */
+
 	if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY)
 		return 0;
 
@@ -2698,15 +2747,6 @@
 		 * EAP-FAST with anonymous provisioning, may require another
 		 * EAPOL authentication to be started to complete connection.
 		 */
-		wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: Force "
-			"disconnection after EAP-Failure");
-		/* Add a small sleep to increase likelihood of previously
-		 * requested EAP-Failure TX getting out before this should the
-		 * driver reorder operations.
-		 */
-		os_sleep(0, 10000);
-		ap_sta_disconnect(hapd, sta, sta->addr,
-				  WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
-		hostapd_wps_eap_completed(hapd);
+		ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta);
 	}
 }
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
index ec80199..9594661 100644
--- a/src/ap/ieee802_1x.h
+++ b/src/ap/ieee802_1x.h
@@ -57,5 +57,10 @@
 			   struct hostapd_radius_attr *req_attr,
 			   struct sta_info *sta,
 			   struct radius_msg *msg);
+void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
+				   struct sta_info *sta,
+				   const u8 *eap, size_t len);
+struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta);
 
 #endif /* IEEE802_1X_H */
diff --git a/src/ap/mbo_ap.c b/src/ap/mbo_ap.c
index 5e0f92a..43b0bf1 100644
--- a/src/ap/mbo_ap.c
+++ b/src/ap/mbo_ap.c
@@ -38,17 +38,16 @@
 	size_t num_chan, i;
 	int ret;
 
-	if (len <= 4)
+	if (len <= 3)
 		return; /* Not enough room for any channels */
 
-	num_chan = len - 4;
+	num_chan = len - 3;
 	info = os_zalloc(sizeof(*info) + num_chan);
 	if (!info)
 		return;
 	info->op_class = buf[0];
-	info->pref = buf[len - 3];
-	info->reason_code = buf[len - 2];
-	info->reason_detail = buf[len - 1];
+	info->pref = buf[len - 2];
+	info->reason_code = buf[len - 1];
 	info->num_channels = num_chan;
 	buf++;
 	os_memcpy(info->channels, buf, num_chan);
@@ -75,9 +74,9 @@
 	}
 
 	wpa_printf(MSG_DEBUG, "MBO: STA " MACSTR
-		   " non-preferred channel list (op class %u, pref %u, reason code %u, reason detail %u, channels %s)",
+		   " non-preferred channel list (op class %u, pref %u, reason code %u, channels %s)",
 		   MAC2STR(sta->addr), info->op_class, info->pref,
-		   info->reason_code, info->reason_detail, channels);
+		   info->reason_code, channels);
 }
 
 
@@ -133,9 +132,9 @@
 		char *pos2 = pos;
 
 		ret = os_snprintf(pos2, end - pos2,
-				  "non_pref_chan[%u]=%u:%u:%u:%u:",
+				  "non_pref_chan[%u]=%u:%u:%u:",
 				  count, info->op_class, info->pref,
-				  info->reason_code, info->reason_detail);
+				  info->reason_code);
 		count++;
 		if (os_snprintf_error(end - pos2, ret))
 			break;
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
index 4a87721..3c086bf 100644
--- a/src/ap/ndisc_snoop.c
+++ b/src/ap/ndisc_snoop.c
@@ -17,6 +17,7 @@
 #include "ap_drv_ops.h"
 #include "list.h"
 #include "x_snoop.h"
+#include "ndisc_snoop.h"
 
 struct ip6addr {
 	struct in6_addr addr;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
new file mode 100644
index 0000000..b8fd592
--- /dev/null
+++ b/src/ap/neighbor_db.c
@@ -0,0 +1,136 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "neighbor_db.h"
+
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+		     const struct wpa_ssid_value *ssid)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		if (os_memcmp(bssid, nr->bssid, ETH_ALEN) == 0 &&
+		    (!ssid ||
+		     (ssid->ssid_len == nr->ssid.ssid_len &&
+		      os_memcmp(ssid->ssid, nr->ssid.ssid,
+				ssid->ssid_len) == 0)))
+			return nr;
+	}
+	return NULL;
+}
+
+
+static void hostapd_neighbor_clear_entry(struct hostapd_neighbor_entry *nr)
+{
+	wpabuf_free(nr->nr);
+	nr->nr = NULL;
+	wpabuf_free(nr->lci);
+	nr->lci = NULL;
+	wpabuf_free(nr->civic);
+	nr->civic = NULL;
+	os_memset(nr->bssid, 0, sizeof(nr->bssid));
+	os_memset(&nr->ssid, 0, sizeof(nr->ssid));
+	nr->stationary = 0;
+}
+
+
+static struct hostapd_neighbor_entry *
+hostapd_neighbor_add(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	nr = os_zalloc(sizeof(struct hostapd_neighbor_entry));
+	if (!nr)
+		return NULL;
+
+	dl_list_add(&hapd->nr_db, &nr->list);
+
+	return nr;
+}
+
+
+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,
+			 const struct wpabuf *civic, int stationary)
+{
+	struct hostapd_neighbor_entry *entry;
+
+	entry = hostapd_neighbor_get(hapd, bssid, ssid);
+	if (!entry)
+		entry = hostapd_neighbor_add(hapd);
+	if (!entry)
+		return -1;
+
+	hostapd_neighbor_clear_entry(entry);
+
+	os_memcpy(entry->bssid, bssid, ETH_ALEN);
+	os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
+
+	entry->nr = wpabuf_dup(nr);
+	if (!entry->nr)
+		goto fail;
+
+	if (lci && wpabuf_len(lci)) {
+		entry->lci = wpabuf_dup(lci);
+		if (!entry->lci || os_get_time(&entry->lci_date))
+			goto fail;
+	}
+
+	if (civic && wpabuf_len(civic)) {
+		entry->civic = wpabuf_dup(civic);
+		if (!entry->civic)
+			goto fail;
+	}
+
+	entry->stationary = stationary;
+
+	return 0;
+
+fail:
+	hostapd_neighbor_remove(hapd, bssid, ssid);
+	return -1;
+}
+
+
+int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+			    const struct wpa_ssid_value *ssid)
+{
+	struct hostapd_neighbor_entry *nr;
+
+	nr = hostapd_neighbor_get(hapd, bssid, ssid);
+	if (!nr)
+		return -1;
+
+	hostapd_neighbor_clear_entry(nr);
+	dl_list_del(&nr->list);
+	os_free(nr);
+
+	return 0;
+}
+
+
+void hostpad_free_neighbor_db(struct hostapd_data *hapd)
+{
+	struct hostapd_neighbor_entry *nr, *prev;
+
+	dl_list_for_each_safe(nr, prev, &hapd->nr_db,
+			      struct hostapd_neighbor_entry, list) {
+		hostapd_neighbor_clear_entry(nr);
+		dl_list_del(&nr->list);
+		os_free(nr);
+	}
+}
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
new file mode 100644
index 0000000..ba46d88
--- /dev/null
+++ b/src/ap/neighbor_db.h
@@ -0,0 +1,24 @@
+/*
+ * hostapd / Neighboring APs DB
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NEIGHBOR_DB_H
+#define NEIGHBOR_DB_H
+
+struct hostapd_neighbor_entry *
+hostapd_neighbor_get(struct hostapd_data *hapd, const u8 *bssid,
+		     const struct wpa_ssid_value *ssid);
+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,
+			 const struct wpabuf *civic, int stationary);
+int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
+			    const struct wpa_ssid_value *ssid);
+void hostpad_free_neighbor_db(struct hostapd_data *hapd);
+
+#endif /* NEIGHBOR_DB_H */
diff --git a/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
index efc1d7e..93e775b 100644
--- a/src/ap/peerkey_auth.c
+++ b/src/ap/peerkey_auth.c
@@ -19,17 +19,6 @@
 
 #ifdef CONFIG_PEERKEY
 
-static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx)
-{
-#if 0
-	struct wpa_authenticator *wpa_auth = eloop_ctx;
-	struct wpa_stsl_negotiation *neg = timeout_ctx;
-#endif
-
-	/* TODO: ? */
-}
-
-
 struct wpa_stsl_search {
 	const u8 *addr;
 	struct wpa_state_machine *sm;
@@ -110,7 +99,6 @@
 			   MAC2STR(kde.mac_addr));
 		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
 				   STK_ERR_STA_NR);
-		/* FIX: wpa_stsl_remove(wpa_auth, neg); */
 		return;
 	}
 
@@ -285,7 +273,6 @@
 			   MAC2STR(kde.mac_addr));
 		wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK,
 				   STK_ERR_STA_NR);
-		/* FIX: wpa_stsl_remove(wpa_auth, neg); */
 		return;
 	}
 
@@ -365,32 +352,4 @@
 	wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type);
 }
 
-
-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
-		    struct wpa_stsl_negotiation *neg)
-{
-	struct wpa_stsl_negotiation *pos, *prev;
-
-	if (wpa_auth == NULL)
-		return -1;
-	pos = wpa_auth->stsl_negotiations;
-	prev = NULL;
-	while (pos) {
-		if (pos == neg) {
-			if (prev)
-				prev->next = pos->next;
-			else
-				wpa_auth->stsl_negotiations = pos->next;
-
-			eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos);
-			os_free(pos);
-			return 0;
-		}
-		prev = pos;
-		pos = pos->next;
-	}
-
-	return -1;
-}
-
 #endif /* CONFIG_PEERKEY */
diff --git a/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
index d610e7e..bce5abf 100644
--- a/src/ap/pmksa_cache_auth.c
+++ b/src/ap/pmksa_cache_auth.c
@@ -282,7 +282,42 @@
 		     const u8 *aa, const u8 *spa, int session_timeout,
 		     struct eapol_state_machine *eapol, int akmp)
 {
-	struct rsn_pmksa_cache_entry *entry, *pos;
+	struct rsn_pmksa_cache_entry *entry;
+
+	entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
+					      aa, spa, session_timeout, eapol,
+					      akmp);
+
+	if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
+		return NULL;
+
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_create_entry - Create a PMKSA cache entry
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @pmkid: Calculated PMKID
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @session_timeout: Session timeout
+ * @eapol: Pointer to EAPOL state machine data
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function creates a PMKSA entry.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			      const u8 *kck, size_t kck_len, const u8 *aa,
+			      const u8 *spa, int session_timeout,
+			      struct eapol_state_machine *eapol, int akmp)
+{
+	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
 
 	if (pmk_len > PMK_LEN_MAX)
@@ -315,9 +350,30 @@
 	os_memcpy(entry->spa, spa, ETH_ALEN);
 	pmksa_cache_from_eapol_data(entry, eapol);
 
+	return entry;
+}
+
+
+/**
+ * pmksa_cache_auth_add_entry - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @entry: Pointer to PMKSA cache entry
+ *
+ * This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
+ * already in the cache for the same Supplicant, this entry will be replaced
+ * with the new entry. PMKID will be calculated based on the PMK.
+ */
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+			       struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos;
+
+	if (entry == NULL)
+		return -1;
+
 	/* Replace an old entry for the same STA (if found) with the new entry
 	 */
-	pos = pmksa_cache_auth_get(pmksa, spa, NULL);
+	pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
 	if (pos)
 		pmksa_cache_free_entry(pmksa, pos);
 
@@ -331,7 +387,7 @@
 
 	pmksa_cache_link_entry(pmksa, entry);
 
-	return entry;
+	return 0;
 }
 
 
@@ -605,3 +661,70 @@
 	}
 	return pos - buf;
 }
+
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+/**
+ * pmksa_cache_auth_list_mesh - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ * @addr: MAC address of the peer (NULL means any)
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA_GET command to store
+ * in external storage.
+ */
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+			       char *buf, size_t len)
+{
+	int ret;
+	char *pos, *end;
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	pos = buf;
+	end = buf + len;
+	os_get_reltime(&now);
+
+
+	/*
+	 * Entry format:
+	 * <BSSID> <PMKID> <PMK> <expiration in seconds>
+	 */
+	for (entry = pmksa->pmksa; entry; entry = entry->next) {
+		if (addr && os_memcmp(entry->spa, addr, ETH_ALEN) != 0)
+			continue;
+
+		ret = os_snprintf(pos, end - pos, MACSTR " ",
+				  MAC2STR(entry->spa));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		pos += wpa_snprintf_hex(pos, end - pos, entry->pmkid,
+					PMKID_LEN);
+
+		ret = os_snprintf(pos, end - pos, " ");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		pos += wpa_snprintf_hex(pos, end - pos, entry->pmk,
+					entry->pmk_len);
+
+		ret = os_snprintf(pos, end - pos, " %d\n",
+				  (int) (entry->expiration - now.sec));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	return pos - buf;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
diff --git a/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
index d8d9c5a..bd1b672 100644
--- a/src/ap/pmksa_cache_auth.h
+++ b/src/ap/pmksa_cache_auth.h
@@ -53,6 +53,13 @@
 		     const u8 *aa, const u8 *spa, int session_timeout,
 		     struct eapol_state_machine *eapol, int akmp);
 struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+			      const u8 *kck, size_t kck_len, const u8 *aa,
+			      const u8 *spa, int session_timeout,
+			      struct eapol_state_machine *eapol, int akmp);
+int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
+			       struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
 pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
 		    const struct rsn_pmksa_cache_entry *old_entry,
 		    const u8 *aa, const u8 *pmkid);
@@ -65,5 +72,7 @@
 					   struct radius_das_attrs *attr);
 int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
 void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
+int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
+			       char *buf, size_t len);
 
 #endif /* PMKSA_CACHE_H */
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
new file mode 100644
index 0000000..56ed29c
--- /dev/null
+++ b/src/ap/rrm.c
@@ -0,0 +1,674 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ * Copyright (c) 2016-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "sta_info.h"
+#include "eloop.h"
+#include "neighbor_db.h"
+#include "rrm.h"
+
+#define HOSTAPD_RRM_REQUEST_TIMEOUT 5
+
+
+static void hostapd_lci_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "RRM: LCI request (token %u) timed out",
+		   hapd->lci_req_token);
+	hapd->lci_req_active = 0;
+}
+
+
+static void hostapd_handle_lci_report(struct hostapd_data *hapd, u8 token,
+				      const u8 *pos, size_t len)
+{
+	if (!hapd->lci_req_active || hapd->lci_req_token != token) {
+		wpa_printf(MSG_DEBUG, "Unexpected LCI report, token %u", token);
+		return;
+	}
+
+	hapd->lci_req_active = 0;
+	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+	wpa_printf(MSG_DEBUG, "LCI report token %u len %zu", token, len);
+}
+
+
+static void hostapd_range_rep_timeout_handler(void *eloop_data, void *user_ctx)
+{
+	struct hostapd_data *hapd = eloop_data;
+
+	wpa_printf(MSG_DEBUG, "RRM: Range request (token %u) timed out",
+		   hapd->range_req_token);
+	hapd->range_req_active = 0;
+}
+
+
+static void hostapd_handle_range_report(struct hostapd_data *hapd, u8 token,
+					const u8 *pos, size_t len)
+{
+	if (!hapd->range_req_active || hapd->range_req_token != token) {
+		wpa_printf(MSG_DEBUG, "Unexpected range report, token %u",
+			   token);
+		return;
+	}
+
+	hapd->range_req_active = 0;
+	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+	wpa_printf(MSG_DEBUG, "Range report token %u len %zu", token, len);
+}
+
+
+static void hostapd_handle_beacon_report(struct hostapd_data *hapd,
+					 const u8 *addr, u8 token, u8 rep_mode,
+					 const u8 *pos, size_t len)
+{
+	char report[2 * 255 + 1];
+
+	wpa_printf(MSG_DEBUG, "Beacon report token %u len %zu from " MACSTR,
+		   token, len, MAC2STR(addr));
+	/* Skip to the beginning of the Beacon report */
+	if (len < 3)
+		return;
+	pos += 3;
+	len -= 3;
+	report[0] = '\0';
+	if (wpa_snprintf_hex(report, sizeof(report), pos, len) < 0)
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_RESP_RX MACSTR " %u %02x %s",
+		MAC2STR(addr), token, rep_mode, report);
+}
+
+
+static void hostapd_handle_radio_msmt_report(struct hostapd_data *hapd,
+					     const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+	const u8 *pos, *ie, *end;
+	u8 token, rep_mode;
+
+	end = buf + len;
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+
+	while ((ie = get_ie(pos, end - pos, WLAN_EID_MEASURE_REPORT))) {
+		if (ie[1] < 3) {
+			wpa_printf(MSG_DEBUG, "Bad Measurement Report element");
+			break;
+		}
+
+		rep_mode = ie[3];
+		wpa_printf(MSG_DEBUG, "Measurement report mode 0x%x type %u",
+			   rep_mode, ie[4]);
+
+		switch (ie[4]) {
+		case MEASURE_TYPE_LCI:
+			hostapd_handle_lci_report(hapd, token, ie + 2, ie[1]);
+			break;
+		case MEASURE_TYPE_FTM_RANGE:
+			hostapd_handle_range_report(hapd, token, ie + 2, ie[1]);
+			break;
+		case MEASURE_TYPE_BEACON:
+			hostapd_handle_beacon_report(hapd, mgmt->sa, token,
+						     rep_mode, ie + 2, ie[1]);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG,
+				   "Measurement report type %u is not supported",
+				   ie[4]);
+			break;
+		}
+
+		pos = ie + ie[1] + 2;
+	}
+}
+
+
+static u16 hostapd_parse_location_lci_req_age(const u8 *buf, size_t len)
+{
+	const u8 *subelem;
+
+	/* Range Request element + Location Subject + Maximum Age subelement */
+	if (len < 3 + 1 + 4)
+		return 0;
+
+	/* Subelements are arranged as IEs */
+	subelem = get_ie(buf + 4, len - 4, LCI_REQ_SUBELEM_MAX_AGE);
+	if (subelem && subelem[1] == 2)
+		return WPA_GET_LE16(subelem + 2);
+
+	return 0;
+}
+
+
+static int hostapd_check_lci_age(struct hostapd_neighbor_entry *nr, u16 max_age)
+{
+	struct os_time curr, diff;
+	unsigned long diff_l;
+
+	if (nr->stationary || max_age == 0xffff)
+		return 1;
+
+	if (!max_age)
+		return 0;
+
+	if (os_get_time(&curr))
+		return 0;
+
+	os_time_sub(&curr, &nr->lci_date, &diff);
+
+	/* avoid overflow */
+	if (diff.sec > 0xffff)
+		return 0;
+
+	/* LCI age is calculated in 10th of a second units. */
+	diff_l = diff.sec * 10 + diff.usec / 100000;
+
+	return max_age > diff_l;
+}
+
+
+static size_t hostapd_neighbor_report_len(struct wpabuf *buf,
+					  struct hostapd_neighbor_entry *nr,
+					  int send_lci, int send_civic)
+{
+	size_t len = 2 + wpabuf_len(nr->nr);
+
+	if (send_lci && nr->lci)
+		len += 2 + wpabuf_len(nr->lci);
+
+	if (send_civic && nr->civic)
+		len += 2 + wpabuf_len(nr->civic);
+
+	return len;
+}
+
+
+static void hostapd_send_nei_report_resp(struct hostapd_data *hapd,
+					 const u8 *addr, u8 dialog_token,
+					 struct wpa_ssid_value *ssid, u8 lci,
+					 u8 civic, u16 lci_max_age)
+{
+	struct hostapd_neighbor_entry *nr;
+	struct wpabuf *buf;
+	u8 *msmt_token;
+
+	/*
+	 * The number and length of the Neighbor Report elements in a Neighbor
+	 * Report frame is limited by the maximum allowed MMPDU size; + 3 bytes
+	 * of RRM header.
+	 */
+	buf = wpabuf_alloc(3 + IEEE80211_MAX_MMPDU_SIZE);
+	if (!buf)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_RESPONSE);
+	wpabuf_put_u8(buf, dialog_token);
+
+	dl_list_for_each(nr, &hapd->nr_db, struct hostapd_neighbor_entry,
+			 list) {
+		int send_lci;
+		size_t len;
+
+		if (ssid->ssid_len != nr->ssid.ssid_len ||
+		    os_memcmp(ssid->ssid, nr->ssid.ssid, ssid->ssid_len) != 0)
+			continue;
+
+		send_lci = (lci != 0) && hostapd_check_lci_age(nr, lci_max_age);
+		len = hostapd_neighbor_report_len(buf, nr, send_lci, civic);
+
+		if (len - 2 > 0xff) {
+			wpa_printf(MSG_DEBUG,
+				   "NR entry for " MACSTR " exceeds 0xFF bytes",
+				   MAC2STR(nr->bssid));
+			continue;
+		}
+
+		if (len > wpabuf_tailroom(buf))
+			break;
+
+		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+		wpabuf_put_u8(buf, len - 2);
+		wpabuf_put_buf(buf, nr->nr);
+
+		if (send_lci && nr->lci) {
+			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+			wpabuf_put_u8(buf, wpabuf_len(nr->lci));
+			/*
+			 * Override measurement token - the first byte of the
+			 * Measurement Report element.
+			 */
+			msmt_token = wpabuf_put(buf, 0);
+			wpabuf_put_buf(buf, nr->lci);
+			*msmt_token = lci;
+		}
+
+		if (civic && nr->civic) {
+			wpabuf_put_u8(buf, WLAN_EID_MEASURE_REPORT);
+			wpabuf_put_u8(buf, wpabuf_len(nr->civic));
+			/*
+			 * Override measurement token - the first byte of the
+			 * Measurement Report element.
+			 */
+			msmt_token = wpabuf_put(buf, 0);
+			wpabuf_put_buf(buf, nr->civic);
+			*msmt_token = civic;
+		}
+	}
+
+	hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+}
+
+
+static void hostapd_handle_nei_report_req(struct hostapd_data *hapd,
+					  const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+	const u8 *pos, *ie, *end;
+	struct wpa_ssid_value ssid = {
+		.ssid_len = 0
+	};
+	u8 token;
+	u8 lci = 0, civic = 0; /* Measurement tokens */
+	u16 lci_max_age = 0;
+
+	if (!(hapd->conf->radio_measurements[0] &
+	      WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+		return;
+
+	end = buf + len;
+
+	token = mgmt->u.action.u.rrm.dialog_token;
+	pos = mgmt->u.action.u.rrm.variable;
+	len = end - pos;
+
+	ie = get_ie(pos, len, WLAN_EID_SSID);
+	if (ie && ie[1] && ie[1] <= SSID_MAX_LEN) {
+		ssid.ssid_len = ie[1];
+		os_memcpy(ssid.ssid, ie + 2, ssid.ssid_len);
+	} else {
+		ssid.ssid_len = hapd->conf->ssid.ssid_len;
+		os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+	}
+
+	while ((ie = get_ie(pos, len, WLAN_EID_MEASURE_REQUEST))) {
+		if (ie[1] < 3)
+			break;
+
+		wpa_printf(MSG_DEBUG,
+			   "Neighbor report request, measure type %u",
+			   ie[4]);
+
+		switch (ie[4]) { /* Measurement Type */
+		case MEASURE_TYPE_LCI:
+			lci = ie[2]; /* Measurement Token */
+			lci_max_age = hostapd_parse_location_lci_req_age(ie + 2,
+									 ie[1]);
+			break;
+		case MEASURE_TYPE_LOCATION_CIVIC:
+			civic = ie[2]; /* Measurement token */
+			break;
+		}
+
+		pos = ie + ie[1] + 2;
+		len = end - pos;
+	}
+
+	hostapd_send_nei_report_resp(hapd, mgmt->sa, token, &ssid, lci, civic,
+				     lci_max_age);
+}
+
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+				      const u8 *buf, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt = (const struct ieee80211_mgmt *) buf;
+
+	/*
+	 * Check for enough bytes: header + (1B)Category + (1B)Action +
+	 * (1B)Dialog Token.
+	 */
+	if (len < IEEE80211_HDRLEN + 3)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Radio measurement frame, action %u from " MACSTR,
+		   mgmt->u.action.u.rrm.action, MAC2STR(mgmt->sa));
+
+	switch (mgmt->u.action.u.rrm.action) {
+	case WLAN_RRM_RADIO_MEASUREMENT_REPORT:
+		hostapd_handle_radio_msmt_report(hapd, buf, len);
+		break;
+	case WLAN_RRM_NEIGHBOR_REPORT_REQUEST:
+		hostapd_handle_nei_report_req(hapd, buf, len);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "RRM action %u is not supported",
+			   mgmt->u.action.u.rrm.action);
+		break;
+	}
+}
+
+
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	int ret;
+
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Request LCI: Destination address is not connected");
+		return -1;
+	}
+
+	if (!(sta->rrm_enabled_capa[1] & WLAN_RRM_CAPS_LCI_MEASUREMENT)) {
+		wpa_printf(MSG_INFO,
+			   "Request LCI: Station does not support LCI in RRM");
+		return -1;
+	}
+
+	if (hapd->lci_req_active) {
+		wpa_printf(MSG_DEBUG,
+			   "Request LCI: LCI request is already in process, overriding");
+		hapd->lci_req_active = 0;
+		eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd,
+				     NULL);
+	}
+
+	/* Measurement request (5) + Measurement element with LCI (10) */
+	buf = wpabuf_alloc(5 + 10);
+	if (!buf)
+		return -1;
+
+	hapd->lci_req_token++;
+	/* For wraparounds - the token must be nonzero */
+	if (!hapd->lci_req_token)
+		hapd->lci_req_token++;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->lci_req_token);
+	wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	wpabuf_put_u8(buf, 3 + 1 + 4);
+
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	/*
+	 * Parallel and Enable bits are 0, Duration, Request, and Report are
+	 * reserved.
+	 */
+	wpabuf_put_u8(buf, 0);
+	wpabuf_put_u8(buf, MEASURE_TYPE_LCI);
+
+	wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+
+	wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
+	wpabuf_put_u8(buf, 2);
+	wpabuf_put_le16(buf, 0xffff);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret)
+		return ret;
+
+	hapd->lci_req_active = 1;
+
+	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+			       hostapd_lci_rep_timeout_handler, hapd, NULL);
+
+	return 0;
+}
+
+
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   u16 random_interval, u8 min_ap,
+			   const u8 *responders, unsigned int n_responders)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta;
+	u8 *len;
+	unsigned int i;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "Request range: dest addr " MACSTR
+		   " rand interval %u min AP %u n_responders %u", MAC2STR(addr),
+		   random_interval, min_ap, n_responders);
+
+	if (min_ap == 0 || min_ap > n_responders) {
+		wpa_printf(MSG_INFO, "Request range: Wrong min AP count");
+		return -1;
+	}
+
+	sta = ap_get_sta(hapd, addr);
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Request range: Destination address is not connected");
+		return -1;
+	}
+
+	if (!(sta->rrm_enabled_capa[4] & WLAN_RRM_CAPS_FTM_RANGE_REPORT)) {
+		wpa_printf(MSG_ERROR,
+			   "Request range: Destination station does not support FTM range report in RRM");
+		return -1;
+	}
+
+	if (hapd->range_req_active) {
+		wpa_printf(MSG_DEBUG,
+			   "Request range: Range request is already in process; overriding");
+		hapd->range_req_active = 0;
+		eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd,
+				     NULL);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	buf = wpabuf_alloc(7 + 255);
+	if (!buf)
+		return -1;
+
+	hapd->range_req_token++;
+	if (!hapd->range_req_token) /* For wraparounds */
+		hapd->range_req_token++;
+
+	/* IEEE P802.11-REVmc/D5.0, 9.6.7.2 */
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->range_req_token); /* Dialog Token */
+	wpabuf_put_le16(buf, 0); /* Number of Repetitions */
+
+	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21 */
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	len = wpabuf_put(buf, 1); /* Length will be set later */
+
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	/*
+	 * Parallel and Enable bits are 0; Duration, Request, and Report are
+	 * reserved.
+	 */
+	wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+	wpabuf_put_u8(buf, MEASURE_TYPE_FTM_RANGE); /* Measurement Type */
+
+	/* IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 */
+	wpabuf_put_le16(buf, random_interval); /* Randomization Interval */
+	wpabuf_put_u8(buf, min_ap); /* Minimum AP Count */
+
+	/* FTM Range Subelements */
+
+	/*
+	 * Taking the neighbor report part of the range request from neighbor
+	 * database instead of requesting the separate bits of data from the
+	 * user.
+	 */
+	for (i = 0; i < n_responders; i++) {
+		struct hostapd_neighbor_entry *nr;
+
+		nr = hostapd_neighbor_get(hapd, responders + ETH_ALEN * i,
+					  NULL);
+		if (!nr) {
+			wpa_printf(MSG_INFO, "Missing neighbor report for "
+				   MACSTR, MAC2STR(responders + ETH_ALEN * i));
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		if (wpabuf_tailroom(buf) < 2 + wpabuf_len(nr->nr)) {
+			wpa_printf(MSG_ERROR, "Too long range request");
+			wpabuf_free(buf);
+			return -1;
+		}
+
+		wpabuf_put_u8(buf, WLAN_EID_NEIGHBOR_REPORT);
+		wpabuf_put_u8(buf, wpabuf_len(nr->nr));
+		wpabuf_put_buf(buf, nr->nr);
+	}
+
+	/* Action + measurement type + token + reps + EID + len = 7 */
+	*len = wpabuf_len(buf) - 7;
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret)
+		return ret;
+
+	hapd->range_req_active = 1;
+
+	eloop_register_timeout(HOSTAPD_RRM_REQUEST_TIMEOUT, 0,
+			       hostapd_range_rep_timeout_handler, hapd, NULL);
+
+	return 0;
+}
+
+
+void hostapd_clean_rrm(struct hostapd_data *hapd)
+{
+	hostpad_free_neighbor_db(hapd);
+	eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
+	hapd->lci_req_active = 0;
+	eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
+	hapd->range_req_active = 0;
+}
+
+
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+			    u8 req_mode, const struct wpabuf *req)
+{
+	struct wpabuf *buf;
+	struct sta_info *sta = ap_get_sta(hapd, addr);
+	int ret;
+	enum beacon_report_mode mode;
+	const u8 *pos;
+
+	/* Request data:
+	 * Operating Class (1), Channel Number (1), Randomization Interval (2),
+	 * Measurement Duration (2), Measurement Mode (1), BSSID (6),
+	 * Optional Subelements (variable)
+	 */
+	if (wpabuf_len(req) < 13) {
+		wpa_printf(MSG_INFO, "Beacon request: Too short request data");
+		return -1;
+	}
+	pos = wpabuf_head(req);
+	mode = pos[6];
+
+	if (!sta || !(sta->flags & WLAN_STA_AUTHORIZED)) {
+		wpa_printf(MSG_INFO,
+			   "Beacon request: " MACSTR " is not connected",
+			   MAC2STR(addr));
+		return -1;
+	}
+
+	switch (mode) {
+	case BEACON_REPORT_MODE_PASSIVE:
+		if (!(sta->rrm_enabled_capa[0] &
+		      WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE)) {
+			wpa_printf(MSG_INFO,
+				   "Beacon request: " MACSTR
+				   " does not support passive beacon report",
+				   MAC2STR(addr));
+			return -1;
+		}
+		break;
+	case BEACON_REPORT_MODE_ACTIVE:
+		if (!(sta->rrm_enabled_capa[0] &
+		      WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE)) {
+			wpa_printf(MSG_INFO,
+				   "Beacon request: " MACSTR
+				   " does not support active beacon report",
+				   MAC2STR(addr));
+			return -1;
+		}
+		break;
+	case BEACON_REPORT_MODE_TABLE:
+		if (!(sta->rrm_enabled_capa[0] &
+		      WLAN_RRM_CAPS_BEACON_REPORT_TABLE)) {
+			wpa_printf(MSG_INFO,
+				   "Beacon request: " MACSTR
+				   " does not support table beacon report",
+				   MAC2STR(addr));
+			return -1;
+		}
+		break;
+	default:
+		wpa_printf(MSG_INFO,
+			   "Beacon request: Unknown measurement mode %d", mode);
+		return -1;
+	}
+
+	buf = wpabuf_alloc(5 + 2 + 3 + wpabuf_len(req));
+	if (!buf)
+		return -1;
+
+	hapd->beacon_req_token++;
+	if (!hapd->beacon_req_token)
+		hapd->beacon_req_token++;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_RADIO_MEASUREMENT_REQUEST);
+	wpabuf_put_u8(buf, hapd->beacon_req_token);
+	wpabuf_put_le16(buf, 0); /* Number of repetitions */
+
+	/* Measurement Request element */
+	wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+	wpabuf_put_u8(buf, 3 + wpabuf_len(req));
+	wpabuf_put_u8(buf, 1); /* Measurement Token */
+	wpabuf_put_u8(buf, req_mode); /* Measurement Request Mode */
+	wpabuf_put_u8(buf, MEASURE_TYPE_BEACON); /* Measurement Type */
+	wpabuf_put_buf(buf, req);
+
+	ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+				      wpabuf_head(buf), wpabuf_len(buf));
+	wpabuf_free(buf);
+	if (ret < 0)
+		return ret;
+
+	return hapd->beacon_req_token;
+}
+
+
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len, int ok)
+{
+	if (len < 24 + 3)
+		return;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, BEACON_REQ_TX_STATUS MACSTR
+		" %u ack=%d", MAC2STR(mgmt->da),
+		mgmt->u.action.u.rrm.dialog_token, ok);
+}
diff --git a/src/ap/rrm.h b/src/ap/rrm.h
new file mode 100644
index 0000000..02cd522
--- /dev/null
+++ b/src/ap/rrm.h
@@ -0,0 +1,33 @@
+/*
+ * hostapd / Radio Measurement (RRM)
+ * Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+ * Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef RRM_H
+#define RRM_H
+
+/*
+ * Max measure request length is 255, -6 of the body we have 249 for the
+ * neighbor report elements. Each neighbor report element is at least 2 + 13
+ * bytes, so we can't have more than 16 responders in the request.
+ */
+#define RRM_RANGE_REQ_MAX_RESPONDERS 16
+
+void hostapd_handle_radio_measurement(struct hostapd_data *hapd,
+				      const u8 *buf, size_t len);
+int hostapd_send_lci_req(struct hostapd_data *hapd, const u8 *addr);
+int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
+			   u16 random_interval, u8 min_ap,
+			   const u8 *responders, unsigned int n_responders);
+void hostapd_clean_rrm(struct hostapd_data *hapd);
+int hostapd_send_beacon_req(struct hostapd_data *hapd, const u8 *addr,
+			    u8 req_mode, const struct wpabuf *req);
+void hostapd_rrm_beacon_req_tx_status(struct hostapd_data *hapd,
+				      const struct ieee80211_mgmt *mgmt,
+				      size_t len, int ok);
+
+#endif /* RRM_H */
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index c36842b..af8c754 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2016, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -36,6 +36,7 @@
 #include "ndisc_snoop.h"
 #include "sta_info.h"
 #include "vlan.h"
+#include "wps_hostapd.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
 				       struct sta_info *sta);
@@ -47,6 +48,7 @@
 static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx);
 #endif /* CONFIG_IEEE80211W */
 static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta);
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx);
 
 int ap_for_each_sta(struct hostapd_data *hapd,
 		    int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
@@ -222,6 +224,13 @@
 		hapd->iface->num_sta_ht_20mhz--;
 	}
 
+#ifdef CONFIG_TAXONOMY
+	wpabuf_free(sta->probe_ie_taxonomy);
+	sta->probe_ie_taxonomy = NULL;
+	wpabuf_free(sta->assoc_ie_taxonomy);
+	sta->assoc_ie_taxonomy = NULL;
+#endif /* CONFIG_TAXONOMY */
+
 #ifdef CONFIG_IEEE80211N
 	ht40_intolerant_remove(hapd->iface, sta);
 #endif /* CONFIG_IEEE80211N */
@@ -330,6 +339,13 @@
 	mbo_ap_sta_free(sta);
 	os_free(sta->supp_op_classes);
 
+#ifdef CONFIG_FILS
+	os_free(sta->fils_pending_assoc_req);
+	wpabuf_free(sta->fils_hlp_resp);
+	wpabuf_free(sta->hlp_dhcp_discover);
+	eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
+#endif /* CONFIG_FILS */
+
 	os_free(sta);
 }
 
@@ -660,6 +676,11 @@
 	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
 	dl_list_init(&sta->ip6addr);
 
+#ifdef CONFIG_TAXONOMY
+	sta_track_claim_taxonomy_info(hapd->iface, addr,
+				      &sta->probe_ie_taxonomy);
+#endif /* CONFIG_TAXONOMY */
+
 	return sta;
 }
 
@@ -733,9 +754,17 @@
 	wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
 		   hapd->conf->iface, MAC2STR(sta->addr));
 	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
-	sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Skip deauthentication in DMG/IEEE 802.11ad */
+		sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+				WLAN_STA_ASSOC_REQ_OK);
+		sta->timeout_next = STA_REMOVE;
+	} else {
+		sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
+		sta->timeout_next = STA_DEAUTH;
+	}
 	ap_sta_set_authorized(hapd, sta, 0);
-	sta->timeout_next = STA_DEAUTH;
 	wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
 		   "for " MACSTR " (%d seconds - "
 		   "AP_MAX_INACTIVITY_AFTER_DISASSOC)",
@@ -771,6 +800,14 @@
 void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 			   u16 reason)
 {
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		ap_sta_disassociate(hapd, sta, reason);
+		return;
+	}
+
 	wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
 		   hapd->conf->iface, MAC2STR(sta->addr));
 	sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
@@ -1217,6 +1254,20 @@
 			       ap_handle_timer, hapd, sta);
 	sta->timeout_next = STA_REMOVE;
 
+	if (hapd->iface->current_mode &&
+	    hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		sta->disassoc_reason = reason;
+		sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
+		eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+		eloop_register_timeout(hapd->iface->drv_flags &
+				       WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS ?
+				       2 : 0, 0, ap_sta_disassoc_cb_timeout,
+				       hapd, sta);
+		return;
+	}
+
 	sta->deauth_reason = reason;
 	sta->flags |= WLAN_STA_PENDING_DEAUTH_CB;
 	eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
@@ -1263,6 +1314,15 @@
 			   "%s: Removed ap_sta_disassoc_cb_timeout timeout for "
 			   MACSTR,
 			   hapd->conf->iface, MAC2STR(sta->addr));
+	if (eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta) > 0)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "%s: Removed ap_sta_delayed_1x_auth_fail_cb timeout for "
+			   MACSTR,
+			   hapd->conf->iface, MAC2STR(sta->addr));
+		if (sta->flags & WLAN_STA_WPS)
+			hostapd_wps_eap_completed(hapd);
+	}
 }
 
 
@@ -1297,3 +1357,45 @@
 
 	return res;
 }
+
+
+static void ap_sta_delayed_1x_auth_fail_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+		"IEEE 802.1X: Scheduled disconnection of " MACSTR
+		" after EAP-Failure", MAC2STR(sta->addr));
+
+	ap_sta_disconnect(hapd, sta, sta->addr,
+			  WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+	if (sta->flags & WLAN_STA_WPS)
+		hostapd_wps_eap_completed(hapd);
+}
+
+
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+					    struct sta_info *sta)
+{
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+		"IEEE 802.1X: Force disconnection of " MACSTR
+		" after EAP-Failure in 10 ms", MAC2STR(sta->addr));
+
+	/*
+	 * Add a small sleep to increase likelihood of previously requested
+	 * EAP-Failure TX getting out before this should the driver reorder
+	 * operations.
+	 */
+	eloop_cancel_timeout(ap_sta_delayed_1x_auth_fail_cb, hapd, sta);
+	eloop_register_timeout(0, 10000, ap_sta_delayed_1x_auth_fail_cb,
+			       hapd, sta);
+}
+
+
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+						   struct sta_info *sta)
+{
+	return eloop_is_timeout_registered(ap_sta_delayed_1x_auth_fail_cb,
+					   hapd, sta);
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 3d9a928..6f55403 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -12,10 +12,12 @@
 #ifdef CONFIG_MESH
 /* needed for mesh_plink_state enum */
 #include "common/defs.h"
+#include "common/wpa_common.h"
 #endif /* CONFIG_MESH */
 
 #include "list.h"
 #include "vlan.h"
+#include "common/ieee802_11_defs.h"
 
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
@@ -37,6 +39,7 @@
 #define WLAN_STA_WNM_SLEEP_MODE BIT(19)
 #define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
 #define WLAN_STA_VENDOR_VHT BIT(21)
+#define WLAN_STA_PENDING_FILS_ERP BIT(22)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -51,7 +54,6 @@
 	u8 op_class;
 	u8 pref;
 	u8 reason_code;
-	u8 reason_detail;
 	u8 num_channels;
 	u8 channels[];
 };
@@ -79,13 +81,22 @@
 	enum mesh_plink_state plink_state;
 	u16 peer_lid;
 	u16 my_lid;
+	u16 peer_aid;
 	u16 mpm_close_reason;
 	int mpm_retries;
-	u8 my_nonce[32];
-	u8 peer_nonce[32];
+	u8 my_nonce[WPA_NONCE_LEN];
+	u8 peer_nonce[WPA_NONCE_LEN];
 	u8 aek[32];	/* SHA256 digest length */
-	u8 mtk[16];
-	u8 mgtk[16];
+	u8 mtk[WPA_TK_MAX_LEN];
+	size_t mtk_len;
+	u8 mgtk_rsc[6];
+	u8 mgtk_key_id;
+	u8 mgtk[WPA_TK_MAX_LEN];
+	size_t mgtk_len;
+	u8 igtk_rsc[6];
+	u8 igtk[WPA_TK_MAX_LEN];
+	size_t igtk_len;
+	u16 igtk_key_id;
 	u8 sae_auth_retry;
 #endif /* CONFIG_MESH */
 
@@ -104,6 +115,7 @@
 	unsigned int radius_das_match:1;
 	unsigned int ecsa_supported:1;
 	unsigned int added_unassoc:1;
+	unsigned int pending_wds_enable:1;
 
 	u16 auth_alg;
 
@@ -202,6 +214,24 @@
 
 	u8 *supp_op_classes; /* Supported Operating Classes element, if
 			      * received, starting from the Length field */
+
+	u8 rrm_enabled_capa[5];
+
+#ifdef CONFIG_TAXONOMY
+	struct wpabuf *probe_ie_taxonomy;
+	struct wpabuf *assoc_ie_taxonomy;
+#endif /* CONFIG_TAXONOMY */
+
+#ifdef CONFIG_FILS
+	u8 fils_snonce[FILS_NONCE_LEN];
+	u8 fils_session[FILS_SESSION_LEN];
+	u8 *fils_pending_assoc_req;
+	size_t fils_pending_assoc_req_len;
+	unsigned int fils_pending_assoc_is_reassoc:1;
+	unsigned int fils_dhcp_rapid_commit_proxy:1;
+	struct wpabuf *fils_hlp_resp;
+	struct wpabuf *hlp_dhcp_discover;
+#endif /* CONFIG_FILS */
 };
 
 
@@ -273,5 +303,9 @@
 				      struct sta_info *sta);
 
 int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+void ap_sta_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+					    struct sta_info *sta);
+int ap_sta_pending_delayed_1x_auth_fail_disconnect(struct hostapd_data *hapd,
+						   struct sta_info *sta);
 
 #endif /* STA_INFO_H */
diff --git a/src/ap/taxonomy.c b/src/ap/taxonomy.c
new file mode 100644
index 0000000..ae157a7
--- /dev/null
+++ b/src/ap/taxonomy.c
@@ -0,0 +1,292 @@
+/*
+ * hostapd / Client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * Parse a series of IEs, as in Probe Request or (Re)Association Request frames,
+ * and render them to a descriptive string. The tag number of standard options
+ * is written to the string, while the vendor ID and subtag are written for
+ * vendor options.
+ *
+ * Example strings:
+ * 0,1,50,45,221(00904c,51)
+ * 0,1,33,36,48,45,221(00904c,51),221(0050f2,2)
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "taxonomy.h"
+
+
+/* Copy a string with no funny schtuff allowed; only alphanumerics. */
+static void no_mischief_strncpy(char *dst, const char *src, size_t n)
+{
+	size_t i;
+
+	for (i = 0; i < n; i++) {
+		unsigned char s = src[i];
+		int is_lower = s >= 'a' && s <= 'z';
+		int is_upper = s >= 'A' && s <= 'Z';
+		int is_digit = s >= '0' && s <= '9';
+
+		if (is_lower || is_upper || is_digit) {
+			/* TODO: if any manufacturer uses Unicode within the
+			 * WPS header, it will get mangled here. */
+			dst[i] = s;
+		} else {
+			/* Note that even spaces will be transformed to
+			 * underscores, so 'Nexus 7' will turn into 'Nexus_7'.
+			 * This is deliberate, to make the string easier to
+			 * parse. */
+			dst[i] = '_';
+		}
+	}
+}
+
+
+static int get_wps_name(char *name, size_t name_len,
+			const u8 *data, size_t data_len)
+{
+	/* Inside the WPS IE are a series of attributes, using two byte IDs
+	 * and two byte lengths. We're looking for the model name, if
+	 * present. */
+	while (data_len >= 4) {
+		u16 id, elen;
+
+		id = WPA_GET_BE16(data);
+		elen = WPA_GET_BE16(data + 2);
+		data += 4;
+		data_len -= 4;
+
+		if (elen > data_len)
+			return 0;
+
+		if (id == 0x1023) {
+			/* Model name, like 'Nexus 7' */
+			size_t n = (elen < name_len) ? elen : name_len;
+			no_mischief_strncpy(name, (const char *) data, n);
+			return n;
+		}
+
+		data += elen;
+		data_len -= elen;
+	}
+
+	return 0;
+}
+
+
+static void ie_to_string(char *fstr, size_t fstr_len, const struct wpabuf *ies)
+{
+	char *fpos = fstr;
+	char *fend = fstr + fstr_len;
+	char htcap[7 + 4 + 1]; /* ",htcap:" + %04hx + trailing NUL */
+	char htagg[7 + 2 + 1]; /* ",htagg:" + %02hx + trailing NUL */
+	char htmcs[7 + 8 + 1]; /* ",htmcs:" + %08x + trailing NUL */
+	char vhtcap[8 + 8 + 1]; /* ",vhtcap:" + %08x + trailing NUL */
+	char vhtrxmcs[10 + 8 + 1]; /* ",vhtrxmcs:" + %08x + trailing NUL */
+	char vhttxmcs[10 + 8 + 1]; /* ",vhttxmcs:" + %08x + trailing NUL */
+#define MAX_EXTCAP	254
+	char extcap[8 + 2 * MAX_EXTCAP + 1]; /* ",extcap:" + hex + trailing NUL
+					      */
+	char txpow[7 + 4 + 1]; /* ",txpow:" + %04hx + trailing NUL */
+#define WPS_NAME_LEN		32
+	char wps[WPS_NAME_LEN + 5 + 1]; /* room to prepend ",wps:" + trailing
+					 * NUL */
+	int num = 0;
+	const u8 *ie;
+	size_t ie_len;
+	int ret;
+
+	os_memset(htcap, 0, sizeof(htcap));
+	os_memset(htagg, 0, sizeof(htagg));
+	os_memset(htmcs, 0, sizeof(htmcs));
+	os_memset(vhtcap, 0, sizeof(vhtcap));
+	os_memset(vhtrxmcs, 0, sizeof(vhtrxmcs));
+	os_memset(vhttxmcs, 0, sizeof(vhttxmcs));
+	os_memset(extcap, 0, sizeof(extcap));
+	os_memset(txpow, 0, sizeof(txpow));
+	os_memset(wps, 0, sizeof(wps));
+	*fpos = '\0';
+
+	if (!ies)
+		return;
+	ie = wpabuf_head(ies);
+	ie_len = wpabuf_len(ies);
+
+	while (ie_len >= 2) {
+		u8 id, elen;
+		char *sep = (num++ == 0) ? "" : ",";
+
+		id = *ie++;
+		elen = *ie++;
+		ie_len -= 2;
+
+		if (elen > ie_len)
+			break;
+
+		if (id == WLAN_EID_VENDOR_SPECIFIC && elen >= 4) {
+			/* Vendor specific */
+			if (WPA_GET_BE32(ie) == WPS_IE_VENDOR_TYPE) {
+				/* WPS */
+				char model_name[WPS_NAME_LEN + 1];
+				const u8 *data = &ie[4];
+				size_t data_len = elen - 4;
+
+				os_memset(model_name, 0, sizeof(model_name));
+				if (get_wps_name(model_name, WPS_NAME_LEN, data,
+						 data_len)) {
+					os_snprintf(wps, sizeof(wps),
+						    ",wps:%s", model_name);
+				}
+			}
+
+			ret = os_snprintf(fpos, fend - fpos,
+					  "%s%d(%02x%02x%02x,%d)",
+					  sep, id, ie[0], ie[1], ie[2], ie[3]);
+		} else {
+			if (id == WLAN_EID_HT_CAP && elen >= 2) {
+				/* HT Capabilities (802.11n) */
+				os_snprintf(htcap, sizeof(htcap),
+					    ",htcap:%04hx",
+					    WPA_GET_LE16(ie));
+			}
+			if (id == WLAN_EID_HT_CAP && elen >= 3) {
+				/* HT Capabilities (802.11n), A-MPDU information
+				 */
+				os_snprintf(htagg, sizeof(htagg),
+					    ",htagg:%02hx", (u16) ie[2]);
+			}
+			if (id == WLAN_EID_HT_CAP && elen >= 7) {
+				/* HT Capabilities (802.11n), MCS information */
+				os_snprintf(htmcs, sizeof(htmcs),
+					    ",htmcs:%08hx",
+					    (u16) WPA_GET_LE32(ie + 3));
+			}
+			if (id == WLAN_EID_VHT_CAP && elen >= 4) {
+				/* VHT Capabilities (802.11ac) */
+				os_snprintf(vhtcap, sizeof(vhtcap),
+					    ",vhtcap:%08x",
+					    WPA_GET_LE32(ie));
+			}
+			if (id == WLAN_EID_VHT_CAP && elen >= 8) {
+				/* VHT Capabilities (802.11ac), RX MCS
+				 * information */
+				os_snprintf(vhtrxmcs, sizeof(vhtrxmcs),
+					    ",vhtrxmcs:%08x",
+					    WPA_GET_LE32(ie + 4));
+			}
+			if (id == WLAN_EID_VHT_CAP && elen >= 12) {
+				/* VHT Capabilities (802.11ac), TX MCS
+				 * information */
+				os_snprintf(vhttxmcs, sizeof(vhttxmcs),
+					    ",vhttxmcs:%08x",
+					    WPA_GET_LE32(ie + 8));
+			}
+			if (id == WLAN_EID_EXT_CAPAB) {
+				/* Extended Capabilities */
+				int i;
+				int len = (elen < MAX_EXTCAP) ? elen :
+					MAX_EXTCAP;
+				char *p = extcap;
+
+				p += os_snprintf(extcap, sizeof(extcap),
+						 ",extcap:");
+				for (i = 0; i < len; i++) {
+					int lim;
+
+					lim = sizeof(extcap) -
+						os_strlen(extcap);
+					if (lim <= 0)
+						break;
+					p += os_snprintf(p, lim, "%02x",
+							 *(ie + i));
+				}
+			}
+			if (id == WLAN_EID_PWR_CAPABILITY && elen == 2) {
+				/* TX Power */
+				os_snprintf(txpow, sizeof(txpow),
+					    ",txpow:%04hx",
+					    WPA_GET_LE16(ie));
+			}
+
+			ret = os_snprintf(fpos, fend - fpos, "%s%d", sep, id);
+		}
+		if (os_snprintf_error(fend - fpos, ret))
+			goto fail;
+		fpos += ret;
+
+		ie += elen;
+		ie_len -= elen;
+	}
+
+	ret = os_snprintf(fpos, fend - fpos, "%s%s%s%s%s%s%s%s%s",
+			  htcap, htagg, htmcs, vhtcap, vhtrxmcs, vhttxmcs,
+			  txpow, extcap, wps);
+	if (os_snprintf_error(fend - fpos, ret)) {
+	fail:
+		fstr[0] = '\0';
+	}
+}
+
+
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+			  struct sta_info *sta, char *buf, size_t buflen)
+{
+	int ret;
+	char *pos, *end;
+
+	if (!sta->probe_ie_taxonomy || !sta->assoc_ie_taxonomy)
+		return 0;
+
+	ret = os_snprintf(buf, buflen, "wifi4|probe:");
+	if (os_snprintf_error(buflen, ret))
+		return 0;
+	pos = buf + ret;
+	end = buf + buflen;
+
+	ie_to_string(pos, end - pos, sta->probe_ie_taxonomy);
+	pos = os_strchr(pos, '\0');
+	if (pos >= end)
+		return 0;
+	ret = os_snprintf(pos, end - pos, "|assoc:");
+	if (os_snprintf_error(end - pos, ret))
+		return 0;
+	pos += ret;
+	ie_to_string(pos, end - pos, sta->assoc_ie_taxonomy);
+	pos = os_strchr(pos, '\0');
+	return pos - buf;
+}
+
+
+void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len)
+{
+	wpabuf_free(sta->probe_ie_taxonomy);
+	sta->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
+
+
+void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
+					 struct hostapd_sta_info *info,
+					 const u8 *ie, size_t ie_len)
+{
+	wpabuf_free(info->probe_ie_taxonomy);
+	info->probe_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
+
+
+void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len)
+{
+	wpabuf_free(sta->assoc_ie_taxonomy);
+	sta->assoc_ie_taxonomy = wpabuf_alloc_copy(ie, ie_len);
+}
diff --git a/src/ap/taxonomy.h b/src/ap/taxonomy.h
new file mode 100644
index 0000000..80f245c
--- /dev/null
+++ b/src/ap/taxonomy.h
@@ -0,0 +1,24 @@
+/*
+ * hostapd / Station client taxonomy
+ * Copyright (c) 2015 Google, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TAXONOMY_H
+#define TAXONOMY_H
+
+void taxonomy_sta_info_probe_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len);
+void taxonomy_hostapd_sta_info_probe_req(const struct hostapd_data *hapd,
+					 struct hostapd_sta_info *sta,
+					 const u8 *ie, size_t ie_len);
+void taxonomy_sta_info_assoc_req(const struct hostapd_data *hapd,
+				 struct sta_info *sta,
+				 const u8 *ie, size_t ie_len);
+int retrieve_sta_taxonomy(const struct hostapd_data *hapd,
+			  struct sta_info *sta, char *buf, size_t buflen);
+
+#endif /* TAXONOMY_H */
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
index 314e244..8054c5d 100644
--- a/src/ap/wmm.c
+++ b/src/ap/wmm.c
@@ -21,11 +21,6 @@
 #include "wmm.h"
 
 
-/* TODO: maintain separate sequence and fragment numbers for each AC
- * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA
- * if only WMM stations are receiving a certain group */
-
-
 static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
 {
 	u8 ret;
@@ -157,8 +152,9 @@
 
 int wmm_process_tspec(struct wmm_tspec_element *tspec)
 {
-	int medium_time, pps, duration;
-	int up, psb, dir, tid;
+	u64 medium_time;
+	unsigned int pps, duration;
+	unsigned int up, psb, dir, tid;
 	u16 val, surplus;
 
 	up = (tspec->ts_info[1] >> 3) & 0x07;
@@ -206,8 +202,9 @@
 		return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
 	}
 
-	medium_time = surplus * pps * duration / 0x2000;
-	wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
+	medium_time = (u64) surplus * pps * duration / 0x2000;
+	wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %lu",
+		   (unsigned long) medium_time);
 
 	/*
 	 * TODO: store list of granted (and still active) TSPECs and check
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 02daa9b..adb66c1 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -95,8 +95,8 @@
 	if (mgmt == NULL) {
 		wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
 			   "WNM-Sleep Response action frame");
-		os_free(wnmtfs_ie);
-		return -1;
+		res = -1;
+		goto fail;
 	}
 	os_memcpy(mgmt->da, addr, ETH_ALEN);
 	os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
@@ -118,11 +118,8 @@
 			   (int) gtk_elem_len);
 #ifdef CONFIG_IEEE80211W
 		res = wpa_wnmsleep_igtk_subelem(sta->wpa_sm, pos);
-		if (res < 0) {
-			os_free(wnmtfs_ie);
-			os_free(mgmt);
-			return -1;
-		}
+		if (res < 0)
+			goto fail;
 		igtk_elem_len = res;
 		pos += igtk_elem_len;
 		wpa_printf(MSG_DEBUG, "Pass 4 igtk_len = %d",
@@ -184,6 +181,7 @@
 
 #undef MAX_GTK_SUBELEM_LEN
 #undef MAX_IGTK_SUBELEM_LEN
+fail:
 	os_free(wnmtfs_ie);
 	os_free(mgmt);
 	return res;
@@ -214,7 +212,8 @@
 		u8 ie_len = pos[1];
 		if (pos + 2 + ie_len > frm + len)
 			break;
-		if (*pos == WLAN_EID_WNMSLEEP)
+		if (*pos == WLAN_EID_WNMSLEEP &&
+		    ie_len >= (int) sizeof(*wnmsleep_ie) - 2)
 			wnmsleep_ie = (struct wnm_sleep_element *) pos;
 		else if (*pos == WLAN_EID_TFS_REQ) {
 			if (!tfsreq_ie_start)
@@ -258,20 +257,14 @@
 
 static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
 						  const u8 *addr,
-						  u8 dialog_token,
-						  const char *url)
+						  u8 dialog_token)
 {
 	struct ieee80211_mgmt *mgmt;
-	size_t url_len, len;
+	size_t len;
 	u8 *pos;
 	int res;
 
-	if (url)
-		url_len = os_strlen(url);
-	else
-		url_len = 0;
-
-	mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0));
+	mgmt = os_zalloc(sizeof(*mgmt));
 	if (mgmt == NULL)
 		return -1;
 	os_memcpy(mgmt->da, addr, ETH_ALEN);
@@ -286,11 +279,6 @@
 	mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
 	mgmt->u.action.u.bss_tm_req.validity_interval = 1;
 	pos = mgmt->u.action.u.bss_tm_req.variable;
-	if (url) {
-		*pos++ += url_len;
-		os_memcpy(pos, url, url_len);
-		pos += url_len;
-	}
 
 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
 		   MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
@@ -333,7 +321,7 @@
 	wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
 		    pos, end - pos);
 
-	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL);
+	ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token);
 }
 
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index bf10cc1..85e85e0 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -13,7 +13,9 @@
 #include "utils/state_machine.h"
 #include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/aes.h"
 #include "crypto/aes_wrap.h"
+#include "crypto/aes_siv.h"
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
@@ -35,6 +37,10 @@
 static int wpa_sm_step(struct wpa_state_machine *sm);
 static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
 			      size_t data_len);
+#ifdef CONFIG_FILS
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			    u8 *buf, size_t buf_len, u16 *_key_data_len);
+#endif /* CONFIG_FILS */
 static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
 static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
 			      struct wpa_group *group);
@@ -52,9 +58,8 @@
 			  struct wpa_group *group);
 static void wpa_group_put(struct wpa_authenticator *wpa_auth,
 			  struct wpa_group *group);
+static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
 
-static const u32 dot11RSNAConfigGroupUpdateCount = 4;
-static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
 static const u32 eapol_key_timeout_first = 100; /* ms */
 static const u32 eapol_key_timeout_subseq = 1000; /* ms */
 static const u32 eapol_key_timeout_first_group = 500; /* ms */
@@ -68,8 +73,8 @@
 static inline int wpa_auth_mic_failure_report(
 	struct wpa_authenticator *wpa_auth, const u8 *addr)
 {
-	if (wpa_auth->cb.mic_failure_report)
-		return wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr);
+	if (wpa_auth->cb->mic_failure_report)
+		return wpa_auth->cb->mic_failure_report(wpa_auth->cb_ctx, addr);
 	return 0;
 }
 
@@ -77,8 +82,8 @@
 static inline void wpa_auth_psk_failure_report(
 	struct wpa_authenticator *wpa_auth, const u8 *addr)
 {
-	if (wpa_auth->cb.psk_failure_report)
-		wpa_auth->cb.psk_failure_report(wpa_auth->cb.ctx, addr);
+	if (wpa_auth->cb->psk_failure_report)
+		wpa_auth->cb->psk_failure_report(wpa_auth->cb_ctx, addr);
 }
 
 
@@ -86,17 +91,17 @@
 				      const u8 *addr, wpa_eapol_variable var,
 				      int value)
 {
-	if (wpa_auth->cb.set_eapol)
-		wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value);
+	if (wpa_auth->cb->set_eapol)
+		wpa_auth->cb->set_eapol(wpa_auth->cb_ctx, addr, var, value);
 }
 
 
 static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
 				     const u8 *addr, wpa_eapol_variable var)
 {
-	if (wpa_auth->cb.get_eapol == NULL)
+	if (wpa_auth->cb->get_eapol == NULL)
 		return -1;
-	return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var);
+	return wpa_auth->cb->get_eapol(wpa_auth->cb_ctx, addr, var);
 }
 
 
@@ -105,19 +110,19 @@
 					  const u8 *p2p_dev_addr,
 					  const u8 *prev_psk)
 {
-	if (wpa_auth->cb.get_psk == NULL)
+	if (wpa_auth->cb->get_psk == NULL)
 		return NULL;
-	return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
-				    prev_psk);
+	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+				     prev_psk);
 }
 
 
 static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
 				   const u8 *addr, u8 *msk, size_t *len)
 {
-	if (wpa_auth->cb.get_msk == NULL)
+	if (wpa_auth->cb->get_msk == NULL)
 		return -1;
-	return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len);
+	return wpa_auth->cb->get_msk(wpa_auth->cb_ctx, addr, msk, len);
 }
 
 
@@ -126,19 +131,19 @@
 				   enum wpa_alg alg, const u8 *addr, int idx,
 				   u8 *key, size_t key_len)
 {
-	if (wpa_auth->cb.set_key == NULL)
+	if (wpa_auth->cb->set_key == NULL)
 		return -1;
-	return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
-				    key, key_len);
+	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+				     key, key_len);
 }
 
 
 static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr, int idx, u8 *seq)
 {
-	if (wpa_auth->cb.get_seqnum == NULL)
+	if (wpa_auth->cb->get_seqnum == NULL)
 		return -1;
-	return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+	return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
 }
 
 
@@ -146,10 +151,10 @@
 wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		    const u8 *data, size_t data_len, int encrypt)
 {
-	if (wpa_auth->cb.send_eapol == NULL)
+	if (wpa_auth->cb->send_eapol == NULL)
 		return -1;
-	return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len,
-				       encrypt);
+	return wpa_auth->cb->send_eapol(wpa_auth->cb_ctx, addr, data, data_len,
+					encrypt);
 }
 
 
@@ -157,9 +162,9 @@
 static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr)
 {
-	if (wpa_auth->cb.start_ampe == NULL)
+	if (wpa_auth->cb->start_ampe == NULL)
 		return -1;
-	return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+	return wpa_auth->cb->start_ampe(wpa_auth->cb_ctx, addr);
 }
 #endif /* CONFIG_MESH */
 
@@ -168,9 +173,9 @@
 			  int (*cb)(struct wpa_state_machine *sm, void *ctx),
 			  void *cb_ctx)
 {
-	if (wpa_auth->cb.for_each_sta == NULL)
+	if (wpa_auth->cb->for_each_sta == NULL)
 		return 0;
-	return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx);
+	return wpa_auth->cb->for_each_sta(wpa_auth->cb_ctx, cb, cb_ctx);
 }
 
 
@@ -178,18 +183,18 @@
 			   int (*cb)(struct wpa_authenticator *a, void *ctx),
 			   void *cb_ctx)
 {
-	if (wpa_auth->cb.for_each_auth == NULL)
+	if (wpa_auth->cb->for_each_auth == NULL)
 		return 0;
-	return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
+	return wpa_auth->cb->for_each_auth(wpa_auth->cb_ctx, cb, cb_ctx);
 }
 
 
 void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
 		     logger_level level, const char *txt)
 {
-	if (wpa_auth->cb.logger == NULL)
+	if (wpa_auth->cb->logger == NULL)
 		return;
-	wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt);
+	wpa_auth->cb->logger(wpa_auth->cb_ctx, addr, level, txt);
 }
 
 
@@ -200,7 +205,7 @@
 	int maxlen;
 	va_list ap;
 
-	if (wpa_auth->cb.logger == NULL)
+	if (wpa_auth->cb->logger == NULL)
 		return;
 
 	maxlen = os_strlen(fmt) + 100;
@@ -221,21 +226,21 @@
 static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
 			       const u8 *addr)
 {
-	if (wpa_auth->cb.disconnect == NULL)
+	if (wpa_auth->cb->disconnect == NULL)
 		return;
 	wpa_printf(MSG_DEBUG, "wpa_sta_disconnect STA " MACSTR, MAC2STR(addr));
-	wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr,
-				WLAN_REASON_PREV_AUTH_NOT_VALID);
+	wpa_auth->cb->disconnect(wpa_auth->cb_ctx, addr,
+				 WLAN_REASON_PREV_AUTH_NOT_VALID);
 }
 
 
 static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
 {
 	int ret = 0;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		ret = 1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
 		ret = 1;
@@ -409,7 +414,8 @@
  */
 struct wpa_authenticator * wpa_init(const u8 *addr,
 				    struct wpa_auth_config *conf,
-				    struct wpa_auth_callbacks *cb)
+				    const struct wpa_auth_callbacks *cb,
+				    void *cb_ctx)
 {
 	struct wpa_authenticator *wpa_auth;
 
@@ -418,7 +424,8 @@
 		return NULL;
 	os_memcpy(wpa_auth->addr, addr, ETH_ALEN);
 	os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
-	os_memcpy(&wpa_auth->cb, cb, sizeof(*cb));
+	wpa_auth->cb = cb;
+	wpa_auth->cb_ctx = cb_ctx;
 
 	if (wpa_auth_gen_wpa_ie(wpa_auth)) {
 		wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
@@ -443,7 +450,7 @@
 		return NULL;
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
 	if (wpa_auth->ft_pmk_cache == NULL) {
 		wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
@@ -453,7 +460,7 @@
 		os_free(wpa_auth);
 		return NULL;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	if (wpa_auth->conf.wpa_gmk_rekey) {
 		eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0,
@@ -506,17 +513,12 @@
 	eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL);
 	eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL);
 
-#ifdef CONFIG_PEERKEY
-	while (wpa_auth->stsl_negotiations)
-		wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations);
-#endif /* CONFIG_PEERKEY */
-
 	pmksa_cache_auth_deinit(wpa_auth->pmksa);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache);
 	wpa_auth->ft_pmk_cache = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_P2P
 	bitfield_free(wpa_auth->ip_pool);
@@ -599,16 +601,28 @@
 	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 		return -1;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (sm->ft_completed) {
 		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 				"FT authentication already completed - do not "
 				"start 4-way handshake");
 		/* Go to PTKINITDONE state to allow GTK rekeying */
 		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+		sm->Pair = TRUE;
 		return 0;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
+
+#ifdef CONFIG_FILS
+	if (sm->fils_completed) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				"FILS authentication already completed - do not start 4-way handshake");
+		/* Go to PTKINITDONE state to allow GTK rekeying */
+		sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
+		sm->Pair = TRUE;
+		return 0;
+	}
+#endif /* CONFIG_FILS */
 
 	if (sm->started) {
 		os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
@@ -660,10 +674,10 @@
 		sm->group->GKeyDoneStations--;
 		sm->GUpdateStationKeys = FALSE;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	os_free(sm->assoc_resp_ftie);
 	wpabuf_free(sm->ft_pending_req_ies);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	os_free(sm->last_rx_eapol_key);
 	os_free(sm->wpa_ie);
 	wpa_group_put(sm->wpa_auth, sm->group);
@@ -739,7 +753,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
 			       struct wpa_state_machine *sm,
 			       struct wpa_eapol_ie_parse *kde)
@@ -786,7 +800,7 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
@@ -830,6 +844,7 @@
 	const u8 *pmk = NULL;
 	unsigned int pmk_len;
 
+	os_memset(&PTK, 0, sizeof(PTK));
 	for (;;) {
 		if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
 			pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
@@ -842,7 +857,8 @@
 			pmk_len = sm->pmk_len;
 		}
 
-		wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK);
+		if (wpa_derive_ptk(sm, sm->alt_SNonce, pmk, pmk_len, &PTK) < 0)
+			break;
 
 		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
 		    == 0) {
@@ -877,39 +893,42 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info, key_data_length;
 	enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
 	       SMK_M1, SMK_M3, SMK_ERROR } msg;
 	char *msgtxt;
 	struct wpa_eapol_ie_parse kde;
-	int ft;
-	const u8 *eapol_key_ie, *key_data;
-	size_t eapol_key_ie_len, keyhdrlen, mic_len;
+	const u8 *key_data;
+	size_t keyhdrlen, mic_len;
+	u8 *mic;
 
 	if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
 		return;
+	wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len);
 
 	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
-	if (data_len < sizeof(*hdr) + keyhdrlen)
+	if (data_len < sizeof(*hdr) + keyhdrlen) {
+		wpa_printf(MSG_DEBUG, "WPA: Ignore too short EAPOL-Key frame");
 		return;
+	}
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	mic = (u8 *) (key + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	if (mic_len == 24) {
-		key_data = (const u8 *) (key192 + 1);
-		key_data_length = WPA_GET_BE16(key192->key_data_length);
-	} else {
-		key_data = (const u8 *) (key + 1);
-		key_data_length = WPA_GET_BE16(key->key_data_length);
-	}
+	key_data = mic + mic_len + 2;
+	key_data_length = WPA_GET_BE16(mic + mic_len);
 	wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
-		   " key_info=0x%x type=%u key_data_length=%u",
-		   MAC2STR(sm->addr), key_info, key->type, key_data_length);
+		   " key_info=0x%x type=%u mic_len=%u key_data_length=%u",
+		   MAC2STR(sm->addr), key_info, key->type,
+		   (unsigned int) mic_len, key_data_length);
+	wpa_hexdump(MSG_MSGDUMP,
+		    "WPA: EAPOL-Key header (ending before Key MIC)",
+		    key, sizeof(*key));
+	wpa_hexdump(MSG_MSGDUMP, "WPA: EAPOL-Key Key MIC",
+		    mic, mic_len);
 	if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
 		wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
 			   "key_data overflow (%d > %lu)",
@@ -968,7 +987,9 @@
 	} else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
 		msg = GROUP_2;
 		msgtxt = "2/2 Group";
-	} else if (key_data_length == 0) {
+	} else if (key_data_length == 0 ||
+		   (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
+		    key_data_length == AES_BLOCK_SIZE)) {
 		msg = PAIRWISE_4;
 		msgtxt = "4/4 Pairwise";
 	} else {
@@ -985,6 +1006,7 @@
 			if (wpa_use_aes_cmac(sm) &&
 			    sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
 			    !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -995,6 +1017,7 @@
 			}
 
 			if (!wpa_use_aes_cmac(sm) &&
+			    !wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
 			    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 				wpa_auth_logger(wpa_auth, sm->addr,
 						LOGGER_WARNING,
@@ -1004,7 +1027,8 @@
 			}
 		}
 
-		if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+		if ((wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		     wpa_key_mgmt_fils(sm->wpa_key_mgmt)) &&
 		    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
 					"did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
@@ -1092,6 +1116,15 @@
 	}
 
 continue_processing:
+#ifdef CONFIG_FILS
+	if (sm->wpa == WPA_VERSION_WPA2 && mic_len == 0 &&
+	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+				 "WPA: Encr Key Data bit not set even though AEAD cipher is supposed to be used - drop frame");
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	switch (msg) {
 	case PAIRWISE_2:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKSTART &&
@@ -1122,67 +1155,6 @@
 			wpa_sta_disconnect(wpa_auth, sm->addr);
 			return;
 		}
-		if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
-			wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
-					 "received EAPOL-Key msg 2/4 with "
-					 "invalid Key Data contents");
-			return;
-		}
-		if (kde.rsn_ie) {
-			eapol_key_ie = kde.rsn_ie;
-			eapol_key_ie_len = kde.rsn_ie_len;
-		} else if (kde.osen) {
-			eapol_key_ie = kde.osen;
-			eapol_key_ie_len = kde.osen_len;
-		} else {
-			eapol_key_ie = kde.wpa_ie;
-			eapol_key_ie_len = kde.wpa_ie_len;
-		}
-		ft = sm->wpa == WPA_VERSION_WPA2 &&
-			wpa_key_mgmt_ft(sm->wpa_key_mgmt);
-		if (sm->wpa_ie == NULL ||
-		    wpa_compare_rsn_ie(ft,
-				       sm->wpa_ie, sm->wpa_ie_len,
-				       eapol_key_ie, eapol_key_ie_len)) {
-			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
-					"WPA IE from (Re)AssocReq did not "
-					"match with msg 2/4");
-			if (sm->wpa_ie) {
-				wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
-					    sm->wpa_ie, sm->wpa_ie_len);
-			}
-			wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
-				    eapol_key_ie, eapol_key_ie_len);
-			/* MLME-DEAUTHENTICATE.request */
-			wpa_sta_disconnect(wpa_auth, sm->addr);
-			return;
-		}
-#ifdef CONFIG_IEEE80211R
-		if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
-			wpa_sta_disconnect(wpa_auth, sm->addr);
-			return;
-		}
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_P2P
-		if (kde.ip_addr_req && kde.ip_addr_req[0] &&
-		    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
-			int idx;
-			wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
-				   "EAPOL-Key exchange");
-			idx = bitfield_get_first_zero(wpa_auth->ip_pool);
-			if (idx >= 0) {
-				u32 start = WPA_GET_BE32(wpa_auth->conf.
-							 ip_addr_start);
-				bitfield_set(wpa_auth->ip_pool, idx);
-				WPA_PUT_BE32(sm->ip_addr, start + idx);
-				wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
-					   "address %u.%u.%u.%u to " MACSTR,
-					   sm->ip_addr[0], sm->ip_addr[1],
-					   sm->ip_addr[2], sm->ip_addr[3],
-					   MAC2STR(sm->addr));
-			}
-		}
-#endif /* CONFIG_P2P */
 		break;
 	case PAIRWISE_4:
 		if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1239,15 +1211,26 @@
 		return;
 	}
 
-	if (!(key_info & WPA_KEY_INFO_MIC)) {
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    !(key_info & WPA_KEY_INFO_MIC)) {
 		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
 				"received invalid EAPOL-Key: Key MIC not set");
 		return;
 	}
 
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (key_info & WPA_KEY_INFO_MIC)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"received invalid EAPOL-Key: Key MIC set");
+		return;
+	}
+#endif /* CONFIG_FILS */
+
 	sm->MICVerified = FALSE;
 	if (sm->PTK_valid && !sm->update_snonce) {
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
 				       data_len) &&
 		    (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
 		     wpa_try_alt_snonce(sm, data, data_len))) {
@@ -1255,6 +1238,15 @@
 					"received EAPOL-Key with invalid MIC");
 			return;
 		}
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &sm->PTK, data, data_len,
+				     &key_data_length) < 0) {
+			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+					"received EAPOL-Key with invalid MIC");
+			return;
+		}
+#endif /* CONFIG_FILS */
 		sm->MICVerified = TRUE;
 		eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
 		sm->pending_1_of_4_timeout = 0;
@@ -1412,24 +1404,24 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	size_t len, mic_len, keyhdrlen;
 	int alg;
 	int key_data_len, pad_len = 0;
 	u8 *buf, *pos;
 	int version, pairwise;
 	int i;
-	u8 *key_data;
+	u8 *key_mic, *key_data;
 
 	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
 
 	if (force_version)
 		version = force_version;
 	else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
-		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+		 wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
+		 wpa_key_mgmt_fils(sm->wpa_key_mgmt))
 		version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
 	else if (wpa_use_aes_cmac(sm))
 		version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
@@ -1463,6 +1455,8 @@
 	}
 
 	len += key_data_len;
+	if (!mic_len && encr)
+		len += AES_BLOCK_SIZE;
 
 	hdr = os_zalloc(len);
 	if (hdr == NULL)
@@ -1471,7 +1465,7 @@
 	hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
 	hdr->length = host_to_be16(len  - sizeof(*hdr));
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	key_mic = (u8 *) (key + 1);
 	key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
 
 	key->type = sm->wpa == WPA_VERSION_WPA2 ?
@@ -1484,9 +1478,11 @@
 	WPA_PUT_BE16(key->key_info, key_info);
 
 	alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group;
-	WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
-	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+	if ((key_info & WPA_KEY_INFO_SMK_MESSAGE) ||
+	    (sm->wpa == WPA_VERSION_WPA2 && !pairwise))
 		WPA_PUT_BE16(key->key_length, 0);
+	else
+		WPA_PUT_BE16(key->key_length, wpa_cipher_key_len(alg));
 
 	/* FIX: STSL: what to use as key_replay_counter? */
 	for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
@@ -1510,10 +1506,31 @@
 
 	if (kde && !encr) {
 		os_memcpy(key_data, kde, kde_len);
-		if (mic_len == 24)
-			WPA_PUT_BE16(key192->key_data_length, kde_len);
-		else
-			WPA_PUT_BE16(key->key_data_length, kde_len);
+		WPA_PUT_BE16(key_mic + mic_len, kde_len);
+#ifdef CONFIG_FILS
+	} else if (!mic_len) {
+		const u8 *aad[1];
+		size_t aad_len[1];
+
+		WPA_PUT_BE16(key_mic, AES_BLOCK_SIZE + kde_len);
+		wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
+				kde, kde_len);
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: KEK",
+				sm->PTK.kek, sm->PTK.kek_len);
+		/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+		 * to Key Data (exclusive). */
+		aad[0] = (u8 *) hdr;
+		aad_len[0] = key_mic + 2 - (u8 *) hdr;
+		if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len, kde, kde_len,
+				    1, aad, aad_len, key_mic + 2) < 0) {
+			wpa_printf(MSG_DEBUG, "WPA: AES-SIV encryption failed");
+			return;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+			    key_mic + 2, AES_BLOCK_SIZE + kde_len);
+#endif /* CONFIG_FILS */
 	} else if (encr && kde) {
 		buf = os_zalloc(key_data_len);
 		if (buf == NULL) {
@@ -1539,12 +1556,7 @@
 				os_free(buf);
 				return;
 			}
-			if (mic_len == 24)
-				WPA_PUT_BE16(key192->key_data_length,
-					     key_data_len);
-			else
-				WPA_PUT_BE16(key->key_data_length,
-					     key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
 #ifndef CONFIG_NO_RC4
 		} else if (sm->PTK.kek_len == 16) {
 			u8 ek[32];
@@ -1555,12 +1567,7 @@
 			os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
 			os_memcpy(key_data, buf, key_data_len);
 			rc4_skip(ek, 32, 256, key_data, key_data_len);
-			if (mic_len == 24)
-				WPA_PUT_BE16(key192->key_data_length,
-					     key_data_len);
-			else
-				WPA_PUT_BE16(key->key_data_length,
-					     key_data_len);
+			WPA_PUT_BE16(key_mic + mic_len, key_data_len);
 #endif /* CONFIG_NO_RC4 */
 		} else {
 			os_free(hdr);
@@ -1571,9 +1578,7 @@
 	}
 
 	if (key_info & WPA_KEY_INFO_MIC) {
-		u8 *key_mic;
-
-		if (!sm->PTK_valid) {
+		if (!sm->PTK_valid || !mic_len) {
 			wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
 					"PTK not valid when sending EAPOL-Key "
 					"frame");
@@ -1581,7 +1586,6 @@
 			return;
 		}
 
-		key_mic = key192->key_mic; /* same offset for key and key192 */
 		wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
 				  sm->wpa_key_mgmt, version,
 				  (u8 *) hdr, len, key_mic);
@@ -1613,7 +1617,7 @@
 {
 	int timeout_ms;
 	int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
-	int ctr;
+	u32 ctr;
 
 	if (sm == NULL)
 		return;
@@ -1630,7 +1634,7 @@
 	if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
 		sm->pending_1_of_4_timeout = 1;
 	wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
-		   "counter %d)", timeout_ms, ctr);
+		   "counter %u)", timeout_ms, ctr);
 	eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
 			       wpa_send_eapol_timeout, wpa_auth, sm);
 }
@@ -1641,10 +1645,9 @@
 {
 	struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info;
 	int ret = 0;
-	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
 	size_t mic_len = wpa_mic_len(akmp);
 
 	if (data_len < sizeof(*hdr) + sizeof(*key))
@@ -1652,16 +1655,16 @@
 
 	hdr = (struct ieee802_1x_hdr *) data;
 	key = (struct wpa_eapol_key *) (hdr + 1);
-	key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+	mic_pos = (u8 *) (key + 1);
 	key_info = WPA_GET_BE16(key->key_info);
-	os_memcpy(mic, key192->key_mic, mic_len);
-	os_memset(key192->key_mic, 0, mic_len);
+	os_memcpy(mic, mic_pos, mic_len);
+	os_memset(mic_pos, 0, mic_len);
 	if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
 			      key_info & WPA_KEY_INFO_TYPE_MASK,
-			      data, data_len, key192->key_mic) ||
-	    os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
+			      data, data_len, mic_pos) ||
+	    os_memcmp_const(mic, mic_pos, mic_len) != 0)
 		ret = -1;
-	os_memcpy(key192->key_mic, mic, mic_len);
+	os_memcpy(mic_pos, mic, mic_len);
 	return ret;
 }
 
@@ -1670,7 +1673,10 @@
 {
 	sm->PTK_valid = FALSE;
 	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
-	wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL, 0);
+	if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
+			     0))
+		wpa_printf(MSG_DEBUG,
+			   "RSN: PTK removal from the driver failed");
 	sm->pairwise_set = FALSE;
 	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 }
@@ -1734,7 +1740,7 @@
 		sm->ReAuthenticationRequest = TRUE;
 		break;
 	case WPA_ASSOC_FT:
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
 			   "after association");
 		wpa_ft_install_ptk(sm);
@@ -1742,22 +1748,27 @@
 		/* Using FT protocol, not WPA auth state machine */
 		sm->ft_completed = 1;
 		return 0;
-#else /* CONFIG_IEEE80211R */
+#else /* CONFIG_IEEE80211R_AP */
 		break;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	case WPA_DRV_STA_REMOVED:
 		sm->tk_already_set = FALSE;
 		return 0;
 	}
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	sm->ft_completed = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #ifdef CONFIG_IEEE80211W
 	if (sm->mgmt_frame_prot && event == WPA_AUTH)
 		remove_ptk = 0;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_FILS
+	if (wpa_key_mgmt_fils(sm->wpa_key_mgmt) &&
+	    (event == WPA_AUTH || event == WPA_ASSOC))
+		remove_ptk = 0;
+#endif /* CONFIG_FILS */
 
 	if (remove_ptk) {
 		sm->PTK_valid = FALSE;
@@ -1922,9 +1933,9 @@
 	size_t len = 2 * PMK_LEN;
 
 	SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	sm->xxkey_len = 0;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (sm->pmksa) {
 		wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache");
 		os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
@@ -1932,7 +1943,7 @@
 	} else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) {
 		unsigned int pmk_len;
 
-		if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt))
 			pmk_len = PMK_LEN_SUITE_B_192;
 		else
 			pmk_len = PMK_LEN;
@@ -1948,15 +1959,15 @@
 		}
 		os_memcpy(sm->PMK, msk, pmk_len);
 		sm->pmk_len = pmk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		if (len >= 2 * PMK_LEN) {
 			os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN);
 			sm->xxkey_len = PMK_LEN;
 		}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	} else {
 		wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
-			   sm->wpa_auth->cb.get_msk);
+			   sm->wpa_auth->cb->get_msk);
 		sm->Disconnect = TRUE;
 		return;
 	}
@@ -1983,10 +1994,10 @@
 	if (psk) {
 		os_memcpy(sm->PMK, psk, PMK_LEN);
 		sm->pmk_len = PMK_LEN;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		os_memcpy(sm->xxkey, psk, PMK_LEN);
 		sm->xxkey_len = PMK_LEN;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	}
 	sm->req_replay_counter_used = 0;
 }
@@ -2003,7 +2014,7 @@
 	sm->alt_snonce_valid = FALSE;
 
 	sm->TimeoutCtr++;
-	if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+	if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
 		/* No point in sending the EAPOL-Key - we will disconnect
 		 * immediately following this. */
 		return;
@@ -2049,10 +2060,10 @@
 			  const u8 *pmk, unsigned int pmk_len,
 			  struct wpa_ptk *ptk)
 {
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
 		return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
 			      sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
@@ -2060,16 +2071,401 @@
 }
 
 
+#ifdef CONFIG_FILS
+
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce)
+{
+	u8 ick[FILS_ICK_MAX_LEN];
+	size_t ick_len;
+	int res;
+
+	res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
+			      snonce, anonce, &sm->PTK, ick, &ick_len,
+			      sm->wpa_key_mgmt, sm->pairwise);
+	if (res < 0)
+		return res;
+	sm->PTK_valid = TRUE;
+
+	res = fils_key_auth_sk(ick, ick_len, snonce, anonce,
+			       sm->addr, sm->wpa_auth->addr,
+			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       sm->wpa_key_mgmt, sm->fils_key_auth_sta,
+			       sm->fils_key_auth_ap,
+			       &sm->fils_key_auth_len);
+	os_memset(ick, 0, sizeof(ick));
+
+	/* Store nonces for (Re)Association Request/Response frame processing */
+	os_memcpy(sm->SNonce, snonce, FILS_NONCE_LEN);
+	os_memcpy(sm->ANonce, anonce, FILS_NONCE_LEN);
+
+	return res;
+}
+
+
+static int wpa_aead_decrypt(struct wpa_state_machine *sm, struct wpa_ptk *ptk,
+			    u8 *buf, size_t buf_len, u16 *_key_data_len)
+{
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 *pos;
+	u16 key_data_len;
+	u8 *tmp;
+	const u8 *aad[1];
+	size_t aad_len[1];
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	pos = (u8 *) (key + 1);
+	key_data_len = WPA_GET_BE16(pos);
+	if (key_data_len < AES_BLOCK_SIZE ||
+	    key_data_len > buf_len - sizeof(*hdr) - sizeof(*key) - 2) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+				"No room for AES-SIV data in the frame");
+		return -1;
+	}
+	pos += 2; /* Pointing at the Encrypted Key Data field */
+
+	tmp = os_malloc(key_data_len);
+	if (!tmp)
+		return -1;
+
+	/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+	 * to Key Data (exclusive). */
+	aad[0] = buf;
+	aad_len[0] = pos - buf;
+	if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, key_data_len,
+			    1, aad, aad_len, tmp) < 0) {
+		wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+				"Invalid AES-SIV data in the frame");
+		bin_clear_free(tmp, key_data_len);
+		return -1;
+	}
+
+	/* AEAD decryption and validation completed successfully */
+	key_data_len -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+			tmp, key_data_len);
+
+	/* Replace Key Data field with the decrypted version */
+	os_memcpy(pos, tmp, key_data_len);
+	pos -= 2; /* Key Data Length field */
+	WPA_PUT_BE16(pos, key_data_len);
+	bin_clear_free(tmp, key_data_len);
+	if (_key_data_len)
+		*_key_data_len = key_data_len;
+	return 0;
+}
+
+
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
+		       u8 *pos, size_t left)
+{
+	u16 fc, stype;
+	const u8 *end, *ie_start, *ie, *session, *crypt;
+	struct ieee802_11_elems elems;
+	const u8 *aad[5];
+	size_t aad_len[5];
+
+	if (!sm || !sm->PTK_valid) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No KEK to decrypt Assocication Request frame");
+		return -1;
+	}
+
+	if (!wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not a FILS AKM - reject association");
+		return -1;
+	}
+
+	end = ((const u8 *) mgmt) + frame_len;
+	fc = le_to_host16(mgmt->frame_control);
+	stype = WLAN_FC_GET_STYPE(fc);
+	if (stype == WLAN_FC_STYPE_REASSOC_REQ)
+		ie_start = mgmt->u.reassoc_req.variable;
+	else
+		ie_start = mgmt->u.assoc_req.variable;
+	ie = ie_start;
+
+	/*
+	 * Find FILS Session element which is the last unencrypted element in
+	 * the frame.
+	 */
+	session = NULL;
+	while (ie + 1 < end) {
+		if (ie + 2 + ie[1] > end)
+			break;
+		if (ie[0] == WLAN_EID_EXTENSION &&
+		    ie[1] >= 1 + FILS_SESSION_LEN &&
+		    ie[2] == WLAN_EID_EXT_FILS_SESSION) {
+			session = ie;
+			break;
+		}
+		ie += 2 + ie[1];
+	}
+
+	if (!session) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Could not find FILS Session element in Association Request frame - reject");
+		return -1;
+	}
+	if (os_memcmp(fils_session, session + 3, FILS_SESSION_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    fils_session, FILS_SESSION_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+			    session + 3, FILS_SESSION_LEN);
+		return -1;
+	}
+	crypt = session + 2 + session[1];
+
+	if (end - crypt < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Too short frame to include AES-SIV data");
+		return -1;
+	}
+
+	/* AES-SIV AAD vectors */
+
+	/* The STA's MAC address */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The AP's BSSID */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The STA's nonce */
+	aad[2] = sm->SNonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The AP's nonce */
+	aad[3] = sm->ANonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Request frame from the Capability Information
+	 * field to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_req.capab_info;
+	aad_len[4] = crypt - aad[4];
+
+	if (aes_siv_decrypt(sm->PTK.kek, sm->PTK.kek_len, crypt, end - crypt,
+			    5, aad, aad_len, pos + (crypt - ie_start)) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Invalid AES-SIV data in the frame");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+		    pos, left - AES_BLOCK_SIZE);
+
+	if (ieee802_11_parse_elems(pos, left - AES_BLOCK_SIZE, &elems, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to parse decrypted elements");
+		return -1;
+	}
+	if (!elems.fils_key_confirm) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+		return -1;
+	}
+	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Unexpected Key-Auth length %d (expected %d)",
+			   elems.fils_key_confirm_len,
+			   (int) sm->fils_key_auth_len);
+		return -1;
+	}
+	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_sta,
+		      sm->fils_key_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+			    elems.fils_key_confirm,
+			    elems.fils_key_confirm_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+			    sm->fils_key_auth_sta, sm->fils_key_auth_len);
+		return -1;
+	}
+
+	return left - AES_BLOCK_SIZE;
+}
+
+
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+		       size_t current_len, size_t max_len,
+		       const struct wpabuf *hlp)
+{
+	u8 *end = buf + max_len;
+	u8 *pos = buf + current_len;
+	struct ieee80211_mgmt *mgmt;
+	struct wpabuf *plain;
+	u8 *len, *tmp, *tmp2;
+	u8 hdr[2];
+	u8 *gtk, dummy_gtk[32];
+	size_t gtk_len;
+	struct wpa_group *gsm;
+	const u8 *aad[5];
+	size_t aad_len[5];
+
+	if (!sm || !sm->PTK_valid)
+		return -1;
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Association Response frame before FILS processing",
+		    buf, current_len);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+
+	/* AES-SIV AAD vectors */
+
+	/* The AP's BSSID */
+	aad[0] = mgmt->sa;
+	aad_len[0] = ETH_ALEN;
+	/* The STA's MAC address */
+	aad[1] = mgmt->da;
+	aad_len[1] = ETH_ALEN;
+	/* The AP's nonce */
+	aad[2] = sm->ANonce;
+	aad_len[2] = FILS_NONCE_LEN;
+	/* The STA's nonce */
+	aad[3] = sm->SNonce;
+	aad_len[3] = FILS_NONCE_LEN;
+	/*
+	 * The (Re)Association Response frame from the Capability Information
+	 * field (the same offset in both Association and Reassociation
+	 * Response frames) to the FILS Session element (both inclusive).
+	 */
+	aad[4] = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+	aad_len[4] = pos - aad[4];
+
+	/* The following elements will be encrypted with AES-SIV */
+
+	plain = wpabuf_alloc(1000);
+	if (!plain)
+		return -1;
+
+	/* TODO: FILS Public Key */
+
+	/* FILS Key Confirmation */
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(plain, 1 + sm->fils_key_auth_len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(plain, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	wpabuf_put_data(plain, sm->fils_key_auth_ap, sm->fils_key_auth_len);
+
+	/* FILS HLP Container */
+	if (hlp)
+		wpabuf_put_buf(plain, hlp);
+
+	/* TODO: FILS IP Address Assignment */
+
+	/* Key Delivery */
+	gsm = sm->group;
+	wpabuf_put_u8(plain, WLAN_EID_EXTENSION); /* Element ID */
+	len = wpabuf_put(plain, 1);
+	wpabuf_put_u8(plain, WLAN_EID_EXT_KEY_DELIVERY);
+	wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN,
+			    wpabuf_put(plain, WPA_KEY_RSC_LEN));
+	/* GTK KDE */
+	gtk = gsm->GTK[gsm->GN - 1];
+	gtk_len = gsm->GTK_len;
+	if (sm->wpa_auth->conf.disable_gtk) {
+		/*
+		 * Provide unique random GTK to each STA to prevent use
+		 * of GTK in the BSS.
+		 */
+		if (random_get_bytes(dummy_gtk, gtk_len) < 0) {
+			wpabuf_free(plain);
+			return -1;
+		}
+		gtk = dummy_gtk;
+	}
+	hdr[0] = gsm->GN & 0x03;
+	hdr[1] = 0;
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = wpa_add_kde(tmp, RSN_KEY_DATA_GROUPKEY, hdr, 2,
+			   gtk, gtk_len);
+	wpabuf_put(plain, tmp2 - tmp);
+
+	/* IGTK KDE */
+	tmp = wpabuf_put(plain, 0);
+	tmp2 = ieee80211w_kde_add(sm, tmp);
+	wpabuf_put(plain, tmp2 - tmp);
+
+	*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+
+	if (pos + wpabuf_len(plain) + AES_BLOCK_SIZE > end) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Not enough room for FILS elements");
+		wpabuf_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump_buf_key(MSG_DEBUG, "FILS: Association Response plaintext",
+			    plain);
+
+	if (aes_siv_encrypt(sm->PTK.kek, sm->PTK.kek_len,
+			    wpabuf_head(plain), wpabuf_len(plain),
+			    5, aad, aad_len, pos) < 0) {
+		wpabuf_free(plain);
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "FILS: Encrypted Association Response elements",
+		    pos, AES_BLOCK_SIZE + wpabuf_len(plain));
+	current_len += wpabuf_len(plain) + AES_BLOCK_SIZE;
+	wpabuf_free(plain);
+
+	sm->fils_completed = 1;
+
+	return current_len;
+}
+
+
+int fils_set_tk(struct wpa_state_machine *sm)
+{
+	enum wpa_alg alg;
+	int klen;
+
+	if (!sm || !sm->PTK_valid)
+		return -1;
+
+	alg = wpa_cipher_to_alg(sm->pairwise);
+	klen = wpa_cipher_key_len(sm->pairwise);
+
+	wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+			     sm->PTK.tk, klen)) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_FILS */
+
+
 SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
 {
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
 	struct wpa_ptk PTK;
 	int ok = 0, psk_found = 0;
 	const u8 *pmk = NULL;
 	unsigned int pmk_len;
+	int ft;
+	const u8 *eapol_key_ie, *key_data, *mic;
+	u16 key_data_length;
+	size_t mic_len, eapol_key_ie_len;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	struct wpa_eapol_ie_parse kde;
 
 	SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
 	sm->EAPOLKeyReceived = FALSE;
 	sm->update_snonce = FALSE;
+	os_memset(&PTK, 0, sizeof(PTK));
+
+	mic_len = wpa_mic_len(sm->wpa_key_mgmt);
 
 	/* WPA with IEEE 802.1X: use the derived PMK from EAP
 	 * WPA-PSK: iterate through possible PSKs and select the one matching
@@ -2087,15 +2483,26 @@
 			pmk_len = sm->pmk_len;
 		}
 
-		wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK);
+		if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
+			break;
 
-		if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+		if (mic_len &&
+		    wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
 				       sm->last_rx_eapol_key,
 				       sm->last_rx_eapol_key_len) == 0) {
 			ok = 1;
 			break;
 		}
 
+#ifdef CONFIG_FILS
+		if (!mic_len &&
+		    wpa_aead_decrypt(sm, &PTK, sm->last_rx_eapol_key,
+				     sm->last_rx_eapol_key_len, NULL) == 0) {
+			ok = 1;
+			break;
+		}
+#endif /* CONFIG_FILS */
+
 		if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
 			break;
 	}
@@ -2108,7 +2515,77 @@
 		return;
 	}
 
-#ifdef CONFIG_IEEE80211R
+	/*
+	 * Note: last_rx_eapol_key length fields have already been validated in
+	 * wpa_receive().
+	 */
+	hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
+	key_data_length = WPA_GET_BE16(mic + mic_len);
+	if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+	    sizeof(*key) - mic_len - 2)
+		return;
+
+	if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+				 "received EAPOL-Key msg 2/4 with invalid Key Data contents");
+		return;
+	}
+	if (kde.rsn_ie) {
+		eapol_key_ie = kde.rsn_ie;
+		eapol_key_ie_len = kde.rsn_ie_len;
+	} else if (kde.osen) {
+		eapol_key_ie = kde.osen;
+		eapol_key_ie_len = kde.osen_len;
+	} else {
+		eapol_key_ie = kde.wpa_ie;
+		eapol_key_ie_len = kde.wpa_ie_len;
+	}
+	ft = sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+	if (sm->wpa_ie == NULL ||
+	    wpa_compare_rsn_ie(ft, sm->wpa_ie, sm->wpa_ie_len,
+			       eapol_key_ie, eapol_key_ie_len)) {
+		wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+				"WPA IE from (Re)AssocReq did not match with msg 2/4");
+		if (sm->wpa_ie) {
+			wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq",
+				    sm->wpa_ie, sm->wpa_ie_len);
+		}
+		wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
+			    eapol_key_ie, eapol_key_ie_len);
+		/* MLME-DEAUTHENTICATE.request */
+		wpa_sta_disconnect(wpa_auth, sm->addr);
+		return;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+		wpa_sta_disconnect(wpa_auth, sm->addr);
+		return;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_P2P
+	if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+	    wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+		int idx;
+		wpa_printf(MSG_DEBUG,
+			   "P2P: IP address requested in EAPOL-Key exchange");
+		idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+		if (idx >= 0) {
+			u32 start = WPA_GET_BE32(wpa_auth->conf.ip_addr_start);
+			bitfield_set(wpa_auth->ip_pool, idx);
+			WPA_PUT_BE32(sm->ip_addr, start + idx);
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Assigned IP address %u.%u.%u.%u to "
+				   MACSTR, sm->ip_addr[0], sm->ip_addr[1],
+				   sm->ip_addr[2], sm->ip_addr[3],
+				   MAC2STR(sm->addr));
+		}
+	}
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_IEEE80211R_AP
 	if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		/*
 		 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
@@ -2127,7 +2604,7 @@
 			return;
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	sm->pending_1_of_4_timeout = 0;
 	eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -2229,7 +2706,7 @@
 	sm->TimeoutEvt = FALSE;
 
 	sm->TimeoutCtr++;
-	if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+	if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
 		/* No point in sending the EAPOL-Key - we will disconnect
 		 * immediately following this. */
 		return;
@@ -2297,12 +2774,12 @@
 	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
 	if (gtk)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
 		kde_len += 300; /* FTIE + 2 * TIE */
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	if (WPA_GET_BE32(sm->ip_addr) > 0)
 		kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
@@ -2314,7 +2791,7 @@
 	pos = kde;
 	os_memcpy(pos, wpa_ie, wpa_ie_len);
 	pos += wpa_ie_len;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		int res;
 		size_t elen;
@@ -2330,7 +2807,7 @@
 		pos -= wpa_ie_len;
 		pos += elen;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (gtk) {
 		u8 hdr[2];
 		hdr[0] = keyidx & 0x03;
@@ -2340,7 +2817,7 @@
 	}
 	pos = ieee80211w_kde_add(sm, pos);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		int res;
 		struct wpa_auth_config *conf;
@@ -2380,7 +2857,7 @@
 		WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
 		pos += 4;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	if (WPA_GET_BE32(sm->ip_addr) > 0) {
 		u8 addr[3 * 4];
@@ -2393,7 +2870,8 @@
 #endif /* CONFIG_P2P */
 
 	wpa_send_eapol(sm->wpa_auth, sm,
-		       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
+		       (secure ? WPA_KEY_INFO_SECURE : 0) |
+		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
 		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
@@ -2449,9 +2927,9 @@
 			 "pairwise key handshake completed (%s)",
 			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN");
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 }
 
 
@@ -2526,11 +3004,12 @@
 		    sm->EAPOLKeyPairwise)
 			SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
 		else if (sm->TimeoutCtr >
-			 (int) dot11RSNAConfigPairwiseUpdateCount) {
+			 sm->wpa_auth->conf.wpa_pairwise_update_count) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
-					 "PTKSTART: Retry limit %d reached",
-					 dot11RSNAConfigPairwiseUpdateCount);
+			wpa_auth_vlogger(
+				sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"PTKSTART: Retry limit %u reached",
+				sm->wpa_auth->conf.wpa_pairwise_update_count);
 			SM_ENTER(WPA_PTK, DISCONNECT);
 		} else if (sm->TimeoutEvt)
 			SM_ENTER(WPA_PTK, PTKSTART);
@@ -2554,12 +3033,12 @@
 			 sm->EAPOLKeyPairwise && sm->MICVerified)
 			SM_ENTER(WPA_PTK, PTKINITDONE);
 		else if (sm->TimeoutCtr >
-			 (int) dot11RSNAConfigPairwiseUpdateCount) {
+			 sm->wpa_auth->conf.wpa_pairwise_update_count) {
 			wpa_auth->dot11RSNA4WayHandshakeFailures++;
-			wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
-					 "PTKINITNEGOTIATING: Retry limit %d "
-					 "reached",
-					 dot11RSNAConfigPairwiseUpdateCount);
+			wpa_auth_vlogger(
+				sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+				"PTKINITNEGOTIATING: Retry limit %u reached",
+				sm->wpa_auth->conf.wpa_pairwise_update_count);
 			SM_ENTER(WPA_PTK, DISCONNECT);
 		} else if (sm->TimeoutEvt)
 			SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
@@ -2594,7 +3073,7 @@
 	SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
 
 	sm->GTimeoutCtr++;
-	if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+	if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
 		/* No point in sending the EAPOL-Key - we will disconnect
 		 * immediately following this. */
 		return;
@@ -2640,10 +3119,11 @@
 	}
 
 	wpa_send_eapol(sm->wpa_auth, sm,
-		       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+		       WPA_KEY_INFO_SECURE |
+		       (wpa_mic_len(sm->wpa_key_mgmt) ? WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK |
 		       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
-		       rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+		       rsc, NULL, kde, kde_len, gsm->GN, 1);
 
 	os_free(kde_buf);
 }
@@ -2672,6 +3152,10 @@
 		sm->group->GKeyDoneStations--;
 	sm->GUpdateStationKeys = FALSE;
 	sm->Disconnect = TRUE;
+	wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO,
+			 "group key handshake failed (%s) after %u tries",
+			 sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN",
+			 sm->wpa_auth->conf.wpa_group_update_count);
 }
 
 
@@ -2691,7 +3175,7 @@
 		    !sm->EAPOLKeyPairwise && sm->MICVerified)
 			SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
 		else if (sm->GTimeoutCtr >
-			 (int) dot11RSNAConfigGroupUpdateCount)
+			 sm->wpa_auth->conf.wpa_group_update_count)
 			SM_ENTER(WPA_PTK_GROUP, KEYERROR);
 		else if (sm->TimeoutEvt)
 			SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
@@ -3151,8 +3635,8 @@
 		"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
 		RSN_VERSION,
 		!!wpa_auth->conf.wpa_strict_rekey,
-		dot11RSNAConfigGroupUpdateCount,
-		dot11RSNAConfigPairwiseUpdateCount,
+		wpa_auth->conf.wpa_group_update_count,
+		wpa_auth->conf.wpa_pairwise_update_count,
 		wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
 		dot11RSNAConfigPMKLifetime,
 		dot11RSNAConfigPMKReauthThreshold,
@@ -3320,7 +3804,7 @@
 	    sm->wpa_auth->conf.disable_pmksa_caching)
 		return -1;
 
-	if (sm->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+	if (wpa_key_mgmt_sha384(sm->wpa_key_mgmt)) {
 		if (pmk_len > PMK_LEN_SUITE_B_192)
 			pmk_len = PMK_LEN_SUITE_B_192;
 	} else if (pmk_len > PMK_LEN) {
@@ -3404,12 +3888,65 @@
 }
 
 
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			     char *buf, size_t len)
+{
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return 0;
+
+	return pmksa_cache_auth_list_mesh(wpa_auth->pmksa, addr, buf, len);
+}
+
+
 struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    const u8 *pmkid, int expiration)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	struct os_reltime now;
+
+	entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
+					      spa, 0, NULL, WPA_KEY_MGMT_SAE);
+	if (!entry)
+		return NULL;
+
+	os_get_reltime(&now);
+	entry->expiration = now.sec + expiration;
+	return entry;
+}
+
+
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+			     struct rsn_pmksa_cache_entry *entry)
+{
+	int ret;
+
+	if (!wpa_auth || !wpa_auth->pmksa)
+		return -1;
+
+	ret = pmksa_cache_auth_add_entry(wpa_auth->pmksa, entry);
+	if (ret < 0)
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Failed to store external PMKSA cache for "
+			   MACSTR, MAC2STR(entry->spa));
+
+	return ret;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		   const u8 *pmkid)
 {
 	if (!wpa_auth || !wpa_auth->pmksa)
 		return NULL;
-	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
+	return pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, pmkid);
 }
 
 
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index e66f08b..387d146 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -144,6 +144,8 @@
 	int wpa_strict_rekey;
 	int wpa_gmk_rekey;
 	int wpa_ptk_rekey;
+	u32 wpa_group_update_count;
+	u32 wpa_pairwise_update_count;
 	int rsn_pairwise;
 	int rsn_preauth;
 	int eapol_version;
@@ -157,7 +159,7 @@
 	enum mfp_options ieee80211w;
 	int group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
 	u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
@@ -170,7 +172,8 @@
 	struct ft_remote_r1kh *r1kh_list;
 	int pmk_r1_push;
 	int ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+	int ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
 	int disable_gtk;
 	int ap_mlme;
 #ifdef CONFIG_TESTING_OPTIONS
@@ -197,7 +200,6 @@
 } wpa_eapol_variable;
 
 struct wpa_auth_callbacks {
-	void *ctx;
 	void (*logger)(void *ctx, const u8 *addr, logger_level level,
 		       const char *txt);
 	void (*disconnect)(void *ctx, const u8 *addr, u16 reason);
@@ -220,13 +222,13 @@
 						  void *ctx), void *cb_ctx);
 	int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
 			  size_t data_len);
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
 	int (*send_ft_action)(void *ctx, const u8 *dst,
 			      const u8 *data, size_t data_len);
 	int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
 			 size_t tspec_ielen);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_MESH
 	int (*start_ampe)(void *ctx, const u8 *sta_addr);
 #endif /* CONFIG_MESH */
@@ -234,7 +236,8 @@
 
 struct wpa_authenticator * wpa_init(const u8 *addr,
 				    struct wpa_auth_config *conf,
-				    struct wpa_auth_callbacks *cb);
+				    const struct wpa_auth_callbacks *cb,
+				    void *cb_ctx);
 int wpa_init_keys(struct wpa_authenticator *wpa_auth);
 void wpa_deinit(struct wpa_authenticator *wpa_auth);
 int wpa_reconfig(struct wpa_authenticator *wpa_auth,
@@ -302,8 +305,16 @@
 int wpa_auth_pmksa_list(struct wpa_authenticator *wpa_auth, char *buf,
 			size_t len);
 void wpa_auth_pmksa_flush(struct wpa_authenticator *wpa_auth);
+int wpa_auth_pmksa_list_mesh(struct wpa_authenticator *wpa_auth, const u8 *addr,
+			     char *buf, size_t len);
 struct rsn_pmksa_cache_entry *
-wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr);
+wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+			    const u8 *pmkid, int expiration);
+int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
+			     struct rsn_pmksa_cache_entry *entry);
+struct rsn_pmksa_cache_entry *
+wpa_auth_pmksa_get(struct wpa_authenticator *wpa_auth, const u8 *sta_addr,
+		   const u8 *pmkid);
 void wpa_auth_pmksa_set_to_sm(struct rsn_pmksa_cache_entry *pmksa,
 			      struct wpa_state_machine *sm,
 			      struct wpa_authenticator *wpa_auth,
@@ -312,7 +323,7 @@
 void wpa_auth_eapol_key_tx_status(struct wpa_authenticator *wpa_auth,
 				  struct wpa_state_machine *sm, int ack);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 				 size_t max_len, int auth_alg,
 				 const u8 *req_ies, size_t req_ies_len);
@@ -328,7 +339,7 @@
 int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
 		  const u8 *data, size_t data_len);
 void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm);
 void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag);
@@ -347,5 +358,14 @@
 
 int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
 int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
+int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
+			 size_t pmk_len, const u8 *snonce, const u8 *anonce);
+int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
+		       const struct ieee80211_mgmt *mgmt, size_t frame_len,
+		       u8 *pos, size_t left);
+int fils_encrypt_assoc(struct wpa_state_machine *sm, u8 *buf,
+		       size_t current_len, size_t max_len,
+		       const struct wpabuf *hlp);
+int fils_set_tk(struct wpa_state_machine *sm);
 
 #endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index e63b99a..210d300 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -22,7 +22,7 @@
 #include "wpa_auth_i.h"
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 
 static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
 				     const u8 *current_ap, const u8 *sta_addr,
@@ -33,30 +33,41 @@
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
 			   const u8 *data, size_t data_len)
 {
-	if (wpa_auth->cb.send_ether == NULL)
+	if (wpa_auth->cb->send_ether == NULL)
 		return -1;
 	wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
-	return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB,
-				       data, data_len);
+	return wpa_auth->cb->send_ether(wpa_auth->cb_ctx, dst, ETH_P_RRB,
+					data, data_len);
 }
 
 
 static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth,
 			      const u8 *dst, const u8 *data, size_t data_len)
 {
-	if (wpa_auth->cb.send_ft_action == NULL)
+	if (wpa_auth->cb->send_ft_action == NULL)
 		return -1;
-	return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst,
-					   data, data_len);
+	return wpa_auth->cb->send_ft_action(wpa_auth->cb_ctx, dst,
+					    data, data_len);
+}
+
+
+static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
+				 const u8 *addr, const u8 *p2p_dev_addr,
+				 const u8 *prev_psk)
+{
+	if (wpa_auth->cb->get_psk == NULL)
+		return NULL;
+	return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
+				     prev_psk);
 }
 
 
 static struct wpa_state_machine *
 wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr)
 {
-	if (wpa_auth->cb.add_sta == NULL)
+	if (wpa_auth->cb->add_sta == NULL)
 		return NULL;
-	return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr);
+	return wpa_auth->cb->add_sta(wpa_auth->cb_ctx, sta_addr);
 }
 
 
@@ -64,12 +75,12 @@
 			    const u8 *sta_addr,
 			    u8 *tspec_ie, size_t tspec_ielen)
 {
-	if (wpa_auth->cb.add_tspec == NULL) {
+	if (wpa_auth->cb->add_tspec == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
 		return -1;
 	}
-	return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
-				      tspec_ielen);
+	return wpa_auth->cb->add_tspec(wpa_auth->cb_ctx, sta_addr, tspec_ie,
+				       tspec_ielen);
 }
 
 
@@ -373,6 +384,7 @@
 	const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder;
 	const u8 *ssid = sm->wpa_auth->conf.ssid;
 	size_t ssid_len = sm->wpa_auth->conf.ssid_len;
+	int psk_local = sm->wpa_auth->conf.ft_psk_generate_local;
 
 	if (sm->xxkey_len == 0) {
 		wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
@@ -380,20 +392,25 @@
 		return -1;
 	}
 
-	wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
-			  r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name);
+	if (wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid,
+			      r0kh, r0kh_len, sm->addr,
+			      pmk_r0, pmk_r0_name) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
-			    sm->pairwise);
+	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name,
+				    sm->pairwise);
 
-	wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
-			  pmk_r1, sm->pmk_r1_name);
+	if (wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+			      pmk_r1, sm->pmk_r1_name) < 0)
+		return -1;
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
-	wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
-			    sm->pairwise);
+	if (!psk_local || !wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt))
+		wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1,
+				    sm->pmk_r1_name, sm->pairwise);
 
 	return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
 				 sm->wpa_auth->addr, sm->pmk_r1_name,
@@ -404,9 +421,9 @@
 static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
 				      const u8 *addr, int idx, u8 *seq)
 {
-	if (wpa_auth->cb.get_seqnum == NULL)
+	if (wpa_auth->cb->get_seqnum == NULL)
 		return -1;
-	return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq);
+	return wpa_auth->cb->get_seqnum(wpa_auth->cb_ctx, addr, idx, seq);
 }
 
 
@@ -759,10 +776,10 @@
 				   enum wpa_alg alg, const u8 *addr, int idx,
 				   u8 *key, size_t key_len)
 {
-	if (wpa_auth->cb.set_key == NULL)
+	if (wpa_auth->cb->set_key == NULL)
 		return -1;
-	return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx,
-				    key, key_len);
+	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
+				     key, key_len);
 }
 
 
@@ -804,6 +821,89 @@
 }
 
 
+/* Derive PMK-R1 from PSK, check all available PSK */
+static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
+			     const u8 *req_pmk_r1_name,
+			     u8 *out_pmk_r1, int *out_pairwise)
+{
+	const u8 *pmk = NULL;
+	u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+	u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN];
+	struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+	const u8 *mdid = wpa_auth->conf.mobility_domain;
+	const u8 *r0kh = sm->r0kh_id;
+	size_t r0kh_len = sm->r0kh_id_len;
+	const u8 *r1kh = wpa_auth->conf.r1_key_holder;
+	const u8 *ssid = wpa_auth->conf.ssid;
+	size_t ssid_len = wpa_auth->conf.ssid_len;
+	int pairwise;
+
+	pairwise = sm->pairwise;
+
+	for (;;) {
+		pmk = wpa_ft_get_psk(wpa_auth, sm->addr, sm->p2p_dev_addr,
+				     pmk);
+		if (pmk == NULL)
+			break;
+
+		if (wpa_derive_pmk_r0(pmk, PMK_LEN, ssid, ssid_len, mdid, r0kh,
+				      r0kh_len, sm->addr,
+				      pmk_r0, pmk_r0_name) < 0 ||
+		    wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+				      pmk_r1, pmk_r1_name) < 0 ||
+		    os_memcmp_const(pmk_r1_name, req_pmk_r1_name,
+				    WPA_PMK_NAME_LEN) != 0)
+			continue;
+
+		/* We found a PSK that matches the requested pmk_r1_name */
+		wpa_printf(MSG_DEBUG,
+			   "FT: Found PSK to generate PMK-R1 locally");
+		os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
+		if (out_pairwise)
+			*out_pairwise = pairwise;
+		return 0;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "FT: Did not find PSK to generate PMK-R1 locally");
+	return -1;
+}
+
+
+/* Detect the configuration the station asked for.
+ * Required to detect FT-PSK and pairwise cipher.
+ */
+static int wpa_ft_set_key_mgmt(struct wpa_state_machine *sm,
+			       struct wpa_ft_ies *parse)
+{
+	int key_mgmt, ciphers;
+
+	if (sm->wpa_key_mgmt)
+		return 0;
+
+	key_mgmt = parse->key_mgmt & sm->wpa_auth->conf.wpa_key_mgmt;
+	if (!key_mgmt) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid key mgmt (0x%x) from "
+			   MACSTR, parse->key_mgmt, MAC2STR(sm->addr));
+		return -1;
+	}
+	if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
+	ciphers = parse->pairwise_cipher & sm->wpa_auth->conf.rsn_pairwise;
+	if (!ciphers) {
+		wpa_printf(MSG_DEBUG, "FT: Invalid pairwise cipher (0x%x) from "
+			   MACSTR,
+			   parse->pairwise_cipher, MAC2STR(sm->addr));
+		return -1;
+	}
+	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
+
+	return 0;
+}
+
+
 static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 				   const u8 *ies, size_t ies_len,
 				   u8 **resp_ies, size_t *resp_ies_len)
@@ -865,16 +965,24 @@
 		return WLAN_STATUS_INVALID_PMKID;
 	}
 
+	if (wpa_ft_set_key_mgmt(sm, &parse) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
 	wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name",
 		    parse.rsn_pmkid, WPA_PMK_NAME_LEN);
-	wpa_derive_pmk_r1_name(parse.rsn_pmkid,
-			       sm->wpa_auth->conf.r1_key_holder, sm->addr,
-			       pmk_r1_name);
+	if (wpa_derive_pmk_r1_name(parse.rsn_pmkid,
+				   sm->wpa_auth->conf.r1_key_holder, sm->addr,
+				   pmk_r1_name) < 0)
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name",
 		    pmk_r1_name, WPA_PMK_NAME_LEN);
 
-	if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
-		    &pairwise) < 0) {
+	if (conf->ft_psk_generate_local &&
+	    wpa_key_mgmt_ft_psk(sm->wpa_key_mgmt)) {
+		if (wpa_ft_psk_pmk_r1(sm, pmk_r1_name, pmk_r1, &pairwise) < 0)
+			return WLAN_STATUS_INVALID_PMKID;
+	} else if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name,
+				       pmk_r1, &pairwise) < 0) {
 		if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
 			wpa_printf(MSG_DEBUG, "FT: Did not have matching "
 				   "PMK-R1 and unknown R0KH-ID");
@@ -913,41 +1021,35 @@
 	buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
 		2 + FT_R1KH_ID_LEN + 200;
 	*resp_ies = os_zalloc(buflen);
-	if (*resp_ies == NULL) {
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
+	if (*resp_ies == NULL)
+		goto fail;
 
 	pos = *resp_ies;
 	end = *resp_ies + buflen;
 
 	ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid);
-	if (ret < 0) {
-		os_free(*resp_ies);
-		*resp_ies = NULL;
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
+	if (ret < 0)
+		goto fail;
 	pos += ret;
 
 	ret = wpa_write_mdie(conf, pos, end - pos);
-	if (ret < 0) {
-		os_free(*resp_ies);
-		*resp_ies = NULL;
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
+	if (ret < 0)
+		goto fail;
 	pos += ret;
 
 	ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len,
 			     sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0);
-	if (ret < 0) {
-		os_free(*resp_ies);
-		*resp_ies = NULL;
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
+	if (ret < 0)
+		goto fail;
 	pos += ret;
 
 	*resp_ies_len = pos - *resp_ies;
 
 	return WLAN_STATUS_SUCCESS;
+fail:
+	os_free(*resp_ies);
+	*resp_ies = NULL;
+	return WLAN_STATUS_UNSPECIFIED_FAILURE;
 }
 
 
@@ -1051,7 +1153,7 @@
 			    ftie->snonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
 			    sm->SNonce, WPA_NONCE_LEN);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
@@ -1060,13 +1162,13 @@
 			    ftie->anonce, WPA_NONCE_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
 			    sm->ANonce, WPA_NONCE_LEN);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 
 	if (parse.r0kh_id == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.r0kh_id_len != sm->r0kh_id_len ||
@@ -1078,12 +1180,12 @@
 			    parse.r0kh_id, parse.r0kh_id_len);
 		wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
 			    sm->r0kh_id, sm->r0kh_id_len);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.r1kh_id == NULL) {
 		wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
@@ -1094,7 +1196,7 @@
 			    parse.r1kh_id, FT_R1KH_ID_LEN);
 		wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
 			    sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
-		return -1;
+		return WLAN_STATUS_INVALID_FTIE;
 	}
 
 	if (parse.rsn_pmkid == NULL ||
@@ -1102,7 +1204,7 @@
 	{
 		wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
 			   "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
-		return -1;
+		return WLAN_STATUS_INVALID_PMKID;
 	}
 
 	count = 3;
@@ -1112,7 +1214,7 @@
 		wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
 			   "Control: received %u expected %u",
 			   ftie->mic_control[1], count);
-		return -1;
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
 
 	if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
@@ -1199,6 +1301,11 @@
 
 	wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len);
 
+	if (!sm->wpa_auth->conf.ft_over_ds) {
+		wpa_printf(MSG_DEBUG, "FT: Over-DS option disabled - reject");
+		return -1;
+	}
+
 	/* RRB - Forward action frame to the target AP */
 	frame = os_malloc(sizeof(*frame) + len);
 	if (frame == NULL)
@@ -1384,8 +1491,11 @@
 		return -1;
 	}
 
-	wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
-			  r.pmk_r1, r.pmk_r1_name);
+	if (wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id,
+			      r.pmk_r1, r.pmk_r1_name) < 0) {
+		os_memset(pmk_r0, 0, PMK_LEN);
+		return -1;
+	}
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
 		    WPA_PMK_NAME_LEN);
@@ -1437,12 +1547,10 @@
 {
 	struct ft_r0kh_r1kh_resp_frame *frame = ctx;
 
-	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
-		return 0;
-	if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
-		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
-		return 0;
-	if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+	if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0 ||
+	    os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
+		      FT_R0KH_R1KH_PULL_NONCE_LEN) != 0 ||
+	    sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
 		return 0;
 
 	wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
@@ -1726,10 +1834,10 @@
 }
 
 
-static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
-				   struct wpa_ft_pmk_r0_sa *pmk_r0,
-				   struct ft_remote_r1kh *r1kh,
-				   const u8 *s1kh_id, int pairwise)
+static int wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
+				  struct wpa_ft_pmk_r0_sa *pmk_r0,
+				  struct ft_remote_r1kh *r1kh,
+				  const u8 *s1kh_id, int pairwise)
 {
 	struct ft_r0kh_r1kh_push_frame frame, f;
 	struct os_time now;
@@ -1747,8 +1855,9 @@
 	os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN);
 	os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
 	os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN);
-	wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
-			  s1kh_id, f.pmk_r1, f.pmk_r1_name);
+	if (wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id,
+			      s1kh_id, f.pmk_r1, f.pmk_r1_name) < 0)
+		return -1;
 	wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id));
 	wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN);
 	wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name,
@@ -1764,9 +1873,10 @@
 	if (aes_wrap(r1kh->key, sizeof(r1kh->key),
 		     (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
 		     plain, crypt) < 0)
-		return;
+		return -1;
 
 	wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
+	return 0;
 }
 
 
@@ -1799,4 +1909,4 @@
 	}
 }
 
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 5fe0987..394f77a 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -41,6 +41,8 @@
 	wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
 	wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
 	wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
+	wconf->wpa_group_update_count = conf->wpa_group_update_count;
+	wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
 	wconf->rsn_pairwise = conf->rsn_pairwise;
 	wconf->rsn_preauth = conf->rsn_preauth;
 	wconf->eapol_version = conf->eapol_version;
@@ -53,7 +55,7 @@
 	wconf->ieee80211w = conf->ieee80211w;
 	wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	wconf->ssid_len = conf->ssid.ssid_len;
 	if (wconf->ssid_len > SSID_MAX_LEN)
 		wconf->ssid_len = SSID_MAX_LEN;
@@ -73,7 +75,8 @@
 	wconf->r1kh_list = conf->r1kh_list;
 	wconf->pmk_r1_push = conf->pmk_r1_push;
 	wconf->ft_over_ds = conf->ft_over_ds;
-#endif /* CONFIG_IEEE80211R */
+	wconf->ft_psk_generate_local = conf->ft_psk_generate_local;
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_HS20
 	wconf->disable_gtk = conf->disable_dgaf;
 	if (conf->osen) {
@@ -401,7 +404,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 
 struct wpa_auth_ft_iface_iter_data {
 	struct hostapd_data *src_hapd;
@@ -440,7 +443,7 @@
 	return 0;
 }
 
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
@@ -465,7 +468,7 @@
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (proto == ETH_P_RRB && hapd->iface->interfaces &&
 	    hapd->iface->interfaces->for_each_interface) {
 		int res;
@@ -480,7 +483,7 @@
 		if (res == 1)
 			return data_len;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	if (hapd->driver && hapd->driver->send_ether)
 		return hapd->driver->send_ether(hapd->drv_priv, dst,
@@ -503,7 +506,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 
 static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
 					   const u8 *data, size_t data_len)
@@ -588,13 +591,33 @@
 	return hostapd_add_tspec(hapd, sta_addr, tspec_ie, tspec_ielen);
 }
 
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 
 int hostapd_setup_wpa(struct hostapd_data *hapd)
 {
 	struct wpa_auth_config _conf;
-	struct wpa_auth_callbacks cb;
+	static const struct wpa_auth_callbacks cb = {
+		.logger = hostapd_wpa_auth_logger,
+		.disconnect = hostapd_wpa_auth_disconnect,
+		.mic_failure_report = hostapd_wpa_auth_mic_failure_report,
+		.psk_failure_report = hostapd_wpa_auth_psk_failure_report,
+		.set_eapol = hostapd_wpa_auth_set_eapol,
+		.get_eapol = hostapd_wpa_auth_get_eapol,
+		.get_psk = hostapd_wpa_auth_get_psk,
+		.get_msk = hostapd_wpa_auth_get_msk,
+		.set_key = hostapd_wpa_auth_set_key,
+		.get_seqnum = hostapd_wpa_auth_get_seqnum,
+		.send_eapol = hostapd_wpa_auth_send_eapol,
+		.for_each_sta = hostapd_wpa_auth_for_each_sta,
+		.for_each_auth = hostapd_wpa_auth_for_each_auth,
+		.send_ether = hostapd_wpa_auth_send_ether,
+#ifdef CONFIG_IEEE80211R_AP
+		.send_ft_action = hostapd_wpa_auth_send_ft_action,
+		.add_sta = hostapd_wpa_auth_add_sta,
+		.add_tspec = hostapd_wpa_auth_add_tspec,
+#endif /* CONFIG_IEEE80211R_AP */
+	};
 	const u8 *wpa_ie;
 	size_t wpa_ie_len;
 
@@ -603,28 +626,7 @@
 		_conf.tx_status = 1;
 	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
 		_conf.ap_mlme = 1;
-	os_memset(&cb, 0, sizeof(cb));
-	cb.ctx = hapd;
-	cb.logger = hostapd_wpa_auth_logger;
-	cb.disconnect = hostapd_wpa_auth_disconnect;
-	cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report;
-	cb.psk_failure_report = hostapd_wpa_auth_psk_failure_report;
-	cb.set_eapol = hostapd_wpa_auth_set_eapol;
-	cb.get_eapol = hostapd_wpa_auth_get_eapol;
-	cb.get_psk = hostapd_wpa_auth_get_psk;
-	cb.get_msk = hostapd_wpa_auth_get_msk;
-	cb.set_key = hostapd_wpa_auth_set_key;
-	cb.get_seqnum = hostapd_wpa_auth_get_seqnum;
-	cb.send_eapol = hostapd_wpa_auth_send_eapol;
-	cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
-	cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
-	cb.send_ether = hostapd_wpa_auth_send_ether;
-#ifdef CONFIG_IEEE80211R
-	cb.send_ft_action = hostapd_wpa_auth_send_ft_action;
-	cb.add_sta = hostapd_wpa_auth_add_sta;
-	cb.add_tspec = hostapd_wpa_auth_add_tspec;
-#endif /* CONFIG_IEEE80211R */
-	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb);
+	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
 	if (hapd->wpa_auth == NULL) {
 		wpa_printf(MSG_ERROR, "WPA initialization failed.");
 		return -1;
@@ -649,8 +651,8 @@
 		return -1;
 	}
 
-#ifdef CONFIG_IEEE80211R
-	if (!hostapd_drv_none(hapd) && hapd->conf->ft_over_ds &&
+#ifdef CONFIG_IEEE80211R_AP
+	if (!hostapd_drv_none(hapd) &&
 	    wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt)) {
 		hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
 					  hapd->conf->bridge :
@@ -664,7 +666,7 @@
 			return -1;
 		}
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	return 0;
 
@@ -702,8 +704,8 @@
 	}
 	ieee802_1x_deinit(hapd);
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	l2_packet_deinit(hapd->l2);
 	hapd->l2 = NULL;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 }
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 7fd8f05..9564f24 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -14,13 +14,6 @@
 
 struct wpa_group;
 
-struct wpa_stsl_negotiation {
-	struct wpa_stsl_negotiation *next;
-	u8 initiator[ETH_ALEN];
-	u8 peer[ETH_ALEN];
-};
-
-
 struct wpa_state_machine {
 	struct wpa_authenticator *wpa_auth;
 	struct wpa_group *group;
@@ -48,8 +41,8 @@
 	Boolean AuthenticationRequest;
 	Boolean ReAuthenticationRequest;
 	Boolean Disconnect;
-	int TimeoutCtr;
-	int GTimeoutCtr;
+	u32 TimeoutCtr;
+	u32 GTimeoutCtr;
 	Boolean TimeoutEvt;
 	Boolean EAPOLKeyReceived;
 	Boolean EAPOLKeyPairwise;
@@ -89,10 +82,10 @@
 	unsigned int rx_eapol_key_secure:1;
 	unsigned int update_snonce:1;
 	unsigned int alt_snonce_valid:1;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	unsigned int ft_completed:1;
 	unsigned int pmk_r1_name_valid:1;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	unsigned int is_wnmsleep:1;
 
 	u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
@@ -113,7 +106,7 @@
 	u32 dot11RSNAStatsTKIPLocalMICFailures;
 	u32 dot11RSNAStatsTKIPRemoteMICFailures;
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
 	size_t xxkey_len;
 	u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
@@ -132,13 +125,20 @@
 	u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
 	u8 ft_pending_auth_transaction;
 	u8 ft_pending_current_ap[ETH_ALEN];
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	int pending_1_of_4_timeout;
 
 #ifdef CONFIG_P2P
 	u8 ip_addr[4];
 #endif /* CONFIG_P2P */
+
+#ifdef CONFIG_FILS
+	u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+	u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+	size_t fils_key_auth_len;
+	unsigned int fils_completed:1;
+#endif /* CONFIG_FILS */
 };
 
 
@@ -194,10 +194,9 @@
 	unsigned int dot11RSNATKIPCounterMeasuresInvoked;
 	unsigned int dot11RSNA4WayHandshakeFailures;
 
-	struct wpa_stsl_negotiation *stsl_negotiations;
-
 	struct wpa_auth_config conf;
-	struct wpa_auth_callbacks cb;
+	const struct wpa_auth_callbacks *cb;
+	void *cb_ctx;
 
 	u8 *wpa_ie;
 	size_t wpa_ie_len;
@@ -232,8 +231,6 @@
 			   void *cb_ctx);
 
 #ifdef CONFIG_PEERKEY
-int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
-		    struct wpa_stsl_negotiation *neg);
 void wpa_smk_error(struct wpa_authenticator *wpa_auth,
 		   struct wpa_state_machine *sm,
 		   const u8 *key_data, size_t key_data_len);
@@ -245,7 +242,7 @@
 		const u8 *key_data, size_t key_data_len);
 #endif /* CONFIG_PEERKEY */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
 int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
 		   size_t r0kh_id_len,
@@ -257,6 +254,6 @@
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 #endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index f79783b..c770d62 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -164,7 +164,7 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
 		pos += RSN_SELECTOR_LEN;
@@ -175,7 +175,7 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
@@ -210,6 +210,30 @@
 		pos += RSN_SELECTOR_LEN;
 		num_suites++;
 	}
+#ifdef CONFIG_FILS
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#ifdef CONFIG_IEEE80211R_AP
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+	if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+		pos += RSN_SELECTOR_LEN;
+		num_suites++;
+	}
+#endif /* CONFIG_IEEE80211R_AP */
+#endif /* CONFIG_FILS */
 
 #ifdef CONFIG_RSN_TESTING
 	if (rsn_testing) {
@@ -407,7 +431,7 @@
 			return res;
 		pos += res;
 	}
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
 		res = wpa_write_mdie(&wpa_auth->conf, pos,
 				     buf + sizeof(buf) - pos);
@@ -415,7 +439,7 @@
 			return res;
 		pos += res;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 	if (wpa_auth->conf.wpa & WPA_PROTO_WPA) {
 		res = wpa_write_wpa_ie(&wpa_auth->conf,
 				       pos, buf + sizeof(buf) - pos);
@@ -509,12 +533,24 @@
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+		else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+			selector = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+		else if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+			selector = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 			selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
 		else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK)
 			selector = RSN_AUTH_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 		else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 			selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
@@ -591,12 +627,24 @@
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R_AP
+	else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+	else if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+#endif /* CONFIG_IEEE80211R_AP */
+	else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+	else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256)
+		sm->wpa_key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
 	else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
 	else if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
 	else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
 		sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
@@ -655,7 +703,7 @@
 		sm->mgmt_frame_prot = 1;
 #endif /* CONFIG_IEEE80211W */
 
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {
 			wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but "
@@ -668,8 +716,12 @@
 				    "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN);
 			return WPA_INVALID_MDIE;
 		}
+	} else if (mdie != NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "RSN: Trying to use non-FT AKM suite, but MDIE included");
+		return WPA_INVALID_AKMP;
 	}
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 
 	sm->pairwise = wpa_pick_pairwise_cipher(ciphers, 0);
 	if (sm->pairwise < 0)
@@ -908,14 +960,14 @@
 		if (*pos == WLAN_EID_RSN) {
 			ie->rsn_ie = pos;
 			ie->rsn_ie_len = pos[1] + 2;
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
 			ie->mdie = pos;
 			ie->mdie_len = pos[1] + 2;
 		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
 			ie->ftie = pos;
 			ie->ftie_len = pos[1] + 2;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
 			ret = wpa_parse_generic(pos, end, ie);
 			if (ret < 0)
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index d2067ba..5c3bd18 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -33,12 +33,12 @@
 	const u8 *igtk;
 	size_t igtk_len;
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_IEEE80211R_AP
 	const u8 *mdie;
 	size_t mdie_len;
 	const u8 *ftie;
 	size_t ftie_len;
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_P2P
 	const u8 *ip_addr_req;
 	const u8 *ip_addr_alloc;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index faf38c9..95b40da 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -269,12 +269,6 @@
 }
 
 
-static int str_starts(const char *str, const char *start)
-{
-	return os_strncmp(str, start, os_strlen(start)) == 0;
-}
-
-
 static void wps_reload_config(void *eloop_data, void *user_ctx)
 {
 	struct hostapd_iface *iface = eloop_data;
diff --git a/src/common/cli.c b/src/common/cli.c
new file mode 100644
index 0000000..b583d1c
--- /dev/null
+++ b/src/common/cli.c
@@ -0,0 +1,267 @@
+/*
+ * Common hostapd/wpa_supplicant command line interface functions
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "common/cli.h"
+
+
+const char *const cli_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"See README for more details.\n";
+
+const char *const cli_full_license =
+"This software may be distributed under the terms of the BSD license.\n"
+"\n"
+"Redistribution and use in source and binary forms, with or without\n"
+"modification, are permitted provided that the following conditions are\n"
+"met:\n"
+"\n"
+"1. Redistributions of source code must retain the above copyright\n"
+"   notice, this list of conditions and the following disclaimer.\n"
+"\n"
+"2. Redistributions in binary form must reproduce the above copyright\n"
+"   notice, this list of conditions and the following disclaimer in the\n"
+"   documentation and/or other materials provided with the distribution.\n"
+"\n"
+"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
+"   names of its contributors may be used to endorse or promote products\n"
+"   derived from this software without specific prior written permission.\n"
+"\n"
+"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
+"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
+"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
+"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
+"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
+"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
+"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
+"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
+"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
+"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
+"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
+"\n";
+
+
+void cli_txt_list_free(struct cli_txt_entry *e)
+{
+	dl_list_del(&e->list);
+	os_free(e->txt);
+	os_free(e);
+}
+
+
+void cli_txt_list_flush(struct dl_list *list)
+{
+	struct cli_txt_entry *e;
+
+	while ((e = dl_list_first(list, struct cli_txt_entry, list)))
+		cli_txt_list_free(e);
+}
+
+
+struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
+					const char *txt)
+{
+	struct cli_txt_entry *e;
+
+	dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+		if (os_strcmp(e->txt, txt) == 0)
+			return e;
+	}
+	return NULL;
+}
+
+
+void cli_txt_list_del(struct dl_list *txt_list, const char *txt)
+{
+	struct cli_txt_entry *e;
+
+	e = cli_txt_list_get(txt_list, txt);
+	if (e)
+		cli_txt_list_free(e);
+}
+
+
+void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
+{
+	u8 addr[ETH_ALEN];
+	char buf[18];
+
+	if (hwaddr_aton(txt, addr) < 0)
+		return;
+	os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	cli_txt_list_del(txt_list, buf);
+}
+
+
+void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
+			   int separator)
+{
+	const char *end;
+	char *buf;
+
+	end = os_strchr(txt, separator);
+	if (end == NULL)
+		end = txt + os_strlen(txt);
+	buf = dup_binstr(txt, end - txt);
+	if (buf == NULL)
+		return;
+	cli_txt_list_del(txt_list, buf);
+	os_free(buf);
+}
+
+
+int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
+{
+	struct cli_txt_entry *e;
+
+	e = cli_txt_list_get(txt_list, txt);
+	if (e)
+		return 0;
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return -1;
+	e->txt = os_strdup(txt);
+	if (e->txt == NULL) {
+		os_free(e);
+		return -1;
+	}
+	dl_list_add(txt_list, &e->list);
+	return 0;
+}
+
+
+int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
+{
+	u8 addr[ETH_ALEN];
+	char buf[18];
+
+	if (hwaddr_aton(txt, addr) < 0)
+		return -1;
+	os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+	return cli_txt_list_add(txt_list, buf);
+}
+
+
+int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
+			  int separator)
+{
+	const char *end;
+	char *buf;
+	int ret;
+
+	end = os_strchr(txt, separator);
+	if (end == NULL)
+		end = txt + os_strlen(txt);
+	buf = dup_binstr(txt, end - txt);
+	if (buf == NULL)
+		return -1;
+	ret = cli_txt_list_add(txt_list, buf);
+	os_free(buf);
+	return ret;
+}
+
+
+char ** cli_txt_list_array(struct dl_list *txt_list)
+{
+	unsigned int i, count = dl_list_len(txt_list);
+	char **res;
+	struct cli_txt_entry *e;
+
+	res = os_calloc(count + 1, sizeof(char *));
+	if (res == NULL)
+		return NULL;
+
+	i = 0;
+	dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
+		res[i] = os_strdup(e->txt);
+		if (res[i] == NULL)
+			break;
+		i++;
+	}
+
+	return res;
+}
+
+
+int get_cmd_arg_num(const char *str, int pos)
+{
+	int arg = 0, i;
+
+	for (i = 0; i <= pos; i++) {
+		if (str[i] != ' ') {
+			arg++;
+			while (i <= pos && str[i] != ' ')
+				i++;
+		}
+	}
+
+	if (arg > 0)
+		arg--;
+	return arg;
+}
+
+
+int write_cmd(char *buf, size_t buflen, const char *cmd, int argc, char *argv[])
+{
+	int i, res;
+	char *pos, *end;
+
+	pos = buf;
+	end = buf + buflen;
+
+	res = os_snprintf(pos, end - pos, "%s", cmd);
+	if (os_snprintf_error(end - pos, res))
+		goto fail;
+	pos += res;
+
+	for (i = 0; i < argc; i++) {
+		res = os_snprintf(pos, end - pos, " %s", argv[i]);
+		if (os_snprintf_error(end - pos, res))
+			goto fail;
+		pos += res;
+	}
+
+	buf[buflen - 1] = '\0';
+	return 0;
+
+fail:
+	printf("Too long command\n");
+	return -1;
+}
+
+
+int tokenize_cmd(char *cmd, char *argv[])
+{
+	char *pos;
+	int argc = 0;
+
+	pos = cmd;
+	for (;;) {
+		while (*pos == ' ')
+			pos++;
+		if (*pos == '\0')
+			break;
+		argv[argc] = pos;
+		argc++;
+		if (argc == max_args)
+			break;
+		if (*pos == '"') {
+			char *pos2 = os_strrchr(pos, '"');
+			if (pos2)
+				pos = pos2 + 1;
+		}
+		while (*pos != '\0' && *pos != ' ')
+			pos++;
+		if (*pos == ' ')
+			*pos++ = '\0';
+	}
+
+	return argc;
+}
diff --git a/src/common/cli.h b/src/common/cli.h
new file mode 100644
index 0000000..41ef329
--- /dev/null
+++ b/src/common/cli.h
@@ -0,0 +1,47 @@
+/*
+ * Common hostapd/wpa_supplicant command line interface functionality
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CLI_H
+#define CLI_H
+
+#include "utils/list.h"
+
+extern const char *const cli_license;
+extern const char *const cli_full_license;
+
+struct cli_txt_entry {
+	struct dl_list list;
+	char *txt;
+};
+
+void cli_txt_list_free(struct cli_txt_entry *e);
+void cli_txt_list_flush(struct dl_list *list);
+
+struct cli_txt_entry *
+cli_txt_list_get(struct dl_list *txt_list, const char *txt);
+
+void cli_txt_list_del(struct dl_list *txt_list, const char *txt);
+void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt);
+void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
+			   int separator);
+
+int cli_txt_list_add(struct dl_list *txt_list, const char *txt);
+int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt);
+int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
+			  int separator);
+
+char ** cli_txt_list_array(struct dl_list *txt_list);
+
+int get_cmd_arg_num(const char *str, int pos);
+int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
+	      char *argv[]);
+
+#define max_args 10
+int tokenize_cmd(char *cmd, char *argv[]);
+
+#endif /* CLI_H */
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index d69448b..0b596bb 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/module_tests.h"
 #include "ieee802_11_common.h"
 #include "ieee802_11_defs.h"
 #include "gas.h"
@@ -52,12 +53,38 @@
 	  18, ParseOK, 9 },
 	{ (u8 *) "\x8b\x00", 2, ParseOK, 1 },
 	{ (u8 *) "\xdd\x04\x00\x90\x4c\x04", 6, ParseUnknown, 1 },
+	{ (u8 *) "\xed\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xef\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xef\x01\x11", 3, ParseOK, 1 },
+	{ (u8 *) "\xf0\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xf1\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xf1\x02\x11\x22", 4, ParseOK, 1 },
+	{ (u8 *) "\xf2\x00", 2, ParseOK, 1 },
+	{ (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
+	{ (u8 *) "\xff\x01\x00", 3, ParseUnknown, 1 },
+	{ (u8 *) "\xff\x01\x01", 3, ParseOK, 1 },
+	{ (u8 *) "\xff\x02\x01\x00", 4, ParseOK, 1 },
+	{ (u8 *) "\xff\x01\x02", 3, ParseOK, 1 },
+	{ (u8 *) "\xff\x04\x02\x11\x22\x33", 6, ParseOK, 1 },
+	{ (u8 *) "\xff\x01\x04", 3, ParseOK, 1 },
+	{ (u8 *) "\xff\x01\x05", 3, ParseOK, 1 },
+	{ (u8 *) "\xff\x0d\x05\x11\x22\x33\x44\x55\x55\x11\x22\x33\x44\x55\x55",
+	  15, ParseOK, 1 },
+	{ (u8 *) "\xff\x01\x06", 3, ParseOK, 1 },
+	{ (u8 *) "\xff\x02\x06\x00", 4, ParseOK, 1 },
+	{ (u8 *) "\xff\x01\x07", 3, ParseOK, 1 },
+	{ (u8 *) "\xff\x09\x07\x11\x22\x33\x44\x55\x66\x77\x88", 11,
+	  ParseOK, 1 },
+	{ (u8 *) "\xff\x01\x0c", 3, ParseOK, 1 },
+	{ (u8 *) "\xff\x02\x0c\x00", 4, ParseOK, 1 },
+	{ (u8 *) "\xff\x01\x0d", 3, ParseOK, 1 },
 	{ NULL, 0, ParseOK, 0 }
 };
 
 static int ieee802_11_parse_tests(void)
 {
 	int i, ret = 0;
+	struct wpabuf *buf;
 
 	wpa_printf(MSG_INFO, "ieee802_11_parse tests");
 
@@ -83,6 +110,35 @@
 		ret = -1;
 	}
 
+	buf = ieee802_11_vendor_ie_concat((const u8 *) "\xdd\x05\x11\x22\x33\x44\x01\xdd\x05\x11\x22\x33\x44\x02\x00\x01",
+					  16, 0x11223344);
+	do {
+		const u8 *pos;
+
+		if (!buf) {
+			wpa_printf(MSG_ERROR,
+				   "ieee802_11_vendor_ie_concat test 2 failed");
+			ret = -1;
+			break;
+		}
+
+		if (wpabuf_len(buf) != 2) {
+			wpa_printf(MSG_ERROR,
+				   "ieee802_11_vendor_ie_concat test 3 failed");
+			ret = -1;
+			break;
+		}
+
+		pos = wpabuf_head(buf);
+		if (pos[0] != 0x01 || pos[1] != 0x02) {
+			wpa_printf(MSG_ERROR,
+				   "ieee802_11_vendor_ie_concat test 3 failed");
+			ret = -1;
+			break;
+		}
+	} while (0);
+	wpabuf_free(buf);
+
 	return ret;
 }
 
diff --git a/src/common/defs.h b/src/common/defs.h
index 6ef929c..eaccced 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -51,6 +51,10 @@
 #define WPA_KEY_MGMT_OSEN BIT(15)
 #define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
 #define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
+#define WPA_KEY_MGMT_FILS_SHA256 BIT(18)
+#define WPA_KEY_MGMT_FILS_SHA384 BIT(19)
+#define WPA_KEY_MGMT_FT_FILS_SHA256 BIT(20)
+#define WPA_KEY_MGMT_FT_FILS_SHA384 BIT(21)
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
@@ -60,7 +64,11 @@
 			 WPA_KEY_MGMT_OSEN |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
 			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
-			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+			 WPA_KEY_MGMT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FILS_SHA384 |
+			 WPA_KEY_MGMT_FT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
 }
 
 static inline int wpa_key_mgmt_wpa_psk(int akm)
@@ -76,7 +84,14 @@
 {
 	return !!(akm & (WPA_KEY_MGMT_FT_PSK |
 			 WPA_KEY_MGMT_FT_IEEE8021X |
-			 WPA_KEY_MGMT_FT_SAE));
+			 WPA_KEY_MGMT_FT_SAE |
+			 WPA_KEY_MGMT_FT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
+}
+
+static inline int wpa_key_mgmt_ft_psk(int akm)
+{
+	return !!(akm & WPA_KEY_MGMT_FT_PSK);
 }
 
 static inline int wpa_key_mgmt_sae(int akm)
@@ -85,17 +100,29 @@
 			 WPA_KEY_MGMT_FT_SAE));
 }
 
+static inline int wpa_key_mgmt_fils(int akm)
+{
+	return !!(akm & (WPA_KEY_MGMT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FILS_SHA384 |
+			 WPA_KEY_MGMT_FT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
+}
+
 static inline int wpa_key_mgmt_sha256(int akm)
 {
 	return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
 			 WPA_KEY_MGMT_IEEE8021X_SHA256 |
 			 WPA_KEY_MGMT_OSEN |
-			 WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+			 WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+			 WPA_KEY_MGMT_FILS_SHA256 |
+			 WPA_KEY_MGMT_FT_FILS_SHA256));
 }
 
 static inline int wpa_key_mgmt_sha384(int akm)
 {
-	return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+	return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 |
+			 WPA_KEY_MGMT_FILS_SHA384 |
+			 WPA_KEY_MGMT_FT_FILS_SHA384));
 }
 
 static inline int wpa_key_mgmt_suite_b(int akm)
@@ -108,6 +135,7 @@
 {
 	return wpa_key_mgmt_wpa_ieee8021x(akm) ||
 		wpa_key_mgmt_wpa_psk(akm) ||
+		wpa_key_mgmt_fils(akm) ||
 		wpa_key_mgmt_sae(akm);
 }
 
@@ -132,6 +160,7 @@
 #define WPA_AUTH_ALG_LEAP BIT(2)
 #define WPA_AUTH_ALG_FT BIT(3)
 #define WPA_AUTH_ALG_SAE BIT(4)
+#define WPA_AUTH_ALG_FILS BIT(5)
 
 
 enum wpa_alg {
@@ -320,13 +349,13 @@
 #define EAP_MAX_METHODS 8
 
 enum mesh_plink_state {
-	PLINK_LISTEN = 1,
-	PLINK_OPEN_SENT,
-	PLINK_OPEN_RCVD,
+	PLINK_IDLE = 1,
+	PLINK_OPN_SNT,
+	PLINK_OPN_RCVD,
 	PLINK_CNF_RCVD,
 	PLINK_ESTAB,
 	PLINK_HOLDING,
-	PLINK_BLOCKED,
+	PLINK_BLOCKED, /* not defined in the IEEE 802.11 standard */
 };
 
 enum set_band {
@@ -341,4 +370,14 @@
 	BAND_60_GHZ = BIT(2),
 };
 
+enum beacon_rate_type {
+	BEACON_RATE_LEGACY,
+	BEACON_RATE_HT,
+	BEACON_RATE_VHT
+};
+
+enum eap_proxy_sim_state {
+	SIM_STATE_ERROR,
+};
+
 #endif /* DEFS_H */
diff --git a/src/common/dhcp.h b/src/common/dhcp.h
new file mode 100644
index 0000000..f2ef61e
--- /dev/null
+++ b/src/common/dhcp.h
@@ -0,0 +1,257 @@
+/*
+ * DHCP definitions
+ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_H
+#define DHCP_H
+
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
+struct dhcp_data {
+	u8 op;
+	u8 htype;
+	u8 hlen;
+	u8 hops;
+	be32 xid;
+	be16 secs;
+	be16 flags;
+	be32 client_ip;
+	be32 your_ip;
+	be32 server_ip;
+	be32 relay_ip;
+	u8 hw_addr[16];
+	u8 serv_name[64];
+	u8 boot_file[128];
+} STRUCT_PACKED;
+
+struct bootp_pkt {
+	struct iphdr iph;
+	struct udphdr udph;
+	u8 op;
+	u8 htype;
+	u8 hlen;
+	u8 hops;
+	be32 xid;
+	be16 secs;
+	be16 flags;
+	be32 client_ip;
+	be32 your_ip;
+	be32 server_ip;
+	be32 relay_ip;
+	u8 hw_addr[16];
+	u8 serv_name[64];
+	u8 boot_file[128];
+	u8 exten[312];
+} STRUCT_PACKED;
+
+#define DHCP_MAGIC 0x63825363
+
+/*
+ * IANA DHCP/BOOTP registry
+ * http://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
+*/
+enum dhcp_options {
+	DHCP_OPT_PAD = 0,
+	DHCP_OPT_SUBNET_MASK = 1,
+	DHCP_OPT_TIME_OFFSET = 2,
+	DHCP_OPT_ROUTER = 3,
+	DHCP_OPT_TIME_SERVER = 4,
+	DHCP_OPT_NAME_SERVER = 5,
+	DHCP_OPT_DOMAIN_NAME_SERVER = 6,
+	DHCP_OPT_LOG_SERVER = 7,
+	DHCP_OPT_QUOTES_SERVER = 8,
+	DHCP_OPT_LPR_SERVER = 9,
+	DHCP_OPT_IMPRESS_SERVER = 10,
+	DHCP_OPT_RLP_SERVER = 11,
+	DHCP_OPT_HOSTNAME = 12,
+	DHCP_OPT_BOOT_FILE_SIZE = 13,
+	DHCP_OPT_MERIT_DUMP_FILE = 14,
+	DHCP_OPT_DOMAIN_NAME = 15,
+	DHCP_OPT_SWAP_SERVER = 16,
+	DHCP_OPT_ROOT_PATH = 17,
+	DHCP_OPT_EXTENSION_PATH = 18,
+	DHCP_OPT_FORWARD = 19,
+	DHCP_OPT_SRC_RTE = 20,
+	DHCP_OPT_POLICY_FILTER = 21,
+	DHCP_OPT_MAX_DG_ASSEMBLY = 22,
+	DHCP_OPT_DEFAULT_IP_TTL = 23,
+	DHCP_OPT_MTU_TIMEOUT = 24,
+	DHCP_OPT_MTU_PLATEAU = 25,
+	DHCP_OPT_MTU_INTERFACE = 26,
+	DHCP_OPT_ALL_SUBNETS_LOCAL = 27,
+	DHCP_OPT_BROADCAST_ADDRESS = 28,
+	DHCP_OPT_MASK_DISCOVERY = 29,
+	DHCP_OPT_MASK_SUPPLIER = 30,
+	DHCP_OPT_ROUTER_DISCOVERY = 31,
+	DHCP_OPT_ROUTER_SOLICITATION_ADDRESS = 32,
+	DHCP_OPT_STATIC_ROUTE = 33,
+	DHCP_OPT_TRAILERS = 34,
+	DHCP_OPT_ARP_TIMEOUT = 35,
+	DHCP_OPT_ETHERNET = 36,
+	DHCP_OPT_TCP_DEFAULT_TTL = 37,
+	DHCP_OPT_TCP_KEEPALIVE_INTERVAL = 38,
+	DHCP_OPT_TCP_KEEPALIVE_GARBAGE = 39,
+	DHCP_OPT_NIS_DOMAIN = 40,
+	DHCP_OPT_NIS_SERVERS = 41,
+	DHCP_OPT_NTP_SERVERS = 42,
+	DHCP_OPT_VENDOR_SPECIFIC = 43,
+	DHCP_OPT_NETBIOS_NAME_SERVER = 44,
+	DHCP_OPT_NETBIOS_DISTRIBUTION_SERVER = 45,
+	DHCP_OPT_NETBIOS_NODE_TYPE = 46,
+	DHCP_OPT_NETBIOS_SCOPE = 47,
+	DHCP_OPT_FONT_SERVER = 48,
+	DHCP_OPT_DISPLAY_MANAGER = 49,
+	DHCP_OPT_REQUESTED_IP_ADDRESS = 50,
+	DHCP_OPT_IP_ADDRESS_LEASE_TIME = 51,
+	DHCP_OPT_OVERLOAD = 52,
+	DHCP_OPT_MSG_TYPE = 53,
+	DHCP_OPT_SERVER_ID = 54,
+	DHCP_OPT_PARAMETER_REQ_LIST = 55,
+	DHCP_OPT_MESSAGE = 56,
+	DHCP_OPT_MAX_MESSAGE_SIZE = 57,
+	DHCP_OPT_RENEWAL_TIME = 58,
+	DHCP_OPT_REBINDING_TIME = 59,
+	DHCP_OPT_VENDOR_CLASS_ID = 60,
+	DHCP_OPT_CLIENT_ID = 61,
+	DHCP_OPT_NETWARE_IP_DOMAIN = 62,
+	DHCP_OPT_NETWARE_IP_OPTION = 63,
+	DHCP_OPT_NIS_V3_DOMAIN = 64,
+	DHCP_OPT_NIS_V3_SERVERS = 65,
+	DHCP_OPT_TFTP_SERVER_NAME = 66,
+	DHCP_OPT_BOOT_FILE_NAME = 67,
+	DHCP_OPT_HOME_AGENT_ADDRESSES = 68,
+	DHCP_OPT_SMTP_SERVER = 69,
+	DHCP_OPT_POP3_SERVER = 70,
+	DHCP_OPT_NNTP_SERVER = 71,
+	DHCP_OPT_WWW_SERVER = 72,
+	DHCP_OPT_FINGER_SERVER = 73,
+	DHCP_OPT_IRC_SERVER = 74,
+	DHCP_OPT_STREETTALK_SERVER = 75,
+	DHCP_OPT_STDA_SERVER = 76,
+	DHCP_OPT_USER_CLASS = 77,
+	DHCP_OPT_DIRECTORY_AGENT = 78,
+	DHCP_OPT_SERVICE_SCOPE = 79,
+	DHCP_OPT_RAPID_COMMIT = 80,
+	DHCP_OPT_CLIENT_FQDN = 81,
+	DHCP_OPT_RELAY_AGENT_INFO = 82,
+	DHCP_OPT_ISNS = 83,
+	DHCP_OPT_NDS_SERVERS = 85,
+	DHCP_OPT_NDS_TREE_NAME = 86,
+	DHCP_OPT_NDS_CONTEXT = 87,
+	DHCP_OPT_BCMCS_CONTROLLER_DOMAIN_NAME_LIST = 88,
+	DHCP_OPT_BCMCS_CONTROLLER_IPV4_ADDRESS = 89,
+	DHCP_OPT_AUTHENTICATION = 90,
+	DHCP_OPT_CLIENT_LAST_TRANSACTION_TIME = 91,
+	DHCP_OPT_ASSOCIATED_IP = 92,
+	DHCP_OPT_CLIENT_SYSYEM = 93,
+	DHCP_OPT_CLIENT_NDI = 94,
+	DHCP_OPT_LDAP = 95,
+	DHCP_OPT_UUID_GUID = 97,
+	DHCP_OPT_USER_AUTH = 98,
+	DHCP_OPT_GEOCONF_CIVIC = 99,
+	DHCP_OPT_PCODE = 100,
+	DHCP_OPT_TCODE = 101,
+	DHCP_OPT_NETINFO_ADDRESS = 112,
+	DHCP_OPT_NETINFO_TAG = 113,
+	DHCP_OPT_URL = 114,
+	DHCP_OPT_AUTO_CONFIG = 116,
+	DHCP_OPT_NAME_SERVICE_SEARCH = 117,
+	DHCP_OPT_SUBNET_SELECTION = 118,
+	DHCP_OPT_DOMAIN_SEARCH = 119,
+	DHCP_OPT_SIP_SERVERS_DCP = 120,
+	DHCP_OPT_CLASSLESS_STATIC_ROUTE = 121,
+	DHCP_OPT_CCC = 122,
+	DHCP_OPT_GEOCONF = 123,
+	DHCP_OPT_V_I_VENDOR_CLASS = 124,
+	DHCP_OPT_V_I_VENDOR_SPECIFIC_INFO = 125,
+	DHCP_OPT_PANA_AGENT = 136,
+	DHCP_OPT_V4_LOST = 137,
+	DHCP_OPT_CAPWAP_AC_V4 = 138,
+	DHCP_OPT_IPV4_ADDRESS_MOS = 139,
+	DHCP_OPT_IPV4_FQDN_MOS = 140,
+	DHCP_OPT_SIP_UA_CONF = 141,
+	DHCP_OPT_IPV4_ADDRESS_ANDSF = 142,
+	DHCP_OPT_GEOLOC = 144,
+	DHCP_OPT_FORCERENEW_NONCE_CAPABLE = 145,
+	DHCP_OPT_RDNSS_SELECTION = 146,
+	DHCP_OPT_TFTP_SERVER_ADDRESS = 150,
+	DHCP_OPT_STATUS_CODE = 151,
+	DHCP_OPT_BASE_TIME = 152,
+	DHCP_OPT_START_TIME_OF_STATE = 153,
+	DHCP_OPT_QUERY_START_TIME = 154,
+	DHCP_OPT_QUERY_END_TIME = 155,
+	DHCP_OPT_STATE = 156,
+	DHCP_OPT_DATA_SOURCE = 157,
+	DHCP_OPT_V4_PCP_SERVER = 158,
+	DHCP_OPT_V4_PORTPARAMS = 159,
+	DHCP_OPT_CAPTIVE_PORTAL = 160,
+	DHCP_OPT_CONF_FILE = 209,
+	DHCP_OPT_PATH_PREFIX = 210,
+	DHCP_OPT_REBOOT_TIME = 211,
+	DHCP_OPT_6RD = 212,
+	DHCP_OPT_V4_ACCESS_DOMAIN = 213,
+	DHCP_OPT_SUBNET_ALLOCATION = 220,
+	DHCP_OPT_VSS = 221,
+	DHCP_OPT_END = 255
+};
+
+enum dhcp_message_types {
+	DHCPDISCOVER = 1,
+	DHCPOFFER = 2,
+	DHCPREQUEST = 3,
+	DHCPDECLINE = 4,
+	DHCPACK = 5,
+	DHCPNAK = 6,
+	DHCPRELEASE = 7,
+	DHCPINFORM = 8,
+	DHCPFORCERENEW = 9,
+	DHCPLEASEQUERY = 10,
+	DHCPLEASEUNASSIGNED = 11,
+	DHCPLEASEUNKNOWN = 12,
+	DHCPLEASEACTIVE = 13,
+	DHCPBULKLEASEQUERY = 14,
+	DHCPLEASEQUERYDONE = 15,
+	DHCPACTIVELEASEQUERY = 16,
+	DHCPLEASEQUERYSTATUS = 17,
+	DHCPTLS = 18,
+};
+
+enum dhcp_relay_agent_suboptions {
+	DHCP_RELAY_OPT_AGENT_CIRCUIT_ID = 1,
+	DHCP_RELAY_OPT_AGENT_REMOTE_ID = 2,
+	DHCP_RELAY_OPT_DOCSIS_DEVICE_CLASS = 4,
+	DHCP_RELAY_OPT_LINK_SELECTION = 5,
+	DHCP_RELAY_OPT_SUBSCRIBE_ID = 6,
+	DHCP_RELAY_OPT_RADIUS_ATTRIBUTES = 7,
+	DHCP_RELAY_OPT_AUTHENTICATION = 8,
+	DHCP_RELAY_OPT_VEDOR_SPECIFIC = 9,
+	DHCP_RELAY_OPT_RELAY_AGENT_FLAGS = 10,
+	DHCP_RELAY_OPT_SERVER_ID_OVERRIDE = 11,
+	DHCP_RELAY_OPT_RELAY_AGENT_ID = 12,
+	DHCP_RELAY_OPT_ACCESS_TECHNOLOGY_TYPE = 13,
+	DHCP_RELAY_OPT_ACCESS_NETWORK_NAME = 14,
+	DHCP_RELAY_OPT_ACCESS_POINT_NAME = 15,
+	DHCP_RELAY_OPT_ACCESS_POINT_BSSID = 16,
+	DHCP_RELAY_OPT_OPERATOR_ID = 17,
+	DHCP_RELAY_OPT_OPERATOR_REALM = 18,
+	DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION = 151,
+	DHCP_RELAY_OPT_DHCPV4_VIRTUAL_SUBNET_SELECTION_CONTROL = 152,
+};
+
+enum access_technology_types {
+	ACCESS_TECHNOLOGY_VIRTUAL = 1,
+	ACCESS_TECHNOLOGY_PPP = 2,
+	ACCESS_TECHNOLOGY_ETHERNET = 3,
+	ACCESS_TECHNOLOGY_WLAN = 4,
+	ACCESS_TECHNOLOGY_WIMAX = 5,
+};
+
+#endif /* DHCP_H */
diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h
index 6958661..d773348 100644
--- a/src/common/eapol_common.h
+++ b/src/common/eapol_common.h
@@ -25,7 +25,7 @@
 struct ieee8023_hdr {
 	u8 dest[ETH_ALEN];
 	u8 src[ETH_ALEN];
-	u16 ethertype;
+	be16 ethertype;
 } STRUCT_PACKED;
 
 #ifdef _MSC_VER
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index 9c37ea6..7a96f96 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -388,8 +388,10 @@
 		/* fall through */
 	case VHT_CHANWIDTH_80MHZ:
 		data->bandwidth = 80;
-		if ((vht_oper_chwidth == 1 && center_segment1) ||
-		    (vht_oper_chwidth == 3 && !center_segment1) ||
+		if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ &&
+		     center_segment1) ||
+		    (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ &&
+		     !center_segment1) ||
 		    !sec_channel_offset)
 			return -1;
 		if (!center_segment0) {
@@ -453,3 +455,102 @@
 
 	return 0;
 }
+
+
+void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
+		      int disabled)
+{
+	/* Masking these out disables HT40 */
+	le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+				HT_CAP_INFO_SHORT_GI40MHZ);
+
+	if (disabled)
+		htcaps->ht_capabilities_info &= ~msk;
+	else
+		htcaps->ht_capabilities_info |= msk;
+}
+
+
+#ifdef CONFIG_IEEE80211AC
+
+static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap,
+				  const char *name)
+{
+	u32 req_cap = conf & cap;
+
+	/*
+	 * Make sure we support all requested capabilities.
+	 * NOTE: We assume that 'cap' represents a capability mask,
+	 * not a discrete value.
+	 */
+	if ((hw & req_cap) != req_cap) {
+		wpa_printf(MSG_ERROR,
+			   "Driver does not support configured VHT capability [%s]",
+			   name);
+		return 0;
+	}
+	return 1;
+}
+
+
+static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
+				     unsigned int shift,
+				     const char *name)
+{
+	u32 hw_max = hw & mask;
+	u32 conf_val = conf & mask;
+
+	if (conf_val > hw_max) {
+		wpa_printf(MSG_ERROR,
+			   "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
+			   name, conf_val >> shift, hw_max >> shift);
+		return 0;
+	}
+	return 1;
+}
+
+
+int ieee80211ac_cap_check(u32 hw, u32 conf)
+{
+#define VHT_CAP_CHECK(cap) \
+	do { \
+		if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \
+			return 0; \
+	} while (0)
+
+#define VHT_CAP_CHECK_MAX(cap) \
+	do { \
+		if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
+					       #cap)) \
+			return 0; \
+	} while (0)
+
+	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
+	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
+	VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
+	VHT_CAP_CHECK(VHT_CAP_RXLDPC);
+	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
+	VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
+	VHT_CAP_CHECK(VHT_CAP_TXSTBC);
+	VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
+	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
+	VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+	VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
+	VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
+	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
+	VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+	VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
+	VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
+	VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
+	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
+	VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+	VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+	VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
+
+#undef VHT_CAP_CHECK
+#undef VHT_CAP_CHECK_MAX
+
+	return 1;
+}
+
+#endif /* CONFIG_IEEE80211AC */
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 7360b4e..9cddbd5 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -35,5 +35,8 @@
 			    int vht_enabled, int sec_channel_offset,
 			    int vht_oper_chwidth, int center_segment0,
 			    int center_segment1, u32 vht_caps);
+void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
+		      int disabled);
+int ieee80211ac_cap_check(u32 hw, u32 conf);
 
 #endif /* HW_FEATURES_COMMON_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index 5b05b68..a8d68e5 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -179,6 +179,90 @@
 }
 
 
+static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
+				      struct ieee802_11_elems *elems,
+				      int show_errors)
+{
+	u8 ext_id;
+
+	if (elen < 1) {
+		if (show_errors) {
+			wpa_printf(MSG_MSGDUMP,
+				   "short information element (Ext)");
+		}
+		return -1;
+	}
+
+	ext_id = *pos++;
+	elen--;
+
+	switch (ext_id) {
+	case WLAN_EID_EXT_ASSOC_DELAY_INFO:
+		if (elen != 1)
+			break;
+		elems->assoc_delay_info = pos;
+		break;
+	case WLAN_EID_EXT_FILS_REQ_PARAMS:
+		if (elen < 3)
+			break;
+		elems->fils_req_params = pos;
+		elems->fils_req_params_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_KEY_CONFIRM:
+		elems->fils_key_confirm = pos;
+		elems->fils_key_confirm_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_SESSION:
+		if (elen != FILS_SESSION_LEN)
+			break;
+		elems->fils_session = pos;
+		break;
+	case WLAN_EID_EXT_FILS_HLP_CONTAINER:
+		if (elen < 2 * ETH_ALEN)
+			break;
+		elems->fils_hlp = pos;
+		elems->fils_hlp_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN:
+		if (elen < 1)
+			break;
+		elems->fils_ip_addr_assign = pos;
+		elems->fils_ip_addr_assign_len = elen;
+		break;
+	case WLAN_EID_EXT_KEY_DELIVERY:
+		if (elen < WPA_KEY_RSC_LEN)
+			break;
+		elems->key_delivery = pos;
+		elems->key_delivery_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_WRAPPED_DATA:
+		elems->fils_wrapped_data = pos;
+		elems->fils_wrapped_data_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_PUBLIC_KEY:
+		if (elen < 1)
+			break;
+		elems->fils_pk = pos;
+		elems->fils_pk_len = elen;
+		break;
+	case WLAN_EID_EXT_FILS_NONCE:
+		if (elen != FILS_NONCE_LEN)
+			break;
+		elems->fils_nonce = pos;
+		break;
+	default:
+		if (show_errors) {
+			wpa_printf(MSG_MSGDUMP,
+				   "IEEE 802.11 element parsing ignored unknown element extension (ext_id=%u elen=%u)",
+				   ext_id, (unsigned int) elen);
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+
 /**
  * ieee802_11_parse_elems - Parse information elements in management frames
  * @start: Pointer to the start of IEs
@@ -375,6 +459,39 @@
 			elems->supp_op_classes = pos;
 			elems->supp_op_classes_len = elen;
 			break;
+		case WLAN_EID_RRM_ENABLED_CAPABILITIES:
+			elems->rrm_enabled = pos;
+			elems->rrm_enabled_len = elen;
+			break;
+		case WLAN_EID_CAG_NUMBER:
+			elems->cag_number = pos;
+			elems->cag_number_len = elen;
+			break;
+		case WLAN_EID_AP_CSN:
+			if (elen < 1)
+				break;
+			elems->ap_csn = pos;
+			break;
+		case WLAN_EID_FILS_INDICATION:
+			if (elen < 2)
+				break;
+			elems->fils_indic = pos;
+			elems->fils_indic_len = elen;
+			break;
+		case WLAN_EID_DILS:
+			if (elen < 2)
+				break;
+			elems->dils = pos;
+			elems->dils_len = elen;
+			break;
+		case WLAN_EID_FRAGMENT:
+			/* TODO */
+			break;
+		case WLAN_EID_EXTENSION:
+			if (ieee802_11_parse_extension(pos, elen, elems,
+						       show_errors))
+				unknown++;
+			break;
 		default:
 			unknown++;
 			if (!show_errors)
@@ -677,6 +794,25 @@
 		return HOSTAPD_MODE_IEEE80211A;
 	}
 
+	/* 5 GHz, channels 52..64 */
+	if (freq >= 5260 && freq <= 5320) {
+		if ((freq - 5000) % 5)
+			return NUM_HOSTAPD_MODES;
+
+		if (vht_opclass)
+			*op_class = vht_opclass;
+		else if (sec_channel == 1)
+			*op_class = 119;
+		else if (sec_channel == -1)
+			*op_class = 120;
+		else
+			*op_class = 118;
+
+		*channel = (freq - 5000) / 5;
+
+		return HOSTAPD_MODE_IEEE80211A;
+	}
+
 	/* 5 GHz, channels 149..169 */
 	if (freq >= 5745 && freq <= 5845) {
 		if ((freq - 5000) % 5)
@@ -1313,3 +1449,148 @@
 
 	return 6 + attr_len;
 }
+
+
+static const struct country_op_class us_op_class[] = {
+	{ 1, 115 },
+	{ 2, 118 },
+	{ 3, 124 },
+	{ 4, 121 },
+	{ 5, 125 },
+	{ 12, 81 },
+	{ 22, 116 },
+	{ 23, 119 },
+	{ 24, 122 },
+	{ 25, 126 },
+	{ 26, 126 },
+	{ 27, 117 },
+	{ 28, 120 },
+	{ 29, 123 },
+	{ 30, 127 },
+	{ 31, 127 },
+	{ 32, 83 },
+	{ 33, 84 },
+	{ 34, 180 },
+};
+
+static const struct country_op_class eu_op_class[] = {
+	{ 1, 115 },
+	{ 2, 118 },
+	{ 3, 121 },
+	{ 4, 81 },
+	{ 5, 116 },
+	{ 6, 119 },
+	{ 7, 122 },
+	{ 8, 117 },
+	{ 9, 120 },
+	{ 10, 123 },
+	{ 11, 83 },
+	{ 12, 84 },
+	{ 17, 125 },
+	{ 18, 180 },
+};
+
+static const struct country_op_class jp_op_class[] = {
+	{ 1, 115 },
+	{ 30, 81 },
+	{ 31, 82 },
+	{ 32, 118 },
+	{ 33, 118 },
+	{ 34, 121 },
+	{ 35, 121 },
+	{ 36, 116 },
+	{ 37, 119 },
+	{ 38, 119 },
+	{ 39, 122 },
+	{ 40, 122 },
+	{ 41, 117 },
+	{ 42, 120 },
+	{ 43, 120 },
+	{ 44, 123 },
+	{ 45, 123 },
+	{ 56, 83 },
+	{ 57, 84 },
+	{ 58, 121 },
+	{ 59, 180 },
+};
+
+static const struct country_op_class cn_op_class[] = {
+	{ 1, 115 },
+	{ 2, 118 },
+	{ 3, 125 },
+	{ 4, 116 },
+	{ 5, 119 },
+	{ 6, 126 },
+	{ 7, 81 },
+	{ 8, 83 },
+	{ 9, 84 },
+};
+
+static u8
+global_op_class_from_country_array(u8 op_class, size_t array_size,
+				   const struct country_op_class *country_array)
+{
+	size_t i;
+
+	for (i = 0; i < array_size; i++) {
+		if (country_array[i].country_op_class == op_class)
+			return country_array[i].global_op_class;
+	}
+
+	return 0;
+}
+
+
+u8 country_to_global_op_class(const char *country, u8 op_class)
+{
+	const struct country_op_class *country_array;
+	size_t size;
+	u8 g_op_class;
+
+	if (country_match(us_op_class_cc, country)) {
+		country_array = us_op_class;
+		size = ARRAY_SIZE(us_op_class);
+	} else if (country_match(eu_op_class_cc, country)) {
+		country_array = eu_op_class;
+		size = ARRAY_SIZE(eu_op_class);
+	} else if (country_match(jp_op_class_cc, country)) {
+		country_array = jp_op_class;
+		size = ARRAY_SIZE(jp_op_class);
+	} else if (country_match(cn_op_class_cc, country)) {
+		country_array = cn_op_class;
+		size = ARRAY_SIZE(cn_op_class);
+	} else {
+		/*
+		 * Countries that do not match any of the above countries use
+		 * global operating classes
+		 */
+		return op_class;
+	}
+
+	g_op_class = global_op_class_from_country_array(op_class, size,
+							country_array);
+
+	/*
+	 * If the given operating class did not match any of the country's
+	 * operating classes, assume that global operating class is used.
+	 */
+	return g_op_class ? g_op_class : op_class;
+}
+
+
+const struct oper_class_map * get_oper_class(const char *country, u8 op_class)
+{
+	const struct oper_class_map *op;
+
+	if (country)
+		op_class = country_to_global_op_class(country, op_class);
+
+	op = &global_op_class[0];
+	while (op->op_class && op->op_class != op_class)
+		op++;
+
+	if (!op->op_class)
+		return NULL;
+
+	return op;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index d9fecd6..966eeac 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -63,6 +63,21 @@
 	const u8 *mic;
 	const u8 *pref_freq_list;
 	const u8 *supp_op_classes;
+	const u8 *rrm_enabled;
+	const u8 *cag_number;
+	const u8 *ap_csn;
+	const u8 *fils_indic;
+	const u8 *dils;
+	const u8 *assoc_delay_info;
+	const u8 *fils_req_params;
+	const u8 *fils_key_confirm;
+	const u8 *fils_session;
+	const u8 *fils_hlp;
+	const u8 *fils_ip_addr_assign;
+	const u8 *key_delivery;
+	const u8 *fils_wrapped_data;
+	const u8 *fils_pk;
+	const u8 *fils_nonce;
 
 	u8 ssid_len;
 	u8 supp_rates_len;
@@ -94,6 +109,17 @@
 	u8 mic_len;
 	u8 pref_freq_list_len;
 	u8 supp_op_classes_len;
+	u8 rrm_enabled_len;
+	u8 cag_number_len;
+	u8 fils_indic_len;
+	u8 dils_len;
+	u8 fils_req_params_len;
+	u8 fils_key_confirm_len;
+	u8 fils_hlp_len;
+	u8 fils_ip_addr_assign_len;
+	u8 key_delivery_len;
+	u8 fils_wrapped_data_len;
+	u8 fils_pk_len;
 
 	struct mb_ies_info mb_ies;
 };
@@ -151,4 +177,13 @@
 
 size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
 
+struct country_op_class {
+	u8 country_op_class;
+	u8 global_op_class;
+};
+
+u8 country_to_global_op_class(const char *country, u8 op_class);
+
+const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
+
 #endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index e1a8ef7..15f6d42 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -81,6 +81,9 @@
 #define WLAN_AUTH_SHARED_KEY		1
 #define WLAN_AUTH_FT			2
 #define WLAN_AUTH_SAE			3
+#define WLAN_AUTH_FILS_SK		4
+#define WLAN_AUTH_FILS_SK_PFS		5
+#define WLAN_AUTH_FILS_PK		6
 #define WLAN_AUTH_LEAP			128
 
 #define WLAN_AUTH_CHALLENGE_LEN 128
@@ -102,7 +105,7 @@
 #define WLAN_CAPABILITY_DELAYED_BLOCK_ACK BIT(14)
 #define WLAN_CAPABILITY_IMM_BLOCK_ACK BIT(15)
 
-/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
+/* Status codes (IEEE Std 802.11-2016, 9.4.1.9, Table 9-46) */
 #define WLAN_STATUS_SUCCESS 0
 #define WLAN_STATUS_UNSPECIFIED_FAILURE 1
 #define WLAN_STATUS_TDLS_WAKEUP_ALTERNATE 2
@@ -119,27 +122,23 @@
 #define WLAN_STATUS_AUTH_TIMEOUT 16
 #define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
 #define WLAN_STATUS_ASSOC_DENIED_RATES 18
-/* IEEE 802.11b */
 #define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
-#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
-#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
-/* IEEE 802.11h */
 #define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
 #define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
 #define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
-/* IEEE 802.11g */
 #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
-#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 26
 #define WLAN_STATUS_ASSOC_DENIED_NO_HT 27
 #define WLAN_STATUS_R0KH_UNREACHABLE 28
 #define WLAN_STATUS_ASSOC_DENIED_NO_PCO 29
-/* IEEE 802.11w */
 #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
 #define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
+#define WLAN_STATUS_DENIED_INSUFFICIENT_BANDWIDTH 33
+#define WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS 34
+#define WLAN_STATUS_DENIED_QOS_NOT_SUPPORTED 35
 #define WLAN_STATUS_REQUEST_DECLINED 37
 #define WLAN_STATUS_INVALID_PARAMETERS 38
-/* IEEE 802.11i */
+#define WLAN_STATUS_REJECTED_WITH_SUGGESTED_CHANGES 39
 #define WLAN_STATUS_INVALID_IE 40
 #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
 #define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
@@ -152,11 +151,13 @@
 #define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
 #define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
 #define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
-/* IEEE 802.11r */
 #define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
 #define WLAN_STATUS_INVALID_PMKID 53
 #define WLAN_STATUS_INVALID_MDIE 54
 #define WLAN_STATUS_INVALID_FTIE 55
+#define WLAN_STATUS_REQUESTED_TCLAS_NOT_SUPPORTED 56
+#define WLAN_STATUS_INSUFFICIENT_TCLAS_PROCESSING_RESOURCES 57
+#define WLAN_STATUS_TRY_ANOTHER_BSS 58
 #define WLAN_STATUS_GAS_ADV_PROTO_NOT_SUPPORTED 59
 #define WLAN_STATUS_NO_OUTSTANDING_GAS_REQ 60
 #define WLAN_STATUS_GAS_RESP_NOT_RECEIVED 61
@@ -167,16 +168,43 @@
 #define WLAN_STATUS_REQ_REFUSED_SSPN 67
 #define WLAN_STATUS_REQ_REFUSED_UNAUTH_ACCESS 68
 #define WLAN_STATUS_INVALID_RSNIE 72
+#define WLAN_STATUS_U_APSD_COEX_NOT_SUPPORTED 73
+#define WLAN_STATUS_U_APSD_COEX_MODE_NOT_SUPPORTED 74
+#define WLAN_STATUS_BAD_INTERVAL_WITH_U_APSD_COEX 75
 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
+#define WLAN_STATUS_CANNOT_FIND_ALT_TBTT 78
 #define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_REQ_TCLAS_NOT_SUPPORTED 80
+#define WLAN_STATUS_TCLAS_RESOURCES_EXCHAUSTED 81
 #define WLAN_STATUS_REJECTED_WITH_SUGGESTED_BSS_TRANSITION 82
+#define WLAN_STATUS_REJECT_WITH_SCHEDULE 83
+#define WLAN_STATUS_REJECT_NO_WAKEUP_SPECIFIED 84
+#define WLAN_STATUS_SUCCESS_POWER_SAVE_MODE 85
 #define WLAN_STATUS_PENDING_ADMITTING_FST_SESSION 86
+#define WLAN_STATUS_PERFORMING_FST_NOW 87
+#define WLAN_STATUS_PENDING_GAP_IN_BA_WINDOW 88
+#define WLAN_STATUS_REJECT_U_PID_SETTING 89
+#define WLAN_STATUS_REFUSED_EXTERNAL_REASON 92
+#define WLAN_STATUS_REFUSED_AP_OUT_OF_MEMORY 93
+#define WLAN_STATUS_REJECTED_EMERGENCY_SERVICE_NOT_SUPPORTED 94
 #define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_REJECT_DSE_BAND 96
+#define WLAN_STATUS_TCLAS_PROCESSING_TERMINATED 97
+#define WLAN_STATUS_TS_SCHEDULE_CONFLICT 98
 #define WLAN_STATUS_DENIED_WITH_SUGGESTED_BAND_AND_CHANNEL 99
+#define WLAN_STATUS_MCCAOP_RESERVATION_CONFLICT 100
+#define WLAN_STATUS_MAF_LIMIT_EXCEEDED 101
+#define WLAN_STATUS_MCCA_TRACK_LIMIT_EXCEEDED 102
+#define WLAN_STATUS_DENIED_DUE_TO_SPECTRUM_MANAGEMENT 103
 #define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
+#define WLAN_STATUS_ENABLEMENT_DENIED 105
+#define WLAN_STATUS_RESTRICTION_FROM_AUTHORIZED_GDB 106
+#define WLAN_STATUS_AUTHORIZATION_DEENABLED 107
+#define WLAN_STATUS_FILS_AUTHENTICATION_FAILURE 112
+#define WLAN_STATUS_UNKNOWN_AUTHENTICATION_SERVER 113
 
-/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
+/* Reason codes (IEEE Std 802.11-2016, 9.4.1.7, Table 9-45) */
 #define WLAN_REASON_UNSPECIFIED 1
 #define WLAN_REASON_PREV_AUTH_NOT_VALID 2
 #define WLAN_REASON_DEAUTH_LEAVING 3
@@ -186,10 +214,9 @@
 #define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
 #define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
 #define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
-/* IEEE 802.11h */
 #define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
 #define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
-/* IEEE 802.11i */
+#define WLAN_REASON_BSS_TRANSITION_DISASSOC 12
 #define WLAN_REASON_INVALID_IE 13
 #define WLAN_REASON_MICHAEL_MIC_FAILURE 14
 #define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
@@ -204,9 +231,26 @@
 #define WLAN_REASON_CIPHER_SUITE_REJECTED 24
 #define WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE 25
 #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
-/* IEEE 802.11e */
+#define WLAN_REASON_SSP_REQUESTED_DISASSOC 27
+#define WLAN_REASON_NO_SSP_ROAMING_AGREEMENT 28
+#define WLAN_REASON_BAD_CIPHER_OR_AKM 29
+#define WLAN_REASON_NOT_AUTHORIZED_THIS_LOCATION 30
+#define WLAN_REASON_SERVICE_CHANGE_PRECLUDES_TS 31
+#define WLAN_REASON_UNSPECIFIED_QOS_REASON 32
+#define WLAN_REASON_NOT_ENOUGH_BANDWIDTH 33
 #define WLAN_REASON_DISASSOC_LOW_ACK 34
-/* IEEE 802.11s */
+#define WLAN_REASON_EXCEEDED_TXOP 35
+#define WLAN_REASON_STA_LEAVING 36
+#define WLAN_REASON_END_TS_BA_DLS 37
+#define WLAN_REASON_UNKNOWN_TS_BA 38
+#define WLAN_REASON_TIMEOUT 39
+#define WLAN_REASON_PEERKEY_MISMATCH 45
+#define WLAN_REASON_AUTHORIZED_ACCESS_LIMIT_REACHED 46
+#define WLAN_REASON_EXTERNAL_SERVICE_REQUIREMENTS 47
+#define WLAN_REASON_INVALID_FT_ACTION_FRAME_COUNT 48
+#define WLAN_REASON_INVALID_PMKID 49
+#define WLAN_REASON_INVALID_MDE 50
+#define WLAN_REASON_INVALID_FTE 51
 #define WLAN_REASON_MESH_PEERING_CANCELLED 52
 #define WLAN_REASON_MESH_MAX_PEERS 53
 #define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
@@ -216,20 +260,29 @@
 #define WLAN_REASON_MESH_INVALID_GTK 58
 #define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
 #define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
+#define WLAN_REASON_MESH_PATH_ERROR_NO_PROXY_INFO 61
+#define WLAN_REASON_MESH_PATH_ERROR_NO_FORWARDING_INFO 62
+#define WLAN_REASON_MESH_PATH_ERROR_DEST_UNREACHABLE 63
+#define WLAN_REASON_MAC_ADDRESS_ALREADY_EXISTS_IN_MBSS 64
+#define WLAN_REASON_MESH_CHANNEL_SWITCH_REGULATORY_REQ 65
+#define WLAN_REASON_MESH_CHANNEL_SWITCH_UNSPECIFIED 66
 
 
-/* Information Element IDs */
+/* Information Element IDs (IEEE Std 802.11-2016, 9.4.2.1, Table 9-77) */
 #define WLAN_EID_SSID 0
 #define WLAN_EID_SUPP_RATES 1
-#define WLAN_EID_FH_PARAMS 2
 #define WLAN_EID_DS_PARAMS 3
 #define WLAN_EID_CF_PARAMS 4
 #define WLAN_EID_TIM 5
 #define WLAN_EID_IBSS_PARAMS 6
 #define WLAN_EID_COUNTRY 7
+#define WLAN_EID_REQUEST 10
 #define WLAN_EID_BSS_LOAD 11
+#define WLAN_EID_EDCA_PARAM_SET 12
+#define WLAN_EID_TSPEC 13
+#define WLAN_EID_TCLAS 14
+#define WLAN_EID_SCHEDULE 15
 #define WLAN_EID_CHALLENGE 16
-/* EIDs defined by IEEE 802.11h - START */
 #define WLAN_EID_PWR_CONSTRAINT 32
 #define WLAN_EID_PWR_CAPABILITY 33
 #define WLAN_EID_TPC_REQUEST 34
@@ -238,50 +291,139 @@
 #define WLAN_EID_CHANNEL_SWITCH 37
 #define WLAN_EID_MEASURE_REQUEST 38
 #define WLAN_EID_MEASURE_REPORT 39
-#define WLAN_EID_QUITE 40
+#define WLAN_EID_QUIET 40
 #define WLAN_EID_IBSS_DFS 41
-/* EIDs defined by IEEE 802.11h - END */
 #define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_TS_DELAY 43
+#define WLAN_EID_TCLAS_PROCESSING 44
 #define WLAN_EID_HT_CAP 45
 #define WLAN_EID_QOS 46
 #define WLAN_EID_RSN 48
 #define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_AP_CHANNEL_REPORT 51
 #define WLAN_EID_NEIGHBOR_REPORT 52
+#define WLAN_EID_RCPI 53
 #define WLAN_EID_MOBILITY_DOMAIN 54
 #define WLAN_EID_FAST_BSS_TRANSITION 55
 #define WLAN_EID_TIMEOUT_INTERVAL 56
 #define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_DSE_REGISTERED_LOCATION 58
 #define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
 #define WLAN_EID_EXT_CHANSWITCH_ANN 60
 #define WLAN_EID_HT_OPERATION 61
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
-#define WLAN_EID_WAPI 68
+#define WLAN_EID_BSS_AVERAGE_ACCESS_DELAY 63
+#define WLAN_EID_ANTENNA 64
+#define WLAN_EID_RSNI 65
+#define WLAN_EID_MEASUREMENT_PILOT_TRANSMISSION 66
+#define WLAN_EID_BSS_AVAILABLE_ADM_CAPA 67
+#define WLAN_EID_BSS_AC_ACCESS_DELAY 68 /* note: also used by WAPI */
 #define WLAN_EID_TIME_ADVERTISEMENT 69
 #define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
+#define WLAN_EID_MULTIPLE_BSSID 71
 #define WLAN_EID_20_40_BSS_COEXISTENCE 72
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
+#define WLAN_EID_RIC_DESCRIPTOR 75
 #define WLAN_EID_MMIE 76
+#define WLAN_EID_EVENT_REQUEST 78
+#define WLAN_EID_EVENT_REPORT 79
+#define WLAN_EID_DIAGNOSTIC_REQUEST 80
+#define WLAN_EID_DIAGNOSTIC_REPORT 81
+#define WLAN_EID_LOCATION_PARAMETERS 82
+#define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83
 #define WLAN_EID_SSID_LIST 84
+#define WLAN_EID_MLTIPLE_BSSID_INDEX 85
+#define WLAN_EID_FMS_DESCRIPTOR 86
+#define WLAN_EID_FMS_REQUEST 87
+#define WLAN_EID_FMS_RESPONSE 88
+#define WLAN_EID_QOS_TRAFFIC_CAPABILITY 89
 #define WLAN_EID_BSS_MAX_IDLE_PERIOD 90
 #define WLAN_EID_TFS_REQ 91
 #define WLAN_EID_TFS_RESP 92
 #define WLAN_EID_WNMSLEEP 93
+#define WLAN_EID_TIM_BROADCAST_REQUEST 94
+#define WLAN_EID_TIM_BROADCAST_RESPONSE 95
+#define WLAN_EID_COLLOCATED_INTERFERENCE_REPORT 96
+#define WLAN_EID_CHANNEL_USAGE 97
 #define WLAN_EID_TIME_ZONE 98
+#define WLAN_EID_DMS_REQUEST 99
+#define WLAN_EID_DMS_RESPONSE 100
 #define WLAN_EID_LINK_ID 101
+#define WLAN_EID_WAKEUP_SCHEDULE 102
+#define WLAN_EID_CHANNEL_SWITCH_TIMING 104
+#define WLAN_EID_PTI_CONTROL 105
+#define WLAN_EID_TPU_BUFFER_STATUS 106
 #define WLAN_EID_INTERWORKING 107
 #define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_EXPEDITED_BANDWIDTH_REQ 109
 #define WLAN_EID_QOS_MAP_SET 110
 #define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_EMERGENCY_ALERT_ID 112
 #define WLAN_EID_MESH_CONFIG 113
 #define WLAN_EID_MESH_ID 114
+#define WLAN_EID_MESH_LINK_METRIC_REPORT 115
+#define WLAN_EID_CONGESTION_NOTIFICATION 116
 #define WLAN_EID_PEER_MGMT 117
+#define WLAN_EID_MESH_CHANNEL_SWITCH_PARAMETERS 118
+#define WLAN_EID_MESH_AWAKE_WINDOW 119
+#define WLAN_EID_BEACON_TIMING 120
+#define WLAN_EID_MCCAOP_SETUP_REQUEST 121
+#define WLAN_EID_MCCAOP_SETUP_REPLY 122
+#define WLAN_EID_MCCAOP_ADVERTISEMENT 123
+#define WLAN_EID_MCCAOP_TEARDOWN 124
+#define WLAN_EID_GANN 125
+#define WLAN_EID_RANN 126
 #define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_PREQ 130
+#define WLAN_EID_PREP 131
+#define WLAN_EID_PERR 132
+#define WLAN_EID_PXU 137
+#define WLAN_EID_PXUC 138
 #define WLAN_EID_AMPE 139
 #define WLAN_EID_MIC 140
+#define WLAN_EID_DESTINATION_URI 141
+#define WLAN_EID_U_APSD_COEX 142
+#define WLAN_EID_DMG_WAKEUP_SCHEDULE 143
+#define WLAN_EID_EXTENDED_SCHEDULE 144
+#define WLAN_EID_STA_AVAILABILITY 145
+#define WLAN_EID_DMG_TSPEC 146
+#define WLAN_EID_NEXT_DMG_ATI 147
+#define WLAN_EID_DMG_CAPABILITIES 148
+#define WLAN_EID_DMG_OPERATION 151
+#define WLAN_EID_DMG_BSS_PARAMETER_CHANGE 152
+#define WLAN_EID_DMG_BEAM_REFINEMENT 153
+#define WLAN_EID_CHANNEL_MEASUREMENT_FEEDBACK 154
 #define WLAN_EID_CCKM 156
+#define WLAN_EID_AWAKE_WINDOW 157
 #define WLAN_EID_MULTI_BAND 158
+#define WLAN_EID_ADDBA_EXTENSION 159
+#define WLAN_EID_NEXTPCP_LIST 160
+#define WLAN_EID_PCP_HANDOVER 161
+#define WLAN_EID_DMG_LINK_MARGIN 162
+#define WLAN_EID_SWITCHING_STREAM 163
 #define WLAN_EID_SESSION_TRANSITION 164
+#define WLAN_EID_DYNAMIC_TONE_PAIRING_REPORT 165
+#define WLAN_EID_CLUSTER_REPORT 166
+#define WLAN_EID_REPLAY_CAPABILITIES 167
+#define WLAN_EID_RELAY_TRANSFER_PARAM_SET 168
+#define WLAN_EID_BEAMLINK_MAINTENANCE 169
+#define WLAN_EID_MULTIPLE_MAC_SUBLAYERS 170
+#define WLAN_EID_U_PID 171
+#define WLAN_EID_DMG_LINK_ADAPTATION_ACK 172
+#define WLAN_EID_MCCAOP_ADVERTISEMENT_OVERVIEW 174
+#define WLAN_EID_QUIET_PERIOD_REQUEST 175
+#define WLAN_EID_QUIET_PERIOD_RESPONSE 177
+#define WLAN_EID_QMF_POLICY 181
+#define WLAN_EID_ECAPC_POLICY 182
+#define WLAN_EID_CLUSTER_TIME_OFFSET 183
+#define WLAN_EID_INTRA_ACCESS_CATEGORY_PRIORITY 184
+#define WLAN_EID_SCS_DESCRIPTOR 185
+#define WLAN_EID_QLOAD_REPORT 186
+#define WLAN_EID_HCCA_TXOP_UPDATE_COUNT 187
+#define WLAN_EID_HIGHER_LAYER_STREAM_ID 188
+#define WLAN_EID_GCR_GROUP_ADDRESS 189
+#define WLAN_EID_ANTENNA_SECTOR_ID_PATTERN 190
 #define WLAN_EID_VHT_CAP 191
 #define WLAN_EID_VHT_OPERATION 192
 #define WLAN_EID_VHT_EXTENDED_BSS_LOAD 193
@@ -291,10 +433,38 @@
 #define WLAN_EID_VHT_AID 197
 #define WLAN_EID_VHT_QUIET_CHANNEL 198
 #define WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION 199
+#define WLAN_EID_UPSIM 200
+#define WLAN_EID_REDUCED_NEIGHBOR_REPORT 201
+#define WLAN_EID_TVHT_OPERATION 202
+#define WLAN_EID_DEVICE_LOCATION 204
+#define WLAN_EID_WHITE_SPACE_MAP 205
+#define WLAN_EID_FTM_PARAMETERS 206
 #define WLAN_EID_VENDOR_SPECIFIC 221
+#define WLAN_EID_CAG_NUMBER 237
+#define WLAN_EID_AP_CSN 239
+#define WLAN_EID_FILS_INDICATION 240
+#define WLAN_EID_DILS 241
+#define WLAN_EID_FRAGMENT 242
+#define WLAN_EID_EXTENSION 255
+
+/* Element ID Extension (EID 255) values */
+#define WLAN_EID_EXT_ASSOC_DELAY_INFO 1
+#define WLAN_EID_EXT_FILS_REQ_PARAMS 2
+#define WLAN_EID_EXT_FILS_KEY_CONFIRM 3
+#define WLAN_EID_EXT_FILS_SESSION 4
+#define WLAN_EID_EXT_FILS_HLP_CONTAINER 5
+#define WLAN_EID_EXT_FILS_IP_ADDR_ASSIGN 6
+#define WLAN_EID_EXT_KEY_DELIVERY 7
+#define WLAN_EID_EXT_FILS_WRAPPED_DATA 8
+#define WLAN_EID_EXT_FTM_SYNC_INFO 9
+#define WLAN_EID_EXT_EXTENDED_REQUEST 10
+#define WLAN_EID_EXT_ESTIMATED_SERVICE_PARAMS 11
+#define WLAN_EID_EXT_FILS_PUBLIC_KEY 12
+#define WLAN_EID_EXT_FILS_NONCE 13
+#define WLAN_EID_EXT_FUTURE_CHANNEL_GUIDANCE 14
 
 
-/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
+/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
 #define WLAN_ACTION_SPECTRUM_MGMT 0
 #define WLAN_ACTION_QOS 1
 #define WLAN_ACTION_DLS 2
@@ -308,21 +478,59 @@
 #define WLAN_ACTION_WNM 10
 #define WLAN_ACTION_UNPROTECTED_WNM 11
 #define WLAN_ACTION_TDLS 12
+#define WLAN_ACTION_MESH 13
+#define WLAN_ACTION_MULTIHOP 14
 #define WLAN_ACTION_SELF_PROTECTED 15
+#define WLAN_ACTION_DMG 16
 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
 #define WLAN_ACTION_FST 18
+#define WLAN_ACTION_ROBUST_AV_STREAMING 19
+#define WLAN_ACTION_UNPROTECTED_DMG 20
+#define WLAN_ACTION_VHT 21
+#define WLAN_ACTION_FILS 26
+#define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
+/* Note: 128-255 used to report errors by setting category | 0x80 */
 
-/* Public action codes */
+/* Public action codes (IEEE Std 802.11-2016, 9.6.8.1, Table 9-307) */
 #define WLAN_PA_20_40_BSS_COEX 0
+#define WLAN_PA_DSE_ENABLEMENT 1
+#define WLAN_PA_DSE_DEENABLEMENT 2
+#define WLAN_PA_DSE_REG_LOCATION_ANNOUNCE 3
+#define WLAN_PA_EXT_CHANNEL_SWITCH_ANNOUNCE 4
+#define WLAN_PA_DSE_MEASUREMENT_REQ 5
+#define WLAN_PA_DSE_MEASUREMENT_RESP 6
+#define WLAN_PA_MEASUREMENT_PILOT 7
+#define WLAN_PA_DSE_POWER_CONSTRAINT 8
 #define WLAN_PA_VENDOR_SPECIFIC 9
 #define WLAN_PA_GAS_INITIAL_REQ 10
 #define WLAN_PA_GAS_INITIAL_RESP 11
 #define WLAN_PA_GAS_COMEBACK_REQ 12
 #define WLAN_PA_GAS_COMEBACK_RESP 13
 #define WLAN_TDLS_DISCOVERY_RESPONSE 14
+#define WLAN_PA_LOCATION_TRACK_NOTIFICATION 15
+#define WLAN_PA_QAB_REQUEST_FRAME 16
+#define WLAN_PA_QAB_RESPONSE_FRAME 17
+#define WLAN_PA_QMF_POLICY 18
+#define WLAN_PA_QMF_POLICY_CHANGE 19
+#define WLAN_PA_QLOAD_REQUEST 20
+#define WLAN_PA_QLOAD_REPORT 21
+#define WLAN_PA_HCCA_TXOP_ADVERTISEMENT 22
+#define WLAN_PA_HCCA_TXOP_RESPONSE 23
+#define WLAN_PA_PUBLIC_KEY 24
+#define WLAN_PA_CHANNEL_AVAILABILITY_QUERY 25
+#define WLAN_PA_CHANNEL_SCHEDULE_MANAGEMENT 26
+#define WLAN_PA_CONTACT_VERIFICATION_SIGNAL 27
+#define WLAN_PA_GDD_ENABLEMENT_REQ 28
+#define WLAN_PA_GDD_ENABLEMENT_RESP 29
+#define WLAN_PA_NETWORK_CHANNEL_CONTROL 30
+#define WLAN_PA_WHITE_SPACE_MAP_ANNOUNCEMENT 31
+#define WLAN_PA_FTM_REQUEST 32
+#define WLAN_PA_FTM 33
+#define WLAN_PA_FILS_DISCOVERY 34
 
-/* Protected Dual of Public Action frames */
+/* Protected Dual of Public Action frames (IEEE Std 802.11-2016, 9.6.11,
+ * Table 9-332) */
 #define WLAN_PROT_DSE_ENABLEMENT 1
 #define WLAN_PROT_DSE_DEENABLEMENT 2
 #define WLAN_PROT_EXT_CSA 4
@@ -334,6 +542,21 @@
 #define WLAN_PROT_GAS_INITIAL_RESP 11
 #define WLAN_PROT_GAS_COMEBACK_REQ 12
 #define WLAN_PROT_GAS_COMEBACK_RESP 13
+#define WLAN_PROT_QAB_REQUEST_FRAME 16
+#define WLAN_PROT_QAB_RESPONSE_FRAME 17
+#define WLAN_PROT_QMF_POLICY 18
+#define WLAN_PROT_QMF_POLICY_CHANGE 19
+#define WLAN_PROT_QLOAD_REQUEST 20
+#define WLAN_PROT_QLOAD_REPORT 21
+#define WLAN_PROT_HCCA_TXOP_ADVERTISEMENT 22
+#define WLAN_PROT_HCCA_TXOP_RESPONSE 23
+#define WLAN_PROT_CHANNEL_AVAILABILITY_QUERY 25
+#define WLAN_PROT_CHANNEL_SCHEDULE_MANAGEMENT 26
+#define WLAN_PROT_CONTACT_VERIFICATION_SIGNAL 27
+#define WLAN_PROT_GDD_ENABLEMENT_REQ 28
+#define WLAN_PROT_GDD_ENABLEMENT_RESP 29
+#define WLAN_PROT_NETWORK_CHANNEL_CONTROL 30
+#define WLAN_PROT_WHITE_SPACE_MAP_ANNOUNCEMENT 31
 
 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
 #define WLAN_SA_QUERY_REQUEST 0
@@ -362,10 +585,24 @@
 #define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
 #define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
 
-/* Radio Measurement capabilities (from RRM Capabilities IE) */
+/* Radio Measurement capabilities (from RM Enabled Capabilities element)
+ * IEEE Std 802.11-2016, 9.4.2.45, Table 9-157 */
 /* byte 1 (out of 5) */
 #define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
 #define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
+#define WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE BIT(4)
+#define WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE BIT(5)
+#define WLAN_RRM_CAPS_BEACON_REPORT_TABLE BIT(6)
+/* byte 2 (out of 5) */
+#define WLAN_RRM_CAPS_LCI_MEASUREMENT BIT(4)
+/* byte 5 (out of 5) */
+#define WLAN_RRM_CAPS_FTM_RANGE_REPORT BIT(2)
+
+/*
+ * IEEE P802.11-REVmc/D5.0, 9.4.2.21.19 (Fine Timing Measurement Range
+ * request) - Minimum AP count
+ */
+#define WLAN_RRM_RANGE_REQ_MAX_MIN_AP 15
 
 /* Timeout Interval Type */
 #define WLAN_TIMEOUT_REASSOC_DEADLINE 1
@@ -388,16 +625,18 @@
 #define INTERWORKING_ANT_TEST 6
 #define INTERWORKING_ANT_WILDCARD 15
 
-/* Advertisement Protocol ID definitions (IEEE Std 802.11u-2011) */
+/* Advertisement Protocol ID definitions (IEEE Std 802.11-2016, Table 9-215) */
 enum adv_proto_id {
 	ACCESS_NETWORK_QUERY_PROTOCOL = 0,
 	MIH_INFO_SERVICE = 1,
 	MIH_CMD_AND_EVENT_DISCOVERY = 2,
 	EMERGENCY_ALERT_SYSTEM = 3,
+	REGISTERED_LOCATION_QUERY_PROTO = 4,
 	ADV_PROTO_VENDOR_SPECIFIC = 221
 };
 
-/* Access Network Query Protocol info ID definitions (IEEE Std 802.11u-2011) */
+/* Access Network Query Protocol info ID definitions (IEEE Std 802.11-2016,
+ * Table 9-271; P802.11ai) */
 enum anqp_info_id {
 	ANQP_QUERY_LIST = 256,
 	ANQP_CAPABILITY_LIST = 257,
@@ -416,9 +655,14 @@
 	ANQP_TDLS_CAPABILITY = 270,
 	ANQP_EMERGENCY_NAI = 271,
 	ANQP_NEIGHBOR_REPORT = 272,
+	ANQP_QUERY_AP_LIST = 273,
+	ANQP_AP_LIST_RESPONSE = 274,
+	ANQP_FILS_REALM_INFO = 275,
+	ANQP_CAG = 276,
 	ANQP_VENUE_URL = 277,
 	ANQP_ADVICE_OF_CHARGE = 278,
 	ANQP_LOCAL_CONTENT = 279,
+	ANQP_NETWORK_AUTH_TYPE_TIMESTAMP = 280,
 	ANQP_VENDOR_SPECIFIC = 56797
 };
 
@@ -453,6 +697,53 @@
 	NAI_REALM_CRED_TYPE_VENDOR_SPECIFIC = 10
 };
 
+/*
+ * IEEE P802.11-REVmc/D5.0 Table 9-81 - Measurement type definitions for
+ * measurement requests
+ */
+enum measure_type {
+	MEASURE_TYPE_BASIC = 0,
+	MEASURE_TYPE_CCA = 1,
+	MEASURE_TYPE_RPI_HIST = 2,
+	MEASURE_TYPE_CHANNEL_LOAD = 3,
+	MEASURE_TYPE_NOISE_HIST = 4,
+	MEASURE_TYPE_BEACON = 5,
+	MEASURE_TYPE_FRAME = 6,
+	MEASURE_TYPE_STA_STATISTICS = 7,
+	MEASURE_TYPE_LCI = 8,
+	MEASURE_TYPE_TRANSMIT_STREAM = 9,
+	MEASURE_TYPE_MULTICAST_DIAG = 10,
+	MEASURE_TYPE_LOCATION_CIVIC = 11,
+	MEASURE_TYPE_LOCATION_ID = 12,
+	MEASURE_TYPE_DIRECTIONAL_CHAN_QUALITY = 13,
+	MEASURE_TYPE_DIRECTIONAL_MEASURE = 14,
+	MEASURE_TYPE_DIRECTIONAL_STATS = 15,
+	MEASURE_TYPE_FTM_RANGE = 16,
+	MEASURE_TYPE_MEASURE_PAUSE = 255,
+};
+
+/* IEEE Std 802.11-2012 Table 8-71 - Location subject definition */
+enum location_subject {
+	LOCATION_SUBJECT_LOCAL = 0,
+	LOCATION_SUBJECT_REMOTE = 1,
+	LOCATION_SUBJECT_3RD_PARTY = 2,
+};
+
+/*
+ * IEEE P802.11-REVmc/D5.0 Table 9-94 - Optional subelement IDs for LCI request
+ */
+enum lci_req_subelem {
+	LCI_REQ_SUBELEM_AZIMUTH_REQ = 1,
+	LCI_REQ_SUBELEM_ORIGINATOR_MAC_ADDR = 2,
+	LCI_REQ_SUBELEM_TARGET_MAC_ADDR = 3,
+	LCI_REQ_SUBELEM_MAX_AGE = 4,
+};
+
+#define FILS_NONCE_LEN 16
+#define FILS_SESSION_LEN 8
+#define FILS_CACHE_ID_LEN 2
+#define FILS_MAX_KEY_AUTH_LEN 48
+
 #ifdef _MSC_VER
 #pragma pack(push, 1)
 #endif /* _MSC_VER */
@@ -633,12 +924,19 @@
 					u8 action;
 					u8 variable[];
 				} STRUCT_PACKED fst_action;
+				struct {
+					u8 action;
+					u8 dialog_token;
+					u8 variable[];
+				} STRUCT_PACKED rrm;
 			} u;
 		} STRUCT_PACKED action;
 	} u;
 } STRUCT_PACKED;
 
 
+#define IEEE80211_MAX_MMPDU_SIZE 2304
+
 /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
 #define IEEE80211_HT_MCS_MASK_LEN 10
 
@@ -698,9 +996,14 @@
 	u8 selected_pairwise_suite[4];
 	u8 local_nonce[32];
 	u8 peer_nonce[32];
-	u8 mgtk[16];
-	u8 key_rsc[8];
-	u8 key_expiration[4];
+	/* Followed by
+	 * Key Replay Counter[8] (optional)
+	 *	(only in Mesh Group Key Inform/Acknowledge frames)
+	 * GTKdata[variable] (optional)
+	 *	(MGTK[variable] || Key RSC[8] || GTKExpirationTime[4])
+	 * IGTKdata[variable] (optional)
+	 *	(Key ID[2], IPN[6], IGTK[variable] in IGTK KDE format)
+	 */
 } STRUCT_PACKED;
 
 #ifdef _MSC_VER
@@ -1116,6 +1419,10 @@
 	WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3,
 };
 
+/* MBO v0.0_r25, 4.3: MBO ANQP-elements */
+#define MBO_ANQP_OUI_TYPE 0x12
+#define MBO_ANQP_SUBTYPE_CELL_CONN_PREF 1
+
 /* Wi-Fi Direct (P2P) */
 
 #define P2P_OUI_TYPE 9
@@ -1273,6 +1580,14 @@
 #define MESH_PATH_PROTOCOL_VENDOR	255
 #define MESH_PATH_METRIC_AIRTIME	1
 #define MESH_PATH_METRIC_VENDOR		255
+/* IEEE 802.11s - Mesh Capability */
+#define MESH_CAP_ACCEPT_ADDITIONAL_PEER	BIT(0)
+#define MESH_CAP_MCCA_SUPPORTED		BIT(1)
+#define MESH_CAP_MCCA_ENABLED		BIT(2)
+#define MESH_CAP_FORWARDING		BIT(3)
+#define MESH_CAP_MBCA_ENABLED		BIT(4)
+#define MESH_CAP_TBTT_ADJUSTING		BIT(5)
+#define MESH_CAP_MESH_PS_LEVEL		BIT(6)
 
 enum plink_action_field {
 	PLINK_OPEN = 1,
@@ -1287,41 +1602,6 @@
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
 
-/* cipher suite selectors */
-#define WLAN_CIPHER_SUITE_USE_GROUP	0x000FAC00
-#define WLAN_CIPHER_SUITE_WEP40		0x000FAC01
-#define WLAN_CIPHER_SUITE_TKIP		0x000FAC02
-/* reserved: 				0x000FAC03 */
-#define WLAN_CIPHER_SUITE_CCMP		0x000FAC04
-#define WLAN_CIPHER_SUITE_WEP104	0x000FAC05
-#define WLAN_CIPHER_SUITE_AES_CMAC	0x000FAC06
-#define WLAN_CIPHER_SUITE_NO_GROUP_ADDR	0x000FAC07
-#define WLAN_CIPHER_SUITE_GCMP		0x000FAC08
-#define WLAN_CIPHER_SUITE_GCMP_256	0x000FAC09
-#define WLAN_CIPHER_SUITE_CCMP_256	0x000FAC0A
-#define WLAN_CIPHER_SUITE_BIP_GMAC_128	0x000FAC0B
-#define WLAN_CIPHER_SUITE_BIP_GMAC_256	0x000FAC0C
-#define WLAN_CIPHER_SUITE_BIP_CMAC_256	0x000FAC0D
-
-#define WLAN_CIPHER_SUITE_SMS4		0x00147201
-
-#define WLAN_CIPHER_SUITE_CKIP		0x00409600
-#define WLAN_CIPHER_SUITE_CKIP_CMIC	0x00409601
-#define WLAN_CIPHER_SUITE_CMIC		0x00409602
-#define WLAN_CIPHER_SUITE_KRK		0x004096FF /* for nl80211 use only */
-
-/* AKM suite selectors */
-#define WLAN_AKM_SUITE_8021X		0x000FAC01
-#define WLAN_AKM_SUITE_PSK		0x000FAC02
-#define WLAN_AKM_SUITE_FT_8021X		0x000FAC03
-#define WLAN_AKM_SUITE_FT_PSK		0x000FAC04
-#define WLAN_AKM_SUITE_8021X_SHA256	0x000FAC05
-#define WLAN_AKM_SUITE_PSK_SHA256	0x000FAC06
-#define WLAN_AKM_SUITE_8021X_SUITE_B	0x000FAC11
-#define WLAN_AKM_SUITE_8021X_SUITE_B_192	0x000FAC12
-#define WLAN_AKM_SUITE_CCKM		0x00409600
-#define WLAN_AKM_SUITE_OSEN		0x506f9a01
-
 
 /* IEEE 802.11v - WNM Action field values */
 enum wnm_action {
@@ -1375,14 +1655,25 @@
 	WNM_BSS_TM_REJECT_LEAVING_ESS = 8
 };
 
+/*
+ * IEEE P802.11-REVmc/D5.0 Table 9-150 - Optional subelement IDs for
+ * neighbor report
+ */
 #define WNM_NEIGHBOR_TSF                         1
 #define WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING    2
 #define WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE    3
 #define WNM_NEIGHBOR_BSS_TERMINATION_DURATION    4
 #define WNM_NEIGHBOR_BEARING                     5
+#define WNM_NEIGHBOR_WIDE_BW_CHAN                6
+#define WNM_NEIGHBOR_MEASUREMENT_REPORT         39
+#define WNM_NEIGHBOR_HT_CAPAB                   45
+#define WNM_NEIGHBOR_HT_OPER                    61
+#define WNM_NEIGHBOR_SEC_CHAN_OFFSET            62
 #define WNM_NEIGHBOR_MEASUREMENT_PILOT          66
 #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES   70
 #define WNM_NEIGHBOR_MULTIPLE_BSSID             71
+#define WNM_NEIGHBOR_VHT_CAPAB                 191
+#define WNM_NEIGHBOR_VHT_OPER                  192
 
 /* QoS action */
 enum qos_action {
@@ -1451,6 +1742,8 @@
 	u8 link_margin;
 } STRUCT_PACKED;
 
+#define RRM_CAPABILITIES_IE_LEN 5
+
 /* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
 struct rrm_link_measurement_request {
 	u8 dialog_token;
@@ -1470,7 +1763,101 @@
 	u8 variable[0];
 } STRUCT_PACKED;
 
-#define SSID_MAX_LEN 32
+/* IEEE Std 802.11-2016, 9.4.2.21 - Measurement Request element */
+struct rrm_measurement_request_element {
+	u8 eid; /* Element ID */
+	u8 len; /* Length */
+	u8 token; /* Measurement Token */
+	u8 mode; /* Measurement Request Mode */
+	u8 type; /* Measurement Type */
+	u8 variable[0]; /* Measurement Request */
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2016, Figure 9-148 - Measurement Request Mode field */
+#define MEASUREMENT_REQUEST_MODE_PARALLEL BIT(0)
+#define MEASUREMENT_REQUEST_MODE_ENABLE BIT(1)
+#define MEASUREMENT_REQUEST_MODE_REQUEST BIT(2)
+#define MEASUREMENT_REQUEST_MODE_REPORT BIT(3)
+#define MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY BIT(4)
+
+/* IEEE Std 802.11-2016, 9.4.2.21.7 - Beacon request */
+struct rrm_measurement_beacon_request {
+	u8 oper_class; /* Operating Class */
+	u8 channel; /* Channel Number */
+	le16 rand_interval; /* Randomization Interval (in TUs) */
+	le16 duration; /* Measurement Duration (in TUs) */
+	u8 mode; /* Measurement Mode */
+	u8 bssid[ETH_ALEN]; /* BSSID */
+	u8 variable[0]; /* Optional Subelements */
+} STRUCT_PACKED;
+
+/*
+ * IEEE Std 802.11-2016, Table 9-87 - Measurement Mode definitions for Beacon
+ * request
+ */
+enum beacon_report_mode {
+	BEACON_REPORT_MODE_PASSIVE = 0,
+	BEACON_REPORT_MODE_ACTIVE = 1,
+	BEACON_REPORT_MODE_TABLE = 2,
+};
+
+/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */
+#define WLAN_BEACON_REQUEST_SUBELEM_SSID	0
+#define WLAN_BEACON_REQUEST_SUBELEM_INFO	1 /* Beacon Reporting */
+#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL	2 /* Reporting Detail */
+#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST	10
+#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL	51 /* AP Channel Report */
+#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR	221
+
+/*
+ * IEEE Std 802.11-2016, Table 9-90 - Reporting Detail values
+ */
+enum beacon_report_detail {
+	/* No fixed-length fields or elements */
+	BEACON_REPORT_DETAIL_NONE = 0,
+	/* All fixed-length fields and any requested elements in the Request
+	 * element if present */
+	BEACON_REPORT_DETAIL_REQUESTED_ONLY = 1,
+	/* All fixed-length fields and elements (default, used when Reporting
+	 * Detail subelement is not included in a Beacon request) */
+	BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS = 2,
+};
+
+/* IEEE Std 802.11-2016, 9.4.2.22 - Measurement Report element */
+struct rrm_measurement_report_element {
+	u8 eid; /* Element ID */
+	u8 len; /* Length */
+	u8 token; /* Measurement Token */
+	u8 mode; /* Measurement Report Mode */
+	u8 type; /* Measurement Type */
+	u8 variable[0]; /* Measurement Report */
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2016, Figure 9-192 - Measurement Report Mode field */
+#define MEASUREMENT_REPORT_MODE_ACCEPT 0
+#define MEASUREMENT_REPORT_MODE_REJECT_LATE BIT(0)
+#define MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE BIT(1)
+#define MEASUREMENT_REPORT_MODE_REJECT_REFUSED BIT(2)
+
+/* IEEE Std 802.11-2016, 9.4.2.22.7 - Beacon report */
+struct rrm_measurement_beacon_report {
+	u8 op_class; /* Operating Class */
+	u8 channel; /* Channel Number */
+	le64 start_time; /* Actual Measurement Start Time
+			  * (in TSF of the BSS requesting the measurement) */
+	le16 duration; /* in TUs */
+	u8 report_info; /* Reported Frame Information */
+	u8 rcpi; /* RCPI */
+	u8 rsni; /* RSNI */
+	u8 bssid[ETH_ALEN]; /* BSSID */
+	u8 antenna_id; /* Antenna ID */
+	le32 parent_tsf; /* Parent TSF */
+	u8 variable[0]; /* Optional Subelements */
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
+#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY	1
+#define WLAN_BEACON_REPORT_SUBELEM_VENDOR	221
 
 /* IEEE Std 802.11ad-2012 - Multi-band element */
 struct multi_band_ie {
@@ -1542,7 +1929,7 @@
 	PHY_TYPE_VHT = 9,
 };
 
-/* IEEE Std 802.11-2012, 8.4.2.39 - Neighbor Report element */
+/* IEEE P802.11-REVmc/D5.0, 9.4.2.37 - Neighbor Report element */
 /* BSSID Information Field */
 #define NEI_REP_BSSID_INFO_AP_NOT_REACH BIT(0)
 #define NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH BIT(1)
@@ -1557,5 +1944,69 @@
 #define NEI_REP_BSSID_INFO_IMM_BA BIT(9)
 #define NEI_REP_BSSID_INFO_MOBILITY_DOMAIN BIT(10)
 #define NEI_REP_BSSID_INFO_HT BIT(11)
+#define NEI_REP_BSSID_INFO_VHT BIT(12)
+#define NEI_REP_BSSID_INFO_FTM BIT(13)
+
+/*
+ * IEEE P802.11-REVmc/D5.0 Table 9-152 - HT/VHT Operation Information
+ * subfields.
+ * Note: These definitions are not the same as other VHT_CHANWIDTH_*.
+ */
+enum nr_chan_width {
+	NR_CHAN_WIDTH_20 = 0,
+	NR_CHAN_WIDTH_40 = 1,
+	NR_CHAN_WIDTH_80 = 2,
+	NR_CHAN_WIDTH_160 = 3,
+	NR_CHAN_WIDTH_80P80 = 4,
+};
+
+struct ieee80211_he_capabilities {
+	u8 he_mac_capab_info[5];
+	u8 he_phy_capab_info[9];
+	u16 he_txrx_mcs_support;
+	/* possibly followed by Tx Rx MCS NSS descriptor */
+	u8 variable[];
+	/* PPE Thresholds (optional) */
+} STRUCT_PACKED;
+
+struct ieee80211_he_operation {
+	u32 he_oper_params;
+	u8 he_mcs_nss_set[3];
+	u8 vht_op_info_chwidth;
+	u8 vht_op_info_chan_center_freq_seg0_idx;
+	u8 vht_op_info_chan_center_freq_seg1_idx;
+} STRUCT_PACKED;
+
+/* HE Capabilities Information defines */
+#define HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX	3
+#define HE_PHYCAP_SU_BEAMFORMER_CAPAB		((u8) BIT(7))
+#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX	4
+#define HE_PHYCAP_SU_BEAMFORMEE_CAPAB		((u8) BIT(0))
+#define HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX	4
+#define HE_PHYCAP_MU_BEAMFORMER_CAPAB		((u8) BIT(1))
+
+/* HE Operation defines */
+#define HE_OPERATION_BSS_COLOR_MASK		((u32) (BIT(0) | BIT(1) | \
+							BIT(2) | BIT(3) | \
+							BIT(4) | BIT(5)))
+#define HE_OPERATION_DFLT_PE_DURATION_MASK	((u32) (BIT(6) | BIT(7) | \
+							BIT(8)))
+#define HE_OPERATION_DFLT_PE_DURATION_OFFSET	6
+#define HE_OPERATION_TWT_REQUIRED		((u32) BIT(9))
+#define HE_OPERATION_RTS_THRESHOLD_MASK	((u32) (BIT(10) | BIT(11) | \
+						BIT(12) | BIT(13) | \
+						BIT(14) | BIT(15) | \
+						BIT(16) | BIT(17) | \
+						BIT(18) | BIT(19)))
+#define HE_OPERATION_RTS_THRESHOLD_OFFSET	10
+#define HE_OPERATION_PARTIAL_BSS_COLOR		((u32) BIT(20))
+#define HE_OPERATION_MAX_BSSID_INDICATOR_MASK	((u32) (BIT(21) | BIT(22) | \
+							BIT(23) | BIT(24) | \
+							BIT(25) | BIT(26) | \
+							BIT(27) | BIT(28)))
+#define HE_OPERATION_MAX_BSSID_INDICATOR_OFFSET 21
+#define HE_OPERATION_TX_BSSID_INDICATOR		((u32) BIT(29))
+#define HE_OPERATION_BSS_COLOR_DISABLED		((u32) BIT(30))
+#define HE_OPERATION_BSS_DUAL_BEACON		((u32) BIT(31))
 
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h
index cc88caa..280c439 100644
--- a/src/common/ieee802_1x_defs.h
+++ b/src/common/ieee802_1x_defs.h
@@ -10,7 +10,7 @@
 #define IEEE802_1X_DEFS_H
 
 #define CS_ID_LEN		8
-#define CS_ID_GCM_AES_128	{0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01}
+#define CS_ID_GCM_AES_128	0x0080020001000001ULL
 #define CS_NAME_GCM_AES_128	"GCM-AES-128"
 
 enum macsec_policy {
@@ -25,6 +25,12 @@
 	 * Disabled MACsec - do not secure sessions.
 	 */
 	DO_NOT_SECURE,
+
+	/**
+	 * Should secure sessions, and try to use encryption.
+	 * Like @SHOULD_SECURE, this follows the key server's decision.
+	 */
+	SHOULD_ENCRYPT,
 };
 
 
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
index 8dff303..0f47518 100644
--- a/src/common/privsep_commands.h
+++ b/src/common/privsep_commands.h
@@ -9,6 +9,7 @@
 #ifndef PRIVSEP_COMMANDS_H
 #define PRIVSEP_COMMANDS_H
 
+#include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 
 enum privsep_cmd {
@@ -29,8 +30,17 @@
 	PRIVSEP_CMD_AUTHENTICATE,
 };
 
-struct privsep_cmd_authenticate
-{
+#define PRIVSEP_MAX_SCAN_FREQS 50
+
+struct privsep_cmd_scan {
+	unsigned int num_ssids;
+	u8 ssids[WPAS_MAX_SCAN_SSIDS][32];
+	u8 ssid_lens[WPAS_MAX_SCAN_SSIDS];
+	unsigned int num_freqs;
+	u16 freqs[PRIVSEP_MAX_SCAN_FREQS];
+};
+
+struct privsep_cmd_authenticate {
 	int freq;
 	u8 bssid[ETH_ALEN];
 	u8 ssid[SSID_MAX_LEN];
@@ -42,13 +52,12 @@
 	int wep_tx_keyidx;
 	int local_state_change;
 	int p2p;
-	size_t sae_data_len;
+	size_t auth_data_len;
 	/* followed by ie_len bytes of ie */
-	/* followed by sae_data_len bytes of sae_data */
+	/* followed by auth_data_len bytes of auth_data */
 };
 
-struct privsep_cmd_associate
-{
+struct privsep_cmd_associate {
 	u8 bssid[ETH_ALEN];
 	u8 ssid[SSID_MAX_LEN];
 	size_t ssid_len;
@@ -64,8 +73,7 @@
 	/* followed by wpa_ie_len bytes of wpa_ie */
 };
 
-struct privsep_cmd_set_key
-{
+struct privsep_cmd_set_key {
 	int alg;
 	u8 addr[ETH_ALEN];
 	int key_idx;
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 87bbc05..934f09d 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -1,6 +1,6 @@
 /*
  * Qualcomm Atheros OUI and vendor specific assignments
- * Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+ * Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -89,6 +89,205 @@
  * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver,
  *	which supports DFS offloading, to indicate a radar pattern has been
  *	detected. The channel is now unusable.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START: Command used to
+ *	start the P2P Listen offload function in device and pass the listen
+ *	channel, period, interval, count, device types, and vendor specific
+ *	information elements to the device driver and firmware.
+ *	Uses the attributes defines in
+ *	enum qca_wlan_vendor_attr_p2p_listen_offload.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP: Command/event used to
+ *	indicate stop request/response of the P2P Listen offload function in
+ *	device. As an event, it indicates either the feature stopped after it
+ *	was already running or feature has actually failed to start. Uses the
+ *	attributes defines in enum qca_wlan_vendor_attr_p2p_listen_offload.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH: After AP starts
+ *	beaconing, this sub command provides the driver, the frequencies on the
+ *	5 GHz band to check for any radar activity. Driver selects one channel
+ *	from this priority list provided through
+ *	@QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST and starts
+ *	to check for radar activity on it. If no radar activity is detected
+ *	during the channel availability check period, driver internally switches
+ *	to the selected frequency of operation. If the frequency is zero, driver
+ *	internally selects a channel. The status of this conditional switch is
+ *	indicated through an event using the same sub command through
+ *	@QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS. Attributes are
+ *	listed in qca_wlan_vendor_attr_sap_conditional_chan_switch.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND: Set GPIO pins. This uses the
+ *	attributes defined in enum qca_wlan_gpio_attr.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY: Fetch hardware capabilities.
+ *	This uses @QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY to indicate which
+ *	capabilities are to be fetched and other
+ *	enum qca_wlan_vendor_attr_get_hw_capability attributes to return the
+ *	requested capabilities.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT: Link layer statistics extension.
+ *	enum qca_wlan_vendor_attr_ll_stats_ext attributes are used with this
+ *	command and event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA: Get capabilities for
+ *	indoor location features. Capabilities are reported in
+ *	QCA_WLAN_VENDOR_ATTR_LOC_CAPA.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION: Start an FTM
+ *	(fine timing measurement) session with one or more peers.
+ *	Specify Session cookie in QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE and
+ *	peer information in QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS.
+ *	On success, 0 or more QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT
+ *	events will be reported, followed by
+ *	QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE event to indicate
+ *	end of session.
+ *	Refer to IEEE P802.11-REVmc/D7.0, 11.24.6
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION: Abort a running session.
+ *	A QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE will be reported with
+ *	status code indicating session was aborted.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT: Event with measurement
+ *	results for one peer. Results are reported in
+ *	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE: Event triggered when
+ *	FTM session is finished, either successfully or aborted by
+ *	request.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER: Configure FTM responder
+ *	mode. QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE specifies whether
+ *	to enable or disable the responder. LCI/LCR reports can be
+ *	configured with QCA_WLAN_VENDOR_ATTR_FTM_LCI and
+ *	QCA_WLAN_VENDOR_ATTR_FTM_LCR. Can be called multiple
+ *	times to update the LCI/LCR reports.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS: Perform a standalone AOA (angle of
+ *	arrival) measurement with a single peer. Specify peer MAC address in
+ *	QCA_WLAN_VENDOR_ATTR_MAC_ADDR and optionally frequency (MHz) in
+ *	QCA_WLAN_VENDOR_ATTR_FREQ (if not specified, locate peer in kernel
+ *	scan results cache and use the frequency from there).
+ *	Also specify measurement type in QCA_WLAN_VENDOR_ATTR_AOA_TYPE.
+ *	Measurement result is reported in
+ *	QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT event.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS: Abort an AOA measurement. Specify
+ *	peer MAC address in QCA_WLAN_VENDOR_ATTR_MAC_ADDR.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT: Event that reports
+ *	the AOA measurement result.
+ *	Peer MAC address reported in QCA_WLAN_VENDOR_ATTR_MAC_ADDR.
+ *	success/failure status is reported in
+ *	QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS.
+ *	Measurement data is reported in QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT.
+ *	The antenna array(s) used in the measurement are reported in
+ *	QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST: Encrypt/decrypt the given
+ *	data as per the given parameters.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI: Get antenna RSSI value for a
+ *	specific chain.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG: Get low level
+ *	configuration for a DMG RF sector. Specify sector index in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and RF modules
+ *	to return sector information for in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK. Returns sector configuration
+ *	in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG. Also return the
+ *	exact time where information was captured in
+ *	QCA_WLAN_VENDOR_ATTR_TSF.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG: Set low level
+ *	configuration for a DMG RF sector. Specify sector index in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX, sector type in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and sector configuration
+ *	for one or more DMG RF modules in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR: Get selected
+ *	DMG RF sector for a station. This is the sector that the HW
+ *	will use to communicate with the station. Specify the MAC address
+ *	of associated station/AP/PCP in QCA_WLAN_VENDOR_ATTR_MAC_ADDR (not
+ *	needed for unassociated	station). Specify sector type to return in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE. Returns the selected
+ *	sector index in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
+ *	Also return the exact time where the information was captured
+ *	in QCA_WLAN_VENDOR_ATTR_TSF.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR: Set the
+ *	selected DMG RF sector for a station. This is the sector that
+ *	the HW will use to communicate with the station.
+ *	Specify the MAC address of associated station/AP/PCP in
+ *	QCA_WLAN_VENDOR_ATTR_MAC_ADDR, the sector type to select in
+ *	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE and the sector index
+ *	in QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX.
+ *	The selected sector will be locked such that it will not be
+ *	modified like it normally does (for example when station
+ *	moves around). To unlock the selected sector for a station
+ *	pass the special value 0xFFFF in the sector index. To unlock
+ *	all connected stations also pass a broadcast MAC address.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS: Configure the TDLS behavior
+ *	in the host driver. The different TDLS configurations are defined
+ *	by the attributes in enum qca_wlan_vendor_attr_tdls_configuration.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES: Query device IEEE 802.11ax HE
+ *	capabilities. The response uses the attributes defined in
+ *	enum qca_wlan_vendor_attr_get_he_capabilities.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN: Abort an ongoing vendor scan that was
+ *	started with QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN. This command
+ *	carries the scan cookie of the corresponding scan request. The scan
+ *	cookie is represented by QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS: Set the Specific
+ *	Absorption Rate (SAR) power limits. A critical regulation for
+ *	FCC compliance, OEMs require methods to set SAR limits on TX
+ *	power of WLAN/WWAN. enum qca_vendor_attr_sar_limits
+ *	attributes are used with this command.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS: This command/event is used by the
+ *	host driver for offloading the implementation of Auto Channel Selection
+ *	(ACS) to an external user space entity. This interface is used as the
+ *	event from the host driver to the user space entity and also as the
+ *	request from the user space entity to the host driver. The event from
+ *	the host driver is used by the user space entity as an indication to
+ *	start the ACS functionality. The attributes used by this event are
+ *	represented by the enum qca_wlan_vendor_attr_external_acs_event.
+ *	User space entity uses the same interface to inform the host driver with
+ *	selected channels after the ACS operation using the attributes defined
+ *	by enum qca_wlan_vendor_attr_external_acs_channels.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE: Vendor event carrying the
+ *	requisite information leading to a power save failure. The information
+ *	carried as part of this event is represented by the
+ *	enum qca_attr_chip_power_save_failure attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET: Start/Stop the NUD statistics
+ *	collection. Uses attributes defined in enum qca_attr_nud_stats_set.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET: Get the NUD statistics. These
+ *	statistics are represented by the enum qca_attr_nud_stats_get
+ *	attributes.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS: Sub-command to fetch
+ *	the BSS transition status, whether accept or reject, for a list of
+ *	candidate BSSIDs provided by the userspace. This uses the vendor
+ *	attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and
+ *	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO. The userspace shall specify
+ *	the attributes QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON and an
+ *	array of QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID nested in
+ *	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO in the request. In the response
+ *	the driver shall specify array of
+ *	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID and
+ *	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS pairs nested in
+ *	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL: Set the trace level for a
+ *	specific QCA module. The trace levels are represented by
+ *	enum qca_attr_trace_level attributes.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -98,7 +297,7 @@
 	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
 	QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY =  11,
 	QCA_NL80211_VENDOR_SUBCMD_NAN =  12,
-	QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
+	QCA_NL80211_VENDOR_SUBCMD_STATS_EXT = 13,
 	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
 	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
 	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
@@ -140,7 +339,23 @@
 	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
 	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
 	QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
-	/* 61-90 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO = 61,
+	QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START = 62,
+	QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_MEMORY_DUMP = 63,
+	QCA_NL80211_VENDOR_SUBCMD_ROAM = 64,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST = 65,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SSID_HOTLIST = 66,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_FOUND = 67,
+	QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_SSID_LOST = 68,
+	QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST = 69,
+	QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST = 70,
+	QCA_NL80211_VENDOR_SUBCMD_PNO_RESET_PASSPOINT_LIST = 71,
+	QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND = 72,
+	QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND = 73,
+	/* Wi-Fi configuration subcommands */
+	QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74,
+	QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION = 75,
+	/* 76-90 - reserved for QCA */
 	QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
 	QCA_NL80211_VENDOR_SUBCMD_OCB_SET_CONFIG = 92,
 	QCA_NL80211_VENDOR_SUBCMD_OCB_SET_UTC_TIME = 93,
@@ -162,9 +377,44 @@
 	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_SCALE = 109,
 	/* 110..114 - reserved for QCA */
 	QCA_NL80211_VENDOR_SUBCMD_SET_TXPOWER_DECR_DB = 115,
-	/* 116..118 - reserved for QCA */
+	/* 116..117 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_SET_SAP_CONFIG = 118,
 	QCA_NL80211_VENDOR_SUBCMD_TSF = 119,
 	QCA_NL80211_VENDOR_SUBCMD_WISA = 120,
+	/* 121 - reserved for QCA */
+	QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 122,
+	QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 123,
+	QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH = 124,
+	QCA_NL80211_VENDOR_SUBCMD_GPIO_CONFIG_COMMAND = 125,
+	QCA_NL80211_VENDOR_SUBCMD_GET_HW_CAPABILITY = 126,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_EXT = 127,
+	/* FTM/indoor location subcommands */
+	QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA = 128,
+	QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION = 129,
+	QCA_NL80211_VENDOR_SUBCMD_FTM_ABORT_SESSION = 130,
+	QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT = 131,
+	QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE = 132,
+	QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER = 133,
+	QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS = 134,
+	QCA_NL80211_VENDOR_SUBCMD_AOA_ABORT_MEAS = 135,
+	QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT = 136,
+	QCA_NL80211_VENDOR_SUBCMD_ENCRYPTION_TEST = 137,
+	QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI = 138,
+	/* DMG low level RF sector operations */
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG = 139,
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG = 140,
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SELECTED_SECTOR = 141,
+	QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR = 142,
+	QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS = 143,
+	QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES = 144,
+	QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN = 145,
+	QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS = 146,
+	QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS = 147,
+	QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE = 148,
+	QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET = 149,
+	QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET = 150,
+	QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS = 151,
+	QCA_NL80211_VENDOR_SUBCMD_SET_TRACE_LEVEL = 152,
 };
 
 
@@ -194,6 +444,136 @@
 	QCA_WLAN_VENDOR_ATTR_MAX_CONCURRENT_CHANNELS_5_0_BAND = 11,
 	/* Unsigned 32-bit value from enum qca_set_band. */
 	QCA_WLAN_VENDOR_ATTR_SETBAND_VALUE = 12,
+	/* Dummy (NOP) attribute for 64 bit padding */
+	QCA_WLAN_VENDOR_ATTR_PAD = 13,
+	/* Unique FTM session cookie (Unsigned 64 bit). Specified in
+	 * QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION. Reported in
+	 * the session in QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT and
+	 * QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FTM_SESSION_COOKIE = 14,
+	/* Indoor location capabilities, returned by
+	 * QCA_NL80211_VENDOR_SUBCMD_LOC_GET_CAPA.
+	 * see enum qca_wlan_vendor_attr_loc_capa.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA = 15,
+	/* Array of nested attributes containing information about each peer
+	 * in FTM measurement session. See enum qca_wlan_vendor_attr_peer_info
+	 * for supported attributes for each peer.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEERS = 16,
+	/* Array of nested attributes containing measurement results for
+	 * one or more peers, reported by the
+	 * QCA_NL80211_VENDOR_SUBCMD_FTM_MEAS_RESULT event.
+	 * See enum qca_wlan_vendor_attr_peer_result for list of supported
+	 * attributes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PEER_RESULTS = 17,
+	/* Flag attribute for enabling or disabling responder functionality. */
+	QCA_WLAN_VENDOR_ATTR_FTM_RESPONDER_ENABLE = 18,
+	/* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
+	 * command to specify the LCI report that will be sent by
+	 * the responder during a measurement exchange. The format is
+	 * defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.10.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FTM_LCI = 19,
+	/* Used in the QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
+	 * command to specify the location civic report that will
+	 * be sent by the responder during a measurement exchange.
+	 * The format is defined in IEEE P802.11-REVmc/D7.0, 9.4.2.22.13.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FTM_LCR = 20,
+	/* Session/measurement completion status code,
+	 * reported in QCA_NL80211_VENDOR_SUBCMD_FTM_SESSION_DONE and
+	 * QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT
+	 * see enum qca_vendor_attr_loc_session_status.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS = 21,
+	/* Initial dialog token used by responder (0 if not specified),
+	 * unsigned 8 bit value.
+	 */
+	QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN = 22,
+	/* AOA measurement type. Requested in QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS
+	 * and optionally in QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION if
+	 * AOA measurements are needed as part of an FTM session.
+	 * Reported by QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS_RESULT. See
+	 * enum qca_wlan_vendor_attr_aoa_type.
+	 */
+	QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23,
+	/* A bit mask (unsigned 32 bit value) of antenna arrays used
+	 * by indoor location measurements. Refers to the antenna
+	 * arrays described by QCA_VENDOR_ATTR_LOC_CAPA_ANTENNA_ARRAYS.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24,
+	/* AOA measurement data. Its contents depends on the AOA measurement
+	 * type and antenna array mask:
+	 * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: array of U16 values,
+	 * phase of the strongest CIR path for each antenna in the measured
+	 * array(s).
+	 * QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16
+	 * values, phase and amplitude of the strongest CIR path for each
+	 * antenna in the measured array(s).
+	 */
+	QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
+	 * to specify the chain number (unsigned 32 bit value) to inquire
+	 * the corresponding antenna RSSI value */
+	QCA_WLAN_VENDOR_ATTR_CHAIN_INDEX = 26,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command
+	 * to report the specific antenna RSSI value (unsigned 32 bit value) */
+	QCA_WLAN_VENDOR_ATTR_CHAIN_RSSI = 27,
+	/* Frequency in MHz, various uses. Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_FREQ = 28,
+	/* TSF timer value, unsigned 64 bit value.
+	 * May be returned by various commands.
+	 */
+	QCA_WLAN_VENDOR_ATTR_TSF = 29,
+	/* DMG RF sector index, unsigned 16 bit number. Valid values are
+	 * 0..127 for sector indices or 65535 as special value used to
+	 * unlock sector selection in
+	 * QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SELECTED_SECTOR.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_INDEX = 30,
+	/* DMG RF sector type, unsigned 8 bit value. One of the values
+	 * in enum qca_wlan_vendor_attr_dmg_rf_sector_type.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE = 31,
+	/* Bitmask of DMG RF modules for which information is requested. Each
+	 * bit corresponds to an RF module with the same index as the bit
+	 * number. Unsigned 32 bit number but only low 8 bits can be set since
+	 * all DMG chips currently have up to 8 RF modules.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_MODULE_MASK = 32,
+	/* Array of nested attributes where each entry is DMG RF sector
+	 * configuration for a single RF module.
+	 * Attributes for each entry are taken from enum
+	 * qca_wlan_vendor_attr_dmg_rf_sector_cfg.
+	 * Specified in QCA_NL80211_VENDOR_SUBCMD_DMG_RF_SET_SECTOR_CFG
+	 * and returned by QCA_NL80211_VENDOR_SUBCMD_DMG_RF_GET_SECTOR_CFG.
+	 */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG = 33,
+	/* Used in QCA_NL80211_VENDOR_SUBCMD_STATS_EXT command
+	 * to report frame aggregation statistics to userspace.
+	 */
+	QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_NUM = 34,
+	QCA_WLAN_VENDOR_ATTR_RX_AGGREGATION_STATS_HOLES_INFO = 35,
+	/* Unsigned 8-bit value representing MBO transition reason code as
+	 * provided by the AP used by subcommand
+	 * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS. This is
+	 * specified by the userspace in the request to the driver.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BTM_MBO_TRANSITION_REASON = 36,
+	/* Array of nested attributes, BSSID and status code, used by subcommand
+	 * QCA_NL80211_VENDOR_SUBCMD_FETCH_BSS_TRANSITION_STATUS, where each
+	 * entry is taken from enum qca_wlan_vendor_attr_btm_candidate_info.
+	 * The userspace space specifies the list/array of candidate BSSIDs in
+	 * the order of preference in the request. The driver specifies the
+	 * status code, for each BSSID in the list, in the response. The
+	 * acceptable candidates are listed in the order preferred by the
+	 * driver.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO = 37,
+
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_MAX	= QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
@@ -221,6 +601,43 @@
 	QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
 };
 
+enum qca_wlan_vendor_attr_p2p_listen_offload {
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INVALID = 0,
+	/* A 32-bit unsigned value; the P2P listen frequency (MHz); must be one
+	 * of the social channels.
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
+	/* A 32-bit unsigned value; the P2P listen offload period (ms).
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
+	/* A 32-bit unsigned value; the P2P listen interval duration (ms).
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
+	/* A 32-bit unsigned value; number of interval times the firmware needs
+	 * to run the offloaded P2P listen operation before it stops.
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
+	/* An array of arbitrary binary data with one or more 8-byte values.
+	 * The device types include both primary and secondary device types.
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
+	/* An array of unsigned 8-bit characters; vendor information elements.
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
+	/* A 32-bit unsigned value; a control flag to indicate whether listen
+	 * results need to be flushed to wpa_supplicant.
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CTRL_FLAG,
+	/* A 8-bit unsigned value; reason code for P2P listen offload stop
+	 * event.
+	 */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX =
+	QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_AFTER_LAST - 1
+};
+
 enum qca_wlan_vendor_attr_acs_offload {
 	QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
 	QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
@@ -259,12 +676,19 @@
  *	band selection based on channel selection results.
  * @QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS: Device supports
  * 	simultaneous off-channel operations.
+ * @QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD: Device supports P2P
+ *	Listen offload; a mechanism where the station's firmware takes care of
+ *	responding to incoming Probe Request frames received from other P2P
+ *	Devices whilst in Listen state, rather than having the user space
+ *	wpa_supplicant do it. Information from received P2P requests are
+ *	forwarded from firmware to host whenever the host processor wakes up.
  * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
  */
 enum qca_wlan_vendor_features {
 	QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD	= 0,
 	QCA_WLAN_VENDOR_FEATURE_SUPPORT_HW_MODE_ANY     = 1,
 	QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS = 2,
+	QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD	= 3,
 	NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
 };
 
@@ -338,6 +762,25 @@
 };
 
 /**
+ * enum qca_access_policy - Access control policy
+ *
+ * Access control policy is applied on the configured IE
+ * (QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE).
+ * To be set with QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY.
+ *
+ * @QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED: Deny Wi-Fi connections which match
+ *	the specific configuration (IE) set, i.e., allow all the
+ *	connections which do not match the configuration.
+ * @QCA_ACCESS_POLICY_DENY_UNLESS_LISTED: Accept Wi-Fi connections which match
+ *	the specific configuration (IE) set, i.e., deny all the
+ *	connections which do not match the configuration.
+ */
+enum qca_access_policy {
+	QCA_ACCESS_POLICY_ACCEPT_UNLESS_LISTED,
+	QCA_ACCESS_POLICY_DENY_UNLESS_LISTED,
+};
+
+/**
  * enum qca_vendor_attr_get_tsf: Vendor attributes for TSF capture
  * @QCA_WLAN_VENDOR_ATTR_TSF_CMD: enum qca_tsf_operation (u32)
  * @QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE: Unsigned 64 bit TSF timer value
@@ -404,9 +847,53 @@
  *
  *	This vendor element may be included in GO Negotiation Request, P2P
  *	Invitation Request, and Provision Discovery Request frames.
+ *
+ * @QCA_VENDOR_ELEM_HE_CAPAB: HE Capabilities element.
+ *	This element can be used for pre-standard publication testing of HE
+ *	before P802.11ax draft assigns the element ID. The payload of this
+ *	vendor specific element is defined by the latest P802.11ax draft.
+ *	Please note that the draft is still work in progress and this element
+ *	payload is subject to change.
+ *
+ * @QCA_VENDOR_ELEM_HE_OPER: HE Operation element.
+ *	This element can be used for pre-standard publication testing of HE
+ *	before P802.11ax draft assigns the element ID. The payload of this
+ *	vendor specific element is defined by the latest P802.11ax draft.
+ *	Please note that the draft is still work in progress and this element
+ *	payload is subject to change.
+ *
+ * @QCA_VENDOR_ELEM_RAPS: RAPS element (OFDMA-based Random Access Parameter Set
+ *	element).
+ *	This element can be used for pre-standard publication testing of HE
+ *	before P802.11ax draft assigns the element ID extension. The payload of
+ *	this vendor specific element is defined by the latest P802.11ax draft
+ *	(not including the Element ID Extension field). Please note that the
+ *	draft is still work in progress and this element payload is subject to
+ *	change.
+ *
+ * @QCA_VENDOR_ELEM_MU_EDCA_PARAMS: MU EDCA Parameter Set element.
+ *	This element can be used for pre-standard publication testing of HE
+ *	before P802.11ax draft assigns the element ID extension. The payload of
+ *	this vendor specific element is defined by the latest P802.11ax draft
+ *	(not including the Element ID Extension field). Please note that the
+ *	draft is still work in progress and this element payload is subject to
+ *	change.
+ *
+ * @QCA_VENDOR_ELEM_BSS_COLOR_CHANGE: BSS Color Change Announcement element.
+ *	This element can be used for pre-standard publication testing of HE
+ *	before P802.11ax draft assigns the element ID extension. The payload of
+ *	this vendor specific element is defined by the latest P802.11ax draft
+ *	(not including the Element ID Extension field). Please note that the
+ *	draft is still work in progress and this element payload is subject to
+ *	change.
  */
 enum qca_vendor_element_id {
 	QCA_VENDOR_ELEM_P2P_PREF_CHAN_LIST = 0,
+	QCA_VENDOR_ELEM_HE_CAPAB = 1,
+	QCA_VENDOR_ELEM_HE_OPER = 2,
+	QCA_VENDOR_ELEM_RAPS = 3,
+	QCA_VENDOR_ELEM_MU_EDCA_PARAMS = 4,
+	QCA_VENDOR_ELEM_BSS_COLOR_CHANGE = 5,
 };
 
 /**
@@ -429,19 +916,22 @@
  * 	scan flag is set
  * @QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK: 6-byte MAC address mask to be used with
  * 	randomisation
+ * @QCA_WLAN_VENDOR_ATTR_SCAN_BSSID: 6-byte MAC address representing the
+ *	specific BSSID to scan for.
  */
 enum qca_wlan_vendor_attr_scan {
 	QCA_WLAN_VENDOR_ATTR_SCAN_INVALID_PARAM = 0,
-	QCA_WLAN_VENDOR_ATTR_SCAN_IE,
-	QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES,
-	QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS,
-	QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES,
-	QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE,
-	QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS,
-	QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE,
-	QCA_WLAN_VENDOR_ATTR_SCAN_STATUS,
-	QCA_WLAN_VENDOR_ATTR_SCAN_MAC,
-	QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK,
+	QCA_WLAN_VENDOR_ATTR_SCAN_IE = 1,
+	QCA_WLAN_VENDOR_ATTR_SCAN_FREQUENCIES = 2,
+	QCA_WLAN_VENDOR_ATTR_SCAN_SSIDS = 3,
+	QCA_WLAN_VENDOR_ATTR_SCAN_SUPP_RATES = 4,
+	QCA_WLAN_VENDOR_ATTR_SCAN_TX_NO_CCK_RATE = 5,
+	QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS = 6,
+	QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE = 7,
+	QCA_WLAN_VENDOR_ATTR_SCAN_STATUS = 8,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAC = 9,
+	QCA_WLAN_VENDOR_ATTR_SCAN_MAC_MASK = 10,
+	QCA_WLAN_VENDOR_ATTR_SCAN_BSSID = 11,
 	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_SCAN_MAX =
 	QCA_WLAN_VENDOR_ATTR_SCAN_AFTER_LAST - 1
@@ -507,4 +997,2429 @@
 	QCA_WLAN_VENDOR_ATTR_TXPOWER_DECR_DB_AFTER_LAST - 1
 };
 
+/* Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION and
+ * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands.
+ */
+enum qca_wlan_vendor_attr_config {
+	QCA_WLAN_VENDOR_ATTR_CONFIG_INVALID = 0,
+	/* Unsigned 32-bit value to set the DTIM period.
+	 * Whether the wifi chipset wakes at every dtim beacon or a multiple of
+	 * the DTIM period. If DTIM is set to 3, the STA shall wake up every 3
+	 * DTIM beacons.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_DYNAMIC_DTIM = 1,
+	/* Unsigned 32-bit value to set the wifi_iface stats averaging factor
+	 * used to calculate statistics like average the TSF offset or average
+	 * number of frame leaked.
+	 * For instance, upon Beacon frame reception:
+	 * current_avg = ((beacon_TSF - TBTT) * factor + previous_avg * (0x10000 - factor) ) / 0x10000
+	 * For instance, when evaluating leaky APs:
+	 * current_avg = ((num frame received within guard time) * factor + previous_avg * (0x10000 - factor)) / 0x10000
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_STATS_AVG_FACTOR = 2,
+	/* Unsigned 32-bit value to configure guard time, i.e., when
+	 * implementing IEEE power management based on frame control PM bit, how
+	 * long the driver waits before shutting down the radio and after
+	 * receiving an ACK frame for a Data frame with PM bit set.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GUARD_TIME = 3,
+	/* Unsigned 32-bit value to change the FTM capability dynamically */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_FINE_TIME_MEASUREMENT = 4,
+	/* Unsigned 16-bit value to configure maximum TX rate dynamically */
+	QCA_WLAN_VENDOR_ATTR_CONF_TX_RATE = 5,
+	/* Unsigned 32-bit value to configure the number of continuous
+	 * Beacon Miss which shall be used by the firmware to penalize
+	 * the RSSI.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PENALIZE_AFTER_NCONS_BEACON_MISS = 6,
+	/* Unsigned 8-bit value to configure the channel avoidance indication
+	 * behavior. Firmware to send only one indication and ignore duplicate
+	 * indications when set to avoid multiple Apps wakeups.
+	 */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_CHANNEL_AVOIDANCE_IND = 7,
+	/* 8-bit unsigned value to configure the maximum TX MPDU for
+	 * aggregation. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_MPDU_AGGREGATION = 8,
+	/* 8-bit unsigned value to configure the maximum RX MPDU for
+	 * aggregation. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_MPDU_AGGREGATION = 9,
+	/* 8-bit unsigned value to configure the Non aggregrate/11g sw
+	 * retry threshold (0 disable, 31 max). */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_NON_AGG_RETRY = 10,
+	/* 8-bit unsigned value to configure the aggregrate sw
+	 * retry threshold (0 disable, 31 max). */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_AGG_RETRY = 11,
+	/* 8-bit unsigned value to configure the MGMT frame
+	 * retry threshold (0 disable, 31 max). */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_MGMT_RETRY = 12,
+	/* 8-bit unsigned value to configure the CTRL frame
+	 * retry threshold (0 disable, 31 max). */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_CTRL_RETRY = 13,
+	/* 8-bit unsigned value to configure the propagation delay for
+	 * 2G/5G band (0~63, units in us) */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_PROPAGATION_DELAY = 14,
+	/* Unsigned 32-bit value to configure the number of unicast TX fail
+	 * packet count. The peer is disconnected once this threshold is
+	 * reached. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_TX_FAIL_COUNT = 15,
+	/* Attribute used to set scan default IEs to the driver.
+	 *
+	 * These IEs can be used by scan operations that will be initiated by
+	 * the driver/firmware.
+	 *
+	 * For further scan requests coming to the driver, these IEs should be
+	 * merged with the IEs received along with scan request coming to the
+	 * driver. If a particular IE is present in the scan default IEs but not
+	 * present in the scan request, then that IE should be added to the IEs
+	 * sent in the Probe Request frames for that scan request. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES = 16,
+	/* Unsigned 32-bit attribute for generic commands */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_COMMAND = 17,
+	/* Unsigned 32-bit value attribute for generic commands */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_VALUE = 18,
+	/* Unsigned 32-bit data attribute for generic command response */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA = 19,
+	/* Unsigned 32-bit length attribute for
+	 * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_LENGTH = 20,
+	/* Unsigned 32-bit flags attribute for
+	 * QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_DATA */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_GENERIC_FLAGS = 21,
+	/* Unsigned 32-bit, defining the access policy.
+	 * See enum qca_access_policy. Used with
+	 * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY = 22,
+	/* Sets the list of full set of IEs for which a specific access policy
+	 * has to be applied. Used along with
+	 * QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY to control the access.
+	 * Zero length payload can be used to clear this access constraint. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ACCESS_POLICY_IE_LIST = 23,
+	/* Unsigned 32-bit, specifies the interface index (netdev) for which the
+	 * corresponding configurations are applied. If the interface index is
+	 * not specified, the configurations are attributed to the respective
+	 * wiphy. */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_IFINDEX = 24,
+	/* 8-bit unsigned value to trigger QPower: 1-Enable, 0-Disable */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_QPOWER = 25,
+	/* 8-bit unsigned value to configure the driver and below layers to
+	 * ignore the assoc disallowed set by APs while connecting
+	 * 1-Ignore, 0-Don't ignore */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_IGNORE_ASSOC_DISALLOWED = 26,
+	/* 32-bit unsigned value to trigger antenna diversity features:
+	 * 1-Enable, 0-Disable */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_ENA = 27,
+	/* 32-bit unsigned value to configure specific chain antenna */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_CHAIN = 28,
+	/* 32-bit unsigned value to trigger cycle selftest
+	 * 1-Enable, 0-Disable */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST = 29,
+	/* 32-bit unsigned to configure the cycle time of selftest
+	 * the unit is micro-second */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_ANT_DIV_SELFTEST_INTVL = 30,
+	/* 32-bit unsigned value to set reorder timeout for AC_VO */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VOICE = 31,
+	/* 32-bit unsigned value to set reorder timeout for AC_VI */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_VIDEO = 32,
+	/* 32-bit unsigned value to set reorder timeout for AC_BE */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BESTEFFORT = 33,
+	/* 32-bit unsigned value to set reorder timeout for AC_BK */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_REORDER_TIMEOUT_BACKGROUND = 34,
+	/* 6-byte MAC address to point out the specific peer */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_PEER_MAC = 35,
+	/* 32-bit unsigned value to set window size for specific peer */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_RX_BLOCKSIZE_WINLIMIT = 36,
+	/* 8-bit unsigned value to set the beacon miss threshold in 2.4 GHz */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_24 = 37,
+	/* 8-bit unsigned value to set the beacon miss threshold in 5 GHz */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_MISS_THRESHOLD_5 = 38,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sap_config - Parameters for AP configuration
+ */
+enum qca_wlan_vendor_attr_sap_config {
+	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_INVALID = 0,
+	/* 1 - reserved for QCA */
+	/* List of frequencies on which AP is expected to operate.
+	 * This is irrespective of ACS configuration. This list is a priority
+	 * based one and is looked for before the AP is created to ensure the
+	 * best concurrency sessions (avoid MCC and use DBS/SCC) co-exist in
+	 * the system.
+	 */
+	QCA_WLAN_VENDOR_ATTR_SAP_MANDATORY_FREQUENCY_LIST = 2,
+
+	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_SAP_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sap_conditional_chan_switch - Parameters for AP
+ *					conditional channel switch
+ */
+enum qca_wlan_vendor_attr_sap_conditional_chan_switch {
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0,
+	/* Priority based frequency list (an array of u32 values in host byte
+	 * order) */
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1,
+	/* Status of the conditional switch (u32).
+	 * 0: Success, Non-zero: Failure
+	 */
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS = 2,
+
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX =
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_gpio_attr - Parameters for GPIO configuration
+ */
+enum qca_wlan_gpio_attr {
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INVALID = 0,
+	/* Unsigned 32-bit attribute for GPIO command */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_COMMAND,
+	/* Unsigned 32-bit attribute for GPIO PIN number to configure */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PINNUM,
+	/* Unsigned 32-bit attribute for GPIO value to configure */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_VALUE,
+	/* Unsigned 32-bit attribute for GPIO pull type */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_PULL_TYPE,
+	/* Unsigned 32-bit attribute for GPIO interrupt mode */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_INTR_MODE,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST,
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_MAX =
+	QCA_WLAN_VENDOR_ATTR_GPIO_PARAM_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_hw_capability - Wi-Fi hardware capability
+ */
+enum qca_wlan_vendor_attr_get_hw_capability {
+	QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_INVALID,
+	/* Antenna isolation
+	 * An attribute used in the response.
+	 * The content of this attribute is encoded in a byte array. Each byte
+	 * value is an antenna isolation value. The array length is the number
+	 * of antennas.
+	 */
+	QCA_WLAN_VENDOR_ATTR_ANTENNA_ISOLATION,
+	/* Request HW capability
+	 * An attribute used in the request.
+	 * The content of this attribute is a u32 array for one or more of
+	 * hardware capabilities (attribute IDs) that are being requested. Each
+	 * u32 value has a value from this
+	 * enum qca_wlan_vendor_attr_get_hw_capability
+	 * identifying which capabilities are requested.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GET_HW_CAPABILITY,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_MAX =
+	QCA_WLAN_VENDOR_ATTR_HW_CAPABILITY_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ll_stats_ext - Attributes for MAC layer monitoring
+ *    offload which is an extension for LL_STATS.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD: Monitoring period. Unit in ms.
+ *    If MAC counters do not exceed the threshold, FW will report monitored
+ *    link layer counters periodically as this setting. The first report is
+ *    always triggered by this timer.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD: It is a percentage (1-99).
+ *    For each MAC layer counter, FW holds two copies. One is the current value.
+ *    The other is the last report. Once a current counter's increment is larger
+ *    than the threshold, FW will indicate that counter to host even if the
+ *    monitoring timer does not expire.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG: Peer STA power state change
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID: TID of MSDU
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU: Count of MSDU with the same
+ *    failure code.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS: TX failure code
+ *    1: TX packet discarded
+ *    2: No ACK
+ *    3: Postpone
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS: peer MAC address
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE: Peer STA current state
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL: Global threshold.
+ *    Threshold for all monitored parameters. If per counter dedicated threshold
+ *    is not enabled, this threshold will take effect.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE: Indicate what triggers this
+ *    event, PERORID_TIMEOUT == 1, THRESH_EXCEED == 0.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID: interface ID
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID: peer ID
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP: bitmap for TX counters
+ *    Bit0: TX counter unit in MSDU
+ *    Bit1: TX counter unit in MPDU
+ *    Bit2: TX counter unit in PPDU
+ *    Bit3: TX counter unit in byte
+ *    Bit4: Dropped MSDUs
+ *    Bit5: Dropped Bytes
+ *    Bit6: MPDU retry counter
+ *    Bit7: MPDU failure counter
+ *    Bit8: PPDU failure counter
+ *    Bit9: MPDU aggregation counter
+ *    Bit10: MCS counter for ACKed MPDUs
+ *    Bit11: MCS counter for Failed MPDUs
+ *    Bit12: TX Delay counter
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP: bitmap for RX counters
+ *    Bit0: MAC RX counter unit in MPDU
+ *    Bit1: MAC RX counter unit in byte
+ *    Bit2: PHY RX counter unit in PPDU
+ *    Bit3: PHY RX counter unit in byte
+ *    Bit4: Disorder counter
+ *    Bit5: Retry counter
+ *    Bit6: Duplication counter
+ *    Bit7: Discard counter
+ *    Bit8: MPDU aggregation size counter
+ *    Bit9: MCS counter
+ *    Bit10: Peer STA power state change (wake to sleep) counter
+ *    Bit11: Peer STA power save counter, total time in PS mode
+ *    Bit12: Probe request counter
+ *    Bit13: Other management frames counter
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP: bitmap for CCA
+ *    Bit0: Idle time
+ *    Bit1: TX time
+ *    Bit2: time RX in current bss
+ *    Bit3: Out of current bss time
+ *    Bit4: Wireless medium busy time
+ *    Bit5: RX in bad condition time
+ *    Bit6: TX in bad condition time
+ *    Bit7: time wlan card not available
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP: bitmap for signal
+ *    Bit0: Per channel SNR counter
+ *    Bit1: Per channel noise floor counter
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM: number of peers
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM: number of channels
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_AC_RX_NUM: number of RX stats
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS: per channel BSS CCA stats
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER: container for per PEER stats
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU: Number of total TX MSDUs
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU: Number of total TX MPDUs
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU: Number of total TX PPDUs
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES: bytes of TX data
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP: Number of dropped TX packets
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES: Bytes dropped
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY: waiting time without an ACK
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK: number of MPDU not-ACKed
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK: number of PPDU not-ACKed
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM:
+ *    aggregation stats buffer length
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM: length of mcs stats
+ *    buffer for ACKed MPDUs.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM: length of mcs stats
+ *    buffer for failed MPDUs.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE:
+ *    length of delay stats array.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR: TX aggregation stats
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS: MCS stats for ACKed MPDUs
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS: MCS stats for failed MPDUs
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY: tx delay stats
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU: MPDUs received
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES: bytes received
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU: PPDU received
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES: PPDU bytes received
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST: packets lost
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY: number of RX packets
+ *    flagged as retransmissions
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP: number of RX packets
+ *    flagged as duplicated
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD: number of RX
+ *    packets discarded
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM: length of RX aggregation
+ *    stats buffer.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM: length of RX mcs
+ *    stats buffer.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS: RX mcs stats buffer
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR: aggregation stats buffer
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES: times STAs go to sleep
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION: STAs' total sleep time
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ: number of probe
+ *    requests received
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT: number of other mgmt
+ *    frames received
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME: Percentage of idle time
+ *    there is no TX, nor RX, nor interference.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME: percentage of time
+ *    transmitting packets.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME: percentage of time
+ *    for receiving.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY: percentage of time
+ *    interference detected.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD: percentage of time
+ *    receiving packets with errors.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD: percentage of time
+ *    TX no-ACK.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL: percentage of time
+ *    the chip is unable to work in normal conditions.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME: percentage of time
+ *    receiving packets in current BSS.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME: percentage of time
+ *    receiving packets not in current BSS.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM: number of antennas
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL:
+ *    This is a container for per antenna signal stats.
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR: per antenna SNR value
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF: per antenna NF value
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON: RSSI of beacon
+ * @QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON: SNR of beacon
+ */
+enum qca_wlan_vendor_attr_ll_stats_ext {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_INVALID = 0,
+
+	/* Attributes for configurations */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_PERIOD,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CFG_THRESHOLD,
+
+	/* Peer STA power state change */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_CHG,
+
+	/* TX failure event */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TID,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NUM_MSDU,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_STATUS,
+
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_STATE,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_MAC_ADDRESS,
+
+	/* MAC counters */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_GLOBAL,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_EVENT_MODE,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_ID,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ID,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BITMAP,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BITMAP,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS_BITMAP,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_SIGNAL_BITMAP,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CHANNEL_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_CCA_BSS,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER,
+
+	/* Sub-attributes for PEER_AC_TX */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MSDU,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_MPDU,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_PPDU,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BYTES,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DROP_BYTES,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_RETRY,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_ACK,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_NO_BACK,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_AGGR,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_SUCC_MCS,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_FAIL_MCS,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_DELAY_ARRAY_SIZE,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_DELAY,
+
+	/* Sub-attributes for PEER_AC_RX */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_BYTES,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PPDU_BYTES,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_LOST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_RETRY,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DUP,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MPDU_DISCARD,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MCS,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_AGGR,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_TIMES,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_PS_DURATION,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_PROBE_REQ,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_MGMT,
+
+	/* Sub-attributes for CCA_BSS */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IDLE_TIME,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_TIME,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_TIME,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BUSY,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_RX_BAD,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_TX_BAD,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_NO_AVAIL,
+
+	/* sub-attribute for BSS_RX_TIME */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IN_BSS_TIME,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_OUT_BSS_TIME,
+
+	/* Sub-attributes for PEER_SIGNAL */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_ANT_NUM,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_PEER_SIGNAL,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_SNR,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_ANT_NF,
+
+	/* Sub-attributes for IFACE_BSS */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_RSSI_BEACON,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_IFACE_SNR_BEACON,
+
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_MAX =
+		QCA_WLAN_VENDOR_ATTR_LL_STATS_EXT_LAST - 1
+};
+
+/* Attributes for FTM commands and events */
+
+/**
+ * enum qca_wlan_vendor_attr_loc_capa - Indoor location capabilities
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS: Various flags. See
+ *	enum qca_wlan_vendor_attr_loc_capa_flags.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS: Maximum number
+ *	of measurement sessions that can run concurrently.
+ *	Default is one session (no session concurrency).
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS: The total number of unique
+ *	peers that are supported in running sessions. For example,
+ *	if the value is 8 and maximum number of sessions is 2, you can
+ *	have one session with 8 unique peers, or 2 sessions with 4 unique
+ *	peers each, and so on.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP: Maximum number
+ *	of bursts per peer, as an exponent (2^value). Default is 0,
+ *	meaning no multi-burst support.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST: Maximum number
+ *	of measurement exchanges allowed in a single burst.
+ * @QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES: Supported AOA measurement
+ *	types. A bit mask (unsigned 32 bit value), each bit corresponds
+ *	to an AOA type as defined by enum qca_vendor_attr_aoa_type.
+ */
+enum qca_wlan_vendor_attr_loc_capa {
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_INVALID,
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAGS,
+	QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_SESSIONS,
+	QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_PEERS,
+	QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_NUM_BURSTS_EXP,
+	QCA_WLAN_VENDOR_ATTR_FTM_CAPA_MAX_MEAS_PER_BURST,
+	QCA_WLAN_VENDOR_ATTR_AOA_CAPA_SUPPORTED_TYPES,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_MAX =
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_loc_capa_flags: Indoor location capability flags
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER: Set if driver
+ *	can be configured as an FTM responder (for example, an AP that
+ *	services FTM requests). QCA_NL80211_VENDOR_SUBCMD_FTM_CFG_RESPONDER
+ *	will be supported if set.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR: Set if driver
+ *	can run FTM sessions. QCA_NL80211_VENDOR_SUBCMD_FTM_START_SESSION
+ *	will be supported if set.
+* @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP: Set if FTM responder
+ *	supports immediate (ASAP) response.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA: Set if driver supports standalone
+ *	AOA measurement using QCA_NL80211_VENDOR_SUBCMD_AOA_MEAS.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM: Set if driver supports
+ *	requesting AOA measurements as part of an FTM session.
+ */
+enum qca_wlan_vendor_attr_loc_capa_flags {
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_RESPONDER = 1 << 0,
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_FTM_INITIATOR = 1 << 1,
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_ASAP = 1 << 2,
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA = 1 << 3,
+	QCA_WLAN_VENDOR_ATTR_LOC_CAPA_FLAG_AOA_IN_FTM = 1 << 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_info: Information about
+ *	a single peer in a measurement session.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR: The MAC address of the peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS: Various flags related
+ *	to measurement. See enum qca_wlan_vendor_attr_ftm_peer_meas_flags.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS: Nested attribute of
+ *	FTM measurement parameters, as specified by IEEE P802.11-REVmc/D7.0
+ *	9.4.2.167. See enum qca_wlan_vendor_attr_ftm_meas_param for
+ *	list of supported attributes.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID: Initial token ID for
+ *	secure measurement.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA
+ *	measurement every <value> bursts. If 0 or not specified,
+ *	AOA measurements will be disabled for this peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where
+ *	the measurement frames are exchanged. Optional; if not
+ *	specified, try to locate the peer in the kernel scan
+ *	results cache and use frequency from there.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_info {
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX =
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_meas_flags: Measurement request flags,
+ *	per-peer
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP: If set, request
+ *	immediate (ASAP) response from peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI: If set, request
+ *	LCI report from peer. The LCI report includes the absolute
+ *	location of the peer in "official" coordinates (similar to GPS).
+ *	See IEEE P802.11-REVmc/D7.0, 11.24.6.7 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR: If set, request
+ *	Location civic report from peer. The LCR includes the location
+ *	of the peer in free-form format. See IEEE P802.11-REVmc/D7.0,
+ *	11.24.6.7 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE: If set,
+ *	request a secure measurement.
+ *	QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID must also be provided.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_meas_flags {
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_ASAP	= 1 << 0,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCI	= 1 << 1,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_LCR	= 1 << 2,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE	= 1 << 3,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_meas_param: Measurement parameters
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST: Number of measurements
+ *	to perform in a single burst.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP: Number of bursts to
+ *	perform, specified as an exponent (2^value).
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION: Duration of burst
+ *	instance, as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD: Time between bursts,
+ *	as specified in IEEE P802.11-REVmc/D7.0, 9.4.2.167. Must
+ *	be larger than QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION.
+ */
+enum qca_wlan_vendor_attr_ftm_meas_param {
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_INVALID,
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MEAS_PER_BURST,
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_NUM_BURSTS_EXP,
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_DURATION,
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_MAX =
+	QCA_WLAN_VENDOR_ATTR_FTM_PARAM_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_result: Per-peer results
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR: MAC address of the reported
+ *	 peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS: Status of measurement
+ *	request for this peer.
+ *	See enum qca_wlan_vendor_attr_ftm_peer_result_status.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS: Various flags related
+ *	to measurement results for this peer.
+ *	See enum qca_wlan_vendor_attr_ftm_peer_result_flags.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS: Specified when
+ *	request failed and peer requested not to send an additional request
+ *	for this number of seconds.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI: LCI report when received
+ *	from peer. In the format specified by IEEE P802.11-REVmc/D7.0,
+ *	9.4.2.22.10.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR: Location civic report when
+ *	received from peer. In the format specified by IEEE P802.11-REVmc/D7.0,
+ *	9.4.2.22.13.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS: Reported when peer
+ *	overridden some measurement request parameters. See
+ *	enum qca_wlan_vendor_attr_ftm_meas_param.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS: AOA measurement
+ *	for this peer. Same contents as @QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS: Array of measurement
+ *	results. Each entry is a nested attribute defined
+ *	by enum qca_wlan_vendor_attr_ftm_meas.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_result {
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_INVALID,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAC_ADDR,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAGS,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCI,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_LCR,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAMS,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AOA_MEAS,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MAX =
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_result_status
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK: Request sent ok and results
+ *	will be provided. Peer may have overridden some measurement parameters,
+ *	in which case overridden parameters will be report by
+ *	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_MEAS_PARAM attribute.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE: Peer is incapable
+ *	of performing the measurement request. No more results will be sent
+ *	for this peer in this session.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED: Peer reported request
+ *	failed, and requested not to send an additional request for number
+ *	of seconds specified by QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_VALUE_SECONDS
+ *	attribute.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID: Request validation
+ *	failed. Request was not sent over the air.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_result_status {
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_OK,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INCAPABLE,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_FAILED,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_STATUS_INVALID,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_peer_result_flags: Various flags
+ *  for measurement result, per-peer
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE: If set,
+ *	measurement completed for this peer. No more results will be reported
+ *	for this peer in this session.
+ */
+enum qca_wlan_vendor_attr_ftm_peer_result_flags {
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_RES_FLAG_DONE = 1 << 0,
+};
+
+/**
+ * enum qca_vendor_attr_loc_session_status: Session completion status code
+ *
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK: Session completed
+ *	successfully.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED: Session aborted
+ *	by request.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID: Session request
+ *	was invalid and was not started.
+ * @QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED: Session had an error
+ *	and did not complete normally (for example out of resources).
+ */
+enum qca_vendor_attr_loc_session_status {
+	QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_OK,
+	QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_ABORTED,
+	QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_INVALID,
+	QCA_WLAN_VENDOR_ATTR_LOC_SESSION_STATUS_FAILED,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_ftm_meas: Single measurement data
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1: Time of departure (TOD) of FTM packet as
+ *	recorded by responder, in picoseconds.
+ *	See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2: Time of arrival (TOA) of FTM packet at
+ *	initiator, in picoseconds.
+ *	See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3: TOD of ACK packet as recorded by
+ *	initiator, in picoseconds.
+ *	See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4: TOA of ACK packet at
+ *	responder, in picoseconds.
+ *	See IEEE P802.11-REVmc/D7.0, 11.24.6.4 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI: RSSI (signal level) as recorded
+ *	during this measurement exchange. Optional and will be provided if
+ *	the hardware can measure it.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR: TOD error reported by
+ *	responder. Not always provided.
+ *	See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR: TOA error reported by
+ *	responder. Not always provided.
+ *	See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR: TOD error measured by
+ *	initiator. Not always provided.
+ *	See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR: TOA error measured by
+ *	initiator. Not always provided.
+ *	See IEEE P802.11-REVmc/D7.0, 9.6.8.33 for more information.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD: Dummy attribute for padding.
+ */
+enum qca_wlan_vendor_attr_ftm_meas {
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INVALID,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T1,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T2,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T3,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_T4,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_RSSI,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOD_ERR,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_TOA_ERR,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOD_ERR,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_INITIATOR_TOA_ERR,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_PAD,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_MAX =
+	QCA_WLAN_VENDOR_ATTR_FTM_MEAS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_aoa_type - AOA measurement type
+ *
+ * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE: Phase of the strongest
+ *	CIR (channel impulse response) path for each antenna.
+ * @QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: Phase and amplitude
+ *	of the strongest CIR path for each antenna.
+ */
+enum qca_wlan_vendor_attr_aoa_type {
+	QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE,
+	QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP,
+	QCA_WLAN_VENDOR_ATTR_AOA_TYPE_MAX
+};
+
+/**
+ * enum qca_wlan_vendor_attr_encryption_test - Attributes to
+ * validate encryption engine
+ *
+ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION: Flag attribute.
+ *	This will be included if the request is for decryption; if not included,
+ *	the request is treated as a request for encryption by default.
+ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER: Unsigned 32-bit value
+ *	indicating the key cipher suite. Takes same values as
+ *	NL80211_ATTR_KEY_CIPHER.
+ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID: Unsigned 8-bit value
+ *	Key Id to be used for encryption
+ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK: Array of 8-bit values.
+ *	Key (TK) to be used for encryption/decryption
+ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN: Array of 8-bit values.
+ *	Packet number to be specified for encryption/decryption
+ *	6 bytes for TKIP/CCMP/GCMP.
+ * @QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA: Array of 8-bit values
+ *	representing the 802.11 packet (header + payload + FCS) that
+ *	needs to be encrypted/decrypted.
+ *	Encrypted/decrypted response from the driver will also be sent
+ *	to userspace with the same attribute.
+ */
+enum qca_wlan_vendor_attr_encryption_test {
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_NEEDS_DECRYPTION,
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_CIPHER,
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_KEYID,
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_TK,
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_PN,
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_DATA,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_MAX =
+	QCA_WLAN_VENDOR_ATTR_ENCRYPTION_TEST_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dmg_rf_sector_type - Type of
+ * sector for DMG RF sector operations.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX: RX sector
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX: TX sector
+ */
+enum qca_wlan_vendor_attr_dmg_rf_sector_type {
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_RX,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_TX,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_TYPE_MAX
+};
+
+/**
+ * enum qca_wlan_vendor_attr_dmg_rf_sector_cfg - Attributes for
+ * DMG RF sector configuration for a single RF module.
+ * The values are defined in a compact way which closely matches
+ * the way it is stored in HW registers.
+ * The configuration provides values for 32 antennas and 8 distribution
+ * amplifiers, and together describes the characteristics of the RF
+ * sector - such as a beam in some direction with some gain.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX: Index
+ *	of RF module for this configuration.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0: Bit 0 of edge
+ *	amplifier gain index. Unsigned 32 bit number containing
+ *	bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1: Bit 1 of edge
+ *	amplifier gain index. Unsigned 32 bit number containing
+ *	bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2: Bit 2 of edge
+ *	amplifier gain index. Unsigned 32 bit number containing
+ *	bits for all 32 antennas.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI: Phase values
+ *	for first 16 antennas, 2 bits per antenna.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO: Phase values
+ *	for last 16 antennas, 2 bits per antenna.
+ * @QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16: Contains
+ *	DTYPE values (3 bits) for each distribution amplifier, followed
+ *	by X16 switch bits for each distribution amplifier. There are
+ *	total of 8 distribution amplifiers.
+ */
+enum qca_wlan_vendor_attr_dmg_rf_sector_cfg {
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MODULE_INDEX = 1,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE0 = 2,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE1 = 3,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_ETYPE2 = 4,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_HI = 5,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_PSH_LO = 6,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_DTYPE_X16 = 7,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_MAX =
+	QCA_WLAN_VENDOR_ATTR_DMG_RF_SECTOR_CFG_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_attr_ll_stats_set {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_INVALID = 0,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_MPDU_SIZE_THRESHOLD = 1,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_CONFIG_AGGRESSIVE_STATS_GATHERING = 2,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_SET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_clr {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_INVALID = 0,
+	/* Unsigned 32bit bitmap for clearing statistics
+	 * All radio statistics                     0x00000001
+	 * cca_busy_time (within radio statistics)  0x00000002
+	 * All channel stats (within radio statistics) 0x00000004
+	 * All scan statistics (within radio statistics) 0x00000008
+	 * All interface statistics                     0x00000010
+	 * All tx rate statistics (within interface statistics) 0x00000020
+	 * All ac statistics (with in interface statistics) 0x00000040
+	 * All contention (min, max, avg) statistics (within ac statisctics)
+	 * 0x00000080.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_REQ_MASK = 1,
+	/* Unsigned 8 bit value: Request to stop statistics collection */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_REQ = 2,
+
+	/* Unsigned 32 bit bitmap: Response from the driver
+	 * for the cleared statistics
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_RSP_MASK = 3,
+	/* Unsigned 8 bit value: Response from driver/firmware
+	 * for the stop request
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_CONFIG_STOP_RSP = 4,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CLR_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_get {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_INVALID = 0,
+	/* Unsigned 32 bit value provided by the caller issuing the GET stats
+	 * command. When reporting the stats results, the driver uses the same
+	 * value to indicate which GET request the results correspond to.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_ID = 1,
+	/* Unsigned 32 bit value - bit mask to identify what statistics are
+	 * requested for retrieval.
+	 * Radio Statistics 0x00000001
+	 * Interface Statistics 0x00000020
+	 * All Peer Statistics 0x00000040
+	 * Peer Statistics     0x00000080
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_CONFIG_REQ_MASK = 2,
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_GET_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_results {
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_INVALID = 0,
+	/* Unsigned 32bit value. Used by the driver; must match the request id
+	 * provided with the QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET command.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_REQ_ID = 1,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_BEACON_RX = 2,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_RX = 3,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_RX = 4,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_MGMT_ACTION_TX = 5,
+	/* Signed 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_MGMT = 6,
+	/* Signed 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_DATA = 7,
+	/* Signed 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RSSI_ACK = 8,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_* are
+	 * nested within the interface stats.
+	 */
+
+	/* Interface mode, e.g., STA, SOFTAP, IBSS, etc.
+	 * Type = enum wifi_interface_mode.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MODE = 9,
+	/* Interface MAC address. An array of 6 Unsigned int8 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_MAC_ADDR = 10,
+	/* Type = enum wifi_connection_state, e.g., DISCONNECTED,
+	 * AUTHENTICATING, etc. valid for STA, CLI only.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_STATE = 11,
+	/* Type = enum wifi_roam_state. Roaming state, e.g., IDLE or ACTIVE
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_ROAMING = 12,
+	/* Unsigned 32 bit value. WIFI_CAPABILITY_XXX */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_CAPABILITIES = 13,
+	/* NULL terminated SSID. An array of 33 Unsigned 8bit values */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_SSID = 14,
+	/* BSSID. An array of 6 unsigned 8 bit values */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_BSSID = 15,
+	/* Country string advertised by AP. An array of 3 unsigned 8 bit
+	 * values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_AP_COUNTRY_STR = 16,
+	/* Country string for this association. An array of 3 unsigned 8 bit
+	 * values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_COUNTRY_STR = 17,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_* could
+	 * be nested within the interface stats.
+	 */
+
+	/* Type = enum wifi_traffic_ac, e.g., V0, VI, BE and BK */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_AC = 18,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MPDU = 19,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MPDU = 20,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_MCAST = 21,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_MCAST = 22,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RX_AMPDU = 23,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_TX_AMPDU = 24,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_MPDU_LOST = 25,
+	/* Unsigned int 32 value corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES = 26,
+	/* Unsigned int 32 value corresponding to respective AC  */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_SHORT = 27,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_RETRIES_LONG = 28,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MIN = 29,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_MAX = 30,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_TIME_AVG = 31,
+	/* Unsigned int 32 values corresponding to respective AC */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_CONTENTION_NUM_SAMPLES = 32,
+	/* Unsigned 32 bit value. Number of peers */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_NUM_PEERS = 33,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_* are
+	 * nested within the interface stats.
+	 */
+
+	/* Type = enum wifi_peer_type. Peer type, e.g., STA, AP, P2P GO etc. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_TYPE = 34,
+	/* MAC addr corresponding to respective peer. An array of 6 unsigned
+	 * 8 bit values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_MAC_ADDRESS = 35,
+	/* Unsigned int 32 bit value representing capabilities corresponding
+	 * to respective peer.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_CAPABILITIES = 36,
+	/* Unsigned 32 bit value. Number of rates */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_NUM_RATES = 37,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
+	 * are nested within the rate stat.
+	 */
+
+	/* Wi-Fi Rate - separate attributes defined for individual fields */
+
+	/* Unsigned int 8 bit value; 0: OFDM, 1:CCK, 2:HT 3:VHT 4..7 reserved */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_PREAMBLE = 38,
+	/* Unsigned int 8 bit value; 0:1x1, 1:2x2, 3:3x3, 4:4x4 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_NSS = 39,
+	/* Unsigned int 8 bit value; 0:20 MHz, 1:40 MHz, 2:80 MHz, 3:160 MHz */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BW = 40,
+	/* Unsigned int 8 bit value; OFDM/CCK rate code would be as per IEEE Std
+	 * in the units of 0.5 Mbps HT/VHT it would be MCS index */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MCS_INDEX = 41,
+
+	/* Unsigned 32 bit value. Bit rate in units of 100 kbps */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_BIT_RATE = 42,
+
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_STAT_* could be
+	 * nested within the peer info stats.
+	 */
+
+	/* Unsigned int 32 bit value. Number of successfully transmitted data
+	 * packets, i.e., with ACK received corresponding to the respective
+	 * rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_TX_MPDU = 43,
+	/* Unsigned int 32 bit value. Number of received data packets
+	 * corresponding to the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RX_MPDU = 44,
+	/* Unsigned int 32 bit value. Number of data packet losses, i.e., no ACK
+	 * received corresponding to the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_MPDU_LOST = 45,
+	/* Unsigned int 32 bit value. Total number of data packet retries for
+	 * the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES = 46,
+	/* Unsigned int 32 bit value. Total number of short data packet retries
+	 * for the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_SHORT = 47,
+	/* Unsigned int 32 bit value. Total number of long data packet retries
+	 * for the respective rate.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_RETRIES_LONG = 48,
+
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ID = 49,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake
+	 * accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME = 50,
+	/* Unsigned 32 bit value. Total number of msecs the radio is
+	 * transmitting accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME = 51,
+	/* Unsigned 32 bit value. Total number of msecs the radio is in active
+	 * receive accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_RX_TIME = 52,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to all scan accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_SCAN = 53,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to NAN accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_NBD = 54,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to GSCAN accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_GSCAN = 55,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to roam scan accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_ROAM_SCAN = 56,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to PNO scan accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_PNO_SCAN = 57,
+	/* Unsigned 32 bit value. Total number of msecs the radio is awake due
+	 * to Hotspot 2.0 scans and GAS exchange accruing over time.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_ON_TIME_HS20 = 58,
+	/* Unsigned 32 bit value. Number of channels. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_CHANNELS = 59,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_* could
+	 * be nested within the channel stats.
+	 */
+
+	/* Type = enum wifi_channel_width. Channel width, e.g., 20, 40, 80 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_WIDTH = 60,
+	/* Unsigned 32 bit value. Primary 20 MHz channel. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ = 61,
+	/* Unsigned 32 bit value. Center frequency (MHz) first segment. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ0 = 62,
+	/* Unsigned 32 bit value. Center frequency (MHz) second segment. */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_CENTER_FREQ1 = 63,
+
+	/* Attributes of type QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_* could be
+	 * nested within the radio stats.
+	 */
+
+	/* Unsigned int 32 bit value representing total number of msecs the
+	 * radio is awake on that channel accruing over time, corresponding to
+	 * the respective channel.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_ON_TIME = 64,
+	/* Unsigned int 32 bit value representing total number of msecs the CCA
+	 * register is busy accruing over time corresponding to the respective
+	 * channel.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_CCA_BUSY_TIME = 65,
+
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_NUM_RADIOS = 66,
+
+	/* Signifies the nested list of channel attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_CHANNEL_INFO_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_CH_INFO = 67,
+
+	/* Signifies the nested list of peer info attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO = 68,
+
+	/* Signifies the nested list of rate info attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_RATE_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_PEER_INFO_RATE_INFO = 69,
+
+	/* Signifies the nested list of wmm info attributes
+	 * QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_AC_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_WMM_INFO = 70,
+
+	/* Unsigned 8 bit value. Used by the driver; if set to 1, it indicates
+	 * that more stats, e.g., peers or radio, are to follow in the next
+	 * QCA_NL80211_VENDOR_SUBCMD_LL_STATS_*_RESULTS event.
+	 * Otherwise, it is set to 0.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RESULTS_MORE_DATA = 71,
+
+	/* Unsigned 64 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_AVERAGE_TSF_OFFSET = 72,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_DETECTED = 73,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_AVG_NUM_FRAMES_LEAKED = 74,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_LEAKY_AP_GUARD_TIME = 75,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_TYPE = 76,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_NUM_TX_LEVELS = 77,
+
+	/* Number of msecs the radio spent in transmitting for each power level
+	 */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_RADIO_TX_TIME_PER_LEVEL = 78,
+
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_SUCC_CNT = 79,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_RTS_FAIL_CNT = 80,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_SUCC_CNT = 81,
+	/* Unsigned 32 bit value */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_PPDU_FAIL_CNT = 82,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
+	QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_ll_stats_type
+{
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_INVALID = 0,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_RADIO = 1,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_IFACE = 2,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_PEERS = 3,
+
+	/* keep last */
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST,
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_MAX =
+	QCA_NL80211_VENDOR_SUBCMD_LL_STATS_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_tdls_configuration - Attributes for
+ * TDLS configuration to the host driver.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE: Configure the TDLS trigger
+ *	mode in the host driver. enum qca_wlan_vendor_tdls_trigger_mode
+ *	represents the different TDLS trigger modes.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD: Duration (u32) within
+ *      which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD number
+ *      of packets shall meet the criteria for implicit TDLS setup.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD: Number (u32) of Tx/Rx packets
+ *      within a duration QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD
+ *      to initiate a TDLS setup.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD: Time (u32) to initiate
+ *      a TDLS Discovery to the peer.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT: Max number (u32) of
+ *      discovery attempts to know the TDLS capability of the peer. A peer is
+ *      marked as TDLS not capable if there is no response for all the attempts.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT: Represents a duration (u32)
+ *      within which QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD
+ *      number of TX / RX frames meet the criteria for TDLS teardown.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD: Minimum number (u32)
+ *      of Tx/Rx packets within a duration
+ *      QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT to tear down a TDLS link.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD: Threshold
+ *	corresponding to the RSSI of the peer below which a TDLS setup is
+ *	triggered.
+ * @QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD: Threshold
+ *	corresponding to the RSSI of the peer above which a TDLS teardown is
+ *	triggered.
+ */
+enum qca_wlan_vendor_attr_tdls_configuration {
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE = 1,
+
+	/* Attributes configuring the TDLS Implicit Trigger */
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_STATS_PERIOD = 2,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TX_THRESHOLD = 3,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_DISCOVERY_PERIOD = 4,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX_DISCOVERY_ATTEMPT = 5,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_TIMEOUT = 6,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IDLE_PACKET_THRESHOLD = 7,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_SETUP_RSSI_THRESHOLD = 8,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TEARDOWN_RSSI_THRESHOLD = 9,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_MAX =
+	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_tdls_trigger_mode: Represents the TDLS trigger mode in
+ *	the driver
+ *
+ * The following are the different values for
+ *	QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE.
+ *
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT: The trigger to initiate/teardown
+ *	the TDLS connection to a respective peer comes from the user space.
+ *	wpa_supplicant provides the commands TDLS_SETUP, TDLS_TEARDOWN,
+ *	TDLS_DISCOVER to do this.
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT: Host driver triggers this TDLS
+ *	setup/teardown to the eligible peer once the configured criteria
+ *	(such as TX/RX threshold, RSSI) is met. The attributes
+ *	in QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_IMPLICIT_PARAMS correspond to
+ *	the different configuration criteria for the TDLS trigger from the
+ *	host driver.
+ * @QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL: Enables the driver to trigger
+ *	the TDLS setup / teardown through the implicit mode only to the
+ *	configured MAC addresses (wpa_supplicant, with tdls_external_control=1,
+ *	configures the MAC address through TDLS_SETUP / TDLS_TEARDOWN commands).
+ *	External mode works on top of the implicit mode. Thus the host driver
+ *	is expected to configure in TDLS Implicit mode too to operate in
+ *	External mode.
+ *	Configuring External mode alone without	Implicit mode is invalid.
+ *
+ * All the above implementations work as expected only when the host driver
+ * advertises the capability WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP - representing
+ * that the TDLS message exchange is not internal to the host driver, but
+ * depends on wpa_supplicant to do the message exchange.
+ */
+enum qca_wlan_vendor_tdls_trigger_mode {
+	QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT = 1 << 0,
+	QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT = 1 << 1,
+	QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL = 1 << 2,
+};
+
+/**
+ * enum qca_vendor_attr_sar_limits_selections - Source of SAR power limits
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0: Select SAR profile #0
+ *	that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1: Select SAR profile #1
+ *	that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2: Select SAR profile #2
+ *	that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3: Select SAR profile #3
+ *	that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4: Select SAR profile #4
+ *	that is hard-coded in the Board Data File (BDF).
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE: Do not select any
+ *	source of SAR power limits, thereby disabling the SAR power
+ *	limit feature.
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER: Select the SAR power
+ *	limits configured by %QCA_NL80211_VENDOR_SUBCMD_SET_SAR.
+ *
+ * This enumerates the valid set of values that may be supplied for
+ * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT in an instance of
+ * the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor command.
+ */
+enum qca_vendor_attr_sar_limits_selections {
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF0 = 0,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF1 = 1,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF2 = 2,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF3 = 3,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_BDF4 = 4,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_NONE = 5,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT_USER = 6,
+};
+
+/**
+ * enum qca_vendor_attr_sar_limits_spec_modulations -
+ *	SAR limits specification modulation
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK -
+ *	CCK modulation
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM -
+ *	OFDM modulation
+ *
+ * This enumerates the valid set of values that may be supplied for
+ * attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION in an
+ * instance of attribute %QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC in an
+ * instance of the %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS vendor
+ * command.
+ */
+enum qca_vendor_attr_sar_limits_spec_modulations {
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_CCK = 0,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION_OFDM = 1,
+};
+
+/**
+ * enum qca_vendor_attr_sar_limits - Attributes for SAR power limits
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SELECT: Optional (u32) value to
+ *	select which SAR power limit table should be used. Valid
+ *	values are enumerated in enum
+ *	%qca_vendor_attr_sar_limits_selections. The existing SAR
+ *	power limit selection is unchanged if this attribute is not
+ *	present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS: Optional (u32) value
+ *	which specifies the number of SAR power limit specifications
+ *	which will follow.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC: Nested array of SAR power
+ *	limit specifications. The number of specifications is
+ *	specified by @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS. Each
+ *	specification contains a set of
+ *	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_* attributes. A
+ *	specification is uniquely identified by the attributes
+ *	%QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND,
+ *	%QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN, and
+ *	%QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION and always
+ *	contains as a payload the attribute
+ *	%QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND: Optional (u32) value to
+ *	indicate for which band this specification applies. Valid
+ *	values are enumerated in enum %nl80211_band (although not all
+ *	bands may be supported by a given device). If the attribute is
+ *	not supplied then the specification will be applied to all
+ *	supported bands.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN: Optional (u32) value
+ *	to indicate for which antenna chain this specification
+ *	applies, i.e. 1 for chain 1, 2 for chain 2, etc. If the
+ *	attribute is not supplied then the specification will be
+ *	applied to all chains.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION: Optional (u32)
+ *	value to indicate for which modulation scheme this
+ *	specification applies. Valid values are enumerated in enum
+ *	%qca_vendor_attr_sar_limits_spec_modulations. If the attribute
+ *	is not supplied then the specification will be applied to all
+ *	modulation schemes.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT: Required (u32)
+ *	value to specify the actual power limit value in units of 0.5
+ *	dBm (i.e., a value of 11 represents 5.5 dBm).
+ *
+ * These attributes are used with %QCA_NL80211_VENDOR_SUBCMD_SET_SAR_LIMITS.
+ */
+enum qca_vendor_attr_sar_limits {
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SAR_ENABLE = 1,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_NUM_SPECS = 2,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC = 3,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_BAND = 4,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_CHAIN = 5,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_MODULATION = 6,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_SPEC_POWER_LIMIT = 7,
+
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_MAX =
+		QCA_WLAN_VENDOR_ATTR_SAR_LIMITS_AFTER_LAST - 1
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_wifi_info: Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_INFO sub command.
+ */
+enum qca_wlan_vendor_attr_get_wifi_info {
+	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_DRIVER_VERSION = 1,
+	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_FIRMWARE_VERSION = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_MAX =
+	QCA_WLAN_VENDOR_ATTR_WIFI_INFO_GET_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_wifi_logger_start: Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_WIFI_LOGGER_START sub command.
+ */
+enum qca_wlan_vendor_attr_wifi_logger_start {
+	QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_RING_ID = 1,
+	QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_VERBOSE_LEVEL = 2,
+	QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_FLAGS = 3,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_GET_MAX =
+	QCA_WLAN_VENDOR_ATTR_WIFI_LOGGER_START_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_logger_results {
+	QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_INVALID = 0,
+
+	/* Unsigned 32-bit value; must match the request Id supplied by
+	 * Wi-Fi HAL in the corresponding subcmd NL msg.
+	 */
+	QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_REQUEST_ID = 1,
+
+	/* Unsigned 32-bit value; used to indicate the size of memory
+	 * dump to be allocated.
+	*/
+	QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MEMDUMP_SIZE = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_MAX =
+	QCA_WLAN_VENDOR_ATTR_LOGGER_RESULTS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_roaming_config_params {
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_INVALID = 0,
+
+	QCA_WLAN_VENDOR_ATTR_ROAMING_SUBCMD = 1,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_REQ_ID = 2,
+
+	/* Attributes for wifi_set_ssid_white_list */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_NUM_NETWORKS = 3,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID_LIST = 4,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_WHITE_LIST_SSID = 5,
+
+	/* Attributes for set_roam_params */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_THRESHOLD = 6,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_THRESHOLD = 7,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_BOOST_FACTOR = 8,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_PENALTY_FACTOR = 9,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_A_BAND_MAX_BOOST = 10,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_LAZY_ROAM_HISTERESYS = 11,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_ALERT_ROAM_RSSI_TRIGGER = 12,
+
+	/* Attribute for set_lazy_roam */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_ENABLE = 13,
+
+	/* Attribute for set_lazy_roam with preferences */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PREFS = 14,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_NUM_BSSID = 15,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_BSSID = 16,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_LAZY_ROAM_RSSI_MODIFIER = 17,
+
+	/* Attribute for set_blacklist bssid params */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST - 1,
+};
+
+/*
+ * enum qca_wlan_vendor_attr_roam_subcmd: Attributes for data used by
+ * QCA_NL80211_VENDOR_SUBCMD_ROAM sub command.
+ */
+enum qca_wlan_vendor_attr_roam_subcmd {
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SSID_WHITE_LIST = 1,
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_GSCAN_ROAM_PARAMS = 2,
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_LAZY_ROAM = 3,
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PREFS = 4,
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BSSID_PARAMS = 5,
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_SET_BLACKLIST_BSSID = 6,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_MAX =
+	QCA_WLAN_VENDOR_ATTR_ROAM_SUBCMD_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_gscan_config_params {
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_INVALID = 0,
+
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_REQUEST_ID = 1,
+
+	/* Attributes for data used by
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS sub command.
+	 */
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_WIFI_BAND
+	= 2,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_GET_VALID_CHANNELS_CONFIG_PARAM_MAX_CHANNELS
+	= 3,
+
+	/* Attributes for input params used by
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_START sub command.
+	 */
+
+	/* Unsigned 32-bit value; channel frequency */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CHANNEL = 4,
+	/* Unsigned 32-bit value; dwell time in ms. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_DWELL_TIME = 5,
+	/* Unsigned 8-bit value; 0: active; 1: passive; N/A for DFS */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_PASSIVE = 6,
+	/* Unsigned 8-bit value; channel class */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_CLASS = 7,
+
+	/* Unsigned 8-bit value; bucket index, 0 based */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_INDEX = 8,
+	/* Unsigned 8-bit value; band. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BAND = 9,
+	/* Unsigned 32-bit value; desired period, in ms. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_PERIOD = 10,
+	/* Unsigned 8-bit value; report events semantics. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_REPORT_EVENTS = 11,
+	/* Unsigned 32-bit value. Followed by a nested array of
+	 * GSCAN_CHANNEL_SPEC_* attributes.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS = 12,
+
+	/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC_* attributes.
+	 * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CHANNEL_SPEC = 13,
+
+	/* Unsigned 32-bit value; base timer period in ms. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_BASE_PERIOD = 14,
+	/* Unsigned 32-bit value; number of APs to store in each scan in the
+	 * BSSID/RSSI history buffer (keep the highest RSSI APs).
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_MAX_AP_PER_SCAN = 15,
+	/* Unsigned 8-bit value; in %, when scan buffer is this much full, wake
+	 * up AP.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_PERCENT
+	= 16,
+
+	/* Unsigned 8-bit value; number of scan bucket specs; followed by a
+	 * nested array of_GSCAN_BUCKET_SPEC_* attributes and values. The size
+	 * of the array is determined by NUM_BUCKETS.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS = 17,
+
+	/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_* attributes.
+	 * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_NUM_BUCKETS
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC = 18,
+
+	/* Unsigned 8-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_FLUSH
+	= 19,
+	/* Unsigned 32-bit value; maximum number of results to be returned. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_GET_CACHED_SCAN_RESULTS_CONFIG_PARAM_MAX
+	= 20,
+
+	/* An array of 6 x unsigned 8-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_BSSID = 21,
+	/* Signed 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_LOW = 22,
+	/* Signed 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_RSSI_HIGH = 23,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_CHANNEL = 24,
+
+	/* Number of hotlist APs as unsigned 32-bit value, followed by a nested
+	 * array of AP_THRESHOLD_PARAM attributes and values. The size of the
+	 * array is determined by NUM_AP.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_NUM_AP = 25,
+
+	/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM_* attributes.
+	 * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_NUM_CHANNEL_SPECS
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_AP_THRESHOLD_PARAM = 26,
+
+	/* Unsigned 32-bit value; number of samples for averaging RSSI. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_RSSI_SAMPLE_SIZE
+	= 27,
+	/* Unsigned 32-bit value; number of samples to confirm AP loss. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_LOST_AP_SAMPLE_SIZE
+	= 28,
+	/* Unsigned 32-bit value; number of APs breaching threshold. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_MIN_BREACHING = 29,
+	/* Unsigned 32-bit value; number of APs. Followed by an array of
+	 * AP_THRESHOLD_PARAM attributes. Size of the array is NUM_AP.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SIGNIFICANT_CHANGE_PARAMS_NUM_AP = 30,
+	/* Unsigned 32-bit value; number of samples to confirm AP loss. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BSSID_HOTLIST_PARAMS_LOST_AP_SAMPLE_SIZE
+	= 31,
+	/* Unsigned 32-bit value. If max_period is non zero or different than
+	 * period, then this bucket is an exponential backoff bucket.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_MAX_PERIOD = 32,
+	/* Unsigned 32-bit value. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_BASE = 33,
+	/* Unsigned 32-bit value. For exponential back off bucket, number of
+	 * scans to perform for a given period.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_BUCKET_SPEC_STEP_COUNT = 34,
+	/* Unsigned 8-bit value; in number of scans, wake up AP after these
+	 * many scans.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SCAN_CMD_PARAMS_REPORT_THRESHOLD_NUM_SCANS
+	= 35,
+
+	/* Attributes for data used by
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SSID_HOTLIST sub command.
+	 */
+	/* Unsigned 3-2bit value; number of samples to confirm SSID loss. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_LOST_SSID_SAMPLE_SIZE
+	= 36,
+	/* Number of hotlist SSIDs as unsigned 32-bit value, followed by a
+	 * nested array of SSID_THRESHOLD_PARAM_* attributes and values. The
+	 * size of the array is determined by NUM_SSID.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID = 37,
+	/* Array of QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_*
+	 * attributes.
+	 * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_HOTLIST_PARAMS_NUM_SSID
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM = 38,
+
+	/* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_SSID = 39,
+	/* Unsigned 8-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_BAND = 40,
+	/* Signed 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_LOW = 41,
+	/* Signed 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SSID_THRESHOLD_PARAM_RSSI_HIGH = 42,
+	/* Unsigned 32-bit value; a bitmask with additional gscan config flag.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CONFIGURATION_FLAGS = 43,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_MAX =
+	QCA_WLAN_VENDOR_ATTR_GSCAN_SUBCMD_CONFIG_PARAM_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_gscan_results {
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_INVALID = 0,
+
+	/* Unsigned 32-bit value; must match the request Id supplied by
+	 * Wi-Fi HAL in the corresponding subcmd NL msg.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_REQUEST_ID = 1,
+
+	/* Unsigned 32-bit value; used to indicate the status response from
+	 * firmware/driver for the vendor sub-command.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_STATUS = 2,
+
+	/* GSCAN Valid Channels attributes */
+	/* Unsigned 32bit value; followed by a nested array of CHANNELS. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_CHANNELS = 3,
+	/* An array of NUM_CHANNELS x unsigned 32-bit value integers
+	 * representing channel numbers.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CHANNELS = 4,
+
+	/* GSCAN Capabilities attributes */
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_CACHE_SIZE = 5,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_BUCKETS = 6,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_AP_CACHE_PER_SCAN
+	= 7,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_RSSI_SAMPLE_SIZE
+	= 8,
+	/* Signed 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SCAN_REPORTING_THRESHOLD
+	= 9,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_BSSIDS = 10,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_SIGNIFICANT_WIFI_CHANGE_APS
+	= 11,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_BSSID_HISTORY_ENTRIES
+	= 12,
+
+	/* GSCAN Attributes used with
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE sub-command.
+	 */
+
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE = 13,
+
+	/* GSCAN attributes used with
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT sub-command.
+	 */
+
+	/* An array of NUM_RESULTS_AVAILABLE x
+	 * QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST = 14,
+
+	/* Unsigned 64-bit value; age of sample at the time of retrieval */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_TIME_STAMP = 15,
+	/* 33 x unsigned 8-bit value; NULL terminated SSID */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_SSID = 16,
+	/* An array of 6 x unsigned 8-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BSSID = 17,
+	/* Unsigned 32-bit value; channel frequency in MHz */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CHANNEL = 18,
+	/* Signed 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RSSI = 19,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT = 20,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_RTT_SD = 21,
+	/* Unsigned 16-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_BEACON_PERIOD = 22,
+	/* Unsigned 16-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_CAPABILITY = 23,
+	/* Unsigned 32-bit value; size of the IE DATA blob */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_LENGTH = 24,
+	/* An array of IE_LENGTH x unsigned 8-bit value; blob of all the
+	 * information elements found in the beacon; this data should be a
+	 * packed list of wifi_information_element objects, one after the
+	 * other.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_IE_DATA = 25,
+
+	/* Unsigned 8-bit value; set by driver to indicate more scan results are
+	 * available.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_RESULT_MORE_DATA = 26,
+
+	/* GSCAN attributes for
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT sub-command.
+	 */
+	/* Unsigned 8-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_TYPE = 27,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SCAN_EVENT_STATUS = 28,
+
+	/* GSCAN attributes for
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND sub-command.
+	 */
+	/* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
+	 * to indicate number of results.
+	 * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
+	 * list of results.
+	 */
+
+	/* GSCAN attributes for
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE sub-command.
+	 */
+	/* An array of 6 x unsigned 8-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_BSSID = 29,
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_CHANNEL
+	= 30,
+	/* Unsigned 32-bit value. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_NUM_RSSI
+	= 31,
+	/* A nested array of signed 32-bit RSSI values. Size of the array is
+	 * determined by (NUM_RSSI of SIGNIFICANT_CHANGE_RESULT_NUM_RSSI.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_SIGNIFICANT_CHANGE_RESULT_RSSI_LIST
+	= 32,
+
+	/* GSCAN attributes used with
+	 * QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS sub-command.
+	 */
+	/* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
+	 * to indicate number of gscan cached results returned.
+	 * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST to indicate
+	 *  the list of gscan cached results.
+	 */
+
+	/* An array of NUM_RESULTS_AVAILABLE x
+	 * QCA_NL80211_VENDOR_ATTR_GSCAN_CACHED_RESULTS_*
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_LIST = 33,
+	/* Unsigned 32-bit value; a unique identifier for the scan unit. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_SCAN_ID = 34,
+	/* Unsigned 32-bit value; a bitmask w/additional information about scan.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_CACHED_RESULTS_FLAGS = 35,
+	/* Use attr QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE
+	 * to indicate number of wifi scan results/bssids retrieved by the scan.
+	 * Also, use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the
+	 * list of wifi scan results returned for each cached result block.
+	 */
+
+	/* GSCAN attributes for
+	 * QCA_NL80211_VENDOR_SUBCMD_PNO_NETWORK_FOUND sub-command.
+	 */
+	/* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE for
+	 * number of results.
+	 * Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
+	 * list of wifi scan results returned for each
+	 * wifi_passpoint_match_result block.
+	 * Array size: QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_NUM_RESULTS_AVAILABLE.
+	 */
+
+	/* GSCAN attributes for
+	 * QCA_NL80211_VENDOR_SUBCMD_PNO_PASSPOINT_NETWORK_FOUND sub-command.
+	 */
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES
+	= 36,
+	/* A nested array of
+	 * QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_*
+	 * attributes. Array size =
+	 * *_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_NETWORK_FOUND_NUM_MATCHES.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_RESULT_LIST = 37,
+
+	/* Unsigned 32-bit value; network block id for the matched network */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ID = 38,
+	/* Use QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_LIST to indicate the nested
+	 * list of wifi scan results returned for each
+	 * wifi_passpoint_match_result block.
+	 */
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP_LEN = 39,
+	/* An array size of PASSPOINT_MATCH_ANQP_LEN of unsigned 8-bit values;
+	 * ANQP data in the information_element format.
+	 */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_PNO_RESULTS_PASSPOINT_MATCH_ANQP = 40,
+
+	/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_HOTLIST_SSIDS = 41,
+	/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS = 42,
+	/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_EPNO_NETS_BY_SSID
+	= 43,
+	/* Unsigned 32-bit value; a GSCAN Capabilities attribute. */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_CAPABILITIES_MAX_NUM_WHITELISTED_SSID
+	= 44,
+
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_BUCKETS_SCANNED = 45,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_MAX =
+	QCA_WLAN_VENDOR_ATTR_GSCAN_RESULTS_AFTER_LAST - 1,
+};
+
+enum qca_wlan_vendor_attr_pno_config_params {
+	QCA_WLAN_VENDOR_ATTR_PNO_INVALID = 0,
+	/* Attributes for data used by
+	 * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_PASSPOINT_LIST sub command.
+	 */
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM = 1,
+	/* Array of nested QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_*
+	 * attributes. Array size =
+	 * QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NUM.
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_LIST_PARAM_NETWORK_ARRAY = 2,
+
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ID = 3,
+	/* An array of 256 x unsigned 8-bit value; NULL terminated UTF-8 encoded
+	 * realm, 0 if unspecified.
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_REALM = 4,
+	/* An array of 16 x unsigned 32-bit value; roaming consortium ids to
+	 * match, 0 if unspecified.
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_CNSRTM_ID = 5,
+	/* An array of 6 x unsigned 8-bit value; MCC/MNC combination, 0s if
+	 * unspecified.
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_PASSPOINT_NETWORK_PARAM_ROAM_PLMN = 6,
+
+	/* Attributes for data used by
+	 * QCA_NL80211_VENDOR_SUBCMD_PNO_SET_LIST sub command.
+	 */
+	/* Unsigned 32-bit value */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS = 7,
+	/* Array of nested
+	 * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_*
+	 * attributes. Array size =
+	 * QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_NUM_NETWORKS.
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORKS_LIST = 8,
+	/* An array of 33 x unsigned 8-bit value; NULL terminated SSID */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_SSID = 9,
+	/* Signed 8-bit value; threshold for considering this SSID as found,
+	 * required granularity for this threshold is 4 dBm to 8 dBm.
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_RSSI_THRESHOLD
+	= 10,
+	/* Unsigned 8-bit value; WIFI_PNO_FLAG_XXX */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_FLAGS = 11,
+	/* Unsigned 8-bit value; auth bit field for matching WPA IE */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_NETWORK_AUTH_BIT = 12,
+	/* Unsigned 8-bit to indicate ePNO type;
+	 * It takes values from qca_wlan_epno_type
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_TYPE = 13,
+
+	/* Nested attribute to send the channel list */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_CHANNEL_LIST = 14,
+
+	/* Unsigned 32-bit value; indicates the interval between PNO scan
+	 * cycles in msec.
+	 */
+	QCA_WLAN_VENDOR_ATTR_PNO_SET_LIST_PARAM_EPNO_SCAN_INTERVAL = 15,
+	QCA_WLAN_VENDOR_ATTR_EPNO_MIN5GHZ_RSSI = 16,
+	QCA_WLAN_VENDOR_ATTR_EPNO_MIN24GHZ_RSSI = 17,
+	QCA_WLAN_VENDOR_ATTR_EPNO_INITIAL_SCORE_MAX = 18,
+	QCA_WLAN_VENDOR_ATTR_EPNO_CURRENT_CONNECTION_BONUS = 19,
+	QCA_WLAN_VENDOR_ATTR_EPNO_SAME_NETWORK_BONUS = 20,
+	QCA_WLAN_VENDOR_ATTR_EPNO_SECURE_BONUS = 21,
+	QCA_WLAN_VENDOR_ATTR_EPNO_BAND5GHZ_BONUS = 22,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_PNO_MAX =
+	QCA_WLAN_VENDOR_ATTR_PNO_AFTER_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_acs_select_reason: This represents the different reasons why
+ * the ACS has to be triggered. These values are used by
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON and
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON
+ */
+enum qca_wlan_vendor_acs_select_reason {
+	/* Represents the reason that the ACS triggered during the AP start */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_INIT,
+	/* Represents the reason that DFS found with the current channel */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_DFS,
+	/* Represents the reason that LTE co-exist in the current band. */
+	QCA_WLAN_VENDOR_ACS_SELECT_REASON_LTE_COEX,
+};
+
+/**
+ * qca_wlan_vendor_channel_prop_flags: This represent the flags for a channel.
+ * This is used by QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS.
+ */
+enum qca_wlan_vendor_channel_prop_flags {
+	/* Bits 0, 1, 2, and 3 are reserved */
+
+	/* Turbo channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_TURBO         = 1 << 4,
+	/* CCK channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_CCK           = 1 << 5,
+	/* OFDM channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_OFDM          = 1 << 6,
+	/* 2.4 GHz spectrum channel. */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_2GHZ          = 1 << 7,
+	/* 5 GHz spectrum channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_5GHZ          = 1 << 8,
+	/* Only passive scan allowed */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_PASSIVE       = 1 << 9,
+	/* Dynamic CCK-OFDM channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_DYN           = 1 << 10,
+	/* GFSK channel (FHSS PHY) */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_GFSK          = 1 << 11,
+	/* Radar found on channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_RADAR         = 1 << 12,
+	/* 11a static turbo channel only */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_STURBO        = 1 << 13,
+	/* Half rate channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HALF          = 1 << 14,
+	/* Quarter rate channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_QUARTER       = 1 << 15,
+	/* HT 20 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT20          = 1 << 16,
+	/* HT 40 with extension channel above */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40PLUS      = 1 << 17,
+	/* HT 40 with extension channel below */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40MINUS     = 1 << 18,
+	/* HT 40 intolerant */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOL     = 1 << 19,
+	/* VHT 20 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT20         = 1 << 20,
+	/* VHT 40 with extension channel above */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40PLUS     = 1 << 21,
+	/* VHT 40 with extension channel below */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT40MINUS    = 1 << 22,
+	/* VHT 80 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80         = 1 << 23,
+	/* HT 40 intolerant mark bit for ACS use */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HT40INTOLMARK = 1 << 24,
+	/* Channel temporarily blocked due to noise */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_BLOCKED       = 1 << 25,
+	/* VHT 160 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT160        = 1 << 26,
+	/* VHT 80+80 channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_VHT80_80      = 1 << 27,
+};
+
+/**
+ * qca_wlan_vendor_channel_prop_flags_ext: This represent the extended flags for
+ * each channel. This is used by
+ * QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT.
+ */
+enum qca_wlan_vendor_channel_prop_flags_ext {
+	/* Radar found on channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_RADAR_FOUND     = 1 << 0,
+	/* DFS required on channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS             = 1 << 1,
+	/* DFS required on channel for 2nd band of 80+80 */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CFREQ2      = 1 << 2,
+	/* If channel has been checked for DFS */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DFS_CLEAR       = 1 << 3,
+	/* Excluded in 802.11d */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_11D_EXCLUDED    = 1 << 4,
+	/* Channel Switch Announcement received on this channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CSA_RECEIVED    = 1 << 5,
+	/* Ad-hoc is not allowed */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_ADHOC  = 1 << 6,
+	/* Station only channel */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_DISALLOW_HOSTAP = 1 << 7,
+	/* DFS radar history for slave device (STA mode) */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_HISTORY_RADAR   = 1 << 8,
+	/* DFS CAC valid for slave device (STA mode) */
+	QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_EXT_CAC_VALID       = 1 << 9,
+};
+
+/**
+ * qca_wlan_vendor_external_acs_event_chan_info_attr: Represents per channel
+ * information. These attributes are sent as part of
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO. Each set of the following
+ * attributes correspond to a single channel.
+ */
+enum qca_wlan_vendor_external_acs_event_chan_info_attr {
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_INVALID = 0,
+
+	/* A bitmask (u32) with flags specified in
+	 * enum qca_wlan_vendor_channel_prop_flags.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAGS = 1,
+	/* A bitmask (u32) with flags specified in
+	 * enum qca_wlan_vendor_channel_prop_flags_ext.
+	 */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FLAG_EXT = 2,
+	/* frequency in MHz (u32) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_FREQ = 3,
+	/* maximum regulatory transmission power (u32) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_REG_POWER = 4,
+	/* maximum transmission power (u32) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX_POWER = 5,
+	/* minimum transmission power (u32) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MIN_POWER = 6,
+	/* regulatory class id (u8) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_REG_CLASS_ID = 7,
+	/* maximum antenna gain in (u8) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_ANTENNA_GAIN = 8,
+	/* VHT segment 0 (u8) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_0 = 9,
+	/* VHT segment 1 (u8) */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_VHT_SEG_1 = 10,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST,
+	QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_MAX =
+		QCA_WLAN_VENDOR_EXTERNAL_ACS_EVENT_CHAN_INFO_ATTR_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_pcl: Represents attributes for
+ * preferred channel list (PCL). These attributes are sent as part of
+ * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL.
+ */
+enum qca_wlan_vendor_attr_pcl {
+	QCA_WLAN_VENDOR_ATTR_PCL_INVALID = 0,
+
+	/* Channel number (u8) */
+	QCA_WLAN_VENDOR_ATTR_PCL_CHANNEL = 1,
+	/* Channel weightage (u8) */
+	QCA_WLAN_VENDOR_ATTR_PCL_WEIGHT = 2,
+};
+
+/**
+ * qca_wlan_vendor_attr_external_acs_event: Attribute to vendor sub-command
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This attribute will be sent by
+ * host driver.
+ */
+enum qca_wlan_vendor_attr_external_acs_event {
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_INVALID = 0,
+
+	/* This reason (u8) refers to enum qca_wlan_vendor_acs_select_reason.
+	 * This helps ACS module to understand why ACS needs to be started.
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_REASON = 1,
+	/* Flag attribute to indicate if driver supports spectral scanning */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_SPECTRAL_SUPPORTED = 2,
+	/* Flag attribute to indicate if 11ac is offloaded to firmware */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_IS_OFFLOAD_ENABLED = 3,
+	/* Flag attribute to indicate if driver provides additional channel
+	 * capability as part of scan operation */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_ADD_CHAN_STATS_SUPPORT = 4,
+	/* Flag attribute to indicate interface status is UP */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_AP_UP = 5,
+	/* Operating mode (u8) of interface. Takes one of enum nl80211_iftype
+	 * values. */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_SAP_MODE = 6,
+	/* Channel width (u8). It takes one of enum nl80211_chan_width values.
+	 * This is the upper bound of channel width. ACS logic should try to get
+	 * a channel with the specified width and if not found, look for lower
+	 * values.
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_WIDTH = 7,
+	/* This (u8) will hold values of one of enum nl80211_bands */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_BAND = 8,
+	/* PHY/HW mode (u8). Takes one of enum qca_wlan_vendor_acs_hw_mode
+	 * values */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PHY_MODE = 9,
+	/* Array of (u32) supported frequency list among which ACS should choose
+	 * best frequency.
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_FREQ_LIST = 10,
+	/* Preferred channel list by the driver which will have array of nested
+	 * values as per enum qca_wlan_vendor_attr_pcl attribute.
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_PCL = 11,
+	/* Array of nested attribute for each channel. It takes attr as defined
+	 * in enum qca_wlan_vendor_external_acs_event_chan_info_attr.
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_CHAN_INFO = 12,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_MAX =
+		QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_EVENT_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_external_acs_channels: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_EXTERNAL_ACS. This carries a list of channels
+ * in priority order as decided after ACS operation in userspace.
+ */
+enum qca_wlan_vendor_attr_external_acs_channels {
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_INVALID = 0,
+
+	/* One of reason code (u8) from enum qca_wlan_vendor_acs_select_reason
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_REASON = 1,
+
+	/* Array of nested values for each channel with following attributes:
+	 * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND,
+	 * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY,
+	 * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY,
+	 * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0,
+	 * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1,
+	 * QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH
+	 */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LIST = 2,
+	/* This (u8) will hold values of one of enum nl80211_bands */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_BAND = 3,
+	/* Primary channel (u8) */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_PRIMARY = 4,
+	/* Secondary channel (u8) used for HT 40 MHz channels */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_SECONDARY = 5,
+	/* VHT seg0 channel (u8) */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG0 = 6,
+	/* VHT seg1 channel (u8) */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_CENTER_SEG1 = 7,
+	/* Channel width (u8). Takes one of enum nl80211_chan_width values. */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_WIDTH = 8,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST,
+	QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_MAX =
+		QCA_WLAN_VENDOR_ATTR_EXTERNAL_ACS_CHANNEL_LAST - 1
+};
+
+enum qca_chip_power_save_failure_reason {
+	/* Indicates if the reason for the failure is due to a protocol
+	 * layer/module.
+	 */
+        QCA_CHIP_POWER_SAVE_FAILURE_REASON_PROTOCOL = 0,
+	/* Indicates if the reason for the failure is due to a hardware issue.
+	 */
+        QCA_CHIP_POWER_SAVE_FAILURE_REASON_HARDWARE = 1,
+};
+
+/**
+ * qca_attr_chip_power_save_failure: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_CHIP_PWRSAVE_FAILURE. This carries the requisite
+ * information leading to the power save failure.
+ */
+enum qca_attr_chip_power_save_failure {
+        QCA_ATTR_CHIP_POWER_SAVE_FAILURE_INVALID = 0,
+        /* Reason to cause the power save failure.
+	 * These reasons are represented by
+	 * enum qca_chip_power_save_failure_reason.
+	 */
+        QCA_ATTR_CHIP_POWER_SAVE_FAILURE_REASON = 1,
+
+        /* keep last */
+        QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST,
+        QCA_ATTR_CHIP_POWER_SAVE_FAILURE_MAX =
+                QCA_ATTR_CHIP_POWER_SAVE_FAILURE_LAST - 1,
+};
+
+/**
+ * qca_wlan_vendor_attr_nud_stats_set: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_SET. This carries the requisite
+ * information to start/stop the NUD statistics collection.
+ */
+enum qca_attr_nud_stats_set {
+	QCA_ATTR_NUD_STATS_SET_INVALID = 0,
+
+	/* Flag to start/stop the NUD statistics collection.
+	 * Start - If included, Stop - If not included
+	 */
+	QCA_ATTR_NUD_STATS_SET_START = 1,
+	/* IPv4 address of the default gateway (in network byte order) */
+	QCA_ATTR_NUD_STATS_GW_IPV4 = 2,
+
+	/* keep last */
+	QCA_ATTR_NUD_STATS_SET_LAST,
+	QCA_ATTR_NUD_STATS_SET_MAX =
+		QCA_ATTR_NUD_STATS_SET_LAST - 1,
+};
+
+/**
+ * qca_attr_nud_stats_get: Attributes to vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_NUD_STATS_GET. This carries the requisite
+ * NUD statistics collected when queried.
+ */
+enum qca_attr_nud_stats_get {
+	QCA_ATTR_NUD_STATS_GET_INVALID = 0,
+	/* ARP Request count from netdev */
+	QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_FROM_NETDEV = 1,
+	/* ARP Request count sent to lower MAC from upper MAC */
+	QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TO_LOWER_MAC = 2,
+	/* ARP Request count received by lower MAC from upper MAC */
+	QCA_ATTR_NUD_STATS_ARP_REQ_RX_COUNT_BY_LOWER_MAC = 3,
+	/* ARP Request count successfully transmitted by the device */
+	QCA_ATTR_NUD_STATS_ARP_REQ_COUNT_TX_SUCCESS = 4,
+	/* ARP Response count received by lower MAC */
+	QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_LOWER_MAC = 5,
+	/* ARP Response count received by upper MAC */
+	QCA_ATTR_NUD_STATS_ARP_RSP_RX_COUNT_BY_UPPER_MAC = 6,
+	/* ARP Response count delivered to netdev */
+	QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_TO_NETDEV = 7,
+	/* ARP Response count delivered to netdev */
+	QCA_ATTR_NUD_STATS_ARP_RSP_COUNT_OUT_OF_ORDER_DROP = 8,
+	/* Flag indicating if the station's link to the AP is active.
+	 * Active Link - If included, Inactive link - If not included
+	 */
+	QCA_ATTR_NUD_STATS_AP_LINK_ACTIVE= 9,
+	/* Flag indicating if there is any duplicate address detected (DAD).
+	 * Yes - If detected, No - If not detected.
+	 */
+	QCA_ATTR_NUD_STATS_IS_DAD = 9,
+
+	/* keep last */
+	QCA_ATTR_NUD_STATS_GET_LAST,
+	QCA_ATTR_NUD_STATS_GET_MAX =
+		QCA_ATTR_NUD_STATS_GET_LAST - 1,
+};
+
+enum qca_wlan_btm_candidate_status {
+	QCA_STATUS_ACCEPT = 0,
+	QCA_STATUS_REJECT_EXCESSIVE_FRAME_LOSS_EXPECTED = 1,
+	QCA_STATUS_REJECT_EXCESSIVE_DELAY_EXPECTED = 2,
+	QCA_STATUS_REJECT_INSUFFICIENT_QOS_CAPACITY = 3,
+	QCA_STATUS_REJECT_LOW_RSSI = 4,
+	QCA_STATUS_REJECT_HIGH_INTERFERENCE = 5,
+	QCA_STATUS_REJECT_UNKNOWN = 6,
+};
+
+enum qca_wlan_vendor_attr_btm_candidate_info {
+	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_INVALID = 0,
+
+	/* 6-byte MAC address representing the BSSID of transition candidate */
+	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID = 1,
+	/* Unsigned 32-bit value from enum qca_wlan_btm_candidate_status
+	 * returned by the driver. It says whether the BSSID provided in
+	 * QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_BSSID is acceptable by
+	 * the driver, if not it specifies the reason for rejection.
+	 * Note that the user-space can overwrite the transition reject reason
+	 * codes provided by driver based on more information.
+	 */
+	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_STATUS = 2,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_MAX =
+	QCA_WLAN_VENDOR_ATTR_BTM_CANDIDATE_INFO_AFTER_LAST - 1,
+};
+
+enum qca_attr_trace_level {
+	QCA_ATTR_TRACE_LEVEL_INVALID = 0,
+	/*
+	 * Nested array of the following attributes:
+	 * QCA_ATTR_TRACE_LEVEL_MODULE,
+	 * QCA_ATTR_TRACE_LEVEL_MASK.
+	 */
+	QCA_ATTR_TRACE_LEVEL_PARAM = 1,
+	/*
+	 * Specific QCA host driver module. Please refer to the QCA host
+	 * driver implementation to get the specific module ID.
+	 */
+	QCA_ATTR_TRACE_LEVEL_MODULE = 2,
+	/* Different trace level masks represented in the QCA host driver. */
+	QCA_ATTR_TRACE_LEVEL_MASK = 3,
+
+	/* keep last */
+	QCA_ATTR_TRACE_LEVEL_AFTER_LAST,
+	QCA_ATTR_TRACE_LEVEL_MAX =
+		QCA_ATTR_TRACE_LEVEL_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_get_he_capabilities - IEEE 802.11ax HE capabilities
+ */
+enum qca_wlan_vendor_attr_get_he_capabilities {
+	QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_INVALID = 0,
+	/* Whether HE capabilities is supported
+	 * (u8 attribute: 0 = not supported, 1 = supported) */
+	QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED = 1,
+	/* HE PHY capabilities, array of 3 u32 values  */
+	QCA_WLAN_VENDOR_ATTR_PHY_CAPAB = 2,
+	/* HE MAC capabilities (u32 attribute) */
+	QCA_WLAN_VENDOR_ATTR_MAC_CAPAB = 3,
+	/* HE MCS map (u32 attribute) */
+	QCA_WLAN_VENDOR_ATTR_HE_MCS = 4,
+	/* Number of SS (u32 attribute) */
+	QCA_WLAN_VENDOR_ATTR_NUM_SS = 5,
+	/* RU count (u32 attribute) */
+	QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK = 6,
+	/* PPE threshold data, array of 8 u32 values */
+	QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD = 7,
+
+	/* keep last */
+	QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX =
+	QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_AFTER_LAST - 1,
+};
+
 #endif /* QCA_VENDOR_H */
diff --git a/src/common/version.h b/src/common/version.h
index ae5c9d4..16c1004 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -9,6 +9,6 @@
 #define GIT_VERSION_STR_POSTFIX ""
 #endif /* GIT_VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.6-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.7-devel" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index d6295b2..fd167d6 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -22,25 +22,49 @@
 
 static unsigned int wpa_kck_len(int akmp)
 {
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	switch (akmp) {
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 		return 24;
-	return 16;
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return 0;
+	default:
+		return 16;
+	}
 }
 
 
 static unsigned int wpa_kek_len(int akmp)
 {
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	switch (akmp) {
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return 64;
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
 		return 32;
-	return 16;
+	default:
+		return 16;
+	}
 }
 
 
 unsigned int wpa_mic_len(int akmp)
 {
-	if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+	switch (akmp) {
+	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 		return 24;
-	return 16;
+	case WPA_KEY_MGMT_FILS_SHA256:
+	case WPA_KEY_MGMT_FILS_SHA384:
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return 0;
+	default:
+		return 16;
+	}
 }
 
 
@@ -170,12 +194,12 @@
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
 
-#ifdef CONFIG_SUITEB192
+#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
 	if (wpa_key_mgmt_sha384(akmp))
 		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
 			   tmp, ptk_len);
 	else
-#endif /* CONFIG_SUITEB192 */
+#endif /* CONFIG_SUITEB192 || CONFIG_FILS */
 #ifdef CONFIG_IEEE80211W
 	if (wpa_key_mgmt_sha256(akmp))
 		sha256_prf(pmk, pmk_len, label, data, sizeof(data),
@@ -204,6 +228,227 @@
 	return 0;
 }
 
+#ifdef CONFIG_FILS
+
+int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
+		     const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
+		     size_t dh_ss_len, u8 *pmk, size_t *pmk_len)
+{
+	u8 nonces[2 * FILS_NONCE_LEN];
+	const u8 *addr[2];
+	size_t len[2];
+	size_t num_elem;
+	int res;
+
+	/* PMK = HMAC-Hash(SNonce || ANonce, rMSK [ || DHss ]) */
+	wpa_printf(MSG_DEBUG, "FILS: rMSK to PMK derivation");
+
+	if (wpa_key_mgmt_sha384(akmp))
+		*pmk_len = SHA384_MAC_LEN;
+	else if (wpa_key_mgmt_sha256(akmp))
+		*pmk_len = SHA256_MAC_LEN;
+	else
+		return -1;
+
+	wpa_hexdump_key(MSG_DEBUG, "FILS: rMSK", rmsk, rmsk_len);
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: DHss", dh_ss, dh_ss_len);
+
+	os_memcpy(nonces, snonce, FILS_NONCE_LEN);
+	os_memcpy(&nonces[FILS_NONCE_LEN], anonce, FILS_NONCE_LEN);
+	addr[0] = rmsk;
+	len[0] = rmsk_len;
+	num_elem = 1;
+	if (dh_ss) {
+		addr[1] = dh_ss;
+		len[1] = dh_ss_len;
+		num_elem++;
+	}
+	if (wpa_key_mgmt_sha384(akmp))
+		res = hmac_sha384_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
+					 addr, len, pmk);
+	else
+		res = hmac_sha256_vector(nonces, 2 * FILS_NONCE_LEN, num_elem,
+					 addr, len, pmk);
+	if (res == 0)
+		wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, *pmk_len);
+	return res;
+}
+
+
+int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
+		   u8 *pmkid)
+{
+	const u8 *addr[1];
+	size_t len[1];
+	u8 hash[SHA384_MAC_LEN];
+	int res;
+
+	/* PMKID = Truncate-128(Hash(EAP-Initiate/Reauth)) */
+	addr[0] = reauth;
+	len[0] = reauth_len;
+	if (wpa_key_mgmt_sha384(akmp))
+		res = sha384_vector(1, addr, len, hash);
+	else if (wpa_key_mgmt_sha256(akmp))
+		res = sha256_vector(1, addr, len, hash);
+	else
+		return -1;
+	if (res)
+		return res;
+	os_memcpy(pmkid, hash, PMKID_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: PMKID", pmkid, PMKID_LEN);
+	return 0;
+}
+
+
+int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
+		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher)
+{
+	u8 data[2 * ETH_ALEN + 2 * FILS_NONCE_LEN];
+	u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+	size_t key_data_len;
+	const char *label = "FILS PTK Derivation";
+
+	/*
+	 * FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
+	 *                       SPA || AA || SNonce || ANonce)
+	 * ICK = L(FILS-Key-Data, 0, ICK_bits)
+	 * KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
+	 * TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
+	 * If doing FT initial mobility domain association:
+	 * FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
+	 *             FILS-FT_bits)
+	 */
+	os_memcpy(data, spa, ETH_ALEN);
+	os_memcpy(data + ETH_ALEN, aa, ETH_ALEN);
+	os_memcpy(data + 2 * ETH_ALEN, snonce, FILS_NONCE_LEN);
+	os_memcpy(data + 2 * ETH_ALEN + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+
+	ptk->kck_len = 0;
+	ptk->kek_len = wpa_kek_len(akmp);
+	ptk->tk_len = wpa_cipher_key_len(cipher);
+	if (wpa_key_mgmt_sha384(akmp))
+		*ick_len = 48;
+	else if (wpa_key_mgmt_sha256(akmp))
+		*ick_len = 32;
+	else
+		return -1;
+	key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
+
+	if (wpa_key_mgmt_sha384(akmp))
+		sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+			   tmp, key_data_len);
+	else if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+			    tmp, key_data_len) < 0)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
+		   " AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
+
+	os_memcpy(ick, tmp, *ick_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: ICK", ick, *ick_len);
+
+	os_memcpy(ptk->kek, tmp + *ick_len, ptk->kek_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: KEK", ptk->kek, ptk->kek_len);
+
+	os_memcpy(ptk->tk, tmp + *ick_len + ptk->kek_len, ptk->tk_len);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: TK", ptk->tk, ptk->tk_len);
+
+	/* TODO: FILS-FT */
+
+	os_memset(tmp, 0, sizeof(tmp));
+	return 0;
+}
+
+
+int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
+		     const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
+		     const u8 *g_sta, size_t g_sta_len,
+		     const u8 *g_ap, size_t g_ap_len,
+		     int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
+		     size_t *key_auth_len)
+{
+	const u8 *addr[6];
+	size_t len[6];
+	size_t num_elem = 4;
+	int res;
+
+	/*
+	 * For (Re)Association Request frame (STA->AP):
+	 * Key-Auth = HMAC-Hash(ICK, SNonce || ANonce || STA-MAC || AP-BSSID
+	 *                      [ || gSTA || gAP ])
+	 */
+	addr[0] = snonce;
+	len[0] = FILS_NONCE_LEN;
+	addr[1] = anonce;
+	len[1] = FILS_NONCE_LEN;
+	addr[2] = sta_addr;
+	len[2] = ETH_ALEN;
+	addr[3] = bssid;
+	len[3] = ETH_ALEN;
+	if (g_sta && g_ap_len && g_ap && g_ap_len) {
+		addr[4] = g_sta;
+		len[4] = g_sta_len;
+		addr[5] = g_ap;
+		len[5] = g_ap_len;
+		num_elem = 6;
+	}
+
+	if (wpa_key_mgmt_sha384(akmp)) {
+		*key_auth_len = 48;
+		res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_sta);
+	} else if (wpa_key_mgmt_sha256(akmp)) {
+		*key_auth_len = 32;
+		res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_sta);
+	} else {
+		return -1;
+	}
+	if (res < 0)
+		return res;
+
+	/*
+	 * For (Re)Association Response frame (AP->STA):
+	 * Key-Auth = HMAC-Hash(ICK, ANonce || SNonce || AP-BSSID || STA-MAC
+	 *                      [ || gAP || gSTA ])
+	 */
+	addr[0] = anonce;
+	addr[1] = snonce;
+	addr[2] = bssid;
+	addr[3] = sta_addr;
+	if (g_sta && g_ap_len && g_ap && g_ap_len) {
+		addr[4] = g_ap;
+		len[4] = g_ap_len;
+		addr[5] = g_sta;
+		len[5] = g_sta_len;
+	}
+
+	if (wpa_key_mgmt_sha384(akmp))
+		res = hmac_sha384_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_ap);
+	else if (wpa_key_mgmt_sha256(akmp))
+		res = hmac_sha256_vector(ick, ick_len, num_elem, addr, len,
+					 key_auth_ap);
+	if (res < 0)
+		return res;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (STA)",
+		    key_auth_sta, *key_auth_len);
+	wpa_hexdump(MSG_DEBUG, "FILS: Key-Auth (AP)",
+		    key_auth_ap, *key_auth_len);
+
+	return 0;
+}
+
+#endif /* CONFIG_FILS */
+
 
 #ifdef CONFIG_IEEE80211R
 int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
@@ -376,6 +621,8 @@
 			}
 			if (data.num_pmkid == 1 && data.pmkid)
 				parse->rsn_pmkid = data.pmkid;
+			parse->key_mgmt = data.key_mgmt;
+			parse->pairwise_cipher = data.pairwise_cipher;
 			break;
 		case WLAN_EID_MOBILITY_DOMAIN:
 			if (len < sizeof(struct rsn_mdie))
@@ -510,6 +757,14 @@
 		return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
 		return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA256)
+		return WPA_KEY_MGMT_FILS_SHA256;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FILS_SHA384)
+		return WPA_KEY_MGMT_FILS_SHA384;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA256)
+		return WPA_KEY_MGMT_FT_FILS_SHA256;
+	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_FILS_SHA384)
+		return WPA_KEY_MGMT_FT_FILS_SHA384;
 	if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_OSEN)
 		return WPA_KEY_MGMT_OSEN;
 	return 0;
@@ -849,10 +1104,10 @@
  *
  * IEEE Std 802.11r-2008 - 8.5.1.5.3
  */
-void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
-		       const u8 *ssid, size_t ssid_len,
-		       const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
-		       const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
+int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+		      const u8 *ssid, size_t ssid_len,
+		      const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+		      const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
 {
 	u8 buf[1 + SSID_MAX_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
 	       FT_R0KH_ID_MAX_LEN + ETH_ALEN];
@@ -869,7 +1124,7 @@
 	 * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
 	 */
 	if (ssid_len > SSID_MAX_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
-		return;
+		return -1;
 	pos = buf;
 	*pos++ = ssid_len;
 	os_memcpy(pos, ssid, ssid_len);
@@ -882,8 +1137,9 @@
 	os_memcpy(pos, s0kh_id, ETH_ALEN);
 	pos += ETH_ALEN;
 
-	sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
-		   r0_key_data, sizeof(r0_key_data));
+	if (sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+		       r0_key_data, sizeof(r0_key_data)) < 0)
+		return -1;
 	os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
 
 	/*
@@ -894,8 +1150,10 @@
 	addr[1] = r0_key_data + PMK_LEN;
 	len[1] = 16;
 
-	sha256_vector(2, addr, len, hash);
+	if (sha256_vector(2, addr, len, hash) < 0)
+		return -1;
 	os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
+	return 0;
 }
 
 
@@ -904,8 +1162,8 @@
  *
  * IEEE Std 802.11r-2008 - 8.5.1.5.4
  */
-void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
-			    const u8 *s1kh_id, u8 *pmk_r1_name)
+int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+			   const u8 *s1kh_id, u8 *pmk_r1_name)
 {
 	u8 hash[32];
 	const u8 *addr[4];
@@ -924,8 +1182,10 @@
 	addr[3] = s1kh_id;
 	len[3] = ETH_ALEN;
 
-	sha256_vector(4, addr, len, hash);
+	if (sha256_vector(4, addr, len, hash) < 0)
+		return -1;
 	os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+	return 0;
 }
 
 
@@ -934,9 +1194,9 @@
  *
  * IEEE Std 802.11r-2008 - 8.5.1.5.4
  */
-void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
-		       const u8 *r1kh_id, const u8 *s1kh_id,
-		       u8 *pmk_r1, u8 *pmk_r1_name)
+int wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+		      const u8 *r1kh_id, const u8 *s1kh_id,
+		      u8 *pmk_r1, u8 *pmk_r1_name)
 {
 	u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
 	u8 *pos;
@@ -948,9 +1208,12 @@
 	os_memcpy(pos, s1kh_id, ETH_ALEN);
 	pos += ETH_ALEN;
 
-	sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
+	if (sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf,
+		       pmk_r1, PMK_LEN) < 0)
+		return -1;
 
-	wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
+	return wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id,
+				      pmk_r1_name);
 }
 
 
@@ -990,7 +1253,9 @@
 	ptk->tk_len = wpa_cipher_key_len(cipher);
 	ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
 
-	sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
+	if (sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf,
+		       tmp, ptk_len) < 0)
+		return -1;
 
 	/*
 	 * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
@@ -1009,7 +1274,8 @@
 	addr[5] = sta_addr;
 	len[5] = ETH_ALEN;
 
-	sha256_vector(6, addr, len, hash);
+	if (sha256_vector(6, addr, len, hash) < 0)
+		return -1;
 	os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
 
 	os_memcpy(ptk->kck, tmp, ptk->kck_len);
@@ -1184,6 +1450,8 @@
 			"WPA2-PSK" : "WPA-PSK";
 	case WPA_KEY_MGMT_NONE:
 		return "NONE";
+	case WPA_KEY_MGMT_WPA_NONE:
+		return "WPA-NONE";
 	case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
 		return "IEEE 802.1X (no WPA)";
 #ifdef CONFIG_IEEE80211R
@@ -1210,6 +1478,14 @@
 		return "WPA2-EAP-SUITE-B";
 	case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
 		return "WPA2-EAP-SUITE-B-192";
+	case WPA_KEY_MGMT_FILS_SHA256:
+		return "FILS-SHA256";
+	case WPA_KEY_MGMT_FILS_SHA384:
+		return "FILS-SHA384";
+	case WPA_KEY_MGMT_FT_FILS_SHA256:
+		return "FT-FILS-SHA256";
+	case WPA_KEY_MGMT_FT_FILS_SHA384:
+		return "FT-FILS-SHA384";
 	default:
 		return "UNKNOWN";
 	}
@@ -1219,27 +1495,33 @@
 u32 wpa_akm_to_suite(int akm)
 {
 	if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
-		return WLAN_AKM_SUITE_FT_8021X;
+		return RSN_AUTH_KEY_MGMT_FT_802_1X;
 	if (akm & WPA_KEY_MGMT_FT_PSK)
-		return WLAN_AKM_SUITE_FT_PSK;
-	if (akm & WPA_KEY_MGMT_IEEE8021X)
-		return WLAN_AKM_SUITE_8021X;
+		return RSN_AUTH_KEY_MGMT_FT_PSK;
 	if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
-		return WLAN_AKM_SUITE_8021X_SHA256;
+		return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
 	if (akm & WPA_KEY_MGMT_IEEE8021X)
-		return WLAN_AKM_SUITE_8021X;
+		return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 	if (akm & WPA_KEY_MGMT_PSK_SHA256)
-		return WLAN_AKM_SUITE_PSK_SHA256;
+		return RSN_AUTH_KEY_MGMT_PSK_SHA256;
 	if (akm & WPA_KEY_MGMT_PSK)
-		return WLAN_AKM_SUITE_PSK;
+		return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 	if (akm & WPA_KEY_MGMT_CCKM)
-		return WLAN_AKM_SUITE_CCKM;
+		return RSN_AUTH_KEY_MGMT_CCKM;
 	if (akm & WPA_KEY_MGMT_OSEN)
-		return WLAN_AKM_SUITE_OSEN;
+		return RSN_AUTH_KEY_MGMT_OSEN;
 	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
-		return WLAN_AKM_SUITE_8021X_SUITE_B;
+		return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
 	if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
-		return WLAN_AKM_SUITE_8021X_SUITE_B_192;
+		return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+	if (akm & WPA_KEY_MGMT_FILS_SHA256)
+		return RSN_AUTH_KEY_MGMT_FILS_SHA256;
+	if (akm & WPA_KEY_MGMT_FILS_SHA384)
+		return RSN_AUTH_KEY_MGMT_FILS_SHA384;
+	if (akm & WPA_KEY_MGMT_FT_FILS_SHA256)
+		return RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+	if (akm & WPA_KEY_MGMT_FT_FILS_SHA384)
+		return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
 	return 0;
 }
 
@@ -1281,7 +1563,7 @@
 }
 
 
-#ifdef CONFIG_IEEE80211R
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_FILS)
 int wpa_insert_pmkid(u8 *ies, size_t *ies_len, const u8 *pmkid)
 {
 	u8 *start, *end, *rpos, *rend;
@@ -1380,7 +1662,7 @@
 
 	return 0;
 }
-#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_IEEE80211R || CONFIG_FILS */
 
 
 int wpa_cipher_key_len(int cipher)
@@ -1419,7 +1701,7 @@
 }
 
 
-int wpa_cipher_to_alg(int cipher)
+enum wpa_alg wpa_cipher_to_alg(int cipher)
 {
 	switch (cipher) {
 	case WPA_CIPHER_CCMP_256:
@@ -1703,3 +1985,29 @@
 		return WPA_CIPHER_CCMP_256;
 	return WPA_CIPHER_CCMP;
 }
+
+
+#ifdef CONFIG_FILS
+int fils_domain_name_hash(const char *domain, u8 *hash)
+{
+	char buf[255], *wpos = buf;
+	const char *pos = domain;
+	size_t len;
+	const u8 *addr[1];
+	u8 mac[SHA256_MAC_LEN];
+
+	for (len = 0; len < sizeof(buf) && *pos; len++) {
+		if (isalpha(*pos) && isupper(*pos))
+			*wpos++ = tolower(*pos);
+		else
+			*wpos++ = *pos;
+		pos++;
+	}
+
+	addr[0] = (const u8 *) buf;
+	if (sha256_vector(1, addr, &len, mac) < 0)
+		return -1;
+	os_memcpy(hash, mac, 2);
+	return 0;
+}
+#endif /* CONFIG_FILS */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 1021ccb..5733c75 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -48,10 +48,8 @@
 
 #define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
 #define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
-#ifdef CONFIG_IEEE80211R
 #define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 #define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
-#endif /* CONFIG_IEEE80211R */
 #define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
 #define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
@@ -61,15 +59,21 @@
 #define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 #define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_AUTH_KEY_MGMT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
+#define RSN_AUTH_KEY_MGMT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 15)
+#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
+#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
 #define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
 
 #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
+#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
 #define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
 #if 0
 #define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
 #endif
 #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
 #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
 #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
@@ -78,6 +82,12 @@
 #define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
 #define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 #define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
+#define RSN_CIPHER_SUITE_SMS4 RSN_SELECTOR(0x00, 0x14, 0x72, 1)
+#define RSN_CIPHER_SUITE_CKIP RSN_SELECTOR(0x00, 0x40, 0x96, 0)
+#define RSN_CIPHER_SUITE_CKIP_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 1)
+#define RSN_CIPHER_SUITE_CMIC RSN_SELECTOR(0x00, 0x40, 0x96, 2)
+/* KRK is defined for nl80211 use only */
+#define RSN_CIPHER_SUITE_KRK RSN_SELECTOR(0x00, 0x40, 0x96, 255)
 
 /* EAPOL-Key Key Data Encapsulation
  * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
@@ -179,30 +189,16 @@
 	u8 key_iv[16];
 	u8 key_rsc[WPA_KEY_RSC_LEN];
 	u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
-	u8 key_mic[16];
-	u8 key_data_length[2]; /* big endian */
-	/* followed by key_data_length bytes of key_data */
-} STRUCT_PACKED;
-
-struct wpa_eapol_key_192 {
-	u8 type;
-	/* Note: key_info, key_length, and key_data_length are unaligned */
-	u8 key_info[2]; /* big endian */
-	u8 key_length[2]; /* big endian */
-	u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
-	u8 key_nonce[WPA_NONCE_LEN];
-	u8 key_iv[16];
-	u8 key_rsc[WPA_KEY_RSC_LEN];
-	u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
-	u8 key_mic[24];
-	u8 key_data_length[2]; /* big endian */
-	/* followed by key_data_length bytes of key_data */
+	/* variable length Key MIC field */
+	/* big endian 2-octet Key Data Length field */
+	/* followed by Key Data Length bytes of Key Data */
 } STRUCT_PACKED;
 
 #define WPA_EAPOL_KEY_MIC_MAX_LEN 24
 #define WPA_KCK_MAX_LEN 24
-#define WPA_KEK_MAX_LEN 32
+#define WPA_KEK_MAX_LEN 64
 #define WPA_TK_MAX_LEN 32
+#define FILS_ICK_MAX_LEN 48
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -352,6 +348,20 @@
 		   const u8 *addr1, const u8 *addr2,
 		   const u8 *nonce1, const u8 *nonce2,
 		   struct wpa_ptk *ptk, int akmp, int cipher);
+int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
+		     const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
+		     size_t dh_ss_len, u8 *pmk, size_t *pmk_len);
+int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
+		   u8 *pmkid);
+int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
+		    const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
+		    u8 *ick, size_t *ick_len, int akmp, int cipher);
+int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
+		     const u8 *anonce, const u8 *sta_addr, const u8 *bssid,
+		     const u8 *g_sta, size_t g_sta_len,
+		     const u8 *g_ap, size_t g_ap_len,
+		     int akmp, u8 *key_auth_sta, u8 *key_auth_ap,
+		     size_t *key_auth_len);
 
 #ifdef CONFIG_IEEE80211R
 int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
@@ -360,15 +370,15 @@
 	       const u8 *ftie, size_t ftie_len,
 	       const u8 *rsnie, size_t rsnie_len,
 	       const u8 *ric, size_t ric_len, u8 *mic);
-void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
-		       const u8 *ssid, size_t ssid_len,
-		       const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
-		       const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
-void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
-			    const u8 *s1kh_id, u8 *pmk_r1_name);
-void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
-		       const u8 *r1kh_id, const u8 *s1kh_id,
-		       u8 *pmk_r1, u8 *pmk_r1_name);
+int wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+		      const u8 *ssid, size_t ssid_len,
+		      const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+		      const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
+int wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+			   const u8 *s1kh_id, u8 *pmk_r1_name);
+int wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+		      const u8 *r1kh_id, const u8 *s1kh_id,
+		      u8 *pmk_r1, u8 *pmk_r1_name);
 int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
 		      const u8 *sta_addr, const u8 *bssid,
 		      const u8 *pmk_r1_name,
@@ -442,13 +452,15 @@
 	size_t igtk_len;
 	const u8 *ric;
 	size_t ric_len;
+	int key_mgmt;
+	int pairwise_cipher;
 };
 
 int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
 
 int wpa_cipher_key_len(int cipher);
 int wpa_cipher_rsc_len(int cipher);
-int wpa_cipher_to_alg(int cipher);
+enum wpa_alg wpa_cipher_to_alg(int cipher);
 int wpa_cipher_valid_group(int cipher);
 int wpa_cipher_valid_pairwise(int cipher);
 int wpa_cipher_valid_mgmt_group(int cipher);
@@ -461,5 +473,6 @@
 int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
 int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
 unsigned int wpa_mic_len(int akmp);
+int fils_domain_name_hash(const char *domain, u8 *hash);
 
 #endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index d9641bb..4649eab 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant/hostapd control interface library
- * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -74,8 +74,12 @@
 #define WPA_EVENT_NETWORK_NOT_FOUND "CTRL-EVENT-NETWORK-NOT-FOUND "
 /** Change in the signal level was reported by the driver */
 #define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
+/** Beacon loss reported by the driver */
+#define WPA_EVENT_BEACON_LOSS "CTRL-EVENT-BEACON-LOSS "
 /** Regulatory domain channel */
 #define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
+/** Channel switch (followed by freq=<MHz> and other channel parameters) */
+#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
 
 /** IP subnet status change notification
  *
@@ -187,6 +191,7 @@
 #define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
 #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
 #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
+#define P2P_EVENT_INVITATION_ACCEPTED "P2P-INVITATION-ACCEPTED "
 #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
 #define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
 #define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
@@ -225,6 +230,11 @@
 /* parameters: <addr> <result> */
 #define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
 
+#define RX_ANQP "RX-ANQP "
+#define RX_HS20_ANQP "RX-HS20-ANQP "
+#define RX_HS20_ANQP_ICON "RX-HS20-ANQP-ICON "
+#define RX_HS20_ICON "RX-HS20-ICON "
+
 #define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
 #define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
 
@@ -245,6 +255,7 @@
 #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 "
+#define AP_STA_POLL_OK "AP-STA-POLL-OK "
 
 #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
 #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
@@ -267,6 +278,9 @@
 
 #define AP_CSA_FINISHED "AP-CSA-FINISHED "
 
+#define P2P_EVENT_LISTEN_OFFLOAD_STOP "P2P-LISTEN-OFFLOAD-STOPPED "
+#define P2P_LISTEN_OFFLOAD_STOP_REASON "P2P-LISTEN-OFFLOAD-STOP-REASON "
+
 /* BSS Transition Management Response frame received */
 #define BSS_TM_RESP "BSS-TM-RESP "
 
@@ -276,6 +290,20 @@
 /* BSS Transition Management Request received with MBO transition reason */
 #define MBO_TRANSITION_REASON "MBO-TRANSITION-REASON "
 
+/* parameters: <STA address> <dialog token> <ack=0/1> */
+#define BEACON_REQ_TX_STATUS "BEACON-REQ-TX-STATUS "
+/* parameters: <STA address> <dialog token> <report mode> <beacon report> */
+#define BEACON_RESP_RX "BEACON-RESP-RX "
+
+/* PMKSA cache entry added; parameters: <BSSID> <network_id> */
+#define PMKSA_CACHE_ADDED "PMKSA-CACHE-ADDED "
+/* PMKSA cache entry removed; parameters: <BSSID> <network_id> */
+#define PMKSA_CACHE_REMOVED "PMKSA-CACHE-REMOVED "
+
+/* FILS HLP Container receive; parameters: dst=<addr> src=<addr> frame=<hexdump>
+ */
+#define FILS_HLP_RX "FILS-HLP-RX "
+
 /* BSS command information masks */
 
 #define WPA_BSS_MASK_ALL		0xFFFDFFFF
@@ -301,6 +329,9 @@
 #define WPA_BSS_MASK_SNR		BIT(19)
 #define WPA_BSS_MASK_EST_THROUGHPUT	BIT(20)
 #define WPA_BSS_MASK_FST		BIT(21)
+#define WPA_BSS_MASK_UPDATE_IDX		BIT(22)
+#define WPA_BSS_MASK_BEACON_IE		BIT(23)
+#define WPA_BSS_MASK_FILS_INDICATION	BIT(24)
 
 
 /* VENDOR_ELEM_* frame id values */
@@ -319,6 +350,7 @@
 	VENDOR_ELEM_P2P_ASSOC_REQ = 11,
 	VENDOR_ELEM_P2P_ASSOC_RESP = 12,
 	VENDOR_ELEM_ASSOC_REQ = 13,
+	VENDOR_ELEM_PROBE_REQ = 14,
 	NUM_VENDOR_ELEM_FRAMES
 };
 
diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c
index d4d874d..e27f3bb 100644
--- a/src/crypto/aes-ctr.c
+++ b/src/crypto/aes-ctr.c
@@ -1,5 +1,5 @@
 /*
- * AES-128 CTR
+ * AES-128/192/256 CTR
  *
  * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
  *
@@ -14,15 +14,16 @@
 #include "aes_wrap.h"
 
 /**
- * aes_128_ctr_encrypt - AES-128 CTR mode encryption
- * @key: Key for encryption (16 bytes)
+ * aes_ctr_encrypt - AES-128/192/256 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @key_len: Length of the key (16, 24, or 32 bytes)
  * @nonce: Nonce for counter mode (16 bytes)
  * @data: Data to encrypt in-place
  * @data_len: Length of data in bytes
  * Returns: 0 on success, -1 on failure
  */
-int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
-			u8 *data, size_t data_len)
+int aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+		    u8 *data, size_t data_len)
 {
 	void *ctx;
 	size_t j, len, left = data_len;
@@ -30,7 +31,7 @@
 	u8 *pos = data;
 	u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
 
-	ctx = aes_encrypt_init(key, 16);
+	ctx = aes_encrypt_init(key, key_len);
 	if (ctx == NULL)
 		return -1;
 	os_memcpy(counter, nonce, AES_BLOCK_SIZE);
@@ -53,3 +54,18 @@
 	aes_encrypt_deinit(ctx);
 	return 0;
 }
+
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (key_len bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+			u8 *data, size_t data_len)
+{
+	return aes_ctr_encrypt(key, 16, nonce, data, data_len);
+}
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
index 5ac82c2..2bb79b5 100644
--- a/src/crypto/aes-siv.c
+++ b/src/crypto/aes-siv.c
@@ -61,26 +61,33 @@
 }
 
 
-static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
-		   size_t *len, u8 *mac)
+static int aes_s2v(const u8 *key, size_t key_len,
+		   size_t num_elem, const u8 *addr[], size_t *len, u8 *mac)
 {
 	u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
 	u8 *buf = NULL;
 	int ret;
 	size_t i;
+	const u8 *data[1];
+	size_t data_len[1];
 
 	if (!num_elem) {
 		os_memcpy(tmp, zero, sizeof(zero));
 		tmp[AES_BLOCK_SIZE - 1] = 1;
-		return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+		data[0] = tmp;
+		data_len[0] = sizeof(tmp);
+		return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
 	}
 
-	ret = omac1_aes_128(key, zero, sizeof(zero), tmp);
+	data[0] = zero;
+	data_len[0] = sizeof(zero);
+	ret = omac1_aes_vector(key, key_len, 1, data, data_len, tmp);
 	if (ret)
 		return ret;
 
 	for (i = 0; i < num_elem - 1; i++) {
-		ret = omac1_aes_128(key, addr[i], len[i], tmp2);
+		ret = omac1_aes_vector(key, key_len, 1, &addr[i], &len[i],
+				       tmp2);
 		if (ret)
 			return ret;
 
@@ -94,7 +101,8 @@
 
 		os_memcpy(buf, addr[i], len[i]);
 		xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
-		ret = omac1_aes_128(key, buf, len[i], mac);
+		data[0] = buf;
+		ret = omac1_aes_vector(key, key_len, 1, data, &len[i], mac);
 		bin_clear_free(buf, len[i]);
 		return ret;
 	}
@@ -103,24 +111,32 @@
 	pad_block(tmp2, addr[i], len[i]);
 	xor(tmp, tmp2);
 
-	return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+	data[0] = tmp;
+	data_len[0] = sizeof(tmp);
+	return omac1_aes_vector(key, key_len, 1, data, data_len, mac);
 }
 
 
-int aes_siv_encrypt(const u8 *key, const u8 *pw,
-		    size_t pwlen, size_t num_elem,
-		    const u8 *addr[], const size_t *len, u8 *out)
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+		    const u8 *pw, size_t pwlen,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out)
 {
 	const u8 *_addr[6];
 	size_t _len[6];
-	const u8 *k1 = key, *k2 = key + 16;
+	const u8 *k1, *k2;
 	u8 v[AES_BLOCK_SIZE];
 	size_t i;
 	u8 *iv, *crypt_pw;
 
-	if (num_elem > ARRAY_SIZE(_addr) - 1)
+	if (num_elem > ARRAY_SIZE(_addr) - 1 ||
+	    (key_len != 32 && key_len != 48 && key_len != 64))
 		return -1;
 
+	key_len /= 2;
+	k1 = key;
+	k2 = key + key_len;
+
 	for (i = 0; i < num_elem; i++) {
 		_addr[i] = addr[i];
 		_len[i] = len[i];
@@ -128,7 +144,7 @@
 	_addr[num_elem] = pw;
 	_len[num_elem] = pwlen;
 
-	if (aes_s2v(k1, num_elem + 1, _addr, _len, v))
+	if (aes_s2v(k1, key_len, num_elem + 1, _addr, _len, v))
 		return -1;
 
 	iv = out;
@@ -140,26 +156,31 @@
 	/* zero out 63rd and 31st bits of ctr (from right) */
 	v[8] &= 0x7f;
 	v[12] &= 0x7f;
-	return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen);
+	return aes_ctr_encrypt(k2, key_len, v, crypt_pw, pwlen);
 }
 
 
-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+		    const u8 *iv_crypt, size_t iv_c_len,
 		    size_t num_elem, const u8 *addr[], const size_t *len,
 		    u8 *out)
 {
 	const u8 *_addr[6];
 	size_t _len[6];
-	const u8 *k1 = key, *k2 = key + 16;
+	const u8 *k1, *k2;
 	size_t crypt_len;
 	size_t i;
 	int ret;
 	u8 iv[AES_BLOCK_SIZE];
 	u8 check[AES_BLOCK_SIZE];
 
-	if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1)
+	if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1 ||
+	    (key_len != 32 && key_len != 48 && key_len != 64))
 		return -1;
 	crypt_len = iv_c_len - AES_BLOCK_SIZE;
+	key_len /= 2;
+	k1 = key;
+	k2 = key + key_len;
 
 	for (i = 0; i < num_elem; i++) {
 		_addr[i] = addr[i];
@@ -174,11 +195,11 @@
 	iv[8] &= 0x7f;
 	iv[12] &= 0x7f;
 
-	ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len);
+	ret = aes_ctr_encrypt(k2, key_len, iv, out, crypt_len);
 	if (ret)
 		return ret;
 
-	ret = aes_s2v(k1, num_elem + 1, _addr, _len, check);
+	ret = aes_s2v(k1, key_len, num_elem + 1, _addr, _len, check);
 	if (ret)
 		return ret;
 	if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
diff --git a/src/crypto/aes_siv.h b/src/crypto/aes_siv.h
index 463cf65..fb05d80 100644
--- a/src/crypto/aes_siv.h
+++ b/src/crypto/aes_siv.h
@@ -9,10 +9,12 @@
 #ifndef AES_SIV_H
 #define AES_SIV_H
 
-int aes_siv_encrypt(const u8 *key, const u8 *pw,
-		    size_t pwlen, size_t num_elem,
-		    const u8 *addr[], const size_t *len, u8 *out);
-int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+int aes_siv_encrypt(const u8 *key, size_t key_len,
+		    const u8 *pw, size_t pwlen,
+		    size_t num_elem, const u8 *addr[], const size_t *len,
+		    u8 *out);
+int aes_siv_decrypt(const u8 *key, size_t key_len,
+		    const u8 *iv_crypt, size_t iv_c_len,
 		    size_t num_elem, const u8 *addr[], const size_t *len,
 		    u8 *out);
 
diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h
index 4a14209..b70b1d2 100644
--- a/src/crypto/aes_wrap.h
+++ b/src/crypto/aes_wrap.h
@@ -3,7 +3,7 @@
  *
  * - AES Key Wrap Algorithm (RFC3394)
  * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
- * - AES-128 CTR mode encryption
+ * - AES-128/192/256 CTR mode encryption
  * - AES-128 EAX mode encryption/decryption
  * - AES-128 CBC
  * - AES-GCM
@@ -33,6 +33,8 @@
 int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
 			       u8 *mac);
 int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_ctr_encrypt(const u8 *key, size_t key_len, const u8 *nonce,
+				 u8 *data, size_t data_len);
 int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
 				     u8 *data, size_t data_len);
 int __must_check aes_128_eax_encrypt(const u8 *key,
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index 087953b..0fa06d9 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/module_tests.h"
 #include "crypto/aes_siv.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/aes.h"
@@ -16,6 +17,7 @@
 #include "crypto/crypto.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
 
 
 static int test_siv(void)
@@ -91,7 +93,7 @@
 	addr[0] = ad;
 	len[0] = sizeof(ad);
 
-	if (aes_siv_encrypt(key, plaintext, sizeof(plaintext),
+	if (aes_siv_encrypt(key, sizeof(key), plaintext, sizeof(plaintext),
 			    1, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
 		return 1;
@@ -102,7 +104,8 @@
 		return 1;
 	}
 
-	if (aes_siv_decrypt(key, iv_c, sizeof(iv_c), 1, addr, len, out)) {
+	if (aes_siv_decrypt(key, sizeof(key), iv_c, sizeof(iv_c),
+			    1, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
 		return 1;
 	}
@@ -120,7 +123,8 @@
 	addr[2] = nonce_2;
 	len[2] = sizeof(nonce_2);
 
-	if (aes_siv_encrypt(key_2, plaintext_2, sizeof(plaintext_2),
+	if (aes_siv_encrypt(key_2, sizeof(key_2),
+			    plaintext_2, sizeof(plaintext_2),
 			    3, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode encryption failed");
 		return 1;
@@ -131,7 +135,8 @@
 		return 1;
 	}
 
-	if (aes_siv_decrypt(key_2, iv_c_2, sizeof(iv_c_2), 3, addr, len, out)) {
+	if (aes_siv_decrypt(key_2, sizeof(key_2), iv_c_2, sizeof(iv_c_2),
+			    3, addr, len, out)) {
 		wpa_printf(MSG_ERROR, "AES-SIV mode decryption failed");
 		return 1;
 	}
@@ -1266,7 +1271,7 @@
 }
 
 
-const struct {
+static const struct {
 	char *data;
 	u8 hash[32];
 } tests[] = {
@@ -1290,14 +1295,15 @@
 	}
 };
 
-const struct hmac_test {
-	u8 key[80];
+static const struct hmac_test {
+	u8 key[150];
 	size_t key_len;
-	u8 data[128];
+	u8 data[160];
 	size_t data_len;
-	u8 hash[32];
+	u8 hash[32]; /* HMAC-SHA-256 */
+	u8 hash384[48]; /* HMAC-SHA-384 */
 } hmac_tests[] = {
-	/* draft-ietf-ipsec-ciph-sha-256-01.txt */
+	/* draft-ietf-ipsec-ciph-sha-256-01.txt; RFC 4231 */
 	{
 		{
 			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
@@ -1312,7 +1318,8 @@
 			0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
 			0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
 			0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
-		}
+		},
+		{ }
 	},
 	{
 		{
@@ -1329,7 +1336,8 @@
 			0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
 			0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
 			0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
-		}
+		},
+		{ }
 	},
 	{
 		{
@@ -1347,7 +1355,8 @@
 			0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
 			0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
 			0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
-		}
+		},
+		{ }
 	},
 	{
 		{
@@ -1364,9 +1373,34 @@
 			0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
 			0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
 			0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 1 */
+		{
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+			0x0b, 0x0b, 0x0b, 0x0b
+		},
+		20,
+		"Hi There",
+		8,
+		{
+			0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53,
+			0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b,
+			0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7,
+			0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7
+		},
+		{
+			0xaf, 0xd0, 0x39, 0x44, 0xd8, 0x48, 0x95, 0x62,
+			0x6b, 0x08, 0x25, 0xf4, 0xab, 0x46, 0x90, 0x7f,
+			0x15, 0xf9, 0xda, 0xdb, 0xe4, 0x10, 0x1e, 0xc6,
+			0x82, 0xaa, 0x03, 0x4c, 0x7c, 0xeb, 0xc5, 0x9c,
+			0xfa, 0xea, 0x9e, 0xa9, 0x07, 0x6e, 0xde, 0x7f,
+			0x4a, 0xf1, 0x52, 0xe8, 0xb2, 0xfa, 0x9c, 0xb6
 		}
 	},
-	{
+	{ /* RFC 4231 - Test Case 2 */
 		"Jefe",
 		4,
 		"what do ya want for nothing?",
@@ -1376,6 +1410,14 @@
 			0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
 			0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
 			0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
+		},
+		{
+			0xaf, 0x45, 0xd2, 0xe3, 0x76, 0x48, 0x40, 0x31,
+			0x61, 0x7f, 0x78, 0xd2, 0xb5, 0x8a, 0x6b, 0x1b,
+			0x9c, 0x7e, 0xf4, 0x64, 0xf5, 0xa0, 0x1b, 0x47,
+			0xe4, 0x2e, 0xc3, 0x73, 0x63, 0x22, 0x44, 0x5e,
+			0x8e, 0x22, 0x40, 0xca, 0x5e, 0x69, 0xe2, 0xc7,
+			0x8b, 0x32, 0x39, 0xec, 0xfa, 0xb2, 0x16, 0x49
 		}
 	},
 	{
@@ -1401,6 +1443,39 @@
 			0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
 			0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
 			0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 3 */
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa
+		},
+		20,
+		{
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+			0xdd, 0xdd
+		},
+		50,
+		{
+			0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46,
+			0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7,
+			0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22,
+			0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe
+		},
+		{
+			0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a,
+			0x0a, 0xa2, 0xac, 0xe0, 0x14, 0xc8, 0xa8, 0x6f,
+			0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+			0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b,
+			0x2a, 0x5a, 0xb3, 0x9d, 0xc1, 0x38, 0x14, 0xb9,
+			0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27
 		}
 	},
 	{
@@ -1427,6 +1502,40 @@
 			0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
 			0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
 			0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 4 */
+		{
+			0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+			0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+			0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+			0x19,
+		},
+		25,
+		{
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+			0xcd, 0xcd
+		},
+		50,
+		{
+			0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e,
+			0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a,
+			0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07,
+			0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b
+		},
+		{
+			0x3e, 0x8a, 0x69, 0xb7, 0x78, 0x3c, 0x25, 0x85,
+			0x19, 0x33, 0xab, 0x62, 0x90, 0xaf, 0x6c, 0xa7,
+			0x7a, 0x99, 0x81, 0x48, 0x08, 0x50, 0x00, 0x9c,
+			0xc5, 0x57, 0x7c, 0x6e, 0x1f, 0x57, 0x3b, 0x4e,
+			0x68, 0x01, 0xdd, 0x23, 0xc4, 0xa7, 0xd6, 0x79,
+			0xcc, 0xf8, 0xa3, 0x86, 0xc6, 0x74, 0xcf, 0xfb
 		}
 	},
 	{
@@ -1444,7 +1553,8 @@
 			0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
 			0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
 			0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
-		}
+		},
+		{ }
 	},
 	{
 		{
@@ -1467,6 +1577,45 @@
 			0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
 			0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
 			0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 6 */
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa
+		},
+		131,
+		"Test Using Larger Than Block-Size Key - Hash Key First",
+		54,
+		{
+			0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f,
+			0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f,
+			0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14,
+			0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54
+		},
+		{
+			0x4e, 0xce, 0x08, 0x44, 0x85, 0x81, 0x3e, 0x90,
+			0x88, 0xd2, 0xc6, 0x3a, 0x04, 0x1b, 0xc5, 0xb4,
+			0x4f, 0x9e, 0xf1, 0x01, 0x2a, 0x2b, 0x58, 0x8f,
+			0x3c, 0xd1, 0x1f, 0x05, 0x03, 0x3a, 0xc4, 0xc6,
+			0x0c, 0x2e, 0xf6, 0xab, 0x40, 0x30, 0xfe, 0x82,
+			0x96, 0x24, 0x8d, 0xf1, 0x63, 0xf4, 0x49, 0x52
 		}
 	},
 	{
@@ -1491,6 +1640,45 @@
 			0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
 			0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
 			0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
+		},
+		{ }
+	},
+	{ /* RFC 4231 - Test Case 7 */
+		{
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+			0xaa, 0xaa, 0xaa
+		},
+		131,
+		"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.",
+		152,
+		{
+			0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb,
+			0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44,
+			0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93,
+			0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2
+		},
+		{
+			0x66, 0x17, 0x17, 0x8e, 0x94, 0x1f, 0x02, 0x0d,
+			0x35, 0x1e, 0x2f, 0x25, 0x4e, 0x8f, 0xd3, 0x2c,
+			0x60, 0x24, 0x20, 0xfe, 0xb0, 0xb8, 0xfb, 0x9a,
+			0xdc, 0xce, 0xbb, 0x82, 0x46, 0x1e, 0x99, 0xc5,
+			0xa6, 0x78, 0xcc, 0x31, 0xe7, 0x99, 0x17, 0x6d,
+			0x38, 0x60, 0xe6, 0x11, 0x0c, 0x46, 0x52, 0x3e
 		}
 	}
 };
@@ -1605,6 +1793,96 @@
 }
 
 
+static int test_sha384(void)
+{
+#ifdef CONFIG_SHA384
+	unsigned int i;
+	u8 hash[48];
+	const u8 *addr[2];
+	size_t len[2];
+	int errors = 0;
+	const char *data = "hello";
+	const u8 hash_res[] = {
+		0x59, 0xe1, 0x74, 0x87, 0x77, 0x44, 0x8c, 0x69,
+		0xde, 0x6b, 0x80, 0x0d, 0x7a, 0x33, 0xbb, 0xfb,
+		0x9f, 0xf1, 0xb4, 0x63, 0xe4, 0x43, 0x54, 0xc3,
+		0x55, 0x3b, 0xcd, 0xb9, 0xc6, 0x66, 0xfa, 0x90,
+		0x12, 0x5a, 0x3c, 0x79, 0xf9, 0x03, 0x97, 0xbd,
+		0xf5, 0xf6, 0xa1, 0x3d, 0xe8, 0x28, 0x68, 0x4f
+	};
+
+	addr[0] = (const u8 *) data;
+	len[0] = 5;
+	if (sha384_vector(1, addr, len, hash) < 0 ||
+	    os_memcmp(hash, hash_res, 48) != 0) {
+		wpa_printf(MSG_INFO, "SHA384 test case 1: FAIL");
+		errors++;
+	} else {
+		wpa_printf(MSG_INFO, "SHA384 test case 1: OK");
+	}
+
+	addr[0] = (const u8 *) data;
+	len[0] = 4;
+	addr[1] = (const u8 *) data + 4;
+	len[1] = 1;
+	if (sha384_vector(2, addr, len, hash) < 0 ||
+	    os_memcmp(hash, hash_res, 48) != 0) {
+		wpa_printf(MSG_INFO, "SHA384 test case 2: FAIL");
+		errors++;
+	} else {
+		wpa_printf(MSG_INFO, "SHA384 test case 2: OK");
+	}
+
+	for (i = 0; i < ARRAY_SIZE(hmac_tests); i++) {
+		const struct hmac_test *t = &hmac_tests[i];
+
+		if (t->hash384[0] == 0 && t->hash384[1] == 0 &&
+		    t->hash384[2] == 0 && t->hash384[3] == 0)
+			continue;
+		wpa_printf(MSG_INFO, "HMAC-SHA384 test case %d:", i + 1);
+
+		if (hmac_sha384(t->key, t->key_len, t->data, t->data_len,
+				hash) < 0 ||
+		    os_memcmp(hash, t->hash384, 48) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		addr[0] = t->data;
+		len[0] = t->data_len;
+		if (hmac_sha384_vector(t->key, t->key_len, 1, addr, len,
+				       hash) < 0 ||
+		    os_memcmp(hash, t->hash384, 48) != 0) {
+			wpa_printf(MSG_INFO, " FAIL");
+			errors++;
+		} else
+			wpa_printf(MSG_INFO, " OK");
+
+		if (len[0]) {
+			addr[0] = t->data;
+			len[0] = 1;
+			addr[1] = t->data + 1;
+			len[1] = t->data_len - 1;
+			if (hmac_sha384_vector(t->key, t->key_len, 2, addr, len,
+					       hash) < 0 ||
+			    os_memcmp(hash, t->hash384, 48) != 0) {
+				wpa_printf(MSG_INFO, " FAIL");
+				errors++;
+			} else
+				wpa_printf(MSG_INFO, " OK");
+		}
+	}
+
+	if (!errors)
+		wpa_printf(MSG_INFO, "SHA384 test cases passed");
+	return errors;
+#else /* CONFIG_SHA384 */
+	return 0;
+#endif /* CONFIG_SHA384 */
+}
+
+
 static int test_fips186_2_prf(void)
 {
 	/* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
@@ -1750,6 +2028,7 @@
 	    test_md5() ||
 	    test_sha1() ||
 	    test_sha256() ||
+	    test_sha384() ||
 	    test_fips186_2_prf() ||
 	    test_ms_funcs())
 		ret = -1;
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index c5a28ce..5f7896c 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -29,6 +29,8 @@
 #include "sha1.h"
 #include "sha256.h"
 #include "sha384.h"
+#include "md5.h"
+#include "aes_wrap.h"
 #include "crypto.h"
 
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
@@ -47,6 +49,8 @@
 
 static void HMAC_CTX_free(HMAC_CTX *ctx)
 {
+	if (!ctx)
+		return;
 	HMAC_CTX_cleanup(ctx);
 	bin_clear_free(ctx, sizeof(*ctx));
 }
@@ -65,6 +69,9 @@
 
 static void EVP_MD_CTX_free(EVP_MD_CTX *ctx)
 {
+	if (!ctx)
+		return;
+	EVP_MD_CTX_cleanup(ctx);
 	bin_clear_free(ctx, sizeof(*ctx));
 }
 
@@ -72,7 +79,11 @@
 
 static BIGNUM * get_group5_prime(void)
 {
-#ifdef OPENSSL_IS_BORINGSSL
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
+	return BN_get_rfc3526_prime_1536(NULL);
+#elif !defined(OPENSSL_IS_BORINGSSL)
+	return get_rfc3526_prime_1536(NULL);
+#else
 	static const unsigned char RFC3526_PRIME_1536[] = {
 		0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
 		0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
@@ -92,14 +103,15 @@
 		0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
 	};
         return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
-#else /* OPENSSL_IS_BORINGSSL */
-	return get_rfc3526_prime_1536(NULL);
-#endif /* OPENSSL_IS_BORINGSSL */
+#endif
 }
 
 #ifdef OPENSSL_NO_SHA256
 #define NO_SHA256_WRAPPER
 #endif
+#ifdef OPENSSL_NO_SHA512
+#define NO_SHA384_WRAPPER
+#endif
 
 static int openssl_digest_vector(const EVP_MD *type, size_t num_elem,
 				 const u8 *addr[], const size_t *len, u8 *mac)
@@ -233,6 +245,14 @@
 }
 #endif /* NO_SHA256_WRAPPER */
 
+#ifndef NO_SHA384_WRAPPER
+int sha384_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+		  u8 *mac)
+{
+	return openssl_digest_vector(EVP_sha384(), num_elem, addr, len, mac);
+}
+#endif /* NO_SHA384_WRAPPER */
+
 
 static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
 {
@@ -363,6 +383,8 @@
 	AES_KEY actx;
 	int res;
 
+	if (TEST_FAIL())
+		return -1;
 	if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
 		return -1;
 	res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
@@ -377,6 +399,8 @@
 	AES_KEY actx;
 	int res;
 
+	if (TEST_FAIL())
+		return -1;
 	if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
 		return -1;
 	res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
@@ -602,11 +626,13 @@
 
 void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
 {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	DH *dh;
 	struct wpabuf *pubkey = NULL, *privkey = NULL;
 	size_t publen, privlen;
 
 	*priv = NULL;
+	wpabuf_free(*publ);
 	*publ = NULL;
 
 	dh = DH_new();
@@ -645,11 +671,63 @@
 	wpabuf_clear_free(privkey);
 	DH_free(dh);
 	return NULL;
+#else
+	DH *dh;
+	struct wpabuf *pubkey = NULL, *privkey = NULL;
+	size_t publen, privlen;
+	BIGNUM *p = NULL, *g;
+	const BIGNUM *priv_key = NULL, *pub_key = NULL;
+
+	*priv = NULL;
+	wpabuf_free(*publ);
+	*publ = NULL;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	g = BN_new();
+	p = get_group5_prime();
+	if (!g || BN_set_word(g, 2) != 1 || !p ||
+	    DH_set0_pqg(dh, p, NULL, g) != 1)
+		goto err;
+	p = NULL;
+	g = NULL;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	DH_get0_key(dh, &pub_key, &priv_key);
+	publen = BN_num_bytes(pub_key);
+	pubkey = wpabuf_alloc(publen);
+	if (!pubkey)
+		goto err;
+	privlen = BN_num_bytes(priv_key);
+	privkey = wpabuf_alloc(privlen);
+	if (!privkey)
+		goto err;
+
+	BN_bn2bin(pub_key, wpabuf_put(pubkey, publen));
+	BN_bn2bin(priv_key, wpabuf_put(privkey, privlen));
+
+	*priv = privkey;
+	*publ = pubkey;
+	return dh;
+
+err:
+	BN_free(p);
+	BN_free(g);
+	wpabuf_clear_free(pubkey);
+	wpabuf_clear_free(privkey);
+	DH_free(dh);
+	return NULL;
+#endif
 }
 
 
 void * dh5_init_fixed(const struct wpabuf *priv, const struct wpabuf *publ)
 {
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	DH *dh;
 
 	dh = DH_new();
@@ -680,6 +758,42 @@
 err:
 	DH_free(dh);
 	return NULL;
+#else
+	DH *dh;
+	BIGNUM *p = NULL, *g, *priv_key = NULL, *pub_key = NULL;
+
+	dh = DH_new();
+	if (dh == NULL)
+		return NULL;
+
+	g = BN_new();
+	p = get_group5_prime();
+	if (!g || BN_set_word(g, 2) != 1 || !p ||
+	    DH_set0_pqg(dh, p, NULL, g) != 1)
+		goto err;
+	p = NULL;
+	g = NULL;
+
+	priv_key = BN_bin2bn(wpabuf_head(priv), wpabuf_len(priv), NULL);
+	pub_key = BN_bin2bn(wpabuf_head(publ), wpabuf_len(publ), NULL);
+	if (!priv_key || !pub_key || DH_set0_key(dh, pub_key, priv_key) != 1)
+		goto err;
+	pub_key = NULL;
+	priv_key = NULL;
+
+	if (DH_generate_key(dh) != 1)
+		goto err;
+
+	return dh;
+
+err:
+	BN_free(p);
+	BN_free(g);
+	BN_free(pub_key);
+	BN_clear_free(priv_key);
+	DH_free(dh);
+	return NULL;
+#endif
 }
 
 
@@ -917,7 +1031,7 @@
 		       const u8 *addr[], const size_t *len, u8 *mac)
 {
 	return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
-				   len, mac, 32);
+				   len, mac, 48);
 }
 
 
diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c
index dec39ef..ebd9952 100644
--- a/src/crypto/des-internal.c
+++ b/src/crypto/des-internal.c
@@ -48,7 +48,7 @@
 
 static const u32 bytebit[8] =
 {
-	0200, 0100, 040, 020, 010, 04, 02, 01 
+	0200, 0100, 040, 020, 010, 04, 02, 01
 };
 
 static const u32 bigbyte[24] =
@@ -58,22 +58,22 @@
 	0x8000UL,    0x4000UL,    0x2000UL,    0x1000UL,
 	0x800UL,     0x400UL,     0x200UL,     0x100UL,
 	0x80UL,      0x40UL,      0x20UL,      0x10UL,
-	0x8UL,       0x4UL,       0x2UL,       0x1L 
+	0x8UL,       0x4UL,       0x2UL,       0x1L
 };
 
 /* Use the key schedule specific in the standard (ANSI X3.92-1981) */
 
 static const u8 pc1[56] = {
-	56, 48, 40, 32, 24, 16,  8,  0, 57, 49, 41, 33, 25, 17,  
-	 9,  1, 58, 50, 42, 34, 26, 18, 10,  2, 59, 51, 43, 35, 
+	56, 48, 40, 32, 24, 16,  8,  0, 57, 49, 41, 33, 25, 17,
+	 9,  1, 58, 50, 42, 34, 26, 18, 10,  2, 59, 51, 43, 35,
 	62, 54, 46, 38, 30, 22, 14,  6, 61, 53, 45, 37, 29, 21,
-	13,  5, 60, 52, 44, 36, 28, 20, 12,  4, 27, 19, 11,  3 
+	13,  5, 60, 52, 44, 36, 28, 20, 12,  4, 27, 19, 11,  3
 };
 
 static const u8 totrot[16] = {
 	1,   2,  4,  6,
-	8,  10, 12, 14, 
-	15, 17, 19, 21, 
+	8,  10, 12, 14,
+	15, 17, 19, 21,
 	23, 25, 27, 28
 };
 
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
index 9d094b8..4697e04 100644
--- a/src/crypto/fips_prf_openssl.c
+++ b/src/crypto/fips_prf_openssl.c
@@ -76,12 +76,11 @@
 			/* w_i = G(t, XVAL) */
 			os_memcpy(_t, t, 20);
 			sha1_transform(_t, xkey);
-			_t[0] = host_to_be32(_t[0]);
-			_t[1] = host_to_be32(_t[1]);
-			_t[2] = host_to_be32(_t[2]);
-			_t[3] = host_to_be32(_t[3]);
-			_t[4] = host_to_be32(_t[4]);
-			os_memcpy(xpos, _t, 20);
+			WPA_PUT_BE32(xpos, _t[0]);
+			WPA_PUT_BE32(xpos + 4, _t[1]);
+			WPA_PUT_BE32(xpos + 8, _t[2]);
+			WPA_PUT_BE32(xpos + 12, _t[3]);
+			WPA_PUT_BE32(xpos + 16, _t[4]);
 
 			/* XKEY = (1 + XKEY + w_i) mod 2^b */
 			carry = 1;
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
index ffcba66..a491707 100644
--- a/src/crypto/sha1-internal.c
+++ b/src/crypto/sha1-internal.c
@@ -53,7 +53,7 @@
 100% Public Domain
 
 -----------------
-Modified 7/98 
+Modified 7/98
 By James H. Brown <jbrown@burgoyne.com>
 Still 100% Public Domain
 
@@ -75,7 +75,7 @@
 be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
 "a"s).
 
-I also changed the declaration of variables i & j in SHA1Update to 
+I also changed the declaration of variables i & j in SHA1Update to
 unsigned long from unsigned int for the same reason.
 
 These changes should make no difference to any 32 bit implementations since
@@ -102,7 +102,7 @@
 Modified 4/01
 By Saul Kravitz <Saul.Kravitz@celera.com>
 Still 100% PD
-Modified to run on Compaq Alpha hardware.  
+Modified to run on Compaq Alpha hardware.
 
 -----------------
 Modified 4/01
@@ -162,7 +162,7 @@
 {
 	printf("%s (%d,%d) %x %x %x %x %x\n",
 	       msg,
-	       context->count[0], context->count[1], 
+	       context->count[0], context->count[1],
 	       context->state[0],
 	       context->state[1],
 	       context->state[2],
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
index 86a548e..ff1e2ba 100644
--- a/src/crypto/sha256-internal.c
+++ b/src/crypto/sha256-internal.c
@@ -69,7 +69,7 @@
 ( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
    ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
 #define Ch(x,y,z)       (z ^ (x & (y ^ z)))
-#define Maj(x,y,z)      (((x | y) & z) | (x & y)) 
+#define Maj(x,y,z)      (((x | y) & z) | (x & y))
 #define S(x, n)         RORc((x), (n))
 #define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
 #define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
@@ -100,7 +100,7 @@
 	for (i = 16; i < 64; i++) {
 		W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
 			W[i - 16];
-	}        
+	}
 
 	/* Compress */
 #define RND(a,b,c,d,e,f,g,h,i)                          \
@@ -111,7 +111,7 @@
 
 	for (i = 0; i < 64; ++i) {
 		RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
-		t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; 
+		t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4];
 		S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
 	}
 
diff --git a/src/crypto/sha384.c b/src/crypto/sha384.c
new file mode 100644
index 0000000..ee136ce
--- /dev/null
+++ b/src/crypto/sha384.c
@@ -0,0 +1,104 @@
+/*
+ * SHA-384 hash implementation and interface functions
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha384.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha384_vector - HMAC-SHA384 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (48 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+		       const u8 *addr[], const size_t *len, u8 *mac)
+{
+	unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
+	unsigned char tk[48];
+	const u8 *_addr[6];
+	size_t _len[6], i;
+
+	if (num_elem > 5) {
+		/*
+		 * Fixed limit on the number of fragments to avoid having to
+		 * allocate memory (which could fail).
+		 */
+		return -1;
+	}
+
+	/* if key is longer than 128 bytes reset it to key = SHA384(key) */
+	if (key_len > 128) {
+		if (sha384_vector(1, &key, &key_len, tk) < 0)
+			return -1;
+		key = tk;
+		key_len = 48;
+	}
+
+	/* the HMAC_SHA384 transform looks like:
+	 *
+	 * SHA384(K XOR opad, SHA384(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 128 times
+	 * opad is the byte 0x5c repeated 128 times
+	 * and text is the data being protected */
+
+	/* start out by storing key in ipad */
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with ipad values */
+	for (i = 0; i < 128; i++)
+		k_pad[i] ^= 0x36;
+
+	/* perform inner SHA384 */
+	_addr[0] = k_pad;
+	_len[0] = 128;
+	for (i = 0; i < num_elem; i++) {
+		_addr[i + 1] = addr[i];
+		_len[i + 1] = len[i];
+	}
+	if (sha384_vector(1 + num_elem, _addr, _len, mac) < 0)
+		return -1;
+
+	os_memset(k_pad, 0, sizeof(k_pad));
+	os_memcpy(k_pad, key, key_len);
+	/* XOR key with opad values */
+	for (i = 0; i < 128; i++)
+		k_pad[i] ^= 0x5c;
+
+	/* perform outer SHA384 */
+	_addr[0] = k_pad;
+	_len[0] = 128;
+	_addr[1] = mac;
+	_len[1] = SHA384_MAC_LEN;
+	return sha384_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha384 - HMAC-SHA384 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (48 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+		size_t data_len, u8 *mac)
+{
+	return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c
index 66ef331..76c4fe7 100644
--- a/src/crypto/sha512-internal.c
+++ b/src/crypto/sha512-internal.c
@@ -242,7 +242,7 @@
 		md->curlen = 0;
 	}
 
-	/* pad upto 120 bytes of zeroes
+	/* pad up to 120 bytes of zeroes
 	 * note: that from 112 to 120 is the 64 MSB of the length.  We assume
 	 * that you won't hash > 2^64 bits of data... :-)
 	 */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 15a3bcf..11d504a 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -336,29 +336,36 @@
 					 struct tls_random *data);
 
 /**
- * tls_connection_prf - Use TLS-PRF to derive keying material
+ * tls_connection_export_key - Derive keying material from a TLS connection
  * @tls_ctx: TLS context data from tls_init()
  * @conn: Connection context data from tls_connection_init()
  * @label: Label (e.g., description of the key) for PRF
- * @server_random_first: seed is 0 = client_random|server_random,
- * 1 = server_random|client_random
- * @skip_keyblock: Skip TLS key block from the beginning of PRF output
  * @out: Buffer for output data from TLS-PRF
  * @out_len: Length of the output buffer
  * Returns: 0 on success, -1 on failure
  *
- * tls_connection_prf() is required so that further keying material can be
- * derived from the master secret. Example implementation of this function is in
- * tls_prf_sha1_md5() when it is called with seed set to
- * client_random|server_random (or server_random|client_random). For TLSv1.2 and
- * newer, a different PRF is needed, though.
+ * Exports keying material using the mechanism described in RFC 5705.
  */
-int __must_check  tls_connection_prf(void *tls_ctx,
-				     struct tls_connection *conn,
-				     const char *label,
-				     int server_random_first,
-				     int skip_keyblock,
-				     u8 *out, size_t out_len);
+int __must_check tls_connection_export_key(void *tls_ctx,
+					   struct tls_connection *conn,
+					   const char *label,
+					   u8 *out, size_t out_len);
+
+/**
+ * tls_connection_get_eap_fast_key - Derive key material for EAP-FAST
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * Exports key material after the normal TLS key block for use with
+ * EAP-FAST. Most callers will want tls_connection_export_key(), but EAP-FAST
+ * uses a different legacy mechanism.
+ */
+int __must_check tls_connection_get_eap_fast_key(void *tls_ctx,
+						 struct tls_connection *conn,
+						 u8 *out, size_t out_len);
 
 /**
  * tls_connection_handshake - Process TLS handshake (client side)
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index c4cd3c1..8c76bfa 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -402,7 +402,7 @@
 		return -1;
 	}
 
-	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags); 
+	/* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
 	 * to force peer validation(?) */
 
 	if (params->ca_cert) {
@@ -810,15 +810,22 @@
 }
 
 
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       int skip_keyblock, u8 *out, size_t out_len)
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, u8 *out, size_t out_len)
 {
-	if (conn == NULL || conn->session == NULL || skip_keyblock)
+	if (conn == NULL || conn->session == NULL)
 		return -1;
 
 	return gnutls_prf(conn->session, os_strlen(label), label,
-			  server_random_first, 0, NULL, out_len, (char *) out);
+			  0 /* client_random first */, 0, NULL, out_len,
+			  (char *) out);
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	return -1;
 }
 
 
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index 01a7c97..c7cb5de 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -394,9 +394,9 @@
 }
 
 
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       int skip_keyblock, u8 *out, size_t out_len)
+static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, int server_random_first,
+			      int skip_keyblock, u8 *out, size_t out_len)
 {
 	int ret = -1, skip = 0;
 	u8 *tmp_out = NULL;
@@ -434,6 +434,21 @@
 }
 
 
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, u8 *out, size_t out_len)
+{
+	return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len);
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+	return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out,
+				  out_len);
+}
+
+
 struct wpabuf * tls_connection_handshake(void *tls_ctx,
 					 struct tls_connection *conn,
 					 const struct wpabuf *in_data,
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index ae392ad..dd5681e 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -86,9 +86,15 @@
 }
 
 
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       int skip_keyblock, u8 *out, size_t out_len)
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, u8 *out, size_t out_len)
+{
+	return -1;
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
 {
 	return -1;
 }
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index ebcc545..4521891 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -18,6 +18,7 @@
 
 #include <openssl/ssl.h>
 #include <openssl/err.h>
+#include <openssl/opensslv.h>
 #include <openssl/pkcs12.h>
 #include <openssl/x509v3.h>
 #ifndef OPENSSL_NO_ENGINE
@@ -37,6 +38,12 @@
 #include "tls.h"
 #include "tls_openssl.h"
 
+#if !defined(CONFIG_FIPS) &&                             \
+    (defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) ||   \
+     defined(EAP_SERVER_FAST))
+#define OPENSSL_NEED_EAP_FAST_PRF
+#endif
+
 #if defined(OPENSSL_IS_BORINGSSL)
 /* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
 typedef size_t stack_index_t;
@@ -51,10 +58,13 @@
 #endif /* OPENSSL_NO_TLSEXT */
 #endif /* SSL_set_tlsext_status_type */
 
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L || \
+     defined(LIBRESSL_VERSION_NUMBER)) &&    \
+    !defined(BORINGSSL_API_VERSION)
 /*
  * SSL_get_client_random() and SSL_get_server_random() were added in OpenSSL
- * 1.1.0. Provide compatibility wrappers for older versions.
+ * 1.1.0 and newer BoringSSL revisions. Provide compatibility wrappers for
+ * older versions.
  */
 
 static size_t SSL_get_client_random(const SSL *ssl, unsigned char *out,
@@ -77,6 +87,7 @@
 }
 
 
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
 static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
 					 unsigned char *out, size_t outlen)
 {
@@ -88,6 +99,7 @@
 	os_memcpy(out, session->master_key, outlen);
 	return outlen;
 }
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
 
 #endif
 
@@ -618,7 +630,8 @@
 		wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
 			   "system certificate store: subject='%s'", buf);
 
-		if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+		if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+					 cert)) {
 			tls_show_errors(MSG_WARNING, __func__,
 					"Failed to add ca_cert to OpenSSL "
 					"certificate store");
@@ -716,10 +729,16 @@
 
 	engine = ENGINE_by_id(id);
 	if (engine) {
-		ENGINE_free(engine);
 		wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
 			   "available", id);
-		return 0;
+		/*
+		 * If it was auto-loaded by ENGINE_by_id() we might still
+		 * need to tell it which PKCS#11 module to use in legacy
+		 * (non-p11-kit) environments. Do so now; even if it was
+		 * properly initialised before, setting it again will be
+		 * harmless.
+		 */
+		goto found;
 	}
 	ERR_clear_error();
 
@@ -756,7 +775,7 @@
 			   id, ERR_error_string(ERR_get_error(), NULL));
 		return -1;
 	}
-
+ found:
 	while (post && post[0]) {
 		wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
 		if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
@@ -900,7 +919,7 @@
 		}
 #endif /* OPENSSL_FIPS */
 #endif /* CONFIG_FIPS */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 		SSL_load_error_strings();
 		SSL_library_init();
 #ifndef OPENSSL_NO_SHA256
@@ -953,6 +972,14 @@
 	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
 	SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
 
+#ifdef SSL_MODE_NO_AUTO_CHAIN
+	/* Number of deployed use cases assume the default OpenSSL behavior of
+	 * auto chaining the local certificate is in use. BoringSSL removed this
+	 * functionality by default, so we need to restore it here to avoid
+	 * breaking existing use cases. */
+	SSL_CTX_clear_mode(ssl, SSL_MODE_NO_AUTO_CHAIN);
+#endif /* SSL_MODE_NO_AUTO_CHAIN */
+
 	SSL_CTX_set_info_callback(ssl, ssl_info_cb);
 	SSL_CTX_set_app_data(ssl, context);
 	if (data->tls_session_lifetime > 0) {
@@ -1024,7 +1051,7 @@
 
 	tls_openssl_ref_count--;
 	if (tls_openssl_ref_count == 0) {
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 #ifndef OPENSSL_NO_ENGINE
 		ENGINE_cleanup();
 #endif /* OPENSSL_NO_ENGINE */
@@ -2061,7 +2088,7 @@
 #ifdef ANDROID
 	/* Single alias */
 	if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
-		if (tls_add_ca_from_keystore(ssl_ctx->cert_store,
+		if (tls_add_ca_from_keystore(SSL_CTX_get_cert_store(ssl_ctx),
 					     &ca_cert[11]) < 0)
 			return -1;
 		SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
@@ -2081,7 +2108,7 @@
 		alias = strtok_r(aliases, delim, &savedptr);
 		for (; alias; alias = strtok_r(NULL, delim, &savedptr)) {
 			if (tls_add_ca_from_keystore_encoded(
-				    ssl_ctx->cert_store, alias)) {
+				    SSL_CTX_get_cert_store(ssl_ctx), alias)) {
 				wpa_printf(MSG_WARNING,
 					   "OpenSSL: %s - Failed to add ca_cert %s from keystore",
 					   __func__, alias);
@@ -2235,10 +2262,8 @@
 #ifdef SSL_OP_NO_TICKET
 	if (flags & TLS_CONN_DISABLE_SESSION_TICKET)
 		SSL_set_options(ssl, SSL_OP_NO_TICKET);
-#ifdef SSL_clear_options
 	else
 		SSL_clear_options(ssl, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
 #endif /* SSL_OP_NO_TICKET */
 
 #ifdef SSL_OP_NO_TLSv1
@@ -2315,7 +2340,7 @@
 		return 0;
 
 #ifdef PKCS12_FUNCS
-#if OPENSSL_VERSION_NUMBER < 0x10002000L
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
 	/*
 	 * Clear previously set extra chain certificates, if any, from PKCS#12
 	 * processing in tls_parse_pkcs12() to allow OpenSSL to build a new
@@ -2344,15 +2369,26 @@
 		BIO *bio = BIO_from_keystore(&client_cert[11]);
 		X509 *x509 = NULL;
 		int ret = -1;
-		if (bio) {
+		if (bio)
 			x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
-			BIO_free(bio);
-		}
+
 		if (x509) {
 			if (SSL_use_certificate(conn->ssl, x509) == 1)
 				ret = 0;
 			X509_free(x509);
 		}
+
+		/* Read additional certificates into the chain. */
+		while (bio) {
+			x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+			if (x509) {
+				/* Takes ownership of x509 */
+				SSL_add0_chain_cert(conn->ssl, x509);
+			} else {
+				BIO_free(bio);
+				bio = NULL;
+			}
+		}
 		return ret;
 	}
 #endif /* ANDROID */
@@ -3083,7 +3119,7 @@
 }
 
 
-#ifndef CONFIG_FIPS
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
 static int openssl_get_keyblock_size(SSL *ssl)
 {
 #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
@@ -3138,18 +3174,24 @@
 		    EVP_CIPHER_iv_length(c));
 #endif
 }
-#endif /* CONFIG_FIPS */
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
 
 
-static int openssl_tls_prf(struct tls_connection *conn,
-			   const char *label, int server_random_first,
-			   int skip_keyblock, u8 *out, size_t out_len)
+int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
+			      const char *label, u8 *out, size_t out_len)
 {
-#ifdef CONFIG_FIPS
-	wpa_printf(MSG_ERROR, "OpenSSL: TLS keys cannot be exported in FIPS "
-		   "mode");
-	return -1;
-#else /* CONFIG_FIPS */
+	if (!conn ||
+	    SSL_export_keying_material(conn->ssl, out, out_len, label,
+				       os_strlen(label), NULL, 0, 0) != 1)
+		return -1;
+	return 0;
+}
+
+
+int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
+				    u8 *out, size_t out_len)
+{
+#ifdef OPENSSL_NEED_EAP_FAST_PRF
 	SSL *ssl;
 	SSL_SESSION *sess;
 	u8 *rnd;
@@ -3164,9 +3206,9 @@
 	const char *ver;
 
 	/*
-	 * TLS library did not support key generation, so get the needed TLS
-	 * session parameters and use an internal implementation of TLS PRF to
-	 * derive the key.
+	 * TLS library did not support EAP-FAST key generation, so get the
+	 * needed TLS session parameters and use an internal implementation of
+	 * TLS PRF to derive the key.
 	 */
 
 	if (conn == NULL)
@@ -3179,15 +3221,13 @@
 	if (!ver || !sess)
 		return -1;
 
-	if (skip_keyblock) {
-		skip = openssl_get_keyblock_size(ssl);
-		if (skip < 0)
-			return -1;
-		tmp_out = os_malloc(skip + out_len);
-		if (!tmp_out)
-			return -1;
-		_out = tmp_out;
-	}
+	skip = openssl_get_keyblock_size(ssl);
+	if (skip < 0)
+		return -1;
+	tmp_out = os_malloc(skip + out_len);
+	if (!tmp_out)
+		return -1;
+	_out = tmp_out;
 
 	rnd = os_malloc(2 * SSL3_RANDOM_SIZE);
 	if (!rnd) {
@@ -3200,54 +3240,31 @@
 	master_key_len = SSL_SESSION_get_master_key(sess, master_key,
 						    sizeof(master_key));
 
-	if (server_random_first) {
-		os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
-		os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random,
-			  SSL3_RANDOM_SIZE);
-	} else {
-		os_memcpy(rnd, client_random, SSL3_RANDOM_SIZE);
-		os_memcpy(rnd + SSL3_RANDOM_SIZE, server_random,
-			  SSL3_RANDOM_SIZE);
-	}
+	os_memcpy(rnd, server_random, SSL3_RANDOM_SIZE);
+	os_memcpy(rnd + SSL3_RANDOM_SIZE, client_random, SSL3_RANDOM_SIZE);
 
 	if (os_strcmp(ver, "TLSv1.2") == 0) {
 		tls_prf_sha256(master_key, master_key_len,
-			       label, rnd, 2 * SSL3_RANDOM_SIZE,
+			       "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
 			       _out, skip + out_len);
 		ret = 0;
 	} else if (tls_prf_sha1_md5(master_key, master_key_len,
-				    label, rnd, 2 * SSL3_RANDOM_SIZE,
+				    "key expansion", rnd, 2 * SSL3_RANDOM_SIZE,
 				    _out, skip + out_len) == 0) {
 		ret = 0;
 	}
 	os_memset(master_key, 0, sizeof(master_key));
 	os_free(rnd);
-	if (ret == 0 && skip_keyblock)
+	if (ret == 0)
 		os_memcpy(out, _out + skip, out_len);
 	bin_clear_free(tmp_out, skip);
 
 	return ret;
-#endif /* CONFIG_FIPS */
-}
-
-
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-		       const char *label, int server_random_first,
-		       int skip_keyblock, u8 *out, size_t out_len)
-{
-	if (conn == NULL)
-		return -1;
-	if (server_random_first || skip_keyblock)
-		return openssl_tls_prf(conn, label,
-				       server_random_first, skip_keyblock,
-				       out, out_len);
-	if (SSL_export_keying_material(conn->ssl, out, out_len, label,
-				       os_strlen(label), NULL, 0, 0) == 1) {
-		wpa_printf(MSG_DEBUG, "OpenSSL: Using internal PRF");
-		return 0;
-	}
-	return openssl_tls_prf(conn, label, server_random_first,
-			       skip_keyblock, out, out_len);
+#else /* OPENSSL_NEED_EAP_FAST_PRF */
+	wpa_printf(MSG_ERROR,
+		   "OpenSSL: EAP-FAST keys cannot be exported in FIPS mode");
+	return -1;
+#endif /* OPENSSL_NEED_EAP_FAST_PRF */
 }
 
 
@@ -3976,7 +3993,7 @@
 		engine_id = "pkcs11";
 
 #if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
 	if (params->flags & TLS_CONN_EAP_FAST) {
 		wpa_printf(MSG_DEBUG,
 			   "OpenSSL: Use TLSv1_method() for EAP-FAST");
@@ -4120,10 +4137,8 @@
 #ifdef SSL_OP_NO_TICKET
 	if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
 		SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
-#ifdef SSL_CTX_clear_options
 	else
 		SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TICKET);
-#endif /* SSL_clear_options */
 #endif /*  SSL_OP_NO_TICKET */
 
 #ifdef HAVE_OCSP
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index b7e0d16..7b3a6bd 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1,6 +1,6 @@
 /*
  * Driver interface definition
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -21,6 +21,9 @@
 
 #include "common/defs.h"
 #include "common/ieee802_11_defs.h"
+#ifdef CONFIG_MACSEC
+#include "pae/ieee802_1x_kay.h"
+#endif /* CONFIG_MACSEC */
 #include "utils/list.h"
 
 #define HOSTAPD_CHAN_DISABLED 0x00000001
@@ -54,6 +57,13 @@
 #define HOSTAPD_CHAN_VHT_130_30 0x04000000
 #define HOSTAPD_CHAN_VHT_150_10 0x08000000
 
+/* Filter gratuitous ARP */
+#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0)
+/* Filter unsolicited Neighbor Advertisement */
+#define WPA_DATA_FRAME_FILTER_FLAG_NA BIT(1)
+/* Filter unicast IP packets encrypted using the GTK */
+#define WPA_DATA_FRAME_FILTER_FLAG_GTK BIT(2)
+
 /**
  * enum reg_change_initiator - Regulatory change initiator
  */
@@ -126,6 +136,29 @@
 	unsigned int dfs_cac_ms;
 };
 
+#define HE_MAX_NUM_SS 		8
+#define HE_MAX_PHY_CAPAB_SIZE	3
+
+/**
+ * struct he_ppe_threshold - IEEE 802.11ax HE PPE Threshold
+ */
+struct he_ppe_threshold {
+	u32 numss_m1;
+	u32 ru_count;
+	u32 ppet16_ppet8_ru3_ru0[HE_MAX_NUM_SS];
+};
+
+/**
+ * struct he_capabilities - IEEE 802.11ax HE capabilities
+ */
+struct he_capabilities {
+	u8 he_supported;
+	u32 phy_cap[HE_MAX_PHY_CAPAB_SIZE];
+	u32 mac_cap;
+	u32 mcs;
+	struct he_ppe_threshold ppet;
+};
+
 #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
 #define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
 
@@ -184,6 +217,11 @@
 	u8 vht_mcs_set[8];
 
 	unsigned int flags; /* HOSTAPD_MODE_FLAG_* */
+
+	/**
+	 * he_capab - HE (IEEE 802.11ax) capabilities
+	 */
+	struct he_capabilities he_capab;
 };
 
 
@@ -226,6 +264,9 @@
  * @est_throughput: Estimated throughput in kbps (this is calculated during
  * scan result processing if left zero by the driver wrapper)
  * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
+ * @parent_tsf: Time when the Beacon/Probe Response frame was received in terms
+ * of TSF of the BSS specified by %tsf_bssid.
+ * @tsf_bssid: The BSS that %parent_tsf TSF time refers to.
  * @ie_len: length of the following IE field in octets
  * @beacon_ie_len: length of the following Beacon IE field in octets
  *
@@ -256,6 +297,8 @@
 	unsigned int age;
 	unsigned int est_throughput;
 	int snr;
+	u64 parent_tsf;
+	u8 tsf_bssid[ETH_ALEN];
 	size_t ie_len;
 	size_t beacon_ie_len;
 	/* Followed by ie_len + beacon_ie_len octets of IE data */
@@ -448,6 +491,68 @@
 	 */
 	const u8 *bssid;
 
+	/**
+	 * scan_cookie - Unique identification representing the scan request
+	 *
+	 * This scan_cookie carries a unique identification representing the
+	 * scan request if the host driver/kernel supports concurrent scan
+	 * requests. This cookie is returned from the corresponding driver
+	 * interface.
+	 *
+	 * Note: Unlike other parameters in this structure, scan_cookie is used
+	 * only to return information instead of setting parameters for the
+	 * scan.
+	 */
+	u64 scan_cookie;
+
+	 /**
+	  * duration - Dwell time on each channel
+	  *
+	  * This optional parameter can be used to set the dwell time on each
+	  * channel. In TUs.
+	  */
+	 u16 duration;
+
+	 /**
+	  * duration_mandatory - Whether the specified duration is mandatory
+	  *
+	  * If this is set, the duration specified by the %duration field is
+	  * mandatory (and the driver should reject the scan request if it is
+	  * unable to comply with the specified duration), otherwise it is the
+	  * maximum duration and the actual duration may be shorter.
+	  */
+	 unsigned int duration_mandatory:1;
+
+	/**
+	 * relative_rssi_set - Whether relative RSSI parameters are set
+	 */
+	unsigned int relative_rssi_set:1;
+
+	/**
+	 * relative_rssi - Relative RSSI for reporting better BSSs
+	 *
+	 * Amount of RSSI by which a BSS should be better than the current
+	 * connected BSS to report the new BSS to user space.
+	 */
+	s8 relative_rssi;
+
+	/**
+	 * relative_adjust_band - Band to which RSSI should be adjusted
+	 *
+	 * The relative_adjust_rssi should be added to the band specified
+	 * by relative_adjust_band.
+	 */
+	enum set_band relative_adjust_band;
+
+	/**
+	 * relative_adjust_rssi - RSSI to be added to relative_adjust_band
+	 *
+	 * An amount of relative_band_rssi should be added to the BSSs that
+	 * belong to the band specified by relative_adjust_band while comparing
+	 * with other bands for BSS reporting.
+	 */
+	s8 relative_adjust_rssi;
+
 	/*
 	 * NOTE: Whenever adding new parameters here, please make sure
 	 * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
@@ -478,17 +583,18 @@
 	int p2p;
 
 	/**
-	 * sae_data - SAE elements for Authentication frame
+	 * auth_data - Additional elements for Authentication frame
 	 *
 	 * This buffer starts with the Authentication transaction sequence
-	 * number field. If SAE is not used, this pointer is %NULL.
+	 * number field. If no special handling of such elements is needed, this
+	 * pointer is %NULL. This is used with SAE and FILS.
 	 */
-	const u8 *sae_data;
+	const u8 *auth_data;
 
 	/**
-	 * sae_data_len - Length of sae_data buffer in octets
+	 * auth_data_len - Length of auth_data buffer in octets
 	 */
-	size_t sae_data_len;
+	size_t auth_data_len;
 };
 
 /**
@@ -875,6 +981,27 @@
 	 * AP as usual. Valid for DMG network only.
 	 */
 	int pbss;
+
+	/**
+	 * fils_kek - KEK for FILS association frame protection (AES-SIV)
+	 */
+	const u8 *fils_kek;
+
+	/**
+	 * fils_kek_len: Length of fils_kek in bytes
+	 */
+	size_t fils_kek_len;
+
+	/**
+	 * fils_nonces - Nonces for FILS association frame protection
+	 * (AES-SIV AAD)
+	 */
+	const u8 *fils_nonces;
+
+	/**
+	 * fils_nonces_len: Length of fils_nonce in bytes
+	 */
+	size_t fils_nonces_len;
 };
 
 enum hide_ssid {
@@ -933,6 +1060,22 @@
 	int *basic_rates;
 
 	/**
+	 * beacon_rate: Beacon frame data rate
+	 *
+	 * This parameter can be used to set a specific Beacon frame data rate
+	 * for the BSS. The interpretation of this value depends on the
+	 * rate_type (legacy: in 100 kbps units, HT: HT-MCS, VHT: VHT-MCS). If
+	 * beacon_rate == 0 and rate_type == 0 (BEACON_RATE_LEGACY), the default
+	 * Beacon frame data rate is used.
+	 */
+	unsigned int beacon_rate;
+
+	/**
+	 * beacon_rate_type: Beacon data rate type (legacy/HT/VHT)
+	 */
+	enum beacon_rate_type rate_type;
+
+	/**
 	 * proberesp - Probe Response template
 	 *
 	 * This is used by drivers that reply to Probe Requests internally in
@@ -1108,16 +1251,43 @@
 	 * infrastructure BSS. Valid only for DMG network.
 	 */
 	int pbss;
+
+	/**
+	 * multicast_to_unicast - Whether to use multicast_to_unicast
+	 *
+	 * If this is non-zero, the AP is requested to perform multicast to
+	 * unicast conversion for ARP, IPv4, and IPv6 frames (possibly within
+	 * 802.1Q). If enabled, such frames are to be sent to each station
+	 * separately, with the DA replaced by their own MAC address rather
+	 * than the group address.
+	 *
+	 * Note that this may break certain expectations of the receiver, such
+	 * as the ability to drop unicast IP packets received within multicast
+	 * L2 frames, or the ability to not send ICMP destination unreachable
+	 * messages for packets received in L2 multicast (which is required,
+	 * but the receiver can't tell the difference if this new option is
+	 * enabled.)
+	 *
+	 * This also doesn't implement the 802.11 DMS (directed multicast
+	 * service).
+	 */
+	int multicast_to_unicast;
 };
 
 struct wpa_driver_mesh_bss_params {
-#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS	0x00000001
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS		0x00000001
+#define WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT	0x00000002
+#define WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS	0x00000004
+#define WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE		0x00000008
 	/*
 	 * TODO: Other mesh configuration parameters would go here.
 	 * See NL80211_MESHCONF_* for all the mesh config parameters.
 	 */
 	unsigned int flags;
+	int auto_plinks;
 	int peer_link_timeout;
+	int max_peer_links;
+	u16 ht_opmode;
 };
 
 struct wpa_driver_mesh_join_params {
@@ -1128,7 +1298,7 @@
 	int ie_len;
 	struct hostapd_freq_params freq;
 	int beacon_int;
-	int max_peer_links;
+	int dtim_period;
 	struct wpa_driver_mesh_bss_params conf;
 #define WPA_DRIVER_MESH_FLAG_USER_MPM	0x00000001
 #define WPA_DRIVER_MESH_FLAG_DRIVER_MPM	0x00000002
@@ -1271,6 +1441,24 @@
 #define WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS	0x0000008000000000ULL
 /** Driver supports full AP client state */
 #define WPA_DRIVER_FLAGS_FULL_AP_CLIENT_STATE	0x0000010000000000ULL
+/** Driver supports P2P Listen offload */
+#define WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD     0x0000020000000000ULL
+/** Driver supports FILS */
+#define WPA_DRIVER_FLAGS_SUPPORT_FILS		0x0000040000000000ULL
+/** Driver supports Beacon frame TX rate configuration (legacy rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY	0x0000080000000000ULL
+/** Driver supports Beacon frame TX rate configuration (HT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_HT		0x0000100000000000ULL
+/** Driver supports Beacon frame TX rate configuration (VHT rates) */
+#define WPA_DRIVER_FLAGS_BEACON_RATE_VHT	0x0000200000000000ULL
+/** Driver supports mgmt_tx with random TX address in non-connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA	0x0000400000000000ULL
+/** Driver supports mgmt_tx with random TX addr in connected state */
+#define WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED	0x0000800000000000ULL
+/** Driver supports better BSS reporting with sched_scan in connected mode */
+#define WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI	0x0001000000000000ULL
+/** Driver supports HE capabilities */
+#define WPA_DRIVER_FLAGS_HE_CAPABILITIES	0x0002000000000000ULL
 	u64 flags;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -1365,6 +1553,17 @@
  * offset, namely the 6th byte in the Action frame body.
  */
 #define WPA_DRIVER_FLAGS_TX_POWER_INSERTION		0x00000008
+/**
+ * Driver supports RRM. With this support, the driver will accept to use RRM in
+ * (Re)Association Request frames, without supporting quiet period.
+ */
+#define WPA_DRIVER_FLAGS_SUPPORT_RRM			0x00000010
+
+/** Driver supports setting the scan dwell time */
+#define WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL		0x00000020
+/** Driver supports Beacon Report Measurement */
+#define WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT		0x00000040
+
 	u32 rrm_flags;
 
 	/* Driver concurrency capabilities */
@@ -1410,6 +1609,7 @@
 	u32 flags_mask; /* unset bits in flags */
 #ifdef CONFIG_MESH
 	enum mesh_plink_state plink_state;
+	u16 peer_aid;
 #endif /* CONFIG_MESH */
 	int set; /* Set STA parameters instead of add */
 	u8 qosinfo;
@@ -1419,6 +1619,7 @@
 	size_t supp_channels_len;
 	const u8 *supp_oper_classes;
 	size_t supp_oper_classes_len;
+	int support_p2p_ps;
 };
 
 struct mac_address {
@@ -2650,6 +2851,9 @@
 	 * transmitted on that channel; alternatively the frame may be sent on
 	 * the current operational channel (if in associated state in station
 	 * mode or while operating as an AP.)
+	 *
+	 * If @src differs from the device MAC address, use of a random
+	 * transmitter address is requested for this message exchange.
 	 */
 	int (*send_action)(void *priv, unsigned int freq, unsigned int wait,
 			   const u8 *dst, const u8 *src, const u8 *bssid,
@@ -3272,6 +3476,14 @@
 	int (*macsec_deinit)(void *priv);
 
 	/**
+	 * macsec_get_capability - Inform MKA of this driver's capability
+	 * @priv: Private driver interface data
+	 * @cap: Driver's capability
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
+
+	/**
 	 * enable_protect_frames - Set protect frames status
 	 * @priv: Private driver interface data
 	 * @enabled: TRUE = protect frames enabled
@@ -3281,6 +3493,15 @@
 	int (*enable_protect_frames)(void *priv, Boolean enabled);
 
 	/**
+	 * enable_encrypt - Set encryption status
+	 * @priv: Private driver interface data
+	 * @enabled: TRUE = encrypt outgoing traffic
+	 *           FALSE = integrity-only protection on outgoing traffic
+	 * Returns: 0 on success, -1 on failure (or if not supported)
+	 */
+	int (*enable_encrypt)(void *priv, Boolean enabled);
+
+	/**
 	 * set_replay_protect - Set replay protect status and window size
 	 * @priv: Private driver interface data
 	 * @enabled: TRUE = replay protect enabled
@@ -3294,11 +3515,9 @@
 	 * set_current_cipher_suite - Set current cipher suite
 	 * @priv: Private driver interface data
 	 * @cs: EUI64 identifier
-	 * @cs_len: Length of the cs buffer in octets
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*set_current_cipher_suite)(void *priv, const u8 *cs,
-					size_t cs_len);
+	int (*set_current_cipher_suite)(void *priv, u64 cs);
 
 	/**
 	 * enable_controlled_port - Set controlled port status
@@ -3312,155 +3531,129 @@
 	/**
 	 * get_receive_lowest_pn - Get receive lowest pn
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @an: association number
-	 * @lowest_pn: lowest accept pn
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
-				     u32 *lowest_pn);
+	int (*get_receive_lowest_pn)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * get_transmit_next_pn - Get transmit next pn
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @an: association number
-	 * @next_pn: next pn
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
-				    u32 *next_pn);
+	int (*get_transmit_next_pn)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * set_transmit_next_pn - Set transmit next pn
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @an: association number
-	 * @next_pn: next pn
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
-				    u32 next_pn);
-
-	/**
-	 * get_available_receive_sc - get available receive channel
-	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * Returns: 0 on success, -1 on failure (or if not supported)
-	 */
-	int (*get_available_receive_sc)(void *priv, u32 *channel);
+	int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * create_receive_sc - create secure channel for receiving
 	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * @sci_addr: secure channel identifier - address
-	 * @sci_port: secure channel identifier - port
+	 * @sc: secure channel
 	 * @conf_offset: confidentiality offset (0, 30, or 50)
 	 * @validation: frame validation policy (0 = Disabled, 1 = Checked,
 	 *	2 = Strict)
 	 * Returns: 0 on success, -1 on failure (or if not supported)
 	 */
-	int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
-				 u16 sci_port, unsigned int conf_offset,
+	int (*create_receive_sc)(void *priv, struct receive_sc *sc,
+				 unsigned int conf_offset,
 				 int validation);
 
 	/**
 	 * delete_receive_sc - delete secure connection for receiving
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
+	 * @sc: secure channel
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*delete_receive_sc)(void *priv, u32 channel);
+	int (*delete_receive_sc)(void *priv, struct receive_sc *sc);
 
 	/**
 	 * create_receive_sa - create secure association for receive
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
-	 * @lowest_pn: the lowest packet number can be received
-	 * @sak: the secure association key
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*create_receive_sa)(void *priv, u32 channel, u8 an,
-				 u32 lowest_pn, const u8 *sak);
+	int (*create_receive_sa)(void *priv, struct receive_sa *sa);
+
+	/**
+	 * delete_receive_sa - Delete secure association for receive
+	 * @priv: Private driver interface data from init()
+	 * @sa: Secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_receive_sa)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * enable_receive_sa - enable the SA for receive
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
+	int (*enable_receive_sa)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * disable_receive_sa - disable SA for receive
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel index
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
-
-	/**
-	 * get_available_transmit_sc - get available transmit channel
-	 * @priv: Private driver interface data
-	 * @channel: secure channel
-	 * Returns: 0 on success, -1 on failure (or if not supported)
-	 */
-	int (*get_available_transmit_sc)(void *priv, u32 *channel);
+	int (*disable_receive_sa)(void *priv, struct receive_sa *sa);
 
 	/**
 	 * create_transmit_sc - create secure connection for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @sci_addr: secure channel identifier - address
-	 * @sci_port: secure channel identifier - port
+	 * @sc: secure channel
+	 * @conf_offset: confidentiality offset (0, 30, or 50)
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
-				  u16 sci_port, unsigned int conf_offset);
+	int (*create_transmit_sc)(void *priv, struct transmit_sc *sc,
+				  unsigned int conf_offset);
 
 	/**
 	 * delete_transmit_sc - delete secure connection for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
+	 * @sc: secure channel
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*delete_transmit_sc)(void *priv, u32 channel);
+	int (*delete_transmit_sc)(void *priv, struct transmit_sc *sc);
 
 	/**
 	 * create_transmit_sa - create secure association for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel index
-	 * @an: association number
-	 * @next_pn: the packet number used as next transmit packet
-	 * @confidentiality: True if the SA is to provide confidentiality
-	 *                   as well as integrity
-	 * @sak: the secure association key
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
-				  Boolean confidentiality, const u8 *sak);
+	int (*create_transmit_sa)(void *priv, struct transmit_sa *sa);
+
+	/**
+	 * delete_transmit_sa - Delete secure association for transmit
+	 * @priv: Private driver interface data from init()
+	 * @sa: Secure association
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*delete_transmit_sa)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * enable_transmit_sa - enable SA for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
+	int (*enable_transmit_sa)(void *priv, struct transmit_sa *sa);
 
 	/**
 	 * disable_transmit_sa - disable SA for transmit
 	 * @priv: private driver interface data from init()
-	 * @channel: secure channel
-	 * @an: association number
+	 * @sa: secure association
 	 * Returns: 0 on success, -1 on failure
 	 */
-	int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
+	int (*disable_transmit_sa)(void *priv, struct transmit_sa *sa);
 #endif /* CONFIG_MACSEC */
 
 	/**
@@ -3534,9 +3727,87 @@
 	/**
 	 * abort_scan - Request the driver to abort an ongoing scan
 	 * @priv: Private driver interface data
+	 * @scan_cookie: Cookie identifying the scan request. This is used only
+	 *	when the vendor interface QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN
+	 *	was used to trigger scan. Otherwise, 0 is used.
 	 * Returns 0 on success, -1 on failure
 	 */
-	int (*abort_scan)(void *priv);
+	int (*abort_scan)(void *priv, u64 scan_cookie);
+
+	/**
+	 * configure_data_frame_filters - Request to configure frame filters
+	 * @priv: Private driver interface data
+	 * @filter_flags: The type of frames to filter (bitfield of
+	 * WPA_DATA_FRAME_FILTER_FLAG_*)
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*configure_data_frame_filters)(void *priv, u32 filter_flags);
+
+	/**
+	 * get_ext_capab - Get extended capabilities for the specified interface
+	 * @priv: Private driver interface data
+	 * @type: Interface type for which to get extended capabilities
+	 * @ext_capab: Extended capabilities fetched
+	 * @ext_capab_mask: Extended capabilities mask
+	 * @ext_capab_len: Length of the extended capabilities
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*get_ext_capab)(void *priv, enum wpa_driver_if_type type,
+			     const u8 **ext_capab, const u8 **ext_capab_mask,
+			     unsigned int *ext_capab_len);
+
+	/**
+	 * p2p_lo_start - Start offloading P2P listen to device
+	 * @priv: Private driver interface data
+	 * @freq: Listening frequency (MHz) for P2P listen
+	 * @period: Length of the listen operation in milliseconds
+	 * @interval: Interval for running the listen operation in milliseconds
+	 * @count: Number of times to run the listen operation
+	 * @device_types: Device primary and secondary types
+	 * @dev_types_len: Number of bytes for device_types
+	 * @ies: P2P IE and WSC IE for Probe Response frames
+	 * @ies_len: Length of ies in bytes
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*p2p_lo_start)(void *priv, unsigned int freq,
+			    unsigned int period, unsigned int interval,
+			    unsigned int count,
+			    const u8 *device_types, size_t dev_types_len,
+			    const u8 *ies, size_t ies_len);
+
+	/**
+	 * p2p_lo_stop - Stop P2P listen offload
+	 * @priv: Private driver interface data
+	 * Returns: 0 on success or -1 on failure
+	 */
+	int (*p2p_lo_stop)(void *priv);
+
+	/**
+	 * set_default_scan_ies - Set default scan IEs
+	 * @priv: Private driver interface data
+	 * @ies: Scan default IEs buffer
+	 * @ies_len: Length of IEs in bytes
+	 * Returns: 0 on success or -1 on failure
+	 *
+	 * The driver can use these by default when there are no scan IEs coming
+	 * in the subsequent scan requests. Also in case of one or more of IEs
+	 * given in set_default_scan_ies() are missing in the subsequent scan
+	 * request, the driver should merge the missing scan IEs in the scan
+	 * request from the IEs set by set_default_scan_ies() in the Probe
+	 * Request frames sent.
+	 */
+	int (*set_default_scan_ies)(void *priv, const u8 *ies, size_t ies_len);
+
+	/**
+	 * set_tdls_mode - Set TDLS trigger mode to the host driver
+	 * @priv: Private driver interface data
+	 * @tdls_external_control: Represents if TDLS external trigger control
+	 *  mode is enabled/disabled.
+	 *
+	 * This optional callback can be used to configure the TDLS external
+	 * trigger control mode to the host driver.
+	 */
+	int (*set_tdls_mode)(void *priv, int tdls_external_control);
 };
 
 
@@ -3957,7 +4228,7 @@
 	 * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
 	 *
 	 * The CAC was not successful, and the channel remains in the previous
-	 * state. This may happen due to a radar beeing detected or other
+	 * state. This may happen due to a radar being detected or other
 	 * external influences.
 	 */
 	EVENT_DFS_CAC_ABORTED,
@@ -4021,6 +4292,20 @@
 	 * on a DFS frequency by a driver that supports DFS Offload.
 	 */
 	EVENT_DFS_CAC_STARTED,
+
+	/**
+	 * EVENT_P2P_LO_STOP - Notify that P2P listen offload is stopped
+	 */
+	EVENT_P2P_LO_STOP,
+
+	/**
+	 * EVENT_BEACON_LOSS - Beacon loss detected
+	 *
+	 * This event indicates that no Beacon frames has been received from
+	 * the current AP. This may indicate that the AP is not anymore in
+	 * range.
+	 */
+	EVENT_BEACON_LOSS,
 };
 
 
@@ -4113,6 +4398,16 @@
 		size_t resp_ies_len;
 
 		/**
+		 * resp_frame - (Re)Association Response frame
+		 */
+		const u8 *resp_frame;
+
+		/**
+		 * resp_frame_len - (Re)Association Response frame length
+		 */
+		size_t resp_frame_len;
+
+		/**
 		 * beacon_ies - Beacon or Probe Response IEs
 		 *
 		 * Optional Beacon/ProbeResp data: IEs included in Beacon or
@@ -4406,6 +4701,17 @@
 		 * status_code - Status Code from (Re)association Response
 		 */
 		u16 status_code;
+
+		/**
+		 * timed_out - Whether failure is due to timeout (etc.) rather
+		 * than explicit rejection response from the AP.
+		 */
+		int timed_out;
+
+		/**
+		 * timeout_reason - Reason for the timeout
+		 */
+		const char *timeout_reason;
 	} assoc_reject;
 
 	struct timeout_event {
@@ -4489,6 +4795,11 @@
 	 * @external_scan: Whether the scan info is for an external scan
 	 * @nl_scan_event: 1 if the source of this scan event is a normal scan,
 	 * 	0 if the source of the scan event is a vendor scan
+	 * @scan_start_tsf: Time when the scan started in terms of TSF of the
+	 *	BSS that the interface that requested the scan is connected to
+	 *	(if available).
+	 * @scan_start_tsf_bssid: The BSSID according to which %scan_start_tsf
+	 *	is set.
 	 */
 	struct scan_info {
 		int aborted;
@@ -4498,6 +4809,8 @@
 		size_t num_ssids;
 		int external_scan;
 		int nl_scan_event;
+		u64 scan_start_tsf;
+		u8 scan_start_tsf_bssid[ETH_ALEN];
 	} scan_info;
 
 	/**
@@ -4587,9 +4900,12 @@
 	/**
 	 * struct low_ack - Data for EVENT_STATION_LOW_ACK events
 	 * @addr: station address
+	 * @num_packets: Number of packets lost (consecutive packets not
+	 * acknowledged)
 	 */
 	struct low_ack {
 		u8 addr[ETH_ALEN];
+		u32 num_packets;
 	} low_ack;
 
 	/**
@@ -4740,6 +5056,27 @@
 		u16 ch_width;
 		enum hostapd_hw_mode hw_mode;
 	} acs_selected_channels;
+
+	/**
+	 * struct p2p_lo_stop - Reason code for P2P Listen offload stop event
+	 * @reason_code: Reason for stopping offload
+	 *	P2P_LO_STOPPED_REASON_COMPLETE: Listen offload finished as
+	 *	scheduled.
+	 *	P2P_LO_STOPPED_REASON_RECV_STOP_CMD: Host requested offload to
+	 *	be stopped.
+	 *	P2P_LO_STOPPED_REASON_INVALID_PARAM: Invalid listen offload
+	 *	parameters.
+	 *	P2P_LO_STOPPED_REASON_NOT_SUPPORTED: Listen offload not
+	 *	supported by device.
+	 */
+	struct p2p_lo_stop {
+		enum {
+			P2P_LO_STOPPED_REASON_COMPLETE = 0,
+			P2P_LO_STOPPED_REASON_RECV_STOP_CMD,
+			P2P_LO_STOPPED_REASON_INVALID_PARAM,
+			P2P_LO_STOPPED_REASON_NOT_SUPPORTED,
+		} reason_code;
+	} p2p_lo_stop;
 };
 
 /**
@@ -4819,8 +5156,56 @@
 struct wowlan_triggers *
 wpa_get_wowlan_triggers(const char *wowlan_triggers,
 			const struct wpa_driver_capa *capa);
+/* Convert driver flag to string */
+const char * driver_flag_to_string(u64 flag);
 
 /* NULL terminated array of linked in driver wrappers */
 extern const struct wpa_driver_ops *const wpa_drivers[];
 
+
+/* Available drivers */
+
+#ifdef CONFIG_DRIVER_WEXT
+extern const struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+/* driver_nl80211.c */
+extern const struct wpa_driver_ops wpa_driver_nl80211_ops;
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern const struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_BSD
+extern const struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_OPENBSD
+/* driver_openbsd.c */
+extern const struct wpa_driver_ops wpa_driver_openbsd_ops;
+#endif /* CONFIG_DRIVER_OPENBSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+extern const struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+/* driver_macsec_qca.c */
+extern const struct wpa_driver_ops wpa_driver_macsec_qca_ops;
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
+/* driver_macsec_linux.c */
+extern const struct wpa_driver_ops wpa_driver_macsec_linux_ops;
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+/* driver_roboswitch.c */
+extern const struct wpa_driver_ops wpa_driver_roboswitch_ops;
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+/* driver_atheros.c */
+extern const struct wpa_driver_ops wpa_driver_atheros_ops;
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+extern const struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
+#endif /* CONFIG_DRIVER_NONE */
+
 #endif /* DRIVER_H */
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index ba3cad0..a88345f 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -868,6 +868,16 @@
 		return;
 	}
 
+	if (stype == WLAN_FC_STYPE_ACTION &&
+	    (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) == 0 ||
+	     is_broadcast_ether_addr(mgmt->bssid))) {
+		os_memset(&event, 0, sizeof(event));
+		event.rx_mgmt.frame = buf;
+		event.rx_mgmt.frame_len = len;
+		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+		return;
+	}
+
 	if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
 		wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
 			   __func__);
@@ -889,12 +899,6 @@
 		iebuf = mgmt->u.reassoc_req.variable;
 		drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
 		break;
-	case WLAN_FC_STYPE_ACTION:
-		os_memset(&event, 0, sizeof(event));
-		event.rx_mgmt.frame = buf;
-		event.rx_mgmt.frame_len = len;
-		wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
-		break;
 	case WLAN_FC_STYPE_AUTH:
 		if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
 			break;
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index fd73f2e..8621aa0 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -726,7 +726,7 @@
 }
 
 
-static int 
+static int
 bsd_flush(void *priv)
 {
 	u8 allsta[IEEE80211_ADDR_LEN];
@@ -1376,11 +1376,16 @@
 	result->caps = sr->isr_capinfo;
 	result->qual = sr->isr_rssi;
 	result->noise = sr->isr_noise;
+
+#ifdef __FreeBSD__
 	/*
 	 * the rssi value reported by the kernel is in 0.5dB steps relative to
 	 * the reported noise floor. see ieee80211_node.h for details.
 	 */
 	result->level = sr->isr_rssi / 2 + sr->isr_noise;
+#else
+	result->level = sr->isr_rssi;
+#endif
 
 	pos = (u8 *)(result + 1);
 
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index b32d35f..b6bcbca 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -1,6 +1,6 @@
 /*
  * Common driver-related functions
- * Copyright (c) 2003-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -80,6 +80,8 @@
 	E2S(NEW_PEER_CANDIDATE);
 	E2S(ACS_CHANNEL_SELECTED);
 	E2S(DFS_CAC_STARTED);
+	E2S(P2P_LO_STOP);
+	E2S(BEACON_LOSS);
 	}
 
 	return "UNKNOWN";
@@ -218,3 +220,55 @@
 	os_free(buf);
 	return triggers;
 }
+
+
+const char * driver_flag_to_string(u64 flag)
+{
+#define DF2S(x) case WPA_DRIVER_FLAGS_ ## x: return #x
+	switch (flag) {
+	DF2S(DRIVER_IE);
+	DF2S(SET_KEYS_AFTER_ASSOC);
+	DF2S(DFS_OFFLOAD);
+	DF2S(4WAY_HANDSHAKE);
+	DF2S(WIRED);
+	DF2S(SME);
+	DF2S(AP);
+	DF2S(SET_KEYS_AFTER_ASSOC_DONE);
+	DF2S(HT_2040_COEX);
+	DF2S(P2P_CONCURRENT);
+	DF2S(P2P_DEDICATED_INTERFACE);
+	DF2S(P2P_CAPABLE);
+	DF2S(AP_TEARDOWN_SUPPORT);
+	DF2S(P2P_MGMT_AND_NON_P2P);
+	DF2S(SANE_ERROR_CODES);
+	DF2S(OFFCHANNEL_TX);
+	DF2S(EAPOL_TX_STATUS);
+	DF2S(DEAUTH_TX_STATUS);
+	DF2S(BSS_SELECTION);
+	DF2S(TDLS_SUPPORT);
+	DF2S(TDLS_EXTERNAL_SETUP);
+	DF2S(PROBE_RESP_OFFLOAD);
+	DF2S(AP_UAPSD);
+	DF2S(INACTIVITY_TIMER);
+	DF2S(AP_MLME);
+	DF2S(SAE);
+	DF2S(OBSS_SCAN);
+	DF2S(IBSS);
+	DF2S(RADAR);
+	DF2S(DEDICATED_P2P_DEVICE);
+	DF2S(QOS_MAPPING);
+	DF2S(AP_CSA);
+	DF2S(MESH);
+	DF2S(ACS_OFFLOAD);
+	DF2S(KEY_MGMT_OFFLOAD);
+	DF2S(TDLS_CHANNEL_SWITCH);
+	DF2S(HT_IBSS);
+	DF2S(VHT_IBSS);
+	DF2S(SUPPORT_HW_MODE_ANY);
+	DF2S(OFFCHANNEL_SIMULTANEOUS);
+	DF2S(FULL_AP_CLIENT_STATE);
+	DF2S(P2P_LISTEN_OFFLOAD);
+	}
+	return "UNKNOWN";
+#undef DF2S
+}
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
new file mode 100644
index 0000000..5dab77a
--- /dev/null
+++ b/src/drivers/driver_macsec_linux.c
@@ -0,0 +1,1265 @@
+/*
+ * Driver interaction with Linux MACsec kernel module
+ * Copyright (c) 2016, Sabrina Dubroca <sd@queasysnail.net> and Red Hat, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/route/link.h>
+#include <netlink/route/link/macsec.h>
+#include <linux/if_macsec.h>
+#include <inttypes.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "pae/ieee802_1x_kay.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#define DRV_PREFIX "macsec_linux: "
+
+#define UNUSED_SCI 0xffffffffffffffff
+
+struct cb_arg {
+	struct macsec_drv_data *drv;
+	u32 *pn;
+	int ifindex;
+	u8 txsa;
+	u8 rxsa;
+	u64 rxsci;
+};
+
+struct macsec_genl_ctx {
+	struct nl_sock *sk;
+	int macsec_genl_id;
+	struct cb_arg cb_arg;
+};
+
+struct macsec_drv_data {
+	struct driver_wired_common_data common;
+	struct rtnl_link *link;
+	struct nl_cache *link_cache;
+	struct nl_sock *sk;
+	struct macsec_genl_ctx ctx;
+
+	struct netlink_data *netlink;
+	struct nl_handle *nl;
+	char ifname[IFNAMSIZ + 1];
+	int ifi;
+	int parent_ifi;
+
+	Boolean created_link;
+
+	Boolean controlled_port_enabled;
+	Boolean controlled_port_enabled_set;
+
+	Boolean protect_frames;
+	Boolean protect_frames_set;
+
+	Boolean encrypt;
+	Boolean encrypt_set;
+
+	Boolean replay_protect;
+	Boolean replay_protect_set;
+
+	u32 replay_window;
+
+	u8 encoding_sa;
+	Boolean encoding_sa_set;
+};
+
+
+static int dump_callback(struct nl_msg *msg, void *argp);
+
+
+static struct nl_msg * msg_prepare(enum macsec_nl_commands cmd,
+				   const struct macsec_genl_ctx *ctx,
+				   unsigned int ifindex)
+{
+	struct nl_msg *msg;
+
+	msg = nlmsg_alloc();
+	if (!msg) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc message");
+		return NULL;
+	}
+
+	if (!genlmsg_put(msg, 0, 0, ctx->macsec_genl_id, 0, 0, cmd, 0)) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to put header");
+		goto nla_put_failure;
+	}
+
+	NLA_PUT_U32(msg, MACSEC_ATTR_IFINDEX, ifindex);
+
+	return msg;
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return NULL;
+}
+
+
+static int nla_put_rxsc_config(struct nl_msg *msg, u64 sci)
+{
+	struct nlattr *nest = nla_nest_start(msg, MACSEC_ATTR_RXSC_CONFIG);
+
+	if (!nest)
+		return -1;
+
+	NLA_PUT_U64(msg, MACSEC_RXSC_ATTR_SCI, sci);
+
+	nla_nest_end(msg, nest);
+
+	return 0;
+
+nla_put_failure:
+	return -1;
+}
+
+
+static int init_genl_ctx(struct macsec_drv_data *drv)
+{
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	ctx->sk = nl_socket_alloc();
+	if (!ctx->sk) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to alloc genl socket");
+		return -1;
+	}
+
+	if (genl_connect(ctx->sk) < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "connection to genl socket failed");
+		goto out_free;
+	}
+
+	ctx->macsec_genl_id = genl_ctrl_resolve(ctx->sk, "macsec");
+	if (ctx->macsec_genl_id < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "genl resolve failed");
+		goto out_free;
+	}
+
+	memset(&ctx->cb_arg, 0, sizeof(ctx->cb_arg));
+	ctx->cb_arg.drv = drv;
+
+	nl_socket_modify_cb(ctx->sk, NL_CB_VALID, NL_CB_CUSTOM, dump_callback,
+			    &ctx->cb_arg);
+
+	return 0;
+
+out_free:
+	nl_socket_free(ctx->sk);
+	ctx->sk = NULL;
+	return -1;
+}
+
+
+static int try_commit(struct macsec_drv_data *drv)
+{
+	int err;
+
+	if (!drv->link)
+		return 0;
+
+	if (drv->controlled_port_enabled_set) {
+		struct rtnl_link *change = rtnl_link_alloc();
+
+		if (!change)
+			return -1;
+
+		rtnl_link_set_name(change, drv->ifname);
+
+		if (drv->controlled_port_enabled)
+			rtnl_link_set_flags(change, IFF_UP);
+		else
+			rtnl_link_unset_flags(change, IFF_UP);
+
+		err = rtnl_link_change(drv->sk, change, change, 0);
+		if (err < 0)
+			return err;
+
+		rtnl_link_put(change);
+
+		drv->controlled_port_enabled_set = FALSE;
+	}
+
+	if (drv->protect_frames_set)
+		rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
+
+	if (drv->encrypt_set)
+		rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
+
+	if (drv->replay_protect_set) {
+		rtnl_link_macsec_set_replay_protect(drv->link,
+						    drv->replay_protect);
+		if (drv->replay_protect)
+			rtnl_link_macsec_set_window(drv->link,
+						    drv->replay_window);
+	}
+
+	if (drv->encoding_sa_set)
+		rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
+
+	err = rtnl_link_add(drv->sk, drv->link, 0);
+	if (err < 0)
+		return err;
+
+	drv->protect_frames_set = FALSE;
+	drv->encrypt_set = FALSE;
+	drv->replay_protect_set = FALSE;
+
+	return 0;
+}
+
+
+static void macsec_drv_wpa_deinit(void *priv)
+{
+	struct macsec_drv_data *drv = priv;
+
+	driver_wired_deinit_common(&drv->common);
+	os_free(drv);
+}
+
+
+static void * macsec_drv_wpa_init(void *ctx, const char *ifname)
+{
+	struct macsec_drv_data *drv;
+
+	drv = os_zalloc(sizeof(*drv));
+	if (!drv)
+		return NULL;
+
+	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
+		os_free(drv);
+		return NULL;
+	}
+
+	return drv;
+}
+
+
+static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	drv->sk = nl_socket_alloc();
+	if (!drv->sk)
+		return -1;
+
+	err = nl_connect(drv->sk, NETLINK_ROUTE);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX
+			   "Unable to connect NETLINK_ROUTE socket: %s",
+			   strerror(errno));
+		goto sock;
+	}
+
+	err = rtnl_link_alloc_cache(drv->sk, AF_UNSPEC, &drv->link_cache);
+	if (err < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "Unable to get link cache: %s",
+			   strerror(errno));
+		goto sock;
+	}
+
+	drv->parent_ifi = rtnl_link_name2i(drv->link_cache, drv->common.ifname);
+	if (drv->parent_ifi == 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX
+			   "couldn't find ifindex for interface %s",
+			   drv->common.ifname);
+		goto cache;
+	}
+
+	err = init_genl_ctx(drv);
+	if (err < 0)
+		goto cache;
+
+	return 0;
+
+cache:
+	nl_cache_free(drv->link_cache);
+	drv->link_cache = NULL;
+sock:
+	nl_socket_free(drv->sk);
+	drv->sk = NULL;
+	return -1;
+}
+
+
+static int macsec_drv_macsec_deinit(void *priv)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	if (drv->sk)
+		nl_socket_free(drv->sk);
+	drv->sk = NULL;
+
+	if (drv->link_cache)
+		nl_cache_free(drv->link_cache);
+	drv->link_cache = NULL;
+
+	if (drv->ctx.sk)
+		nl_socket_free(drv->ctx.sk);
+
+	return 0;
+}
+
+
+static int macsec_drv_get_capability(void *priv, enum macsec_cap *cap)
+{
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	*cap = MACSEC_CAP_INTEG_AND_CONF;
+
+	return 0;
+}
+
+
+/**
+ * macsec_drv_enable_protect_frames - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = protect frames enabled
+ *           FALSE = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_protect_frames(void *priv, Boolean enabled)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+	drv->protect_frames_set = TRUE;
+	drv->protect_frames = enabled;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_enable_encrypt - Set protect frames status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = protect frames enabled
+ *           FALSE = protect frames disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_encrypt(void *priv, Boolean enabled)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+	drv->encrypt_set = TRUE;
+	drv->encrypt = enabled;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_replay_protect - Set replay protect status and window size
+ * @priv: Private driver interface data
+ * @enabled: TRUE = replay protect enabled
+ *           FALSE = replay protect disabled
+ * @window: replay window size, valid only when replay protect enabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_replay_protect(void *priv, Boolean enabled,
+					 u32 window)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s, %u", __func__,
+		   enabled ? "TRUE" : "FALSE", window);
+
+	drv->replay_protect_set = TRUE;
+	drv->replay_protect = enabled;
+	if (enabled)
+		drv->replay_window = window;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_set_current_cipher_suite - Set current cipher suite
+ * @priv: Private driver interface data
+ * @cs: EUI64 identifier
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_current_cipher_suite(void *priv, u64 cs)
+{
+	wpa_printf(MSG_DEBUG, "%s -> %016" PRIx64, __func__, cs);
+	return 0;
+}
+
+
+/**
+ * macsec_drv_enable_controlled_port - Set controlled port status
+ * @priv: Private driver interface data
+ * @enabled: TRUE = controlled port enabled
+ *           FALSE = controlled port disabled
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_enable_controlled_port(void *priv, Boolean enabled)
+{
+	struct macsec_drv_data *drv = priv;
+
+	wpa_printf(MSG_DEBUG, "%s -> %s", __func__, enabled ? "TRUE" : "FALSE");
+
+	drv->controlled_port_enabled = enabled;
+	drv->controlled_port_enabled_set = TRUE;
+
+	return try_commit(drv);
+}
+
+
+static struct nla_policy sa_policy[MACSEC_SA_ATTR_MAX + 1] = {
+	[MACSEC_SA_ATTR_AN] = { .type = NLA_U8 },
+	[MACSEC_SA_ATTR_ACTIVE] = { .type = NLA_U8 },
+	[MACSEC_SA_ATTR_PN] = { .type = NLA_U32 },
+	[MACSEC_SA_ATTR_KEYID] = { .type = NLA_BINARY },
+};
+
+static struct nla_policy sc_policy[MACSEC_RXSC_ATTR_MAX + 1] = {
+	[MACSEC_RXSC_ATTR_SCI] = { .type = NLA_U64 },
+	[MACSEC_RXSC_ATTR_ACTIVE] = { .type = NLA_U8 },
+	[MACSEC_RXSC_ATTR_SA_LIST] = { .type = NLA_NESTED },
+};
+
+static struct nla_policy main_policy[MACSEC_ATTR_MAX + 1] = {
+	[MACSEC_ATTR_IFINDEX] = { .type = NLA_U32 },
+	[MACSEC_ATTR_SECY] = { .type = NLA_NESTED },
+	[MACSEC_ATTR_TXSA_LIST] = { .type = NLA_NESTED },
+	[MACSEC_ATTR_RXSC_LIST] = { .type = NLA_NESTED },
+};
+
+static int dump_callback(struct nl_msg *msg, void *argp)
+{
+	struct nlmsghdr *ret_hdr = nlmsg_hdr(msg);
+	struct nlattr *tb_msg[MACSEC_ATTR_MAX + 1];
+	struct cb_arg *arg = (struct cb_arg *) argp;
+	struct genlmsghdr *gnlh = (struct genlmsghdr *) nlmsg_data(ret_hdr);
+	int err;
+
+	if (ret_hdr->nlmsg_type != arg->drv->ctx.macsec_genl_id)
+		return 0;
+
+	err = nla_parse(tb_msg, MACSEC_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+			genlmsg_attrlen(gnlh, 0), main_policy);
+	if (err < 0)
+		return 0;
+
+	if (!tb_msg[MACSEC_ATTR_IFINDEX])
+		return 0;
+
+	if (nla_get_u32(tb_msg[MACSEC_ATTR_IFINDEX]) != (u32) arg->ifindex)
+		return 0;
+
+	if (arg->txsa < 4 && !tb_msg[MACSEC_ATTR_TXSA_LIST]) {
+		return 0;
+	} else if (arg->txsa < 4) {
+		struct nlattr *nla;
+		int rem;
+
+		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_TXSA_LIST], rem) {
+			struct nlattr *tb[MACSEC_SA_ATTR_MAX + 1];
+
+			err = nla_parse_nested(tb, MACSEC_SA_ATTR_MAX, nla,
+					       sa_policy);
+			if (err < 0)
+				continue;
+			if (!tb[MACSEC_SA_ATTR_AN])
+				continue;
+			if (nla_get_u8(tb[MACSEC_SA_ATTR_AN]) != arg->txsa)
+				continue;
+			if (!tb[MACSEC_SA_ATTR_PN])
+				return 0;
+			*arg->pn = nla_get_u32(tb[MACSEC_SA_ATTR_PN]);
+			return 0;
+		}
+
+		return 0;
+	}
+
+	if (arg->rxsci == UNUSED_SCI)
+		return 0;
+
+	if (tb_msg[MACSEC_ATTR_RXSC_LIST]) {
+		struct nlattr *nla;
+		int rem;
+
+		nla_for_each_nested(nla, tb_msg[MACSEC_ATTR_RXSC_LIST], rem) {
+			struct nlattr *tb[MACSEC_RXSC_ATTR_MAX + 1];
+
+			err = nla_parse_nested(tb, MACSEC_RXSC_ATTR_MAX, nla,
+					       sc_policy);
+			if (err < 0)
+				return 0;
+			if (!tb[MACSEC_RXSC_ATTR_SCI])
+				continue;
+			if (nla_get_u64(tb[MACSEC_RXSC_ATTR_SCI]) != arg->rxsci)
+				continue;
+			if (!tb[MACSEC_RXSC_ATTR_SA_LIST])
+				return 0;
+
+			nla_for_each_nested(nla, tb[MACSEC_RXSC_ATTR_SA_LIST],
+					    rem) {
+				struct nlattr *tb_sa[MACSEC_SA_ATTR_MAX + 1];
+
+				err = nla_parse_nested(tb_sa,
+						       MACSEC_SA_ATTR_MAX, nla,
+						       sa_policy);
+				if (err < 0)
+					continue;
+				if (!tb_sa[MACSEC_SA_ATTR_AN])
+					continue;
+				if (nla_get_u8(tb_sa[MACSEC_SA_ATTR_AN]) !=
+				    arg->rxsa)
+					continue;
+				if (!tb_sa[MACSEC_SA_ATTR_PN])
+					return 0;
+				*arg->pn =
+					nla_get_u32(tb_sa[MACSEC_SA_ATTR_PN]);
+
+				return 0;
+			}
+
+			return 0;
+		}
+
+		return 0;
+	}
+
+	return 0;
+}
+
+
+static int nl_send_recv(struct nl_sock *sk, struct nl_msg *msg)
+{
+	int ret;
+
+	ret = nl_send_auto_complete(sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to send: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+		return ret;
+	}
+
+	ret = nl_recvmsgs_default(sk);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to recv: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+	return ret;
+}
+
+
+static int do_dump(struct macsec_drv_data *drv, u8 txsa, u64 rxsci, u8 rxsa,
+		   u32 *pn)
+{
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	int ret = 1;
+
+	ctx->cb_arg.ifindex = drv->ifi;
+	ctx->cb_arg.rxsci = rxsci;
+	ctx->cb_arg.rxsa = rxsa;
+	ctx->cb_arg.txsa = txsa;
+	ctx->cb_arg.pn = pn;
+
+	msg = nlmsg_alloc();
+	if (!msg) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to alloc message",
+			   __func__);
+		return 1;
+	}
+
+	if (!genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, ctx->macsec_genl_id, 0,
+			 NLM_F_DUMP, MACSEC_CMD_GET_TXSC, 0)) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "%s: failed to put header",
+			   __func__);
+		goto out_free_msg;
+	}
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "failed to communicate: %d (%s)",
+			   ret, nl_geterror(-ret));
+
+	ctx->cb_arg.pn = 0;
+
+out_free_msg:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_get_receive_lowest_pn - Get receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s", __func__);
+
+	err = do_dump(drv, 0xff, mka_sci_u64(&sa->sc->sci), sa->an,
+		      &sa->lowest_pn);
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: result %d", __func__,
+		   sa->lowest_pn);
+
+	return err;
+}
+
+
+/**
+ * macsec_drv_get_transmit_next_pn - Get transmit next PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	err = do_dump(drv, sa->an, UNUSED_SCI, 0xff, &sa->next_pn);
+	wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: err %d result %d", __func__, err,
+		   sa->next_pn);
+	return err;
+}
+
+
+/**
+ * macsec_drv_set_transmit_next_pn - Set transmit next pn
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d: %d", __func__, sa->an, sa->next_pn);
+
+	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "failed to communicate: %d (%s)",
+			   ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+#define SCISTR MACSTR "::%hx"
+#define SCI2STR(addr, port) MAC2STR(addr), htons(port)
+
+/**
+ * macsec_drv_create_receive_sc - Create secure channel for receiving
+ * @priv: Private driver interface data
+ * @sc: secure channel
+ * @sci_addr: secure channel identifier - address
+ * @sci_port: secure channel identifier - port
+ * @conf_offset: confidentiality offset (0, 30, or 50)
+ * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+ *	2 = Strict)
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
+					unsigned int conf_offset,
+					int validation)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
+		   SCI2STR(sc->sci.addr, sc->sci.port));
+
+	msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+		goto nla_put_failure;
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sc - Delete secure connection for receiving
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
+		   SCI2STR(sc->sci.addr, sc->sci.port));
+
+	msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sc->sci)))
+		goto nla_put_failure;
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_create_receive_sa - Create secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_receive);
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+		&sa->pkey->key_identifier);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_delete_receive_sa - Delete secure association for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, mka_sci_u64(&sa->sc->sci)))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int set_active_rx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+			    u64 sci, unsigned char an, Boolean state)
+{
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, ifindex);
+	if (!msg)
+		return ret;
+
+	if (nla_put_rxsc_config(msg, sci))
+		goto nla_put_failure;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0)
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_enable_receive_sa - Enable the SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+				sa->an, TRUE);
+}
+
+
+/**
+ * macsec_drv_disable_receive_sa - Disable SA for receive
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+		   SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+
+	return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
+				sa->an, FALSE);
+}
+
+
+static struct rtnl_link * lookup_sc(struct nl_cache *cache, int parent, u64 sci)
+{
+	struct rtnl_link *needle;
+	void *match;
+
+	needle = rtnl_link_macsec_alloc();
+	if (!needle)
+		return NULL;
+
+	rtnl_link_set_link(needle, parent);
+	rtnl_link_macsec_set_sci(needle, sci);
+
+	match = nl_cache_find(cache, (struct nl_object *) needle);
+	rtnl_link_put(needle);
+
+	return (struct rtnl_link *) match;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sc - Create secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * @conf_offset: confidentiality offset
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sc(
+	void *priv, struct transmit_sc *sc,
+	enum confidentiality_offset conf_offset)
+{
+	struct macsec_drv_data *drv = priv;
+	struct rtnl_link *link;
+	char *ifname;
+	u64 sci;
+	int err;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	link = rtnl_link_macsec_alloc();
+	if (!link) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+		return -1;
+	}
+
+	rtnl_link_set_link(link, drv->parent_ifi);
+
+	sci = mka_sci_u64(&sc->sci);
+	rtnl_link_macsec_set_sci(link, sci);
+
+	drv->created_link = TRUE;
+
+	err = rtnl_link_add(drv->sk, link, NLM_F_CREATE);
+	if (err == -NLE_BUSY) {
+		wpa_printf(MSG_INFO,
+			   DRV_PREFIX "link already exists, using it");
+		drv->created_link = FALSE;
+	} else if (err < 0) {
+		rtnl_link_put(link);
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't create link: err %d",
+			   err);
+		return err;
+	}
+
+	rtnl_link_put(link);
+
+	nl_cache_refill(drv->sk, drv->link_cache);
+	link = lookup_sc(drv->link_cache, drv->parent_ifi, sci);
+	if (!link) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't find link");
+		return -1;
+	}
+
+	drv->ifi = rtnl_link_get_ifindex(link);
+	ifname = rtnl_link_get_name(link);
+	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+	rtnl_link_put(link);
+
+	drv->link = rtnl_link_macsec_alloc();
+	if (!drv->link) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't allocate link");
+		return -1;
+	}
+
+	rtnl_link_set_name(drv->link, drv->ifname);
+
+	/* In case some settings have already been done but we couldn't apply
+	 * them. */
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sc - Delete secure connection for transmit
+ * @priv: private driver interface data from init()
+ * @sc: secure channel
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
+{
+	struct macsec_drv_data *drv = priv;
+	int err;
+
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	if (!drv->created_link) {
+		rtnl_link_put(drv->link);
+		drv->link = NULL;
+		wpa_printf(MSG_DEBUG, DRV_PREFIX
+			   "we didn't create the link, leave it alone");
+		return 0;
+	}
+
+	err = rtnl_link_delete(drv->sk, drv->link);
+	if (err < 0)
+		wpa_printf(MSG_ERROR, DRV_PREFIX "couldn't delete link");
+	rtnl_link_put(drv->link);
+	drv->link = NULL;
+
+	return err;
+}
+
+
+/**
+ * macsec_drv_create_transmit_sa - Create secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+	msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+	NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEYID, sizeof(sa->pkey->key_identifier),
+		&sa->pkey->key_identifier);
+	NLA_PUT(msg, MACSEC_SA_ATTR_KEY, sa->pkey->key_len, sa->pkey->key);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, sa->enable_transmit);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_delete_transmit_sa - Delete secure association for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+	msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+static int set_active_tx_sa(const struct macsec_genl_ctx *ctx, int ifindex,
+			    unsigned char an, Boolean state)
+{
+	struct nl_msg *msg;
+	struct nlattr *nest;
+	int ret = -1;
+
+	msg = msg_prepare(MACSEC_CMD_UPD_TXSA, ctx, ifindex);
+	if (!msg)
+		return ret;
+
+	nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+	if (!nest)
+		goto nla_put_failure;
+
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, an);
+	NLA_PUT_U8(msg, MACSEC_SA_ATTR_ACTIVE, !!state);
+
+	nla_nest_end(msg, nest);
+
+	ret = nl_send_recv(ctx->sk, msg);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR,
+			   DRV_PREFIX "%s: failed to communicate: %d (%s)",
+			   __func__, ret, nl_geterror(-ret));
+	}
+
+nla_put_failure:
+	nlmsg_free(msg);
+	return ret;
+}
+
+
+/**
+ * macsec_drv_enable_transmit_sa - Enable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+	ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE);
+	if (ret < 0) {
+		wpa_printf(MSG_ERROR, DRV_PREFIX "failed to enable txsa");
+		return ret;
+	}
+
+	drv->encoding_sa_set = TRUE;
+	drv->encoding_sa = sa->an;
+
+	return try_commit(drv);
+}
+
+
+/**
+ * macsec_drv_disable_transmit_sa - Disable SA for transmit
+ * @priv: private driver interface data from init()
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure
+ */
+static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
+{
+	struct macsec_drv_data *drv = priv;
+	struct macsec_genl_ctx *ctx = &drv->ctx;
+
+	wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+
+	return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE);
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
+	.name = "macsec_linux",
+	.desc = "MACsec Ethernet driver for Linux",
+	.get_ssid = driver_wired_get_ssid,
+	.get_bssid = driver_wired_get_bssid,
+	.get_capa = driver_wired_get_capa,
+	.init = macsec_drv_wpa_init,
+	.deinit = macsec_drv_wpa_deinit,
+
+	.macsec_init = macsec_drv_macsec_init,
+	.macsec_deinit = macsec_drv_macsec_deinit,
+	.macsec_get_capability = macsec_drv_get_capability,
+	.enable_protect_frames = macsec_drv_enable_protect_frames,
+	.enable_encrypt = macsec_drv_enable_encrypt,
+	.set_replay_protect = macsec_drv_set_replay_protect,
+	.set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
+	.enable_controlled_port = macsec_drv_enable_controlled_port,
+	.get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
+	.get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
+	.set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
+	.create_receive_sc = macsec_drv_create_receive_sc,
+	.delete_receive_sc = macsec_drv_delete_receive_sc,
+	.create_receive_sa = macsec_drv_create_receive_sa,
+	.delete_receive_sa = macsec_drv_delete_receive_sa,
+	.enable_receive_sa = macsec_drv_enable_receive_sa,
+	.disable_receive_sa = macsec_drv_disable_receive_sa,
+	.create_transmit_sc = macsec_drv_create_transmit_sc,
+	.delete_transmit_sc = macsec_drv_delete_transmit_sc,
+	.create_transmit_sa = macsec_drv_create_transmit_sa,
+	.delete_transmit_sa = macsec_drv_delete_transmit_sa,
+	.enable_transmit_sa = macsec_drv_enable_transmit_sa,
+	.disable_transmit_sa = macsec_drv_disable_transmit_sa,
+};
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
index 3eae2f8..d3be19c 100644
--- a/src/drivers/driver_macsec_qca.c
+++ b/src/drivers/driver_macsec_qca.c
@@ -11,6 +11,7 @@
 #include "includes.h"
 #include <sys/ioctl.h>
 #include <net/if.h>
+#include <inttypes.h>
 #ifdef __linux__
 #include <netpacket/packet.h>
 #include <net/if_arp.h>
@@ -28,7 +29,9 @@
 #include "utils/eloop.h"
 #include "common/defs.h"
 #include "common/ieee802_1x_defs.h"
+#include "pae/ieee802_1x_kay.h"
 #include "driver.h"
+#include "driver_wired_common.h"
 
 #include "nss_macsec_secy.h"
 #include "nss_macsec_secy_rx.h"
@@ -51,17 +54,14 @@
 #pragma pack(pop)
 #endif /* _MSC_VER */
 
-static const u8 pae_group_addr[ETH_ALEN] =
-{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+struct channel_map {
+	struct ieee802_1x_mka_sci sci;
+};
 
 struct macsec_qca_data {
-	char ifname[IFNAMSIZ + 1];
-	u32 secy_id;
-	void *ctx;
+	struct driver_wired_common_data common;
 
-	int sock; /* raw packet socket for driver access */
-	int pf_sock;
-	int membership, multi, iff_allmulti, iff_up;
+	u32 secy_id;
 
 	/* shadow */
 	Boolean always_include_sci;
@@ -70,194 +70,12 @@
 	Boolean protect_frames;
 	Boolean replay_protect;
 	u32 replay_window;
+
+	struct channel_map receive_channel_map[MAXSC];
+	struct channel_map transmit_channel_map[MAXSC];
 };
 
 
-static int macsec_qca_multicast_membership(int sock, int ifindex,
-					   const u8 *addr, int add)
-{
-#ifdef __linux__
-	struct packet_mreq mreq;
-
-	if (sock < 0)
-		return -1;
-
-	os_memset(&mreq, 0, sizeof(mreq));
-	mreq.mr_ifindex = ifindex;
-	mreq.mr_type = PACKET_MR_MULTICAST;
-	mreq.mr_alen = ETH_ALEN;
-	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
-
-	if (setsockopt(sock, SOL_PACKET,
-		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
-		       &mreq, sizeof(mreq)) < 0) {
-		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
-		return -1;
-	}
-	return 0;
-#else /* __linux__ */
-	return -1;
-#endif /* __linux__ */
-}
-
-
-static int macsec_qca_get_ssid(void *priv, u8 *ssid)
-{
-	ssid[0] = 0;
-	return 0;
-}
-
-
-static int macsec_qca_get_bssid(void *priv, u8 *bssid)
-{
-	/* Report PAE group address as the "BSSID" for macsec connection. */
-	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
-	return 0;
-}
-
-
-static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
-	os_memset(capa, 0, sizeof(*capa));
-	capa->flags = WPA_DRIVER_FLAGS_WIRED;
-	return 0;
-}
-
-
-static int macsec_qca_get_ifflags(const char *ifname, int *flags)
-{
-	struct ifreq ifr;
-	int s;
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	*flags = ifr.ifr_flags & 0xffff;
-	return 0;
-}
-
-
-static int macsec_qca_set_ifflags(const char *ifname, int flags)
-{
-	struct ifreq ifr;
-	int s;
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-	ifr.ifr_flags = flags & 0xffff;
-	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	return 0;
-}
-
-
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-static int macsec_qca_get_ifstatus(const char *ifname, int *status)
-{
-	struct ifmediareq ifmr;
-	int s;
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_print(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifmr, 0, sizeof(ifmr));
-	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
-	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	*status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
-		(IFM_ACTIVE | IFM_AVALID);
-
-	return 0;
-}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-
-
-static int macsec_qca_multi(const char *ifname, const u8 *addr, int add)
-{
-	struct ifreq ifr;
-	int s;
-
-#ifdef __sun__
-	return -1;
-#endif /* __sun__ */
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-#ifdef __linux__
-	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
-	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
-#endif /* __linux__ */
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-	{
-		struct sockaddr_dl *dlp;
-		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
-		dlp->sdl_len = sizeof(struct sockaddr_dl);
-		dlp->sdl_family = AF_LINK;
-		dlp->sdl_index = 0;
-		dlp->sdl_nlen = 0;
-		dlp->sdl_alen = ETH_ALEN;
-		dlp->sdl_slen = 0;
-		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
-	}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
-	{
-		struct sockaddr *sap;
-		sap = (struct sockaddr *) &ifr.ifr_addr;
-		sap->sa_len = sizeof(struct sockaddr);
-		sap->sa_family = AF_UNSPEC;
-		os_memcpy(sap->sa_data, addr, ETH_ALEN);
-	}
-#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
-
-	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	return 0;
-}
-
-
 static void __macsec_drv_init(struct macsec_qca_data *drv)
 {
 	int ret = 0;
@@ -308,76 +126,23 @@
 static void * macsec_qca_init(void *ctx, const char *ifname)
 {
 	struct macsec_qca_data *drv;
-	int flags;
 
 	drv = os_zalloc(sizeof(*drv));
 	if (drv == NULL)
 		return NULL;
-	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
-	drv->ctx = ctx;
 
 	/* Board specific settings */
-	if (os_memcmp("eth2", drv->ifname, 4) == 0)
+	if (os_memcmp("eth2", ifname, 4) == 0)
 		drv->secy_id = 1;
-	else if (os_memcmp("eth3", drv->ifname, 4) == 0)
+	else if (os_memcmp("eth3", ifname, 4) == 0)
 		drv->secy_id = 2;
 	else
 		drv->secy_id = -1;
 
-#ifdef __linux__
-	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
-	if (drv->pf_sock < 0)
-		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
-#else /* __linux__ */
-	drv->pf_sock = -1;
-#endif /* __linux__ */
-
-	if (macsec_qca_get_ifflags(ifname, &flags) == 0 &&
-	    !(flags & IFF_UP) &&
-	    macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) {
-		drv->iff_up = 1;
-	}
-
-	if (macsec_qca_multicast_membership(drv->pf_sock,
-					    if_nametoindex(drv->ifname),
-					    pae_group_addr, 1) == 0) {
-		wpa_printf(MSG_DEBUG,
-			   "%s: Added multicast membership with packet socket",
-			   __func__);
-		drv->membership = 1;
-	} else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) {
-		wpa_printf(MSG_DEBUG,
-			   "%s: Added multicast membership with SIOCADDMULTI",
-			   __func__);
-		drv->multi = 1;
-	} else if (macsec_qca_get_ifflags(ifname, &flags) < 0) {
-		wpa_printf(MSG_INFO, "%s: Could not get interface flags",
-			   __func__);
+	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
 		os_free(drv);
 		return NULL;
-	} else if (flags & IFF_ALLMULTI) {
-		wpa_printf(MSG_DEBUG,
-			   "%s: Interface is already configured for multicast",
-			   __func__);
-	} else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) {
-		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
-			   __func__);
-		os_free(drv);
-		return NULL;
-	} else {
-		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
-		drv->iff_allmulti = 1;
 	}
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-	{
-		int status;
-		wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
-			   __func__);
-		while (macsec_qca_get_ifstatus(ifname, &status) == 0 &&
-		       status == 0)
-			sleep(1);
-	}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
 
 	return drv;
 }
@@ -386,42 +151,8 @@
 static void macsec_qca_deinit(void *priv)
 {
 	struct macsec_qca_data *drv = priv;
-	int flags;
 
-	if (drv->membership &&
-	    macsec_qca_multicast_membership(drv->pf_sock,
-					    if_nametoindex(drv->ifname),
-					    pae_group_addr, 0) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "%s: Failed to remove PAE multicast group (PACKET)",
-			   __func__);
-	}
-
-	if (drv->multi &&
-	    macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
-			   __func__);
-	}
-
-	if (drv->iff_allmulti &&
-	    (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 ||
-	     macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
-			   __func__);
-	}
-
-	if (drv->iff_up &&
-	    macsec_qca_get_ifflags(drv->ifname, &flags) == 0 &&
-	    (flags & IFF_UP) &&
-	    macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
-			   __func__);
-	}
-
-	if (drv->pf_sock != -1)
-		close(drv->pf_sock);
-
+	driver_wired_deinit_common(&drv->common);
 	os_free(drv);
 }
 
@@ -456,6 +187,16 @@
 }
 
 
+static int macsec_qca_get_capability(void *priv, enum macsec_cap *cap)
+{
+	wpa_printf(MSG_DEBUG, "%s", __func__);
+
+	*cap = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+
+	return 0;
+}
+
+
 static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled)
 {
 	struct macsec_qca_data *drv = priv;
@@ -485,15 +226,12 @@
 }
 
 
-static int macsec_qca_set_current_cipher_suite(void *priv, const u8 *cs,
-					       size_t cs_len)
+static int macsec_qca_set_current_cipher_suite(void *priv, u64 cs)
 {
-	u8 default_cs_id[] = CS_ID_GCM_AES_128;
-
-	if (cs_len != CS_ID_LEN ||
-	    os_memcmp(cs, default_cs_id, cs_len) != 0) {
-		wpa_hexdump(MSG_ERROR, "macsec: NOT supported CipherSuite",
-			    cs, cs_len);
+	if (cs != CS_ID_GCM_AES_128) {
+		wpa_printf(MSG_ERROR,
+			   "%s: NOT supported CipherSuite: %016" PRIx64,
+			   __func__, cs);
 		return -1;
 	}
 
@@ -517,16 +255,82 @@
 }
 
 
-static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an,
-					    u32 *lowest_pn)
+static int macsec_qca_lookup_channel(struct channel_map *map,
+				     struct ieee802_1x_mka_sci *sci,
+				     u32 *channel)
+{
+	u32 i;
+
+	for (i = 0; i < MAXSC; i++) {
+		if (os_memcmp(&map[i].sci, sci,
+			      sizeof(struct ieee802_1x_mka_sci)) == 0) {
+			*channel = i;
+			return 0;
+		}
+	}
+
+	return -1;
+}
+
+
+static void macsec_qca_register_channel(struct channel_map *map,
+					struct ieee802_1x_mka_sci *sci,
+					u32 channel)
+{
+	os_memcpy(&map[channel].sci, sci, sizeof(struct ieee802_1x_mka_sci));
+}
+
+
+static int macsec_qca_lookup_receive_channel(struct macsec_qca_data *drv,
+					     struct receive_sc *sc,
+					     u32 *channel)
+{
+	return macsec_qca_lookup_channel(drv->receive_channel_map, &sc->sci,
+					 channel);
+}
+
+
+static void macsec_qca_register_receive_channel(struct macsec_qca_data *drv,
+						struct receive_sc *sc,
+						u32 channel)
+{
+	macsec_qca_register_channel(drv->receive_channel_map, &sc->sci,
+				    channel);
+}
+
+
+static int macsec_qca_lookup_transmit_channel(struct macsec_qca_data *drv,
+					      struct transmit_sc *sc,
+					      u32 *channel)
+{
+	return macsec_qca_lookup_channel(drv->transmit_channel_map, &sc->sci,
+					 channel);
+}
+
+
+static void macsec_qca_register_transmit_channel(struct macsec_qca_data *drv,
+						 struct transmit_sc *sc,
+						 u32 channel)
+{
+	macsec_qca_register_channel(drv->transmit_channel_map, &sc->sci,
+				    channel);
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
 	int ret = 0;
 	u32 next_pn = 0;
 	bool enabled = FALSE;
 	u32 win;
+	u32 channel;
 
-	ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an,
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
+
+	ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, sa->an,
 						 &next_pn);
 	ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
 							&enabled);
@@ -534,40 +338,49 @@
 							    channel, &win);
 
 	if (enabled)
-		*lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+		sa->lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
 	else
-		*lowest_pn = next_pn;
+		sa->lowest_pn = next_pn;
 
-	wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn);
+	wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, sa->lowest_pn);
 
 	return ret;
 }
 
 
-static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an,
-					   u32 *next_pn)
+static int macsec_qca_get_transmit_next_pn(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
 	int ret = 0;
+	u32 channel;
 
-	ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an,
-						 next_pn);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn);
+	ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, sa->an,
+						 &sa->next_pn);
+
+	wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, sa->next_pn);
 
 	return ret;
 }
 
 
-int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn)
+int macsec_qca_set_transmit_next_pn(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
 	int ret = 0;
+	u32 channel;
 
-	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
-						 next_pn);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn);
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+						 sa->next_pn);
+
+	wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, sa->next_pn);
 
 	return ret;
 }
@@ -600,8 +413,7 @@
 }
 
 
-static int macsec_qca_create_receive_sc(void *priv, u32 channel,
-					const u8 *sci_addr, u16 sci_port,
+static int macsec_qca_create_receive_sc(void *priv, struct receive_sc *sc,
 					unsigned int conf_offset,
 					int validation)
 {
@@ -610,6 +422,13 @@
 	fal_rx_prc_lut_t entry;
 	fal_rx_sc_validate_frame_e vf;
 	enum validate_frames validate_frames = validation;
+	u32 channel;
+	const u8 *sci_addr = sc->sci.addr;
+	u16 sci_port = be_to_host16(sc->sci.port);
+
+	ret = macsec_qca_get_available_receive_sc(priv, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -644,15 +463,22 @@
 							    channel,
 							    drv->replay_window);
 
+	macsec_qca_register_receive_channel(drv, sc, channel);
+
 	return ret;
 }
 
 
-static int macsec_qca_delete_receive_sc(void *priv, u32 channel)
+static int macsec_qca_delete_receive_sc(void *priv, struct receive_sc *sc)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_rx_prc_lut_t entry;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -666,49 +492,68 @@
 }
 
 
-static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an,
-					u32 lowest_pn, const u8 *sak)
+static int macsec_qca_create_receive_sa(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_rx_sak_t rx_sak;
 	int i = 0;
+	u32 channel;
+
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
-		   __func__, channel, an, lowest_pn);
+		   __func__, channel, sa->an, sa->lowest_pn);
 
 	os_memset(&rx_sak, 0, sizeof(rx_sak));
 	for (i = 0; i < 16; i++)
-		rx_sak.sak[i] = sak[15 - i];
+		rx_sak.sak[i] = sa->pkey->key[15 - i];
 
-	ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an);
-	ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak);
+	ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, sa->an);
+	ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, sa->an,
+					  &rx_sak);
 
 	return ret;
 }
 
 
-static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_enable_receive_sa(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+					    TRUE);
 
 	return ret;
 }
 
 
-static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_disable_receive_sa(void *priv, struct receive_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_receive_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, sa->an,
+					    FALSE);
 
 	return ret;
 }
@@ -717,14 +562,12 @@
 static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
 	u32 sc_ch = 0;
 	bool in_use = FALSE;
 
 	for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
-		ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
-							&in_use);
-		if (ret)
+		if (nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+						      &in_use))
 			continue;
 
 		if (!in_use) {
@@ -741,14 +584,18 @@
 }
 
 
-static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
-					 const u8 *sci_addr, u16 sci_port,
+static int macsec_qca_create_transmit_sc(void *priv, struct transmit_sc *sc,
 					 unsigned int conf_offset)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_tx_class_lut_t entry;
 	u8 psci[ETH_ALEN + 2];
+	u32 channel;
+
+	ret = macsec_qca_get_available_transmit_sc(priv, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -759,9 +606,9 @@
 	entry.action = FAL_TX_CLASS_ACTION_FORWARD;
 	entry.channel = channel;
 
-	os_memcpy(psci, sci_addr, ETH_ALEN);
-	psci[6] = (sci_port >> 8) & 0xf;
-	psci[7] = sci_port & 0xf;
+	os_memcpy(psci, sc->sci.addr, ETH_ALEN);
+	psci[6] = (sc->sci.port >> 8) & 0xf;
+	psci[7] = sc->sci.port & 0xf;
 
 	ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
 	ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
@@ -771,15 +618,22 @@
 								channel,
 								conf_offset);
 
+	macsec_qca_register_transmit_channel(drv, sc, channel);
+
 	return ret;
 }
 
 
-static int macsec_qca_delete_transmit_sc(void *priv, u32 channel)
+static int macsec_qca_delete_transmit_sc(void *priv, struct transmit_sc *sc)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	fal_tx_class_lut_t entry;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
 
@@ -793,19 +647,22 @@
 }
 
 
-static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an,
-					 u32 next_pn, Boolean confidentiality,
-					 const u8 *sak)
+static int macsec_qca_create_transmit_sa(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
 	u8 tci = 0;
 	fal_tx_sak_t tx_sak;
 	int i;
+	u32 channel;
+
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
 	wpa_printf(MSG_DEBUG,
 		   "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
-		   __func__, channel, an, next_pn, confidentiality);
+		   __func__, channel, sa->an, sa->next_pn, sa->confidentiality);
 
 	if (drv->always_include_sci)
 		tci |= TCI_SC;
@@ -814,45 +671,60 @@
 	else if (drv->use_scb)
 		tci |= TCI_SCB;
 
-	if (confidentiality)
+	if (sa->confidentiality)
 		tci |= TCI_E | TCI_C;
 
 	os_memset(&tx_sak, 0, sizeof(tx_sak));
 	for (i = 0; i < 16; i++)
-		tx_sak.sak[i] = sak[15 - i];
+		tx_sak.sak[i] = sa->pkey->key[15 - i];
 
-	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
-						 next_pn);
-	ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak);
+	ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, sa->an,
+						 sa->next_pn);
+	ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, sa->an,
+					  &tx_sak);
 	ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
 						 (tci >> 2));
-	ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an);
+	ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, sa->an);
 
 	return ret;
 }
 
 
-static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_enable_transmit_sa(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+					    TRUE);
 
 	return ret;
 }
 
 
-static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an)
+static int macsec_qca_disable_transmit_sa(void *priv, struct transmit_sa *sa)
 {
 	struct macsec_qca_data *drv = priv;
-	int ret = 0;
+	int ret;
+	u32 channel;
 
-	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+	ret = macsec_qca_lookup_transmit_channel(priv, sa->sc, &channel);
+	if (ret != 0)
+		return ret;
 
-	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE);
+	wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel,
+		   sa->an);
+
+	ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, sa->an,
+					    FALSE);
 
 	return ret;
 }
@@ -861,14 +733,15 @@
 const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
 	.name = "macsec_qca",
 	.desc = "QCA MACsec Ethernet driver",
-	.get_ssid = macsec_qca_get_ssid,
-	.get_bssid = macsec_qca_get_bssid,
-	.get_capa = macsec_qca_get_capa,
+	.get_ssid = driver_wired_get_ssid,
+	.get_bssid = driver_wired_get_bssid,
+	.get_capa = driver_wired_get_capa,
 	.init = macsec_qca_init,
 	.deinit = macsec_qca_deinit,
 
 	.macsec_init = macsec_qca_macsec_init,
 	.macsec_deinit = macsec_qca_macsec_deinit,
+	.macsec_get_capability = macsec_qca_get_capability,
 	.enable_protect_frames = macsec_qca_enable_protect_frames,
 	.set_replay_protect = macsec_qca_set_replay_protect,
 	.set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
@@ -876,13 +749,11 @@
 	.get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
 	.get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
 	.set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
-	.get_available_receive_sc = macsec_qca_get_available_receive_sc,
 	.create_receive_sc = macsec_qca_create_receive_sc,
 	.delete_receive_sc = macsec_qca_delete_receive_sc,
 	.create_receive_sa = macsec_qca_create_receive_sa,
 	.enable_receive_sa = macsec_qca_enable_receive_sa,
 	.disable_receive_sa = macsec_qca_disable_receive_sa,
-	.get_available_transmit_sc = macsec_qca_get_available_transmit_sc,
 	.create_transmit_sc = macsec_qca_create_transmit_sc,
 	.delete_transmit_sc = macsec_qca_delete_transmit_sc,
 	.create_transmit_sa = macsec_qca_create_transmit_sa,
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 08945bd..e9107b3 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -29,6 +29,7 @@
 #include "common/qca-vendor-attr.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
 #include "l2_packet/l2_packet.h"
 #include "netlink.h"
 #include "linux_defines.h"
@@ -200,6 +201,12 @@
 
 static int i802_set_iface_flags(struct i802_bss *bss, int up);
 static int nl80211_set_param(void *priv, const char *param);
+#ifdef CONFIG_MESH
+static int nl80211_put_mesh_config(struct nl_msg *msg,
+				   struct wpa_driver_mesh_bss_params *params);
+#endif /* CONFIG_MESH */
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+			     int reason);
 
 
 /* Converts nl80211_chan_width to a common format */
@@ -445,6 +452,8 @@
 void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
 		   struct nl_msg *msg, int flags, uint8_t cmd)
 {
+	if (TEST_FAIL())
+		return NULL;
 	return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
 			   0, flags, cmd, 0);
 }
@@ -667,6 +676,7 @@
 	struct nl80211_wiphy_data *w;
 	int wiphy_idx, found = 0;
 	struct i802_bss *tmp_bss;
+	u8 channel;
 
 	if (bss->wiphy_data != NULL)
 		return bss->wiphy_data;
@@ -686,29 +696,35 @@
 	dl_list_init(&w->bsss);
 	dl_list_init(&w->drvs);
 
-	w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
-	if (!w->nl_cb) {
-		os_free(w);
-		return NULL;
-	}
-	nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
-	nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM, process_beacon_event,
-		  w);
+	/* Beacon frames not supported in IEEE 802.11ad */
+	if (ieee80211_freq_to_chan(bss->freq, &channel) !=
+	    HOSTAPD_MODE_IEEE80211AD) {
+		w->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+		if (!w->nl_cb) {
+			os_free(w);
+			return NULL;
+		}
+		nl_cb_set(w->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+			  no_seq_check, NULL);
+		nl_cb_set(w->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+			  process_beacon_event, w);
 
-	w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
-					 "wiphy beacons");
-	if (w->nl_beacons == NULL) {
-		os_free(w);
-		return NULL;
-	}
+		w->nl_beacons = nl_create_handle(bss->drv->global->nl_cb,
+						 "wiphy beacons");
+		if (w->nl_beacons == NULL) {
+			os_free(w);
+			return NULL;
+		}
 
-	if (nl80211_register_beacons(bss->drv, w)) {
-		nl_destroy_handles(&w->nl_beacons);
-		os_free(w);
-		return NULL;
-	}
+		if (nl80211_register_beacons(bss->drv, w)) {
+			nl_destroy_handles(&w->nl_beacons);
+			os_free(w);
+			return NULL;
+		}
 
-	nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w);
+		nl80211_register_eloop_read(&w->nl_beacons,
+					    nl80211_recv_beacons, w);
+	}
 
 	dl_list_add(&nl80211_wiphys, &w->list);
 
@@ -755,7 +771,8 @@
 	if (!dl_list_empty(&w->bsss))
 		return;
 
-	nl80211_destroy_eloop_handle(&w->nl_beacons);
+	if (w->nl_beacons)
+		nl80211_destroy_eloop_handle(&w->nl_beacons);
 
 	nl_cb_put(w->nl_cb);
 	dl_list_del(&w->list);
@@ -1175,16 +1192,108 @@
 }
 
 
-unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+struct nl80211_get_assoc_freq_arg {
+	struct wpa_driver_nl80211_data *drv;
+	unsigned int assoc_freq;
+	unsigned int ibss_freq;
+	u8 assoc_bssid[ETH_ALEN];
+	u8 assoc_ssid[SSID_MAX_LEN];
+	u8 assoc_ssid_len;
+};
+
+static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct nlattr *bss[NL80211_BSS_MAX + 1];
+	static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+		[NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+		[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+	};
+	struct nl80211_get_assoc_freq_arg *ctx = arg;
+	enum nl80211_bss_status status;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+	if (!tb[NL80211_ATTR_BSS] ||
+	    nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+			     bss_policy) ||
+	    !bss[NL80211_BSS_STATUS])
+		return NL_SKIP;
+
+	status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+	    bss[NL80211_BSS_FREQUENCY]) {
+		ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+		wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+			   ctx->assoc_freq);
+	}
+	if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+	    bss[NL80211_BSS_FREQUENCY]) {
+		ctx->ibss_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+		wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+			   ctx->ibss_freq);
+	}
+	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+	    bss[NL80211_BSS_BSSID]) {
+		os_memcpy(ctx->assoc_bssid,
+			  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+		wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+			   MACSTR, MAC2STR(ctx->assoc_bssid));
+	}
+
+	if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+	    bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+		const u8 *ie, *ssid;
+		size_t ie_len;
+
+		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+		ssid = get_ie(ie, ie_len, WLAN_EID_SSID);
+		if (ssid && ssid[1] > 0 && ssid[1] <= SSID_MAX_LEN) {
+			ctx->assoc_ssid_len = ssid[1];
+			os_memcpy(ctx->assoc_ssid, ssid + 2, ssid[1]);
+		}
+	}
+
+	return NL_SKIP;
+}
+
+
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid)
 {
 	struct nl_msg *msg;
 	int ret;
-	struct nl80211_bss_info_arg arg;
+	struct nl80211_get_assoc_freq_arg arg;
 
 	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
 	os_memset(&arg, 0, sizeof(arg));
 	arg.drv = drv;
-	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+	ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+				 &arg);
+	if (ret == 0) {
+		os_memcpy(ssid, arg.assoc_ssid, arg.assoc_ssid_len);
+		return arg.assoc_ssid_len;
+	}
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d (%s)",
+		   ret, strerror(-ret));
+	return ret;
+}
+
+
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int ret;
+	struct nl80211_get_assoc_freq_arg arg;
+
+	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+	os_memset(&arg, 0, sizeof(arg));
+	arg.drv = drv;
+	ret = send_and_recv_msgs(drv, msg, nl80211_get_assoc_freq_handler,
+				 &arg);
 	if (ret == 0) {
 		unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
 			arg.ibss_freq : arg.assoc_freq;
@@ -1989,6 +2098,10 @@
 	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
 		ret = -1;
 
+	/* Radio Measurement - Radio Measurement Request */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x00", 2) < 0)
+		ret = -1;
+
 	/* Radio Measurement - Link Measurement Request */
 	if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) &&
 	    (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
@@ -2060,6 +2173,9 @@
 	/* RRM Measurement Report */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x01", 2) < 0)
 		ret = -1;
+	/* RRM Link Measurement Report */
+	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x03", 2) < 0)
+		ret = -1;
 	/* RRM Neighbor Report Request */
 	if (nl80211_register_action_frame(bss, (u8 *) "\x05\x04", 2) < 0)
 		ret = -1;
@@ -2131,9 +2247,6 @@
 	if (nl80211_register_spurious_class3(bss))
 		goto out_err;
 
-	if (nl80211_get_wiphy_data_ap(bss) == NULL)
-		goto out_err;
-
 	nl80211_mgmt_handle_register_eloop(bss);
 	return 0;
 
@@ -2326,7 +2439,8 @@
 
 	if (drv->hostapd || bss->static_ap)
 		nlmode = NL80211_IFTYPE_AP;
-	else if (bss->if_dynamic)
+	else if (bss->if_dynamic ||
+		 nl80211_get_ifmode(bss) == NL80211_IFTYPE_MESH_POINT)
 		nlmode = nl80211_get_ifmode(bss);
 	else
 		nlmode = NL80211_IFTYPE_STATION;
@@ -2387,12 +2501,14 @@
 }
 
 
-static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
+static int wpa_driver_nl80211_del_beacon(struct i802_bss *bss)
 {
 	struct nl_msg *msg;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
 		   drv->ifindex);
+	nl80211_put_wiphy_data_ap(bss);
 	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
 	return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
@@ -2408,6 +2524,7 @@
 static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
 {
 	struct wpa_driver_nl80211_data *drv = bss->drv;
+	unsigned int i;
 
 	wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
 		   bss->ifname, drv->disabled_11b_rates);
@@ -2444,7 +2561,7 @@
 	nl80211_remove_monitor_interface(drv);
 
 	if (is_ap_interface(drv->nlmode))
-		wpa_driver_nl80211_del_beacon(drv);
+		wpa_driver_nl80211_del_beacon(bss);
 
 	if (drv->eapol_sock >= 0) {
 		eloop_unregister_read_sock(drv->eapol_sock);
@@ -2504,6 +2621,10 @@
 
 	os_free(drv->extended_capa);
 	os_free(drv->extended_capa_mask);
+	for (i = 0; i < drv->num_iface_ext_capa; i++) {
+		os_free(drv->iface_ext_capa[i].ext_capa);
+		os_free(drv->iface_ext_capa[i].ext_capa_mask);
+	}
 	os_free(drv->first_bss);
 	os_free(drv);
 }
@@ -2514,30 +2635,30 @@
 	switch (alg) {
 	case WPA_ALG_WEP:
 		if (key_len == 5)
-			return WLAN_CIPHER_SUITE_WEP40;
-		return WLAN_CIPHER_SUITE_WEP104;
+			return RSN_CIPHER_SUITE_WEP40;
+		return RSN_CIPHER_SUITE_WEP104;
 	case WPA_ALG_TKIP:
-		return WLAN_CIPHER_SUITE_TKIP;
+		return RSN_CIPHER_SUITE_TKIP;
 	case WPA_ALG_CCMP:
-		return WLAN_CIPHER_SUITE_CCMP;
+		return RSN_CIPHER_SUITE_CCMP;
 	case WPA_ALG_GCMP:
-		return WLAN_CIPHER_SUITE_GCMP;
+		return RSN_CIPHER_SUITE_GCMP;
 	case WPA_ALG_CCMP_256:
-		return WLAN_CIPHER_SUITE_CCMP_256;
+		return RSN_CIPHER_SUITE_CCMP_256;
 	case WPA_ALG_GCMP_256:
-		return WLAN_CIPHER_SUITE_GCMP_256;
+		return RSN_CIPHER_SUITE_GCMP_256;
 	case WPA_ALG_IGTK:
-		return WLAN_CIPHER_SUITE_AES_CMAC;
+		return RSN_CIPHER_SUITE_AES_128_CMAC;
 	case WPA_ALG_BIP_GMAC_128:
-		return WLAN_CIPHER_SUITE_BIP_GMAC_128;
+		return RSN_CIPHER_SUITE_BIP_GMAC_128;
 	case WPA_ALG_BIP_GMAC_256:
-		return WLAN_CIPHER_SUITE_BIP_GMAC_256;
+		return RSN_CIPHER_SUITE_BIP_GMAC_256;
 	case WPA_ALG_BIP_CMAC_256:
-		return WLAN_CIPHER_SUITE_BIP_CMAC_256;
+		return RSN_CIPHER_SUITE_BIP_CMAC_256;
 	case WPA_ALG_SMS4:
-		return WLAN_CIPHER_SUITE_SMS4;
+		return RSN_CIPHER_SUITE_SMS4;
 	case WPA_ALG_KRK:
-		return WLAN_CIPHER_SUITE_KRK;
+		return RSN_CIPHER_SUITE_KRK;
 	case WPA_ALG_NONE:
 	case WPA_ALG_PMK:
 		wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
@@ -2555,21 +2676,21 @@
 {
 	switch (cipher) {
 	case WPA_CIPHER_CCMP_256:
-		return WLAN_CIPHER_SUITE_CCMP_256;
+		return RSN_CIPHER_SUITE_CCMP_256;
 	case WPA_CIPHER_GCMP_256:
-		return WLAN_CIPHER_SUITE_GCMP_256;
+		return RSN_CIPHER_SUITE_GCMP_256;
 	case WPA_CIPHER_CCMP:
-		return WLAN_CIPHER_SUITE_CCMP;
+		return RSN_CIPHER_SUITE_CCMP;
 	case WPA_CIPHER_GCMP:
-		return WLAN_CIPHER_SUITE_GCMP;
+		return RSN_CIPHER_SUITE_GCMP;
 	case WPA_CIPHER_TKIP:
-		return WLAN_CIPHER_SUITE_TKIP;
+		return RSN_CIPHER_SUITE_TKIP;
 	case WPA_CIPHER_WEP104:
-		return WLAN_CIPHER_SUITE_WEP104;
+		return RSN_CIPHER_SUITE_WEP104;
 	case WPA_CIPHER_WEP40:
-		return WLAN_CIPHER_SUITE_WEP40;
+		return RSN_CIPHER_SUITE_WEP40;
 	case WPA_CIPHER_GTK_NOT_USED:
-		return WLAN_CIPHER_SUITE_NO_GROUP_ADDR;
+		return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
 	}
 
 	return 0;
@@ -2582,19 +2703,19 @@
 	int num_suites = 0;
 
 	if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+		suites[num_suites++] = RSN_CIPHER_SUITE_CCMP_256;
 	if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
+		suites[num_suites++] = RSN_CIPHER_SUITE_GCMP_256;
 	if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+		suites[num_suites++] = RSN_CIPHER_SUITE_CCMP;
 	if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
+		suites[num_suites++] = RSN_CIPHER_SUITE_GCMP;
 	if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
+		suites[num_suites++] = RSN_CIPHER_SUITE_TKIP;
 	if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
+		suites[num_suites++] = RSN_CIPHER_SUITE_WEP104;
 	if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
-		suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
+		suites[num_suites++] = RSN_CIPHER_SUITE_WEP40;
 
 	return num_suites;
 }
@@ -2852,8 +2973,8 @@
 			    params->wep_key[i]) ||
 		    nla_put_u32(msg, NL80211_KEY_CIPHER,
 				params->wep_key_len[i] == 5 ?
-				WLAN_CIPHER_SUITE_WEP40 :
-				WLAN_CIPHER_SUITE_WEP104) ||
+				RSN_CIPHER_SUITE_WEP40 :
+				RSN_CIPHER_SUITE_WEP104) ||
 		    nla_put_u8(msg, NL80211_KEY_IDX, i) ||
 		    (i == params->wep_tx_keyidx &&
 		     nla_put_flag(msg, NL80211_KEY_DEFAULT)))
@@ -3079,11 +3200,11 @@
 	if (params->ie &&
 	    nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
 		goto fail;
-	if (params->sae_data) {
-		wpa_hexdump(MSG_DEBUG, "  * SAE data", params->sae_data,
-			    params->sae_data_len);
-		if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
-			    params->sae_data))
+	if (params->auth_data) {
+		wpa_hexdump(MSG_DEBUG, "  * auth_data", params->auth_data,
+			    params->auth_data_len);
+		if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->auth_data_len,
+			    params->auth_data))
 			goto fail;
 	}
 	if (params->auth_alg & WPA_AUTH_ALG_OPEN)
@@ -3096,6 +3217,8 @@
 		type = NL80211_AUTHTYPE_FT;
 	else if (params->auth_alg & WPA_AUTH_ALG_SAE)
 		type = NL80211_AUTHTYPE_SAE;
+	else if (params->auth_alg & WPA_AUTH_ALG_FILS)
+		type = NL80211_AUTHTYPE_FILS_SK;
 	else
 		goto fail;
 	wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
@@ -3457,6 +3580,189 @@
 }
 
 
+static int nl80211_put_dtim_period(struct nl_msg *msg, int dtim_period)
+{
+	if (dtim_period > 0) {
+		wpa_printf(MSG_DEBUG, "  * dtim_period=%d", dtim_period);
+		return nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+	}
+
+	return 0;
+}
+
+
+#ifdef CONFIG_MESH
+static int nl80211_set_mesh_config(void *priv,
+				   struct wpa_driver_mesh_bss_params *params)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MESH_CONFIG);
+	if (!msg)
+		return -1;
+
+	ret = nl80211_put_mesh_config(msg, params);
+	if (ret < 0) {
+		nlmsg_free(msg);
+		return ret;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Mesh config set failed: %d (%s)",
+			   ret, strerror(-ret));
+		return ret;
+	}
+	return 0;
+}
+#endif /* CONFIG_MESH */
+
+
+static int nl80211_put_beacon_rate(struct nl_msg *msg, const u64 flags,
+				   struct wpa_driver_ap_params *params)
+{
+	struct nlattr *bands, *band;
+	struct nl80211_txrate_vht vht_rate;
+
+	if (!params->freq ||
+	    (params->beacon_rate == 0 &&
+	     params->rate_type == BEACON_RATE_LEGACY))
+		return 0;
+
+	bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+	if (!bands)
+		return -1;
+
+	switch (params->freq->mode) {
+	case HOSTAPD_MODE_IEEE80211B:
+	case HOSTAPD_MODE_IEEE80211G:
+		band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+		break;
+	case HOSTAPD_MODE_IEEE80211A:
+		band = nla_nest_start(msg, NL80211_BAND_5GHZ);
+		break;
+	case HOSTAPD_MODE_IEEE80211AD:
+		band = nla_nest_start(msg, NL80211_BAND_60GHZ);
+		break;
+	default:
+		return 0;
+	}
+
+	if (!band)
+		return -1;
+
+	os_memset(&vht_rate, 0, sizeof(vht_rate));
+
+	switch (params->rate_type) {
+	case BEACON_RATE_LEGACY:
+		if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support setting Beacon frame rate (legacy)");
+			return -1;
+		}
+
+		if (nla_put_u8(msg, NL80211_TXRATE_LEGACY,
+			       (u8) params->beacon_rate / 5) ||
+		    nla_put(msg, NL80211_TXRATE_HT, 0, NULL) ||
+		    (params->freq->vht_enabled &&
+		     nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+			     &vht_rate)))
+			return -1;
+
+		wpa_printf(MSG_DEBUG, " * beacon_rate = legacy:%u (* 100 kbps)",
+			   params->beacon_rate);
+		break;
+	case BEACON_RATE_HT:
+		if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_HT)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support setting Beacon frame rate (HT)");
+			return -1;
+		}
+		if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL) ||
+		    nla_put_u8(msg, NL80211_TXRATE_HT, params->beacon_rate) ||
+		    (params->freq->vht_enabled &&
+		     nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+			     &vht_rate)))
+			return -1;
+		wpa_printf(MSG_DEBUG, " * beacon_rate = HT-MCS %u",
+			   params->beacon_rate);
+		break;
+	case BEACON_RATE_VHT:
+		if (!(flags & WPA_DRIVER_FLAGS_BEACON_RATE_VHT)) {
+			wpa_printf(MSG_INFO,
+				   "nl80211: Driver does not support setting Beacon frame rate (VHT)");
+			return -1;
+		}
+		vht_rate.mcs[0] = BIT(params->beacon_rate);
+		if (nla_put(msg, NL80211_TXRATE_LEGACY, 0, NULL))
+			return -1;
+		if (nla_put(msg, NL80211_TXRATE_HT, 0, NULL))
+			return -1;
+		if (nla_put(msg, NL80211_TXRATE_VHT, sizeof(vht_rate),
+			    &vht_rate))
+			return -1;
+		wpa_printf(MSG_DEBUG, " * beacon_rate = VHT-MCS %u",
+			   params->beacon_rate);
+		break;
+	}
+
+	nla_nest_end(msg, band);
+	nla_nest_end(msg, bands);
+
+	return 0;
+}
+
+
+static int nl80211_set_multicast_to_unicast(struct i802_bss *bss,
+					    int multicast_to_unicast)
+{
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	int ret;
+
+	msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_MULTICAST_TO_UNICAST);
+	if (!msg ||
+	    (multicast_to_unicast &&
+	     nla_put_flag(msg, NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED))) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to build NL80211_CMD_SET_MULTICAST_TO_UNICAST msg for %s",
+			   bss->ifname);
+		nlmsg_free(msg);
+		return -ENOBUFS;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+	switch (ret) {
+	case 0:
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: multicast to unicast %s on interface %s",
+			   multicast_to_unicast ? "enabled" : "disabled",
+			   bss->ifname);
+		break;
+	case -EOPNOTSUPP:
+		if (!multicast_to_unicast)
+			break;
+		wpa_printf(MSG_INFO,
+			   "nl80211: multicast to unicast not supported on interface %s",
+			   bss->ifname);
+		break;
+	default:
+		wpa_printf(MSG_ERROR,
+			   "nl80211: %s multicast to unicast failed with %d (%s) on interface %s",
+			   multicast_to_unicast ? "enabling" : "disabling",
+			   ret, strerror(-ret), bss->ifname);
+		break;
+	}
+
+	return ret;
+}
+
+
 static int wpa_driver_nl80211_set_ap(void *priv,
 				     struct wpa_driver_ap_params *params)
 {
@@ -3470,6 +3776,9 @@
 	int smps_mode;
 	u32 suites[10], suite;
 	u32 ver;
+#ifdef CONFIG_MESH
+	struct wpa_driver_mesh_bss_params mesh_params;
+#endif /* CONFIG_MESH */
 
 	beacon_set = params->reenable ? 0 : bss->beacon_set;
 
@@ -3477,6 +3786,9 @@
 		   beacon_set);
 	if (beacon_set)
 		cmd = NL80211_CMD_SET_BEACON;
+	else if (!drv->device_ap_sme && !drv->use_monitor &&
+		 !nl80211_get_wiphy_data_ap(bss))
+		return -ENOBUFS;
 
 	wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
 		    params->head, params->head_len);
@@ -3484,6 +3796,8 @@
 		    params->tail, params->tail_len);
 	wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
 	wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
+	wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate);
+	wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type);
 	wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
 	wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",
 			  params->ssid, params->ssid_len);
@@ -3493,7 +3807,8 @@
 	    nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
 		    params->tail) ||
 	    nl80211_put_beacon_int(msg, params->beacon_int) ||
-	    nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) ||
+	    nl80211_put_beacon_rate(msg, drv->capa.flags, params) ||
+	    nl80211_put_dtim_period(msg, params->dtim_period) ||
 	    nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
 		goto fail;
 	if (params->proberesp && params->proberesp_len) {
@@ -3555,17 +3870,19 @@
 		   params->key_mgmt_suites);
 	num_suites = 0;
 	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
-		suites[num_suites++] = WLAN_AKM_SUITE_8021X;
+		suites[num_suites++] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 	if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
-		suites[num_suites++] = WLAN_AKM_SUITE_PSK;
+		suites[num_suites++] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 	if (num_suites &&
 	    nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
 		    suites))
 		goto fail;
 
 	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
-	    params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) &&
-	    nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))
+	    (!params->pairwise_ciphers ||
+	     params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) &&
+	    (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
 		goto fail;
 
 	wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
@@ -3668,6 +3985,8 @@
 		nl80211_set_bss(bss, params->cts_protect, params->preamble,
 				params->short_slot_time, params->ht_opmode,
 				params->isolate, params->basic_rates);
+		nl80211_set_multicast_to_unicast(bss,
+						 params->multicast_to_unicast);
 		if (beacon_set && params->freq &&
 		    params->freq->bandwidth != bss->bandwidth) {
 			wpa_printf(MSG_DEBUG,
@@ -3684,7 +4003,7 @@
 					   "nl80211: Frequency set succeeded for ht2040 coex");
 				bss->bandwidth = params->freq->bandwidth;
 			}
-		} else if (!beacon_set) {
+		} else if (!beacon_set && params->freq) {
 			/*
 			 * cfg80211 updates the driver on frequence change in AP
 			 * mode only at the point when beaconing is started, so
@@ -3693,6 +4012,18 @@
 			bss->bandwidth = params->freq->bandwidth;
 		}
 	}
+
+#ifdef CONFIG_MESH
+	if (is_mesh_interface(drv->nlmode) && params->ht_opmode != -1) {
+		os_memset(&mesh_params, 0, sizeof(mesh_params));
+		mesh_params.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE;
+		mesh_params.ht_opmode = params->ht_opmode;
+		ret = nl80211_set_mesh_config(priv, &mesh_params);
+		if (ret < 0)
+			return ret;
+	}
+#endif /* CONFIG_MESH */
+
 	return ret;
 fail:
 	nlmsg_free(msg);
@@ -3766,6 +4097,12 @@
 		wpa_printf(MSG_DEBUG, "  * channel_type=%d", ct);
 		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
 			return -ENOBUFS;
+	} else {
+		wpa_printf(MSG_DEBUG, "  * channel_type=%d",
+			   NL80211_CHAN_NO_HT);
+		if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+				NL80211_CHAN_NO_HT))
+			return -ENOBUFS;
 	}
 	return 0;
 }
@@ -3828,11 +4165,11 @@
 static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
 {
 	switch (state) {
-	case PLINK_LISTEN:
+	case PLINK_IDLE:
 		return NL80211_PLINK_LISTEN;
-	case PLINK_OPEN_SENT:
+	case PLINK_OPN_SNT:
 		return NL80211_PLINK_OPN_SNT;
-	case PLINK_OPEN_RCVD:
+	case PLINK_OPN_RCVD:
 		return NL80211_PLINK_OPN_RCVD;
 	case PLINK_CNF_RCVD:
 		return NL80211_PLINK_CNF_RCVD;
@@ -3919,6 +4256,13 @@
 				    params->ext_capab_len, params->ext_capab))
 				goto fail;
 		}
+
+		if (is_ap_interface(drv->nlmode) &&
+		    nla_put_u8(msg, NL80211_ATTR_STA_SUPPORT_P2P_PS,
+			       params->support_p2p_ps ?
+			       NL80211_P2P_PS_SUPPORTED :
+			       NL80211_P2P_PS_UNSUPPORTED))
+			goto fail;
 	}
 	if (!params->set) {
 		if (params->aid) {
@@ -4014,6 +4358,15 @@
 			if (!(params->flags & WPA_STA_ASSOCIATED))
 				upd.mask |= BIT(NL80211_STA_FLAG_ASSOCIATED);
 		}
+#ifdef CONFIG_MESH
+	} else {
+		if (params->plink_state == PLINK_ESTAB && params->peer_aid) {
+			ret = nla_put_u16(msg, NL80211_ATTR_MESH_PEER_AID,
+					  params->peer_aid);
+			if (ret)
+				goto fail;
+		}
+#endif /* CONFIG_MESH */
 	}
 
 	wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
@@ -4151,7 +4504,7 @@
 }
 
 
-static const char * nl80211_iftype_str(enum nl80211_iftype mode)
+const char * nl80211_iftype_str(enum nl80211_iftype mode)
 {
 	switch (mode) {
 	case NL80211_IFTYPE_ADHOC:
@@ -4333,7 +4686,8 @@
 
 	if (drv->device_ap_sme && !drv->use_monitor)
 		if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
-			return -1;
+			wpa_printf(MSG_DEBUG,
+				   "nl80211: Failed to subscribe for mgmt frames from SME driver - trying to run without it");
 
 	if (!drv->device_ap_sme && drv->use_monitor &&
 	    nl80211_create_monitor_interface(drv) &&
@@ -4366,6 +4720,7 @@
 	else
 		nl80211_mgmt_unsubscribe(bss, "AP teardown");
 
+	nl80211_put_wiphy_data_ap(bss);
 	bss->beacon_set = 0;
 }
 
@@ -4714,6 +5069,9 @@
 				  struct wpa_driver_associate_params *params,
 				  struct nl_msg *msg)
 {
+	if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+		return -1;
+
 	if (params->bssid) {
 		wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
 			   MAC2STR(params->bssid));
@@ -4815,39 +5173,39 @@
 	    params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
 	    params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
-		int mgmt = WLAN_AKM_SUITE_PSK;
+		int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 
 		switch (params->key_mgmt_suite) {
 		case WPA_KEY_MGMT_CCKM:
-			mgmt = WLAN_AKM_SUITE_CCKM;
+			mgmt = RSN_AUTH_KEY_MGMT_CCKM;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X:
-			mgmt = WLAN_AKM_SUITE_8021X;
+			mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
 			break;
 		case WPA_KEY_MGMT_FT_IEEE8021X:
-			mgmt = WLAN_AKM_SUITE_FT_8021X;
+			mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X;
 			break;
 		case WPA_KEY_MGMT_FT_PSK:
-			mgmt = WLAN_AKM_SUITE_FT_PSK;
+			mgmt = RSN_AUTH_KEY_MGMT_FT_PSK;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X_SHA256:
-			mgmt = WLAN_AKM_SUITE_8021X_SHA256;
+			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
 			break;
 		case WPA_KEY_MGMT_PSK_SHA256:
-			mgmt = WLAN_AKM_SUITE_PSK_SHA256;
+			mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256;
 			break;
 		case WPA_KEY_MGMT_OSEN:
-			mgmt = WLAN_AKM_SUITE_OSEN;
+			mgmt = RSN_AUTH_KEY_MGMT_OSEN;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
-			mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
+			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
 			break;
 		case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
-			mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+			mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
 			break;
 		case WPA_KEY_MGMT_PSK:
 		default:
-			mgmt = WLAN_AKM_SUITE_PSK;
+			mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
 			break;
 		}
 		wpa_printf(MSG_DEBUG, "  * akm=0x%x", mgmt);
@@ -4858,15 +5216,24 @@
 	if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
 		return -1;
 
+	if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+	    (params->pairwise_suite == WPA_CIPHER_NONE ||
+	     params->pairwise_suite == WPA_CIPHER_WEP104 ||
+	     params->pairwise_suite == WPA_CIPHER_WEP40) &&
+	    (nla_put_u16(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE, ETH_P_PAE) ||
+	     nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
+		return -1;
+
 	if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
 	    nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
 		return -1;
 
 	if (params->rrm_used) {
 		u32 drv_rrm_flags = drv->capa.rrm_flags;
-		if (!(drv_rrm_flags &
-		      WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
-		    !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) ||
+		if ((!((drv_rrm_flags &
+			WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
+		       (drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
+		     !(drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) ||
 		    nla_put_flag(msg, NL80211_ATTR_USE_RRM))
 			return -1;
 	}
@@ -5045,6 +5412,22 @@
 	if (ret)
 		goto fail;
 
+	if (params->fils_kek) {
+		wpa_printf(MSG_DEBUG, "  * FILS KEK (len=%u)",
+			   (unsigned int) params->fils_kek_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_KEK, params->fils_kek_len,
+			    params->fils_kek))
+			goto fail;
+	}
+	if (params->fils_nonces) {
+		wpa_hexdump(MSG_DEBUG, "  * FILS nonces (for AAD)",
+			    params->fils_nonces,
+			    params->fils_nonces_len);
+		if (nla_put(msg, NL80211_ATTR_FILS_NONCES,
+			    params->fils_nonces_len, params->fils_nonces))
+			goto fail;
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -5100,6 +5483,9 @@
 	int res;
 	int mode_switch_res;
 
+	if (TEST_FAIL())
+		return -1;
+
 	mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
 	if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
 		mode_switch_res = 0;
@@ -5519,8 +5905,6 @@
 {
 	struct nl_msg *msg;
 
-	os_memset(data, 0, sizeof(*data));
-
 	if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
 	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
 		nlmsg_free(msg);
@@ -5626,6 +6010,7 @@
 	struct hostap_sta_driver_data data;
 	int ret;
 
+	os_memset(&data, 0, sizeof(data));
 	data.inactive_msec = (unsigned long) -1;
 	ret = i802_read_sta_data(priv, &data, addr);
 	if (ret == -ENOENT)
@@ -5651,6 +6036,14 @@
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct ieee80211_mgmt mgmt;
+	u8 channel;
+
+	if (ieee80211_freq_to_chan(bss->freq, &channel) ==
+	    HOSTAPD_MODE_IEEE80211AD) {
+		/* Deauthentication is not used in DMG/IEEE 802.11ad;
+		 * disassociate the STA instead. */
+		return i802_sta_disassoc(priv, own_addr, addr, reason);
+	}
 
 	if (is_mesh_interface(drv->nlmode))
 		return -1;
@@ -6364,7 +6757,7 @@
 		wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
 		nl80211_teardown_ap(bss);
 		if (!bss->added_if && !drv->first_bss->next)
-			wpa_driver_nl80211_del_beacon(drv);
+			wpa_driver_nl80211_del_beacon(bss);
 		nl80211_destroy_bss(bss);
 		if (!bss->added_if)
 			i802_set_iface_flags(bss, 0);
@@ -6488,6 +6881,14 @@
 	os_memcpy(hdr->addr2, src, ETH_ALEN);
 	os_memcpy(hdr->addr3, bssid, ETH_ALEN);
 
+	if (os_memcmp(bss->addr, src, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "nl80211: Use random TA " MACSTR,
+			   MAC2STR(src));
+		os_memcpy(bss->rand_addr, src, ETH_ALEN);
+	} else {
+		os_memset(bss->rand_addr, 0, ETH_ALEN);
+	}
+
 	if (is_ap_interface(drv->nlmode) &&
 	    (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
 	     (int) freq == bss->freq || drv->device_ap_sme ||
@@ -6726,7 +7127,7 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
-	wpa_driver_nl80211_del_beacon(drv);
+	wpa_driver_nl80211_del_beacon(bss);
 	bss->beacon_set = 0;
 
 	/*
@@ -6746,7 +7147,7 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	if (!is_ap_interface(drv->nlmode))
 		return -1;
-	wpa_driver_nl80211_del_beacon(drv);
+	wpa_driver_nl80211_del_beacon(bss);
 	bss->beacon_set = 0;
 	return 0;
 }
@@ -6878,15 +7279,15 @@
 
 static int nl80211_set_param(void *priv, const char *param)
 {
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
 	if (param == NULL)
 		return 0;
 	wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
 
 #ifdef CONFIG_P2P
 	if (os_strstr(param, "use_p2p_group_interface=1")) {
-		struct i802_bss *bss = priv;
-		struct wpa_driver_nl80211_data *drv = bss->drv;
-
 		wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
 			   "interface");
 		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
@@ -6894,22 +7295,18 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (os_strstr(param, "use_monitor=1")) {
-		struct i802_bss *bss = priv;
-		struct wpa_driver_nl80211_data *drv = bss->drv;
+	if (os_strstr(param, "use_monitor=1"))
 		drv->use_monitor = 1;
-	}
 
 	if (os_strstr(param, "force_connect_cmd=1")) {
-		struct i802_bss *bss = priv;
-		struct wpa_driver_nl80211_data *drv = bss->drv;
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
 		drv->force_connect_cmd = 1;
 	}
 
+	if (os_strstr(param, "force_bss_selection=1"))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+
 	if (os_strstr(param, "no_offchannel_tx=1")) {
-		struct i802_bss *bss = priv;
-		struct wpa_driver_nl80211_data *drv = bss->drv;
 		drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
 		drv->test_use_roc_tx = 1;
 	}
@@ -7439,6 +7836,7 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	struct nl_msg *msg;
 	enum nl80211_tdls_operation nl80211_oper;
+	int res;
 
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
 		return -EOPNOTSUPP;
@@ -7474,7 +7872,11 @@
 		return -ENOBUFS;
 	}
 
-	return send_and_recv_msgs(drv, msg, NULL, NULL);
+	res = send_and_recv_msgs(drv, msg, NULL, NULL);
+	wpa_printf(MSG_DEBUG, "nl80211: TDLS_OPER: oper=%d mac=" MACSTR
+		   " --> res=%d (%s)", nl80211_oper, MAC2STR(peer), res,
+		   strerror(-res));
+	return res;
 }
 
 
@@ -7632,6 +8034,8 @@
 					const u8 *addr)
 {
 	struct i802_bss *bss = priv;
+
+	os_memset(data, 0, sizeof(*data));
 	return i802_read_sta_data(bss, data, addr);
 }
 
@@ -7683,7 +8087,7 @@
 }
 
 
-const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
+static const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
 {
 	struct i802_bss *bss = priv;
 	struct wpa_driver_nl80211_data *drv = bss->drv;
@@ -8291,6 +8695,9 @@
 	struct wpa_driver_nl80211_data *drv = bss->drv;
 	int new_addr = addr != NULL;
 
+	if (TEST_FAIL())
+		return -1;
+
 	if (!addr)
 		addr = drv->perm_addr;
 
@@ -8351,6 +8758,46 @@
 }
 
 
+static int nl80211_put_mesh_config(struct nl_msg *msg,
+				   struct wpa_driver_mesh_bss_params *params)
+{
+	struct nlattr *container;
+
+	container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+	if (!container)
+		return -1;
+
+	if (((params->flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
+	     nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+			 params->auto_plinks)) ||
+	    ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS) &&
+	     nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+			 params->max_peer_links)))
+		return -1;
+
+	/*
+	 * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+	 * the timer could disconnect stations even in that case.
+	 */
+	if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT) &&
+	    nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+			params->peer_link_timeout)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+		return -1;
+	}
+
+	if ((params->flags & WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE) &&
+	    nla_put_u16(msg, NL80211_MESHCONF_HT_OPMODE, params->ht_opmode)) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to set HT_OP_MODE");
+		return -1;
+	}
+
+	nla_nest_end(msg, container);
+
+	return 0;
+}
+
+
 static int nl80211_join_mesh(struct i802_bss *bss,
 			     struct wpa_driver_mesh_join_params *params)
 {
@@ -8365,7 +8812,8 @@
 	    nl80211_put_freq_params(msg, &params->freq) ||
 	    nl80211_put_basic_rates(msg, params->basic_rates) ||
 	    nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
-	    nl80211_put_beacon_int(msg, params->beacon_int))
+	    nl80211_put_beacon_int(msg, params->beacon_int) ||
+	    nl80211_put_dtim_period(msg, params->dtim_period))
 		goto fail;
 
 	wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
@@ -8394,30 +8842,12 @@
 		goto fail;
 	nla_nest_end(msg, container);
 
-	container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
-	if (!container)
+	params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+	params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_PEER_LINK_TIMEOUT;
+	params->conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_MAX_PEER_LINKS;
+	if (nl80211_put_mesh_config(msg, &params->conf) < 0)
 		goto fail;
 
-	if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
-	    nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0))
-		goto fail;
-	if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) &&
-	    nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
-			params->max_peer_links))
-		goto fail;
-
-	/*
-	 * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
-	 * the timer could disconnect stations even in that case.
-	 */
-	if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
-			params->conf.peer_link_timeout)) {
-		wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
-		goto fail;
-	}
-
-	nla_nest_end(msg, container);
-
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 	msg = NULL;
 	if (ret) {
@@ -8426,7 +8856,7 @@
 		goto fail;
 	}
 	ret = 0;
-	bss->freq = params->freq.freq;
+	drv->assoc_freq = bss->freq = params->freq.freq;
 	wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
 
 fail:
@@ -9092,9 +9522,266 @@
 	return 0;
 }
 
+
+static int nl80211_p2p_lo_start(void *priv, unsigned int freq,
+				unsigned int period, unsigned int interval,
+				unsigned int count, const u8 *device_types,
+				size_t dev_types_len,
+				const u8 *ies, size_t ies_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *container;
+	int ret;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Start P2P Listen offload: freq=%u, period=%u, interval=%u, count=%u",
+		   freq, period, interval, count);
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START))
+		goto fail;
+
+	container = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!container)
+		goto fail;
+
+	if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_CHANNEL,
+			freq) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_PERIOD,
+			period) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_INTERVAL,
+			interval) ||
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_COUNT,
+			count) ||
+	    nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_DEVICE_TYPES,
+		    dev_types_len, device_types) ||
+	    nla_put(msg, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_VENDOR_IE,
+		    ies_len, ies))
+		goto fail;
+
+	nla_nest_end(msg, container);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Failed to send P2P Listen offload vendor command");
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
+
+static int nl80211_p2p_lo_stop(void *priv)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Stop P2P Listen offload");
+
+	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD))
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP)) {
+		nlmsg_free(msg);
+		return -1;
+	}
+
+	return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
+
+
+static int nl80211_set_tdls_mode(void *priv, int tdls_external_control)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+	u32 tdls_mode;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: Set TDKS mode: tdls_external_control=%d",
+		   tdls_external_control);
+
+	if (tdls_external_control == 1)
+		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_IMPLICIT |
+			QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXTERNAL;
+	else
+		tdls_mode = QCA_WLAN_VENDOR_TDLS_TRIGGER_MODE_EXPLICIT;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_CONFIGURE_TDLS))
+		goto fail;
+
+	params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (!params)
+		goto fail;
+
+	if (nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TDLS_CONFIG_TRIGGER_MODE,
+			tdls_mode))
+		goto fail;
+
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set TDLS mode failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+	return 0;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
+static int nl80211_write_to_file(const char *name, unsigned int val)
+{
+	int fd, len;
+	char tmp[128];
+
+	fd = open(name, O_RDWR);
+	if (fd < 0) {
+		wpa_printf(MSG_ERROR, "nl80211: Failed to open %s: %s",
+			   name, strerror(errno));
+		return fd;
+	}
+
+	len = os_snprintf(tmp, sizeof(tmp), "%u\n", val);
+	len = write(fd, tmp, len);
+	if (len < 0)
+		wpa_printf(MSG_ERROR, "nl80211: Failed to write to %s: %s",
+			   name, strerror(errno));
+	close(fd);
+
+	return 0;
+}
+
+
+static int nl80211_configure_data_frame_filters(void *priv, u32 filter_flags)
+{
+	struct i802_bss *bss = priv;
+	char path[128];
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Data frame filter flags=0x%x",
+		   filter_flags);
+
+	/* Configure filtering of unicast frame encrypted using GTK */
+	ret = os_snprintf(path, sizeof(path),
+			  "/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast",
+			  bss->ifname);
+	if (os_snprintf_error(sizeof(path), ret))
+		return -1;
+
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_GTK));
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set IPv4 unicast in multicast filter");
+		return ret;
+	}
+
+	os_snprintf(path, sizeof(path),
+		    "/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast",
+		    bss->ifname);
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_GTK));
+
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set IPv6 unicast in multicast filter");
+		return ret;
+	}
+
+	/* Configure filtering of unicast frame encrypted using GTK */
+	os_snprintf(path, sizeof(path),
+		    "/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp",
+		    bss->ifname);
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_ARP));
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed set gratuitous ARP filter");
+		return ret;
+	}
+
+	/* Configure filtering of IPv6 NA frames */
+	os_snprintf(path, sizeof(path),
+		    "/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na",
+		    bss->ifname);
+	ret = nl80211_write_to_file(path,
+				    !!(filter_flags &
+				       WPA_DATA_FRAME_FILTER_FLAG_NA));
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Failed to set unsolicited NA filter");
+		return ret;
+	}
+
+	return 0;
+}
+
+
+static int nl80211_get_ext_capab(void *priv, enum wpa_driver_if_type type,
+				 const u8 **ext_capa, const u8 **ext_capa_mask,
+				 unsigned int *ext_capa_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	enum nl80211_iftype nlmode;
+	unsigned int i;
+
+	if (!ext_capa || !ext_capa_mask || !ext_capa_len)
+		return -1;
+
+	nlmode = wpa_driver_nl80211_if_type(type);
+
+	/* By default, use the per-radio values */
+	*ext_capa = drv->extended_capa;
+	*ext_capa_mask = drv->extended_capa_mask;
+	*ext_capa_len = drv->extended_capa_len;
+
+	/* Replace the default value if a per-interface type value exists */
+	for (i = 0; i < drv->num_iface_ext_capa; i++) {
+		if (nlmode == drv->iface_ext_capa[i].iftype) {
+			*ext_capa = drv->iface_ext_capa[i].ext_capa;
+			*ext_capa_mask = drv->iface_ext_capa[i].ext_capa_mask;
+			*ext_capa_len = drv->iface_ext_capa[i].ext_capa_len;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.name = "nl80211",
 	.desc = "Linux nl80211/cfg80211",
@@ -9189,9 +9876,6 @@
 	.vendor_cmd = nl80211_vendor_cmd,
 	.set_qos_map = nl80211_set_qos_map,
 	.set_wowlan = nl80211_set_wowlan,
-#ifdef CONFIG_DRIVER_NL80211_QCA
-	.roaming = nl80211_roaming,
-#endif /* CONFIG_DRIVER_NL80211_QCA */
 	.set_mac_addr = nl80211_set_mac_addr,
 #ifdef CONFIG_MESH
 	.init_mesh = wpa_driver_nl80211_init_mesh,
@@ -9206,9 +9890,16 @@
 	.del_tx_ts = nl80211_del_ts,
 	.get_ifindex = nl80211_get_ifindex,
 #ifdef CONFIG_DRIVER_NL80211_QCA
+	.roaming = nl80211_roaming,
 	.do_acs = wpa_driver_do_acs,
 	.set_band = nl80211_set_band,
 	.get_pref_freq_list = nl80211_get_pref_freq_list,
 	.set_prob_oper_freq = nl80211_set_prob_oper_freq,
+	.p2p_lo_start = nl80211_p2p_lo_start,
+	.p2p_lo_stop = nl80211_p2p_lo_stop,
+	.set_default_scan_ies = nl80211_set_default_scan_ies,
+	.set_tdls_mode = nl80211_set_tdls_mode,
 #endif /* CONFIG_DRIVER_NL80211_QCA */
+	.configure_data_frame_filters = nl80211_configure_data_frame_filters,
+	.get_ext_capab = nl80211_get_ext_capab,
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index b0d2b6d..bdc79c5 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -78,6 +78,7 @@
 
 	struct nl80211_wiphy_data *wiphy_data;
 	struct dl_list wiphy_list;
+	u8 rand_addr[ETH_ALEN];
 };
 
 struct wpa_driver_nl80211_data {
@@ -96,6 +97,13 @@
 	struct wpa_driver_capa capa;
 	u8 *extended_capa, *extended_capa_mask;
 	unsigned int extended_capa_len;
+	struct drv_nl80211_ext_capa {
+		enum nl80211_iftype iftype;
+		u8 *ext_capa, *ext_capa_mask;
+		unsigned int ext_capa_len;
+	} iface_ext_capa[NL80211_IFTYPE_MAX];
+	unsigned int num_iface_ext_capa;
+
 	int has_capability;
 
 	int operstate;
@@ -152,6 +160,8 @@
 	unsigned int set_prob_oper_freq:1;
 	unsigned int scan_vendor_cmd_avail:1;
 	unsigned int connect_reassoc:1;
+	unsigned int set_wifi_conf_vendor_cmd_avail:1;
+	unsigned int he_capab_vendor_cmd_avail:1;
 
 	u64 vendor_scan_cookie;
 	u64 remain_on_chan_cookie;
@@ -200,6 +210,8 @@
 	 * (NL80211_CMD_VENDOR). 0 if no pending scan request.
 	 */
 	int last_scan_cmd;
+
+	struct he_capabilities he_capab;
 };
 
 struct nl_msg;
@@ -220,6 +232,7 @@
 			 void *arg, int use_existing);
 void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
 unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_assoc_ssid(struct wpa_driver_nl80211_data *drv, u8 *ssid);
 enum chan_width convert2width(int width);
 void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
 struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
@@ -251,6 +264,8 @@
 int process_global_event(struct nl_msg *msg, void *arg);
 int process_bss_event(struct nl_msg *msg, void *arg);
 
+const char * nl80211_iftype_str(enum nl80211_iftype mode);
+
 #ifdef ANDROID
 int android_nl_socket_set_nonblocking(struct nl_handle *handle);
 int android_pno_start(struct i802_bss *bss,
@@ -272,15 +287,6 @@
 
 /* driver_nl80211_scan.c */
 
-struct nl80211_bss_info_arg {
-	struct wpa_driver_nl80211_data *drv;
-	struct wpa_scan_results *res;
-	unsigned int assoc_freq;
-	unsigned int ibss_freq;
-	u8 assoc_bssid[ETH_ALEN];
-};
-
-int bss_info_handler(struct nl_msg *msg, void *arg);
 void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
 int wpa_driver_nl80211_scan(struct i802_bss *bss,
 			    struct wpa_driver_scan_params *params);
@@ -289,8 +295,9 @@
 int wpa_driver_nl80211_stop_sched_scan(void *priv);
 struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
 void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
-int wpa_driver_nl80211_abort_scan(void *priv);
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie);
 int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
 				   struct wpa_driver_scan_params *params);
+int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len);
 
 #endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 004d88e..7064ce1 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -12,8 +12,8 @@
 #include <netlink/genl/genl.h>
 
 #include "utils/common.h"
-#include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
 #include "common/qca-vendor.h"
 #include "common/qca-vendor-attr.h"
 #include "driver_nl80211.h"
@@ -66,7 +66,6 @@
 	unsigned int device_ap_sme:1;
 	unsigned int poll_command_supported:1;
 	unsigned int data_tx_status:1;
-	unsigned int monitor_supported:1;
 	unsigned int auth_supported:1;
 	unsigned int connect_supported:1;
 	unsigned int p2p_go_supported:1;
@@ -129,9 +128,6 @@
 		case NL80211_IFTYPE_P2P_CLIENT:
 			info->p2p_client_supported = 1;
 			break;
-		case NL80211_IFTYPE_MONITOR:
-			info->monitor_supported = 1;
-			break;
 		}
 	}
 }
@@ -270,40 +266,40 @@
 			   c >> 24, (c >> 16) & 0xff,
 			   (c >> 8) & 0xff, c & 0xff);
 		switch (c) {
-		case WLAN_CIPHER_SUITE_CCMP_256:
+		case RSN_CIPHER_SUITE_CCMP_256:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
 			break;
-		case WLAN_CIPHER_SUITE_GCMP_256:
+		case RSN_CIPHER_SUITE_GCMP_256:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
 			break;
-		case WLAN_CIPHER_SUITE_CCMP:
+		case RSN_CIPHER_SUITE_CCMP:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
 			break;
-		case WLAN_CIPHER_SUITE_GCMP:
+		case RSN_CIPHER_SUITE_GCMP:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
 			break;
-		case WLAN_CIPHER_SUITE_TKIP:
+		case RSN_CIPHER_SUITE_TKIP:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
 			break;
-		case WLAN_CIPHER_SUITE_WEP104:
+		case RSN_CIPHER_SUITE_WEP104:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
 			break;
-		case WLAN_CIPHER_SUITE_WEP40:
+		case RSN_CIPHER_SUITE_WEP40:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
 			break;
-		case WLAN_CIPHER_SUITE_AES_CMAC:
+		case RSN_CIPHER_SUITE_AES_128_CMAC:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
 			break;
-		case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+		case RSN_CIPHER_SUITE_BIP_GMAC_128:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
 			break;
-		case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+		case RSN_CIPHER_SUITE_BIP_GMAC_256:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
 			break;
-		case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+		case RSN_CIPHER_SUITE_BIP_CMAC_256:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
 			break;
-		case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
+		case RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED:
 			info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
 			break;
 		}
@@ -352,13 +348,56 @@
 					 struct nlattr *tb)
 {
 	struct wpa_driver_capa *capa = info->capa;
+	u8 *ext_features;
+	int len;
 
 	if (tb == NULL)
 		return;
 
-	if (ext_feature_isset(nla_data(tb), nla_len(tb),
-			      NL80211_EXT_FEATURE_VHT_IBSS))
+	ext_features = nla_data(tb);
+	len = nla_len(tb);
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_VHT_IBSS))
 		capa->flags |= WPA_DRIVER_FLAGS_VHT_IBSS;
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_RRM))
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_RRM;
+
+	if (ext_feature_isset(ext_features, len, NL80211_EXT_FEATURE_FILS_STA))
+		capa->flags |= WPA_DRIVER_FLAGS_SUPPORT_FILS;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_RATE_LEGACY))
+		capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_LEGACY;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_RATE_HT))
+		capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_HT;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BEACON_RATE_VHT))
+		capa->flags |= WPA_DRIVER_FLAGS_BEACON_RATE_VHT;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SCAN_START_TIME) &&
+	    ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_BSS_PARENT_TSF) &&
+	    ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SET_SCAN_DWELL))
+		capa->rrm_flags |= WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA))
+		capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED))
+		capa->flags |= WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED;
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI))
+		capa->flags |= WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI;
 }
 
 
@@ -479,6 +518,74 @@
 }
 
 
+static void wiphy_info_extended_capab(struct wpa_driver_nl80211_data *drv,
+				      struct nlattr *tb)
+{
+	int rem = 0, i;
+	struct nlattr *tb1[NL80211_ATTR_MAX + 1], *attr;
+
+	if (!tb || drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+		return;
+
+	nla_for_each_nested(attr, tb, rem) {
+		unsigned int len;
+		struct drv_nl80211_ext_capa *capa;
+
+		nla_parse(tb1, NL80211_ATTR_MAX, nla_data(attr),
+			  nla_len(attr), NULL);
+
+		if (!tb1[NL80211_ATTR_IFTYPE] ||
+		    !tb1[NL80211_ATTR_EXT_CAPA] ||
+		    !tb1[NL80211_ATTR_EXT_CAPA_MASK])
+			continue;
+
+		capa = &drv->iface_ext_capa[drv->num_iface_ext_capa];
+		capa->iftype = nla_get_u32(tb1[NL80211_ATTR_IFTYPE]);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Driver-advertised extended capabilities for interface type %s",
+			   nl80211_iftype_str(capa->iftype));
+
+		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA]);
+		capa->ext_capa = os_malloc(len);
+		if (!capa->ext_capa)
+			goto err;
+
+		os_memcpy(capa->ext_capa, nla_data(tb1[NL80211_ATTR_EXT_CAPA]),
+			  len);
+		capa->ext_capa_len = len;
+		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities",
+			    capa->ext_capa, capa->ext_capa_len);
+
+		len = nla_len(tb1[NL80211_ATTR_EXT_CAPA_MASK]);
+		capa->ext_capa_mask = os_malloc(len);
+		if (!capa->ext_capa_mask)
+			goto err;
+
+		os_memcpy(capa->ext_capa_mask,
+			  nla_data(tb1[NL80211_ATTR_EXT_CAPA_MASK]), len);
+		wpa_hexdump(MSG_DEBUG, "nl80211: Extended capabilities mask",
+			    capa->ext_capa_mask, capa->ext_capa_len);
+
+		drv->num_iface_ext_capa++;
+		if (drv->num_iface_ext_capa == NL80211_IFTYPE_MAX)
+			break;
+	}
+
+	return;
+
+err:
+	/* Cleanup allocated memory on error */
+	for (i = 0; i < NL80211_IFTYPE_MAX; i++) {
+		os_free(drv->iface_ext_capa[i].ext_capa);
+		drv->iface_ext_capa[i].ext_capa = NULL;
+		os_free(drv->iface_ext_capa[i].ext_capa_mask);
+		drv->iface_ext_capa[i].ext_capa_mask = NULL;
+		drv->iface_ext_capa[i].ext_capa_len = 0;
+	}
+	drv->num_iface_ext_capa = 0;
+}
+
+
 static int wiphy_info_handler(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -569,6 +676,9 @@
 				  nla_len(tb[NL80211_ATTR_EXT_CAPA]));
 			drv->extended_capa_len =
 				nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+			wpa_hexdump(MSG_DEBUG,
+				    "nl80211: Driver-advertised extended capabilities (default)",
+				    drv->extended_capa, drv->extended_capa_len);
 		}
 		drv->extended_capa_mask =
 			os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
@@ -576,6 +686,10 @@
 			os_memcpy(drv->extended_capa_mask,
 				  nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
 				  nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+			wpa_hexdump(MSG_DEBUG,
+				    "nl80211: Driver-advertised extended capabilities mask (default)",
+				    drv->extended_capa_mask,
+				    drv->extended_capa_len);
 		} else {
 			os_free(drv->extended_capa);
 			drv->extended_capa = NULL;
@@ -583,6 +697,8 @@
 		}
 	}
 
+	wiphy_info_extended_capab(drv, tb[NL80211_ATTR_IFTYPE_EXT_CAPA]);
+
 	if (tb[NL80211_ATTR_VENDOR_DATA]) {
 		struct nlattr *nl;
 		int rem;
@@ -625,6 +741,12 @@
 				case QCA_NL80211_VENDOR_SUBCMD_TRIGGER_SCAN:
 					drv->scan_vendor_cmd_avail = 1;
 					break;
+				case QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION:
+					drv->set_wifi_conf_vendor_cmd_avail = 1;
+					break;
+				case QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES:
+					drv->he_capab_vendor_cmd_avail = 1;
+					break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 				}
 			}
@@ -794,6 +916,100 @@
 }
 
 
+static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg)
+{
+	struct nlattr *tb[NL80211_ATTR_MAX + 1];
+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+	struct he_capabilities *he_capab = arg;
+	struct nlattr *nl_vend;
+	struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX + 1];
+	size_t len;
+
+	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+		  genlmsg_attrlen(gnlh, 0), NULL);
+
+	if (!tb[NL80211_ATTR_VENDOR_DATA])
+		return NL_SKIP;
+
+	nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+	nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_HE_CAPABILITIES_MAX,
+		  nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]) {
+		u8 he_supported;
+
+		he_supported = nla_get_u8(
+			tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_SUPPORTED]);
+		wpa_printf(MSG_DEBUG, "nl80211: HE capabilities supported: %u",
+			   he_supported);
+		he_capab->he_supported = he_supported;
+		if (!he_supported)
+			return NL_SKIP;
+	}
+
+	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]) {
+		len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]);
+
+		if (len > sizeof(he_capab->phy_cap))
+			len = sizeof(he_capab->phy_cap);
+		os_memcpy(he_capab->phy_cap,
+			  nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PHY_CAPAB]),
+			  len);
+	}
+
+	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB])
+		he_capab->mac_cap =
+			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_MAC_CAPAB]);
+
+	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS])
+		he_capab->mcs =
+			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_HE_MCS]);
+
+	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS])
+		he_capab->ppet.numss_m1 =
+			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_NUM_SS]);
+
+	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK])
+		he_capab->ppet.ru_count =
+			nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_RU_IDX_MASK]);
+
+	if (tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]) {
+		len = nla_len(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]);
+
+		if (len > sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0))
+			len = sizeof(he_capab->ppet.ppet16_ppet8_ru3_ru0);
+		os_memcpy(he_capab->ppet.ppet16_ppet8_ru3_ru0,
+			  nla_data(tb_vendor[QCA_WLAN_VENDOR_ATTR_PPE_THRESHOLD]),
+			  len);
+	}
+
+	return NL_SKIP;
+}
+
+
+static void qca_nl80211_check_he_capab(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	int ret;
+
+	if (!drv->he_capab_vendor_cmd_avail)
+		return;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+		nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+		nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			    QCA_NL80211_VENDOR_SUBCMD_GET_HE_CAPABILITIES)) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	ret = send_and_recv_msgs(drv, msg, qca_nl80211_he_capab_handler,
+				 &drv->he_capab);
+	if (!ret && drv->he_capab.he_supported)
+		drv->capa.flags |= WPA_DRIVER_FLAGS_HE_CAPABILITIES;
+}
+
+
 struct features_info {
 	u8 *flags;
 	size_t flags_len;
@@ -888,6 +1104,8 @@
 	if (check_feature(QCA_WLAN_VENDOR_FEATURE_OFFCHANNEL_SIMULTANEOUS,
 			  &info))
 		drv->capa.flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_SIMULTANEOUS;
+	if (check_feature(QCA_WLAN_VENDOR_FEATURE_P2P_LISTEN_OFFLOAD, &info))
+		drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD;
 	os_free(info.flags);
 }
 
@@ -949,21 +1167,8 @@
 	 * If poll command and tx status are supported, mac80211 is new enough
 	 * to have everything we need to not need monitor interfaces.
 	 */
-	drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
-
-	if (drv->device_ap_sme && drv->use_monitor) {
-		/*
-		 * Non-mac80211 drivers may not support monitor interface.
-		 * Make sure we do not get stuck with incorrect capability here
-		 * by explicitly testing this.
-		 */
-		if (!info.monitor_supported) {
-			wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
-				   "with device_ap_sme since no monitor mode "
-				   "support detected");
-			drv->use_monitor = 0;
-		}
-	}
+	drv->use_monitor = !info.device_ap_sme &&
+		(!info.poll_command_supported || !info.data_tx_status);
 
 	/*
 	 * If we aren't going to use monitor interfaces, but the
@@ -976,6 +1181,7 @@
 #ifdef CONFIG_DRIVER_NL80211_QCA
 	qca_nl80211_check_dfs_capa(drv);
 	qca_nl80211_get_features(drv);
+	qca_nl80211_check_he_capab(drv);
 
 	/*
 	 * To enable offchannel simultaneous support in wpa_supplicant, the
@@ -1684,6 +1890,7 @@
 				os_free(result.modes[i].rates);
 			}
 			os_free(result.modes);
+			*num_modes = 0;
 			return NULL;
 		}
 		return wpa_driver_nl80211_postprocess_modes(result.modes,
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index bd16edb..ed2cbe4 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211 - Event processing
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
  * Copyright (c) 2009-2010, Atheros Communications
  *
@@ -206,6 +206,7 @@
 	const struct ieee80211_mgmt *mgmt;
 	union wpa_event_data event;
 	u16 status;
+	int ssid_len;
 
 	if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
 	    drv->force_connect_cmd) {
@@ -247,6 +248,8 @@
 	os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
 
 	os_memset(&event, 0, sizeof(event));
+	event.assoc_info.resp_frame = frame;
+	event.assoc_info.resp_frame_len = len;
 	if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
 		event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
 		event.assoc_info.resp_ies_len =
@@ -255,6 +258,16 @@
 
 	event.assoc_info.freq = drv->assoc_freq;
 
+	/* When this association was initiated outside of wpa_supplicant,
+	 * drv->ssid needs to be set here to satisfy later checking. */
+	ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid);
+	if (ssid_len > 0) {
+		drv->ssid_len = ssid_len;
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Set drv->ssid based on scan res info to '%s'",
+			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
+	}
+
 	nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
 
 	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
@@ -265,6 +278,8 @@
 			       enum nl80211_commands cmd, struct nlattr *status,
 			       struct nlattr *addr, struct nlattr *req_ie,
 			       struct nlattr *resp_ie,
+			       struct nlattr *timed_out,
+			       struct nlattr *timeout_reason,
 			       struct nlattr *authorized,
 			       struct nlattr *key_replay_ctr,
 			       struct nlattr *ptk_kck,
@@ -272,8 +287,9 @@
 			       struct nlattr *subnet_status)
 {
 	union wpa_event_data event;
-	const u8 *ssid;
+	const u8 *ssid = NULL;
 	u16 status_code;
+	int ssid_len;
 
 	if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
 		/*
@@ -322,6 +338,25 @@
 			event.assoc_reject.resp_ies_len = nla_len(resp_ie);
 		}
 		event.assoc_reject.status_code = status_code;
+		event.assoc_reject.timed_out = timed_out != NULL;
+		if (timed_out && timeout_reason) {
+			enum nl80211_timeout_reason reason;
+
+			reason = nla_get_u32(timeout_reason);
+			switch (reason) {
+			case NL80211_TIMEOUT_SCAN:
+				event.assoc_reject.timeout_reason = "scan";
+				break;
+			case NL80211_TIMEOUT_AUTH:
+				event.assoc_reject.timeout_reason = "auth";
+				break;
+			case NL80211_TIMEOUT_ASSOC:
+				event.assoc_reject.timeout_reason = "assoc";
+				break;
+			default:
+				break;
+			}
+		}
 		wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
 		return;
 	}
@@ -343,6 +378,10 @@
 			if (ssid && ssid[1] > 0 && ssid[1] <= 32) {
 				drv->ssid_len = ssid[1];
 				os_memcpy(drv->ssid, ssid + 2, ssid[1]);
+				wpa_printf(MSG_DEBUG,
+					   "nl80211: Set drv->ssid based on req_ie to '%s'",
+					   wpa_ssid_txt(drv->ssid,
+							drv->ssid_len));
 			}
 		}
 	}
@@ -353,6 +392,16 @@
 
 	event.assoc_info.freq = nl80211_get_assoc_freq(drv);
 
+	if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
+	    (ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
+		/* When this connection was initiated outside of wpa_supplicant,
+		 * drv->ssid needs to be set here to satisfy later checking. */
+		drv->ssid_len = ssid_len;
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Set drv->ssid based on scan res info to '%s'",
+			   wpa_ssid_txt(drv->ssid, drv->ssid_len));
+	}
+
 	if (authorized && nla_get_u8(authorized)) {
 		event.assoc_info.authorized = 1;
 		wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
@@ -514,6 +563,7 @@
 		data.ch_switch.cf2 = nla_get_u32(cf2);
 
 	bss->freq = data.ch_switch.freq;
+	drv->assoc_freq = data.ch_switch.freq;
 
 	wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
 }
@@ -829,6 +879,8 @@
 		   MAC2STR(data + 4 + ETH_ALEN));
 	if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
 	    os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+	    (is_zero_ether_addr(bss->rand_addr) ||
+	     os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
 	    os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
 		wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
 			   "for foreign address", bss->ifname);
@@ -914,6 +966,7 @@
 				 struct nlattr *tb[])
 {
 	unsigned int freq;
+	union wpa_event_data event;
 
 	if (tb[NL80211_ATTR_MAC] == NULL) {
 		wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
@@ -933,7 +986,10 @@
 		drv->first_bss->freq = freq;
 	}
 
-	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+	os_memset(&event, 0, sizeof(event));
+	event.assoc_info.freq = freq;
+
+	wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
 }
 
 
@@ -1075,6 +1131,16 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
 			   msg);
 	}
+
+	if (tb[NL80211_ATTR_SCAN_START_TIME_TSF] &&
+	    tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]) {
+		info->scan_start_tsf =
+			nla_get_u64(tb[NL80211_ATTR_SCAN_START_TIME_TSF]);
+		os_memcpy(info->scan_start_tsf_bssid,
+			  nla_data(tb[NL80211_ATTR_SCAN_START_TIME_TSF_BSSID]),
+			  ETH_ALEN);
+	}
+
 	wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
 }
 
@@ -1087,6 +1153,10 @@
 		[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
 		[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
 		[NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
+		[NL80211_ATTR_CQM_BEACON_LOSS_EVENT] = { .type = NLA_FLAG },
 	};
 	struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
 	enum nl80211_cqm_rssi_threshold_event event;
@@ -1108,12 +1178,39 @@
 			return;
 		os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
 			  ETH_ALEN);
+		ed.low_ack.num_packets =
+			nla_get_u32(cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]);
+		wpa_printf(MSG_DEBUG, "nl80211: Packet loss event for " MACSTR
+			   " (num_packets %u)",
+			   MAC2STR(ed.low_ack.addr), ed.low_ack.num_packets);
 		wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
 		return;
 	}
 
-	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+	if (cqm[NL80211_ATTR_CQM_BEACON_LOSS_EVENT]) {
+		wpa_printf(MSG_DEBUG, "nl80211: Beacon loss event");
+		wpa_supplicant_event(drv->ctx, EVENT_BEACON_LOSS, NULL);
 		return;
+	}
+
+	if (cqm[NL80211_ATTR_CQM_TXE_RATE] &&
+	    cqm[NL80211_ATTR_CQM_TXE_PKTS] &&
+	    cqm[NL80211_ATTR_CQM_TXE_INTVL] &&
+	    cqm[NL80211_ATTR_MAC]) {
+		wpa_printf(MSG_DEBUG, "nl80211: CQM TXE event for " MACSTR
+			   " (rate: %u pkts: %u interval: %u)",
+			   MAC2STR((u8 *) nla_data(cqm[NL80211_ATTR_MAC])),
+			   nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_RATE]),
+			   nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_PKTS]),
+			   nla_get_u32(cqm[NL80211_ATTR_CQM_TXE_INTVL]));
+		return;
+	}
+
+	if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Not a CQM RSSI threshold event");
+		return;
+	}
 	event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
 
 	if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
@@ -1124,8 +1221,12 @@
 		wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
 			   "event: RSSI low");
 		ed.signal_change.above_threshold = 0;
-	} else
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Unknown CQM RSSI threshold event: %d",
+			   event);
 		return;
+	}
 
 	res = nl80211_get_link_signal(drv, &sig);
 	if (res == 0) {
@@ -1644,6 +1745,7 @@
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+			   NULL, NULL,
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
 			   tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
@@ -1869,6 +1971,31 @@
 			       external_scan);
 }
 
+
+static void qca_nl80211_p2p_lo_stop_event(struct wpa_driver_nl80211_data *drv,
+					  u8 *data, size_t len)
+{
+	struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX + 1];
+	union wpa_event_data event;
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: P2P listen offload stop vendor event received");
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_MAX,
+		      (struct nlattr *) data, len, NULL) ||
+	    !tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON])
+		return;
+
+	os_memset(&event, 0, sizeof(event));
+	event.p2p_lo_stop.reason_code =
+		nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_P2P_LISTEN_OFFLOAD_STOP_REASON]);
+
+	wpa_printf(MSG_DEBUG,
+		   "nl80211: P2P Listen offload stop reason: %d",
+		   event.p2p_lo_stop.reason_code);
+	wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event);
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 
 
@@ -1902,6 +2029,9 @@
 	case QCA_NL80211_VENDOR_SUBCMD_SCAN_DONE:
 		qca_nl80211_scan_done_event(drv, data, len);
 		break;
+	case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP:
+		qca_nl80211_p2p_lo_stop_event(drv, data, len);
+		break;
 #endif /* CONFIG_DRIVER_NL80211_QCA */
 	default:
 		wpa_printf(MSG_DEBUG,
@@ -2078,9 +2208,10 @@
 	case NL80211_CMD_NEW_SCAN_RESULTS:
 		wpa_dbg(drv->ctx, MSG_DEBUG,
 			"nl80211: New scan results available");
+		if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
+			drv->scan_state = SCAN_COMPLETED;
 		drv->scan_complete_events = 1;
 		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
-			drv->scan_state = SCAN_COMPLETED;
 			eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout,
 					     drv, drv->ctx);
 			drv->last_scan_cmd = 0;
@@ -2097,8 +2228,9 @@
 		break;
 	case NL80211_CMD_SCAN_ABORTED:
 		wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
-		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
+		if (drv->last_scan_cmd != NL80211_CMD_VENDOR)
 			drv->scan_state = SCAN_ABORTED;
+		if (drv->last_scan_cmd == NL80211_CMD_TRIGGER_SCAN) {
 			/*
 			 * Need to indicate that scan results are available in
 			 * order not to make wpa_supplicant stop its scanning.
@@ -2132,6 +2264,8 @@
 				   tb[NL80211_ATTR_MAC],
 				   tb[NL80211_ATTR_REQ_IE],
 				   tb[NL80211_ATTR_RESP_IE],
+				   tb[NL80211_ATTR_TIMED_OUT],
+				   tb[NL80211_ATTR_TIMEOUT_REASON],
 				   NULL, NULL, NULL, NULL, NULL);
 		break;
 	case NL80211_CMD_CH_SWITCH_NOTIFY:
diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c
index 45385da..9376d11 100644
--- a/src/drivers/driver_nl80211_monitor.c
+++ b/src/drivers/driver_nl80211_monitor.c
@@ -136,7 +136,7 @@
 			break;
 		case IEEE80211_RADIOTAP_TX_FLAGS:
 			injected = 1;
-			failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
+			failed = le_to_host16((*(le16 *) iter.this_arg)) &
 					IEEE80211_RADIOTAP_F_TX_FAIL;
 			break;
 		case IEEE80211_RADIOTAP_DATA_RETRIES:
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index c089891..4417721 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -20,6 +20,14 @@
 #include "driver_nl80211.h"
 
 
+#define MAX_NL80211_NOISE_FREQS 50
+
+struct nl80211_noise_info {
+	u32 freq[MAX_NL80211_NOISE_FREQS];
+	s8 noise[MAX_NL80211_NOISE_FREQS];
+	unsigned int count;
+};
+
 static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -29,9 +37,10 @@
 		[NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
 		[NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
 	};
-	struct wpa_scan_results *scan_results = arg;
-	struct wpa_scan_res *scan_res;
-	size_t i;
+	struct nl80211_noise_info *info = arg;
+
+	if (info->count >= MAX_NL80211_NOISE_FREQS)
+		return NL_STOP;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
@@ -55,36 +64,83 @@
 	if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
 		return NL_SKIP;
 
-	for (i = 0; i < scan_results->num; ++i) {
-		scan_res = scan_results->res[i];
-		if (!scan_res)
-			continue;
-		if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
-		    scan_res->freq)
-			continue;
-		if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
-			continue;
-		scan_res->noise = (s8)
-			nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
-		scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
-	}
+	info->freq[info->count] =
+		nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+	info->noise[info->count] =
+		(s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+	info->count++;
 
 	return NL_SKIP;
 }
 
 
 static int nl80211_get_noise_for_scan_results(
-	struct wpa_driver_nl80211_data *drv,
-	struct wpa_scan_results *scan_res)
+	struct wpa_driver_nl80211_data *drv, struct nl80211_noise_info *info)
 {
 	struct nl_msg *msg;
 
+	os_memset(info, 0, sizeof(*info));
 	msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
-	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
-				  scan_res);
+	return send_and_recv_msgs(drv, msg, get_noise_for_scan_results, info);
 }
 
 
+static int nl80211_abort_scan(struct i802_bss *bss)
+{
+	int ret;
+	struct nl_msg *msg;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
+	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+	}
+	return ret;
+}
+
+
+#ifdef CONFIG_DRIVER_NL80211_QCA
+static int nl80211_abort_vendor_scan(struct wpa_driver_nl80211_data *drv,
+				     u64 scan_cookie)
+{
+	struct nl_msg *msg;
+	struct nlattr *params;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Abort vendor scan with cookie 0x%llx",
+		   (long long unsigned int) scan_cookie);
+
+	msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+	if (!msg ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_ABORT_SCAN) ||
+	    !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+	    nla_put_u64(msg, QCA_WLAN_VENDOR_ATTR_SCAN_COOKIE, scan_cookie))
+		goto fail;
+
+	nla_nest_end(msg, params);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_INFO,
+			   "nl80211: Aborting vendor scan with cookie 0x%llx failed: ret=%d (%s)",
+			   (long long unsigned int) scan_cookie, ret,
+			   strerror(-ret));
+		goto fail;
+	}
+	return 0;
+fail:
+	nlmsg_free(msg);
+	return -1;
+}
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+
 /**
  * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
  * @eloop_ctx: Driver private data
@@ -98,7 +154,13 @@
 	struct wpa_driver_nl80211_data *drv = eloop_ctx;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Scan timeout - try to abort it");
-	if (!wpa_driver_nl80211_abort_scan(drv->first_bss))
+#ifdef CONFIG_DRIVER_NL80211_QCA
+	if (drv->vendor_scan_cookie &&
+	    nl80211_abort_vendor_scan(drv, drv->vendor_scan_cookie) == 0)
+		return;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	if (!drv->vendor_scan_cookie &&
+	    nl80211_abort_scan(drv->first_bss) == 0)
 		return;
 
 	wpa_printf(MSG_DEBUG, "nl80211: Failed to abort scan");
@@ -206,6 +268,19 @@
 		}
 	}
 
+	if (params->duration) {
+		if (!(drv->capa.rrm_flags &
+		      WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) ||
+		    nla_put_u16(msg, NL80211_ATTR_MEASUREMENT_DURATION,
+				params->duration))
+			goto fail;
+
+		if (params->duration_mandatory &&
+		    nla_put_flag(msg,
+				 NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY))
+			goto fail;
+	}
+
 	if (scan_flags &&
 	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
 		goto fail;
@@ -487,6 +562,39 @@
 		nla_nest_end(msg, match_sets);
 	}
 
+	if (params->relative_rssi_set) {
+		struct nl80211_bss_select_rssi_adjust rssi_adjust;
+
+		os_memset(&rssi_adjust, 0, sizeof(rssi_adjust));
+		wpa_printf(MSG_DEBUG, "nl80211: Relative RSSI: %d",
+			   params->relative_rssi);
+		if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+				params->relative_rssi))
+			goto fail;
+
+		if (params->relative_adjust_rssi) {
+			int pref_band_set = 1;
+
+			switch (params->relative_adjust_band) {
+			case WPA_SETBAND_5G:
+				rssi_adjust.band = NL80211_BAND_5GHZ;
+				break;
+			case WPA_SETBAND_2G:
+				rssi_adjust.band = NL80211_BAND_2GHZ;
+				break;
+			default:
+				pref_band_set = 0;
+				break;
+			}
+			rssi_adjust.delta = params->relative_adjust_rssi;
+
+			if (pref_band_set &&
+			    nla_put(msg, NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+				    sizeof(rssi_adjust), &rssi_adjust))
+				goto fail;
+		}
+	}
+
 	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
 
 	/* TODO: if we get an error here, we should fall back to normal scan */
@@ -562,7 +670,9 @@
 }
 
 
-int bss_info_handler(struct nl_msg *msg, void *arg)
+static struct wpa_scan_res *
+nl80211_parse_bss_info(struct wpa_driver_nl80211_data *drv,
+		       struct nl_msg *msg)
 {
 	struct nlattr *tb[NL80211_ATTR_MAX + 1];
 	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
@@ -579,50 +689,21 @@
 		[NL80211_BSS_STATUS] = { .type = NLA_U32 },
 		[NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
 		[NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+		[NL80211_BSS_PARENT_TSF] = { .type = NLA_U64 },
+		[NL80211_BSS_PARENT_BSSID] = { .type = NLA_UNSPEC },
 	};
-	struct nl80211_bss_info_arg *_arg = arg;
-	struct wpa_scan_results *res = _arg->res;
-	struct wpa_scan_res **tmp;
 	struct wpa_scan_res *r;
 	const u8 *ie, *beacon_ie;
 	size_t ie_len, beacon_ie_len;
 	u8 *pos;
-	size_t i;
 
 	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
 		  genlmsg_attrlen(gnlh, 0), NULL);
 	if (!tb[NL80211_ATTR_BSS])
-		return NL_SKIP;
+		return NULL;
 	if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
 			     bss_policy))
-		return NL_SKIP;
-	if (bss[NL80211_BSS_STATUS]) {
-		enum nl80211_bss_status status;
-		status = nla_get_u32(bss[NL80211_BSS_STATUS]);
-		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-		    bss[NL80211_BSS_FREQUENCY]) {
-			_arg->assoc_freq =
-				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-			wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
-				   _arg->assoc_freq);
-		}
-		if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
-		    bss[NL80211_BSS_FREQUENCY]) {
-			_arg->ibss_freq =
-				nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-			wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
-				   _arg->ibss_freq);
-		}
-		if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-		    bss[NL80211_BSS_BSSID]) {
-			os_memcpy(_arg->assoc_bssid,
-				  nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
-			wpa_printf(MSG_DEBUG, "nl80211: Associated with "
-				   MACSTR, MAC2STR(_arg->assoc_bssid));
-		}
-	}
-	if (!res)
-		return NL_SKIP;
+		return NULL;
 	if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
 		ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
 		ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
@@ -638,13 +719,13 @@
 		beacon_ie_len = 0;
 	}
 
-	if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+	if (nl80211_scan_filtered(drv, ie ? ie : beacon_ie,
 				  ie ? ie_len : beacon_ie_len))
-		return NL_SKIP;
+		return NULL;
 
 	r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
 	if (r == NULL)
-		return NL_SKIP;
+		return NULL;
 	if (bss[NL80211_BSS_BSSID])
 		os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
 			  ETH_ALEN);
@@ -695,40 +776,36 @@
 		}
 	}
 
-	/*
-	 * cfg80211 maintains separate BSS table entries for APs if the same
-	 * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
-	 * not use frequency as a separate key in the BSS table, so filter out
-	 * duplicated entries. Prefer associated BSS entry in such a case in
-	 * order to get the correct frequency into the BSS table. Similarly,
-	 * prefer newer entries over older.
-	 */
-	for (i = 0; i < res->num; i++) {
-		const u8 *s1, *s2;
-		if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
-			continue;
-
-		s1 = get_ie((u8 *) (res->res[i] + 1),
-			    res->res[i]->ie_len, WLAN_EID_SSID);
-		s2 = get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
-		if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
-		    os_memcmp(s1, s2, 2 + s1[1]) != 0)
-			continue;
-
-		/* Same BSSID,SSID was already included in scan results */
-		wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
-			   "for " MACSTR, MAC2STR(r->bssid));
-
-		if (((r->flags & WPA_SCAN_ASSOCIATED) &&
-		     !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) ||
-		    r->age < res->res[i]->age) {
-			os_free(res->res[i]);
-			res->res[i] = r;
-		} else
-			os_free(r);
-		return NL_SKIP;
+	if (bss[NL80211_BSS_PARENT_TSF] && bss[NL80211_BSS_PARENT_BSSID]) {
+		r->parent_tsf = nla_get_u64(bss[NL80211_BSS_PARENT_TSF]);
+		os_memcpy(r->tsf_bssid, nla_data(bss[NL80211_BSS_PARENT_BSSID]),
+			  ETH_ALEN);
 	}
 
+	return r;
+}
+
+
+struct nl80211_bss_info_arg {
+	struct wpa_driver_nl80211_data *drv;
+	struct wpa_scan_results *res;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+	struct nl80211_bss_info_arg *_arg = arg;
+	struct wpa_scan_results *res = _arg->res;
+	struct wpa_scan_res **tmp;
+	struct wpa_scan_res *r;
+
+	r = nl80211_parse_bss_info(_arg->drv, msg);
+	if (!r)
+		return NL_SKIP;
+
+	if (!res) {
+		os_free(r);
+		return NL_SKIP;
+	}
 	tmp = os_realloc_array(res->res, res->num + 1,
 			       sizeof(struct wpa_scan_res *));
 	if (tmp == NULL) {
@@ -755,36 +832,51 @@
 }
 
 
+static void nl80211_check_bss_status(struct wpa_driver_nl80211_data *drv,
+				     struct wpa_scan_res *r)
+{
+	if (!(r->flags & WPA_SCAN_ASSOCIATED))
+		return;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan results indicate BSS status with "
+		   MACSTR " as associated", MAC2STR(r->bssid));
+	if (is_sta_interface(drv->nlmode) && !drv->associated) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Local state (not associated) does not match with BSS state");
+		clear_state_mismatch(drv, r->bssid);
+	} else if (is_sta_interface(drv->nlmode) &&
+		   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Local state (associated with " MACSTR
+			   ") does not match with BSS state",
+			   MAC2STR(drv->bssid));
+		clear_state_mismatch(drv, r->bssid);
+		clear_state_mismatch(drv, drv->bssid);
+	}
+}
+
+
 static void wpa_driver_nl80211_check_bss_status(
 	struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
 {
 	size_t i;
 
-	for (i = 0; i < res->num; i++) {
-		struct wpa_scan_res *r = res->res[i];
+	for (i = 0; i < res->num; i++)
+		nl80211_check_bss_status(drv, res->res[i]);
+}
 
-		if (r->flags & WPA_SCAN_ASSOCIATED) {
-			wpa_printf(MSG_DEBUG, "nl80211: Scan results "
-				   "indicate BSS status with " MACSTR
-				   " as associated",
-				   MAC2STR(r->bssid));
-			if (is_sta_interface(drv->nlmode) &&
-			    !drv->associated) {
-				wpa_printf(MSG_DEBUG, "nl80211: Local state "
-					   "(not associated) does not match "
-					   "with BSS state");
-				clear_state_mismatch(drv, r->bssid);
-			} else if (is_sta_interface(drv->nlmode) &&
-				   os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
-				   0) {
-				wpa_printf(MSG_DEBUG, "nl80211: Local state "
-					   "(associated with " MACSTR ") does "
-					   "not match with BSS state",
-					   MAC2STR(drv->bssid));
-				clear_state_mismatch(drv, r->bssid);
-				clear_state_mismatch(drv, drv->bssid);
-			}
-		}
+
+static void nl80211_update_scan_res_noise(struct wpa_scan_res *res,
+					  struct nl80211_noise_info *info)
+{
+	unsigned int i;
+
+	for (i = 0; res && i < info->count; i++) {
+		if ((int) info->freq[i] != res->freq ||
+		    !(res->flags & WPA_SCAN_NOISE_INVALID))
+			continue;
+		res->noise = info->noise[i];
+		res->flags &= ~WPA_SCAN_NOISE_INVALID;
 	}
 }
 
@@ -810,9 +902,17 @@
 	arg.res = res;
 	ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
 	if (ret == 0) {
+		struct nl80211_noise_info info;
+
 		wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
 			   "BSSes)", (unsigned long) res->num);
-		nl80211_get_noise_for_scan_results(drv, res);
+		if (nl80211_get_noise_for_scan_results(drv, &info) == 0) {
+			size_t i;
+
+			for (i = 0; i < res->num; ++i)
+				nl80211_update_scan_res_noise(res->res[i],
+							      &info);
+		}
 		return res;
 	}
 	wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
@@ -840,45 +940,57 @@
 }
 
 
-void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+struct nl80211_dump_scan_ctx {
+	struct wpa_driver_nl80211_data *drv;
+	int idx;
+};
+
+static int nl80211_dump_scan_handler(struct nl_msg *msg, void *arg)
 {
-	struct wpa_scan_results *res;
-	size_t i;
+	struct nl80211_dump_scan_ctx *ctx = arg;
+	struct wpa_scan_res *r;
 
-	res = nl80211_get_scan_results(drv);
-	if (res == NULL) {
-		wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
-	for (i = 0; i < res->num; i++) {
-		struct wpa_scan_res *r = res->res[i];
-		wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s",
-			   (int) i, (int) res->num, MAC2STR(r->bssid),
-			   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
-	}
-
-	wpa_scan_results_free(res);
+	r = nl80211_parse_bss_info(ctx->drv, msg);
+	if (!r)
+		return NL_SKIP;
+	wpa_printf(MSG_DEBUG, "nl80211: %d " MACSTR " %d%s",
+		   ctx->idx, MAC2STR(r->bssid), r->freq,
+		   r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+	ctx->idx++;
+	os_free(r);
+	return NL_SKIP;
 }
 
 
-int wpa_driver_nl80211_abort_scan(void *priv)
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+	struct nl_msg *msg;
+	struct nl80211_dump_scan_ctx ctx;
+
+	wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+	ctx.drv = drv;
+	ctx.idx = 0;
+	msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
+	if (msg)
+		send_and_recv_msgs(drv, msg, nl80211_dump_scan_handler, &ctx);
+}
+
+
+int wpa_driver_nl80211_abort_scan(void *priv, u64 scan_cookie)
 {
 	struct i802_bss *bss = priv;
+#ifdef CONFIG_DRIVER_NL80211_QCA
 	struct wpa_driver_nl80211_data *drv = bss->drv;
-	int ret;
-	struct nl_msg *msg;
 
-	wpa_printf(MSG_DEBUG, "nl80211: Abort scan");
-	msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ABORT_SCAN);
-	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-	if (ret) {
-		wpa_printf(MSG_DEBUG, "nl80211: Abort scan failed: ret=%d (%s)",
-			   ret, strerror(-ret));
-	}
-
-	return ret;
+	/*
+	 * If scan_cookie is zero, a normal scan through kernel (cfg80211)
+	 * was triggered, hence abort the cfg80211 scan instead of the vendor
+	 * scan.
+	 */
+	if (drv->scan_vendor_cmd_avail && scan_cookie)
+		return nl80211_abort_vendor_scan(drv, scan_cookie);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+	return nl80211_abort_scan(bss);
 }
 
 
@@ -1015,7 +1127,7 @@
 	}
 
 	if (scan_flags &&
-	    nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+	    nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SCAN_FLAGS, scan_flags))
 		goto fail;
 
 	if (params->p2p_probe) {
@@ -1043,6 +1155,14 @@
 			goto fail;
 	}
 
+	if (params->bssid) {
+		wpa_printf(MSG_DEBUG, "nl80211: Scan for a specific BSSID: "
+			   MACSTR, MAC2STR(params->bssid));
+		if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SCAN_BSSID, ETH_ALEN,
+			    params->bssid))
+			goto fail;
+	}
+
 	nla_nest_end(msg, attr);
 
 	ret = send_and_recv_msgs(drv, msg, scan_cookie_handler, &cookie);
@@ -1056,6 +1176,8 @@
 
 	drv->vendor_scan_cookie = cookie;
 	drv->scan_state = SCAN_REQUESTED;
+	/* Pass the cookie to the caller to help distinguish the scans. */
+	params->scan_cookie = cookie;
 
 	wpa_printf(MSG_DEBUG,
 		   "nl80211: Vendor scan requested (ret=%d) - scan timeout 30 seconds, scan cookie:0x%llx",
@@ -1070,4 +1192,54 @@
 	return ret;
 }
 
+
+/**
+ * nl80211_set_default_scan_ies - Set the scan default IEs to the driver
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @ies: Pointer to IEs buffer
+ * @ies_len: Length of IEs in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int nl80211_set_default_scan_ies(void *priv, const u8 *ies, size_t ies_len)
+{
+	struct i802_bss *bss = priv;
+	struct wpa_driver_nl80211_data *drv = bss->drv;
+	struct nl_msg *msg = NULL;
+	struct nlattr *attr;
+	int ret = -1;
+
+	if (!drv->set_wifi_conf_vendor_cmd_avail)
+		return -1;
+
+	if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+	    nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+			QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION))
+		goto fail;
+
+	attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+	if (attr == NULL)
+		goto fail;
+
+	wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan default IEs", ies, ies_len);
+	if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_CONFIG_SCAN_DEFAULT_IES,
+		    ies_len, ies))
+		goto fail;
+
+	nla_nest_end(msg, attr);
+
+	ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+	msg = NULL;
+	if (ret) {
+		wpa_printf(MSG_ERROR,
+			   "nl80211: Set scan default IEs failed: ret=%d (%s)",
+			   ret, strerror(-ret));
+		goto fail;
+	}
+
+fail:
+	nlmsg_free(msg);
+	return ret;
+}
+
 #endif /* CONFIG_DRIVER_NL80211_QCA */
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
index 43d4193..655128a 100644
--- a/src/drivers/driver_privsep.c
+++ b/src/drivers/driver_privsep.c
@@ -97,15 +97,31 @@
 	return 0;
 }
 
-			     
+
 static int wpa_driver_privsep_scan(void *priv,
 				   struct wpa_driver_scan_params *params)
 {
 	struct wpa_driver_privsep_data *drv = priv;
-	const u8 *ssid = params->ssids[0].ssid;
-	size_t ssid_len = params->ssids[0].ssid_len;
+	struct privsep_cmd_scan scan;
+	size_t i;
+
 	wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
-	return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len,
+	os_memset(&scan, 0, sizeof(scan));
+	scan.num_ssids = params->num_ssids;
+	for (i = 0; i < params->num_ssids; i++) {
+		if (!params->ssids[i].ssid)
+			continue;
+		scan.ssid_lens[i] = params->ssids[i].ssid_len;
+		os_memcpy(scan.ssids[i], params->ssids[i].ssid,
+			  scan.ssid_lens[i]);
+	}
+
+	for (i = 0; i < PRIVSEP_MAX_SCAN_FREQS &&
+		     params->freqs && params->freqs[i]; i++)
+		scan.freqs[i] = params->freqs[i];
+	scan.num_freqs = i;
+
+	return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, &scan, sizeof(scan),
 			    NULL, NULL);
 }
 
@@ -173,7 +189,11 @@
 			break;
 		os_memcpy(r, pos, len);
 		pos += len;
-		if (sizeof(*r) + r->ie_len > (size_t) len) {
+		if (sizeof(*r) + r->ie_len + r->beacon_ie_len > (size_t) len) {
+			wpa_printf(MSG_ERROR,
+				   "privsep: Invalid scan result len (%d + %d + %d > %d)",
+				   (int) sizeof(*r), (int) r->ie_len,
+				   (int) r->beacon_ie_len, len);
 			os_free(r);
 			break;
 		}
@@ -234,7 +254,7 @@
 		   __func__, priv, params->freq, MAC2STR(params->bssid),
 		   params->auth_alg, params->local_state_change, params->p2p);
 
-	buflen = sizeof(*data) + params->ie_len + params->sae_data_len;
+	buflen = sizeof(*data) + params->ie_len + params->auth_data_len;
 	data = os_zalloc(buflen);
 	if (data == NULL)
 		return -1;
@@ -259,8 +279,8 @@
 		os_memcpy(pos, params->ie, params->ie_len);
 		pos += params->ie_len;
 	}
-	if (params->sae_data_len)
-		os_memcpy(pos, params->sae_data, params->sae_data_len);
+	if (params->auth_data_len)
+		os_memcpy(pos, params->auth_data, params->auth_data_len);
 
 	res = wpa_priv_cmd(drv, PRIVSEP_CMD_AUTHENTICATE, data, buflen,
 			   NULL, NULL);
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index d3e0595..e8a5135 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - roboswitch driver interface
- * Copyright (c) 2008-2009 Jouke Witteveen
+ * Copyright (c) 2008-2012 Jouke Witteveen
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -401,7 +401,9 @@
 		os_free(drv);
 		return NULL;
 	}
-	if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) {
+	/* BCM63xx devices provide 0 here */
+	if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR &&
+	    if_mii(&drv->ifr)->phy_id != 0) {
 		wpa_printf(MSG_INFO, "%s: Invalid phy address (not a "
 			   "RoboSwitch?)", __func__);
 		os_free(drv);
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 791cd5d..458d458 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -1042,6 +1042,7 @@
 	wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
 
 	eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+	eloop_cancel_timeout(wpa_driver_wext_send_rfkill, drv, drv->ctx);
 
 	/*
 	 * Clear possibly configured driver parameters in order to make it
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
index 15e82df..7e09dcf 100644
--- a/src/drivers/driver_wired.c
+++ b/src/drivers/driver_wired.c
@@ -12,13 +12,14 @@
 #include "common.h"
 #include "eloop.h"
 #include "driver.h"
+#include "driver_wired_common.h"
 
 #include <sys/ioctl.h>
+#undef IFNAMSIZ
 #include <net/if.h>
 #ifdef __linux__
 #include <netpacket/packet.h>
 #include <net/if_arp.h>
-#include <net/if.h>
 #endif /* __linux__ */
 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
 #include <net/if_dl.h>
@@ -42,20 +43,12 @@
 #pragma pack(pop)
 #endif /* _MSC_VER */
 
-static const u8 pae_group_addr[ETH_ALEN] =
-{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
-
 
 struct wpa_driver_wired_data {
-	char ifname[IFNAMSIZ + 1];
-	void *ctx;
+	struct driver_wired_common_data common;
 
-	int sock; /* raw packet socket for driver access */
 	int dhcp_sock; /* socket for dhcp packets */
 	int use_pae_group_addr;
-
-	int pf_sock;
-	int membership, multi, iff_allmulti, iff_up;
 };
 
 
@@ -83,34 +76,6 @@
 };
 
 
-static int wired_multicast_membership(int sock, int ifindex,
-				      const u8 *addr, int add)
-{
-#ifdef __linux__
-	struct packet_mreq mreq;
-
-	if (sock < 0)
-		return -1;
-
-	os_memset(&mreq, 0, sizeof(mreq));
-	mreq.mr_ifindex = ifindex;
-	mreq.mr_type = PACKET_MR_MULTICAST;
-	mreq.mr_alen = ETH_ALEN;
-	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
-
-	if (setsockopt(sock, SOL_PACKET,
-		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
-		       &mreq, sizeof(mreq)) < 0) {
-		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
-		return -1;
-	}
-	return 0;
-#else /* __linux__ */
-	return -1;
-#endif /* __linux__ */
-}
-
-
 #ifdef __linux__
 static void handle_data(void *ctx, unsigned char *buf, size_t len)
 {
@@ -208,21 +173,22 @@
 	struct sockaddr_in addr2;
 	int n = 1;
 
-	drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
-	if (drv->sock < 0) {
+	drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+	if (drv->common.sock < 0) {
 		wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
 			   strerror(errno));
 		return -1;
 	}
 
-	if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
+	if (eloop_register_read_sock(drv->common.sock, handle_read,
+				     drv->common.ctx, NULL)) {
 		wpa_printf(MSG_INFO, "Could not register read socket");
 		return -1;
 	}
 
 	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
-	if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) {
 		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
 			   strerror(errno));
 		return -1;
@@ -234,13 +200,14 @@
 	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
 		   addr.sll_ifindex);
 
-	if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+	if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+	{
 		wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
 		return -1;
 	}
 
 	/* filter multicast address */
-	if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex,
+	if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex,
 				       pae_group_addr, 1) < 0) {
 		wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
 			   "membership");
@@ -248,8 +215,8 @@
 	}
 
 	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
-	if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
+	os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name));
+	if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) {
 		wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
 			   strerror(errno));
 		return -1;
@@ -269,8 +236,8 @@
 		return -1;
 	}
 
-	if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
-				     NULL)) {
+	if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp,
+				     drv->common.ctx, NULL)) {
 		wpa_printf(MSG_INFO, "Could not register read socket");
 		return -1;
 	}
@@ -294,7 +261,7 @@
 	}
 
 	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
+	os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->common.ifname, IFNAMSIZ);
 	if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
 		       (char *) &ifr, sizeof(ifr)) < 0) {
 		wpa_printf(MSG_ERROR,
@@ -343,7 +310,7 @@
 	pos = (u8 *) (hdr + 1);
 	os_memcpy(pos, data, data_len);
 
-	res = send(drv->sock, (u8 *) hdr, len, 0);
+	res = send(drv->common.sock, (u8 *) hdr, len, 0);
 	os_free(hdr);
 
 	if (res < 0) {
@@ -368,8 +335,9 @@
 		return NULL;
 	}
 
-	drv->ctx = hapd;
-	os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+	drv->common.ctx = hapd;
+	os_strlcpy(drv->common.ifname, params->ifname,
+		   sizeof(drv->common.ifname));
 	drv->use_pae_group_addr = params->use_pae_group_addr;
 
 	if (wired_init_sockets(drv, params->own_addr)) {
@@ -385,9 +353,9 @@
 {
 	struct wpa_driver_wired_data *drv = priv;
 
-	if (drv->sock >= 0) {
-		eloop_unregister_read_sock(drv->sock);
-		close(drv->sock);
+	if (drv->common.sock >= 0) {
+		eloop_unregister_read_sock(drv->common.sock);
+		close(drv->common.sock);
 	}
 
 	if (drv->dhcp_sock >= 0) {
@@ -399,227 +367,18 @@
 }
 
 
-static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
-{
-	ssid[0] = 0;
-	return 0;
-}
-
-
-static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
-{
-	/* Report PAE group address as the "BSSID" for wired connection. */
-	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
-	return 0;
-}
-
-
-static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
-{
-	os_memset(capa, 0, sizeof(*capa));
-	capa->flags = WPA_DRIVER_FLAGS_WIRED;
-	return 0;
-}
-
-
-static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
-{
-	struct ifreq ifr;
-	int s;
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	*flags = ifr.ifr_flags & 0xffff;
-	return 0;
-}
-
-
-static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
-{
-	struct ifreq ifr;
-	int s;
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-	ifr.ifr_flags = flags & 0xffff;
-	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	return 0;
-}
-
-
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status)
-{
-	struct ifmediareq ifmr;
-	int s;
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifmr, 0, sizeof(ifmr));
-	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
-	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	*status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
-		(IFM_ACTIVE | IFM_AVALID);
-
-	return 0;
-}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-
-
-static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
-{
-	struct ifreq ifr;
-	int s;
-
-#ifdef __sun__
-	return -1;
-#endif /* __sun__ */
-
-	s = socket(PF_INET, SOCK_DGRAM, 0);
-	if (s < 0) {
-		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
-		return -1;
-	}
-
-	os_memset(&ifr, 0, sizeof(ifr));
-	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
-#ifdef __linux__
-	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
-	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
-#endif /* __linux__ */
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-	{
-		struct sockaddr_dl *dlp;
-		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
-		dlp->sdl_len = sizeof(struct sockaddr_dl);
-		dlp->sdl_family = AF_LINK;
-		dlp->sdl_index = 0;
-		dlp->sdl_nlen = 0;
-		dlp->sdl_alen = ETH_ALEN;
-		dlp->sdl_slen = 0;
-		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
-	}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
-#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
-	{
-		struct sockaddr *sap;
-		sap = (struct sockaddr *) &ifr.ifr_addr;
-		sap->sa_len = sizeof(struct sockaddr);
-		sap->sa_family = AF_UNSPEC;
-		os_memcpy(sap->sa_data, addr, ETH_ALEN);
-	}
-#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
-
-	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
-		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
-			   strerror(errno));
-		close(s);
-		return -1;
-	}
-	close(s);
-	return 0;
-}
-
-
 static void * wpa_driver_wired_init(void *ctx, const char *ifname)
 {
 	struct wpa_driver_wired_data *drv;
-	int flags;
 
 	drv = os_zalloc(sizeof(*drv));
 	if (drv == NULL)
 		return NULL;
-	os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
-	drv->ctx = ctx;
 
-#ifdef __linux__
-	drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
-	if (drv->pf_sock < 0)
-		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
-#else /* __linux__ */
-	drv->pf_sock = -1;
-#endif /* __linux__ */
-
-	if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 &&
-	    !(flags & IFF_UP) &&
-	    wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) {
-		drv->iff_up = 1;
-	}
-
-	if (wired_multicast_membership(drv->pf_sock,
-				       if_nametoindex(drv->ifname),
-				       pae_group_addr, 1) == 0) {
-		wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
-			   "packet socket", __func__);
-		drv->membership = 1;
-	} else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
-		wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
-			   "SIOCADDMULTI", __func__);
-		drv->multi = 1;
-	} else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) {
-		wpa_printf(MSG_INFO, "%s: Could not get interface "
-			   "flags", __func__);
+	if (driver_wired_init_common(&drv->common, ifname, ctx) < 0) {
 		os_free(drv);
 		return NULL;
-	} else if (flags & IFF_ALLMULTI) {
-		wpa_printf(MSG_DEBUG, "%s: Interface is already configured "
-			   "for multicast", __func__);
-	} else if (wpa_driver_wired_set_ifflags(ifname,
-						flags | IFF_ALLMULTI) < 0) {
-		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
-			   __func__);
-		os_free(drv);
-		return NULL;
-	} else {
-		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode",
-			   __func__);
-		drv->iff_allmulti = 1;
 	}
-#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
-	{
-		int status;
-		wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
-			   __func__);
-		while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 &&
-		       status == 0)
-			sleep(1);
-	}
-#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
 
 	return drv;
 }
@@ -628,41 +387,8 @@
 static void wpa_driver_wired_deinit(void *priv)
 {
 	struct wpa_driver_wired_data *drv = priv;
-	int flags;
 
-	if (drv->membership &&
-	    wired_multicast_membership(drv->pf_sock,
-				       if_nametoindex(drv->ifname),
-				       pae_group_addr, 0) < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
-			   "group (PACKET)", __func__);
-	}
-
-	if (drv->multi &&
-	    wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
-			   "group (SIOCDELMULTI)", __func__);
-	}
-
-	if (drv->iff_allmulti &&
-	    (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
-	     wpa_driver_wired_set_ifflags(drv->ifname,
-					  flags & ~IFF_ALLMULTI) < 0)) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
-			   __func__);
-	}
-
-	if (drv->iff_up &&
-	    wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
-	    (flags & IFF_UP) &&
-	    wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
-		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
-			   __func__);
-	}
-
-	if (drv->pf_sock != -1)
-		close(drv->pf_sock);
-
+	driver_wired_deinit_common(&drv->common);
 	os_free(drv);
 }
 
@@ -673,9 +399,9 @@
 	.hapd_init = wired_driver_hapd_init,
 	.hapd_deinit = wired_driver_hapd_deinit,
 	.hapd_send_eapol = wired_send_eapol,
-	.get_ssid = wpa_driver_wired_get_ssid,
-	.get_bssid = wpa_driver_wired_get_bssid,
-	.get_capa = wpa_driver_wired_get_capa,
+	.get_ssid = driver_wired_get_ssid,
+	.get_bssid = driver_wired_get_bssid,
+	.get_capa = driver_wired_get_capa,
 	.init = wpa_driver_wired_init,
 	.deinit = wpa_driver_wired_deinit,
 };
diff --git a/src/drivers/driver_wired_common.c b/src/drivers/driver_wired_common.c
new file mode 100644
index 0000000..a860b1c
--- /dev/null
+++ b/src/drivers/driver_wired_common.c
@@ -0,0 +1,322 @@
+/*
+ * Common functions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+#include "driver_wired_common.h"
+
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+
+static int driver_wired_get_ifflags(const char *ifname, int *flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	*flags = ifr.ifr_flags & 0xffff;
+	return 0;
+}
+
+
+static int driver_wired_set_ifflags(const char *ifname, int flags)
+{
+	struct ifreq ifr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+	ifr.ifr_flags = flags & 0xffff;
+	if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+static int driver_wired_multi(const char *ifname, const u8 *addr, int add)
+{
+	struct ifreq ifr;
+	int s;
+
+#ifdef __sun__
+	return -1;
+#endif /* __sun__ */
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifr, 0, sizeof(ifr));
+	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+	ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+	os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+	{
+		struct sockaddr_dl *dlp;
+
+		dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+		dlp->sdl_len = sizeof(struct sockaddr_dl);
+		dlp->sdl_family = AF_LINK;
+		dlp->sdl_index = 0;
+		dlp->sdl_nlen = 0;
+		dlp->sdl_alen = ETH_ALEN;
+		dlp->sdl_slen = 0;
+		os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+	}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+	{
+		struct sockaddr *sap;
+
+		sap = (struct sockaddr *) &ifr.ifr_addr;
+		sap->sa_len = sizeof(struct sockaddr);
+		sap->sa_family = AF_UNSPEC;
+		os_memcpy(sap->sa_data, addr, ETH_ALEN);
+	}
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+	if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	return 0;
+}
+
+
+int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add)
+{
+#ifdef __linux__
+	struct packet_mreq mreq;
+
+	if (sock < 0)
+		return -1;
+
+	os_memset(&mreq, 0, sizeof(mreq));
+	mreq.mr_ifindex = ifindex;
+	mreq.mr_type = PACKET_MR_MULTICAST;
+	mreq.mr_alen = ETH_ALEN;
+	os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+	if (setsockopt(sock, SOL_PACKET,
+		       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+		       &mreq, sizeof(mreq)) < 0) {
+		wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+		return -1;
+	}
+	return 0;
+#else /* __linux__ */
+	return -1;
+#endif /* __linux__ */
+}
+
+
+int driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+	ssid[0] = 0;
+	return 0;
+}
+
+
+int driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+	/* Report PAE group address as the "BSSID" for wired connection. */
+	os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+	return 0;
+}
+
+
+int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+	os_memset(capa, 0, sizeof(*capa));
+	capa->flags = WPA_DRIVER_FLAGS_WIRED;
+	return 0;
+}
+
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int driver_wired_get_ifstatus(const char *ifname, int *status)
+{
+	struct ifmediareq ifmr;
+	int s;
+
+	s = socket(PF_INET, SOCK_DGRAM, 0);
+	if (s < 0) {
+		wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+		return -1;
+	}
+
+	os_memset(&ifmr, 0, sizeof(ifmr));
+	os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+	if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+		wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+			   strerror(errno));
+		close(s);
+		return -1;
+	}
+	close(s);
+	*status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+		(IFM_ACTIVE | IFM_AVALID);
+
+	return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+			     const char *ifname, void *ctx)
+{
+	int flags;
+
+	os_strlcpy(common->ifname, ifname, sizeof(common->ifname));
+	common->ctx = ctx;
+
+#ifdef __linux__
+	common->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+	if (common->pf_sock < 0)
+		wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#else /* __linux__ */
+	common->pf_sock = -1;
+#endif /* __linux__ */
+
+	if (driver_wired_get_ifflags(ifname, &flags) == 0 &&
+	    !(flags & IFF_UP) &&
+	    driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0)
+		common->iff_up = 1;
+
+	if (wired_multicast_membership(common->pf_sock,
+				       if_nametoindex(common->ifname),
+				       pae_group_addr, 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Added multicast membership with packet socket",
+			   __func__);
+		common->membership = 1;
+	} else if (driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Added multicast membership with SIOCADDMULTI",
+			   __func__);
+		common->multi = 1;
+	} else if (driver_wired_get_ifflags(ifname, &flags) < 0) {
+		wpa_printf(MSG_INFO, "%s: Could not get interface flags",
+			   __func__);
+		return -1;
+	} else if (flags & IFF_ALLMULTI) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Interface is already configured for multicast",
+			   __func__);
+	} else if (driver_wired_set_ifflags(ifname,
+						flags | IFF_ALLMULTI) < 0) {
+		wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", __func__);
+		return -1;
+	} else {
+		wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
+		common->iff_allmulti = 1;
+	}
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+	{
+		int status;
+
+		wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+			   __func__);
+		while (driver_wired_get_ifstatus(ifname, &status) == 0 &&
+		       status == 0)
+			sleep(1);
+	}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+	return 0;
+}
+
+
+void driver_wired_deinit_common(struct driver_wired_common_data *common)
+{
+	int flags;
+
+	if (common->membership &&
+	    wired_multicast_membership(common->pf_sock,
+				       if_nametoindex(common->ifname),
+				       pae_group_addr, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to remove PAE multicast group (PACKET)",
+			   __func__);
+	}
+
+	if (common->multi &&
+	    driver_wired_multi(common->ifname, pae_group_addr, 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
+			   __func__);
+	}
+
+	if (common->iff_allmulti &&
+	    (driver_wired_get_ifflags(common->ifname, &flags) < 0 ||
+	     driver_wired_set_ifflags(common->ifname,
+				      flags & ~IFF_ALLMULTI) < 0)) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+			   __func__);
+	}
+
+	if (common->iff_up &&
+	    driver_wired_get_ifflags(common->ifname, &flags) == 0 &&
+	    (flags & IFF_UP) &&
+	    driver_wired_set_ifflags(common->ifname, flags & ~IFF_UP) < 0) {
+		wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+			   __func__);
+	}
+
+	if (common->pf_sock != -1)
+		close(common->pf_sock);
+}
diff --git a/src/drivers/driver_wired_common.h b/src/drivers/driver_wired_common.h
new file mode 100644
index 0000000..2bb0710
--- /dev/null
+++ b/src/drivers/driver_wired_common.h
@@ -0,0 +1,34 @@
+/*
+ * Common definitions for Wired Ethernet driver interfaces
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_WIRED_COMMON_H
+#define DRIVER_WIRED_COMMON_H
+
+struct driver_wired_common_data {
+	char ifname[IFNAMSIZ + 1];
+	void *ctx;
+
+	int sock; /* raw packet socket for driver access */
+	int pf_sock;
+	int membership, multi, iff_allmulti, iff_up;
+};
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+int wired_multicast_membership(int sock, int ifindex, const u8 *addr, int add);
+int driver_wired_get_ssid(void *priv, u8 *ssid);
+int driver_wired_get_bssid(void *priv, u8 *bssid);
+int driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa);
+
+int driver_wired_init_common(struct driver_wired_common_data *common,
+			     const char *ifname, void *ctx);
+void driver_wired_deinit_common(struct driver_wired_common_data *common);
+
+#endif /* DRIVER_WIRED_COMMON_H */
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
index a98af9a..e95df6d 100644
--- a/src/drivers/drivers.c
+++ b/src/drivers/drivers.c
@@ -10,42 +10,6 @@
 #include "utils/common.h"
 #include "driver.h"
 
-#ifdef CONFIG_DRIVER_WEXT
-extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
-#endif /* CONFIG_DRIVER_WEXT */
-#ifdef CONFIG_DRIVER_NL80211
-extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
-#endif /* CONFIG_DRIVER_NL80211 */
-#ifdef CONFIG_DRIVER_HOSTAP
-extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
-#endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_BSD
-extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
-#endif /* CONFIG_DRIVER_BSD */
-#ifdef CONFIG_DRIVER_OPENBSD
-extern struct wpa_driver_ops wpa_driver_openbsd_ops; /* driver_openbsd.c */
-#endif /* CONFIG_DRIVER_OPENBSD */
-#ifdef CONFIG_DRIVER_NDIS
-extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
-#endif /* CONFIG_DRIVER_NDIS */
-#ifdef CONFIG_DRIVER_WIRED
-extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
-#endif /* CONFIG_DRIVER_WIRED */
-#ifdef CONFIG_DRIVER_MACSEC_QCA
- /* driver_macsec_qca.c */
-extern struct wpa_driver_ops wpa_driver_macsec_qca_ops;
-#endif /* CONFIG_DRIVER_MACSEC_QCA */
-#ifdef CONFIG_DRIVER_ROBOSWITCH
-/* driver_roboswitch.c */
-extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
-#endif /* CONFIG_DRIVER_ROBOSWITCH */
-#ifdef CONFIG_DRIVER_ATHEROS
-extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */
-#endif /* CONFIG_DRIVER_ATHEROS */
-#ifdef CONFIG_DRIVER_NONE
-extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
-#endif /* CONFIG_DRIVER_NONE */
-
 
 const struct wpa_driver_ops *const wpa_drivers[] =
 {
@@ -70,6 +34,9 @@
 #ifdef CONFIG_DRIVER_WIRED
 	&wpa_driver_wired_ops,
 #endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_MACSEC_LINUX
+	&wpa_driver_macsec_linux_ops,
+#endif /* CONFIG_DRIVER_MACSEC_LINUX */
 #ifdef CONFIG_DRIVER_MACSEC_QCA
 	&wpa_driver_macsec_qca_ops,
 #endif /* CONFIG_DRIVER_MACSEC_QCA */
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index c1bfff1..1496b47 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -15,11 +15,24 @@
 ifdef CONFIG_DRIVER_WIRED
 DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
 DRV_OBJS += ../src/drivers/driver_wired.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += ../src/drivers/driver_macsec_linux.o
+NEED_DRV_WIRED_COMMON=1
+CONFIG_LIBNL3_ROUTE=y
 endif
 
 ifdef CONFIG_DRIVER_MACSEC_QCA
 DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
 DRV_OBJS += ../src/drivers/driver_macsec_qca.o
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += ../src/drivers/driver_wired_common.o
 endif
 
 ifdef CONFIG_DRIVER_NL80211
@@ -29,7 +42,6 @@
 DRV_OBJS += ../src/drivers/driver_nl80211_event.o
 DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
 DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
-DRV_OBJS += ../src/utils/radiotap.o
 ifdef CONFIG_DRIVER_NL80211_QCA
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
 endif
@@ -38,6 +50,7 @@
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
 NEED_RFKILL=y
+NEED_RADIOTAP=y
 
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
@@ -164,6 +177,10 @@
 DRV_OBJS += ../src/drivers/rfkill.o
 endif
 
+ifdef NEED_RADIOTAP
+DRV_OBJS += ../src/utils/radiotap.o
+endif
+
 ifdef CONFIG_VLAN_NETLINK
 ifdef CONFIG_FULL_DYNAMIC_VLAN
 ifdef CONFIG_LIBNL32
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index 0444e52..cd25133 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -15,6 +15,18 @@
 ifdef CONFIG_DRIVER_WIRED
 DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
 DRV_OBJS += src/drivers/driver_wired.c
+NEED_DRV_WIRED_COMMON=1
+endif
+
+ifdef CONFIG_DRIVER_MACSEC_LINUX
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
+DRV_OBJS += src/drivers/driver_macsec_linux.c
+NEED_DRV_WIRED_COMMON=1
+CONFIG_LIBNL3_ROUTE=y
+endif
+
+ifdef NEED_DRV_WIRED_COMMON
+DRV_OBJS += src/drivers/driver_wired_common.c
 endif
 
 ifdef CONFIG_DRIVER_NL80211
@@ -25,7 +37,6 @@
 DRV_OBJS += src/drivers/driver_nl80211_event.c
 DRV_OBJS += src/drivers/driver_nl80211_monitor.c
 DRV_OBJS += src/drivers/driver_nl80211_scan.c
-DRV_OBJS += src/utils/radiotap.c
 ifdef CONFIG_DRIVER_NL80211_QCA
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211_QCA
 endif
@@ -34,6 +45,7 @@
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
 NEED_RFKILL=y
+NEED_RADIOTAP=y
 
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
@@ -149,6 +161,10 @@
 DRV_OBJS += src/drivers/rfkill.c
 endif
 
+ifdef NEED_RADIOTAP
+DRV_OBJS += src/utils/radiotap.c
+endif
+
 ifdef CONFIG_DRIVER_CUSTOM
 DRV_CFLAGS += -DCONFIG_DRIVER_CUSTOM
 endif
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 7758969..d6c62ee 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -48,6 +48,7 @@
 #define NL80211_MULTICAST_GROUP_REG		"regulatory"
 #define NL80211_MULTICAST_GROUP_MLME		"mlme"
 #define NL80211_MULTICAST_GROUP_VENDOR		"vendor"
+#define NL80211_MULTICAST_GROUP_NAN		"nan"
 #define NL80211_MULTICAST_GROUP_TESTMODE	"testmode"
 
 /**
@@ -322,7 +323,9 @@
  * @NL80211_CMD_GET_SCAN: get scan results
  * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
  *	%NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
- *	probe requests at CCK rate or not.
+ *	probe requests at CCK rate or not. %NL80211_ATTR_BSSID can be used to
+ *	specify a BSSID to scan for; if not included, the wildcard BSSID will
+ *	be used.
  * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
  *	NL80211_CMD_GET_SCAN and on the "scan" multicast group)
  * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
@@ -427,7 +430,11 @@
  * @NL80211_CMD_ASSOCIATE: association request and notification; like
  *	NL80211_CMD_AUTHENTICATE but for Association and Reassociation
  *	(similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request,
- *	MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives).
+ *	MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). The
+ *	%NL80211_ATTR_PREV_BSSID attribute is used to specify whether the
+ *	request is for the initial association to an ESS (that attribute not
+ *	included) or for reassociation within the ESS (that attribute is
+ *	included).
  * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like
  *	NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to
  *	MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication
@@ -477,6 +484,9 @@
  *	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).
+ *	%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.
  *	Background scan period can optionally be
  *	specified in %NL80211_ATTR_BG_SCAN_PERIOD,
  *	if not specified default background scan configuration
@@ -484,7 +494,12 @@
  *	This attribute is ignored if driver does not support roam scan.
  *	It is also sent as an event, with the BSSID and response IEs when the
  *	connection is established or failed to be established. This can be
- *	determined by the STATUS_CODE attribute.
+ *	determined by the %NL80211_ATTR_STATUS_CODE attribute (0 = success,
+ *	non-zero = failure). If %NL80211_ATTR_TIMED_OUT is included in the
+ *	event, the connection attempt failed due to not being able to initiate
+ *	authentication/association or not receiving a response from the AP.
+ *	Non-zero %NL80211_ATTR_STATUS_CODE value is indicated in that case as
+ *	well to remain backwards compatible.
  * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
  *	sent as an event when the card/driver roamed by itself.
  * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
@@ -585,6 +600,20 @@
  *
  * @NL80211_CMD_SET_WDS_PEER: Set the MAC address of the peer on a WDS interface.
  *
+ * @NL80211_CMD_SET_MULTICAST_TO_UNICAST: Configure if this AP should perform
+ *	multicast to unicast conversion. When enabled, all multicast packets
+ *	with ethertype ARP, IPv4 or IPv6 (possibly within an 802.1Q header)
+ *	will be sent out to each station once with the destination (multicast)
+ *	MAC address replaced by the station's MAC address. Note that this may
+ *	break certain expectations of the receiver, e.g. the ability to drop
+ *	unicast IP packets encapsulated in multicast L2 frames, or the ability
+ *	to not send destination unreachable messages in such cases.
+ *	This can only be toggled per BSS. Configure this on an interface of
+ *	type %NL80211_IFTYPE_AP. It applies to all its VLAN interfaces
+ *	(%NL80211_IFTYPE_AP_VLAN), except for those in 4addr (WDS) mode.
+ *	If %NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED is not present with this
+ *	command, the feature is disabled.
+ *
  * @NL80211_CMD_JOIN_MESH: Join a mesh. The mesh ID must be given, and initial
  *	mesh config parameters may be given.
  * @NL80211_CMD_LEAVE_MESH: Leave the mesh network -- no special arguments, the
@@ -824,6 +853,47 @@
  *	not running. The driver indicates the status of the scan through
  *	cfg80211_scan_done().
  *
+ * @NL80211_CMD_START_NAN: Start NAN operation, identified by its
+ *	%NL80211_ATTR_WDEV interface. This interface must have been previously
+ *	created with %NL80211_CMD_NEW_INTERFACE. After it has been started, the
+ *	NAN interface will create or join a cluster. This command must have a
+ *	valid %NL80211_ATTR_NAN_MASTER_PREF attribute and optional
+ *	%NL80211_ATTR_NAN_DUAL attributes.
+ *	After this command NAN functions can be added.
+ * @NL80211_CMD_STOP_NAN: Stop the NAN operation, identified by
+ *	its %NL80211_ATTR_WDEV interface.
+ * @NL80211_CMD_ADD_NAN_FUNCTION: Add a NAN function. The function is defined
+ *	with %NL80211_ATTR_NAN_FUNC nested attribute. When called, this
+ *	operation returns the strictly positive and unique instance id
+ *	(%NL80211_ATTR_NAN_FUNC_INST_ID) and a cookie (%NL80211_ATTR_COOKIE)
+ *	of the function upon success.
+ *	Since instance ID's can be re-used, this cookie is the right
+ *	way to identify the function. This will avoid races when a termination
+ *	event is handled by the user space after it has already added a new
+ *	function that got the same instance id from the kernel as the one
+ *	which just terminated.
+ *	This cookie may be used in NAN events even before the command
+ *	returns, so userspace shouldn't process NAN events until it processes
+ *	the response to this command.
+ *	Look at %NL80211_ATTR_SOCKET_OWNER as well.
+ * @NL80211_CMD_DEL_NAN_FUNCTION: Delete a NAN function by cookie.
+ *	This command is also used as a notification sent when a NAN function is
+ *	terminated. This will contain a %NL80211_ATTR_NAN_FUNC_INST_ID
+ *	and %NL80211_ATTR_COOKIE attributes.
+ * @NL80211_CMD_CHANGE_NAN_CONFIG: Change current NAN configuration. NAN
+ *	must be operational (%NL80211_CMD_START_NAN was executed).
+ *	It must contain at least one of the following attributes:
+ *	%NL80211_ATTR_NAN_MASTER_PREF, %NL80211_ATTR_NAN_DUAL.
+ * @NL80211_CMD_NAN_FUNC_MATCH: Notification sent when a match is reported.
+ *	This will contain a %NL80211_ATTR_NAN_MATCH nested attribute and
+ *	%NL80211_ATTR_COOKIE.
+ *
+ * @NL80211_CMD_UPDATE_CONNECT_PARAMS: Update one or more connect parameters
+ *	for subsequent roaming cases if the driver or firmware uses internal
+ *	BSS selection. This command can be issued only while connected and it
+ *	does not result in a change for the current association. Currently,
+ *	only the %NL80211_ATTR_IE data is used and updated with this command.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1012,6 +1082,17 @@
 
 	NL80211_CMD_ABORT_SCAN,
 
+	NL80211_CMD_START_NAN,
+	NL80211_CMD_STOP_NAN,
+	NL80211_CMD_ADD_NAN_FUNCTION,
+	NL80211_CMD_DEL_NAN_FUNCTION,
+	NL80211_CMD_CHANGE_NAN_CONFIG,
+	NL80211_CMD_NAN_MATCH,
+
+	NL80211_CMD_SET_MULTICAST_TO_UNICAST,
+
+	NL80211_CMD_UPDATE_CONNECT_PARAMS,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -1285,8 +1366,11 @@
  * @NL80211_ATTR_RESP_IE: (Re)association response information elements as
  *	sent by peer, for ROAM and successful CONNECT events.
  *
- * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE
- *	commands to specify using a reassociate frame
+ * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used in ASSOCIATE and CONNECT
+ *	commands to specify a request to reassociate within an ESS, i.e., to use
+ *	Reassociate Request frame (with the value of this attribute in the
+ *	Current AP address field) instead of Association Request frame which is
+ *	used for the initial association to an ESS.
  *
  * @NL80211_ATTR_KEY: key information in a nested attribute with
  *	%NL80211_KEY_* sub-attributes
@@ -1326,7 +1410,13 @@
  *	enum nl80211_band value is used as the index (nla_type() of the nested
  *	data. If a band is not included, it will be configured to allow all
  *	rates based on negotiated supported rates information. This attribute
- *	is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
+ *	is used with %NL80211_CMD_SET_TX_BITRATE_MASK and with starting AP,
+ *	and joining mesh networks (not IBSS yet). In the later case, it must
+ *	specify just a single bitrate, which is to be used for the beacon.
+ *	The driver must also specify support for this with the extended
+ *	features NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+ *	NL80211_EXT_FEATURE_BEACON_RATE_HT and
+ *	NL80211_EXT_FEATURE_BEACON_RATE_VHT.
  *
  * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
  *	at least one byte, currently used with @NL80211_CMD_REGISTER_FRAME.
@@ -1572,8 +1662,16 @@
  *	the connection request from a station. nl80211_connect_failed_reason
  *	enum has different reasons of connection failure.
  *
- * @NL80211_ATTR_SAE_DATA: SAE elements in Authentication frames. This starts
- *	with the Authentication transaction sequence number field.
+ * @NL80211_ATTR_AUTH_DATA: Fields and elements in Authentication frames.
+ *	This contains the authentication frame body (non-IE and IE data),
+ *	excluding the Authentication algorithm number, i.e., starting at the
+ *	Authentication transaction sequence number field. It is used with
+ *	authentication algorithms that need special fields to be added into
+ *	the frames (SAE and FILS). Currently, only the SAE cases use the
+ *	initial two fields (Authentication transaction sequence number and
+ *	Status code). However, those fields are included in the attribute data
+ *	for all authentication algorithms to keep the attribute definition
+ *	consistent.
  *
  * @NL80211_ATTR_VHT_CAPABILITY: VHT Capability information element (from
  *	association request when used with NL80211_CMD_NEW_STATION)
@@ -1674,7 +1772,9 @@
  *
  * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
  *	Notification Element based on association request when used with
- *	%NL80211_CMD_NEW_STATION; u8 attribute.
+ *	%NL80211_CMD_NEW_STATION or %NL80211_CMD_SET_STATION (only when
+ *	%NL80211_FEATURE_FULL_AP_CLIENT_STATE is supported, or with TDLS);
+ *	u8 attribute.
  *
  * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
  *	%NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
@@ -1716,6 +1816,14 @@
  *	regulatory indoor configuration would be owned by the netlink socket
  *	that configured the indoor setting, and the indoor operation would be
  *	cleared when the socket is closed.
+ *	If set during NAN interface creation, the interface will be destroyed
+ *	if the socket is closed just like any other interface. Moreover, only
+ *	the netlink socket that created the interface will be allowed to add
+ *	and remove functions. NAN notifications will be sent in unicast to that
+ *	socket. Without this attribute, any socket can add functions and the
+ *	notifications will be sent to the %NL80211_MCGRP_NAN multicast group.
+ *	If set during %NL80211_CMD_ASSOCIATE or %NL80211_CMD_CONNECT the
+ *	station will deauthenticate when the socket is closed.
  *
  * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
  *	the TDLS link initiator.
@@ -1727,6 +1835,8 @@
  *	underlying device supports these minimal RRM features:
  *		%NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES,
  *		%NL80211_FEATURE_QUIET,
+ *	Or, if global RRM is supported, see:
+ *		%NL80211_EXT_FEATURE_RRM
  *	If this flag is used, driver must add the Power Capabilities IE to the
  *	association request. In addition, it must also set the RRM capability
  *	flag in the association request's Capability Info field.
@@ -1793,6 +1903,104 @@
  *	in a PBSS. Specified in %NL80211_CMD_CONNECT to request
  *	connecting to a PCP, and in %NL80211_CMD_START_AP to start
  *	a PCP instead of AP. Relevant for DMG networks only.
+ * @NL80211_ATTR_BSS_SELECT: nested attribute for driver supporting the
+ *	BSS selection feature. When used with %NL80211_CMD_GET_WIPHY it contains
+ *	attributes according &enum nl80211_bss_select_attr to indicate what
+ *	BSS selection behaviours are supported. When used with %NL80211_CMD_CONNECT
+ *	it contains the behaviour-specific attribute containing the parameters for
+ *	BSS selection to be done by driver and/or firmware.
+ *
+ * @NL80211_ATTR_STA_SUPPORT_P2P_PS: whether P2P PS mechanism supported
+ *	or not. u8, one of the values of &enum nl80211_sta_p2p_ps_status
+ *
+ * @NL80211_ATTR_PAD: attribute used for padding for 64-bit alignment
+ *
+ * @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
+ *	%NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
+ *	%NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
+ *	interface type.
+ *
+ * @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
+ *	groupID for monitor mode.
+ *	The first 8 bytes are a mask that defines the membership in each
+ *	group (there are 64 groups, group 0 and 63 are reserved),
+ *	each bit represents a group and set to 1 for being a member in
+ *	that group and 0 for not being a member.
+ *	The remaining 16 bytes define the position in each group: 2 bits for
+ *	each group.
+ *	(smaller group numbers represented on most significant bits and bigger
+ *	group numbers on least significant bits.)
+ *	This attribute is used only if all interfaces are in monitor mode.
+ *	Set this attribute in order to monitor packets using the given MU-MIMO
+ *	groupID data.
+ *	to turn off that feature set all the bits of the groupID to zero.
+ * @NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR: mac address for the sniffer to follow
+ *	when using MU-MIMO air sniffer.
+ *	to turn that feature off set an invalid mac address
+ *	(e.g. FF:FF:FF:FF:FF:FF)
+ *
+ * @NL80211_ATTR_SCAN_START_TIME_TSF: The time at which the scan was actually
+ *	started (u64). The time is the TSF of the BSS the interface that
+ *	requested the scan is connected to (if available, otherwise this
+ *	attribute must not be included).
+ * @NL80211_ATTR_SCAN_START_TIME_TSF_BSSID: The BSS according to which
+ *	%NL80211_ATTR_SCAN_START_TIME_TSF is set.
+ * @NL80211_ATTR_MEASUREMENT_DURATION: measurement duration in TUs (u16). If
+ *	%NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY is not set, this is the
+ *	maximum measurement duration allowed. This attribute is used with
+ *	measurement requests. It can also be used with %NL80211_CMD_TRIGGER_SCAN
+ *	if the scan is used for beacon report radio measurement.
+ * @NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY: flag attribute that indicates
+ *	that the duration specified with %NL80211_ATTR_MEASUREMENT_DURATION is
+ *	mandatory. If this flag is not set, the duration is the maximum duration
+ *	and the actual measurement duration may be shorter.
+ *
+ * @NL80211_ATTR_MESH_PEER_AID: Association ID for the mesh peer (u16). This is
+ *	used to pull the stored data for mesh peer in power save state.
+ *
+ * @NL80211_ATTR_NAN_MASTER_PREF: the master preference to be used by
+ *	%NL80211_CMD_START_NAN and optionally with
+ *	%NL80211_CMD_CHANGE_NAN_CONFIG. Its type is u8 and it can't be 0.
+ *	Also, values 1 and 255 are reserved for certification purposes and
+ *	should not be used during a normal device operation.
+ * @NL80211_ATTR_NAN_DUAL: NAN dual band operation config (see
+ *	&enum nl80211_nan_dual_band_conf). This attribute is used with
+ *	%NL80211_CMD_START_NAN and optionally with
+ *	%NL80211_CMD_CHANGE_NAN_CONFIG.
+ * @NL80211_ATTR_NAN_FUNC: a function that can be added to NAN. See
+ *	&enum nl80211_nan_func_attributes for description of this nested
+ *	attribute.
+ * @NL80211_ATTR_NAN_MATCH: used to report a match. This is a nested attribute.
+ *	See &enum nl80211_nan_match_attributes.
+ * @NL80211_ATTR_FILS_KEK: KEK for FILS (Re)Association Request/Response frame
+ *	protection.
+ * @NL80211_ATTR_FILS_NONCES: Nonces (part of AAD) for FILS (Re)Association
+ *	Request/Response frame protection. This attribute contains the 16 octet
+ *	STA Nonce followed by 16 octets of AP Nonce.
+ *
+ * @NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED: Indicates whether or not multicast
+ *	packets should be send out as unicast to all stations (flag attribute).
+ *
+ * @NL80211_ATTR_BSSID: The BSSID of the AP. Note that %NL80211_ATTR_MAC is also
+ *	used in various commands/events for specifying the BSSID.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI: Relative RSSI threshold by which
+ *	other BSSs has to be better or slightly worse than the current
+ *	connected BSS so that they get reported to user space.
+ *	This will give an opportunity to userspace to consider connecting to
+ *	other matching BSSs which have better or slightly worse RSSI than
+ *	the current connected BSS by using an offloaded operation to avoid
+ *	unnecessary wakeups.
+ *
+ * @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
+ *	the specified band is to be adjusted before doing
+ *	%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
+ *	better BSSs. The attribute value is a packed structure
+ *	value as specified by &struct nl80211_bss_select_rssi_adjust.
+ *
+ * @NL80211_ATTR_TIMEOUT_REASON: The reason for which an operation timed out.
+ *	u32 attribute with an &enum nl80211_timeout_reason value. This is used,
+ *	e.g., with %NL80211_CMD_CONNECT event.
  *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
@@ -2052,7 +2260,7 @@
 
 	NL80211_ATTR_CONN_FAILED_REASON,
 
-	NL80211_ATTR_SAE_DATA,
+	NL80211_ATTR_AUTH_DATA,
 
 	NL80211_ATTR_VHT_CAPABILITY,
 
@@ -2170,6 +2378,41 @@
 
 	NL80211_ATTR_PBSS,
 
+	NL80211_ATTR_BSS_SELECT,
+
+	NL80211_ATTR_STA_SUPPORT_P2P_PS,
+
+	NL80211_ATTR_PAD,
+
+	NL80211_ATTR_IFTYPE_EXT_CAPA,
+
+	NL80211_ATTR_MU_MIMO_GROUP_DATA,
+	NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR,
+
+	NL80211_ATTR_SCAN_START_TIME_TSF,
+	NL80211_ATTR_SCAN_START_TIME_TSF_BSSID,
+	NL80211_ATTR_MEASUREMENT_DURATION,
+	NL80211_ATTR_MEASUREMENT_DURATION_MANDATORY,
+
+	NL80211_ATTR_MESH_PEER_AID,
+
+	NL80211_ATTR_NAN_MASTER_PREF,
+	NL80211_ATTR_NAN_DUAL,
+	NL80211_ATTR_NAN_FUNC,
+	NL80211_ATTR_NAN_MATCH,
+
+	NL80211_ATTR_FILS_KEK,
+	NL80211_ATTR_FILS_NONCES,
+
+	NL80211_ATTR_MULTICAST_TO_UNICAST_ENABLED,
+
+	NL80211_ATTR_BSSID,
+
+	NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI,
+	NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST,
+
+	NL80211_ATTR_TIMEOUT_REASON,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -2181,6 +2424,7 @@
 #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
 #define	NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
 #define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
+#define NL80211_ATTR_SAE_DATA NL80211_ATTR_AUTH_DATA
 
 /*
  * Allow user space programs to use #ifdef on new attributes by defining them
@@ -2248,6 +2492,7 @@
  *	commands to create and destroy one
  * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
  *	This mode corresponds to the MIB variable dot11OCBActivated=true
+ * @NL80211_IFTYPE_NAN: NAN device interface type (not a netdev)
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NUM_NL80211_IFTYPES: number of defined interface types
  *
@@ -2268,6 +2513,7 @@
 	NL80211_IFTYPE_P2P_GO,
 	NL80211_IFTYPE_P2P_DEVICE,
 	NL80211_IFTYPE_OCB,
+	NL80211_IFTYPE_NAN,
 
 	/* keep last */
 	NUM_NL80211_IFTYPES,
@@ -2313,6 +2559,20 @@
 	NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_sta_p2p_ps_status - station support of P2P PS
+ *
+ * @NL80211_P2P_PS_UNSUPPORTED: station doesn't support P2P PS mechanism
+ * @@NL80211_P2P_PS_SUPPORTED: station supports P2P PS mechanism
+ * @NUM_NL80211_P2P_PS_STATUS: number of values
+ */
+enum nl80211_sta_p2p_ps_status {
+	NL80211_P2P_PS_UNSUPPORTED = 0,
+	NL80211_P2P_PS_SUPPORTED,
+
+	NUM_NL80211_P2P_PS_STATUS,
+};
+
 #define NL80211_STA_FLAG_MAX_OLD_API	NL80211_STA_FLAG_TDLS_PEER
 
 /**
@@ -2470,6 +2730,9 @@
  *	TID+1 and the special TID 16 (i.e. value 17) is used for non-QoS frames;
  *	each one of those is again nested with &enum nl80211_tid_stats
  *	attributes carrying the actual values.
+ * @NL80211_STA_INFO_RX_DURATION: aggregate PPDU duration for all frames
+ *	received from the station (u64, usec)
+ * @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -2506,6 +2769,8 @@
 	NL80211_STA_INFO_BEACON_RX,
 	NL80211_STA_INFO_BEACON_SIGNAL_AVG,
 	NL80211_STA_INFO_TID_STATS,
+	NL80211_STA_INFO_RX_DURATION,
+	NL80211_STA_INFO_PAD,
 
 	/* keep last */
 	__NL80211_STA_INFO_AFTER_LAST,
@@ -2522,6 +2787,7 @@
  *	transmitted MSDUs (not counting the first attempt; u64)
  * @NL80211_TID_STATS_TX_MSDU_FAILED: number of failed transmitted
  *	MSDUs (u64)
+ * @NL80211_TID_STATS_PAD: attribute used for padding for 64-bit alignment
  * @NUM_NL80211_TID_STATS: number of attributes here
  * @NL80211_TID_STATS_MAX: highest numbered attribute here
  */
@@ -2531,6 +2797,7 @@
 	NL80211_TID_STATS_TX_MSDU,
 	NL80211_TID_STATS_TX_MSDU_RETRIES,
 	NL80211_TID_STATS_TX_MSDU_FAILED,
+	NL80211_TID_STATS_PAD,
 
 	/* keep last */
 	NUM_NL80211_TID_STATS,
@@ -2838,6 +3105,13 @@
  *	how this API was implemented in the past. Also, due to the same problem,
  *	the only way to create a matchset with only an RSSI filter (with this
  *	attribute) is if there's only a single matchset with the RSSI attribute.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI: Flag indicating whether
+ *	%NL80211_SCHED_SCAN_MATCH_ATTR_RSSI to be used as absolute RSSI or
+ *	relative to current bss's RSSI.
+ * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST: When present the RSSI level for
+ *	BSS-es in the specified band is to be adjusted before doing
+ *	RSSI-based BSS selection. The attribute value is a packed structure
+ *	value as specified by &struct nl80211_bss_select_rssi_adjust.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *	attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -2847,6 +3121,8 @@
 
 	NL80211_SCHED_SCAN_MATCH_ATTR_SSID,
 	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RELATIVE_RSSI,
+	NL80211_SCHED_SCAN_MATCH_ATTR_RSSI_ADJUST,
 
 	/* keep last */
 	__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST,
@@ -2967,6 +3243,7 @@
  *	transmitting data (on channel or globally)
  * @NL80211_SURVEY_INFO_TIME_SCAN: time the radio spent for scan
  *	(on this channel or globally)
+ * @NL80211_SURVEY_INFO_PAD: attribute used for padding for 64-bit alignment
  * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number
  *	currently defined
  * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use
@@ -2982,6 +3259,7 @@
 	NL80211_SURVEY_INFO_TIME_RX,
 	NL80211_SURVEY_INFO_TIME_TX,
 	NL80211_SURVEY_INFO_TIME_SCAN,
+	NL80211_SURVEY_INFO_PAD,
 
 	/* keep last */
 	__NL80211_SURVEY_INFO_AFTER_LAST,
@@ -3407,6 +3685,13 @@
  * @NL80211_BSS_LAST_SEEN_BOOTTIME: CLOCK_BOOTTIME timestamp when this entry
  *	was last updated by a received frame. The value is expected to be
  *	accurate to about 10ms. (u64, nanoseconds)
+ * @NL80211_BSS_PAD: attribute used for padding for 64-bit alignment
+ * @NL80211_BSS_PARENT_TSF: the time at the start of reception of the first
+ *	octet of the timestamp field of the last beacon/probe received for
+ *	this BSS. The time is the TSF of the BSS specified by
+ *	@NL80211_BSS_PARENT_BSSID. (u64).
+ * @NL80211_BSS_PARENT_BSSID: the BSS according to which @NL80211_BSS_PARENT_TSF
+ *	is set.
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -3427,6 +3712,9 @@
 	NL80211_BSS_BEACON_TSF,
 	NL80211_BSS_PRESP_DATA,
 	NL80211_BSS_LAST_SEEN_BOOTTIME,
+	NL80211_BSS_PAD,
+	NL80211_BSS_PARENT_TSF,
+	NL80211_BSS_PARENT_BSSID,
 
 	/* keep last */
 	__NL80211_BSS_AFTER_LAST,
@@ -3459,6 +3747,9 @@
  * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
  * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
  * @NL80211_AUTHTYPE_SAE: Simultaneous authentication of equals
+ * @NL80211_AUTHTYPE_FILS_SK: Fast Initial Link Setup shared key
+ * @NL80211_AUTHTYPE_FILS_SK_PFS: Fast Initial Link Setup shared key with PFS
+ * @NL80211_AUTHTYPE_FILS_PK: Fast Initial Link Setup public key
  * @__NL80211_AUTHTYPE_NUM: internal
  * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
  * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
@@ -3471,6 +3762,9 @@
 	NL80211_AUTHTYPE_FT,
 	NL80211_AUTHTYPE_NETWORK_EAP,
 	NL80211_AUTHTYPE_SAE,
+	NL80211_AUTHTYPE_FILS_SK,
+	NL80211_AUTHTYPE_FILS_SK_PFS,
+	NL80211_AUTHTYPE_FILS_PK,
 
 	/* keep last */
 	__NL80211_AUTHTYPE_NUM,
@@ -3612,11 +3906,15 @@
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
  * @NL80211_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
  * @NL80211_BAND_60GHZ: around 60 GHz band (58.32 - 64.80 GHz)
+ * @NUM_NL80211_BANDS: number of bands, avoid using this in userspace
+ *	since newer kernel versions may support more bands
  */
 enum nl80211_band {
 	NL80211_BAND_2GHZ,
 	NL80211_BAND_5GHZ,
 	NL80211_BAND_60GHZ,
+
+	NUM_NL80211_BANDS,
 };
 
 /**
@@ -4075,6 +4373,9 @@
  *	of supported channel widths for radar detection.
  * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
  *	of supported regulatory regions for radar detection.
+ * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of
+ *	different beacon intervals supported by all the interface combinations
+ *	in this group (if not present, all beacon intervals be identical).
  * @NUM_NL80211_IFACE_COMB: number of attributes
  * @MAX_NL80211_IFACE_COMB: highest attribute number
  *
@@ -4082,8 +4383,8 @@
  *	limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
  *	=> allows an AP and a STA that must match BIs
  *
- *	numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8
- *	=> allows 8 of AP/GO
+ *	numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8,
+ *	=> allows 8 of AP/GO that can have BI gcd >= min gcd
  *
  *	numbers = [ #{STA} <= 2 ], channels = 2, max = 2
  *	=> allows two STAs on different channels
@@ -4109,6 +4410,7 @@
 	NL80211_IFACE_COMB_NUM_CHANNELS,
 	NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
 	NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+	NL80211_IFACE_COMB_BI_MIN_GCD,
 
 	/* keep last */
 	NUM_NL80211_IFACE_COMB,
@@ -4402,12 +4704,60 @@
 /**
  * enum nl80211_ext_feature_index - bit index of extended features.
  * @NL80211_EXT_FEATURE_VHT_IBSS: This driver supports IBSS with VHT datarates.
+ * @NL80211_EXT_FEATURE_RRM: This driver supports RRM. When featured, user can
+ *	can request to use RRM (see %NL80211_ATTR_USE_RRM) with
+ *	%NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests, which will set
+ *	the ASSOC_REQ_USE_RRM flag in the association request even if
+ *	NL80211_FEATURE_QUIET is not advertized.
+ * @NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER: This device supports MU-MIMO air
+ *	sniffer which means that it can be configured to hear packets from
+ *	certain groups which can be configured by the
+ *	%NL80211_ATTR_MU_MIMO_GROUP_DATA attribute,
+ *	or can be configured to follow a station by configuring the
+ *	%NL80211_ATTR_MU_MIMO_FOLLOW_MAC_ADDR attribute.
+ * @NL80211_EXT_FEATURE_SCAN_START_TIME: This driver includes the actual
+ *	time the scan started in scan results event. The time is the TSF of
+ *	the BSS that the interface that requested the scan is connected to
+ *	(if available).
+ * @NL80211_EXT_FEATURE_BSS_PARENT_TSF: Per BSS, this driver reports the
+ *	time the last beacon/probe was received. The time is the TSF of the
+ *	BSS that the interface that requested the scan is connected to
+ *	(if available).
+ * @NL80211_EXT_FEATURE_SET_SCAN_DWELL: This driver supports configuration of
+ *	channel dwell time.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_LEGACY: Driver supports beacon rate
+ *	configuration (AP/mesh), supporting a legacy (non HT/VHT) rate.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_HT: Driver supports beacon rate
+ *	configuration (AP/mesh) with HT rates.
+ * @NL80211_EXT_FEATURE_BEACON_RATE_VHT: Driver supports beacon rate
+ *	configuration (AP/mesh) with VHT rates.
+ * @NL80211_EXT_FEATURE_FILS_STA: This driver supports Fast Initial Link Setup
+ *	with user space SME (NL80211_CMD_AUTHENTICATE) in station mode.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA: This driver supports randomized TA
+ *	in @NL80211_CMD_FRAME while not associated.
+ * @NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED: This driver supports
+ *	randomized TA in @NL80211_CMD_FRAME while associated.
+ * @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
+ *	for reporting BSSs with better RSSI than the current connected BSS
+ *	(%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
 enum nl80211_ext_feature_index {
 	NL80211_EXT_FEATURE_VHT_IBSS,
+	NL80211_EXT_FEATURE_RRM,
+	NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER,
+	NL80211_EXT_FEATURE_SCAN_START_TIME,
+	NL80211_EXT_FEATURE_BSS_PARENT_TSF,
+	NL80211_EXT_FEATURE_SET_SCAN_DWELL,
+	NL80211_EXT_FEATURE_BEACON_RATE_LEGACY,
+	NL80211_EXT_FEATURE_BEACON_RATE_HT,
+	NL80211_EXT_FEATURE_BEACON_RATE_VHT,
+	NL80211_EXT_FEATURE_FILS_STA,
+	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
+	NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
+	NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -4447,6 +4797,21 @@
 };
 
 /**
+ * enum nl80211_timeout_reason - timeout reasons
+ *
+ * @NL80211_TIMEOUT_UNSPECIFIED: Timeout reason unspecified.
+ * @NL80211_TIMEOUT_SCAN: Scan (AP discovery) timed out.
+ * @NL80211_TIMEOUT_AUTH: Authentication timed out.
+ * @NL80211_TIMEOUT_ASSOC: Association timed out.
+ */
+enum nl80211_timeout_reason {
+	NL80211_TIMEOUT_UNSPECIFIED,
+	NL80211_TIMEOUT_SCAN,
+	NL80211_TIMEOUT_AUTH,
+	NL80211_TIMEOUT_ASSOC,
+};
+
+/**
  * enum nl80211_scan_flags -  scan request control flags
  *
  * Scan request control flags are used to control the handling
@@ -4657,4 +5022,231 @@
 		__NL80211_SCHED_SCAN_PLAN_AFTER_LAST - 1
 };
 
+/**
+ * struct nl80211_bss_select_rssi_adjust - RSSI adjustment parameters.
+ *
+ * @band: band of BSS that must match for RSSI value adjustment. The value
+ *	of this field is according to &enum nl80211_band.
+ * @delta: value used to adjust the RSSI value of matching BSS in dB.
+ */
+struct nl80211_bss_select_rssi_adjust {
+	__u8 band;
+	__s8 delta;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_bss_select_attr - attributes for bss selection.
+ *
+ * @__NL80211_BSS_SELECT_ATTR_INVALID: reserved.
+ * @NL80211_BSS_SELECT_ATTR_RSSI: Flag indicating only RSSI-based BSS selection
+ *	is requested.
+ * @NL80211_BSS_SELECT_ATTR_BAND_PREF: attribute indicating BSS
+ *	selection should be done such that the specified band is preferred.
+ *	When there are multiple BSS-es in the preferred band, the driver
+ *	shall use RSSI-based BSS selection as a second step. The value of
+ *	this attribute is according to &enum nl80211_band (u32).
+ * @NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: When present the RSSI level for
+ *	BSS-es in the specified band is to be adjusted before doing
+ *	RSSI-based BSS selection. The attribute value is a packed structure
+ *	value as specified by &struct nl80211_bss_select_rssi_adjust.
+ * @NL80211_BSS_SELECT_ATTR_MAX: highest bss select attribute number.
+ * @__NL80211_BSS_SELECT_ATTR_AFTER_LAST: internal use.
+ *
+ * One and only one of these attributes are found within %NL80211_ATTR_BSS_SELECT
+ * for %NL80211_CMD_CONNECT. It specifies the required BSS selection behaviour
+ * which the driver shall use.
+ */
+enum nl80211_bss_select_attr {
+	__NL80211_BSS_SELECT_ATTR_INVALID,
+	NL80211_BSS_SELECT_ATTR_RSSI,
+	NL80211_BSS_SELECT_ATTR_BAND_PREF,
+	NL80211_BSS_SELECT_ATTR_RSSI_ADJUST,
+
+	/* keep last */
+	__NL80211_BSS_SELECT_ATTR_AFTER_LAST,
+	NL80211_BSS_SELECT_ATTR_MAX = __NL80211_BSS_SELECT_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_nan_dual_band_conf - NAN dual band configuration
+ *
+ * Defines the NAN dual band mode of operation
+ *
+ * @NL80211_NAN_BAND_DEFAULT: device default mode
+ * @NL80211_NAN_BAND_2GHZ: 2.4GHz mode
+ * @NL80211_NAN_BAND_5GHZ: 5GHz mode
+  */
+enum nl80211_nan_dual_band_conf {
+	NL80211_NAN_BAND_DEFAULT	= 1 << 0,
+	NL80211_NAN_BAND_2GHZ		= 1 << 1,
+	NL80211_NAN_BAND_5GHZ		= 1 << 2,
+};
+
+/**
+ * enum nl80211_nan_function_type - NAN function type
+ *
+ * Defines the function type of a NAN function
+ *
+ * @NL80211_NAN_FUNC_PUBLISH: function is publish
+ * @NL80211_NAN_FUNC_SUBSCRIBE: function is subscribe
+ * @NL80211_NAN_FUNC_FOLLOW_UP: function is follow-up
+ */
+enum nl80211_nan_function_type {
+	NL80211_NAN_FUNC_PUBLISH,
+	NL80211_NAN_FUNC_SUBSCRIBE,
+	NL80211_NAN_FUNC_FOLLOW_UP,
+
+	/* keep last */
+	__NL80211_NAN_FUNC_TYPE_AFTER_LAST,
+	NL80211_NAN_FUNC_MAX_TYPE = __NL80211_NAN_FUNC_TYPE_AFTER_LAST - 1,
+};
+
+/**
+ * enum nl80211_nan_publish_type - NAN publish tx type
+ *
+ * Defines how to send publish Service Discovery Frames
+ *
+ * @NL80211_NAN_SOLICITED_PUBLISH: publish function is solicited
+ * @NL80211_NAN_UNSOLICITED_PUBLISH: publish function is unsolicited
+ */
+enum nl80211_nan_publish_type {
+	NL80211_NAN_SOLICITED_PUBLISH = 1 << 0,
+	NL80211_NAN_UNSOLICITED_PUBLISH = 1 << 1,
+};
+
+/**
+ * enum nl80211_nan_func_term_reason - NAN functions termination reason
+ *
+ * Defines termination reasons of a NAN function
+ *
+ * @NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST: requested by user
+ * @NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED: timeout
+ * @NL80211_NAN_FUNC_TERM_REASON_ERROR: errored
+ */
+enum nl80211_nan_func_term_reason {
+	NL80211_NAN_FUNC_TERM_REASON_USER_REQUEST,
+	NL80211_NAN_FUNC_TERM_REASON_TTL_EXPIRED,
+	NL80211_NAN_FUNC_TERM_REASON_ERROR,
+};
+
+#define NL80211_NAN_FUNC_SERVICE_ID_LEN 6
+#define NL80211_NAN_FUNC_SERVICE_SPEC_INFO_MAX_LEN 0xff
+#define NL80211_NAN_FUNC_SRF_MAX_LEN 0xff
+
+/**
+ * enum nl80211_nan_func_attributes - NAN function attributes
+ * @__NL80211_NAN_FUNC_INVALID: invalid
+ * @NL80211_NAN_FUNC_TYPE: &enum nl80211_nan_function_type (u8).
+ * @NL80211_NAN_FUNC_SERVICE_ID: 6 bytes of the service ID hash as
+ *	specified in NAN spec. This is a binary attribute.
+ * @NL80211_NAN_FUNC_PUBLISH_TYPE: relevant if the function's type is
+ *	publish. Defines the transmission type for the publish Service Discovery
+ *	Frame, see &enum nl80211_nan_publish_type. Its type is u8.
+ * @NL80211_NAN_FUNC_PUBLISH_BCAST: relevant if the function is a solicited
+ *	publish. Should the solicited publish Service Discovery Frame be sent to
+ *	the NAN Broadcast address. This is a flag.
+ * @NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE: relevant if the function's type is
+ *	subscribe. Is the subscribe active. This is a flag.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_ID: relevant if the function's type is follow up.
+ *	The instance ID for the follow up Service Discovery Frame. This is u8.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID: relevant if the function's type
+ *	is follow up. This is a u8.
+ *	The requestor instance ID for the follow up Service Discovery Frame.
+ * @NL80211_NAN_FUNC_FOLLOW_UP_DEST: the MAC address of the recipient of the
+ *	follow up Service Discovery Frame. This is a binary attribute.
+ * @NL80211_NAN_FUNC_CLOSE_RANGE: is this function limited for devices in a
+ *	close range. The range itself (RSSI) is defined by the device.
+ *	This is a flag.
+ * @NL80211_NAN_FUNC_TTL: strictly positive number of DWs this function should
+ *	stay active. If not present infinite TTL is assumed. This is a u32.
+ * @NL80211_NAN_FUNC_SERVICE_INFO: array of bytes describing the service
+ *	specific info. This is a binary attribute.
+ * @NL80211_NAN_FUNC_SRF: Service Receive Filter. This is a nested attribute.
+ *	See &enum nl80211_nan_srf_attributes.
+ * @NL80211_NAN_FUNC_RX_MATCH_FILTER: Receive Matching filter. This is a nested
+ *	attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_TX_MATCH_FILTER: Transmit Matching filter. This is a
+ *	nested attribute. It is a list of binary values.
+ * @NL80211_NAN_FUNC_INSTANCE_ID: The instance ID of the function.
+ *	Its type is u8 and it cannot be 0.
+ * @NL80211_NAN_FUNC_TERM_REASON: NAN function termination reason.
+ *	See &enum nl80211_nan_func_term_reason.
+ *
+ * @NUM_NL80211_NAN_FUNC_ATTR: internal
+ * @NL80211_NAN_FUNC_ATTR_MAX: highest NAN function attribute
+ */
+enum nl80211_nan_func_attributes {
+	__NL80211_NAN_FUNC_INVALID,
+	NL80211_NAN_FUNC_TYPE,
+	NL80211_NAN_FUNC_SERVICE_ID,
+	NL80211_NAN_FUNC_PUBLISH_TYPE,
+	NL80211_NAN_FUNC_PUBLISH_BCAST,
+	NL80211_NAN_FUNC_SUBSCRIBE_ACTIVE,
+	NL80211_NAN_FUNC_FOLLOW_UP_ID,
+	NL80211_NAN_FUNC_FOLLOW_UP_REQ_ID,
+	NL80211_NAN_FUNC_FOLLOW_UP_DEST,
+	NL80211_NAN_FUNC_CLOSE_RANGE,
+	NL80211_NAN_FUNC_TTL,
+	NL80211_NAN_FUNC_SERVICE_INFO,
+	NL80211_NAN_FUNC_SRF,
+	NL80211_NAN_FUNC_RX_MATCH_FILTER,
+	NL80211_NAN_FUNC_TX_MATCH_FILTER,
+	NL80211_NAN_FUNC_INSTANCE_ID,
+	NL80211_NAN_FUNC_TERM_REASON,
+
+	/* keep last */
+	NUM_NL80211_NAN_FUNC_ATTR,
+	NL80211_NAN_FUNC_ATTR_MAX = NUM_NL80211_NAN_FUNC_ATTR - 1
+};
+
+/**
+ * enum nl80211_nan_srf_attributes - NAN Service Response filter attributes
+ * @__NL80211_NAN_SRF_INVALID: invalid
+ * @NL80211_NAN_SRF_INCLUDE: present if the include bit of the SRF set.
+ *	This is a flag.
+ * @NL80211_NAN_SRF_BF: Bloom Filter. Present if and only if
+ *	&NL80211_NAN_SRF_MAC_ADDRS isn't present. This attribute is binary.
+ * @NL80211_NAN_SRF_BF_IDX: index of the Bloom Filter. Mandatory if
+ *	&NL80211_NAN_SRF_BF is present. This is a u8.
+ * @NL80211_NAN_SRF_MAC_ADDRS: list of MAC addresses for the SRF. Present if
+ *	and only if &NL80211_NAN_SRF_BF isn't present. This is a nested
+ *	attribute. Each nested attribute is a MAC address.
+ * @NUM_NL80211_NAN_SRF_ATTR: internal
+ * @NL80211_NAN_SRF_ATTR_MAX: highest NAN SRF attribute
+ */
+enum nl80211_nan_srf_attributes {
+	__NL80211_NAN_SRF_INVALID,
+	NL80211_NAN_SRF_INCLUDE,
+	NL80211_NAN_SRF_BF,
+	NL80211_NAN_SRF_BF_IDX,
+	NL80211_NAN_SRF_MAC_ADDRS,
+
+	/* keep last */
+	NUM_NL80211_NAN_SRF_ATTR,
+	NL80211_NAN_SRF_ATTR_MAX = NUM_NL80211_NAN_SRF_ATTR - 1,
+};
+
+/**
+ * enum nl80211_nan_match_attributes - NAN match attributes
+ * @__NL80211_NAN_MATCH_INVALID: invalid
+ * @NL80211_NAN_MATCH_FUNC_LOCAL: the local function that had the
+ *	match. This is a nested attribute.
+ *	See &enum nl80211_nan_func_attributes.
+ * @NL80211_NAN_MATCH_FUNC_PEER: the peer function
+ *	that caused the match. This is a nested attribute.
+ *	See &enum nl80211_nan_func_attributes.
+ *
+ * @NUM_NL80211_NAN_MATCH_ATTR: internal
+ * @NL80211_NAN_MATCH_ATTR_MAX: highest NAN match attribute
+ */
+enum nl80211_nan_match_attributes {
+	__NL80211_NAN_MATCH_INVALID,
+	NL80211_NAN_MATCH_FUNC_LOCAL,
+	NL80211_NAN_MATCH_FUNC_PEER,
+
+	/* keep last */
+	NUM_NL80211_NAN_MATCH_ATTR,
+	NL80211_NAN_MATCH_ATTR_MAX = NUM_NL80211_NAN_MATCH_ATTR - 1
+};
+
 #endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index e8587fd..57990d2 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -79,7 +79,7 @@
 
 	/*
 	 * RFC 4851, Section 5.1:
-	 * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", 
+	 * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash",
 	 *                       server_random + client_random, 48)
 	 */
 	os_memcpy(seed, server_random, TLS_RANDOM_LEN);
@@ -93,8 +93,7 @@
 }
 
 
-u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
-			 const char *label, size_t len)
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn, size_t len)
 {
 	u8 *out;
 
@@ -102,7 +101,7 @@
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_prf(ssl_ctx, conn, label, 1, 1, out, len)) {
+	if (tls_connection_get_eap_fast_key(ssl_ctx, conn, out, len)) {
 		os_free(out);
 		return NULL;
 	}
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
index 6756dd2..724204c 100644
--- a/src/eap_common/eap_fast_common.h
+++ b/src/eap_common/eap_fast_common.h
@@ -98,7 +98,7 @@
 void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
 				   const u8 *client_random, u8 *master_secret);
 u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
-			 const char *label, size_t len);
+			 size_t len);
 int eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
 int eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
index 9110ca5..bc90c7a 100644
--- a/src/eap_peer/eap.c
+++ b/src/eap_peer/eap.c
@@ -489,7 +489,7 @@
 	u8 *emsk = NULL;
 	size_t emsk_len = 0;
 	u8 EMSKname[EAP_EMSK_NAME_LEN];
-	u8 len[2];
+	u8 len[2], ctx[3];
 	char *realm;
 	size_t realm_len, nai_buf_len;
 	struct eap_erp_key *erp = NULL;
@@ -526,7 +526,7 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
 
-	WPA_PUT_BE16(len, 8);
+	WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN);
 	if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
 			    len, sizeof(len),
 			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
@@ -550,9 +550,11 @@
 	erp->rRK_len = emsk_len;
 	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
 
+	ctx[0] = EAP_ERP_CS_HMAC_SHA256_128;
+	WPA_PUT_BE16(&ctx[1], erp->rRK_len);
 	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
-			    "EAP Re-authentication Integrity Key@ietf.org",
-			    len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+			    "Re-authentication Integrity Key@ietf.org",
+			    ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) {
 		wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
 		goto fail;
 	}
@@ -571,8 +573,7 @@
 
 
 #ifdef CONFIG_ERP
-static int eap_peer_erp_reauth_start(struct eap_sm *sm,
-				     const struct eap_hdr *hdr, size_t len)
+struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id)
 {
 	char *realm;
 	struct eap_erp_key *erp;
@@ -581,16 +582,16 @@
 
 	realm = eap_home_realm(sm);
 	if (!realm)
-		return -1;
+		return NULL;
 
 	erp = eap_erp_get_key(sm, realm);
 	os_free(realm);
 	realm = NULL;
 	if (!erp)
-		return -1;
+		return NULL;
 
 	if (erp->next_seq >= 65536)
-		return -1; /* SEQ has range of 0..65535 */
+		return NULL; /* SEQ has range of 0..65535 */
 
 	/* TODO: check rRK lifetime expiration */
 
@@ -599,9 +600,9 @@
 
 	msg = eap_msg_alloc(EAP_VENDOR_IETF, (EapType) EAP_ERP_TYPE_REAUTH,
 			    1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
-			    EAP_CODE_INITIATE, hdr->identifier);
+			    EAP_CODE_INITIATE, eap_id);
 	if (msg == NULL)
-		return -1;
+		return NULL;
 
 	wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
 	wpabuf_put_be16(msg, erp->next_seq);
@@ -615,13 +616,28 @@
 	if (hmac_sha256(erp->rIK, erp->rIK_len,
 			wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
 		wpabuf_free(msg);
-		return -1;
+		return NULL;
 	}
 	wpabuf_put_data(msg, hash, 16);
 
-	wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
 	sm->erp_seq = erp->next_seq;
 	erp->next_seq++;
+
+	wpa_hexdump_buf(MSG_DEBUG, "ERP: EAP-Initiate/Re-auth", msg);
+
+	return msg;
+}
+
+
+static int eap_peer_erp_reauth_start(struct eap_sm *sm, u8 eap_id)
+{
+	struct wpabuf *msg;
+
+	msg = eap_peer_build_erp_reauth_start(sm, eap_id);
+	if (!msg)
+		return -1;
+
+	wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
 	wpabuf_free(sm->eapRespData);
 	sm->eapRespData = msg;
 	sm->reauthInit = TRUE;
@@ -1566,7 +1582,7 @@
 		/* TODO: Derivation of domain specific keys for local ER */
 	}
 
-	if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+	if (eap_peer_erp_reauth_start(sm, hdr->identifier) == 0)
 		return;
 
 invalid:
@@ -1577,8 +1593,7 @@
 }
 
 
-static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
-			    size_t len)
+void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len)
 {
 #ifdef CONFIG_ERP
 	const u8 *pos = (const u8 *) (hdr + 1);
@@ -2231,6 +2246,7 @@
 		config->pending_req_passphrase++;
 		break;
 	case WPA_CTRL_REQ_SIM:
+		config->pending_req_sim++;
 		txt = msg;
 		break;
 	case WPA_CTRL_REQ_EXT_CERT_CHECK:
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
index 1a645af..932584f 100644
--- a/src/eap_peer/eap.h
+++ b/src/eap_peer/eap.h
@@ -252,6 +252,14 @@
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
 	 */
 	void (*eap_proxy_cb)(void *ctx);
+
+	/**
+	 * eap_proxy_notify_sim_status - Notification of SIM status change
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @sim_state: One of enum value from sim_state
+	 */
+	void (*eap_proxy_notify_sim_status)(void *ctx,
+					    enum eap_proxy_sim_state sim_state);
 #endif /* CONFIG_EAP_PROXY */
 
 	/**
@@ -348,6 +356,8 @@
 void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
 int eap_peer_was_failure_expected(struct eap_sm *sm);
 void eap_peer_erp_free_keys(struct eap_sm *sm);
+struct wpabuf * eap_peer_build_erp_reauth_start(struct eap_sm *sm, u8 eap_id);
+void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr, size_t len);
 
 #endif /* IEEE8021X_EAPOL */
 
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
index 0bac62d..4188817 100644
--- a/src/eap_peer/eap_aka.c
+++ b/src/eap_peer/eap_aka.c
@@ -48,6 +48,7 @@
 	struct wpabuf *id_msgs;
 	int prev_id;
 	int result_ind, use_result_ind;
+	int use_pseudonym;
 	u8 eap_method;
 	u8 *network_name;
 	size_t network_name_len;
@@ -101,7 +102,8 @@
 
 	data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
 
-	if (config && config->anonymous_identity) {
+	data->use_pseudonym = !sm->init_phase2;
+	if (config && config->anonymous_identity && data->use_pseudonym) {
 		data->pseudonym = os_malloc(config->anonymous_identity_len);
 		if (data->pseudonym) {
 			os_memcpy(data->pseudonym, config->anonymous_identity,
@@ -350,7 +352,8 @@
 		os_free(data->pseudonym);
 		data->pseudonym = NULL;
 		data->pseudonym_len = 0;
-		eap_set_anon_id(sm, NULL, 0);
+		if (data->use_pseudonym)
+			eap_set_anon_id(sm, NULL, 0);
 	}
 	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 		wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old reauth_id");
@@ -405,7 +408,9 @@
 				  realm, realm_len);
 		}
 		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
-		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
+		if (data->use_pseudonym)
+			eap_set_anon_id(sm, data->pseudonym,
+					data->pseudonym_len);
 	}
 
 	if (attr->next_reauth_id) {
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index 39ddcff..16521c3 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -181,13 +181,13 @@
 	 * subject_match - Constraint for server certificate subject
 	 *
 	 * This substring is matched against the subject of the authentication
-	 * server certificate. If this string is set, the server sertificate is
+	 * server certificate. If this string is set, the server certificate is
 	 * only accepted if it contains this string in the subject. The subject
 	 * string is in following format:
 	 *
 	 * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
 	 *
-	 * Note: Since this is a substring match, this cannot be used securily
+	 * Note: Since this is a substring match, this cannot be used securely
 	 * to do a suffix match against a possible domain name in the CN entry.
 	 * For such a use case, domain_suffix_match should be used instead.
 	 */
@@ -198,7 +198,7 @@
 	 *
 	 * Semicolon separated string of entries to be matched against the
 	 * alternative subject name of the authentication server certificate.
-	 * If this string is set, the server sertificate is only accepted if it
+	 * If this string is set, the server certificate is only accepted if it
 	 * contains one of the entries in an alternative subject name
 	 * extension.
 	 *
@@ -628,6 +628,15 @@
 	int pending_req_passphrase;
 
 	/**
+	 * pending_req_sim - Pending SIM request
+	 *
+	 * This field should not be set in configuration step. It is only used
+	 * internally when control interface is used to request needed
+	 * information.
+	 */
+	int pending_req_sim;
+
+	/**
 	 * pending_req_otp - Whether there is a pending OTP request
 	 *
 	 * This field should not be set in configuration step. It is only used
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index f03cd4a..e4b0c10 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -275,7 +275,7 @@
 	 * Extra key material after TLS key_block: session_key_seed[40]
 	 */
 
-	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
+	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
 				  EAP_FAST_SKS_LEN);
 	if (sks == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
@@ -303,7 +303,6 @@
 	os_free(data->key_block_p);
 	data->key_block_p = (struct eap_fast_key_block_provisioning *)
 		eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
-				    "key expansion",
 				    sizeof(*data->key_block_p));
 	if (data->key_block_p == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
@@ -485,7 +484,8 @@
 
 	if (*resp == NULL && config &&
 	    (config->pending_req_identity || config->pending_req_password ||
-	     config->pending_req_otp || config->pending_req_new_password)) {
+	     config->pending_req_otp || config->pending_req_new_password ||
+	     config->pending_req_sim)) {
 		wpabuf_free(data->pending_phase2_req);
 		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
 	} else if (*resp == NULL)
@@ -710,9 +710,10 @@
 	if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
 		return -1;
 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
-	sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
-		   "Inner Methods Compound Keys",
-		   isk, sizeof(isk), imck, sizeof(imck));
+	if (sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+		       "Inner Methods Compound Keys",
+		       isk, sizeof(isk), imck, sizeof(imck)) < 0)
+		return -1;
 	data->simck_idx++;
 	os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
 	wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
@@ -1677,6 +1678,10 @@
 static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_fast_data *data = priv;
+
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->deinit_for_reauth)
+		data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
 	os_free(data->key_block_p);
 	data->key_block_p = NULL;
 	wpabuf_free(data->pending_phase2_req);
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
index 5f0b7fb..a7012d2 100644
--- a/src/eap_peer/eap_pax.c
+++ b/src/eap_peer/eap_pax.c
@@ -278,8 +278,15 @@
 		    pos, EAP_PAX_MAC_LEN);
 	if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
 			data->rand.r.y, EAP_PAX_RAND_LEN,
-			(u8 *) data->cid, data->cid_len, NULL, 0, mac) < 0 ||
-	    os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) {
+			(u8 *) data->cid, data->cid_len, NULL, 0, mac) < 0) {
+		wpa_printf(MSG_INFO,
+			   "EAP-PAX: Could not derive MAC_CK(B, CID)");
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		return NULL;
+	}
+
+	if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
 			   "received");
 		wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 45ba381..2ff6076 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -726,7 +726,8 @@
 
 	if (*resp == NULL &&
 	    (config->pending_req_identity || config->pending_req_password ||
-	     config->pending_req_otp || config->pending_req_new_password)) {
+	     config->pending_req_otp || config->pending_req_new_password ||
+	     config->pending_req_sim)) {
 		wpabuf_free(data->pending_phase2_req);
 		data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
 	}
@@ -1082,7 +1083,7 @@
 				eap_peer_tls_derive_key(sm, &data->ssl, label,
 							EAP_TLS_KEY_LEN);
 			if (data->key_data) {
-				wpa_hexdump_key(MSG_DEBUG, 
+				wpa_hexdump_key(MSG_DEBUG,
 						"EAP-PEAP: Derived key",
 						data->key_data,
 						EAP_TLS_KEY_LEN);
@@ -1163,6 +1164,10 @@
 static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_peap_data *data = priv;
+
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->deinit_for_reauth)
+		data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
 	wpabuf_free(data->pending_resp);
diff --git a/src/eap_peer/eap_proxy.h b/src/eap_peer/eap_proxy.h
index 23cdbe6..7205fad 100644
--- a/src/eap_peer/eap_proxy.h
+++ b/src/eap_peer/eap_proxy.h
@@ -20,7 +20,7 @@
 };
 
 struct eap_proxy_sm *
-eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb,
 	       void *msg_ctx);
 
 void eap_proxy_deinit(struct eap_proxy_sm *eap_proxy);
diff --git a/src/eap_peer/eap_proxy_dummy.c b/src/eap_peer/eap_proxy_dummy.c
index d84f012..08009ca 100644
--- a/src/eap_peer/eap_proxy_dummy.c
+++ b/src/eap_peer/eap_proxy_dummy.c
@@ -12,7 +12,7 @@
 #include "eap_proxy.h"
 
 struct eap_proxy_sm *
-eap_proxy_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb,
+eap_proxy_init(void *eapol_ctx, const struct eapol_callbacks *eapol_cb,
 	       void *msg_ctx)
 {
 	return NULL;
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index d2bc981..662347b 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -345,7 +345,7 @@
 	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_RAND_FUNC);
 	wpabuf_put_u8(data->outbuf, EAP_PWD_DEFAULT_PRF);
 	wpabuf_put_data(data->outbuf, id->token, sizeof(id->token));
-	wpabuf_put_u8(data->outbuf, EAP_PWD_PREP_NONE);
+	wpabuf_put_u8(data->outbuf, id->prep);
 	wpabuf_put_data(data->outbuf, data->id_peer, data->id_peer_len);
 
 	eap_pwd_state(data, PWD_Commit_Req);
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 80f4667..330febb 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -309,11 +309,20 @@
 		return NULL;
 	}
 
-	eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-			     data->serverid, data->serverid_len,
-			     data->peerid, data->peerid_len, 0,
-			     wpabuf_head(reqData), wpabuf_len(reqData),
-			     attr.mic_s, mic_s);
+	if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+				 data->serverid, data->serverid_len,
+				 data->peerid, data->peerid_len, 0,
+				 wpabuf_head(reqData), wpabuf_len(reqData),
+				 attr.mic_s, mic_s)) {
+		wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+		eap_sake_state(data, FAILURE);
+		ret->methodState = METHOD_DONE;
+		ret->decision = DECISION_FAIL;
+		ret->allowNotifications = FALSE;
+		wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Auth-Reject");
+		return eap_sake_build_msg(data, id, 0,
+					  EAP_SAKE_SUBTYPE_AUTH_REJECT);
+	}
 	if (os_memcmp_const(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
 		eap_sake_state(data, FAILURE);
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
index b97c95d..95ecdf7 100644
--- a/src/eap_peer/eap_sim.c
+++ b/src/eap_peer/eap_sim.c
@@ -46,6 +46,7 @@
 		CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
 	} state;
 	int result_ind, use_result_ind;
+	int use_pseudonym;
 };
 
 
@@ -115,7 +116,8 @@
 			NULL;
 	}
 
-	if (config && config->anonymous_identity) {
+	data->use_pseudonym = !sm->init_phase2;
+	if (config && config->anonymous_identity && data->use_pseudonym) {
 		data->pseudonym = os_malloc(config->anonymous_identity_len);
 		if (data->pseudonym) {
 			os_memcpy(data->pseudonym, config->anonymous_identity,
@@ -372,7 +374,8 @@
 		os_free(data->pseudonym);
 		data->pseudonym = NULL;
 		data->pseudonym_len = 0;
-		eap_set_anon_id(sm, NULL, 0);
+		if (data->use_pseudonym)
+			eap_set_anon_id(sm, NULL, 0);
 	}
 	if ((id & CLEAR_REAUTH_ID) && data->reauth_id) {
 		wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old reauth_id");
@@ -427,7 +430,9 @@
 				  realm, realm_len);
 		}
 		data->pseudonym_len = attr->next_pseudonym_len + realm_len;
-		eap_set_anon_id(sm, data->pseudonym, data->pseudonym_len);
+		if (data->use_pseudonym)
+			eap_set_anon_id(sm, data->pseudonym,
+					data->pseudonym_len);
 	}
 
 	if (attr->next_reauth_id) {
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 406c162..0dcb9c1 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -328,8 +328,8 @@
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_prf(data->ssl_ctx, data->conn, label, 0, 0,
-			       out, len)) {
+	if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out,
+				      len)) {
 		os_free(out);
 		return NULL;
 	}
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index 92f94dc..3354b2d 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -458,7 +458,7 @@
 
 	if (*resp == NULL &&
 	    (config->pending_req_identity || config->pending_req_password ||
-	     config->pending_req_otp)) {
+	     config->pending_req_otp || config->pending_req_sim)) {
 		return 0;
 	}
 
@@ -1280,7 +1280,8 @@
 	} else if (config->pending_req_identity ||
 		   config->pending_req_password ||
 		   config->pending_req_otp ||
-		   config->pending_req_new_password) {
+		   config->pending_req_new_password ||
+		   config->pending_req_sim) {
 		wpabuf_free(data->pending_phase2_req);
 		data->pending_phase2_req = wpabuf_dup(in_decrypted);
 	}
@@ -1317,7 +1318,8 @@
 		    (config->pending_req_identity ||
 		     config->pending_req_password ||
 		     config->pending_req_otp ||
-		     config->pending_req_new_password)) {
+		     config->pending_req_new_password ||
+		     config->pending_req_sim)) {
 			/*
 			 * Use empty buffer to force implicit request
 			 * processing when EAP request is re-processed after
@@ -1537,7 +1539,7 @@
 }
 
 
-static void eap_ttls_check_auth_status(struct eap_sm *sm, 
+static void eap_ttls_check_auth_status(struct eap_sm *sm,
 				       struct eap_ttls_data *data,
 				       struct eap_method_ret *ret)
 {
@@ -1648,6 +1650,10 @@
 static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 {
 	struct eap_ttls_data *data = priv;
+
+	if (data->phase2_priv && data->phase2_method &&
+	    data->phase2_method->deinit_for_reauth)
+		data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
 	wpabuf_free(data->pending_phase2_req);
 	data->pending_phase2_req = NULL;
 	wpabuf_free(data->pending_resp);
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
index 9965513..0c5caa7 100644
--- a/src/eap_peer/tncc.c
+++ b/src/eap_peer/tncc.c
@@ -104,7 +104,7 @@
 
 /* TNCC functions that IMCs can call */
 
-TNC_Result TNC_TNCC_ReportMessageTypes(
+static TNC_Result TNC_TNCC_ReportMessageTypes(
 	TNC_IMCID imcID,
 	TNC_MessageTypeList supportedTypes,
 	TNC_UInt32 typeCount)
@@ -138,7 +138,7 @@
 }
 
 
-TNC_Result TNC_TNCC_SendMessage(
+static TNC_Result TNC_TNCC_SendMessage(
 	TNC_IMCID imcID,
 	TNC_ConnectionID connectionID,
 	TNC_BufferReference message,
@@ -183,7 +183,7 @@
 }
 
 
-TNC_Result TNC_TNCC_RequestHandshakeRetry(
+static TNC_Result TNC_TNCC_RequestHandshakeRetry(
 	TNC_IMCID imcID,
 	TNC_ConnectionID connectionID,
 	TNC_RetryReason reason)
@@ -203,8 +203,8 @@
 }
 
 
-TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
-			       const char *message)
+static TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
+				      const char *message)
 {
 	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
 		   "severity==%lu message='%s')",
@@ -213,8 +213,9 @@
 }
 
 
-TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
-				const char *message)
+static TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID,
+				       TNC_ConnectionID connectionID,
+				       const char *message)
 {
 	wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
 		   "connectionID==%lu message='%s')",
@@ -223,7 +224,7 @@
 }
 
 
-TNC_Result TNC_TNCC_BindFunction(
+static TNC_Result TNC_TNCC_BindFunction(
 	TNC_IMCID imcID,
 	char *functionName,
 	void **pOutfunctionPointer)
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 69eaab8..93eab62 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -153,5 +153,6 @@
 void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
 				   const u8 *username, size_t username_len,
 				   const u8 *challenge, const u8 *response);
+void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len);
 
 #endif /* EAP_H */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 84ecafc..1b571cf 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -415,7 +415,7 @@
 	u8 *emsk = NULL;
 	size_t emsk_len = 0;
 	u8 EMSKname[EAP_EMSK_NAME_LEN];
-	u8 len[2];
+	u8 len[2], ctx[3];
 	const char *domain;
 	size_t domain_len, nai_buf_len;
 	struct eap_server_erp_key *erp = NULL;
@@ -452,7 +452,7 @@
 
 	wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
 
-	WPA_PUT_BE16(len, 8);
+	WPA_PUT_BE16(len, EAP_EMSK_NAME_LEN);
 	if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen,
 			    "EMSK", len, sizeof(len),
 			    EMSKname, EAP_EMSK_NAME_LEN) < 0) {
@@ -476,9 +476,11 @@
 	erp->rRK_len = emsk_len;
 	wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
 
+	ctx[0] = EAP_ERP_CS_HMAC_SHA256_128;
+	WPA_PUT_BE16(&ctx[1], erp->rRK_len);
 	if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
-			    "EAP Re-authentication Integrity Key@ietf.org",
-			    len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+			    "Re-authentication Integrity Key@ietf.org",
+			    ctx, sizeof(ctx), erp->rIK, erp->rRK_len) < 0) {
 		wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
 		goto fail;
 	}
@@ -1968,6 +1970,44 @@
 }
 
 
+void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len)
+{
+#ifdef CONFIG_ERP
+	const struct eap_hdr *hdr;
+	const u8 *pos, *end;
+	struct erp_tlvs parse;
+
+	if (len < sizeof(*hdr) + 1)
+		return;
+	hdr = (const struct eap_hdr *) eap;
+	end = eap + len;
+	pos = (const u8 *) (hdr + 1);
+	if (hdr->code != EAP_CODE_INITIATE || *pos != EAP_ERP_TYPE_REAUTH)
+		return;
+	pos++;
+	if (pos + 3 > end)
+		return;
+
+	/* Skip Flags and SEQ */
+	pos += 3;
+
+	if (erp_parse_tlvs(pos, end, &parse, 1) < 0 || !parse.keyname)
+		return;
+	wpa_hexdump_ascii(MSG_DEBUG,
+			  "EAP: Update identity based on EAP-Initiate/Re-auth keyName-NAI",
+			  parse.keyname, parse.keyname_len);
+	os_free(sm->identity);
+	sm->identity = os_malloc(parse.keyname_len);
+	if (sm->identity) {
+		os_memcpy(sm->identity, parse.keyname, parse.keyname_len);
+		sm->identity_len = parse.keyname_len;
+	} else {
+		sm->identity_len = 0;
+	}
+#endif /* CONFIG_ERP */
+}
+
+
 /**
  * eap_get_interface - Get pointer to EAP-EAPOL interface data
  * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index 6993159..f6d7e32 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -278,7 +278,7 @@
 	 * Extra key material after TLS key_block: session_key_seed[40]
 	 */
 
-	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
+	sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
 				  EAP_FAST_SKS_LEN);
 	if (sks == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
@@ -305,7 +305,6 @@
 	os_free(data->key_block_p);
 	data->key_block_p = (struct eap_fast_key_block_provisioning *)
 		eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
-				    "key expansion",
 				    sizeof(*data->key_block_p));
 	if (data->key_block_p == NULL) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
@@ -562,7 +561,7 @@
 		return -1;
 	}
 	data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
-		    
+
 	if (data->anon_provisioning) {
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning");
 		eap_fast_derive_key_provisioning(sm, data);
@@ -790,7 +789,7 @@
 
 	/* A-ID (inside PAC-Info) */
 	eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
-	
+
 	/* Note: headers may be misaligned after A-ID */
 
 	if (sm->identity) {
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index 64bf708..c60539f 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -602,7 +602,9 @@
 	if ((data->group_num != be_to_host16(id->group_num)) ||
 	    (id->random_function != EAP_PWD_DEFAULT_RAND_FUNC) ||
 	    (os_memcmp(id->token, (u8 *)&data->token, sizeof(data->token))) ||
-	    (id->prf != EAP_PWD_DEFAULT_PRF)) {
+	    (id->prf != EAP_PWD_DEFAULT_PRF) ||
+	    id->prep !=
+	    data->password_hash ? EAP_PWD_PREP_MS : EAP_PWD_PREP_NONE) {
 		wpa_printf(MSG_INFO, "EAP-pwd: peer changed parameters");
 		eap_pwd_state(data, FAILURE);
 		return;
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 05677b7..6909695 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -115,8 +115,8 @@
 	if (out == NULL)
 		return NULL;
 
-	if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, 0,
-			       out, len)) {
+	if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out,
+				      len)) {
 		os_free(out);
 		return NULL;
 	}
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
index dc6f689..cfcbd3e 100644
--- a/src/eap_server/tncs.c
+++ b/src/eap_server/tncs.c
@@ -140,7 +140,7 @@
 
 
 /* TNCS functions that IMVs can call */
-TNC_Result TNC_TNCS_ReportMessageTypes(
+static TNC_Result TNC_TNCS_ReportMessageTypes(
 	TNC_IMVID imvID,
 	TNC_MessageTypeList supportedTypes,
 	TNC_UInt32 typeCount)
@@ -173,7 +173,7 @@
 }
 
 
-TNC_Result TNC_TNCS_SendMessage(
+static TNC_Result TNC_TNCS_SendMessage(
 	TNC_IMVID imvID,
 	TNC_ConnectionID connectionID,
 	TNC_BufferReference message,
@@ -222,7 +222,7 @@
 }
 
 
-TNC_Result TNC_TNCS_RequestHandshakeRetry(
+static TNC_Result TNC_TNCS_RequestHandshakeRetry(
 	TNC_IMVID imvID,
 	TNC_ConnectionID connectionID,
 	TNC_RetryReason reason)
@@ -233,7 +233,7 @@
 }
 
 
-TNC_Result TNC_TNCS_ProvideRecommendation(
+static TNC_Result TNC_TNCS_ProvideRecommendation(
 	TNC_IMVID imvID,
 	TNC_ConnectionID connectionID,
 	TNC_IMV_Action_Recommendation recommendation,
@@ -260,7 +260,7 @@
 }
 
 
-TNC_Result TNC_TNCS_GetAttribute(
+static TNC_Result TNC_TNCS_GetAttribute(
 	TNC_IMVID imvID,
 	TNC_ConnectionID connectionID,
 	TNC_AttributeID attribureID,
@@ -274,7 +274,7 @@
 }
 
 
-TNC_Result TNC_TNCS_SetAttribute(
+static TNC_Result TNC_TNCS_SetAttribute(
 	TNC_IMVID imvID,
 	TNC_ConnectionID connectionID,
 	TNC_AttributeID attribureID,
@@ -287,7 +287,7 @@
 }
 
 
-TNC_Result TNC_TNCS_BindFunction(
+static TNC_Result TNC_TNCS_BindFunction(
 	TNC_IMVID imvID,
 	char *functionName,
 	void **pOutFunctionPointer)
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 65460fc..e727005 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -95,7 +95,7 @@
 		SUPP_BE_RECEIVE = 4,
 		SUPP_BE_RESPONSE = 5,
 		SUPP_BE_FAIL = 6,
-		SUPP_BE_TIMEOUT = 7, 
+		SUPP_BE_TIMEOUT = 7,
 		SUPP_BE_SUCCESS = 8
 	} SUPP_BE_state; /* dot1xSuppBackendPaeState */
 	/* Variables */
@@ -250,6 +250,8 @@
 
 	if (sm->eapTriggerStart)
 		send_start = 1;
+	if (sm->ctx->preauth)
+		send_start = 1;
 	sm->eapTriggerStart = FALSE;
 
 	if (send_start) {
@@ -1999,6 +2001,7 @@
 
 
 #ifdef CONFIG_EAP_PROXY
+
 static void eapol_sm_eap_proxy_cb(void *ctx)
 {
 	struct eapol_sm *sm = ctx;
@@ -2006,6 +2009,18 @@
 	if (sm->ctx->eap_proxy_cb)
 		sm->ctx->eap_proxy_cb(sm->ctx->ctx);
 }
+
+
+static void
+eapol_sm_eap_proxy_notify_sim_status(void *ctx,
+				     enum eap_proxy_sim_state sim_state)
+{
+	struct eapol_sm *sm = ctx;
+
+	if (sm->ctx->eap_proxy_notify_sim_status)
+		sm->ctx->eap_proxy_notify_sim_status(sm->ctx->ctx, sim_state);
+}
+
 #endif /* CONFIG_EAP_PROXY */
 
 
@@ -2034,6 +2049,7 @@
 	eapol_sm_notify_status,
 #ifdef CONFIG_EAP_PROXY
 	eapol_sm_eap_proxy_cb,
+	eapol_sm_eap_proxy_notify_sim_status,
 #endif /* CONFIG_EAP_PROXY */
 	eapol_sm_set_anon_id
 };
@@ -2141,16 +2157,14 @@
 }
 
 
+#ifdef CONFIG_EAP_PROXY
 int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
 {
-#ifdef CONFIG_EAP_PROXY
 	if (sm->eap_proxy == NULL)
 		return -1;
 	return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
-#else /* CONFIG_EAP_PROXY */
-	return -1;
-#endif /* CONFIG_EAP_PROXY */
 }
+#endif /* CONFIG_EAP_PROXY */
 
 
 void eapol_sm_erp_flush(struct eapol_sm *sm)
@@ -2158,3 +2172,26 @@
 	if (sm)
 		eap_peer_erp_free_keys(sm->eap);
 }
+
+
+struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return NULL;
+	return eap_peer_build_erp_reauth_start(sm->eap, 0);
+#else /* CONFIG_ERP */
+	return NULL;
+#endif /* CONFIG_ERP */
+}
+
+
+void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
+				 size_t len)
+{
+#ifdef CONFIG_ERP
+	if (!sm)
+		return;
+	eap_peer_finish(sm->eap, (const struct eap_hdr *) buf, len);
+#endif /* CONFIG_ERP */
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index 1309ff7..cb06e9a 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -277,6 +277,14 @@
 	 * @ctx: eapol_ctx from eap_peer_sm_init() call
 	 */
 	void (*eap_proxy_cb)(void *ctx);
+
+	/**
+	 * eap_proxy_notify_sim_status - Notification of SIM status change
+	 * @ctx: eapol_ctx from eap_peer_sm_init() call
+	 * @status: One of enum value from sim_state
+	 */
+	void (*eap_proxy_notify_sim_status)(void *ctx,
+					    enum eap_proxy_sim_state sim_state);
 #endif /* CONFIG_EAP_PROXY */
 
 	/**
@@ -328,6 +336,9 @@
 			     struct ext_password_data *ext);
 int eapol_sm_failed(struct eapol_sm *sm);
 void eapol_sm_erp_flush(struct eapol_sm *sm);
+struct wpabuf * eapol_sm_build_erp_reauth_start(struct eapol_sm *sm);
+void eapol_sm_process_erp_finish(struct eapol_sm *sm, const u8 *buf,
+				 size_t len);
 int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
@@ -438,6 +449,15 @@
 static inline void eapol_sm_erp_flush(struct eapol_sm *sm)
 {
 }
+static inline struct wpabuf *
+eapol_sm_build_erp_reauth_start(struct eapol_sm *sm)
+{
+	return NULL;
+}
+static inline void eapol_sm_process_erp_finish(struct eapol_sm *sm,
+					       const u8 *buf, size_t len)
+{
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
diff --git a/src/fst/fst.c b/src/fst/fst.c
index 40430e2..32cd941 100644
--- a/src/fst/fst.c
+++ b/src/fst/fst.c
@@ -15,6 +15,7 @@
 #include "fst/fst_defs.h"
 #include "fst/fst_ctrl_iface.h"
 
+static int fst_global_initialized = 0;
 struct dl_list fst_global_ctrls_list;
 
 
@@ -106,6 +107,7 @@
 	dl_list_init(&fst_global_groups_list);
 	dl_list_init(&fst_global_ctrls_list);
 	fst_session_global_init();
+	fst_global_initialized = 1;
 	return 0;
 }
 
@@ -115,6 +117,9 @@
 	struct fst_group *group;
 	struct fst_ctrl_handle *h;
 
+	if (!fst_global_initialized)
+		return;
+
 	fst_session_global_deinit();
 	while ((group = fst_first_group()) != NULL)
 		fst_group_delete(group);
@@ -122,6 +127,7 @@
 				  struct fst_ctrl_handle,
 				  global_ctrls_lentry)))
 		fst_global_del_ctrl(h);
+	fst_global_initialized = 0;
 }
 
 
diff --git a/src/fst/fst_defs.h b/src/fst/fst_defs.h
index 8ddcc61..5859f6f 100644
--- a/src/fst/fst_defs.h
+++ b/src/fst/fst_defs.h
@@ -34,7 +34,7 @@
 struct session_transition_ie {
 	u8 element_id;
 	u8 length;
-	u32 fsts_id;
+	le32 fsts_id;
 	u8 session_control;
 	u8 new_band_id;
 	u8 new_band_setup;
@@ -47,7 +47,7 @@
 struct fst_setup_req {
 	u8 action;
 	u8 dialog_token;
-	u32 llt;
+	le32 llt;
 	struct session_transition_ie stie;
 	/* Multi-band (optional) */
 	/* Wakeup Schedule (optional) */
@@ -70,18 +70,18 @@
 struct fst_ack_req {
 	u8 action;
 	u8 dialog_token;
-	u32 fsts_id;
+	le32 fsts_id;
 } STRUCT_PACKED;
 
 struct fst_ack_res {
 	u8 action;
 	u8 dialog_token;
-	u32 fsts_id;
+	le32 fsts_id;
 } STRUCT_PACKED;
 
 struct fst_tear_down {
 	u8 action;
-	u32 fsts_id;
+	le32 fsts_id;
 } STRUCT_PACKED;
 
 #endif /* IEEE_80211_FST_DEFS_H */
diff --git a/src/fst/fst_group.c b/src/fst/fst_group.c
index d6157b1..a4ae016 100644
--- a/src/fst/fst_group.c
+++ b/src/fst/fst_group.c
@@ -29,7 +29,7 @@
 		const struct multi_band_ie *mbie =
 			(const struct multi_band_ie *) p;
 		WPA_ASSERT(mbie->eid == WLAN_EID_MULTI_BAND);
-		WPA_ASSERT(2 + mbie->len >= sizeof(*mbie));
+		WPA_ASSERT(2U + mbie->len >= sizeof(*mbie));
 
 		fst_printf(MSG_WARNING,
 			   "%s: %s: mb_ctrl=%u band_id=%u op_class=%u chan=%u bssid="
@@ -196,44 +196,35 @@
 }
 
 
-static struct fst_iface *
-fst_group_get_new_iface_by_mbie_and_band_id(struct fst_group *g,
-					    const u8 *mb_ies_buff,
-					    size_t mb_ies_size,
-					    u8 band_id,
-					    u8 *iface_peer_addr)
+static const u8 * fst_mbie_get_peer_addr_for_band(const struct wpabuf *mbies,
+						  u8 band_id)
 {
-	while (mb_ies_size >= 2) {
+	const u8 *p = wpabuf_head(mbies);
+	size_t s = wpabuf_len(mbies);
+
+	while (s >= 2) {
 		const struct multi_band_ie *mbie =
-			(const struct multi_band_ie *) mb_ies_buff;
+			(const struct multi_band_ie *) p;
 
-		if (mbie->eid != WLAN_EID_MULTI_BAND ||
-		    (size_t) 2 + mbie->len < sizeof(*mbie))
-			break;
-
-		if (mbie->band_id == band_id) {
-			struct fst_iface *iface;
-
-			foreach_fst_group_iface(g, iface) {
-				const u8 *peer_addr =
-					fst_mbie_get_peer_addr(mbie);
-
-				if (peer_addr &&
-				    fst_iface_is_connected(iface, peer_addr,
-							   FALSE) &&
-				    band_id == fst_iface_get_band_id(iface)) {
-					os_memcpy(iface_peer_addr, peer_addr,
-						  ETH_ALEN);
-					return iface;
-				}
-			}
-			break;
+		if (mbie->eid != WLAN_EID_MULTI_BAND) {
+			fst_printf(MSG_INFO, "unexpected eid %d", mbie->eid);
+			return NULL;
 		}
 
-		mb_ies_buff += 2 + mbie->len;
-		mb_ies_size -= 2 + mbie->len;
+		if (mbie->len < sizeof(*mbie) - 2 || mbie->len > s - 2) {
+			fst_printf(MSG_INFO, "invalid mbie len %d",
+				   mbie->len);
+			return NULL;
+		}
+
+		if (mbie->band_id == band_id)
+			return fst_mbie_get_peer_addr(mbie);
+
+		p += 2 + mbie->len;
+		s -= 2 + mbie->len;
 	}
 
+	fst_printf(MSG_INFO, "mbie doesn't contain band %d", band_id);
 	return NULL;
 }
 
@@ -270,78 +261,172 @@
 }
 
 
-static Boolean
-fst_group_does_iface_appear_in_other_mbies(struct fst_group *g,
-					   struct fst_iface *iface,
-					   struct fst_iface *other,
-					   u8 *peer_addr)
+/**
+ * fst_group_get_peer_other_connection_1 - Find peer's "other" connection
+ * (iface, MAC tuple) by using peer's MB IE on iface.
+ *
+ * @iface: iface on which FST Setup Request was received
+ * @peer_addr: Peer address on iface
+ * @band_id: "other" connection band id
+ * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
+ *   "other" iface)
+ *
+ * This function parses peer's MB IE on iface. It looks for peer's MAC address
+ * on band_id (tmp_peer_addr). Next all interfaces are iterated to find an
+ * interface which correlates with band_id. If such interface is found, peer
+ * database is iterated to see if tmp_peer_addr is connected over it.
+ */
+static struct fst_iface *
+fst_group_get_peer_other_connection_1(struct fst_iface *iface,
+				      const u8 *peer_addr, u8 band_id,
+				      u8 *other_peer_addr)
 {
-	struct fst_get_peer_ctx *ctx;
-	const u8 *addr;
-	const u8 *iface_addr;
-	enum mb_band_id  iface_band_id;
+	const struct wpabuf *mbies;
+	struct fst_iface *other_iface;
+	const u8 *tmp_peer_addr;
 
-	WPA_ASSERT(g == fst_iface_get_group(iface));
-	WPA_ASSERT(g == fst_iface_get_group(other));
+	/* Get peer's MB IEs on iface */
+	mbies = fst_iface_get_peer_mb_ie(iface, peer_addr);
+	if (!mbies)
+		return NULL;
 
-	iface_addr = fst_iface_get_addr(iface);
-	iface_band_id = fst_iface_get_band_id(iface);
+	/* Get peer's MAC address on the "other" interface */
+	tmp_peer_addr = fst_mbie_get_peer_addr_for_band(mbies, band_id);
+	if (!tmp_peer_addr) {
+		fst_printf(MSG_INFO,
+			   "couldn't extract other peer addr from mbies");
+		return NULL;
+	}
 
-	addr = fst_iface_get_peer_first(other, &ctx, TRUE);
-	for (; addr; addr = fst_iface_get_peer_next(other, &ctx, TRUE)) {
-		const struct wpabuf *mbies;
-		u8 other_iface_peer_addr[ETH_ALEN];
-		struct fst_iface *other_new_iface;
+	fst_printf(MSG_DEBUG, "found other peer addr from mbies: " MACSTR,
+		   MAC2STR(tmp_peer_addr));
 
-		mbies = fst_iface_get_peer_mb_ie(other, addr);
-		if (!mbies)
+	foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) {
+		if (other_iface == iface ||
+		    band_id != fst_iface_get_band_id(other_iface))
 			continue;
-
-		other_new_iface = fst_group_get_new_iface_by_mbie_and_band_id(
-			g, wpabuf_head(mbies), wpabuf_len(mbies),
-			iface_band_id, other_iface_peer_addr);
-		if (other_new_iface == iface &&
-		    os_memcmp(iface_addr, other_iface_peer_addr,
-			      ETH_ALEN) != 0) {
-			os_memcpy(peer_addr, addr, ETH_ALEN);
-			return TRUE;
+		if (fst_iface_is_connected(other_iface, tmp_peer_addr, FALSE)) {
+			os_memcpy(other_peer_addr, tmp_peer_addr, ETH_ALEN);
+			return other_iface;
 		}
 	}
 
-	return FALSE;
-}
-
-
-struct fst_iface *
-fst_group_find_new_iface_by_stie(struct fst_group *g,
-				 struct fst_iface *iface,
-				 const u8 *peer_addr,
-				 const struct session_transition_ie *stie,
-				 u8 *iface_peer_addr)
-{
-	struct fst_iface *i;
-
-	foreach_fst_group_iface(g, i) {
-		if (i == iface ||
-		    stie->new_band_id != fst_iface_get_band_id(i))
-			continue;
-		if (fst_group_does_iface_appear_in_other_mbies(g, iface, i,
-			iface_peer_addr))
-			return i;
-		break;
-	}
 	return NULL;
 }
 
 
-struct fst_iface *
-fst_group_get_new_iface_by_stie_and_mbie(
-	struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
-	const struct session_transition_ie *stie, u8 *iface_peer_addr)
+/**
+ * fst_group_get_peer_other_connection_2 - Find peer's "other" connection
+ * (iface, MAC tuple) by using MB IEs of other peers.
+ *
+ * @iface: iface on which FST Setup Request was received
+ * @peer_addr: Peer address on iface
+ * @band_id: "other" connection band id
+ * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
+ *   "other" iface)
+ *
+ * This function iterates all connection (other_iface, cur_peer_addr tuples).
+ * For each connection, MB IE (of cur_peer_addr on other_iface) is parsed and
+ * MAC address on iface's band_id is extracted (this_peer_addr).
+ * this_peer_addr is then compared to peer_addr. A match indicates we have
+ * found the "other" connection.
+ */
+static struct fst_iface *
+fst_group_get_peer_other_connection_2(struct fst_iface *iface,
+				      const u8 *peer_addr, u8 band_id,
+				      u8 *other_peer_addr)
 {
-	return fst_group_get_new_iface_by_mbie_and_band_id(
-		g, mb_ies_buff, mb_ies_size, stie->new_band_id,
-		iface_peer_addr);
+	u8 this_band_id = fst_iface_get_band_id(iface);
+	const u8 *cur_peer_addr, *this_peer_addr;
+	struct fst_get_peer_ctx *ctx;
+	struct fst_iface *other_iface;
+	const struct wpabuf *cur_mbie;
+
+	foreach_fst_group_iface(fst_iface_get_group(iface), other_iface) {
+		if (other_iface == iface ||
+		    band_id != fst_iface_get_band_id(other_iface))
+			continue;
+		cur_peer_addr = fst_iface_get_peer_first(other_iface, &ctx,
+							 TRUE);
+		for (; cur_peer_addr;
+		     cur_peer_addr = fst_iface_get_peer_next(other_iface, &ctx,
+							     TRUE)) {
+			cur_mbie = fst_iface_get_peer_mb_ie(other_iface,
+							    cur_peer_addr);
+			if (!cur_mbie)
+				continue;
+			this_peer_addr = fst_mbie_get_peer_addr_for_band(
+				cur_mbie, this_band_id);
+			if (!this_peer_addr)
+				continue;
+			if (os_memcmp(this_peer_addr, peer_addr, ETH_ALEN) ==
+			    0) {
+				os_memcpy(other_peer_addr, cur_peer_addr,
+					  ETH_ALEN);
+				return other_iface;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+
+/**
+ * fst_group_get_peer_other_connection - Find peer's "other" connection (iface,
+ * MAC tuple).
+ *
+ * @iface: iface on which FST Setup Request was received
+ * @peer_addr: Peer address on iface
+ * @band_id: "other" connection band id
+ * @other_peer_addr (out): Peer's MAC address on the "other" connection (on the
+ *   "other" iface)
+ *
+ * This function is called upon receiving FST Setup Request from some peer who
+ * has peer_addr on iface. It searches for another connection of the same peer
+ * on different interface which correlates with band_id. MB IEs received from
+ * peer (on the two different interfaces) are used to identify same peer.
+ */
+struct fst_iface *
+fst_group_get_peer_other_connection(struct fst_iface *iface,
+				    const u8 *peer_addr, u8 band_id,
+				    u8 *other_peer_addr)
+{
+	struct fst_iface *other_iface;
+
+	fst_printf(MSG_DEBUG, "%s: %s:" MACSTR ", %d", __func__,
+		   fst_iface_get_name(iface), MAC2STR(peer_addr), band_id);
+
+	/*
+	 * Two search methods are used:
+	 * 1. Use peer's MB IE on iface to extract peer's MAC address on
+	 *    "other" connection. Then check if such "other" connection exists.
+	 * 2. Iterate peer database, examine each MB IE to see if it points to
+	 *    (iface, peer_addr) tuple
+	 */
+
+	other_iface = fst_group_get_peer_other_connection_1(iface, peer_addr,
+							    band_id,
+							    other_peer_addr);
+	if (other_iface) {
+		fst_printf(MSG_DEBUG, "found by method #1. %s:" MACSTR,
+			   fst_iface_get_name(other_iface),
+			   MAC2STR(other_peer_addr));
+		return other_iface;
+	}
+
+	other_iface = fst_group_get_peer_other_connection_2(iface, peer_addr,
+							    band_id,
+							    other_peer_addr);
+	if (other_iface) {
+		fst_printf(MSG_DEBUG, "found by method #2. %s:" MACSTR,
+			   fst_iface_get_name(other_iface),
+			   MAC2STR(other_peer_addr));
+		return other_iface;
+	}
+
+	fst_printf(MSG_INFO, "%s: other connection not found", __func__);
+	return NULL;
 }
 
 
diff --git a/src/fst/fst_group.h b/src/fst/fst_group.h
index 3a87c0b..00aee9c 100644
--- a/src/fst/fst_group.h
+++ b/src/fst/fst_group.h
@@ -48,15 +48,9 @@
 struct fst_iface * fst_group_get_iface_by_name(struct fst_group *g,
 					       const char *ifname);
 struct fst_iface *
-fst_group_find_new_iface_by_stie(struct fst_group *g,
-				 struct fst_iface *iface,
-				 const u8 *peer_addr,
-				 const struct session_transition_ie *stie,
-				 u8 *iface_peer_addr);
-struct fst_iface *
-fst_group_get_new_iface_by_stie_and_mbie(
-	struct fst_group *g, const u8 *mb_ies_buff, size_t mb_ies_size,
-	const struct session_transition_ie *stie, u8 *iface_peer_addr);
+fst_group_get_peer_other_connection(struct fst_iface *iface,
+				    const u8 *peer_addr, u8 band_id,
+				    u8 *other_peer_addr);
 u8  fst_group_assign_dialog_token(struct fst_group *g);
 u32 fst_group_assign_fsts_id(struct fst_group *g);
 
diff --git a/src/fst/fst_iface.h b/src/fst/fst_iface.h
index 0eb2732..cbaa7d8 100644
--- a/src/fst/fst_iface.h
+++ b/src/fst/fst_iface.h
@@ -106,7 +106,7 @@
 					  const u8 *addr,
 					  const u8 *buf, size_t size)
 {
-	return i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
+	i->iface_obj.update_mb_ie(i->iface_obj.ctx, addr, buf, size);
 }
 
 static inline const u8 * fst_iface_get_peer_first(struct fst_iface *i,
diff --git a/src/fst/fst_session.c b/src/fst/fst_session.c
index 449e304..a02a93e 100644
--- a/src/fst/fst_session.c
+++ b/src/fst/fst_session.c
@@ -44,7 +44,7 @@
 #define FST_LLT_MS_DEFAULT 50
 #define FST_ACTION_MAX_SUPPORTED   FST_ACTION_ON_CHANNEL_TUNNEL
 
-const char * const fst_action_names[] = {
+static const char * const fst_action_names[] = {
 	[FST_ACTION_SETUP_REQUEST]     = "Setup Request",
 	[FST_ACTION_SETUP_RESPONSE]    = "Setup Response",
 	[FST_ACTION_TEAR_DOWN]         = "Tear Down",
@@ -364,7 +364,6 @@
 	struct fst_iface *new_iface = NULL;
 	struct fst_group *g;
 	u8 new_iface_peer_addr[ETH_ALEN];
-	const struct wpabuf *peer_mbies;
 	size_t plen;
 
 	if (frame_len < IEEE80211_HDRLEN + 1 + sizeof(*req))  {
@@ -400,36 +399,18 @@
 				 MAC2STR(mgmt->sa));
 	}
 
-	peer_mbies = fst_iface_get_peer_mb_ie(iface, mgmt->sa);
-	if (peer_mbies) {
-		new_iface = fst_group_get_new_iface_by_stie_and_mbie(
-			g, wpabuf_head(peer_mbies), wpabuf_len(peer_mbies),
-			&req->stie, new_iface_peer_addr);
-		if (new_iface)
-			fst_printf_iface(iface, MSG_INFO,
-					 "FST Request: new iface (%s:" MACSTR
-					 ") found by MB IEs",
-					 fst_iface_get_name(new_iface),
-					 MAC2STR(new_iface_peer_addr));
-	}
-
-	if (!new_iface) {
-		new_iface = fst_group_find_new_iface_by_stie(
-			g, iface, mgmt->sa, &req->stie,
-			new_iface_peer_addr);
-		if (new_iface)
-			fst_printf_iface(iface, MSG_INFO,
-					 "FST Request: new iface (%s:" MACSTR
-					 ") found by others",
-					 fst_iface_get_name(new_iface),
-					 MAC2STR(new_iface_peer_addr));
-	}
-
+	new_iface = fst_group_get_peer_other_connection(iface, mgmt->sa,
+							req->stie.new_band_id,
+							new_iface_peer_addr);
 	if (!new_iface) {
 		fst_printf_iface(iface, MSG_WARNING,
 				 "FST Request dropped: new iface not found");
 		return;
 	}
+	fst_printf_iface(iface, MSG_INFO,
+			 "FST Request: new iface (%s:" MACSTR ") found",
+			 fst_iface_get_name(new_iface),
+			 MAC2STR(new_iface_peer_addr));
 
 	s = fst_find_session_in_progress(mgmt->sa, g);
 	if (s) {
@@ -775,8 +756,6 @@
 	struct fst_session *s;
 	u32 id;
 
-	WPA_ASSERT(!is_zero_ether_addr(own_addr));
-
 	id = fst_find_free_session_id();
 	if (id == FST_INVALID_SESSION_ID) {
 		fst_printf(MSG_ERROR, "Cannot assign new session ID");
@@ -994,7 +973,7 @@
 	res.stie.length = sizeof(res.stie) - 2;
 
 	if (status_code == WLAN_STATUS_SUCCESS) {
-		res.stie.fsts_id = s->data.fsts_id;
+		res.stie.fsts_id = host_to_le32(s->data.fsts_id);
 		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
 
 		fst_iface_get_channel_info(s->data.new_iface, &hw_mode,
@@ -1468,7 +1447,7 @@
 	res.stie.length = sizeof(res.stie) - 2;
 
 	if (res.status_code == WLAN_STATUS_SUCCESS) {
-		res.stie.fsts_id = fsts_id;
+		res.stie.fsts_id = host_to_le32(fsts_id);
 		res.stie.session_control = SESSION_CONTROL(SESSION_TYPE_BSS, 0);
 
 		fst_iface_get_channel_info(s.data.new_iface, &hw_mode,
@@ -1517,7 +1496,7 @@
 	os_memset(&req, 0, sizeof(req));
 	req.action = FST_ACTION_ACK_REQUEST;
 	req.dialog_token = g->dialog_token;
-	req.fsts_id = fsts_id;
+	req.fsts_id = host_to_le32(fsts_id);
 
 	return fst_session_send_action(&s, FALSE, &req, sizeof(req), NULL);
 }
@@ -1545,7 +1524,7 @@
 	os_memset(&res, 0, sizeof(res));
 	res.action = FST_ACTION_ACK_RESPONSE;
 	res.dialog_token = g->dialog_token;
-	res.fsts_id = fsts_id;
+	res.fsts_id = host_to_le32(fsts_id);
 
 	return fst_session_send_action(&s, FALSE, &res, sizeof(res), NULL);
 }
@@ -1572,7 +1551,7 @@
 
 	os_memset(&td, 0, sizeof(td));
 	td.action = FST_ACTION_TEAR_DOWN;
-	td.fsts_id = fsts_id;
+	td.fsts_id = host_to_le32(fsts_id);
 
 	return fst_session_send_action(&s, TRUE, &td, sizeof(td), NULL);
 }
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
index e26ca20..ce86802 100644
--- a/src/l2_packet/l2_packet_privsep.c
+++ b/src/l2_packet/l2_packet_privsep.c
@@ -51,7 +51,7 @@
 	return 0;
 }
 
-			     
+
 int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
 {
 	os_memcpy(addr, l2->own_addr, ETH_ALEN);
@@ -258,7 +258,7 @@
 		unlink(l2->own_socket_path);
 		os_free(l2->own_socket_path);
 	}
-		
+
 	os_free(l2);
 }
 
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index 6942c85..14d6279 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -711,6 +711,7 @@
 	struct p2p_message msg;
 	const u8 *p2p_dev_addr;
 	int wfd_changed;
+	int dev_name_changed;
 	int i;
 	struct os_reltime time_now;
 
@@ -788,11 +789,11 @@
 		dev->oper_ssid_len = msg.ssid[1];
 	}
 
-	if (msg.adv_service_instance && msg.adv_service_instance_len) {
-		wpabuf_free(dev->info.p2ps_instance);
+	wpabuf_free(dev->info.p2ps_instance);
+	dev->info.p2ps_instance = NULL;
+	if (msg.adv_service_instance && msg.adv_service_instance_len)
 		dev->info.p2ps_instance = wpabuf_alloc_copy(
 			msg.adv_service_instance, msg.adv_service_instance_len);
-	}
 
 	if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
 	    *msg.ds_params >= 1 && *msg.ds_params <= 14) {
@@ -821,6 +822,9 @@
 	}
 	dev->info.level = level;
 
+	dev_name_changed = os_strncmp(dev->info.device_name, msg.device_name,
+				      WPS_DEV_NAME_MAX_LEN) != 0;
+
 	p2p_copy_wps_info(p2p, dev, 0, &msg);
 
 	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
@@ -839,9 +843,12 @@
 
 	wfd_changed = p2p_compare_wfd_info(dev, &msg);
 
-	if (msg.wfd_subelems) {
+	if (wfd_changed) {
 		wpabuf_free(dev->info.wfd_subelems);
-		dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+		if (msg.wfd_subelems)
+			dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
+		else
+			dev->info.wfd_subelems = NULL;
 	}
 
 	if (scan_res) {
@@ -855,6 +862,7 @@
 	p2p_update_peer_vendor_elems(dev, ies, ies_len);
 
 	if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+	    !dev_name_changed &&
 	    (!msg.adv_service_instance ||
 	     (dev->flags & P2P_DEV_P2PS_REPORTED)))
 		return 0;
@@ -1833,6 +1841,7 @@
 	p2p_clear_timeout(p2p);
 	p2p->ssid_set = 0;
 	peer->go_neg_req_sent = 0;
+	peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
 	peer->wps_method = WPS_NOT_READY;
 	peer->oob_pw_id = 0;
 	wpabuf_free(peer->go_neg_conf);
@@ -2234,6 +2243,58 @@
 	return buf;
 }
 
+static int p2p_build_probe_resp_buf(struct p2p_data *p2p, struct wpabuf *buf,
+				    struct wpabuf *ies,
+				    const u8 *addr, int rx_freq)
+{
+	struct ieee80211_mgmt *resp;
+	u8 channel, op_class;
+
+	resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+					u.probe_resp.variable));
+
+	resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+					   (WLAN_FC_STYPE_PROBE_RESP << 4));
+	os_memcpy(resp->da, addr, ETH_ALEN);
+	os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
+	os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
+	resp->u.probe_resp.beacon_int = host_to_le16(100);
+	/* hardware or low-level driver will setup seq_ctrl and timestamp */
+	resp->u.probe_resp.capab_info =
+	    host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
+		     WLAN_CAPABILITY_PRIVACY |
+		     WLAN_CAPABILITY_SHORT_SLOT_TIME);
+
+	wpabuf_put_u8(buf, WLAN_EID_SSID);
+	wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
+	wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+
+	wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
+	wpabuf_put_u8(buf, 8);
+	wpabuf_put_u8(buf, (60 / 5) | 0x80);
+	wpabuf_put_u8(buf, 90 / 5);
+	wpabuf_put_u8(buf, (120 / 5) | 0x80);
+	wpabuf_put_u8(buf, 180 / 5);
+	wpabuf_put_u8(buf, (240 / 5) | 0x80);
+	wpabuf_put_u8(buf, 360 / 5);
+	wpabuf_put_u8(buf, 480 / 5);
+	wpabuf_put_u8(buf, 540 / 5);
+
+	if (!rx_freq) {
+		channel = p2p->cfg->channel;
+	} else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
+		p2p_err(p2p, "Failed to convert freq to channel");
+		return -1;
+	}
+
+	wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
+	wpabuf_put_u8(buf, 1);
+	wpabuf_put_u8(buf, channel);
+
+	wpabuf_put_buf(buf, ies);
+
+	return 0;
+}
 
 static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
 {
@@ -2267,10 +2328,8 @@
 {
 	struct ieee802_11_elems elems;
 	struct wpabuf *buf;
-	struct ieee80211_mgmt *resp;
 	struct p2p_message msg;
 	struct wpabuf *ies;
-	u8 channel, op_class;
 
 	if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
 	    ParseFailed) {
@@ -2414,49 +2473,12 @@
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
-	resp = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
-					u.probe_resp.variable));
-
-	resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
-					   (WLAN_FC_STYPE_PROBE_RESP << 4));
-	os_memcpy(resp->da, addr, ETH_ALEN);
-	os_memcpy(resp->sa, p2p->cfg->dev_addr, ETH_ALEN);
-	os_memcpy(resp->bssid, p2p->cfg->dev_addr, ETH_ALEN);
-	resp->u.probe_resp.beacon_int = host_to_le16(100);
-	/* hardware or low-level driver will setup seq_ctrl and timestamp */
-	resp->u.probe_resp.capab_info =
-		host_to_le16(WLAN_CAPABILITY_SHORT_PREAMBLE |
-			     WLAN_CAPABILITY_PRIVACY |
-			     WLAN_CAPABILITY_SHORT_SLOT_TIME);
-
-	wpabuf_put_u8(buf, WLAN_EID_SSID);
-	wpabuf_put_u8(buf, P2P_WILDCARD_SSID_LEN);
-	wpabuf_put_data(buf, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
-
-	wpabuf_put_u8(buf, WLAN_EID_SUPP_RATES);
-	wpabuf_put_u8(buf, 8);
-	wpabuf_put_u8(buf, (60 / 5) | 0x80);
-	wpabuf_put_u8(buf, 90 / 5);
-	wpabuf_put_u8(buf, (120 / 5) | 0x80);
-	wpabuf_put_u8(buf, 180 / 5);
-	wpabuf_put_u8(buf, (240 / 5) | 0x80);
-	wpabuf_put_u8(buf, 360 / 5);
-	wpabuf_put_u8(buf, 480 / 5);
-	wpabuf_put_u8(buf, 540 / 5);
-
-	if (!rx_freq) {
-		channel = p2p->cfg->channel;
-	} else if (p2p_freq_to_channel(rx_freq, &op_class, &channel)) {
+	if (p2p_build_probe_resp_buf(p2p, buf, ies, addr, rx_freq)) {
 		wpabuf_free(ies);
 		wpabuf_free(buf);
 		return P2P_PREQ_NOT_PROCESSED;
 	}
 
-	wpabuf_put_u8(buf, WLAN_EID_DS_PARAMS);
-	wpabuf_put_u8(buf, 1);
-	wpabuf_put_u8(buf, channel);
-
-	wpabuf_put_buf(buf, ies);
 	wpabuf_free(ies);
 
 	p2p->cfg->send_probe_resp(p2p->cfg->cb_ctx, buf, rx_freq);
@@ -2470,12 +2492,18 @@
 enum p2p_probe_req_status
 p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
 		 const u8 *bssid, const u8 *ie, size_t ie_len,
-		 unsigned int rx_freq)
+		 unsigned int rx_freq, int p2p_lo_started)
 {
 	enum p2p_probe_req_status res;
 
 	p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 
+	if (p2p_lo_started) {
+		p2p_dbg(p2p,
+			"Probe Response is offloaded, do not reply Probe Request");
+		return P2P_PREQ_PROCESSED;
+	}
+
 	res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len, rx_freq);
 	if (res != P2P_PREQ_PROCESSED && res != P2P_PREQ_NOT_PROCESSED)
 		return res;
@@ -2803,6 +2831,7 @@
 	}
 
 	p2p->p2ps_adv_list = NULL;
+	p2ps_prov_free(p2p);
 	p2p_dbg(p2p, "All ASP advertisements flushed");
 }
 
@@ -2980,7 +3009,6 @@
 	os_free(p2p->groups);
 	p2ps_prov_free(p2p);
 	wpabuf_free(p2p->sd_resp);
-	os_free(p2p->after_scan_tx);
 	p2p_remove_wps_vendor_extensions(p2p);
 	os_free(p2p->no_go_freq.range);
 	p2p_service_flush_asp(p2p);
@@ -3004,6 +3032,10 @@
 	os_free(p2p->after_scan_tx);
 	p2p->after_scan_tx = NULL;
 	p2p->ssid_set = 0;
+	p2ps_prov_free(p2p);
+	p2p_reset_pending_pd(p2p);
+	p2p->override_pref_op_class = 0;
+	p2p->override_pref_channel = 0;
 }
 
 
@@ -3782,6 +3814,8 @@
 		break;
 	case P2P_PENDING_INVITATION_RESPONSE:
 		p2p_invitation_resp_cb(p2p, success);
+		if (p2p->inv_status != P2P_SC_SUCCESS)
+			p2p_check_after_scan_tx_continuation(p2p);
 		break;
 	case P2P_PENDING_DEV_DISC_REQUEST:
 		p2p_dev_disc_req_cb(p2p, success);
@@ -5488,3 +5522,42 @@
 			i, p2p->pref_freq_list[i]);
 	}
 }
+
+
+void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class,
+				   u8 chan)
+{
+	p2p->override_pref_op_class = op_class;
+	p2p->override_pref_channel = chan;
+}
+
+
+struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p,
+					      unsigned int freq)
+{
+	struct wpabuf *ies, *buf;
+	u8 addr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+	int ret;
+
+	ies = p2p_build_probe_resp_ies(p2p, NULL, 0);
+	if (!ies) {
+		wpa_printf(MSG_ERROR,
+			   "CTRL: Failed to build Probe Response IEs");
+		return NULL;
+	}
+
+	buf = wpabuf_alloc(200 + wpabuf_len(ies));
+	if (!buf) {
+		wpabuf_free(ies);
+		return NULL;
+	}
+
+	ret = p2p_build_probe_resp_buf(p2p, buf, ies, addr, freq);
+	wpabuf_free(ies);
+	if (ret) {
+		wpabuf_free(buf);
+		return NULL;
+	}
+
+	return buf;
+}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 0feafd3..70d3a90 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -1555,12 +1555,13 @@
  * @ie: Information elements from the Probe Request frame body
  * @ie_len: Length of ie buffer in octets
  * @rx_freq: Probe Request frame RX frequency
+ * @p2p_lo_started: Whether P2P Listen Offload is started
  * Returns: value indicating the type and status of the probe request
  */
 enum p2p_probe_req_status
 p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
 		 const u8 *bssid, const u8 *ie, size_t ie_len,
-		 unsigned int rx_freq);
+		 unsigned int rx_freq, int p2p_lo_started);
 
 /**
  * p2p_rx_action - Report received Action frame
@@ -2133,6 +2134,16 @@
 const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
 
 /**
+ * p2p_group_get_client_interface_addr - Get P2P Interface Address of a client in a group
+ * @group: P2P group context from p2p_group_init()
+ * @dev_addr: P2P Device Address of the client
+ * Returns: P2P Interface Address of the client if found or %NULL if no match
+ * found
+ */
+const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group,
+					       const u8 *dev_addr);
+
+/**
  * p2p_group_get_dev_addr - Get a P2P Device Address of a client in a group
  * @group: P2P group context from p2p_group_init()
  * @addr: P2P Interface Address of the client
@@ -2274,7 +2285,7 @@
  * discovery (p2p_find). A random number of 100 TU units is picked for each
  * Listen state iteration from [min_disc_int,max_disc_int] range.
  *
- * max_disc_tu can be used to futher limit the discoverable duration. However,
+ * max_disc_tu can be used to further limit the discoverable duration. However,
  * it should be noted that use of this parameter is not recommended since it
  * would not be compliant with the P2P specification.
  */
@@ -2362,6 +2373,8 @@
 void p2p_set_own_pref_freq_list(struct p2p_data *p2p,
 				const unsigned int *pref_freq_list,
 				unsigned int size);
+void p2p_set_override_pref_op_chan(struct p2p_data *p2p, u8 op_class,
+				   u8 chan);
 
 /**
  * p2p_group_get_common_freqs - Get the group common frequencies
@@ -2373,4 +2386,7 @@
 int p2p_group_get_common_freqs(struct p2p_group *group, int *common_freqs,
 			       unsigned int *num);
 
+struct wpabuf * p2p_build_probe_resp_template(struct p2p_data *p2p,
+					      unsigned int freq);
+
 #endif /* P2P_H */
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 793d28b..2882c6a 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -202,11 +202,11 @@
 	if (peer && peer->wps_method != WPS_NOT_READY) {
 		if (peer->wps_method == WPS_PBC)
 			methods |= WPS_CONFIG_PUSHBUTTON;
-		else if (peer->wps_method == WPS_PIN_DISPLAY ||
-			 peer->wps_method == WPS_PIN_KEYPAD) {
-			methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+		else if (peer->wps_method == WPS_P2PS)
 			methods |= WPS_CONFIG_P2PS;
-		}
+		else if (peer->wps_method == WPS_PIN_DISPLAY ||
+			 peer->wps_method == WPS_PIN_KEYPAD)
+			methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
 	} else if (p2p->cfg->config_methods) {
 		methods |= p2p->cfg->config_methods &
 			(WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 9f0b3f3..65ab4b8 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -315,7 +315,12 @@
 			       group_capab);
 	p2p_buf_add_go_intent(buf, (p2p->go_intent << 1) | tie_breaker);
 	p2p_buf_add_config_timeout(buf, p2p->go_timeout, p2p->client_timeout);
-	if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
+	if (p2p->override_pref_op_class) {
+		p2p_dbg(p2p, "Override operating channel preference");
+		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+					      p2p->override_pref_op_class,
+					      p2p->override_pref_channel);
+	} else if (peer && peer->go_state == REMOTE_GO && !p2p->num_pref_freq) {
 		p2p_dbg(p2p, "Omit Operating Channel attribute");
 	} else {
 		p2p_buf_add_operating_channel(buf, p2p->cfg->country,
@@ -562,26 +567,11 @@
 	 * also supported by the peer device.
 	 */
 	for (i = 0; i < size && !found; i++) {
-		/*
-		 * Make sure that the common frequency is:
-		 * 1. Supported by peer
-		 * 2. Allowed for P2P use.
-		 */
+		/* Make sure that the common frequency is supported by peer. */
 		oper_freq = freq_list[i];
 		if (p2p_freq_to_channel(oper_freq, &op_class,
-					&op_channel) < 0) {
-			p2p_dbg(p2p, "Unsupported frequency %u MHz", oper_freq);
-			continue;
-		}
-		if (!p2p_channels_includes(&p2p->cfg->channels,
-					   op_class, op_channel) &&
-		    (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
-						  op_class, op_channel))) {
-			p2p_dbg(p2p,
-				"Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
-				oper_freq, op_class, op_channel);
-			break;
-		}
+					&op_channel) < 0)
+			continue; /* cannot happen due to earlier check */
 		for (j = 0; j < msg->channel_list_len; j++) {
 
 			if (op_channel != msg->channel_list[j])
@@ -602,8 +592,7 @@
 			oper_freq);
 	} else {
 		p2p_dbg(p2p,
-			"None of our preferred channels are supported by peer!. Use: %d MHz for oper_channel",
-			dev->oper_freq);
+			"None of our preferred channels are supported by peer!");
 	}
 }
 
@@ -629,29 +618,9 @@
 				msg->pref_freq_list[2 * j + 1]);
 			if (freq_list[i] != oper_freq)
 				continue;
-
-			/*
-			 * Make sure that the found frequency is:
-			 * 1. Supported
-			 * 2. Allowed for P2P use.
-			 */
 			if (p2p_freq_to_channel(oper_freq, &op_class,
-						&op_channel) < 0) {
-				p2p_dbg(p2p, "Unsupported frequency %u MHz",
-					oper_freq);
-				continue;
-			}
-
-			if (!p2p_channels_includes(&p2p->cfg->channels,
-						   op_class, op_channel) &&
-			    (go ||
-			     !p2p_channels_includes(&p2p->cfg->cli_channels,
-						    op_class, op_channel))) {
-				p2p_dbg(p2p,
-					"Freq %u MHz (oper_class %u channel %u) not allowed for P2P",
-					oper_freq, op_class, op_channel);
-				break;
-			}
+						&op_channel) < 0)
+				continue; /* cannot happen */
 			p2p->op_reg_class = op_class;
 			p2p->op_channel = op_channel;
 			os_memcpy(&p2p->channels, &p2p->cfg->channels,
@@ -666,9 +635,7 @@
 			"Freq %d MHz is a common preferred channel for both peer and local, use it as operating channel",
 			oper_freq);
 	} else {
-		p2p_dbg(p2p,
-			"No common preferred channels found! Use: %d MHz for oper_channel",
-			dev->oper_freq);
+		p2p_dbg(p2p, "No common preferred channels found!");
 	}
 }
 
@@ -679,6 +646,8 @@
 	unsigned int freq_list[P2P_MAX_PREF_CHANNELS], size;
 	unsigned int i;
 	u8 op_class, op_channel;
+	char txt[100], *pos, *end;
+	int res;
 
 	/*
 	 * Use the preferred channel list from the driver only if there is no
@@ -694,6 +663,39 @@
 	if (p2p->cfg->get_pref_freq_list(p2p->cfg->cb_ctx, go, &size,
 					 freq_list))
 		return;
+	/* Filter out frequencies that are not acceptable for P2P use */
+	i = 0;
+	while (i < size) {
+		if (p2p_freq_to_channel(freq_list[i], &op_class,
+					&op_channel) < 0 ||
+		    (!p2p_channels_includes(&p2p->cfg->channels,
+					    op_class, op_channel) &&
+		     (go || !p2p_channels_includes(&p2p->cfg->cli_channels,
+						   op_class, op_channel)))) {
+			p2p_dbg(p2p,
+				"Ignore local driver frequency preference %u MHz since it is not acceptable for P2P use (go=%d)",
+				freq_list[i], go);
+			if (size - i - 1 > 0)
+				os_memmove(&freq_list[i], &freq_list[i + 1], size - i - 1);
+			size--;
+			continue;
+		}
+
+		/* Preferred frequency is acceptable for P2P use */
+		i++;
+	}
+
+	pos = txt;
+	end = pos + sizeof(txt);
+	for (i = 0; i < size; i++) {
+		res = os_snprintf(pos, end - pos, " %u", freq_list[i]);
+		if (os_snprintf_error(end - pos, res))
+			break;
+		pos += res;
+	}
+	*pos = '\0';
+	p2p_dbg(p2p, "Local driver frequency preference (size=%u):%s",
+		size, txt);
 
 	/*
 	 * Check if peer's preference of operating channel is in
@@ -703,20 +705,14 @@
 		if (freq_list[i] == (unsigned int) dev->oper_freq)
 			break;
 	}
-	if (i != size) {
+	if (i != size &&
+	    p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) == 0) {
 		/* Peer operating channel preference matches our preference */
-		if (p2p_freq_to_channel(freq_list[i], &op_class, &op_channel) <
-		    0) {
-			p2p_dbg(p2p,
-				"Peer operating channel preference is unsupported frequency %u MHz",
-				freq_list[i]);
-		} else {
-			p2p->op_reg_class = op_class;
-			p2p->op_channel = op_channel;
-			os_memcpy(&p2p->channels, &p2p->cfg->channels,
-				  sizeof(struct p2p_channels));
-			return;
-		}
+		p2p->op_reg_class = op_class;
+		p2p->op_channel = op_channel;
+		os_memcpy(&p2p->channels, &p2p->cfg->channels,
+			  sizeof(struct p2p_channels));
+		return;
 	}
 
 	p2p_dbg(p2p,
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index eac73ef..051b4e3 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -850,6 +850,20 @@
 }
 
 
+const u8 * p2p_group_get_client_interface_addr(struct p2p_group *group,
+					       const u8 *dev_addr)
+{
+	struct p2p_group_member *m;
+
+	if (!group)
+		return NULL;
+	m = p2p_group_get_client(group, dev_addr);
+	if (m)
+		return m->addr;
+	return NULL;
+}
+
+
 static struct p2p_group_member * p2p_group_get_client_iface(
 	struct p2p_group *group, const u8 *interface_addr)
 {
@@ -1098,7 +1112,7 @@
 		struct p2p_device *dev;
 
 		dev = p2p_get_device(group->p2p, m->dev_addr);
-		if (!dev)
+		if (!dev || dev->channels.reg_classes == 0)
 			continue;
 
 		p2p_channels_intersect(&intersect, &dev->channels, &res);
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 47524d4..ce69932 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -553,6 +553,10 @@
 
 	unsigned int pref_freq_list[P2P_MAX_PREF_CHANNELS];
 	unsigned int num_pref_freq;
+
+	/* Override option for preferred operating channel in GO Negotiation */
+	u8 override_pref_op_class;
+	u8 override_pref_channel;
 };
 
 /**
diff --git a/src/p2p/p2p_pd.c b/src/p2p/p2p_pd.c
index 93a0535..3994ec0 100644
--- a/src/p2p/p2p_pd.c
+++ b/src/p2p/p2p_pd.c
@@ -1163,6 +1163,9 @@
 					msg.group_id, msg.group_id_len);
 	}
 
+	if (reject != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+		p2ps_prov_free(p2p);
+
 	if (reject == P2P_SC_SUCCESS) {
 		switch (config_methods) {
 		case WPS_CONFIG_DISPLAY:
@@ -1581,7 +1584,7 @@
 					 report_config_methods);
 
 	if (p2p->state == P2P_PD_DURING_FIND) {
-		p2p_clear_timeout(p2p);
+		p2p_stop_listen_for_freq(p2p, 0);
 		p2p_continue_find(p2p);
 	}
 }
diff --git a/src/p2p/p2p_sd.c b/src/p2p/p2p_sd.c
index a8bc5ba..d2fb4b5 100644
--- a/src/p2p/p2p_sd.c
+++ b/src/p2p/p2p_sd.c
@@ -426,6 +426,7 @@
 {
 	struct wpabuf *resp;
 	size_t max_len;
+	unsigned int wait_time = 200;
 
 	/*
 	 * In the 60 GHz, we have a smaller maximum frame length for management
@@ -460,6 +461,7 @@
 					     1, p2p->srv_update_indic, NULL);
 	} else {
 		p2p_dbg(p2p, "SD response fits in initial response");
+		wait_time = 0; /* no more SD frames in the sequence */
 		resp = p2p_build_sd_response(dialog_token,
 					     WLAN_STATUS_SUCCESS, 0,
 					     p2p->srv_update_indic, resp_tlvs);
@@ -470,7 +472,7 @@
 	p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 	if (p2p_send_action(p2p, freq, dst, p2p->cfg->dev_addr,
 			    p2p->cfg->dev_addr,
-			    wpabuf_head(resp), wpabuf_len(resp), 200) < 0)
+			    wpabuf_head(resp), wpabuf_len(resp), wait_time) < 0)
 		p2p_dbg(p2p, "Failed to send Action frame");
 
 	wpabuf_free(resp);
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
index cf43c59..360fcd3 100644
--- a/src/pae/ieee802_1x_cp.c
+++ b/src/pae/ieee802_1x_cp.c
@@ -20,7 +20,7 @@
 #define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
 #define STATE_MACHINE_DEBUG_PREFIX "CP"
 
-static u8 default_cs_id[] = CS_ID_GCM_AES_128;
+static u64 default_cs_id = CS_ID_GCM_AES_128;
 
 /* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
 enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
@@ -45,7 +45,7 @@
 	Boolean elected_self;
 	u8 *authorization_data1;
 	enum confidentiality_offset cipher_offset;
-	u8 *cipher_suite;
+	u64 cipher_suite;
 	Boolean new_sak; /* clear by CP */
 	struct ieee802_1x_mka_ki distributed_ki;
 	u8 distributed_an;
@@ -71,7 +71,7 @@
 	Boolean replay_protect;
 	u32 replay_window;
 
-	u8 *current_cipher_suite;
+	u64 current_cipher_suite;
 	enum confidentiality_offset confidentiality_offset;
 	Boolean controlled_port_enabled;
 
@@ -97,8 +97,7 @@
 static int changed_cipher(struct ieee802_1x_cp_sm *sm)
 {
 	return sm->confidentiality_offset != sm->cipher_offset ||
-		os_memcmp(sm->current_cipher_suite, sm->cipher_suite,
-			  CS_ID_LEN) != 0;
+		sm->current_cipher_suite != sm->cipher_suite;
 }
 
 
@@ -160,6 +159,7 @@
 
 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
 }
@@ -178,6 +178,7 @@
 
 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
 }
@@ -185,21 +186,17 @@
 
 SM_STATE(CP, SECURED)
 {
-	struct ieee802_1x_cp_conf conf;
-
 	SM_ENTRY(CP, SECURED);
 
 	sm->chgd_server = FALSE;
 
-	ieee802_1x_kay_cp_conf(sm->kay, &conf);
-	sm->protect_frames = conf.protect;
-	sm->replay_protect = conf.replay_protect;
-	sm->validate_frames = conf.validate;
+	sm->protect_frames = sm->kay->macsec_protect;
+	sm->replay_protect = sm->kay->macsec_replay_protect;
+	sm->validate_frames = sm->kay->macsec_validate;
 
-	/* NOTE: now no other than default cipher suiter(AES-GCM-128) */
-	os_memcpy(sm->current_cipher_suite, sm->cipher_suite, CS_ID_LEN);
-	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite,
-					     CS_ID_LEN);
+	/* NOTE: now no other than default cipher suite (AES-GCM-128) */
+	sm->current_cipher_suite = sm->cipher_suite;
+	secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite);
 
 	sm->confidentiality_offset = sm->cipher_offset;
 
@@ -208,6 +205,7 @@
 	secy_cp_control_confidentiality_offset(sm->kay,
 					       sm->confidentiality_offset);
 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
 }
@@ -428,9 +426,7 @@
 /**
  * ieee802_1x_cp_sm_init -
  */
-struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(
-	struct ieee802_1x_kay *kay,
-	struct ieee802_1x_cp_conf *pcp_conf)
+struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
 {
 	struct ieee802_1x_cp_sm *sm;
 
@@ -446,10 +442,10 @@
 
 	sm->chgd_server = FALSE;
 
-	sm->protect_frames = pcp_conf->protect;
-	sm->validate_frames = pcp_conf->validate;
-	sm->replay_protect = pcp_conf->replay_protect;
-	sm->replay_window = pcp_conf->replay_window;
+	sm->protect_frames = kay->macsec_protect;
+	sm->validate_frames = kay->macsec_validate;
+	sm->replay_protect = kay->macsec_replay_protect;
+	sm->replay_window = kay->macsec_replay_window;
 
 	sm->controlled_port_enabled = FALSE;
 
@@ -460,17 +456,8 @@
 	sm->orx = FALSE;
 	sm->otx = FALSE;
 
-	sm->cipher_suite = os_zalloc(CS_ID_LEN);
-	sm->current_cipher_suite = os_zalloc(CS_ID_LEN);
-	if (!sm->cipher_suite || !sm->current_cipher_suite) {
-		wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
-		os_free(sm->cipher_suite);
-		os_free(sm->current_cipher_suite);
-		os_free(sm);
-		return NULL;
-	}
-	os_memcpy(sm->current_cipher_suite, default_cs_id, CS_ID_LEN);
-	os_memcpy(sm->cipher_suite, default_cs_id, CS_ID_LEN);
+	sm->current_cipher_suite = default_cs_id;
+	sm->cipher_suite = default_cs_id;
 	sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
 	sm->confidentiality_offset = sm->cipher_offset;
 	sm->transmit_delay = MKA_LIFE_TIME;
@@ -482,6 +469,7 @@
 	wpa_printf(MSG_DEBUG, "CP: state machine created");
 
 	secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+	secy_cp_control_encrypt(sm->kay, sm->kay->macsec_encrypt);
 	secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
 	secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
 	secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
@@ -530,8 +518,6 @@
 	eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
 	os_free(sm->lki);
 	os_free(sm->oki);
-	os_free(sm->cipher_suite);
-	os_free(sm->current_cipher_suite);
 	os_free(sm->authorization_data);
 	os_free(sm);
 }
@@ -618,10 +604,10 @@
 /**
  * ieee802_1x_cp_set_ciphersuite -
  */
-void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid)
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs)
 {
 	struct ieee802_1x_cp_sm *sm = cp_ctx;
-	os_memcpy(sm->cipher_suite, pid, CS_ID_LEN);
+	sm->cipher_suite = cs;
 }
 
 
diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h
index 773c930..695629e 100644
--- a/src/pae/ieee802_1x_cp.h
+++ b/src/pae/ieee802_1x_cp.h
@@ -16,17 +16,7 @@
 struct ieee802_1x_kay;
 struct ieee802_1x_mka_ki;
 
-struct ieee802_1x_cp_conf {
-	Boolean protect;
-	Boolean replay_protect;
-	enum validate_frames validate;
-	u32 replay_window;
-};
-
-
-struct ieee802_1x_cp_sm *
-ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay,
-		      struct ieee802_1x_cp_conf *pcp_conf);
+struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay);
 void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm);
 void ieee802_1x_cp_sm_step(void *cp_ctx);
 void ieee802_1x_cp_connect_pending(void *cp_ctx);
@@ -36,7 +26,7 @@
 void ieee802_1x_cp_signal_chgdserver(void *cp_ctx);
 void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status);
 void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len);
-void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid);
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs);
 void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset);
 void ieee802_1x_cp_signal_newsak(void *cp_ctx);
 void ieee802_1x_cp_set_distributedki(void *cp_ctx,
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index ef74430..3f9e53d 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -29,6 +29,8 @@
 
 #define PENDING_PN_EXHAUSTION 0xC0000000
 
+#define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3)
+
 /* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */
 #define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 }
 static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009;
@@ -37,12 +39,11 @@
 static struct macsec_ciphersuite cipher_suite_tbl[] = {
 	/* GCM-AES-128 */
 	{
-		CS_ID_GCM_AES_128,
-		CS_NAME_GCM_AES_128,
-		MACSEC_CAP_INTEG_AND_CONF_0_30_50,
-		16,
-
-		0 /* index */
+		.id = CS_ID_GCM_AES_128,
+		.name = CS_NAME_GCM_AES_128,
+		.capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+		.sak_len = DEFAULT_SA_KEY_LEN,
+		.index = 0,
 	},
 };
 #define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
@@ -50,16 +51,21 @@
 
 static struct mka_alg mka_alg_tbl[] = {
 	{
-		MKA_ALGO_AGILITY_2009,
-		/* 128-bit CAK, KEK, ICK, ICV */
-		16, 16,	16, 16,
-		ieee802_1x_cak_128bits_aes_cmac,
-		ieee802_1x_ckn_128bits_aes_cmac,
-		ieee802_1x_kek_128bits_aes_cmac,
-		ieee802_1x_ick_128bits_aes_cmac,
-		ieee802_1x_icv_128bits_aes_cmac,
+		.parameter = MKA_ALGO_AGILITY_2009,
 
-		1, /* index */
+		/* 128-bit CAK, KEK, ICK, ICV */
+		.cak_len = DEFAULT_ICV_LEN,
+		.kek_len = DEFAULT_ICV_LEN,
+		.ick_len = DEFAULT_ICV_LEN,
+		.icv_len = DEFAULT_ICV_LEN,
+
+		.cak_trfm = ieee802_1x_cak_128bits_aes_cmac,
+		.ckn_trfm = ieee802_1x_ckn_128bits_aes_cmac,
+		.kek_trfm = ieee802_1x_kek_128bits_aes_cmac,
+		.ick_trfm = ieee802_1x_ick_128bits_aes_cmac,
+		.icv_hash = ieee802_1x_icv_128bits_aes_cmac,
+
+		.index = 1,
 	},
 };
 #define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
@@ -73,16 +79,6 @@
 }
 
 
-struct mka_param_body_handler {
-	int (*body_tx)(struct ieee802_1x_mka_participant *participant,
-		       struct wpabuf *buf);
-	int (*body_rx)(struct ieee802_1x_mka_participant *participant,
-		       const u8 *mka_msg, size_t msg_len);
-	int (*body_length)(struct ieee802_1x_mka_participant *participant);
-	Boolean (*body_present)(struct ieee802_1x_mka_participant *participant);
-};
-
-
 static void set_mka_param_body_len(void *body, unsigned int len)
 {
 	struct ieee802_1x_mka_hdr *hdr = body;
@@ -98,7 +94,7 @@
 }
 
 
-static int get_mka_param_body_type(const void *body)
+static u8 get_mka_param_body_type(const void *body)
 {
 	const struct ieee802_1x_mka_hdr *hdr = body;
 	return hdr->type;
@@ -122,8 +118,8 @@
 	wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority);
 	wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server);
 	wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired);
-	wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capbility);
-	wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+	wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capability);
+	wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len);
 	wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR,
 		   MAC2STR(body->actor_sci.addr));
 	wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d",
@@ -148,7 +144,7 @@
 	size_t body_len;
 	size_t i;
 	u8 *mi;
-	u32 mn;
+	be32 mn;
 
 	if (body == NULL)
 		return;
@@ -156,10 +152,10 @@
 	body_len = get_mka_param_body_len(body);
 	if (body->type == MKA_LIVE_PEER_LIST) {
 		wpa_printf(MSG_DEBUG, "*** Live Peer List ***");
-		wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+		wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len);
 	} else if (body->type == MKA_POTENTIAL_PEER_LIST) {
 		wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***");
-		wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+		wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len);
 	}
 
 	for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) {
@@ -187,7 +183,7 @@
 	wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan);
 	wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d",
 		   body->confid_offset);
-	wpa_printf(MSG_INFO, "\tBody Length...........: %d", (int) body_len);
+	wpa_printf(MSG_INFO, "\tBody Length...........: %zu", body_len);
 	if (!body_len)
 		return;
 
@@ -280,7 +276,7 @@
 			return participant;
 	}
 
-	wpa_printf(MSG_DEBUG, "KaY: principal participant is not founded");
+	wpa_printf(MSG_DEBUG, "KaY: principal participant is not found");
 	return NULL;
 }
 
@@ -300,52 +296,12 @@
 
 
 /**
- * ieee802_1x_kay_is_in_potential_peer
- */
-static Boolean
-ieee802_1x_kay_is_in_potential_peer(
-	struct ieee802_1x_mka_participant *participant, const u8 *mi)
-{
-	return get_peer_mi(&participant->potential_peers, mi) != NULL;
-}
-
-
-/**
- * ieee802_1x_kay_is_in_live_peer
- */
-static Boolean
-ieee802_1x_kay_is_in_live_peer(
-	struct ieee802_1x_mka_participant *participant, const u8 *mi)
-{
-	return get_peer_mi(&participant->live_peers, mi) != NULL;
-}
-
-
-/**
- * ieee802_1x_kay_is_in_peer
- */
-static Boolean
-ieee802_1x_kay_is_in_peer(struct ieee802_1x_mka_participant *participant,
-			  const u8 *mi)
-{
-	return ieee802_1x_kay_is_in_live_peer(participant, mi) ||
-		ieee802_1x_kay_is_in_potential_peer(participant, mi);
-}
-
-
-/**
- * ieee802_1x_kay_get_peer
+ * ieee802_1x_kay_get_potential_peer
  */
 static struct ieee802_1x_kay_peer *
-ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
-			const u8 *mi)
+ieee802_1x_kay_get_potential_peer(
+	struct ieee802_1x_mka_participant *participant, const u8 *mi)
 {
-	struct ieee802_1x_kay_peer *peer;
-
-	peer = get_peer_mi(&participant->live_peers, mi);
-	if (peer)
-		return peer;
-
 	return get_peer_mi(&participant->potential_peers, mi);
 }
 
@@ -362,22 +318,82 @@
 
 
 /**
+ * ieee802_1x_kay_is_in_potential_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_potential_peer(
+	struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+	return ieee802_1x_kay_get_potential_peer(participant, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_live_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_live_peer(
+	struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+	return ieee802_1x_kay_get_live_peer(participant, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_get_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
+			const u8 *mi)
+{
+	struct ieee802_1x_kay_peer *peer;
+
+	peer = ieee802_1x_kay_get_live_peer(participant, mi);
+	if (peer)
+		return peer;
+
+	return ieee802_1x_kay_get_potential_peer(participant, mi);
+}
+
+
+/**
  * ieee802_1x_kay_get_cipher_suite
  */
 static struct macsec_ciphersuite *
 ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
-				u8 *cs_id)
+				const u8 *cs_id)
 {
 	unsigned int i;
+	u64 cs;
+	be64 _cs;
+
+	os_memcpy(&_cs, cs_id, CS_ID_LEN);
+	cs = be_to_host64(_cs);
 
 	for (i = 0; i < CS_TABLE_SIZE; i++) {
-		if (os_memcmp(cipher_suite_tbl[i].id, cs_id, CS_ID_LEN) == 0)
-			break;
+		if (cipher_suite_tbl[i].id == cs)
+			return &cipher_suite_tbl[i];
 	}
-	if (i >= CS_TABLE_SIZE)
-		return NULL;
 
-	return &cipher_suite_tbl[i];
+	return NULL;
+}
+
+
+u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci)
+{
+	struct ieee802_1x_mka_sci tmp;
+
+	os_memcpy(tmp.addr, sci->addr, ETH_ALEN);
+	tmp.port = sci->port;
+
+	return *((u64 *) &tmp);
+}
+
+
+static Boolean sci_equal(const struct ieee802_1x_mka_sci *a,
+			 const struct ieee802_1x_mka_sci *b)
+{
+	return os_memcmp(a, b, sizeof(struct ieee802_1x_mka_sci)) == 0;
 }
 
 
@@ -392,13 +408,13 @@
 
 	dl_list_for_each(peer, &participant->live_peers,
 			 struct ieee802_1x_kay_peer, list) {
-		if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+		if (sci_equal(&peer->sci, sci))
 			return peer;
 	}
 
 	dl_list_for_each(peer, &participant->potential_peers,
 			 struct ieee802_1x_kay_peer, list) {
-		if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+		if (sci_equal(&peer->sci, sci))
 			return peer;
 	}
 
@@ -406,6 +422,8 @@
 }
 
 
+static void ieee802_1x_kay_use_data_key(struct data_key *pkey);
+
 /**
  * ieee802_1x_kay_init_receive_sa -
  */
@@ -424,6 +442,7 @@
 		return NULL;
 	}
 
+	ieee802_1x_kay_use_data_key(key);
 	psa->pkey = key;
 	psa->lowest_pn = lowest_pn;
 	psa->next_pn = lowest_pn;
@@ -435,22 +454,25 @@
 
 	dl_list_add(&psc->sa_list, &psa->list);
 	wpa_printf(MSG_DEBUG,
-		   "KaY: Create receive SA(AN: %d lowest_pn: %u of SC(channel: %d)",
-		   (int) an, lowest_pn, psc->channel);
+		   "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC",
+		   an, lowest_pn);
 
 	return psa;
 }
 
 
+static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey);
+
 /**
  * ieee802_1x_kay_deinit_receive_sa -
  */
 static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
 {
+	ieee802_1x_kay_deinit_data_key(psa->pkey);
 	psa->pkey = NULL;
 	wpa_printf(MSG_DEBUG,
-		   "KaY: Delete receive SA(an: %d) of SC(channel: %d)",
-		   psa->an, psa->sc->channel);
+		   "KaY: Delete receive SA(an: %hhu) of SC",
+		   psa->an);
 	dl_list_del(&psa->list);
 	os_free(psa);
 }
@@ -460,8 +482,7 @@
  * ieee802_1x_kay_init_receive_sc -
  */
 static struct receive_sc *
-ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci,
-			       int channel)
+ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci)
 {
 	struct receive_sc *psc;
 
@@ -475,19 +496,27 @@
 	}
 
 	os_memcpy(&psc->sci, psci, sizeof(psc->sci));
-	psc->channel = channel;
 
 	os_get_time(&psc->created_time);
 	psc->receiving = FALSE;
 
 	dl_list_init(&psc->sa_list);
-	wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel);
+	wpa_printf(MSG_DEBUG, "KaY: Create receive SC");
 	wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci));
 
 	return psc;
 }
 
 
+static void ieee802_1x_delete_receive_sa(struct ieee802_1x_kay *kay,
+					 struct receive_sa *sa)
+{
+	secy_disable_receive_sa(kay, sa);
+	secy_delete_receive_sa(kay, sa);
+	ieee802_1x_kay_deinit_receive_sa(sa);
+}
+
+
 /**
  * ieee802_1x_kay_deinit_receive_sc -
  **/
@@ -497,31 +526,32 @@
 {
 	struct receive_sa *psa, *pre_sa;
 
-	wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)",
-		   psc->channel);
+	wpa_printf(MSG_DEBUG, "KaY: Delete receive SC");
 	dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
-			      list)  {
-		secy_disable_receive_sa(participant->kay, psa);
-		ieee802_1x_kay_deinit_receive_sa(psa);
-	}
+			      list)
+		ieee802_1x_delete_receive_sa(participant->kay, psa);
+
 	dl_list_del(&psc->list);
 	os_free(psc);
 }
 
 
-/**
- * ieee802_1x_kay_create_live_peer
- */
+static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer)
+{
+	wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+	wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+	wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+	wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+}
+
+
 static struct ieee802_1x_kay_peer *
-ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
-				u8 *mi, u32 mn)
+ieee802_1x_kay_create_peer(const u8 *mi, u32 mn)
 {
 	struct ieee802_1x_kay_peer *peer;
-	struct receive_sc *rxsc;
-	u32 sc_ch = 0;
 
 	peer = os_zalloc(sizeof(*peer));
-	if (peer == NULL) {
+	if (!peer) {
 		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
 		return NULL;
 	}
@@ -530,24 +560,40 @@
 	peer->mn = mn;
 	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
 	peer->sak_used = FALSE;
-	os_memcpy(&peer->sci, &participant->current_peer_sci,
-		  sizeof(peer->sci));
-	dl_list_add(&participant->live_peers, &peer->list);
 
-	secy_get_available_receive_sc(participant->kay, &sc_ch);
+	return peer;
+}
 
-	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
-	if (!rxsc)
+
+/**
+ * ieee802_1x_kay_create_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
+				const u8 *mi, u32 mn)
+{
+	struct ieee802_1x_kay_peer *peer;
+	struct receive_sc *rxsc;
+
+	peer = ieee802_1x_kay_create_peer(mi, mn);
+	if (!peer)
 		return NULL;
 
+	os_memcpy(&peer->sci, &participant->current_peer_sci,
+		  sizeof(peer->sci));
+
+	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci);
+	if (!rxsc) {
+		os_free(peer);
+		return NULL;
+	}
+
+	dl_list_add(&participant->live_peers, &peer->list);
 	dl_list_add(&participant->rxsc_list, &rxsc->list);
 	secy_create_receive_sc(participant->kay, rxsc);
 
 	wpa_printf(MSG_DEBUG, "KaY: Live peer created");
-	wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
-	wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
-	wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
-	wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+	ieee802_1x_kay_dump_peer(peer);
 
 	return peer;
 }
@@ -562,24 +608,14 @@
 {
 	struct ieee802_1x_kay_peer *peer;
 
-	peer = os_zalloc(sizeof(*peer));
-	if (peer == NULL) {
-		wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+	peer = ieee802_1x_kay_create_peer(mi, mn);
+	if (!peer)
 		return NULL;
-	}
-
-	os_memcpy(peer->mi, mi, MI_LEN);
-	peer->mn = mn;
-	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
-	peer->sak_used = FALSE;
 
 	dl_list_add(&participant->potential_peers, &peer->list);
 
 	wpa_printf(MSG_DEBUG, "KaY: potential peer created");
-	wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
-	wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
-	wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
-	wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+	ieee802_1x_kay_dump_peer(peer);
 
 	return peer;
 }
@@ -594,13 +630,12 @@
 {
 	struct ieee802_1x_kay_peer *peer;
 	struct receive_sc *rxsc;
-	u32 sc_ch = 0;
 
-	dl_list_for_each(peer, &participant->potential_peers,
-			 struct ieee802_1x_kay_peer, list) {
-		if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
-			break;
-	}
+	peer = ieee802_1x_kay_get_potential_peer(participant, mi);
+
+	rxsc = ieee802_1x_kay_init_receive_sc(&participant->current_peer_sci);
+	if (!rxsc)
+		return NULL;
 
 	os_memcpy(&peer->sci, &participant->current_peer_sci,
 		  sizeof(peer->sci));
@@ -608,20 +643,11 @@
 	peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
 
 	wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer");
-	wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
-	wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
-	wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
-	wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+	ieee802_1x_kay_dump_peer(peer);
 
 	dl_list_del(&peer->list);
 	dl_list_add_tail(&participant->live_peers, &peer->list);
 
-	secy_get_available_receive_sc(participant->kay, &sc_ch);
-
-	rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
-	if (!rxsc)
-		return NULL;
-
 	dl_list_add(&participant->rxsc_list, &rxsc->list);
 	secy_create_receive_sc(participant->kay, rxsc);
 
@@ -651,7 +677,7 @@
 
 	length = sizeof(struct ieee802_1x_mka_basic_body);
 	length += participant->ckn.len;
-	return (length + 0x3) & ~0x3;
+	return MKA_ALIGN_LENGTH(length);
 }
 
 
@@ -677,17 +703,17 @@
 		body->key_server = participant->can_be_key_server;
 
 	body->macsec_desired = kay->macsec_desired;
-	body->macsec_capbility = kay->macsec_capable;
+	body->macsec_capability = kay->macsec_capable;
 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
 
 	os_memcpy(body->actor_sci.addr, kay->actor_sci.addr,
 		  sizeof(kay->actor_sci.addr));
-	body->actor_sci.port = host_to_be16(kay->actor_sci.port);
+	body->actor_sci.port = kay->actor_sci.port;
 
 	os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi));
 	participant->mn = participant->mn + 1;
 	body->actor_mn = host_to_be32(participant->mn);
-	os_memcpy(body->algo_agility, participant->kay->algo_agility,
+	os_memcpy(body->algo_agility, kay->algo_agility,
 		  sizeof(body->algo_agility));
 
 	os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len);
@@ -698,6 +724,17 @@
 }
 
 
+static Boolean
+reset_participant_mi(struct ieee802_1x_mka_participant *participant)
+{
+	if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+		return FALSE;
+	participant->mn = 0;
+
+	return TRUE;
+}
+
+
 /**
  * ieee802_1x_mka_decode_basic_body -
  */
@@ -729,16 +766,15 @@
 
 	/* If the peer's MI is my MI, I will choose new MI */
 	if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
-		if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+		if (!reset_participant_mi(participant))
 			return NULL;
-		participant->mn = 0;
 	}
 
 	os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
-	participant->current_peer_id.mn =  be_to_host32(body->actor_mn);
+	participant->current_peer_id.mn = body->actor_mn;
 	os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr,
 		  sizeof(participant->current_peer_sci.addr));
-	participant->current_peer_sci.port = be_to_host16(body->actor_sci.port);
+	participant->current_peer_sci.port = body->actor_sci.port;
 
 	/* handler peer */
 	peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
@@ -763,14 +799,14 @@
 			return NULL;
 
 		peer->macsec_desired = body->macsec_desired;
-		peer->macsec_capbility = body->macsec_capbility;
+		peer->macsec_capability = body->macsec_capability;
 		peer->is_key_server = (Boolean) body->key_server;
 		peer->key_server_priority = body->priority;
 	} else if (peer->mn < be_to_host32(body->actor_mn)) {
 		peer->mn = be_to_host32(body->actor_mn);
 		peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
 		peer->macsec_desired = body->macsec_desired;
-		peer->macsec_capbility = body->macsec_capbility;
+		peer->macsec_capability = body->macsec_capability;
 		peer->is_key_server = (Boolean) body->key_server;
 		peer->key_server_priority = body->priority;
 	} else {
@@ -807,7 +843,7 @@
 			 struct ieee802_1x_kay_peer, list)
 		len += sizeof(struct ieee802_1x_mka_peer_id);
 
-	return (len + 0x3) & ~0x3;
+	return MKA_ALIGN_LENGTH(len);
 }
 
 
@@ -836,7 +872,6 @@
 				       sizeof(struct ieee802_1x_mka_peer_id));
 		os_memcpy(body_peer->mi, peer->mi, MI_LEN);
 		body_peer->mn = host_to_be32(peer->mn);
-		body_peer++;
 	}
 
 	ieee802_1x_mka_dump_peer_body(body);
@@ -868,7 +903,7 @@
 			 struct ieee802_1x_kay_peer, list)
 		len += sizeof(struct ieee802_1x_mka_peer_id);
 
-	return (len + 0x3) & ~0x3;
+	return MKA_ALIGN_LENGTH(len);
 }
 
 
@@ -897,7 +932,6 @@
 				       sizeof(struct ieee802_1x_mka_peer_id));
 		os_memcpy(body_peer->mi, peer->mi, MI_LEN);
 		body_peer->mn = host_to_be32(peer->mn);
-		body_peer++;
 	}
 
 	ieee802_1x_mka_dump_peer_body(body);
@@ -912,62 +946,54 @@
 ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
 			     const u8 *mka_msg, size_t msg_len)
 {
-	Boolean included = FALSE;
 	struct ieee802_1x_mka_hdr *hdr;
 	size_t body_len;
 	size_t left_len;
-	int body_type;
-	u32 peer_mn;
-	const u8 *peer_mi;
+	u8 body_type;
 	const u8 *pos;
 	size_t i;
 
-	pos = mka_msg;
-	left_len = msg_len;
-	while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
+	for (pos = mka_msg, left_len = msg_len;
+	     left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
+	     left_len -= body_len + MKA_HDR_LEN,
+		     pos += body_len + MKA_HDR_LEN) {
 		hdr = (struct ieee802_1x_mka_hdr *) pos;
 		body_len = get_mka_param_body_len(hdr);
 		body_type = get_mka_param_body_type(hdr);
 
 		if (body_type != MKA_LIVE_PEER_LIST &&
 		    body_type != MKA_POTENTIAL_PEER_LIST)
-			goto SKIP_PEER;
+			continue;
 
 		ieee802_1x_mka_dump_peer_body(
 			(struct ieee802_1x_mka_peer_body *)pos);
 
 		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
 			wpa_printf(MSG_ERROR,
-				   "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
-				   (int) left_len, (int) MKA_HDR_LEN,
-				   (int) body_len, DEFAULT_ICV_LEN);
-			goto SKIP_PEER;
+				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
+				   left_len, MKA_HDR_LEN,
+				   body_len, DEFAULT_ICV_LEN);
+			continue;
 		}
 
 		if ((body_len % 16) != 0) {
 			wpa_printf(MSG_ERROR,
-				   "KaY: MKA Peer Packet Body Length (%d bytes) should multiple of 16 octets",
-				   (int) body_len);
-			goto SKIP_PEER;
+				   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
+				   body_len);
+			continue;
 		}
 
-		for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
-			peer_mi = MKA_HDR_LEN + pos + i;
-			os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
-			peer_mn = be_to_host32(peer_mn);
-			if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0 &&
-			    peer_mn == participant->mn) {
-				included = TRUE;
-				break;
-			}
+		for (i = 0; i < body_len;
+		     i += sizeof(struct ieee802_1x_mka_peer_id)) {
+			const struct ieee802_1x_mka_peer_id *peer_mi;
+
+			peer_mi = (const struct ieee802_1x_mka_peer_id *)
+				(pos + MKA_HDR_LEN + i);
+			if (os_memcmp(peer_mi->mi, participant->mi,
+				      MI_LEN) == 0 &&
+			    be_to_host32(peer_mi->mn) == participant->mn)
+				return TRUE;
 		}
-
-		if (included)
-			return TRUE;
-
-SKIP_PEER:
-		left_len -= body_len + MKA_HDR_LEN;
-		pos += body_len + MKA_HDR_LEN;
 	}
 
 	return FALSE;
@@ -984,8 +1010,6 @@
 	const struct ieee802_1x_mka_hdr *hdr;
 	struct ieee802_1x_kay_peer *peer;
 	size_t body_len;
-	u32 peer_mn;
-	const u8 *peer_mi;
 	size_t i;
 	Boolean is_included;
 
@@ -994,36 +1018,40 @@
 
 	hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
 	body_len = get_mka_param_body_len(hdr);
+	if (body_len % 16 != 0) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
+			   body_len);
+		return -1;
+	}
 
-	for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
-		peer_mi = MKA_HDR_LEN + peer_msg + i;
-		os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
-		peer_mn = be_to_host32(peer_mn);
+	for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
+		const struct ieee802_1x_mka_peer_id *peer_mi;
+		u32 peer_mn;
+
+		peer_mi = (const struct ieee802_1x_mka_peer_id *)
+			(peer_msg + MKA_HDR_LEN + i);
+		peer_mn = be_to_host32(peer_mi->mn);
 
 		/* it is myself */
 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
 			/* My message id is used by other participant */
-			if (peer_mn > participant->mn) {
-				if (os_get_random(participant->mi,
-						  sizeof(participant->mi)) < 0)
-					wpa_printf(MSG_DEBUG,
-						   "KaY: Could not update mi");
-				participant->mn = 0;
-			}
+			if (peer_mn > participant->mn &&
+			    !reset_participant_mi(participant))
+				wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
 			continue;
 		}
+
 		if (!is_included)
 			continue;
 
-		peer = ieee802_1x_kay_get_peer(participant, peer_mi);
-		if (NULL != peer) {
+		peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi);
+		if (peer) {
 			peer->mn = peer_mn;
 			peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
-		} else {
-			if (!ieee802_1x_kay_create_potential_peer(
-				participant, peer_mi, peer_mn)) {
-				return -1;
-			}
+		} else if (!ieee802_1x_kay_create_potential_peer(
+				participant, peer_mi->mi, peer_mn)) {
+			return -1;
 		}
 	}
 
@@ -1039,30 +1067,33 @@
 	struct ieee802_1x_mka_participant *participant,
 	const u8 *peer_msg, size_t msg_len)
 {
-	struct ieee802_1x_mka_hdr *hdr;
+	const struct ieee802_1x_mka_hdr *hdr;
 	size_t body_len;
-	u32 peer_mn;
-	const u8 *peer_mi;
 	size_t i;
 
-	hdr = (struct ieee802_1x_mka_hdr *) peer_msg;
+	hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
 	body_len = get_mka_param_body_len(hdr);
+	if (body_len % 16 != 0) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: MKA Peer Packet Body Length (%zu bytes) should be a multiple of 16 octets",
+			   body_len);
+		return -1;
+	}
 
-	for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
-		peer_mi = MKA_HDR_LEN + peer_msg + i;
-		os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
-		peer_mn = be_to_host32(peer_mn);
+	for (i = 0; i < body_len; i += sizeof(struct ieee802_1x_mka_peer_id)) {
+		const struct ieee802_1x_mka_peer_id *peer_mi;
+		u32 peer_mn;
+
+		peer_mi = (struct ieee802_1x_mka_peer_id *)
+			(peer_msg + MKA_HDR_LEN + i);
+		peer_mn = be_to_host32(peer_mi->mn);
 
 		/* it is myself */
 		if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
 			/* My message id is used by other participant */
-			if (peer_mn > participant->mn) {
-				if (os_get_random(participant->mi,
-						  sizeof(participant->mi)) < 0)
-					wpa_printf(MSG_DEBUG,
-						   "KaY: Could not update mi");
-				participant->mn = 0;
-			}
+			if (peer_mn > participant->mn &&
+			    !reset_participant_mi(participant))
+				wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
 			continue;
 		}
 	}
@@ -1078,10 +1109,7 @@
 ieee802_1x_mka_sak_use_body_present(
 	struct ieee802_1x_mka_participant *participant)
 {
-	if (participant->to_use_sak)
-		return TRUE;
-	else
-		return FALSE;
+	return participant->to_use_sak;
 }
 
 
@@ -1096,12 +1124,8 @@
 
 	if (participant->kay->macsec_desired && participant->advised_desired)
 		length = sizeof(struct ieee802_1x_mka_sak_use_body);
-	else
-		length = MKA_HDR_LEN;
 
-	length = (length + 0x3) & ~0x3;
-
-	return length;
+	return MKA_ALIGN_LENGTH(length);
 }
 
 
@@ -1146,11 +1170,12 @@
 	struct wpabuf *buf)
 {
 	struct ieee802_1x_mka_sak_use_body *body;
+	struct ieee802_1x_kay *kay = participant->kay;
 	unsigned int length;
 	u32 pn = 1;
 
 	length = ieee802_1x_mka_get_sak_use_length(participant);
-	body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_sak_use_body));
+	body = wpabuf_put(buf, length);
 
 	body->type = MKA_SAK_USE;
 	set_mka_param_body_len(body, length - MKA_HDR_LEN);
@@ -1166,9 +1191,9 @@
 	}
 
 	/* data protect, lowest accept packet number */
-	body->delay_protect = participant->kay->macsec_replay_protect;
+	body->delay_protect = kay->macsec_replay_protect;
 	pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
-	if (pn > participant->kay->pn_exhaustion) {
+	if (pn > kay->pn_exhaustion) {
 		wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
 		if (participant->is_key_server)
 			participant->new_sak = TRUE;
@@ -1179,20 +1204,12 @@
 	body->olpn = host_to_be32(pn);
 
 	/* plain tx, plain rx */
-	if (participant->kay->macsec_protect)
-		body->ptx = FALSE;
-	else
-		body->ptx = TRUE;
-
-	if (participant->kay->macsec_validate == Strict)
-		body->prx = FALSE;
-	else
-		body->prx = TRUE;
+	body->ptx = !kay->macsec_protect;
+	body->prx = kay->macsec_validate != Strict;
 
 	/* latest key: rx, tx, key server member identifier key number */
 	body->lan = participant->lan;
-	os_memcpy(body->lsrv_mi, participant->lki.mi,
-		  sizeof(body->lsrv_mi));
+	os_memcpy(body->lsrv_mi, participant->lki.mi, sizeof(body->lsrv_mi));
 	body->lkn = host_to_be32(participant->lki.kn);
 	body->lrx = participant->lrx;
 	body->ltx = participant->ltx;
@@ -1213,16 +1230,11 @@
 
 	/* set CP's variable */
 	if (body->ltx) {
-		if (!participant->kay->tx_enable)
-			participant->kay->tx_enable = TRUE;
-
-		if (!participant->kay->port_enable)
-			participant->kay->port_enable = TRUE;
+		kay->tx_enable = TRUE;
+		kay->port_enable = TRUE;
 	}
-	if (body->lrx) {
-		if (!participant->kay->rx_enable)
-			participant->kay->rx_enable = TRUE;
-	}
+	if (body->lrx)
+		kay->rx_enable = TRUE;
 
 	ieee802_1x_mka_dump_sak_use_body(body);
 	return 0;
@@ -1246,7 +1258,8 @@
 	struct ieee802_1x_mka_ki ki;
 	u32 lpn;
 	Boolean all_receiving;
-	Boolean founded;
+	Boolean found;
+	struct ieee802_1x_kay *kay = participant->kay;
 
 	if (!participant->principal) {
 		wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
@@ -1266,8 +1279,8 @@
 
 	if ((body_len != 0) && (body_len < 40)) {
 		wpa_printf(MSG_ERROR,
-			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 40, or more octets",
-			   (int) body_len);
+			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 40, or more octets",
+			   body_len);
 		return -1;
 	}
 
@@ -1288,30 +1301,29 @@
 
 	/* check latest key is valid */
 	if (body->ltx || body->lrx) {
-		founded = FALSE;
+		found = FALSE;
 		os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
-		ki.kn = ntohl(body->lkn);
+		ki.kn = be_to_host32(body->lkn);
 		dl_list_for_each(sa_key, &participant->sak_list,
 				 struct data_key, list) {
 			if (is_ki_equal(&sa_key->key_identifier, &ki)) {
-				founded = TRUE;
+				found = TRUE;
 				break;
 			}
 		}
-		if (!founded) {
+		if (!found) {
 			wpa_printf(MSG_WARNING, "KaY: Latest key is invalid");
 			return -1;
 		}
 		if (os_memcmp(participant->lki.mi, body->lsrv_mi,
 			      sizeof(participant->lki.mi)) == 0 &&
-		    ntohl(body->lkn) == participant->lki.kn &&
+		    be_to_host32(body->lkn) == participant->lki.kn &&
 		    body->lan == participant->lan) {
 			peer->sak_used = TRUE;
 		}
 		if (body->ltx && peer->is_key_server) {
-			ieee802_1x_cp_set_servertransmitting(
-				participant->kay->cp, TRUE);
-			ieee802_1x_cp_sm_step(participant->kay->cp);
+			ieee802_1x_cp_set_servertransmitting(kay->cp, TRUE);
+			ieee802_1x_cp_sm_step(kay->cp);
 		}
 	}
 
@@ -1319,7 +1331,7 @@
 	if (body->otx || body->orx) {
 		if (os_memcmp(participant->oki.mi, body->osrv_mi,
 			      sizeof(participant->oki.mi)) != 0 ||
-		    ntohl(body->okn) != participant->oki.kn ||
+		    be_to_host32(body->okn) != participant->oki.kn ||
 		    body->oan != participant->oan) {
 			wpa_printf(MSG_WARNING, "KaY: Old key is invalid");
 			return -1;
@@ -1327,7 +1339,8 @@
 	}
 
 	/* TODO: how to set the MACsec hardware when delay_protect is true */
-	if (body->delay_protect && (!ntohl(body->llpn) || !ntohl(body->olpn))) {
+	if (body->delay_protect &&
+	    (!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) {
 		wpa_printf(MSG_WARNING,
 			   "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE");
 		return -1;
@@ -1344,28 +1357,28 @@
 	}
 	if (all_receiving) {
 		participant->to_dist_sak = FALSE;
-		ieee802_1x_cp_set_allreceiving(participant->kay->cp, TRUE);
-		ieee802_1x_cp_sm_step(participant->kay->cp);
+		ieee802_1x_cp_set_allreceiving(kay->cp, TRUE);
+		ieee802_1x_cp_sm_step(kay->cp);
 	}
 
 	/* if i'm key server, and detects peer member pn exhaustion, rekey.*/
-	lpn = ntohl(body->llpn);
-	if (lpn > participant->kay->pn_exhaustion) {
+	lpn = be_to_host32(body->llpn);
+	if (lpn > kay->pn_exhaustion) {
 		if (participant->is_key_server) {
 			participant->new_sak = TRUE;
 			wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
 		}
 	}
 
-	founded = FALSE;
+	found = FALSE;
 	dl_list_for_each(txsa, &participant->txsc->sa_list,
 			 struct transmit_sa, list) {
 		if (sa_key != NULL && txsa->pkey == sa_key) {
-			founded = TRUE;
+			found = TRUE;
 			break;
 		}
 	}
-	if (!founded) {
+	if (!found) {
 		wpa_printf(MSG_WARNING, "KaY: Can't find txsa");
 		return -1;
 	}
@@ -1373,9 +1386,9 @@
 	/* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
 	 * npn is larger than txsa's npn, set it to txsa.
 	 */
-	secy_get_transmit_next_pn(participant->kay, txsa);
+	secy_get_transmit_next_pn(kay, txsa);
 	if (lpn > txsa->next_pn) {
-		secy_set_transmit_next_pn(participant->kay, txsa);
+		secy_set_transmit_next_pn(kay, txsa);
 		wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn);
 	}
 
@@ -1390,10 +1403,7 @@
 ieee802_1x_mka_dist_sak_body_present(
 	struct ieee802_1x_mka_participant *participant)
 {
-	if (!participant->to_dist_sak || !participant->new_key)
-		return FALSE;
-
-	return TRUE;
+	return participant->to_dist_sak && participant->new_key;
 }
 
 
@@ -1404,21 +1414,18 @@
 ieee802_1x_mka_get_dist_sak_length(
 	struct ieee802_1x_mka_participant *participant)
 {
-	int length;
-	int cs_index = participant->kay->macsec_csindex;
+	int length = MKA_HDR_LEN;
+	unsigned int cs_index = participant->kay->macsec_csindex;
 
-	if (participant->advised_desired) {
+	if (participant->advised_desired && cs_index < CS_TABLE_SIZE) {
 		length = sizeof(struct ieee802_1x_mka_dist_sak_body);
 		if (cs_index != DEFAULT_CS_INDEX)
 			length += CS_ID_LEN;
 
 		length += cipher_suite_tbl[cs_index].sak_len + 8;
-	} else {
-		length = MKA_HDR_LEN;
 	}
-	length = (length + 0x3) & ~0x3;
 
-	return length;
+	return MKA_ALIGN_LENGTH(length);
 }
 
 
@@ -1433,7 +1440,7 @@
 	struct ieee802_1x_mka_dist_sak_body *body;
 	struct data_key *sak;
 	unsigned int length;
-	int cs_index;
+	unsigned int cs_index;
 	int sak_pos;
 
 	length = ieee802_1x_mka_get_dist_sak_length(participant);
@@ -1452,8 +1459,13 @@
 	body->kn = host_to_be32(sak->key_identifier.kn);
 	cs_index = participant->kay->macsec_csindex;
 	sak_pos = 0;
+	if (cs_index >= CS_TABLE_SIZE)
+		return -1;
 	if (cs_index != DEFAULT_CS_INDEX) {
-		os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN);
+		be64 cs;
+
+		cs = host_to_be64(cipher_suite_tbl[cs_index].id);
+		os_memcpy(body->sak, &cs, CS_ID_LEN);
 		sak_pos = CS_ID_LEN;
 	}
 	if (aes_wrap(participant->kek.key, 16,
@@ -1472,39 +1484,13 @@
 /**
  * ieee802_1x_kay_init_data_key -
  */
-static struct data_key *
-ieee802_1x_kay_init_data_key(const struct key_conf *conf)
+static void ieee802_1x_kay_init_data_key(struct data_key *pkey)
 {
-	struct data_key *pkey;
-
-	if (!conf)
-		return NULL;
-
-	pkey = os_zalloc(sizeof(*pkey));
-	if (pkey == NULL) {
-		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
-		return NULL;
-	}
-
-	pkey->key = os_zalloc(conf->key_len);
-	if (pkey->key == NULL) {
-		wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
-		os_free(pkey);
-		return NULL;
-	}
-
-	os_memcpy(pkey->key, conf->key, conf->key_len);
-	os_memcpy(&pkey->key_identifier, &conf->ki,
-		  sizeof(pkey->key_identifier));
-	pkey->confidentiality_offset = conf->offset;
-	pkey->an = conf->an;
-	pkey->transmits = conf->tx;
-	pkey->receives = conf->rx;
+	pkey->transmits = TRUE;
+	pkey->receives = TRUE;
 	os_get_time(&pkey->created_time);
 
 	pkey->user = 1;
-
-	return pkey;
 }
 
 
@@ -1521,19 +1507,18 @@
 	struct ieee802_1x_kay_peer *peer;
 	struct macsec_ciphersuite *cs;
 	size_t body_len;
-	struct key_conf *conf;
 	struct data_key *sa_key = NULL;
-	struct ieee802_1x_mka_ki sak_ki;
 	int sak_len;
 	u8 *wrap_sak;
 	u8 *unwrap_sak;
+	struct ieee802_1x_kay *kay = participant->kay;
 
 	hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
 	body_len = get_mka_param_body_len(hdr);
 	if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
 		wpa_printf(MSG_ERROR,
-			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 28, 36, or more octets",
-			   (int) body_len);
+			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 0, 28, 36, or more octets",
+			   body_len);
 		return -1;
 	}
 
@@ -1547,8 +1532,8 @@
 			   "KaY: I can't accept the distributed SAK as myself is key server ");
 		return -1;
 	}
-	if (!participant->kay->macsec_desired ||
-	    participant->kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
+	if (!kay->macsec_desired ||
+	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
 		wpa_printf(MSG_ERROR,
 			   "KaY: I am not MACsec-desired or without MACsec capable");
 		return -1;
@@ -1561,28 +1546,29 @@
 			   "KaY: The key server is not in my live peers list");
 		return -1;
 	}
-	if (os_memcmp(&participant->kay->key_server_sci,
-		      &peer->sci, sizeof(struct ieee802_1x_mka_sci)) != 0) {
+	if (!sci_equal(&kay->key_server_sci, &peer->sci)) {
 		wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
 		return -1;
 	}
+
 	if (body_len == 0) {
-		participant->kay->authenticated = TRUE;
-		participant->kay->secured = FALSE;
-		participant->kay->failed = FALSE;
+		kay->authenticated = TRUE;
+		kay->secured = FALSE;
+		kay->failed = FALSE;
 		participant->advised_desired = FALSE;
-		ieee802_1x_cp_connect_authenticated(participant->kay->cp);
-		ieee802_1x_cp_sm_step(participant->kay->cp);
+		ieee802_1x_cp_connect_authenticated(kay->cp);
+		ieee802_1x_cp_sm_step(kay->cp);
 		wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec");
-		participant->to_use_sak = TRUE;
+		participant->to_use_sak = FALSE;
 		return 0;
 	}
+
 	participant->advised_desired = TRUE;
-	participant->kay->authenticated = FALSE;
-	participant->kay->secured = TRUE;
-	participant->kay->failed = FALSE;
-	ieee802_1x_cp_connect_secure(participant->kay->cp);
-	ieee802_1x_cp_sm_step(participant->kay->cp);
+	kay->authenticated = FALSE;
+	kay->secured = TRUE;
+	kay->failed = FALSE;
+	ieee802_1x_cp_connect_secure(kay->cp);
+	ieee802_1x_cp_sm_step(kay->cp);
 
 	body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
 	ieee802_1x_mka_dump_dist_sak_body(body);
@@ -1595,10 +1581,12 @@
 			return 0;
 		}
 	}
+
 	if (body_len == 28) {
 		sak_len = DEFAULT_SA_KEY_LEN;
 		wrap_sak =  body->sak;
-		participant->kay->macsec_csindex = DEFAULT_CS_INDEX;
+		kay->macsec_csindex = DEFAULT_CS_INDEX;
+		cs = &cipher_suite_tbl[kay->macsec_csindex];
 	} else {
 		cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak);
 		if (!cs) {
@@ -1608,7 +1596,7 @@
 		}
 		sak_len = cs->sak_len;
 		wrap_sak = body->sak + CS_ID_LEN;
-		participant->kay->macsec_csindex = cs->index;
+		kay->macsec_csindex = cs->index;
 	}
 
 	unwrap_sak = os_zalloc(sak_len);
@@ -1624,62 +1612,38 @@
 	}
 	wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len);
 
-	conf = os_zalloc(sizeof(*conf));
-	if (!conf) {
-		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
-		os_free(unwrap_sak);
-		return -1;
-	}
-	conf->key_len = sak_len;
-
-	conf->key = os_zalloc(conf->key_len);
-	if (!conf->key) {
-		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
-		os_free(unwrap_sak);
-		os_free(conf);
-		return -1;
-	}
-
-	os_memcpy(conf->key, unwrap_sak, conf->key_len);
-
-	os_memcpy(&sak_ki.mi, &participant->current_peer_id.mi,
-		  sizeof(sak_ki.mi));
-	sak_ki.kn = be_to_host32(body->kn);
-
-	os_memcpy(conf->ki.mi, sak_ki.mi, MI_LEN);
-	conf->ki.kn = sak_ki.kn;
-	conf->an = body->dan;
-	conf->offset = body->confid_offset;
-	conf->rx = TRUE;
-	conf->tx = TRUE;
-
-	sa_key = ieee802_1x_kay_init_data_key(conf);
+	sa_key = os_zalloc(sizeof(*sa_key));
 	if (!sa_key) {
 		os_free(unwrap_sak);
-		os_free(conf->key);
-		os_free(conf);
 		return -1;
 	}
 
+	os_memcpy(&sa_key->key_identifier.mi, &participant->current_peer_id.mi,
+		  MI_LEN);
+	sa_key->key_identifier.kn = be_to_host32(body->kn);
+
+	sa_key->key = unwrap_sak;
+	sa_key->key_len = sak_len;
+
+	sa_key->confidentiality_offset = body->confid_offset;
+	sa_key->an = body->dan;
+	ieee802_1x_kay_init_data_key(sa_key);
+
+	ieee802_1x_kay_use_data_key(sa_key);
 	dl_list_add(&participant->sak_list, &sa_key->list);
 
-	ieee802_1x_cp_set_ciphersuite(
-		participant->kay->cp,
-		cipher_suite_tbl[participant->kay->macsec_csindex].id);
-	ieee802_1x_cp_sm_step(participant->kay->cp);
-	ieee802_1x_cp_set_offset(participant->kay->cp, body->confid_offset);
-	ieee802_1x_cp_sm_step(participant->kay->cp);
-	ieee802_1x_cp_set_distributedki(participant->kay->cp, &sak_ki);
-	ieee802_1x_cp_set_distributedan(participant->kay->cp, body->dan);
-	ieee802_1x_cp_signal_newsak(participant->kay->cp);
-	ieee802_1x_cp_sm_step(participant->kay->cp);
+	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
+	ieee802_1x_cp_sm_step(kay->cp);
+	ieee802_1x_cp_set_offset(kay->cp, body->confid_offset);
+	ieee802_1x_cp_sm_step(kay->cp);
+	ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
+	ieee802_1x_cp_set_distributedan(kay->cp, body->dan);
+	ieee802_1x_cp_signal_newsak(kay->cp);
+	ieee802_1x_cp_sm_step(kay->cp);
 
+	kay->rcvd_keys++;
 	participant->to_use_sak = TRUE;
 
-	os_free(unwrap_sak);
-	os_free(conf->key);
-	os_free(conf);
-
 	return 0;
 }
 
@@ -1705,7 +1669,7 @@
 	length = sizeof(struct ieee802_1x_mka_icv_body);
 	length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
 
-	return (length + 0x3) & ~0x3;
+	return MKA_ALIGN_LENGTH(length);
 }
 
 
@@ -1733,12 +1697,9 @@
 		return -1;
 	}
 
-	if (length != DEFAULT_ICV_LEN)  {
-		os_memcpy(wpabuf_put(buf, length - MKA_HDR_LEN), cmac,
-			  length - MKA_HDR_LEN);
-	} else {
-		os_memcpy(wpabuf_put(buf, length), cmac, length);
-	}
+	if (length != DEFAULT_ICV_LEN)
+		length -= MKA_HDR_LEN;
+	os_memcpy(wpabuf_put(buf, length), cmac, length);
 
 	return 0;
 }
@@ -1754,7 +1715,7 @@
 	struct ieee802_1x_mka_icv_body *body;
 	size_t body_len;
 	size_t left_len;
-	int body_type;
+	u8 body_type;
 	const u8 *pos;
 
 	pos = mka_msg;
@@ -1801,8 +1762,8 @@
 	body_len = get_mka_param_body_len(hdr);
 	if (body_len < 28) {
 		wpa_printf(MSG_ERROR,
-			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 28 or more octets",
-			   (int) body_len);
+			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 28 or more octets",
+			   body_len);
 		return -1;
 	}
 
@@ -1825,8 +1786,8 @@
 	body_len = get_mka_param_body_len(hdr);
 	if (body_len < 5) {
 		wpa_printf(MSG_ERROR,
-			   "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 5 or more octets",
-			   (int) body_len);
+			   "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 5 or more octets",
+			   body_len);
 		return -1;
 	}
 
@@ -1845,85 +1806,105 @@
 }
 
 
-static struct mka_param_body_handler mak_body_handler[] = {
+struct mka_param_body_handler {
+	int (*body_tx)(struct ieee802_1x_mka_participant *participant,
+		       struct wpabuf *buf);
+	int (*body_rx)(struct ieee802_1x_mka_participant *participant,
+		       const u8 *mka_msg, size_t msg_len);
+	int (*body_length)(struct ieee802_1x_mka_participant *participant);
+	Boolean (*body_present)(struct ieee802_1x_mka_participant *participant);
+};
+
+
+static struct mka_param_body_handler mka_body_handler[] = {
 	/* basic parameter set */
 	{
-		ieee802_1x_mka_encode_basic_body,
-		NULL,
-		ieee802_1x_mka_basic_body_length,
-		ieee802_1x_mka_basic_body_present
+		.body_tx      = ieee802_1x_mka_encode_basic_body,
+		.body_rx      = NULL,
+		.body_length  = ieee802_1x_mka_basic_body_length,
+		.body_present = ieee802_1x_mka_basic_body_present
 	},
 
 	/* live peer list parameter set */
 	{
-		ieee802_1x_mka_encode_live_peer_body,
-		ieee802_1x_mka_decode_live_peer_body,
-		ieee802_1x_mka_get_live_peer_length,
-		ieee802_1x_mka_live_peer_body_present
+		.body_tx      = ieee802_1x_mka_encode_live_peer_body,
+		.body_rx      = ieee802_1x_mka_decode_live_peer_body,
+		.body_length  = ieee802_1x_mka_get_live_peer_length,
+		.body_present = ieee802_1x_mka_live_peer_body_present
 	},
 
 	/* potential peer list parameter set */
 	{
-		ieee802_1x_mka_encode_potential_peer_body,
-		ieee802_1x_mka_decode_potential_peer_body,
-		ieee802_1x_mka_get_potential_peer_length,
-		ieee802_1x_mka_potential_peer_body_present
+		.body_tx      = ieee802_1x_mka_encode_potential_peer_body,
+		.body_rx      = ieee802_1x_mka_decode_potential_peer_body,
+		.body_length  = ieee802_1x_mka_get_potential_peer_length,
+		.body_present = ieee802_1x_mka_potential_peer_body_present
 	},
 
 	/* sak use parameter set */
 	{
-		ieee802_1x_mka_encode_sak_use_body,
-		ieee802_1x_mka_decode_sak_use_body,
-		ieee802_1x_mka_get_sak_use_length,
-		ieee802_1x_mka_sak_use_body_present
+		.body_tx      = ieee802_1x_mka_encode_sak_use_body,
+		.body_rx      = ieee802_1x_mka_decode_sak_use_body,
+		.body_length  = ieee802_1x_mka_get_sak_use_length,
+		.body_present = ieee802_1x_mka_sak_use_body_present
 	},
 
 	/* distribute sak parameter set */
 	{
-		ieee802_1x_mka_encode_dist_sak_body,
-		ieee802_1x_mka_decode_dist_sak_body,
-		ieee802_1x_mka_get_dist_sak_length,
-		ieee802_1x_mka_dist_sak_body_present
+		.body_tx      = ieee802_1x_mka_encode_dist_sak_body,
+		.body_rx      = ieee802_1x_mka_decode_dist_sak_body,
+		.body_length  = ieee802_1x_mka_get_dist_sak_length,
+		.body_present = ieee802_1x_mka_dist_sak_body_present
 	},
 
 	/* distribute cak parameter set */
 	{
-		NULL,
-		ieee802_1x_mka_decode_dist_cak_body,
-		NULL,
-		NULL
+		.body_tx      = NULL,
+		.body_rx      = ieee802_1x_mka_decode_dist_cak_body,
+		.body_length  = NULL,
+		.body_present = NULL
 	},
 
 	/* kmd parameter set */
 	{
-		NULL,
-		ieee802_1x_mka_decode_kmd_body,
-		NULL,
-		NULL
+		.body_tx      = NULL,
+		.body_rx      = ieee802_1x_mka_decode_kmd_body,
+		.body_length  = NULL,
+		.body_present = NULL
 	},
 
 	/* announce parameter set */
 	{
-		NULL,
-		ieee802_1x_mka_decode_announce_body,
-		NULL,
-		NULL
+		.body_tx      = NULL,
+		.body_rx      = ieee802_1x_mka_decode_announce_body,
+		.body_length  = NULL,
+		.body_present = NULL
 	},
 
 	/* icv parameter set */
 	{
-		ieee802_1x_mka_encode_icv_body,
-		NULL,
-		ieee802_1x_mka_get_icv_length,
-		ieee802_1x_mka_icv_body_present
+		.body_tx      = ieee802_1x_mka_encode_icv_body,
+		.body_rx      = NULL,
+		.body_length  = ieee802_1x_mka_get_icv_length,
+		.body_present = ieee802_1x_mka_icv_body_present
 	},
 };
 
 
 /**
- * ieee802_1x_kay_deinit_data_key -
+ * ieee802_1x_kay_use_data_key - Take reference on a key
  */
-void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
+static void ieee802_1x_kay_use_data_key(struct data_key *pkey)
+{
+	pkey->user++;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_data_key - Release reference on a key and
+ * free if there are no remaining users
+ */
+static void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
 {
 	if (!pkey)
 		return;
@@ -1932,7 +1913,6 @@
 	if (pkey->user > 1)
 		return;
 
-	dl_list_del(&pkey->list);
 	os_free(pkey->key);
 	os_free(pkey);
 }
@@ -1945,11 +1925,13 @@
 ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
 {
 	struct data_key *sa_key = NULL;
-	struct key_conf *conf;
 	struct ieee802_1x_kay_peer *peer;
 	struct ieee802_1x_kay *kay = participant->kay;
 	int ctx_len, ctx_offset;
 	u8 *context;
+	unsigned int key_len;
+	u8 *key;
+	struct macsec_ciphersuite *cs;
 
 	/* check condition for generating a fresh SAK:
 	 * must have one live peer
@@ -1976,40 +1958,29 @@
 		return -1;
 	}
 
-	conf = os_zalloc(sizeof(*conf));
-	if (!conf) {
-		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
-		return -1;
-	}
-	conf->key_len = cipher_suite_tbl[kay->macsec_csindex].sak_len;
-
-	conf->key = os_zalloc(conf->key_len);
-	if (!conf->key) {
-		os_free(conf);
+	cs = &cipher_suite_tbl[kay->macsec_csindex];
+	key_len = cs->sak_len;
+	key = os_zalloc(key_len);
+	if (!key) {
 		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
 		return -1;
 	}
 
-	ctx_len = conf->key_len + sizeof(kay->dist_kn);
+	ctx_len = key_len + sizeof(kay->dist_kn);
 	dl_list_for_each(peer, &participant->live_peers,
 			 struct ieee802_1x_kay_peer, list)
 		ctx_len += sizeof(peer->mi);
 	ctx_len += sizeof(participant->mi);
 
 	context = os_zalloc(ctx_len);
-	if (!context) {
-		os_free(conf->key);
-		os_free(conf);
-		return -1;
-	}
+	if (!context)
+		goto fail;
+
 	ctx_offset = 0;
-	if (os_get_random(context + ctx_offset, conf->key_len) < 0) {
-		os_free(context);
-		os_free(conf->key);
-		os_free(conf);
-		return -1;
-	}
-	ctx_offset += conf->key_len;
+	if (os_get_random(context + ctx_offset, key_len) < 0)
+		goto fail;
+
+	ctx_offset += key_len;
 	dl_list_for_each(peer, &participant->live_peers,
 			 struct ieee802_1x_kay_peer, list) {
 		os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
@@ -2020,46 +1991,46 @@
 	ctx_offset += sizeof(participant->mi);
 	os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
 
-	if (conf->key_len == 16) {
+	if (key_len == 16) {
 		ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
-						context, ctx_len, conf->key);
-	} else if (conf->key_len == 32) {
+						context, ctx_len, key);
+	} else if (key_len == 32) {
 		ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
-						context, ctx_len, conf->key);
+						context, ctx_len, key);
 	} else {
 		wpa_printf(MSG_ERROR, "KaY: SAK Length not support");
-		os_free(conf->key);
-		os_free(conf);
-		os_free(context);
-		return -1;
+		goto fail;
 	}
-	wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK",
-		    conf->key, conf->key_len);
+	wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
+	os_free(context);
+	context = NULL;
 
-	os_memcpy(conf->ki.mi, participant->mi, MI_LEN);
-	conf->ki.kn = participant->kay->dist_kn;
-	conf->an = participant->kay->dist_an;
-	conf->offset = kay->macsec_confidentiality;
-	conf->rx = TRUE;
-	conf->tx = TRUE;
-
-	sa_key = ieee802_1x_kay_init_data_key(conf);
+	sa_key = os_zalloc(sizeof(*sa_key));
 	if (!sa_key) {
-		os_free(conf->key);
-		os_free(conf);
-		os_free(context);
-		return -1;
+		wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+		goto fail;
 	}
+
+	sa_key->key = key;
+	sa_key->key_len = key_len;
+	os_memcpy(sa_key->key_identifier.mi, participant->mi, MI_LEN);
+	sa_key->key_identifier.kn = kay->dist_kn;
+
+	sa_key->confidentiality_offset = kay->macsec_confidentiality;
+	sa_key->an = kay->dist_an;
+	ieee802_1x_kay_init_data_key(sa_key);
+
 	participant->new_key = sa_key;
 
+	ieee802_1x_kay_use_data_key(sa_key);
 	dl_list_add(&participant->sak_list, &sa_key->list);
-	ieee802_1x_cp_set_ciphersuite(participant->kay->cp,
-				      cipher_suite_tbl[kay->macsec_csindex].id);
+
+	ieee802_1x_cp_set_ciphersuite(kay->cp, cs->id);
 	ieee802_1x_cp_sm_step(kay->cp);
-	ieee802_1x_cp_set_offset(kay->cp, conf->offset);
+	ieee802_1x_cp_set_offset(kay->cp, kay->macsec_confidentiality);
 	ieee802_1x_cp_sm_step(kay->cp);
-	ieee802_1x_cp_set_distributedki(kay->cp, &conf->ki);
-	ieee802_1x_cp_set_distributedan(kay->cp, conf->an);
+	ieee802_1x_cp_set_distributedki(kay->cp, &sa_key->key_identifier);
+	ieee802_1x_cp_set_distributedan(kay->cp, sa_key->an);
 	ieee802_1x_cp_signal_newsak(kay->cp);
 	ieee802_1x_cp_sm_step(kay->cp);
 
@@ -2067,17 +2038,31 @@
 			 struct ieee802_1x_kay_peer, list)
 		peer->sak_used = FALSE;
 
-	participant->kay->dist_kn++;
-	participant->kay->dist_an++;
-	if (participant->kay->dist_an > 3)
-		participant->kay->dist_an = 0;
+	kay->dist_kn++;
+	kay->dist_an++;
+	if (kay->dist_an > 3)
+		kay->dist_an = 0;
 
-	participant->kay->dist_time = time(NULL);
+	kay->dist_time = time(NULL);
 
-	os_free(conf->key);
-	os_free(conf);
-	os_free(context);
 	return 0;
+
+fail:
+	os_free(key);
+	os_free(context);
+	return -1;
+}
+
+
+static int compare_priorities(const struct ieee802_1x_kay_peer *peer,
+			      const struct ieee802_1x_kay_peer *other)
+{
+	if (peer->key_server_priority < other->key_server_priority)
+		return -1;
+	if (other->key_server_priority < peer->key_server_priority)
+		return 1;
+
+	return os_memcmp(peer->sci.addr, other->sci.addr, ETH_ALEN);
 }
 
 
@@ -2092,7 +2077,6 @@
 	struct ieee802_1x_kay_peer *key_server = NULL;
 	struct ieee802_1x_kay *kay = participant->kay;
 	Boolean i_is_key_server;
-	int i;
 
 	if (participant->is_obliged_key_server) {
 		participant->new_sak = TRUE;
@@ -2112,47 +2096,26 @@
 			continue;
 		}
 
-		if (peer->key_server_priority <
-		    key_server->key_server_priority) {
+		if (compare_priorities(peer, key_server) < 0)
 			key_server = peer;
-		} else if (peer->key_server_priority ==
-			   key_server->key_server_priority) {
-			for (i = 0; i < 6; i++) {
-				if (peer->sci.addr[i] <
-				    key_server->sci.addr[i])
-					key_server = peer;
-			}
-		}
 	}
 
 	/* elect the key server between me and the above elected peer */
 	i_is_key_server = FALSE;
 	if (key_server && participant->can_be_key_server) {
-		if (kay->actor_priority
-			   < key_server->key_server_priority) {
-			i_is_key_server = TRUE;
-		} else if (kay->actor_priority
-					== key_server->key_server_priority) {
-			for (i = 0; i < 6; i++) {
-				if (kay->actor_sci.addr[i]
-					< key_server->sci.addr[i]) {
-					i_is_key_server = TRUE;
-				}
-			}
-		}
-	}
+		struct ieee802_1x_kay_peer tmp;
 
-	if (!key_server && !i_is_key_server) {
-		participant->principal = FALSE;
-		participant->is_key_server = FALSE;
-		participant->is_elected = FALSE;
-		return 0;
+		tmp.key_server_priority = kay->actor_priority;
+		os_memcpy(&tmp.sci, &kay->actor_sci, sizeof(tmp.sci));
+		if (compare_priorities(&tmp, key_server) < 0)
+			i_is_key_server = TRUE;
+	} else if (participant->can_be_key_server) {
+		i_is_key_server = TRUE;
 	}
 
 	if (i_is_key_server) {
 		ieee802_1x_cp_set_electedself(kay->cp, TRUE);
-		if (os_memcmp(&kay->key_server_sci, &kay->actor_sci,
-			      sizeof(kay->key_server_sci))) {
+		if (!sci_equal(&kay->key_server_sci, &kay->actor_sci)) {
 			ieee802_1x_cp_signal_chgdserver(kay->cp);
 			ieee802_1x_cp_sm_step(kay->cp);
 		}
@@ -2167,12 +2130,9 @@
 		os_memcpy(&kay->key_server_sci, &kay->actor_sci,
 			  sizeof(kay->key_server_sci));
 		kay->key_server_priority = kay->actor_priority;
-	}
-
-	if (key_server) {
+	} else if (key_server) {
 		ieee802_1x_cp_set_electedself(kay->cp, FALSE);
-		if (os_memcmp(&kay->key_server_sci, &key_server->sci,
-			      sizeof(kay->key_server_sci))) {
+		if (!sci_equal(&kay->key_server_sci, &key_server->sci)) {
 			ieee802_1x_cp_signal_chgdserver(kay->cp);
 			ieee802_1x_cp_sm_step(kay->cp);
 		}
@@ -2184,6 +2144,10 @@
 		os_memcpy(&kay->key_server_sci, &key_server->sci,
 			  sizeof(kay->key_server_sci));
 		kay->key_server_priority = key_server->key_server_priority;
+	} else {
+		participant->principal = FALSE;
+		participant->is_key_server = FALSE;
+		participant->is_elected = FALSE;
 	}
 
 	return 0;
@@ -2226,11 +2190,11 @@
 		if (!peer->macsec_desired)
 			continue;
 
-		if (peer->macsec_capbility == MACSEC_CAP_NOT_IMPLEMENTED)
+		if (peer->macsec_capability == MACSEC_CAP_NOT_IMPLEMENTED)
 			continue;
 
-		less_capability = (less_capability < peer->macsec_capbility) ?
-			less_capability : peer->macsec_capbility;
+		less_capability = (less_capability < peer->macsec_capability) ?
+			less_capability : peer->macsec_capability;
 		has_peer = TRUE;
 	}
 
@@ -2291,10 +2255,10 @@
 	eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
 	eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used);
 
-	for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
-		if (mak_body_handler[i].body_present &&
-		    mak_body_handler[i].body_present(participant)) {
-			if (mak_body_handler[i].body_tx(participant, pbuf))
+	for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
+		if (mka_body_handler[i].body_present &&
+		    mka_body_handler[i].body_present(participant)) {
+			if (mka_body_handler[i].body_tx(participant, pbuf))
 				return -1;
 		}
 	}
@@ -2316,10 +2280,10 @@
 
 	wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU");
 	length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
-	for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
-		if (mak_body_handler[i].body_present &&
-		    mak_body_handler[i].body_present(participant))
-			length += mak_body_handler[i].body_length(participant);
+	for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
+		if (mka_body_handler[i].body_present &&
+		    mka_body_handler[i].body_present(participant))
+			length += mka_body_handler[i].body_length(participant);
 	}
 
 	buf = wpabuf_alloc(length);
@@ -2344,6 +2308,16 @@
 
 
 static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
+
+static void ieee802_1x_delete_transmit_sa(struct ieee802_1x_kay *kay,
+					  struct transmit_sa *sa)
+{
+	secy_disable_transmit_sa(kay, sa);
+	secy_delete_transmit_sa(kay, sa);
+	ieee802_1x_kay_deinit_transmit_sa(sa);
+}
+
+
 /**
  * ieee802_1x_participant_timer -
  */
@@ -2360,27 +2334,16 @@
 	participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
 	kay = participant->kay;
 	if (participant->cak_life) {
-		if (now > participant->cak_life) {
-			kay->authenticated = FALSE;
-			kay->secured = FALSE;
-			kay->failed = TRUE;
-			ieee802_1x_kay_delete_mka(kay, &participant->ckn);
-			return;
-		}
+		if (now > participant->cak_life)
+			goto delete_mka;
 	}
 
 	/* should delete MKA instance if there are not live peers
 	 * when the MKA life elapsed since its creating */
 	if (participant->mka_life) {
 		if (dl_list_empty(&participant->live_peers)) {
-			if (now > participant->mka_life) {
-				kay->authenticated = FALSE;
-				kay->secured = FALSE;
-				kay->failed = TRUE;
-				ieee802_1x_kay_delete_mka(kay,
-							  &participant->ckn);
-				return;
-			}
+			if (now > participant->mka_life)
+				goto delete_mka;
 		} else {
 			participant->mka_life = 0;
 		}
@@ -2397,11 +2360,10 @@
 			dl_list_for_each_safe(rxsc, pre_rxsc,
 					      &participant->rxsc_list,
 					      struct receive_sc, list) {
-				if (os_memcmp(&rxsc->sci, &peer->sci,
-					      sizeof(rxsc->sci)) == 0) {
-					secy_delete_receive_sc(kay, rxsc);
+				if (sci_equal(&rxsc->sci, &peer->sci)) {
 					ieee802_1x_kay_deinit_receive_sc(
 						participant, rxsc);
+					secy_delete_receive_sc(kay, rxsc);
 				}
 			}
 			dl_list_del(&peer->list);
@@ -2416,6 +2378,12 @@
 			participant->advised_capability =
 				MACSEC_CAP_NOT_IMPLEMENTED;
 			participant->to_use_sak = FALSE;
+			participant->ltx = FALSE;
+			participant->lrx = FALSE;
+			participant->otx = FALSE;
+			participant->orx = FALSE;
+			participant->is_key_server = FALSE;
+			participant->is_elected = FALSE;
 			kay->authenticated = TRUE;
 			kay->secured = FALSE;
 			kay->failed = FALSE;
@@ -2430,8 +2398,7 @@
 			dl_list_for_each_safe(txsa, pre_txsa,
 					      &participant->txsc->sa_list,
 					      struct transmit_sa, list) {
-				secy_disable_transmit_sa(kay, txsa);
-				ieee802_1x_kay_deinit_transmit_sa(txsa);
+				ieee802_1x_delete_transmit_sa(kay, txsa);
 			}
 
 			ieee802_1x_cp_connect_authenticated(kay->cp);
@@ -2461,7 +2428,8 @@
 		participant->new_sak = FALSE;
 	}
 
-	if (participant->retry_count < MAX_RETRY_CNT) {
+	if (participant->retry_count < MAX_RETRY_CNT ||
+	    participant->mode == PSK) {
 		ieee802_1x_participant_send_mkpdu(participant);
 		participant->retry_count++;
 	}
@@ -2469,6 +2437,14 @@
 	eloop_register_timeout(MKA_HELLO_TIME / 1000, 0,
 			       ieee802_1x_participant_timer,
 			       participant, NULL);
+
+	return;
+
+delete_mka:
+	kay->authenticated = FALSE;
+	kay->secured = FALSE;
+	kay->failed = TRUE;
+	ieee802_1x_kay_delete_mka(kay, &participant->ckn);
 }
 
 
@@ -2497,6 +2473,7 @@
 		psa->confidentiality = FALSE;
 
 	psa->an = an;
+	ieee802_1x_kay_use_data_key(key);
 	psa->pkey = key;
 	psa->next_pn = next_PN;
 	psa->sc = psc;
@@ -2506,8 +2483,8 @@
 
 	dl_list_add(&psc->sa_list, &psa->list);
 	wpa_printf(MSG_DEBUG,
-		   "KaY: Create transmit SA(an: %d, next_PN: %u) of SC(channel: %d)",
-		   (int) an, next_PN, psc->channel);
+		   "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC",
+		   an, next_PN);
 
 	return psa;
 }
@@ -2518,10 +2495,11 @@
  */
 static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
 {
+	ieee802_1x_kay_deinit_data_key(psa->pkey);
 	psa->pkey = NULL;
 	wpa_printf(MSG_DEBUG,
-		   "KaY: Delete transmit SA(an: %d) of SC(channel: %d)",
-		   psa->an, psa->sc->channel);
+		   "KaY: Delete transmit SA(an: %hhu) of SC",
+		   psa->an);
 	dl_list_del(&psa->list);
 	os_free(psa);
 }
@@ -2531,8 +2509,7 @@
  * init_transmit_sc -
  */
 static struct transmit_sc *
-ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
-				int channel)
+ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
 {
 	struct transmit_sc *psc;
 
@@ -2542,7 +2519,6 @@
 		return NULL;
 	}
 	os_memcpy(&psc->sci, sci, sizeof(psc->sci));
-	psc->channel = channel;
 
 	os_get_time(&psc->created_time);
 	psc->transmitting = FALSE;
@@ -2550,7 +2526,7 @@
 	psc->enciphering_sa = FALSE;
 
 	dl_list_init(&psc->sa_list);
-	wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel);
+	wpa_printf(MSG_DEBUG, "KaY: Create transmit SC");
 	wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci));
 
 	return psc;
@@ -2566,13 +2542,9 @@
 {
 	struct transmit_sa *psa, *tmp;
 
-	wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)",
-		   psc->channel);
-	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa,
-			      list) {
-		secy_disable_transmit_sa(participant->kay, psa);
-		ieee802_1x_kay_deinit_transmit_sa(psa);
-	}
+	wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC");
+	dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa, list)
+		ieee802_1x_delete_transmit_sa(participant->kay, psa);
 
 	os_free(psc);
 }
@@ -2650,6 +2622,32 @@
 }
 
 
+static struct transmit_sa * lookup_txsa_by_an(struct transmit_sc *txsc, u8 an)
+{
+	struct transmit_sa *txsa;
+
+	dl_list_for_each(txsa, &txsc->sa_list, struct transmit_sa, list) {
+		if (txsa->an == an)
+			return txsa;
+	}
+
+	return NULL;
+}
+
+
+static struct receive_sa * lookup_rxsa_by_an(struct receive_sc *rxsc, u8 an)
+{
+	struct receive_sa *rxsa;
+
+	dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list) {
+		if (rxsa->an == an)
+			return rxsa;
+	}
+
+	return NULL;
+}
+
+
 /**
  * ieee802_1x_kay_create_sas -
  */
@@ -2684,6 +2682,9 @@
 	}
 
 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+		while ((rxsa = lookup_rxsa_by_an(rxsc, latest_sak->an)) != NULL)
+			ieee802_1x_delete_receive_sa(kay, rxsa);
+
 		rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
 						      latest_sak);
 		if (!rxsa)
@@ -2692,6 +2693,10 @@
 		secy_create_receive_sa(kay, rxsa);
 	}
 
+	while ((txsa = lookup_txsa_by_an(principal->txsc, latest_sak->an)) !=
+	       NULL)
+		ieee802_1x_delete_transmit_sa(kay, txsa);
+
 	txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
 					       1, latest_sak);
 	if (!txsa)
@@ -2725,20 +2730,16 @@
 	/* remove the transmit sa */
 	dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
 			      struct transmit_sa, list) {
-		if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
-			secy_disable_transmit_sa(kay, txsa);
-			ieee802_1x_kay_deinit_transmit_sa(txsa);
-		}
+		if (is_ki_equal(&txsa->pkey->key_identifier, ki))
+			ieee802_1x_delete_transmit_sa(kay, txsa);
 	}
 
 	/* remove the receive sa */
 	dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
 		dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
 				      struct receive_sa, list) {
-			if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
-				secy_disable_receive_sa(kay, rxsa);
-				ieee802_1x_kay_deinit_receive_sa(rxsa);
-			}
+			if (is_ki_equal(&rxsa->pkey->key_identifier, ki))
+				ieee802_1x_delete_receive_sa(kay, rxsa);
 		}
 	}
 
@@ -2746,6 +2747,7 @@
 	dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
 			      struct data_key, list) {
 		if (is_ki_equal(&sa_key->key_identifier, ki)) {
+			dl_list_del(&sa_key->list);
 			ieee802_1x_kay_deinit_data_key(sa_key);
 			break;
 		}
@@ -2827,7 +2829,7 @@
 	if (!principal)
 		return -1;
 
-	if (principal->retry_count < MAX_RETRY_CNT) {
+	if (principal->retry_count < MAX_RETRY_CNT || principal->mode == PSK) {
 		ieee802_1x_participant_send_mkpdu(principal);
 		principal->retry_count++;
 	}
@@ -2837,38 +2839,6 @@
 
 
 /**
- * ieee802_1x_kay_cp_conf -
- */
-int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
-			   struct ieee802_1x_cp_conf *pconf)
-{
-	pconf->protect = kay->macsec_protect;
-	pconf->replay_protect = kay->macsec_replay_protect;
-	pconf->validate = kay->macsec_validate;
-
-	return 0;
-}
-
-
-/**
- * ieee802_1x_kay_alloc_cp_sm -
- */
-static struct ieee802_1x_cp_sm *
-ieee802_1x_kay_alloc_cp_sm(struct ieee802_1x_kay *kay)
-{
-	struct ieee802_1x_cp_conf conf;
-
-	os_memset(&conf, 0, sizeof(conf));
-	conf.protect = kay->macsec_protect;
-	conf.replay_protect = kay->macsec_replay_protect;
-	conf.validate = kay->macsec_validate;
-	conf.replay_window = kay->macsec_replay_window;
-
-	return ieee802_1x_cp_sm_init(kay, &conf);
-}
-
-
-/**
  * ieee802_1x_kay_mkpdu_sanity_check -
  *     sanity check specified in clause 11.11.2 of IEEE802.1X-2010
  */
@@ -2896,13 +2866,13 @@
 		return -1;
 	}
 
-	/* MKPDU should not less than 32 octets */
+	/* MKPDU should not be less than 32 octets */
 	mka_msg_len = be_to_host16(eapol_hdr->length);
 	if (mka_msg_len < 32) {
 		wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets");
 		return -1;
 	}
-	/* MKPDU should multiple 4 octets */
+	/* MKPDU should be a multiple of 4 octets */
 	if ((mka_msg_len % 4) != 0) {
 		wpa_printf(MSG_MSGDUMP,
 			   "KaY: MKPDU is not multiple of 4 octets");
@@ -2915,9 +2885,9 @@
 	/* EAPOL-MKA body should comprise basic parameter set and ICV */
 	if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
 		wpa_printf(MSG_ERROR,
-			   "KaY: Received EAPOL-MKA Packet Body Length (%d bytes) is less than the Basic Parameter Set Header Length (%d bytes) + the Basic Parameter Set Body Length (%d bytes) + %d bytes of ICV",
-			   (int) mka_msg_len, (int) MKA_HDR_LEN,
-			   (int) body_len, DEFAULT_ICV_LEN);
+			   "KaY: Received EAPOL-MKA Packet Body Length (%zu bytes) is less than the Basic Parameter Set Header Length (%zu bytes) + the Basic Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
+			   mka_msg_len, MKA_HDR_LEN,
+			   body_len, DEFAULT_ICV_LEN);
 		return -1;
 	}
 
@@ -2948,21 +2918,19 @@
 		wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed");
 		return -1;
 	}
+
 	msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr,
 						 mka_msg_len);
-
-	if (msg_icv) {
-		if (os_memcmp_const(msg_icv, icv,
-				    mka_alg_tbl[kay->mka_algindex].icv_len) !=
-		    0) {
-			wpa_printf(MSG_ERROR,
-				   "KaY: Computed ICV is not equal to Received ICV");
-		return -1;
-		}
-	} else {
+	if (!msg_icv) {
 		wpa_printf(MSG_ERROR, "KaY: No ICV");
 		return -1;
 	}
+	if (os_memcmp_const(msg_icv, icv,
+			    mka_alg_tbl[kay->mka_algindex].icv_len) != 0) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: Computed ICV is not equal to Received ICV");
+		return -1;
+	}
 
 	return 0;
 }
@@ -2978,10 +2946,9 @@
 	struct ieee802_1x_mka_hdr *hdr;
 	size_t body_len;
 	size_t left_len;
-	int body_type;
+	u8 body_type;
 	int i;
 	const u8 *pos;
-	Boolean my_included;
 	Boolean handled[256];
 
 	if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
@@ -3002,28 +2969,27 @@
 	left_len -= body_len + MKA_HDR_LEN;
 
 	/* check i am in the peer's peer list */
-	my_included = ieee802_1x_mka_i_in_peerlist(participant, pos, left_len);
-	if (my_included) {
+	if (ieee802_1x_mka_i_in_peerlist(participant, pos, left_len) &&
+	    !ieee802_1x_kay_is_in_live_peer(participant,
+					    participant->current_peer_id.mi)) {
 		/* accept the peer as live peer */
-		if (!ieee802_1x_kay_is_in_peer(
-			    participant,
-			    participant->current_peer_id.mi)) {
-			if (!ieee802_1x_kay_create_live_peer(
-				    participant,
-				    participant->current_peer_id.mi,
-				    participant->current_peer_id.mn))
-				return -1;
-			ieee802_1x_kay_elect_key_server(participant);
-			ieee802_1x_kay_decide_macsec_use(participant);
-		}
 		if (ieee802_1x_kay_is_in_potential_peer(
 			    participant, participant->current_peer_id.mi)) {
-			ieee802_1x_kay_move_live_peer(
-				participant, participant->current_peer_id.mi,
-				participant->current_peer_id.mn);
-			ieee802_1x_kay_elect_key_server(participant);
-			ieee802_1x_kay_decide_macsec_use(participant);
+			if (!ieee802_1x_kay_move_live_peer(
+				    participant,
+				    participant->current_peer_id.mi,
+				    be_to_host32(participant->
+						 current_peer_id.mn)))
+				return -1;
+		} else if (!ieee802_1x_kay_create_live_peer(
+				   participant, participant->current_peer_id.mi,
+				   be_to_host32(participant->
+						current_peer_id.mn))) {
+				return -1;
 		}
+
+		ieee802_1x_kay_elect_key_server(participant);
+		ieee802_1x_kay_decide_macsec_use(participant);
 	}
 
 	/*
@@ -3034,7 +3000,9 @@
 		handled[i] = FALSE;
 
 	handled[0] = TRUE;
-	while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
+	for (; left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
+	     pos += body_len + MKA_HDR_LEN,
+		     left_len -= body_len + MKA_HDR_LEN) {
 		hdr = (struct ieee802_1x_mka_hdr *) pos;
 		body_len = get_mka_param_body_len(hdr);
 		body_type = get_mka_param_body_type(hdr);
@@ -3044,28 +3012,25 @@
 
 		if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
 			wpa_printf(MSG_ERROR,
-				   "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
-				   (int) left_len, (int) MKA_HDR_LEN,
-				   (int) body_len, DEFAULT_ICV_LEN);
-			goto next_para_set;
+				   "KaY: MKA Peer Packet Body Length (%zu bytes) is less than the Parameter Set Header Length (%zu bytes) + the Parameter Set Body Length (%zu bytes) + %d bytes of ICV",
+				   left_len, MKA_HDR_LEN,
+				   body_len, DEFAULT_ICV_LEN);
+			continue;
 		}
 
 		if (handled[body_type])
-			goto next_para_set;
+			continue;
 
 		handled[body_type] = TRUE;
-		if (mak_body_handler[body_type].body_rx) {
-			mak_body_handler[body_type].body_rx
+		if (body_type < ARRAY_SIZE(mka_body_handler) &&
+		    mka_body_handler[body_type].body_rx) {
+			mka_body_handler[body_type].body_rx
 				(participant, pos, left_len);
 		} else {
 			wpa_printf(MSG_ERROR,
-				   "The type %d not supported in this MKA version %d",
+				   "The type %d is not supported in this MKA version %d",
 				   body_type, MKA_VERSION_ID);
 		}
-
-next_para_set:
-		pos += body_len + MKA_HDR_LEN;
-		left_len -= body_len + MKA_HDR_LEN;
 	}
 
 	kay->active = TRUE;
@@ -3094,10 +3059,10 @@
 	eth_hdr = (struct ieee8023_hdr *) buf;
 	eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
 	if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
-	    ntohs(eapol_hdr->length)) {
+	    be_to_host16(eapol_hdr->length)) {
 		wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)",
 			   (unsigned long) len,
-			   (unsigned long) ntohs(eapol_hdr->length));
+			   (unsigned long) be_to_host16(eapol_hdr->length));
 		return;
 	}
 
@@ -3106,7 +3071,7 @@
 			   eapol_hdr->version);
 		return;
 	}
-	if (ntohs(eth_hdr->ethertype) != ETH_P_PAE ||
+	if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE ||
 	    eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
 		return;
 
@@ -3125,7 +3090,7 @@
  */
 struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
-		    const char *ifname, const u8 *addr)
+		    u16 port, u8 priority, const char *ifname, const u8 *addr)
 {
 	struct ieee802_1x_kay *kay;
 
@@ -3147,8 +3112,8 @@
 
 	os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
 	os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
-	kay->actor_sci.port = 0x0001;
-	kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+	kay->actor_sci.port = host_to_be16(port ? port : 0x0001);
+	kay->actor_priority = priority;
 
 	/* While actor acts as a key server, shall distribute sakey */
 	kay->dist_kn = 1;
@@ -3165,7 +3130,14 @@
 
 	dl_list_init(&kay->participant_list);
 
-	if (policy == DO_NOT_SECURE) {
+	if (policy != DO_NOT_SECURE &&
+	    secy_get_capability(kay, &kay->macsec_capable) < 0) {
+		os_free(kay);
+		return NULL;
+	}
+
+	if (policy == DO_NOT_SECURE ||
+	    kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
 		kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
 		kay->macsec_desired = FALSE;
 		kay->macsec_protect = FALSE;
@@ -3174,25 +3146,27 @@
 		kay->macsec_replay_window = 0;
 		kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
 	} else {
-		kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
 		kay->macsec_desired = TRUE;
 		kay->macsec_protect = TRUE;
+		kay->macsec_encrypt = policy == SHOULD_ENCRYPT;
 		kay->macsec_validate = Strict;
 		kay->macsec_replay_protect = FALSE;
 		kay->macsec_replay_window = 0;
-		kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+		if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF)
+			kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+		else
+			kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
 	}
 
 	wpa_printf(MSG_DEBUG, "KaY: state machine created");
 
 	/* Initialize the SecY must be prio to CP, as CP will control SecY */
 	secy_init_macsec(kay);
-	secy_get_available_transmit_sc(kay, &kay->sc_ch);
 
 	wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
 
 	/* init CP */
-	kay->cp = ieee802_1x_kay_alloc_cp_sm(kay);
+	kay->cp = ieee802_1x_cp_sm_init(kay);
 	if (kay->cp == NULL) {
 		ieee802_1x_kay_deinit(kay);
 		return NULL;
@@ -3314,7 +3288,7 @@
 	default:
 		participant->is_obliged_key_server = FALSE;
 		participant->can_be_key_server = TRUE;
-		participant->is_key_server = FALSE;
+		participant->is_key_server = TRUE;
 		participant->is_elected = FALSE;
 		break;
 	}
@@ -3335,9 +3309,8 @@
 	participant->retry_count = 0;
 	participant->kay = kay;
 
-	if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+	if (!reset_participant_mi(participant))
 		goto fail;
-	participant->mn = 0;
 
 	participant->lrx = FALSE;
 	participant->ltx = FALSE;
@@ -3349,8 +3322,7 @@
 	dl_list_init(&participant->sak_list);
 	participant->new_key = NULL;
 	dl_list_init(&participant->rxsc_list);
-	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci,
-							    kay->sc_ch);
+	participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci);
 	secy_cp_control_protect_frames(kay, kay->macsec_protect);
 	secy_cp_control_replay(kay, kay->macsec_replay_protect,
 			       kay->macsec_replay_window);
@@ -3387,8 +3359,17 @@
 	usecs = os_random() % (MKA_HELLO_TIME * 1000);
 	eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
 			       participant, NULL);
-	participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
-		usecs / 1000000;
+
+	/* Disable MKA lifetime for PSK mode.
+	 * The peer(s) can take a long time to come up, because we
+	 * create a "standby" MKA, and we need it to remain live until
+	 * some peer appears.
+	 */
+	if (mode != PSK) {
+		participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
+			usecs / 1000000;
+	}
+	participant->mode = mode;
 
 	return participant;
 
@@ -3422,6 +3403,7 @@
 		return;
 	}
 
+	eloop_cancel_timeout(ieee802_1x_participant_timer, participant, NULL);
 	dl_list_del(&participant->list);
 
 	/* remove live peer */
@@ -3445,17 +3427,16 @@
 		sak = dl_list_entry(participant->sak_list.next,
 				    struct data_key, list);
 		dl_list_del(&sak->list);
-		os_free(sak->key);
-		os_free(sak);
+		ieee802_1x_kay_deinit_data_key(sak);
 	}
 	while (!dl_list_empty(&participant->rxsc_list)) {
 		rxsc = dl_list_entry(participant->rxsc_list.next,
 				     struct receive_sc, list);
-		secy_delete_receive_sc(kay, rxsc);
 		ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
+		secy_delete_receive_sc(kay, rxsc);
 	}
-	secy_delete_transmit_sc(kay, participant->txsc);
 	ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
+	secy_delete_transmit_sc(kay, participant->txsc);
 
 	os_memset(&participant->cak, 0, sizeof(participant->cak));
 	os_memset(&participant->kek, 0, sizeof(participant->kek));
@@ -3510,14 +3491,16 @@
  * ieee802_1x_kay_change_cipher_suite -
  */
 int
-ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, int cs_index)
+ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
+				   unsigned int cs_index)
 {
 	struct ieee802_1x_mka_participant *participant;
+	enum macsec_cap secy_cap;
 
 	if (!kay)
 		return -1;
 
-	if ((unsigned int) cs_index >= CS_TABLE_SIZE) {
+	if (cs_index >= CS_TABLE_SIZE) {
 		wpa_printf(MSG_ERROR,
 			   "KaY: Configured cipher suite index is out of range");
 		return -1;
@@ -3531,6 +3514,12 @@
 	kay->macsec_csindex = cs_index;
 	kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
 
+	if (secy_get_capability(kay, &secy_cap) < 0)
+		return -3;
+
+	if (kay->macsec_capable > secy_cap)
+		kay->macsec_capable = secy_cap;
+
 	participant = ieee802_1x_kay_get_principal_participant(kay);
 	if (participant) {
 		wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
@@ -3539,3 +3528,51 @@
 
 	return 0;
 }
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details
+ * @sm: Pointer to KaY allocated with ieee802_1x_kay_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query KAY status information. This function fills in a text area with current
+ * status information. If the buffer (buf) is not large enough, status
+ * information will be truncated to fit the buffer.
+ */
+int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
+			      size_t buflen)
+{
+	int len;
+
+	if (!kay)
+		return 0;
+
+	len = os_snprintf(buf, buflen,
+			  "PAE KaY status=%s\n"
+			  "Authenticated=%s\n"
+			  "Secured=%s\n"
+			  "Failed=%s\n"
+			  "Actor Priority=%u\n"
+			  "Key Server Priority=%u\n"
+			  "Is Key Server=%s\n"
+			  "Number of Keys Distributed=%u\n"
+			  "Number of Keys Received=%u\n",
+			  kay->active ? "Active" : "Not-Active",
+			  kay->authenticated ? "Yes" : "No",
+			  kay->secured ? "Yes" : "No",
+			  kay->failed ? "Yes" : "No",
+			  kay->actor_priority,
+			  kay->key_server_priority,
+			  kay->is_key_server ? "Yes" : "No",
+			  kay->dist_kn - 1,
+			  kay->rcvd_keys);
+	if (os_snprintf_error(buflen, len))
+		return 0;
+
+	return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index 064417e..8f394fd 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -14,9 +14,8 @@
 #include "common/ieee802_1x_defs.h"
 
 struct macsec_init_params;
-struct ieee802_1x_cp_conf;
 
-#define MI_LEN			12
+#define MI_LEN			12  /* 96-bit Member Identifier */
 #define MAX_KEY_LEN		32  /* 32 bytes, 256 bits */
 #define MAX_CKN_LEN		32  /* 32 bytes, 256 bits */
 
@@ -25,6 +24,12 @@
 #define MKA_LIFE_TIME		6000
 #define MKA_SAK_RETIRE_TIME	3000
 
+/**
+ * struct ieee802_1x_mka_ki - Key Identifier (KI)
+ * @mi: Key Server's Member Identifier
+ * @kn: Key Number, assigned by the Key Server
+ * IEEE 802.1X-2010 9.8 SAK generation, distribution, and selection
+ */
 struct ieee802_1x_mka_ki {
 	u8 mi[MI_LEN];
 	u32 kn;
@@ -32,7 +37,7 @@
 
 struct ieee802_1x_mka_sci {
 	u8 addr[ETH_ALEN];
-	u16 port;
+	be16 port;
 };
 
 struct mka_key {
@@ -48,8 +53,84 @@
 enum mka_created_mode {
 	PSK,
 	EAP_EXCHANGE,
-	DISTRIBUTED,
-	CACHED,
+};
+
+struct data_key {
+	u8 *key;
+	int key_len;
+	struct ieee802_1x_mka_ki key_identifier;
+	enum confidentiality_offset confidentiality_offset;
+	u8 an;
+	Boolean transmits;
+	Boolean receives;
+	struct os_time created_time;
+	u32 next_pn;
+
+	/* not defined data */
+	Boolean rx_latest;
+	Boolean tx_latest;
+
+	int user;
+
+	struct dl_list list;
+};
+
+/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sc {
+	struct ieee802_1x_mka_sci sci; /* const SCI sci */
+	Boolean transmitting; /* bool transmitting (read only) */
+
+	struct os_time created_time; /* Time createdTime */
+
+	u8 encoding_sa; /* AN encodingSA (read only) */
+	u8 enciphering_sa; /* AN encipheringSA (read only) */
+
+	/* not defined data */
+	struct dl_list list;
+	struct dl_list sa_list;
+};
+
+/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sa {
+	Boolean in_use; /* bool inUse (read only) */
+	u32 next_pn; /* PN nextPN (read only) */
+	struct os_time created_time; /* Time createdTime */
+
+	Boolean enable_transmit; /* bool EnableTransmit */
+
+	u8 an;
+	Boolean confidentiality;
+	struct data_key *pkey;
+
+	struct transmit_sc *sc;
+	struct dl_list list; /* list entry in struct transmit_sc::sa_list */
+};
+
+/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sc {
+	struct ieee802_1x_mka_sci sci; /* const SCI sci */
+	Boolean receiving; /* bool receiving (read only) */
+
+	struct os_time created_time; /* Time createdTime */
+
+	struct dl_list list;
+	struct dl_list sa_list;
+};
+
+/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sa {
+	Boolean enable_receive; /* bool enableReceive */
+	Boolean in_use; /* bool inUse (read only) */
+
+	u32 next_pn; /* PN nextPN (read only) */
+	u32 lowest_pn; /* PN lowestPN (read only) */
+	u8 an;
+	struct os_time created_time;
+
+	struct data_key *pkey;
+	struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
+
+	struct dl_list list;
 };
 
 struct ieee802_1x_kay_ctx {
@@ -59,34 +140,30 @@
 	/* abstract wpa driver interface */
 	int (*macsec_init)(void *ctx, struct macsec_init_params *params);
 	int (*macsec_deinit)(void *ctx);
+	int (*macsec_get_capability)(void *priv, enum macsec_cap *cap);
 	int (*enable_protect_frames)(void *ctx, Boolean enabled);
+	int (*enable_encrypt)(void *ctx, Boolean enabled);
 	int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window);
-	int (*set_current_cipher_suite)(void *ctx, const u8 *cs, size_t cs_len);
+	int (*set_current_cipher_suite)(void *ctx, u64 cs);
 	int (*enable_controlled_port)(void *ctx, Boolean enabled);
-	int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an,
-				     u32 *lowest_pn);
-	int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an,
-				    u32 *next_pn);
-	int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn);
-	int (*get_available_receive_sc)(void *ctx, u32 *channel);
-	int (*create_receive_sc)(void *ctx, u32 channel,
-				 struct ieee802_1x_mka_sci *sci,
+	int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
+	int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+	int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+	int (*create_receive_sc)(void *ctx, struct receive_sc *sc,
 				 enum validate_frames vf,
 				 enum confidentiality_offset co);
-	int (*delete_receive_sc)(void *ctx, u32 channel);
-	int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn,
-				 const u8 *sak);
-	int (*enable_receive_sa)(void *ctx, u32 channel, u8 an);
-	int (*disable_receive_sa)(void *ctx, u32 channel, u8 an);
-	int (*get_available_transmit_sc)(void *ctx, u32 *channel);
-	int (*create_transmit_sc)(void *ctx, u32 channel,
-				  const struct ieee802_1x_mka_sci *sci,
+	int (*delete_receive_sc)(void *ctx, struct receive_sc *sc);
+	int (*create_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*delete_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*enable_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*disable_receive_sa)(void *ctx, struct receive_sa *sa);
+	int (*create_transmit_sc)(void *ctx, struct transmit_sc *sc,
 				  enum confidentiality_offset co);
-	int (*delete_transmit_sc)(void *ctx, u32 channel);
-	int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn,
-				  Boolean confidentiality, const u8 *sak);
-	int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an);
-	int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an);
+	int (*delete_transmit_sc)(void *ctx, struct transmit_sc *sc);
+	int (*create_transmit_sa)(void *ctx, struct transmit_sa *sa);
+	int (*delete_transmit_sa)(void *ctx, struct transmit_sa *sa);
+	int (*enable_transmit_sa)(void *ctx, struct transmit_sa *sa);
+	int (*disable_transmit_sa)(void *ctx, struct transmit_sa *sa);
 };
 
 struct ieee802_1x_kay {
@@ -105,6 +182,7 @@
 	enum macsec_cap macsec_capable;
 	Boolean macsec_desired;
 	Boolean macsec_protect;
+	Boolean macsec_encrypt;
 	Boolean macsec_replay_protect;
 	u32 macsec_replay_window;
 	enum validate_frames macsec_validate;
@@ -126,16 +204,16 @@
 	Boolean is_obliged_key_server;
 	char if_name[IFNAMSIZ];
 
-	int macsec_csindex;  /*  MACsec cipher suite table index */
+	unsigned int macsec_csindex;  /* MACsec cipher suite table index */
 	int mka_algindex;  /* MKA alg table index */
 
 	u32 dist_kn;
+	u32 rcvd_keys;
 	u8 dist_an;
 	time_t dist_time;
 
 	u8 mka_version;
 	u8 algo_agility[4];
-	u32 sc_ch;
 
 	u32 pn_exhaustion;
 	Boolean port_enable;
@@ -154,9 +232,11 @@
 };
 
 
+u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci);
+
 struct ieee802_1x_kay *
 ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
-		    const char *ifname, const u8 *addr);
+		    u16 port, u8 priority, const char *ifname, const u8 *addr);
 void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
 
 struct ieee802_1x_mka_participant *
@@ -171,7 +251,7 @@
 				    Boolean status);
 int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay);
 int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
-				       int cs_index);
+				       unsigned int cs_index);
 
 int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
 				      struct ieee802_1x_mka_ki *lki, u8 lan,
@@ -188,7 +268,7 @@
 int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
 				 struct ieee802_1x_mka_ki *lki);
 int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay);
-int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
-			   struct ieee802_1x_cp_conf *pconf);
+int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
+			      size_t buflen);
 
 #endif /* IEEE802_1X_KAY_H */
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
index bdad3a5..bc522d8 100644
--- a/src/pae/ieee802_1x_kay_i.h
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -38,7 +38,7 @@
 
 struct ieee802_1x_mka_peer_id {
 	u8 mi[MI_LEN];
-	u32 mn;
+	be32 mn;
 };
 
 struct ieee802_1x_kay_peer {
@@ -49,105 +49,13 @@
 	Boolean is_key_server;
 	u8 key_server_priority;
 	Boolean macsec_desired;
-	enum macsec_cap macsec_capbility;
+	enum macsec_cap macsec_capability;
 	Boolean sak_used;
 	struct dl_list list;
 };
 
-struct key_conf {
-	u8 *key;
-	struct ieee802_1x_mka_ki ki;
-	enum confidentiality_offset offset;
-	u8 an;
-	Boolean tx;
-	Boolean rx;
-	int key_len; /* unit: byte */
-};
-
-struct data_key {
-	u8 *key;
-	int key_len;
-	struct ieee802_1x_mka_ki key_identifier;
-	enum confidentiality_offset confidentiality_offset;
-	u8 an;
-	Boolean transmits;
-	Boolean receives;
-	struct os_time created_time;
-	u32 next_pn;
-
-	/* not defined data */
-	Boolean rx_latest;
-	Boolean tx_latest;
-
-	int user;  /* FIXME: to indicate if it can be delete safely */
-
-	struct dl_list list;
-};
-
-/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct transmit_sc {
-	struct ieee802_1x_mka_sci sci; /* const SCI sci */
-	Boolean transmitting; /* bool transmitting (read only) */
-
-	struct os_time created_time; /* Time createdTime */
-
-	u8 encoding_sa; /* AN encodingSA (read only) */
-	u8 enciphering_sa; /* AN encipheringSA (read only) */
-
-	/* not defined data */
-	unsigned int channel;
-
-	struct dl_list list;
-	struct dl_list sa_list;
-};
-
-/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct transmit_sa {
-	Boolean in_use; /* bool inUse (read only) */
-	u32 next_pn; /* PN nextPN (read only) */
-	struct os_time created_time; /* Time createdTime */
-
-	Boolean enable_transmit; /* bool EnableTransmit */
-
-	u8 an;
-	Boolean confidentiality;
-	struct data_key *pkey;
-
-	struct transmit_sc *sc;
-	struct dl_list list; /* list entry in struct transmit_sc::sa_list */
-};
-
-/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct receive_sc {
-	struct ieee802_1x_mka_sci sci; /* const SCI sci */
-	Boolean receiving; /* bool receiving (read only) */
-
-	struct os_time created_time; /* Time createdTime */
-
-	unsigned int channel;
-
-	struct dl_list list;
-	struct dl_list sa_list;
-};
-
-/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
-struct receive_sa {
-	Boolean enable_receive; /* bool enableReceive */
-	Boolean in_use; /* bool inUse (read only) */
-
-	u32 next_pn; /* PN nextPN (read only) */
-	u32 lowest_pn; /* PN lowestPN (read only) */
-	u8 an;
-	struct os_time created_time;
-
-	struct data_key *pkey;
-	struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
-
-	struct dl_list list;
-};
-
 struct macsec_ciphersuite {
-	u8 id[CS_ID_LEN];
+	u64 id;
 	char name[32];
 	enum macsec_cap capable;
 	int sak_len; /* unit: byte */
@@ -185,6 +93,7 @@
 	Boolean active;
 	Boolean participant;
 	Boolean retain;
+	enum mka_created_mode mode;
 
 	enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
 
@@ -241,153 +150,213 @@
 
 struct ieee802_1x_mka_hdr {
 	/* octet 1 */
-	u32 type:8;
+	u8 type;
 	/* octet 2 */
-	u32 reserve:8;
+	u8 reserve;
 	/* octet 3 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 length:4;
-	u32 reserve1:4;
+	u8 length:4;
+	u8 reserve1:4;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 reserve1:4;
-	u32 length:4;
+	u8 reserve1:4;
+	u8 length:4;
 #else
 #error "Please fix <bits/endian.h>"
 #endif
 	/* octet 4 */
-	u32 length1:8;
+	u8 length1;
 };
 
 #define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
 
+/**
+ * struct ieee802_1x_mka_basic_body - Basic Parameter Set (Figure 11-8)
+ * @version: MKA Version Identifier
+ * @priority: Key Server Priority
+ * @length: Parameter set body length
+ * @macsec_capability: MACsec capability, as defined in ieee802_1x_defs.h
+ * @macsec_desired: the participant wants MACsec to be used to protect frames
+ *	(9.6.1)
+ * @key_server: the participant has not decided that another participant is or
+ *	will be the key server (9.5.1)
+ * @length1: Parameter set body length (cont)
+ * @actor_mi: Actor's Member Identifier
+ * @actor_mn: Actor's Message Number
+ * @algo_agility: Algorithm Agility parameter
+ * @ckn: CAK Name
+ */
 struct ieee802_1x_mka_basic_body {
 	/* octet 1 */
-	u32 version:8;
+	u8 version;
 	/* octet 2 */
-	u32 priority:8;
+	u8 priority;
 	/* octet 3 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 length:4;
-	u32 macsec_capbility:2;
-	u32 macsec_desired:1;
-	u32 key_server:1;
+	u8 length:4;
+	u8 macsec_capability:2;
+	u8 macsec_desired:1;
+	u8 key_server:1;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 key_server:1;
-	u32 macsec_desired:1;
-	u32 macsec_capbility:2;
-	u32 length:4;
+	u8 key_server:1;
+	u8 macsec_desired:1;
+	u8 macsec_capability:2;
+	u8 length:4;
 #endif
 	/* octet 4 */
-	u32 length1:8;
+	u8 length1;
 
 	struct ieee802_1x_mka_sci actor_sci;
 	u8 actor_mi[MI_LEN];
-	u32 actor_mn;
+	be32 actor_mn;
 	u8 algo_agility[4];
 
 	/* followed by CAK Name*/
 	u8 ckn[0];
 };
 
+/**
+ * struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List
+ *	parameter sets (Figure 11-9)
+ * @type: Parameter set type (1 or 2)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ * @peer: array of (MI, MN) pairs
+ */
 struct ieee802_1x_mka_peer_body {
 	/* octet 1 */
-	u32 type:8;
+	u8 type;
 	/* octet 2 */
-	u32 reserve:8;
+	u8 reserve;
 	/* octet 3 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 length:4;
-	u32 reserve1:4;
+	u8 length:4;
+	u8 reserve1:4;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 reserve1:4;
-	u32 length:4;
+	u8 reserve1:4;
+	u8 length:4;
 #endif
 	/* octet 4 */
-	u32 length1:8;
+	u8 length1;
 
 	u8 peer[0];
 	/* followed by Peers */
 };
 
+/**
+ * struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure
+ *	11-10)
+ * @type: MKA message type
+ * @lan: latest key AN
+ * @ltx: latest key TX
+ * @lrx: latest key RX
+ * @oan: old key AN
+ * @otx: old key TX
+ * @orx: old key RX
+ * @ptx: plain TX, ie protectFrames is False
+ * @prx: plain RX, ie validateFrames is not Strict
+ * @delay_protect: True if LPNs are being reported sufficiently frequently to
+ *	allow the recipient to provide data delay protection. If False, the LPN
+ *	can be reported as zero.
+ * @lsrv_mi: latest key server MI
+ * @lkn: latest key number (together with MI, form the KI)
+ * @llpn: latest lowest acceptable PN (LPN)
+ * @osrv_mi: old key server MI
+ * @okn: old key number (together with MI, form the KI)
+ * @olpn: old lowest acceptable PN (LPN)
+ */
 struct ieee802_1x_mka_sak_use_body {
 	/* octet 1 */
-	u32 type:8;
+	u8 type;
 	/* octet 2 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 orx:1;
-	u32 otx:1;
-	u32 oan:2;
-	u32 lrx:1;
-	u32 ltx:1;
-	u32 lan:2;
+	u8 orx:1;
+	u8 otx:1;
+	u8 oan:2;
+	u8 lrx:1;
+	u8 ltx:1;
+	u8 lan:2;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 lan:2;
-	u32 ltx:1;
-	u32 lrx:1;
-	u32 oan:2;
-	u32 otx:1;
-	u32 orx:1;
+	u8 lan:2;
+	u8 ltx:1;
+	u8 lrx:1;
+	u8 oan:2;
+	u8 otx:1;
+	u8 orx:1;
 #endif
 
 	/* octet 3 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 length:4;
-	u32 delay_protect:1;
-	u32 reserve:1;
-	u32 prx:1;
-	u32 ptx:1;
+	u8 length:4;
+	u8 delay_protect:1;
+	u8 reserve:1;
+	u8 prx:1;
+	u8 ptx:1;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 ptx:1;
-	u32 prx:1;
-	u32 reserve:1;
-	u32 delay_protect:1;
-	u32 length:4;
+	u8 ptx:1;
+	u8 prx:1;
+	u8 reserve:1;
+	u8 delay_protect:1;
+	u8 length:4;
 #endif
 
 	/* octet 4 */
-	u32 length1:8;
+	u8 length1;
 
 	/* octet 5 - 16 */
 	u8 lsrv_mi[MI_LEN];
 	/* octet 17 - 20 */
-	u32 lkn;
+	be32 lkn;
 	/* octet 21 - 24 */
-	u32 llpn;
+	be32 llpn;
 
 	/* octet 25 - 36 */
 	u8 osrv_mi[MI_LEN];
 	/* octet 37 - 40 */
-	u32 okn;
+	be32 okn;
 	/* octet 41 - 44 */
-	u32 olpn;
+	be32 olpn;
 };
 
-
+/**
+ * struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set
+ *	(GCM-AES-128, Figure 11-11)
+ * @type: Parameter set type (4)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ *           Total parameter body length values:
+ *            -  0 for plain text
+ *            - 28 for GCM-AES-128
+ *            - 36 or more for other cipher suites
+ * @confid_offset: confidentiality offset, as defined in ieee802_1x_defs.h
+ * @dan: distributed AN (0 for plain text)
+ * @kn: Key Number
+ * @sak: AES Key Wrap of SAK (see 9.8)
+ */
 struct ieee802_1x_mka_dist_sak_body {
 	/* octet 1 */
-	u32 type:8;
+	u8 type;
 	/* octet 2 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 reserve:4;
-	u32 confid_offset:2;
-	u32 dan:2;
+	u8 reserve:4;
+	u8 confid_offset:2;
+	u8 dan:2;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 dan:2;
-	u32 confid_offset:2;
-	u32 reserve:4;
+	u8 dan:2;
+	u8 confid_offset:2;
+	u8 reserve:4;
 #endif
 	/* octet 3 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 length:4;
-	u32 reserve1:4;
+	u8 length:4;
+	u8 reserve1:4;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 reserve1:4;
-	u32 length:4;
+	u8 reserve1:4;
+	u8 length:4;
 #endif
 	/* octet 4 */
-	u32 length1:8;
+	u8 length1;
 	/* octet 5 - 8 */
-	u32 kn;
+	be32 kn;
 
 	/* for GCM-AES-128: octet 9-32: SAK
 	 * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK
@@ -395,22 +364,57 @@
 	u8 sak[0];
 };
 
+/**
+ * struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure
+ *	11-13)
+ * @type: Parameter set type (5)
+ * @length: Parameter set body length
+ * @length1: Parameter set body length (cont)
+ *           Total parameter body length values:
+ *            -  0 for plain text
+ *            - 28 for GCM-AES-128
+ *            - 36 or more for other cipher suites
+ * @cak: AES Key Wrap of CAK (see 9.8)
+ * @ckn: CAK Name
+ */
+struct ieee802_1x_mka_dist_cak_body {
+	/* octet 1 */
+	u8 type;
+	/* octet 2 */
+	u8 reserve;
+	/* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	u8 length:4;
+	u8 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+	u8 reserve1:4;
+	u8 length:4;
+#endif
+	/* octet 4 */
+	u8 length1;
+
+	/* octet 5 - 28 */
+	u8 cak[24];
+
+	/* followed by CAK Name, 29- */
+	u8 ckn[0];
+};
 
 struct ieee802_1x_mka_icv_body {
 	/* octet 1 */
-	u32 type:8;
+	u8 type;
 	/* octet 2 */
-	u32 reserve:8;
+	u8 reserve;
 	/* octet 3 */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
-	u32 length:4;
-	u32 reserve1:4;
+	u8 length:4;
+	u8 reserve1:4;
 #elif __BYTE_ORDER == __BIG_ENDIAN
-	u32 reserve1:4;
-	u32 length:4;
+	u8 reserve1:4;
+	u8 length:4;
 #endif
 	/* octet 4 */
-	u32 length1:8;
+	u8 length1;
 
 	/* octet 5 - */
 	u8 icv[0];
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
index fbe05dc..ab5339b 100644
--- a/src/pae/ieee802_1x_secy_ops.c
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -45,6 +45,26 @@
 }
 
 
+int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->enable_encrypt) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy enable_encrypt operation not supported");
+		return -1;
+	}
+
+	return ops->enable_encrypt(ops->ctx, enabled);
+}
+
+
 int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win)
 {
 	struct ieee802_1x_kay_ctx *ops;
@@ -65,8 +85,7 @@
 }
 
 
-int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
-					 const u8 *cs, size_t cs_len)
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs)
 {
 	struct ieee802_1x_kay_ctx *ops;
 
@@ -82,7 +101,7 @@
 		return -1;
 	}
 
-	return ops->set_current_cipher_suite(ops->ctx, cs, cs_len);
+	return ops->set_current_cipher_suite(ops->ctx, cs);
 }
 
 
@@ -114,6 +133,26 @@
 }
 
 
+int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->macsec_get_capability) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy macsec_get_capability operation not supported");
+		return -1;
+	}
+
+	return ops->macsec_get_capability(ops->ctx, cap);
+}
+
+
 int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
 			       struct receive_sa *rxsa)
 {
@@ -131,10 +170,7 @@
 		return -1;
 	}
 
-	return ops->get_receive_lowest_pn(ops->ctx,
-					rxsa->sc->channel,
-					rxsa->an,
-					&rxsa->lowest_pn);
+	return ops->get_receive_lowest_pn(ops->ctx, rxsa);
 }
 
 
@@ -155,10 +191,7 @@
 		return -1;
 	}
 
-	return ops->get_transmit_next_pn(ops->ctx,
-					txsa->sc->channel,
-					txsa->an,
-					&txsa->next_pn);
+	return ops->get_transmit_next_pn(ops->ctx, txsa);
 }
 
 
@@ -179,30 +212,7 @@
 		return -1;
 	}
 
-	return ops->set_transmit_next_pn(ops->ctx,
-					txsa->sc->channel,
-					txsa->an,
-					txsa->next_pn);
-}
-
-
-int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel)
-{
-	struct ieee802_1x_kay_ctx *ops;
-
-	if (!kay) {
-		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
-		return -1;
-	}
-
-	ops = kay->ctx;
-	if (!ops || !ops->get_available_receive_sc) {
-		wpa_printf(MSG_ERROR,
-			   "KaY: secy get_available_receive_sc operation not supported");
-		return -1;
-	}
-
-	return ops->get_available_receive_sc(ops->ctx, channel);
+	return ops->set_transmit_next_pn(ops->ctx, txsa);
 }
 
 
@@ -222,8 +232,7 @@
 		return -1;
 	}
 
-	return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci,
-				      kay->vf, kay->co);
+	return ops->create_receive_sc(ops->ctx, rxsc, kay->vf, kay->co);
 }
 
 
@@ -243,7 +252,7 @@
 		return -1;
 	}
 
-	return ops->delete_receive_sc(ops->ctx, rxsc->channel);
+	return ops->delete_receive_sc(ops->ctx, rxsc);
 }
 
 
@@ -263,8 +272,27 @@
 		return -1;
 	}
 
-	return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an,
-				      rxsa->lowest_pn, rxsa->pkey->key);
+	return ops->create_receive_sa(ops->ctx, rxsa);
+}
+
+
+int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !rxsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->delete_receive_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy delete_receive_sa operation not supported");
+		return -1;
+	}
+
+	return ops->delete_receive_sa(ops->ctx, rxsa);
 }
 
 
@@ -286,7 +314,7 @@
 
 	rxsa->enable_receive = TRUE;
 
-	return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+	return ops->enable_receive_sa(ops->ctx, rxsa);
 }
 
 
@@ -308,27 +336,7 @@
 
 	rxsa->enable_receive = FALSE;
 
-	return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
-}
-
-
-int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel)
-{
-	struct ieee802_1x_kay_ctx *ops;
-
-	if (!kay) {
-		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
-		return -1;
-	}
-
-	ops = kay->ctx;
-	if (!ops || !ops->get_available_transmit_sc) {
-		wpa_printf(MSG_ERROR,
-			   "KaY: secy get_available_transmit_sc operation not supported");
-		return -1;
-	}
-
-	return ops->get_available_transmit_sc(ops->ctx, channel);
+	return ops->disable_receive_sa(ops->ctx, rxsa);
 }
 
 
@@ -349,8 +357,7 @@
 		return -1;
 	}
 
-	return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci,
-				       kay->co);
+	return ops->create_transmit_sc(ops->ctx, txsc, kay->co);
 }
 
 
@@ -371,7 +378,7 @@
 		return -1;
 	}
 
-	return ops->delete_transmit_sc(ops->ctx, txsc->channel);
+	return ops->delete_transmit_sc(ops->ctx, txsc);
 }
 
 
@@ -392,9 +399,28 @@
 		return -1;
 	}
 
-	return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an,
-					txsa->next_pn, txsa->confidentiality,
-					txsa->pkey->key);
+	return ops->create_transmit_sa(ops->ctx, txsa);
+}
+
+
+int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa)
+{
+	struct ieee802_1x_kay_ctx *ops;
+
+	if (!kay || !txsa) {
+		wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+		return -1;
+	}
+
+	ops = kay->ctx;
+	if (!ops || !ops->delete_transmit_sa) {
+		wpa_printf(MSG_ERROR,
+			   "KaY: secy delete_transmit_sa operation not supported");
+		return -1;
+	}
+
+	return ops->delete_transmit_sa(ops->ctx, txsa);
 }
 
 
@@ -417,7 +443,7 @@
 
 	txsa->enable_transmit = TRUE;
 
-	return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+	return ops->enable_transmit_sa(ops->ctx, txsa);
 }
 
 
@@ -440,7 +466,7 @@
 
 	txsa->enable_transmit = FALSE;
 
-	return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+	return ops->disable_transmit_sa(ops->ctx, txsa);
 }
 
 
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
index 295b823..9fb29c3 100644
--- a/src/pae/ieee802_1x_secy_ops.h
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -13,10 +13,6 @@
 #include "common/ieee802_1x_defs.h"
 
 struct ieee802_1x_kay_conf;
-struct receive_sa;
-struct transmit_sa;
-struct receive_sc;
-struct transmit_sc;
 
 int secy_init_macsec(struct ieee802_1x_kay *kay);
 int secy_deinit_macsec(struct ieee802_1x_kay *kay);
@@ -25,35 +21,37 @@
 int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
 				    enum validate_frames vf);
 int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag);
+int secy_cp_control_encrypt(struct ieee802_1x_kay *kay, Boolean enabled);
 int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win);
-int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
-					 const u8 *cs, size_t cs_len);
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay, u64 cs);
 int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
 					   enum confidentiality_offset co);
 int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag);
 
 /****** KaY -> SecY *******/
+int secy_get_capability(struct ieee802_1x_kay *kay, enum macsec_cap *cap);
 int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
 			       struct receive_sa *rxsa);
 int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
 			      struct transmit_sa *txsa);
 int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
 			      struct transmit_sa *txsa);
-int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel);
 int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
 int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_delete_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
 int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
 int secy_disable_receive_sa(struct ieee802_1x_kay *kay,
 			    struct receive_sa *rxsa);
 
-int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel);
 int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
 			    struct transmit_sc *txsc);
 int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
 			    struct transmit_sc *txsc);
 int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
 			    struct transmit_sa *txsa);
+int secy_delete_transmit_sa(struct ieee802_1x_kay *kay,
+			    struct transmit_sa *txsa);
 int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
 			    struct transmit_sa *txsa);
 int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
diff --git a/src/radius/radius.c b/src/radius/radius.c
index da978db..9ddc19b 100644
--- a/src/radius/radius.c
+++ b/src/radius/radius.c
@@ -210,7 +210,7 @@
 	{ RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
 	  RADIUS_ATTR_TEXT },
 	{ RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
-	{ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
+	{ RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords",
 	  RADIUS_ATTR_INT32 },
 	{ RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
 	  RADIUS_ATTR_INT32 },
@@ -538,7 +538,8 @@
 
 
 int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
-			      size_t secret_len)
+			      size_t secret_len,
+			      int require_message_authenticator)
 {
 	const u8 *addr[4];
 	size_t len[4];
@@ -577,7 +578,11 @@
 	}
 
 	if (attr == NULL) {
-		/* Message-Authenticator is MAY; not required */
+		if (require_message_authenticator) {
+			wpa_printf(MSG_WARNING,
+				   "Missing Message-Authenticator attribute in RADIUS message");
+			return 1;
+		}
 		return 0;
 	}
 
@@ -818,8 +823,9 @@
 		os_memcpy(msg->hdr->authenticator, req_auth,
 			  sizeof(msg->hdr->authenticator));
 	}
-	hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
-		 wpabuf_len(msg->buf), auth);
+	if (hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+		     wpabuf_len(msg->buf), auth) < 0)
+		return 1;
 	os_memcpy(attr + 1, orig, MD5_MAC_LEN);
 	if (req_auth) {
 		os_memcpy(msg->hdr->authenticator, orig_authenticator,
@@ -862,8 +868,8 @@
 	len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
 	addr[3] = secret;
 	len[3] = secret_len;
-	md5_vector(4, addr, len, hash);
-	if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+	if (md5_vector(4, addr, len, hash) < 0 ||
+	    os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
 		wpa_printf(MSG_INFO, "Response Authenticator invalid!");
 		return 1;
 	}
@@ -1017,7 +1023,10 @@
 			addr[1] = pos - MD5_MAC_LEN;
 			elen[1] = MD5_MAC_LEN;
 		}
-		md5_vector(first ? 3 : 2, addr, elen, hash);
+		if (md5_vector(first ? 3 : 2, addr, elen, hash) < 0) {
+			os_free(plain);
+			return NULL;
+		}
 		first = 0;
 
 		for (i = 0; i < MD5_MAC_LEN; i++)
@@ -1199,8 +1208,10 @@
 	vhdr = (struct radius_attr_vendor *) pos;
 	vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
 	pos = (u8 *) (vhdr + 1);
-	if (os_get_random((u8 *) &salt, sizeof(salt)) < 0)
+	if (os_get_random((u8 *) &salt, sizeof(salt)) < 0) {
+		os_free(buf);
 		return 0;
+	}
 	salt |= 0x8000;
 	WPA_PUT_BE16(pos, salt);
 	pos += 2;
diff --git a/src/radius/radius.h b/src/radius/radius.h
index cba2b91..cd510d2 100644
--- a/src/radius/radius.h
+++ b/src/radius/radius.h
@@ -242,7 +242,8 @@
 int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
 			       size_t secret_len);
 int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
-			       size_t secret_len);
+			      size_t secret_len,
+			      int require_message_authenticator);
 struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
 					     const u8 *data, size_t data_len);
 struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index a4edd5f..06c804d 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -1636,11 +1636,16 @@
 int radius_client_get_mib(struct radius_client_data *radius, char *buf,
 			  size_t buflen)
 {
-	struct hostapd_radius_servers *conf = radius->conf;
+	struct hostapd_radius_servers *conf;
 	int i;
 	struct hostapd_radius_server *serv;
 	int count = 0;
 
+	if (!radius)
+		return 0;
+
+	conf = radius->conf;
+
 	if (conf->auth_servers) {
 		for (i = 0; i < conf->num_auth_servers; i++) {
 			serv = &conf->auth_servers[i];
diff --git a/src/radius/radius_das.c b/src/radius/radius_das.c
index b7d991b..8a3d7e0 100644
--- a/src/radius/radius_das.c
+++ b/src/radius/radius_das.c
@@ -23,6 +23,7 @@
 	struct hostapd_ip_addr client_addr;
 	unsigned int time_window;
 	int require_event_timestamp;
+	int require_message_authenticator;
 	void *ctx;
 	enum radius_das_res (*disconnect)(void *ctx,
 					  struct radius_das_attrs *attr);
@@ -234,9 +235,11 @@
 		radius_msg_dump(msg);
 
 	if (radius_msg_verify_das_req(msg, das->shared_secret,
-				       das->shared_secret_len)) {
-		wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet "
-			   "from %s:%d - drop", abuf, from_port);
+				       das->shared_secret_len,
+				       das->require_message_authenticator)) {
+		wpa_printf(MSG_DEBUG,
+			   "DAS: Invalid authenticator or Message-Authenticator in packet from %s:%d - drop",
+			   abuf, from_port);
 		goto fail;
 	}
 
@@ -362,6 +365,8 @@
 
 	das->time_window = conf->time_window;
 	das->require_event_timestamp = conf->require_event_timestamp;
+	das->require_message_authenticator =
+		conf->require_message_authenticator;
 	das->ctx = conf->ctx;
 	das->disconnect = conf->disconnect;
 
diff --git a/src/radius/radius_das.h b/src/radius/radius_das.h
index ce731d4..9863fdc 100644
--- a/src/radius/radius_das.h
+++ b/src/radius/radius_das.h
@@ -44,6 +44,7 @@
 	const struct hostapd_ip_addr *client_addr;
 	unsigned int time_window;
 	int require_event_timestamp;
+	int require_message_authenticator;
 	void *ctx;
 	enum radius_das_res (*disconnect)(void *ctx,
 					  struct radius_das_attrs *attr);
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index 744283c..e8bef45 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -26,9 +26,14 @@
 #define RADIUS_SESSION_TIMEOUT 60
 
 /**
+ * RADIUS_SESSION_MAINTAIN - Completed session expiration timeout in seconds
+ */
+#define RADIUS_SESSION_MAINTAIN 5
+
+/**
  * RADIUS_MAX_SESSION - Maximum number of active sessions
  */
-#define RADIUS_MAX_SESSION 100
+#define RADIUS_MAX_SESSION 1000
 
 /**
  * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages
@@ -1057,7 +1062,7 @@
 			     "message");
 		return -1;
 	}
-		      
+
 	eap = radius_msg_get_eap(msg);
 	if (eap == NULL && sess->macacl) {
 		reply = radius_server_macacl(data, client, sess, msg);
@@ -1172,7 +1177,7 @@
 			     sess->sess_id);
 		eloop_cancel_timeout(radius_server_session_remove_timeout,
 				     data, sess);
-		eloop_register_timeout(10, 0,
+		eloop_register_timeout(RADIUS_SESSION_MAINTAIN, 0,
 				       radius_server_session_remove_timeout,
 				       data, sess);
 	}
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
index 79764d9..ce338f8 100644
--- a/src/rsn_supp/peerkey.c
+++ b/src/rsn_supp/peerkey.c
@@ -65,10 +65,9 @@
 {
 	size_t rlen;
 	struct wpa_eapol_key *err;
-	struct wpa_eapol_key_192 *err192;
 	struct rsn_error_kde error;
-	u8 *rbuf, *pos;
-	size_t kde_len;
+	u8 *rbuf, *pos, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info;
 
 	kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
@@ -76,11 +75,11 @@
 		kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
 
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*err) + kde_len, &rlen,
-				  (void *) &err);
+				  NULL, sizeof(*err) + mic_len + 2 + kde_len,
+				  &rlen, (void *) &err);
 	if (rbuf == NULL)
 		return -1;
-	err192 = (struct wpa_eapol_key_192 *) err;
+	mic = (u8 *) (err + 1);
 
 	err->type = EAPOL_KEY_TYPE_RSN;
 	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -92,8 +91,8 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(err->key_data_length, (u16) kde_len);
-	pos = (u8 *) (err + 1);
+	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
+	pos = mic + mic_len + 2;
 
 	if (peer) {
 		/* Peer MAC Address KDE */
@@ -114,8 +113,8 @@
 			   "(mui %d error_type %d)", mui, error_type);
 	}
 
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst,
-			   ETH_P_EAPOL, rbuf, rlen, err192->key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+			   mic);
 
 	return 0;
 }
@@ -128,9 +127,8 @@
 {
 	size_t rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
-	u8 *rbuf, *pos;
-	size_t kde_len;
+	u8 *rbuf, *pos, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info;
 
 	/* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
@@ -139,11 +137,10 @@
 		2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
 
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-				  NULL, sizeof(*reply) + kde_len, &rlen,
-				  (void *) &reply);
+				  NULL, sizeof(*reply) + mic_len + 2 + kde_len,
+				  &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = EAPOL_KEY_TYPE_RSN;
 	key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -155,8 +152,9 @@
 
 	os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
 
-	WPA_PUT_BE16(reply->key_data_length, (u16) kde_len);
-	pos = (u8 *) (reply + 1);
+	mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(mic + mic_len, (u16) kde_len);
+	pos = mic + mic_len + 2;
 
 	/* Peer RSN IE */
 	pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
@@ -168,8 +166,8 @@
 	wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr,
-			   ETH_P_EAPOL, rbuf, rlen, reply192->key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, src_addr, ETH_P_EAPOL, rbuf, rlen,
+			   mic);
 
 	return 0;
 }
@@ -177,7 +175,8 @@
 
 static int wpa_supplicant_process_smk_m2(
 	struct wpa_sm *sm, const unsigned char *src_addr,
-	const struct wpa_eapol_key *key, size_t extra_len, int ver)
+	const struct wpa_eapol_key *key, const u8 *key_data,
+	size_t key_data_len, int ver)
 {
 	struct wpa_peerkey *peerkey;
 	struct wpa_eapol_ie_parse kde;
@@ -194,8 +193,7 @@
 		return -1;
 	}
 
-	if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
-	    0) {
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
 		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
 		return -1;
 	}
@@ -324,18 +322,19 @@
 {
 	size_t mlen;
 	struct wpa_eapol_key *msg;
-	u8 *mbuf;
-	size_t kde_len;
+	u8 *mbuf, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info, ver;
 
 	kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
 
 	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + kde_len, &mlen,
+				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
 				  (void *) &msg);
 	if (mbuf == NULL)
 		return;
 
+	mic = (u8 *) (msg + 1);
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
 	if (peerkey->cipher != WPA_CIPHER_TKIP)
@@ -355,8 +354,8 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(msg->key_data_length, kde_len);
-	wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
+	WPA_PUT_BE16(mic + mic_len, kde_len);
+	wpa_add_kde(mic + mic_len + 2, RSN_KEY_DATA_PMKID,
 		    peerkey->smkid, PMKID_LEN);
 
 	if (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) {
@@ -371,7 +370,7 @@
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
 		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL,
+	wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
 			   mbuf, mlen, NULL);
 }
 
@@ -381,8 +380,8 @@
 {
 	size_t mlen;
 	struct wpa_eapol_key *msg;
-	u8 *mbuf, *pos;
-	size_t kde_len;
+	u8 *mbuf, *pos, *mic;
+	size_t kde_len, mic_len = 16;
 	u16 key_info, ver;
 	be32 lifetime;
 
@@ -390,11 +389,12 @@
 		2 + RSN_SELECTOR_LEN + sizeof(lifetime);
 
 	mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*msg) + kde_len, &mlen,
+				  sizeof(*msg) + mic_len + 2 + kde_len, &mlen,
 				  (void *) &msg);
 	if (mbuf == NULL)
 		return;
 
+	mic = (u8 *) (msg + 1);
 	msg->type = EAPOL_KEY_TYPE_RSN;
 
 	if (peerkey->cipher != WPA_CIPHER_TKIP)
@@ -415,8 +415,8 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
 
-	WPA_PUT_BE16(msg->key_data_length, kde_len);
-	pos = (u8 *) (msg + 1);
+	WPA_PUT_BE16(mic + mic_len, kde_len);
+	pos = mic + mic_len + 2;
 	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
 	lifetime = host_to_be32(peerkey->lifetime);
 	wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
@@ -426,9 +426,8 @@
 
 	wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
 		   MAC2STR(peerkey->addr));
-	wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver,
-			   peerkey->addr, ETH_P_EAPOL, mbuf, mlen,
-			   msg->key_mic);
+	wpa_eapol_key_send(sm, &peerkey->stk, ver, peerkey->addr, ETH_P_EAPOL,
+			   mbuf, mlen, mic);
 }
 
 
@@ -512,7 +511,8 @@
 
 static int wpa_supplicant_process_smk_m45(
 	struct wpa_sm *sm, const unsigned char *src_addr,
-	const struct wpa_eapol_key *key, size_t extra_len, int ver)
+	const struct wpa_eapol_key *key, const u8 *key_data,
+	size_t key_data_len, int ver)
 {
 	struct wpa_peerkey *peerkey;
 	struct wpa_eapol_ie_parse kde;
@@ -524,8 +524,7 @@
 		return -1;
 	}
 
-	if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
-	    0) {
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
 		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
 		return -1;
 	}
@@ -591,7 +590,7 @@
 
 static int wpa_supplicant_process_smk_error(
 	struct wpa_sm *sm, const unsigned char *src_addr,
-	const struct wpa_eapol_key *key, size_t extra_len)
+	const u8 *key_data, size_t key_data_len)
 {
 	struct wpa_eapol_ie_parse kde;
 	struct rsn_error_kde error;
@@ -606,8 +605,7 @@
 		return -1;
 	}
 
-	if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
-	    0) {
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
 		wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
 		return -1;
 	}
@@ -717,7 +715,8 @@
 
 	if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
 				       peerkey->pnonce, kde_buf, kde_buf_len,
-				       stk)) {
+				       stk) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to send STK message 2/4");
 		os_free(kde_buf);
 		return;
 	}
@@ -845,6 +844,10 @@
 			   "4-Way Handshake differs from 3 of STK 4-Way "
 			   "Handshake - drop packet (src=" MACSTR ")",
 			   MAC2STR(peerkey->addr));
+		wpa_hexdump(MSG_DEBUG, "RSN: INonce from message 1",
+			    peerkey->inonce, WPA_NONCE_LEN);
+		wpa_hexdump(MSG_DEBUG, "RSN: INonce from message 3",
+			    key->key_nonce, WPA_NONCE_LEN);
 		return;
 	}
 
@@ -852,8 +855,10 @@
 
 	if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
 				       WPA_GET_BE16(key->key_info),
-				       &peerkey->stk))
+				       &peerkey->stk) < 0) {
+		wpa_printf(MSG_INFO, "RSN: Failed to send STK message 4/4");
 		return;
+	}
 
 	_key = peerkey->stk.tk;
 	if (peerkey->cipher == WPA_CIPHER_TKIP) {
@@ -911,10 +916,10 @@
  */
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
 				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key_192 *key, u16 ver,
+				 struct wpa_eapol_key *key, u16 ver,
 				 const u8 *buf, size_t len)
 {
-	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+	u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN], *mic_pos;
 	size_t mic_len = 16;
 	int ok = 0;
 
@@ -926,12 +931,13 @@
 		peerkey->stk_set = 1;
 	}
 
-	os_memcpy(mic, key->key_mic, mic_len);
+	mic_pos = (u8 *) (key + 1);
+	os_memcpy(mic, mic_pos, mic_len);
 	if (peerkey->tstk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(mic_pos, 0, mic_len);
 		wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
-				  sm->key_mgmt, ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  sm->key_mgmt, ver, buf, len, mic_pos);
+		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "when using TSTK - ignoring TSTK");
 		} else {
@@ -945,10 +951,10 @@
 	}
 
 	if (!ok && peerkey->stk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(mic_pos, 0, mic_len);
 		wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
-				  sm->key_mgmt, ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  sm->key_mgmt, ver, buf, len, mic_pos);
+		if (os_memcmp_const(mic, mic_pos, mic_len) != 0) {
 			wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
 				   "- dropping packet");
 			return -1;
@@ -980,10 +986,10 @@
  */
 int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
 {
-	size_t rlen, kde_len;
+	size_t rlen, kde_len, mic_len;
 	struct wpa_eapol_key *req;
 	int key_info, ver;
-	u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos;
+	u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos, *mic;
 	u16 count;
 	struct rsn_ie_hdr *hdr;
 	struct wpa_peerkey *peerkey;
@@ -999,6 +1005,7 @@
 		return -1;
 	}
 
+	mic_len = wpa_mic_len(sm->key_mgmt);
 	if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
 		ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
 	else
@@ -1047,7 +1054,7 @@
 	kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
 
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-				  sizeof(*req) + kde_len, &rlen,
+				  sizeof(*req) + mic_len + 2 + kde_len, &rlen,
 				  (void *) &req);
 	if (rbuf == NULL) {
 		wpa_supplicant_peerkey_free(sm, peerkey);
@@ -1074,8 +1081,10 @@
 	wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
 		    req->key_nonce, WPA_NONCE_LEN);
 
-	WPA_PUT_BE16(req->key_data_length, (u16) kde_len);
-	pos = (u8 *) (req + 1);
+	mic = pos = (u8 *) (req + 1);
+	pos += mic_len;
+	WPA_PUT_BE16(pos, (u16) kde_len);
+	pos += 2;
 
 	/* Initiator RSN IE */
 	pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
@@ -1084,8 +1093,8 @@
 
 	wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
 		   MACSTR ")", MAC2STR(peer));
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
-			   ETH_P_EAPOL, rbuf, rlen, req->key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
+			   mic);
 
 	peerkey->next = sm->peerkey;
 	sm->peerkey = peerkey;
@@ -1135,20 +1144,22 @@
 
 
 void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-			  struct wpa_eapol_key *key, size_t extra_len,
+			  struct wpa_eapol_key *key, const u8 *key_data,
+			  size_t key_data_len,
 			  u16 key_info, u16 ver)
 {
 	if (key_info & WPA_KEY_INFO_ERROR) {
 		/* SMK Error */
-		wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len);
+		wpa_supplicant_process_smk_error(sm, src_addr, key_data,
+						 key_data_len);
 	} else if (key_info & WPA_KEY_INFO_ACK) {
 		/* SMK M2 */
-		wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len,
-					      ver);
+		wpa_supplicant_process_smk_m2(sm, src_addr, key, key_data,
+					      key_data_len, ver);
 	} else {
 		/* SMK M4 or M5 */
-		wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len,
-					       ver);
+		wpa_supplicant_process_smk_m45(sm, src_addr, key, key_data,
+					       key_data_len, ver);
 	}
 }
 
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
index 6ccd948..02e12e9 100644
--- a/src/rsn_supp/peerkey.h
+++ b/src/rsn_supp/peerkey.h
@@ -38,14 +38,14 @@
 
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
 				 struct wpa_peerkey *peerkey,
-				 struct wpa_eapol_key_192 *key, u16 ver,
+				 struct wpa_eapol_key *key, u16 ver,
 				 const u8 *buf, size_t len);
 void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
 			   struct wpa_eapol_key *key, u16 key_info, u16 ver,
 			   const u8 *key_data, size_t key_data_len);
 void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-			  struct wpa_eapol_key *key, size_t extra_len,
-			  u16 key_info, u16 ver);
+			  struct wpa_eapol_key *key, const u8 *key_data,
+			  size_t key_data_len, u16 key_info, u16 ver);
 void peerkey_deinit(struct wpa_sm *sm);
 
 #else /* CONFIG_PEERKEY */
@@ -68,8 +68,8 @@
 
 static inline void
 peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
-		     struct wpa_eapol_key *key, size_t extra_len,
-		     u16 key_info, u16 ver)
+		     struct wpa_eapol_key *key, const u8 *key_data,
+		     size_t key_data_len, u16 key_info, u16 ver)
 {
 }
 
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index 3d8d122..f723bb0 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -43,7 +43,8 @@
 				   struct rsn_pmksa_cache_entry *entry,
 				   enum pmksa_free_reason reason)
 {
-	wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+	wpa_sm_remove_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
+			    entry->pmkid);
 	pmksa->pmksa_count--;
 	pmksa->free_cb(entry, pmksa->ctx, reason);
 	_pmksa_cache_free_entry(entry);
@@ -128,7 +129,7 @@
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
 		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
 {
-	struct rsn_pmksa_cache_entry *entry, *pos, *prev;
+	struct rsn_pmksa_cache_entry *entry;
 	struct os_reltime now;
 
 	if (pmk_len > PMK_LEN_MAX)
@@ -159,14 +160,25 @@
 	os_memcpy(entry->aa, aa, ETH_ALEN);
 	entry->network_ctx = network_ctx;
 
+	return pmksa_cache_add_entry(pmksa, entry);
+}
+
+
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+		      struct rsn_pmksa_cache_entry *entry)
+{
+	struct rsn_pmksa_cache_entry *pos, *prev;
+
 	/* Replace an old entry for the same Authenticator (if found) with the
 	 * new entry */
 	pos = pmksa->pmksa;
 	prev = NULL;
 	while (pos) {
-		if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
-			if (pos->pmk_len == pmk_len &&
-			    os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
+		if (os_memcmp(entry->aa, pos->aa, ETH_ALEN) == 0) {
+			if (pos->pmk_len == entry->pmk_len &&
+			    os_memcmp_const(pos->pmk, entry->pmk,
+					    entry->pmk_len) == 0 &&
 			    os_memcmp_const(pos->pmkid, entry->pmkid,
 					    PMKID_LEN) == 0) {
 				wpa_printf(MSG_DEBUG, "WPA: reusing previous "
@@ -192,8 +204,8 @@
 				   "the current AP and any PMKSA cache entry "
 				   "that was based on the old PMK");
 			if (!pos->opportunistic)
-				pmksa_cache_flush(pmksa, network_ctx, pos->pmk,
-						  pos->pmk_len);
+				pmksa_cache_flush(pmksa, entry->network_ctx,
+						  pos->pmk, pos->pmk_len);
 			pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
 			break;
 		}
@@ -244,8 +256,9 @@
 	}
 	pmksa->pmksa_count++;
 	wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
-		   " network_ctx=%p", MAC2STR(entry->aa), network_ctx);
-	wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+		   " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx);
+	wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa,
+			 entry->pmkid);
 
 	return entry;
 }
@@ -513,6 +526,12 @@
 }
 
 
+struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
+{
+	return pmksa->pmksa;
+}
+
+
 /**
  * pmksa_cache_init - Initialize PMKSA cache
  * @free_cb: Callback function to be called when a PMKSA cache entry is freed
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
index daede6d..adc251a 100644
--- a/src/rsn_supp/pmksa_cache.h
+++ b/src/rsn_supp/pmksa_cache.h
@@ -55,10 +55,14 @@
 					       const u8 *aa, const u8 *pmkid,
 					       const void *network_ctx);
 int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+struct rsn_pmksa_cache_entry * pmksa_cache_head(struct rsn_pmksa_cache *pmksa);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
 		const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+		      struct rsn_pmksa_cache_entry *entry);
 struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
 void pmksa_cache_clear_current(struct wpa_sm *sm);
 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
@@ -104,6 +108,19 @@
 }
 
 static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_head(struct rsn_pmksa_cache *pmksa)
+{
+	return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
+		      struct rsn_pmksa_cache_entry *entry)
+{
+	return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
 		const u8 *pmkid, const u8 *kck, size_t kck_len,
 		const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
index 4c9a4fb..e83d073 100644
--- a/src/rsn_supp/preauth.c
+++ b/src/rsn_supp/preauth.c
@@ -342,7 +342,7 @@
 		/* Some drivers (e.g., NDIS) expect to get notified about the
 		 * PMKIDs again, so report the existing data now. */
 		if (p) {
-			wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
+			wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid);
 		}
 
 		dl_list_del(&candidate->list);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 9eb9738..c2ed952 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -1220,9 +1220,10 @@
 
 #ifdef CONFIG_TDLS_TESTING
 	if (tdls_testing & TDLS_TESTING_DIFF_BSSID) {
+		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
+
 		wpa_printf(MSG_DEBUG, "TDLS: Testing - use incorrect BSSID in "
 			   "Link Identifier");
-		struct wpa_tdls_lnkid *l = (struct wpa_tdls_lnkid *) pos;
 		wpa_tdls_linkid(sm, peer, l);
 		l->bssid[5] ^= 0x01;
 		pos += sizeof(*l);
@@ -2912,14 +2913,14 @@
 static int wpa_tdls_prohibited(struct ieee802_11_elems *elems)
 {
 	/* bit 38 - TDLS Prohibited */
-	return !!(elems->ext_capab[2 + 4] & 0x40);
+	return !!(elems->ext_capab[4] & 0x40);
 }
 
 
 static int wpa_tdls_chan_switch_prohibited(struct ieee802_11_elems *elems)
 {
 	/* bit 39 - TDLS Channel Switch Prohibited */
-	return !!(elems->ext_capab[2 + 4] & 0x80);
+	return !!(elems->ext_capab[4] & 0x80);
 }
 
 
@@ -2932,7 +2933,7 @@
 
 	if (ies == NULL ||
 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
-	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
 		return;
 
 	sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
@@ -2951,7 +2952,7 @@
 
 	if (ies == NULL ||
 	    ieee802_11_parse_elems(ies, len, &elems, 0) == ParseFailed ||
-	    elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+	    elems.ext_capab == NULL || elems.ext_capab_len < 5)
 		return;
 
 	if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 441c856..8153f79 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -10,10 +10,14 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/aes.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "crypto/random.h"
+#include "crypto/aes_siv.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eap_common/eap_defs.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "wpa.h"
 #include "eloop.h"
@@ -30,8 +34,7 @@
 /**
  * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @kck: Key Confirmation Key (KCK, part of PTK)
- * @kck_len: KCK length in octets
+ * @ptk: PTK for Key Confirmation/Encryption Key
  * @ver: Version field from Key Info
  * @dest: Destination address for the frame
  * @proto: Ethertype (usually ETH_P_EAPOL)
@@ -40,7 +43,7 @@
  * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
  * Returns: >= 0 on success, < 0 on failure
  */
-int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
 		       int ver, const u8 *dest, u16 proto,
 		       u8 *msg, size_t msg_len, u8 *key_mic)
 {
@@ -64,16 +67,89 @@
 				MAC2STR(dest));
 		}
 	}
-	if (key_mic &&
-	    wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
-			      key_mic)) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
-			"WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
-			ver, sm->key_mgmt);
+
+	if (mic_len) {
+		if (key_mic && (!ptk || !ptk->kck_len))
+			goto out;
+
+		if (key_mic &&
+		    wpa_eapol_key_mic(ptk->kck, ptk->kck_len, sm->key_mgmt, ver,
+				      msg, msg_len, key_mic)) {
+			wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
+				"WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+				ver, sm->key_mgmt);
+			goto out;
+		}
+		if (ptk)
+			wpa_hexdump_key(MSG_DEBUG, "WPA: KCK",
+					ptk->kck, ptk->kck_len);
+		wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC",
+			    key_mic, mic_len);
+	} else {
+#ifdef CONFIG_FILS
+		/* AEAD cipher - Key MIC field not used */
+		struct ieee802_1x_hdr *s_hdr, *hdr;
+		struct wpa_eapol_key *s_key, *key;
+		u8 *buf, *s_key_data, *key_data;
+		size_t buf_len = msg_len + AES_BLOCK_SIZE;
+		size_t key_data_len;
+		u16 eapol_len;
+		const u8 *aad[1];
+		size_t aad_len[1];
+
+		if (!ptk || !ptk->kek_len)
+			goto out;
+
+		key_data_len = msg_len - sizeof(struct ieee802_1x_hdr) -
+			sizeof(struct wpa_eapol_key) - 2;
+
+		buf = os_malloc(buf_len);
+		if (!buf)
+			goto out;
+
+		os_memcpy(buf, msg, msg_len);
+		hdr = (struct ieee802_1x_hdr *) buf;
+		key = (struct wpa_eapol_key *) (hdr + 1);
+		key_data = ((u8 *) (key + 1)) + 2;
+
+		/* Update EAPOL header to include AES-SIV overhead */
+		eapol_len = be_to_host16(hdr->length);
+		eapol_len += AES_BLOCK_SIZE;
+		hdr->length = host_to_be16(eapol_len);
+
+		/* Update Key Data Length field to include AES-SIV overhead */
+		WPA_PUT_BE16((u8 *) (key + 1), AES_BLOCK_SIZE + key_data_len);
+
+		s_hdr = (struct ieee802_1x_hdr *) msg;
+		s_key = (struct wpa_eapol_key *) (s_hdr + 1);
+		s_key_data = ((u8 *) (s_key + 1)) + 2;
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: Plaintext Key Data",
+				s_key_data, key_data_len);
+
+		wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+		 /* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+		  * to Key Data (exclusive). */
+		aad[0] = buf;
+		aad_len[0] = key_data - buf;
+		if (aes_siv_encrypt(ptk->kek, ptk->kek_len,
+				    s_key_data, key_data_len,
+				    1, aad, aad_len, key_data) < 0) {
+			os_free(buf);
+			goto out;
+		}
+
+		wpa_hexdump(MSG_DEBUG, "WPA: Encrypted Key Data from SIV",
+			    key_data, AES_BLOCK_SIZE + key_data_len);
+
+		os_free(msg);
+		msg = buf;
+		msg_len = buf_len;
+#else /* CONFIG_FILS */
 		goto out;
+#endif /* CONFIG_FILS */
 	}
-	wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
-	wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
+
 	wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
 	ret = wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
 	eapol_sm_notify_tx_eapol_key(sm->eapol);
@@ -97,9 +173,8 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	int key_info, ver;
-	u8 bssid[ETH_ALEN], *rbuf, *key_mic;
+	u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
 
 	if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
 	    wpa_key_mgmt_suite_b(sm->key_mgmt))
@@ -119,19 +194,20 @@
 	}
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info = WPA_KEY_INFO_REQUEST | ver;
 	if (sm->ptk_set)
-		key_info |= WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+		key_info |= WPA_KEY_INFO_SECURE;
+	if (sm->ptk_set && mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
 	if (error)
 		key_info |= WPA_KEY_INFO_ERROR;
 	if (pairwise)
@@ -142,21 +218,19 @@
 		  WPA_REPLAY_COUNTER_LEN);
 	inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 
-	if (mic_len == 24)
-		WPA_PUT_BE16(reply192->key_data_length, 0);
-	else
-		WPA_PUT_BE16(reply->key_data_length, 0);
+	mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(mic + mic_len, 0);
 	if (!(key_info & WPA_KEY_INFO_MIC))
 		key_mic = NULL;
 	else
-		key_mic = reply192->key_mic; /* same offset in reply */
+		key_mic = mic;
 
 	wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 		"WPA: Sending EAPOL-Key Request (error=%d "
 		"pairwise=%d ptk_set=%d len=%lu)",
 		error, pairwise, sm->ptk_set, (unsigned long) rlen);
-	wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
-			   ETH_P_EAPOL, rbuf, rlen, key_mic);
+	wpa_eapol_key_send(sm, &sm->ptk, ver, bssid, ETH_P_EAPOL, rbuf, rlen,
+			   key_mic);
 }
 
 
@@ -214,7 +288,7 @@
 	} else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
 		int res, pmk_len;
 
-		if (sm->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+		if (wpa_key_mgmt_sha384(sm->key_mgmt))
 			pmk_len = PMK_LEN_SUITE_B_192;
 		else
 			pmk_len = PMK_LEN;
@@ -341,9 +415,9 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	u8 *rbuf, *key_mic;
 	u8 *rsn_ie_buf = NULL;
+	u16 key_info;
 
 	if (wpa_ie == NULL) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - "
@@ -384,7 +458,7 @@
 	wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
 				  NULL, hdrlen + wpa_ie_len,
 				  &rlen, (void *) &reply);
@@ -392,13 +466,16 @@
 		os_free(rsn_ie_buf);
 		return -1;
 	}
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
-	WPA_PUT_BE16(reply->key_info,
-		     ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
+	key_info = ver | WPA_KEY_INFO_KEY_TYPE;
+	if (mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
+	else
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
+	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
 	else
@@ -408,21 +485,16 @@
 	wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
 		    WPA_REPLAY_COUNTER_LEN);
 
-	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
-	if (mic_len == 24) {
-		WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
-		os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
-	} else {
-		WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
-		os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
-	}
+	key_mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(key_mic + mic_len, wpa_ie_len); /* Key Data Length */
+	os_memcpy(key_mic + mic_len + 2, wpa_ie, wpa_ie_len); /* Key Data */
 	os_free(rsn_ie_buf);
 
 	os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
-	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
-				  ETH_P_EAPOL, rbuf, rlen, key_mic);
+	return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+				  key_mic);
 }
 
 
@@ -1203,22 +1275,24 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	u8 *rbuf, *key_mic;
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_SECURE;
-	key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
+	key_info |= ver | WPA_KEY_INFO_KEY_TYPE;
+	if (mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
+	else
+		key_info |= WPA_KEY_INFO_ENCR_KEY_DATA;
 	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
@@ -1227,15 +1301,12 @@
 	os_memcpy(reply->replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
 
-	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
-	if (mic_len == 24)
-		WPA_PUT_BE16(reply192->key_data_length, 0);
-	else
-		WPA_PUT_BE16(reply->key_data_length, 0);
+	key_mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(key_mic + mic_len, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
-	return wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst,
-				  ETH_P_EAPOL, rbuf, rlen, key_mic);
+	return wpa_eapol_key_send(sm, ptk, ver, dst, ETH_P_EAPOL, rbuf, rlen,
+				  key_mic);
 }
 
 
@@ -1378,7 +1449,8 @@
 	int maxkeylen;
 	struct wpa_eapol_ie_parse ie;
 
-	wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
+	wpa_hexdump_key(MSG_DEBUG, "RSN: msg 1/2 key data",
+			keydata, keydatalen);
 	if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0)
 		return -1;
 	if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
@@ -1512,22 +1584,22 @@
 {
 	size_t mic_len, hdrlen, rlen;
 	struct wpa_eapol_key *reply;
-	struct wpa_eapol_key_192 *reply192;
 	u8 *rbuf, *key_mic;
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
+	hdrlen = sizeof(*reply) + mic_len + 2;
 	rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
 				  hdrlen, &rlen, (void *) &reply);
 	if (rbuf == NULL)
 		return -1;
-	reply192 = (struct wpa_eapol_key_192 *) reply;
 
 	reply->type = (sm->proto == WPA_PROTO_RSN ||
 		       sm->proto == WPA_PROTO_OSEN) ?
 		EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
 	key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
-	key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+	key_info |= ver | WPA_KEY_INFO_SECURE;
+	if (mic_len)
+		key_info |= WPA_KEY_INFO_MIC;
 	WPA_PUT_BE16(reply->key_info, key_info);
 	if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
 		WPA_PUT_BE16(reply->key_length, 0);
@@ -1536,15 +1608,12 @@
 	os_memcpy(reply->replay_counter, key->replay_counter,
 		  WPA_REPLAY_COUNTER_LEN);
 
-	key_mic = reply192->key_mic; /* same offset for reply and reply192 */
-	if (mic_len == 24)
-		WPA_PUT_BE16(reply192->key_data_length, 0);
-	else
-		WPA_PUT_BE16(reply->key_data_length, 0);
+	key_mic = (u8 *) (reply + 1);
+	WPA_PUT_BE16(key_mic + mic_len, 0);
 
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
-	return wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver,
-				  sm->bssid, ETH_P_EAPOL, rbuf, rlen, key_mic);
+	return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
+				  rbuf, rlen, key_mic);
 }
 
 
@@ -1620,7 +1689,7 @@
 
 
 static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
-					       struct wpa_eapol_key_192 *key,
+					       struct wpa_eapol_key *key,
 					       u16 ver,
 					       const u8 *buf, size_t len)
 {
@@ -1628,12 +1697,12 @@
 	int ok = 0;
 	size_t mic_len = wpa_mic_len(sm->key_mgmt);
 
-	os_memcpy(mic, key->key_mic, mic_len);
+	os_memcpy(mic, key + 1, mic_len);
 	if (sm->tptk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(key + 1, 0, mic_len);
 		wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  ver, buf, len, (u8 *) (key + 1));
+		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC "
 				"when using TPTK - ignoring TPTK");
@@ -1647,10 +1716,10 @@
 	}
 
 	if (!ok && sm->ptk_set) {
-		os_memset(key->key_mic, 0, mic_len);
+		os_memset(key + 1, 0, mic_len);
 		wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
-				  ver, buf, len, key->key_mic);
-		if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
+				  ver, buf, len, (u8 *) (key + 1));
+		if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 				"WPA: Invalid EAPOL-Key MIC - "
 				"dropping packet");
@@ -1675,7 +1744,8 @@
 
 /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
 static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
-					   struct wpa_eapol_key *key, u16 ver,
+					   struct wpa_eapol_key *key,
+					   size_t mic_len, u16 ver,
 					   u8 *key_data, size_t *key_data_len)
 {
 	wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
@@ -1734,7 +1804,7 @@
 		}
 		os_memcpy(key_data, buf, *key_data_len);
 		bin_clear_free(buf, *key_data_len);
-		WPA_PUT_BE16(key->key_data_length, *key_data_len);
+		WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len);
 	} else {
 		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
 			"WPA: Unsupported key_info type %d", ver);
@@ -1797,6 +1867,76 @@
 }
 
 
+#ifdef CONFIG_FILS
+static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len,
+				 size_t *key_data_len)
+{
+	struct wpa_ptk *ptk;
+	struct ieee802_1x_hdr *hdr;
+	struct wpa_eapol_key *key;
+	u8 *pos, *tmp;
+	const u8 *aad[1];
+	size_t aad_len[1];
+
+	if (*key_data_len < AES_BLOCK_SIZE) {
+		wpa_printf(MSG_INFO, "No room for AES-SIV data in the frame");
+		return -1;
+	}
+
+	if (sm->tptk_set)
+		ptk = &sm->tptk;
+	else if (sm->ptk_set)
+		ptk = &sm->ptk;
+	else
+		return -1;
+
+	hdr = (struct ieee802_1x_hdr *) buf;
+	key = (struct wpa_eapol_key *) (hdr + 1);
+	pos = (u8 *) (key + 1);
+	pos += 2; /* Pointing at the Encrypted Key Data field */
+
+	tmp = os_malloc(*key_data_len);
+	if (!tmp)
+		return -1;
+
+	/* AES-SIV AAD from EAPOL protocol version field (inclusive) to
+	 * to Key Data (exclusive). */
+	aad[0] = buf;
+	aad_len[0] = pos - buf;
+	if (aes_siv_decrypt(ptk->kek, ptk->kek_len, pos, *key_data_len,
+			    1, aad, aad_len, tmp) < 0) {
+		wpa_printf(MSG_INFO, "Invalid AES-SIV data in the frame");
+		bin_clear_free(tmp, *key_data_len);
+		return -1;
+	}
+
+	/* AEAD decryption and validation completed successfully */
+	(*key_data_len) -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "WPA: Decrypted Key Data",
+			tmp, *key_data_len);
+
+	/* Replace Key Data field with the decrypted version */
+	os_memcpy(pos, tmp, *key_data_len);
+	pos -= 2; /* Key Data Length field */
+	WPA_PUT_BE16(pos, *key_data_len);
+	bin_clear_free(tmp, *key_data_len);
+
+	if (sm->tptk_set) {
+		sm->tptk_set = 0;
+		sm->ptk_set = 1;
+		os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+		os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+	}
+
+	os_memcpy(sm->rx_replay_counter, key->replay_counter,
+		  WPA_REPLAY_COUNTER_LEN);
+	sm->rx_replay_counter_set = 1;
+
+	return 0;
+}
+#endif /* CONFIG_FILS */
+
+
 /**
  * wpa_sm_rx_eapol - Process received WPA EAPOL frames
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -1819,12 +1959,11 @@
 	size_t plen, data_len, key_data_len;
 	const struct ieee802_1x_hdr *hdr;
 	struct wpa_eapol_key *key;
-	struct wpa_eapol_key_192 *key192;
 	u16 key_info, ver;
 	u8 *tmp = NULL;
 	int ret = -1;
 	struct wpa_peerkey *peerkey = NULL;
-	u8 *key_data;
+	u8 *mic, *key_data;
 	size_t mic_len, keyhdrlen;
 
 #ifdef CONFIG_IEEE80211R
@@ -1832,7 +1971,7 @@
 #endif /* CONFIG_IEEE80211R */
 
 	mic_len = wpa_mic_len(sm->key_mgmt);
-	keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+	keyhdrlen = sizeof(*key) + mic_len + 2;
 
 	if (len < sizeof(*hdr) + keyhdrlen) {
 		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -1884,12 +2023,8 @@
 		goto out;
 	os_memcpy(tmp, buf, data_len);
 	key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
-	key192 = (struct wpa_eapol_key_192 *)
-		(tmp + sizeof(struct ieee802_1x_hdr));
-	if (mic_len == 24)
-		key_data = (u8 *) (key192 + 1);
-	else
-		key_data = (u8 *) (key + 1);
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
 
 	if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
 	{
@@ -1900,11 +2035,8 @@
 		goto out;
 	}
 
-	if (mic_len == 24)
-		key_data_len = WPA_GET_BE16(key192->key_data_length);
-	else
-		key_data_len = WPA_GET_BE16(key->key_data_length);
-	wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+	key_data_len = WPA_GET_BE16(mic + mic_len);
+	wpa_eapol_key_dump(sm, key, key_data_len, mic, mic_len);
 
 	if (key_data_len > plen - keyhdrlen) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
@@ -1923,6 +2055,7 @@
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
 	    sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: Unsupported EAPOL-Key descriptor version %d",
@@ -1938,7 +2071,8 @@
 		goto out;
 	}
 
-	if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	if ((wpa_key_mgmt_suite_b(sm->key_mgmt) ||
+	     wpa_key_mgmt_fils(sm->key_mgmt)) &&
 	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
@@ -1960,6 +2094,7 @@
 	if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
 		if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 		    sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
+		    !wpa_key_mgmt_fils(sm->key_mgmt) &&
 		    !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
 			wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 				"WPA: AP did not use the "
@@ -1970,6 +2105,7 @@
 #endif /* CONFIG_IEEE80211W */
 	if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
 	    !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+	    !wpa_key_mgmt_fils(sm->key_mgmt) &&
 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
 		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
 			"WPA: CCMP is used, but EAPOL-Key "
@@ -2060,19 +2196,27 @@
 	}
 
 	if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
-	    wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
+	    wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
 		goto out;
 
 #ifdef CONFIG_PEERKEY
 	if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
-	    peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp,
+	    peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp,
 					 data_len))
 		goto out;
 #endif /* CONFIG_PEERKEY */
 
+#ifdef CONFIG_FILS
+	if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		if (wpa_supp_aead_decrypt(sm, tmp, data_len, &key_data_len))
+			goto out;
+	}
+#endif /* CONFIG_FILS */
+
 	if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
-	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-		if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
+		if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
+						    ver, key_data,
 						    &key_data_len))
 			goto out;
 	}
@@ -2088,7 +2232,8 @@
 			/* PeerKey 4-Way Handshake */
 			peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
 					      key_data, key_data_len);
-		} else if (key_info & WPA_KEY_INFO_MIC) {
+		} else if (key_info & (WPA_KEY_INFO_MIC |
+				       WPA_KEY_INFO_ENCR_KEY_DATA)) {
 			/* 3/4 4-Way Handshake */
 			wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
 						      key_data_len);
@@ -2100,17 +2245,18 @@
 		}
 	} else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
 		/* PeerKey SMK Handshake */
-		peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
-				     ver);
+		peerkey_rx_eapol_smk(sm, src_addr, key, key_data, key_data_len,
+				     key_info, ver);
 	} else {
-		if (key_info & WPA_KEY_INFO_MIC) {
+		if ((mic_len && (key_info & WPA_KEY_INFO_MIC)) ||
+		    (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA))) {
 			/* 1/2 Group Key Handshake */
 			wpa_supplicant_process_1_of_2(sm, src_addr, key,
 						      key_data, key_data_len,
 						      ver);
 		} else {
 			wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-				"WPA: EAPOL-Key (Group) without Mic bit - "
+				"WPA: EAPOL-Key (Group) without Mic/Encr bit - "
 				"dropped");
 		}
 	}
@@ -2392,6 +2538,16 @@
 		clear_keys = 0;
 	}
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_FILS
+	if (sm->fils_completed) {
+		/*
+		 * Clear portValid to kick EAPOL state machine to re-enter
+		 * AUTHENTICATED state to get the EAPOL port Authorized.
+		 */
+		wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+		clear_keys = 0;
+	}
+#endif /* CONFIG_FILS */
 
 	if (clear_keys) {
 		/*
@@ -2440,9 +2596,13 @@
 #ifdef CONFIG_TDLS
 	wpa_tdls_disassoc(sm);
 #endif /* CONFIG_TDLS */
+#ifdef CONFIG_FILS
+	sm->fils_completed = 0;
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R
 	sm->ft_reassoc_completed = 0;
 #endif /* CONFIG_IEEE80211R */
+
 	/* Keys are not needed in the WPA state machine anymore */
 	wpa_sm_drop_sa(sm);
 
@@ -2790,6 +2950,10 @@
 
 		os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
 		sm->assoc_wpa_ie_len = *wpa_ie_len;
+	} else {
+		wpa_hexdump(MSG_DEBUG,
+			    "WPA: Leave previously set WPA IE default",
+			    sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
 	}
 
 	return 0;
@@ -2933,6 +3097,20 @@
 }
 
 
+struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm)
+{
+	return pmksa_cache_head(sm->pmksa);
+}
+
+
+struct rsn_pmksa_cache_entry *
+wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
+			     struct rsn_pmksa_cache_entry * entry)
+{
+	return pmksa_cache_add_entry(sm->pmksa, entry);
+}
+
+
 void wpa_sm_drop_sa(struct wpa_sm *sm)
 {
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK");
@@ -3106,3 +3284,570 @@
 	sm->test_assoc_ie = buf;
 }
 #endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_FILS
+
+struct wpabuf * fils_build_auth(struct wpa_sm *sm)
+{
+	struct wpabuf *buf = NULL;
+	struct wpabuf *erp_msg;
+
+	erp_msg = eapol_sm_build_erp_reauth_start(sm->eapol);
+	if (!erp_msg && !sm->cur_pmksa) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Neither ERP EAP-Initiate/Re-auth nor PMKSA cache entry is available - skip FILS");
+		goto fail;
+	}
+
+	wpa_printf(MSG_DEBUG, "FILS: Try to use FILS (erp=%d pmksa_cache=%d)",
+		   erp_msg != NULL, sm->cur_pmksa != NULL);
+
+	sm->fils_completed = 0;
+
+	if (!sm->assoc_wpa_ie) {
+		wpa_printf(MSG_INFO, "FILS: No own RSN IE set for FILS");
+		goto fail;
+	}
+
+	if (random_get_bytes(sm->fils_nonce, FILS_NONCE_LEN) < 0 ||
+	    random_get_bytes(sm->fils_session, FILS_SESSION_LEN) < 0)
+		goto fail;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Nonce",
+		    sm->fils_nonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: Generated FILS Session",
+		    sm->fils_session, FILS_SESSION_LEN);
+
+	buf = wpabuf_alloc(1000 + sm->assoc_wpa_ie_len);
+	if (!buf)
+		goto fail;
+
+	/* Fields following the Authentication algorithm number field */
+
+	/* Authentication Transaction seq# */
+	wpabuf_put_le16(buf, 1);
+
+	/* Status Code */
+	wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	/* RSNE */
+	wpa_hexdump(MSG_DEBUG, "FILS: RSNE in FILS Authentication frame",
+		    sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
+	wpabuf_put_data(buf, sm->assoc_wpa_ie, sm->assoc_wpa_ie_len);
+
+	/* TODO: MDE when using FILS for FT initial association */
+	/* TODO: FTE when using FILS for FT initial association */
+
+	/* FILS Nonce */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+	wpabuf_put_data(buf, sm->fils_nonce, FILS_NONCE_LEN);
+
+	/* FILS Session */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+	/* FILS Wrapped Data */
+	sm->fils_erp_pmkid_set = 0;
+	if (erp_msg) {
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+		wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg)); /* Length */
+		/* Element ID Extension */
+		wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_WRAPPED_DATA);
+		wpabuf_put_buf(buf, erp_msg);
+		/* Calculate pending PMKID here so that we do not need to
+		 * maintain a copy of the EAP-Initiate/Reauth message. */
+		if (fils_pmkid_erp(sm->key_mgmt, wpabuf_head(erp_msg),
+				   wpabuf_len(erp_msg),
+				   sm->fils_erp_pmkid) == 0)
+			sm->fils_erp_pmkid_set = 1;
+	}
+
+	wpa_hexdump_buf(MSG_DEBUG, "RSN: FILS fields for Authentication frame",
+			buf);
+
+fail:
+	wpabuf_free(erp_msg);
+	return buf;
+}
+
+
+int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len)
+{
+	const u8 *pos, *end;
+	struct ieee802_11_elems elems;
+	struct wpa_ie_data rsn;
+	int pmkid_match = 0;
+	u8 ick[FILS_ICK_MAX_LEN];
+	size_t ick_len;
+	int res;
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Authentication frame fields",
+		    data, len);
+	pos = data;
+	end = data + len;
+
+	/* TODO: Finite Cyclic Group when using PK or PFS */
+	/* TODO: Element when using PK or PFS */
+
+	wpa_hexdump(MSG_DEBUG, "FILS: Remaining IEs", pos, end - pos);
+	if (ieee802_11_parse_elems(pos, end - pos, &elems, 1) == ParseFailed) {
+		wpa_printf(MSG_DEBUG, "FILS: Could not parse elements");
+		return -1;
+	}
+
+	/* RSNE */
+	wpa_hexdump(MSG_DEBUG, "FILS: RSN element", elems.rsn_ie,
+		    elems.rsn_ie_len);
+	if (!elems.rsn_ie ||
+	    wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+				 &rsn) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: No RSN element");
+		return -1;
+	}
+
+	if (!elems.fils_nonce) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Nonce field");
+		return -1;
+	}
+	os_memcpy(sm->fils_anonce, elems.fils_nonce, FILS_NONCE_LEN);
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce", sm->fils_anonce, FILS_NONCE_LEN);
+
+	/* TODO: MDE when using FILS+FT */
+	/* TODO: FTE when using FILS+FT */
+
+	/* PMKID List */
+	if (rsn.pmkid && rsn.num_pmkid > 0) {
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID List",
+			    rsn.pmkid, rsn.num_pmkid * PMKID_LEN);
+
+		if (rsn.num_pmkid != 1) {
+			wpa_printf(MSG_DEBUG, "FILS: Invalid PMKID selection");
+			return -1;
+		}
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID", rsn.pmkid, PMKID_LEN);
+		if (os_memcmp(sm->cur_pmksa->pmkid, rsn.pmkid, PMKID_LEN) != 0)
+		{
+			wpa_printf(MSG_DEBUG, "FILS: PMKID mismatch");
+			wpa_hexdump(MSG_DEBUG, "FILS: Expected PMKID",
+				    sm->cur_pmksa->pmkid, PMKID_LEN);
+			return -1;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Matching PMKID - continue using PMKSA caching");
+		pmkid_match = 1;
+	}
+	if (!pmkid_match && sm->cur_pmksa) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No PMKID match - cannot use cached PMKSA entry");
+		sm->cur_pmksa = NULL;
+	}
+
+	/* FILS Session */
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		return -1;
+	}
+	wpa_hexdump(MSG_DEBUG, "FILS: FILS Session", elems.fils_session,
+		    FILS_SESSION_LEN);
+	if (os_memcmp(sm->fils_session, elems.fils_session, FILS_SESSION_LEN)
+	    != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    sm->fils_session, FILS_SESSION_LEN);
+		return -1;
+	}
+
+	/* FILS Wrapped Data */
+	if (!sm->cur_pmksa && elems.fils_wrapped_data) {
+		u8 rmsk[ERP_MAX_KEY_LEN];
+		size_t rmsk_len;
+
+		wpa_hexdump(MSG_DEBUG, "FILS: Wrapped Data",
+			    elems.fils_wrapped_data,
+			    elems.fils_wrapped_data_len);
+		eapol_sm_process_erp_finish(sm->eapol, elems.fils_wrapped_data,
+					    elems.fils_wrapped_data_len);
+		if (eapol_sm_failed(sm->eapol))
+			return -1;
+
+		rmsk_len = ERP_MAX_KEY_LEN;
+		res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
+		if (res == PMK_LEN) {
+			rmsk_len = PMK_LEN;
+			res = eapol_sm_get_key(sm->eapol, rmsk, rmsk_len);
+		}
+		if (res)
+			return -1;
+
+		res = fils_rmsk_to_pmk(sm->key_mgmt, rmsk, rmsk_len,
+				       sm->fils_nonce, sm->fils_anonce, NULL, 0,
+				       sm->pmk, &sm->pmk_len);
+		os_memset(rmsk, 0, sizeof(rmsk));
+		if (res)
+			return -1;
+
+		if (!sm->fils_erp_pmkid_set) {
+			wpa_printf(MSG_DEBUG, "FILS: PMKID not available");
+			return -1;
+		}
+		wpa_hexdump(MSG_DEBUG, "FILS: PMKID", sm->fils_erp_pmkid,
+			    PMKID_LEN);
+		wpa_printf(MSG_DEBUG, "FILS: ERP processing succeeded - add PMKSA cache entry for the result");
+		sm->cur_pmksa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
+						sm->fils_erp_pmkid, NULL, 0,
+						sm->bssid, sm->own_addr,
+						sm->network_ctx, sm->key_mgmt);
+	}
+
+	if (!sm->cur_pmksa) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: No remaining options to continue FILS authentication");
+		return -1;
+	}
+
+	if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
+			    sm->fils_nonce, sm->fils_anonce, &sm->ptk,
+			    ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher) <
+	    0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
+		return -1;
+	}
+	sm->ptk_set = 1;
+	sm->tptk_set = 0;
+	os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+
+	res = fils_key_auth_sk(ick, ick_len, sm->fils_nonce,
+			       sm->fils_anonce, sm->own_addr, sm->bssid,
+			       NULL, 0, NULL, 0, /* TODO: SK+PFS */
+			       sm->key_mgmt, sm->fils_key_auth_sta,
+			       sm->fils_key_auth_ap,
+			       &sm->fils_key_auth_len);
+	os_memset(ick, 0, sizeof(ick));
+	return res;
+}
+
+
+struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
+				     size_t *kek_len, const u8 **snonce,
+				     const u8 **anonce,
+				     const struct wpabuf **hlp,
+				     unsigned int num_hlp)
+{
+	struct wpabuf *buf;
+	size_t len;
+	unsigned int i;
+
+	len = 1000;
+	for (i = 0; hlp && i < num_hlp; i++)
+		len += 10 + wpabuf_len(hlp[i]);
+	buf = wpabuf_alloc(len);
+	if (!buf)
+		return NULL;
+
+	/* FILS Session */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+	wpabuf_put_data(buf, sm->fils_session, FILS_SESSION_LEN);
+
+	/* Everything after FILS Session element gets encrypted in the driver
+	 * with KEK. The buffer returned from here is the plaintext version. */
+
+	/* TODO: FILS Public Key */
+
+	/* FILS Key Confirm */
+	wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+	wpabuf_put_u8(buf, 1 + sm->fils_key_auth_len); /* Length */
+	/* Element ID Extension */
+	wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+	wpabuf_put_data(buf, sm->fils_key_auth_sta, sm->fils_key_auth_len);
+
+	/* FILS HLP Container */
+	for (i = 0; hlp && i < num_hlp; i++) {
+		const u8 *pos = wpabuf_head(hlp[i]);
+		size_t left = wpabuf_len(hlp[i]);
+
+		wpabuf_put_u8(buf, WLAN_EID_EXTENSION); /* Element ID */
+		if (left <= 254)
+			len = 1 + left;
+		else
+			len = 255;
+		wpabuf_put_u8(buf, len); /* Length */
+		/* Element ID Extension */
+		wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_HLP_CONTAINER);
+		/* Destination MAC Address, Source MAC Address, HLP Packet.
+		 * HLP Packet is in MSDU format (i.e., included the LLC/SNAP
+		 * header when LPD is used). */
+		wpabuf_put_data(buf, pos, len - 1);
+		pos += len - 1;
+		left -= len - 1;
+		while (left) {
+			wpabuf_put_u8(buf, WLAN_EID_FRAGMENT);
+			len = left > 255 ? 255 : left;
+			wpabuf_put_u8(buf, len);
+			wpabuf_put_data(buf, pos, len);
+			pos += len;
+			left -= len;
+		}
+	}
+
+	/* TODO: FILS IP Address Assignment */
+
+	wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf);
+
+	*kek = sm->ptk.kek;
+	*kek_len = sm->ptk.kek_len;
+	wpa_hexdump_key(MSG_DEBUG, "FILS: KEK for AEAD", *kek, *kek_len);
+	*snonce = sm->fils_nonce;
+	wpa_hexdump(MSG_DEBUG, "FILS: SNonce for AEAD AAD",
+		    *snonce, FILS_NONCE_LEN);
+	*anonce = sm->fils_anonce;
+	wpa_hexdump(MSG_DEBUG, "FILS: ANonce for AEAD AAD",
+		    *anonce, FILS_NONCE_LEN);
+
+	return buf;
+}
+
+
+static void fils_process_hlp_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
+{
+	const u8 *pos, *end;
+
+	wpa_hexdump(MSG_MSGDUMP, "FILS: HLP response", resp, len);
+	if (len < 2 * ETH_ALEN)
+		return;
+	pos = resp + 2 * ETH_ALEN;
+	end = resp + len;
+	if (end - pos >= 6 &&
+	    os_memcmp(pos, "\xaa\xaa\x03\x00\x00\x00", 6) == 0)
+		pos += 6; /* Remove SNAP/LLC header */
+	wpa_sm_fils_hlp_rx(sm, resp, resp + ETH_ALEN, pos, end - pos);
+}
+
+
+static void fils_process_hlp_container(struct wpa_sm *sm, const u8 *pos,
+				       size_t len)
+{
+	const u8 *end = pos + len;
+	u8 *tmp, *tmp_pos;
+
+	/* Check if there are any FILS HLP Container elements */
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos)
+			return;
+		if (pos[0] == WLAN_EID_EXTENSION &&
+		    pos[1] >= 1 + 2 * ETH_ALEN &&
+		    pos[2] == WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		pos += 2 + pos[1];
+	}
+	if (end - pos < 2)
+		return; /* No FILS HLP Container elements */
+
+	tmp = os_malloc(end - pos);
+	if (!tmp)
+		return;
+
+	while (end - pos >= 2) {
+		if (2 + pos[1] > end - pos ||
+		    pos[0] != WLAN_EID_EXTENSION ||
+		    pos[1] < 1 + 2 * ETH_ALEN ||
+		    pos[2] != WLAN_EID_EXT_FILS_HLP_CONTAINER)
+			break;
+		tmp_pos = tmp;
+		os_memcpy(tmp_pos, pos + 3, pos[1] - 1);
+		tmp_pos += pos[1] - 1;
+		pos += 2 + pos[1];
+
+		/* Add possible fragments */
+		while (end - pos >= 2 && pos[0] == WLAN_EID_FRAGMENT &&
+		       2 + pos[1] <= end - pos) {
+			os_memcpy(tmp_pos, pos + 2, pos[1]);
+			tmp_pos += pos[1];
+			pos += 2 + pos[1];
+		}
+
+		fils_process_hlp_resp(sm, tmp, tmp_pos - tmp);
+	}
+
+	os_free(tmp);
+}
+
+
+int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
+{
+	const struct ieee80211_mgmt *mgmt;
+	const u8 *end, *ie_start;
+	struct ieee802_11_elems elems;
+	int keylen, rsclen;
+	enum wpa_alg alg;
+	struct wpa_gtk_data gd;
+	int maxkeylen;
+	struct wpa_eapol_ie_parse kde;
+
+	if (!sm || !sm->ptk_set) {
+		wpa_printf(MSG_DEBUG, "FILS: No KEK available");
+		return -1;
+	}
+
+	if (!wpa_key_mgmt_fils(sm->key_mgmt)) {
+		wpa_printf(MSG_DEBUG, "FILS: Not a FILS AKM");
+		return -1;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "FILS: (Re)Association Response frame",
+		    resp, len);
+
+	mgmt = (const struct ieee80211_mgmt *) resp;
+	if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_resp))
+		return -1;
+
+	end = resp + len;
+	/* Same offset for Association Response and Reassociation Response */
+	ie_start = mgmt->u.assoc_resp.variable;
+
+	if (ieee802_11_parse_elems(ie_start, end - ie_start, &elems, 1) ==
+	    ParseFailed) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Failed to parse decrypted elements");
+		goto fail;
+	}
+
+	if (!elems.fils_session) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Session element");
+		return -1;
+	}
+	if (os_memcmp(elems.fils_session, sm->fils_session,
+		      FILS_SESSION_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: FILS Session mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received FILS Session",
+			    elems.fils_session, FILS_SESSION_LEN);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected FILS Session",
+			    sm->fils_session, FILS_SESSION_LEN);
+	}
+
+	/* TODO: FILS Public Key */
+
+	if (!elems.fils_key_confirm) {
+		wpa_printf(MSG_DEBUG, "FILS: No FILS Key Confirm element");
+		goto fail;
+	}
+	if (elems.fils_key_confirm_len != sm->fils_key_auth_len) {
+		wpa_printf(MSG_DEBUG,
+			   "FILS: Unexpected Key-Auth length %d (expected %d)",
+			   elems.fils_key_confirm_len,
+			   (int) sm->fils_key_auth_len);
+		goto fail;
+	}
+	if (os_memcmp(elems.fils_key_confirm, sm->fils_key_auth_ap,
+		      sm->fils_key_auth_len) != 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Key-Auth mismatch");
+		wpa_hexdump(MSG_DEBUG, "FILS: Received Key-Auth",
+			    elems.fils_key_confirm,
+			    elems.fils_key_confirm_len);
+		wpa_hexdump(MSG_DEBUG, "FILS: Expected Key-Auth",
+			    sm->fils_key_auth_ap, sm->fils_key_auth_len);
+		goto fail;
+	}
+
+	/* Key Delivery */
+	if (!elems.key_delivery) {
+		wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
+		goto fail;
+	}
+
+	/* Parse GTK and set the key to the driver */
+	os_memset(&gd, 0, sizeof(gd));
+	if (wpa_supplicant_parse_ies(elems.key_delivery + WPA_KEY_RSC_LEN,
+				     elems.key_delivery_len - WPA_KEY_RSC_LEN,
+				     &kde) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to parse KDEs");
+		goto fail;
+	}
+	if (!kde.gtk) {
+		wpa_printf(MSG_DEBUG, "FILS: No GTK KDE");
+		goto fail;
+	}
+	maxkeylen = gd.gtk_len = kde.gtk_len - 2;
+	if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+					      gd.gtk_len, maxkeylen,
+					      &gd.key_rsc_len, &gd.alg))
+		goto fail;
+
+	wpa_hexdump_key(MSG_DEBUG, "FILS: Received GTK", kde.gtk, kde.gtk_len);
+	gd.keyidx = kde.gtk[0] & 0x3;
+	gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+						     !!(kde.gtk[0] & BIT(2)));
+	if (kde.gtk_len - 2 > sizeof(gd.gtk)) {
+		wpa_printf(MSG_DEBUG, "FILS: Too long GTK in GTK KDE (len=%lu)",
+			   (unsigned long) kde.gtk_len - 2);
+		goto fail;
+	}
+	os_memcpy(gd.gtk, kde.gtk + 2, kde.gtk_len - 2);
+
+	wpa_printf(MSG_DEBUG, "FILS: Set GTK to driver");
+	if (wpa_supplicant_install_gtk(sm, &gd, elems.key_delivery, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set GTK");
+		goto fail;
+	}
+
+	if (ieee80211w_set_keys(sm, &kde) < 0) {
+		wpa_printf(MSG_DEBUG, "FILS: Failed to set IGTK");
+		goto fail;
+	}
+
+	alg = wpa_cipher_to_alg(sm->pairwise_cipher);
+	keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+	rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
+	wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
+			sm->ptk.tk, keylen);
+	if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, null_rsc, rsclen,
+			   sm->ptk.tk, keylen) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+			"FILS: Failed to set PTK to the driver (alg=%d keylen=%d bssid="
+			MACSTR ")",
+			alg, keylen, MAC2STR(sm->bssid));
+		goto fail;
+	}
+
+	/* TODO: TK could be cleared after auth frame exchange now that driver
+	 * takes care of association frame encryption/decryption. */
+	/* TK is not needed anymore in supplicant */
+	os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+	sm->ptk.installed = 1;
+
+	/* FILS HLP Container */
+	fils_process_hlp_container(sm, ie_start, end - ie_start);
+
+	/* TODO: FILS IP Address Assignment */
+
+	wpa_printf(MSG_DEBUG, "FILS: Auth+Assoc completed successfully");
+	sm->fils_completed = 1;
+
+	return 0;
+fail:
+	return -1;
+}
+
+#endif /* CONFIG_FILS */
+
+
+int wpa_fils_is_completed(struct wpa_sm *sm)
+{
+#ifdef CONFIG_FILS
+	return sm && sm->fils_completed;
+#else /* CONFIG_FILS */
+	return 0;
+#endif /* CONFIG_FILS */
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index c89799a..bde8c78 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -25,7 +25,7 @@
 
 	void (*set_state)(void *ctx, enum wpa_states state);
 	enum wpa_states (*get_state)(void *ctx);
-	void (*deauthenticate)(void * ctx, int reason_code); 
+	void (*deauthenticate)(void * ctx, int reason_code);
 	int (*set_key)(void *ctx, enum wpa_alg alg,
 		       const u8 *addr, int key_idx, int set_tx,
 		       const u8 *seq, size_t seq_len,
@@ -38,8 +38,10 @@
 	void (*cancel_auth_timeout)(void *ctx);
 	u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
 			    size_t *msg_len, void **data_pos);
-	int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
-	int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+	int (*add_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
+			 const u8 *pmkid);
+	int (*remove_pmkid)(void *ctx, void *network_ctx, const u8 *bssid,
+			    const u8 *pmkid);
 	void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
 	const struct wpa_config_blob * (*get_config_blob)(void *ctx,
 							  const char *name);
@@ -77,6 +79,8 @@
 				  const u8 *kck, size_t kck_len,
 				  const u8 *replay_ctr);
 	int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
+	void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src,
+			    const u8 *pkt, size_t pkt_len);
 };
 
 
@@ -147,6 +151,10 @@
 		    const u8 *buf, size_t len);
 int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
 int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
+struct rsn_pmksa_cache_entry * wpa_sm_pmksa_cache_head(struct wpa_sm *sm);
+struct rsn_pmksa_cache_entry *
+wpa_sm_pmksa_cache_add_entry(struct wpa_sm *sm,
+			     struct rsn_pmksa_cache_entry * entry);
 void wpa_sm_drop_sa(struct wpa_sm *sm);
 int wpa_sm_has_ptk(struct wpa_sm *sm);
 
@@ -418,8 +426,22 @@
 				u8 oper_class,
 				struct hostapd_freq_params *freq_params);
 int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
+#ifdef CONFIG_TDLS_TESTING
+extern unsigned int tdls_testing;
+#endif /* CONFIG_TDLS_TESTING */
+
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 void wpa_sm_set_test_assoc_ie(struct wpa_sm *sm, struct wpabuf *buf);
 
+struct wpabuf * fils_build_auth(struct wpa_sm *sm);
+int fils_process_auth(struct wpa_sm *sm, const u8 *data, size_t len);
+struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
+				     size_t *kek_len, const u8 **snonce,
+				     const u8 **anonce,
+				     const struct wpabuf **hlp,
+				     unsigned int num_hlp);
+int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len);
+int wpa_fils_is_completed(struct wpa_sm *sm);
+
 #endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 56f88dc..b061638 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -144,6 +144,18 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	struct wpabuf *test_assoc_ie;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_FILS
+	u8 fils_nonce[FILS_NONCE_LEN];
+	u8 fils_session[FILS_SESSION_LEN];
+	u8 fils_anonce[FILS_NONCE_LEN];
+	u8 fils_key_auth_ap[FILS_MAX_KEY_AUTH_LEN];
+	u8 fils_key_auth_sta[FILS_MAX_KEY_AUTH_LEN];
+	size_t fils_key_auth_len;
+	unsigned int fils_completed:1;
+	unsigned int fils_erp_pmkid_set:1;
+	u8 fils_erp_pmkid[PMKID_LEN];
+#endif /* CONFIG_FILS */
 };
 
 
@@ -215,18 +227,18 @@
 				    msg_len, data_pos);
 }
 
-static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid,
-				   const u8 *pmkid)
+static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, void *network_ctx,
+				   const u8 *bssid, const u8 *pmkid)
 {
 	WPA_ASSERT(sm->ctx->add_pmkid);
-	return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid);
+	return sm->ctx->add_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid);
 }
 
-static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid,
-				      const u8 *pmkid)
+static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, void *network_ctx,
+				      const u8 *bssid, const u8 *pmkid)
 {
 	WPA_ASSERT(sm->ctx->remove_pmkid);
-	return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid);
+	return sm->ctx->remove_pmkid(sm->ctx->ctx, network_ctx, bssid, pmkid);
 }
 
 static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
@@ -359,7 +371,16 @@
 	return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
 }
 
-int wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
+static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm,
+				      const u8 *dst, const u8 *src,
+				      const u8 *pkt, size_t pkt_len)
+{
+	if (sm->ctx->fils_hlp_rx)
+		sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len);
+}
+
+
+int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
 		       int ver, const u8 *dest, u16 proto,
 		       u8 *msg, size_t msg_len, u8 *key_mic);
 int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index c44844e..3be3087 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -180,6 +180,18 @@
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+#ifdef CONFIG_FILS
+	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
+	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
+#ifdef CONFIG_IEEE80211R
+	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
+	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
 	} else {
 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
 			   key_mgmt);
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
index 8bc824f..4f7a148 100644
--- a/src/tls/libtommath.c
+++ b/src/tls/libtommath.c
@@ -116,7 +116,7 @@
       #define MP_PREC                 32     /* default digits of precision */
    #else
       #define MP_PREC                 8      /* default digits of precision */
-   #endif   
+   #endif
 #endif
 
 /* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
@@ -274,8 +274,8 @@
       *tmpc++ &= MP_MASK;
     }
 
-    /* now copy higher words if any, that is in A+B 
-     * if A or B has more digits add those in 
+    /* now copy higher words if any, that is in A+B
+     * if A or B has more digits add those in
      */
     if (min != max) {
       for (; i < max; i++) {
@@ -499,29 +499,29 @@
 #ifdef BN_MP_TOOM_MUL_C
   if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) {
     res = mp_toom_mul(a, b, c);
-  } else 
+  } else
 #endif
 #ifdef BN_MP_KARATSUBA_MUL_C
   /* use Karatsuba? */
   if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
     res = mp_karatsuba_mul (a, b, c);
-  } else 
+  } else
 #endif
   {
     /* can we use the fast multiplier?
      *
-     * The fast multiplier can be used if the output will 
-     * have less than MP_WARRAY digits and the number of 
+     * The fast multiplier can be used if the output will
+     * have less than MP_WARRAY digits and the number of
      * digits won't affect carry propagation
      */
 #ifdef BN_FAST_S_MP_MUL_DIGS_C
     int     digs = a->used + b->used + 1;
 
     if ((digs < MP_WARRAY) &&
-        MIN(a->used, b->used) <= 
+        MIN(a->used, b->used) <=
         (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
       res = fast_s_mp_mul_digs (a, b, c, digs);
-    } else 
+    } else
 #endif
 #ifdef BN_S_MP_MUL_DIGS_C
       res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */
@@ -629,7 +629,7 @@
      err = mp_exptmod(&tmpG, &tmpX, P, Y);
      mp_clear_multi(&tmpG, &tmpX, NULL);
      return err;
-#else 
+#else
 #error mp_exptmod would always fail
      /* no invmod */
      return MP_VAL;
@@ -658,7 +658,7 @@
      dr = mp_reduce_is_2k(P) << 1;
   }
 #endif
-    
+
   /* if the modulus is odd or dr != 0 use the montgomery method */
 #ifdef BN_MP_EXPTMOD_FAST_C
   if (mp_isodd (P) == 1 || dr !=  0) {
@@ -693,7 +693,7 @@
         return MP_GT;
      }
   }
-  
+
   /* compare digits */
   if (a->sign == MP_NEG) {
      /* if negative compare opposite direction */
@@ -779,7 +779,7 @@
   }
 
   /* init temps */
-  if ((res = mp_init_multi(&x, &y, &u, &v, 
+  if ((res = mp_init_multi(&x, &y, &u, &v,
                            &A, &B, &C, &D, NULL)) != MP_OKAY) {
      return res;
   }
@@ -906,14 +906,14 @@
          goto LBL_ERR;
       }
   }
-  
+
   /* too big */
   while (mp_cmp_mag(&C, b) != MP_LT) {
       if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
          goto LBL_ERR;
       }
   }
-  
+
   /* C is now the inverse */
   mp_exch (&C, c);
   res = MP_OKAY;
@@ -933,7 +933,7 @@
   if (a->used > b->used) {
     return MP_GT;
   }
-  
+
   if (a->used < b->used) {
     return MP_LT;
   }
@@ -1199,8 +1199,8 @@
     /* top [offset into digits] */
     top = a->dp + b;
 
-    /* this is implemented as a sliding window where 
-     * the window is b-digits long and digits from 
+    /* this is implemented as a sliding window where
+     * the window is b-digits long and digits from
      * the top of the window are copied to the bottom
      *
      * e.g.
@@ -1218,13 +1218,13 @@
       *bottom++ = 0;
     }
   }
-  
+
   /* remove excess digits */
   a->used -= b;
 }
 
 
-/* swap the elements of two integers, for cases where you can't simply swap the 
+/* swap the elements of two integers, for cases where you can't simply swap the
  * mp_int pointers around
  */
 static void mp_exch (mp_int * a, mp_int * b)
@@ -1237,7 +1237,7 @@
 }
 
 
-/* trim unused digits 
+/* trim unused digits
  *
  * This is used to ensure that leading zero digits are
  * trimed and the leading "used" digit will be non-zero
@@ -1298,7 +1298,7 @@
 
 
 #ifdef BN_MP_ABS_C
-/* b = |a| 
+/* b = |a|
  *
  * Simple function copies the input and fixes the sign to positive
  */
@@ -1434,7 +1434,7 @@
       /* set the carry to the carry bits of the current word */
       r = rr;
     }
-    
+
     /* set final carry */
     if (r != 0) {
        c->dp[(c->used)++] = r;
@@ -1446,7 +1446,7 @@
 
 
 #ifdef BN_MP_INIT_MULTI_C
-static int mp_init_multi(mp_int *mp, ...) 
+static int mp_init_multi(mp_int *mp, ...)
 {
     mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */
     int n = 0;                 /* Number of ok inits */
@@ -1460,11 +1460,11 @@
                succeeded in init-ing, then return error.
             */
             va_list clean_args;
-            
+
             /* end the current list */
             va_end(args);
-            
-            /* now start cleaning up */            
+
+            /* now start cleaning up */
             cur_arg = mp;
             va_start(clean_args, mp);
             while (n--) {
@@ -1484,7 +1484,7 @@
 
 
 #ifdef BN_MP_CLEAR_MULTI_C
-static void mp_clear_multi(mp_int *mp, ...) 
+static void mp_clear_multi(mp_int *mp, ...)
 {
     mp_int* next_mp = mp;
     va_list args;
@@ -1558,7 +1558,7 @@
 
   /* get number of digits and add that */
   r = (a->used - 1) * DIGIT_BIT;
-  
+
   /* take the last digit and count the bits in it */
   q = a->dp[a->used - 1];
   while (q > ((mp_digit) 0)) {
@@ -1628,7 +1628,7 @@
     }
     return res;
   }
-	
+
   /* init our temps */
   if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL)) != MP_OKAY) {
      return res;
@@ -1638,7 +1638,7 @@
   mp_set(&tq, 1);
   n = mp_count_bits(a) - mp_count_bits(b);
   if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
-      ((res = mp_abs(b, &tb)) != MP_OKAY) || 
+      ((res = mp_abs(b, &tb)) != MP_OKAY) ||
       ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
       ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
       goto LBL_ERR;
@@ -1675,17 +1675,17 @@
 
 #else
 
-/* integer signed division. 
+/* integer signed division.
  * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
  * HAC pp.598 Algorithm 14.20
  *
- * Note that the description in HAC is horribly 
- * incomplete.  For example, it doesn't consider 
- * the case where digits are removed from 'x' in 
- * the inner loop.  It also doesn't consider the 
+ * Note that the description in HAC is horribly
+ * incomplete.  For example, it doesn't consider
+ * the case where digits are removed from 'x' in
+ * the inner loop.  It also doesn't consider the
  * case that y has fewer than three digits, etc..
  *
- * The overall algorithm is as described as 
+ * The overall algorithm is as described as
  * 14.20 from HAC but fixed to treat these cases.
 */
 static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
@@ -1775,7 +1775,7 @@
       continue;
     }
 
-    /* step 3.1 if xi == yt then set q{i-t-1} to b-1, 
+    /* step 3.1 if xi == yt then set q{i-t-1} to b-1,
      * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
     if (x.dp[i] == y.dp[t]) {
       q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
@@ -1789,10 +1789,10 @@
       q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
     }
 
-    /* while (q{i-t-1} * (yt * b + y{t-1})) > 
-             xi * b**2 + xi-1 * b + xi-2 
-     
-       do q{i-t-1} -= 1; 
+    /* while (q{i-t-1} * (yt * b + y{t-1})) >
+             xi * b**2 + xi-1 * b + xi-2
+
+       do q{i-t-1} -= 1;
     */
     q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
     do {
@@ -1843,10 +1843,10 @@
     }
   }
 
-  /* now q is the quotient and x is the remainder 
-   * [which we have to normalize] 
+  /* now q is the quotient and x is the remainder
+   * [which we have to normalize]
    */
-  
+
   /* get sign before writing to c */
   x.sign = x.used == 0 ? MP_ZPOS : a->sign;
 
@@ -1914,7 +1914,7 @@
   /* init M array */
   /* init first cell */
   if ((err = mp_init(&M[1])) != MP_OKAY) {
-     return err; 
+     return err;
   }
 
   /* now init the second half of the array */
@@ -1932,7 +1932,7 @@
   if ((err = mp_init (&mu)) != MP_OKAY) {
     goto LBL_M;
   }
-  
+
   if (redmode == 0) {
      if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) {
         goto LBL_MU;
@@ -1943,22 +1943,22 @@
         goto LBL_MU;
      }
      redux = mp_reduce_2k_l;
-  }    
+  }
 
   /* create M table
    *
-   * The M table contains powers of the base, 
+   * The M table contains powers of the base,
    * e.g. M[x] = G**x mod P
    *
-   * The first half of the table is not 
+   * The first half of the table is not
    * computed though accept for M[0] and M[1]
    */
   if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) {
     goto LBL_MU;
   }
 
-  /* compute the value at M[1<<(winsize-1)] by squaring 
-   * M[1] (winsize-1) times 
+  /* compute the value at M[1<<(winsize-1)] by squaring
+   * M[1] (winsize-1) times
    */
   if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
     goto LBL_MU;
@@ -1966,7 +1966,7 @@
 
   for (x = 0; x < (winsize - 1); x++) {
     /* square it */
-    if ((err = mp_sqr (&M[1 << (winsize - 1)], 
+    if ((err = mp_sqr (&M[1 << (winsize - 1)],
                        &M[1 << (winsize - 1)])) != MP_OKAY) {
       goto LBL_MU;
     }
@@ -2117,18 +2117,18 @@
   if (a->used >= TOOM_SQR_CUTOFF) {
     res = mp_toom_sqr(a, b);
   /* Karatsuba? */
-  } else 
+  } else
 #endif
 #ifdef BN_MP_KARATSUBA_SQR_C
 if (a->used >= KARATSUBA_SQR_CUTOFF) {
     res = mp_karatsuba_sqr (a, b);
-  } else 
+  } else
 #endif
   {
 #ifdef BN_FAST_S_MP_SQR_C
     /* can we use the fast comba multiplier? */
-    if ((a->used * 2 + 1) < MP_WARRAY && 
-         a->used < 
+    if ((a->used * 2 + 1) < MP_WARRAY &&
+         a->used <
          (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) {
       res = fast_s_mp_sqr (a, b);
     } else
@@ -2145,7 +2145,7 @@
 }
 
 
-/* reduces a modulo n where n is of the form 2**p - d 
+/* reduces a modulo n where n is of the form 2**p - d
    This differs from reduce_2k since "d" can be larger
    than a single digit.
 */
@@ -2153,33 +2153,33 @@
 {
    mp_int q;
    int    p, res;
-   
+
    if ((res = mp_init(&q)) != MP_OKAY) {
       return res;
    }
-   
-   p = mp_count_bits(n);    
+
+   p = mp_count_bits(n);
 top:
    /* q = a/2**p, a = a mod 2**p */
    if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
       goto ERR;
    }
-   
+
    /* q = q * d */
-   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { 
+   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) {
       goto ERR;
    }
-   
+
    /* a = a + q */
    if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
       goto ERR;
    }
-   
+
    if (mp_cmp_mag(a, n) != MP_LT) {
       s_mp_sub(a, n, a);
       goto top;
    }
-   
+
 ERR:
    mp_clear(&q);
    return res;
@@ -2191,26 +2191,26 @@
 {
    int    res;
    mp_int tmp;
-   
+
    if ((res = mp_init(&tmp)) != MP_OKAY) {
       return res;
    }
-   
+
    if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
       goto ERR;
    }
-   
+
    if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
       goto ERR;
    }
-   
+
 ERR:
    mp_clear(&tmp);
    return res;
 }
 
 
-/* computes a = 2**b 
+/* computes a = 2**b
  *
  * Simple algorithm which zeroes the int, grows it then just sets one bit
  * as required.
@@ -2243,7 +2243,7 @@
 static int mp_reduce_setup (mp_int * a, mp_int * b)
 {
   int     res;
-  
+
   if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
     return res;
   }
@@ -2251,7 +2251,7 @@
 }
 
 
-/* reduces x mod m, assumes 0 < x < m**2, mu is 
+/* reduces x mod m, assumes 0 < x < m**2, mu is
  * precomputed via mp_reduce_setup.
  * From HAC pp.604 Algorithm 14.42
  */
@@ -2266,7 +2266,7 @@
   }
 
   /* q1 = x / b**(k-1)  */
-  mp_rshd (&q, um - 1);         
+  mp_rshd (&q, um - 1);
 
   /* according to HAC this optimization is ok */
   if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
@@ -2282,8 +2282,8 @@
     if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
       goto CLEANUP;
     }
-#else 
-    { 
+#else
+    {
 #error mp_reduce would always fail
       res = MP_VAL;
       goto CLEANUP;
@@ -2292,7 +2292,7 @@
   }
 
   /* q3 = q2 / b**(k+1) */
-  mp_rshd (&q, um + 1);         
+  mp_rshd (&q, um + 1);
 
   /* x = x mod b**(k+1), quick (no division) */
   if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
@@ -2326,7 +2326,7 @@
       goto CLEANUP;
     }
   }
-  
+
 CLEANUP:
   mp_clear (&q);
 
@@ -2335,7 +2335,7 @@
 
 
 /* multiplies |a| * |b| and only computes up to digs digits of result
- * HAC pp. 595, Algorithm 14.12  Modified so you can control how 
+ * HAC pp. 595, Algorithm 14.12  Modified so you can control how
  * many digits of output are created.
  */
 static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
@@ -2349,7 +2349,7 @@
 #ifdef BN_FAST_S_MP_MUL_DIGS_C
   /* can we use the fast multiplier? */
   if (((digs) < MP_WARRAY) &&
-      MIN (a->used, b->used) < 
+      MIN (a->used, b->used) <
           (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
     return fast_s_mp_mul_digs (a, b, c, digs);
   }
@@ -2372,10 +2372,10 @@
     /* setup some aliases */
     /* copy of the digit from a used within the nested loop */
     tmpx = a->dp[ix];
-    
+
     /* an alias for the destination shifted ix places */
     tmpt = t.dp + ix;
-    
+
     /* an alias for the digits of b */
     tmpy = b->dp;
 
@@ -2409,15 +2409,15 @@
 #ifdef BN_FAST_S_MP_MUL_DIGS_C
 /* Fast (comba) multiplier
  *
- * This is the fast column-array [comba] multiplier.  It is 
- * designed to compute the columns of the product first 
- * then handle the carries afterwards.  This has the effect 
+ * This is the fast column-array [comba] multiplier.  It is
+ * designed to compute the columns of the product first
+ * then handle the carries afterwards.  This has the effect
  * of making the nested loops that compute the columns very
  * simple and schedulable on super-scalar processors.
  *
- * This has been modified to produce a variable number of 
- * digits of output so if say only a half-product is required 
- * you don't have to compute the upper half (a feature 
+ * This has been modified to produce a variable number of
+ * digits of output so if say only a half-product is required
+ * you don't have to compute the upper half (a feature
  * required for fast Barrett reduction).
  *
  * Based on Algorithm 14.12 on pp.595 of HAC.
@@ -2441,7 +2441,7 @@
 
   /* clear the carry */
   _W = 0;
-  for (ix = 0; ix < pa; ix++) { 
+  for (ix = 0; ix < pa; ix++) {
       int      tx, ty;
       int      iy;
       mp_digit *tmpx, *tmpy;
@@ -2454,7 +2454,7 @@
       tmpx = a->dp + tx;
       tmpy = b->dp + ty;
 
-      /* this is the number of times the loop will iterrate, essentially 
+      /* this is the number of times the loop will iterrate, essentially
          while (tx++ < a->used && ty-- >= 0) { ... }
        */
       iy = MIN(a->used-tx, ty+1);
@@ -2501,8 +2501,8 @@
   int x;
 
   /* pad size so there are always extra digits */
-  size += (MP_PREC * 2) - (size % MP_PREC);	
-  
+  size += (MP_PREC * 2) - (size % MP_PREC);
+
   /* alloc mem */
   a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size);
   if (a->dp == NULL) {
@@ -2556,7 +2556,7 @@
 
     /* alias for where to store the results */
     tmpt        = t.dp + (2*ix + 1);
-    
+
     for (iy = ix + 1; iy < pa; iy++) {
       /* first calculate the product */
       r       = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
@@ -2863,24 +2863,24 @@
 
     /* alias for source */
     tmpa = a->dp;
-    
+
     /* alias for dest */
     tmpb = b->dp;
 
     /* carry */
     r = 0;
     for (x = 0; x < a->used; x++) {
-    
-      /* get what will be the *next* carry bit from the 
-       * MSB of the current digit 
+
+      /* get what will be the *next* carry bit from the
+       * MSB of the current digit
        */
       rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
-      
+
       /* now shift up this digit, add in the carry [from the previous] */
       *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
-      
-      /* copy the carry that would be from the source 
-       * digit into the next iteration 
+
+      /* copy the carry that would be from the source
+       * digit into the next iteration
        */
       r = rr;
     }
@@ -2892,8 +2892,8 @@
       ++(b->used);
     }
 
-    /* now zero any excess digits on the destination 
-     * that we didn't write to 
+    /* now zero any excess digits on the destination
+     * that we didn't write to
      */
     tmpb = b->dp + b->used;
     for (x = b->used; x < oldused; x++) {
@@ -3011,7 +3011,7 @@
 
   /* determine and setup reduction code */
   if (redmode == 0) {
-#ifdef BN_MP_MONTGOMERY_SETUP_C     
+#ifdef BN_MP_MONTGOMERY_SETUP_C
      /* now setup montgomery  */
      if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) {
         goto LBL_M;
@@ -3026,7 +3026,7 @@
      if (((P->used * 2 + 1) < MP_WARRAY) &&
           P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
         redux = fast_mp_montgomery_reduce;
-     } else 
+     } else
 #endif
      {
 #ifdef BN_MP_MONTGOMERY_REDUCE_C
@@ -3077,7 +3077,7 @@
      if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) {
        goto LBL_RES;
      }
-#else 
+#else
      err = MP_VAL;
      goto LBL_RES;
 #endif
@@ -3245,10 +3245,10 @@
 
 #ifdef BN_FAST_S_MP_SQR_C
 /* the jist of squaring...
- * you do like mult except the offset of the tmpx [one that 
- * starts closer to zero] can't equal the offset of tmpy.  
+ * you do like mult except the offset of the tmpx [one that
+ * starts closer to zero] can't equal the offset of tmpy.
  * So basically you set up iy like before then you min it with
- * (ty-tx) so that it never happens.  You double all those 
+ * (ty-tx) so that it never happens.  You double all those
  * you add in the inner loop
 
 After that loop you do the squares and add them in.
@@ -3270,7 +3270,7 @@
 
   /* number of output digits to produce */
   W1 = 0;
-  for (ix = 0; ix < pa; ix++) { 
+  for (ix = 0; ix < pa; ix++) {
       int      tx, ty, iy;
       mp_word  _W;
       mp_digit *tmpy;
@@ -3291,7 +3291,7 @@
        */
       iy = MIN(a->used-tx, ty+1);
 
-      /* now for squaring tx can never equal ty 
+      /* now for squaring tx can never equal ty
        * we halve the distance since they approach at a rate of 2x
        * and we have to round because odd cases need to be executed
        */
diff --git a/src/tls/rsa.c b/src/tls/rsa.c
index 0b7b530..3525eb9 100644
--- a/src/tls/rsa.c
+++ b/src/tls/rsa.c
@@ -80,7 +80,7 @@
 	 * PKCS #1, 7.1:
 	 * RSAPublicKey ::= SEQUENCE {
 	 *     modulus INTEGER, -- n
-	 *     publicExponent INTEGER -- e 
+	 *     publicExponent INTEGER -- e
 	 * }
 	 */
 
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 9bc0d21..76e1974 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -264,7 +264,7 @@
  * @in_data: Pointer to plaintext data to be encrypted
  * @in_len: Input buffer length
  * @out_data: Pointer to output buffer (encrypted TLS data)
- * @out_len: Maximum out_data length 
+ * @out_len: Maximum out_data length
  * Returns: Number of bytes written to out_data, -1 on failure
  *
  * This function is used after TLS handshake has been completed successfully to
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
index 6b28417..e178915 100644
--- a/src/tls/tlsv1_common.c
+++ b/src/tls/tlsv1_common.c
@@ -21,7 +21,7 @@
  * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
  * Add support for commonly used cipher suites; don't bother with exportable
  * suites.
- */ 
+ */
 
 static const struct tls_cipher_suite tls_cipher_suites[] = {
 	{ TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
@@ -482,21 +482,21 @@
 		    os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
 			      "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
 		{
-			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-256");
 			decrypted = buf + 19;
 			buflen -= 19;
 		} else if (buflen >= 19 + 48 &&
 		    os_memcmp(buf, "\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01"
 			      "\x65\x03\x04\x02\x02\x05\x00\x04\x30", 19) == 0)
 		{
-			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-384");
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-384");
 			decrypted = buf + 19;
 			buflen -= 19;
 		} else if (buflen >= 19 + 64 &&
 		    os_memcmp(buf, "\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01"
 			      "\x65\x03\x04\x02\x03\x05\x00\x04\x40", 19) == 0)
 		{
-			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-512");
+			wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithm = SHA-512");
 			decrypted = buf + 19;
 			buflen -= 19;
 
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index ba47337..5406969 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -216,7 +216,7 @@
  * @in_data: Pointer to plaintext data to be encrypted
  * @in_len: Input buffer length
  * @out_data: Pointer to output buffer (encrypted TLS data)
- * @out_len: Maximum out_data length 
+ * @out_len: Maximum out_data length
  * Returns: Number of bytes written to out_data, -1 on failure
  *
  * This function is used after TLS handshake has been completed successfully to
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index 75f222c..0d6ee67 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -2039,7 +2039,7 @@
 
 	for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
 		cert->issuer_trusted = 0;
-		x509_name_string(&cert->subject, buf, sizeof(buf)); 
+		x509_name_string(&cert->subject, buf, sizeof(buf));
 		wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
 
 		if (chain_trusted)
@@ -2063,11 +2063,11 @@
 				wpa_printf(MSG_DEBUG, "X509: Certificate "
 					   "chain issuer name mismatch");
 				x509_name_string(&cert->issuer, buf,
-						 sizeof(buf)); 
+						 sizeof(buf));
 				wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
 					   buf);
 				x509_name_string(&cert->next->subject, buf,
-						 sizeof(buf)); 
+						 sizeof(buf));
 				wpa_printf(MSG_DEBUG, "X509: next cert "
 					   "subject: %s", buf);
 				*reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 8aad813..52efc53 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -17,6 +17,7 @@
 	base64.o \
 	bitfield.o \
 	common.o \
+	crc32.o \
 	ip_addr.o \
 	radiotap.o \
 	trace.o \
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
index 59ba4d1..062e6fe 100644
--- a/src/utils/browser-wpadebug.c
+++ b/src/utils/browser-wpadebug.c
@@ -52,7 +52,7 @@
 			eloop_terminate();
 		return;
 	}
-	wpabuf_put_str(resp, "User input completed");
+	wpabuf_put_str(resp, "HTTP/1.1\r\n\r\nUser input completed");
 
 	if (done) {
 		eloop_cancel_timeout(browser_timeout, NULL, NULL);
diff --git a/src/utils/common.c b/src/utils/common.c
index 9c7d0d4..1eb3370 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -697,6 +697,29 @@
 }
 
 
+int has_ctrl_char(const u8 *data, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len; i++) {
+		if (data[i] < 32 || data[i] == 127)
+			return 1;
+	}
+	return 0;
+}
+
+
+int has_newline(const char *str)
+{
+	while (*str) {
+		if (*str == '\n' || *str == '\r')
+			return 1;
+		str++;
+	}
+	return 0;
+}
+
+
 size_t merge_byte_arrays(u8 *res, size_t res_len,
 			 const u8 *src1, size_t src1_len,
 			 const u8 *src2, size_t src2_len)
@@ -978,7 +1001,7 @@
  * @delim: a string of delimiters
  * @last: a pointer to a character following the returned token
  *      It has to be set to NULL for the first call and passed for any
- *      futher call.
+ *      further call.
  * Returns: a pointer to token position in str or NULL
  *
  * This function is similar to str_token, but it can be used with both
@@ -1123,3 +1146,78 @@
 {
 	return c > 0 && c < 32;
 }
+
+
+/**
+ * ssid_parse - Parse a string that contains SSID in hex or text format
+ * @buf: Input NULL terminated string that contains the SSID
+ * @ssid: Output SSID
+ * Returns: 0 on success, -1 otherwise
+ *
+ * The SSID has to be enclosed in double quotes for the text format or space
+ * or NULL terminated string of hex digits for the hex format. buf can include
+ * additional arguments after the SSID.
+ */
+int ssid_parse(const char *buf, struct wpa_ssid_value *ssid)
+{
+	char *tmp, *res, *end;
+	size_t len;
+
+	ssid->ssid_len = 0;
+
+	tmp = os_strdup(buf);
+	if (!tmp)
+		return -1;
+
+	if (*tmp != '"') {
+		end = os_strchr(tmp, ' ');
+		if (end)
+			*end = '\0';
+	} else {
+		end = os_strchr(tmp + 1, '"');
+		if (!end) {
+			os_free(tmp);
+			return -1;
+		}
+
+		end[1] = '\0';
+	}
+
+	res = wpa_config_parse_string(tmp, &len);
+	if (res && len <= SSID_MAX_LEN) {
+		ssid->ssid_len = len;
+		os_memcpy(ssid->ssid, res, len);
+	}
+
+	os_free(tmp);
+	os_free(res);
+
+	return ssid->ssid_len ? 0 : -1;
+}
+
+
+int str_starts(const char *str, const char *start)
+{
+	return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+/**
+ * rssi_to_rcpi - Convert RSSI to RCPI
+ * @rssi: RSSI to convert
+ * Returns: RCPI corresponding to the given RSSI value, or 255 if not available.
+ *
+ * It's possible to estimate RCPI based on RSSI in dBm. This calculation will
+ * not reflect the correct value for high rates, but it's good enough for Action
+ * frames which are transmitted with up to 24 Mbps rates.
+ */
+u8 rssi_to_rcpi(int rssi)
+{
+	if (!rssi)
+		return 255; /* not available */
+	if (rssi < -110)
+		return 0;
+	if (rssi > 0)
+		return 220;
+	return (rssi + 110) * 2;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index 6f0de69..8842864 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -419,6 +419,7 @@
  */
 #ifdef __CHECKER__
 #define __force __attribute__((force))
+#undef __bitwise
 #define __bitwise __attribute__((bitwise))
 #else
 #define __force
@@ -448,6 +449,13 @@
 #endif /* __GNUC__ */
 #endif /* __must_check */
 
+#define SSID_MAX_LEN 32
+
+struct wpa_ssid_value {
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+};
+
 int hwaddr_aton(const char *txt, u8 *addr);
 int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
 int hwaddr_compact_aton(const char *txt, u8 *addr);
@@ -464,6 +472,7 @@
 			       size_t len);
 
 int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+int ssid_parse(const char *buf, struct wpa_ssid_value *ssid);
 
 #ifdef CONFIG_NATIVE_WINDOWS
 void wpa_unicode2ascii_inplace(TCHAR *str);
@@ -480,6 +489,8 @@
 
 char * wpa_config_parse_string(const char *value, size_t *len);
 int is_hex(const u8 *data, size_t len);
+int has_ctrl_char(const u8 *data, size_t len);
+int has_newline(const char *str);
 size_t merge_byte_arrays(u8 *res, size_t res_len,
 			 const u8 *src1, size_t src1_len,
 			 const u8 *src2, size_t src2_len);
@@ -539,6 +550,9 @@
 		     char *outp, size_t out_size);
 int is_ctrl_char(char c);
 
+int str_starts(const char *str, const char *start);
+
+u8 rssi_to_rcpi(int rssi);
 
 /*
  * gcc 4.4 ends up generating strict-aliasing warnings about some very common
diff --git a/src/utils/crc32.h b/src/utils/crc32.h
new file mode 100644
index 0000000..dc31399
--- /dev/null
+++ b/src/utils/crc32.h
@@ -0,0 +1,14 @@
+/*
+ * 32-bit CRC for FCS calculation
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef CRC32_H
+#define CRC32_H
+
+u32 crc32(const u8 *frame, size_t frame_len);
+
+#endif /* CRC32_H */
diff --git a/src/utils/edit_simple.c b/src/utils/edit_simple.c
index 13173cb..2ffd1a2 100644
--- a/src/utils/edit_simple.c
+++ b/src/utils/edit_simple.c
@@ -47,6 +47,12 @@
 		return;
 	}
 
+	if (c == '\b') {
+		if (cmdbuf_pos > 0)
+			cmdbuf_pos--;
+		return;
+	}
+
 	if (c >= 32 && c <= 255) {
 		if (cmdbuf_pos < (int) sizeof(cmdbuf) - 1) {
 			cmdbuf[cmdbuf_pos++] = c;
diff --git a/src/utils/eloop.h b/src/utils/eloop.h
index 97af16f..04ee6d1 100644
--- a/src/utils/eloop.h
+++ b/src/utils/eloop.h
@@ -45,16 +45,16 @@
 /**
  * eloop_event_handler - eloop generic event callback type
  * @eloop_ctx: Registered callback context data (eloop_data)
- * @sock_ctx: Registered callback context data (user_data)
+ * @user_ctx: Registered callback context data (user_data)
  */
-typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx);
+typedef void (*eloop_event_handler)(void *eloop_ctx, void *user_ctx);
 
 /**
  * eloop_timeout_handler - eloop timeout event callback type
  * @eloop_ctx: Registered callback context data (eloop_data)
- * @sock_ctx: Registered callback context data (user_data)
+ * @user_ctx: Registered callback context data (user_data)
  */
-typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx);
+typedef void (*eloop_timeout_handler)(void *eloop_ctx, void *user_ctx);
 
 /**
  * eloop_signal_handler - eloop signal event callback type
diff --git a/src/utils/ext_password.c b/src/utils/ext_password.c
index 0613119..5615bd7 100644
--- a/src/utils/ext_password.c
+++ b/src/utils/ext_password.c
@@ -16,10 +16,6 @@
 #include "ext_password_i.h"
 
 
-#ifdef CONFIG_EXT_PASSWORD_TEST
-extern struct ext_password_backend ext_password_test;
-#endif /* CONFIG_EXT_PASSWORD_TEST */
-
 static const struct ext_password_backend *backends[] = {
 #ifdef CONFIG_EXT_PASSWORD_TEST
 	&ext_password_test,
diff --git a/src/utils/ext_password_i.h b/src/utils/ext_password_i.h
index 043e731..948eaf5 100644
--- a/src/utils/ext_password_i.h
+++ b/src/utils/ext_password_i.h
@@ -20,4 +20,10 @@
 
 struct wpabuf * ext_password_alloc(size_t len);
 
+/* Available ext_password backends */
+
+#ifdef CONFIG_EXT_PASSWORD_TEST
+extern const struct ext_password_backend ext_password_test;
+#endif /* CONFIG_EXT_PASSWORD_TEST */
+
 #endif /* EXT_PASSWORD_I_H */
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index d594398..a06aae8 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -857,10 +857,8 @@
 	os_memset(hcert, 0, sizeof(*hcert));
 
 	*names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-	if (*names) {
+	if (*names)
 		add_alt_names(ctx, hcert, *names);
-		sk_GENERAL_NAME_pop_free(*names, GENERAL_NAME_free);
-	}
 
 	add_logotype_ext(ctx, hcert, cert);
 }
diff --git a/src/utils/module_tests.h b/src/utils/module_tests.h
new file mode 100644
index 0000000..3bfe4ad
--- /dev/null
+++ b/src/utils/module_tests.h
@@ -0,0 +1,20 @@
+/*
+ * Module tests
+ * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MODULE_TESTS_H
+#define MODULE_TESTS_H
+
+int wpas_module_tests(void);
+int hapd_module_tests(void);
+
+int utils_module_tests(void);
+int wps_module_tests(void);
+int common_module_tests(void);
+int crypto_module_tests(void);
+
+#endif /* MODULE_TESTS_H */
diff --git a/src/utils/os.h b/src/utils/os.h
index 9e496fb..e8f0b79 100644
--- a/src/utils/os.h
+++ b/src/utils/os.h
@@ -657,6 +657,10 @@
 #if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
 #define TEST_FAIL() testing_test_fail()
 int testing_test_fail(void);
+extern char wpa_trace_fail_func[256];
+extern unsigned int wpa_trace_fail_after;
+extern char wpa_trace_test_fail_func[256];
+extern unsigned int wpa_trace_test_fail_after;
 #else
 #define TEST_FAIL() 0
 #endif
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index 0118d98..26fd172 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -80,6 +80,9 @@
 	struct timespec ts;
 	int res;
 
+	if (TEST_FAIL())
+		return -1;
+
 	while (1) {
 		res = clock_gettime(clock_id, &ts);
 		if (res == 0) {
@@ -435,11 +438,7 @@
 
 int os_file_exists(const char *fname)
 {
-	FILE *f = fopen(fname, "rb");
-	if (f == NULL)
-		return 0;
-	fclose(f);
-	return 1;
+	return access(fname, F_OK) == 0;
 }
 
 
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
index 2f1157b..383ed3d 100644
--- a/src/utils/pcsc_funcs.c
+++ b/src/utils/pcsc_funcs.c
@@ -11,7 +11,11 @@
  */
 
 #include "includes.h"
+#ifdef __APPLE__
+#include <PCSC/winscard.h>
+#else
 #include <winscard.h>
+#endif
 
 #include "common.h"
 #include "pcsc_funcs.h"
@@ -110,7 +114,11 @@
 struct scard_data {
 	SCARDCONTEXT ctx;
 	SCARDHANDLE card;
+#ifdef __APPLE__
+	uint32_t protocol;
+#else
 	DWORD protocol;
+#endif
 	sim_types sim_type;
 	int pin1_required;
 };
@@ -504,7 +512,12 @@
 struct scard_data * scard_init(const char *reader)
 {
 	long ret;
-	unsigned long len, pos;
+#ifdef __APPLE__
+	uint32_t len;
+#else
+	unsigned long len;
+#endif
+	unsigned long pos;
 	struct scard_data *scard;
 #ifdef CONFIG_NATIVE_WINDOWS
 	TCHAR *readers = NULL;
@@ -605,7 +618,7 @@
 	readers = NULL;
 
 	wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)",
-		   (unsigned int) scard->card, scard->protocol,
+		   (unsigned int) scard->card, (unsigned long) scard->protocol,
 		   scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1");
 
 	ret = SCardBeginTransaction(scard->card);
@@ -764,7 +777,11 @@
 			   unsigned char *_recv, size_t *recv_len)
 {
 	long ret;
+#ifdef __APPLE__
+	uint32_t rlen;
+#else
 	unsigned long rlen;
+#endif
 
 	wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
 			_send, send_len);
diff --git a/src/utils/platform.h b/src/utils/platform.h
index 46cfe78..813987e 100644
--- a/src/utils/platform.h
+++ b/src/utils/platform.h
@@ -15,7 +15,7 @@
 								\
 	__ptr->__val;						\
 })
-#define get_unaligned_le16(p)	le16_to_cpu(get_unaligned((uint16_t *)(p)))
-#define get_unaligned_le32(p)	le32_to_cpu(get_unaligned((uint32_t *)(p)))
+#define get_unaligned_le16(p)	le16_to_cpu(get_unaligned((le16 *)(p)))
+#define get_unaligned_le32(p)	le32_to_cpu(get_unaligned((le32 *)(p)))
 
 #endif /* PLATFORM_H */
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
index c9a5023..71996eb 100644
--- a/src/utils/radiotap.c
+++ b/src/utils/radiotap.c
@@ -13,8 +13,8 @@
  *
  * See COPYING for more details.
  */
-#include "radiotap_iter.h"
 #include "platform.h"
+#include "radiotap_iter.h"
 
 /* function prototypes and related defs are in radiotap_iter.h */
 
diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h
index 0572e7c..460af23 100644
--- a/src/utils/radiotap.h
+++ b/src/utils/radiotap.h
@@ -65,12 +65,12 @@
 				 * new fields does not count.
 				 */
 	uint8_t it_pad;
-	uint16_t it_len;	/* length of the whole
+	le16 it_len;		/* length of the whole
 				 * header in bytes, including
 				 * it_version, it_pad,
 				 * it_len, and data fields.
 				 */
-	uint32_t it_present;	/* A bitmap telling which
+	le32 it_present;	/* A bitmap telling which
 				 * fields are present. Set bit 31
 				 * (0x80000000) to extend the
 				 * bitmap by another 32 bits.
diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h
index b768c85..6ea07e3 100644
--- a/src/utils/radiotap_iter.h
+++ b/src/utils/radiotap_iter.h
@@ -67,7 +67,7 @@
 	const struct ieee80211_radiotap_namespace *current_namespace;
 
 	unsigned char *_arg, *_next_ns_data;
-	uint32_t *_next_bitmap;
+	le32 *_next_bitmap;
 
 	unsigned char *this_arg;
 #ifdef RADIOTAP_SUPPORT_OVERRIDES
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index 41511b9..abdb79c 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -16,6 +16,7 @@
 #include "utils/base64.h"
 #include "utils/ip_addr.h"
 #include "utils/eloop.h"
+#include "utils/module_tests.h"
 
 
 struct printf_test_data {
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index f7acf6b..62758d8 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -13,7 +13,7 @@
 #ifdef CONFIG_DEBUG_SYSLOG
 #include <syslog.h>
 
-static int wpa_debug_syslog = 0;
+int wpa_debug_syslog = 0;
 #endif /* CONFIG_DEBUG_SYSLOG */
 
 #ifdef CONFIG_DEBUG_LINUX_TRACING
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
index 17d8f96..1fe0b7d 100644
--- a/src/utils/wpa_debug.h
+++ b/src/utils/wpa_debug.h
@@ -14,6 +14,9 @@
 extern int wpa_debug_level;
 extern int wpa_debug_show_keys;
 extern int wpa_debug_timestamp;
+#ifdef CONFIG_DEBUG_SYSLOG
+extern int wpa_debug_syslog;
+#endif /* CONFIG_DEBUG_SYSLOG */
 
 /* Debugging function - conditional printf and hex dump. Driver wrappers can
  * use these for debugging purposes. */
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
index 11e7323..77ee472 100644
--- a/src/utils/wpabuf.c
+++ b/src/utils/wpabuf.c
@@ -244,15 +244,13 @@
 
 	if (a)
 		len += wpabuf_len(a);
-	if (b)
-		len += wpabuf_len(b);
+	len += wpabuf_len(b);
 
 	n = wpabuf_alloc(len);
 	if (n) {
 		if (a)
 			wpabuf_put_buf(n, a);
-		if (b)
-			wpabuf_put_buf(n, b);
+		wpabuf_put_buf(n, b);
 	}
 
 	wpabuf_free(a);
@@ -310,3 +308,33 @@
 		wpabuf_overflow(buf, res);
 	buf->used += res;
 }
+
+
+/**
+ * wpabuf_parse_bin - Parse a null terminated string of binary data to a wpabuf
+ * @buf: Buffer with null terminated string (hexdump) of binary data
+ * Returns: wpabuf or %NULL on failure
+ *
+ * The string len must be a multiple of two and contain only hexadecimal digits.
+ */
+struct wpabuf * wpabuf_parse_bin(const char *buf)
+{
+	size_t len;
+	struct wpabuf *ret;
+
+	len = os_strlen(buf);
+	if (len & 0x01)
+		return NULL;
+	len /= 2;
+
+	ret = wpabuf_alloc(len);
+	if (ret == NULL)
+		return NULL;
+
+	if (hexstr2bin(buf, wpabuf_put(ret, len), len)) {
+		wpabuf_free(ret);
+		return NULL;
+	}
+
+	return ret;
+}
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
index 9cd8a07..01da41b 100644
--- a/src/utils/wpabuf.h
+++ b/src/utils/wpabuf.h
@@ -37,6 +37,7 @@
 struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
 struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
 void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+struct wpabuf * wpabuf_parse_bin(const char *buf);
 
 
 /**
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 7c6dcb2..fade6b6 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -174,7 +174,7 @@
 	} else if (data->registrar)
 		wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
 
-	wpabuf_free(data->dh_privkey);
+	wpabuf_clear_free(data->dh_privkey);
 	wpabuf_free(data->dh_pubkey_e);
 	wpabuf_free(data->dh_pubkey_r);
 	wpabuf_free(data->last_msg);
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 748620f..770f5e9 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -20,10 +20,10 @@
 
 int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
 {
-	struct wpabuf *pubkey;
+	struct wpabuf *pubkey = NULL;
 
 	wpa_printf(MSG_DEBUG, "WPS:  * Public Key");
-	wpabuf_free(wps->dh_privkey);
+	wpabuf_clear_free(wps->dh_privkey);
 	wps->dh_privkey = NULL;
 	if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey &&
 	    wps->wps->dh_ctx) {
@@ -413,7 +413,8 @@
 		   dev_pw_id);
 	addr[0] = wpabuf_head(pubkey);
 	hash_len = wpabuf_len(pubkey);
-	sha256_vector(1, addr, &hash_len, pubkey_hash);
+	if (sha256_vector(1, addr, &hash_len, pubkey_hash) < 0)
+		return -1;
 #ifdef CONFIG_WPS_TESTING
 	if (wps_corrupt_pkhash) {
 		wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c
index eadb22f..e8c4579 100644
--- a/src/wps/wps_attr_process.c
+++ b/src/wps/wps_attr_process.c
@@ -229,6 +229,16 @@
 		cred->key_len--;
 #endif /* CONFIG_WPS_STRICT */
 	}
+
+
+	if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
+	    (cred->key_len < 8 || has_ctrl_char(cred->key, cred->key_len))) {
+		wpa_printf(MSG_INFO, "WPS: Reject credential with invalid WPA/WPA2-Personal passphrase");
+		wpa_hexdump_ascii_key(MSG_INFO, "WPS: Network Key",
+				      cred->key, cred->key_len);
+		return -1;
+	}
+
 	return 0;
 }
 
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index c6a1cfd..2e34721 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -90,7 +90,7 @@
 	}
 
 	/* Own DH private key is not needed anymore */
-	wpabuf_free(wps->dh_privkey);
+	wpabuf_clear_free(wps->dh_privkey);
 	wps->dh_privkey = NULL;
 
 	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
@@ -100,7 +100,7 @@
 	len[0] = wpabuf_len(dh_shared);
 	sha256_vector(1, addr, len, dhkey);
 	wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
-	wpabuf_free(dh_shared);
+	wpabuf_clear_free(dh_shared);
 
 	/* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
 	addr[0] = wps->nonce_e;
@@ -129,23 +129,26 @@
 }
 
 
-void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
-		    size_t dev_passwd_len)
+int wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+		   size_t dev_passwd_len)
 {
 	u8 hash[SHA256_MAC_LEN];
 
-	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
-		    (dev_passwd_len + 1) / 2, hash);
+	if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+			(dev_passwd_len + 1) / 2, hash) < 0)
+		return -1;
 	os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
-	hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
-		    dev_passwd + (dev_passwd_len + 1) / 2,
-		    dev_passwd_len / 2, hash);
+	if (hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+			dev_passwd + (dev_passwd_len + 1) / 2,
+			dev_passwd_len / 2, hash) < 0)
+		return -1;
 	os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
 
 	wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
 			      dev_passwd, dev_passwd_len);
 	wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
 	wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+	return 0;
 }
 
 
@@ -173,7 +176,7 @@
 	wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
 	if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
 				wpabuf_len(decrypted))) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		return NULL;
 	}
 
@@ -184,14 +187,14 @@
 	pad = *pos;
 	if (pad > wpabuf_len(decrypted)) {
 		wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		return NULL;
 	}
 	for (i = 0; i < pad; i++) {
 		if (*pos-- != pad) {
 			wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
 				   "string");
-			wpabuf_free(decrypted);
+			wpabuf_clear_free(decrypted);
 			return NULL;
 		}
 	}
@@ -373,7 +376,7 @@
 	    wps_build_mac_addr(plain, wps->dev.mac_addr) ||
 	    wps_build_wfa_ext(plain, 0, NULL, 0)) {
 		os_free(data.new_psk);
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		return NULL;
 	}
 
@@ -421,7 +424,7 @@
 	    wps_build_wfa_ext(data, 0, NULL, 0)) {
 		wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
 			   "token");
-		wpabuf_free(data);
+		wpabuf_clear_free(data);
 		return NULL;
 	}
 
@@ -658,7 +661,7 @@
 
 	wpabuf_free(*pubkey);
 	*pubkey = pub;
-	wpabuf_free(*privkey);
+	wpabuf_clear_free(*privkey);
 	*privkey = priv;
 
 	return 0;
@@ -689,7 +692,7 @@
 	}
 
 	*id = 0x10 + val % 0xfff0;
-	wpabuf_free(*dev_pw);
+	wpabuf_clear_free(*dev_pw);
 	*dev_pw = pw;
 
 	return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 9321b72..4175077 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -173,7 +173,8 @@
 		wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
 		return NULL;
 	}
-	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+	if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0)
+		return NULL;
 
 	if (wps->wps->ap && random_pool_ready() != 1) {
 		wpa_printf(MSG_INFO,
@@ -224,11 +225,11 @@
 	    wps_build_encr_settings(wps, msg, plain) ||
 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
 	    wps_build_authenticator(wps, msg)) {
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
 		return NULL;
 	}
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	wps->state = RECV_M6;
 	return msg;
@@ -394,11 +395,11 @@
 	    wps_build_encr_settings(wps, msg, plain) ||
 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
 	    wps_build_authenticator(wps, msg)) {
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
 		return NULL;
 	}
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	if (wps->wps->ap && wps->wps->registrar) {
 		/*
@@ -1007,11 +1008,11 @@
 					      eattr.key_wrap_auth) ||
 		    wps_process_creds(wps, eattr.cred, eattr.cred_len,
 				      eattr.num_cred, attr->version2 != NULL)) {
-			wpabuf_free(decrypted);
+			wpabuf_clear_free(decrypted);
 			wps->state = SEND_WSC_NACK;
 			return WPS_CONTINUE;
 		}
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 
 		wps->state = WPS_MSG_DONE;
 		return WPS_CONTINUE;
@@ -1112,7 +1113,7 @@
 	}
 
 	if (wps_validate_m4_encr(decrypted, attr->version2 != NULL) < 0) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
@@ -1122,11 +1123,11 @@
 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
 	    wps_process_r_snonce1(wps, eattr.r_snonce1)) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
-	wpabuf_free(decrypted);
+	wpabuf_clear_free(decrypted);
 
 	wps->state = SEND_M5;
 	return WPS_CONTINUE;
@@ -1165,7 +1166,7 @@
 	}
 
 	if (wps_validate_m6_encr(decrypted, attr->version2 != NULL) < 0) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
@@ -1175,11 +1176,11 @@
 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
 	    wps_process_r_snonce2(wps, eattr.r_snonce2)) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
-	wpabuf_free(decrypted);
+	wpabuf_clear_free(decrypted);
 
 	if (wps->wps->ap)
 		wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_AP_PIN_SUCCESS,
@@ -1236,7 +1237,7 @@
 
 	if (wps_validate_m8_encr(decrypted, wps->wps->ap,
 				 attr->version2 != NULL) < 0) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
@@ -1249,11 +1250,11 @@
 			      eattr.num_cred, attr->version2 != NULL) ||
 	    wps_process_ap_settings_e(wps, &eattr, decrypted,
 				      attr->version2 != NULL)) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
-	wpabuf_free(decrypted);
+	wpabuf_clear_free(decrypted);
 
 	wps->state = WPS_MSG_DONE;
 	return WPS_CONTINUE;
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index f7154f8..fe0c60b 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -132,8 +132,8 @@
 void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
 	     const char *label, u8 *res, size_t res_len);
 int wps_derive_keys(struct wps_data *wps);
-void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
-		    size_t dev_passwd_len);
+int wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+		   size_t dev_passwd_len);
 struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
 					  size_t encr_len);
 void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
diff --git a/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c
index 3506307..23bed4b 100644
--- a/src/wps/wps_module_tests.c
+++ b/src/wps/wps_module_tests.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/module_tests.h"
 #include "wps_attr_parse.h"
 
 struct wps_attr_parse_test {
@@ -17,7 +18,7 @@
 	int extra;
 };
 
-const struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
+static const struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
 	/* Empty message */
 	{ "", 0, 0 },
 	/* Truncated attribute header */
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 25e88d5..fac8bd8 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -703,7 +703,7 @@
 	eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
 	eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
 	wps_registrar_flush(reg);
-	wpabuf_free(reg->extra_cred);
+	wpabuf_clear_free(reg->extra_cred);
 	os_free(reg);
 }
 
@@ -1577,13 +1577,13 @@
 	if (wbuf == NULL)
 		return -1;
 	if (wps_build_credential(wbuf, cred)) {
-		wpabuf_free(wbuf);
+		wpabuf_clear_free(wbuf);
 		return -1;
 	}
 	wpabuf_put_be16(msg, ATTR_CRED);
 	wpabuf_put_be16(msg, wpabuf_len(wbuf));
 	wpabuf_put_buf(msg, wbuf);
-	wpabuf_free(wbuf);
+	wpabuf_clear_free(wbuf);
 	return 0;
 }
 
@@ -1751,14 +1751,14 @@
 		return -1;
 
 	if (wps_build_credential(cred, &wps->cred)) {
-		wpabuf_free(cred);
+		wpabuf_clear_free(cred);
 		return -1;
 	}
 
 	wpabuf_put_be16(msg, ATTR_CRED);
 	wpabuf_put_be16(msg, wpabuf_len(cred));
 	wpabuf_put_buf(msg, cred);
-	wpabuf_free(cred);
+	wpabuf_clear_free(cred);
 
 skip_cred_build:
 	if (wps->wps->registrar->extra_cred) {
@@ -1796,7 +1796,7 @@
 	}
 
 	if (wps_build_ap_settings(wps, plain)) {
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
 		return NULL;
 	}
@@ -1804,7 +1804,7 @@
 	wpabuf_put_be16(msg, ATTR_CRED);
 	wpabuf_put_be16(msg, wpabuf_len(plain));
 	wpabuf_put_buf(msg, plain);
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	return msg;
 }
@@ -1864,10 +1864,10 @@
 		    wps_build_key_wrap_auth(wps, plain) ||
 		    wps_build_encr_settings(wps, msg, plain)) {
 			wpabuf_free(msg);
-			wpabuf_free(plain);
+			wpabuf_clear_free(plain);
 			return NULL;
 		}
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		config_in_m2 = 1;
 	}
 #endif /* CONFIG_WPS_NFC */
@@ -1928,7 +1928,8 @@
 
 	wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
 
-	wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+	if (wps_derive_psk(wps, wps->dev_password, wps->dev_password_len) < 0)
+		return NULL;
 
 	plain = wpabuf_alloc(200);
 	if (plain == NULL)
@@ -1949,11 +1950,11 @@
 	    wps_build_encr_settings(wps, msg, plain) ||
 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
 	    wps_build_authenticator(wps, msg)) {
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
 		return NULL;
 	}
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	wps->state = RECV_M5;
 	return msg;
@@ -1984,11 +1985,11 @@
 	    wps_build_encr_settings(wps, msg, plain) ||
 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
 	    wps_build_authenticator(wps, msg)) {
-		wpabuf_free(plain);
+		wpabuf_clear_free(plain);
 		wpabuf_free(msg);
 		return NULL;
 	}
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	wps->wps_pin_revealed = 1;
 	wps->state = RECV_M7;
@@ -2021,11 +2022,11 @@
 	    wps_build_encr_settings(wps, msg, plain) ||
 	    wps_build_wfa_ext(msg, 0, NULL, 0) ||
 	    wps_build_authenticator(wps, msg)) {
-		wpabuf_free(plain);
-		wpabuf_free(msg);
+		wpabuf_clear_free(plain);
+		wpabuf_clear_free(msg);
 		return NULL;
 	}
-	wpabuf_free(plain);
+	wpabuf_clear_free(plain);
 
 	wps->state = RECV_DONE;
 	return msg;
@@ -2785,7 +2786,7 @@
 	}
 
 	if (wps_validate_m5_encr(decrypted, attr->version2 != NULL) < 0) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
@@ -2795,11 +2796,11 @@
 	if (wps_parse_msg(decrypted, &eattr) < 0 ||
 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
 	    wps_process_e_snonce1(wps, eattr.e_snonce1)) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
-	wpabuf_free(decrypted);
+	wpabuf_clear_free(decrypted);
 
 	wps->state = SEND_M6;
 	return WPS_CONTINUE;
@@ -2937,7 +2938,7 @@
 
 	if (wps_validate_m7_encr(decrypted, wps->wps->ap || wps->er,
 				 attr->version2 != NULL) < 0) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
@@ -2948,12 +2949,12 @@
 	    wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
 	    wps_process_e_snonce2(wps, eattr.e_snonce2) ||
 	    wps_process_ap_settings_r(wps, &eattr)) {
-		wpabuf_free(decrypted);
+		wpabuf_clear_free(decrypted);
 		wps->state = SEND_WSC_NACK;
 		return WPS_CONTINUE;
 	}
 
-	wpabuf_free(decrypted);
+	wpabuf_clear_free(decrypted);
 
 	wps->state = SEND_M8;
 	return WPS_CONTINUE;
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
index 968fc03..a685ce4 100644
--- a/src/wps/wps_upnp_ssdp.c
+++ b/src/wps/wps_upnp_ssdp.c
@@ -100,12 +100,6 @@
 }
 
 
-static int str_starts(const char *str, const char *start)
-{
-	return os_strncmp(str, start, os_strlen(start)) == 0;
-}
-
-
 /***************************************************************************
  * Advertisements.
  * These are multicast to the world to tell them we are here.
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 09399cd..96d5cd0 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -25,6 +25,9 @@
 # Disable unused parameter warnings
 L_CFLAGS += -Wno-unused-parameter
 
+# Disable redefined macro warnings
+L_CFLAGS += -Wno-macro-redefined
+
 # Set Android extended P2P functionality
 L_CFLAGS += -DANDROID_P2P
 
@@ -49,6 +52,12 @@
 L_CFLAGS += -mabi=aapcs-linux
 endif
 
+# C++ flags for hidl interface
+L_CPPFLAGS := -Wall -Werror
+# TODO: Remove these allowed warnings later.
+L_CPPFLAGS += -Wno-unused-variable -Wno-unused-parameter
+L_CPPFLAGS += -Wno-unused-private-field
+
 INCLUDES = $(LOCAL_PATH)
 INCLUDES += $(LOCAL_PATH)/src
 INCLUDES += $(LOCAL_PATH)/src/common
@@ -86,7 +95,10 @@
 OBJS += src/utils/common.c
 OBJS += src/utils/wpa_debug.c
 OBJS += src/utils/wpabuf.c
+OBJS += src/utils/bitfield.c
 OBJS += wmm_ac.c
+OBJS += op_classes.c
+OBJS += rrm.c
 OBJS_p = wpa_passphrase.c
 OBJS_p += src/utils/common.c
 OBJS_p += src/utils/wpa_debug.c
@@ -94,6 +106,7 @@
 OBJS_c = wpa_cli.c src/common/wpa_ctrl.c
 OBJS_c += src/utils/wpa_debug.c
 OBJS_c += src/utils/common.c
+OBJS_c += src/common/cli.c
 OBJS_d =
 OBJS_priv =
 
@@ -203,6 +216,10 @@
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_IEEE80211R_AP
+CONFIG_IEEE80211R=y
+endif
+
 ifdef CONFIG_IEEE80211R
 L_CFLAGS += -DCONFIG_IEEE80211R
 OBJS += src/rsn_supp/wpa_ft.c
@@ -231,6 +248,12 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_FILS
+L_CFLAGS += -DCONFIG_FILS
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 L_CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.c
@@ -251,6 +274,10 @@
 L_CFLAGS += -DCONFIG_PEERKEY
 endif
 
+ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+L_CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
+endif
+
 ifndef CONFIG_NO_WPA
 OBJS += src/rsn_supp/wpa.c
 OBJS += src/rsn_supp/preauth.c
@@ -287,7 +314,6 @@
 OBJS += src/p2p/p2p_dev_disc.c
 OBJS += src/p2p/p2p_group.c
 OBJS += src/ap/p2p_hostapd.c
-OBJS += src/utils/bitfield.c
 L_CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
@@ -797,11 +823,16 @@
 OBJS += src/ap/beacon.c
 OBJS += src/ap/bss_load.c
 OBJS += src/ap/eap_user_db.c
+OBJS += src/ap/neighbor_db.c
+OBJS += src/ap/rrm.c
 ifdef CONFIG_IEEE80211N
 OBJS += src/ap/ieee802_11_ht.c
 ifdef CONFIG_IEEE80211AC
 OBJS += src/ap/ieee802_11_vht.c
 endif
+ifdef CONFIG_IEEE80211AX
+OBJS += src/ap/ieee802_11_he.c
+endif
 endif
 ifdef CONFIG_WNM
 OBJS += src/ap/wnm_ap.c
@@ -809,6 +840,9 @@
 ifdef CONFIG_MBO
 OBJS += src/ap/mbo_ap.c
 endif
+ifdef CONFIG_FILS
+OBJS += src/ap/fils_hlp.c
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += src/ap/ctrl_iface_ap.c
 endif
@@ -823,11 +857,9 @@
 ifdef CONFIG_IEEE80211AC
 L_CFLAGS += -DCONFIG_IEEE80211AC
 endif
+ifdef CONFIG_IEEE80211AX
+L_CFLAGS += -DCONFIG_IEEE80211AX
 endif
-
-ifdef CONFIG_MBO
-OBJS += mbo.c
-L_CFLAGS += -DCONFIG_MBO
 endif
 
 ifdef NEED_AP_MLME
@@ -851,13 +883,19 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.c
+L_CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_RSN_AUTHENTICATOR
 L_CFLAGS += -DCONFIG_NO_RADIUS
 NEED_AES_WRAP=y
 OBJS += src/ap/wpa_auth.c
 OBJS += src/ap/wpa_auth_ie.c
 OBJS += src/ap/pmksa_cache_auth.c
-ifdef CONFIG_IEEE80211R
+ifdef CONFIG_IEEE80211R_AP
+L_CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += src/ap/wpa_auth_ft.c
 endif
 ifdef CONFIG_PEERKEY
@@ -1257,6 +1295,9 @@
 endif
 ifdef NEED_SHA384
 L_CFLAGS += -DCONFIG_SHA384
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/sha384.c
+endif
 OBJS += src/crypto/sha384-prf.c
 endif
 
@@ -1347,14 +1388,9 @@
 OBJS += $(DBUS_OBJS)
 L_CFLAGS += $(DBUS_CFLAGS)
 
-ifdef CONFIG_CTRL_IFACE_BINDER
-BINDER=y
-L_CFLAGS += -DCONFIG_BINDER -DCONFIG_CTRL_IFACE_BINDER
-OBJS += binder/binder.cpp binder/binder_manager.cpp
-OBJS += binder/supplicant.cpp binder/iface.cpp
-OBJS += binder/fi/w1/wpa_supplicant/ISupplicant.aidl
-OBJS += binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
-OBJS += binder/fi/w1/wpa_supplicant/IIface.aidl
+ifdef CONFIG_CTRL_IFACE_HIDL
+WPA_SUPPLICANT_USE_HIDL=y
+L_CFLAGS += -DCONFIG_HIDL -DCONFIG_CTRL_IFACE_HIDL
 endif
 
 ifdef CONFIG_READLINE
@@ -1550,6 +1586,7 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := wpa_cli
+LOCAL_PROPRIETARY_MODULE := true
 LOCAL_MODULE_TAGS := debug
 LOCAL_SHARED_LIBRARIES := libc libcutils liblog
 LOCAL_CFLAGS := $(L_CFLAGS)
@@ -1560,6 +1597,8 @@
 ########################
 include $(CLEAR_VARS)
 LOCAL_MODULE := wpa_supplicant
+LOCAL_PROPRIETARY_MODULE := true
+LOCAL_MODULE_RELATIVE_PATH := hw
 ifdef CONFIG_DRIVER_CUSTOM
 LOCAL_STATIC_LIBRARIES := libCustomWifi
 endif
@@ -1572,14 +1611,12 @@
 LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY)
 endif
 ifeq ($(CONFIG_TLS), openssl)
-LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder
+LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore-wifi-hidl
 endif
 
 # With BoringSSL we need libkeystore-engine in order to provide access to
 # keystore keys.
-ifneq (,$(wildcard external/boringssl/flavor.mk))
-LOCAL_SHARED_LIBRARIES += libkeystore-engine
-endif
+LOCAL_SHARED_LIBRARIES += libkeystore-engine-wifi-hidl
 
 ifdef CONFIG_DRIVER_NL80211
 ifneq ($(wildcard external/libnl),)
@@ -1594,9 +1631,10 @@
 ifeq ($(DBUS), y)
 LOCAL_SHARED_LIBRARIES += libdbus
 endif
-ifeq ($(BINDER), y)
-LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder frameworks/native/aidl/binder
-LOCAL_SHARED_LIBRARIES += libutils libbinder
+ifeq ($(WPA_SUPPLICANT_USE_HIDL), y)
+LOCAL_SHARED_LIBRARIES += android.hardware.wifi.supplicant@1.0
+LOCAL_SHARED_LIBRARIES += libhidlbase libhidltransport libhwbinder libutils
+LOCAL_STATIC_LIBRARIES += libwpa_hidl
 endif
 include $(BUILD_EXECUTABLE)
 
@@ -1628,11 +1666,39 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE = libwpa_client
+LOCAL_PROPRIETARY_MODULE := true
 LOCAL_CFLAGS = $(L_CFLAGS)
 LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
 LOCAL_C_INCLUDES = $(INCLUDES)
 LOCAL_SHARED_LIBRARIES := libcutils liblog
-LOCAL_COPY_HEADERS_TO := libwpa_client
-LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h
-LOCAL_COPY_HEADERS += src/common/qca-vendor.h
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/wpa_client_include $(LOCAL_PATH)/wpa_client_include/libwpa_client
 include $(BUILD_SHARED_LIBRARY)
+
+ifeq ($(WPA_SUPPLICANT_USE_HIDL), y)
+### Hidl service library ###
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := libwpa_hidl
+LOCAL_CPPFLAGS := $(L_CPPFLAGS)
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+HIDL_INTERFACE_VERSION = 1.0
+LOCAL_SRC_FILES := \
+    hidl/$(HIDL_INTERFACE_VERSION)/hidl.cpp \
+    hidl/$(HIDL_INTERFACE_VERSION)/hidl_manager.cpp \
+    hidl/$(HIDL_INTERFACE_VERSION)/iface_config_utils.cpp \
+    hidl/$(HIDL_INTERFACE_VERSION)/p2p_iface.cpp \
+    hidl/$(HIDL_INTERFACE_VERSION)/p2p_network.cpp \
+    hidl/$(HIDL_INTERFACE_VERSION)/sta_iface.cpp \
+    hidl/$(HIDL_INTERFACE_VERSION)/sta_network.cpp \
+    hidl/$(HIDL_INTERFACE_VERSION)/supplicant.cpp
+LOCAL_SHARED_LIBRARIES := \
+    android.hardware.wifi.supplicant@$(HIDL_INTERFACE_VERSION) \
+    libhidlbase \
+    libhidltransport \
+    libhwbinder \
+    libutils
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+    $(LOCAL_PATH)/hidl/$(HIDL_INTERFACE_VERSION)
+include $(BUILD_STATIC_LIBRARY)
+endif # WPA_SUPPLICANT_USE_HIDL == y
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
index facd90e..f28055f 100644
--- a/wpa_supplicant/ChangeLog
+++ b/wpa_supplicant/ChangeLog
@@ -1,5 +1,149 @@
 ChangeLog for wpa_supplicant
 
+2016-10-02 - v2.6
+	* fixed WNM Sleep Mode processing when PMF is not enabled
+	  [http://w1.fi/security/2015-6/] (CVE-2015-5310)
+	* fixed EAP-pwd last fragment validation
+	  [http://w1.fi/security/2015-7/] (CVE-2015-5315)
+	* fixed EAP-pwd unexpected Confirm message processing
+	  [http://w1.fi/security/2015-8/] (CVE-2015-5316)
+	* fixed WPS configuration update vulnerability with malformed passphrase
+	  [http://w1.fi/security/2016-1/] (CVE-2016-4476)
+	* fixed configuration update vulnerability with malformed parameters set
+	  over the local control interface
+	  [http://w1.fi/security/2016-1/] (CVE-2016-4477)
+	* fixed TK configuration to the driver in EAPOL-Key 3/4 retry case
+	* extended channel switch support for P2P GO
+	* started to throttle control interface event message bursts to avoid
+	  issues with monitor sockets running out of buffer space
+	* mesh mode fixes/improvements
+	  - generate proper AID for peer
+	  - enable WMM by default
+	  - add VHT support
+	  - fix PMKID derivation
+	  - improve robustness on various exchanges
+	  - fix peer link counting in reconnect case
+	  - improve mesh joining behavior
+	  - allow DTIM period to be configured
+	  - allow HT to be disabled (disable_ht=1)
+	  - add MESH_PEER_ADD and MESH_PEER_REMOVE commands
+	  - add support for PMKSA caching
+	  - add minimal support for SAE group negotiation
+	  - allow pairwise/group cipher to be configured in the network profile
+	  - use ieee80211w profile parameter to enable/disable PMF and derive
+	    a separate TX IGTK if PMF is enabled instead of using MGTK
+	    incorrectly
+	  - fix AEK and MTK derivation
+	  - remove GTKdata and IGTKdata from Mesh Peering Confirm/Close
+	  - note: these changes are not fully backwards compatible for secure
+	    (RSN) mesh network
+	* fixed PMKID derivation with SAE
+	* added support for requesting and fetching arbitrary ANQP-elements
+	  without internal support in wpa_supplicant for the specific element
+	  (anqp[265]=<hexdump> in "BSS <BSSID>" command output)
+	* P2P
+	  - filter control characters in group client device names to be
+	    consistent with other P2P peer cases
+	  - support VHT 80+80 MHz and 160 MHz
+	  - indicate group completion in P2P Client role after data association
+	    instead of already after the WPS provisioning step
+	  - improve group-join operation to use SSID, if known, to filter BSS
+	    entries
+	  - added optional ssid=<hexdump> argument to P2P_CONNECT for join case
+	  - added P2P_GROUP_MEMBER command to fetch client interface address
+	* P2PS
+	  - fix follow-on PD Response behavior
+	  - fix PD Response generation for unknown peer
+	  - fix persistent group reporting
+	  - add channel policy to PD Request
+	  - add group SSID to the P2PS-PROV-DONE event
+	  - allow "P2P_CONNECT <addr> p2ps" to be used without specifying the
+	    default PIN
+	* BoringSSL
+	  - support for OCSP stapling
+	  - support building of h20-osu-client
+	* D-Bus
+	  - add ExpectDisconnect()
+	  - add global config parameters as properties
+	  - add SaveConfig()
+	  - add VendorElemAdd(), VendorElemGet(), VendorElemRem()
+	* fixed Suite B 192-bit AKM to use proper PMK length
+	  (note: this makes old releases incompatible with the fixed behavior)
+	* improved PMF behavior for cases where the AP and STA has different
+	  configuration by not trying to connect in some corner cases where the
+	  connection cannot succeed
+	* added option to reopen debug log (e.g., to rotate the file) upon
+	  receipt of SIGHUP signal
+	* EAP-pwd: added support for Brainpool Elliptic Curves
+	  (with OpenSSL 1.0.2 and newer)
+	* fixed EAPOL reauthentication after FT protocol run
+	* fixed FTIE generation for 4-way handshake after FT protocol run
+	* extended INTERFACE_ADD command to allow certain type (sta/ap)
+	  interface to be created
+	* fixed and improved various FST operations
+	* added 80+80 MHz and 160 MHz VHT support for IBSS/mesh
+	* fixed SIGNAL_POLL in IBSS and mesh cases
+	* added an option to abort an ongoing scan (used to speed up connection
+	  and can also be done with the new ABORT_SCAN command)
+	* TLS client
+	  - do not verify CA certificates when ca_cert is not specified
+	  - support validating server certificate hash
+	  - support SHA384 and SHA512 hashes
+	  - add signature_algorithms extension into ClientHello
+	  - support TLS v1.2 signature algorithm with SHA384 and SHA512
+	  - support server certificate probing
+	  - allow specific TLS versions to be disabled with phase2 parameter
+	  - support extKeyUsage
+	  - support PKCS #5 v2.0 PBES2
+	  - support PKCS #5 with PKCS #12 style key decryption
+	  - minimal support for PKCS #12
+	  - support OCSP stapling (including ocsp_multi)
+	* OpenSSL
+	  - support OpenSSL 1.1 API changes
+	  - drop support for OpenSSL 0.9.8
+	  - drop support for OpenSSL 1.0.0
+	* added support for multiple schedule scan plans (sched_scan_plans)
+	* added support for external server certificate chain validation
+	  (tls_ext_cert_check=1 in the network profile phase1 parameter)
+	* made phase2 parser more strict about correct use of auth=<val> and
+	  autheap=<val> values
+	* improved GAS offchannel operations with comeback request
+	* added SIGNAL_MONITOR command to request signal strength monitoring
+	  events
+	* added command for retrieving HS 2.0 icons with in-memory storage
+	  (REQ_HS20_ICON, GET_HS20_ICON, DEL_HS20_ICON commands and
+	  RX-HS20-ICON event)
+	* enabled ACS support for AP mode operations with wpa_supplicant
+	* EAP-PEAP: fixed interoperability issue with Windows 2012r2 server
+	  ("Invalid Compound_MAC in cryptobinding TLV")
+	* EAP-TTLS: fixed success after fragmented final Phase 2 message
+	* VHT: added interoperability workaround for 80+80 and 160 MHz channels
+	* WNM: workaround for broken AP operating class behavior
+	* added kqueue(2) support for eloop (CONFIG_ELOOP_KQUEUE)
+	* nl80211:
+	  - add support for full station state operations
+	  - do not add NL80211_ATTR_SMPS_MODE attribute if HT is disabled
+	  - add NL80211_ATTR_PREV_BSSID with Connect command
+	  - fix IEEE 802.1X/WEP EAP reauthentication and rekeying to use
+	    unencrypted EAPOL frames
+	* added initial MBO support; number of extensions to WNM BSS Transition
+	  Management
+	* added support for PBSS/PCP and P2P on 60 GHz
+	* Interworking: add credential realm to EAP-TLS identity
+	* fixed EAPOL-Key Request Secure bit to be 1 if PTK is set
+	* HS 2.0: add support for configuring frame filters
+	* added POLL_STA command to check connectivity in AP mode
+	* added initial functionality for location related operations
+	* started to ignore pmf=1/2 parameter for non-RSN networks
+	* added wps_disabled=1 network profile parameter to allow AP mode to
+	  be started without enabling WPS
+	* wpa_cli: added action script support for AP-ENABLED and AP-DISABLED
+	  events
+	* improved Public Action frame addressing
+	  - add gas_address3 configuration parameter to control Address 3
+	    behavior
+	* number of small fixes
+
 2015-09-27 - v2.5
 	* fixed P2P validation of SSID element length before copying it
 	  [http://w1.fi/security/2015-1/] (CVE-2015-1863)
diff --git a/wpa_supplicant/CleanSpec.mk b/wpa_supplicant/CleanSpec.mk
new file mode 100644
index 0000000..26b4fd9
--- /dev/null
+++ b/wpa_supplicant/CleanSpec.mk
@@ -0,0 +1,54 @@
+# -*- mode: makefile -*-
+#  Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list.  These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+#     $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+#     $(call add-clean-step, rm -rf $(OUT_DIR)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list.  E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+#
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/obj/SHARED_LIBRARIES/libdvm*)
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/wpa_supplicant*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/wpa_cli*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libwpa_client*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib64/libwpa_client*)
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 550d44b..7bcb7e4 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -103,6 +103,9 @@
 OBJS += ../src/utils/common.o
 OBJS += ../src/utils/wpa_debug.o
 OBJS += ../src/utils/wpabuf.o
+OBJS += ../src/utils/bitfield.o
+OBJS += op_classes.o
+OBJS += rrm.o
 OBJS_p = wpa_passphrase.o
 OBJS_p += ../src/utils/common.o
 OBJS_p += ../src/utils/wpa_debug.o
@@ -110,6 +113,7 @@
 OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
 OBJS_c += ../src/utils/wpa_debug.o
 OBJS_c += ../src/utils/common.o
+OBJS_c += ../src/common/cli.o
 OBJS += wmm_ac.o
 
 ifndef CONFIG_OS
@@ -242,6 +246,10 @@
 NEED_AES_OMAC1=y
 endif
 
+ifdef CONFIG_IEEE80211R_AP
+CONFIG_IEEE80211R=y
+endif
+
 ifdef CONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R
 OBJS += ../src/rsn_supp/wpa_ft.o
@@ -270,6 +278,12 @@
 NEED_DH_GROUPS=y
 endif
 
+ifdef CONFIG_FILS
+CFLAGS += -DCONFIG_FILS
+NEED_SHA384=y
+NEED_AES_SIV=y
+endif
+
 ifdef CONFIG_WNM
 CFLAGS += -DCONFIG_WNM
 OBJS += wnm_sta.o
@@ -290,6 +304,10 @@
 CFLAGS += -DCONFIG_PEERKEY
 endif
 
+ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+CFLAGS += -DCONFIG_PMKSA_CACHE_EXTERNAL
+endif
+
 ifndef CONFIG_NO_WPA
 OBJS += ../src/rsn_supp/wpa.o
 OBJS += ../src/rsn_supp/preauth.o
@@ -334,7 +352,6 @@
 OBJS += ../src/p2p/p2p_dev_disc.o
 OBJS += ../src/p2p/p2p_group.o
 OBJS += ../src/ap/p2p_hostapd.o
-OBJS += ../src/utils/bitfield.o
 CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
@@ -844,11 +861,16 @@
 OBJS += ../src/ap/beacon.o
 OBJS += ../src/ap/bss_load.o
 OBJS += ../src/ap/eap_user_db.o
+OBJS += ../src/ap/neighbor_db.o
+OBJS += ../src/ap/rrm.o
 ifdef CONFIG_IEEE80211N
 OBJS += ../src/ap/ieee802_11_ht.o
 ifdef CONFIG_IEEE80211AC
 OBJS += ../src/ap/ieee802_11_vht.o
 endif
+ifdef CONFIG_IEEE80211AX
+OBJS += ../src/ap/ieee802_11_he.o
+endif
 endif
 ifdef CONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
@@ -856,6 +878,9 @@
 ifdef CONFIG_MBO
 OBJS += ../src/ap/mbo_ap.o
 endif
+ifdef CONFIG_FILS
+OBJS += ../src/ap/fils_hlp.o
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
@@ -870,11 +895,9 @@
 ifdef CONFIG_IEEE80211AC
 CFLAGS += -DCONFIG_IEEE80211AC
 endif
+ifdef CONFIG_IEEE80211AX
+CFLAGS += -DCONFIG_IEEE80211AX
 endif
-
-ifdef CONFIG_MBO
-OBJS += mbo.o
-CFLAGS += -DCONFIG_MBO
 endif
 
 ifdef NEED_AP_MLME
@@ -898,13 +921,19 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.o
+CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_RSN_AUTHENTICATOR
 CFLAGS += -DCONFIG_NO_RADIUS
 NEED_AES_WRAP=y
 OBJS += ../src/ap/wpa_auth.o
 OBJS += ../src/ap/wpa_auth_ie.o
 OBJS += ../src/ap/pmksa_cache_auth.o
-ifdef CONFIG_IEEE80211R
+ifdef CONFIG_IEEE80211R_AP
+CFLAGS += -DCONFIG_IEEE80211R_AP
 OBJS += ../src/ap/wpa_auth_ft.o
 endif
 ifdef CONFIG_PEERKEY
@@ -928,9 +957,13 @@
 #dynamic symbol loading that is now used in pcsc_funcs.c
 #LIBS += -lwinscard
 else
+ifdef CONFIG_OSX
+LIBS += -framework PCSC
+else
 LIBS += -lpcsclite -lpthread
 endif
 endif
+endif
 
 ifdef CONFIG_SIM_SIMULATOR
 CFLAGS += -DCONFIG_SIM_SIMULATOR
@@ -1303,6 +1336,9 @@
 OBJS += $(SHA256OBJS)
 endif
 ifdef NEED_SHA384
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += ../src/crypto/sha384.o
+endif
 CFLAGS += -DCONFIG_SHA384
 OBJS += ../src/crypto/sha384-prf.o
 endif
@@ -1382,6 +1418,7 @@
 DBUS_INCLUDE := $(shell $(PKG_CONFIG) --cflags dbus-1)
 endif
 DBUS_CFLAGS += $(DBUS_INCLUDE)
+DBUS_INTERFACE=fi.epitest.hostap.WPASupplicant
 endif
 
 ifdef CONFIG_CTRL_IFACE_DBUS_NEW
@@ -1407,6 +1444,7 @@
 DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
 endif
 DBUS_CFLAGS += $(DBUS_INCLUDE)
+DBUS_INTERFACE=fi.w1.wpa_supplicant1
 endif
 
 ifdef DBUS
@@ -1573,9 +1611,6 @@
 ifdef CONFIG_WPS
 OBJS += ../src/wps/wps_module_tests.o
 endif
-ifndef CONFIG_P2P
-OBJS += ../src/utils/bitfield.o
-endif
 endif
 
 OBJS += ../src/drivers/driver_common.o
@@ -1769,11 +1804,13 @@
 endif
 
 %.service: %.service.in
-	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \
+		-e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@
 	@$(E) "  sed" $<
 
 %@.service: %.service.arg.in
-	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+	$(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' \
+		-e 's|\@DBUS_INTERFACE\@|$(DBUS_INTERFACE)|g' $< >$@
 	@$(E) "  sed" $<
 
 wpa_supplicant.exe: wpa_supplicant
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
index 11ab01a..730714b 100644
--- a/wpa_supplicant/README
+++ b/wpa_supplicant/README
@@ -1,7 +1,7 @@
 WPA Supplicant
 ==============
 
-Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
@@ -83,7 +83,7 @@
 	  authentication)
   (following methods are supported, but since they do not generate keying
    material, they cannot be used with WPA or IEEE 802.1X WEP keying)
-  * EAP-MD5-Challenge 
+  * EAP-MD5-Challenge
   * EAP-MSCHAPv2
   * EAP-GTC
   * EAP-OTP
@@ -965,6 +965,17 @@
 also possible to run multiple wpa_priv processes at the same time, if
 desired.
 
+It should be noted that the interface used between wpa_supplicant and
+wpa_priv does not include all the capabilities of the wpa_supplicant
+driver interface and at times, this interface lacks update especially
+for recent addition. Consequently, use of wpa_priv does come with the
+price of somewhat reduced available functionality. The next section
+describing how wpa_supplicant can be used with reduced privileges
+without having to handle the complexity of separate wpa_priv. While that
+approve does not provide separation for network admin capabilities, it
+does allow other root privileges to be dropped without the drawbacks of
+the wpa_priv process.
+
 
 Linux capabilities instead of privileged process
 ------------------------------------------------
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index 0a8bf98..1679347 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -1,9 +1,9 @@
 # Example wpa_supplicant build time configuration
 #
 # This file lists the configuration options that are used when building the
-# hostapd binary. All lines starting with # are ignored. Configuration option
-# lines must be commented out complete, if they are not to be included, i.e.,
-# just setting VARIABLE=n is not disabling that variable.
+# wpa_supplicant binary. All lines starting with # are ignored. Configuration
+# option lines must be commented out complete, if they are not to be included,
+# i.e., just setting VARIABLE=n is not disabling that variable.
 #
 # This file is included in Makefile, so variables like CFLAGS and LIBS can also
 # be modified from here. In most cases, these lines should use += in order not
@@ -91,11 +91,10 @@
 CONFIG_EAP_TTLS=y
 
 # EAP-FAST
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch)
-# to add the needed functions.
-CONFIG_EAP_FAST=y
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
+#CONFIG_EAP_FAST=y
 
 # EAP-GTC
 CONFIG_EAP_GTC=y
@@ -152,6 +151,9 @@
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
 
+# EAP-EKE
+#CONFIG_EAP_EKE=y
+
 # PKCS#12 (PFX) support (used to read private key and certificate file from
 # a file that usually has extension .p12 or .pfx)
 CONFIG_PKCS12=y
@@ -176,8 +178,10 @@
 # Select control interface backend for external programs, e.g, wpa_cli:
 # unix = UNIX domain sockets (default for Linux/*BSD)
 # udp = UDP sockets using localhost (127.0.0.1)
+# udp6 = UDP IPv6 sockets using localhost (::1)
 # named_pipe = Windows Named Pipe (default for Windows)
 # udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose)
 # y = use default (backwards compatibility)
 # If this option is commented out, control interface is not included in the
 # build.
@@ -254,6 +258,9 @@
 # Should we use epoll instead of select? Select is used by default.
 #CONFIG_ELOOP_EPOLL=y
 
+# Should we use kqueue instead of select? Select is used by default.
+#CONFIG_ELOOP_KQUEUE=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -263,6 +270,12 @@
 # none = Empty template
 CONFIG_L2_PACKET=linux
 
+# Disable Linux packet socket workaround applicable for station interface
+# in a bridge for EAPOL frames. This should be uncommented only if the kernel
+# is known to not have the regression issue in packet socket behavior with
+# bridge interfaces (commit 'bridge: respect RFC2863 operational state')').
+#CONFIG_NO_LINUX_PACKET_SOCKET_WAR=y
+
 # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 CONFIG_PEERKEY=y
 
@@ -324,9 +337,9 @@
 # Add introspection support for new DBus control interface
 #CONFIG_CTRL_IFACE_DBUS_INTRO=y
 
-# Add support for Binder control interface
+# Add support for Hidl control interface
 # Only applicable for Android platforms.
-#CONFIG_CTRL_IFACE_BINDER=y
+CONFIG_CTRL_IFACE_HIDL=y
 
 # Add support for loading EAP methods dynamically as shared libraries.
 # When this option is enabled, each EAP method can be either included
@@ -349,9 +362,13 @@
 # amount of memory/flash.
 #CONFIG_DYNAMIC_EAP_METHODS=y
 
-# IEEE Std 802.11r-2008 (Fast BSS Transition)
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode
 CONFIG_IEEE80211R=y
 
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for AP mode (implies
+# CONFIG_IEEE80211R).
+#CONFIG_IEEE80211R_AP=y
+
 # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
 #CONFIG_DEBUG_FILE=y
 
@@ -429,6 +446,10 @@
 # IEEE 802.11n (High Throughput) support (mainly for AP mode)
 CONFIG_IEEE80211N=y
 
+# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode)
+# (depends on CONFIG_IEEE80211N)
+#CONFIG_IEEE80211AC=y
+
 # Wireless Network Management (IEEE Std 802.11v-2011)
 # Note: This is experimental and not complete implementation.
 CONFIG_WNM=y
@@ -442,6 +463,9 @@
 # Hotspot 2.0
 CONFIG_HS20=y
 
+# Enable interface matching in wpa_supplicant
+#CONFIG_MATCH_IFACE=y
+
 # Disable roaming in wpa_supplicant
 CONFIG_NO_ROAMING=y
 
@@ -489,4 +513,32 @@
 # Support Multi Band Operation
 #CONFIG_MBO=y
 
+# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
+# Note: This is an experimental and not yet complete implementation. This
+# should not be enabled for production use.
+#CONFIG_FILS=y
+
+# Support RSN on IBSS networks
+# This is needed to be able to use mode=1 network profile with proto=RSN and
+# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None).
+#CONFIG_IBSS_RSN=y
+
+# External PMKSA cache control
+# This can be used to enable control interface commands that allow the current
+# PMKSA cache entries to be fetched and new entries to be added.
+#CONFIG_PMKSA_CACHE_EXTERNAL=y
+
+# Mesh Networking (IEEE 802.11s)
+#CONFIG_MESH=y
+
+# Background scanning modules
+# These can be used to request wpa_supplicant to perform background scanning
+# operations for roaming within an ESS (same SSID). See the bgscan parameter in
+# the wpa_supplicant.conf file for more details.
+# Periodic background scans based on signal strength
+#CONFIG_BGSCAN_SIMPLE=y
+# Learn channels used by the network and try to avoid bgscans on other
+# channels (experimental)
+#CONFIG_BGSCAN_LEARN=y
+
 include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index b133d03..1721d6a 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -168,6 +168,7 @@
 
 			if (mode->vht_capab && ssid->vht) {
 				conf->ieee80211ac = 1;
+				conf->vht_capab |= mode->vht_capab;
 				wpas_conf_ap_vht(wpa_s, conf, mode);
 			}
 		}
@@ -214,6 +215,13 @@
 	if (wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf))
 		return -1;
 
+	if (ssid->pbss > 1) {
+		wpa_printf(MSG_ERROR, "Invalid pbss value(%d) for AP mode",
+			   ssid->pbss);
+		return -1;
+	}
+	bss->pbss = ssid->pbss;
+
 #ifdef CONFIG_ACS
 	if (ssid->acs) {
 		/* Setting channel to 0 in order to enable ACS */
@@ -227,6 +235,7 @@
 		conf->ieee80211d = 1;
 		conf->country[0] = wpa_s->conf->country[0];
 		conf->country[1] = wpa_s->conf->country[1];
+		conf->country[2] = ' ';
 	}
 
 #ifdef CONFIG_P2P
@@ -287,7 +296,10 @@
 
 	if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt))
 		bss->wpa = ssid->proto;
-	bss->wpa_key_mgmt = ssid->key_mgmt;
+	if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
+		bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+	else
+		bss->wpa_key_mgmt = ssid->key_mgmt;
 	bss->wpa_pairwise = ssid->pairwise_cipher;
 	if (ssid->psk_set) {
 		bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk));
@@ -296,6 +308,7 @@
 			return -1;
 		os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
 		bss->ssid.wpa_psk->group = 1;
+		bss->ssid.wpa_psk_set = 1;
 	} else if (ssid->passphrase) {
 		bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
 	} else if (ssid->wep_key_len[0] || ssid->wep_key_len[1] ||
@@ -409,6 +422,8 @@
 	     !(bss->wpa & 2)))
 		goto no_wps; /* WPS2 does not allow WPA/TKIP-only
 			      * configuration */
+	if (ssid->wps_disabled)
+		goto no_wps;
 	bss->eap_server = 1;
 
 	if (!ssid->ignore_broadcast_ssid)
@@ -437,6 +452,8 @@
 		os_memcpy(bss->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
 	os_memcpy(bss->os_version, wpa_s->conf->os_version, 4);
 	bss->pbc_in_m1 = wpa_s->conf->pbc_in_m1;
+	if (ssid->eap.fragment_size != DEFAULT_FRAGMENT_SIZE)
+		bss->fragment_size = ssid->eap.fragment_size;
 no_wps:
 #endif /* CONFIG_WPS */
 
@@ -453,7 +470,8 @@
 			wpabuf_dup(wpa_s->conf->ap_vendor_elements);
 	}
 
-	bss->pbss = ssid->pbss;
+	bss->ftm_responder = wpa_s->conf->ftm_responder;
+	bss->ftm_initiator = wpa_s->conf->ftm_initiator;
 
 	return 0;
 }
@@ -649,12 +667,17 @@
 	if (ieee80211_is_dfs(params.freq.freq))
 		params.freq.freq = 0; /* set channel after CAC */
 
+	if (params.p2p)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_GO);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_AP_BSS);
+
 	if (wpa_drv_associate(wpa_s, &params) < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
 		return -1;
 	}
 
-	wpa_s->ap_iface = hapd_iface = os_zalloc(sizeof(*wpa_s->ap_iface));
+	wpa_s->ap_iface = hapd_iface = hostapd_alloc_iface();
 	if (hapd_iface == NULL)
 		return -1;
 	hapd_iface->owner = wpa_s;
@@ -1363,7 +1386,6 @@
 	hapd = wpa_s->ap_iface->bss[0];
 	return hostapd_ctrl_iface_stop_ap(hapd);
 }
-#endif /* CONFIG_CTRL_IFACE */
 
 
 int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
@@ -1418,6 +1440,44 @@
 }
 
 
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+
+int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr,
+				  char *buf, size_t len)
+{
+	return hostapd_ctrl_iface_pmksa_list_mesh(wpa_s->ifmsh->bss[0], addr,
+						  &buf[0], len);
+}
+
+
+int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	struct external_pmksa_cache *entry;
+	void *pmksa_cache;
+
+	pmksa_cache = hostapd_ctrl_iface_pmksa_create_entry(wpa_s->own_addr,
+							    cmd);
+	if (!pmksa_cache)
+		return -1;
+
+	entry = os_zalloc(sizeof(struct external_pmksa_cache));
+	if (!entry)
+		return -1;
+
+	entry->pmksa_cache = pmksa_cache;
+
+	dl_list_add(&wpa_s->mesh_external_pmksa_cache, &entry->list);
+
+	return 0;
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+#endif /* CONFIG_CTRL_IFACE */
+
+
 #ifdef NEED_AP_MLME
 void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
 				   struct dfs_event *radar)
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
index 5a59ddc..3fa656f 100644
--- a/wpa_supplicant/ap.h
+++ b/wpa_supplicant/ap.h
@@ -85,6 +85,9 @@
 int wpas_ap_pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf,
 			     size_t len);
 void wpas_ap_pmksa_cache_flush(struct wpa_supplicant *wpa_s);
+int wpas_ap_pmksa_cache_list_mesh(struct wpa_supplicant *wpa_s, const u8 *addr,
+				  char *buf, size_t len);
+int wpas_ap_pmksa_cache_add_external(struct wpa_supplicant *wpa_s, char *cmd);
 
 void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
 				   struct dfs_event *radar);
diff --git a/wpa_supplicant/autoscan.c b/wpa_supplicant/autoscan.c
index d12eb21..5056a93 100644
--- a/wpa_supplicant/autoscan.c
+++ b/wpa_supplicant/autoscan.c
@@ -16,13 +16,6 @@
 #include "scan.h"
 #include "autoscan.h"
 
-#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
-extern const struct autoscan_ops autoscan_exponential_ops;
-#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
-
-#ifdef CONFIG_AUTOSCAN_PERIODIC
-extern const struct autoscan_ops autoscan_periodic_ops;
-#endif /* CONFIG_AUTOSCAN_PERIODIC */
 
 static const struct autoscan_ops * autoscan_modules[] = {
 #ifdef CONFIG_AUTOSCAN_EXPONENTIAL
@@ -54,11 +47,16 @@
 	struct sched_scan_plan *scan_plans;
 
 	/* Give preference to scheduled scan plans if supported/configured */
-	if (wpa_s->sched_scan_plans)
+	if (wpa_s->sched_scan_plans) {
+		wpa_printf(MSG_DEBUG,
+			   "autoscan: sched_scan_plans set - use it instead");
 		return 0;
+	}
 
-	if (wpa_s->autoscan && wpa_s->autoscan_priv)
+	if (wpa_s->autoscan && wpa_s->autoscan_priv) {
+		wpa_printf(MSG_DEBUG, "autoscan: Already initialized");
 		return 0;
+	}
 
 	if (name == NULL)
 		return 0;
diff --git a/wpa_supplicant/autoscan.h b/wpa_supplicant/autoscan.h
index e2a7652..560684f 100644
--- a/wpa_supplicant/autoscan.h
+++ b/wpa_supplicant/autoscan.h
@@ -27,6 +27,16 @@
 int autoscan_notify_scan(struct wpa_supplicant *wpa_s,
 			 struct wpa_scan_results *scan_res);
 
+/* Available autoscan modules */
+
+#ifdef CONFIG_AUTOSCAN_EXPONENTIAL
+extern const struct autoscan_ops autoscan_exponential_ops;
+#endif /* CONFIG_AUTOSCAN_EXPONENTIAL */
+
+#ifdef CONFIG_AUTOSCAN_PERIODIC
+extern const struct autoscan_ops autoscan_periodic_ops;
+#endif /* CONFIG_AUTOSCAN_PERIODIC */
+
 #else /* CONFIG_AUTOSCAN */
 
 static inline int autoscan_init(struct wpa_supplicant *wpa_s, int req_scan)
diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c
index f74cdbf..1ea6401 100644
--- a/wpa_supplicant/bgscan.c
+++ b/wpa_supplicant/bgscan.c
@@ -13,12 +13,6 @@
 #include "config_ssid.h"
 #include "bgscan.h"
 
-#ifdef CONFIG_BGSCAN_SIMPLE
-extern const struct bgscan_ops bgscan_simple_ops;
-#endif /* CONFIG_BGSCAN_SIMPLE */
-#ifdef CONFIG_BGSCAN_LEARN
-extern const struct bgscan_ops bgscan_learn_ops;
-#endif /* CONFIG_BGSCAN_LEARN */
 
 static const struct bgscan_ops * bgscan_modules[] = {
 #ifdef CONFIG_BGSCAN_SIMPLE
@@ -40,8 +34,6 @@
 	const struct bgscan_ops *ops = NULL;
 
 	bgscan_deinit(wpa_s);
-	if (name == NULL)
-		return -1;
 
 	params = os_strchr(name, ':');
 	if (params == NULL) {
diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h
index 9131e4e..3df1550 100644
--- a/wpa_supplicant/bgscan.h
+++ b/wpa_supplicant/bgscan.h
@@ -39,6 +39,15 @@
 				 int current_signal, int current_noise,
 				 int current_txrate);
 
+/* Available bgscan modules */
+
+#ifdef CONFIG_BGSCAN_SIMPLE
+extern const struct bgscan_ops bgscan_simple_ops;
+#endif /* CONFIG_BGSCAN_SIMPLE */
+#ifdef CONFIG_BGSCAN_LEARN
+extern const struct bgscan_ops bgscan_learn_ops;
+#endif /* CONFIG_BGSCAN_LEARN */
+
 #else /* CONFIG_BGSCAN */
 
 static inline int bgscan_init(struct wpa_supplicant *wpa_s,
diff --git a/wpa_supplicant/bgscan_learn.c b/wpa_supplicant/bgscan_learn.c
index a320cc4..cb732f7 100644
--- a/wpa_supplicant/bgscan_learn.c
+++ b/wpa_supplicant/bgscan_learn.c
@@ -320,9 +320,6 @@
 {
 	const char *pos;
 
-	if (params == NULL)
-		return 0;
-
 	data->short_interval = atoi(params);
 
 	pos = os_strchr(params, ':');
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
index a467cc5..41a26df 100644
--- a/wpa_supplicant/bgscan_simple.c
+++ b/wpa_supplicant/bgscan_simple.c
@@ -56,12 +56,7 @@
 	} else {
 		if (data->scan_interval == data->short_interval) {
 			data->short_scan_count++;
-			/*
-			 * Spend at most the duration of a long scan interval
-			 * scanning at the short scan interval. After that,
-			 * revert to the long scan interval.
-			 */
-			if (data->short_scan_count > data->max_short_scans) {
+			if (data->short_scan_count >= data->max_short_scans) {
 				data->scan_interval = data->long_interval;
 				wpa_printf(MSG_DEBUG, "bgscan simple: Backing "
 					   "off to long scan interval");
@@ -85,9 +80,6 @@
 {
 	const char *pos;
 
-	if (params == NULL)
-		return 0;
-
 	data->short_interval = atoi(params);
 
 	pos = os_strchr(params, ':');
diff --git a/wpa_supplicant/binder/binder.cpp b/wpa_supplicant/binder/binder.cpp
deleted file mode 100644
index 28f7a2b..0000000
--- a/wpa_supplicant/binder/binder.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-
-#include "binder_manager.h"
-
-extern "C" {
-#include "utils/includes.h"
-#include "utils/common.h"
-#include "utils/eloop.h"
-#include "binder.h"
-#include "binder_i.h"
-}
-
-void wpas_binder_sock_handler(int sock, void *eloop_ctx, void *sock_ctx)
-{
-	struct wpa_global *global = (wpa_global *) eloop_ctx;
-	struct wpas_binder_priv *priv = (wpas_binder_priv *) sock_ctx;
-
-	wpa_printf(MSG_DEBUG, "Processing binder events on FD %d",
-		   priv->binder_fd);
-	android::IPCThreadState::self()->handlePolledCommands();
-}
-
-
-struct wpas_binder_priv * wpas_binder_init(struct wpa_global *global)
-{
-	struct wpas_binder_priv *priv;
-	wpa_supplicant_binder::BinderManager *binder_manager;
-
-	priv = (wpas_binder_priv *) os_zalloc(sizeof(*priv));
-	if (!priv)
-		return NULL;
-	priv->global = global;
-
-	android::ProcessState::self()->setThreadPoolMaxThreadCount(0);
-	android::IPCThreadState::self()->disableBackgroundScheduling(true);
-	android::IPCThreadState::self()->setupPolling(&priv->binder_fd);
-	wpa_printf(MSG_INFO, "Process binder events on FD %d", priv->binder_fd);
-	if (priv->binder_fd < 0)
-		goto err;
-	/* Look for read events from the binder socket in the eloop. */
-	if (eloop_register_read_sock(priv->binder_fd, wpas_binder_sock_handler,
-				     global, priv) < 0)
-		goto err;
-
-	binder_manager = wpa_supplicant_binder::BinderManager::getInstance();
-	if (!binder_manager)
-		goto err;
-	binder_manager->registerBinderService(global);
-	/* We may not need to store this binder manager reference in the
-	 * global data strucure because we've made it a singleton class. */
-	priv->binder_manager = (void *) binder_manager;
-
-	return priv;
-
-err:
-	wpas_binder_deinit(priv);
-	return NULL;
-}
-
-
-void wpas_binder_deinit(struct wpas_binder_priv *priv)
-{
-	if (!priv)
-		return;
-
-	wpa_supplicant_binder::BinderManager::destroyInstance();
-	eloop_unregister_read_sock(priv->binder_fd);
-	android::IPCThreadState::shutdown();
-}
-
-
-int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s->global->binder)
-		return 1;
-
-	wpa_supplicant_binder::BinderManager *binder_manager =
-		wpa_supplicant_binder::BinderManager::getInstance();
-	if (!binder_manager)
-		return 1;
-
-	return binder_manager->registerInterface(wpa_s);
-}
-
-
-int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s->global->binder)
-		return 1;
-
-	wpa_supplicant_binder::BinderManager *binder_manager =
-		wpa_supplicant_binder::BinderManager::getInstance();
-	if (!binder_manager)
-		return 1;
-
-	return binder_manager->unregisterInterface(wpa_s);
-}
diff --git a/wpa_supplicant/binder/binder.h b/wpa_supplicant/binder/binder.h
deleted file mode 100644
index a165074..0000000
--- a/wpa_supplicant/binder/binder.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef BINDER_H
-#define BINDER_H
-
-#ifdef _cplusplus
-extern "C" {
-#endif /* _cplusplus */
-
-/**
- * This is the binder RPC interface entry point to the wpa_supplicant core.
- * This initializes the binder driver & BinderManager instance and then forwards
- * all the notifcations from the supplicant core to the BinderManager.
- */
-struct wpas_binder_priv;
-struct wpa_global;
-
-struct wpas_binder_priv * wpas_binder_init(struct wpa_global *global);
-void wpas_binder_deinit(struct wpas_binder_priv *priv);
-
-#ifdef CONFIG_CTRL_IFACE_BINDER
-int wpas_binder_register_interface(struct wpa_supplicant *wpa_s);
-int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s);
-#else /* CONFIG_CTRL_IFACE_BINDER */
-static inline int wpas_binder_register_interface(struct wpa_supplicant *wpa_s)
-{
-	return 0;
-}
-static inline int wpas_binder_unregister_interface(struct wpa_supplicant *wpa_s)
-{
-	return 0;
-}
-#endif /* CONFIG_CTRL_IFACE_BINDER */
-
-#ifdef _cplusplus
-}
-#endif /* _cplusplus */
-
-#endif /* BINDER_H */
diff --git a/wpa_supplicant/binder/binder_i.h b/wpa_supplicant/binder/binder_i.h
deleted file mode 100644
index e8087b6..0000000
--- a/wpa_supplicant/binder/binder_i.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef BINDER_I_H
-#define BINDER_I_H
-
-#ifdef _cplusplus
-extern "C" {
-#endif // _cplusplus
-
-struct wpas_binder_priv {
-	int binder_fd;
-	struct wpa_global *global;
-	void *binder_manager;
-};
-
-#ifdef _cplusplus
-}
-#endif /* _cplusplus */
-
-#endif /* BINDER_I_H */
diff --git a/wpa_supplicant/binder/binder_manager.cpp b/wpa_supplicant/binder/binder_manager.cpp
deleted file mode 100644
index 728f4b7..0000000
--- a/wpa_supplicant/binder/binder_manager.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include <binder/IServiceManager.h>
-
-#include "binder_manager.h"
-
-extern "C" {
-#include "utils/includes.h"
-#include "utils/common.h"
-}
-
-namespace wpa_supplicant_binder {
-
-const char BinderManager::kBinderServiceName[] = "fi.w1.wpa_supplicant";
-BinderManager *BinderManager::instance_ = NULL;
-
-
-BinderManager * BinderManager::getInstance()
-{
-	if (!instance_)
-		instance_ = new BinderManager();
-	return instance_;
-}
-
-
-void BinderManager::destroyInstance()
-{
-	if (instance_)
-		delete instance_;
-	instance_ = NULL;
-}
-
-
-int BinderManager::registerBinderService(struct wpa_global *global)
-{
-	/* Create the main binder service object and register with
-	 * system service manager. */
-	supplicant_object_ = new Supplicant(global);
-	android::String16 service_name(kBinderServiceName);
-	android::defaultServiceManager()->addService(
-		service_name,
-		android::IInterface::asBinder(supplicant_object_));
-	return 0;
-}
-
-
-int BinderManager::registerInterface(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s)
-		return 1;
-
-	/* Using the corresponding wpa_supplicant pointer as key to our
-	 * object map. */
-	const void *iface_key = wpa_s;
-
-	/* Return failure if we already have an object for that iface_key. */
-	if (iface_object_map_.find(iface_key) != iface_object_map_.end())
-		return 1;
-
-	iface_object_map_[iface_key] = new Iface(wpa_s);
-	if (!iface_object_map_[iface_key].get())
-		return 1;
-
-	wpa_s->binder_object_key = iface_key;
-
-	return 0;
-}
-
-
-int BinderManager::unregisterInterface(struct wpa_supplicant *wpa_s)
-{
-	if (!wpa_s || !wpa_s->binder_object_key)
-		return 1;
-
-	const void *iface_key = wpa_s;
-	if (iface_object_map_.find(iface_key) == iface_object_map_.end())
-		return 1;
-
-	/* Delete the corresponding iface object from our map. */
-	iface_object_map_.erase(iface_key);
-	wpa_s->binder_object_key = NULL;
-	return 0;
-}
-
-
-int BinderManager::getIfaceBinderObjectByKey(
-	const void *iface_object_key,
-	android::sp<fi::w1::wpa_supplicant::IIface> *iface_object)
-{
-	if (!iface_object_key || !iface_object)
-		return 1;
-
-	if (iface_object_map_.find(iface_object_key) == iface_object_map_.end())
-		return 1;
-
-	*iface_object = iface_object_map_[iface_object_key];
-	return 0;
-}
-
-} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/binder_manager.h b/wpa_supplicant/binder/binder_manager.h
deleted file mode 100644
index 687e740..0000000
--- a/wpa_supplicant/binder/binder_manager.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef BINDER_MANAGER_H
-#define BINDER_MANAGER_H
-
-#include <map>
-#include <string>
-
-#include "supplicant.h"
-#include "iface.h"
-
-struct wpa_global;
-struct wpa_supplicant;
-
-namespace wpa_supplicant_binder {
-
-/**
- * BinderManager is responsible for managing the lifetime of all
- * binder objects created by wpa_supplicant. This is a singleton
- * class which is created by the supplicant core and can be used
- * to get references to the binder objects.
- */
-class BinderManager {
-public:
-	static const char kBinderServiceName[];
-
-	static BinderManager * getInstance();
-	static void destroyInstance();
-	int registerBinderService(struct wpa_global *global);
-	int registerInterface(struct wpa_supplicant *wpa_s);
-	int unregisterInterface(struct wpa_supplicant *wpa_s);
-	int getIfaceBinderObjectByKey(
-		const void *iface_object_key,
-		android::sp<fi::w1::wpa_supplicant::IIface> *iface_object);
-
-private:
-	BinderManager() = default;
-	~BinderManager() = default;
-
-	/* Singleton instance of this class. */
-	static BinderManager *instance_;
-	/* The main binder service object. */
-	android::sp<Supplicant> supplicant_object_;
-	/* Map of all the interface specific binder objects controlled by
-	 * wpa_supplicant. This map is keyed in by the corresponding
-	 * wpa_supplicant structure pointer. */
-	std::map<const void *, android::sp<Iface>> iface_object_map_;
-};
-
-} /* namespace wpa_supplicant_binder */
-
-#endif /* BINDER_MANAGER_H */
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
deleted file mode 100644
index ea11d42..0000000
--- a/wpa_supplicant/binder/fi/w1/wpa_supplicant/IIface.aidl
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-package fi.w1.wpa_supplicant;
-
-/**
- * Interface exposed by wpa_supplicant for each network interface it controls.
- */
-interface IIface {
-}
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
deleted file mode 100644
index 1cbee20..0000000
--- a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicant.aidl
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * WPA Supplicant - binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-package fi.w1.wpa_supplicant;
-
-import android.os.PersistableBundle;
-import fi.w1.wpa_supplicant.IIface;
-
-/**
- * Interface exposed by the wpa_supplicant binder service registered
- * with the service manager with name: fi.w1.wpa_supplicant.
- */
-interface ISupplicant {
-	/* Error values returned by the service to RPC method calls. */
-	const int ERROR_INVALID_ARGS = 1;
-	const int ERROR_UNKNOWN = 2;
-	const int ERROR_IFACE_EXISTS = 3;
-	const int ERROR_IFACE_UNKNOWN = 4;
-
-	/**
-	 * Registers a wireless interface in wpa_supplicant.
-	 *
-	 * @param args A dictionary with arguments used to add the interface to
-	 *             wpa_supplicant.
-	 * The dictionary may contain the following entries:
-	 *   Ifname(String) Name of the network interface to control, e.g.,
-	 *   wlan0.
-	 *   BridgeIfname(String) Name of the bridge interface to control, e.g.,
-	 *   br0.
-	 *   Driver(String) Driver name which the interface uses, e.g., nl80211.
-	 *   ConfigFile(String) Configuration file path.
-	 *
-	 * @return Binder object representing the interface.
-	 */
-	IIface CreateInterface(in PersistableBundle args);
-
-	/**
-	 * Deregisters a wireless interface from wpa_supplicant.
-	 *
-	 * @param ifname Name of the network interface, e.g., wlan0
-	 */
-	void RemoveInterface(in @utf8InCpp String ifname);
-
-	/**
-	 * Gets a binder object for the interface corresponding to ifname
-	 * which wpa_supplicant already controls.
-	 *
-	 * @param ifname Name of the network interface, e.g., wlan0
-	 *
-	 * @return Binder object representing the interface.
-	 */
-	IIface GetInterface(in @utf8InCpp String ifname);
-}
diff --git a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl b/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
deleted file mode 100644
index d624d91..0000000
--- a/wpa_supplicant/binder/fi/w1/wpa_supplicant/ISupplicantCallbacks.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-package fi.w1.wpa_supplicant;
-
-import android.os.PersistableBundle;
-
-/**
- * Callback Interface exposed by the wpa_supplicant service. Clients need
- * to host an instance of this binder object and pass a reference of the object
- * to wpa_supplicant via the registerCallbacksObject method.
- */
-interface ISupplicantCallbacks {
-}
diff --git a/wpa_supplicant/binder/iface.cpp b/wpa_supplicant/binder/iface.cpp
deleted file mode 100644
index af2548d..0000000
--- a/wpa_supplicant/binder/iface.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "iface.h"
-
-namespace wpa_supplicant_binder {
-
-Iface::Iface(struct wpa_supplicant *wpa_s)
-	: wpa_s_(wpa_s)
-{
-}
-
-} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/iface.h b/wpa_supplicant/binder/iface.h
deleted file mode 100644
index acc8e55..0000000
--- a/wpa_supplicant/binder/iface.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef IFACE_H
-#define IFACE_H
-
-#include "fi/w1/wpa_supplicant/BnIface.h"
-
-extern "C" {
-#include "utils/includes.h"
-#include "utils/common.h"
-#include "../wpa_supplicant_i.h"
-}
-
-namespace wpa_supplicant_binder {
-
-/**
- * Implementation of Iface binder object. Each unique binder
- * object is used for control operations on a specific interface
- * controlled by wpa_supplicant.
- */
-class Iface : public fi::w1::wpa_supplicant::BnIface
-{
-public:
-	Iface(struct wpa_supplicant *wpa_s);
-	virtual ~Iface() = default;
-
-private:
-	/* Raw pointer to the structure maintained by the core for this
-	 * interface. */
-	struct wpa_supplicant *wpa_s_;
-};
-
-} /* namespace wpa_supplicant_binder */
-
-#endif /* IFACE_H */
diff --git a/wpa_supplicant/binder/supplicant.cpp b/wpa_supplicant/binder/supplicant.cpp
deleted file mode 100644
index 6844e5a..0000000
--- a/wpa_supplicant/binder/supplicant.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "binder_manager.h"
-#include "supplicant.h"
-
-namespace wpa_supplicant_binder {
-
-Supplicant::Supplicant(struct wpa_global *global)
-	: wpa_global_(global)
-{
-}
-
-
-android::binder::Status Supplicant::CreateInterface(
-	const android::os::PersistableBundle &params,
-	android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
-{
-	android::String16 driver, ifname, confname, bridge_ifname;
-
-	/* Check if required Ifname argument is missing */
-	if (!params.getString(android::String16("Ifname"), &ifname))
-		return android::binder::Status::fromServiceSpecificError(
-			ERROR_INVALID_ARGS,
-			android::String8("Ifname missing in params."));
-	/* Retrieve the remaining params from the dictionary */
-	params.getString(android::String16("Driver"), &driver);
-	params.getString(android::String16("ConfigFile"), &confname);
-	params.getString(android::String16("BridgeIfname"), &bridge_ifname);
-
-	/*
-	 * Try to get the wpa_supplicant record for this iface, return
-	 * an error if we already control it.
-	 */
-	if (wpa_supplicant_get_iface(wpa_global_,
-				     android::String8(ifname).string()) != NULL)
-		return android::binder::Status::fromServiceSpecificError(
-			ERROR_IFACE_EXISTS,
-			android::String8("wpa_supplicant already controls this interface."));
-
-	android::binder::Status status;
-	struct wpa_supplicant *wpa_s = NULL;
-	struct wpa_interface iface;
-
-	os_memset(&iface, 0, sizeof(iface));
-	iface.driver = os_strdup(android::String8(driver).string());
-	iface.ifname = os_strdup(android::String8(ifname).string());
-	iface.confname = os_strdup(android::String8(confname).string());
-	iface.bridge_ifname = os_strdup(
-		android::String8(bridge_ifname).string());
-	/* Otherwise, have wpa_supplicant attach to it. */
-	wpa_s = wpa_supplicant_add_iface(wpa_global_, &iface, NULL);
-	/* The supplicant core creates a corresponding binder object via
-	 * BinderManager when |wpa_supplicant_add_iface| is called. */
-	if (!wpa_s || !wpa_s->binder_object_key) {
-		status = android::binder::Status::fromServiceSpecificError(
-			ERROR_UNKNOWN,
-			android::String8("wpa_supplicant couldn't grab this interface."));
-	} else {
-		BinderManager *binder_manager = BinderManager::getInstance();
-
-		if (!binder_manager ||
-		    binder_manager->getIfaceBinderObjectByKey(
-			    wpa_s->binder_object_key, aidl_return))
-			status = android::binder::Status::fromServiceSpecificError(
-				ERROR_UNKNOWN,
-				android::String8("wpa_supplicant encountered a binder error."));
-		else
-			status = android::binder::Status::ok();
-	}
-	os_free((void *) iface.driver);
-	os_free((void *) iface.ifname);
-	os_free((void *) iface.confname);
-	os_free((void *) iface.bridge_ifname);
-	return status;
-}
-
-
-android::binder::Status Supplicant::RemoveInterface(const std::string &ifname)
-{
-	struct wpa_supplicant *wpa_s;
-
-	wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str());
-	if (!wpa_s || !wpa_s->binder_object_key)
-		return android::binder::Status::fromServiceSpecificError(
-			ERROR_IFACE_UNKNOWN,
-			android::String8("wpa_supplicant does not control this interface."));
-	if (wpa_supplicant_remove_iface(wpa_global_, wpa_s, 0))
-		return android::binder::Status::fromServiceSpecificError(
-			ERROR_UNKNOWN,
-			android::String8("wpa_supplicant couldn't remove this interface."));
-	return android::binder::Status::ok();
-}
-
-
-android::binder::Status Supplicant::GetInterface(
-	const std::string &ifname,
-	android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
-{
-	struct wpa_supplicant *wpa_s;
-
-	wpa_s = wpa_supplicant_get_iface(wpa_global_, ifname.c_str());
-	if (!wpa_s || !wpa_s->binder_object_key)
-		return android::binder::Status::fromServiceSpecificError(
-			ERROR_IFACE_UNKNOWN,
-			android::String8("wpa_supplicant does not control this interface."));
-
-	BinderManager *binder_manager = BinderManager::getInstance();
-	if (!binder_manager ||
-	    binder_manager->getIfaceBinderObjectByKey(wpa_s->binder_object_key,
-						      aidl_return))
-		return android::binder::Status::fromServiceSpecificError(
-			ERROR_UNKNOWN,
-			android::String8("wpa_supplicant encountered a binder error."));
-
-	return android::binder::Status::ok();
-}
-
-} /* namespace wpa_supplicant_binder */
diff --git a/wpa_supplicant/binder/supplicant.h b/wpa_supplicant/binder/supplicant.h
deleted file mode 100644
index b96f4e6..0000000
--- a/wpa_supplicant/binder/supplicant.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * binder interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
- * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#ifndef SUPPLICANT_H
-#define SUPPLICANT_H
-
-#include "fi/w1/wpa_supplicant/BnSupplicant.h"
-#include "fi/w1/wpa_supplicant/IIface.h"
-#include "fi/w1/wpa_supplicant/ISupplicantCallbacks.h"
-
-extern "C" {
-#include "utils/includes.h"
-#include "utils/common.h"
-#include "../wpa_supplicant_i.h"
-}
-
-namespace wpa_supplicant_binder {
-
-/**
- * Implementation of the supplicant binder object. This binder
- * object is used core for global control operations on
- * wpa_supplicant.
- */
-class Supplicant : public fi::w1::wpa_supplicant::BnSupplicant
-{
-public:
-	Supplicant(struct wpa_global *global);
-	virtual ~Supplicant() = default;
-
-	android::binder::Status CreateInterface(
-		const android::os::PersistableBundle &params,
-		android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
-		override;
-	android::binder::Status RemoveInterface(
-		const std::string &ifname) override;
-	android::binder::Status GetInterface(
-		const std::string &ifname,
-		android::sp<fi::w1::wpa_supplicant::IIface> *aidl_return)
-		override;
-
-private:
-	/* Raw pointer to the global structure maintained by the core. */
-	struct wpa_global *wpa_global_;
-	/* All the callback objects registered by the clients. */
-	std::vector<android::sp<fi::w1::wpa_supplicant::ISupplicantCallbacks>>
-		callbacks_;
-};
-
-} /* namespace wpa_supplicant_binder */
-
-#endif /* SUPPLICANT_H */
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index a83ca10..914bd5d 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "drivers/driver.h"
+#include "eap_peer/eap.h"
 #include "wpa_supplicant_i.h"
 #include "config.h"
 #include "notify.h"
@@ -92,6 +93,7 @@
 	ANQP_DUP(nai_realm);
 	ANQP_DUP(anqp_3gpp);
 	ANQP_DUP(domain_name);
+	ANQP_DUP(fils_realm_info);
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
 	ANQP_DUP(hs20_capability_list);
@@ -167,6 +169,7 @@
 	wpabuf_free(anqp->nai_realm);
 	wpabuf_free(anqp->anqp_3gpp);
 	wpabuf_free(anqp->domain_name);
+	wpabuf_free(anqp->fils_realm_info);
 
 	while ((elem = dl_list_first(&anqp->anqp_elems,
 				     struct wpa_bss_anqp_elem, list))) {
@@ -213,8 +216,8 @@
 }
 
 
-static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
-			   const char *reason)
+void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+		    const char *reason)
 {
 	if (wpa_s->last_scan_res) {
 		unsigned int i;
@@ -266,9 +269,9 @@
 }
 
 
-static void calculate_update_time(const struct os_reltime *fetch_time,
-				  unsigned int age_ms,
-				  struct os_reltime *update_time)
+void calculate_update_time(const struct os_reltime *fetch_time,
+			   unsigned int age_ms,
+			   struct os_reltime *update_time)
 {
 	os_time_t usec;
 
@@ -303,6 +306,47 @@
 }
 
 
+static int wpa_bss_is_wps_candidate(struct wpa_supplicant *wpa_s,
+				    struct wpa_bss *bss)
+{
+#ifdef CONFIG_WPS
+	struct wpa_ssid *ssid;
+	struct wpabuf *wps_ie;
+	int pbc = 0, ret;
+
+	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+	if (!wps_ie)
+		return 0;
+
+	if (wps_is_selected_pbc_registrar(wps_ie)) {
+		pbc = 1;
+	} else if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
+		wpabuf_free(wps_ie);
+		return 0;
+	}
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+			continue;
+		if (ssid->ssid_len &&
+		    (ssid->ssid_len != bss->ssid_len ||
+		     os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) != 0))
+			continue;
+
+		if (pbc)
+			ret = eap_is_wps_pbc_enrollee(&ssid->eap);
+		else
+			ret = eap_is_wps_pin_enrollee(&ssid->eap);
+		wpabuf_free(wps_ie);
+		return ret;
+	}
+	wpabuf_free(wps_ie);
+#endif /* CONFIG_WPS */
+
+	return 0;
+}
+
+
 static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
 	struct wpa_ssid *ssid;
@@ -341,7 +385,8 @@
 	struct wpa_bss *bss;
 
 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-		if (!wpa_bss_known(wpa_s, bss)) {
+		if (!wpa_bss_known(wpa_s, bss) &&
+		    !wpa_bss_is_wps_candidate(wpa_s, bss)) {
 			wpa_bss_remove(wpa_s, bss, __func__);
 			return 0;
 		}
@@ -552,6 +597,42 @@
 {
 	u32 changes;
 
+	if (bss->last_update_idx == wpa_s->bss_update_idx) {
+		struct os_reltime update_time;
+
+		/*
+		 * Some drivers (e.g., cfg80211) include multiple BSS entries
+		 * for the same BSS if that BSS's channel changes. The BSS list
+		 * implementation in wpa_supplicant does not do that and we need
+		 * to filter out the obsolete results here to make sure only the
+		 * most current BSS information remains in the table.
+		 */
+		wpa_printf(MSG_DEBUG, "BSS: " MACSTR
+			   " has multiple entries in the scan results - select the most current one",
+			   MAC2STR(bss->bssid));
+		calculate_update_time(fetch_time, res->age, &update_time);
+		wpa_printf(MSG_DEBUG,
+			   "Previous last_update: %u.%06u (freq %d%s)",
+			   (unsigned int) bss->last_update.sec,
+			   (unsigned int) bss->last_update.usec,
+			   bss->freq,
+			   (bss->flags & WPA_BSS_ASSOCIATED) ? " assoc" : "");
+		wpa_printf(MSG_DEBUG, "New last_update: %u.%06u (freq %d%s)",
+			   (unsigned int) update_time.sec,
+			   (unsigned int) update_time.usec,
+			   res->freq,
+			   (res->flags & WPA_SCAN_ASSOCIATED) ? " assoc" : "");
+		if ((bss->flags & WPA_BSS_ASSOCIATED) ||
+		    (!(res->flags & WPA_SCAN_ASSOCIATED) &&
+		     !os_reltime_before(&bss->last_update, &update_time))) {
+			wpa_printf(MSG_DEBUG,
+				   "Ignore this BSS entry since the previous update looks more current");
+			return bss;
+		}
+		wpa_printf(MSG_DEBUG,
+			   "Accept this BSS entry since it looks more current than the previous update");
+	}
+
 	changes = wpa_bss_compare_res(bss, res);
 	if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
 		wpa_printf(MSG_DEBUG, "BSS: " MACSTR " changed freq %d --> %d",
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index f7f72f3..84505fa 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -40,6 +40,7 @@
 	struct wpabuf *nai_realm;
 	struct wpabuf *anqp_3gpp;
 	struct wpabuf *domain_name;
+	struct wpabuf *fils_realm_info;
 	struct dl_list anqp_elems; /* list of struct wpa_bss_anqp_elem */
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
@@ -113,6 +114,8 @@
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 			     struct wpa_scan_res *res,
 			     struct os_reltime *fetch_time);
+void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+		    const char *reason);
 void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
 			int new_scan);
 int wpa_bss_init(struct wpa_supplicant *wpa_s);
@@ -165,4 +168,8 @@
 		bss->level = new_level;
 }
 
+void calculate_update_time(const struct os_reltime *fetch_time,
+			   unsigned int age_ms,
+			   struct os_reltime *update_time);
+
 #endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 4376676..58ecf19 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "utils/uuid.h"
 #include "utils/ip_addr.h"
+#include "common/ieee802_1x_defs.h"
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
@@ -478,6 +479,12 @@
 		}
 		wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
 				      (u8 *) value, len);
+		if (has_ctrl_char((u8 *) value, len)) {
+			wpa_printf(MSG_ERROR,
+				   "Line %d: Invalid passphrase character",
+				   line);
+			return -1;
+		}
 		if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
 		    os_memcmp(ssid->passphrase, value, len) == 0) {
 			/* No change to the previously configured value */
@@ -713,6 +720,18 @@
 		else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
 			val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+		else if (os_strcmp(start, "FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA256;
+		else if (os_strcmp(start, "FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FILS_SHA384;
+#ifdef CONFIG_IEEE80211R
+		else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA256;
+		else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
+			val |= WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
 		else {
 			wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
 				   line, start);
@@ -1810,6 +1829,69 @@
 #endif /* CONFIG_MESH */
 
 
+#ifdef CONFIG_MACSEC
+
+static int wpa_config_parse_mka_cak(const struct parse_data *data,
+				    struct wpa_ssid *ssid, int line,
+				    const char *value)
+{
+	if (hexstr2bin(value, ssid->mka_cak, MACSEC_CAK_LEN) ||
+	    value[MACSEC_CAK_LEN * 2] != '\0') {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
+			   line, value);
+		return -1;
+	}
+
+	ssid->mka_psk_set |= MKA_PSK_SET_CAK;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak, MACSEC_CAK_LEN);
+	return 0;
+}
+
+
+static int wpa_config_parse_mka_ckn(const struct parse_data *data,
+				    struct wpa_ssid *ssid, int line,
+				    const char *value)
+{
+	if (hexstr2bin(value, ssid->mka_ckn, MACSEC_CKN_LEN) ||
+	    value[MACSEC_CKN_LEN * 2] != '\0') {
+		wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
+			   line, value);
+		return -1;
+	}
+
+	ssid->mka_psk_set |= MKA_PSK_SET_CKN;
+
+	wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn, MACSEC_CKN_LEN);
+	return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mka_cak(const struct parse_data *data,
+				       struct wpa_ssid *ssid)
+{
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
+		return NULL;
+
+	return wpa_config_write_string_hex(ssid->mka_cak, MACSEC_CAK_LEN);
+}
+
+
+static char * wpa_config_write_mka_ckn(const struct parse_data *data,
+				       struct wpa_ssid *ssid)
+{
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
+		return NULL;
+	return wpa_config_write_string_hex(ssid->mka_ckn, MACSEC_CKN_LEN);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MACSEC */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -1999,6 +2081,7 @@
 	{ INT(dot11MeshHoldingTimeout) },
 #endif /* CONFIG_MESH */
 	{ INT(wpa_ptk_rekey) },
+	{ INT(group_rekey) },
 	{ STR(bgscan) },
 	{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
 #ifdef CONFIG_P2P
@@ -2043,12 +2126,18 @@
 	{ INT(beacon_int) },
 #ifdef CONFIG_MACSEC
 	{ INT_RANGE(macsec_policy, 0, 1) },
+	{ INT_RANGE(macsec_integ_only, 0, 1) },
+	{ INT_RANGE(macsec_port, 1, 65534) },
+	{ INT_RANGE(mka_priority, 0, 255) },
+	{ FUNC_KEY(mka_cak) },
+	{ FUNC_KEY(mka_ckn) },
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
 	{ INT(update_identifier) },
 #endif /* CONFIG_HS20 */
 	{ INT_RANGE(mac_addr, 0, 2) },
-	{ INT_RANGE(pbss, 0, 1) },
+	{ INT_RANGE(pbss, 0, 2) },
+	{ INT_RANGE(wps_disabled, 0, 1) },
 };
 
 #undef OFFSET
@@ -2530,6 +2619,9 @@
 #ifdef CONFIG_IEEE80211W
 	ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_MACSEC
+	ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+#endif /* CONFIG_MACSEC */
 	ssid->mac_addr = -1;
 }
 
@@ -2662,9 +2754,8 @@
 	return props;
 
 err:
-	value = *props;
-	while (value)
-		os_free(value++);
+	for (i = 0; props[i]; i++)
+		os_free(props[i]);
 	os_free(props);
 	return NULL;
 #endif /* NO_CONFIG_WRITE */
@@ -2695,14 +2786,15 @@
 		const struct parse_data *field = &ssid_fields[i];
 		if (os_strcmp(var, field->name) == 0) {
 			char *ret = field->writer(field, ssid);
-			if (ret != NULL && (os_strchr(ret, '\r') != NULL ||
-				os_strchr(ret, '\n') != NULL)) {
+
+			if (ret && has_newline(ret)) {
 				wpa_printf(MSG_ERROR,
-					"Found newline in value for %s; "
-					"not returning it", var);
+					   "Found newline in value for %s; not returning it",
+					   var);
 				os_free(ret);
 				ret = NULL;
 			}
+
 			return ret;
 		}
 	}
@@ -2889,6 +2981,8 @@
 
 	if (os_strcmp(var, "password") == 0 &&
 	    os_strncmp(value, "ext:", 4) == 0) {
+		if (has_newline(value))
+			return -1;
 		str_clear_free(cred->password);
 		cred->password = os_strdup(value);
 		cred->ext_password = 1;
@@ -2939,9 +3033,14 @@
 	}
 
 	val = wpa_config_parse_string(value, &len);
-	if (val == NULL) {
+	if (val == NULL ||
+	    (os_strcmp(var, "excluded_ssid") != 0 &&
+	     os_strcmp(var, "roaming_consortium") != 0 &&
+	     os_strcmp(var, "required_roaming_consortium") != 0 &&
+	     has_newline(val))) {
 		wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
 			   "value '%s'.", line, var, value);
+		os_free(val);
 		return -1;
 	}
 
@@ -3649,6 +3748,7 @@
 		config->ctrl_interface = os_strdup(ctrl_interface);
 	if (driver_param)
 		config->driver_param = os_strdup(driver_param);
+	config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
 
 	return config;
 }
@@ -3750,6 +3850,12 @@
 		return -1;
 	}
 
+	if (has_newline(pos)) {
+		wpa_printf(MSG_ERROR, "Line %d: invalid %s value with newline",
+			   line, data->name);
+		return -1;
+	}
+
 	tmp = os_strdup(pos);
 	if (tmp == NULL)
 		return -1;
@@ -3788,22 +3894,12 @@
 				       struct wpa_config *config, int line,
 				       const char *pos)
 {
-	size_t len;
 	struct wpabuf **dst, *tmp;
 
-	len = os_strlen(pos);
-	if (len & 0x01)
+	tmp = wpabuf_parse_bin(pos);
+	if (!tmp)
 		return -1;
 
-	tmp = wpabuf_alloc(len / 2);
-	if (tmp == NULL)
-		return -1;
-
-	if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
-		wpabuf_free(tmp);
-		return -1;
-	}
-
 	dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
 	wpabuf_free(*dst);
 	*dst = tmp;
@@ -4350,6 +4446,7 @@
 	{ INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
 	{ INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
 #endif /* CONFIG_FST */
+	{ INT_RANGE(cert_in_cb, 0, 1), 0 },
 	{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
 	{ STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
 #ifdef CONFIG_MBO
@@ -4357,6 +4454,11 @@
 	{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
 		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
 #endif /*CONFIG_MBO */
+	{ INT(gas_address3), 0 },
+	{ INT_RANGE(ftm_responder, 0, 1), 0 },
+	{ INT_RANGE(ftm_initiator, 0, 1), 0 },
+	{ INT(gas_rand_addr_lifetime), 0 },
+	{ INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 9a13f5f..2f2bb01 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -1039,7 +1039,8 @@
 	 *
 	 * By default, PMF is disabled unless enabled by the per-network
 	 * ieee80211w=1 or ieee80211w=2 parameter. pmf=1/2 can be used to change
-	 * this default behavior.
+	 * this default behavior for RSN network (this is not applicable for
+	 * non-RSN cases).
 	 */
 	enum mfp_options pmf;
 
@@ -1291,6 +1292,57 @@
 	 */
 	enum mbo_cellular_capa mbo_cell_capa;
 #endif /* CONFIG_MBO */
+
+	/**
+	 * gas_address3 - GAS Address3 field behavior
+	 *
+	 * Values:
+	 * 0 - P2P specification (Address3 = AP BSSID)
+	 * 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when
+	 *	sent to not-associated AP; if associated, AP BSSID)
+	 */
+	int gas_address3;
+
+	/**
+	 * ftm_responder - Publish FTM (fine timing measurement)
+	 * responder functionality
+	 *
+	 * Values:
+	 * 0 - do not publish FTM responder functionality (Default)
+	 * 1 - publish FTM responder functionality in
+	 *	bit 70 of Extended Capabilities element
+	 * Note, actual FTM responder operation is managed outside
+	 * wpa_supplicant.
+	 */
+	int ftm_responder;
+
+	/**
+	 * ftm_initiator - Publish FTM (fine timing measurement)
+	 * initiator functionality
+	 *
+	 * Values:
+	 * 0 - do not publish FTM initiator functionality (Default)
+	 * 1 - publish FTM initiator functionality in
+	 *	bit 71 of Extended Capabilities element
+	 * Note, actual FTM initiator operation is managed outside
+	 * wpa_supplicant.
+	 */
+	int ftm_initiator;
+
+	/**
+	 * gas_rand_addr_lifetime - Lifetime of random MAC address for ANQP in
+	 *	seconds
+	 */
+	unsigned int gas_rand_addr_lifetime;
+
+	/**
+	 * gas_rand_mac_addr - GAS MAC address policy
+	 *
+	 * 0 = use permanent MAC address
+	 * 1 = use random MAC address
+	 * 2 = like 1, but maintain OUI (with local admin bit set)
+	 */
+	int gas_rand_mac_addr;
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 38061f1..84a1ee9 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -19,6 +19,7 @@
 #include "config.h"
 #include "base64.h"
 #include "uuid.h"
+#include "common/ieee802_1x_defs.h"
 #include "p2p/p2p.h"
 #include "eap_peer/eap_methods.h"
 #include "eap_peer/eap.h"
@@ -136,6 +137,9 @@
 		wpa_config_update_psk(ssid);
 	}
 
+	if (ssid->disabled == 2)
+		ssid->p2p_persistent_group = 1;
+
 	if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
 	    !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
 	    !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
@@ -662,6 +666,40 @@
 #endif /* CONFIG_P2P */
 
 
+#ifdef CONFIG_MACSEC
+
+static void write_mka_cak(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
+		return;
+
+	value = wpa_config_get(ssid, "mka_cak");
+	if (!value)
+		return;
+	fprintf(f, "\tmka_cak=%s\n", value);
+	os_free(value);
+}
+
+
+static void write_mka_ckn(FILE *f, struct wpa_ssid *ssid)
+{
+	char *value;
+
+	if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
+		return;
+
+	value = wpa_config_get(ssid, "mka_ckn");
+	if (!value)
+		return;
+	fprintf(f, "\tmka_ckn=%s\n", value);
+	os_free(value);
+}
+
+#endif /* CONFIG_MACSEC */
+
+
 static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 {
 	int i;
@@ -756,6 +794,7 @@
 	INT(mixed_cell);
 	INT(max_oper_chwidth);
 	INT(pbss);
+	INT(wps_disabled);
 #ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
@@ -771,6 +810,11 @@
 	INT(beacon_int);
 #ifdef CONFIG_MACSEC
 	INT(macsec_policy);
+	write_mka_cak(f, ssid);
+	write_mka_ckn(f, ssid);
+	INT(macsec_integ_only);
+	INT(macsec_port);
+	INT_DEF(mka_priority, DEFAULT_PRIO_NOT_KEY_SERVER);
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
 	INT(update_identifier);
@@ -784,6 +828,7 @@
 	INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
 #endif /* CONFIG_MESH */
 	INT(wpa_ptk_rekey);
+	INT(group_rekey);
 	INT(ignore_broadcast_ssid);
 #ifdef CONFIG_HT_OVERRIDES
 	INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
@@ -1074,6 +1119,17 @@
 	}
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
+	{
+		int i;
+		char _buf[WPS_DEV_TYPE_BUFSIZE], *buf;
+
+		for (i = 0; i < config->num_sec_device_types; i++) {
+			buf = wps_dev_type_bin2str(config->sec_device_type[i],
+						   _buf, sizeof(_buf));
+			if (buf)
+				fprintf(f, "sec_device_type=%s\n", buf);
+		}
+	}
 	if (config->p2p_listen_reg_class)
 		fprintf(f, "p2p_listen_reg_class=%d\n",
 			config->p2p_listen_reg_class);
@@ -1173,6 +1229,8 @@
 			config->bss_expiration_scan_count);
 	if (config->filter_ssids)
 		fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
+	if (config->filter_rssi)
+		fprintf(f, "filter_rssi=%d\n", config->filter_rssi);
 	if (config->max_num_sta != DEFAULT_MAX_NUM_STA)
 		fprintf(f, "max_num_sta=%u\n", config->max_num_sta);
 	if (config->disassoc_low_ack)
@@ -1224,7 +1282,7 @@
 	if (config->sae_groups) {
 		int i;
 		fprintf(f, "sae_groups=");
-		for (i = 0; config->sae_groups[i] >= 0; i++) {
+		for (i = 0; config->sae_groups[i] > 0; i++) {
 			fprintf(f, "%s%d", i > 0 ? " " : "",
 				config->sae_groups[i]);
 		}
@@ -1276,6 +1334,9 @@
 	if (config->bgscan)
 		fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
 
+	if (config->autoscan)
+		fprintf(f, "autoscan=%s\n", config->autoscan);
+
 	if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
 		fprintf(f, "p2p_search_delay=%u\n",
 			config->p2p_search_delay);
@@ -1335,6 +1396,30 @@
 		fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
 #endif /* CONFIG_MBO */
 
+	if (config->gas_address3)
+		fprintf(f, "gas_address3=%d\n", config->gas_address3);
+
+	if (config->ftm_responder)
+		fprintf(f, "ftm_responder=%d\n", config->ftm_responder);
+	if (config->ftm_initiator)
+		fprintf(f, "ftm_initiator=%d\n", config->ftm_initiator);
+
+	if (config->osu_dir)
+		fprintf(f, "osu_dir=%s\n", config->osu_dir);
+
+	if (config->fst_group_id)
+		fprintf(f, "fst_group_id=%s\n", config->fst_group_id);
+	if (config->fst_priority)
+		fprintf(f, "fst_priority=%d\n", config->fst_priority);
+	if (config->fst_llt)
+		fprintf(f, "fst_llt=%d\n", config->fst_llt);
+
+	if (config->gas_rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
+		fprintf(f, "gas_rand_addr_lifetime=%u\n",
+			config->gas_rand_addr_lifetime);
+	if (config->gas_rand_mac_addr)
+		fprintf(f, "gas_rand_mac_addr=%d\n", config->gas_rand_mac_addr);
+
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index eb7b87b..69ace37 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -361,10 +361,14 @@
 
 	/**
 	 * pbss - Whether to use PBSS. Relevant to DMG networks only.
+	 * 0 = do not use PBSS
+	 * 1 = use PBSS
+	 * 2 = don't care (not allowed in AP mode)
 	 * Used together with mode configuration. When mode is AP, it
 	 * means to start a PCP instead of a regular AP. When mode is INFRA it
-	 * means connect to a PCP instead of AP. P2P_GO and P2P_GROUP_FORMATION
-	 * modes must use PBSS in DMG network.
+	 * means connect to a PCP instead of AP. In this mode you can also
+	 * specify 2 (don't care) meaning connect to either AP or PCP.
+	 * P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in DMG network.
 	 */
 	int pbss;
 
@@ -483,6 +487,14 @@
 	int wpa_ptk_rekey;
 
 	/**
+	 * group_rekey - Group rekeying time in seconds
+	 *
+	 * This value, if non-zero, is used as the dot11RSNAConfigGroupRekeyTime
+	 * parameter when operating in Authenticator role in IBSS.
+	 */
+	int group_rekey;
+
+	/**
 	 * scan_freq - Array of frequencies to scan or %NULL for all
 	 *
 	 * This is an optional zero-terminated array of frequencies in
@@ -716,6 +728,54 @@
 	 *    determine whether to use a secure session or not.
 	 */
 	int macsec_policy;
+
+	/**
+	 * macsec_integ_only - Determines how MACsec are transmitted
+	 *
+	 * This setting applies only when MACsec is in use, i.e.,
+	 *  - macsec_policy is enabled
+	 *  - the key server has decided to enable MACsec
+	 *
+	 * 0: Encrypt traffic (default)
+	 * 1: Integrity only
+	 */
+	int macsec_integ_only;
+
+	/**
+	 * macsec_port - MACsec port (in SCI)
+	 *
+	 * Port component of the SCI.
+	 *
+	 * Range: 1-65534 (default: 1)
+	 */
+	int macsec_port;
+
+	/**
+	 * mka_priority - Priority of MKA Actor
+	 *
+	 * Range: 0-255 (default: 255)
+	 */
+	int mka_priority;
+
+	/**
+	 * mka_ckn - MKA pre-shared CKN
+	 */
+#define MACSEC_CKN_LEN 32
+	u8 mka_ckn[MACSEC_CKN_LEN];
+
+	/**
+	 * mka_cak - MKA pre-shared CAK
+	 */
+#define MACSEC_CAK_LEN 16
+	u8 mka_cak[MACSEC_CAK_LEN];
+
+#define MKA_PSK_SET_CKN BIT(0)
+#define MKA_PSK_SET_CAK BIT(1)
+#define MKA_PSK_SET (MKA_PSK_SET_CKN | MKA_PSK_SET_CAK)
+	/**
+	 * mka_psk_set - Whether mka_ckn and mka_cak are set
+	 */
+	u8 mka_psk_set;
 #endif /* CONFIG_MACSEC */
 
 #ifdef CONFIG_HS20
@@ -744,6 +804,14 @@
 	 * this MBSS will trigger a peering attempt.
 	 */
 	int no_auto_peer;
+
+	/**
+	 * wps_disabled - WPS disabled in AP mode
+	 *
+	 * 0 = WPS enabled and configured (default)
+	 * 1 = WPS disabled
+	 */
+	int wps_disabled;
 };
 
 #endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
index 199f04f..82ba3b0 100644
--- a/wpa_supplicant/config_winreg.c
+++ b/wpa_supplicant/config_winreg.c
@@ -933,6 +933,7 @@
 #ifdef CONFIG_HS20
 	INT(update_identifier);
 #endif /* CONFIG_HS20 */
+	INT(group_rekey);
 
 #undef STR
 #undef INT
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 8574437..761d917 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -15,6 +15,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/uuid.h"
+#include "utils/module_tests.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -311,6 +312,102 @@
 }
 
 
+static int wpas_ctrl_iface_set_lci(struct wpa_supplicant *wpa_s,
+				   const char *cmd)
+{
+	struct wpabuf *lci;
+
+	if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) {
+		wpabuf_free(wpa_s->lci);
+		wpa_s->lci = NULL;
+		return 0;
+	}
+
+	lci = wpabuf_parse_bin(cmd);
+	if (!lci)
+		return -1;
+
+	if (os_get_reltime(&wpa_s->lci_time)) {
+		wpabuf_free(lci);
+		return -1;
+	}
+
+	wpabuf_free(wpa_s->lci);
+	wpa_s->lci = lci;
+
+	return 0;
+}
+
+
+static int
+wpas_ctrl_set_relative_rssi(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	int relative_rssi;
+
+	if (os_strcmp(cmd, "disable") == 0) {
+		wpa_s->srp.relative_rssi_set = 0;
+		return 0;
+	}
+
+	relative_rssi = atoi(cmd);
+	if (relative_rssi < 0 || relative_rssi > 100)
+		return -1;
+	wpa_s->srp.relative_rssi = relative_rssi;
+	wpa_s->srp.relative_rssi_set = 1;
+	return 0;
+}
+
+
+static int wpas_ctrl_set_relative_band_adjust(struct wpa_supplicant *wpa_s,
+					      const char *cmd)
+{
+	char *pos;
+	int adjust_rssi;
+
+	/* <band>:adjust_value */
+	pos = os_strchr(cmd, ':');
+	if (!pos)
+		return -1;
+	pos++;
+	adjust_rssi = atoi(pos);
+	if (adjust_rssi < -100 || adjust_rssi > 100)
+		return -1;
+
+	if (os_strncmp(cmd, "2G", 2) == 0)
+		wpa_s->srp.relative_adjust_band = WPA_SETBAND_2G;
+	else if (os_strncmp(cmd, "5G", 2) == 0)
+		wpa_s->srp.relative_adjust_band = WPA_SETBAND_5G;
+	else
+		return -1;
+
+	wpa_s->srp.relative_adjust_rssi = adjust_rssi;
+
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_set_ric_ies(struct wpa_supplicant *wpa_s,
+				   const char *cmd)
+{
+	struct wpabuf *ric_ies;
+
+	if (*cmd == '\0' || os_strcmp(cmd, "\"\"") == 0) {
+		wpabuf_free(wpa_s->ric_ies);
+		wpa_s->ric_ies = NULL;
+		return 0;
+	}
+
+	ric_ies = wpabuf_parse_bin(cmd);
+	if (!ric_ies)
+		return -1;
+
+	wpabuf_free(wpa_s->ric_ies);
+	wpa_s->ric_ies = ric_ies;
+
+	return 0;
+}
+
+
 static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
 					 char *cmd)
 {
@@ -337,16 +434,29 @@
 				   -1, -1, -1, atoi(value));
 	} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
 		if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
-				     atoi(value)))
+				     atoi(value))) {
 			ret = -1;
+		} else {
+			value[-1] = '=';
+			wpa_config_process_global(wpa_s->conf, cmd, -1);
+		}
 	} else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
 		   0) {
 		if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
-				     atoi(value)))
+				     atoi(value))) {
 			ret = -1;
+		} else {
+			value[-1] = '=';
+			wpa_config_process_global(wpa_s->conf, cmd, -1);
+		}
 	} else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
-		if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
+		if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
+				     atoi(value))) {
 			ret = -1;
+		} else {
+			value[-1] = '=';
+			wpa_config_process_global(wpa_s->conf, cmd, -1);
+		}
 	} else if (os_strcasecmp(cmd, "wps_fragment_size") == 0) {
 		wpa_s->wps_fragment_size = atoi(value);
 #ifdef CONFIG_WPS_TESTING
@@ -393,7 +503,6 @@
 #ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
 	} else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
-		extern unsigned int tdls_testing;
 		tdls_testing = strtol(value, NULL, 0);
 		wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
 #endif /* CONFIG_TDLS_TESTING */
@@ -484,6 +593,18 @@
 		wpa_s->test_failure = atoi(value);
 	} else if (os_strcasecmp(cmd, "p2p_go_csa_on_inv") == 0) {
 		wpa_s->p2p_go_csa_on_inv = !!atoi(value);
+	} else if (os_strcasecmp(cmd, "ignore_auth_resp") == 0) {
+		wpa_s->ignore_auth_resp = !!atoi(value);
+	} else if (os_strcasecmp(cmd, "ignore_assoc_disallow") == 0) {
+		wpa_s->ignore_assoc_disallow = !!atoi(value);
+	} else if (os_strcasecmp(cmd, "reject_btm_req_reason") == 0) {
+		wpa_s->reject_btm_req_reason = atoi(value);
+	} else if (os_strcasecmp(cmd, "get_pref_freq_list_override") == 0) {
+		os_free(wpa_s->get_pref_freq_list_override);
+		if (!value[0])
+			wpa_s->get_pref_freq_list_override = NULL;
+		else
+			wpa_s->get_pref_freq_list_override = os_strdup(value);
 #endif /* CONFIG_TESTING_OPTIONS */
 #ifndef CONFIG_NO_CONFIG_BLOBS
 	} else if (os_strcmp(cmd, "blob") == 0) {
@@ -494,9 +615,23 @@
 #ifdef CONFIG_MBO
 	} else if (os_strcasecmp(cmd, "non_pref_chan") == 0) {
 		ret = wpas_mbo_update_non_pref_chan(wpa_s, value);
+		if (ret == 0) {
+			value[-1] = '=';
+			wpa_config_process_global(wpa_s->conf, cmd, -1);
+		}
 	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
 		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
 #endif /* CONFIG_MBO */
+	} else if (os_strcasecmp(cmd, "lci") == 0) {
+		ret = wpas_ctrl_iface_set_lci(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "tdls_trigger_control") == 0) {
+		ret = wpa_drv_set_tdls_mode(wpa_s, atoi(value));
+	} else if (os_strcasecmp(cmd, "relative_rssi") == 0) {
+		ret = wpas_ctrl_set_relative_rssi(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "relative_band_adjust") == 0) {
+		ret = wpas_ctrl_set_relative_band_adjust(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "ric_ies") == 0) {
+		ret = wpas_ctrl_iface_set_ric_ies(wpa_s, value);
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -1857,6 +1992,10 @@
 						  "mode=P2P GO - group "
 						  "formation\n");
 				break;
+			case WPAS_MODE_MESH:
+				ret = os_snprintf(pos, end - pos,
+						  "mode=mesh\n");
+				break;
 			default:
 				ret = 0;
 				break;
@@ -1875,6 +2014,7 @@
 #endif /* CONFIG_AP */
 		pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
 	}
+#ifdef CONFIG_SME
 #ifdef CONFIG_SAE
 	if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
 #ifdef CONFIG_AP
@@ -1888,6 +2028,7 @@
 		pos += ret;
 	}
 #endif /* CONFIG_SAE */
+#endif /* CONFIG_SME */
 	ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
 			  wpa_supplicant_state_txt(wpa_s->wpa_state));
 	if (os_snprintf_error(end - pos, ret))
@@ -2009,6 +2150,12 @@
 			pos += res;
 	}
 
+#ifdef CONFIG_MACSEC
+	res = ieee802_1x_kay_get_status(wpa_s->kay, pos, end - pos);
+	if (res > 0)
+		pos += res;
+#endif /* CONFIG_MACSEC */
+
 	sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
 	if (sess_id) {
 		char *start = pos;
@@ -2398,6 +2545,39 @@
 	}
 #endif /* CONFIG_SUITEB192 */
 
+#ifdef CONFIG_FILS
+	if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#ifdef CONFIG_IEEE80211R
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+	if (data.key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
+				  pos == start ? "" : "+");
+		if (os_snprintf_error(end - pos, ret))
+			return pos;
+		pos += ret;
+	}
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
 	if (data.key_mgmt & WPA_KEY_MGMT_OSEN) {
 		ret = os_snprintf(pos, end - pos, "%sOSEN",
 				  pos == start ? "" : "+");
@@ -2569,6 +2749,14 @@
 		pos += ret;
 	}
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_FILS
+	if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
+		ret = os_snprintf(pos, end - pos, "[FILS]");
+		if (os_snprintf_error(end - pos, ret))
+			return -1;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_FST
 	if (wpa_bss_get_ie(bss, WLAN_EID_MULTI_BAND)) {
 		ret = os_snprintf(pos, end - pos, "[FST]");
@@ -2890,15 +3078,10 @@
 
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK");
 
-	ssid = wpa_config_add_network(wpa_s->conf);
+	ssid = wpa_supplicant_add_network(wpa_s);
 	if (ssid == NULL)
 		return -1;
 
-	wpas_notify_network_added(wpa_s, ssid);
-
-	ssid->disabled = 1;
-	wpa_config_set_network_defaults(ssid);
-
 	ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
 	if (os_snprintf_error(buflen, ret))
 		return -1;
@@ -2911,7 +3094,7 @@
 {
 	int id;
 	struct wpa_ssid *ssid;
-	int was_disabled;
+	int result;
 
 	/* cmd: "<network id>" or "all" */
 	if (os_strcmp(cmd, "all") == 0) {
@@ -2947,54 +3130,17 @@
 	id = atoi(cmd);
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
 
-	ssid = wpa_config_get_network(wpa_s->conf, id);
-	if (ssid)
-		wpas_notify_network_removed(wpa_s, ssid);
-	if (ssid == NULL) {
+	result = wpa_supplicant_remove_network(wpa_s, id);
+	if (result == -1) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
 			   "id=%d", id);
 		return -1;
 	}
-
-	if (wpa_s->last_ssid == ssid)
-		wpa_s->last_ssid = NULL;
-
-	if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
-#ifdef CONFIG_SME
-		wpa_s->sme.prev_bssid_set = 0;
-#endif /* CONFIG_SME */
-		/*
-		 * Invalidate the EAP session cache if the current or
-		 * previously used network is removed.
-		 */
-		eapol_sm_invalidate_cached_session(wpa_s->eapol);
-	}
-
-	if (ssid == wpa_s->current_ssid) {
-		wpa_sm_set_config(wpa_s->wpa, NULL);
-		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
-
-		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
-			wpa_s->own_disconnect_req = 1;
-		wpa_supplicant_deauthenticate(wpa_s,
-					      WLAN_REASON_DEAUTH_LEAVING);
-	}
-
-	was_disabled = ssid->disabled;
-
-	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+	if (result == -2) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Not able to remove the "
 			   "network id=%d", id);
 		return -1;
 	}
-
-	if (!was_disabled && wpa_s->sched_scanning) {
-		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
-			   "network from filters");
-		wpa_supplicant_cancel_sched_scan(wpa_s);
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
-	}
-
 	return 0;
 }
 
@@ -4009,6 +4155,16 @@
 	}
 #endif /* CONFIG_ACS */
 
+#ifdef CONFIG_FILS
+	if (os_strcmp(field, "fils") == 0 &&
+	    (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SUPPORT_FILS)) {
+		res = os_snprintf(buf, buflen, "FILS");
+		if (os_snprintf_error(buflen, res))
+			return -1;
+		return res;
+	}
+#endif /* CONFIG_FILS */
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
 		   field);
 
@@ -4051,13 +4207,85 @@
 #endif /* CONFIG_INTERWORKING */
 
 
+#ifdef CONFIG_FILS
+static int print_fils_indication(struct wpa_bss *bss, char *pos, char *end)
+{
+	char *start = pos;
+	const u8 *ie, *ie_end;
+	u16 info, realms;
+	int ret;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+	if (!ie)
+		return 0;
+	ie_end = ie + 2 + ie[1];
+	ie += 2;
+	if (ie_end - ie < 2)
+		return -1;
+
+	info = WPA_GET_LE16(ie);
+	ie += 2;
+	ret = os_snprintf(pos, end - pos, "fils_info=%04x\n", info);
+	if (os_snprintf_error(end - pos, ret))
+		return 0;
+	pos += ret;
+
+	if (info & BIT(7)) {
+		/* Cache Identifier Included */
+		if (ie_end - ie < 2)
+			return -1;
+		ret = os_snprintf(pos, end - pos, "fils_cache_id=%02x%02x\n",
+				  ie[0], ie[1]);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie += 2;
+	}
+
+	if (info & BIT(8)) {
+		/* HESSID Included */
+		if (ie_end - ie < ETH_ALEN)
+			return -1;
+		ret = os_snprintf(pos, end - pos, "fils_hessid=" MACSTR "\n",
+				  MAC2STR(ie));
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+		ie += ETH_ALEN;
+	}
+
+	realms = (info & (BIT(3) | BIT(4) | BIT(5))) >> 3;
+	if (realms) {
+		if (ie_end - ie < realms * 2)
+			return -1;
+		ret = os_snprintf(pos, end - pos, "fils_realms=");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		ret = wpa_snprintf_hex(pos, end - pos, ie, realms * 2);
+		if (ret <= 0)
+			return 0;
+		pos += ret;
+		ie += realms * 2;
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	return pos - start;
+}
+#endif /* CONFIG_FILS */
+
+
 static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 			  unsigned long mask, char *buf, size_t buflen)
 {
 	size_t i;
 	int ret;
 	char *pos, *end;
-	const u8 *ie, *ie2, *osen_ie;
+	const u8 *ie, *ie2, *osen_ie, *mesh;
 
 	pos = buf;
 	end = buf + buflen;
@@ -4166,13 +4394,16 @@
 			return 0;
 		pos += ret;
 
+		mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
+
 		ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 		if (ie)
 			pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie,
 						    2 + ie[1]);
 		ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
 		if (ie2)
-			pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2,
+			pos = wpa_supplicant_ie_txt(pos, end,
+						    mesh ? "RSN" : "WPA2", ie2,
 						    2 + ie2[1]);
 		osen_ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 		if (osen_ie)
@@ -4186,6 +4417,14 @@
 				return 0;
 			pos += ret;
 		}
+
+		if (mesh) {
+			ret = os_snprintf(pos, end - pos, "[MESH]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+
 		if (bss_is_dmg(bss)) {
 			const char *s;
 			ret = os_snprintf(pos, end - pos, "[DMG]");
@@ -4239,6 +4478,14 @@
 			pos += ret;
 		}
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_FILS
+		if (wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION)) {
+			ret = os_snprintf(pos, end - pos, "[FILS]");
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+#endif /* CONFIG_FILS */
 
 		ret = os_snprintf(pos, end - pos, "\n");
 		if (os_snprintf_error(end - pos, ret))
@@ -4269,9 +4516,10 @@
 	if (mask & WPA_BSS_MASK_P2P_SCAN) {
 		ie = (const u8 *) (bss + 1);
 		ret = wpas_p2p_scan_result_text(ie, bss->ie_len, pos, end);
-		if (ret < 0 || ret >= end - pos)
+		if (ret >= end - pos)
 			return 0;
-		pos += ret;
+		if (ret > 0)
+			pos += ret;
 	}
 #endif /* CONFIG_P2P */
 
@@ -4322,6 +4570,8 @@
 		pos = anqp_add_hex(pos, end, "anqp_3gpp", anqp->anqp_3gpp);
 		pos = anqp_add_hex(pos, end, "anqp_domain_name",
 				   anqp->domain_name);
+		pos = anqp_add_hex(pos, end, "anqp_fils_realm_info",
+				   anqp->fils_realm_info);
 #ifdef CONFIG_HS20
 		pos = anqp_add_hex(pos, end, "hs20_capability_list",
 				   anqp->hs20_capability_list);
@@ -4352,9 +4602,10 @@
 	if (mask & WPA_BSS_MASK_MESH_SCAN) {
 		ie = (const u8 *) (bss + 1);
 		ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
-		if (ret < 0 || ret >= end - pos)
+		if (ret >= end - pos)
 			return 0;
-		pos += ret;
+		if (ret > 0)
+			pos += ret;
 	}
 #endif /* CONFIG_MESH */
 
@@ -4382,6 +4633,44 @@
 	}
 #endif /* CONFIG_FST */
 
+	if (mask & WPA_BSS_MASK_UPDATE_IDX) {
+		ret = os_snprintf(pos, end - pos, "update_idx=%u\n",
+				  bss->last_update_idx);
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+	if ((mask & WPA_BSS_MASK_BEACON_IE) && bss->beacon_ie_len) {
+		ret = os_snprintf(pos, end - pos, "beacon_ie=");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+
+		ie = (const u8 *) (bss + 1);
+		ie += bss->ie_len;
+		for (i = 0; i < bss->beacon_ie_len; i++) {
+			ret = os_snprintf(pos, end - pos, "%02x", *ie++);
+			if (os_snprintf_error(end - pos, ret))
+				return 0;
+			pos += ret;
+		}
+
+		ret = os_snprintf(pos, end - pos, "\n");
+		if (os_snprintf_error(end - pos, ret))
+			return 0;
+		pos += ret;
+	}
+
+#ifdef CONFIG_FILS
+	if (mask & WPA_BSS_MASK_FILS_INDICATION) {
+		ret = print_fils_indication(bss, pos, end);
+		if (ret < 0)
+			return 0;
+		pos += ret;
+	}
+#endif /* CONFIG_FILS */
+
 	if (mask & WPA_BSS_MASK_DELIM) {
 		ret = os_snprintf(pos, end - pos, "====\n");
 		if (os_snprintf_error(end - pos, ret))
@@ -4472,6 +4761,8 @@
 				bss = dl_list_entry(next, struct wpa_bss,
 						    list_id);
 		}
+	} else if (os_strncmp(cmd, "CURRENT", 7) == 0) {
+		bss = wpa_s->current_bss;
 #ifdef CONFIG_P2P
 	} else if (os_strncmp(cmd, "p2p_dev_addr=", 13) == 0) {
 		if (hwaddr_aton(cmd + 13, bssid) == 0)
@@ -5069,6 +5360,8 @@
 		wps_method = WPS_PIN_DISPLAY;
 	} else if (os_strncmp(pos, "pbc", 3) == 0) {
 		wps_method = WPS_PBC;
+	} else if (os_strstr(pos, "p2ps") != NULL) {
+		wps_method = WPS_P2PS;
 	} else {
 		pin = pos;
 		pos = os_strchr(pin, ' ');
@@ -5077,8 +5370,6 @@
 			*pos++ = '\0';
 			if (os_strncmp(pos, "display", 7) == 0)
 				wps_method = WPS_PIN_DISPLAY;
-			else if (os_strncmp(pos, "p2ps", 4) == 0)
-				wps_method = WPS_P2PS;
 		}
 		if (!wps_pin_str_valid(pin)) {
 			os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
@@ -5805,10 +6096,47 @@
 }
 
 
+static int p2p_ctrl_group_member(struct wpa_supplicant *wpa_s, const char *cmd,
+				 char *buf, size_t buflen)
+{
+	u8 dev_addr[ETH_ALEN];
+	struct wpa_ssid *ssid;
+	int res;
+	const u8 *iaddr;
+
+	ssid = wpa_s->current_ssid;
+	if (!wpa_s->global->p2p || !ssid || ssid->mode != WPAS_MODE_P2P_GO ||
+	    hwaddr_aton(cmd, dev_addr))
+		return -1;
+
+	iaddr = p2p_group_get_client_interface_addr(wpa_s->p2p_group, dev_addr);
+	if (!iaddr)
+		return -1;
+	res = os_snprintf(buf, buflen, MACSTR, MAC2STR(iaddr));
+	if (os_snprintf_error(buflen, res))
+		return -1;
+	return res;
+}
+
+
+static int wpas_find_p2p_dev_addr_bss(struct wpa_global *global,
+				      const u8 *p2p_dev_addr)
+{
+	struct wpa_supplicant *wpa_s;
+
+	for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+		if (wpa_bss_get_p2p_dev_addr(wpa_s, p2p_dev_addr))
+			return 1;
+	}
+
+	return 0;
+}
+
+
 static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
 			 char *buf, size_t buflen)
 {
-	u8 addr[ETH_ALEN], *addr_ptr;
+	u8 addr[ETH_ALEN], *addr_ptr, group_capab;
 	int next, res;
 	const struct p2p_peer_info *info;
 	char *pos, *end;
@@ -5837,6 +6165,16 @@
 	info = p2p_get_peer_info(wpa_s->global->p2p, addr_ptr, next);
 	if (info == NULL)
 		return -1;
+	group_capab = info->group_capab;
+
+	if (group_capab &&
+	    !wpas_find_p2p_dev_addr_bss(wpa_s->global, info->p2p_device_addr)) {
+		wpa_printf(MSG_DEBUG,
+			   "P2P: Could not find any BSS with p2p_dev_addr "
+			   MACSTR ", hence override group_capab from 0x%x to 0",
+			   MAC2STR(info->p2p_device_addr), group_capab);
+		group_capab = 0;
+	}
 
 	pos = buf;
 	end = buf + buflen;
@@ -5862,7 +6200,7 @@
 			  info->serial_number,
 			  info->config_methods,
 			  info->dev_capab,
-			  info->group_capab,
+			  group_capab,
 			  info->level);
 	if (os_snprintf_error(end - pos, res))
 		return pos - buf;
@@ -6143,6 +6481,20 @@
 		return 0;
 	}
 
+	if (os_strcmp(cmd, "override_pref_op_chan") == 0) {
+		int op_class, chan;
+
+		op_class = atoi(param);
+		param = os_strchr(param, ':');
+		if (!param)
+			return -1;
+		param++;
+		chan = atoi(param);
+		p2p_set_override_pref_op_chan(wpa_s->global->p2p, op_class,
+					      chan);
+		return 0;
+	}
+
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
 		   cmd);
 
@@ -6229,6 +6581,21 @@
 	return 0;
 }
 
+
+static int p2p_ctrl_iface_p2p_lo_start(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int freq = 0, period = 0, interval = 0, count = 0;
+
+	if (sscanf(cmd, "%d %d %d %d", &freq, &period, &interval, &count) != 4)
+	{
+		wpa_printf(MSG_DEBUG,
+			   "CTRL: Invalid P2P LO Start parameter: '%s'", cmd);
+		return -1;
+	}
+
+	return wpas_p2p_lo_start(wpa_s, freq, period, interval, count);
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -6346,6 +6713,7 @@
 	u16 id[MAX_ANQP_INFO_ID];
 	size_t num_id = 0;
 	u32 subtypes = 0;
+	int get_cell_pref = 0;
 
 	used = hwaddr_aton2(dst, dst_addr);
 	if (used < 0)
@@ -6363,6 +6731,15 @@
 #else /* CONFIG_HS20 */
 			return -1;
 #endif /* CONFIG_HS20 */
+		} else if (os_strncmp(pos, "mbo:", 4) == 0) {
+#ifdef CONFIG_MBO
+			int num = atoi(pos + 4);
+			if (num != MBO_ANQP_SUBTYPE_CELL_CONN_PREF)
+				return -1;
+			get_cell_pref = 1;
+#else /* CONFIG_MBO */
+			return -1;
+#endif /* CONFIG_MBO */
 		} else {
 			id[num_id] = atoi(pos);
 			if (id[num_id])
@@ -6377,7 +6754,8 @@
 	if (num_id == 0)
 		return -1;
 
-	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
+	return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes,
+			     get_cell_pref);
 }
 
 
@@ -6714,6 +7092,9 @@
 		autoscan_init(wpa_s, 1);
 	else if (state == WPA_SCANNING)
 		wpa_supplicant_reinit_autoscan(wpa_s);
+	else
+		wpa_printf(MSG_DEBUG, "No autoscan update in state %s",
+			   wpa_supplicant_state_txt(state));
 
 	return 0;
 }
@@ -6882,6 +7263,46 @@
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
+						enum wpa_driver_if_type if_type,
+						unsigned int *num,
+						unsigned int *freq_list)
+{
+	char *pos = wpa_s->get_pref_freq_list_override;
+	char *end;
+	unsigned int count = 0;
+
+	/* Override string format:
+	 *  <if_type1>:<freq1>,<freq2>,... <if_type2>:... */
+
+	while (pos) {
+		if (atoi(pos) == (int) if_type)
+			break;
+		pos = os_strchr(pos, ' ');
+		if (pos)
+			pos++;
+	}
+	if (!pos)
+		return -1;
+	pos = os_strchr(pos, ':');
+	if (!pos)
+		return -1;
+	pos++;
+	end = os_strchr(pos, ' ');
+	while (pos && (!end || pos < end) && count < *num) {
+		freq_list[count++] = atoi(pos);
+		pos = os_strchr(pos, ',');
+		if (pos)
+			pos++;
+	}
+
+	*num = count;
+	return 0;
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
 static int wpas_ctrl_iface_get_pref_freq_list(
 	struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
 {
@@ -6929,6 +7350,34 @@
 }
 
 
+static int wpas_ctrl_iface_driver_flags(struct wpa_supplicant *wpa_s,
+					char *buf, size_t buflen)
+{
+	int ret, i;
+	char *pos, *end;
+
+	ret = os_snprintf(buf, buflen, "%016llX:\n",
+			  (long long unsigned) wpa_s->drv_flags);
+	if (os_snprintf_error(buflen, ret))
+		return -1;
+
+	pos = buf + ret;
+	end = buf + buflen;
+
+	for (i = 0; i < 64; i++) {
+		if (wpa_s->drv_flags & (1LLU << i)) {
+			ret = os_snprintf(pos, end - pos, "%s\n",
+					  driver_flag_to_string(1LLU << i));
+			if (os_snprintf_error(end - pos, ret))
+				return -1;
+			pos += ret;
+		}
+	}
+
+	return pos - buf;
+}
+
+
 static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
 				      size_t buflen)
 {
@@ -7040,7 +7489,8 @@
 
 	wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
 
-	wpas_abort_ongoing_scan(wpa_s);
+	if (wpas_abort_ongoing_scan(wpa_s) == 0)
+		wpa_s->ignore_post_flush_scan_res = 1;
 
 	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
 		/*
@@ -7083,7 +7533,6 @@
 
 #ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
-	extern unsigned int tdls_testing;
 	tdls_testing = 0;
 #endif /* CONFIG_TDLS_TESTING */
 	wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
@@ -7141,7 +7590,12 @@
 	wpa_s->extra_roc_dur = 0;
 	wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
 	wpa_s->p2p_go_csa_on_inv = 0;
+	wpa_s->ignore_auth_resp = 0;
+	wpa_s->ignore_assoc_disallow = 0;
+	wpa_s->reject_btm_req_reason = 0;
 	wpa_sm_set_test_assoc_ie(wpa_s->wpa, NULL);
+	os_free(wpa_s->get_pref_freq_list_override);
+	wpa_s->get_pref_freq_list_override = NULL;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	wpa_s->disconnected = 0;
@@ -7160,6 +7614,13 @@
 
 	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
 	wpa_s->wnmsleep_used = 0;
+
+#ifdef CONFIG_SME
+	wpa_s->sme.last_unprot_disconnect.sec = 0;
+#endif /* CONFIG_SME */
+
+	wpabuf_free(wpa_s->ric_ies);
+	wpa_s->ric_ies = NULL;
 }
 
 
@@ -7216,6 +7677,13 @@
 			eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
 					     work, NULL);
 
+		/*
+		 * work->type points to a buffer in ework, so need to replace
+		 * that here with a fixed string to avoid use of freed memory
+		 * in debug prints.
+		 */
+		work->type = "freed-ext-work";
+		work->ctx = NULL;
 		os_free(ework);
 		return;
 	}
@@ -7665,6 +8133,194 @@
 }
 
 
+static int wpas_ctrl_iface_mgmt_rx_process(struct wpa_supplicant *wpa_s,
+					   char *cmd)
+{
+	char *pos, *param;
+	size_t len;
+	u8 *buf;
+	int freq = 0, datarate = 0, ssi_signal = 0;
+	union wpa_event_data event;
+
+	if (!wpa_s->ext_mgmt_frame_handling)
+		return -1;
+
+	/* freq=<MHz> datarate=<val> ssi_signal=<val> frame=<frame hexdump> */
+
+	wpa_printf(MSG_DEBUG, "External MGMT RX process: %s", cmd);
+
+	pos = cmd;
+	param = os_strstr(pos, "freq=");
+	if (param) {
+		param += 5;
+		freq = atoi(param);
+	}
+
+	param = os_strstr(pos, " datarate=");
+	if (param) {
+		param += 10;
+		datarate = atoi(param);
+	}
+
+	param = os_strstr(pos, " ssi_signal=");
+	if (param) {
+		param += 12;
+		ssi_signal = atoi(param);
+	}
+
+	param = os_strstr(pos, " frame=");
+	if (param == NULL)
+		return -1;
+	param += 7;
+
+	len = os_strlen(param);
+	if (len & 1)
+		return -1;
+	len /= 2;
+
+	buf = os_malloc(len);
+	if (buf == NULL)
+		return -1;
+
+	if (hexstr2bin(param, buf, len) < 0) {
+		os_free(buf);
+		return -1;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	event.rx_mgmt.freq = freq;
+	event.rx_mgmt.frame = buf;
+	event.rx_mgmt.frame_len = len;
+	event.rx_mgmt.ssi_signal = ssi_signal;
+	event.rx_mgmt.datarate = datarate;
+	wpa_s->ext_mgmt_frame_handling = 0;
+	wpa_supplicant_event(wpa_s, EVENT_RX_MGMT, &event);
+	wpa_s->ext_mgmt_frame_handling = 1;
+
+	os_free(buf);
+
+	return 0;
+}
+
+
+static int wpas_ctrl_iface_driver_scan_res(struct wpa_supplicant *wpa_s,
+					   char *param)
+{
+	struct wpa_scan_res *res;
+	struct os_reltime now;
+	char *pos, *end;
+	int ret = -1;
+
+	if (!param)
+		return -1;
+
+	if (os_strcmp(param, "START") == 0) {
+		wpa_bss_update_start(wpa_s);
+		return 0;
+	}
+
+	if (os_strcmp(param, "END") == 0) {
+		wpa_bss_update_end(wpa_s, NULL, 1);
+		return 0;
+	}
+
+	if (os_strncmp(param, "BSS ", 4) != 0)
+		return -1;
+	param += 3;
+
+	res = os_zalloc(sizeof(*res) + os_strlen(param) / 2);
+	if (!res)
+		return -1;
+
+	pos = os_strstr(param, " flags=");
+	if (pos)
+		res->flags = strtol(pos + 7, NULL, 16);
+
+	pos = os_strstr(param, " bssid=");
+	if (pos && hwaddr_aton(pos + 7, res->bssid))
+		goto fail;
+
+	pos = os_strstr(param, " freq=");
+	if (pos)
+		res->freq = atoi(pos + 6);
+
+	pos = os_strstr(param, " beacon_int=");
+	if (pos)
+		res->beacon_int = atoi(pos + 12);
+
+	pos = os_strstr(param, " caps=");
+	if (pos)
+		res->caps = strtol(pos + 6, NULL, 16);
+
+	pos = os_strstr(param, " qual=");
+	if (pos)
+		res->qual = atoi(pos + 6);
+
+	pos = os_strstr(param, " noise=");
+	if (pos)
+		res->noise = atoi(pos + 7);
+
+	pos = os_strstr(param, " level=");
+	if (pos)
+		res->level = atoi(pos + 7);
+
+	pos = os_strstr(param, " tsf=");
+	if (pos)
+		res->tsf = strtoll(pos + 5, NULL, 16);
+
+	pos = os_strstr(param, " age=");
+	if (pos)
+		res->age = atoi(pos + 5);
+
+	pos = os_strstr(param, " est_throughput=");
+	if (pos)
+		res->est_throughput = atoi(pos + 16);
+
+	pos = os_strstr(param, " snr=");
+	if (pos)
+		res->snr = atoi(pos + 5);
+
+	pos = os_strstr(param, " parent_tsf=");
+	if (pos)
+		res->parent_tsf = strtoll(pos + 7, NULL, 16);
+
+	pos = os_strstr(param, " tsf_bssid=");
+	if (pos && hwaddr_aton(pos + 11, res->tsf_bssid))
+		goto fail;
+
+	pos = os_strstr(param, " ie=");
+	if (pos) {
+		pos += 4;
+		end = os_strchr(pos, ' ');
+		if (!end)
+			end = pos + os_strlen(pos);
+		res->ie_len = (end - pos) / 2;
+		if (hexstr2bin(pos, (u8 *) (res + 1), res->ie_len))
+			goto fail;
+	}
+
+	pos = os_strstr(param, " beacon_ie=");
+	if (pos) {
+		pos += 11;
+		end = os_strchr(pos, ' ');
+		if (!end)
+			end = pos + os_strlen(pos);
+		res->beacon_ie_len = (end - pos) / 2;
+		if (hexstr2bin(pos, ((u8 *) (res + 1)) + res->ie_len,
+			       res->beacon_ie_len))
+			goto fail;
+	}
+
+	os_get_reltime(&now);
+	wpa_bss_update_scan_res(wpa_s, res, &now);
+	ret = 0;
+fail:
+	os_free(res);
+
+	return ret;
+}
+
+
 static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
 {
 	char *pos, *param;
@@ -7695,6 +8351,8 @@
 		wpa_supplicant_event(wpa_s, ev, &event);
 		os_free(event.freq_range.range);
 		return 0;
+	} else if (os_strcmp(cmd, "SCAN_RES") == 0) {
+		return wpas_ctrl_iface_driver_scan_res(wpa_s, param);
 	} else {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
 			cmd);
@@ -7764,7 +8422,8 @@
 #define HWSIM_PACKETLEN 1500
 #define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
 
-void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+static void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+			      size_t len)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 	const struct ether_header *eth;
@@ -7940,8 +8599,6 @@
 static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_fail_func[256];
-	extern unsigned int wpa_trace_fail_after;
 	char *pos;
 
 	wpa_trace_fail_after = atoi(cmd);
@@ -7964,9 +8621,6 @@
 				    char *buf, size_t buflen)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_fail_func[256];
-	extern unsigned int wpa_trace_fail_after;
-
 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
 			   wpa_trace_fail_func);
 #else /* WPA_TRACE_BFD */
@@ -7978,8 +8632,6 @@
 static int wpas_ctrl_test_fail(struct wpa_supplicant *wpa_s, char *cmd)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_test_fail_func[256];
-	extern unsigned int wpa_trace_test_fail_after;
 	char *pos;
 
 	wpa_trace_test_fail_after = atoi(cmd);
@@ -8002,9 +8654,6 @@
 				    char *buf, size_t buflen)
 {
 #ifdef WPA_TRACE_BFD
-	extern char wpa_trace_test_fail_func[256];
-	extern unsigned int wpa_trace_test_fail_after;
-
 	return os_snprintf(buf, buflen, "%u:%s", wpa_trace_test_fail_after,
 			   wpa_trace_test_fail_func);
 #else /* WPA_TRACE_BFD */
@@ -8207,34 +8856,140 @@
 static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	size_t len;
+	const u8 *data;
 
-	if (neighbor_rep) {
-		wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
-			     "length=%u",
-			     (unsigned int) wpabuf_len(neighbor_rep));
-		wpabuf_free(neighbor_rep);
-	} else {
+	/*
+	 * Neighbor Report element (IEEE P802.11-REVmc/D5.0)
+	 * BSSID[6]
+	 * BSSID Information[4]
+	 * Operating Class[1]
+	 * Channel Number[1]
+	 * PHY Type[1]
+	 * Optional Subelements[variable]
+	 */
+#define NR_IE_MIN_LEN (ETH_ALEN + 4 + 1 + 1 + 1)
+
+	if (!neighbor_rep || wpabuf_len(neighbor_rep) == 0) {
 		wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED);
+		goto out;
 	}
+
+	data = wpabuf_head_u8(neighbor_rep);
+	len = wpabuf_len(neighbor_rep);
+
+	while (len >= 2 + NR_IE_MIN_LEN) {
+		const u8 *nr;
+		char lci[256 * 2 + 1];
+		char civic[256 * 2 + 1];
+		u8 nr_len = data[1];
+		const u8 *pos = data, *end;
+
+		if (pos[0] != WLAN_EID_NEIGHBOR_REPORT ||
+		    nr_len < NR_IE_MIN_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL: Invalid Neighbor Report element: id=%u len=%u",
+				   data[0], nr_len);
+			goto out;
+		}
+
+		if (2U + nr_len > len) {
+			wpa_printf(MSG_DEBUG,
+				   "CTRL: Invalid Neighbor Report element: id=%u len=%zu nr_len=%u",
+				   data[0], len, nr_len);
+			goto out;
+		}
+		pos += 2;
+		end = pos + nr_len;
+
+		nr = pos;
+		pos += NR_IE_MIN_LEN;
+
+		lci[0] = '\0';
+		civic[0] = '\0';
+		while (end - pos > 2) {
+			u8 s_id, s_len;
+
+			s_id = *pos++;
+			s_len = *pos++;
+			if (s_len > end - pos)
+				goto out;
+			if (s_id == WLAN_EID_MEASURE_REPORT && s_len > 3) {
+				/* Measurement Token[1] */
+				/* Measurement Report Mode[1] */
+				/* Measurement Type[1] */
+				/* Measurement Report[variable] */
+				switch (pos[2]) {
+				case MEASURE_TYPE_LCI:
+					if (lci[0])
+						break;
+					wpa_snprintf_hex(lci, sizeof(lci),
+							 pos, s_len);
+					break;
+				case MEASURE_TYPE_LOCATION_CIVIC:
+					if (civic[0])
+						break;
+					wpa_snprintf_hex(civic, sizeof(civic),
+							 pos, s_len);
+					break;
+				}
+			}
+
+			pos += s_len;
+		}
+
+		wpa_msg(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
+			"bssid=" MACSTR
+			" info=0x%x op_class=%u chan=%u phy_type=%u%s%s%s%s",
+			MAC2STR(nr), WPA_GET_LE32(nr + ETH_ALEN),
+			nr[ETH_ALEN + 4], nr[ETH_ALEN + 5],
+			nr[ETH_ALEN + 6],
+			lci[0] ? " lci=" : "", lci,
+			civic[0] ? " civic=" : "", civic);
+
+		data = end;
+		len -= 2 + nr_len;
+	}
+
+out:
+	wpabuf_free(neighbor_rep);
 }
 
 
-static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s,
-					    char *cmd)
+static int wpas_ctrl_iface_send_neighbor_rep(struct wpa_supplicant *wpa_s,
+					     char *cmd)
 {
-	struct wpa_ssid ssid;
-	struct wpa_ssid *ssid_p = NULL;
-	int ret = 0;
+	struct wpa_ssid_value ssid, *ssid_p = NULL;
+	int ret, lci = 0, civic = 0;
+	char *ssid_s;
 
-	if (os_strncmp(cmd, " ssid=", 6) == 0) {
-		ssid.ssid_len = os_strlen(cmd + 6);
-		if (ssid.ssid_len > SSID_MAX_LEN)
+	ssid_s = os_strstr(cmd, "ssid=");
+	if (ssid_s) {
+		if (ssid_parse(ssid_s + 5, &ssid)) {
+			wpa_printf(MSG_ERROR,
+				   "CTRL: Send Neighbor Report: bad SSID");
 			return -1;
-		ssid.ssid = (u8 *) (cmd + 6);
+		}
+
 		ssid_p = &ssid;
+
+		/*
+		 * Move cmd after the SSID text that may include "lci" or
+		 * "civic".
+		 */
+		cmd = os_strchr(ssid_s + 6, ssid_s[5] == '"' ? '"' : ' ');
+		if (cmd)
+			cmd++;
+
 	}
 
-	ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p,
+	if (cmd && os_strstr(cmd, "lci"))
+		lci = 1;
+
+	if (cmd && os_strstr(cmd, "civic"))
+		civic = 1;
+
+	ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p, lci, civic,
 						 wpas_ctrl_neighbor_rep_cb,
 						 wpa_s);
 
@@ -8319,10 +9074,7 @@
 			}
 		} else if (wpa_s->sched_scanning &&
 			   (type & MAC_ADDR_RAND_SCHED_SCAN)) {
-			/* simulate timeout to restart the sched scan */
-			wpa_s->sched_scan_timed_out = 1;
-			wpa_s->prev_sched_ssid = NULL;
-			wpa_supplicant_cancel_sched_scan(wpa_s);
+			wpas_scan_restart_sched_scan(wpa_s);
 		}
 		return 0;
 	}
@@ -8348,12 +9100,8 @@
 		wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
 					    addr, mask);
 
-		if (wpa_s->sched_scanning && !wpa_s->pno) {
-			/* simulate timeout to restart the sched scan */
-			wpa_s->sched_scan_timed_out = 1;
-			wpa_s->prev_sched_ssid = NULL;
-			wpa_supplicant_cancel_sched_scan(wpa_s);
-		}
+		if (wpa_s->sched_scanning && !wpa_s->pno)
+			wpas_scan_restart_sched_scan(wpa_s);
 	}
 
 	if (type & MAC_ADDR_RAND_PNO) {
@@ -8392,6 +9140,222 @@
 }
 
 
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+
+static int wpas_ctrl_iface_pmksa_get(struct wpa_supplicant *wpa_s,
+				     const char *cmd, char *buf, size_t buflen)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	struct wpa_ssid *ssid;
+	char *pos, *pos2, *end;
+	int ret;
+	struct os_reltime now;
+
+	ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
+	if (!ssid)
+		return -1;
+
+	pos = buf;
+	end = buf + buflen;
+
+	os_get_reltime(&now);
+
+	/*
+	 * Entry format:
+	 * <BSSID> <PMKID> <PMK> <reauth_time in seconds>
+	 * <expiration in seconds> <akmp> <opportunistic>
+	 */
+
+	for (entry = wpa_sm_pmksa_cache_head(wpa_s->wpa); entry;
+	     entry = entry->next) {
+		if (entry->network_ctx != ssid)
+			continue;
+
+		pos2 = pos;
+		ret = os_snprintf(pos2, end - pos2, MACSTR " ",
+				  MAC2STR(entry->aa));
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+
+		pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmkid,
+					 PMKID_LEN);
+
+		ret = os_snprintf(pos2, end - pos2, " ");
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+
+		pos2 += wpa_snprintf_hex(pos2, end - pos2, entry->pmk,
+					 entry->pmk_len);
+
+		ret = os_snprintf(pos2, end - pos2, " %d %d %d %d",
+				  (int) (entry->reauth_time - now.sec),
+				  (int) (entry->expiration - now.sec),
+				  entry->akmp,
+				  entry->opportunistic);
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+
+		ret = os_snprintf(pos2, end - pos2, "\n");
+		if (os_snprintf_error(end - pos2, ret))
+			break;
+		pos2 += ret;
+
+		pos = pos2;
+	}
+
+	return pos - buf;
+}
+
+
+static int wpas_ctrl_iface_pmksa_add(struct wpa_supplicant *wpa_s,
+				     char *cmd)
+{
+	struct rsn_pmksa_cache_entry *entry;
+	struct wpa_ssid *ssid;
+	char *pos, *pos2;
+	int ret = -1;
+	struct os_reltime now;
+	int reauth_time = 0, expiration = 0;
+
+	/*
+	 * Entry format:
+	 * <network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds>
+	 * <expiration in seconds> <akmp> <opportunistic>
+	 */
+
+	ssid = wpa_config_get_network(wpa_s->conf, atoi(cmd));
+	if (!ssid)
+		return -1;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		return -1;
+	pos++;
+
+	entry = os_zalloc(sizeof(*entry));
+	if (!entry)
+		return -1;
+
+	if (hwaddr_aton(pos, entry->aa))
+		goto fail;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		goto fail;
+	pos++;
+
+	if (hexstr2bin(pos, entry->pmkid, PMKID_LEN) < 0)
+		goto fail;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		goto fail;
+	pos++;
+
+	pos2 = os_strchr(pos, ' ');
+	if (!pos2)
+		goto fail;
+	entry->pmk_len = (pos2 - pos) / 2;
+	if (entry->pmk_len < PMK_LEN || entry->pmk_len > PMK_LEN_MAX ||
+	    hexstr2bin(pos, entry->pmk, entry->pmk_len) < 0)
+		goto fail;
+
+	pos = os_strchr(pos, ' ');
+	if (!pos)
+		goto fail;
+	pos++;
+
+	if (sscanf(pos, "%d %d %d %d", &reauth_time, &expiration,
+		   &entry->akmp, &entry->opportunistic) != 4)
+		goto fail;
+	os_get_reltime(&now);
+	entry->expiration = now.sec + expiration;
+	entry->reauth_time = now.sec + reauth_time;
+
+	entry->network_ctx = ssid;
+
+	wpa_sm_pmksa_cache_add_entry(wpa_s->wpa, entry);
+	entry = NULL;
+	ret = 0;
+fail:
+	os_free(entry);
+	return ret;
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpas_ctrl_iface_mesh_pmksa_get(struct wpa_supplicant *wpa_s,
+					  const char *cmd, char *buf,
+					  size_t buflen)
+{
+	u8 spa[ETH_ALEN];
+
+	if (!wpa_s->ifmsh)
+		return -1;
+
+	if (os_strcasecmp(cmd, "any") == 0)
+		return wpas_ap_pmksa_cache_list_mesh(wpa_s, NULL, buf, buflen);
+
+	if (hwaddr_aton(cmd, spa))
+		return -1;
+
+	return wpas_ap_pmksa_cache_list_mesh(wpa_s, spa, buf, buflen);
+}
+
+
+static int wpas_ctrl_iface_mesh_pmksa_add(struct wpa_supplicant *wpa_s,
+					  char *cmd)
+{
+	/*
+	 * We do not check mesh interface existance because PMKSA should be
+	 * stored before wpa_s->ifmsh creation to suppress commit message
+	 * creation.
+	 */
+	return wpas_ap_pmksa_cache_add_external(wpa_s, cmd);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
+#ifdef CONFIG_FILS
+static int wpas_ctrl_iface_fils_hlp_req_add(struct wpa_supplicant *wpa_s,
+					    const char *cmd)
+{
+	struct fils_hlp_req *req;
+	const char *pos;
+
+	/* format: <dst> <packet starting from ethertype> */
+
+	req = os_zalloc(sizeof(*req));
+	if (!req)
+		return -1;
+
+	if (hwaddr_aton(cmd, req->dst))
+		goto fail;
+
+	pos = os_strchr(cmd, ' ');
+	if (!pos)
+		goto fail;
+	pos++;
+	req->pkt = wpabuf_parse_bin(pos);
+	if (!req->pkt)
+		goto fail;
+
+	dl_list_add_tail(&wpa_s->fils_hlp_req, &req->list);
+	return 0;
+fail:
+	wpabuf_free(req->pkt);
+	os_free(req);
+	return -1;
+}
+#endif /* CONFIG_FILS */
+
+
 static int wpas_ctrl_cmd_debug_level(const char *cmd)
 {
 	if (os_strcmp(cmd, "PING") == 0 ||
@@ -8466,6 +9430,22 @@
 		reply_len = wpas_ctrl_iface_pmksa(wpa_s, reply, reply_size);
 	} else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
 		wpas_ctrl_iface_pmksa_flush(wpa_s);
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+	} else if (os_strncmp(buf, "PMKSA_GET ", 10) == 0) {
+		reply_len = wpas_ctrl_iface_pmksa_get(wpa_s, buf + 10,
+						      reply, reply_size);
+	} else if (os_strncmp(buf, "PMKSA_ADD ", 10) == 0) {
+		if (wpas_ctrl_iface_pmksa_add(wpa_s, buf + 10) < 0)
+			reply_len = -1;
+#ifdef CONFIG_MESH
+	} else if (os_strncmp(buf, "MESH_PMKSA_GET ", 15) == 0) {
+		reply_len = wpas_ctrl_iface_mesh_pmksa_get(wpa_s, buf + 15,
+							   reply, reply_size);
+	} else if (os_strncmp(buf, "MESH_PMKSA_ADD ", 15) == 0) {
+		if (wpas_ctrl_iface_mesh_pmksa_add(wpa_s, buf + 15) < 0)
+			reply_len = -1;
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
 	} else if (os_strncmp(buf, "SET ", 4) == 0) {
 		if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
 			reply_len = -1;
@@ -8673,6 +9653,9 @@
 	} else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
 		if (p2p_ctrl_group_add(wpa_s, buf + 14))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_GROUP_MEMBER ", 17) == 0) {
+		reply_len = p2p_ctrl_group_member(wpa_s, buf + 17, reply,
+						  reply_size);
 	} else if (os_strncmp(buf, "P2P_PROV_DISC ", 14) == 0) {
 		if (p2p_ctrl_prov_disc(wpa_s, buf + 14))
 			reply_len = -1;
@@ -8738,6 +9721,12 @@
 	} else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
 		if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
 			reply_len = -1;
+	} else if (os_strncmp(buf, "P2P_LO_START ", 13) == 0) {
+		if (p2p_ctrl_iface_p2p_lo_start(wpa_s, buf + 13))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "P2P_LO_STOP") == 0) {
+		if (wpas_p2p_lo_stop(wpa_s))
+			reply_len = -1;
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_WIFI_DISPLAY
 	} else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
@@ -8802,7 +9791,10 @@
 		if (del_hs20_icon(wpa_s, buf + 14) < 0)
 			reply_len = -1;
 	} else if (os_strcmp(buf, "FETCH_OSU") == 0) {
-		if (hs20_fetch_osu(wpa_s) < 0)
+		if (hs20_fetch_osu(wpa_s, 0) < 0)
+			reply_len = -1;
+	} else if (os_strcmp(buf, "FETCH_OSU no-scan") == 0) {
+		if (hs20_fetch_osu(wpa_s, 1) < 0)
 			reply_len = -1;
 	} else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
 		hs20_cancel_fetch_osu(wpa_s);
@@ -8841,16 +9833,7 @@
 		reply_len = wpa_supplicant_ctrl_iface_list_networks(
 			wpa_s, NULL, reply, reply_size);
 	} else if (os_strcmp(buf, "DISCONNECT") == 0) {
-#ifdef CONFIG_SME
-		wpa_s->sme.prev_bssid_set = 0;
-#endif /* CONFIG_SME */
-		wpa_s->reassociate = 0;
-		wpa_s->disconnected = 1;
-		wpa_supplicant_cancel_sched_scan(wpa_s);
-		wpa_supplicant_cancel_scan(wpa_s);
-		wpa_supplicant_deauthenticate(wpa_s,
-					      WLAN_REASON_DEAUTH_LEAVING);
-		eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+		wpas_request_disconnection(wpa_s);
 	} else if (os_strcmp(buf, "SCAN") == 0) {
 		wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
 	} else if (os_strncmp(buf, "SCAN ", 5) == 0) {
@@ -9013,6 +9996,9 @@
 		if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
 			reply_len = -1;
 #endif /* CONFIG_AUTOSCAN */
+	} else if (os_strcmp(buf, "DRIVER_FLAGS") == 0) {
+		reply_len = wpas_ctrl_iface_driver_flags(wpa_s, reply,
+							 reply_size);
 #ifdef ANDROID
 	} else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
 		reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
@@ -9043,6 +10029,9 @@
 			reply_len = -1;
 	} else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) {
 		wpas_ctrl_iface_mgmt_tx_done(wpa_s);
+	} else if (os_strncmp(buf, "MGMT_RX_PROCESS ", 16) == 0) {
+		if (wpas_ctrl_iface_mgmt_rx_process(wpa_s, buf + 16) < 0)
+			reply_len = -1;
 	} else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
 		if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
 			reply_len = -1;
@@ -9085,7 +10074,7 @@
 		if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
 			reply_len = -1;
 	} else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
-		if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20))
+		if (wpas_ctrl_iface_send_neighbor_rep(wpa_s, buf + 20))
 			reply_len = -1;
 	} else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
 		wpas_ctrl_iface_erp_flush(wpa_s);
@@ -9095,6 +10084,13 @@
 	} else if (os_strncmp(buf, "GET_PREF_FREQ_LIST ", 19) == 0) {
 		reply_len = wpas_ctrl_iface_get_pref_freq_list(
 			wpa_s, buf + 19, reply, reply_size);
+#ifdef CONFIG_FILS
+	} else if (os_strncmp(buf, "FILS_HLP_REQ_ADD ", 17) == 0) {
+		if (wpas_ctrl_iface_fils_hlp_req_add(wpa_s, buf + 17))
+			reply_len = -1;
+	} else if (os_strcmp(buf, "FILS_HLP_REQ_FLUSH") == 0) {
+		wpas_flush_fils_hlp_req(wpa_s);
+#endif /* CONFIG_FILS */
 	} else {
 		os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
 		reply_len = 16;
@@ -9404,6 +10400,9 @@
 		"P2P_CANCEL",
 		"P2P_PRESENCE_REQ",
 		"P2P_EXT_LISTEN",
+#ifdef CONFIG_AP
+		"STA-FIRST",
+#endif /* CONFIG_AP */
 		NULL
 	};
 	static const char * prefix[] = {
@@ -9417,6 +10416,7 @@
 		"P2P_LISTEN ",
 		"P2P_GROUP_REMOVE ",
 		"P2P_GROUP_ADD ",
+		"P2P_GROUP_MEMBER ",
 		"P2P_PROV_DISC ",
 		"P2P_SERV_DISC_REQ ",
 		"P2P_SERV_DISC_CANCEL_REQ ",
@@ -9440,6 +10440,10 @@
 		"NFC_REPORT_HANDOVER ",
 		"P2P_ASP_PROVISION ",
 		"P2P_ASP_PROVISION_RESP ",
+#ifdef CONFIG_AP
+		"STA ",
+		"STA-NEXT ",
+#endif /* CONFIG_AP */
 		NULL
 	};
 	int found = 0;
@@ -9788,7 +10792,6 @@
 							  reply_size);
 #ifdef CONFIG_MODULE_TESTS
 	} else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
-		int wpas_module_tests(void);
 		if (wpas_module_tests() < 0)
 			reply_len = -1;
 #endif /* CONFIG_MODULE_TESTS */
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
index d894f6a..0c355f7 100644
--- a/wpa_supplicant/dbus/dbus_new.c
+++ b/wpa_supplicant/dbus/dbus_new.c
@@ -1254,14 +1254,14 @@
  * irrespective of the role (client/GO) of the current device
  *
  * @wpa_s: %wpa_supplicant network interface data
- * @ssid: SSID object
  * @client: this device is P2P client
- * @network_id: network id of the group started, use instead of ssid->id
- *	to account for persistent groups
+ * @persistent: 0 - non persistent group, 1 - persistent group
+ * @ip: When group role is client, it contains local IP address, netmask, and
+ *	GO's IP address, if assigned; otherwise, NULL
  */
 void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
-					const struct wpa_ssid *ssid,
-					int client, int network_id)
+					int client, int persistent,
+					const u8 *ip)
 {
 	DBusMessage *msg;
 	DBusMessageIter iter, dict_iter;
@@ -1300,8 +1300,16 @@
 					      wpa_s->dbus_new_path) ||
 	    !wpa_dbus_dict_append_string(&dict_iter, "role",
 					 client ? "client" : "GO") ||
+	    !wpa_dbus_dict_append_bool(&dict_iter, "persistent", persistent) ||
 	    !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
 					      wpa_s->dbus_groupobj_path) ||
+	    (ip &&
+	     (!wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddr",
+					       (char *) ip, 4) ||
+	      !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask",
+					       (char *) ip + 4, 4) ||
+	      !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGo",
+					       (char *) ip + 8, 4))) ||
 	    !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
 		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 	} else {
@@ -1979,6 +1987,11 @@
 	case WPAS_DBUS_PROP_AP_SCAN:
 		prop = "ApScan";
 		break;
+#ifdef CONFIG_IEEE80211W
+	case WPAS_DBUS_PROP_PMF:
+		prop = "Pmf";
+		break;
+#endif /* CONFIG_IEEE80211W */
 	case WPAS_DBUS_PROP_SCANNING:
 		prop = "Scanning";
 		break;
@@ -3130,6 +3143,13 @@
 	  wpas_dbus_setter_ap_scan,
 	  NULL
 	},
+#ifdef CONFIG_IEEE80211W
+	{ "Pmf", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+	  wpas_dbus_getter_pmf,
+	  wpas_dbus_setter_pmf,
+	  NULL
+	},
+#endif /* CONFIG_IEEE80211W */
 	{ "BSSExpireAge", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
 	  wpas_dbus_getter_bss_expire_age,
 	  wpas_dbus_setter_bss_expire_age,
@@ -3160,6 +3180,11 @@
 	  NULL,
 	  NULL
 	},
+	{ "ConfigFile", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+	  wpas_dbus_getter_config_file,
+	  NULL,
+	  NULL
+	},
 	{ "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
 	  wpas_dbus_getter_current_bss,
 	  NULL,
@@ -3221,6 +3246,42 @@
 	  wpas_dbus_setter_config_methods,
 	  NULL
 	},
+	{
+	  "DeviceName", WPAS_DBUS_NEW_IFACE_WPS, "s",
+	  wpas_dbus_getter_wps_device_name,
+	  wpas_dbus_setter_wps_device_name,
+	  NULL
+	},
+	{
+	  "Manufacturer", WPAS_DBUS_NEW_IFACE_WPS, "s",
+	  wpas_dbus_getter_wps_manufacturer,
+	  wpas_dbus_setter_wps_manufacturer,
+	  NULL
+	},
+	{
+	  "ModelName", WPAS_DBUS_NEW_IFACE_WPS, "s",
+	  wpas_dbus_getter_wps_device_model_name,
+	  wpas_dbus_setter_wps_device_model_name,
+	  NULL
+	},
+	{
+	  "ModelNumber", WPAS_DBUS_NEW_IFACE_WPS, "s",
+	  wpas_dbus_getter_wps_device_model_number,
+	  wpas_dbus_setter_wps_device_model_number,
+	  NULL
+	},
+	{
+	  "SerialNumber", WPAS_DBUS_NEW_IFACE_WPS, "s",
+	  wpas_dbus_getter_wps_device_serial_number,
+	  wpas_dbus_setter_wps_device_serial_number,
+	  NULL
+	},
+	{
+	  "DeviceType", WPAS_DBUS_NEW_IFACE_WPS, "ay",
+	  wpas_dbus_getter_wps_device_device_type,
+	  wpas_dbus_setter_wps_device_device_type,
+	  NULL
+	},
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
 	{ "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
@@ -3354,6 +3415,13 @@
 		  END_ARGS
 	  }
 	},
+	{ "DeviceFoundProperties", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+	  {
+		  { "path", "o", ARG_OUT },
+		  { "properties", "a{sv}", ARG_OUT },
+		  END_ARGS
+	  }
+	},
 	{ "DeviceLost", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
 	  {
 		  { "path", "o", ARG_OUT },
@@ -3802,12 +3870,13 @@
  *	In case of peer objects, it would be emitted by either
  *	the "interface object" or by "peer objects"
  * @sig_name: signal name - DeviceFound
+ * @properties: Whether to add a second argument with object properties
  *
- * Notify listeners about event related with newly found p2p peer device
+ * Notify listeners about event related with p2p peer device
  */
 static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s,
 				  const u8 *dev_addr, const char *interface,
-				  const char *sig_name)
+				  const char *sig_name, int properties)
 {
 	struct wpas_dbus_priv *iface;
 	DBusMessage *msg;
@@ -3835,7 +3904,10 @@
 	dbus_message_iter_init_append(msg, &iter);
 	path = peer_obj_path;
 	if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-					    &path))
+					    &path) ||
+	    (properties && !wpa_dbus_get_object_properties(
+		    iface, peer_obj_path, WPAS_DBUS_NEW_IFACE_P2P_PEER,
+		    &iter)))
 		wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
 	else
 		dbus_connection_send(iface->con, msg, NULL);
@@ -3856,7 +3928,11 @@
 {
 	wpas_dbus_signal_peer(wpa_s, dev_addr,
 			      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-			      "DeviceFound");
+			      "DeviceFound", FALSE);
+
+	wpas_dbus_signal_peer(wpa_s, dev_addr,
+			      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+			      "DeviceFoundProperties", TRUE);
 }
 
 /**
@@ -3871,7 +3947,7 @@
 {
 	wpas_dbus_signal_peer(wpa_s, dev_addr,
 			      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-			      "DeviceLost");
+			      "DeviceLost", FALSE);
 }
 
 /**
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
index 3ac66db..bd0e074 100644
--- a/wpa_supplicant/dbus/dbus_new.h
+++ b/wpa_supplicant/dbus/dbus_new.h
@@ -22,6 +22,7 @@
 
 enum wpas_dbus_prop {
 	WPAS_DBUS_PROP_AP_SCAN,
+	WPAS_DBUS_PROP_PMF,
 	WPAS_DBUS_PROP_SCANNING,
 	WPAS_DBUS_PROP_STATE,
 	WPAS_DBUS_PROP_CURRENT_BSS,
@@ -190,8 +191,8 @@
 				     const u8 *src, u16 dev_passwd_id,
 				     u8 go_intent);
 void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
-					const struct wpa_ssid *ssid,
-					int client, int network_id);
+					int client, int persistent,
+					const u8 *ip);
 void wpas_dbus_signal_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
 						  const char *reason);
 void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
@@ -401,8 +402,8 @@
 
 static inline void
 wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
-				   const struct wpa_ssid *ssid,
-				   int client, int network_id)
+				   int client, int persistent,
+				   const u8 *ip)
 {
 }
 
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
index da90ea1..e6f356b 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers.c
@@ -517,6 +517,27 @@
 
 
 /**
+ * wpas_dbus_string_property_getter - Get string type property
+ * @iter: Message iter to use when appending arguments
+ * @val: Pointer to place holding property value, can be %NULL
+ * @error: On failure an error describing the failure
+ * Returns: TRUE if the request was successful, FALSE if it failed
+ *
+ * Generic getter for string type properties. %NULL is converted to an empty
+ * string.
+ */
+dbus_bool_t wpas_dbus_string_property_getter(DBusMessageIter *iter,
+					     const void *val,
+					     DBusError *error)
+{
+	if (!val)
+		val = "";
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+						&val, error);
+}
+
+
+/**
  * wpas_dbus_handler_create_interface - Request registration of a network iface
  * @message: Pointer to incoming dbus message
  * @global: %wpa_supplicant global data structure
@@ -970,6 +991,9 @@
 #ifdef CONFIG_INTERWORKING
 	capabilities[num_items++] = "interworking";
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_IEEE80211W
+	capabilities[num_items++] = "pmf";
+#endif /* CONFIG_IEEE80211W */
 
 	return wpas_dbus_simple_array_property_getter(iter,
 						      DBUS_TYPE_STRING,
@@ -1475,10 +1499,7 @@
 					   struct wpa_supplicant *wpa_s)
 {
 	if (wpa_s->current_ssid != NULL) {
-		wpa_s->disconnected = 1;
-		wpa_supplicant_deauthenticate(wpa_s,
-					      WLAN_REASON_DEAUTH_LEAVING);
-
+		wpas_request_disconnection(wpa_s);
 		return NULL;
 	}
 
@@ -1507,7 +1528,7 @@
 	dbus_message_iter_init(message, &iter);
 
 	if (wpa_s->dbus_new_path)
-		ssid = wpa_config_add_network(wpa_s->conf);
+		ssid = wpa_supplicant_add_network(wpa_s);
 	if (ssid == NULL) {
 		wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.",
 			   __func__);
@@ -1516,9 +1537,6 @@
 			"wpa_supplicant could not add a network on this interface.");
 		goto err;
 	}
-	wpas_notify_network_added(wpa_s, ssid);
-	ssid->disabled = 1;
-	wpa_config_set_network_defaults(ssid);
 
 	dbus_error_init(&error);
 	if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
@@ -1665,8 +1683,7 @@
 	const char *op;
 	char *iface, *net_id;
 	int id;
-	struct wpa_ssid *ssid;
-	int was_disabled;
+	int result;
 
 	dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
 			      DBUS_TYPE_INVALID);
@@ -1689,27 +1706,12 @@
 		goto out;
 	}
 
-	ssid = wpa_config_get_network(wpa_s->conf, id);
-	if (ssid == NULL) {
+	result = wpa_supplicant_remove_network(wpa_s, id);
+	if (result == -1) {
 		reply = wpas_dbus_error_network_unknown(message);
 		goto out;
 	}
-
-	was_disabled = ssid->disabled;
-
-	wpas_notify_network_removed(wpa_s, ssid);
-
-	if (ssid == wpa_s->current_ssid)
-		wpa_supplicant_deauthenticate(wpa_s,
-					      WLAN_REASON_DEAUTH_LEAVING);
-	else if (!was_disabled && wpa_s->sched_scanning) {
-		wpa_printf(MSG_DEBUG,
-			   "Stop ongoing sched_scan to remove network from filters");
-		wpa_supplicant_cancel_sched_scan(wpa_s);
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
-	}
-
-	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+	if (result == -2) {
 		wpa_printf(MSG_ERROR,
 			   "%s[dbus]: error occurred when removing network %d",
 			   __func__, id);
@@ -2490,6 +2492,28 @@
 			goto nomem;
 	}
 
+	if (!wpa_dbus_dict_begin_string_array(&iter_dict, "GroupMgmt",
+					      &iter_dict_entry,
+					      &iter_dict_val,
+					      &iter_array) ||
+	    (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "aes-128-cmac")) ||
+	    (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_GMAC_128) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "bip-gmac-128")) ||
+	    (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_GMAC_256) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "bip-gmac-256")) ||
+	    (res == 0 && (capa.enc & WPA_DRIVER_CAPA_ENC_BIP_CMAC_256) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "bip-cmac-256")) ||
+	    !wpa_dbus_dict_end_string_array(&iter_dict,
+					    &iter_dict_entry,
+					    &iter_dict_val,
+					    &iter_array))
+		goto nomem;
+
 	/***** key management */
 	if (res < 0) {
 		const char *args[] = {
@@ -2639,8 +2663,9 @@
 					      &iter_array) ||
 	    !wpa_dbus_dict_string_array_add_element(
 		    &iter_array, "infrastructure") ||
-	    !wpa_dbus_dict_string_array_add_element(
-		    &iter_array, "ad-hoc") ||
+	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_IBSS) &&
+	     !wpa_dbus_dict_string_array_add_element(
+		     &iter_array, "ad-hoc")) ||
 	    (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) &&
 	     !wpa_dbus_dict_string_array_add_element(
 		     &iter_array, "ap")) ||
@@ -2787,6 +2812,61 @@
 }
 
 
+#ifdef CONFIG_IEEE80211W
+
+/**
+ * wpas_dbus_getter_pmf - Control PMF default
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter function for "Pmf" property.
+ */
+dbus_bool_t wpas_dbus_getter_pmf(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t pmf = wpa_s->conf->pmf;
+
+	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32,
+						&pmf, error);
+}
+
+
+/**
+ * wpas_dbus_setter_pmf - Control PMF default
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter function for "Pmf" property.
+ */
+dbus_bool_t wpas_dbus_setter_pmf(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	dbus_uint32_t pmf;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_UINT32,
+					      &pmf))
+		return FALSE;
+
+	if (pmf > 2) {
+		dbus_set_error_const(error, DBUS_ERROR_FAILED,
+				     "Pmf must be 0, 1, or 2");
+		return FALSE;
+	}
+	wpa_s->conf->pmf = pmf;
+	return TRUE;
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
 /**
  * wpas_dbus_getter_fast_reauth - Control fast
  * reauthentication (TLS session resumption)
@@ -3107,10 +3187,8 @@
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	const char *ifname = wpa_s->ifname;
 
-	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
-						&ifname, error);
+	return wpas_dbus_string_property_getter(iter, wpa_s->ifname, error);
 }
 
 
@@ -3128,7 +3206,6 @@
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	const char *driver;
 
 	if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
 		wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set",
@@ -3138,9 +3215,8 @@
 		return FALSE;
 	}
 
-	driver = wpa_s->driver->name;
-	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
-						&driver, error);
+	return wpas_dbus_string_property_getter(iter, wpa_s->driver->name,
+						error);
 }
 
 
@@ -3227,9 +3303,11 @@
 			    "EAP-%s", eap_mode);
 		auth_mode = eap_mode_buf;
 
-	} else {
+	} else if (wpa_s->current_ssid) {
 		auth_mode = wpa_key_mgmt_txt(wpa_s->key_mgmt,
 					     wpa_s->current_ssid->proto);
+	} else {
+		auth_mode = "UNKNOWN";
 	}
 
 	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
@@ -3251,10 +3329,28 @@
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	const char *bridge_ifname = wpa_s->bridge_ifname;
 
-	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
-						&bridge_ifname, error);
+	return wpas_dbus_string_property_getter(iter, wpa_s->bridge_ifname,
+						error);
+}
+
+
+/**
+ * wpas_dbus_getter_config_file - Get interface configuration file path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ConfigFile" property.
+ */
+dbus_bool_t wpas_dbus_getter_config_file(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	return wpas_dbus_string_property_getter(iter, wpa_s->confname, error);
 }
 
 
@@ -3394,14 +3490,10 @@
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	const char *pkcs11_engine_path;
 
-	if (wpa_s->conf->pkcs11_engine_path == NULL)
-		pkcs11_engine_path = "";
-	else
-		pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
-	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
-						&pkcs11_engine_path, error);
+	return wpas_dbus_string_property_getter(iter,
+						wpa_s->conf->pkcs11_engine_path,
+						error);
 }
 
 
@@ -3419,14 +3511,10 @@
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	const char *pkcs11_module_path;
 
-	if (wpa_s->conf->pkcs11_module_path == NULL)
-		pkcs11_module_path = "";
-	else
-		pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
-	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
-						&pkcs11_module_path, error);
+	return wpas_dbus_string_property_getter(iter,
+						wpa_s->conf->pkcs11_module_path,
+						error);
 }
 
 
@@ -3678,6 +3766,7 @@
 	struct bss_handler_args *args = user_data;
 	struct wpa_bss *res;
 	const char *mode;
+	const u8 *mesh;
 
 	res = get_bss_helper(args, error, __func__);
 	if (!res)
@@ -3691,9 +3780,15 @@
 		case IEEE80211_CAP_DMG_AP:
 			mode = "infrastructure";
 			break;
+		default:
+			mode = "";
+			break;
 		}
 	} else {
-		if (res->caps & IEEE80211_CAP_IBSS)
+		mesh = wpa_bss_get_ie(res, WLAN_EID_MESH_ID);
+		if (mesh)
+			mode = "mesh";
+		else if (res->caps & IEEE80211_CAP_IBSS)
 			mode = "ad-hoc";
 		else
 			mode = "infrastructure";
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
index cd299c0..3b8f096 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers.h
+++ b/wpa_supplicant/dbus/dbus_new_handlers.h
@@ -43,6 +43,10 @@
 							 size_t array_len,
 							 DBusError *error);
 
+dbus_bool_t wpas_dbus_string_property_getter(DBusMessageIter *iter,
+					     const void *val,
+					     DBusError *error);
+
 DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
 						 struct wpa_global *global);
 
@@ -134,6 +138,8 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_scanning);
 DECLARE_ACCESSOR(wpas_dbus_getter_ap_scan);
 DECLARE_ACCESSOR(wpas_dbus_setter_ap_scan);
+DECLARE_ACCESSOR(wpas_dbus_getter_pmf);
+DECLARE_ACCESSOR(wpas_dbus_setter_pmf);
 DECLARE_ACCESSOR(wpas_dbus_getter_fast_reauth);
 DECLARE_ACCESSOR(wpas_dbus_setter_fast_reauth);
 DECLARE_ACCESSOR(wpas_dbus_getter_disconnect_reason);
@@ -150,6 +156,7 @@
 DECLARE_ACCESSOR(wpas_dbus_getter_ifname);
 DECLARE_ACCESSOR(wpas_dbus_getter_driver);
 DECLARE_ACCESSOR(wpas_dbus_getter_bridge_ifname);
+DECLARE_ACCESSOR(wpas_dbus_getter_config_file);
 DECLARE_ACCESSOR(wpas_dbus_getter_current_bss);
 DECLARE_ACCESSOR(wpas_dbus_getter_current_network);
 DECLARE_ACCESSOR(wpas_dbus_getter_current_auth_mode);
@@ -185,6 +192,18 @@
 DECLARE_ACCESSOR(wpas_dbus_setter_process_credentials);
 DECLARE_ACCESSOR(wpas_dbus_getter_config_methods);
 DECLARE_ACCESSOR(wpas_dbus_setter_config_methods);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_name);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_name);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_manufacturer);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_manufacturer);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_model_name);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_model_name);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_model_number);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_model_number);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_serial_number);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_serial_number);
+DECLARE_ACCESSOR(wpas_dbus_getter_wps_device_device_type);
+DECLARE_ACCESSOR(wpas_dbus_setter_wps_device_device_type);
 
 DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
 					      struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
index 73b9e20..a04783d 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_p2p.c
@@ -28,6 +28,18 @@
 #include "../p2p_supplicant.h"
 #include "../wifi_display.h"
 
+
+static int wpas_dbus_validate_dbus_ipaddr(struct wpa_dbus_dict_entry entry)
+{
+	if (entry.type != DBUS_TYPE_ARRAY ||
+	    entry.array_type != DBUS_TYPE_BYTE ||
+	    entry.array_len != 4)
+		return 0;
+
+	return 1;
+}
+
+
 /**
  * Parses out the mac address from the peer object path.
  * @peer_path - object path of the form
@@ -78,6 +90,7 @@
 	int num_req_dev_types = 0;
 	unsigned int i;
 	u8 *req_dev_types = NULL;
+	unsigned int freq = 0;
 
 	dbus_message_iter_init(message, &iter);
 	entry.key = NULL;
@@ -122,6 +135,10 @@
 				type = P2P_FIND_PROGRESSIVE;
 			else
 				goto error_clear;
+		} else if (os_strcmp(entry.key, "freq") == 0 &&
+			   (entry.type == DBUS_TYPE_INT32 ||
+			    entry.type == DBUS_TYPE_UINT32)) {
+			freq = entry.uint32_value;
 		} else
 			goto error_clear;
 		wpa_dbus_dict_entry_clear(&entry);
@@ -130,7 +147,7 @@
 	wpa_s = wpa_s->global->p2p_init_wpa_s;
 
 	wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
-		      NULL, 0, 0, NULL, 0);
+		      NULL, 0, 0, NULL, freq);
 	os_free(req_dev_types);
 	return reply;
 
@@ -867,6 +884,35 @@
 			goto err_no_mem;
 	}
 
+	/* GO IP address */
+	if (WPA_GET_BE32(wpa_s->conf->ip_addr_go) &&
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGo",
+					     (char *) wpa_s->conf->ip_addr_go,
+					     4))
+		goto err_no_mem;
+
+	/* IP address mask */
+	if (WPA_GET_BE32(wpa_s->conf->ip_addr_mask) &&
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask",
+					     (char *) wpa_s->conf->ip_addr_mask,
+					     4))
+		goto err_no_mem;
+
+	/* IP address start */
+	if (WPA_GET_BE32(wpa_s->conf->ip_addr_start) &&
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrStart",
+					     (char *)
+					     wpa_s->conf->ip_addr_start,
+					     4))
+		goto err_no_mem;
+
+	/* IP address end */
+	if (WPA_GET_BE32(wpa_s->conf->ip_addr_end) &&
+	    !wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrEnd",
+					     (char *) wpa_s->conf->ip_addr_end,
+					     4))
+		goto err_no_mem;
+
 	/* Vendor Extensions */
 	for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
 		if (wpa_s->conf->wps_vendor_ext[i] == NULL)
@@ -1051,6 +1097,26 @@
 			wpa_s->conf->p2p_intra_bss = entry.bool_value;
 			wpa_s->conf->changed_parameters |=
 				CFG_CHANGED_P2P_INTRA_BSS;
+		} else if (os_strcmp(entry.key, "IpAddrGo") == 0) {
+			if (!wpas_dbus_validate_dbus_ipaddr(entry))
+				goto error;
+			os_memcpy(wpa_s->conf->ip_addr_go,
+				  entry.bytearray_value, 4);
+		} else if (os_strcmp(entry.key, "IpAddrMask") == 0) {
+			if (!wpas_dbus_validate_dbus_ipaddr(entry))
+				goto error;
+			os_memcpy(wpa_s->conf->ip_addr_mask,
+				  entry.bytearray_value, 4);
+		} else if (os_strcmp(entry.key, "IpAddrStart") == 0) {
+			if (!wpas_dbus_validate_dbus_ipaddr(entry))
+				goto error;
+			os_memcpy(wpa_s->conf->ip_addr_start,
+				  entry.bytearray_value, 4);
+		} else if (os_strcmp(entry.key, "IpAddrEnd") == 0) {
+			if (!wpas_dbus_validate_dbus_ipaddr(entry))
+				goto error;
+			os_memcpy(wpa_s->conf->ip_addr_end,
+				  entry.bytearray_value, 4);
 		} else if (os_strcmp(entry.key, "GroupIdle") == 0 &&
 			   entry.type == DBUS_TYPE_UINT32)
 			wpa_s->conf->p2p_group_idle = entry.uint32_value;
@@ -2286,19 +2352,12 @@
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	char *p_pass;
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 
 	if (ssid == NULL)
 		return FALSE;
 
-	p_pass = ssid->passphrase;
-	if (!p_pass)
-		p_pass = "";
-
-	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
-						&p_pass, error);
-
+	return wpas_dbus_string_property_getter(iter, ssid->passphrase, error);
 }
 
 
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
index 1d5dd1c..f762b3f 100644
--- a/wpa_supplicant/dbus/dbus_new_handlers_wps.c
+++ b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
@@ -325,7 +325,7 @@
  * @wpa_s: %wpa_supplicant data structure
  * Returns: NULL on success or DBus error on failure
  *
- * Handler for "Cancel" method call. Returns NULL if WPS cancel successfull
+ * Handler for "Cancel" method call. Returns NULL if WPS cancel successful
  * or DBus error on WPS cancel failure
  */
 DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
@@ -412,12 +412,10 @@
 	DBusMessageIter *iter, DBusError *error, void *user_data)
 {
 	struct wpa_supplicant *wpa_s = user_data;
-	char *methods = wpa_s->conf->config_methods;
 
-	if (methods == NULL)
-		methods = "";
-	return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
-						&methods, error);
+	return wpas_dbus_string_property_getter(iter,
+						wpa_s->conf->config_methods,
+						error);
 }
 
 
@@ -454,3 +452,349 @@
 
 	return TRUE;
 }
+
+
+/**
+ * wpas_dbus_getter_wps_device_name - Get current WPS device name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DeviceName" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_name(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	return wpas_dbus_string_property_getter(iter, wpa_s->conf->device_name,
+						error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_name - Set current WPS device name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "DeviceName" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_name(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *methods, *devname;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &methods))
+		return FALSE;
+
+	if (os_strlen(methods) > WPS_DEV_NAME_MAX_LEN)
+		return FALSE;
+
+	devname = os_strdup(methods);
+	if (!devname)
+		return FALSE;
+
+	os_free(wpa_s->conf->device_name);
+	wpa_s->conf->device_name = devname;
+	wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_NAME;
+	wpa_supplicant_update_config(wpa_s);
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_manufacturer - Get current manufacturer name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Manufacturer" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_manufacturer(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	return wpas_dbus_string_property_getter(iter, wpa_s->conf->manufacturer,
+						error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_manufacturer - Set current manufacturer name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Manufacturer" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_manufacturer(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *methods, *manufacturer;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &methods))
+		return FALSE;
+
+	if (os_strlen(methods) > WPS_MANUFACTURER_MAX_LEN)
+		return FALSE;
+
+	manufacturer = os_strdup(methods);
+	if (!manufacturer)
+		return FALSE;
+
+	os_free(wpa_s->conf->manufacturer);
+	wpa_s->conf->manufacturer = manufacturer;
+	wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+	wpa_supplicant_update_config(wpa_s);
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_model_name - Get current device model name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ModelName" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_model_name(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	return wpas_dbus_string_property_getter(iter, wpa_s->conf->model_name,
+						error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_model_name - Set current device model name
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "ModelName" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_model_name(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *methods, *model_name;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &methods))
+		return FALSE;
+
+	if (os_strlen(methods) > WPS_MODEL_NAME_MAX_LEN)
+		return FALSE;
+
+	model_name = os_strdup(methods);
+	if (!model_name)
+		return FALSE;
+	os_free(wpa_s->conf->model_name);
+	wpa_s->conf->model_name = model_name;
+	wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+	wpa_supplicant_update_config(wpa_s);
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_model_number - Get current device model number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ModelNumber" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_model_number(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	return wpas_dbus_string_property_getter(iter, wpa_s->conf->model_number,
+						error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_model_number - Set current device model number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "ModelNumber" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_model_number(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *methods, *model_number;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &methods))
+		return FALSE;
+
+	if (os_strlen(methods) > WPS_MODEL_NUMBER_MAX_LEN)
+		return FALSE;
+
+	model_number = os_strdup(methods);
+	if (!model_number)
+		return FALSE;
+
+	os_free(wpa_s->conf->model_number);
+	wpa_s->conf->model_number = model_number;
+	wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+	wpa_supplicant_update_config(wpa_s);
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_serial_number - Get current device serial number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "SerialNumber" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_serial_number(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	return wpas_dbus_string_property_getter(iter,
+						wpa_s->conf->serial_number,
+						error);
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_serial_number - Set current device serial number
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "SerialNumber" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_serial_number(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	char *methods, *serial_number;
+
+	if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+					      &methods))
+		return FALSE;
+
+	if (os_strlen(methods) > WPS_SERIAL_NUMBER_MAX_LEN)
+		return FALSE;
+
+	serial_number = os_strdup(methods);
+	if (!serial_number)
+		return FALSE;
+	os_free(wpa_s->conf->serial_number);
+	wpa_s->conf->serial_number = serial_number;
+	wpa_s->conf->changed_parameters |= CFG_CHANGED_WPS_STRING;
+	wpa_supplicant_update_config(wpa_s);
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_getter_wps_device_device_type - Get current device type
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "DeviceType" property.
+ */
+dbus_bool_t wpas_dbus_getter_wps_device_device_type(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+
+	if (!wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+						    (char *)
+						    wpa_s->conf->device_type,
+						    WPS_DEV_TYPE_LEN, error)) {
+		dbus_set_error(error, DBUS_ERROR_FAILED,
+			       "%s: error constructing reply", __func__);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/**
+ * wpas_dbus_setter_wps_device_device_type - Set current device type
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "DeviceType" property.
+ */
+dbus_bool_t wpas_dbus_setter_wps_device_device_type(
+	const struct wpa_dbus_property_desc *property_desc,
+	DBusMessageIter *iter, DBusError *error, void *user_data)
+{
+	struct wpa_supplicant *wpa_s = user_data;
+	u8 *dev_type;
+	int dev_len;
+	DBusMessageIter variant, array_iter;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+		return FALSE;
+
+	dbus_message_iter_recurse(iter, &variant);
+	if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY)
+		return FALSE;
+
+	dbus_message_iter_recurse(&variant, &array_iter);
+	dbus_message_iter_get_fixed_array(&array_iter, &dev_type, &dev_len);
+
+	if (dev_len != WPS_DEV_TYPE_LEN)
+		return FALSE;
+
+	os_memcpy(wpa_s->conf->device_type, dev_type, WPS_DEV_TYPE_LEN);
+	wpa_s->conf->changed_parameters |= CFG_CHANGED_DEVICE_TYPE;
+	wpa_supplicant_update_config(wpa_s);
+
+	return TRUE;
+}
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
index e8f62ef..e540832 100644
--- a/wpa_supplicant/dbus/dbus_old_handlers.c
+++ b/wpa_supplicant/dbus/dbus_old_handlers.c
@@ -717,16 +717,13 @@
 	char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
 
 	if (wpa_s->dbus_path)
-		ssid = wpa_config_add_network(wpa_s->conf);
+		ssid = wpa_supplicant_add_network(wpa_s);
 	if (ssid == NULL) {
 		reply = dbus_message_new_error(
 			message, WPAS_ERROR_ADD_NETWORK_ERROR,
 			"wpa_supplicant could not add a network on this interface.");
 		goto out;
 	}
-	wpas_notify_network_added(wpa_s, ssid);
-	ssid->disabled = 1;
-	wpa_config_set_network_defaults(ssid);
 
 	/* Construct the object path for this network. */
 	os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
@@ -758,7 +755,7 @@
 	const char *op;
 	char *iface = NULL, *net_id = NULL;
 	int id;
-	struct wpa_ssid *ssid;
+	int result;
 
 	if (!dbus_message_get_args(message, NULL,
 				   DBUS_TYPE_OBJECT_PATH, &op,
@@ -781,19 +778,12 @@
 	}
 
 	id = strtoul(net_id, NULL, 10);
-	ssid = wpa_config_get_network(wpa_s->conf, id);
-	if (ssid == NULL) {
+	result = wpa_supplicant_remove_network(wpa_s, id);
+	if (result == -1) {
 		reply = wpas_dbus_new_invalid_network_error(message);
 		goto out;
 	}
-
-	wpas_notify_network_removed(wpa_s, ssid);
-
-	if (ssid == wpa_s->current_ssid)
-		wpa_supplicant_deauthenticate(wpa_s,
-					      WLAN_REASON_DEAUTH_LEAVING);
-
-	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+	if (result == -2) {
 		reply = dbus_message_new_error(
 			message, WPAS_ERROR_REMOVE_NETWORK_ERROR,
 			"error removing the specified on this interface.");
@@ -1069,8 +1059,7 @@
 DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
 					 struct wpa_supplicant *wpa_s)
 {
-	wpa_s->disconnected = 1;
-	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	wpas_request_disconnection(wpa_s);
 
 	return wpas_dbus_new_success_reply(message);
 }
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 1d05198..1e37e27 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -1,9 +1,9 @@
 # Example wpa_supplicant build time configuration
 #
 # This file lists the configuration options that are used when building the
-# hostapd binary. All lines starting with # are ignored. Configuration option
-# lines must be commented out complete, if they are not to be included, i.e.,
-# just setting VARIABLE=n is not disabling that variable.
+# wpa_supplicant binary. All lines starting with # are ignored. Configuration
+# option lines must be commented out complete, if they are not to be included,
+# i.e., just setting VARIABLE=n is not disabling that variable.
 #
 # This file is included in Makefile, so variables like CFLAGS and LIBS can also
 # be modified from here. In most cases, these lines should use += in order not
@@ -370,9 +370,13 @@
 # amount of memory/flash.
 #CONFIG_DYNAMIC_EAP_METHODS=y
 
-# IEEE Std 802.11r-2008 (Fast BSS Transition)
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for station mode
 #CONFIG_IEEE80211R=y
 
+# IEEE Std 802.11r-2008 (Fast BSS Transition) for AP mode (implies
+# CONFIG_IEEE80211R).
+#CONFIG_IEEE80211R_AP=y
+
 # Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
 #CONFIG_DEBUG_FILE=y
 
@@ -548,3 +552,31 @@
 
 # Support Multi Band Operation
 #CONFIG_MBO=y
+
+# Fast Initial Link Setup (FILS) (IEEE 802.11ai)
+# Note: This is an experimental and not yet complete implementation. This
+# should not be enabled for production use.
+#CONFIG_FILS=y
+
+# Support RSN on IBSS networks
+# This is needed to be able to use mode=1 network profile with proto=RSN and
+# key_mgmt=WPA-PSK (i.e., full key management instead of WPA-None).
+#CONFIG_IBSS_RSN=y
+
+# External PMKSA cache control
+# This can be used to enable control interface commands that allow the current
+# PMKSA cache entries to be fetched and new entries to be added.
+#CONFIG_PMKSA_CACHE_EXTERNAL=y
+
+# Mesh Networking (IEEE 802.11s)
+#CONFIG_MESH=y
+
+# Background scanning modules
+# These can be used to request wpa_supplicant to perform background scanning
+# operations for roaming within an ESS (same SSID). See the bgscan parameter in
+# the wpa_supplicant.conf file for more details.
+# Periodic background scans based on signal strength
+#CONFIG_BGSCAN_SIMPLE=y
+# Learn channels used by the network and try to avoid bgscans on other
+# channels (experimental)
+#CONFIG_BGSCAN_LEARN=y
diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml
index 3f22413..25cfd06 100644
--- a/wpa_supplicant/doc/docbook/eapol_test.sgml
+++ b/wpa_supplicant/doc/docbook/eapol_test.sgml
@@ -194,7 +194,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2016,
+    <para>wpa_supplicant is copyright (c) 2003-2017,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml
index 13c9f45..fa94ae4 100644
--- a/wpa_supplicant/doc/docbook/wpa_background.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_background.sgml
@@ -90,7 +90,7 @@
 
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2016,
+    <para>wpa_supplicant is copyright (c) 2003-2017,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml
index 15400f0..be3045a 100644
--- a/wpa_supplicant/doc/docbook/wpa_cli.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_cli.sgml
@@ -345,7 +345,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2016,
+    <para>wpa_supplicant is copyright (c) 2003-2017,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
index 352d3d2..cee9ed6 100644
--- a/wpa_supplicant/doc/docbook/wpa_gui.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_gui.sgml
@@ -91,7 +91,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2016,
+    <para>wpa_supplicant is copyright (c) 2003-2017,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
index faf1f27..6667a07 100644
--- a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
@@ -18,7 +18,7 @@
   </refsynopsisdiv>
 
   <refsect1>
-    <title>Overview</title> 
+    <title>Overview</title>
 
     <para><command>wpa_passphrase</command> pre-computes PSK entries for
     network configuration blocks of a
@@ -62,7 +62,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2016,
+    <para>wpa_supplicant is copyright (c) 2003-2017,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml
index 403c9b2..3796b93 100644
--- a/wpa_supplicant/doc/docbook/wpa_priv.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_priv.sgml
@@ -137,7 +137,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2016,
+    <para>wpa_supplicant is copyright (c) 2003-2017,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
index c8c1ac4..80b3878 100644
--- a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
+++ b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
@@ -377,13 +377,6 @@
       </varlistentry>
 
       <varlistentry>
-	<term>-t</term>
-	<listitem>
-	  <para>Include timestamp in debug messages.</para>
-	</listitem>
-      </varlistentry>
-
-      <varlistentry>
 	<term>-h</term>
 	<listitem>
 	  <para>Help.  Show a usage message.</para>
@@ -572,7 +565,7 @@
 	using ap_scan=0 option in configuration file.</para>
 	</listitem>
       </varlistentry>
-      
+
       <varlistentry>
 	<term>Wired Ethernet drivers</term>
 	<listitem>
@@ -597,7 +590,7 @@
       </varlistentry>
     </variablelist>
 
-	
+
     <para>wpa_supplicant was designed to be portable for different
     drivers and operating systems. Hopefully, support for more wlan
     cards and OSes will be added in the future. See developer.txt for
@@ -736,7 +729,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2016,
+    <para>wpa_supplicant is copyright (c) 2003-2017,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 699fd4f..0af63c9 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -158,6 +158,15 @@
 	return -1;
 }
 
+static inline int wpa_drv_get_seqnum(struct wpa_supplicant *wpa_s,
+				     const u8 *addr, int idx, u8 *seq)
+{
+	if (wpa_s->driver->get_seqnum)
+		return wpa_s->driver->get_seqnum(wpa_s->ifname, wpa_s->drv_priv,
+						 addr, idx, seq);
+	return -1;
+}
+
 static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
 				     const u8 *addr, int reason_code)
 {
@@ -706,6 +715,14 @@
 	return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
 }
 
+static inline int wpa_drv_macsec_get_capability(struct wpa_supplicant *wpa_s,
+						enum macsec_cap *cap)
+{
+	if (!wpa_s->driver->macsec_get_capability)
+		return -1;
+	return wpa_s->driver->macsec_get_capability(wpa_s->drv_priv, cap);
+}
+
 static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
 						Boolean enabled)
 {
@@ -714,6 +731,14 @@
 	return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled);
 }
 
+static inline int wpa_drv_enable_encrypt(struct wpa_supplicant *wpa_s,
+						Boolean enabled)
+{
+	if (!wpa_s->driver->enable_encrypt)
+		return -1;
+	return wpa_s->driver->enable_encrypt(wpa_s->drv_priv, enabled);
+}
+
 static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s,
 					     Boolean enabled, u32 window)
 {
@@ -724,12 +749,11 @@
 }
 
 static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s,
-						   const u8 *cs, size_t cs_len)
+						   u64 cs)
 {
 	if (!wpa_s->driver->set_current_cipher_suite)
 		return -1;
-	return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs,
-						       cs_len);
+	return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs);
 }
 
 static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s,
@@ -741,145 +765,127 @@
 }
 
 static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s,
-						u32 channel, u8 an,
-						u32 *lowest_pn)
+						struct receive_sa *sa)
 {
 	if (!wpa_s->driver->get_receive_lowest_pn)
 		return -1;
-	return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel,
-						    an, lowest_pn);
+	return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s,
-						u32 channel, u8 an,
-						u32 *next_pn)
+						struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->get_transmit_next_pn)
 		return -1;
-	return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel,
-						    an, next_pn);
+	return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
-						u32 channel, u8 an,
-						u32 next_pn)
+						struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->set_transmit_next_pn)
 		return -1;
-	return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel,
-						    an, next_pn);
-}
-
-static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s,
-						   u32 *channel)
-{
-	if (!wpa_s->driver->get_available_receive_sc)
-		return -1;
-	return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv,
-						       channel);
+	return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, sa);
 }
 
 static inline int
-wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel,
-			  const u8 *sci_addr, u16 sci_port,
+wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, struct receive_sc *sc,
 			  unsigned int conf_offset, int validation)
 {
 	if (!wpa_s->driver->create_receive_sc)
 		return -1;
-	return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel,
-						sci_addr, sci_port, conf_offset,
-						validation);
+	return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, sc,
+						conf_offset, validation);
 }
 
 static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s,
-					    u32 channel)
+					    struct receive_sc *sc)
 {
 	if (!wpa_s->driver->delete_receive_sc)
 		return -1;
-	return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel);
+	return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, sc);
 }
 
 static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s,
-					    u32 channel, u8 an,
-					    u32 lowest_pn, const u8 *sak)
+					    struct receive_sa *sa)
 {
 	if (!wpa_s->driver->create_receive_sa)
 		return -1;
-	return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an,
-						lowest_pn, sak);
+	return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, sa);
+}
+
+static inline int wpa_drv_delete_receive_sa(struct wpa_supplicant *wpa_s,
+					    struct receive_sa *sa)
+{
+	if (!wpa_s->driver->delete_receive_sa)
+		return -1;
+	return wpa_s->driver->delete_receive_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
-					    u32 channel, u8 an)
+					    struct receive_sa *sa)
 {
 	if (!wpa_s->driver->enable_receive_sa)
 		return -1;
-	return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
-					     u32 channel, u8 an)
+					     struct receive_sa *sa)
 {
 	if (!wpa_s->driver->disable_receive_sa)
 		return -1;
-	return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int
-wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel)
-{
-	if (!wpa_s->driver->get_available_transmit_sc)
-		return -1;
-	return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv,
-							channel);
-}
-
-static inline int
-wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel,
-			   const u8 *sci_addr, u16 sci_port,
+wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, struct transmit_sc *sc,
 			   unsigned int conf_offset)
 {
 	if (!wpa_s->driver->create_transmit_sc)
 		return -1;
-	return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel,
-						 sci_addr, sci_port,
+	return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, sc,
 						 conf_offset);
 }
 
 static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s,
-					     u32 channel)
+					     struct transmit_sc *sc)
 {
 	if (!wpa_s->driver->delete_transmit_sc)
 		return -1;
-	return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel);
+	return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, sc);
 }
 
 static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s,
-					     u32 channel, u8 an,
-					     u32 next_pn,
-					     Boolean confidentiality,
-					     const u8 *sak)
+					     struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->create_transmit_sa)
 		return -1;
-	return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an,
-						 next_pn, confidentiality, sak);
+	return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, sa);
+}
+
+static inline int wpa_drv_delete_transmit_sa(struct wpa_supplicant *wpa_s,
+					     struct transmit_sa *sa)
+{
+	if (!wpa_s->driver->delete_transmit_sa)
+		return -1;
+	return wpa_s->driver->delete_transmit_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s,
-					     u32 channel, u8 an)
+					     struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->enable_transmit_sa)
 		return -1;
-	return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, sa);
 }
 
 static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s,
-					      u32 channel, u8 an)
+					      struct transmit_sa *sa)
 {
 	if (!wpa_s->driver->disable_transmit_sa)
 		return -1;
-	return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an);
+	return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, sa);
 }
 #endif /* CONFIG_MACSEC */
 
@@ -896,6 +902,11 @@
 					     unsigned int *num,
 					     unsigned int *freq_list)
 {
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->get_pref_freq_list_override)
+		return wpas_ctrl_iface_get_pref_freq_list_override(
+			wpa_s, if_type, num, freq_list);
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (!wpa_s->driver->get_pref_freq_list)
 		return -1;
 	return wpa_s->driver->get_pref_freq_list(wpa_s->drv_priv, if_type,
@@ -910,11 +921,72 @@
 	return wpa_s->driver->set_prob_oper_freq(wpa_s->drv_priv, freq);
 }
 
-static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s)
+static inline int wpa_drv_abort_scan(struct wpa_supplicant *wpa_s,
+				     u64 scan_cookie)
 {
 	if (!wpa_s->driver->abort_scan)
 		return -1;
-	return wpa_s->driver->abort_scan(wpa_s->drv_priv);
+	return wpa_s->driver->abort_scan(wpa_s->drv_priv, scan_cookie);
+}
+
+static inline int wpa_drv_configure_frame_filters(struct wpa_supplicant *wpa_s,
+						  u32 filters)
+{
+	if (!wpa_s->driver->configure_data_frame_filters)
+		return -1;
+	return wpa_s->driver->configure_data_frame_filters(wpa_s->drv_priv,
+							   filters);
+}
+
+static inline int wpa_drv_get_ext_capa(struct wpa_supplicant *wpa_s,
+				       enum wpa_driver_if_type type)
+{
+	if (!wpa_s->driver->get_ext_capab)
+		return -1;
+	return wpa_s->driver->get_ext_capab(wpa_s->drv_priv, type,
+					    &wpa_s->extended_capa,
+					    &wpa_s->extended_capa_mask,
+					    &wpa_s->extended_capa_len);
+}
+
+static inline int wpa_drv_p2p_lo_start(struct wpa_supplicant *wpa_s,
+				       unsigned int channel,
+				       unsigned int period,
+				       unsigned int interval,
+				       unsigned int count,
+				       const u8 *device_types,
+				       size_t dev_types_len,
+				       const u8 *ies, size_t ies_len)
+{
+	if (!wpa_s->driver->p2p_lo_start)
+		return -1;
+	return wpa_s->driver->p2p_lo_start(wpa_s->drv_priv, channel, period,
+					   interval, count, device_types,
+					   dev_types_len, ies, ies_len);
+}
+
+static inline int wpa_drv_p2p_lo_stop(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s->driver->p2p_lo_stop)
+		return -1;
+	return wpa_s->driver->p2p_lo_stop(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_default_scan_ies(struct wpa_supplicant *wpa_s,
+					       const u8 *ies, size_t len)
+{
+	if (!wpa_s->driver->set_default_scan_ies)
+		return -1;
+	return wpa_s->driver->set_default_scan_ies(wpa_s->drv_priv, ies, len);
+}
+
+static inline int wpa_drv_set_tdls_mode(struct wpa_supplicant *wpa_s,
+					int tdls_external_control)
+{
+	if (!wpa_s->driver->set_tdls_mode)
+		return -1;
+	return wpa_s->driver->set_tdls_mode(wpa_s->drv_priv,
+					    tdls_external_control);
 }
 
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 1b3d8a9..bc3c90e 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -281,6 +281,11 @@
 	wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
 
+#ifdef CONFIG_HS20
+	/* Clear possibly configured frame filters */
+	wpa_drv_configure_frame_filters(wpa_s, 0);
+#endif /* CONFIG_HS20 */
+
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
 		return;
 
@@ -498,7 +503,7 @@
 
 static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
 					 struct wpa_ssid *ssid,
-					 struct wpa_bss *bss)
+					 struct wpa_bss *bss, int debug_print)
 {
 	struct wpa_ie_data ie;
 	int proto_match = 0;
@@ -521,40 +526,46 @@
 		proto_match++;
 
 		if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - parse "
-				"failed");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - parse failed");
 			break;
 		}
 
 		if (wep_ok &&
 		    (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
 		{
-			wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
-				"in RSN IE");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   selected based on TSN in RSN IE");
 			return 1;
 		}
 
 		if (!(ie.proto & ssid->proto)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - proto "
-				"mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - proto mismatch");
 			break;
 		}
 
 		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - PTK "
-				"cipher mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - PTK cipher mismatch");
 			break;
 		}
 
 		if (!(ie.group_cipher & ssid->group_cipher)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - GTK "
-				"cipher mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - GTK cipher mismatch");
 			break;
 		}
 
 		if (!(ie.key_mgmt & ssid->key_mgmt)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - key mgmt "
-				"mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - key mgmt mismatch");
 			break;
 		}
 
@@ -562,16 +573,18 @@
 		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
 		    wpas_get_ssid_pmf(wpa_s, ssid) ==
 		    MGMT_FRAME_PROTECTION_REQUIRED) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - no mgmt "
-				"frame protection");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - no mgmt frame protection");
 			break;
 		}
 #endif /* CONFIG_IEEE80211W */
 		if ((ie.capabilities & WPA_CAPABILITY_MFPR) &&
 		    wpas_get_ssid_pmf(wpa_s, ssid) ==
 		    NO_MGMT_FRAME_PROTECTION) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip RSN IE - no mgmt frame protection enabled but AP requires it");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - no mgmt frame protection enabled but AP requires it");
 			break;
 		}
 #ifdef CONFIG_MBO
@@ -579,87 +592,114 @@
 		    wpas_mbo_get_bss_attr(bss, MBO_ATTR_ID_AP_CAPA_IND) &&
 		    wpas_get_ssid_pmf(wpa_s, ssid) !=
 		    NO_MGMT_FRAME_PROTECTION) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip RSN IE - no mgmt frame protection enabled on MBO AP");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip RSN IE - no mgmt frame protection enabled on MBO AP");
 			break;
 		}
 #endif /* CONFIG_MBO */
 
-		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   selected based on RSN IE");
 		return 1;
 	}
 
+#ifdef CONFIG_IEEE80211W
+	if (wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - MFP Required but network not MFP Capable");
+		return 0;
+	}
+#endif /* CONFIG_IEEE80211W */
+
 	wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
 		proto_match++;
 
 		if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - parse "
-				"failed");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip WPA IE - parse failed");
 			break;
 		}
 
 		if (wep_ok &&
 		    (ie.group_cipher & (WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)))
 		{
-			wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on TSN "
-				"in WPA IE");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   selected based on TSN in WPA IE");
 			return 1;
 		}
 
 		if (!(ie.proto & ssid->proto)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - proto "
-				"mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip WPA IE - proto mismatch");
 			break;
 		}
 
 		if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - PTK "
-				"cipher mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip WPA IE - PTK cipher mismatch");
 			break;
 		}
 
 		if (!(ie.group_cipher & ssid->group_cipher)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - GTK "
-				"cipher mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip WPA IE - GTK cipher mismatch");
 			break;
 		}
 
 		if (!(ie.key_mgmt & ssid->key_mgmt)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip WPA IE - key mgmt "
-				"mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip WPA IE - key mgmt mismatch");
 			break;
 		}
 
-		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on WPA IE");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   selected based on WPA IE");
 		return 1;
 	}
 
 	if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && !wpa_ie &&
 	    !rsn_ie) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "   allow for non-WPA IEEE 802.1X");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   allow for non-WPA IEEE 802.1X");
 		return 1;
 	}
 
 	if ((ssid->proto & (WPA_PROTO_WPA | WPA_PROTO_RSN)) &&
 	    wpa_key_mgmt_wpa(ssid->key_mgmt) && proto_match == 0) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no WPA/RSN proto match");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"   skip - no WPA/RSN proto match");
 		return 0;
 	}
 
 	if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
 	    wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "   allow in OSEN");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   allow in OSEN");
 		return 1;
 	}
 
 	if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "   allow in non-WPA/WPA2");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   allow in non-WPA/WPA2");
 		return 1;
 	}
 
-	wpa_dbg(wpa_s, MSG_DEBUG, "   reject due to mismatch with "
-		"WPA/WPA2");
+	if (debug_print)
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"   reject due to mismatch with WPA/WPA2");
 
 	return 0;
 }
@@ -679,7 +719,8 @@
 }
 
 
-static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+		      int debug_print)
 {
 	const struct hostapd_hw_modes *mode = NULL, *modes;
 	const u8 scan_ie[2] = { WLAN_EID_SUPP_RATES, WLAN_EID_EXT_SUPP_RATES };
@@ -736,9 +777,9 @@
 			if (flagged && ((rate_ie[j] & 0x7f) ==
 					BSS_MEMBERSHIP_SELECTOR_HT_PHY)) {
 				if (!ht_supported(mode)) {
-					wpa_dbg(wpa_s, MSG_DEBUG,
-						"   hardware does not support "
-						"HT PHY");
+					if (debug_print)
+						wpa_dbg(wpa_s, MSG_DEBUG,
+							"   hardware does not support HT PHY");
 					return 0;
 				}
 				continue;
@@ -748,9 +789,9 @@
 			if (flagged && ((rate_ie[j] & 0x7f) ==
 					BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) {
 				if (!vht_supported(mode)) {
-					wpa_dbg(wpa_s, MSG_DEBUG,
-						"   hardware does not support "
-						"VHT PHY");
+					if (debug_print)
+						wpa_dbg(wpa_s, MSG_DEBUG,
+							"   hardware does not support VHT PHY");
 					return 0;
 				}
 				continue;
@@ -770,10 +811,11 @@
 				 * order to join a BSS all required rates
 				 * have to be supported by the hardware.
 				 */
-				wpa_dbg(wpa_s, MSG_DEBUG,
-					"   hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
-					r / 10, r % 10,
-					bss->freq, mode->mode, mode->num_rates);
+				if (debug_print)
+					wpa_dbg(wpa_s, MSG_DEBUG,
+						"   hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
+						r / 10, r % 10,
+						bss->freq, mode->mode, mode->num_rates);
 				return 0;
 			}
 		}
@@ -829,7 +871,7 @@
 struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 				     int i, struct wpa_bss *bss,
 				     struct wpa_ssid *group,
-				     int only_first_ssid)
+				     int only_first_ssid, int debug_print)
 {
 	u8 wpa_ie_len, rsn_ie_len;
 	int wpa;
@@ -850,15 +892,20 @@
 	ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
 	osen = ie != NULL;
 
-	wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
-		"wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
-		i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
-		wpa_ie_len, rsn_ie_len, bss->caps, bss->level, bss->freq,
-		wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
-		(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
-		 wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
-		" p2p" : "",
-		osen ? " osen=1" : "");
+	if (debug_print) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR
+			" ssid='%s' wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d freq=%d %s%s%s",
+			i, MAC2STR(bss->bssid),
+			wpa_ssid_txt(bss->ssid, bss->ssid_len),
+			wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
+			bss->freq,
+			wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ?
+			" wps" : "",
+			(wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+			 wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE))
+			? " p2p" : "",
+			osen ? " osen=1" : "");
+	}
 
 	e = wpa_blacklist_get(wpa_s, bss->bssid);
 	if (e) {
@@ -875,24 +922,30 @@
 			limit = 0;
 		}
 		if (e->count > limit) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - blacklisted "
-				"(count=%d limit=%d)", e->count, limit);
+			if (debug_print) {
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - blacklisted (count=%d limit=%d)",
+					e->count, limit);
+			}
 			return NULL;
 		}
 	}
 
 	if (bss->ssid_len == 0) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID not known");
 		return NULL;
 	}
 
 	if (disallowed_bssid(wpa_s, bss->bssid)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID disallowed");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID disallowed");
 		return NULL;
 	}
 
 	if (disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID disallowed");
+		if (debug_print)
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID disallowed");
 		return NULL;
 	}
 
@@ -903,21 +956,25 @@
 		int res;
 
 		if (wpas_network_disabled(wpa_s, ssid)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled");
 			continue;
 		}
 
 		res = wpas_temp_disabled(wpa_s, ssid);
 		if (res > 0) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - disabled "
-				"temporarily for %d second(s)", res);
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - disabled temporarily for %d second(s)",
+					res);
 			continue;
 		}
 
 #ifdef CONFIG_WPS
 		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - blacklisted "
-				"(WPS)");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - blacklisted (WPS)");
 			continue;
 		}
 
@@ -943,13 +1000,17 @@
 		if (check_ssid &&
 		    (bss->ssid_len != ssid->ssid_len ||
 		     os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - SSID mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - SSID mismatch");
 			continue;
 		}
 
 		if (ssid->bssid_set &&
 		    os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - BSSID mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - BSSID mismatch");
 			continue;
 		}
 
@@ -957,8 +1018,9 @@
 		if (ssid->num_bssid_blacklist &&
 		    addr_in_list(bss->bssid, ssid->bssid_blacklist,
 				 ssid->num_bssid_blacklist)) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - BSSID blacklisted");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - BSSID blacklisted");
 			continue;
 		}
 
@@ -966,69 +1028,106 @@
 		if (ssid->num_bssid_whitelist &&
 		    !addr_in_list(bss->bssid, ssid->bssid_whitelist,
 				  ssid->num_bssid_whitelist)) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - BSSID not in whitelist");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - BSSID not in whitelist");
 			continue;
 		}
 
-		if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+		if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss,
+						   debug_print))
 			continue;
 
 		if (!osen && !wpa &&
 		    !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
 		    !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
 		    !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - non-WPA network "
-				"not allowed");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - non-WPA network not allowed");
 			continue;
 		}
 
 		if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
 		    has_wep_key(ssid)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - ignore WPA/WPA2 AP for WEP network block");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - ignore WPA/WPA2 AP for WEP network block");
 			continue;
 		}
 
 		if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - non-OSEN network "
-				"not allowed");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - non-OSEN network not allowed");
 			continue;
 		}
 
 		if (!wpa_supplicant_match_privacy(bss, ssid)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - privacy "
-				"mismatch");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - privacy mismatch");
 			continue;
 		}
 
-		if (!bss_is_ess(bss) && !bss_is_pbss(bss)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - neither ESS nor PBSS network");
+		if (ssid->mode != IEEE80211_MODE_MESH && !bss_is_ess(bss) &&
+		    !bss_is_pbss(bss)) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - not ESS, PBSS, or MBSS");
 			continue;
 		}
 
-		if (ssid->pbss != bss_is_pbss(bss)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - PBSS mismatch (ssid %d bss %d)",
-				ssid->pbss, bss_is_pbss(bss));
+		if (ssid->pbss != 2 && ssid->pbss != bss_is_pbss(bss)) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - PBSS mismatch (ssid %d bss %d)",
+					ssid->pbss, bss_is_pbss(bss));
 			continue;
 		}
 
 		if (!freq_allowed(ssid->freq_list, bss->freq)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - frequency not "
-				"allowed");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - frequency not allowed");
 			continue;
 		}
 
-		if (!rate_match(wpa_s, bss)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - rate sets do "
-				"not match");
+#ifdef CONFIG_MESH
+		if (ssid->mode == IEEE80211_MODE_MESH && ssid->frequency > 0 &&
+		    ssid->frequency != bss->freq) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - frequency not allowed (mesh)");
 			continue;
 		}
+#endif /* CONFIG_MESH */
+
+		if (!rate_match(wpa_s, bss, debug_print)) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - rate sets do not match");
+			continue;
+		}
+
+#ifndef CONFIG_IBSS_RSN
+		if (ssid->mode == WPAS_MODE_IBSS &&
+		    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
+					WPA_KEY_MGMT_WPA_NONE))) {
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - IBSS RSN not supported in the build");
+			continue;
+		}
+#endif /* !CONFIG_IBSS_RSN */
 
 #ifdef CONFIG_P2P
 		if (ssid->p2p_group &&
 		    !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
 		    !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no P2P IE seen");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - no P2P IE seen");
 			continue;
 		}
 
@@ -1038,20 +1137,26 @@
 
 			ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
 			if (ie == NULL) {
-				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no P2P element");
+				if (debug_print)
+					wpa_dbg(wpa_s, MSG_DEBUG,
+						"   skip - no P2P element");
 				continue;
 			}
 			p2p_ie = wpa_bss_get_vendor_ie_multi(
 				bss, P2P_IE_VENDOR_TYPE);
 			if (p2p_ie == NULL) {
-				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - could not fetch P2P element");
+				if (debug_print)
+					wpa_dbg(wpa_s, MSG_DEBUG,
+						"   skip - could not fetch P2P element");
 				continue;
 			}
 
 			if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
 			    || os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
 					 ETH_ALEN) != 0) {
-				wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no matching GO P2P Device Address in P2P element");
+				if (debug_print)
+					wpa_dbg(wpa_s, MSG_DEBUG,
+						"   skip - no matching GO P2P Device Address in P2P element");
 				wpabuf_free(p2p_ie);
 				continue;
 			}
@@ -1071,27 +1176,37 @@
 
 			os_reltime_sub(&wpa_s->scan_min_time,
 				       &bss->last_update, &diff);
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - scan result not recent enough (%u.%06u seconds too old)",
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - scan result not recent enough (%u.%06u seconds too old)",
 				(unsigned int) diff.sec,
 				(unsigned int) diff.usec);
 			continue;
 		}
 #ifdef CONFIG_MBO
+#ifdef CONFIG_TESTING_OPTIONS
+		if (wpa_s->ignore_assoc_disallow)
+			goto skip_assoc_disallow;
+#endif /* CONFIG_TESTING_OPTIONS */
 		assoc_disallow = wpas_mbo_get_bss_attr(
 			bss, MBO_ATTR_ID_ASSOC_DISALLOW);
 		if (assoc_disallow && assoc_disallow[1] >= 1) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - MBO association disallowed (reason %u)",
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - MBO association disallowed (reason %u)",
 				assoc_disallow[2]);
 			continue;
 		}
 
 		if (wpa_is_bss_tmp_disallowed(wpa_s, bss->bssid)) {
-			wpa_dbg(wpa_s, MSG_DEBUG,
-				"   skip - MBO retry delay has not passed yet");
+			if (debug_print)
+				wpa_dbg(wpa_s, MSG_DEBUG,
+					"   skip - MBO retry delay has not passed yet");
 			continue;
 		}
+#ifdef CONFIG_TESTING_OPTIONS
+	skip_assoc_disallow:
+#endif /* CONFIG_TESTING_OPTIONS */
 #endif /* CONFIG_MBO */
 
 		/* Matching configuration found */
@@ -1111,6 +1226,25 @@
 {
 	unsigned int i;
 
+	if (wpa_s->current_ssid) {
+		struct wpa_ssid *ssid;
+
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Scan results matching the currently selected network");
+		for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+			struct wpa_bss *bss = wpa_s->last_scan_res[i];
+
+			ssid = wpa_scan_res_match(wpa_s, i, bss, group,
+						  only_first_ssid, 0);
+			if (ssid != wpa_s->current_ssid)
+				continue;
+			wpa_dbg(wpa_s, MSG_DEBUG, "%u: " MACSTR
+				" freq=%d level=%d snr=%d est_throughput=%u",
+				i, MAC2STR(bss->bssid), bss->freq, bss->level,
+				bss->snr, bss->est_throughput);
+		}
+	}
+
 	if (only_first_ssid)
 		wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
 			group->id);
@@ -1121,7 +1255,7 @@
 	for (i = 0; i < wpa_s->last_scan_res_used; i++) {
 		struct wpa_bss *bss = wpa_s->last_scan_res[i];
 		*selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
-						    only_first_ssid);
+						    only_first_ssid, 1);
 		if (!*selected_ssid)
 			continue;
 		wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
@@ -1302,6 +1436,17 @@
 		{
 			if (wpas_network_disabled(wpa_s, ssid))
 				continue;
+#ifndef CONFIG_IBSS_RSN
+			if (ssid->mode == WPAS_MODE_IBSS &&
+			    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE |
+						WPA_KEY_MGMT_WPA_NONE))) {
+				wpa_msg(wpa_s, MSG_INFO,
+					"IBSS RSN not supported in the build - cannot use the profile for SSID '%s'",
+					wpa_ssid_txt(ssid->ssid,
+						     ssid->ssid_len));
+				continue;
+			}
+#endif /* !CONFIG_IBSS_RSN */
 			if (ssid->mode == IEEE80211_MODE_IBSS ||
 			    ssid->mode == IEEE80211_MODE_AP ||
 			    ssid->mode == IEEE80211_MODE_MESH)
@@ -1345,8 +1490,9 @@
 {
 	struct wpa_bss *current_bss = NULL;
 #ifndef CONFIG_NO_ROAMING
-	int min_diff;
+	int min_diff, diff;
 	int to_5ghz;
+	int cur_est, sel_est;
 #endif /* CONFIG_NO_ROAMING */
 
 	if (wpa_s->reassociate)
@@ -1380,12 +1526,13 @@
 #ifndef CONFIG_NO_ROAMING
 	wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
 	wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
-		" level=%d snr=%d est_throughput=%u",
-		MAC2STR(current_bss->bssid), current_bss->level,
+		" freq=%d level=%d snr=%d est_throughput=%u",
+		MAC2STR(current_bss->bssid),
+		current_bss->freq, current_bss->level,
 		current_bss->snr, current_bss->est_throughput);
 	wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR
-		" level=%d snr=%d est_throughput=%u",
-		MAC2STR(selected->bssid), selected->level,
+		" freq=%d level=%d snr=%d est_throughput=%u",
+		MAC2STR(selected->bssid), selected->freq, selected->level,
 		selected->snr, selected->est_throughput);
 
 	if (wpa_s->current_ssid->bssid_set &&
@@ -1411,6 +1558,14 @@
 		return 0;
 	}
 
+	if (current_bss->est_throughput > selected->est_throughput + 5000) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Skip roam - Current BSS has better estimated throughput");
+		return 0;
+	}
+
+	cur_est = current_bss->est_throughput;
+	sel_est = selected->est_throughput;
 	min_diff = 2;
 	if (current_bss->level < 0) {
 		if (current_bss->level < -85)
@@ -1423,20 +1578,42 @@
 			min_diff = 4;
 		else
 			min_diff = 5;
+		if (cur_est > sel_est * 1.5)
+			min_diff += 10;
+		else if (cur_est > sel_est * 1.2)
+			min_diff += 5;
+		else if (cur_est > sel_est * 1.1)
+			min_diff += 2;
+		else if (cur_est > sel_est)
+			min_diff++;
 	}
 	if (to_5ghz) {
+		int reduce = 2;
+
 		/* Make it easier to move to 5 GHz band */
-		if (min_diff > 2)
-			min_diff -= 2;
+		if (sel_est > cur_est * 1.5)
+			reduce = 5;
+		else if (sel_est > cur_est * 1.2)
+			reduce = 4;
+		else if (sel_est > cur_est * 1.1)
+			reduce = 3;
+
+		if (min_diff > reduce)
+			min_diff -= reduce;
 		else
 			min_diff = 0;
 	}
-	if (abs(current_bss->level - selected->level) < min_diff) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - too small difference "
-			"in signal level");
+	diff = abs(current_bss->level - selected->level);
+	if (diff < min_diff) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Skip roam - too small difference in signal level (%d < %d)",
+			diff, min_diff);
 		return 0;
 	}
 
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"Allow reassociation due to difference in signal level (%d >= %d)",
+		diff, min_diff);
 	return 1;
 #else /* CONFIG_NO_ROAMING */
 	return 0;
@@ -1444,11 +1621,18 @@
 }
 
 
-/* Return != 0 if no scan results could be fetched or if scan results should not
- * be shared with other virtual interfaces. */
+/*
+ * Return a negative value if no scan results could be fetched or if scan
+ * results should not be shared with other virtual interfaces.
+ * Return 0 if scan results were fetched and may be shared with other
+ * interfaces.
+ * Return 1 if scan results may be shared with other virtual interfaces but may
+ * not trigger any operations.
+ * Return 2 if the interface was removed and cannot be used.
+ */
 static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 					      union wpa_event_data *data,
-					      int own_request)
+					      int own_request, int update_only)
 {
 	struct wpa_scan_results *scan_res = NULL;
 	int ret = 0;
@@ -1498,6 +1682,11 @@
 	}
 #endif /* CONFIG_NO_RANDOM_POOL */
 
+	if (update_only) {
+		ret = 1;
+		goto scan_work_done;
+	}
+
 	if (own_request && wpa_s->scan_res_handler &&
 	    !(data && data->scan_info.external_scan)) {
 		void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
@@ -1506,7 +1695,7 @@
 		scan_res_handler = wpa_s->scan_res_handler;
 		wpa_s->scan_res_handler = NULL;
 		scan_res_handler(wpa_s, scan_res);
-		ret = -2;
+		ret = 1;
 		goto scan_work_done;
 	}
 
@@ -1547,6 +1736,10 @@
 	if (sme_proc_obss_scan(wpa_s) > 0)
 		goto scan_work_done;
 
+	if (own_request &&
+	    wpas_beacon_rep_scan_process(wpa_s, scan_res, &data->scan_info) > 0)
+		goto scan_work_done;
+
 	if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
 		goto scan_work_done;
 
@@ -1611,6 +1804,14 @@
 
 	selected = wpa_supplicant_pick_network(wpa_s, &ssid);
 
+#ifdef CONFIG_MESH
+	if (wpa_s->ifmsh) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Avoiding join because we already joined a mesh group");
+		return 0;
+	}
+#endif /* CONFIG_MESH */
+
 	if (selected) {
 		int skip;
 		skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid);
@@ -1634,18 +1835,12 @@
 		if (new_scan)
 			wpa_supplicant_rsn_preauth_scan_results(wpa_s);
 		/*
-		 * Do not notify other virtual radios of scan results since we do not
-		 * want them to start other associations at the same time.
+		 * Do not allow other virtual radios to trigger operations based
+		 * on these scan results since we do not want them to start
+		 * other associations at the same time.
 		 */
 		return 1;
 	} else {
-#ifdef CONFIG_MESH
-		if (wpa_s->ifmsh) {
-			wpa_msg(wpa_s, MSG_INFO,
-				"Avoiding join because we already joined a mesh group");
-			return 0;
-		}
-#endif /* CONFIG_MESH */
 		wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
 		ssid = wpa_supplicant_pick_new_network(wpa_s);
 		if (ssid) {
@@ -1726,7 +1921,7 @@
 	struct wpa_supplicant *ifs;
 	int res;
 
-	res = _wpa_supplicant_event_scan_results(wpa_s, data, 1);
+	res = _wpa_supplicant_event_scan_results(wpa_s, data, 1, 0);
 	if (res == 2) {
 		/*
 		 * Interface may have been removed, so must not dereference
@@ -1734,7 +1929,8 @@
 		 */
 		return 1;
 	}
-	if (res != 0) {
+
+	if (res < 0) {
 		/*
 		 * If no scan results could be fetched, then no need to
 		 * notify those interfaces that did not actually request
@@ -1754,7 +1950,10 @@
 		if (ifs != wpa_s) {
 			wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
 				   "sibling", ifs->ifname);
-			_wpa_supplicant_event_scan_results(ifs, data, 0);
+			res = _wpa_supplicant_event_scan_results(ifs, data, 0,
+								 res > 0);
+			if (res < 0)
+				return 0;
 		}
 	}
 
@@ -1771,6 +1970,8 @@
 #else /* CONFIG_NO_SCAN_PROCESSING */
 	struct os_reltime now;
 
+	wpa_s->ignore_post_flush_scan_res = 0;
+
 	if (wpa_s->last_scan_res_used == 0)
 		return -1;
 
@@ -2010,6 +2211,19 @@
 	if (!found && data->assoc_info.req_ies)
 		wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 
+#ifdef CONFIG_FILS
+#ifdef CONFIG_SME
+	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FILS &&
+	    (!data->assoc_info.resp_frame ||
+	     fils_process_assoc_resp(wpa_s->wpa,
+				     data->assoc_info.resp_frame,
+				     data->assoc_info.resp_frame_len) < 0)) {
+		wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
+		return -1;
+	}
+#endif /* CONFIG_SME */
+#endif /* CONFIG_FILS */
+
 #ifdef CONFIG_IEEE80211R
 #ifdef CONFIG_SME
 	if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
@@ -2210,7 +2424,7 @@
 				       union wpa_event_data *data)
 {
 	u8 bssid[ETH_ALEN];
-	int ft_completed;
+	int ft_completed, already_authorized;
 	int new_bss = 0;
 
 #ifdef CONFIG_AP
@@ -2231,6 +2445,13 @@
 	ft_completed = wpa_ft_is_completed(wpa_s->wpa);
 	if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
 		return;
+	/*
+	 * FILS authentication can share the same mechanism to mark the
+	 * connection fully authenticated, so set ft_completed also based on
+	 * FILS result.
+	 */
+	if (!ft_completed)
+		ft_completed = wpa_fils_is_completed(wpa_s->wpa);
 
 	if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
 		wpa_dbg(wpa_s, MSG_ERROR, "Failed to get BSSID");
@@ -2259,12 +2480,8 @@
 		}
 	}
 
-#ifdef ANDROID
-	if (wpa_s->conf->ap_scan == 1) {
-#else
 	if (wpa_s->conf->ap_scan == 1 &&
 	    wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
-#endif
 		if (wpa_supplicant_assoc_update_ie(wpa_s) < 0 && new_bss)
 			wpa_msg(wpa_s, MSG_WARNING,
 				"WPA/RSN IEs not updated");
@@ -2290,6 +2507,8 @@
 	if (wpa_s->l2)
 		l2_packet_notify_auth_start(wpa_s->l2);
 
+	already_authorized = data && data->assoc_info.authorized;
+
 	/*
 	 * Set portEnabled first to FALSE in order to get EAP state machine out
 	 * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
@@ -2298,11 +2517,12 @@
 	 * AUTHENTICATED without ever giving chance to EAP state machine to
 	 * reset the state.
 	 */
-	if (!ft_completed) {
+	if (!ft_completed && !already_authorized) {
 		eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
 		eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
 	}
-	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed)
+	if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed ||
+	    already_authorized)
 		eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
 	/* 802.1X::portControl = Auto */
 	eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
@@ -2365,7 +2585,7 @@
 		struct os_reltime now, age;
 		os_get_reltime(&now);
 		os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
-		if (age.sec == 0 && age.usec < 100000 &&
+		if (age.sec == 0 && age.usec < 200000 &&
 		    os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
 		    0) {
 			wpa_dbg(wpa_s, MSG_DEBUG, "Process pending EAPOL "
@@ -2394,7 +2614,7 @@
 	    wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
 	    wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE &&
 	    wpa_s->ibss_rsn == NULL) {
-		wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
+		wpa_s->ibss_rsn = ibss_rsn_init(wpa_s, wpa_s->current_ssid);
 		if (!wpa_s->ibss_rsn) {
 			wpa_msg(wpa_s, MSG_INFO, "Failed to init IBSS RSN");
 			wpa_supplicant_deauthenticate(
@@ -2488,6 +2708,7 @@
 	struct wpa_bss *fast_reconnect = NULL;
 	struct wpa_ssid *fast_reconnect_ssid = NULL;
 	struct wpa_ssid *last_ssid;
+	struct wpa_bss *curr = NULL;
 
 	authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
 	os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
@@ -2503,6 +2724,19 @@
 		return;
 	}
 
+	if (!wpa_s->disconnected && wpa_s->wpa_state >= WPA_AUTHENTICATING &&
+	    reason_code == WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY &&
+	    locally_generated)
+		/*
+		 * Remove the inactive AP (which is probably out of range) from
+		 * the BSS list after marking disassociation. In particular
+		 * mac80211-based drivers use the
+		 * WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY reason code in
+		 * locally generated disconnection events for cases where the
+		 * AP does not reply anymore.
+		 */
+		curr = wpa_s->current_bss;
+
 	if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
 		wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
 			"pre-shared key may be incorrect");
@@ -2564,6 +2798,9 @@
 	last_ssid = wpa_s->current_ssid;
 	wpa_supplicant_mark_disassoc(wpa_s);
 
+	if (curr)
+		wpa_bss_remove(wpa_s, curr, "Connection to AP lost");
+
 	if (authenticating && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
 		sme_disassoc_while_authenticating(wpa_s, prev_pending_bssid);
 		wpa_s->current_ssid = last_ssid;
@@ -3165,7 +3402,16 @@
 {
 	struct wpa_supplicant *ifs;
 
-	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
+	/*
+	 * To allow backwards compatibility with higher level layers that
+	 * assumed the REGDOM_CHANGE event is sent over the initially added
+	 * interface. Find the highest parent of this interface and use it to
+	 * send the event.
+	 */
+	for (ifs = wpa_s; ifs->parent && ifs != ifs->parent; ifs = ifs->parent)
+		;
+
+	wpa_msg(ifs, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
 		reg_init_str(info->initiator), reg_type_str(info->type),
 		info->alpha2[0] ? " alpha2=" : "",
 		info->alpha2[0] ? info->alpha2 : "");
@@ -3180,14 +3426,16 @@
 		free_hw_features(ifs);
 		ifs->hw.modes = wpa_drv_get_hw_feature_data(
 			ifs, &ifs->hw.num_modes, &ifs->hw.flags);
-	}
 
-	/* Restart sched_scan with updated channel list */
-	if (wpa_s->sched_scanning) {
-		wpa_dbg(wpa_s, MSG_DEBUG,
-			"Channel list changed restart sched scan.");
-		wpa_supplicant_cancel_sched_scan(wpa_s);
-		wpa_supplicant_req_scan(wpa_s, 0, 0);
+		/* Restart PNO/sched_scan with updated channel list */
+		if (ifs->pno) {
+			wpas_stop_pno(ifs);
+			wpas_start_pno(ifs);
+		} else if (ifs->sched_scanning && !ifs->pno_sched_pending) {
+			wpa_dbg(ifs, MSG_DEBUG,
+				"Channel list changed - restart sched_scan");
+			wpas_scan_restart_sched_scan(ifs);
+		}
 	}
 
 	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DRIVER);
@@ -3278,6 +3526,14 @@
 #endif /* CONFIG_INTERWORKING */
 
 	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+	    payload[0] == WLAN_RRM_RADIO_MEASUREMENT_REQUEST) {
+		wpas_rrm_handle_radio_measurement_request(wpa_s, mgmt->sa,
+							  payload + 1,
+							  plen - 1);
+		return;
+	}
+
+	if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
 	    payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
 		wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
 		return;
@@ -3362,6 +3618,7 @@
 			  union wpa_event_data *data)
 {
 	struct wpa_supplicant *wpa_s = ctx;
+	char buf[100];
 	int resched;
 
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
@@ -3405,6 +3662,13 @@
 		sme_event_auth(wpa_s, data);
 		break;
 	case EVENT_ASSOC:
+#ifdef CONFIG_TESTING_OPTIONS
+		if (wpa_s->ignore_auth_resp) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_ASSOC - ignore_auth_resp active!");
+			break;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 		wpa_supplicant_event_assoc(wpa_s, data);
 		if (data && data->assoc_info.authorized)
 			wpa_supplicant_event_assoc_auth(wpa_s, data);
@@ -3419,6 +3683,13 @@
 				    data ? &data->disassoc_info : NULL);
 		break;
 	case EVENT_DEAUTH:
+#ifdef CONFIG_TESTING_OPTIONS
+		if (wpa_s->ignore_auth_resp) {
+			wpa_printf(MSG_INFO,
+				   "EVENT_DEAUTH - ignore_auth_resp active!");
+			break;
+		}
+#endif /* CONFIG_TESTING_OPTIONS */
 		wpas_event_deauth(wpa_s,
 				  data ? &data->deauth_info : NULL);
 		break;
@@ -3516,16 +3787,26 @@
 		break;
 #endif /* CONFIG_IBSS_RSN */
 	case EVENT_ASSOC_REJECT:
+		if (data->assoc_reject.timeout_reason)
+			os_snprintf(buf, sizeof(buf), "=%s",
+				    data->assoc_reject.timeout_reason);
+		else
+			buf[0] = '\0';
 		if (data->assoc_reject.bssid)
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"bssid=" MACSTR	" status_code=%u",
+				"bssid=" MACSTR	" status_code=%u%s%s",
 				MAC2STR(data->assoc_reject.bssid),
-				data->assoc_reject.status_code);
+				data->assoc_reject.status_code,
+				data->assoc_reject.timed_out ? " timeout" : "",
+				buf);
 		else
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_ASSOC_REJECT
-				"status_code=%u",
-				data->assoc_reject.status_code);
+				"status_code=%u%s%s",
+				data->assoc_reject.status_code,
+				data->assoc_reject.timed_out ? " timeout" : "",
+				buf);
 		wpa_s->assoc_status_code = data->assoc_reject.status_code;
+		wpa_s->assoc_timed_out = data->assoc_reject.timed_out;
 		wpas_notify_assoc_status_code(wpa_s);
 		if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
 			sme_event_assoc_reject(wpa_s, data);
@@ -3582,17 +3863,20 @@
 #endif /* CONFIG_AP */
 #ifdef CONFIG_OFFCHANNEL
 		wpa_dbg(wpa_s, MSG_DEBUG, "EVENT_TX_STATUS pending_dst="
-			MACSTR, MAC2STR(wpa_s->parent->pending_action_dst));
+			MACSTR, MAC2STR(wpa_s->p2pdev->pending_action_dst));
 		/*
 		 * Catch TX status events for Action frames we sent via group
-		 * interface in GO mode.
+		 * interface in GO mode, or via standalone AP interface.
+		 * Note, wpa_s->p2pdev will be the same as wpa_s->parent,
+		 * except when the primary interface is used as a GO interface
+		 * (for drivers which do not have group interface concurrency)
 		 */
 		if (data->tx_status.type == WLAN_FC_TYPE_MGMT &&
 		    data->tx_status.stype == WLAN_FC_STYPE_ACTION &&
-		    os_memcmp(wpa_s->parent->pending_action_dst,
+		    os_memcmp(wpa_s->p2pdev->pending_action_dst,
 			      data->tx_status.dst, ETH_ALEN) == 0) {
 			offchannel_send_action_tx_status(
-				wpa_s->parent, data->tx_status.dst,
+				wpa_s->p2pdev, data->tx_status.dst,
 				data->tx_status.data,
 				data->tx_status.data_len,
 				data->tx_status.ack ?
@@ -3638,6 +3922,15 @@
 		if (!data || !wpa_s->current_ssid)
 			break;
 
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CHANNEL_SWITCH
+			"freq=%d ht_enabled=%d ch_offset=%d ch_width=%s cf1=%d cf2=%d",
+			data->ch_switch.freq,
+			data->ch_switch.ht_enabled,
+			data->ch_switch.ch_offset,
+			channel_width_to_string(data->ch_switch.ch_width),
+			data->ch_switch.cf1,
+			data->ch_switch.cf2);
+
 		wpa_s->assoc_freq = data->ch_switch.freq;
 		wpa_s->current_ssid->frequency = data->ch_switch.freq;
 
@@ -3905,6 +4198,7 @@
 				wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1);
 		}
 		wpa_supplicant_mark_disassoc(wpa_s);
+		wpa_bss_flush(wpa_s);
 		radio_remove_works(wpa_s, NULL, 0);
 
 		wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
@@ -3971,6 +4265,20 @@
 			break;
 
 		/*
+		 * If the driver stopped scanning without being requested to,
+		 * request a new scan to continue scanning for networks.
+		 */
+		if (!wpa_s->sched_scan_stop_req &&
+		    wpa_s->wpa_state == WPA_SCANNING) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"Restart scanning after unexpected sched_scan stop event");
+			wpa_supplicant_req_scan(wpa_s, 1, 0);
+			break;
+		}
+
+		wpa_s->sched_scan_stop_req = 0;
+
+		/*
 		 * Start a new sched scan to continue searching for more SSIDs
 		 * either if timed out or PNO schedule scan is pending.
 		 */
@@ -4020,12 +4328,28 @@
 #endif /* CONFIG_AP */
 		break;
 	case EVENT_ACS_CHANNEL_SELECTED:
+#ifdef CONFIG_AP
 #ifdef CONFIG_ACS
 		if (!wpa_s->ap_iface)
 			break;
 		hostapd_acs_channel_selected(wpa_s->ap_iface->bss[0],
 					     &data->acs_selected_channels);
 #endif /* CONFIG_ACS */
+#endif /* CONFIG_AP */
+		break;
+	case EVENT_P2P_LO_STOP:
+#ifdef CONFIG_P2P
+		wpa_s->p2p_lo_started = 0;
+		wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_LISTEN_OFFLOAD_STOP
+			P2P_LISTEN_OFFLOAD_STOP_REASON "reason=%d",
+			data->p2p_lo_stop.reason_code);
+#endif /* CONFIG_P2P */
+		break;
+	case EVENT_BEACON_LOSS:
+		if (!wpa_s->current_bss || !wpa_s->current_ssid)
+			break;
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_BEACON_LOSS);
+		bgscan_notify_beacon_loss(wpa_s);
 		break;
 	default:
 		wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 4f0d0e6..db481a5 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -17,6 +17,7 @@
 #include "common/wpa_ctrl.h"
 #include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
+#include "config.h"
 #include "driver_i.h"
 #include "offchannel.h"
 #include "gas_query.h"
@@ -52,6 +53,7 @@
 		   const struct wpabuf *adv_proto,
 		   const struct wpabuf *resp, u16 status_code);
 	void *ctx;
+	u8 sa[ETH_ALEN];
 };
 
 /**
@@ -62,6 +64,9 @@
 	struct dl_list pending; /* struct gas_query_pending */
 	struct gas_query_pending *current;
 	struct wpa_radio_work *work;
+	struct os_reltime last_mac_addr_rand;
+	int last_rand_sa_type;
+	u8 rand_addr[ETH_ALEN];
 };
 
 
@@ -116,8 +121,6 @@
 		return "PEER_ERROR";
 	case GAS_QUERY_INTERNAL_ERROR:
 		return "INTERNAL_ERROR";
-	case GAS_QUERY_CANCELLED:
-		return "CANCELLED";
 	case GAS_QUERY_DELETED_AT_DEINIT:
 		return "DELETED_AT_DEINIT";
 	}
@@ -273,10 +276,15 @@
 			struct wpabuf *req, unsigned int wait_time)
 {
 	int res, prot = pmf_in_use(gas->wpa_s, query->addr);
+	const u8 *bssid;
+	const u8 wildcard_bssid[ETH_ALEN] = {
+		0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+	};
 
 	wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
-		   "freq=%d prot=%d", MAC2STR(query->addr),
-		   (unsigned int) wpabuf_len(req), query->freq, prot);
+		   "freq=%d prot=%d using src addr " MACSTR,
+		   MAC2STR(query->addr), (unsigned int) wpabuf_len(req),
+		   query->freq, prot, MAC2STR(query->sa));
 	if (prot) {
 		u8 *categ = wpabuf_mhead_u8(req);
 		*categ = WLAN_ACTION_PROTECTED_DUAL;
@@ -285,10 +293,19 @@
 	if (gas->wpa_s->max_remain_on_chan &&
 	    wait_time > gas->wpa_s->max_remain_on_chan)
 		wait_time = gas->wpa_s->max_remain_on_chan;
+	if (!gas->wpa_s->conf->gas_address3 ||
+	    (gas->wpa_s->current_ssid &&
+	     gas->wpa_s->wpa_state >= WPA_ASSOCIATED &&
+	     os_memcmp(query->addr, gas->wpa_s->bssid, ETH_ALEN) == 0))
+		bssid = query->addr;
+	else
+		bssid = wildcard_bssid;
+
 	res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
-				     gas->wpa_s->own_addr, query->addr,
-				     wpabuf_head(req), wpabuf_len(req),
-				     wait_time, gas_query_tx_status, 0);
+				     query->sa, bssid, wpabuf_head(req),
+				     wpabuf_len(req), wait_time,
+				     gas_query_tx_status, 0);
+
 	if (res == 0)
 		query->offchannel_tx_started = 1;
 	return res;
@@ -397,6 +414,7 @@
 	}
 
 	if (comeback_delay) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
 		query->wait_comeback = 1;
 		gas_query_tx_comeback_req_delay(gas, query, comeback_delay);
 		return;
@@ -500,6 +518,14 @@
 	if (gas == NULL || len < 4)
 		return -1;
 
+	pos = data;
+	action = *pos++;
+	dialog_token = *pos++;
+
+	if (action != WLAN_PA_GAS_INITIAL_RESP &&
+	    action != WLAN_PA_GAS_COMEBACK_RESP)
+		return -1; /* Not a GAS response */
+
 	prot = categ == WLAN_ACTION_PROTECTED_DUAL;
 	pmf = pmf_in_use(gas->wpa_s, sa);
 	if (prot && !pmf) {
@@ -511,14 +537,6 @@
 		return 0;
 	}
 
-	pos = data;
-	action = *pos++;
-	dialog_token = *pos++;
-
-	if (action != WLAN_PA_GAS_INITIAL_RESP &&
-	    action != WLAN_PA_GAS_COMEBACK_RESP)
-		return -1; /* Not a GAS response */
-
 	query = gas_query_get_pending(gas, sa, dialog_token);
 	if (query == NULL) {
 		wpa_printf(MSG_DEBUG, "GAS: No pending query found for " MACSTR
@@ -684,7 +702,7 @@
 			 GAS_QUERY_WAIT_TIME_INITIAL) < 0) {
 		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
 			   MACSTR, MAC2STR(query->addr));
-		gas_query_free(query, 1);
+		gas_query_done(gas, query, GAS_QUERY_INTERNAL_ERROR);
 		return;
 	}
 	gas->current = query;
@@ -714,6 +732,58 @@
 }
 
 
+static int gas_query_set_sa(struct gas_query *gas,
+			    struct gas_query_pending *query)
+{
+	struct wpa_supplicant *wpa_s = gas->wpa_s;
+	struct os_reltime now;
+
+	if (!wpa_s->conf->gas_rand_mac_addr ||
+	    !(wpa_s->current_bss ?
+	      (wpa_s->drv_flags &
+	       WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA_CONNECTED) :
+	      (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MGMT_TX_RANDOM_TA))) {
+		/* Use own MAC address as the transmitter address */
+		os_memcpy(query->sa, wpa_s->own_addr, ETH_ALEN);
+		return 0;
+	}
+
+	os_get_reltime(&now);
+
+	if (wpa_s->conf->gas_rand_mac_addr == gas->last_rand_sa_type &&
+	    gas->last_mac_addr_rand.sec != 0 &&
+	    !os_reltime_expired(&now, &gas->last_mac_addr_rand,
+				wpa_s->conf->gas_rand_addr_lifetime)) {
+		wpa_printf(MSG_DEBUG,
+			   "GAS: Use the previously selected random transmitter address "
+			   MACSTR, MAC2STR(gas->rand_addr));
+		os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
+		return 0;
+	}
+
+	if (wpa_s->conf->gas_rand_mac_addr == 1 &&
+	    random_mac_addr(gas->rand_addr) < 0) {
+		wpa_printf(MSG_ERROR, "GAS: Failed to get random address");
+		return -1;
+	}
+
+	if (wpa_s->conf->gas_rand_mac_addr == 2 &&
+	    random_mac_addr_keep_oui(gas->rand_addr) < 0) {
+		wpa_printf(MSG_ERROR,
+			   "GAS: Failed to get random address with same OUI");
+		return -1;
+	}
+
+	wpa_printf(MSG_DEBUG, "GAS: Use a new random transmitter address "
+		   MACSTR, MAC2STR(gas->rand_addr));
+	os_memcpy(query->sa, gas->rand_addr, ETH_ALEN);
+	os_get_reltime(&gas->last_mac_addr_rand);
+	gas->last_rand_sa_type = wpa_s->conf->gas_rand_mac_addr;
+
+	return 0;
+}
+
+
 /**
  * gas_query_req - Request a GAS query
  * @gas: GAS query data from gas_query_init()
@@ -748,6 +818,10 @@
 		return -1;
 
 	query->gas = gas;
+	if (gas_query_set_sa(gas, query)) {
+		os_free(query);
+		return -1;
+	}
 	os_memcpy(query->addr, dst, ETH_ALEN);
 	query->dialog_token = dialog_token;
 	query->freq = freq;
@@ -764,26 +838,10 @@
 
 	if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
 			   query) < 0) {
+		query->req = NULL; /* caller will free this in error case */
 		gas_query_free(query, 1);
 		return -1;
 	}
 
 	return dialog_token;
 }
-
-
-/**
- * gas_query_cancel - Cancel a pending GAS query
- * @gas: GAS query data from gas_query_init()
- * @dst: Destination MAC address for the query
- * @dialog_token: Dialog token from gas_query_req()
- */
-void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token)
-{
-	struct gas_query_pending *query;
-
-	query = gas_query_get_pending(gas, dst, dialog_token);
-	if (query)
-		gas_query_done(gas, query, GAS_QUERY_CANCELLED);
-
-}
diff --git a/wpa_supplicant/gas_query.h b/wpa_supplicant/gas_query.h
index ad13490..ef82097 100644
--- a/wpa_supplicant/gas_query.h
+++ b/wpa_supplicant/gas_query.h
@@ -29,7 +29,6 @@
 	GAS_QUERY_TIMEOUT,
 	GAS_QUERY_PEER_ERROR,
 	GAS_QUERY_INTERNAL_ERROR,
-	GAS_QUERY_CANCELLED,
 	GAS_QUERY_DELETED_AT_DEINIT
 };
 
@@ -40,7 +39,6 @@
 			     const struct wpabuf *adv_proto,
 			     const struct wpabuf *resp, u16 status_code),
 		  void *ctx);
-void gas_query_cancel(struct gas_query *gas, const u8 *dst, u8 dialog_token);
 
 #else /* CONFIG_GAS */
 
diff --git a/wpa_supplicant/hidl/.clang-format b/wpa_supplicant/hidl/.clang-format
new file mode 100644
index 0000000..42fadb5
--- /dev/null
+++ b/wpa_supplicant/hidl/.clang-format
@@ -0,0 +1,9 @@
+BasedOnStyle: Google
+IndentWidth: 8
+UseTab: Always
+BreakBeforeBraces: Mozilla
+AllowShortIfStatementsOnASingleLine: false
+IndentCaseLabels: false
+AccessModifierOffset: -8
+AlignAfterOpenBracket: AlwaysBreak
+SortIncludes: false
diff --git a/wpa_supplicant/hidl/1.0/hidl.cpp b/wpa_supplicant/hidl/1.0/hidl.cpp
new file mode 100644
index 0000000..e5f5303
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/hidl.cpp
@@ -0,0 +1,623 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <hwbinder/IPCThreadState.h>
+
+#include <hidl/HidlTransportSupport.h>
+#include "hidl_manager.h"
+
+extern "C" {
+#include "hidl.h"
+#include "hidl_i.h"
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/includes.h"
+}
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::IPCThreadState;
+using android::hardware::wifi::supplicant::V1_0::implementation::HidlManager;
+
+void wpas_hidl_sock_handler(
+    int /* sock */, void * /* eloop_ctx */, void * /* sock_ctx */)
+{
+	IPCThreadState::self()->handlePolledCommands();
+}
+
+struct wpas_hidl_priv *wpas_hidl_init(struct wpa_global *global)
+{
+	struct wpas_hidl_priv *priv;
+	HidlManager *hidl_manager;
+
+	priv = (wpas_hidl_priv *)os_zalloc(sizeof(*priv));
+	if (!priv)
+		return NULL;
+	priv->global = global;
+
+	wpa_printf(MSG_DEBUG, "Initing hidl control");
+
+	configureRpcThreadpool(1, true /* callerWillJoin */);
+	IPCThreadState::self()->disableBackgroundScheduling(true);
+	IPCThreadState::self()->setupPolling(&priv->hidl_fd);
+	if (priv->hidl_fd < 0)
+		goto err;
+
+	wpa_printf(MSG_INFO, "Processing hidl events on FD %d", priv->hidl_fd);
+	// Look for read events from the hidl socket in the eloop.
+	if (eloop_register_read_sock(
+		priv->hidl_fd, wpas_hidl_sock_handler, global, priv) < 0)
+		goto err;
+
+	hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		goto err;
+	hidl_manager->registerHidlService(global);
+	// We may not need to store this hidl manager reference in the
+	// global data strucure because we've made it a singleton class.
+	priv->hidl_manager = (void *)hidl_manager;
+
+	return priv;
+err:
+	wpas_hidl_deinit(priv);
+	return NULL;
+}
+
+void wpas_hidl_deinit(struct wpas_hidl_priv *priv)
+{
+	if (!priv)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Deiniting hidl control");
+
+	HidlManager::destroyInstance();
+	eloop_unregister_read_sock(priv->hidl_fd);
+	IPCThreadState::shutdown();
+	os_free(priv);
+}
+
+int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->global->hidl)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Registering interface to hidl control: %s",
+	    wpa_s->ifname);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return 1;
+
+	return hidl_manager->registerInterface(wpa_s);
+}
+
+int wpas_hidl_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->global->hidl)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Deregistering interface from hidl control: %s",
+	    wpa_s->ifname);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return 1;
+
+	return hidl_manager->unregisterInterface(wpa_s);
+}
+
+int wpas_hidl_register_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !ssid)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Registering network to hidl control: %d", ssid->id);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return 1;
+
+	return hidl_manager->registerNetwork(wpa_s, ssid);
+}
+
+int wpas_hidl_unregister_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !ssid)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Deregistering network from hidl control: %d", ssid->id);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return 1;
+
+	return hidl_manager->unregisterNetwork(wpa_s, ssid);
+}
+
+int wpas_hidl_notify_state_changed(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s || !wpa_s->global->hidl)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying state change event to hidl control: %d",
+	    wpa_s->wpa_state);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return 1;
+
+	return hidl_manager->notifyStateChange(wpa_s);
+}
+
+int wpas_hidl_notify_network_request(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+    enum wpa_ctrl_req_type rtype, const char *default_txt)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !ssid)
+		return 1;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying network request to hidl control: %d",
+	    ssid->id);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return 1;
+
+	return hidl_manager->notifyNetworkRequest(
+	    wpa_s, ssid, rtype, default_txt);
+}
+
+void wpas_hidl_notify_anqp_query_done(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
+    const struct wpa_bss_anqp *anqp)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !bssid || !result || !anqp)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying ANQP query done to hidl control: " MACSTR "result: %s",
+	    MAC2STR(bssid), result);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyAnqpQueryDone(wpa_s, bssid, result, anqp);
+}
+
+void wpas_hidl_notify_hs20_icon_query_done(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *file_name,
+    const u8 *image, u32 image_length)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !bssid || !file_name || !image)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying HS20 icon query done to hidl control: " MACSTR
+		       "file_name: %s",
+	    MAC2STR(bssid), file_name);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyHs20IconQueryDone(
+	    wpa_s, bssid, file_name, image, image_length);
+}
+
+void wpas_hidl_notify_hs20_rx_subscription_remediation(
+    struct wpa_supplicant *wpa_s, const char *url, u8 osu_method)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !url)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying HS20 subscription remediation rx to hidl control: %s",
+	    url);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyHs20RxSubscriptionRemediation(
+	    wpa_s, url, osu_method);
+}
+
+void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
+    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
+{
+	if (!wpa_s || !wpa_s->global->hidl || !url)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying HS20 deauth imminent notice rx to hidl control: %s",
+	    url);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyHs20RxDeauthImminentNotice(
+	    wpa_s, code, reauth_delay, url);
+}
+
+void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying disconnect reason to hidl control: %d",
+	    wpa_s->disconnect_reason);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyDisconnectReason(wpa_s);
+}
+
+void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying assoc reject to hidl control: %d",
+	    wpa_s->assoc_status_code);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyAssocReject(wpa_s);
+}
+
+void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying auth timeout to hidl control");
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyAuthTimeout(wpa_s);
+}
+
+void wpas_hidl_notify_bssid_changed(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying bssid changed to hidl control");
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyBssidChanged(wpa_s);
+}
+
+void wpas_hidl_notify_wps_event_fail(
+    struct wpa_supplicant *wpa_s, uint8_t *peer_macaddr, uint16_t config_error,
+    uint16_t error_indication)
+{
+	if (!wpa_s || !peer_macaddr)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying Wps event fail to hidl control: %d, %d",
+	    config_error, error_indication);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyWpsEventFail(
+	    wpa_s, peer_macaddr, config_error, error_indication);
+}
+
+void wpas_hidl_notify_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying Wps event success to hidl control");
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyWpsEventSuccess(wpa_s);
+}
+
+void wpas_hidl_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying Wps event PBC overlap to hidl control");
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyWpsEventPbcOverlap(wpa_s);
+}
+
+void wpas_hidl_notify_p2p_device_found(
+    struct wpa_supplicant *wpa_s, const u8 *addr,
+    const struct p2p_peer_info *info, const u8 *peer_wfd_device_info,
+    u8 peer_wfd_device_info_len)
+{
+	if (!wpa_s || !addr || !info)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying P2P device found to hidl control " MACSTR,
+	    MAC2STR(info->p2p_device_addr));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pDeviceFound(
+	    wpa_s, addr, info, peer_wfd_device_info, peer_wfd_device_info_len);
+}
+
+void wpas_hidl_notify_p2p_device_lost(
+    struct wpa_supplicant *wpa_s, const u8 *p2p_device_addr)
+{
+	if (!wpa_s || !p2p_device_addr)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying P2P device lost to hidl control " MACSTR,
+	    MAC2STR(p2p_device_addr));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pDeviceLost(wpa_s, p2p_device_addr);
+}
+
+void wpas_hidl_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	wpa_printf(MSG_DEBUG, "Notifying P2P find stop to hidl control");
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pFindStopped(wpa_s);
+}
+
+void wpas_hidl_notify_p2p_go_neg_req(
+    struct wpa_supplicant *wpa_s, const u8 *src_addr, u16 dev_passwd_id,
+    u8 go_intent)
+{
+	if (!wpa_s || !src_addr)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P GO negotiation request to hidl control " MACSTR,
+	    MAC2STR(src_addr));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pGoNegReq(
+	    wpa_s, src_addr, dev_passwd_id, go_intent);
+}
+
+void wpas_hidl_notify_p2p_go_neg_completed(
+    struct wpa_supplicant *wpa_s, const struct p2p_go_neg_results *res)
+{
+	if (!wpa_s || !res)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P GO negotiation completed to hidl control: %d",
+	    res->status);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pGoNegCompleted(wpa_s, res);
+}
+
+void wpas_hidl_notify_p2p_group_formation_failure(
+    struct wpa_supplicant *wpa_s, const char *reason)
+{
+	if (!wpa_s || !reason)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P Group formation failure to hidl control: %s",
+	    reason);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pGroupFormationFailure(wpa_s, reason);
+}
+
+void wpas_hidl_notify_p2p_group_started(
+    struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, int persistent,
+    int client)
+{
+	if (!wpa_s || !ssid)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying P2P Group start to hidl control: %d",
+	    ssid->id);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pGroupStarted(wpa_s, ssid, persistent, client);
+}
+
+void wpas_hidl_notify_p2p_group_removed(
+    struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, const char *role)
+{
+	if (!wpa_s || !ssid || !role)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG, "Notifying P2P Group removed to hidl control: %d",
+	    ssid->id);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pGroupRemoved(wpa_s, ssid, role);
+}
+
+void wpas_hidl_notify_p2p_invitation_received(
+    struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *go_dev_addr,
+    const u8 *bssid, int id, int op_freq)
+{
+	if (!wpa_s || !sa || !go_dev_addr || !bssid)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P invitation received to hidl control: %d " MACSTR, id,
+	    MAC2STR(bssid));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pInvitationReceived(
+	    wpa_s, sa, go_dev_addr, bssid, id, op_freq);
+}
+
+void wpas_hidl_notify_p2p_invitation_result(
+    struct wpa_supplicant *wpa_s, int status, const u8 *bssid)
+{
+	if (!wpa_s || !bssid)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P invitation result to hidl control: " MACSTR,
+	    MAC2STR(bssid));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pInvitationResult(wpa_s, status, bssid);
+}
+
+void wpas_hidl_notify_p2p_provision_discovery(
+    struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
+    enum p2p_prov_disc_status status, u16 config_methods,
+    unsigned int generated_pin)
+{
+	if (!wpa_s || !dev_addr)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P provision discovery to hidl control " MACSTR,
+	    MAC2STR(dev_addr));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pProvisionDiscovery(
+	    wpa_s, dev_addr, request, status, config_methods, generated_pin);
+}
+
+void wpas_hidl_notify_p2p_sd_response(
+    struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
+    const u8 *tlvs, size_t tlvs_len)
+{
+	if (!wpa_s || !sa || !tlvs)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P service discovery response to hidl control " MACSTR,
+	    MAC2STR(sa));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyP2pSdResponse(
+	    wpa_s, sa, update_indic, tlvs, tlvs_len);
+}
+
+void wpas_hidl_notify_ap_sta_authorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+{
+	if (!wpa_s || !sta)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P AP STA authorized to hidl control " MACSTR,
+	    MAC2STR(sta));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyApStaAuthorized(wpa_s, sta, p2p_dev_addr);
+}
+
+void wpas_hidl_notify_ap_sta_deauthorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+{
+	if (!wpa_s || !sta)
+		return;
+
+	wpa_printf(
+	    MSG_DEBUG,
+	    "Notifying P2P AP STA deauthorized to hidl control " MACSTR,
+	    MAC2STR(sta));
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager)
+		return;
+
+	hidl_manager->notifyApStaDeauthorized(wpa_s, sta, p2p_dev_addr);
+}
diff --git a/wpa_supplicant/hidl/1.0/hidl.h b/wpa_supplicant/hidl/1.0/hidl.h
new file mode 100644
index 0000000..96631f2
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/hidl.h
@@ -0,0 +1,221 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_HIDL_H
+#define WPA_SUPPLICANT_HIDL_HIDL_H
+
+#ifdef _cplusplus
+extern "C" {
+#endif  // _cplusplus
+
+/**
+ * This is the hidl RPC interface entry point to the wpa_supplicant core.
+ * This initializes the hidl driver & HidlManager instance and then forwards
+ * all the notifcations from the supplicant core to the HidlManager.
+ */
+struct wpas_hidl_priv;
+struct wpa_global;
+
+struct wpas_hidl_priv *wpas_hidl_init(struct wpa_global *global);
+void wpas_hidl_deinit(struct wpas_hidl_priv *priv);
+
+#ifdef CONFIG_CTRL_IFACE_HIDL
+int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s);
+int wpas_hidl_unregister_interface(struct wpa_supplicant *wpa_s);
+int wpas_hidl_register_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_hidl_unregister_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_hidl_notify_state_changed(struct wpa_supplicant *wpa_s);
+int wpas_hidl_notify_network_request(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+    enum wpa_ctrl_req_type rtype, const char *default_txt);
+void wpas_hidl_notify_anqp_query_done(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
+    const struct wpa_bss_anqp *anqp);
+void wpas_hidl_notify_hs20_icon_query_done(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *file_name,
+    const u8 *image, u32 image_length);
+void wpas_hidl_notify_hs20_rx_subscription_remediation(
+    struct wpa_supplicant *wpa_s, const char *url, u8 osu_method);
+void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
+    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url);
+void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
+void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s);
+void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s);
+void wpas_hidl_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_hidl_notify_wps_event_fail(
+    struct wpa_supplicant *wpa_s, uint8_t *peer_macaddr, uint16_t config_error,
+    uint16_t error_indication);
+void wpas_hidl_notify_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_hidl_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s);
+void wpas_hidl_notify_p2p_device_found(
+    struct wpa_supplicant *wpa_s, const u8 *addr,
+    const struct p2p_peer_info *info, const u8 *peer_wfd_device_info,
+    u8 peer_wfd_device_info_len);
+void wpas_hidl_notify_p2p_device_lost(
+    struct wpa_supplicant *wpa_s, const u8 *p2p_device_addr);
+void wpas_hidl_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s);
+void wpas_hidl_notify_p2p_go_neg_req(
+    struct wpa_supplicant *wpa_s, const u8 *src_addr, u16 dev_passwd_id,
+    u8 go_intent);
+void wpas_hidl_notify_p2p_go_neg_completed(
+    struct wpa_supplicant *wpa_s, const struct p2p_go_neg_results *res);
+void wpas_hidl_notify_p2p_group_formation_failure(
+    struct wpa_supplicant *wpa_s, const char *reason);
+void wpas_hidl_notify_p2p_group_started(
+    struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, int persistent,
+    int client);
+void wpas_hidl_notify_p2p_group_removed(
+    struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid,
+    const char *role);
+void wpas_hidl_notify_p2p_invitation_received(
+    struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *go_dev_addr,
+    const u8 *bssid, int id, int op_freq);
+void wpas_hidl_notify_p2p_invitation_result(
+    struct wpa_supplicant *wpa_s, int status, const u8 *bssid);
+void wpas_hidl_notify_p2p_provision_discovery(
+    struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
+    enum p2p_prov_disc_status status, u16 config_methods,
+    unsigned int generated_pin);
+void wpas_hidl_notify_p2p_sd_response(
+    struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
+    const u8 *tlvs, size_t tlvs_len);
+void wpas_hidl_notify_ap_sta_authorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr);
+void wpas_hidl_notify_ap_sta_deauthorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr);
+#else   // CONFIG_CTRL_IFACE_HIDL
+static inline int wpas_hidl_register_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+static inline int wpas_hidl_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+static inline int wpas_hidl_register_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	return 0;
+}
+static inline int wpas_hidl_unregister_network(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	return 0;
+}
+static inline int wpas_hidl_notify_state_changed(struct wpa_supplicant *wpa_s)
+{
+	return 0;
+}
+static inline int wpas_hidl_notify_network_request(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+    enum wpa_ctrl_req_type rtype, const char *default_txt)
+{
+	return 0;
+}
+static void wpas_hidl_notify_anqp_query_done(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
+    const struct wpa_bss_anqp *anqp)
+{
+}
+static void wpas_hidl_notify_hs20_icon_query_done(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *file_name,
+    const u8 *image, u32 image_length)
+{
+}
+static void wpas_hidl_notify_hs20_rx_subscription_remediation(
+    struct wpa_supplicant *wpa_s, const char *url, u8 osu_method)
+{
+}
+static void wpas_hidl_notify_hs20_rx_deauth_imminent_notice(
+    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
+{
+}
+static void wpas_hidl_notify_disconnect_reason(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_assoc_reject(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_auth_timeout(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_wps_event_fail(
+    struct wpa_supplicant *wpa_s, uint8_t *peer_macaddr, uint16_t config_error,
+    uint16_t error_indication)
+{
+}
+static void wpas_hidl_notify_bssid_changed(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_wps_event_success(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_wps_event_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+}
+static void wpas_hidl_notify_p2p_device_found(
+    struct wpa_supplicant *wpa_s, const u8 *addr,
+    const struct p2p_peer_info *info, const u8 *peer_wfd_device_info,
+    u8 peer_wfd_device_info_len);
+{
+}
+static void wpas_hidl_notify_p2p_device_lost(
+    struct wpa_supplicant *wpa_s, const u8 *p2p_device_addr)
+{
+}
+static void wpas_hidl_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s) {}
+static void wpas_hidl_notify_p2p_go_neg_req(
+    struct wpa_supplicant *wpa_s, const u8 *src_addr, u16 dev_passwd_id,
+    u8 go_intent)
+{
+}
+static void wpas_hidl_notify_p2p_go_neg_completed(
+    struct wpa_supplicant *wpa_s, const struct p2p_go_neg_results *res)
+{
+}
+static void wpas_hidl_notify_p2p_group_formation_failure(
+    struct wpa_supplicant *wpa_s, const char *reason)
+{
+}
+static void wpas_hidl_notify_p2p_group_started(
+    struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, int persistent,
+    int client)
+{
+}
+static void wpas_hidl_notify_p2p_group_removed(
+    struct wpa_supplicant *wpa_s, const struct wpa_ssid *ssid, const char *role)
+{
+}
+static void wpas_hidl_notify_p2p_invitation_received(
+    struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *go_dev_addr,
+    const u8 *bssid, int id, int op_freq)
+{
+}
+static void wpas_hidl_notify_p2p_invitation_result(
+    struct wpa_supplicant *wpa_s, int status, const u8 *bssid)
+{
+}
+static void wpas_hidl_notify_p2p_provision_discovery(
+    struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
+    enum p2p_prov_disc_status status, u16 config_methods,
+    unsigned int generated_pin)
+{
+}
+static void wpas_hidl_notify_p2p_sd_response(
+    struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
+    const u8 *tlvs, size_t tlvs_len)
+{
+}
+static void wpas_hidl_notify_ap_sta_authorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+{
+}
+static void wpas_hidl_notify_ap_sta_deauthorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+{
+}
+#endif  // CONFIG_CTRL_IFACE_HIDL
+
+#ifdef _cplusplus
+}
+#endif  // _cplusplus
+
+#endif  // WPA_SUPPLICANT_HIDL_HIDL_H
diff --git a/wpa_supplicant/hidl/1.0/hidl_constants.h b/wpa_supplicant/hidl/1.0/hidl_constants.h
new file mode 100644
index 0000000..988a590
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/hidl_constants.h
@@ -0,0 +1,21 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_HIDL_CONSTANTS_H
+#define WPA_SUPPLICANT_HIDL_HIDL_CONSTANTS_H
+
+namespace wpa_supplicant_hidl {
+namespace hidl_constants {
+
+extern const char kServiceName[];
+
+} /* namespace hidl_constants */
+} /* namespace wpa_supplicant_hidl */
+
+#endif /* WPA_SUPPLICANT_HIDL_HIDL_CONSTANTS_H */
diff --git a/wpa_supplicant/hidl/1.0/hidl_i.h b/wpa_supplicant/hidl/1.0/hidl_i.h
new file mode 100644
index 0000000..c7a0142
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/hidl_i.h
@@ -0,0 +1,28 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HIDL_I_H
+#define HIDL_I_H
+
+#ifdef _cplusplus
+extern "C" {
+#endif  // _cplusplus
+
+struct wpas_hidl_priv
+{
+	int hidl_fd;
+	struct wpa_global *global;
+	void *hidl_manager;
+};
+
+#ifdef _cplusplus
+}
+#endif  // _cplusplus
+
+#endif  // HIDL_I_H
diff --git a/wpa_supplicant/hidl/1.0/hidl_manager.cpp b/wpa_supplicant/hidl/1.0/hidl_manager.cpp
new file mode 100644
index 0000000..72b53a4
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/hidl_manager.cpp
@@ -0,0 +1,1773 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <algorithm>
+#include <regex>
+
+#include "hidl_manager.h"
+#include "misc_utils.h"
+
+extern "C" {
+#include "src/eap_common/eap_sim_common.h"
+}
+
+namespace {
+using android::hardware::hidl_array;
+
+constexpr uint8_t kWfdDeviceInfoLen = 6;
+// GSM-AUTH:<RAND1>:<RAND2>[:<RAND3>]
+constexpr char kGsmAuthRegex2[] = "GSM-AUTH:([0-9a-f]+):([0-9a-f]+)";
+constexpr char kGsmAuthRegex3[] =
+    "GSM-AUTH:([0-9a-f]+):([0-9a-f]+):([0-9a-f]+)";
+// UMTS-AUTH:<RAND>:<AUTN>
+constexpr char kUmtsAuthRegex[] = "UMTS-AUTH:([0-9a-f]+):([0-9a-f]+)";
+constexpr size_t kGsmRandLenBytes = GSM_RAND_LEN;
+constexpr size_t kUmtsRandLenBytes = EAP_AKA_RAND_LEN;
+constexpr size_t kUmtsAutnLenBytes = EAP_AKA_AUTN_LEN;
+constexpr u8 kZeroBssid[6] = {0, 0, 0, 0, 0, 0};
+/**
+ * Check if the provided |wpa_supplicant| structure represents a P2P iface or
+ * not.
+ */
+constexpr bool isP2pIface(const struct wpa_supplicant *wpa_s)
+{
+	return (wpa_s->global->p2p_init_wpa_s == wpa_s);
+}
+
+/**
+ * Creates a unique key for the network using the provided |ifname| and
+ * |network_id| to be used in the internal map of |ISupplicantNetwork| objects.
+ * This is of the form |ifname|_|network_id|. For ex: "wlan0_1".
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ */
+const std::string getNetworkObjectMapKey(
+    const std::string &ifname, int network_id)
+{
+	return ifname + "_" + std::to_string(network_id);
+}
+
+/**
+ * Add callback to the corresponding list after linking to death on the
+ * corresponding hidl object reference.
+ */
+template <class CallbackType>
+int registerForDeathAndAddCallbackHidlObjectToList(
+    const android::sp<CallbackType> &callback,
+    const std::function<void(const android::sp<CallbackType> &)>
+	&on_hidl_died_fctor,
+    std::vector<android::sp<CallbackType>> &callback_list)
+{
+#if 0   // TODO(b/31632518): HIDL object death notifications.
+	auto death_notifier = new CallbackObjectDeathNotifier<CallbackType>(
+	    callback, on_hidl_died_fctor);
+	// Use the |callback.get()| as cookie so that we don't need to
+	// store a reference to this |CallbackObjectDeathNotifier| instance
+	// to use in |unlinkToDeath| later.
+	// NOTE: This may cause an immediate callback if the object is already
+	// dead, so add it to the list before we register for callback!
+	if (android::hardware::IInterface::asBinder(callback)->linkToDeath(
+		death_notifier, callback.get()) != android::OK) {
+		wpa_printf(
+		    MSG_ERROR,
+		    "Error registering for death notification for "
+		    "supplicant callback object");
+		callback_list.erase(
+		    std::remove(
+			callback_list.begin(), callback_list.end(), callback),
+		    callback_list.end());
+		return 1;
+	}
+#endif  // TODO(b/31632518): HIDL object death notifications.
+	callback_list.push_back(callback);
+	return 0;
+}
+
+template <class ObjectType>
+int addHidlObjectToMap(
+    const std::string &key, const android::sp<ObjectType> object,
+    std::map<const std::string, android::sp<ObjectType>> &object_map)
+{
+	// Return failure if we already have an object for that |key|.
+	if (object_map.find(key) != object_map.end())
+		return 1;
+	object_map[key] = object;
+	if (!object_map[key].get())
+		return 1;
+	return 0;
+}
+
+template <class ObjectType>
+int removeHidlObjectFromMap(
+    const std::string &key,
+    std::map<const std::string, android::sp<ObjectType>> &object_map)
+{
+	// Return failure if we dont have an object for that |key|.
+	const auto &object_iter = object_map.find(key);
+	if (object_iter == object_map.end())
+		return 1;
+	object_iter->second->invalidate();
+	object_map.erase(object_iter);
+	return 0;
+}
+
+template <class CallbackType>
+int addIfaceCallbackHidlObjectToMap(
+    const std::string &ifname, const android::sp<CallbackType> &callback,
+    const std::function<void(const android::sp<CallbackType> &)>
+	&on_hidl_died_fctor,
+    std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	if (ifname.empty())
+		return 1;
+
+	auto iface_callback_map_iter = callbacks_map.find(ifname);
+	if (iface_callback_map_iter == callbacks_map.end())
+		return 1;
+	auto &iface_callback_list = iface_callback_map_iter->second;
+
+	// Register for death notification before we add it to our list.
+	return registerForDeathAndAddCallbackHidlObjectToList<CallbackType>(
+	    callback, on_hidl_died_fctor, iface_callback_list);
+}
+
+template <class CallbackType>
+int addNetworkCallbackHidlObjectToMap(
+    const std::string &ifname, int network_id,
+    const android::sp<CallbackType> &callback,
+    const std::function<void(const android::sp<CallbackType> &)>
+	&on_hidl_died_fctor,
+    std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	if (ifname.empty() || network_id < 0)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(ifname, network_id);
+	auto network_callback_map_iter = callbacks_map.find(network_key);
+	if (network_callback_map_iter == callbacks_map.end())
+		return 1;
+	auto &network_callback_list = network_callback_map_iter->second;
+
+	// Register for death notification before we add it to our list.
+	return registerForDeathAndAddCallbackHidlObjectToList<CallbackType>(
+	    callback, on_hidl_died_fctor, network_callback_list);
+}
+
+template <class CallbackType>
+int removeAllIfaceCallbackHidlObjectsFromMap(
+    const std::string &ifname,
+    std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	auto iface_callback_map_iter = callbacks_map.find(ifname);
+	if (iface_callback_map_iter == callbacks_map.end())
+		return 1;
+#if 0   // TODO(b/31632518): HIDL object death notifications.
+	const auto &iface_callback_list = iface_callback_map_iter->second;
+	for (const auto &callback : iface_callback_list) {
+		if (android::hardware::IInterface::asBinder(callback)
+			->unlinkToDeath(nullptr, callback.get()) !=
+		    android::OK) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Error deregistering for death notification for "
+			    "iface callback object");
+		}
+	}
+#endif  // TODO(b/31632518): HIDL object death notifications.
+	callbacks_map.erase(iface_callback_map_iter);
+	return 0;
+}
+
+template <class CallbackType>
+int removeAllNetworkCallbackHidlObjectsFromMap(
+    const std::string &network_key,
+    std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	auto network_callback_map_iter = callbacks_map.find(network_key);
+	if (network_callback_map_iter == callbacks_map.end())
+		return 1;
+#if 0   // TODO(b/31632518): HIDL object death notifications.
+	const auto &network_callback_list = network_callback_map_iter->second;
+	for (const auto &callback : network_callback_list) {
+		if (android::hardware::IInterface::asBinder(callback)
+			->unlinkToDeath(nullptr, callback.get()) !=
+		    android::OK) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Error deregistering for death "
+			    "notification for "
+			    "network callback object");
+		}
+	}
+#endif  // TODO(b/31632518): HIDL object death notifications.
+	callbacks_map.erase(network_callback_map_iter);
+	return 0;
+}
+
+template <class CallbackType>
+void removeIfaceCallbackHidlObjectFromMap(
+    const std::string &ifname, const android::sp<CallbackType> &callback,
+    std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	if (ifname.empty())
+		return;
+
+	auto iface_callback_map_iter = callbacks_map.find(ifname);
+	if (iface_callback_map_iter == callbacks_map.end())
+		return;
+
+	auto &iface_callback_list = iface_callback_map_iter->second;
+	iface_callback_list.erase(
+	    std::remove(
+		iface_callback_list.begin(), iface_callback_list.end(),
+		callback),
+	    iface_callback_list.end());
+}
+
+template <class CallbackType>
+void removeNetworkCallbackHidlObjectFromMap(
+    const std::string &ifname, int network_id,
+    const android::sp<CallbackType> &callback,
+    std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	if (ifname.empty() || network_id < 0)
+		return;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(ifname, network_id);
+
+	auto network_callback_map_iter = callbacks_map.find(network_key);
+	if (network_callback_map_iter == callbacks_map.end())
+		return;
+
+	auto &network_callback_list = network_callback_map_iter->second;
+	network_callback_list.erase(
+	    std::remove(
+		network_callback_list.begin(), network_callback_list.end(),
+		callback),
+	    network_callback_list.end());
+}
+
+template <class CallbackType>
+void callWithEachIfaceCallback(
+    const std::string &ifname,
+    const std::function<
+	android::hardware::Return<void>(android::sp<CallbackType>)> &method,
+    const std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	if (ifname.empty())
+		return;
+
+	auto iface_callback_map_iter = callbacks_map.find(ifname);
+	if (iface_callback_map_iter == callbacks_map.end())
+		return;
+	const auto &iface_callback_list = iface_callback_map_iter->second;
+	for (const auto &callback : iface_callback_list) {
+		if (!method(callback).isOk()) {
+			wpa_printf(
+			    MSG_ERROR, "Failed to invoke HIDL iface callback");
+		}
+	}
+}
+
+template <class CallbackType>
+void callWithEachNetworkCallback(
+    const std::string &ifname, int network_id,
+    const std::function<
+	android::hardware::Return<void>(android::sp<CallbackType>)> &method,
+    const std::map<const std::string, std::vector<android::sp<CallbackType>>>
+	&callbacks_map)
+{
+	if (ifname.empty() || network_id < 0)
+		return;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(ifname, network_id);
+	auto network_callback_map_iter = callbacks_map.find(network_key);
+	if (network_callback_map_iter == callbacks_map.end())
+		return;
+	const auto &network_callback_list = network_callback_map_iter->second;
+	for (const auto &callback : network_callback_list) {
+		if (!method(callback).isOk()) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to invoke HIDL network callback");
+		}
+	}
+}
+
+int parseGsmAuthNetworkRequest(
+    const std::string &params_str,
+    std::vector<hidl_array<uint8_t, kGsmRandLenBytes>> *out_rands)
+{
+	std::smatch matches;
+	std::regex params_gsm_regex2(kGsmAuthRegex2);
+	std::regex params_gsm_regex3(kGsmAuthRegex3);
+	if (!std::regex_match(params_str, matches, params_gsm_regex3) &&
+	    !std::regex_match(params_str, matches, params_gsm_regex2)) {
+		return 1;
+	}
+	for (uint32_t i = 1; i < matches.size(); i++) {
+		hidl_array<uint8_t, kGsmRandLenBytes> rand;
+		const auto &match = matches[i];
+		WPA_ASSERT(match.size() >= 2 * rand.size());
+		if (hexstr2bin(match.str().c_str(), rand.data(), rand.size())) {
+			wpa_printf(
+			    MSG_ERROR, "Failed to parse GSM auth params");
+			return 1;
+		}
+		out_rands->push_back(rand);
+	}
+	return 0;
+}
+
+int parseUmtsAuthNetworkRequest(
+    const std::string &params_str,
+    hidl_array<uint8_t, kUmtsRandLenBytes> *out_rand,
+    hidl_array<uint8_t, kUmtsAutnLenBytes> *out_autn)
+{
+	std::smatch matches;
+	std::regex params_umts_regex(kUmtsAuthRegex);
+	if (!std::regex_match(params_str, matches, params_umts_regex)) {
+		return 1;
+	}
+	WPA_ASSERT(matches[1].size() >= 2 * out_rand->size());
+	if (hexstr2bin(
+		matches[1].str().c_str(), out_rand->data(), out_rand->size())) {
+		wpa_printf(MSG_ERROR, "Failed to parse UMTS auth params");
+		return 1;
+	}
+	WPA_ASSERT(matches[2].size() >= 2 * out_autn->size());
+	if (hexstr2bin(
+		matches[2].str().c_str(), out_autn->data(), out_autn->size())) {
+		wpa_printf(MSG_ERROR, "Failed to parse UMTS auth params");
+		return 1;
+	}
+	return 0;
+}
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+
+HidlManager *HidlManager::instance_ = NULL;
+
+HidlManager *HidlManager::getInstance()
+{
+	if (!instance_)
+		instance_ = new HidlManager();
+	return instance_;
+}
+
+void HidlManager::destroyInstance()
+{
+	if (instance_)
+		delete instance_;
+	instance_ = NULL;
+}
+
+int HidlManager::registerHidlService(struct wpa_global *global)
+{
+	// Create the main hidl service object and register it.
+	supplicant_object_ = new Supplicant(global);
+	if (supplicant_object_->registerAsService() != android::NO_ERROR) {
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * Register an interface to hidl manager.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::registerInterface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return 1;
+
+	if (isP2pIface(wpa_s)) {
+		if (addHidlObjectToMap<P2pIface>(
+			wpa_s->ifname,
+			new P2pIface(wpa_s->global, wpa_s->ifname),
+			p2p_iface_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to register P2P interface with HIDL "
+			    "control: %s",
+			    wpa_s->ifname);
+			return 1;
+		}
+		p2p_iface_callbacks_map_[wpa_s->ifname] =
+		    std::vector<android::sp<ISupplicantP2pIfaceCallback>>();
+	} else {
+		if (addHidlObjectToMap<StaIface>(
+			wpa_s->ifname,
+			new StaIface(wpa_s->global, wpa_s->ifname),
+			sta_iface_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to register STA interface with HIDL "
+			    "control: %s",
+			    wpa_s->ifname);
+			return 1;
+		}
+		sta_iface_callbacks_map_[wpa_s->ifname] =
+		    std::vector<android::sp<ISupplicantStaIfaceCallback>>();
+	}
+
+	// Invoke the |onInterfaceCreated| method on all registered callbacks.
+	callWithEachSupplicantCallback(std::bind(
+	    &ISupplicantCallback::onInterfaceCreated, std::placeholders::_1,
+	    wpa_s->ifname));
+	return 0;
+}
+
+/**
+ * Unregister an interface from hidl manager.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::unregisterInterface(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return 1;
+
+	if (isP2pIface(wpa_s)) {
+		if (removeHidlObjectFromMap(
+			wpa_s->ifname, p2p_iface_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to unregister P2P interface with HIDL "
+			    "control: %s",
+			    wpa_s->ifname);
+			return 1;
+		}
+		if (removeAllIfaceCallbackHidlObjectsFromMap(
+			wpa_s->ifname, p2p_iface_callbacks_map_)) {
+			return 1;
+		}
+	} else {
+		if (removeHidlObjectFromMap(
+			wpa_s->ifname, sta_iface_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to unregister STA interface with HIDL "
+			    "control: %s",
+			    wpa_s->ifname);
+			return 1;
+		}
+		if (removeAllIfaceCallbackHidlObjectsFromMap(
+			wpa_s->ifname, sta_iface_callbacks_map_)) {
+			return 1;
+		}
+	}
+
+	// Invoke the |onInterfaceRemoved| method on all registered callbacks.
+	callWithEachSupplicantCallback(std::bind(
+	    &ISupplicantCallback::onInterfaceRemoved, std::placeholders::_1,
+	    wpa_s->ifname));
+	return 0;
+}
+
+/**
+ * Register a network to hidl manager.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is added.
+ * @param ssid |wpa_ssid| struct corresponding to the network being added.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::registerNetwork(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s || !ssid)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(wpa_s->ifname, ssid->id);
+
+	if (isP2pIface(wpa_s)) {
+		if (addHidlObjectToMap<P2pNetwork>(
+			network_key,
+			new P2pNetwork(wpa_s->global, wpa_s->ifname, ssid->id),
+			p2p_network_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to register P2P network with HIDL "
+			    "control: %d",
+			    ssid->id);
+			return 1;
+		}
+		p2p_network_callbacks_map_[network_key] =
+		    std::vector<android::sp<ISupplicantP2pNetworkCallback>>();
+		// Invoke the |onNetworkAdded| method on all registered
+		// callbacks.
+		callWithEachP2pIfaceCallback(
+		    wpa_s->ifname,
+		    std::bind(
+			&ISupplicantP2pIfaceCallback::onNetworkAdded,
+			std::placeholders::_1, ssid->id));
+	} else {
+		if (addHidlObjectToMap<StaNetwork>(
+			network_key,
+			new StaNetwork(wpa_s->global, wpa_s->ifname, ssid->id),
+			sta_network_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to register STA network with HIDL "
+			    "control: %d",
+			    ssid->id);
+			return 1;
+		}
+		sta_network_callbacks_map_[network_key] =
+		    std::vector<android::sp<ISupplicantStaNetworkCallback>>();
+		// Invoke the |onNetworkAdded| method on all registered
+		// callbacks.
+		callWithEachStaIfaceCallback(
+		    wpa_s->ifname,
+		    std::bind(
+			&ISupplicantStaIfaceCallback::onNetworkAdded,
+			std::placeholders::_1, ssid->id));
+	}
+	return 0;
+}
+
+/**
+ * Unregister a network from hidl manager.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is added.
+ * @param ssid |wpa_ssid| struct corresponding to the network being added.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::unregisterNetwork(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+	if (!wpa_s || !ssid)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(wpa_s->ifname, ssid->id);
+
+	if (isP2pIface(wpa_s)) {
+		if (removeHidlObjectFromMap(
+			network_key, p2p_network_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to unregister P2P network with HIDL "
+			    "control: %d",
+			    ssid->id);
+			return 1;
+		}
+		if (removeAllNetworkCallbackHidlObjectsFromMap(
+			network_key, p2p_network_callbacks_map_))
+			return 1;
+
+		// Invoke the |onNetworkRemoved| method on all registered
+		// callbacks.
+		callWithEachP2pIfaceCallback(
+		    wpa_s->ifname,
+		    std::bind(
+			&ISupplicantP2pIfaceCallback::onNetworkRemoved,
+			std::placeholders::_1, ssid->id));
+	} else {
+		if (removeHidlObjectFromMap(
+			network_key, sta_network_object_map_)) {
+			wpa_printf(
+			    MSG_ERROR,
+			    "Failed to unregister STA network with HIDL "
+			    "control: %d",
+			    ssid->id);
+			return 1;
+		}
+		if (removeAllNetworkCallbackHidlObjectsFromMap(
+			network_key, sta_network_callbacks_map_))
+			return 1;
+
+		// Invoke the |onNetworkRemoved| method on all registered
+		// callbacks.
+		callWithEachStaIfaceCallback(
+		    wpa_s->ifname,
+		    std::bind(
+			&ISupplicantStaIfaceCallback::onNetworkRemoved,
+			std::placeholders::_1, ssid->id));
+	}
+	return 0;
+}
+
+/**
+ * Notify all listeners about any state changes on a particular interface.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the state change event occured.
+ */
+int HidlManager::notifyStateChange(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return 1;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return 1;
+
+	// Invoke the |onStateChanged| method on all registered callbacks.
+	uint32_t hidl_network_id = UINT32_MAX;
+	std::vector<uint8_t> hidl_ssid;
+	if (wpa_s->current_ssid) {
+		hidl_network_id = wpa_s->current_ssid->id;
+		hidl_ssid.assign(
+		    wpa_s->current_ssid->ssid,
+		    wpa_s->current_ssid->ssid + wpa_s->current_ssid->ssid_len);
+	}
+	uint8_t *bssid;
+	// wpa_supplicant sets the |pending_bssid| field when it starts a
+	// connection. Only after association state does it update the |bssid|
+	// field. So, in the HIDL callback send the appropriate bssid.
+	if (wpa_s->wpa_state <= WPA_ASSOCIATED) {
+		bssid = wpa_s->pending_bssid;
+	} else {
+		bssid = wpa_s->bssid;
+	}
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname, std::bind(
+			       &ISupplicantStaIfaceCallback::onStateChanged,
+			       std::placeholders::_1,
+			       static_cast<ISupplicantStaIfaceCallback::State>(
+				   wpa_s->wpa_state),
+			       bssid, hidl_network_id, hidl_ssid));
+	return 0;
+}
+
+/**
+ * Notify all listeners about a request on a particular network.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is present.
+ * @param ssid |wpa_ssid| struct corresponding to the network.
+ * @param type type of request.
+ * @param param addition params associated with the request.
+ */
+int HidlManager::notifyNetworkRequest(
+    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int type,
+    const char *param)
+{
+	if (!wpa_s || !ssid)
+		return 1;
+
+	const std::string network_key =
+	    getNetworkObjectMapKey(wpa_s->ifname, ssid->id);
+	if (sta_network_object_map_.find(network_key) ==
+	    sta_network_object_map_.end())
+		return 1;
+
+	if (type == WPA_CTRL_REQ_EAP_IDENTITY) {
+		callWithEachStaNetworkCallback(
+		    wpa_s->ifname, ssid->id,
+		    std::bind(
+			&ISupplicantStaNetworkCallback::
+			    onNetworkEapIdentityRequest,
+			std::placeholders::_1));
+		return 0;
+	}
+	if (type == WPA_CTRL_REQ_SIM) {
+		std::vector<hidl_array<uint8_t, 16>> gsm_rands;
+		hidl_array<uint8_t, 16> umts_rand;
+		hidl_array<uint8_t, 16> umts_autn;
+		if (!parseGsmAuthNetworkRequest(param, &gsm_rands)) {
+			ISupplicantStaNetworkCallback::
+			    NetworkRequestEapSimGsmAuthParams hidl_params;
+			hidl_params.rands = gsm_rands;
+			callWithEachStaNetworkCallback(
+			    wpa_s->ifname, ssid->id,
+			    std::bind(
+				&ISupplicantStaNetworkCallback::
+				    onNetworkEapSimGsmAuthRequest,
+				std::placeholders::_1, hidl_params));
+			return 0;
+		}
+		if (!parseUmtsAuthNetworkRequest(
+			param, &umts_rand, &umts_autn)) {
+			ISupplicantStaNetworkCallback::
+			    NetworkRequestEapSimUmtsAuthParams hidl_params;
+			hidl_params.rand = umts_rand;
+			hidl_params.autn = umts_autn;
+			callWithEachStaNetworkCallback(
+			    wpa_s->ifname, ssid->id,
+			    std::bind(
+				&ISupplicantStaNetworkCallback::
+				    onNetworkEapSimUmtsAuthRequest,
+				std::placeholders::_1, hidl_params));
+			return 0;
+		}
+	}
+	return 1;
+}
+
+/**
+ * Notify all listeners about the end of an ANQP query.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ * @param bssid BSSID of the access point.
+ * @param result Result of the operation ("SUCCESS" or "FAILURE").
+ * @param anqp |wpa_bss_anqp| ANQP data fetched.
+ */
+void HidlManager::notifyAnqpQueryDone(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
+    const struct wpa_bss_anqp *anqp)
+{
+	if (!wpa_s || !bssid || !result || !anqp)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	ISupplicantStaIfaceCallback::AnqpData hidl_anqp_data;
+	ISupplicantStaIfaceCallback::Hs20AnqpData hidl_hs20_anqp_data;
+	if (std::string(result) == "SUCCESS") {
+		hidl_anqp_data.venueName =
+		    misc_utils::convertWpaBufToVector(anqp->venue_name);
+		hidl_anqp_data.roamingConsortium =
+		    misc_utils::convertWpaBufToVector(anqp->roaming_consortium);
+		hidl_anqp_data.ipAddrTypeAvailability =
+		    misc_utils::convertWpaBufToVector(
+			anqp->ip_addr_type_availability);
+		hidl_anqp_data.naiRealm =
+		    misc_utils::convertWpaBufToVector(anqp->nai_realm);
+		hidl_anqp_data.anqp3gppCellularNetwork =
+		    misc_utils::convertWpaBufToVector(anqp->anqp_3gpp);
+		hidl_anqp_data.domainName =
+		    misc_utils::convertWpaBufToVector(anqp->domain_name);
+
+		hidl_hs20_anqp_data.operatorFriendlyName =
+		    misc_utils::convertWpaBufToVector(
+			anqp->hs20_operator_friendly_name);
+		hidl_hs20_anqp_data.wanMetrics =
+		    misc_utils::convertWpaBufToVector(anqp->hs20_wan_metrics);
+		hidl_hs20_anqp_data.connectionCapability =
+		    misc_utils::convertWpaBufToVector(
+			anqp->hs20_connection_capability);
+		hidl_hs20_anqp_data.osuProvidersList =
+		    misc_utils::convertWpaBufToVector(
+			anqp->hs20_osu_providers_list);
+	}
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname, std::bind(
+			       &ISupplicantStaIfaceCallback::onAnqpQueryDone,
+			       std::placeholders::_1, bssid, hidl_anqp_data,
+			       hidl_hs20_anqp_data));
+}
+
+/**
+ * Notify all listeners about the end of an HS20 icon query.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ * @param bssid BSSID of the access point.
+ * @param file_name Name of the icon file.
+ * @param image Raw bytes of the icon file.
+ * @param image_length Size of the the icon file.
+ */
+void HidlManager::notifyHs20IconQueryDone(
+    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *file_name,
+    const u8 *image, u32 image_length)
+{
+	if (!wpa_s || !bssid || !file_name || !image)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onHs20IconQueryDone,
+		std::placeholders::_1, bssid, file_name,
+		std::vector<uint8_t>(image, image + image_length)));
+}
+
+/**
+ * Notify all listeners about the reception of HS20 subscription
+ * remediation notification from the server.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ * @param url URL of the server.
+ * @param osu_method OSU method (OMA_DM or SOAP_XML_SPP).
+ */
+void HidlManager::notifyHs20RxSubscriptionRemediation(
+    struct wpa_supplicant *wpa_s, const char *url, u8 osu_method)
+{
+	if (!wpa_s || !url)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	ISupplicantStaIfaceCallback::OsuMethod hidl_osu_method = {};
+	if (osu_method & 0x1) {
+		hidl_osu_method =
+		    ISupplicantStaIfaceCallback::OsuMethod::OMA_DM;
+	} else if (osu_method & 0x2) {
+		hidl_osu_method =
+		    ISupplicantStaIfaceCallback::OsuMethod::SOAP_XML_SPP;
+	}
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onHs20SubscriptionRemediation,
+		std::placeholders::_1, wpa_s->bssid, hidl_osu_method, url));
+}
+
+/**
+ * Notify all listeners about the reception of HS20 immient deauth
+ * notification from the server.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface.
+ * @param code Deauth reason code sent from server.
+ * @param reauth_delay Reauthentication delay in seconds sent from server.
+ * @param url URL of the server.
+ */
+void HidlManager::notifyHs20RxDeauthImminentNotice(
+    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay, const char *url)
+{
+	if (!wpa_s || !url)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onHs20DeauthImminentNotice,
+		std::placeholders::_1, wpa_s->bssid, code, reauth_delay, url));
+}
+
+/**
+ * Notify all listeners about the reason code for disconnection from the
+ * currently connected network.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is present.
+ */
+void HidlManager::notifyDisconnectReason(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	const u8 *bssid = wpa_s->bssid;
+	if (is_zero_ether_addr(bssid)) {
+		bssid = wpa_s->pending_bssid;
+	}
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onDisconnected,
+		std::placeholders::_1, bssid, wpa_s->disconnect_reason < 0,
+		static_cast<ISupplicantStaIfaceCallback::ReasonCode>(
+		    wpa_s->disconnect_reason)));
+}
+
+/**
+ * Notify all listeners about association reject from the access point to which
+ * we are attempting to connect.
+ *
+ * @param wpa_s |wpa_supplicant| struct corresponding to the interface on which
+ * the network is present.
+ */
+void HidlManager::notifyAssocReject(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	const u8 *bssid = wpa_s->bssid;
+	if (is_zero_ether_addr(bssid)) {
+		bssid = wpa_s->pending_bssid;
+	}
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onAssociationRejected,
+		std::placeholders::_1, bssid,
+		static_cast<ISupplicantStaIfaceCallback::StatusCode>(
+		    wpa_s->assoc_status_code),
+		wpa_s->assoc_timed_out == 1));
+}
+
+void HidlManager::notifyAuthTimeout(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	const std::string ifname(wpa_s->ifname);
+	if (sta_iface_object_map_.find(ifname) == sta_iface_object_map_.end())
+		return;
+
+	const u8 *bssid = wpa_s->bssid;
+	if (is_zero_ether_addr(bssid)) {
+		bssid = wpa_s->pending_bssid;
+	}
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onAuthenticationTimeout,
+		std::placeholders::_1, bssid));
+}
+
+void HidlManager::notifyBssidChanged(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	const std::string ifname(wpa_s->ifname);
+	if (sta_iface_object_map_.find(ifname) == sta_iface_object_map_.end())
+		return;
+
+	// wpa_supplicant does not explicitly give us the reason for bssid
+	// change, but we figure that out from what is set out of |wpa_s->bssid|
+	// & |wpa_s->pending_bssid|.
+	const u8 *bssid;
+	ISupplicantStaIfaceCallback::BssidChangeReason reason;
+	if (is_zero_ether_addr(wpa_s->bssid) &&
+	    !is_zero_ether_addr(wpa_s->pending_bssid)) {
+		bssid = wpa_s->pending_bssid;
+		reason =
+		    ISupplicantStaIfaceCallback::BssidChangeReason::ASSOC_START;
+	} else if (
+	    !is_zero_ether_addr(wpa_s->bssid) &&
+	    is_zero_ether_addr(wpa_s->pending_bssid)) {
+		bssid = wpa_s->bssid;
+		reason = ISupplicantStaIfaceCallback::BssidChangeReason::
+		    ASSOC_COMPLETE;
+	} else if (
+	    is_zero_ether_addr(wpa_s->bssid) &&
+	    is_zero_ether_addr(wpa_s->pending_bssid)) {
+		bssid = wpa_s->pending_bssid;
+		reason =
+		    ISupplicantStaIfaceCallback::BssidChangeReason::DISASSOC;
+	} else {
+		wpa_printf(MSG_ERROR, "Unknown bssid change reason");
+		return;
+	}
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname, std::bind(
+			       &ISupplicantStaIfaceCallback::onBssidChanged,
+			       std::placeholders::_1, reason, bssid));
+}
+
+void HidlManager::notifyWpsEventFail(
+    struct wpa_supplicant *wpa_s, uint8_t *peer_macaddr, uint16_t config_error,
+    uint16_t error_indication)
+{
+	if (!wpa_s || !peer_macaddr)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onWpsEventFail,
+		std::placeholders::_1, peer_macaddr,
+		static_cast<ISupplicantStaIfaceCallback::WpsConfigError>(
+		    config_error),
+		static_cast<ISupplicantStaIfaceCallback::WpsErrorIndication>(
+		    error_indication)));
+}
+
+void HidlManager::notifyWpsEventSuccess(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname, std::bind(
+			       &ISupplicantStaIfaceCallback::onWpsEventSuccess,
+			       std::placeholders::_1));
+}
+
+void HidlManager::notifyWpsEventPbcOverlap(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onWpsEventPbcOverlap,
+		std::placeholders::_1));
+}
+
+void HidlManager::notifyP2pDeviceFound(
+    struct wpa_supplicant *wpa_s, const u8 *addr,
+    const struct p2p_peer_info *info, const u8 *peer_wfd_device_info,
+    u8 peer_wfd_device_info_len)
+{
+	if (!wpa_s || !addr || !info)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	std::array<uint8_t, kWfdDeviceInfoLen> hidl_peer_wfd_device_info{};
+	if (peer_wfd_device_info) {
+		if (peer_wfd_device_info_len != kWfdDeviceInfoLen) {
+			wpa_printf(
+			    MSG_ERROR, "Unexpected WFD device info len: %d",
+			    peer_wfd_device_info_len);
+		} else {
+			os_memcpy(
+			    hidl_peer_wfd_device_info.data(),
+			    peer_wfd_device_info, kWfdDeviceInfoLen);
+		}
+	}
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onDeviceFound,
+		std::placeholders::_1, addr, info->p2p_device_addr,
+		info->pri_dev_type, info->device_name, info->config_methods,
+		info->dev_capab, info->group_capab, hidl_peer_wfd_device_info));
+}
+
+void HidlManager::notifyP2pDeviceLost(
+    struct wpa_supplicant *wpa_s, const u8 *p2p_device_addr)
+{
+	if (!wpa_s || !p2p_device_addr)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname, std::bind(
+			       &ISupplicantP2pIfaceCallback::onDeviceLost,
+			       std::placeholders::_1, p2p_device_addr));
+}
+
+void HidlManager::notifyP2pFindStopped(struct wpa_supplicant *wpa_s)
+{
+	if (!wpa_s)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname, std::bind(
+			       &ISupplicantP2pIfaceCallback::onFindStopped,
+			       std::placeholders::_1));
+}
+
+void HidlManager::notifyP2pGoNegReq(
+    struct wpa_supplicant *wpa_s, const u8 *src_addr, u16 dev_passwd_id,
+    u8 /* go_intent */)
+{
+	if (!wpa_s || !src_addr)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onGoNegotiationRequest,
+		std::placeholders::_1, src_addr,
+		static_cast<ISupplicantP2pIfaceCallback::WpsDevPasswordId>(
+		    dev_passwd_id)));
+}
+
+void HidlManager::notifyP2pGoNegCompleted(
+    struct wpa_supplicant *wpa_s, const struct p2p_go_neg_results *res)
+{
+	if (!wpa_s || !res)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onGoNegotiationCompleted,
+		std::placeholders::_1,
+		static_cast<ISupplicantP2pIfaceCallback::P2pStatusCode>(
+		    res->status)));
+}
+
+void HidlManager::notifyP2pGroupFormationFailure(
+    struct wpa_supplicant *wpa_s, const char *reason)
+{
+	if (!wpa_s || !reason)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onGroupFormationFailure,
+		std::placeholders::_1, reason));
+}
+
+void HidlManager::notifyP2pGroupStarted(
+    struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid, int persistent, int client)
+{
+	if (!wpa_group_s || !wpa_group_s->parent || !ssid)
+		return;
+
+	// For group notifications, need to use the parent iface for callbacks.
+	struct wpa_supplicant *wpa_s = wpa_group_s->parent;
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	uint32_t hidl_freq = wpa_group_s->current_bss
+				 ? wpa_group_s->current_bss->freq
+				 : wpa_group_s->assoc_freq;
+	std::array<uint8_t, 32> hidl_psk;
+	if (ssid->psk_set) {
+		os_memcpy(hidl_psk.data(), ssid->psk, 32);
+	}
+	bool hidl_is_go = (client == 0 ? true : false);
+	bool hidl_is_persistent = (persistent == 1 ? true : false);
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onGroupStarted,
+		std::placeholders::_1, wpa_group_s->ifname, hidl_is_go,
+		std::vector<uint8_t>{ssid->ssid, ssid->ssid + ssid->ssid_len},
+		hidl_freq, hidl_psk, ssid->passphrase, wpa_group_s->go_dev_addr,
+		hidl_is_persistent));
+}
+
+void HidlManager::notifyP2pGroupRemoved(
+    struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid,
+    const char *role)
+{
+	if (!wpa_group_s || !wpa_group_s->parent || !ssid || !role)
+		return;
+
+	// For group notifications, need to use the parent iface for callbacks.
+	struct wpa_supplicant *wpa_s = wpa_group_s->parent;
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	bool hidl_is_go = (std::string(role) == "GO");
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onGroupRemoved,
+		std::placeholders::_1, wpa_group_s->ifname, hidl_is_go));
+}
+
+void HidlManager::notifyP2pInvitationReceived(
+    struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *go_dev_addr,
+    const u8 *bssid, int id, int op_freq)
+{
+	if (!wpa_s || !sa || !go_dev_addr || !bssid)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	SupplicantNetworkId hidl_network_id;
+	if (id < 0) {
+		hidl_network_id = UINT32_MAX;
+	}
+	hidl_network_id = id;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onInvitationReceived,
+		std::placeholders::_1, sa, go_dev_addr, bssid, hidl_network_id,
+		op_freq));
+}
+
+void HidlManager::notifyP2pInvitationResult(
+    struct wpa_supplicant *wpa_s, int status, const u8 *bssid)
+{
+	if (!wpa_s || !bssid)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onInvitationResult,
+		std::placeholders::_1, bssid,
+		static_cast<ISupplicantP2pIfaceCallback::P2pStatusCode>(
+		    status)));
+}
+
+void HidlManager::notifyP2pProvisionDiscovery(
+    struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
+    enum p2p_prov_disc_status status, u16 config_methods,
+    unsigned int generated_pin)
+{
+	if (!wpa_s || !dev_addr)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	std::string hidl_generated_pin;
+	if (generated_pin > 0) {
+		hidl_generated_pin =
+		    misc_utils::convertWpsPinToString(generated_pin);
+	}
+	bool hidl_is_request = (request == 1 ? true : false);
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onProvisionDiscoveryCompleted,
+		std::placeholders::_1, dev_addr, hidl_is_request,
+		static_cast<ISupplicantP2pIfaceCallback::P2pProvDiscStatusCode>(
+		    status),
+		config_methods, hidl_generated_pin));
+}
+
+void HidlManager::notifyP2pSdResponse(
+    struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
+    const u8 *tlvs, size_t tlvs_len)
+{
+	if (!wpa_s || !sa || !tlvs)
+		return;
+
+	if (p2p_iface_object_map_.find(wpa_s->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantP2pIfaceCallback::onServiceDiscoveryResponse,
+		std::placeholders::_1, sa, update_indic,
+		std::vector<uint8_t>{tlvs, tlvs + tlvs_len}));
+}
+
+void HidlManager::notifyApStaAuthorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+{
+	if (!wpa_s || !wpa_s->parent || !sta)
+		return;
+	if (p2p_iface_object_map_.find(wpa_s->parent->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+	callWithEachP2pIfaceCallback(
+	    wpa_s->parent->ifname, std::bind(
+			       &ISupplicantP2pIfaceCallback::onStaAuthorized,
+			       std::placeholders::_1, sta, p2p_dev_addr ? p2p_dev_addr : kZeroBssid));
+}
+
+void HidlManager::notifyApStaDeauthorized(
+    struct wpa_supplicant *wpa_s, const u8 *sta, const u8 *p2p_dev_addr)
+{
+	if (!wpa_s || !wpa_s->parent || !sta)
+		return;
+	if (p2p_iface_object_map_.find(wpa_s->parent->ifname) ==
+	    p2p_iface_object_map_.end())
+		return;
+
+	callWithEachP2pIfaceCallback(
+	    wpa_s->parent->ifname, std::bind(
+			       &ISupplicantP2pIfaceCallback::onStaDeauthorized,
+			       std::placeholders::_1, sta, p2p_dev_addr ? p2p_dev_addr : kZeroBssid));
+}
+
+void HidlManager::notifyExtRadioWorkStart(
+    struct wpa_supplicant *wpa_s, uint32_t id)
+{
+	if (!wpa_s)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onExtRadioWorkStart,
+		std::placeholders::_1, id));
+}
+
+void HidlManager::notifyExtRadioWorkTimeout(
+    struct wpa_supplicant *wpa_s, uint32_t id)
+{
+	if (!wpa_s)
+		return;
+
+	if (sta_iface_object_map_.find(wpa_s->ifname) ==
+	    sta_iface_object_map_.end())
+		return;
+
+	callWithEachStaIfaceCallback(
+	    wpa_s->ifname,
+	    std::bind(
+		&ISupplicantStaIfaceCallback::onExtRadioWorkTimeout,
+		std::placeholders::_1, id));
+}
+
+/**
+ * Retrieve the |ISupplicantP2pIface| hidl object reference using the provided
+ * ifname.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param iface_object Hidl reference corresponding to the iface.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::getP2pIfaceHidlObjectByIfname(
+    const std::string &ifname, android::sp<ISupplicantP2pIface> *iface_object)
+{
+	if (ifname.empty() || !iface_object)
+		return 1;
+
+	auto iface_object_iter = p2p_iface_object_map_.find(ifname);
+	if (iface_object_iter == p2p_iface_object_map_.end())
+		return 1;
+
+	*iface_object = iface_object_iter->second;
+	return 0;
+}
+
+/**
+ * Retrieve the |ISupplicantStaIface| hidl object reference using the provided
+ * ifname.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param iface_object Hidl reference corresponding to the iface.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::getStaIfaceHidlObjectByIfname(
+    const std::string &ifname, android::sp<ISupplicantStaIface> *iface_object)
+{
+	if (ifname.empty() || !iface_object)
+		return 1;
+
+	auto iface_object_iter = sta_iface_object_map_.find(ifname);
+	if (iface_object_iter == sta_iface_object_map_.end())
+		return 1;
+
+	*iface_object = iface_object_iter->second;
+	return 0;
+}
+
+/**
+ * Retrieve the |ISupplicantP2pNetwork| hidl object reference using the provided
+ * ifname and network_id.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param network_object Hidl reference corresponding to the network.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::getP2pNetworkHidlObjectByIfnameAndNetworkId(
+    const std::string &ifname, int network_id,
+    android::sp<ISupplicantP2pNetwork> *network_object)
+{
+	if (ifname.empty() || network_id < 0 || !network_object)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(ifname, network_id);
+
+	auto network_object_iter = p2p_network_object_map_.find(network_key);
+	if (network_object_iter == p2p_network_object_map_.end())
+		return 1;
+
+	*network_object = network_object_iter->second;
+	return 0;
+}
+
+/**
+ * Retrieve the |ISupplicantStaNetwork| hidl object reference using the provided
+ * ifname and network_id.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param network_object Hidl reference corresponding to the network.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::getStaNetworkHidlObjectByIfnameAndNetworkId(
+    const std::string &ifname, int network_id,
+    android::sp<ISupplicantStaNetwork> *network_object)
+{
+	if (ifname.empty() || network_id < 0 || !network_object)
+		return 1;
+
+	// Generate the key to be used to lookup the network.
+	const std::string network_key =
+	    getNetworkObjectMapKey(ifname, network_id);
+
+	auto network_object_iter = sta_network_object_map_.find(network_key);
+	if (network_object_iter == sta_network_object_map_.end())
+		return 1;
+
+	*network_object = network_object_iter->second;
+	return 0;
+}
+
+/**
+ * Add a new |ISupplicantCallback| hidl object reference to our
+ * global callback list.
+ *
+ * @param callback Hidl reference of the |ISupplicantCallback| object.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::addSupplicantCallbackHidlObject(
+    const android::sp<ISupplicantCallback> &callback)
+{
+	// Register for death notification before we add it to our list.
+	auto on_hidl_died_fctor = std::bind(
+	    &HidlManager::removeSupplicantCallbackHidlObject, this,
+	    std::placeholders::_1);
+	return registerForDeathAndAddCallbackHidlObjectToList<
+	    ISupplicantCallback>(
+	    callback, on_hidl_died_fctor, supplicant_callbacks_);
+}
+
+/**
+ * Add a new iface callback hidl object reference to our
+ * interface callback list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param callback Hidl reference of the callback object.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::addP2pIfaceCallbackHidlObject(
+    const std::string &ifname,
+    const android::sp<ISupplicantP2pIfaceCallback> &callback)
+{
+	const std::function<void(
+	    const android::sp<ISupplicantP2pIfaceCallback> &)>
+	    on_hidl_died_fctor = std::bind(
+		&HidlManager::removeP2pIfaceCallbackHidlObject, this, ifname,
+		std::placeholders::_1);
+	return addIfaceCallbackHidlObjectToMap(
+	    ifname, callback, on_hidl_died_fctor, p2p_iface_callbacks_map_);
+}
+
+/**
+ * Add a new iface callback hidl object reference to our
+ * interface callback list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param callback Hidl reference of the callback object.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::addStaIfaceCallbackHidlObject(
+    const std::string &ifname,
+    const android::sp<ISupplicantStaIfaceCallback> &callback)
+{
+	const std::function<void(
+	    const android::sp<ISupplicantStaIfaceCallback> &)>
+	    on_hidl_died_fctor = std::bind(
+		&HidlManager::removeStaIfaceCallbackHidlObject, this, ifname,
+		std::placeholders::_1);
+	return addIfaceCallbackHidlObjectToMap(
+	    ifname, callback, on_hidl_died_fctor, sta_iface_callbacks_map_);
+}
+
+/**
+ * Add a new network callback hidl object reference to our network callback
+ * list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param callback Hidl reference of the callback object.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::addP2pNetworkCallbackHidlObject(
+    const std::string &ifname, int network_id,
+    const android::sp<ISupplicantP2pNetworkCallback> &callback)
+{
+	const std::function<void(
+	    const android::sp<ISupplicantP2pNetworkCallback> &)>
+	    on_hidl_died_fctor = std::bind(
+		&HidlManager::removeP2pNetworkCallbackHidlObject, this, ifname,
+		network_id, std::placeholders::_1);
+	return addNetworkCallbackHidlObjectToMap(
+	    ifname, network_id, callback, on_hidl_died_fctor,
+	    p2p_network_callbacks_map_);
+}
+
+/**
+ * Add a new network callback hidl object reference to our network callback
+ * list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param callback Hidl reference of the callback object.
+ *
+ * @return 0 on success, 1 on failure.
+ */
+int HidlManager::addStaNetworkCallbackHidlObject(
+    const std::string &ifname, int network_id,
+    const android::sp<ISupplicantStaNetworkCallback> &callback)
+{
+	const std::function<void(
+	    const android::sp<ISupplicantStaNetworkCallback> &)>
+	    on_hidl_died_fctor = std::bind(
+		&HidlManager::removeStaNetworkCallbackHidlObject, this, ifname,
+		network_id, std::placeholders::_1);
+	return addNetworkCallbackHidlObjectToMap(
+	    ifname, network_id, callback, on_hidl_died_fctor,
+	    sta_network_callbacks_map_);
+}
+
+/**
+ * Removes the provided |ISupplicantCallback| hidl object reference
+ * from our global callback list.
+ *
+ * @param callback Hidl reference of the |ISupplicantCallback| object.
+ */
+void HidlManager::removeSupplicantCallbackHidlObject(
+    const android::sp<ISupplicantCallback> &callback)
+{
+	supplicant_callbacks_.erase(
+	    std::remove(
+		supplicant_callbacks_.begin(), supplicant_callbacks_.end(),
+		callback),
+	    supplicant_callbacks_.end());
+}
+
+/**
+ * Removes the provided iface callback hidl object reference from
+ * our interface callback list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param callback Hidl reference of the callback object.
+ */
+void HidlManager::removeP2pIfaceCallbackHidlObject(
+    const std::string &ifname,
+    const android::sp<ISupplicantP2pIfaceCallback> &callback)
+{
+	return removeIfaceCallbackHidlObjectFromMap(
+	    ifname, callback, p2p_iface_callbacks_map_);
+}
+
+/**
+ * Removes the provided iface callback hidl object reference from
+ * our interface callback list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param callback Hidl reference of the callback object.
+ */
+void HidlManager::removeStaIfaceCallbackHidlObject(
+    const std::string &ifname,
+    const android::sp<ISupplicantStaIfaceCallback> &callback)
+{
+	return removeIfaceCallbackHidlObjectFromMap(
+	    ifname, callback, sta_iface_callbacks_map_);
+}
+
+/**
+ * Removes the provided network callback hidl object reference from
+ * our network callback list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param callback Hidl reference of the callback object.
+ */
+void HidlManager::removeP2pNetworkCallbackHidlObject(
+    const std::string &ifname, int network_id,
+    const android::sp<ISupplicantP2pNetworkCallback> &callback)
+{
+	return removeNetworkCallbackHidlObjectFromMap(
+	    ifname, network_id, callback, p2p_network_callbacks_map_);
+}
+
+/**
+ * Removes the provided network callback hidl object reference from
+ * our network callback list.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param callback Hidl reference of the callback object.
+ */
+void HidlManager::removeStaNetworkCallbackHidlObject(
+    const std::string &ifname, int network_id,
+    const android::sp<ISupplicantStaNetworkCallback> &callback)
+{
+	return removeNetworkCallbackHidlObjectFromMap(
+	    ifname, network_id, callback, sta_network_callbacks_map_);
+}
+
+/**
+ * Helper function to invoke the provided callback method on all the
+ * registered |ISupplicantCallback| callback hidl objects.
+ *
+ * @param method Pointer to the required hidl method from
+ * |ISupplicantCallback|.
+ */
+void HidlManager::callWithEachSupplicantCallback(
+    const std::function<Return<void>(android::sp<ISupplicantCallback>)> &method)
+{
+	for (const auto &callback : supplicant_callbacks_) {
+		if (!method(callback).isOk()) {
+			wpa_printf(MSG_ERROR, "Failed to invoke HIDL callback");
+		}
+	}
+}
+
+/**
+ * Helper fucntion to invoke the provided callback method on all the
+ * registered iface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * |ISupplicantIfaceCallback|.
+ */
+void HidlManager::callWithEachP2pIfaceCallback(
+    const std::string &ifname,
+    const std::function<Return<void>(android::sp<ISupplicantP2pIfaceCallback>)>
+	&method)
+{
+	callWithEachIfaceCallback(ifname, method, p2p_iface_callbacks_map_);
+}
+
+/**
+ * Helper fucntion to invoke the provided callback method on all the
+ * registered iface callback hidl objects for the specified
+ * |ifname|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param method Pointer to the required hidl method from
+ * |ISupplicantIfaceCallback|.
+ */
+void HidlManager::callWithEachStaIfaceCallback(
+    const std::string &ifname,
+    const std::function<Return<void>(android::sp<ISupplicantStaIfaceCallback>)>
+	&method)
+{
+	callWithEachIfaceCallback(ifname, method, sta_iface_callbacks_map_);
+}
+
+/**
+ * Helper function to invoke the provided callback method on all the
+ * registered network callback hidl objects for the specified
+ * |ifname| & |network_id|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param method Pointer to the required hidl method from
+ * |ISupplicantP2pNetworkCallback| or |ISupplicantStaNetworkCallback| .
+ */
+void HidlManager::callWithEachP2pNetworkCallback(
+    const std::string &ifname, int network_id,
+    const std::function<
+	Return<void>(android::sp<ISupplicantP2pNetworkCallback>)> &method)
+{
+	callWithEachNetworkCallback(
+	    ifname, network_id, method, p2p_network_callbacks_map_);
+}
+
+/**
+ * Helper function to invoke the provided callback method on all the
+ * registered network callback hidl objects for the specified
+ * |ifname| & |network_id|.
+ *
+ * @param ifname Name of the corresponding interface.
+ * @param network_id ID of the corresponding network.
+ * @param method Pointer to the required hidl method from
+ * |ISupplicantP2pNetworkCallback| or |ISupplicantStaNetworkCallback| .
+ */
+void HidlManager::callWithEachStaNetworkCallback(
+    const std::string &ifname, int network_id,
+    const std::function<
+	Return<void>(android::sp<ISupplicantStaNetworkCallback>)> &method)
+{
+	callWithEachNetworkCallback(
+	    ifname, network_id, method, sta_network_callbacks_map_);
+}
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
diff --git a/wpa_supplicant/hidl/1.0/hidl_manager.h b/wpa_supplicant/hidl/1.0/hidl_manager.h
new file mode 100644
index 0000000..9d9527c
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/hidl_manager.h
@@ -0,0 +1,678 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_HIDL_MANAGER_H
+#define WPA_SUPPLICANT_HIDL_HIDL_MANAGER_H
+
+#include <map>
+#include <string>
+
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetworkCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
+
+#include "p2p_iface.h"
+#include "p2p_network.h"
+#include "sta_iface.h"
+#include "sta_network.h"
+#include "supplicant.h"
+
+extern "C" {
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * HidlManager is responsible for managing the lifetime of all
+ * hidl objects created by wpa_supplicant. This is a singleton
+ * class which is created by the supplicant core and can be used
+ * to get references to the hidl objects.
+ */
+class HidlManager
+{
+public:
+	static HidlManager *getInstance();
+	static void destroyInstance();
+
+	// Methods called from wpa_supplicant core.
+	int registerHidlService(struct wpa_global *global);
+	int registerInterface(struct wpa_supplicant *wpa_s);
+	int unregisterInterface(struct wpa_supplicant *wpa_s);
+	int registerNetwork(
+	    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+	int unregisterNetwork(
+	    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+	int notifyStateChange(struct wpa_supplicant *wpa_s);
+	int notifyNetworkRequest(
+	    struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, int type,
+	    const char *param);
+	void notifyAnqpQueryDone(
+	    struct wpa_supplicant *wpa_s, const u8 *bssid, const char *result,
+	    const struct wpa_bss_anqp *anqp);
+	void notifyHs20IconQueryDone(
+	    struct wpa_supplicant *wpa_s, const u8 *bssid,
+	    const char *file_name, const u8 *image, u32 image_length);
+	void notifyHs20RxSubscriptionRemediation(
+	    struct wpa_supplicant *wpa_s, const char *url, u8 osu_method);
+	void notifyHs20RxDeauthImminentNotice(
+	    struct wpa_supplicant *wpa_s, u8 code, u16 reauth_delay,
+	    const char *url);
+	void notifyDisconnectReason(struct wpa_supplicant *wpa_s);
+	void notifyAssocReject(struct wpa_supplicant *wpa_s);
+	void notifyAuthTimeout(struct wpa_supplicant *wpa_s);
+	void notifyBssidChanged(struct wpa_supplicant *wpa_s);
+	void notifyWpsEventFail(
+	    struct wpa_supplicant *wpa_s, uint8_t *peer_macaddr,
+	    uint16_t config_error, uint16_t error_indication);
+	void notifyWpsEventSuccess(struct wpa_supplicant *wpa_s);
+	void notifyWpsEventPbcOverlap(struct wpa_supplicant *wpa_s);
+	void notifyP2pDeviceFound(
+	    struct wpa_supplicant *wpa_s, const u8 *addr,
+	    const struct p2p_peer_info *info, const u8 *peer_wfd_device_info,
+	    u8 peer_wfd_device_info_len);
+	void notifyP2pDeviceLost(
+	    struct wpa_supplicant *wpa_s, const u8 *p2p_device_addr);
+	void notifyP2pFindStopped(struct wpa_supplicant *wpa_s);
+	void notifyP2pGoNegReq(
+	    struct wpa_supplicant *wpa_s, const u8 *src_addr, u16 dev_passwd_id,
+	    u8 go_intent);
+	void notifyP2pGoNegCompleted(
+	    struct wpa_supplicant *wpa_s, const struct p2p_go_neg_results *res);
+	void notifyP2pGroupFormationFailure(
+	    struct wpa_supplicant *wpa_s, const char *reason);
+	void notifyP2pGroupStarted(
+	    struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid,
+	    int persistent, int client);
+	void notifyP2pGroupRemoved(
+	    struct wpa_supplicant *wpa_group_s, const struct wpa_ssid *ssid,
+	    const char *role);
+	void notifyP2pInvitationReceived(
+	    struct wpa_supplicant *wpa_s, const u8 *sa, const u8 *go_dev_addr,
+	    const u8 *bssid, int id, int op_freq);
+	void notifyP2pInvitationResult(
+	    struct wpa_supplicant *wpa_s, int status, const u8 *bssid);
+	void notifyP2pProvisionDiscovery(
+	    struct wpa_supplicant *wpa_s, const u8 *dev_addr, int request,
+	    enum p2p_prov_disc_status status, u16 config_methods,
+	    unsigned int generated_pin);
+	void notifyP2pSdResponse(
+	    struct wpa_supplicant *wpa_s, const u8 *sa, u16 update_indic,
+	    const u8 *tlvs, size_t tlvs_len);
+	void notifyApStaAuthorized(
+	    struct wpa_supplicant *wpa_s, const u8 *sta,
+	    const u8 *p2p_dev_addr);
+	void notifyApStaDeauthorized(
+	    struct wpa_supplicant *wpa_s, const u8 *sta,
+	    const u8 *p2p_dev_addr);
+
+	// Methods called from hidl objects.
+	void notifyExtRadioWorkStart(struct wpa_supplicant *wpa_s, uint32_t id);
+	void notifyExtRadioWorkTimeout(
+	    struct wpa_supplicant *wpa_s, uint32_t id);
+
+	int getP2pIfaceHidlObjectByIfname(
+	    const std::string &ifname,
+	    android::sp<ISupplicantP2pIface> *iface_object);
+	int getStaIfaceHidlObjectByIfname(
+	    const std::string &ifname,
+	    android::sp<ISupplicantStaIface> *iface_object);
+	int getP2pNetworkHidlObjectByIfnameAndNetworkId(
+	    const std::string &ifname, int network_id,
+	    android::sp<ISupplicantP2pNetwork> *network_object);
+	int getStaNetworkHidlObjectByIfnameAndNetworkId(
+	    const std::string &ifname, int network_id,
+	    android::sp<ISupplicantStaNetwork> *network_object);
+	int addSupplicantCallbackHidlObject(
+	    const android::sp<ISupplicantCallback> &callback);
+	int addP2pIfaceCallbackHidlObject(
+	    const std::string &ifname,
+	    const android::sp<ISupplicantP2pIfaceCallback> &callback);
+	int addStaIfaceCallbackHidlObject(
+	    const std::string &ifname,
+	    const android::sp<ISupplicantStaIfaceCallback> &callback);
+	int addP2pNetworkCallbackHidlObject(
+	    const std::string &ifname, int network_id,
+	    const android::sp<ISupplicantP2pNetworkCallback> &callback);
+	int addStaNetworkCallbackHidlObject(
+	    const std::string &ifname, int network_id,
+	    const android::sp<ISupplicantStaNetworkCallback> &callback);
+
+private:
+	HidlManager() = default;
+	~HidlManager() = default;
+	HidlManager(const HidlManager &) = default;
+	HidlManager &operator=(const HidlManager &) = default;
+
+	void removeSupplicantCallbackHidlObject(
+	    const android::sp<ISupplicantCallback> &callback);
+	void removeP2pIfaceCallbackHidlObject(
+	    const std::string &ifname,
+	    const android::sp<ISupplicantP2pIfaceCallback> &callback);
+	void removeStaIfaceCallbackHidlObject(
+	    const std::string &ifname,
+	    const android::sp<ISupplicantStaIfaceCallback> &callback);
+	void removeP2pNetworkCallbackHidlObject(
+	    const std::string &ifname, int network_id,
+	    const android::sp<ISupplicantP2pNetworkCallback> &callback);
+	void removeStaNetworkCallbackHidlObject(
+	    const std::string &ifname, int network_id,
+	    const android::sp<ISupplicantStaNetworkCallback> &callback);
+
+	void callWithEachSupplicantCallback(
+	    const std::function<android::hardware::Return<void>(
+		android::sp<ISupplicantCallback>)> &method);
+	void callWithEachP2pIfaceCallback(
+	    const std::string &ifname,
+	    const std::function<android::hardware::Return<void>(
+		android::sp<ISupplicantP2pIfaceCallback>)> &method);
+	void callWithEachStaIfaceCallback(
+	    const std::string &ifname,
+	    const std::function<android::hardware::Return<void>(
+		android::sp<ISupplicantStaIfaceCallback>)> &method);
+	void callWithEachP2pNetworkCallback(
+	    const std::string &ifname, int network_id,
+	    const std::function<android::hardware::Return<void>(
+		android::sp<ISupplicantP2pNetworkCallback>)> &method);
+	void callWithEachStaNetworkCallback(
+	    const std::string &ifname, int network_id,
+	    const std::function<android::hardware::Return<void>(
+		android::sp<ISupplicantStaNetworkCallback>)> &method);
+
+	// Singleton instance of this class.
+	static HidlManager *instance_;
+	// The main hidl service object.
+	android::sp<Supplicant> supplicant_object_;
+	// Map of all the P2P interface specific hidl objects controlled by
+	// wpa_supplicant. This map is keyed in by the corresponding
+	// |ifname|.
+	std::map<const std::string, android::sp<P2pIface>>
+	    p2p_iface_object_map_;
+	// Map of all the STA interface specific hidl objects controlled by
+	// wpa_supplicant. This map is keyed in by the corresponding
+	// |ifname|.
+	std::map<const std::string, android::sp<StaIface>>
+	    sta_iface_object_map_;
+	// Map of all the P2P network specific hidl objects controlled by
+	// wpa_supplicant. This map is keyed in by the corresponding
+	// |ifname| & |network_id|.
+	std::map<const std::string, android::sp<P2pNetwork>>
+	    p2p_network_object_map_;
+	// Map of all the STA network specific hidl objects controlled by
+	// wpa_supplicant. This map is keyed in by the corresponding
+	// |ifname| & |network_id|.
+	std::map<const std::string, android::sp<StaNetwork>>
+	    sta_network_object_map_;
+
+	// Callback registered for the main hidl service object.
+	std::vector<android::sp<ISupplicantCallback>> supplicant_callbacks_;
+	// Map of all the callbacks registered for P2P interface specific
+	// hidl objects controlled by wpa_supplicant.  This map is keyed in by
+	// the corresponding |ifname|.
+	std::map<
+	    const std::string,
+	    std::vector<android::sp<ISupplicantP2pIfaceCallback>>>
+	    p2p_iface_callbacks_map_;
+	// Map of all the callbacks registered for STA interface specific
+	// hidl objects controlled by wpa_supplicant.  This map is keyed in by
+	// the corresponding |ifname|.
+	std::map<
+	    const std::string,
+	    std::vector<android::sp<ISupplicantStaIfaceCallback>>>
+	    sta_iface_callbacks_map_;
+	// Map of all the callbacks registered for P2P network specific
+	// hidl objects controlled by wpa_supplicant.  This map is keyed in by
+	// the corresponding |ifname| & |network_id|.
+	std::map<
+	    const std::string,
+	    std::vector<android::sp<ISupplicantP2pNetworkCallback>>>
+	    p2p_network_callbacks_map_;
+	// Map of all the callbacks registered for STA network specific
+	// hidl objects controlled by wpa_supplicant.  This map is keyed in by
+	// the corresponding |ifname| & |network_id|.
+	std::map<
+	    const std::string,
+	    std::vector<android::sp<ISupplicantStaNetworkCallback>>>
+	    sta_network_callbacks_map_;
+
+#if 0  // TODO(b/31632518): HIDL object death notifications.
+	/**
+	 * Helper class used to deregister the callback object reference from
+	 * our callback list on the death of the hidl object.
+	 * This class stores a reference of the callback hidl object and a
+	 * function to be called to indicate the death of the hidl object.
+	 */
+	template <class CallbackType>
+	class CallbackObjectDeathNotifier
+	    : public android::hardware::IBinder::DeathRecipient
+	{
+	public:
+		CallbackObjectDeathNotifier(
+		    const android::sp<CallbackType> &callback,
+		    const std::function<void(const android::sp<CallbackType> &)>
+			&on_hidl_died)
+		    : callback_(callback), on_hidl_died_(on_hidl_died)
+		{
+		}
+		void binderDied(const android::wp<android::hardware::IBinder>
+				    & /* who */) override
+		{
+			on_hidl_died_(callback_);
+		}
+
+	private:
+		// The callback hidl object reference.
+		const android::sp<CallbackType> callback_;
+		// Callback function to be called when the hidl dies.
+		const std::function<void(const android::sp<CallbackType> &)>
+		    on_hidl_died_;
+	};
+#endif
+};
+
+// The hidl interface uses some values which are the same as internal ones to
+// avoid nasty runtime conversion functions. So, adding compile time asserts
+// to guard against any internal changes breaking the hidl interface.
+static_assert(
+    static_cast<uint32_t>(ISupplicant::DebugLevel::EXCESSIVE) == MSG_EXCESSIVE,
+    "Debug level value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicant::DebugLevel::ERROR) == MSG_ERROR,
+    "Debug level value mismatch");
+
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::NONE) ==
+	WPA_KEY_MGMT_NONE,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK) ==
+	WPA_KEY_MGMT_PSK,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP) ==
+	WPA_KEY_MGMT_IEEE8021X,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X) ==
+	WPA_KEY_MGMT_IEEE8021X_NO_WPA,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_EAP) ==
+	WPA_KEY_MGMT_FT_IEEE8021X,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_PSK) ==
+	WPA_KEY_MGMT_FT_PSK,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::OSEN) ==
+	WPA_KEY_MGMT_OSEN,
+    "KeyMgmt value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::WPA) ==
+	WPA_PROTO_WPA,
+    "Proto value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::RSN) ==
+	WPA_PROTO_RSN,
+    "Proto value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::OSEN) ==
+	WPA_PROTO_OSEN,
+    "Proto value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::OPEN) ==
+	WPA_AUTH_ALG_OPEN,
+    "AuthAlg value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::SHARED) ==
+	WPA_AUTH_ALG_SHARED,
+    "AuthAlg value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::LEAP) ==
+	WPA_AUTH_ALG_LEAP,
+    "AuthAlg value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP40) ==
+	WPA_CIPHER_WEP40,
+    "GroupCipher value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP104) ==
+	WPA_CIPHER_WEP104,
+    "GroupCipher value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::TKIP) ==
+	WPA_CIPHER_TKIP,
+    "GroupCipher value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::CCMP) ==
+	WPA_CIPHER_CCMP,
+    "GroupCipher value mismatch");
+static_assert(
+    static_cast<uint32_t>(
+	ISupplicantStaNetwork::GroupCipherMask::GTK_NOT_USED) ==
+	WPA_CIPHER_GTK_NOT_USED,
+    "GroupCipher value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::NONE) ==
+	WPA_CIPHER_NONE,
+    "PairwiseCipher value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::TKIP) ==
+	WPA_CIPHER_TKIP,
+    "PairwiseCipher value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::CCMP) ==
+	WPA_CIPHER_CCMP,
+    "PairwiseCipher value mismatch");
+
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaIfaceCallback::State::DISCONNECTED) ==
+	WPA_DISCONNECTED,
+    "State value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaIfaceCallback::State::COMPLETED) ==
+	WPA_COMPLETED,
+    "State value mismatch");
+
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaIface::AnqpInfoId::VENUE_NAME) ==
+	ANQP_VENUE_NAME,
+    "ANQP ID value mismatch");
+static_assert(
+    static_cast<uint32_t>(
+	ISupplicantStaIface::AnqpInfoId::ROAMING_CONSORTIUM) ==
+	ANQP_ROAMING_CONSORTIUM,
+    "ANQP ID value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaIface::AnqpInfoId::NAI_REALM) ==
+	ANQP_NAI_REALM,
+    "ANQP ID value mismatch");
+static_assert(
+    static_cast<uint32_t>(
+	ISupplicantStaIface::AnqpInfoId::IP_ADDR_TYPE_AVAILABILITY) ==
+	ANQP_IP_ADDR_TYPE_AVAILABILITY,
+    "ANQP ID value mismatch");
+static_assert(
+    static_cast<uint32_t>(
+	ISupplicantStaIface::AnqpInfoId::ANQP_3GPP_CELLULAR_NETWORK) ==
+	ANQP_3GPP_CELLULAR_NETWORK,
+    "ANQP ID value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaIface::AnqpInfoId::DOMAIN_NAME) ==
+	ANQP_DOMAIN_NAME,
+    "ANQP ID value mismatch");
+static_assert(
+    static_cast<uint32_t>(
+	ISupplicantStaIface::Hs20AnqpSubtypes::OPERATOR_FRIENDLY_NAME) ==
+	HS20_STYPE_OPERATOR_FRIENDLY_NAME,
+    "HS Subtype value mismatch");
+static_assert(
+    static_cast<uint32_t>(ISupplicantStaIface::Hs20AnqpSubtypes::WAN_METRICS) ==
+	HS20_STYPE_WAN_METRICS,
+    "HS Subtype value mismatch");
+static_assert(
+    static_cast<uint32_t>(
+	ISupplicantStaIface::Hs20AnqpSubtypes::CONNECTION_CAPABILITY) ==
+	HS20_STYPE_CONNECTION_CAPABILITY,
+    "HS Subtype value mismatch");
+static_assert(
+    static_cast<uint32_t>(
+	ISupplicantStaIface::Hs20AnqpSubtypes::OSU_PROVIDERS_LIST) ==
+	HS20_STYPE_OSU_PROVIDERS_LIST,
+    "HS Subtype value mismatch");
+
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantStaIfaceCallback::WpsConfigError::NO_ERROR) ==
+	WPS_CFG_NO_ERROR,
+    "Wps config error value mismatch");
+static_assert(
+    static_cast<uint16_t>(ISupplicantStaIfaceCallback::WpsConfigError::
+			      PUBLIC_KEY_HASH_MISMATCH) ==
+	WPS_CFG_PUBLIC_KEY_HASH_MISMATCH,
+    "Wps config error value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantStaIfaceCallback::WpsErrorIndication::NO_ERROR) ==
+	WPS_EI_NO_ERROR,
+    "Wps error indication value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantStaIfaceCallback::WpsErrorIndication::AUTH_FAILURE) ==
+	WPS_EI_AUTH_FAILURE,
+    "Wps error indication value mismatch");
+
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::USBA) == WPS_CONFIG_USBA,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::ETHERNET) == WPS_CONFIG_ETHERNET,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::LABEL) == WPS_CONFIG_LABEL,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::DISPLAY) == WPS_CONFIG_DISPLAY,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::INT_NFC_TOKEN) ==
+	WPS_CONFIG_INT_NFC_TOKEN,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::EXT_NFC_TOKEN) ==
+	WPS_CONFIG_EXT_NFC_TOKEN,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::NFC_INTERFACE) ==
+	WPS_CONFIG_NFC_INTERFACE,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::PUSHBUTTON) ==
+	WPS_CONFIG_PUSHBUTTON,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::KEYPAD) == WPS_CONFIG_KEYPAD,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::VIRT_PUSHBUTTON) ==
+	WPS_CONFIG_VIRT_PUSHBUTTON,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::PHY_PUSHBUTTON) ==
+	WPS_CONFIG_PHY_PUSHBUTTON,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::P2PS) == WPS_CONFIG_P2PS,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::VIRT_DISPLAY) ==
+	WPS_CONFIG_VIRT_DISPLAY,
+    "Wps config value mismatch");
+static_assert(
+    static_cast<uint32_t>(WpsConfigMethods::PHY_DISPLAY) ==
+	WPS_CONFIG_PHY_DISPLAY,
+    "Wps config value mismatch");
+
+static_assert(
+    static_cast<uint32_t>(P2pGroupCapabilityMask::GROUP_OWNER) ==
+	P2P_GROUP_CAPAB_GROUP_OWNER,
+    "P2P capability value mismatch");
+static_assert(
+    static_cast<uint32_t>(P2pGroupCapabilityMask::PERSISTENT_GROUP) ==
+	P2P_GROUP_CAPAB_PERSISTENT_GROUP,
+    "P2P capability value mismatch");
+static_assert(
+    static_cast<uint32_t>(P2pGroupCapabilityMask::GROUP_LIMIT) ==
+	P2P_GROUP_CAPAB_GROUP_LIMIT,
+    "P2P capability value mismatch");
+static_assert(
+    static_cast<uint32_t>(P2pGroupCapabilityMask::INTRA_BSS_DIST) ==
+	P2P_GROUP_CAPAB_INTRA_BSS_DIST,
+    "P2P capability value mismatch");
+static_assert(
+    static_cast<uint32_t>(P2pGroupCapabilityMask::CROSS_CONN) ==
+	P2P_GROUP_CAPAB_CROSS_CONN,
+    "P2P capability value mismatch");
+static_assert(
+    static_cast<uint32_t>(P2pGroupCapabilityMask::PERSISTENT_RECONN) ==
+	P2P_GROUP_CAPAB_PERSISTENT_RECONN,
+    "P2P capability value mismatch");
+static_assert(
+    static_cast<uint32_t>(P2pGroupCapabilityMask::GROUP_FORMATION) ==
+	P2P_GROUP_CAPAB_GROUP_FORMATION,
+    "P2P capability value mismatch");
+
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::WpsDevPasswordId::DEFAULT) ==
+	DEV_PW_DEFAULT,
+    "Wps dev password id value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::WpsDevPasswordId::USER_SPECIFIED) ==
+	DEV_PW_USER_SPECIFIED,
+    "Wps dev password id value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::WpsDevPasswordId::MACHINE_SPECIFIED) ==
+	DEV_PW_MACHINE_SPECIFIED,
+    "Wps dev password id value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::WpsDevPasswordId::REKEY) == DEV_PW_REKEY,
+    "Wps dev password id value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::WpsDevPasswordId::PUSHBUTTON) ==
+	DEV_PW_PUSHBUTTON,
+    "Wps dev password id value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::WpsDevPasswordId::REGISTRAR_SPECIFIED) ==
+	DEV_PW_REGISTRAR_SPECIFIED,
+    "Wps dev password id value mismatch");
+static_assert(
+    static_cast<uint16_t>(ISupplicantP2pIfaceCallback::WpsDevPasswordId::
+			      NFC_CONNECTION_HANDOVER) ==
+	DEV_PW_NFC_CONNECTION_HANDOVER,
+    "Wps dev password id value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::WpsDevPasswordId::P2PS_DEFAULT) ==
+	DEV_PW_P2PS_DEFAULT,
+    "Wps dev password id value mismatch");
+
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::SUCCESS) == P2P_SC_SUCCESS,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(ISupplicantP2pIfaceCallback::P2pStatusCode::
+			      FAIL_INFO_CURRENTLY_UNAVAILABLE) ==
+	P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_INCOMPATIBLE_PARAMS) ==
+	P2P_SC_FAIL_INCOMPATIBLE_PARAMS,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_LIMIT_REACHED) ==
+	P2P_SC_FAIL_LIMIT_REACHED,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_INVALID_PARAMS) ==
+	P2P_SC_FAIL_INVALID_PARAMS,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(ISupplicantP2pIfaceCallback::P2pStatusCode::
+			      FAIL_UNABLE_TO_ACCOMMODATE) ==
+	P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_PREV_PROTOCOL_ERROR) ==
+	P2P_SC_FAIL_PREV_PROTOCOL_ERROR,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_NO_COMMON_CHANNELS) ==
+	P2P_SC_FAIL_NO_COMMON_CHANNELS,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_UNKNOWN_GROUP) ==
+	P2P_SC_FAIL_UNKNOWN_GROUP,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_BOTH_GO_INTENT_15) ==
+	P2P_SC_FAIL_BOTH_GO_INTENT_15,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(ISupplicantP2pIfaceCallback::P2pStatusCode::
+			      FAIL_INCOMPATIBLE_PROV_METHOD) ==
+	P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::FAIL_REJECTED_BY_USER) ==
+	P2P_SC_FAIL_REJECTED_BY_USER,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pStatusCode::SUCCESS_DEFERRED) ==
+	P2P_SC_SUCCESS_DEFERRED,
+    "P2P status code value mismatch");
+
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pProvDiscStatusCode::SUCCESS) ==
+	P2P_PROV_DISC_SUCCESS,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pProvDiscStatusCode::TIMEOUT) ==
+	P2P_PROV_DISC_TIMEOUT,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pProvDiscStatusCode::REJECTED) ==
+	P2P_PROV_DISC_REJECTED,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pProvDiscStatusCode::TIMEOUT_JOIN) ==
+	P2P_PROV_DISC_TIMEOUT_JOIN,
+    "P2P status code value mismatch");
+static_assert(
+    static_cast<uint16_t>(
+	ISupplicantP2pIfaceCallback::P2pProvDiscStatusCode::INFO_UNAVAILABLE) ==
+	P2P_PROV_DISC_INFO_UNAVAILABLE,
+    "P2P status code value mismatch");
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
+#endif  // WPA_SUPPLICANT_HIDL_HIDL_MANAGER_H
diff --git a/wpa_supplicant/hidl/1.0/hidl_return_util.h b/wpa_supplicant/hidl/1.0/hidl_return_util.h
new file mode 100644
index 0000000..c077fc8
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/hidl_return_util.h
@@ -0,0 +1,101 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HIDL_RETURN_UTIL_H_
+#define HIDL_RETURN_UTIL_H_
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+namespace hidl_return_util {
+
+/**
+ * These utility functions are used to invoke a method on the provided
+ * HIDL interface object.
+ * These functions checks if the provided HIDL interface object is valid.
+ * a) if valid, Invokes the corresponding internal implementation function of
+ * the HIDL method. It then invokes the HIDL continuation callback with
+ * the status and any returned values.
+ * b) if invalid, invokes the HIDL continuation callback with the
+ * provided error status and default values.
+ */
+// Use for HIDL methods which return only an instance of SupplicantStatus.
+template <typename ObjT, typename WorkFuncT, typename... Args>
+Return<void> validateAndCall(
+    ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
+    const std::function<void(const SupplicantStatus&)>& hidl_cb, Args&&... args)
+{
+	if (obj->isValid()) {
+		hidl_cb((obj->*work)(std::forward<Args>(args)...));
+	} else {
+		hidl_cb({status_code_if_invalid, ""});
+	}
+	return Void();
+}
+
+// Use for HIDL methods which return instance of SupplicantStatus and a single
+// return value.
+template <typename ObjT, typename WorkFuncT, typename ReturnT, typename... Args>
+Return<void> validateAndCall(
+    ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
+    const std::function<void(const SupplicantStatus&, ReturnT)>& hidl_cb,
+    Args&&... args)
+{
+	if (obj->isValid()) {
+		const auto& ret_pair =
+		    (obj->*work)(std::forward<Args>(args)...);
+		const SupplicantStatus& status = std::get<0>(ret_pair);
+		const auto& ret_value = std::get<1>(ret_pair);
+		hidl_cb(status, ret_value);
+	} else {
+		hidl_cb(
+		    {status_code_if_invalid, ""},
+		    typename std::remove_reference<ReturnT>::type());
+	}
+	return Void();
+}
+
+// Use for HIDL methods which return instance of SupplicantStatus and 2 return
+// values.
+template <
+    typename ObjT, typename WorkFuncT, typename ReturnT1, typename ReturnT2,
+    typename... Args>
+Return<void> validateAndCall(
+    ObjT* obj, SupplicantStatusCode status_code_if_invalid, WorkFuncT&& work,
+    const std::function<void(const SupplicantStatus&, ReturnT1, ReturnT2)>&
+	hidl_cb,
+    Args&&... args)
+{
+	if (obj->isValid()) {
+		const auto& ret_tuple =
+		    (obj->*work)(std::forward<Args>(args)...);
+		const SupplicantStatus& status = std::get<0>(ret_tuple);
+		const auto& ret_value1 = std::get<1>(ret_tuple);
+		const auto& ret_value2 = std::get<2>(ret_tuple);
+		hidl_cb(status, ret_value1, ret_value2);
+	} else {
+		hidl_cb(
+		    {status_code_if_invalid, ""},
+		    typename std::remove_reference<ReturnT1>::type(),
+		    typename std::remove_reference<ReturnT2>::type());
+	}
+	return Void();
+}
+
+}  // namespace hidl_util
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace supplicant
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+#endif  // HIDL_RETURN_UTIL_H_
diff --git a/wpa_supplicant/hidl/1.0/iface_config_utils.cpp b/wpa_supplicant/hidl/1.0/iface_config_utils.cpp
new file mode 100644
index 0000000..7dc5a6c
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/iface_config_utils.cpp
@@ -0,0 +1,180 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (struct wpa_supplicant* wpa_s, c) 2004-2016, Jouni Malinen
+ * <j@w1.fi>
+ * Copyright (struct wpa_supplicant* wpa_s, c) 2004-2016, Roshan Pius
+ * <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "hidl_manager.h"
+#include "hidl_return_util.h"
+#include "iface_config_utils.h"
+
+namespace {
+constexpr uint32_t kMaxWpsDeviceNameSize = WPS_DEV_NAME_MAX_LEN;
+constexpr uint32_t kMaxWpsManufacturerSize = WPS_MANUFACTURER_MAX_LEN;
+constexpr uint32_t kMaxWpsModelNameSize = WPS_MODEL_NAME_MAX_LEN;
+constexpr uint32_t kMaxWpsModelNumberSize = WPS_MODEL_NUMBER_MAX_LEN;
+constexpr uint32_t kMaxWpsSerialNumberSize = WPS_SERIAL_NUMBER_MAX_LEN;
+
+void processConfigUpdate(struct wpa_supplicant* wpa_s, uint32_t changed_param)
+{
+	wpa_s->conf->changed_parameters |= changed_param;
+	wpa_supplicant_update_config(wpa_s);
+}
+
+// Free any existing pointer stored in |dst| and store the provided string value
+// there.
+int freeAndSetStringConfigParam(
+    struct wpa_supplicant* wpa_s, const std::string& value, uint32_t max_size,
+    uint32_t changed_param, char** dst)
+{
+	if (value.size() > max_size) {
+		return -1;
+	}
+	WPA_ASSERT(dst);
+	os_free(static_cast<void*>(*dst));
+	*dst = os_strdup(value.c_str());
+	processConfigUpdate(wpa_s, changed_param);
+	return 0;
+}
+
+std::string convertWpsConfigMethodsMaskToString(uint16_t config_methods)
+{
+	using WpsConfigMethods =
+	    android::hardware::wifi::supplicant::V1_0::WpsConfigMethods;
+	std::string config_methods_str;
+	for (const auto& flag_and_name :
+	     {std::make_pair(WpsConfigMethods::USBA, "usba"),
+	      {WpsConfigMethods::ETHERNET, "ethernet"},
+	      {WpsConfigMethods::LABEL, "label"},
+	      {WpsConfigMethods::DISPLAY, "display"},
+	      {WpsConfigMethods::INT_NFC_TOKEN, "int_nfc_token"},
+	      {WpsConfigMethods::EXT_NFC_TOKEN, "ext_nfc_token"},
+	      {WpsConfigMethods::NFC_INTERFACE, "nfc_interface"},
+	      {WpsConfigMethods::PUSHBUTTON, "push_button"},
+	      {WpsConfigMethods::KEYPAD, "keypad"},
+	      {WpsConfigMethods::VIRT_PUSHBUTTON, "virtual_push_button"},
+	      {WpsConfigMethods::PHY_PUSHBUTTON, "physical_push_button"},
+	      {WpsConfigMethods::P2PS, "p2ps"},
+	      {WpsConfigMethods::VIRT_DISPLAY, "virtual_display"},
+	      {WpsConfigMethods::PHY_DISPLAY, "physical_display"}}) {
+		const auto flag =
+		    static_cast<std::underlying_type<WpsConfigMethods>::type>(
+			flag_and_name.first);
+		if ((config_methods & flag) == flag) {
+			config_methods_str += flag_and_name.second;
+			config_methods_str += " ";
+		}
+	}
+	return config_methods_str;
+}
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+namespace iface_config_utils {
+SupplicantStatus setWpsDeviceName(
+    struct wpa_supplicant* wpa_s, const std::string& name)
+{
+	WPA_ASSERT(wpa_s);
+	if (freeAndSetStringConfigParam(
+		wpa_s, name, kMaxWpsDeviceNameSize, CFG_CHANGED_DEVICE_NAME,
+		&wpa_s->conf->device_name)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus setWpsDeviceType(
+    struct wpa_supplicant* wpa_s, const std::array<uint8_t, 8>& type)
+{
+	WPA_ASSERT(wpa_s);
+	WPA_ASSERT(type.size() == WPS_DEV_TYPE_LEN);
+	os_memcpy(wpa_s->conf->device_type, type.data(), WPS_DEV_TYPE_LEN);
+	processConfigUpdate(wpa_s, CFG_CHANGED_DEVICE_TYPE);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus setWpsManufacturer(
+    struct wpa_supplicant* wpa_s, const std::string& manufacturer)
+{
+	WPA_ASSERT(wpa_s);
+	if (freeAndSetStringConfigParam(
+		wpa_s, manufacturer, kMaxWpsManufacturerSize,
+		CFG_CHANGED_WPS_STRING, &wpa_s->conf->manufacturer)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus setWpsModelName(
+    struct wpa_supplicant* wpa_s, const std::string& model_name)
+{
+	WPA_ASSERT(wpa_s);
+	if (freeAndSetStringConfigParam(
+		wpa_s, model_name, kMaxWpsModelNameSize, CFG_CHANGED_WPS_STRING,
+		&wpa_s->conf->model_name)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus setWpsModelNumber(
+    struct wpa_supplicant* wpa_s, const std::string& model_number)
+{
+	WPA_ASSERT(wpa_s);
+	if (freeAndSetStringConfigParam(
+		wpa_s, model_number, kMaxWpsModelNumberSize,
+		CFG_CHANGED_WPS_STRING, &wpa_s->conf->model_number)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus setWpsSerialNumber(
+    struct wpa_supplicant* wpa_s, const std::string& serial_number)
+{
+	WPA_ASSERT(wpa_s);
+	if (freeAndSetStringConfigParam(
+		wpa_s, serial_number, kMaxWpsSerialNumberSize,
+		CFG_CHANGED_WPS_STRING, &wpa_s->conf->serial_number)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus setWpsConfigMethods(
+    struct wpa_supplicant* wpa_s, uint16_t config_methods)
+{
+	WPA_ASSERT(wpa_s);
+	if (freeAndSetStringConfigParam(
+		wpa_s, convertWpsConfigMethodsMaskToString(config_methods),
+		UINT32_MAX, CFG_CHANGED_CONFIG_METHODS,
+		&wpa_s->conf->config_methods)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus setExternalSim(
+    struct wpa_supplicant* wpa_s, bool useExternalSim)
+{
+	WPA_ASSERT(wpa_s);
+	wpa_s->conf->external_sim = useExternalSim ? 1 : 0;
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+}  // namespace iface_config_utils
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
diff --git a/wpa_supplicant/hidl/1.0/iface_config_utils.h b/wpa_supplicant/hidl/1.0/iface_config_utils.h
new file mode 100644
index 0000000..789cc38
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/iface_config_utils.h
@@ -0,0 +1,59 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (struct wpa_supplicant* wpa_s, c) 2004-2016, Jouni Malinen
+ * <j@w1.fi>
+ * Copyright (struct wpa_supplicant* wpa_s, c) 2004-2016, Roshan Pius
+ * <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_IFACE_CONFIG_UTILS_H
+#define WPA_SUPPLICANT_HIDL_IFACE_CONFIG_UTILS_H
+
+#include <android-base/macros.h>
+
+extern "C" {
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+}
+
+/**
+ * Utility functions to set various config parameters of an iface via HIDL
+ * methods.
+ */
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+namespace iface_config_utils {
+SupplicantStatus setWpsDeviceName(
+    struct wpa_supplicant* wpa_s, const std::string& name);
+SupplicantStatus setWpsDeviceType(
+    struct wpa_supplicant* wpa_s, const std::array<uint8_t, 8>& type);
+SupplicantStatus setWpsManufacturer(
+    struct wpa_supplicant* wpa_s, const std::string& manufacturer);
+SupplicantStatus setWpsModelName(
+    struct wpa_supplicant* wpa_s, const std::string& model_name);
+SupplicantStatus setWpsModelNumber(
+    struct wpa_supplicant* wpa_s, const std::string& model_number);
+SupplicantStatus setWpsSerialNumber(
+    struct wpa_supplicant* wpa_s, const std::string& serial_number);
+SupplicantStatus setWpsConfigMethods(
+    struct wpa_supplicant* wpa_s, uint16_t config_methods);
+SupplicantStatus setExternalSim(
+    struct wpa_supplicant* wpa_s, bool useExternalSim);
+}  // namespace iface_config_utils
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
+
+#endif  // WPA_SUPPLICANT_HIDL_IFACE_CONFIG_UTILS_H
diff --git a/wpa_supplicant/hidl/1.0/misc_utils.h b/wpa_supplicant/hidl/1.0/misc_utils.h
new file mode 100644
index 0000000..354ec32
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/misc_utils.h
@@ -0,0 +1,72 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MISC_UTILS_H_
+#define MISC_UTILS_H_
+
+extern "C" {
+#include "wpabuf.h"
+}
+
+namespace {
+constexpr size_t kWpsPinNumDigits = 8;
+// Custom deleter for wpabuf.
+void freeWpaBuf(wpabuf *ptr) { wpabuf_free(ptr); }
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+namespace misc_utils {
+using wpabuf_unique_ptr = std::unique_ptr<wpabuf, void (*)(wpabuf *)>;
+
+// Creates a unique_ptr for wpabuf ptr with a custom deleter.
+inline wpabuf_unique_ptr createWpaBufUniquePtr(struct wpabuf *raw_ptr)
+{
+	return {raw_ptr, freeWpaBuf};
+}
+
+// Creates a wpabuf ptr with a custom deleter copying the data from the provided
+// vector.
+inline wpabuf_unique_ptr convertVectorToWpaBuf(const std::vector<uint8_t> &data)
+{
+	return createWpaBufUniquePtr(
+	    wpabuf_alloc_copy(data.data(), data.size()));
+}
+
+// Copies the provided wpabuf contents to a std::vector.
+inline std::vector<uint8_t> convertWpaBufToVector(const struct wpabuf *buf)
+{
+	if (buf) {
+		return std::vector<uint8_t>(
+		    wpabuf_head_u8(buf), wpabuf_head_u8(buf) + wpabuf_len(buf));
+	} else {
+		return std::vector<uint8_t>();
+	}
+}
+
+// Returns a string holding the wps pin.
+inline std::string convertWpsPinToString(int pin)
+{
+	char pin_str[kWpsPinNumDigits + 1];
+	snprintf(pin_str, sizeof(pin_str), "%08d", pin);
+	return pin_str;
+}
+
+}  // namespace misc_utils
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace supplicant
+}  // namespace wifi
+}  // namespace hardware
+}  // namespace android
+#endif  // MISC_UTILS_H_
diff --git a/wpa_supplicant/hidl/1.0/p2p_iface.cpp b/wpa_supplicant/hidl/1.0/p2p_iface.cpp
new file mode 100644
index 0000000..ab4014d
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/p2p_iface.cpp
@@ -0,0 +1,1282 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ * Copyright (C) 2017 Sony Mobile Communications Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "hidl_manager.h"
+#include "hidl_return_util.h"
+#include "iface_config_utils.h"
+#include "misc_utils.h"
+#include "p2p_iface.h"
+
+extern "C" {
+#include "ap.h"
+#include "wps_supplicant.h"
+#include "wifi_display.h"
+}
+
+namespace {
+const char kConfigMethodStrPbc[] = "pbc";
+const char kConfigMethodStrDisplay[] = "display";
+const char kConfigMethodStrKeypad[] = "keypad";
+constexpr char kSetMiracastMode[] = "MIRACAST ";
+constexpr uint8_t kWfdDeviceInfoSubelemId = 0;
+constexpr char kWfdDeviceInfoSubelemLenHexStr[] = "0006";
+
+using android::hardware::wifi::supplicant::V1_0::ISupplicantP2pIface;
+uint8_t convertHidlMiracastModeToInternal(
+    ISupplicantP2pIface::MiracastMode mode)
+{
+	switch (mode) {
+	case ISupplicantP2pIface::MiracastMode::DISABLED:
+		return 0;
+	case ISupplicantP2pIface::MiracastMode::SOURCE:
+		return 1;
+	case ISupplicantP2pIface::MiracastMode::SINK:
+		return 2;
+	};
+	WPA_ASSERT(false);
+}
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+using hidl_return_util::validateAndCall;
+
+P2pIface::P2pIface(struct wpa_global* wpa_global, const char ifname[])
+    : wpa_global_(wpa_global), ifname_(ifname), is_valid_(true)
+{
+}
+
+void P2pIface::invalidate() { is_valid_ = false; }
+bool P2pIface::isValid()
+{
+	return (is_valid_ && (retrieveIfacePtr() != nullptr));
+}
+Return<void> P2pIface::getName(getName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::getNameInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::getType(getType_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::getTypeInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::addNetwork(addNetwork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::addNetworkInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::removeNetwork(
+    SupplicantNetworkId id, removeNetwork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::removeNetworkInternal, _hidl_cb, id);
+}
+
+Return<void> P2pIface::getNetwork(
+    SupplicantNetworkId id, getNetwork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::getNetworkInternal, _hidl_cb, id);
+}
+
+Return<void> P2pIface::listNetworks(listNetworks_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::listNetworksInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::registerCallback(
+    const sp<ISupplicantP2pIfaceCallback>& callback,
+    registerCallback_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> P2pIface::getDeviceAddress(getDeviceAddress_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::getDeviceAddressInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::setSsidPostfix(
+    const hidl_vec<uint8_t>& postfix, setSsidPostfix_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setSsidPostfixInternal, _hidl_cb, postfix);
+}
+
+Return<void> P2pIface::setGroupIdle(
+    const hidl_string& group_ifname, uint32_t timeout_in_sec,
+    setGroupIdle_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setGroupIdleInternal, _hidl_cb, group_ifname,
+	    timeout_in_sec);
+}
+
+Return<void> P2pIface::setPowerSave(
+    const hidl_string& group_ifname, bool enable, setPowerSave_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setPowerSaveInternal, _hidl_cb, group_ifname, enable);
+}
+
+Return<void> P2pIface::find(uint32_t timeout_in_sec, find_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::findInternal, _hidl_cb, timeout_in_sec);
+}
+
+Return<void> P2pIface::stopFind(stopFind_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::stopFindInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::flush(flush_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::flushInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::connect(
+    const hidl_array<uint8_t, 6>& peer_address,
+    ISupplicantP2pIface::WpsProvisionMethod provision_method,
+    const hidl_string& pre_selected_pin, bool join_existing_group,
+    bool persistent, uint32_t go_intent, connect_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::connectInternal, _hidl_cb, peer_address,
+	    provision_method, pre_selected_pin, join_existing_group, persistent,
+	    go_intent);
+}
+
+Return<void> P2pIface::cancelConnect(cancelConnect_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::cancelConnectInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::provisionDiscovery(
+    const hidl_array<uint8_t, 6>& peer_address,
+    ISupplicantP2pIface::WpsProvisionMethod provision_method,
+    provisionDiscovery_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::provisionDiscoveryInternal, _hidl_cb, peer_address,
+	    provision_method);
+}
+
+Return<void> P2pIface::addGroup(
+    bool persistent, SupplicantNetworkId persistent_network_id,
+    addGroup_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::addGroupInternal, _hidl_cb, persistent,
+	    persistent_network_id);
+}
+
+Return<void> P2pIface::removeGroup(
+    const hidl_string& group_ifname, removeGroup_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::removeGroupInternal, _hidl_cb, group_ifname);
+}
+
+Return<void> P2pIface::reject(
+    const hidl_array<uint8_t, 6>& peer_address, reject_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::rejectInternal, _hidl_cb, peer_address);
+}
+
+Return<void> P2pIface::invite(
+    const hidl_string& group_ifname,
+    const hidl_array<uint8_t, 6>& go_device_address,
+    const hidl_array<uint8_t, 6>& peer_address, invite_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::inviteInternal, _hidl_cb, group_ifname,
+	    go_device_address, peer_address);
+}
+
+Return<void> P2pIface::reinvoke(
+    SupplicantNetworkId persistent_network_id,
+    const hidl_array<uint8_t, 6>& peer_address, reinvoke_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::reinvokeInternal, _hidl_cb, persistent_network_id,
+	    peer_address);
+}
+
+Return<void> P2pIface::configureExtListen(
+    uint32_t period_in_millis, uint32_t interval_in_millis,
+    configureExtListen_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::configureExtListenInternal, _hidl_cb, period_in_millis,
+	    interval_in_millis);
+}
+
+Return<void> P2pIface::setListenChannel(
+    uint32_t channel, uint32_t operating_class, setListenChannel_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setListenChannelInternal, _hidl_cb, channel,
+	    operating_class);
+}
+
+Return<void> P2pIface::setDisallowedFrequencies(
+    const hidl_vec<FreqRange>& ranges, setDisallowedFrequencies_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setDisallowedFrequenciesInternal, _hidl_cb, ranges);
+}
+
+Return<void> P2pIface::getSsid(
+    const hidl_array<uint8_t, 6>& peer_address, getSsid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::getSsidInternal, _hidl_cb, peer_address);
+}
+
+Return<void> P2pIface::getGroupCapability(
+    const hidl_array<uint8_t, 6>& peer_address, getGroupCapability_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::getGroupCapabilityInternal, _hidl_cb, peer_address);
+}
+
+Return<void> P2pIface::addBonjourService(
+    const hidl_vec<uint8_t>& query, const hidl_vec<uint8_t>& response,
+    addBonjourService_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::addBonjourServiceInternal, _hidl_cb, query, response);
+}
+
+Return<void> P2pIface::removeBonjourService(
+    const hidl_vec<uint8_t>& query, removeBonjourService_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::removeBonjourServiceInternal, _hidl_cb, query);
+}
+
+Return<void> P2pIface::addUpnpService(
+    uint32_t version, const hidl_string& service_name,
+    addUpnpService_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::addUpnpServiceInternal, _hidl_cb, version, service_name);
+}
+
+Return<void> P2pIface::removeUpnpService(
+    uint32_t version, const hidl_string& service_name,
+    removeUpnpService_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::removeUpnpServiceInternal, _hidl_cb, version,
+	    service_name);
+}
+
+Return<void> P2pIface::flushServices(flushServices_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::flushServicesInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::requestServiceDiscovery(
+    const hidl_array<uint8_t, 6>& peer_address, const hidl_vec<uint8_t>& query,
+    requestServiceDiscovery_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::requestServiceDiscoveryInternal, _hidl_cb, peer_address,
+	    query);
+}
+
+Return<void> P2pIface::cancelServiceDiscovery(
+    uint64_t identifier, cancelServiceDiscovery_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::cancelServiceDiscoveryInternal, _hidl_cb, identifier);
+}
+
+Return<void> P2pIface::setMiracastMode(
+    ISupplicantP2pIface::MiracastMode mode, setMiracastMode_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setMiracastModeInternal, _hidl_cb, mode);
+}
+
+Return<void> P2pIface::startWpsPbc(
+    const hidl_string& group_ifname, const hidl_array<uint8_t, 6>& bssid,
+    startWpsPbc_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::startWpsPbcInternal, _hidl_cb, group_ifname, bssid);
+}
+
+Return<void> P2pIface::startWpsPinKeypad(
+    const hidl_string& group_ifname, const hidl_string& pin,
+    startWpsPinKeypad_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::startWpsPinKeypadInternal, _hidl_cb, group_ifname, pin);
+}
+
+Return<void> P2pIface::startWpsPinDisplay(
+    const hidl_string& group_ifname, const hidl_array<uint8_t, 6>& bssid,
+    startWpsPinDisplay_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::startWpsPinDisplayInternal, _hidl_cb, group_ifname,
+	    bssid);
+}
+
+Return<void> P2pIface::cancelWps(
+    const hidl_string& group_ifname, cancelWps_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::cancelWpsInternal, _hidl_cb, group_ifname);
+}
+
+Return<void> P2pIface::setWpsDeviceName(
+    const hidl_string& name, setWpsDeviceName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWpsDeviceNameInternal, _hidl_cb, name);
+}
+
+Return<void> P2pIface::setWpsDeviceType(
+    const hidl_array<uint8_t, 8>& type, setWpsDeviceType_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWpsDeviceTypeInternal, _hidl_cb, type);
+}
+
+Return<void> P2pIface::setWpsManufacturer(
+    const hidl_string& manufacturer, setWpsManufacturer_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWpsManufacturerInternal, _hidl_cb, manufacturer);
+}
+
+Return<void> P2pIface::setWpsModelName(
+    const hidl_string& model_name, setWpsModelName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWpsModelNameInternal, _hidl_cb, model_name);
+}
+
+Return<void> P2pIface::setWpsModelNumber(
+    const hidl_string& model_number, setWpsModelNumber_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWpsModelNumberInternal, _hidl_cb, model_number);
+}
+
+Return<void> P2pIface::setWpsSerialNumber(
+    const hidl_string& serial_number, setWpsSerialNumber_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWpsSerialNumberInternal, _hidl_cb, serial_number);
+}
+
+Return<void> P2pIface::setWpsConfigMethods(
+    uint16_t config_methods, setWpsConfigMethods_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWpsConfigMethodsInternal, _hidl_cb, config_methods);
+}
+
+Return<void> P2pIface::enableWfd(bool enable, enableWfd_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::enableWfdInternal, _hidl_cb, enable);
+}
+
+Return<void> P2pIface::setWfdDeviceInfo(
+    const hidl_array<uint8_t, 6>& info, setWfdDeviceInfo_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::setWfdDeviceInfoInternal, _hidl_cb, info);
+}
+
+Return<void> P2pIface::createNfcHandoverRequestMessage(
+    createNfcHandoverRequestMessage_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::createNfcHandoverRequestMessageInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::createNfcHandoverSelectMessage(
+    createNfcHandoverSelectMessage_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::createNfcHandoverSelectMessageInternal, _hidl_cb);
+}
+
+Return<void> P2pIface::reportNfcHandoverResponse(
+    const hidl_vec<uint8_t>& request, reportNfcHandoverResponse_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::reportNfcHandoverResponseInternal, _hidl_cb, request);
+}
+
+Return<void> P2pIface::reportNfcHandoverInitiation(
+    const hidl_vec<uint8_t>& select, reportNfcHandoverInitiation_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::reportNfcHandoverInitiationInternal, _hidl_cb, select);
+}
+
+Return<void> P2pIface::saveConfig(saveConfig_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &P2pIface::saveConfigInternal, _hidl_cb);
+}
+
+std::pair<SupplicantStatus, std::string> P2pIface::getNameInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, ifname_};
+}
+
+std::pair<SupplicantStatus, IfaceType> P2pIface::getTypeInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, IfaceType::P2P};
+}
+
+std::pair<SupplicantStatus, sp<ISupplicantP2pNetwork>>
+P2pIface::addNetworkInternal()
+{
+	android::sp<ISupplicantP2pNetwork> network;
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	struct wpa_ssid* ssid = wpa_supplicant_add_network(wpa_s);
+	if (!ssid) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
+	}
+	HidlManager* hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->getP2pNetworkHidlObjectByIfnameAndNetworkId(
+		wpa_s->ifname, ssid->id, &network)) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, network};
+}
+
+SupplicantStatus P2pIface::removeNetworkInternal(SupplicantNetworkId id)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	int result = wpa_supplicant_remove_network(wpa_s, id);
+	if (result == -1) {
+		return {SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN, ""};
+	}
+	if (result != 0) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, sp<ISupplicantP2pNetwork>>
+P2pIface::getNetworkInternal(SupplicantNetworkId id)
+{
+	android::sp<ISupplicantP2pNetwork> network;
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	struct wpa_ssid* ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (!ssid) {
+		return {{SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN, ""},
+			network};
+	}
+	HidlManager* hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->getP2pNetworkHidlObjectByIfnameAndNetworkId(
+		wpa_s->ifname, ssid->id, &network)) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, network};
+}
+
+std::pair<SupplicantStatus, std::vector<SupplicantNetworkId>>
+P2pIface::listNetworksInternal()
+{
+	std::vector<SupplicantNetworkId> network_ids;
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	for (struct wpa_ssid* wpa_ssid = wpa_s->conf->ssid; wpa_ssid;
+	     wpa_ssid = wpa_ssid->next) {
+		network_ids.emplace_back(wpa_ssid->id);
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, std::move(network_ids)};
+}
+
+SupplicantStatus P2pIface::registerCallbackInternal(
+    const sp<ISupplicantP2pIfaceCallback>& callback)
+{
+	HidlManager* hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->addP2pIfaceCallbackHidlObject(ifname_, callback)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::array<uint8_t, 6>>
+P2pIface::getDeviceAddressInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	std::array<uint8_t, 6> addr;
+	static_assert(ETH_ALEN == addr.size(), "Size mismatch");
+	os_memcpy(addr.data(), wpa_s->global->p2p_dev_addr, ETH_ALEN);
+	return {{SupplicantStatusCode::SUCCESS, ""}, addr};
+}
+
+SupplicantStatus P2pIface::setSsidPostfixInternal(
+    const std::vector<uint8_t>& postfix)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (p2p_set_ssid_postfix(
+		wpa_s->global->p2p, postfix.data(), postfix.size())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::setGroupIdleInternal(
+    const std::string& group_ifname, uint32_t timeout_in_sec)
+{
+	struct wpa_supplicant* wpa_group_s =
+	    retrieveGroupIfacePtr(group_ifname);
+	if (!wpa_group_s) {
+		return {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+	}
+	wpa_group_s->conf->p2p_group_idle = timeout_in_sec;
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::setPowerSaveInternal(
+    const std::string& group_ifname, bool enable)
+{
+	struct wpa_supplicant* wpa_group_s =
+	    retrieveGroupIfacePtr(group_ifname);
+	if (!wpa_group_s) {
+		return {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+	}
+	if (wpa_drv_set_p2p_powersave(wpa_group_s, enable, -1, -1)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::findInternal(uint32_t timeout_in_sec)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
+	}
+	uint32_t search_delay = wpas_p2p_search_delay(wpa_s);
+	if (wpas_p2p_find(
+		wpa_s, timeout_in_sec, P2P_FIND_START_WITH_FULL, 0, nullptr,
+		nullptr, search_delay, 0, nullptr, 0)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::stopFindInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
+	}
+	wpas_p2p_stop_find(wpa_s);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::flushInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
+	wpa_s->force_long_sd = 0;
+	wpas_p2p_stop_find(wpa_s);
+	wpa_s->parent->p2ps_method_config_any = 0;
+	if (wpa_s->global->p2p)
+		p2p_flush(wpa_s->global->p2p);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+// This method only implements support for subset (needed by Android framework)
+// of parameters that can be specified for connect.
+std::pair<SupplicantStatus, std::string> P2pIface::connectInternal(
+    const std::array<uint8_t, 6>& peer_address,
+    ISupplicantP2pIface::WpsProvisionMethod provision_method,
+    const std::string& pre_selected_pin, bool join_existing_group,
+    bool persistent, uint32_t go_intent)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (go_intent > 15) {
+		return {{SupplicantStatusCode::FAILURE_ARGS_INVALID, ""}, {}};
+	}
+	int go_intent_signed = join_existing_group ? -1 : go_intent;
+	p2p_wps_method wps_method = {};
+	switch (provision_method) {
+	case WpsProvisionMethod::PBC:
+		wps_method = WPS_PBC;
+		break;
+	case WpsProvisionMethod::DISPLAY:
+		wps_method = WPS_PIN_DISPLAY;
+		break;
+	case WpsProvisionMethod::KEYPAD:
+		wps_method = WPS_PIN_KEYPAD;
+		break;
+	}
+	const char* pin = pre_selected_pin.length() > 0 ? pre_selected_pin.data() : nullptr;
+	int new_pin = wpas_p2p_connect(
+	    wpa_s, peer_address.data(), pin, wps_method,
+	    persistent, false, join_existing_group, false, go_intent_signed, 0, 0, -1,
+	    false, false, false, VHT_CHANWIDTH_USE_HT, nullptr, 0);
+	if (new_pin < 0) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	std::string pin_ret;
+	if (provision_method == WpsProvisionMethod::DISPLAY &&
+	    pre_selected_pin.empty()) {
+		pin_ret = misc_utils::convertWpsPinToString(new_pin);
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, pin_ret};
+}
+
+SupplicantStatus P2pIface::cancelConnectInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpas_p2p_cancel(wpa_s)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::provisionDiscoveryInternal(
+    const std::array<uint8_t, 6>& peer_address,
+    ISupplicantP2pIface::WpsProvisionMethod provision_method)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	p2ps_provision* prov_param;
+	const char* config_method_str = nullptr;
+	switch (provision_method) {
+	case WpsProvisionMethod::PBC:
+		config_method_str = kConfigMethodStrPbc;
+		break;
+	case WpsProvisionMethod::DISPLAY:
+		config_method_str = kConfigMethodStrDisplay;
+		break;
+	case WpsProvisionMethod::KEYPAD:
+		config_method_str = kConfigMethodStrKeypad;
+		break;
+	}
+	if (wpas_p2p_prov_disc(
+		wpa_s, peer_address.data(), config_method_str,
+		WPAS_P2P_PD_FOR_GO_NEG, nullptr)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::addGroupInternal(
+    bool persistent, SupplicantNetworkId persistent_network_id)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	int vht = wpa_s->conf->p2p_go_vht;
+	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	struct wpa_ssid* ssid =
+	    wpa_config_get_network(wpa_s->conf, persistent_network_id);
+	if (ssid == NULL) {
+		if (wpas_p2p_group_add(
+			wpa_s, persistent, 0, 0, ht40, vht,
+			VHT_CHANWIDTH_USE_HT)) {
+			return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+		} else {
+			return {SupplicantStatusCode::SUCCESS, ""};
+		}
+	} else if (ssid->disabled == 2) {
+		if (wpas_p2p_group_add_persistent(
+			wpa_s, ssid, 0, 0, 0, 0, ht40, vht,
+			VHT_CHANWIDTH_USE_HT, NULL, 0, 0)) {
+			return {SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN,
+				""};
+		} else {
+			return {SupplicantStatusCode::SUCCESS, ""};
+		}
+	}
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+SupplicantStatus P2pIface::removeGroupInternal(const std::string& group_ifname)
+{
+	struct wpa_supplicant* wpa_group_s =
+	    retrieveGroupIfacePtr(group_ifname);
+	if (!wpa_group_s) {
+		return {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+	}
+	if (wpas_p2p_group_remove(wpa_group_s, group_ifname.c_str())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::rejectInternal(
+    const std::array<uint8_t, 6>& peer_address)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
+	}
+	if (wpas_p2p_reject(wpa_s, peer_address.data())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::inviteInternal(
+    const std::string& group_ifname,
+    const std::array<uint8_t, 6>& go_device_address,
+    const std::array<uint8_t, 6>& peer_address)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpas_p2p_invite_group(
+		wpa_s, group_ifname.c_str(), peer_address.data(),
+		go_device_address.data())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::reinvokeInternal(
+    SupplicantNetworkId persistent_network_id,
+    const std::array<uint8_t, 6>& peer_address)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	int vht = wpa_s->conf->p2p_go_vht;
+	int ht40 = wpa_s->conf->p2p_go_ht40 || vht;
+	struct wpa_ssid* ssid =
+	    wpa_config_get_network(wpa_s->conf, persistent_network_id);
+	if (ssid == NULL || ssid->disabled != 2) {
+		return {SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN, ""};
+	}
+	if (wpas_p2p_invite(
+		wpa_s, peer_address.data(), ssid, NULL, 0, 0, ht40, vht,
+		VHT_CHANWIDTH_USE_HT, 0)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::configureExtListenInternal(
+    uint32_t period_in_millis, uint32_t interval_in_millis)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpas_p2p_ext_listen(wpa_s, period_in_millis, interval_in_millis)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::setListenChannelInternal(
+    uint32_t channel, uint32_t operating_class)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (p2p_set_listen_channel(
+		wpa_s->global->p2p, operating_class, channel, 1)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::setDisallowedFrequenciesInternal(
+    const std::vector<FreqRange>& ranges)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	using DestT = struct wpa_freq_range_list::wpa_freq_range;
+	DestT* freq_ranges = nullptr;
+	// Empty ranges is used to enable all frequencies.
+	if (ranges.size() != 0) {
+		freq_ranges =
+		    static_cast<DestT*>(os_malloc(sizeof(DestT) * ranges.size()));
+		if (!freq_ranges) {
+			return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+		}
+		uint32_t i = 0;
+		for (const auto& range : ranges) {
+			freq_ranges[i].min = range.min;
+			freq_ranges[i].max = range.max;
+			i++;
+		}
+	}
+
+	os_free(wpa_s->global->p2p_disallow_freq.range);
+	wpa_s->global->p2p_disallow_freq.range = freq_ranges;
+	wpa_s->global->p2p_disallow_freq.num = ranges.size();
+	wpas_p2p_update_channel_list(wpa_s, WPAS_P2P_CHANNEL_UPDATE_DISALLOW);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>> P2pIface::getSsidInternal(
+    const std::array<uint8_t, 6>& peer_address)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	const struct p2p_peer_info* info =
+	    p2p_get_peer_info(wpa_s->global->p2p, peer_address.data(), 0);
+	if (!info) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	const struct p2p_device* dev =
+	    reinterpret_cast<const struct p2p_device*>(
+		(reinterpret_cast<const uint8_t*>(info)) -
+		offsetof(struct p2p_device, info));
+	std::vector<uint8_t> ssid;
+	if (dev && dev->oper_ssid_len) {
+		ssid.assign(
+		    dev->oper_ssid, dev->oper_ssid + dev->oper_ssid_len);
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, ssid};
+}
+
+std::pair<SupplicantStatus, uint32_t> P2pIface::getGroupCapabilityInternal(
+    const std::array<uint8_t, 6>& peer_address)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	const struct p2p_peer_info* info =
+	    p2p_get_peer_info(wpa_s->global->p2p, peer_address.data(), 0);
+	if (!info) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, info->group_capab};
+}
+
+SupplicantStatus P2pIface::addBonjourServiceInternal(
+    const std::vector<uint8_t>& query, const std::vector<uint8_t>& response)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	auto query_buf = misc_utils::convertVectorToWpaBuf(query);
+	auto response_buf = misc_utils::convertVectorToWpaBuf(response);
+	if (!query_buf || !response_buf) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	if (wpas_p2p_service_add_bonjour(
+		wpa_s, query_buf.get(), response_buf.get())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	// If successful, the wpabuf is referenced internally and hence should
+	// not be freed.
+	query_buf.release();
+	response_buf.release();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::removeBonjourServiceInternal(
+    const std::vector<uint8_t>& query)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	auto query_buf = misc_utils::convertVectorToWpaBuf(query);
+	if (!query_buf) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	if (wpas_p2p_service_del_bonjour(wpa_s, query_buf.get())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::addUpnpServiceInternal(
+    uint32_t version, const std::string& service_name)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpas_p2p_service_add_upnp(wpa_s, version, service_name.c_str())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::removeUpnpServiceInternal(
+    uint32_t version, const std::string& service_name)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpas_p2p_service_del_upnp(wpa_s, version, service_name.c_str())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::flushServicesInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	wpas_p2p_service_flush(wpa_s);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, uint64_t> P2pIface::requestServiceDiscoveryInternal(
+    const std::array<uint8_t, 6>& peer_address,
+    const std::vector<uint8_t>& query)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	auto query_buf = misc_utils::convertVectorToWpaBuf(query);
+	if (!query_buf) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	const uint8_t* dst_addr = is_zero_ether_addr(peer_address.data())
+				      ? nullptr
+				      : peer_address.data();
+	uint64_t identifier =
+	    wpas_p2p_sd_request(wpa_s, dst_addr, query_buf.get());
+	if (identifier == 0) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, identifier};
+}
+
+SupplicantStatus P2pIface::cancelServiceDiscoveryInternal(uint64_t identifier)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (wpas_p2p_sd_cancel_request(wpa_s, identifier)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::setMiracastModeInternal(
+    ISupplicantP2pIface::MiracastMode mode)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	uint8_t mode_internal = convertHidlMiracastModeToInternal(mode);
+	const std::string cmd_str =
+	    kSetMiracastMode + std::to_string(mode_internal);
+	std::vector<char> cmd(
+	    cmd_str.c_str(), cmd_str.c_str() + cmd_str.size() + 1);
+	char driver_cmd_reply_buf[4096] = {};
+	if (wpa_drv_driver_cmd(
+		wpa_s, cmd.data(), driver_cmd_reply_buf,
+		sizeof(driver_cmd_reply_buf))) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::startWpsPbcInternal(
+    const std::string& group_ifname, const std::array<uint8_t, 6>& bssid)
+{
+	struct wpa_supplicant* wpa_group_s =
+	    retrieveGroupIfacePtr(group_ifname);
+	if (!wpa_group_s) {
+		return {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+	}
+	const uint8_t* bssid_addr =
+	    is_zero_ether_addr(bssid.data()) ? nullptr : bssid.data();
+#ifdef CONFIG_AP
+	if (wpa_group_s->ap_iface) {
+		if (wpa_supplicant_ap_wps_pbc(wpa_group_s, bssid_addr, NULL)) {
+			return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+		}
+		return {SupplicantStatusCode::SUCCESS, ""};
+	}
+#endif /* CONFIG_AP */
+	if (wpas_wps_start_pbc(wpa_group_s, bssid_addr, 0)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::startWpsPinKeypadInternal(
+    const std::string& group_ifname, const std::string& pin)
+{
+	struct wpa_supplicant* wpa_group_s =
+	    retrieveGroupIfacePtr(group_ifname);
+	if (!wpa_group_s) {
+		return {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+	}
+#ifdef CONFIG_AP
+	if (wpa_group_s->ap_iface) {
+		if (wpa_supplicant_ap_wps_pin(
+				wpa_group_s, nullptr, pin.c_str(), nullptr, 0, 0) < 0) {
+			return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+		}
+		return {SupplicantStatusCode::SUCCESS, ""};
+	}
+#endif /* CONFIG_AP */
+	if (wpas_wps_start_pin(
+		wpa_group_s, nullptr, pin.c_str(), 0, DEV_PW_DEFAULT)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::string> P2pIface::startWpsPinDisplayInternal(
+    const std::string& group_ifname, const std::array<uint8_t, 6>& bssid)
+{
+	struct wpa_supplicant* wpa_group_s =
+	    retrieveGroupIfacePtr(group_ifname);
+	if (!wpa_group_s) {
+		return {{SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""}, ""};
+	}
+	const uint8_t* bssid_addr =
+	    is_zero_ether_addr(bssid.data()) ? nullptr : bssid.data();
+	int pin = wpas_wps_start_pin(
+	    wpa_group_s, bssid_addr, nullptr, 0, DEV_PW_DEFAULT);
+	if (pin < 0) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, ""};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		misc_utils::convertWpsPinToString(pin)};
+}
+
+SupplicantStatus P2pIface::cancelWpsInternal(const std::string& group_ifname)
+{
+	struct wpa_supplicant* wpa_group_s =
+	    retrieveGroupIfacePtr(group_ifname);
+	if (!wpa_group_s) {
+		return {SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""};
+	}
+	if (wpas_wps_cancel(wpa_group_s)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::setWpsDeviceNameInternal(const std::string& name)
+{
+	return iface_config_utils::setWpsDeviceName(retrieveIfacePtr(), name);
+}
+
+SupplicantStatus P2pIface::setWpsDeviceTypeInternal(
+    const std::array<uint8_t, 8>& type)
+{
+	return iface_config_utils::setWpsDeviceType(retrieveIfacePtr(), type);
+}
+
+SupplicantStatus P2pIface::setWpsManufacturerInternal(
+    const std::string& manufacturer)
+{
+	return iface_config_utils::setWpsManufacturer(
+	    retrieveIfacePtr(), manufacturer);
+}
+
+SupplicantStatus P2pIface::setWpsModelNameInternal(
+    const std::string& model_name)
+{
+	return iface_config_utils::setWpsModelName(
+	    retrieveIfacePtr(), model_name);
+}
+
+SupplicantStatus P2pIface::setWpsModelNumberInternal(
+    const std::string& model_number)
+{
+	return iface_config_utils::setWpsModelNumber(
+	    retrieveIfacePtr(), model_number);
+}
+
+SupplicantStatus P2pIface::setWpsSerialNumberInternal(
+    const std::string& serial_number)
+{
+	return iface_config_utils::setWpsSerialNumber(
+	    retrieveIfacePtr(), serial_number);
+}
+
+SupplicantStatus P2pIface::setWpsConfigMethodsInternal(uint16_t config_methods)
+{
+	return iface_config_utils::setWpsConfigMethods(
+	    retrieveIfacePtr(), config_methods);
+}
+
+SupplicantStatus P2pIface::enableWfdInternal(bool enable)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	wifi_display_enable(wpa_s->global, enable);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::setWfdDeviceInfoInternal(
+    const std::array<uint8_t, 6>& info)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	std::vector<char> wfd_device_info_hex(info.size() * 2 + 1);
+	wpa_snprintf_hex(
+	    wfd_device_info_hex.data(), wfd_device_info_hex.size(), info.data(),
+	    info.size());
+	// |wifi_display_subelem_set| expects the first 2 bytes
+	// to hold the lenght of the subelement. In this case it's
+	// fixed to 6, so prepend that.
+	std::string wfd_device_info_set_cmd_str =
+	    std::to_string(kWfdDeviceInfoSubelemId) + " " +
+	    kWfdDeviceInfoSubelemLenHexStr + wfd_device_info_hex.data();
+	std::vector<char> wfd_device_info_set_cmd(
+	    wfd_device_info_set_cmd_str.c_str(),
+	    wfd_device_info_set_cmd_str.c_str() +
+		wfd_device_info_set_cmd_str.size() + 1);
+	if (wifi_display_subelem_set(
+		wpa_s->global, wfd_device_info_set_cmd.data())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>>
+P2pIface::createNfcHandoverRequestMessageInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	auto buf = misc_utils::createWpaBufUniquePtr(
+	    wpas_p2p_nfc_handover_req(wpa_s, 1));
+	if (!buf) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		misc_utils::convertWpaBufToVector(buf.get())};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>>
+P2pIface::createNfcHandoverSelectMessageInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	auto buf = misc_utils::createWpaBufUniquePtr(
+	    wpas_p2p_nfc_handover_sel(wpa_s, 1, 0));
+	if (!buf) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		misc_utils::convertWpaBufToVector(buf.get())};
+}
+
+SupplicantStatus P2pIface::reportNfcHandoverResponseInternal(
+    const std::vector<uint8_t>& request)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	auto req = misc_utils::convertVectorToWpaBuf(request);
+	auto sel = misc_utils::convertVectorToWpaBuf(std::vector<uint8_t>{0});
+	if (!req || !sel) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+
+	if (wpas_p2p_nfc_report_handover(wpa_s, 0, req.get(), sel.get(), 0)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::reportNfcHandoverInitiationInternal(
+    const std::vector<uint8_t>& select)
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	auto req = misc_utils::convertVectorToWpaBuf(std::vector<uint8_t>{0});
+	auto sel = misc_utils::convertVectorToWpaBuf(select);
+	if (!req || !sel) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+
+	if (wpas_p2p_nfc_report_handover(wpa_s, 1, req.get(), sel.get(), 0)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus P2pIface::saveConfigInternal()
+{
+	struct wpa_supplicant* wpa_s = retrieveIfacePtr();
+	if (!wpa_s->conf->update_config) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	if (wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+/**
+ * Retrieve the underlying |wpa_supplicant| struct
+ * pointer for this iface.
+ * If the underlying iface is removed, then all RPC method calls on this object
+ * will return failure.
+ */
+wpa_supplicant* P2pIface::retrieveIfacePtr()
+{
+	return wpa_supplicant_get_iface(wpa_global_, ifname_.c_str());
+}
+
+/**
+ * Retrieve the underlying |wpa_supplicant| struct
+ * pointer for this group iface.
+ */
+wpa_supplicant* P2pIface::retrieveGroupIfacePtr(const std::string& group_ifname)
+{
+	return wpa_supplicant_get_iface(wpa_global_, group_ifname.c_str());
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
diff --git a/wpa_supplicant/hidl/1.0/p2p_iface.h b/wpa_supplicant/hidl/1.0/p2p_iface.h
new file mode 100644
index 0000000..4f4a79d
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/p2p_iface.h
@@ -0,0 +1,314 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_P2P_IFACE_H
+#define WPA_SUPPLICANT_HIDL_P2P_IFACE_H
+
+#include <array>
+#include <vector>
+
+#include <android-base/macros.h>
+
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pIface.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetwork.h>
+
+extern "C" {
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "p2p/p2p.h"
+#include "p2p/p2p_i.h"
+#include "p2p_supplicant.h"
+#include "p2p_supplicant.h"
+#include "config.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Implementation of P2pIface hidl object. Each unique hidl
+ * object is used for control operations on a specific interface
+ * controlled by wpa_supplicant.
+ */
+class P2pIface
+    : public android::hardware::wifi::supplicant::V1_0::ISupplicantP2pIface
+{
+public:
+	P2pIface(struct wpa_global* wpa_global, const char ifname[]);
+	~P2pIface() override = default;
+	// Refer to |StaIface::invalidate()|.
+	void invalidate();
+	bool isValid();
+
+	// Hidl methods exposed.
+	Return<void> getName(getName_cb _hidl_cb) override;
+	Return<void> getType(getType_cb _hidl_cb) override;
+	Return<void> addNetwork(addNetwork_cb _hidl_cb) override;
+	Return<void> removeNetwork(
+	    SupplicantNetworkId id, removeNetwork_cb _hidl_cb) override;
+	Return<void> getNetwork(
+	    SupplicantNetworkId id, getNetwork_cb _hidl_cb) override;
+	Return<void> listNetworks(listNetworks_cb _hidl_cb) override;
+	Return<void> registerCallback(
+	    const sp<ISupplicantP2pIfaceCallback>& callback,
+	    registerCallback_cb _hidl_cb) override;
+	Return<void> getDeviceAddress(getDeviceAddress_cb _hidl_cb) override;
+	Return<void> setSsidPostfix(
+	    const hidl_vec<uint8_t>& postfix,
+	    setSsidPostfix_cb _hidl_cb) override;
+	Return<void> setGroupIdle(
+	    const hidl_string& group_ifname, uint32_t timeout_in_sec,
+	    setGroupIdle_cb _hidl_cb) override;
+	Return<void> setPowerSave(
+	    const hidl_string& group_ifname, bool enable,
+	    setPowerSave_cb _hidl_cb) override;
+	Return<void> find(uint32_t timeout_in_sec, find_cb _hidl_cb) override;
+	Return<void> stopFind(stopFind_cb _hidl_cb) override;
+	Return<void> flush(flush_cb _hidl_cb) override;
+	Return<void> connect(
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    ISupplicantP2pIface::WpsProvisionMethod provision_method,
+	    const hidl_string& pre_selected_pin, bool join_existing_group,
+	    bool persistent, uint32_t go_intent, connect_cb _hidl_cb) override;
+	Return<void> cancelConnect(cancelConnect_cb _hidl_cb) override;
+	Return<void> provisionDiscovery(
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    ISupplicantP2pIface::WpsProvisionMethod provision_method,
+	    provisionDiscovery_cb _hidl_cb) override;
+	Return<void> addGroup(
+	    bool persistent, SupplicantNetworkId persistent_network_id,
+	    addGroup_cb _hidl_cb) override;
+	Return<void> removeGroup(
+	    const hidl_string& group_ifname, removeGroup_cb _hidl_cb) override;
+	Return<void> reject(
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    reject_cb _hidl_cb) override;
+	Return<void> invite(
+	    const hidl_string& group_ifname,
+	    const hidl_array<uint8_t, 6>& go_device_address,
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    invite_cb _hidl_cb) override;
+	Return<void> reinvoke(
+	    SupplicantNetworkId persistent_network_id,
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    reinvoke_cb _hidl_cb) override;
+	Return<void> configureExtListen(
+	    uint32_t period_in_millis, uint32_t interval_in_millis,
+	    configureExtListen_cb _hidl_cb) override;
+	Return<void> setListenChannel(
+	    uint32_t channel, uint32_t operating_class,
+	    setListenChannel_cb _hidl_cb) override;
+	Return<void> setDisallowedFrequencies(
+	    const hidl_vec<FreqRange>& ranges,
+	    setDisallowedFrequencies_cb _hidl_cb) override;
+	Return<void> getSsid(
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    getSsid_cb _hidl_cb) override;
+	Return<void> getGroupCapability(
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    getGroupCapability_cb _hidl_cb) override;
+	Return<void> addBonjourService(
+	    const hidl_vec<uint8_t>& query, const hidl_vec<uint8_t>& response,
+	    addBonjourService_cb _hidl_cb) override;
+	Return<void> removeBonjourService(
+	    const hidl_vec<uint8_t>& query,
+	    removeBonjourService_cb _hidl_cb) override;
+	Return<void> addUpnpService(
+	    uint32_t version, const hidl_string& service_name,
+	    addUpnpService_cb _hidl_cb) override;
+	Return<void> removeUpnpService(
+	    uint32_t version, const hidl_string& service_name,
+	    removeUpnpService_cb _hidl_cb) override;
+	Return<void> flushServices(flushServices_cb _hidl_cb) override;
+	Return<void> requestServiceDiscovery(
+	    const hidl_array<uint8_t, 6>& peer_address,
+	    const hidl_vec<uint8_t>& query,
+	    requestServiceDiscovery_cb _hidl_cb) override;
+	Return<void> cancelServiceDiscovery(
+	    uint64_t identifier, cancelServiceDiscovery_cb _hidl_cb) override;
+	Return<void> setMiracastMode(
+	    ISupplicantP2pIface::MiracastMode mode,
+	    setMiracastMode_cb _hidl_cb) override;
+	Return<void> startWpsPbc(
+	    const hidl_string& groupIfName, const hidl_array<uint8_t, 6>& bssid,
+	    startWpsPbc_cb _hidl_cb) override;
+	Return<void> startWpsPinKeypad(
+	    const hidl_string& groupIfName, const hidl_string& pin,
+	    startWpsPinKeypad_cb _hidl_cb) override;
+	Return<void> startWpsPinDisplay(
+	    const hidl_string& groupIfName, const hidl_array<uint8_t, 6>& bssid,
+	    startWpsPinDisplay_cb _hidl_cb) override;
+	Return<void> cancelWps(
+	    const hidl_string& groupIfName, cancelWps_cb _hidl_cb) override;
+	Return<void> setWpsDeviceName(
+	    const hidl_string& name, setWpsDeviceName_cb _hidl_cb) override;
+	Return<void> setWpsDeviceType(
+	    const hidl_array<uint8_t, 8>& type,
+	    setWpsDeviceType_cb _hidl_cb) override;
+	Return<void> setWpsManufacturer(
+	    const hidl_string& manufacturer,
+	    setWpsManufacturer_cb _hidl_cb) override;
+	Return<void> setWpsModelName(
+	    const hidl_string& model_name,
+	    setWpsModelName_cb _hidl_cb) override;
+	Return<void> setWpsModelNumber(
+	    const hidl_string& model_number,
+	    setWpsModelNumber_cb _hidl_cb) override;
+	Return<void> setWpsSerialNumber(
+	    const hidl_string& serial_number,
+	    setWpsSerialNumber_cb _hidl_cb) override;
+	Return<void> setWpsConfigMethods(
+	    uint16_t config_methods, setWpsConfigMethods_cb _hidl_cb) override;
+	Return<void> enableWfd(bool enable, enableWfd_cb _hidl_cb) override;
+	Return<void> setWfdDeviceInfo(
+	    const hidl_array<uint8_t, 6>& info,
+	    setWfdDeviceInfo_cb _hidl_cb) override;
+	Return<void> createNfcHandoverRequestMessage(
+	    createNfcHandoverRequestMessage_cb _hidl_cb) override;
+	Return<void> createNfcHandoverSelectMessage(
+	    createNfcHandoverSelectMessage_cb _hidl_cb) override;
+	Return<void> reportNfcHandoverResponse(
+	    const hidl_vec<uint8_t>& request,
+	    reportNfcHandoverResponse_cb _hidl_cb) override;
+	Return<void> reportNfcHandoverInitiation(
+	    const hidl_vec<uint8_t>& select,
+	    reportNfcHandoverInitiation_cb _hidl_cb) override;
+	Return<void> saveConfig(saveConfig_cb _hidl_cb) override;
+
+private:
+	// Corresponding worker functions for the HIDL methods.
+	std::pair<SupplicantStatus, std::string> getNameInternal();
+	std::pair<SupplicantStatus, IfaceType> getTypeInternal();
+	std::pair<SupplicantStatus, sp<ISupplicantP2pNetwork>>
+	addNetworkInternal();
+	SupplicantStatus removeNetworkInternal(SupplicantNetworkId id);
+	std::pair<SupplicantStatus, sp<ISupplicantP2pNetwork>>
+	getNetworkInternal(SupplicantNetworkId id);
+	std::pair<SupplicantStatus, std::vector<SupplicantNetworkId>>
+	listNetworksInternal();
+	SupplicantStatus registerCallbackInternal(
+	    const sp<ISupplicantP2pIfaceCallback>& callback);
+	std::pair<SupplicantStatus, std::array<uint8_t, 6>>
+	getDeviceAddressInternal();
+	SupplicantStatus setSsidPostfixInternal(
+	    const std::vector<uint8_t>& postfix);
+	SupplicantStatus setGroupIdleInternal(
+	    const std::string& group_ifname, uint32_t timeout_in_sec);
+	SupplicantStatus setPowerSaveInternal(
+	    const std::string& group_ifname, bool enable);
+	SupplicantStatus findInternal(uint32_t timeout_in_sec);
+	SupplicantStatus stopFindInternal();
+	SupplicantStatus flushInternal();
+	std::pair<SupplicantStatus, std::string> connectInternal(
+	    const std::array<uint8_t, 6>& peer_address,
+	    ISupplicantP2pIface::WpsProvisionMethod provision_method,
+	    const std::string& pre_selected_pin, bool join_existing_group,
+	    bool persistent, uint32_t go_intent);
+	SupplicantStatus cancelConnectInternal();
+	SupplicantStatus provisionDiscoveryInternal(
+	    const std::array<uint8_t, 6>& peer_address,
+	    ISupplicantP2pIface::WpsProvisionMethod provision_method);
+	SupplicantStatus addGroupInternal(
+	    bool persistent, SupplicantNetworkId persistent_network_id);
+	SupplicantStatus removeGroupInternal(const std::string& group_ifname);
+	SupplicantStatus rejectInternal(
+	    const std::array<uint8_t, 6>& peer_address);
+	SupplicantStatus inviteInternal(
+	    const std::string& group_ifname,
+	    const std::array<uint8_t, 6>& go_device_address,
+	    const std::array<uint8_t, 6>& peer_address);
+	SupplicantStatus reinvokeInternal(
+	    SupplicantNetworkId persistent_network_id,
+	    const std::array<uint8_t, 6>& peer_address);
+	SupplicantStatus configureExtListenInternal(
+	    uint32_t period_in_millis, uint32_t interval_in_millis);
+	SupplicantStatus setListenChannelInternal(
+	    uint32_t channel, uint32_t operating_class);
+	SupplicantStatus setDisallowedFrequenciesInternal(
+	    const std::vector<FreqRange>& ranges);
+	std::pair<SupplicantStatus, std::vector<uint8_t>> getSsidInternal(
+	    const std::array<uint8_t, 6>& peer_address);
+	std::pair<SupplicantStatus, uint32_t> getGroupCapabilityInternal(
+	    const std::array<uint8_t, 6>& peer_address);
+	SupplicantStatus addBonjourServiceInternal(
+	    const std::vector<uint8_t>& query,
+	    const std::vector<uint8_t>& response);
+	SupplicantStatus removeBonjourServiceInternal(
+	    const std::vector<uint8_t>& query);
+	SupplicantStatus addUpnpServiceInternal(
+	    uint32_t version, const std::string& service_name);
+	SupplicantStatus removeUpnpServiceInternal(
+	    uint32_t version, const std::string& service_name);
+	SupplicantStatus flushServicesInternal();
+	std::pair<SupplicantStatus, uint64_t> requestServiceDiscoveryInternal(
+	    const std::array<uint8_t, 6>& peer_address,
+	    const std::vector<uint8_t>& query);
+	SupplicantStatus cancelServiceDiscoveryInternal(uint64_t identifier);
+	SupplicantStatus setMiracastModeInternal(
+	    ISupplicantP2pIface::MiracastMode mode);
+	SupplicantStatus startWpsPbcInternal(
+	    const std::string& group_ifname,
+	    const std::array<uint8_t, 6>& bssid);
+	SupplicantStatus startWpsPinKeypadInternal(
+	    const std::string& group_ifname, const std::string& pin);
+	std::pair<SupplicantStatus, std::string> startWpsPinDisplayInternal(
+	    const std::string& group_ifname,
+	    const std::array<uint8_t, 6>& bssid);
+	SupplicantStatus cancelWpsInternal(const std::string& group_ifname);
+	SupplicantStatus setWpsDeviceNameInternal(const std::string& name);
+	SupplicantStatus setWpsDeviceTypeInternal(
+	    const std::array<uint8_t, 8>& type);
+	SupplicantStatus setWpsManufacturerInternal(
+	    const std::string& manufacturer);
+	SupplicantStatus setWpsModelNameInternal(const std::string& model_name);
+	SupplicantStatus setWpsModelNumberInternal(
+	    const std::string& model_number);
+	SupplicantStatus setWpsSerialNumberInternal(
+	    const std::string& serial_number);
+	SupplicantStatus setWpsConfigMethodsInternal(uint16_t config_methods);
+	SupplicantStatus enableWfdInternal(bool enable);
+	SupplicantStatus setWfdDeviceInfoInternal(
+	    const std::array<uint8_t, 6>& info);
+	std::pair<SupplicantStatus, std::vector<uint8_t>>
+	createNfcHandoverRequestMessageInternal();
+	std::pair<SupplicantStatus, std::vector<uint8_t>>
+	createNfcHandoverSelectMessageInternal();
+	SupplicantStatus reportNfcHandoverResponseInternal(
+	    const std::vector<uint8_t>& request);
+	SupplicantStatus reportNfcHandoverInitiationInternal(
+	    const std::vector<uint8_t>& select);
+	SupplicantStatus saveConfigInternal();
+
+	struct wpa_supplicant* retrieveIfacePtr();
+	struct wpa_supplicant* retrieveGroupIfacePtr(
+	    const std::string& group_ifname);
+
+	// Reference to the global wpa_struct. This is assumed to be valid for
+	// the lifetime of the process.
+	struct wpa_global* wpa_global_;
+	// Name of the iface this hidl object controls
+	const std::string ifname_;
+	bool is_valid_;
+
+	DISALLOW_COPY_AND_ASSIGN(P2pIface);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
+
+#endif  // WPA_SUPPLICANT_HIDL_P2P_IFACE_H
diff --git a/wpa_supplicant/hidl/1.0/p2p_network.cpp b/wpa_supplicant/hidl/1.0/p2p_network.cpp
new file mode 100644
index 0000000..7daa453
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/p2p_network.cpp
@@ -0,0 +1,257 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "hidl_manager.h"
+#include "hidl_return_util.h"
+#include "p2p_network.h"
+
+extern "C" {
+#include "config_ssid.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+using hidl_return_util::validateAndCall;
+
+P2pNetwork::P2pNetwork(
+    struct wpa_global *wpa_global, const char ifname[], int network_id)
+    : wpa_global_(wpa_global),
+      ifname_(ifname),
+      network_id_(network_id),
+      is_valid_(true)
+{
+}
+
+void P2pNetwork::invalidate() { is_valid_ = false; }
+bool P2pNetwork::isValid()
+{
+	return (is_valid_ && (retrieveNetworkPtr() != nullptr));
+}
+
+Return<void> P2pNetwork::getId(getId_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::getIdInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::getInterfaceName(getInterfaceName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::getInterfaceNameInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::getType(getType_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::getTypeInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::registerCallback(
+    const sp<ISupplicantP2pNetworkCallback> &callback,
+    registerCallback_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> P2pNetwork::getSsid(getSsid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::getSsidInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::getBssid(getBssid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::getBssidInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::isCurrent(isCurrent_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::isCurrentInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::isPersistent(isPersistent_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::isPersistentInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::isGo(isGo_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::isGoInternal, _hidl_cb);
+}
+
+Return<void> P2pNetwork::setClientList(
+    const hidl_vec<hidl_array<uint8_t, 6>> &clients, setClientList_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::setClientListInternal, _hidl_cb, clients);
+}
+
+Return<void> P2pNetwork::getClientList(getClientList_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &P2pNetwork::getClientListInternal, _hidl_cb);
+}
+
+std::pair<SupplicantStatus, uint32_t> P2pNetwork::getIdInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, network_id_};
+}
+
+std::pair<SupplicantStatus, std::string> P2pNetwork::getInterfaceNameInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, ifname_};
+}
+
+std::pair<SupplicantStatus, IfaceType> P2pNetwork::getTypeInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, IfaceType::P2P};
+}
+
+SupplicantStatus P2pNetwork::registerCallbackInternal(
+    const sp<ISupplicantP2pNetworkCallback> &callback)
+{
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->addP2pNetworkCallbackHidlObject(
+		ifname_, network_id_, callback)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>> P2pNetwork::getSsidInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		{wpa_ssid->ssid, wpa_ssid->ssid + wpa_ssid->ssid_len}};
+}
+
+std::pair<SupplicantStatus, std::array<uint8_t, 6>>
+P2pNetwork::getBssidInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	std::array<uint8_t, 6> bssid{};
+	if (wpa_ssid->bssid_set) {
+		os_memcpy(bssid.data(), wpa_ssid->bssid, ETH_ALEN);
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, bssid};
+}
+
+std::pair<SupplicantStatus, bool> P2pNetwork::isCurrentInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		(wpa_s->current_ssid == wpa_ssid)};
+}
+
+std::pair<SupplicantStatus, bool> P2pNetwork::isPersistentInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""}, (wpa_ssid->disabled == 2)};
+}
+
+std::pair<SupplicantStatus, bool> P2pNetwork::isGoInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		(wpa_ssid->mode == wpa_ssid::wpas_mode::WPAS_MODE_P2P_GO)};
+}
+
+SupplicantStatus P2pNetwork::setClientListInternal(
+    const std::vector<hidl_array<uint8_t, 6>> &clients)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	os_free(wpa_ssid->p2p_client_list);
+	// Internal representation uses a generic MAC addr/mask storage format
+	// (even though the mask is always 0xFF'ed for p2p_client_list). So, the
+	// first 6 bytes holds the client MAC address and the next 6 bytes are
+	// OxFF'ed.
+	wpa_ssid->p2p_client_list =
+	    (u8 *)os_malloc(ETH_ALEN * 2 * clients.size());
+	if (!wpa_ssid->p2p_client_list) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	u8 *list = wpa_ssid->p2p_client_list;
+	for (const auto &client : clients) {
+		os_memcpy(list, client.data(), ETH_ALEN);
+		list += ETH_ALEN;
+		os_memset(list, 0xFF, ETH_ALEN);
+		list += ETH_ALEN;
+	}
+	wpa_ssid->num_p2p_clients = clients.size();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::vector<hidl_array<uint8_t, 6>>>
+P2pNetwork::getClientListInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->p2p_client_list) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	std::vector<hidl_array<uint8_t, 6>> clients;
+	u8 *list = wpa_ssid->p2p_client_list;
+	for (size_t i = 0; i < wpa_ssid->num_p2p_clients; i++) {
+		clients.emplace_back(list);
+		list += 2 * ETH_ALEN;
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, clients};
+}
+
+/**
+ * Retrieve the underlying |wpa_ssid| struct pointer for
+ * this network.
+ * If the underlying network is removed or the interface
+ * this network belong to is removed, all RPC method calls
+ * on this object will return failure.
+ */
+struct wpa_ssid *P2pNetwork::retrieveNetworkPtr()
+{
+	wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (!wpa_s)
+		return nullptr;
+	return wpa_config_get_network(wpa_s->conf, network_id_);
+}
+
+/**
+ * Retrieve the underlying |wpa_supplicant| struct
+ * pointer for this network.
+ */
+struct wpa_supplicant *P2pNetwork::retrieveIfacePtr()
+{
+	return wpa_supplicant_get_iface(
+	    (struct wpa_global *)wpa_global_, ifname_.c_str());
+}
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
diff --git a/wpa_supplicant/hidl/1.0/p2p_network.h b/wpa_supplicant/hidl/1.0/p2p_network.h
new file mode 100644
index 0000000..6164f43
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/p2p_network.h
@@ -0,0 +1,102 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_P2P_NETWORK_H
+#define WPA_SUPPLICANT_HIDL_P2P_NETWORK_H
+
+#include <android-base/macros.h>
+
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetwork.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantP2pNetworkCallback.h>
+
+extern "C" {
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "wpa_supplicant_i.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Implementation of P2pNetwork hidl object. Each unique hidl
+ * object is used for control operations on a specific network
+ * controlled by wpa_supplicant.
+ */
+class P2pNetwork : public ISupplicantP2pNetwork
+{
+public:
+	P2pNetwork(
+	    struct wpa_global* wpa_global, const char ifname[], int network_id);
+	~P2pNetwork() override = default;
+	// Refer to |StaIface::invalidate()|.
+	void invalidate();
+	bool isValid();
+
+	// Hidl methods exposed.
+	Return<void> getId(getId_cb _hidl_cb) override;
+	Return<void> getInterfaceName(getInterfaceName_cb _hidl_cb) override;
+	Return<void> getType(getType_cb _hidl_cb) override;
+	Return<void> registerCallback(
+	    const sp<ISupplicantP2pNetworkCallback>& callback,
+	    registerCallback_cb _hidl_cb) override;
+	Return<void> getSsid(getSsid_cb _hidl_cb) override;
+	Return<void> getBssid(getBssid_cb _hidl_cb) override;
+	Return<void> isCurrent(isCurrent_cb _hidl_cb) override;
+	Return<void> isPersistent(isPersistent_cb _hidl_cb) override;
+	Return<void> isGo(isGo_cb _hidl_cb) override;
+	Return<void> setClientList(
+	    const hidl_vec<hidl_array<uint8_t, 6>>& clients,
+	    setClientList_cb _hidl_cb) override;
+	Return<void> getClientList(getClientList_cb _hidl_cb) override;
+
+private:
+	// Corresponding worker functions for the HIDL methods.
+	std::pair<SupplicantStatus, uint32_t> getIdInternal();
+	std::pair<SupplicantStatus, std::string> getInterfaceNameInternal();
+	std::pair<SupplicantStatus, IfaceType> getTypeInternal();
+	SupplicantStatus registerCallbackInternal(
+	    const sp<ISupplicantP2pNetworkCallback>& callback);
+	std::pair<SupplicantStatus, std::vector<uint8_t>> getSsidInternal();
+	std::pair<SupplicantStatus, std::array<uint8_t, 6>> getBssidInternal();
+	std::pair<SupplicantStatus, bool> isCurrentInternal();
+	std::pair<SupplicantStatus, bool> isPersistentInternal();
+	std::pair<SupplicantStatus, bool> isGoInternal();
+	SupplicantStatus setClientListInternal(
+	    const std::vector<hidl_array<uint8_t, 6>>& clients);
+	std::pair<SupplicantStatus, std::vector<hidl_array<uint8_t, 6>>>
+	getClientListInternal();
+
+	struct wpa_ssid* retrieveNetworkPtr();
+	struct wpa_supplicant* retrieveIfacePtr();
+
+	// Reference to the global wpa_struct. This is assumed to be valid
+	// for the lifetime of the process.
+	const struct wpa_global* wpa_global_;
+	// Name of the iface this network belongs to.
+	const std::string ifname_;
+	// Id of the network this hidl object controls.
+	const int network_id_;
+	bool is_valid_;
+
+	DISALLOW_COPY_AND_ASSIGN(P2pNetwork);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
+
+#endif  // WPA_SUPPLICANT_HIDL_P2P_NETWORK_H
diff --git a/wpa_supplicant/hidl/1.0/sta_iface.cpp b/wpa_supplicant/hidl/1.0/sta_iface.cpp
new file mode 100644
index 0000000..8498e69
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/sta_iface.cpp
@@ -0,0 +1,1020 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "hidl_manager.h"
+#include "hidl_return_util.h"
+#include "iface_config_utils.h"
+#include "misc_utils.h"
+#include "sta_iface.h"
+
+extern "C" {
+#include "utils/eloop.h"
+#include "gas_query.h"
+#include "interworking.h"
+#include "hs20_supplicant.h"
+#include "wps_supplicant.h"
+}
+
+namespace {
+using android::hardware::wifi::supplicant::V1_0::ISupplicantStaIface;
+using android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
+using android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode;
+using android::hardware::wifi::supplicant::V1_0::implementation::HidlManager;
+
+constexpr uint32_t kMaxAnqpElems = 100;
+constexpr char kGetMacAddress[] = "MACADDR";
+constexpr char kStartRxFilter[] = "RXFILTER-START";
+constexpr char kStopRxFilter[] = "RXFILTER-STOP";
+constexpr char kAddRxFilter[] = "RXFILTER-ADD";
+constexpr char kRemoveRxFilter[] = "RXFILTER-REMOVE";
+constexpr char kSetBtCoexistenceMode[] = "BTCOEXMODE";
+constexpr char kSetBtCoexistenceScanStart[] = "BTCOEXSCAN-START";
+constexpr char kSetBtCoexistenceScanStop[] = "BTCOEXSCAN-STOP";
+constexpr char kSetSupendModeEnabled[] = "SETSUSPENDMODE 1";
+constexpr char kSetSupendModeDisabled[] = "SETSUSPENDMODE 0";
+constexpr char kSetCountryCode[] = "COUNTRY";
+constexpr uint32_t kExtRadioWorkDefaultTimeoutInSec = static_cast<uint32_t>(
+    ISupplicantStaIface::ExtRadioWorkDefaults::TIMEOUT_IN_SECS);
+constexpr char kExtRadioWorkNamePrefix[] = "ext:";
+
+uint8_t convertHidlRxFilterTypeToInternal(
+    ISupplicantStaIface::RxFilterType type)
+{
+	switch (type) {
+	case ISupplicantStaIface::RxFilterType::V4_MULTICAST:
+		return 2;
+	case ISupplicantStaIface::RxFilterType::V6_MULTICAST:
+		return 3;
+	};
+	WPA_ASSERT(false);
+}
+
+uint8_t convertHidlBtCoexModeToInternal(
+    ISupplicantStaIface::BtCoexistenceMode mode)
+{
+	switch (mode) {
+	case ISupplicantStaIface::BtCoexistenceMode::ENABLED:
+		return 0;
+	case ISupplicantStaIface::BtCoexistenceMode::DISABLED:
+		return 1;
+	case ISupplicantStaIface::BtCoexistenceMode::SENSE:
+		return 2;
+	};
+	WPA_ASSERT(false);
+}
+
+SupplicantStatus doZeroArgDriverCommand(
+    struct wpa_supplicant *wpa_s, const char *cmd)
+{
+	std::vector<char> cmd_vec(cmd, cmd + strlen(cmd) + 1);
+	char driver_cmd_reply_buf[4096] = {};
+	if (wpa_drv_driver_cmd(
+		wpa_s, cmd_vec.data(), driver_cmd_reply_buf,
+		sizeof(driver_cmd_reply_buf))) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus doOneArgDriverCommand(
+    struct wpa_supplicant *wpa_s, const char *cmd, uint8_t arg)
+{
+	std::string cmd_str = std::string(cmd) + " " + std::to_string(arg);
+	return doZeroArgDriverCommand(wpa_s, cmd_str.c_str());
+}
+
+SupplicantStatus doOneArgDriverCommand(
+    struct wpa_supplicant *wpa_s, const char *cmd, const std::string &arg)
+{
+	std::string cmd_str = std::string(cmd) + " " + arg;
+	return doZeroArgDriverCommand(wpa_s, cmd_str.c_str());
+}
+
+void endExtRadioWork(struct wpa_radio_work *work)
+{
+	auto *ework = static_cast<struct wpa_external_work *>(work->ctx);
+	work->wpa_s->ext_work_in_progress = 0;
+	radio_work_done(work);
+	os_free(ework);
+}
+
+void extRadioWorkTimeoutCb(void *eloop_ctx, void *timeout_ctx)
+{
+	auto *work = static_cast<struct wpa_radio_work *>(eloop_ctx);
+	auto *ework = static_cast<struct wpa_external_work *>(work->ctx);
+	wpa_dbg(
+	    work->wpa_s, MSG_DEBUG, "Timing out external radio work %u (%s)",
+	    ework->id, work->type);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	WPA_ASSERT(hidl_manager);
+	hidl_manager->notifyExtRadioWorkTimeout(work->wpa_s, ework->id);
+
+	endExtRadioWork(work);
+}
+
+void startExtRadioWork(struct wpa_radio_work *work)
+{
+	auto *ework = static_cast<struct wpa_external_work *>(work->ctx);
+	work->wpa_s->ext_work_in_progress = 1;
+	if (!ework->timeout) {
+		ework->timeout = kExtRadioWorkDefaultTimeoutInSec;
+	}
+	eloop_register_timeout(
+	    ework->timeout, 0, extRadioWorkTimeoutCb, work, nullptr);
+}
+
+void extRadioWorkStartCb(struct wpa_radio_work *work, int deinit)
+{
+	// deinit==1 is invoked during interface removal. Since the HIDL
+	// interface does not support interface addition/removal, we don't
+	// need to handle this scenario.
+	WPA_ASSERT(!deinit);
+
+	auto *ework = static_cast<struct wpa_external_work *>(work->ctx);
+	wpa_dbg(
+	    work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
+	    ework->id, ework->type);
+
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	WPA_ASSERT(hidl_manager);
+	hidl_manager->notifyExtRadioWorkStart(work->wpa_s, ework->id);
+
+	startExtRadioWork(work);
+}
+
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+using hidl_return_util::validateAndCall;
+
+StaIface::StaIface(struct wpa_global *wpa_global, const char ifname[])
+    : wpa_global_(wpa_global), ifname_(ifname), is_valid_(true)
+{
+}
+
+void StaIface::invalidate() { is_valid_ = false; }
+bool StaIface::isValid()
+{
+	return (is_valid_ && (retrieveIfacePtr() != nullptr));
+}
+
+Return<void> StaIface::getName(getName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::getNameInternal, _hidl_cb);
+}
+
+Return<void> StaIface::getType(getType_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::getTypeInternal, _hidl_cb);
+}
+
+Return<void> StaIface::addNetwork(addNetwork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::addNetworkInternal, _hidl_cb);
+}
+
+Return<void> StaIface::removeNetwork(
+    SupplicantNetworkId id, removeNetwork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::removeNetworkInternal, _hidl_cb, id);
+}
+
+Return<void> StaIface::getNetwork(
+    SupplicantNetworkId id, getNetwork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::getNetworkInternal, _hidl_cb, id);
+}
+
+Return<void> StaIface::listNetworks(listNetworks_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::listNetworksInternal, _hidl_cb);
+}
+
+Return<void> StaIface::registerCallback(
+    const sp<ISupplicantStaIfaceCallback> &callback,
+    registerCallback_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> StaIface::reassociate(reassociate_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::reassociateInternal, _hidl_cb);
+}
+
+Return<void> StaIface::reconnect(reconnect_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::reconnectInternal, _hidl_cb);
+}
+
+Return<void> StaIface::disconnect(disconnect_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::disconnectInternal, _hidl_cb);
+}
+
+Return<void> StaIface::setPowerSave(bool enable, setPowerSave_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setPowerSaveInternal, _hidl_cb, enable);
+}
+
+Return<void> StaIface::initiateTdlsDiscover(
+    const hidl_array<uint8_t, 6> &mac_address, initiateTdlsDiscover_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::initiateTdlsDiscoverInternal, _hidl_cb, mac_address);
+}
+
+Return<void> StaIface::initiateTdlsSetup(
+    const hidl_array<uint8_t, 6> &mac_address, initiateTdlsSetup_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::initiateTdlsSetupInternal, _hidl_cb, mac_address);
+}
+
+Return<void> StaIface::initiateTdlsTeardown(
+    const hidl_array<uint8_t, 6> &mac_address, initiateTdlsTeardown_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::initiateTdlsTeardownInternal, _hidl_cb, mac_address);
+}
+Return<void> StaIface::initiateAnqpQuery(
+    const hidl_array<uint8_t, 6> &mac_address,
+    const hidl_vec<ISupplicantStaIface::AnqpInfoId> &info_elements,
+    const hidl_vec<ISupplicantStaIface::Hs20AnqpSubtypes> &sub_types,
+    initiateAnqpQuery_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::initiateAnqpQueryInternal, _hidl_cb, mac_address,
+	    info_elements, sub_types);
+}
+
+Return<void> StaIface::initiateHs20IconQuery(
+    const hidl_array<uint8_t, 6> &mac_address, const hidl_string &file_name,
+    initiateHs20IconQuery_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::initiateHs20IconQueryInternal, _hidl_cb, mac_address,
+	    file_name);
+}
+
+Return<void> StaIface::getMacAddress(getMacAddress_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::getMacAddressInternal, _hidl_cb);
+}
+
+Return<void> StaIface::startRxFilter(startRxFilter_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::startRxFilterInternal, _hidl_cb);
+}
+
+Return<void> StaIface::stopRxFilter(stopRxFilter_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::stopRxFilterInternal, _hidl_cb);
+}
+
+Return<void> StaIface::addRxFilter(
+    ISupplicantStaIface::RxFilterType type, addRxFilter_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::addRxFilterInternal, _hidl_cb, type);
+}
+
+Return<void> StaIface::removeRxFilter(
+    ISupplicantStaIface::RxFilterType type, removeRxFilter_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::removeRxFilterInternal, _hidl_cb, type);
+}
+
+Return<void> StaIface::setBtCoexistenceMode(
+    ISupplicantStaIface::BtCoexistenceMode mode,
+    setBtCoexistenceMode_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setBtCoexistenceModeInternal, _hidl_cb, mode);
+}
+
+Return<void> StaIface::setBtCoexistenceScanModeEnabled(
+    bool enable, setBtCoexistenceScanModeEnabled_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setBtCoexistenceScanModeEnabledInternal, _hidl_cb,
+	    enable);
+}
+
+Return<void> StaIface::setSuspendModeEnabled(
+    bool enable, setSuspendModeEnabled_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setSuspendModeEnabledInternal, _hidl_cb, enable);
+}
+
+Return<void> StaIface::setCountryCode(
+    const hidl_array<int8_t, 2> &code, setCountryCode_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setCountryCodeInternal, _hidl_cb, code);
+}
+
+Return<void> StaIface::startWpsRegistrar(
+    const hidl_array<uint8_t, 6> &bssid, const hidl_string &pin,
+    startWpsRegistrar_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::startWpsRegistrarInternal, _hidl_cb, bssid, pin);
+}
+
+Return<void> StaIface::startWpsPbc(
+    const hidl_array<uint8_t, 6> &bssid, startWpsPbc_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::startWpsPbcInternal, _hidl_cb, bssid);
+}
+
+Return<void> StaIface::startWpsPinKeypad(
+    const hidl_string &pin, startWpsPinKeypad_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::startWpsPinKeypadInternal, _hidl_cb, pin);
+}
+
+Return<void> StaIface::startWpsPinDisplay(
+    const hidl_array<uint8_t, 6> &bssid, startWpsPinDisplay_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::startWpsPinDisplayInternal, _hidl_cb, bssid);
+}
+
+Return<void> StaIface::cancelWps(cancelWps_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::cancelWpsInternal, _hidl_cb);
+}
+
+Return<void> StaIface::setWpsDeviceName(
+    const hidl_string &name, setWpsDeviceName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setWpsDeviceNameInternal, _hidl_cb, name);
+}
+
+Return<void> StaIface::setWpsDeviceType(
+    const hidl_array<uint8_t, 8> &type, setWpsDeviceType_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setWpsDeviceTypeInternal, _hidl_cb, type);
+}
+
+Return<void> StaIface::setWpsManufacturer(
+    const hidl_string &manufacturer, setWpsManufacturer_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setWpsManufacturerInternal, _hidl_cb, manufacturer);
+}
+
+Return<void> StaIface::setWpsModelName(
+    const hidl_string &model_name, setWpsModelName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setWpsModelNameInternal, _hidl_cb, model_name);
+}
+
+Return<void> StaIface::setWpsModelNumber(
+    const hidl_string &model_number, setWpsModelNumber_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setWpsModelNumberInternal, _hidl_cb, model_number);
+}
+
+Return<void> StaIface::setWpsSerialNumber(
+    const hidl_string &serial_number, setWpsSerialNumber_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setWpsSerialNumberInternal, _hidl_cb, serial_number);
+}
+
+Return<void> StaIface::setWpsConfigMethods(
+    uint16_t config_methods, setWpsConfigMethods_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setWpsConfigMethodsInternal, _hidl_cb, config_methods);
+}
+
+Return<void> StaIface::setExternalSim(
+    bool useExternalSim, setExternalSim_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::setExternalSimInternal, _hidl_cb, useExternalSim);
+}
+
+Return<void> StaIface::addExtRadioWork(
+    const hidl_string &name, uint32_t freq_in_mhz, uint32_t timeout_in_sec,
+    addExtRadioWork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::addExtRadioWorkInternal, _hidl_cb, name, freq_in_mhz,
+	    timeout_in_sec);
+}
+
+Return<void> StaIface::removeExtRadioWork(
+    uint32_t id, removeExtRadioWork_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::removeExtRadioWorkInternal, _hidl_cb, id);
+}
+
+Return<void> StaIface::enableAutoReconnect(
+    bool enable, enableAutoReconnect_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &StaIface::enableAutoReconnectInternal, _hidl_cb, enable);
+}
+
+std::pair<SupplicantStatus, std::string> StaIface::getNameInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, ifname_};
+}
+
+std::pair<SupplicantStatus, IfaceType> StaIface::getTypeInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, IfaceType::STA};
+}
+
+std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
+StaIface::addNetworkInternal()
+{
+	android::sp<ISupplicantStaNetwork> network;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_ssid *ssid = wpa_supplicant_add_network(wpa_s);
+	if (!ssid) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
+	}
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->getStaNetworkHidlObjectByIfnameAndNetworkId(
+		wpa_s->ifname, ssid->id, &network)) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, network};
+}
+
+SupplicantStatus StaIface::removeNetworkInternal(SupplicantNetworkId id)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	int result = wpa_supplicant_remove_network(wpa_s, id);
+	if (result == -1) {
+		return {SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN, ""};
+	}
+	if (result != 0) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
+StaIface::getNetworkInternal(SupplicantNetworkId id)
+{
+	android::sp<ISupplicantStaNetwork> network;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_ssid *ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (!ssid) {
+		return {{SupplicantStatusCode::FAILURE_NETWORK_UNKNOWN, ""},
+			network};
+	}
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->getStaNetworkHidlObjectByIfnameAndNetworkId(
+		wpa_s->ifname, ssid->id, &network)) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, network};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, network};
+}
+
+std::pair<SupplicantStatus, std::vector<SupplicantNetworkId>>
+StaIface::listNetworksInternal()
+{
+	std::vector<SupplicantNetworkId> network_ids;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	for (struct wpa_ssid *wpa_ssid = wpa_s->conf->ssid; wpa_ssid;
+	     wpa_ssid = wpa_ssid->next) {
+		network_ids.emplace_back(wpa_ssid->id);
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, std::move(network_ids)};
+}
+
+SupplicantStatus StaIface::registerCallbackInternal(
+    const sp<ISupplicantStaIfaceCallback> &callback)
+{
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->addStaIfaceCallbackHidlObject(ifname_, callback)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::reassociateInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
+	}
+	wpas_request_connection(wpa_s);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::reconnectInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
+	}
+	if (!wpa_s->disconnected) {
+		return {SupplicantStatusCode::FAILURE_IFACE_NOT_DISCONNECTED,
+			""};
+	}
+	wpas_request_connection(wpa_s);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::disconnectInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
+	}
+	wpas_request_disconnection(wpa_s);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::setPowerSaveInternal(bool enable)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+		return {SupplicantStatusCode::FAILURE_IFACE_DISABLED, ""};
+	}
+	if (wpa_drv_set_p2p_powersave(wpa_s, enable, -1, -1)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::initiateTdlsDiscoverInternal(
+    const std::array<uint8_t, 6> &mac_address)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	int ret;
+	const u8 *peer = mac_address.data();
+	if (wpa_tdls_is_external_setup(wpa_s->wpa)) {
+		ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
+	} else {
+		ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
+	}
+	if (ret) {
+		wpa_printf(MSG_INFO, "StaIface: TDLS discover failed: %d", ret);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::initiateTdlsSetupInternal(
+    const std::array<uint8_t, 6> &mac_address)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	int ret;
+	const u8 *peer = mac_address.data();
+	if (wpa_tdls_is_external_setup(wpa_s->wpa) &&
+	    !(wpa_s->conf->tdls_external_control)) {
+		wpa_tdls_remove(wpa_s->wpa, peer);
+		ret = wpa_tdls_start(wpa_s->wpa, peer);
+	} else {
+		ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+	}
+	if (ret) {
+		wpa_printf(MSG_INFO, "StaIface: TDLS setup failed: %d", ret);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::initiateTdlsTeardownInternal(
+    const std::array<uint8_t, 6> &mac_address)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	int ret;
+	const u8 *peer = mac_address.data();
+	if (wpa_tdls_is_external_setup(wpa_s->wpa) &&
+	    !(wpa_s->conf->tdls_external_control)) {
+		ret = wpa_tdls_teardown_link(
+		    wpa_s->wpa, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+	} else {
+		ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+	}
+	if (ret) {
+		wpa_printf(MSG_INFO, "StaIface: TDLS teardown failed: %d", ret);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::initiateAnqpQueryInternal(
+    const std::array<uint8_t, 6> &mac_address,
+    const std::vector<ISupplicantStaIface::AnqpInfoId> &info_elements,
+    const std::vector<ISupplicantStaIface::Hs20AnqpSubtypes> &sub_types)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (info_elements.size() > kMaxAnqpElems) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	uint16_t info_elems_buf[kMaxAnqpElems];
+	uint32_t num_info_elems = 0;
+	for (const auto &info_element : info_elements) {
+		info_elems_buf[num_info_elems++] =
+		    static_cast<std::underlying_type<
+			ISupplicantStaIface::AnqpInfoId>::type>(info_element);
+	}
+	uint32_t sub_types_bitmask = 0;
+	for (const auto &type : sub_types) {
+		sub_types_bitmask |= BIT(
+		    static_cast<std::underlying_type<
+			ISupplicantStaIface::Hs20AnqpSubtypes>::type>(type));
+	}
+	if (anqp_send_req(
+		wpa_s, mac_address.data(), info_elems_buf, num_info_elems,
+		sub_types_bitmask, false)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::initiateHs20IconQueryInternal(
+    const std::array<uint8_t, 6> &mac_address, const std::string &file_name)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	wpa_s->fetch_osu_icon_in_progress = 0;
+	if (hs20_anqp_send_req(
+		wpa_s, mac_address.data(), BIT(HS20_STYPE_ICON_REQUEST),
+		reinterpret_cast<const uint8_t *>(file_name.c_str()),
+		file_name.size(), true)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::array<uint8_t, 6>>
+StaIface::getMacAddressInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	std::vector<char> cmd(
+	    kGetMacAddress, kGetMacAddress + sizeof(kGetMacAddress));
+	char driver_cmd_reply_buf[4096] = {};
+	int ret = wpa_drv_driver_cmd(
+	    wpa_s, cmd.data(), driver_cmd_reply_buf,
+	    sizeof(driver_cmd_reply_buf));
+	// Reply is of the format: "Macaddr = XX:XX:XX:XX:XX:XX"
+	std::string reply_str = driver_cmd_reply_buf;
+	if (ret < 0 || reply_str.empty() ||
+	    reply_str.find("=") == std::string::npos) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	// Remove all whitespace first and then split using the delimiter "=".
+	reply_str.erase(
+	    remove_if(reply_str.begin(), reply_str.end(), isspace),
+	    reply_str.end());
+	std::string mac_addr_str =
+	    reply_str.substr(reply_str.find("=") + 1, reply_str.size());
+	std::array<uint8_t, 6> mac_addr;
+	if (hwaddr_aton(mac_addr_str.c_str(), mac_addr.data())) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, mac_addr};
+}
+
+SupplicantStatus StaIface::startRxFilterInternal()
+{
+	return doZeroArgDriverCommand(retrieveIfacePtr(), kStartRxFilter);
+}
+
+SupplicantStatus StaIface::stopRxFilterInternal()
+{
+	return doZeroArgDriverCommand(retrieveIfacePtr(), kStopRxFilter);
+}
+
+SupplicantStatus StaIface::addRxFilterInternal(
+    ISupplicantStaIface::RxFilterType type)
+{
+	return doOneArgDriverCommand(
+	    retrieveIfacePtr(), kAddRxFilter,
+	    convertHidlRxFilterTypeToInternal(type));
+}
+
+SupplicantStatus StaIface::removeRxFilterInternal(
+    ISupplicantStaIface::RxFilterType type)
+{
+	return doOneArgDriverCommand(
+	    retrieveIfacePtr(), kRemoveRxFilter,
+	    convertHidlRxFilterTypeToInternal(type));
+}
+
+SupplicantStatus StaIface::setBtCoexistenceModeInternal(
+    ISupplicantStaIface::BtCoexistenceMode mode)
+{
+	return doOneArgDriverCommand(
+	    retrieveIfacePtr(), kSetBtCoexistenceMode,
+	    convertHidlBtCoexModeToInternal(mode));
+}
+
+SupplicantStatus StaIface::setBtCoexistenceScanModeEnabledInternal(bool enable)
+{
+	const char *cmd;
+	if (enable) {
+		cmd = kSetBtCoexistenceScanStart;
+	} else {
+		cmd = kSetBtCoexistenceScanStop;
+	}
+	return doZeroArgDriverCommand(retrieveIfacePtr(), cmd);
+}
+
+SupplicantStatus StaIface::setSuspendModeEnabledInternal(bool enable)
+{
+	const char *cmd;
+	if (enable) {
+		cmd = kSetSupendModeEnabled;
+	} else {
+		cmd = kSetSupendModeDisabled;
+	}
+	return doZeroArgDriverCommand(retrieveIfacePtr(), cmd);
+}
+
+SupplicantStatus StaIface::setCountryCodeInternal(
+    const std::array<int8_t, 2> &code)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	SupplicantStatus status = doOneArgDriverCommand(
+	    wpa_s, kSetCountryCode,
+	    std::string(std::begin(code), std::end(code)));
+	if (status.code != SupplicantStatusCode::SUCCESS) {
+		return status;
+	}
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	if (p2p) {
+		char country[3];
+		country[0] = code[0];
+		country[1] = code[1];
+		country[2] = 0x04;
+		p2p_set_country(p2p, country);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::startWpsRegistrarInternal(
+    const std::array<uint8_t, 6> &bssid, const std::string &pin)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpas_wps_start_reg(wpa_s, bssid.data(), pin.c_str(), nullptr)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::startWpsPbcInternal(
+    const std::array<uint8_t, 6> &bssid)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	const uint8_t *bssid_addr =
+	    is_zero_ether_addr(bssid.data()) ? nullptr : bssid.data();
+	if (wpas_wps_start_pbc(wpa_s, bssid_addr, 0)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::startWpsPinKeypadInternal(const std::string &pin)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpas_wps_start_pin(
+		wpa_s, nullptr, pin.c_str(), 0, DEV_PW_DEFAULT)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::string> StaIface::startWpsPinDisplayInternal(
+    const std::array<uint8_t, 6> &bssid)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	const uint8_t *bssid_addr =
+	    is_zero_ether_addr(bssid.data()) ? nullptr : bssid.data();
+	int pin =
+	    wpas_wps_start_pin(wpa_s, bssid_addr, nullptr, 0, DEV_PW_DEFAULT);
+	if (pin < 0) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, ""};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		misc_utils::convertWpsPinToString(pin)};
+}
+
+SupplicantStatus StaIface::cancelWpsInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpas_wps_cancel(wpa_s)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaIface::setWpsDeviceNameInternal(const std::string &name)
+{
+	return iface_config_utils::setWpsDeviceName(retrieveIfacePtr(), name);
+}
+
+SupplicantStatus StaIface::setWpsDeviceTypeInternal(
+    const std::array<uint8_t, 8> &type)
+{
+	return iface_config_utils::setWpsDeviceType(retrieveIfacePtr(), type);
+}
+
+SupplicantStatus StaIface::setWpsManufacturerInternal(
+    const std::string &manufacturer)
+{
+	return iface_config_utils::setWpsManufacturer(
+	    retrieveIfacePtr(), manufacturer);
+}
+
+SupplicantStatus StaIface::setWpsModelNameInternal(
+    const std::string &model_name)
+{
+	return iface_config_utils::setWpsModelName(
+	    retrieveIfacePtr(), model_name);
+}
+
+SupplicantStatus StaIface::setWpsModelNumberInternal(
+    const std::string &model_number)
+{
+	return iface_config_utils::setWpsModelNumber(
+	    retrieveIfacePtr(), model_number);
+}
+
+SupplicantStatus StaIface::setWpsSerialNumberInternal(
+    const std::string &serial_number)
+{
+	return iface_config_utils::setWpsSerialNumber(
+	    retrieveIfacePtr(), serial_number);
+}
+
+SupplicantStatus StaIface::setWpsConfigMethodsInternal(uint16_t config_methods)
+{
+	return iface_config_utils::setWpsConfigMethods(
+	    retrieveIfacePtr(), config_methods);
+}
+
+SupplicantStatus StaIface::setExternalSimInternal(bool useExternalSim)
+{
+	return iface_config_utils::setExternalSim(
+	    retrieveIfacePtr(), useExternalSim);
+}
+
+std::pair<SupplicantStatus, uint32_t> StaIface::addExtRadioWorkInternal(
+    const std::string &name, uint32_t freq_in_mhz, uint32_t timeout_in_sec)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	auto *ework = static_cast<struct wpa_external_work *>(
+	    os_zalloc(sizeof(struct wpa_external_work)));
+	if (!ework) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
+			UINT32_MAX};
+	}
+
+	std::string radio_work_name = kExtRadioWorkNamePrefix + name;
+	os_strlcpy(ework->type, radio_work_name.c_str(), sizeof(ework->type));
+	ework->timeout = timeout_in_sec;
+	wpa_s->ext_work_id++;
+	if (wpa_s->ext_work_id == 0) {
+		wpa_s->ext_work_id++;
+	}
+	ework->id = wpa_s->ext_work_id;
+
+	if (radio_add_work(
+		wpa_s, freq_in_mhz, ework->type, 0, extRadioWorkStartCb,
+		ework)) {
+		os_free(ework);
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
+			UINT32_MAX};
+	}
+	return {SupplicantStatus{SupplicantStatusCode::SUCCESS, ""}, ework->id};
+}
+
+SupplicantStatus StaIface::removeExtRadioWorkInternal(uint32_t id)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_radio_work *work;
+	dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+	{
+		if (os_strncmp(
+			work->type, kExtRadioWorkNamePrefix,
+			sizeof(kExtRadioWorkNamePrefix)) != 0)
+			continue;
+
+		auto *ework =
+		    static_cast<struct wpa_external_work *>(work->ctx);
+		if (ework->id != id)
+			continue;
+
+		wpa_dbg(
+		    wpa_s, MSG_DEBUG, "Completed external radio work %u (%s)",
+		    ework->id, ework->type);
+		eloop_cancel_timeout(extRadioWorkTimeoutCb, work, NULL);
+		endExtRadioWork(work);
+
+		return {SupplicantStatusCode::SUCCESS, ""};
+	}
+	return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+}
+
+SupplicantStatus StaIface::enableAutoReconnectInternal(bool enable)
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	wpa_s->auto_reconnect_disabled = enable ? 0 : 1;
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+/**
+ * Retrieve the underlying |wpa_supplicant| struct
+ * pointer for this iface.
+ * If the underlying iface is removed, then all RPC method calls on this object
+ * will return failure.
+ */
+wpa_supplicant *StaIface::retrieveIfacePtr()
+{
+	return wpa_supplicant_get_iface(wpa_global_, ifname_.c_str());
+}
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
diff --git a/wpa_supplicant/hidl/1.0/sta_iface.h b/wpa_supplicant/hidl/1.0/sta_iface.h
new file mode 100644
index 0000000..6e62260
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/sta_iface.h
@@ -0,0 +1,249 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_STA_IFACE_H
+#define WPA_SUPPLICANT_HIDL_STA_IFACE_H
+
+#include <array>
+#include <vector>
+
+#include <android-base/macros.h>
+
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIface.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaIfaceCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetwork.h>
+
+extern "C" {
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "driver_i.h"
+#include "wpa.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Implementation of StaIface hidl object. Each unique hidl
+ * object is used for control operations on a specific interface
+ * controlled by wpa_supplicant.
+ */
+class StaIface
+    : public android::hardware::wifi::supplicant::V1_0::ISupplicantStaIface
+{
+public:
+	StaIface(struct wpa_global* wpa_global, const char ifname[]);
+	~StaIface() override = default;
+	// HIDL does not provide a built-in mechanism to let the server
+	// invalidate a HIDL interface object after creation. If any client
+	// process holds onto a reference to the object in their context,
+	// any method calls on that reference will continue to be directed to
+	// the server.
+	// However Supplicant HAL needs to control the lifetime of these
+	// objects. So, add a public |invalidate| method to all |Iface| and
+	// |Network| objects.
+	// This will be used to mark an object invalid when the corresponding
+	// iface or network is removed.
+	// All HIDL method implementations should check if the object is still
+	// marked valid before processing them.
+	void invalidate();
+	bool isValid();
+
+	// Hidl methods exposed.
+	Return<void> getName(getName_cb _hidl_cb) override;
+	Return<void> getType(getType_cb _hidl_cb) override;
+	Return<void> addNetwork(addNetwork_cb _hidl_cb) override;
+	Return<void> removeNetwork(
+	    SupplicantNetworkId id, removeNetwork_cb _hidl_cb) override;
+	Return<void> getNetwork(
+	    SupplicantNetworkId id, getNetwork_cb _hidl_cb) override;
+	Return<void> listNetworks(listNetworks_cb _hidl_cb) override;
+	Return<void> registerCallback(
+	    const sp<ISupplicantStaIfaceCallback>& callback,
+	    registerCallback_cb _hidl_cb) override;
+	Return<void> reassociate(reassociate_cb _hidl_cb) override;
+	Return<void> reconnect(reconnect_cb _hidl_cb) override;
+	Return<void> disconnect(disconnect_cb _hidl_cb) override;
+	Return<void> setPowerSave(
+	    bool enable, setPowerSave_cb _hidl_cb) override;
+	Return<void> initiateTdlsDiscover(
+	    const hidl_array<uint8_t, 6>& mac_address,
+	    initiateTdlsDiscover_cb _hidl_cb) override;
+	Return<void> initiateTdlsSetup(
+	    const hidl_array<uint8_t, 6>& mac_address,
+	    initiateTdlsSetup_cb _hidl_cb) override;
+	Return<void> initiateTdlsTeardown(
+	    const hidl_array<uint8_t, 6>& mac_address,
+	    initiateTdlsTeardown_cb _hidl_cb) override;
+	Return<void> initiateAnqpQuery(
+	    const hidl_array<uint8_t, 6>& mac_address,
+	    const hidl_vec<ISupplicantStaIface::AnqpInfoId>& info_elements,
+	    const hidl_vec<ISupplicantStaIface::Hs20AnqpSubtypes>& sub_types,
+	    initiateAnqpQuery_cb _hidl_cb) override;
+	Return<void> initiateHs20IconQuery(
+	    const hidl_array<uint8_t, 6>& mac_address,
+	    const hidl_string& file_name,
+	    initiateHs20IconQuery_cb _hidl_cb) override;
+	Return<void> getMacAddress(getMacAddress_cb _hidl_cb) override;
+	Return<void> startRxFilter(startRxFilter_cb _hidl_cb) override;
+	Return<void> stopRxFilter(stopRxFilter_cb _hidl_cb) override;
+	Return<void> addRxFilter(
+	    ISupplicantStaIface::RxFilterType type,
+	    addRxFilter_cb _hidl_cb) override;
+	Return<void> removeRxFilter(
+	    ISupplicantStaIface::RxFilterType type,
+	    removeRxFilter_cb _hidl_cb) override;
+	Return<void> setBtCoexistenceMode(
+	    ISupplicantStaIface::BtCoexistenceMode mode,
+	    setBtCoexistenceMode_cb _hidl_cb) override;
+	Return<void> setBtCoexistenceScanModeEnabled(
+	    bool enable, setBtCoexistenceScanModeEnabled_cb _hidl_cb) override;
+	Return<void> setSuspendModeEnabled(
+	    bool enable, setSuspendModeEnabled_cb _hidl_cb) override;
+	Return<void> setCountryCode(
+	    const hidl_array<int8_t, 2>& code,
+	    setCountryCode_cb _hidl_cb) override;
+	Return<void> startWpsRegistrar(
+	    const hidl_array<uint8_t, 6>& bssid, const hidl_string& pin,
+	    startWpsRegistrar_cb _hidl_cb) override;
+	Return<void> startWpsPbc(
+	    const hidl_array<uint8_t, 6>& bssid,
+	    startWpsPbc_cb _hidl_cb) override;
+	Return<void> startWpsPinKeypad(
+	    const hidl_string& pin, startWpsPinKeypad_cb _hidl_cb) override;
+	Return<void> startWpsPinDisplay(
+	    const hidl_array<uint8_t, 6>& bssid,
+	    startWpsPinDisplay_cb _hidl_cb) override;
+	Return<void> cancelWps(cancelWps_cb _hidl_cb) override;
+	Return<void> setWpsDeviceName(
+	    const hidl_string& name, setWpsDeviceName_cb _hidl_cb) override;
+	Return<void> setWpsDeviceType(
+	    const hidl_array<uint8_t, 8>& type,
+	    setWpsDeviceType_cb _hidl_cb) override;
+	Return<void> setWpsManufacturer(
+	    const hidl_string& manufacturer,
+	    setWpsManufacturer_cb _hidl_cb) override;
+	Return<void> setWpsModelName(
+	    const hidl_string& model_name,
+	    setWpsModelName_cb _hidl_cb) override;
+	Return<void> setWpsModelNumber(
+	    const hidl_string& model_number,
+	    setWpsModelNumber_cb _hidl_cb) override;
+	Return<void> setWpsSerialNumber(
+	    const hidl_string& serial_number,
+	    setWpsSerialNumber_cb _hidl_cb) override;
+	Return<void> setWpsConfigMethods(
+	    uint16_t config_methods, setWpsConfigMethods_cb _hidl_cb) override;
+	Return<void> setExternalSim(
+	    bool useExternalSim, setExternalSim_cb _hidl_cb) override;
+	Return<void> addExtRadioWork(
+	    const hidl_string& name, uint32_t freq_in_mhz,
+	    uint32_t timeout_in_sec, addExtRadioWork_cb _hidl_cb) override;
+	Return<void> removeExtRadioWork(
+	    uint32_t id, removeExtRadioWork_cb _hidl_cb) override;
+	Return<void> enableAutoReconnect(
+	    bool enable, enableAutoReconnect_cb _hidl_cb) override;
+
+private:
+	// Corresponding worker functions for the HIDL methods.
+	std::pair<SupplicantStatus, std::string> getNameInternal();
+	std::pair<SupplicantStatus, IfaceType> getTypeInternal();
+	std::pair<SupplicantStatus, sp<ISupplicantNetwork>>
+	addNetworkInternal();
+	SupplicantStatus removeNetworkInternal(SupplicantNetworkId id);
+	std::pair<SupplicantStatus, sp<ISupplicantNetwork>> getNetworkInternal(
+	    SupplicantNetworkId id);
+	std::pair<SupplicantStatus, std::vector<SupplicantNetworkId>>
+	listNetworksInternal();
+	SupplicantStatus registerCallbackInternal(
+	    const sp<ISupplicantStaIfaceCallback>& callback);
+	SupplicantStatus reassociateInternal();
+	SupplicantStatus reconnectInternal();
+	SupplicantStatus disconnectInternal();
+	SupplicantStatus setPowerSaveInternal(bool enable);
+	SupplicantStatus initiateTdlsDiscoverInternal(
+	    const std::array<uint8_t, 6>& mac_address);
+	SupplicantStatus initiateTdlsSetupInternal(
+	    const std::array<uint8_t, 6>& mac_address);
+	SupplicantStatus initiateTdlsTeardownInternal(
+	    const std::array<uint8_t, 6>& mac_address);
+	SupplicantStatus initiateAnqpQueryInternal(
+	    const std::array<uint8_t, 6>& mac_address,
+	    const std::vector<ISupplicantStaIface::AnqpInfoId>& info_elements,
+	    const std::vector<ISupplicantStaIface::Hs20AnqpSubtypes>&
+		sub_types);
+	SupplicantStatus initiateHs20IconQueryInternal(
+	    const std::array<uint8_t, 6>& mac_address,
+	    const std::string& file_name);
+	std::pair<SupplicantStatus, std::array<uint8_t, 6>>
+	getMacAddressInternal();
+	SupplicantStatus startRxFilterInternal();
+	SupplicantStatus stopRxFilterInternal();
+	SupplicantStatus addRxFilterInternal(
+	    ISupplicantStaIface::RxFilterType type);
+	SupplicantStatus removeRxFilterInternal(
+	    ISupplicantStaIface::RxFilterType type);
+	SupplicantStatus setBtCoexistenceModeInternal(
+	    ISupplicantStaIface::BtCoexistenceMode mode);
+	SupplicantStatus setBtCoexistenceScanModeEnabledInternal(bool enable);
+	SupplicantStatus setSuspendModeEnabledInternal(bool enable);
+	SupplicantStatus setCountryCodeInternal(
+	    const std::array<int8_t, 2>& code);
+	SupplicantStatus startWpsRegistrarInternal(
+	    const std::array<uint8_t, 6>& bssid, const std::string& pin);
+	SupplicantStatus startWpsPbcInternal(
+	    const std::array<uint8_t, 6>& bssid);
+	SupplicantStatus startWpsPinKeypadInternal(const std::string& pin);
+	std::pair<SupplicantStatus, std::string> startWpsPinDisplayInternal(
+	    const std::array<uint8_t, 6>& bssid);
+	SupplicantStatus cancelWpsInternal();
+	SupplicantStatus setWpsDeviceNameInternal(const std::string& name);
+	SupplicantStatus setWpsDeviceTypeInternal(
+	    const std::array<uint8_t, 8>& type);
+	SupplicantStatus setWpsManufacturerInternal(
+	    const std::string& manufacturer);
+	SupplicantStatus setWpsModelNameInternal(const std::string& model_name);
+	SupplicantStatus setWpsModelNumberInternal(
+	    const std::string& model_number);
+	SupplicantStatus setWpsSerialNumberInternal(
+	    const std::string& serial_number);
+	SupplicantStatus setWpsConfigMethodsInternal(uint16_t config_methods);
+	SupplicantStatus setExternalSimInternal(bool useExternalSim);
+	std::pair<SupplicantStatus, uint32_t> addExtRadioWorkInternal(
+	    const std::string& name, uint32_t freq_in_mhz,
+	    uint32_t timeout_in_sec);
+	SupplicantStatus removeExtRadioWorkInternal(uint32_t id);
+	SupplicantStatus enableAutoReconnectInternal(bool enable);
+
+	struct wpa_supplicant* retrieveIfacePtr();
+
+	// Reference to the global wpa_struct. This is assumed to be valid for
+	// the lifetime of the process.
+	struct wpa_global* wpa_global_;
+	// Name of the iface this hidl object controls
+	const std::string ifname_;
+	bool is_valid_;
+
+	DISALLOW_COPY_AND_ASSIGN(StaIface);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
+
+#endif  // WPA_SUPPLICANT_HIDL_STA_IFACE_H
diff --git a/wpa_supplicant/hidl/1.0/sta_network.cpp b/wpa_supplicant/hidl/1.0/sta_network.cpp
new file mode 100644
index 0000000..72185b3
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/sta_network.cpp
@@ -0,0 +1,1819 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "hidl_manager.h"
+#include "hidl_return_util.h"
+#include "misc_utils.h"
+#include "sta_network.h"
+
+extern "C" {
+#include "wps_supplicant.h"
+}
+
+namespace {
+using android::hardware::wifi::supplicant::V1_0::ISupplicantStaNetwork;
+using android::hardware::wifi::supplicant::V1_0::SupplicantStatus;
+
+constexpr uint8_t kZeroBssid[6] = {0, 0, 0, 0, 0, 0};
+
+constexpr uint32_t kAllowedKeyMgmtMask =
+    (static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::NONE) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_PSK) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::WPA_EAP) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::IEEE8021X) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_EAP) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::FT_PSK) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::KeyMgmtMask::OSEN));
+constexpr uint32_t kAllowedProtoMask =
+    (static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::WPA) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::RSN) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::ProtoMask::OSEN));
+constexpr uint32_t kAllowedAuthAlgMask =
+    (static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::OPEN) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::SHARED) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::AuthAlgMask::LEAP));
+constexpr uint32_t kAllowedGroupCipherMask =
+    (static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP40) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::WEP104) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::TKIP) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::GroupCipherMask::CCMP) |
+     static_cast<uint32_t>(
+	 ISupplicantStaNetwork::GroupCipherMask::GTK_NOT_USED));
+constexpr uint32_t kAllowedPairwisewCipherMask =
+    (static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::NONE) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::TKIP) |
+     static_cast<uint32_t>(ISupplicantStaNetwork::PairwiseCipherMask::CCMP));
+
+constexpr uint32_t kEapMethodMax =
+    static_cast<uint32_t>(ISupplicantStaNetwork::EapMethod::WFA_UNAUTH_TLS) + 1;
+constexpr char const *kEapMethodStrings[kEapMethodMax] = {
+    "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'", "WFA-UNAUTH-TLS"};
+constexpr uint32_t kEapPhase2MethodMax =
+    static_cast<uint32_t>(ISupplicantStaNetwork::EapPhase2Method::AKA_PRIME) +
+    1;
+constexpr char const *kEapPhase2MethodStrings[kEapPhase2MethodMax] = {
+    "", "PAP", "MSCHAP", "MSCHAPV2", "GTC", "SIM", "AKA", "AKA'"};
+constexpr char kEapPhase2AuthPrefix[] = "auth=";
+constexpr char kEapPhase2AuthEapPrefix[] = "autheap=";
+constexpr char kNetworkEapSimGsmAuthResponse[] = "GSM-AUTH";
+constexpr char kNetworkEapSimUmtsAuthResponse[] = "UMTS-AUTH";
+constexpr char kNetworkEapSimUmtsAutsResponse[] = "UMTS-AUTS";
+constexpr char kNetworkEapSimGsmAuthFailure[] = "GSM-FAIL";
+constexpr char kNetworkEapSimUmtsAuthFailure[] = "UMTS-FAIL";
+}  // namespace
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+using hidl_return_util::validateAndCall;
+
+StaNetwork::StaNetwork(
+    struct wpa_global *wpa_global, const char ifname[], int network_id)
+    : wpa_global_(wpa_global),
+      ifname_(ifname),
+      network_id_(network_id),
+      is_valid_(true)
+{
+}
+
+void StaNetwork::invalidate() { is_valid_ = false; }
+bool StaNetwork::isValid()
+{
+	return (is_valid_ && (retrieveNetworkPtr() != nullptr));
+}
+
+Return<void> StaNetwork::getId(getId_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getIdInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getInterfaceName(getInterfaceName_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getInterfaceNameInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getType(getType_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getTypeInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::registerCallback(
+    const sp<ISupplicantStaNetworkCallback> &callback,
+    registerCallback_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> StaNetwork::setSsid(
+    const hidl_vec<uint8_t> &ssid, setSsid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setSsidInternal, _hidl_cb, ssid);
+}
+
+Return<void> StaNetwork::setBssid(
+    const hidl_array<uint8_t, 6> &bssid, setBssid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setBssidInternal, _hidl_cb, bssid);
+}
+
+Return<void> StaNetwork::setScanSsid(bool enable, setScanSsid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setScanSsidInternal, _hidl_cb, enable);
+}
+
+Return<void> StaNetwork::setKeyMgmt(
+    uint32_t key_mgmt_mask, setKeyMgmt_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setKeyMgmtInternal, _hidl_cb, key_mgmt_mask);
+}
+
+Return<void> StaNetwork::setProto(uint32_t proto_mask, setProto_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setProtoInternal, _hidl_cb, proto_mask);
+}
+
+Return<void> StaNetwork::setAuthAlg(
+    uint32_t auth_alg_mask,
+    std::function<void(const SupplicantStatus &status)> _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setAuthAlgInternal, _hidl_cb, auth_alg_mask);
+}
+
+Return<void> StaNetwork::setGroupCipher(
+    uint32_t group_cipher_mask, setGroupCipher_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setGroupCipherInternal, _hidl_cb, group_cipher_mask);
+}
+
+Return<void> StaNetwork::setPairwiseCipher(
+    uint32_t pairwise_cipher_mask, setPairwiseCipher_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setPairwiseCipherInternal, _hidl_cb,
+	    pairwise_cipher_mask);
+}
+
+Return<void> StaNetwork::setPskPassphrase(
+    const hidl_string &psk, setPskPassphrase_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setPskPassphraseInternal, _hidl_cb, psk);
+}
+
+Return<void> StaNetwork::setPsk(
+    const hidl_array<uint8_t, 32> &psk, setPsk_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setPskInternal, _hidl_cb, psk);
+}
+
+Return<void> StaNetwork::setWepKey(
+    uint32_t key_idx, const hidl_vec<uint8_t> &wep_key, setWepKey_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setWepKeyInternal, _hidl_cb, key_idx, wep_key);
+}
+
+Return<void> StaNetwork::setWepTxKeyIdx(
+    uint32_t key_idx, setWepTxKeyIdx_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setWepTxKeyIdxInternal, _hidl_cb, key_idx);
+}
+
+Return<void> StaNetwork::setRequirePmf(bool enable, setRequirePmf_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setRequirePmfInternal, _hidl_cb, enable);
+}
+
+Return<void> StaNetwork::setEapMethod(
+    ISupplicantStaNetwork::EapMethod method, setEapMethod_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapMethodInternal, _hidl_cb, method);
+}
+
+Return<void> StaNetwork::setEapPhase2Method(
+    ISupplicantStaNetwork::EapPhase2Method method,
+    setEapPhase2Method_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapPhase2MethodInternal, _hidl_cb, method);
+}
+
+Return<void> StaNetwork::setEapIdentity(
+    const hidl_vec<uint8_t> &identity, setEapIdentity_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapIdentityInternal, _hidl_cb, identity);
+}
+
+Return<void> StaNetwork::setEapAnonymousIdentity(
+    const hidl_vec<uint8_t> &identity, setEapAnonymousIdentity_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapAnonymousIdentityInternal, _hidl_cb, identity);
+}
+
+Return<void> StaNetwork::setEapPassword(
+    const hidl_vec<uint8_t> &password, setEapPassword_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapPasswordInternal, _hidl_cb, password);
+}
+
+Return<void> StaNetwork::setEapCACert(
+    const hidl_string &path, setEapCACert_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapCACertInternal, _hidl_cb, path);
+}
+
+Return<void> StaNetwork::setEapCAPath(
+    const hidl_string &path, setEapCAPath_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapCAPathInternal, _hidl_cb, path);
+}
+
+Return<void> StaNetwork::setEapClientCert(
+    const hidl_string &path, setEapClientCert_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapClientCertInternal, _hidl_cb, path);
+}
+
+Return<void> StaNetwork::setEapPrivateKeyId(
+    const hidl_string &id, setEapPrivateKeyId_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapPrivateKeyIdInternal, _hidl_cb, id);
+}
+
+Return<void> StaNetwork::setEapSubjectMatch(
+    const hidl_string &match, setEapSubjectMatch_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapSubjectMatchInternal, _hidl_cb, match);
+}
+
+Return<void> StaNetwork::setEapAltSubjectMatch(
+    const hidl_string &match, setEapAltSubjectMatch_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapAltSubjectMatchInternal, _hidl_cb, match);
+}
+
+Return<void> StaNetwork::setEapEngine(bool enable, setEapEngine_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapEngineInternal, _hidl_cb, enable);
+}
+
+Return<void> StaNetwork::setEapEngineID(
+    const hidl_string &id, setEapEngineID_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapEngineIDInternal, _hidl_cb, id);
+}
+
+Return<void> StaNetwork::setEapDomainSuffixMatch(
+    const hidl_string &match, setEapDomainSuffixMatch_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setEapDomainSuffixMatchInternal, _hidl_cb, match);
+}
+
+Return<void> StaNetwork::setProactiveKeyCaching(
+    bool enable, setProactiveKeyCaching_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setProactiveKeyCachingInternal, _hidl_cb, enable);
+}
+
+Return<void> StaNetwork::setIdStr(
+    const hidl_string &id_str, setIdStr_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setIdStrInternal, _hidl_cb, id_str);
+}
+
+Return<void> StaNetwork::setUpdateIdentifier(
+    uint32_t id, setUpdateIdentifier_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::setUpdateIdentifierInternal, _hidl_cb, id);
+}
+
+Return<void> StaNetwork::getSsid(getSsid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getSsidInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getBssid(getBssid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getBssidInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getScanSsid(getScanSsid_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getScanSsidInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getKeyMgmt(getKeyMgmt_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getKeyMgmtInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getProto(getProto_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getProtoInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getAuthAlg(getAuthAlg_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getAuthAlgInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getGroupCipher(getGroupCipher_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getGroupCipherInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getPairwiseCipher(getPairwiseCipher_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getPairwiseCipherInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getPskPassphrase(getPskPassphrase_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getPskPassphraseInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getPsk(getPsk_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getPskInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getWepKey(uint32_t key_idx, getWepKey_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getWepKeyInternal, _hidl_cb, key_idx);
+}
+
+Return<void> StaNetwork::getWepTxKeyIdx(getWepTxKeyIdx_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getWepTxKeyIdxInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getRequirePmf(getRequirePmf_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getRequirePmfInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapMethod(getEapMethod_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapMethodInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapPhase2Method(getEapPhase2Method_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapPhase2MethodInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapIdentity(getEapIdentity_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapIdentityInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapAnonymousIdentity(
+    getEapAnonymousIdentity_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapAnonymousIdentityInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapPassword(getEapPassword_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapPasswordInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapCACert(getEapCACert_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapCACertInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapCAPath(getEapCAPath_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapCAPathInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapClientCert(getEapClientCert_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapClientCertInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapPrivateKeyId(getEapPrivateKeyId_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapPrivateKeyIdInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapSubjectMatch(getEapSubjectMatch_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapSubjectMatchInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapAltSubjectMatch(
+    getEapAltSubjectMatch_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapAltSubjectMatchInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapEngine(getEapEngine_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapEngineInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapEngineID(getEapEngineID_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapEngineIDInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getEapDomainSuffixMatch(
+    getEapDomainSuffixMatch_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getEapDomainSuffixMatchInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getIdStr(getIdStr_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getIdStrInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::getWpsNfcConfigurationToken(
+    getWpsNfcConfigurationToken_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::getWpsNfcConfigurationTokenInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::enable(bool no_connect, enable_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::enableInternal, _hidl_cb, no_connect);
+}
+
+Return<void> StaNetwork::disable(disable_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::disableInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::select(select_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::selectInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::sendNetworkEapSimGsmAuthResponse(
+    const hidl_vec<ISupplicantStaNetwork::NetworkResponseEapSimGsmAuthParams>
+	&vec_params,
+    sendNetworkEapSimGsmAuthResponse_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::sendNetworkEapSimGsmAuthResponseInternal, _hidl_cb,
+	    vec_params);
+}
+
+Return<void> StaNetwork::sendNetworkEapSimGsmAuthFailure(
+    sendNetworkEapSimGsmAuthFailure_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::sendNetworkEapSimGsmAuthFailureInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::sendNetworkEapSimUmtsAuthResponse(
+    const ISupplicantStaNetwork::NetworkResponseEapSimUmtsAuthParams &params,
+    sendNetworkEapSimUmtsAuthResponse_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::sendNetworkEapSimUmtsAuthResponseInternal, _hidl_cb,
+	    params);
+}
+
+Return<void> StaNetwork::sendNetworkEapSimUmtsAutsResponse(
+    const hidl_array<uint8_t, 14> &auts,
+    sendNetworkEapSimUmtsAutsResponse_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::sendNetworkEapSimUmtsAutsResponseInternal, _hidl_cb,
+	    auts);
+}
+
+Return<void> StaNetwork::sendNetworkEapSimUmtsAuthFailure(
+    sendNetworkEapSimUmtsAuthFailure_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::sendNetworkEapSimUmtsAuthFailureInternal, _hidl_cb);
+}
+
+Return<void> StaNetwork::sendNetworkEapIdentityResponse(
+    const hidl_vec<uint8_t> &identity,
+    sendNetworkEapIdentityResponse_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_NETWORK_INVALID,
+	    &StaNetwork::sendNetworkEapIdentityResponseInternal, _hidl_cb,
+	    identity);
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getIdInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, network_id_};
+}
+
+std::pair<SupplicantStatus, std::string> StaNetwork::getInterfaceNameInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, ifname_};
+}
+
+std::pair<SupplicantStatus, IfaceType> StaNetwork::getTypeInternal()
+{
+	return {{SupplicantStatusCode::SUCCESS, ""}, IfaceType::STA};
+}
+
+SupplicantStatus StaNetwork::registerCallbackInternal(
+    const sp<ISupplicantStaNetworkCallback> &callback)
+{
+	HidlManager *hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->addStaNetworkCallbackHidlObject(
+		ifname_, network_id_, callback)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setSsidInternal(const std::vector<uint8_t> &ssid)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (ssid.size() == 0 ||
+	    ssid.size() >
+		static_cast<uint32_t>(ISupplicantStaNetwork::ParamSizeLimits::
+					  SSID_MAX_LEN_IN_BYTES)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	if (setByteArrayFieldAndResetState(
+		ssid.data(), ssid.size(), &(wpa_ssid->ssid),
+		&(wpa_ssid->ssid_len), "ssid")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	if (wpa_ssid->passphrase) {
+		wpa_config_update_psk(wpa_ssid);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setBssidInternal(
+    const std::array<uint8_t, 6> &bssid)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	int prev_bssid_set = wpa_ssid->bssid_set;
+	u8 prev_bssid[ETH_ALEN];
+	os_memcpy(prev_bssid, wpa_ssid->bssid, ETH_ALEN);
+	// Zero'ed array is used to clear out the BSSID value.
+	if (os_memcmp(bssid.data(), kZeroBssid, ETH_ALEN) == 0) {
+		wpa_ssid->bssid_set = 0;
+		wpa_printf(MSG_MSGDUMP, "BSSID any");
+	} else {
+		os_memcpy(wpa_ssid->bssid, bssid.data(), ETH_ALEN);
+		wpa_ssid->bssid_set = 1;
+		wpa_hexdump(MSG_MSGDUMP, "BSSID", wpa_ssid->bssid, ETH_ALEN);
+	}
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if ((wpa_ssid->bssid_set != prev_bssid_set ||
+	     os_memcmp(wpa_ssid->bssid, prev_bssid, ETH_ALEN) != 0)) {
+		wpas_notify_network_bssid_set_changed(wpa_s, wpa_ssid);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setScanSsidInternal(bool enable)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->scan_ssid = enable ? 1 : 0;
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setKeyMgmtInternal(uint32_t key_mgmt_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (key_mgmt_mask & ~kAllowedKeyMgmtMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->key_mgmt = key_mgmt_mask;
+	wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", wpa_ssid->key_mgmt);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setProtoInternal(uint32_t proto_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (proto_mask & ~kAllowedProtoMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->proto = proto_mask;
+	wpa_printf(MSG_MSGDUMP, "proto: 0x%x", wpa_ssid->proto);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setAuthAlgInternal(uint32_t auth_alg_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (auth_alg_mask & ~kAllowedAuthAlgMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->auth_alg = auth_alg_mask;
+	wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", wpa_ssid->auth_alg);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setGroupCipherInternal(uint32_t group_cipher_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (group_cipher_mask & ~kAllowedGroupCipherMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->group_cipher = group_cipher_mask;
+	wpa_printf(MSG_MSGDUMP, "group_cipher: 0x%x", wpa_ssid->group_cipher);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setPairwiseCipherInternal(
+    uint32_t pairwise_cipher_mask)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (pairwise_cipher_mask & ~kAllowedPairwisewCipherMask) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->pairwise_cipher = pairwise_cipher_mask;
+	wpa_printf(
+	    MSG_MSGDUMP, "pairwise_cipher: 0x%x", wpa_ssid->pairwise_cipher);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setPskPassphraseInternal(const std::string &psk)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (isPskPassphraseValid(psk)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	if (wpa_ssid->passphrase &&
+	    os_strlen(wpa_ssid->passphrase) == psk.size() &&
+	    os_memcmp(wpa_ssid->passphrase, psk.c_str(), psk.size()) == 0) {
+		return {SupplicantStatusCode::SUCCESS, ""};
+	}
+	// Flag to indicate if raw psk is calculated or not using
+	// |wpa_config_update_psk|. Deferred if ssid not already set.
+	wpa_ssid->psk_set = 0;
+	if (setStringKeyFieldAndResetState(
+		psk.c_str(), &(wpa_ssid->passphrase), "psk passphrase")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	if (wpa_ssid->ssid_len) {
+		wpa_config_update_psk(wpa_ssid);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setPskInternal(const std::array<uint8_t, 32> &psk)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	WPA_ASSERT(psk.size() == sizeof(wpa_ssid->psk));
+	str_clear_free(wpa_ssid->passphrase);
+	wpa_ssid->passphrase = nullptr;
+	os_memcpy(wpa_ssid->psk, psk.data(), sizeof(wpa_ssid->psk));
+	wpa_ssid->psk_set = 1;
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setWepKeyInternal(
+    uint32_t key_idx, const std::vector<uint8_t> &wep_key)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (key_idx >=
+	    static_cast<uint32_t>(
+		ISupplicantStaNetwork::ParamSizeLimits::WEP_KEYS_MAX_NUM)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	if (wep_key.size() !=
+		static_cast<uint32_t>(ISupplicantStaNetwork::ParamSizeLimits::
+					  WEP40_KEY_LEN_IN_BYTES) &&
+	    wep_key.size() !=
+		static_cast<uint32_t>(ISupplicantStaNetwork::ParamSizeLimits::
+					  WEP104_KEY_LEN_IN_BYTES)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	os_memcpy(wpa_ssid->wep_key[key_idx], wep_key.data(), wep_key.size());
+	wpa_ssid->wep_key_len[key_idx] = wep_key.size();
+	std::string msg_dump_title("wep_key" + std::to_string(key_idx));
+	wpa_hexdump_key(
+	    MSG_MSGDUMP, msg_dump_title.c_str(), wpa_ssid->wep_key[key_idx],
+	    wpa_ssid->wep_key_len[key_idx]);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setWepTxKeyIdxInternal(uint32_t key_idx)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (key_idx >=
+	    static_cast<uint32_t>(
+		ISupplicantStaNetwork::ParamSizeLimits::WEP_KEYS_MAX_NUM)) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	wpa_ssid->wep_tx_keyidx = key_idx;
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setRequirePmfInternal(bool enable)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->ieee80211w =
+	    enable ? MGMT_FRAME_PROTECTION_REQUIRED : NO_MGMT_FRAME_PROTECTION;
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapMethodInternal(
+    ISupplicantStaNetwork::EapMethod method)
+{
+	uint32_t eap_method_idx = static_cast<
+	    std::underlying_type<ISupplicantStaNetwork::EapMethod>::type>(
+	    method);
+	if (eap_method_idx >= kEapMethodMax) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	int retrieved_vendor, retrieved_method;
+	const char *method_str = kEapMethodStrings[eap_method_idx];
+	// This string lookup is needed to check if the device supports the
+	// corresponding EAP type.
+	retrieved_method = eap_peer_get_type(method_str, &retrieved_vendor);
+	if (retrieved_vendor == EAP_VENDOR_IETF &&
+	    retrieved_method == EAP_TYPE_NONE) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	if (wpa_ssid->eap.eap_methods) {
+		os_free(wpa_ssid->eap.eap_methods);
+	}
+	// wpa_supplicant can support setting multiple eap methods for each
+	// network. But, this is not really used by Android. So, just adding
+	// support for setting one EAP method for each network. The additional
+	// |eap_method_type| member in the array is used to indicate the end
+	// of list.
+	wpa_ssid->eap.eap_methods =
+	    (eap_method_type *)os_malloc(sizeof(eap_method_type) * 2);
+	if (!wpa_ssid->eap.eap_methods) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	wpa_ssid->eap.eap_methods[0].vendor = retrieved_vendor;
+	wpa_ssid->eap.eap_methods[0].method = retrieved_method;
+	wpa_ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
+	wpa_ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
+
+	wpa_ssid->leap = 0;
+	wpa_ssid->non_leap = 0;
+	if (retrieved_vendor == EAP_VENDOR_IETF &&
+	    retrieved_method == EAP_TYPE_LEAP) {
+		wpa_ssid->leap++;
+	} else {
+		wpa_ssid->non_leap++;
+	}
+	wpa_hexdump(
+	    MSG_MSGDUMP, "eap methods", (u8 *)wpa_ssid->eap.eap_methods,
+	    sizeof(eap_method_type) * 2);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapPhase2MethodInternal(
+    ISupplicantStaNetwork::EapPhase2Method method)
+{
+	uint32_t eap_phase2_method_idx = static_cast<
+	    std::underlying_type<ISupplicantStaNetwork::EapPhase2Method>::type>(
+	    method);
+	if (eap_phase2_method_idx >= kEapPhase2MethodMax) {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	// EAP method needs to be set for us to construct the eap
+	// phase 2 method string.
+	SupplicantStatus status;
+	ISupplicantStaNetwork::EapMethod eap_method;
+	std::tie(status, eap_method) = getEapMethodInternal();
+	if (status.code != SupplicantStatusCode::SUCCESS) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN,
+			"EAP method not set"};
+	}
+	std::string eap_phase2_str;
+	if (method == ISupplicantStaNetwork::EapPhase2Method::NONE) {
+		eap_phase2_str = "";
+	} else if (
+	    eap_method == ISupplicantStaNetwork::EapMethod::TTLS &&
+	    method == ISupplicantStaNetwork::EapPhase2Method::GTC) {
+		eap_phase2_str = kEapPhase2AuthEapPrefix;
+	} else {
+		eap_phase2_str = kEapPhase2AuthPrefix;
+	}
+	eap_phase2_str += kEapPhase2MethodStrings[eap_phase2_method_idx];
+	if (setStringFieldAndResetState(
+		eap_phase2_str.c_str(), &(wpa_ssid->eap.phase2),
+		"eap phase2")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapIdentityInternal(
+    const std::vector<uint8_t> &identity)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setByteArrayFieldAndResetState(
+		identity.data(), identity.size(), &(wpa_ssid->eap.identity),
+		&(wpa_ssid->eap.identity_len), "eap identity")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapAnonymousIdentityInternal(
+    const std::vector<uint8_t> &identity)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setByteArrayFieldAndResetState(
+		identity.data(), identity.size(),
+		&(wpa_ssid->eap.anonymous_identity),
+		&(wpa_ssid->eap.anonymous_identity_len),
+		"eap anonymous_identity")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapPasswordInternal(
+    const std::vector<uint8_t> &password)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setByteArrayKeyFieldAndResetState(
+		password.data(), password.size(), &(wpa_ssid->eap.password),
+		&(wpa_ssid->eap.password_len), "eap password")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	wpa_ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+	wpa_ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_PASSWORD;
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapCACertInternal(const std::string &path)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		path.c_str(), &(wpa_ssid->eap.ca_cert), "eap ca_cert")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapCAPathInternal(const std::string &path)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		path.c_str(), &(wpa_ssid->eap.ca_path), "eap ca_path")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapClientCertInternal(const std::string &path)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		path.c_str(), &(wpa_ssid->eap.client_cert),
+		"eap client_cert")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapPrivateKeyIdInternal(const std::string &id)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		id.c_str(), &(wpa_ssid->eap.key_id), "eap key_id")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapSubjectMatchInternal(
+    const std::string &match)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		match.c_str(), &(wpa_ssid->eap.subject_match),
+		"eap subject_match")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapAltSubjectMatchInternal(
+    const std::string &match)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		match.c_str(), &(wpa_ssid->eap.altsubject_match),
+		"eap altsubject_match")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapEngineInternal(bool enable)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->eap.engine = enable ? 1 : 0;
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapEngineIDInternal(const std::string &id)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		id.c_str(), &(wpa_ssid->eap.engine_id), "eap engine_id")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setEapDomainSuffixMatchInternal(
+    const std::string &match)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		match.c_str(), &(wpa_ssid->eap.domain_suffix_match),
+		"eap domain_suffix_match")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setProactiveKeyCachingInternal(bool enable)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->proactive_key_caching = enable ? 1 : 0;
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setIdStrInternal(const std::string &id_str)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (setStringFieldAndResetState(
+		id_str.c_str(), &(wpa_ssid->id_str), "id_str")) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::setUpdateIdentifierInternal(uint32_t id)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	wpa_ssid->update_identifier = id;
+	wpa_printf(
+	    MSG_MSGDUMP, "update_identifier: %d", wpa_ssid->update_identifier);
+	resetInternalStateAfterParamsUpdate();
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>> StaNetwork::getSsidInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	std::vector<uint8_t> ssid;
+	ssid.assign(wpa_ssid->ssid, wpa_ssid->ssid + wpa_ssid->ssid_len);
+	return {{SupplicantStatusCode::SUCCESS, ""}, std::move(ssid)};
+}
+
+std::pair<SupplicantStatus, std::array<uint8_t, 6>>
+StaNetwork::getBssidInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	std::array<uint8_t, 6> bssid{};
+	os_memcpy(bssid.data(), kZeroBssid, ETH_ALEN);
+	if (wpa_ssid->bssid_set) {
+		os_memcpy(bssid.data(), wpa_ssid->bssid, ETH_ALEN);
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, std::move(bssid)};
+}
+
+std::pair<SupplicantStatus, bool> StaNetwork::getScanSsidInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		(wpa_ssid->scan_ssid == 1)};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getKeyMgmtInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->key_mgmt & kAllowedKeyMgmtMask};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getProtoInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->proto & kAllowedProtoMask};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getAuthAlgInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->auth_alg & kAllowedAuthAlgMask};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getGroupCipherInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->group_cipher & kAllowedGroupCipherMask};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getPairwiseCipherInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		wpa_ssid->pairwise_cipher & kAllowedPairwisewCipherMask};
+}
+
+std::pair<SupplicantStatus, std::string> StaNetwork::getPskPassphraseInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->passphrase) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->passphrase};
+}
+
+std::pair<SupplicantStatus, std::array<uint8_t, 32>>
+StaNetwork::getPskInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	WPA_ASSERT(psk.size() == sizeof(wpa_ssid->psk));
+	if (!wpa_ssid->psk_set) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	std::array<uint8_t, 32> psk;
+	os_memcpy(psk.data(), wpa_ssid->psk, psk.size());
+	return {{SupplicantStatusCode::SUCCESS, ""}, psk};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>> StaNetwork::getWepKeyInternal(
+    uint32_t key_idx)
+{
+	std::vector<uint8_t> wep_key;
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (key_idx >=
+	    static_cast<uint32_t>(
+		ISupplicantStaNetwork::ParamSizeLimits::WEP_KEYS_MAX_NUM)) {
+		return {{SupplicantStatusCode::FAILURE_ARGS_INVALID, ""},
+			wep_key};
+	}
+	wep_key.assign(
+	    wpa_ssid->wep_key[key_idx],
+	    wpa_ssid->wep_key[key_idx] + wpa_ssid->wep_key_len[key_idx]);
+	return {{SupplicantStatusCode::SUCCESS, ""}, std::move(wep_key)};
+}
+
+std::pair<SupplicantStatus, uint32_t> StaNetwork::getWepTxKeyIdxInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->wep_tx_keyidx};
+}
+
+std::pair<SupplicantStatus, bool> StaNetwork::getRequirePmfInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		(wpa_ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)};
+}
+
+std::pair<SupplicantStatus, ISupplicantStaNetwork::EapMethod>
+StaNetwork::getEapMethodInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.eap_methods) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	// wpa_supplicant can support setting multiple eap methods for each
+	// network. But, this is not really used by Android. So, just reading
+	// the first EAP method for each network.
+	const std::string eap_method_str = eap_get_name(
+	    wpa_ssid->eap.eap_methods[0].vendor,
+	    static_cast<EapType>(wpa_ssid->eap.eap_methods[0].method));
+	size_t eap_method_idx =
+	    std::find(
+		std::begin(kEapMethodStrings), std::end(kEapMethodStrings),
+		eap_method_str) -
+	    std::begin(kEapMethodStrings);
+	if (eap_method_idx >= kEapMethodMax) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		static_cast<ISupplicantStaNetwork::EapMethod>(eap_method_idx)};
+}
+
+std::pair<SupplicantStatus, ISupplicantStaNetwork::EapPhase2Method>
+StaNetwork::getEapPhase2MethodInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.phase2) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	const std::string eap_phase2_method_str_with_prefix =
+	    wpa_ssid->eap.phase2;
+	std::string eap_phase2_method_str;
+	// Strip out the phase 2 method prefix before doing a reverse lookup
+	// of phase 2 string to the Eap Phase 2 type.
+	if (eap_phase2_method_str_with_prefix.find(kEapPhase2AuthPrefix) == 0) {
+		eap_phase2_method_str =
+		    eap_phase2_method_str_with_prefix.substr(
+			strlen(kEapPhase2AuthPrefix),
+			eap_phase2_method_str_with_prefix.size());
+	} else if (
+	    eap_phase2_method_str_with_prefix.find(kEapPhase2AuthEapPrefix) ==
+	    0) {
+		eap_phase2_method_str =
+		    eap_phase2_method_str_with_prefix.substr(
+			strlen(kEapPhase2AuthEapPrefix),
+			eap_phase2_method_str_with_prefix.size());
+	}
+	size_t eap_phase2_method_idx =
+	    std::find(
+		std::begin(kEapPhase2MethodStrings),
+		std::end(kEapPhase2MethodStrings), eap_phase2_method_str) -
+	    std::begin(kEapPhase2MethodStrings);
+	if (eap_phase2_method_idx >= kEapPhase2MethodMax) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		static_cast<ISupplicantStaNetwork::EapPhase2Method>(
+		    eap_phase2_method_idx)};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>>
+StaNetwork::getEapIdentityInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.identity) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		std::vector<uint8_t>(
+		    wpa_ssid->eap.identity,
+		    wpa_ssid->eap.identity + wpa_ssid->eap.identity_len)};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>>
+StaNetwork::getEapAnonymousIdentityInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.anonymous_identity) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		std::vector<uint8_t>(
+		    wpa_ssid->eap.anonymous_identity,
+		    wpa_ssid->eap.anonymous_identity +
+			wpa_ssid->eap.anonymous_identity_len)};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>>
+StaNetwork::getEapPasswordInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.password) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		std::vector<uint8_t>(
+		    wpa_ssid->eap.password,
+		    wpa_ssid->eap.password + wpa_ssid->eap.password_len)};
+}
+
+std::pair<SupplicantStatus, std::string> StaNetwork::getEapCACertInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.ca_cert) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		reinterpret_cast<char *>(wpa_ssid->eap.ca_cert)};
+}
+
+std::pair<SupplicantStatus, std::string> StaNetwork::getEapCAPathInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.ca_path) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		reinterpret_cast<char *>(wpa_ssid->eap.ca_path)};
+}
+
+std::pair<SupplicantStatus, std::string> StaNetwork::getEapClientCertInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.client_cert) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		reinterpret_cast<char *>(wpa_ssid->eap.client_cert)};
+}
+
+std::pair<SupplicantStatus, std::string>
+StaNetwork::getEapPrivateKeyIdInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.key_id) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->eap.key_id};
+}
+
+std::pair<SupplicantStatus, std::string>
+StaNetwork::getEapSubjectMatchInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.subject_match) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		reinterpret_cast<char *>(wpa_ssid->eap.subject_match)};
+}
+
+std::pair<SupplicantStatus, std::string>
+StaNetwork::getEapAltSubjectMatchInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.altsubject_match) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		reinterpret_cast<char *>(wpa_ssid->eap.altsubject_match)};
+}
+
+std::pair<SupplicantStatus, bool> StaNetwork::getEapEngineInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	return {{SupplicantStatusCode::SUCCESS, ""}, wpa_ssid->eap.engine == 1};
+}
+
+std::pair<SupplicantStatus, std::string> StaNetwork::getEapEngineIDInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.engine_id) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, {wpa_ssid->eap.engine_id}};
+}
+
+std::pair<SupplicantStatus, std::string>
+StaNetwork::getEapDomainSuffixMatchInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->eap.domain_suffix_match) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		{wpa_ssid->eap.domain_suffix_match}};
+}
+
+std::pair<SupplicantStatus, std::string> StaNetwork::getIdStrInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (!wpa_ssid->id_str) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, {wpa_ssid->id_str}};
+}
+
+std::pair<SupplicantStatus, std::vector<uint8_t>>
+StaNetwork::getWpsNfcConfigurationTokenInternal()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	auto token_buf = misc_utils::createWpaBufUniquePtr(
+	    wpas_wps_network_config_token(wpa_s, 0, wpa_ssid));
+	if (!token_buf) {
+		return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""}, {}};
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""},
+		misc_utils::convertWpaBufToVector(token_buf.get())};
+}
+
+SupplicantStatus StaNetwork::enableInternal(bool no_connect)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (wpa_ssid->disabled == 2) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (no_connect) {
+		wpa_ssid->disabled = 0;
+	} else {
+		wpa_s->scan_min_time.sec = 0;
+		wpa_s->scan_min_time.usec = 0;
+		wpa_supplicant_enable_network(wpa_s, wpa_ssid);
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::disableInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (wpa_ssid->disabled == 2) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	wpa_supplicant_disable_network(wpa_s, wpa_ssid);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::selectInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	if (wpa_ssid->disabled == 2) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	wpa_s->scan_min_time.sec = 0;
+	wpa_s->scan_min_time.usec = 0;
+	wpa_supplicant_select_network(wpa_s, wpa_ssid);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::sendNetworkEapSimGsmAuthResponseInternal(
+    const std::vector<ISupplicantStaNetwork::NetworkResponseEapSimGsmAuthParams>
+	&vec_params)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	// Convert the incoming parameters to a string to pass to
+	// wpa_supplicant.
+	std::string ctrl_rsp_param = std::string(kNetworkEapSimGsmAuthResponse);
+	for (const auto &params : vec_params) {
+		uint32_t kc_hex_len = params.kc.size() * 2 + 1;
+		std::vector<char> kc_hex(kc_hex_len);
+		uint32_t sres_hex_len = params.sres.size() * 2 + 1;
+		std::vector<char> sres_hex(sres_hex_len);
+		wpa_snprintf_hex(
+		    kc_hex.data(), kc_hex.size(), params.kc.data(),
+		    params.kc.size());
+		wpa_snprintf_hex(
+		    sres_hex.data(), sres_hex.size(), params.sres.data(),
+		    params.sres.size());
+		ctrl_rsp_param += ":" + std::string(kc_hex.data()) + ":" +
+				  std::string(sres_hex.data());
+	}
+	enum wpa_ctrl_req_type rtype = WPA_CTRL_REQ_SIM;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_supplicant_ctrl_rsp_handle(
+		wpa_s, wpa_ssid, rtype, ctrl_rsp_param.c_str())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	eapol_sm_notify_ctrl_response(wpa_s->eapol);
+	wpa_hexdump_ascii_key(
+	    MSG_DEBUG, "network sim gsm auth response param",
+	    (const u8 *)ctrl_rsp_param.c_str(), ctrl_rsp_param.size());
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::sendNetworkEapSimGsmAuthFailureInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	enum wpa_ctrl_req_type rtype = WPA_CTRL_REQ_SIM;
+	if (wpa_supplicant_ctrl_rsp_handle(
+		wpa_s, wpa_ssid, rtype, kNetworkEapSimGsmAuthFailure)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	eapol_sm_notify_ctrl_response(wpa_s->eapol);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::sendNetworkEapSimUmtsAuthResponseInternal(
+    const ISupplicantStaNetwork::NetworkResponseEapSimUmtsAuthParams &params)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	// Convert the incoming parameters to a string to pass to
+	// wpa_supplicant.
+	uint32_t ik_hex_len = params.ik.size() * 2 + 1;
+	std::vector<char> ik_hex(ik_hex_len);
+	uint32_t ck_hex_len = params.ck.size() * 2 + 1;
+	std::vector<char> ck_hex(ck_hex_len);
+	uint32_t res_hex_len = params.res.size() * 2 + 1;
+	std::vector<char> res_hex(res_hex_len);
+	wpa_snprintf_hex(
+	    ik_hex.data(), ik_hex.size(), params.ik.data(), params.ik.size());
+	wpa_snprintf_hex(
+	    ck_hex.data(), ck_hex.size(), params.ck.data(), params.ck.size());
+	wpa_snprintf_hex(
+	    res_hex.data(), res_hex.size(), params.res.data(),
+	    params.res.size());
+	std::string ctrl_rsp_param =
+	    std::string(kNetworkEapSimUmtsAuthResponse) + ":" +
+	    std::string(ik_hex.data()) + ":" + std::string(ck_hex.data()) +
+	    ":" + std::string(res_hex.data());
+	enum wpa_ctrl_req_type rtype = WPA_CTRL_REQ_SIM;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_supplicant_ctrl_rsp_handle(
+		wpa_s, wpa_ssid, rtype, ctrl_rsp_param.c_str())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	eapol_sm_notify_ctrl_response(wpa_s->eapol);
+	wpa_hexdump_ascii_key(
+	    MSG_DEBUG, "network sim umts auth response param",
+	    (const u8 *)ctrl_rsp_param.c_str(), ctrl_rsp_param.size());
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::sendNetworkEapSimUmtsAutsResponseInternal(
+    const std::array<uint8_t, 14> &auts)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	uint32_t auts_hex_len = auts.size() * 2 + 1;
+	std::vector<char> auts_hex(auts_hex_len);
+	wpa_snprintf_hex(
+	    auts_hex.data(), auts_hex.size(), auts.data(), auts.size());
+	std::string ctrl_rsp_param =
+	    std::string(kNetworkEapSimUmtsAutsResponse) + ":" +
+	    std::string(auts_hex.data());
+	enum wpa_ctrl_req_type rtype = WPA_CTRL_REQ_SIM;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_supplicant_ctrl_rsp_handle(
+		wpa_s, wpa_ssid, rtype, ctrl_rsp_param.c_str())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	eapol_sm_notify_ctrl_response(wpa_s->eapol);
+	wpa_hexdump_ascii_key(
+	    MSG_DEBUG, "network sim umts auts response param",
+	    (const u8 *)ctrl_rsp_param.c_str(), ctrl_rsp_param.size());
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::sendNetworkEapSimUmtsAuthFailureInternal()
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	enum wpa_ctrl_req_type rtype = WPA_CTRL_REQ_SIM;
+	if (wpa_supplicant_ctrl_rsp_handle(
+		wpa_s, wpa_ssid, rtype, kNetworkEapSimUmtsAuthFailure)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	eapol_sm_notify_ctrl_response(wpa_s->eapol);
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus StaNetwork::sendNetworkEapIdentityResponseInternal(
+    const std::vector<uint8_t> &identity)
+{
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+	std::string ctrl_rsp_param(identity.begin(), identity.end());
+	enum wpa_ctrl_req_type rtype = WPA_CTRL_REQ_EAP_IDENTITY;
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (wpa_supplicant_ctrl_rsp_handle(
+		wpa_s, wpa_ssid, rtype, ctrl_rsp_param.c_str())) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	eapol_sm_notify_ctrl_response(wpa_s->eapol);
+	wpa_hexdump_ascii_key(
+	    MSG_DEBUG, "network identity response param",
+	    (const u8 *)ctrl_rsp_param.c_str(), ctrl_rsp_param.size());
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+/**
+ * Retrieve the underlying |wpa_ssid| struct pointer for
+ * this network.
+ * If the underlying network is removed or the interface
+ * this network belong to
+ * is removed, all RPC method calls on this object will
+ * return failure.
+ */
+struct wpa_ssid *StaNetwork::retrieveNetworkPtr()
+{
+	wpa_supplicant *wpa_s = retrieveIfacePtr();
+	if (!wpa_s)
+		return nullptr;
+	return wpa_config_get_network(wpa_s->conf, network_id_);
+}
+
+/**
+ * Retrieve the underlying |wpa_supplicant| struct
+ * pointer for
+ * this network.
+ */
+struct wpa_supplicant *StaNetwork::retrieveIfacePtr()
+{
+	return wpa_supplicant_get_iface(wpa_global_, ifname_.c_str());
+}
+
+/**
+ * Check if the provided psk passhrase is valid or not.
+ *
+ * Returns 0 if valid, 1 otherwise.
+ */
+int StaNetwork::isPskPassphraseValid(const std::string &psk)
+{
+	if (psk.size() <
+		static_cast<uint32_t>(ISupplicantStaNetwork::ParamSizeLimits::
+					  PSK_PASSPHRASE_MIN_LEN_IN_BYTES) ||
+	    psk.size() >
+		static_cast<uint32_t>(ISupplicantStaNetwork::ParamSizeLimits::
+					  PSK_PASSPHRASE_MAX_LEN_IN_BYTES)) {
+		return 1;
+	}
+	if (has_ctrl_char((u8 *)psk.c_str(), psk.size())) {
+		return 1;
+	}
+	return 0;
+}
+
+/**
+ * Reset internal wpa_supplicant state machine state
+ * after params update (except
+ * bssid).
+ */
+void StaNetwork::resetInternalStateAfterParamsUpdate()
+{
+	struct wpa_supplicant *wpa_s = retrieveIfacePtr();
+	struct wpa_ssid *wpa_ssid = retrieveNetworkPtr();
+
+	wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_ssid);
+
+	if (wpa_s->current_ssid == wpa_ssid || wpa_s->current_ssid == NULL) {
+		/*
+		 * Invalidate the EAP session cache if
+		 * anything in the
+		 * current or previously used
+		 * configuration changes.
+		 */
+		eapol_sm_invalidate_cached_session(wpa_s->eapol);
+	}
+}
+
+/**
+ * Helper function to set value in a string field in |wpa_ssid| structue
+ * instance for this network.
+ * This function frees any existing data in these fields.
+ */
+int StaNetwork::setStringFieldAndResetState(
+    const char *value, uint8_t **to_update_field, const char *hexdump_prefix)
+{
+	return setStringFieldAndResetState(
+	    value, (char **)to_update_field, hexdump_prefix);
+}
+
+/**
+ * Helper function to set value in a string field in |wpa_ssid| structue
+ * instance for this network.
+ * This function frees any existing data in these fields.
+ */
+int StaNetwork::setStringFieldAndResetState(
+    const char *value, char **to_update_field, const char *hexdump_prefix)
+{
+	int value_len = strlen(value);
+	if (*to_update_field) {
+		os_free(*to_update_field);
+	}
+	*to_update_field = dup_binstr(value, value_len);
+	if (!(*to_update_field)) {
+		return 1;
+	}
+	wpa_hexdump_ascii(
+	    MSG_MSGDUMP, hexdump_prefix, *to_update_field, value_len);
+	resetInternalStateAfterParamsUpdate();
+	return 0;
+}
+
+/**
+ * Helper function to set value in a string key field in |wpa_ssid| structue
+ * instance for this network.
+ * This function frees any existing data in these fields.
+ */
+int StaNetwork::setStringKeyFieldAndResetState(
+    const char *value, char **to_update_field, const char *hexdump_prefix)
+{
+	int value_len = strlen(value);
+	if (*to_update_field) {
+		str_clear_free(*to_update_field);
+	}
+	*to_update_field = dup_binstr(value, value_len);
+	if (!(*to_update_field)) {
+		return 1;
+	}
+	wpa_hexdump_ascii_key(
+	    MSG_MSGDUMP, hexdump_prefix, *to_update_field, value_len);
+	resetInternalStateAfterParamsUpdate();
+	return 0;
+}
+
+/**
+ * Helper function to set value in a string field with a corresponding length
+ * field in |wpa_ssid| structue instance for this network.
+ * This function frees any existing data in these fields.
+ */
+int StaNetwork::setByteArrayFieldAndResetState(
+    const uint8_t *value, const size_t value_len, uint8_t **to_update_field,
+    size_t *to_update_field_len, const char *hexdump_prefix)
+{
+	if (*to_update_field) {
+		os_free(*to_update_field);
+	}
+	*to_update_field = (uint8_t *)os_malloc(value_len);
+	if (!(*to_update_field)) {
+		return 1;
+	}
+	os_memcpy(*to_update_field, value, value_len);
+	*to_update_field_len = value_len;
+
+	wpa_hexdump_ascii(
+	    MSG_MSGDUMP, hexdump_prefix, *to_update_field,
+	    *to_update_field_len);
+	resetInternalStateAfterParamsUpdate();
+	return 0;
+}
+
+/**
+ * Helper function to set value in a string key field with a corresponding
+ * length field in |wpa_ssid| structue instance for this network.
+ * This function frees any existing data in these fields.
+ */
+int StaNetwork::setByteArrayKeyFieldAndResetState(
+    const uint8_t *value, const size_t value_len, uint8_t **to_update_field,
+    size_t *to_update_field_len, const char *hexdump_prefix)
+{
+	if (*to_update_field) {
+		bin_clear_free(*to_update_field, *to_update_field_len);
+	}
+	*to_update_field = (uint8_t *)os_malloc(value_len);
+	if (!(*to_update_field)) {
+		return 1;
+	}
+	os_memcpy(*to_update_field, value, value_len);
+	*to_update_field_len = value_len;
+
+	wpa_hexdump_ascii_key(
+	    MSG_MSGDUMP, hexdump_prefix, *to_update_field,
+	    *to_update_field_len);
+	resetInternalStateAfterParamsUpdate();
+	return 0;
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
diff --git a/wpa_supplicant/hidl/1.0/sta_network.h b/wpa_supplicant/hidl/1.0/sta_network.h
new file mode 100644
index 0000000..6e8d42b
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/sta_network.h
@@ -0,0 +1,331 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_STA_NETWORK_H
+#define WPA_SUPPLICANT_HIDL_STA_NETWORK_H
+
+#include <array>
+#include <vector>
+
+#include <android-base/macros.h>
+
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetwork.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantStaNetworkCallback.h>
+
+extern "C" {
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "notify.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
+#include "rsn_supp/wpa.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+
+/**
+ * Implementation of StaNetwork hidl object. Each unique hidl
+ * object is used for control operations on a specific network
+ * controlled by wpa_supplicant.
+ */
+class StaNetwork : public ISupplicantStaNetwork
+{
+public:
+	StaNetwork(
+	    struct wpa_global* wpa_global, const char ifname[], int network_id);
+	~StaNetwork() override = default;
+	// Refer to |StaIface::invalidate()|.
+	void invalidate();
+	bool isValid();
+
+	// Hidl methods exposed.
+	Return<void> getId(getId_cb _hidl_cb) override;
+	Return<void> getInterfaceName(getInterfaceName_cb _hidl_cb) override;
+	Return<void> getType(getType_cb _hidl_cb) override;
+	Return<void> registerCallback(
+	    const sp<ISupplicantStaNetworkCallback>& callback,
+	    registerCallback_cb _hidl_cb) override;
+	Return<void> setSsid(
+	    const hidl_vec<uint8_t>& ssid, setSsid_cb _hidl_cb) override;
+	Return<void> setBssid(
+	    const hidl_array<uint8_t, 6>& bssid, setBssid_cb _hidl_cb) override;
+	Return<void> setScanSsid(bool enable, setScanSsid_cb _hidl_cb) override;
+	Return<void> setKeyMgmt(
+	    uint32_t key_mgmt_mask, setKeyMgmt_cb _hidl_cb) override;
+	Return<void> setProto(
+	    uint32_t proto_mask, setProto_cb _hidl_cb) override;
+	Return<void> setAuthAlg(
+	    uint32_t auth_alg_mask, setAuthAlg_cb _hidl_cb) override;
+	Return<void> setGroupCipher(
+	    uint32_t group_cipher_mask, setGroupCipher_cb _hidl_cb) override;
+	Return<void> setPairwiseCipher(
+	    uint32_t pairwise_cipher_mask,
+	    setPairwiseCipher_cb _hidl_cb) override;
+	Return<void> setPskPassphrase(
+	    const hidl_string& psk, setPskPassphrase_cb _hidl_cb) override;
+	Return<void> setPsk(
+	    const hidl_array<uint8_t, 32>& psk, setPsk_cb _hidl_cb) override;
+	Return<void> setWepKey(
+	    uint32_t key_idx, const hidl_vec<uint8_t>& wep_key,
+	    setWepKey_cb _hidl_cb) override;
+	Return<void> setWepTxKeyIdx(
+	    uint32_t key_idx, setWepTxKeyIdx_cb _hidl_cb) override;
+	Return<void> setRequirePmf(
+	    bool enable, setRequirePmf_cb _hidl_cb) override;
+	Return<void> setEapMethod(
+	    ISupplicantStaNetwork::EapMethod method,
+	    setEapMethod_cb _hidl_cb) override;
+	Return<void> setEapPhase2Method(
+	    ISupplicantStaNetwork::EapPhase2Method method,
+	    setEapPhase2Method_cb _hidl_cb) override;
+	Return<void> setEapIdentity(
+	    const hidl_vec<uint8_t>& identity,
+	    setEapIdentity_cb _hidl_cb) override;
+	Return<void> setEapAnonymousIdentity(
+	    const hidl_vec<uint8_t>& identity,
+	    setEapAnonymousIdentity_cb _hidl_cb) override;
+	Return<void> setEapPassword(
+	    const hidl_vec<uint8_t>& password,
+	    setEapPassword_cb _hidl_cb) override;
+	Return<void> setEapCACert(
+	    const hidl_string& path, setEapCACert_cb _hidl_cb) override;
+	Return<void> setEapCAPath(
+	    const hidl_string& path, setEapCAPath_cb _hidl_cb) override;
+	Return<void> setEapClientCert(
+	    const hidl_string& path, setEapClientCert_cb _hidl_cb) override;
+	Return<void> setEapPrivateKeyId(
+	    const hidl_string& id, setEapPrivateKeyId_cb _hidl_cb) override;
+	Return<void> setEapSubjectMatch(
+	    const hidl_string& match, setEapSubjectMatch_cb _hidl_cb) override;
+	Return<void> setEapAltSubjectMatch(
+	    const hidl_string& match,
+	    setEapAltSubjectMatch_cb _hidl_cb) override;
+	Return<void> setEapEngine(
+	    bool enable, setEapEngine_cb _hidl_cb) override;
+	Return<void> setEapEngineID(
+	    const hidl_string& id, setEapEngineID_cb _hidl_cb) override;
+	Return<void> setEapDomainSuffixMatch(
+	    const hidl_string& match,
+	    setEapDomainSuffixMatch_cb _hidl_cb) override;
+	Return<void> setProactiveKeyCaching(
+	    bool enable, setProactiveKeyCaching_cb _hidl_cb) override;
+	Return<void> setIdStr(
+	    const hidl_string& id_str, setIdStr_cb _hidl_cb) override;
+	Return<void> setUpdateIdentifier(
+	    uint32_t id, setUpdateIdentifier_cb _hidl_cb) override;
+	Return<void> getSsid(getSsid_cb _hidl_cb) override;
+	Return<void> getBssid(getBssid_cb _hidl_cb) override;
+	Return<void> getScanSsid(getScanSsid_cb _hidl_cb) override;
+	Return<void> getKeyMgmt(getKeyMgmt_cb _hidl_cb) override;
+	Return<void> getProto(getProto_cb _hidl_cb) override;
+	Return<void> getAuthAlg(getAuthAlg_cb _hidl_cb) override;
+	Return<void> getGroupCipher(getGroupCipher_cb _hidl_cb) override;
+	Return<void> getPairwiseCipher(getPairwiseCipher_cb _hidl_cb) override;
+	Return<void> getPskPassphrase(getPskPassphrase_cb _hidl_cb) override;
+	Return<void> getPsk(getPsk_cb _hidl_cb) override;
+	Return<void> getWepKey(
+	    uint32_t key_idx, getWepKey_cb _hidl_cb) override;
+	Return<void> getWepTxKeyIdx(getWepTxKeyIdx_cb _hidl_cb) override;
+	Return<void> getRequirePmf(getRequirePmf_cb _hidl_cb) override;
+	Return<void> getEapMethod(getEapMethod_cb _hidl_cb) override;
+	Return<void> getEapPhase2Method(
+	    getEapPhase2Method_cb _hidl_cb) override;
+	Return<void> getEapIdentity(getEapIdentity_cb _hidl_cb) override;
+	Return<void> getEapAnonymousIdentity(
+	    getEapAnonymousIdentity_cb _hidl_cb) override;
+	Return<void> getEapPassword(getEapPassword_cb _hidl_cb) override;
+	Return<void> getEapCACert(getEapCACert_cb _hidl_cb) override;
+	Return<void> getEapCAPath(getEapCAPath_cb _hidl_cb) override;
+	Return<void> getEapClientCert(getEapClientCert_cb _hidl_cb) override;
+	Return<void> getEapPrivateKeyId(
+	    getEapPrivateKeyId_cb _hidl_cb) override;
+	Return<void> getEapSubjectMatch(
+	    getEapSubjectMatch_cb _hidl_cb) override;
+	Return<void> getEapAltSubjectMatch(
+	    getEapAltSubjectMatch_cb _hidl_cb) override;
+	Return<void> getEapEngine(getEapEngine_cb _hidl_cb) override;
+	Return<void> getEapEngineID(getEapEngineID_cb _hidl_cb) override;
+	Return<void> getEapDomainSuffixMatch(
+	    getEapDomainSuffixMatch_cb _hidl_cb) override;
+	Return<void> getIdStr(getIdStr_cb _hidl_cb) override;
+	Return<void> getWpsNfcConfigurationToken(
+	    getWpsNfcConfigurationToken_cb _hidl_cb) override;
+	Return<void> enable(bool no_connect, enable_cb _hidl_cb) override;
+	Return<void> disable(disable_cb _hidl_cb) override;
+	Return<void> select(select_cb _hidl_cb) override;
+	Return<void> sendNetworkEapSimGsmAuthResponse(
+	    const hidl_vec<
+		ISupplicantStaNetwork::NetworkResponseEapSimGsmAuthParams>&
+		vec_params,
+	    sendNetworkEapSimGsmAuthResponse_cb _hidl_cb) override;
+	Return<void> sendNetworkEapSimGsmAuthFailure(
+	    sendNetworkEapSimGsmAuthFailure_cb _hidl_cb) override;
+	Return<void> sendNetworkEapSimUmtsAuthResponse(
+	    const ISupplicantStaNetwork::NetworkResponseEapSimUmtsAuthParams&
+		params,
+	    sendNetworkEapSimUmtsAuthResponse_cb _hidl_cb) override;
+	Return<void> sendNetworkEapSimUmtsAutsResponse(
+	    const hidl_array<uint8_t, 14>& auts,
+	    sendNetworkEapSimUmtsAutsResponse_cb _hidl_cb) override;
+	Return<void> sendNetworkEapSimUmtsAuthFailure(
+	    sendNetworkEapSimUmtsAuthFailure_cb _hidl_cb) override;
+	Return<void> sendNetworkEapIdentityResponse(
+	    const hidl_vec<uint8_t>& identity,
+	    sendNetworkEapIdentityResponse_cb _hidl_cb) override;
+
+private:
+	// Corresponding worker functions for the HIDL methods.
+	std::pair<SupplicantStatus, uint32_t> getIdInternal();
+	std::pair<SupplicantStatus, std::string> getInterfaceNameInternal();
+	std::pair<SupplicantStatus, IfaceType> getTypeInternal();
+	SupplicantStatus registerCallbackInternal(
+	    const sp<ISupplicantStaNetworkCallback>& callback);
+	SupplicantStatus setSsidInternal(const std::vector<uint8_t>& ssid);
+	SupplicantStatus setBssidInternal(const std::array<uint8_t, 6>& bssid);
+	SupplicantStatus setScanSsidInternal(bool enable);
+	SupplicantStatus setKeyMgmtInternal(uint32_t key_mgmt_mask);
+	SupplicantStatus setProtoInternal(uint32_t proto_mask);
+	SupplicantStatus setAuthAlgInternal(uint32_t auth_alg_mask);
+	SupplicantStatus setGroupCipherInternal(uint32_t group_cipher_mask);
+	SupplicantStatus setPairwiseCipherInternal(
+	    uint32_t pairwise_cipher_mask);
+	SupplicantStatus setPskPassphraseInternal(const std::string& psk);
+	SupplicantStatus setPskInternal(const std::array<uint8_t, 32>& psk);
+	SupplicantStatus setWepKeyInternal(
+	    uint32_t key_idx, const std::vector<uint8_t>& wep_key);
+	SupplicantStatus setWepTxKeyIdxInternal(uint32_t key_idx);
+	SupplicantStatus setRequirePmfInternal(bool enable);
+	SupplicantStatus setEapMethodInternal(
+	    ISupplicantStaNetwork::EapMethod method);
+	SupplicantStatus setEapPhase2MethodInternal(
+	    ISupplicantStaNetwork::EapPhase2Method method);
+	SupplicantStatus setEapIdentityInternal(
+	    const std::vector<uint8_t>& identity);
+	SupplicantStatus setEapAnonymousIdentityInternal(
+	    const std::vector<uint8_t>& identity);
+	SupplicantStatus setEapPasswordInternal(
+	    const std::vector<uint8_t>& password);
+	SupplicantStatus setEapCACertInternal(const std::string& path);
+	SupplicantStatus setEapCAPathInternal(const std::string& path);
+	SupplicantStatus setEapClientCertInternal(const std::string& path);
+	SupplicantStatus setEapPrivateKeyIdInternal(const std::string& id);
+	SupplicantStatus setEapSubjectMatchInternal(const std::string& match);
+	SupplicantStatus setEapAltSubjectMatchInternal(
+	    const std::string& match);
+	SupplicantStatus setEapEngineInternal(bool enable);
+	SupplicantStatus setEapEngineIDInternal(const std::string& id);
+	SupplicantStatus setEapDomainSuffixMatchInternal(
+	    const std::string& match);
+	SupplicantStatus setProactiveKeyCachingInternal(bool enable);
+	SupplicantStatus setIdStrInternal(const std::string& id_str);
+	SupplicantStatus setUpdateIdentifierInternal(uint32_t id);
+	std::pair<SupplicantStatus, std::vector<uint8_t>> getSsidInternal();
+	std::pair<SupplicantStatus, std::array<uint8_t, 6>> getBssidInternal();
+	std::pair<SupplicantStatus, bool> getScanSsidInternal();
+	std::pair<SupplicantStatus, uint32_t> getKeyMgmtInternal();
+	std::pair<SupplicantStatus, uint32_t> getProtoInternal();
+	std::pair<SupplicantStatus, uint32_t> getAuthAlgInternal();
+	std::pair<SupplicantStatus, uint32_t> getGroupCipherInternal();
+	std::pair<SupplicantStatus, uint32_t> getPairwiseCipherInternal();
+	std::pair<SupplicantStatus, std::string> getPskPassphraseInternal();
+	std::pair<SupplicantStatus, std::array<uint8_t, 32>> getPskInternal();
+	std::pair<SupplicantStatus, std::vector<uint8_t>> getWepKeyInternal(
+	    uint32_t key_idx);
+	std::pair<SupplicantStatus, uint32_t> getWepTxKeyIdxInternal();
+	std::pair<SupplicantStatus, bool> getRequirePmfInternal();
+	std::pair<SupplicantStatus, ISupplicantStaNetwork::EapMethod>
+	getEapMethodInternal();
+	std::pair<SupplicantStatus, ISupplicantStaNetwork::EapPhase2Method>
+	getEapPhase2MethodInternal();
+	std::pair<SupplicantStatus, std::vector<uint8_t>>
+	getEapIdentityInternal();
+	std::pair<SupplicantStatus, std::vector<uint8_t>>
+	getEapAnonymousIdentityInternal();
+	std::pair<SupplicantStatus, std::vector<uint8_t>>
+	getEapPasswordInternal();
+	std::pair<SupplicantStatus, std::string> getEapCACertInternal();
+	std::pair<SupplicantStatus, std::string> getEapCAPathInternal();
+	std::pair<SupplicantStatus, std::string> getEapClientCertInternal();
+	std::pair<SupplicantStatus, std::string> getEapPrivateKeyIdInternal();
+	std::pair<SupplicantStatus, std::string> getEapSubjectMatchInternal();
+	std::pair<SupplicantStatus, std::string>
+	getEapAltSubjectMatchInternal();
+	std::pair<SupplicantStatus, bool> getEapEngineInternal();
+	std::pair<SupplicantStatus, std::string> getEapEngineIDInternal();
+	std::pair<SupplicantStatus, std::string>
+	getEapDomainSuffixMatchInternal();
+	std::pair<SupplicantStatus, std::string> getIdStrInternal();
+	std::pair<SupplicantStatus, std::vector<uint8_t>>
+	getWpsNfcConfigurationTokenInternal();
+	SupplicantStatus enableInternal(bool no_connect);
+	SupplicantStatus disableInternal();
+	SupplicantStatus selectInternal();
+	SupplicantStatus sendNetworkEapSimGsmAuthResponseInternal(
+	    const std::vector<
+		ISupplicantStaNetwork::NetworkResponseEapSimGsmAuthParams>&
+		vec_params);
+	SupplicantStatus sendNetworkEapSimGsmAuthFailureInternal();
+	SupplicantStatus sendNetworkEapSimUmtsAuthResponseInternal(
+	    const ISupplicantStaNetwork::NetworkResponseEapSimUmtsAuthParams&
+		params);
+	SupplicantStatus sendNetworkEapSimUmtsAutsResponseInternal(
+	    const std::array<uint8_t, 14>& auts);
+	SupplicantStatus sendNetworkEapSimUmtsAuthFailureInternal();
+	SupplicantStatus sendNetworkEapIdentityResponseInternal(
+	    const std::vector<uint8_t>& identity);
+
+	struct wpa_ssid* retrieveNetworkPtr();
+	struct wpa_supplicant* retrieveIfacePtr();
+	int isPskPassphraseValid(const std::string& psk);
+	void resetInternalStateAfterParamsUpdate();
+	int setStringFieldAndResetState(
+	    const char* value, uint8_t** to_update_field,
+	    const char* hexdump_prefix);
+	int setStringFieldAndResetState(
+	    const char* value, char** to_update_field,
+	    const char* hexdump_prefix);
+	int setStringKeyFieldAndResetState(
+	    const char* value, char** to_update_field,
+	    const char* hexdump_prefix);
+	int setByteArrayFieldAndResetState(
+	    const uint8_t* value, const size_t value_len,
+	    uint8_t** to_update_field, size_t* to_update_field_len,
+	    const char* hexdump_prefix);
+	int setByteArrayKeyFieldAndResetState(
+	    const uint8_t* value, const size_t value_len,
+	    uint8_t** to_update_field, size_t* to_update_field_len,
+	    const char* hexdump_prefix);
+
+	// Reference to the global wpa_struct. This is assumed to be valid
+	// for the lifetime of the process.
+	struct wpa_global* wpa_global_;
+	// Name of the iface this network belongs to.
+	const std::string ifname_;
+	// Id of the network this hidl object controls.
+	const int network_id_;
+	bool is_valid_;
+
+	DISALLOW_COPY_AND_ASSIGN(StaNetwork);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
+
+#endif  // WPA_SUPPLICANT_HIDL_STA_NETWORK_H
diff --git a/wpa_supplicant/hidl/1.0/supplicant.cpp b/wpa_supplicant/hidl/1.0/supplicant.cpp
new file mode 100644
index 0000000..409547f
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/supplicant.cpp
@@ -0,0 +1,184 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "hidl_manager.h"
+#include "hidl_return_util.h"
+#include "supplicant.h"
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+using hidl_return_util::validateAndCall;
+
+// These are hardcoded for android.
+const char Supplicant::kDriverName[] = "nl80211";
+const char Supplicant::kConfigFilePath[] =
+    "/data/misc/wifi/wpa_supplicant.conf";
+
+Supplicant::Supplicant(struct wpa_global* global) : wpa_global_(global) {}
+bool Supplicant::isValid()
+{
+	// This top level object cannot be invalidated.
+	return true;
+}
+
+Return<void> Supplicant::getInterface(
+    const IfaceInfo& iface_info, getInterface_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &Supplicant::getInterfaceInternal, _hidl_cb, iface_info);
+}
+
+Return<void> Supplicant::listInterfaces(listInterfaces_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &Supplicant::listInterfacesInternal, _hidl_cb);
+}
+
+Return<void> Supplicant::registerCallback(
+    const sp<ISupplicantCallback>& callback, registerCallback_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &Supplicant::registerCallbackInternal, _hidl_cb, callback);
+}
+
+Return<void> Supplicant::setDebugParams(
+    ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys,
+    setDebugParams_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &Supplicant::setDebugParamsInternal, _hidl_cb, level,
+	    show_timestamp, show_keys);
+}
+
+Return<void> Supplicant::setConcurrencyPriority(
+    IfaceType type, setConcurrencyPriority_cb _hidl_cb)
+{
+	return validateAndCall(
+	    this, SupplicantStatusCode::FAILURE_IFACE_INVALID,
+	    &Supplicant::setConcurrencyPriorityInternal, _hidl_cb, type);
+}
+
+Return<ISupplicant::DebugLevel> Supplicant::getDebugLevel()
+{
+	// TODO: Add SupplicantStatus in this method return for uniformity with
+	// the other methods in supplicant HIDL interface.
+	return (ISupplicant::DebugLevel)wpa_debug_level;
+}
+
+Return<bool> Supplicant::isDebugShowTimestampEnabled()
+{
+	// TODO: Add SupplicantStatus in this method return for uniformity with
+	// the other methods in supplicant HIDL interface.
+	return ((wpa_debug_timestamp != 0) ? true : false);
+}
+
+Return<bool> Supplicant::isDebugShowKeysEnabled()
+{
+	// TODO: Add SupplicantStatus in this method return for uniformity with
+	// the other methods in supplicant HIDL interface.
+	return ((wpa_debug_show_keys != 0) ? true : false);
+}
+
+std::pair<SupplicantStatus, sp<ISupplicantIface>>
+Supplicant::getInterfaceInternal(const IfaceInfo& iface_info)
+{
+	struct wpa_supplicant* wpa_s =
+	    wpa_supplicant_get_iface(wpa_global_, iface_info.name.c_str());
+	if (!wpa_s) {
+		return {{SupplicantStatusCode::FAILURE_IFACE_UNKNOWN, ""},
+			nullptr};
+	}
+	HidlManager* hidl_manager = HidlManager::getInstance();
+	if (iface_info.type == IfaceType::P2P) {
+		android::sp<ISupplicantP2pIface> iface;
+		if (!hidl_manager ||
+		    hidl_manager->getP2pIfaceHidlObjectByIfname(
+			wpa_s->ifname, &iface)) {
+			return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
+				iface};
+		}
+		return {{SupplicantStatusCode::SUCCESS, ""}, iface};
+	} else {
+		android::sp<ISupplicantStaIface> iface;
+		if (!hidl_manager ||
+		    hidl_manager->getStaIfaceHidlObjectByIfname(
+			wpa_s->ifname, &iface)) {
+			return {{SupplicantStatusCode::FAILURE_UNKNOWN, ""},
+				iface};
+		}
+		return {{SupplicantStatusCode::SUCCESS, ""}, iface};
+	}
+}
+
+std::pair<SupplicantStatus, std::vector<ISupplicant::IfaceInfo>>
+Supplicant::listInterfacesInternal()
+{
+	std::vector<ISupplicant::IfaceInfo> ifaces;
+	for (struct wpa_supplicant* wpa_s = wpa_global_->ifaces; wpa_s;
+	     wpa_s = wpa_s->next) {
+		if (wpa_s->global->p2p_init_wpa_s == wpa_s) {
+			ifaces.emplace_back(ISupplicant::IfaceInfo{
+			    IfaceType::P2P, wpa_s->ifname});
+		} else {
+			ifaces.emplace_back(ISupplicant::IfaceInfo{
+			    IfaceType::STA, wpa_s->ifname});
+		}
+	}
+	return {{SupplicantStatusCode::SUCCESS, ""}, std::move(ifaces)};
+}
+
+SupplicantStatus Supplicant::registerCallbackInternal(
+    const sp<ISupplicantCallback>& callback)
+{
+	HidlManager* hidl_manager = HidlManager::getInstance();
+	if (!hidl_manager ||
+	    hidl_manager->addSupplicantCallbackHidlObject(callback)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus Supplicant::setDebugParamsInternal(
+    ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys)
+{
+	if (wpa_supplicant_set_debug_params(
+		wpa_global_, static_cast<uint32_t>(level), show_timestamp,
+		show_keys)) {
+		return {SupplicantStatusCode::FAILURE_UNKNOWN, ""};
+	}
+	return {SupplicantStatusCode::SUCCESS, ""};
+}
+
+SupplicantStatus Supplicant::setConcurrencyPriorityInternal(IfaceType type)
+{
+	if (type == IfaceType::STA) {
+		wpa_global_->conc_pref =
+		    wpa_global::wpa_conc_pref::WPA_CONC_PREF_STA;
+	} else if (type == IfaceType::P2P) {
+		wpa_global_->conc_pref =
+		    wpa_global::wpa_conc_pref::WPA_CONC_PREF_P2P;
+	} else {
+		return {SupplicantStatusCode::FAILURE_ARGS_INVALID, ""};
+	}
+	return SupplicantStatus{SupplicantStatusCode::SUCCESS, ""};
+}
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
diff --git a/wpa_supplicant/hidl/1.0/supplicant.h b/wpa_supplicant/hidl/1.0/supplicant.h
new file mode 100644
index 0000000..1ad8402
--- /dev/null
+++ b/wpa_supplicant/hidl/1.0/supplicant.h
@@ -0,0 +1,89 @@
+/*
+ * hidl interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2016, Roshan Pius <rpius@google.com>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_SUPPLICANT_HIDL_SUPPLICANT_H
+#define WPA_SUPPLICANT_HIDL_SUPPLICANT_H
+
+#include <android-base/macros.h>
+
+#include <android/hardware/wifi/supplicant/1.0/ISupplicant.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantCallback.h>
+#include <android/hardware/wifi/supplicant/1.0/ISupplicantIface.h>
+
+extern "C" {
+#include "utils/common.h"
+#include "utils/includes.h"
+#include "utils/wpa_debug.h"
+#include "wpa_supplicant_i.h"
+}
+
+namespace android {
+namespace hardware {
+namespace wifi {
+namespace supplicant {
+namespace V1_0 {
+namespace implementation {
+/**
+ * Implementation of the supplicant hidl object. This hidl
+ * object is used core for global control operations on
+ * wpa_supplicant.
+ */
+class Supplicant : public android::hardware::wifi::supplicant::V1_0::ISupplicant
+{
+public:
+	Supplicant(struct wpa_global* global);
+	~Supplicant() override = default;
+	bool isValid();
+
+	// Hidl methods exposed.
+	Return<void> getInterface(
+	    const IfaceInfo& iface_info, getInterface_cb _hidl_cb) override;
+	Return<void> listInterfaces(listInterfaces_cb _hidl_cb) override;
+	Return<void> registerCallback(
+	    const sp<ISupplicantCallback>& callback,
+	    registerCallback_cb _hidl_cb) override;
+	Return<void> setDebugParams(
+	    ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys,
+	    setDebugParams_cb _hidl_cb) override;
+	Return<ISupplicant::DebugLevel> getDebugLevel() override;
+	Return<bool> isDebugShowTimestampEnabled() override;
+	Return<bool> isDebugShowKeysEnabled() override;
+	Return<void> setConcurrencyPriority(
+	    IfaceType type, setConcurrencyPriority_cb _hidl_cb) override;
+
+private:
+	// Corresponding worker functions for the HIDL methods.
+	std::pair<SupplicantStatus, sp<ISupplicantIface>> getInterfaceInternal(
+	    const IfaceInfo& iface_info);
+	std::pair<SupplicantStatus, std::vector<ISupplicant::IfaceInfo>>
+	listInterfacesInternal();
+	SupplicantStatus registerCallbackInternal(
+	    const sp<ISupplicantCallback>& callback);
+	SupplicantStatus setDebugParamsInternal(
+	    ISupplicant::DebugLevel level, bool show_timestamp, bool show_keys);
+	SupplicantStatus setConcurrencyPriorityInternal(IfaceType type);
+
+	// Raw pointer to the global structure maintained by the core.
+	struct wpa_global* wpa_global_;
+	// Driver name to be used for creating interfaces.
+	static const char kDriverName[];
+	// wpa_supplicant.conf file location on the device.
+	static const char kConfigFilePath[];
+
+	DISALLOW_COPY_AND_ASSIGN(Supplicant);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace wifi
+}  // namespace supplicant
+}  // namespace hardware
+}  // namespace android
+
+#endif  // WPA_SUPPLICANT_HIDL_SUPPLICANT_H
diff --git a/wpa_supplicant/hs20_supplicant.c b/wpa_supplicant/hs20_supplicant.c
index 3128fcb..858636f 100644
--- a/wpa_supplicant/hs20_supplicant.c
+++ b/wpa_supplicant/hs20_supplicant.c
@@ -20,6 +20,7 @@
 #include "driver_i.h"
 #include "config.h"
 #include "scan.h"
+#include "notify.h"
 #include "bss.h"
 #include "blacklist.h"
 #include "gas_query.h"
@@ -61,6 +62,46 @@
 };
 
 
+void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss *bss = wpa_s->current_bss;
+	u8 *bssid = wpa_s->bssid;
+	const u8 *ie;
+	const u8 *ext_capa;
+	u32 filter = 0;
+
+	if (!bss || !is_hs20_network(wpa_s, wpa_s->current_ssid, bss)) {
+		wpa_printf(MSG_DEBUG,
+			   "Not configuring frame filtering - BSS " MACSTR
+			   " is not a Hotspot 2.0 network", MAC2STR(bssid));
+		return;
+	}
+
+	ie = wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE);
+
+	/* Check if DGAF disabled bit is zero (5th byte in the IE) */
+	if (!ie || ie[1] < 5)
+		wpa_printf(MSG_DEBUG,
+			   "Not configuring frame filtering - Can't extract DGAF bit");
+	else if (!(ie[6] & HS20_DGAF_DISABLED))
+		filter |= WPA_DATA_FRAME_FILTER_FLAG_GTK;
+
+	ext_capa = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
+	if (!ext_capa || ext_capa[1] < 2) {
+		wpa_printf(MSG_DEBUG,
+			   "Not configuring frame filtering - Can't extract Proxy ARP bit");
+		return;
+	}
+
+	/* Check if Proxy ARP is enabled (2nd byte in the IE) */
+	if (ext_capa[3] & BIT(4))
+		filter |= WPA_DATA_FRAME_FILTER_FLAG_ARP |
+			WPA_DATA_FRAME_FILTER_FLAG_NA;
+
+	wpa_drv_configure_frame_filters(wpa_s, filter);
+}
+
+
 void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
 {
 	u8 conf;
@@ -165,8 +206,8 @@
 }
 
 
-struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
-				    size_t payload_len)
+static struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+					   size_t payload_len)
 {
 	struct wpabuf *buf;
 
@@ -280,7 +321,7 @@
 		return -1;
 
 	b64 = base64_encode(&icon->image[offset], size, &b64_size);
-	if (buf_len >= b64_size) {
+	if (b64 && buf_len >= b64_size) {
 		os_memcpy(reply, b64, b64_size);
 		reply_size = b64_size;
 	} else {
@@ -396,14 +437,18 @@
 			icon->image_len = slen;
 			hs20_remove_duplicate_icons(wpa_s, icon);
 			wpa_msg(wpa_s, MSG_INFO,
-				"RX-HS20-ICON " MACSTR " %s %u",
+				RX_HS20_ICON MACSTR " %s %u",
 				MAC2STR(sa), icon->file_name,
 				(unsigned int) icon->image_len);
+			wpas_notify_hs20_icon_query_done(wpa_s, sa,
+							 icon->file_name,
+							 icon->image,
+							 icon->image_len);
 			return 0;
 		}
 	}
 
-	wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
+	wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR " Icon Binary File",
 		MAC2STR(sa));
 
 	if (slen < 4) {
@@ -466,7 +511,7 @@
 	}
 	fclose(f);
 
-	wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+	wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP_ICON "%s", fname);
 	return 0;
 }
 
@@ -530,7 +575,7 @@
 
 	switch (subtype) {
 	case HS20_STYPE_CAPABILITY_LIST:
-		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
 			" HS Capability List", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
 		if (anqp) {
@@ -540,7 +585,7 @@
 		}
 		break;
 	case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
-		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
 			" Operator Friendly Name", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "oper friendly name", pos, slen);
 		if (anqp) {
@@ -556,7 +601,7 @@
 				"Metrics value from " MACSTR, MAC2STR(sa));
 			break;
 		}
-		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
 			" WAN Metrics %02x:%u:%u:%u:%u:%u", MAC2STR(sa),
 			pos[0], WPA_GET_LE32(pos + 1), WPA_GET_LE32(pos + 5),
 			pos[9], pos[10], WPA_GET_LE16(pos + 11));
@@ -566,7 +611,7 @@
 		}
 		break;
 	case HS20_STYPE_CONNECTION_CAPABILITY:
-		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
 			" Connection Capability", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "conn capability", pos, slen);
 		if (anqp) {
@@ -576,7 +621,7 @@
 		}
 		break;
 	case HS20_STYPE_OPERATING_CLASS:
-		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
 			" Operating Class", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "Operating Class", pos, slen);
 		if (anqp) {
@@ -586,7 +631,7 @@
 		}
 		break;
 	case HS20_STYPE_OSU_PROVIDERS_LIST:
-		wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_HS20_ANQP MACSTR
 			" OSU Providers list", MAC2STR(sa));
 		wpa_s->num_prov_found++;
 		if (anqp) {
@@ -663,6 +708,8 @@
 		 wpa_s->conf->osu_dir);
 	f = fopen(fname, "w");
 	if (f == NULL) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"Could not write OSU provider information");
 		hs20_free_osu_prov(wpa_s);
 		wpa_s->fetch_anqp_in_progress = 0;
 		return;
@@ -1036,7 +1083,7 @@
 }
 
 
-int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan)
 {
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
@@ -1067,7 +1114,16 @@
 	wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
 	wpa_s->num_osu_scans = 0;
 	wpa_s->num_prov_found = 0;
-	hs20_start_osu_scan(wpa_s);
+	if (skip_scan) {
+		wpa_s->network_select = 0;
+		wpa_s->fetch_all_anqp = 1;
+		wpa_s->fetch_osu_info = 1;
+		wpa_s->fetch_osu_icon_in_progress = 0;
+
+		interworking_start_fetch_anqp(wpa_s);
+	} else {
+		hs20_start_osu_scan(wpa_s);
+	}
 
 	return 0;
 }
@@ -1110,6 +1166,7 @@
 			osu_method, url);
 	else
 		wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
+	wpas_notify_hs20_rx_subscription_remediation(wpa_s, url, osu_method);
 }
 
 
@@ -1123,6 +1180,7 @@
 
 	wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s",
 		code, reauth_delay, url);
+	wpas_notify_hs20_rx_deauth_imminent_notice(wpa_s, code, reauth_delay, url);
 
 	if (code == HS20_DEAUTH_REASON_CODE_BSS) {
 		wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to blacklist");
diff --git a/wpa_supplicant/hs20_supplicant.h b/wpa_supplicant/hs20_supplicant.h
index 9fc654c..0dd559f 100644
--- a/wpa_supplicant/hs20_supplicant.h
+++ b/wpa_supplicant/hs20_supplicant.h
@@ -8,12 +8,11 @@
 #ifndef HS20_SUPPLICANT_H
 #define HS20_SUPPLICANT_H
 
+void hs20_configure_frame_filters(struct wpa_supplicant *wpa_s);
 void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id);
 
 int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
 		       const u8 *payload, size_t payload_len, int inmem);
-struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
-				    size_t payload_len);
 void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
 		       struct wpabuf *buf);
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
@@ -32,7 +31,7 @@
 void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
 void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
 void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
-int hs20_fetch_osu(struct wpa_supplicant *wpa_s);
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s, int skip_scan);
 void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
 void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
 void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index c00db31..954061a 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -221,6 +221,7 @@
 	peer->supp = wpa_sm_init(ctx);
 	if (peer->supp == NULL) {
 		wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
+		os_free(ctx);
 		return -1;
 	}
 
@@ -404,10 +405,18 @@
 
 
 static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
-				    const u8 *own_addr)
+				    const u8 *own_addr, struct wpa_ssid *ssid)
 {
 	struct wpa_auth_config conf;
-	struct wpa_auth_callbacks cb;
+	static const struct wpa_auth_callbacks cb = {
+		.logger = auth_logger,
+		.set_eapol = auth_set_eapol,
+		.send_eapol = auth_send_eapol,
+		.get_psk = auth_get_psk,
+		.set_key = auth_set_key,
+		.for_each_sta = auth_for_each_sta,
+		.disconnect = ibss_rsn_disconnect,
+	};
 
 	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
 
@@ -418,19 +427,11 @@
 	conf.rsn_pairwise = WPA_CIPHER_CCMP;
 	conf.wpa_group = WPA_CIPHER_CCMP;
 	conf.eapol_version = 2;
-	conf.wpa_group_rekey = 600;
+	conf.wpa_group_rekey = ssid->group_rekey ? ssid->group_rekey : 600;
+	conf.wpa_group_update_count = 4;
+	conf.wpa_pairwise_update_count = 4;
 
-	os_memset(&cb, 0, sizeof(cb));
-	cb.ctx = ibss_rsn;
-	cb.logger = auth_logger;
-	cb.set_eapol = auth_set_eapol;
-	cb.send_eapol = auth_send_eapol;
-	cb.get_psk = auth_get_psk;
-	cb.set_key = auth_set_key;
-	cb.for_each_sta = auth_for_each_sta;
-	cb.disconnect = ibss_rsn_disconnect;
-
-	ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
+	ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb, ibss_rsn);
 	if (ibss_rsn->auth_group == NULL) {
 		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
 		return -1;
@@ -665,7 +666,8 @@
 }
 
 
-struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s)
+struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s,
+				struct wpa_ssid *ssid)
 {
 	struct ibss_rsn *ibss_rsn;
 
@@ -674,7 +676,7 @@
 		return NULL;
 	ibss_rsn->wpa_s = wpa_s;
 
-	if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) {
+	if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr, ssid) < 0) {
 		ibss_rsn_deinit(ibss_rsn);
 		return NULL;
 	}
@@ -836,6 +838,18 @@
 		   MAC2STR(addr));
 
 	if (peer &&
+	    peer->authentication_status & (IBSS_RSN_SET_PTK_SUPP |
+					   IBSS_RSN_SET_PTK_AUTH)) {
+		/* Clear the TK for this pair to allow recovery from the case
+		 * where the peer STA has restarted and lost its key while we
+		 * still have a pairwise key configured. */
+		wpa_printf(MSG_DEBUG, "RSN: Clear pairwise key for peer "
+			   MACSTR, MAC2STR(addr));
+		wpa_drv_set_key(ibss_rsn->wpa_s, WPA_ALG_NONE, addr, 0, 0,
+				NULL, 0, NULL, 0);
+	}
+
+	if (peer &&
 	    peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
 		if (peer->own_auth_tx.sec) {
 			struct os_reltime now, diff;
diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h
index 67fae2d..626c543 100644
--- a/wpa_supplicant/ibss_rsn.h
+++ b/wpa_supplicant/ibss_rsn.h
@@ -51,7 +51,8 @@
 };
 
 
-struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s);
+struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s,
+				struct wpa_ssid *ssid);
 void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn);
 int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
 void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
diff --git a/wpa_supplicant/interworking.c b/wpa_supplicant/interworking.c
index 589ee57..fd2b02e 100644
--- a/wpa_supplicant/interworking.c
+++ b/wpa_supplicant/interworking.c
@@ -950,11 +950,9 @@
 	if (!key_mgmt)
 		key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
 			"WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
-	if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
-		return -1;
-	if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
-		return -1;
-	if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
+	if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0 ||
+	    wpa_config_set(ssid, "proto", "RSN", 0) < 0 ||
+	    wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
 		return -1;
 	return 0;
 }
@@ -1590,9 +1588,8 @@
 }
 
 
-static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
-				       struct wpa_bss *bss, int allow_excluded,
-				       int only_add)
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+			 int only_add)
 {
 	struct wpa_cred *cred, *cred_rc, *cred_3gpp;
 	struct wpa_ssid *ssid;
@@ -1600,7 +1597,7 @@
 	struct nai_realm_eap *eap = NULL;
 	u16 count, i;
 	char buf[100];
-	int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
+	int excluded = 0, *excl = &excluded;
 	const char *name;
 
 	if (wpa_s->conf->cred == NULL || bss == NULL)
@@ -1614,8 +1611,8 @@
 	}
 
 	wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
-		   " for connection (allow_excluded=%d)",
-		   MAC2STR(bss->bssid), allow_excluded);
+		   " for connection",
+		   MAC2STR(bss->bssid));
 
 	if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
 		/*
@@ -1633,7 +1630,7 @@
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: Highest roaming consortium matching credential priority %d sp_priority %d",
 			cred_rc->priority, cred_rc->sp_priority);
-		if (allow_excluded && excl && !(*excl))
+		if (excl && !(*excl))
 			excl = NULL;
 	}
 
@@ -1642,7 +1639,7 @@
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d",
 			cred->priority, cred->sp_priority);
-		if (allow_excluded && excl && !(*excl))
+		if (excl && !(*excl))
 			excl = NULL;
 	}
 
@@ -1652,7 +1649,7 @@
 		wpa_msg(wpa_s, MSG_DEBUG,
 			"Interworking: Highest 3GPP matching credential priority %d sp_priority %d",
 			cred_3gpp->priority, cred_3gpp->sp_priority);
-		if (allow_excluded && excl && !(*excl))
+		if (excl && !(*excl))
 			excl = NULL;
 	}
 
@@ -1665,7 +1662,7 @@
 			wpa_msg(wpa_s, MSG_DEBUG,
 				"Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)",
 				cred_rc->priority, cred_rc->sp_priority);
-			if (allow_excluded && excl && !(*excl))
+			if (excl && !(*excl))
 				excl = NULL;
 		}
 
@@ -1675,7 +1672,7 @@
 			wpa_msg(wpa_s, MSG_DEBUG,
 				"Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)",
 				cred->priority, cred->sp_priority);
-			if (allow_excluded && excl && !(*excl))
+			if (excl && !(*excl))
 				excl = NULL;
 		}
 
@@ -1685,7 +1682,7 @@
 			wpa_msg(wpa_s, MSG_DEBUG,
 				"Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)",
 				cred_3gpp->priority, cred_3gpp->sp_priority);
-			if (allow_excluded && excl && !(*excl))
+			if (excl && !(*excl))
 				excl = NULL;
 		}
 	}
@@ -1850,13 +1847,6 @@
 }
 
 
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
-			 int only_add)
-{
-	return interworking_connect_helper(wpa_s, bss, 1, only_add);
-}
-
-
 #ifdef PCSC_FUNCS
 static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s)
 {
@@ -2540,7 +2530,8 @@
 		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
 			MAC2STR(selected->bssid));
 		interworking_connect(wpa_s, selected, 0);
-	}
+	} else if (wpa_s->wpa_state == WPA_SCANNING)
+		wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 }
 
 
@@ -2702,10 +2693,11 @@
 
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-		  u16 info_ids[], size_t num_ids, u32 subtypes)
+		  u16 info_ids[], size_t num_ids, u32 subtypes,
+		  int get_cell_pref)
 {
 	struct wpabuf *buf;
-	struct wpabuf *hs20_buf = NULL;
+	struct wpabuf *extra_buf = NULL;
 	int ret = 0;
 	int freq;
 	struct wpa_bss *bss;
@@ -2728,15 +2720,31 @@
 
 #ifdef CONFIG_HS20
 	if (subtypes != 0) {
-		hs20_buf = wpabuf_alloc(100);
-		if (hs20_buf == NULL)
+		extra_buf = wpabuf_alloc(100);
+		if (extra_buf == NULL)
 			return -1;
-		hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
+		hs20_put_anqp_req(subtypes, NULL, 0, extra_buf);
 	}
 #endif /* CONFIG_HS20 */
 
-	buf = anqp_build_req(info_ids, num_ids, hs20_buf);
-	wpabuf_free(hs20_buf);
+#ifdef CONFIG_MBO
+	if (get_cell_pref) {
+		struct wpabuf *mbo;
+
+		mbo = mbo_build_anqp_buf(wpa_s, bss);
+		if (mbo) {
+			if (wpabuf_resize(&extra_buf, wpabuf_len(mbo))) {
+				wpabuf_free(extra_buf);
+				return -1;
+			}
+			wpabuf_put_buf(extra_buf, mbo);
+			wpabuf_free(mbo);
+		}
+	}
+#endif /* CONFIG_MBO */
+
+	buf = anqp_build_req(info_ids, num_ids, extra_buf);
+	wpabuf_free(extra_buf);
 	if (buf == NULL)
 		return -1;
 
@@ -2806,7 +2814,7 @@
 
 	switch (info_id) {
 	case ANQP_CAPABILITY_LIST:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" ANQP Capability list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list",
 				  pos, slen);
@@ -2816,7 +2824,7 @@
 		}
 		break;
 	case ANQP_VENUE_NAME:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" Venue Name", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
 		if (anqp) {
@@ -2825,7 +2833,7 @@
 		}
 		break;
 	case ANQP_NETWORK_AUTH_TYPE:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" Network Authentication Type information",
 			MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
@@ -2836,7 +2844,7 @@
 		}
 		break;
 	case ANQP_ROAMING_CONSORTIUM:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" Roaming Consortium list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
 				  pos, slen);
@@ -2846,7 +2854,7 @@
 		}
 		break;
 	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" IP Address Type Availability information",
 			MAC2STR(sa));
 		wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
@@ -2858,7 +2866,7 @@
 		}
 		break;
 	case ANQP_NAI_REALM:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" NAI Realm list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
 		if (anqp) {
@@ -2867,7 +2875,7 @@
 		}
 		break;
 	case ANQP_3GPP_CELLULAR_NETWORK:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" 3GPP Cellular Network information", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
 				  pos, slen);
@@ -2877,7 +2885,7 @@
 		}
 		break;
 	case ANQP_DOMAIN_NAME:
-		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
 			" Domain Name list", MAC2STR(sa));
 		wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
 		if (anqp) {
@@ -2885,6 +2893,18 @@
 			anqp->domain_name = wpabuf_alloc_copy(pos, slen);
 		}
 		break;
+#ifdef CONFIG_FILS
+	case ANQP_FILS_REALM_INFO:
+		wpa_msg(wpa_s, MSG_INFO, RX_ANQP MACSTR
+			" FILS Realm Information", MAC2STR(sa));
+		wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: FILS Realm Information",
+			pos, slen);
+		if (anqp) {
+			wpabuf_free(anqp->fils_realm_info);
+			anqp->fils_realm_info = wpabuf_alloc_copy(pos, slen);
+		}
+		break;
+#endif /* CONFIG_FILS */
 	case ANQP_VENDOR_SPECIFIC:
 		if (slen < 3)
 			return;
@@ -3018,6 +3038,7 @@
 out:
 	wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
 		MAC2STR(dst), anqp_result);
+	wpas_notify_anqp_query_done(wpa_s, dst, anqp_result, bss ? bss->anqp : NULL);
 }
 
 
diff --git a/wpa_supplicant/interworking.h b/wpa_supplicant/interworking.h
index 3743dc0..3d22292 100644
--- a/wpa_supplicant/interworking.h
+++ b/wpa_supplicant/interworking.h
@@ -12,7 +12,8 @@
 enum gas_query_result;
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-		  u16 info_ids[], size_t num_ids, u32 subtypes);
+		  u16 info_ids[], size_t num_ids, u32 subtypes,
+		  int get_cell_pref);
 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
 		  enum gas_query_result result,
 		  const struct wpabuf *adv_proto,
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
index 3292e67..d20ae5e 100644
--- a/wpa_supplicant/mbo.c
+++ b/wpa_supplicant/mbo.c
@@ -14,10 +14,12 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/gas.h"
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "bss.h"
+#include "scan.h"
 
 /* type + length + oui + oui type */
 #define MBO_IE_HEADER 6
@@ -67,14 +69,13 @@
 
 	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].preference);
 	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason);
-	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].reason_detail);
 }
 
 
 static void wpas_mbo_non_pref_chan_attr(struct wpa_supplicant *wpa_s,
 					struct wpabuf *mbo, u8 start, u8 end)
 {
-	size_t size = end - start + 4;
+	size_t size = end - start + 3;
 
 	if (size + 2 > wpabuf_tailroom(mbo))
 		return;
@@ -99,7 +100,7 @@
 					      struct wpabuf *mbo, u8 start,
 					      u8 end)
 {
-	size_t size = end - start + 8;
+	size_t size = end - start + 7;
 
 	if (size + 2 > wpabuf_tailroom(mbo))
 		return;
@@ -130,7 +131,6 @@
 		if (!non_pref ||
 		    non_pref->oper_class != start_pref->oper_class ||
 		    non_pref->reason != start_pref->reason ||
-		    non_pref->reason_detail != start_pref->reason_detail ||
 		    non_pref->preference != start_pref->preference) {
 			if (subelement)
 				wpas_mbo_non_pref_chan_subelement(wpa_s, mbo,
@@ -249,9 +249,9 @@
  *
  * In MBO IE non-preferred channel subelement we can put many channels in an
  * attribute if they are in the same operating class and have the same
- * preference, reason, and reason detail. To make it easy for the functions that
- * build the IE attributes and WNM Request subelements, save the channels sorted
- * by their oper_class, reason, and reason_detail.
+ * preference and reason. To make it easy for the functions that build
+ * the IE attributes and WNM Request subelements, save the channels sorted
+ * by their oper_class and reason.
  */
 static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
 {
@@ -261,8 +261,6 @@
 		return a->oper_class - b->oper_class;
 	if (a->reason != b->reason)
 		return a->reason - b->reason;
-	if (a->reason_detail != b->reason_detail)
-		return a->reason_detail - b->reason_detail;
 	return a->preference - b->preference;
 }
 
@@ -297,7 +295,6 @@
 		unsigned int _chan;
 		unsigned int _preference;
 		unsigned int _reason;
-		unsigned int _reason_detail;
 
 		if (num == size) {
 			size = size ? size * 2 : 1;
@@ -313,13 +310,11 @@
 
 		chan = &chans[num];
 
-		ret = sscanf(token, "%u:%u:%u:%u:%u", &_oper_class,
-			     &_chan, &_preference, &_reason,
-			     &_reason_detail);
-		if ((ret != 4 && ret != 5) ||
+		ret = sscanf(token, "%u:%u:%u:%u", &_oper_class,
+			     &_chan, &_preference, &_reason);
+		if (ret != 4 ||
 		    _oper_class > 255 || _chan > 255 ||
-		    _preference > 255 || _reason > 65535 ||
-		    (ret == 5 && _reason_detail > 255)) {
+		    _preference > 255 || _reason > 65535 ) {
 			wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
 				   token);
 			goto fail;
@@ -328,7 +323,6 @@
 		chan->chan = _chan;
 		chan->preference = _preference;
 		chan->reason = _reason;
-		chan->reason_detail = ret == 4 ? 0 : _reason_detail;
 
 		if (wpas_mbo_validate_non_pref_chan(chan->oper_class,
 						    chan->chan, chan->reason)) {
@@ -386,258 +380,6 @@
 }
 
 
-enum chan_allowed {
-	NOT_ALLOWED, ALLOWED
-};
-
-static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan,
-				       unsigned int *flags)
-{
-	int i;
-
-	for (i = 0; i < mode->num_channels; i++) {
-		if (mode->channels[i].chan == chan)
-			break;
-	}
-
-	if (i == mode->num_channels ||
-	    (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
-		return NOT_ALLOWED;
-
-	if (flags)
-		*flags = mode->channels[i].flag;
-
-	return ALLOWED;
-}
-
-
-static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel)
-{
-	u8 center_channels[] = {42, 58, 106, 122, 138, 155};
-	size_t i;
-
-	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
-		return 0;
-
-	for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
-		/*
-		 * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
-		 * so the center channel is 6 channels away from the start/end.
-		 */
-		if (channel >= center_channels[i] - 6 &&
-		    channel <= center_channels[i] + 6)
-			return center_channels[i];
-	}
-
-	return 0;
-}
-
-
-static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel)
-{
-	u8 center_chan;
-	unsigned int i;
-
-	center_chan = get_center_80mhz(mode, channel);
-	if (!center_chan)
-		return NOT_ALLOWED;
-
-	/* check all the channels are available */
-	for (i = 0; i < 4; i++) {
-		unsigned int flags;
-		u8 adj_chan = center_chan - 6 + i * 4;
-
-		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
-			return NOT_ALLOWED;
-
-		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
-		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
-		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
-		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
-			return NOT_ALLOWED;
-	}
-
-	return ALLOWED;
-}
-
-
-static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel)
-{
-	u8 center_channels[] = { 50, 114 };
-	unsigned int i;
-
-	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
-		return 0;
-
-	for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
-		/*
-		 * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
-		 * so the center channel is 14 channels away from the start/end.
-		 */
-		if (channel >= center_channels[i] - 14 &&
-		    channel <= center_channels[i] + 14)
-			return center_channels[i];
-	}
-
-	return 0;
-}
-
-
-static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
-				       u8 channel)
-{
-	u8 center_chan;
-	unsigned int i;
-
-	center_chan = get_center_160mhz(mode, channel);
-	if (!center_chan)
-		return NOT_ALLOWED;
-
-	/* Check all the channels are available */
-	for (i = 0; i < 8; i++) {
-		unsigned int flags;
-		u8 adj_chan = center_chan - 14 + i * 4;
-
-		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
-			return NOT_ALLOWED;
-
-		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
-		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
-		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
-		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
-		    (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
-		    (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
-		    (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
-		    (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
-			return NOT_ALLOWED;
-	}
-
-	return ALLOWED;
-}
-
-
-enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
-				 u8 bw)
-{
-	unsigned int flag = 0;
-	enum chan_allowed res, res2;
-
-	res2 = res = allow_channel(mode, channel, &flag);
-	if (bw == BW40MINUS) {
-		if (!(flag & HOSTAPD_CHAN_HT40MINUS))
-			return NOT_ALLOWED;
-		res2 = allow_channel(mode, channel - 4, NULL);
-	} else if (bw == BW40PLUS) {
-		if (!(flag & HOSTAPD_CHAN_HT40PLUS))
-			return NOT_ALLOWED;
-		res2 = allow_channel(mode, channel + 4, NULL);
-	} else if (bw == BW80) {
-		res2 = verify_80mhz(mode, channel);
-	} else if (bw == BW160) {
-		res2 = verify_160mhz(mode, channel);
-	}
-
-	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
-		return NOT_ALLOWED;
-
-	return ALLOWED;
-}
-
-
-static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
-				   const struct oper_class_map *op_class)
-{
-	int chan;
-	size_t i;
-	struct hostapd_hw_modes *mode;
-
-	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode);
-	if (!mode)
-		return 0;
-
-	if (op_class->op_class == 128 || op_class->op_class == 130) {
-		u8 channels[] = { 42, 58, 106, 122, 138, 155 };
-
-		for (i = 0; i < ARRAY_SIZE(channels); i++) {
-			if (verify_channel(mode, channels[i], op_class->bw) ==
-			    NOT_ALLOWED)
-				return 0;
-		}
-
-		return 1;
-	}
-
-	if (op_class->op_class == 129) {
-		if (verify_channel(mode, 50, op_class->bw) == NOT_ALLOWED ||
-		    verify_channel(mode, 114, op_class->bw) == NOT_ALLOWED)
-			return 0;
-
-		return 1;
-	}
-
-	for (chan = op_class->min_chan; chan <= op_class->max_chan;
-	     chan += op_class->inc) {
-		if (verify_channel(mode, chan, op_class->bw) == NOT_ALLOWED)
-			return 0;
-	}
-
-	return 1;
-}
-
-
-int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
-			      size_t len)
-{
-	struct wpabuf *buf;
-	u8 op, current, chan;
-	u8 *ie_len;
-	int res;
-
-	/*
-	 * Assume 20 MHz channel for now.
-	 * TODO: Use the secondary channel and VHT channel width that will be
-	 * used after association.
-	 */
-	if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
-					  &current, &chan) == NUM_HOSTAPD_MODES)
-		return 0;
-
-	/*
-	 * Need 3 bytes for EID, length, and current operating class, plus
-	 * 1 byte for every other supported operating class.
-	 */
-	buf = wpabuf_alloc(global_op_class_size + 3);
-	if (!buf)
-		return 0;
-
-	wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
-	/* Will set the length later, putting a placeholder */
-	ie_len = wpabuf_put(buf, 1);
-	wpabuf_put_u8(buf, current);
-
-	for (op = 0; global_op_class[op].op_class; op++) {
-		if (wpas_op_class_supported(wpa_s, &global_op_class[op]))
-			wpabuf_put_u8(buf, global_op_class[op].op_class);
-	}
-
-	*ie_len = wpabuf_len(buf) - 2;
-	if (*ie_len < 2 || wpabuf_len(buf) > len) {
-		wpa_printf(MSG_ERROR,
-			   "Failed to add supported operating classes IE");
-		res = 0;
-	} else {
-		os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
-		res = wpabuf_len(buf);
-		wpa_hexdump_buf(MSG_DEBUG,
-				"MBO: Added supported operating classes IE",
-				buf);
-	}
-
-	wpabuf_free(buf);
-	return res;
-}
-
-
 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *mbo_ie,
 			   size_t len)
 {
@@ -768,4 +510,33 @@
 	cell_capa[6] = mbo_cell_capa;
 
 	wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
+	wpa_supplicant_set_default_scan_ies(wpa_s);
+}
+
+
+struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss)
+{
+	struct wpabuf *anqp_buf;
+	u8 *len_pos;
+
+	if (!wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
+		wpa_printf(MSG_INFO, "MBO: " MACSTR
+			   " does not support MBO - cannot request MBO ANQP elements from it",
+			   MAC2STR(bss->bssid));
+		return NULL;
+	}
+
+	anqp_buf = wpabuf_alloc(10);
+	if (!anqp_buf)
+		return NULL;
+
+	len_pos = gas_anqp_add_element(anqp_buf, ANQP_VENDOR_SPECIFIC);
+	wpabuf_put_be24(anqp_buf, OUI_WFA);
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_OUI_TYPE);
+
+	wpabuf_put_u8(anqp_buf, MBO_ANQP_SUBTYPE_CELL_CONN_PREF);
+	gas_anqp_set_element_len(anqp_buf, len_pos);
+
+	return anqp_buf;
 }
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
index 89b033b..2ca81a3 100644
--- a/wpa_supplicant/mesh.c
+++ b/wpa_supplicant/mesh.c
@@ -66,9 +66,11 @@
 }
 
 
-static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid)
+static struct mesh_conf * mesh_config_create(struct wpa_supplicant *wpa_s,
+					     struct wpa_ssid *ssid)
 {
 	struct mesh_conf *conf;
+	int cipher;
 
 	conf = os_zalloc(sizeof(struct mesh_conf));
 	if (!conf)
@@ -82,6 +84,33 @@
 			MESH_CONF_SEC_AMPE;
 	else
 		conf->security |= MESH_CONF_SEC_NONE;
+	conf->ieee80211w = ssid->ieee80211w;
+	if (conf->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
+		if (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)
+			conf->ieee80211w = wpa_s->conf->pmf;
+		else
+			conf->ieee80211w = NO_MGMT_FRAME_PROTECTION;
+	}
+
+	cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher, 0);
+	if (cipher < 0 || cipher == WPA_CIPHER_TKIP) {
+		wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid pairwise cipher");
+		os_free(conf);
+		return NULL;
+	}
+	conf->pairwise_cipher = cipher;
+
+	cipher = wpa_pick_group_cipher(ssid->group_cipher);
+	if (cipher < 0 || cipher == WPA_CIPHER_TKIP ||
+	    cipher == WPA_CIPHER_GTK_NOT_USED) {
+		wpa_msg(wpa_s, MSG_INFO, "mesh: Invalid group cipher");
+		os_free(conf);
+		return NULL;
+	}
+
+	conf->group_cipher = cipher;
+	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION)
+		conf->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
 
 	/* defaults */
 	conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP;
@@ -117,7 +146,8 @@
 
 
 static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
-				    struct wpa_ssid *ssid)
+				    struct wpa_ssid *ssid,
+				    struct hostapd_freq_params *freq)
 {
 	struct hostapd_iface *ifmsh;
 	struct hostapd_data *bss;
@@ -127,6 +157,7 @@
 	static int default_groups[] = { 19, 20, 21, 25, 26, -1 };
 	size_t len;
 	int rate_len;
+	int frequency;
 
 	if (!wpa_s->conf->user_mpm) {
 		/* not much for us to do here */
@@ -146,7 +177,7 @@
 	if (!ifmsh->bss)
 		goto out_free;
 
-	ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data));
+	ifmsh->bss[0] = bss = hostapd_alloc_bss_data(NULL, NULL, NULL);
 	if (!bss)
 		goto out_free;
 
@@ -155,7 +186,13 @@
 	bss->drv_priv = wpa_s->drv_priv;
 	bss->iface = ifmsh;
 	bss->mesh_sta_free_cb = mesh_mpm_free_sta;
-	wpa_s->assoc_freq = ssid->frequency;
+	frequency = ssid->frequency;
+	if (frequency != freq->freq &&
+	    frequency == freq->freq + freq->sec_channel_offset * 20) {
+		wpa_printf(MSG_DEBUG, "mesh: pri/sec channels switched");
+		frequency = freq->freq;
+	}
+	wpa_s->assoc_freq = frequency;
 	wpa_s->current_ssid = ssid;
 
 	/* setup an AP config for auth processing */
@@ -175,22 +212,16 @@
 		wpa_s->conf->dot11RSNASAERetransPeriod;
 	os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
 
-	mconf = mesh_config_create(ssid);
+	mconf = mesh_config_create(wpa_s, ssid);
 	if (!mconf)
 		goto out_free;
 	ifmsh->mconf = mconf;
 
 	/* need conf->hw_mode for supported rates. */
-	if (ssid->frequency == 0) {
-		conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
-		conf->channel = 1;
-	} else {
-		conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
-						       &conf->channel);
-	}
+	conf->hw_mode = ieee80211_freq_to_chan(frequency, &conf->channel);
 	if (conf->hw_mode == NUM_HOSTAPD_MODES) {
 		wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz",
-			   ssid->frequency);
+			   frequency);
 		goto out_free;
 	}
 	if (ssid->ht40)
@@ -201,13 +232,13 @@
 		case VHT_CHANWIDTH_80MHZ:
 		case VHT_CHANWIDTH_80P80MHZ:
 			ieee80211_freq_to_chan(
-				ssid->frequency,
+				frequency,
 				&conf->vht_oper_centr_freq_seg0_idx);
 			conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
 			break;
 		case VHT_CHANWIDTH_160MHZ:
 			ieee80211_freq_to_chan(
-				ssid->frequency,
+				frequency,
 				&conf->vht_oper_centr_freq_seg0_idx);
 			conf->vht_oper_centr_freq_seg0_idx += ssid->ht40 * 2;
 			conf->vht_oper_centr_freq_seg0_idx += 40 / 5;
@@ -341,15 +372,9 @@
 
 	wpa_supplicant_mesh_deinit(wpa_s);
 
-	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
-		wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
-		wpa_s->group_cipher = WPA_CIPHER_CCMP;
-		wpa_s->mgmt_group_cipher = 0;
-	} else {
-		wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
-		wpa_s->group_cipher = WPA_CIPHER_NONE;
-		wpa_s->mgmt_group_cipher = 0;
-	}
+	wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+	wpa_s->group_cipher = WPA_CIPHER_NONE;
+	wpa_s->mgmt_group_cipher = 0;
 
 	os_memset(&params, 0, sizeof(params));
 	params.meshid = ssid->ssid;
@@ -383,7 +408,11 @@
 		params.beacon_int = ssid->beacon_int;
 	else if (wpa_s->conf->beacon_int > 0)
 		params.beacon_int = wpa_s->conf->beacon_int;
-	params.max_peer_links = wpa_s->conf->max_peer_links;
+	if (ssid->dtim_period > 0)
+		params.dtim_period = ssid->dtim_period;
+	else if (wpa_s->conf->dtim_period > 0)
+		params.dtim_period = wpa_s->conf->dtim_period;
+	params.conf.max_peer_links = wpa_s->conf->max_peer_links;
 
 	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
 		params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
@@ -393,35 +422,46 @@
 
 	if (wpa_s->conf->user_mpm) {
 		params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM;
-		params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+		params.conf.auto_plinks = 0;
 	} else {
 		params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM;
-		params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+		params.conf.auto_plinks = 1;
 	}
 	params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity;
 
-	if (wpa_supplicant_mesh_init(wpa_s, ssid)) {
+	if (wpa_supplicant_mesh_init(wpa_s, ssid, &params.freq)) {
 		wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh");
 		wpa_drv_leave_mesh(wpa_s);
 		ret = -1;
 		goto out;
 	}
 
+	if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+		wpa_s->pairwise_cipher = wpa_s->mesh_rsn->pairwise_cipher;
+		wpa_s->group_cipher = wpa_s->mesh_rsn->group_cipher;
+		wpa_s->mgmt_group_cipher = wpa_s->mesh_rsn->mgmt_group_cipher;
+	}
+
 	if (wpa_s->ifmsh) {
 		params.ies = wpa_s->ifmsh->mconf->rsn_ie;
 		params.ie_len = wpa_s->ifmsh->mconf->rsn_ie_len;
 		params.basic_rates = wpa_s->ifmsh->basic_rates;
+		params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_HT_OP_MODE;
+		params.conf.ht_opmode = wpa_s->ifmsh->bss[0]->iface->ht_op_mode;
 	}
 
 	wpa_msg(wpa_s, MSG_INFO, "joining mesh %s",
 		wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
 	ret = wpa_drv_join_mesh(wpa_s, &params);
 	if (ret)
-		wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret);
+		wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d", ret);
 
 	/* hostapd sets the interface down until we associate */
 	wpa_drv_set_operstate(wpa_s, 1);
 
+	if (!ret)
+		wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
 out:
 	return ret;
 }
@@ -591,7 +631,7 @@
 	if (!mesh_wpa_s) {
 		wpa_printf(MSG_ERROR,
 			   "mesh: Failed to create new wpa_supplicant interface");
-		wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+		wpa_drv_if_remove(wpa_s, WPA_IF_MESH, ifname);
 		return -1;
 	}
 	mesh_wpa_s->mesh_if_created = 1;
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
index c014eaf..f152044 100644
--- a/wpa_supplicant/mesh_mpm.c
+++ b/wpa_supplicant/mesh_mpm.c
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
 #include "ap/ieee802_11.h"
@@ -35,19 +36,17 @@
 	PLINK_UNDEFINED,
 	OPN_ACPT,
 	OPN_RJCT,
-	OPN_IGNR,
 	CNF_ACPT,
 	CNF_RJCT,
-	CNF_IGNR,
 	CLS_ACPT,
-	CLS_IGNR
+	REQ_RJCT
 };
 
 static const char * const mplstate[] = {
 	[0] = "UNINITIALIZED",
-	[PLINK_LISTEN] = "LISTEN",
-	[PLINK_OPEN_SENT] = "OPEN_SENT",
-	[PLINK_OPEN_RCVD] = "OPEN_RCVD",
+	[PLINK_IDLE] = "IDLE",
+	[PLINK_OPN_SNT] = "OPN_SNT",
+	[PLINK_OPN_RCVD] = "OPN_RCVD",
 	[PLINK_CNF_RCVD] = "CNF_RCVD",
 	[PLINK_ESTAB] = "ESTAB",
 	[PLINK_HOLDING] = "HOLDING",
@@ -58,12 +57,10 @@
 	[PLINK_UNDEFINED] = "UNDEFINED",
 	[OPN_ACPT] = "OPN_ACPT",
 	[OPN_RJCT] = "OPN_RJCT",
-	[OPN_IGNR] = "OPN_IGNR",
 	[CNF_ACPT] = "CNF_ACPT",
 	[CNF_RJCT] = "CNF_RJCT",
-	[CNF_IGNR] = "CNF_IGNR",
 	[CLS_ACPT] = "CLS_ACPT",
-	[CLS_IGNR] = "CLS_IGNR"
+	[REQ_RJCT] = "REQ_RJCT",
 };
 
 
@@ -195,12 +192,13 @@
 
 	sta->my_lid = llid;
 	sta->peer_lid = 0;
+	sta->peer_aid = 0;
 
 	/*
 	 * We do not use wpa_mesh_set_plink_state() here because there is no
 	 * entry in kernel yet.
 	 */
-	sta->plink_state = PLINK_LISTEN;
+	sta->plink_state = PLINK_IDLE;
 }
 
 
@@ -290,7 +288,8 @@
 		/* TODO: Add Connected to Mesh Gate/AS subfields */
 		wpabuf_put_u8(buf, info);
 		/* always forwarding & accepting plinks for now */
-		wpabuf_put_u8(buf, 0x1 | 0x8);
+		wpabuf_put_u8(buf, MESH_CAP_ACCEPT_ADDITIONAL_PEER |
+			      MESH_CAP_FORWARDING);
 	} else {	/* Peer closing frame */
 		/* IE: Mesh ID */
 		wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
@@ -350,7 +349,7 @@
 	if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) {
 		u8 vht_capa_oper[2 + 12 + 2 + 5];
 
-		pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper);
+		pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper, 0);
 		pos = hostapd_eid_vht_operation(bss, pos);
 		wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper);
 	}
@@ -393,6 +392,7 @@
 	os_memset(&params, 0, sizeof(params));
 	params.addr = sta->addr;
 	params.plink_state = state;
+	params.peer_aid = sta->peer_aid;
 	params.set = 1;
 
 	ret = wpa_drv_sta_add(wpa_s, &params);
@@ -423,8 +423,8 @@
 	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 
 	switch (sta->plink_state) {
-	case PLINK_OPEN_RCVD:
-	case PLINK_OPEN_SENT:
+	case PLINK_OPN_RCVD:
+	case PLINK_OPN_SNT:
 		/* retry timer */
 		if (sta->mpm_retries < conf->dot11MeshMaxRetries) {
 			eloop_register_timeout(
@@ -558,7 +558,7 @@
 		return -1;
 	}
 
-	if ((PLINK_OPEN_SENT <= sta->plink_state &&
+	if ((PLINK_OPN_SNT <= sta->plink_state &&
 	    sta->plink_state <= PLINK_ESTAB) ||
 	    (sta->sae && sta->sae->state > SAE_NOTHING)) {
 		wpa_msg(wpa_s, MSG_INFO,
@@ -567,7 +567,7 @@
 	}
 
 	if (conf->security == MESH_CONF_SEC_NONE) {
-		mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+		mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT);
 	} else {
 		mesh_rsn_auth_sae_sta(wpa_s, sta);
 		os_memcpy(hapd->mesh_required_peer, addr, ETH_ALEN);
@@ -630,7 +630,7 @@
 	if (!sta->my_lid)
 		mesh_mpm_init_link(wpa_s, sta);
 
-	mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+	mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT);
 }
 
 /*
@@ -647,8 +647,19 @@
 	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
 	struct hostapd_data *data = wpa_s->ifmsh->bss[0];
 	struct sta_info *sta;
+#ifdef CONFIG_IEEE80211N
+	struct ieee80211_ht_operation *oper;
+#endif /* CONFIG_IEEE80211N */
 	int ret;
 
+	if (elems->mesh_config_len >= 7 &&
+	    !(elems->mesh_config[6] & MESH_CAP_ACCEPT_ADDITIONAL_PEER)) {
+		wpa_msg(wpa_s, MSG_DEBUG,
+			"mesh: Ignore a crowded peer " MACSTR,
+			MAC2STR(addr));
+		return NULL;
+	}
+
 	sta = ap_get_sta(data, addr);
 	if (!sta) {
 		sta = ap_sta_add(data, addr);
@@ -670,6 +681,17 @@
 
 #ifdef CONFIG_IEEE80211N
 	copy_sta_ht_capab(data, sta, elems->ht_capabilities);
+
+	oper = (struct ieee80211_ht_operation *) elems->ht_operation;
+	if (oper &&
+	    !(oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) &&
+	    sta->ht_capabilities) {
+		wpa_msg(wpa_s, MSG_DEBUG, MACSTR
+			" does not support 40 MHz bandwidth",
+			MAC2STR(sta->addr));
+		set_disable_ht40(sta->ht_capabilities, 1);
+	}
+
 	update_ht_state(data, sta);
 #endif /* CONFIG_IEEE80211N */
 
@@ -691,6 +713,7 @@
 	params.addr = addr;
 	params.plink_state = sta->plink_state;
 	params.aid = sta->aid;
+	params.peer_aid = sta->peer_aid;
 	params.listen_interval = 100;
 	params.ht_capabilities = sta->ht_capabilities;
 	params.vht_capabilities = sta->vht_capabilities;
@@ -761,9 +784,9 @@
 	}
 
 	if (conf->security == MESH_CONF_SEC_NONE) {
-		if (sta->plink_state < PLINK_OPEN_SENT ||
+		if (sta->plink_state < PLINK_OPN_SNT ||
 		    sta->plink_state > PLINK_ESTAB)
-			mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+			mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_SNT);
 	} else {
 		mesh_rsn_auth_sae_sta(wpa_s, sta);
 	}
@@ -793,18 +816,32 @@
 		MAC2STR(sta->addr));
 
 	if (conf->security & MESH_CONF_SEC_AMPE) {
-		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0,
-				seq, sizeof(seq), sta->mtk, sizeof(sta->mtk));
-		wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0,
-				seq, sizeof(seq),
-				sta->mgtk, sizeof(sta->mgtk));
-		wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0,
-				seq, sizeof(seq),
-				sta->mgtk, sizeof(sta->mgtk));
+		wpa_hexdump_key(MSG_DEBUG, "mesh: MTK", sta->mtk, sta->mtk_len);
+		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->pairwise_cipher),
+				sta->addr, 0, 0, seq, sizeof(seq),
+				sta->mtk, sta->mtk_len);
 
-		wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk));
-		wpa_hexdump_key(MSG_DEBUG, "mgtk:",
-				sta->mgtk, sizeof(sta->mgtk));
+		wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK Key RSC",
+				sta->mgtk_rsc, sizeof(sta->mgtk_rsc));
+		wpa_hexdump_key(MSG_DEBUG, "mesh: RX MGTK",
+				sta->mgtk, sta->mgtk_len);
+		wpa_drv_set_key(wpa_s, wpa_cipher_to_alg(conf->group_cipher),
+				sta->addr, sta->mgtk_key_id, 0,
+				sta->mgtk_rsc, sizeof(sta->mgtk_rsc),
+				sta->mgtk, sta->mgtk_len);
+
+		if (sta->igtk_len) {
+			wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK Key RSC",
+					sta->igtk_rsc, sizeof(sta->igtk_rsc));
+			wpa_hexdump_key(MSG_DEBUG, "mesh: RX IGTK",
+					sta->igtk, sta->igtk_len);
+			wpa_drv_set_key(
+				wpa_s,
+				wpa_cipher_to_alg(conf->mgmt_group_cipher),
+				sta->addr, sta->igtk_key_id, 0,
+				sta->igtk_rsc, sizeof(sta->igtk_rsc),
+				sta->igtk, sta->igtk_len);
+		}
 	}
 
 	wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB);
@@ -824,36 +861,40 @@
 
 
 static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta,
-			 enum plink_event event)
+			 enum plink_event event, u16 reason)
 {
 	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 	struct mesh_conf *conf = wpa_s->ifmsh->mconf;
-	u16 reason = 0;
 
 	wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s",
 		MAC2STR(sta->addr), mplstate[sta->plink_state],
 		mplevent[event]);
 
 	switch (sta->plink_state) {
-	case PLINK_LISTEN:
+	case PLINK_IDLE:
 		switch (event) {
 		case CLS_ACPT:
 			mesh_mpm_fsm_restart(wpa_s, sta);
 			break;
 		case OPN_ACPT:
-			mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD);
+			mesh_mpm_plink_open(wpa_s, sta, PLINK_OPN_RCVD);
 			mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM,
 						   0);
 			break;
+		case REQ_RJCT:
+			mesh_mpm_send_plink_action(wpa_s, sta,
+						   PLINK_CLOSE, reason);
+			break;
 		default:
 			break;
 		}
 		break;
-	case PLINK_OPEN_SENT:
+	case PLINK_OPN_SNT:
 		switch (event) {
 		case OPN_RJCT:
 		case CNF_RJCT:
-			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			if (!reason)
+				reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
 			/* fall-through */
 		case CLS_ACPT:
 			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
@@ -868,12 +909,13 @@
 			break;
 		case OPN_ACPT:
 			/* retry timer is left untouched */
-			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD);
+			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPN_RCVD);
 			mesh_mpm_send_plink_action(wpa_s, sta,
 						   PLINK_CONFIRM, 0);
 			break;
 		case CNF_ACPT:
 			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD);
+			eloop_cancel_timeout(plink_timer, wpa_s, sta);
 			eloop_register_timeout(
 				conf->dot11MeshConfirmTimeout / 1000,
 				(conf->dot11MeshConfirmTimeout % 1000) * 1000,
@@ -883,11 +925,12 @@
 			break;
 		}
 		break;
-	case PLINK_OPEN_RCVD:
+	case PLINK_OPN_RCVD:
 		switch (event) {
 		case OPN_RJCT:
 		case CNF_RJCT:
-			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			if (!reason)
+				reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
 			/* fall-through */
 		case CLS_ACPT:
 			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
@@ -918,7 +961,8 @@
 		switch (event) {
 		case OPN_RJCT:
 		case CNF_RJCT:
-			reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+			if (!reason)
+				reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
 			/* fall-through */
 		case CLS_ACPT:
 			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
@@ -933,6 +977,8 @@
 						   PLINK_CLOSE, reason);
 			break;
 		case OPN_ACPT:
+			if (conf->security & MESH_CONF_SEC_AMPE)
+				mesh_rsn_derive_mtk(wpa_s, sta);
 			mesh_mpm_plink_estab(wpa_s, sta);
 			mesh_mpm_send_plink_action(wpa_s, sta,
 						   PLINK_CONFIRM, 0);
@@ -943,9 +989,12 @@
 		break;
 	case PLINK_ESTAB:
 		switch (event) {
+		case OPN_RJCT:
+		case CNF_RJCT:
 		case CLS_ACPT:
 			wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
-			reason = WLAN_REASON_MESH_CLOSE_RCVD;
+			if (!reason)
+				reason = WLAN_REASON_MESH_CLOSE_RCVD;
 
 			eloop_register_timeout(
 				conf->dot11MeshHoldingTimeout / 1000,
@@ -1006,13 +1055,14 @@
 	struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 	struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
 	struct sta_info *sta;
-	u16 plid = 0, llid = 0;
+	u16 plid = 0, llid = 0, aid = 0;
 	enum plink_event event;
 	struct ieee802_11_elems elems;
 	struct mesh_peer_mgmt_ie peer_mgmt_ie;
 	const u8 *ies;
 	size_t ie_len;
 	int ret;
+	u16 reason = 0;
 
 	if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED)
 		return;
@@ -1043,7 +1093,8 @@
 		ie_len -= 2;
 	}
 	if (action_field == PLINK_CONFIRM) {
-		wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies));
+		aid = WPA_GET_LE16(ies);
+		wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", aid);
 		ies += 2;	/* aid */
 		ie_len -= 2;
 	}
@@ -1087,6 +1138,10 @@
 		llid = WPA_GET_LE16(peer_mgmt_ie.plid);
 	wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid);
 
+	if (action_field == PLINK_CLOSE)
+		wpa_printf(MSG_DEBUG, "MPM: close reason=%u",
+			   WPA_GET_LE16(peer_mgmt_ie.reason));
+
 	sta = ap_get_sta(hapd, mgmt->sa);
 
 	/*
@@ -1095,7 +1150,7 @@
 	 */
 	if (!sta && action_field == PLINK_OPEN &&
 	    (!(mconf->security & MESH_CONF_SEC_AMPE) ||
-	     wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa)))
+	     wpa_auth_pmksa_get(hapd->wpa_auth, mgmt->sa, NULL)))
 		sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
 
 	if (!sta) {
@@ -1114,13 +1169,24 @@
 	if (!sta->my_lid)
 		mesh_mpm_init_link(wpa_s, sta);
 
-	if ((mconf->security & MESH_CONF_SEC_AMPE) &&
-	    mesh_rsn_process_ampe(wpa_s, sta, &elems,
-				  &mgmt->u.action.category,
-				  peer_mgmt_ie.chosen_pmk,
-				  ies, ie_len)) {
-		wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame");
-		return;
+	if (mconf->security & MESH_CONF_SEC_AMPE) {
+		int res;
+
+		res = mesh_rsn_process_ampe(wpa_s, sta, &elems,
+					    &mgmt->u.action.category,
+					    peer_mgmt_ie.chosen_pmk,
+					    ies, ie_len);
+		if (res) {
+			wpa_printf(MSG_DEBUG,
+				   "MPM: RSN process rejected frame (res=%d)",
+				   res);
+			if (action_field == PLINK_OPEN && res == -2) {
+				/* AES-SIV decryption failed */
+				mesh_mpm_fsm(wpa_s, sta, OPN_RJCT,
+					     WLAN_REASON_MESH_INVALID_GTK);
+			}
+			return;
+		}
 	}
 
 	if (sta->plink_state == PLINK_BLOCKED) {
@@ -1132,12 +1198,16 @@
 	switch (action_field) {
 	case PLINK_OPEN:
 		if (plink_free_count(hapd) == 0) {
-			event = OPN_IGNR;
+			event = REQ_RJCT;
+			reason = WLAN_REASON_MESH_MAX_PEERS;
 			wpa_printf(MSG_INFO,
 				   "MPM: Peer link num over quota(%d)",
 				   hapd->max_plinks);
 		} else if (sta->peer_lid && sta->peer_lid != plid) {
-			event = OPN_IGNR;
+			wpa_printf(MSG_DEBUG,
+				   "MPM: peer_lid mismatch: 0x%x != 0x%x",
+				   sta->peer_lid, plid);
+			return; /* no FSM event */
 		} else {
 			sta->peer_lid = plid;
 			event = OPN_ACPT;
@@ -1145,16 +1215,21 @@
 		break;
 	case PLINK_CONFIRM:
 		if (plink_free_count(hapd) == 0) {
-			event = CNF_IGNR;
+			event = REQ_RJCT;
+			reason = WLAN_REASON_MESH_MAX_PEERS;
 			wpa_printf(MSG_INFO,
 				   "MPM: Peer link num over quota(%d)",
 				   hapd->max_plinks);
 		} else if (sta->my_lid != llid ||
 			   (sta->peer_lid && sta->peer_lid != plid)) {
-			event = CNF_IGNR;
+			wpa_printf(MSG_DEBUG,
+				   "MPM: lid mismatch: my_lid: 0x%x != 0x%x or peer_lid: 0x%x != 0x%x",
+				   sta->my_lid, llid, sta->peer_lid, plid);
+			return; /* no FSM event */
 		} else {
 			if (!sta->peer_lid)
 				sta->peer_lid = plid;
+			sta->peer_aid = aid;
 			event = CNF_ACPT;
 		}
 		break;
@@ -1170,12 +1245,19 @@
 			 * restarted.
 			 */
 			event = CLS_ACPT;
-		else if (sta->peer_lid != plid)
-			event = CLS_IGNR;
-		else if (peer_mgmt_ie.plid && sta->my_lid != llid)
-			event = CLS_IGNR;
-		else
+		else if (sta->peer_lid != plid) {
+			wpa_printf(MSG_DEBUG,
+				   "MPM: peer_lid mismatch: 0x%x != 0x%x",
+				   sta->peer_lid, plid);
+			return; /* no FSM event */
+		} else if (peer_mgmt_ie.plid && sta->my_lid != llid) {
+			wpa_printf(MSG_DEBUG,
+				   "MPM: my_lid mismatch: 0x%x != 0x%x",
+				   sta->my_lid, llid);
+			return; /* no FSM event */
+		} else {
 			event = CLS_ACPT;
+		}
 		break;
 	default:
 		/*
@@ -1185,7 +1267,7 @@
 		 */
 		return;
 	}
-	mesh_mpm_fsm(wpa_s, sta, event);
+	mesh_mpm_fsm(wpa_s, sta, event, reason);
 }
 
 
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 1994f3f..628382c 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -136,49 +136,71 @@
 }
 
 
-static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr)
+static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
+				enum mfp_options ieee80211w)
 {
 	struct wpa_auth_config conf;
-	struct wpa_auth_callbacks cb;
+	static const struct wpa_auth_callbacks cb = {
+		.logger = auth_logger,
+		.get_psk = auth_get_psk,
+		.set_key = auth_set_key,
+		.start_ampe = auth_start_ampe,
+	};
 	u8 seq[6] = {};
 
 	wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
 
 	os_memset(&conf, 0, sizeof(conf));
-	conf.wpa = 2;
+	conf.wpa = WPA_PROTO_RSN;
 	conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE;
-	conf.wpa_pairwise = WPA_CIPHER_CCMP;
-	conf.rsn_pairwise = WPA_CIPHER_CCMP;
-	conf.wpa_group = WPA_CIPHER_CCMP;
+	conf.wpa_pairwise = rsn->pairwise_cipher;
+	conf.rsn_pairwise = rsn->pairwise_cipher;
+	conf.wpa_group = rsn->group_cipher;
 	conf.eapol_version = 0;
 	conf.wpa_group_rekey = -1;
+	conf.wpa_group_update_count = 4;
+	conf.wpa_pairwise_update_count = 4;
+#ifdef CONFIG_IEEE80211W
+	conf.ieee80211w = ieee80211w;
+	if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
+		conf.group_mgmt_cipher = rsn->mgmt_group_cipher;
+#endif /* CONFIG_IEEE80211W */
 
-	os_memset(&cb, 0, sizeof(cb));
-	cb.ctx = rsn;
-	cb.logger = auth_logger;
-	cb.get_psk = auth_get_psk;
-	cb.set_key = auth_set_key;
-	cb.start_ampe = auth_start_ampe;
-
-	rsn->auth = wpa_init(addr, &conf, &cb);
+	rsn->auth = wpa_init(addr, &conf, &cb, rsn);
 	if (rsn->auth == NULL) {
 		wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
 		return -1;
 	}
 
 	/* TODO: support rekeying */
-	if (random_get_bytes(rsn->mgtk, 16) < 0) {
-		wpa_deinit(rsn->auth);
+	rsn->mgtk_len = wpa_cipher_key_len(conf.wpa_group);
+	if (random_get_bytes(rsn->mgtk, rsn->mgtk_len) < 0)
 		return -1;
-	}
+	rsn->mgtk_key_id = 1;
 
-	/* group mgmt */
-	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
-			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+#ifdef CONFIG_IEEE80211W
+	if (ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+		rsn->igtk_len = wpa_cipher_key_len(conf.group_mgmt_cipher);
+		if (random_get_bytes(rsn->igtk, rsn->igtk_len) < 0)
+			return -1;
+		rsn->igtk_key_id = 4;
+
+		/* group mgmt */
+		wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX IGTK",
+				rsn->igtk, rsn->igtk_len);
+		wpa_drv_set_key(rsn->wpa_s,
+				wpa_cipher_to_alg(rsn->mgmt_group_cipher), NULL,
+				rsn->igtk_key_id, 1,
+				seq, sizeof(seq), rsn->igtk, rsn->igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
 
 	/* group privacy / data frames */
-	wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
-			seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+	wpa_hexdump_key(MSG_DEBUG, "mesh: Own TX MGTK",
+			rsn->mgtk, rsn->mgtk_len);
+	wpa_drv_set_key(rsn->wpa_s, wpa_cipher_to_alg(rsn->group_cipher), NULL,
+			rsn->mgtk_key_id, 1, seq, sizeof(seq),
+			rsn->mgtk, rsn->mgtk_len);
 
 	return 0;
 }
@@ -187,6 +209,9 @@
 static void mesh_rsn_deinit(struct mesh_rsn *rsn)
 {
 	os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
+	rsn->mgtk_len = 0;
+	os_memset(rsn->igtk, 0, sizeof(rsn->igtk));
+	rsn->igtk_len = 0;
 	if (rsn->auth)
 		wpa_deinit(rsn->auth);
 }
@@ -199,13 +224,20 @@
 	struct hostapd_data *bss = wpa_s->ifmsh->bss[0];
 	const u8 *ie;
 	size_t ie_len;
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+	struct external_pmksa_cache *entry;
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
 
 	mesh_rsn = os_zalloc(sizeof(*mesh_rsn));
 	if (mesh_rsn == NULL)
 		return NULL;
 	mesh_rsn->wpa_s = wpa_s;
+	mesh_rsn->pairwise_cipher = conf->pairwise_cipher;
+	mesh_rsn->group_cipher = conf->group_cipher;
+	mesh_rsn->mgmt_group_cipher = conf->mgmt_group_cipher;
 
-	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) {
+	if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr,
+				 conf->ieee80211w) < 0) {
 		mesh_rsn_deinit(mesh_rsn);
 		os_free(mesh_rsn);
 		return NULL;
@@ -213,6 +245,22 @@
 
 	bss->wpa_auth = mesh_rsn->auth;
 
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+	while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache,
+				     struct external_pmksa_cache,
+				     list)) != NULL) {
+		int ret;
+
+		ret = wpa_auth_pmksa_add_entry(bss->wpa_auth,
+					       entry->pmksa_cache);
+		dl_list_del(&entry->list);
+		os_free(entry);
+
+		if (ret < 0)
+			return NULL;
+	}
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
 	ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len);
 	conf->rsn_ie = (u8 *) ie;
 	conf->rsn_ie_len = ie_len;
@@ -304,7 +352,7 @@
 			return -1;
 	}
 
-	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr);
+	pmksa = wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL);
 	if (pmksa) {
 		if (!sta->wpa_sm)
 			sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
@@ -334,7 +382,6 @@
 		"AUTH: started authentication with SAE peer: " MACSTR,
 		MAC2STR(sta->addr));
 
-	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
 	ret = auth_sae_init_committed(hapd, sta);
 	if (ret)
 		return ret;
@@ -358,18 +405,27 @@
 {
 	u8 *myaddr = rsn->wpa_s->own_addr;
 	u8 *peer = sta->addr;
-	u8 *addr1 = peer, *addr2 = myaddr;
-	u8 context[AES_BLOCK_SIZE];
+	u8 *addr1, *addr2;
+	u8 context[RSN_SELECTOR_LEN + 2 * ETH_ALEN], *ptr = context;
 
-	/* SAE */
-	RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+	/*
+	 * AEK = KDF-Hash-256(PMK, "AEK Derivation", Selected AKM Suite ||
+	 *       min(localMAC, peerMAC) || max(localMAC, peerMAC))
+	 */
+	/* Selected AKM Suite: SAE */
+	RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE);
+	ptr += RSN_SELECTOR_LEN;
 
 	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
 		addr1 = myaddr;
 		addr2 = peer;
+	} else {
+		addr1 = peer;
+		addr2 = myaddr;
 	}
-	os_memcpy(context + 4, addr1, ETH_ALEN);
-	os_memcpy(context + 10, addr2, ETH_ALEN);
+	os_memcpy(ptr, addr1, ETH_ALEN);
+	ptr += ETH_ALEN;
+	os_memcpy(ptr, addr2, ETH_ALEN);
 
 	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation",
 		   context, sizeof(context), sta->aek, sizeof(sta->aek));
@@ -381,40 +437,44 @@
 {
 	u8 *ptr;
 	u8 *min, *max;
-	u16 min_lid, max_lid;
-	size_t nonce_len = sizeof(sta->my_nonce);
-	size_t lid_len = sizeof(sta->my_lid);
 	u8 *myaddr = wpa_s->own_addr;
 	u8 *peer = sta->addr;
-	/* 2 nonces, 2 linkids, akm suite, 2 mac addrs */
-	u8 context[64 + 4 + 4 + 12];
+	u8 context[2 * WPA_NONCE_LEN + 2 * 2 + RSN_SELECTOR_LEN + 2 * ETH_ALEN];
 
+	/*
+	 * MTK = KDF-Hash-Length(PMK, "Temporal Key Derivation", min(localNonce,
+	 *  peerNonce) || max(localNonce, peerNonce) || min(localLinkID,
+	 *  peerLinkID) || max(localLinkID, peerLinkID) || Selected AKM Suite ||
+	 *  min(localMAC, peerMAC) || max(localMAC, peerMAC))
+	 */
 	ptr = context;
-	if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) {
+	if (os_memcmp(sta->my_nonce, sta->peer_nonce, WPA_NONCE_LEN) < 0) {
 		min = sta->my_nonce;
 		max = sta->peer_nonce;
 	} else {
 		min = sta->peer_nonce;
 		max = sta->my_nonce;
 	}
-	os_memcpy(ptr, min, nonce_len);
-	os_memcpy(ptr + nonce_len, max, nonce_len);
-	ptr += 2 * nonce_len;
+	os_memcpy(ptr, min, WPA_NONCE_LEN);
+	ptr += WPA_NONCE_LEN;
+	os_memcpy(ptr, max, WPA_NONCE_LEN);
+	ptr += WPA_NONCE_LEN;
 
 	if (sta->my_lid < sta->peer_lid) {
-		min_lid = host_to_le16(sta->my_lid);
-		max_lid = host_to_le16(sta->peer_lid);
+		WPA_PUT_LE16(ptr, sta->my_lid);
+		ptr += 2;
+		WPA_PUT_LE16(ptr, sta->peer_lid);
+		ptr += 2;
 	} else {
-		min_lid = host_to_le16(sta->peer_lid);
-		max_lid = host_to_le16(sta->my_lid);
+		WPA_PUT_LE16(ptr, sta->peer_lid);
+		ptr += 2;
+		WPA_PUT_LE16(ptr, sta->my_lid);
+		ptr += 2;
 	}
-	os_memcpy(ptr, &min_lid, lid_len);
-	os_memcpy(ptr + lid_len, &max_lid, lid_len);
-	ptr += 2 * lid_len;
 
-	/* SAE */
-	RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
-	ptr += 4;
+	/* Selected AKM Suite: SAE */
+	RSN_SELECTOR_PUT(ptr, RSN_AUTH_KEY_MGMT_SAE);
+	ptr += RSN_SELECTOR_LEN;
 
 	if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
 		min = myaddr;
@@ -424,22 +484,24 @@
 		max = myaddr;
 	}
 	os_memcpy(ptr, min, ETH_ALEN);
-	os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN);
+	ptr += ETH_ALEN;
+	os_memcpy(ptr, max, ETH_ALEN);
 
-	sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk),
+	sta->mtk_len = wpa_cipher_key_len(wpa_s->mesh_rsn->pairwise_cipher);
+	sha256_prf(sta->sae->pmk, SAE_PMK_LEN,
 		   "Temporal Key Derivation", context, sizeof(context),
-		   sta->mtk, sizeof(sta->mtk));
+		   sta->mtk, sta->mtk_len);
 	return 0;
 }
 
 
 void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta)
 {
-	if (random_get_bytes(sta->my_nonce, 32) < 0) {
+	if (random_get_bytes(sta->my_nonce, WPA_NONCE_LEN) < 0) {
 		wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce");
 		/* TODO: How to handle this more cleanly? */
 	}
-	os_memset(sta->peer_nonce, 0, 32);
+	os_memset(sta->peer_nonce, 0, WPA_NONCE_LEN);
 	mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta);
 }
 
@@ -455,65 +517,94 @@
 {
 	struct ieee80211_ampe_ie *ampe;
 	u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
-	u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload;
+	u8 *ampe_ie, *pos, *mic_payload;
 	const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
 	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
 	int ret = 0;
+	size_t len;
 
-	if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) {
+	len = sizeof(*ampe);
+	if (cat[1] == PLINK_OPEN)
+		len += rsn->mgtk_len + WPA_KEY_RSC_LEN + 4;
+#ifdef CONFIG_IEEE80211W
+	if (cat[1] == PLINK_OPEN && rsn->igtk_len)
+		len += 2 + 6 + rsn->igtk_len;
+#endif /* CONFIG_IEEE80211W */
+
+	if (2 + AES_BLOCK_SIZE + 2 + len > wpabuf_tailroom(buf)) {
 		wpa_printf(MSG_ERROR, "protect frame: buffer too small");
 		return -EINVAL;
 	}
 
-	ampe_ie = os_zalloc(2 + sizeof(*ampe));
+	ampe_ie = os_zalloc(2 + len);
 	if (!ampe_ie) {
 		wpa_printf(MSG_ERROR, "protect frame: out of memory");
 		return -ENOMEM;
 	}
 
-	mic_ie = os_zalloc(2 + AES_BLOCK_SIZE);
-	if (!mic_ie) {
-		wpa_printf(MSG_ERROR, "protect frame: out of memory");
-		ret = -ENOMEM;
-		goto free;
-	}
-
 	/*  IE: AMPE */
 	ampe_ie[0] = WLAN_EID_AMPE;
-	ampe_ie[1] = sizeof(*ampe);
+	ampe_ie[1] = len;
 	ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
 
 	RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
-		     wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP));
-	os_memcpy(ampe->local_nonce, sta->my_nonce, 32);
-	os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32);
-	/* incomplete: see 13.5.4 */
+			 RSN_CIPHER_SUITE_CCMP);
+	os_memcpy(ampe->local_nonce, sta->my_nonce, WPA_NONCE_LEN);
+	os_memcpy(ampe->peer_nonce, sta->peer_nonce, WPA_NONCE_LEN);
+
+	pos = (u8 *) (ampe + 1);
+	if (cat[1] != PLINK_OPEN)
+		goto skip_keys;
+
+	/* TODO: Key Replay Counter[8] optionally for
+	 * Mesh Group Key Inform/Acknowledge frames */
+
 	/* TODO: static mgtk for now since we don't support rekeying! */
-	os_memcpy(ampe->mgtk, rsn->mgtk, 16);
-	/*  TODO: Populate Key RSC */
-	/*  expire in 13 decades or so */
-	os_memset(ampe->key_expiration, 0xff, 4);
+	/*
+	 * GTKdata[variable]:
+	 * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+	 */
+	os_memcpy(pos, rsn->mgtk, rsn->mgtk_len);
+	pos += rsn->mgtk_len;
+	wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->mgtk_key_id, pos);
+	pos += WPA_KEY_RSC_LEN;
+	/* Use fixed GTKExpirationTime for now */
+	WPA_PUT_LE32(pos, 0xffffffff);
+	pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+	/*
+	 * IGTKdata[variable]:
+	 * Key ID[2], IPN[6], IGTK[variable]
+	 */
+	if (rsn->igtk_len) {
+		WPA_PUT_LE16(pos, rsn->igtk_key_id);
+		pos += 2;
+		wpa_drv_get_seqnum(rsn->wpa_s, NULL, rsn->igtk_key_id, pos);
+		pos += 6;
+		os_memcpy(pos, rsn->igtk, rsn->igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
+
+skip_keys:
+	wpa_hexdump_key(MSG_DEBUG, "mesh: Plaintext AMPE element",
+			ampe_ie, 2 + len);
 
 	/* IE: MIC */
-	mic_ie[0] = WLAN_EID_MIC;
-	mic_ie[1] = AES_BLOCK_SIZE;
-	wpabuf_put_data(buf, mic_ie, 2);
+	wpabuf_put_u8(buf, WLAN_EID_MIC);
+	wpabuf_put_u8(buf, AES_BLOCK_SIZE);
 	/* MIC field is output ciphertext */
 
 	/* encrypt after MIC */
-	mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) +
-					AES_BLOCK_SIZE);
+	mic_payload = wpabuf_put(buf, 2 + len + AES_BLOCK_SIZE);
 
-	if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3,
+	if (aes_siv_encrypt(sta->aek, sizeof(sta->aek), ampe_ie, 2 + len, 3,
 			    aad, aad_len, mic_payload)) {
 		wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
 		ret = -ENOMEM;
-		goto free;
 	}
 
-free:
 	os_free(ampe_ie);
-	os_free(mic_ie);
 
 	return ret;
 }
@@ -526,19 +617,20 @@
 {
 	int ret = 0;
 	struct ieee80211_ampe_ie *ampe;
-	u8 null_nonce[32] = {};
+	u8 null_nonce[WPA_NONCE_LEN] = {};
 	u8 ampe_eid;
 	u8 ampe_ie_len;
-	u8 *ampe_buf, *crypt = NULL;
+	u8 *ampe_buf, *crypt = NULL, *pos, *end;
 	size_t crypt_len;
 	const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
 	const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
 				   (elems->mic - 2) - cat };
+	size_t key_len;
 
 	if (!sta->sae) {
 		struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
 
-		if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr)) {
+		if (!wpa_auth_pmksa_get(hapd->wpa_auth, sta->addr, NULL)) {
 			wpa_printf(MSG_INFO,
 				   "Mesh RSN: SAE is not prepared yet");
 			return -1;
@@ -562,7 +654,7 @@
 		return -1;
 
 	crypt_len = elems_len - (elems->mic - start);
-	if (crypt_len < 2) {
+	if (crypt_len < 2 + AES_BLOCK_SIZE) {
 		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
 		return -1;
 	}
@@ -577,17 +669,22 @@
 
 	os_memcpy(crypt, elems->mic, crypt_len);
 
-	if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3,
+	if (aes_siv_decrypt(sta->aek, sizeof(sta->aek), crypt, crypt_len, 3,
 			    aad, aad_len, ampe_buf)) {
 		wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!");
-		ret = -1;
+		ret = -2;
 		goto free;
 	}
 
+	crypt_len -= AES_BLOCK_SIZE;
+	wpa_hexdump_key(MSG_DEBUG, "mesh: Decrypted AMPE element",
+			ampe_buf, crypt_len);
+
 	ampe_eid = *ampe_buf++;
 	ampe_ie_len = *ampe_buf++;
 
 	if (ampe_eid != WLAN_EID_AMPE ||
+	    (size_t) 2 + ampe_ie_len > crypt_len ||
 	    ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
 		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
 		ret = -1;
@@ -595,17 +692,89 @@
 	}
 
 	ampe = (struct ieee80211_ampe_ie *) ampe_buf;
-	if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 &&
-	    os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) {
+	pos = (u8 *) (ampe + 1);
+	end = ampe_buf + ampe_ie_len;
+	if (os_memcmp(ampe->peer_nonce, null_nonce, WPA_NONCE_LEN) != 0 &&
+	    os_memcmp(ampe->peer_nonce, sta->my_nonce, WPA_NONCE_LEN) != 0) {
 		wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
 		ret = -1;
 		goto free;
 	}
 	os_memcpy(sta->peer_nonce, ampe->local_nonce,
 		  sizeof(ampe->local_nonce));
-	os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk));
 
-	/* todo parse mgtk expiration */
+	/* TODO: Key Replay Counter[8] in Mesh Group Key Inform/Acknowledge
+	 * frames */
+
+	/*
+	 * GTKdata shall not be included in Mesh Peering Confirm. While the
+	 * standard does not state the same about IGTKdata, that same constraint
+	 * needs to apply for it. It makes no sense to include the keys in Mesh
+	 * Peering Close frames either, so while the standard does not seem to
+	 * have a shall statement for these, they are described without
+	 * mentioning GTKdata.
+	 *
+	 * An earlier implementation used to add GTKdata to both Mesh Peering
+	 * Open and Mesh Peering Confirm frames, so ignore the possibly present
+	 * GTKdata frame without rejecting the frame as a backwards
+	 * compatibility mechanism.
+	 */
+	if (cat[1] != PLINK_OPEN) {
+		if (end > pos) {
+			wpa_hexdump_key(MSG_DEBUG,
+					"mesh: Ignore unexpected GTKdata(etc.) fields in the end of AMPE element in Mesh Peering Confirm/Close",
+					pos, end - pos);
+		}
+		goto free;
+	}
+
+	/*
+	 * GTKdata[variable]:
+	 * MGTK[variable] || Key RSC[8] || GTKExpirationTime[4]
+	 */
+	sta->mgtk_key_id = 1; /* FIX: Where to get Key ID? */
+	key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->group_cipher);
+	if ((int) key_len + WPA_KEY_RSC_LEN + 4 > end - pos) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "mesh: Truncated AMPE element");
+		ret = -1;
+		goto free;
+	}
+	sta->mgtk_len = key_len;
+	os_memcpy(sta->mgtk, pos, sta->mgtk_len);
+	wpa_hexdump_key(MSG_DEBUG, "mesh: GTKdata - MGTK",
+			sta->mgtk, sta->mgtk_len);
+	pos += sta->mgtk_len;
+	wpa_hexdump(MSG_DEBUG, "mesh: GTKdata - MGTK - Key RSC",
+		    pos, WPA_KEY_RSC_LEN);
+	os_memcpy(sta->mgtk_rsc, pos, sizeof(sta->mgtk_rsc));
+	pos += WPA_KEY_RSC_LEN;
+	wpa_printf(MSG_DEBUG,
+		   "mesh: GTKdata - MGTK - GTKExpirationTime: %u seconds",
+		   WPA_GET_LE32(pos));
+	pos += 4;
+
+#ifdef CONFIG_IEEE80211W
+	/*
+	 * IGTKdata[variable]:
+	 * Key ID[2], IPN[6], IGTK[variable]
+	 */
+	key_len = wpa_cipher_key_len(wpa_s->mesh_rsn->mgmt_group_cipher);
+	if (end - pos >= (int) (2 + 6 + key_len)) {
+		sta->igtk_key_id = WPA_GET_LE16(pos);
+		wpa_printf(MSG_DEBUG, "mesh: IGTKdata - Key ID %u",
+			   sta->igtk_key_id);
+		pos += 2;
+		os_memcpy(sta->igtk_rsc, pos, sizeof(sta->igtk_rsc));
+		wpa_hexdump(MSG_DEBUG, "mesh: IGTKdata - IPN",
+			    sta->igtk_rsc, sizeof(sta->igtk_rsc));
+		pos += 6;
+		os_memcpy(sta->igtk, pos, key_len);
+		sta->igtk_len = key_len;
+		wpa_hexdump_key(MSG_DEBUG, "mesh: IGTKdata - IGTK",
+				sta->igtk, sta->igtk_len);
+	}
+#endif /* CONFIG_IEEE80211W */
+
 free:
 	os_free(crypt);
 	return ret;
diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h
index 89601d4..8775ced 100644
--- a/wpa_supplicant/mesh_rsn.h
+++ b/wpa_supplicant/mesh_rsn.h
@@ -12,7 +12,15 @@
 struct mesh_rsn {
 	struct wpa_supplicant *wpa_s;
 	struct wpa_authenticator *auth;
-	u8 mgtk[16];
+	unsigned int pairwise_cipher;
+	unsigned int group_cipher;
+	u8 mgtk[WPA_TK_MAX_LEN];
+	size_t mgtk_len;
+	u8 mgtk_key_id;
+	unsigned int mgmt_group_cipher;
+	u8 igtk_key_id;
+	u8 igtk[WPA_TK_MAX_LEN];
+	size_t igtk_len;
 #ifdef CONFIG_SAE
 	struct wpabuf *sae_token;
 	int sae_group_index;
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index e739363..9464c4b 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -13,7 +13,6 @@
 #include "config.h"
 #include "wpa_supplicant_i.h"
 #include "wps_supplicant.h"
-#include "binder/binder.h"
 #include "dbus/dbus_common.h"
 #include "dbus/dbus_old.h"
 #include "dbus/dbus_new.h"
@@ -24,6 +23,7 @@
 #include "p2p_supplicant.h"
 #include "sme.h"
 #include "notify.h"
+#include "hidl.h"
 
 int wpas_notify_supplicant_initialized(struct wpa_global *global)
 {
@@ -35,11 +35,11 @@
 	}
 #endif /* CONFIG_DBUS */
 
-#ifdef CONFIG_BINDER
-	global->binder = wpas_binder_init(global);
-	if (!global->binder)
+#ifdef CONFIG_HIDL
+	global->hidl = wpas_hidl_init(global);
+	if (!global->hidl)
 		return -1;
-#endif /* CONFIG_BINDER */
+#endif /* CONFIG_HIDL */
 
 	return 0;
 }
@@ -52,22 +52,25 @@
 		wpas_dbus_deinit(global->dbus);
 #endif /* CONFIG_DBUS */
 
-#ifdef CONFIG_BINDER
-	if (global->binder)
-		wpas_binder_deinit(global->binder);
-#endif /* CONFIG_BINDER */
+#ifdef CONFIG_HIDL
+	if (global->hidl)
+		wpas_hidl_deinit(global->hidl);
+#endif /* CONFIG_HIDL */
 }
 
 
 int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s->p2p_mgmt)
-		return 0;
+	if (!wpa_s->p2p_mgmt) {
+		if (wpas_dbus_register_iface(wpa_s))
+			return -1;
 
-	if (wpas_dbus_register_iface(wpa_s))
-		return -1;
+		if (wpas_dbus_register_interface(wpa_s))
+			return -1;
+	}
 
-	if (wpas_dbus_register_interface(wpa_s))
+	/* HIDL interface wants to keep track of the P2P mgmt iface. */
+	if (wpas_hidl_register_interface(wpa_s))
 		return -1;
 
 	return 0;
@@ -76,14 +79,16 @@
 
 void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s)
 {
-	if (wpa_s->p2p_mgmt)
-		return;
+	if (!wpa_s->p2p_mgmt) {
+		/* unregister interface in old DBus ctrl iface */
+		wpas_dbus_unregister_iface(wpa_s);
 
-	/* unregister interface in old DBus ctrl iface */
-	wpas_dbus_unregister_iface(wpa_s);
+		/* unregister interface in new DBus ctrl iface */
+		wpas_dbus_unregister_interface(wpa_s);
+	}
 
-	/* unregister interface in new DBus ctrl iface */
-	wpas_dbus_unregister_interface(wpa_s);
+	/* HIDL interface wants to keep track of the P2P mgmt iface. */
+	wpas_hidl_unregister_interface(wpa_s);
 }
 
 
@@ -128,6 +133,8 @@
 		     wpa_ssid_txt(wpa_s->current_ssid->ssid,
 				  wpa_s->current_ssid->ssid_len) : "");
 #endif /* ANDROID */
+
+	wpas_hidl_notify_state_changed(wpa_s);
 }
 
 
@@ -137,6 +144,8 @@
 		return;
 
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
+
+	wpas_hidl_notify_disconnect_reason(wpa_s);
 }
 
 
@@ -146,8 +155,16 @@
 		return;
 
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_ASSOC_STATUS_CODE);
+
+	wpas_hidl_notify_assoc_reject(wpa_s);
 }
 
+void wpas_notify_auth_timeout(struct wpa_supplicant *wpa_s) {
+	if (wpa_s->p2p_mgmt)
+		return;
+
+	wpas_hidl_notify_auth_timeout(wpa_s);
+}
 
 void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
 {
@@ -173,6 +190,8 @@
 		return;
 
 	wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS);
+
+	wpas_hidl_notify_bssid_changed(wpa_s);
 }
 
 
@@ -214,6 +233,8 @@
 		return;
 
 	wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
+
+	wpas_hidl_notify_network_request(wpa_s, ssid, rtype, default_txt);
 }
 
 
@@ -286,6 +307,10 @@
 
 #ifdef CONFIG_WPS
 	wpas_dbus_signal_wps_event_fail(wpa_s, fail);
+
+	wpas_hidl_notify_wps_event_fail(wpa_s, fail->peer_macaddr,
+					fail->config_error,
+					fail->error_indication);
 #endif /* CONFIG_WPS */
 }
 
@@ -297,6 +322,8 @@
 
 #ifdef CONFIG_WPS
 	wpas_dbus_signal_wps_event_success(wpa_s);
+
+	wpas_hidl_notify_wps_event_success(wpa_s);
 #endif /* CONFIG_WPS */
 }
 
@@ -307,6 +334,8 @@
 
 #ifdef CONFIG_WPS
 	wpas_dbus_signal_wps_event_pbc_overlap(wpa_s);
+
+	wpas_hidl_notify_wps_event_pbc_overlap(wpa_s);
 #endif /* CONFIG_WPS */
 }
 
@@ -323,8 +352,10 @@
 	 * applications since these network objects won't behave like
 	 * regular ones.
 	 */
-	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
+	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s) {
 		wpas_dbus_register_network(wpa_s, ssid);
+		wpas_hidl_register_network(wpa_s, ssid);
+	}
 }
 
 
@@ -333,6 +364,7 @@
 {
 #ifdef CONFIG_P2P
 	wpas_dbus_register_persistent_group(wpa_s, ssid);
+	wpas_hidl_register_network(wpa_s, ssid);
 #endif /* CONFIG_P2P */
 }
 
@@ -342,6 +374,7 @@
 {
 #ifdef CONFIG_P2P
 	wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+	wpas_hidl_unregister_network(wpa_s, ssid);
 #endif /* CONFIG_P2P */
 }
 
@@ -354,8 +387,10 @@
 	if (wpa_s->wpa)
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
 	if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s &&
-	    !wpa_s->p2p_mgmt)
+	    !wpa_s->p2p_mgmt) {
 		wpas_dbus_unregister_network(wpa_s, ssid->id);
+		wpas_hidl_unregister_network(wpa_s, ssid);
+	}
 	if (network_is_persistent_group(ssid))
 		wpas_notify_persistent_group_removed(wpa_s, ssid);
 
@@ -566,19 +601,27 @@
 {
 	/* Notify P2P find has stopped */
 	wpas_dbus_signal_p2p_find_stopped(wpa_s);
+
+	wpas_hidl_notify_p2p_find_stopped(wpa_s);
 }
 
 
 void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
-				  const u8 *dev_addr, int new_device)
+				  const u8 *addr, const struct p2p_peer_info *info,
+				  const u8* peer_wfd_device_info, u8 peer_wfd_device_info_len,
+				  int new_device)
 {
 	if (new_device) {
 		/* Create the new peer object */
-		wpas_dbus_register_peer(wpa_s, dev_addr);
+		wpas_dbus_register_peer(wpa_s, info->p2p_device_addr);
 	}
 
 	/* Notify a new peer has been detected*/
-	wpas_dbus_signal_peer_device_found(wpa_s, dev_addr);
+	wpas_dbus_signal_peer_device_found(wpa_s, info->p2p_device_addr);
+
+	wpas_hidl_notify_p2p_device_found(wpa_s, addr, info,
+					  peer_wfd_device_info,
+					  peer_wfd_device_info_len);
 }
 
 
@@ -589,6 +632,8 @@
 
 	/* Create signal on interface object*/
 	wpas_dbus_signal_peer_device_lost(wpa_s, dev_addr);
+
+	wpas_hidl_notify_p2p_device_lost(wpa_s, dev_addr);
 }
 
 
@@ -599,6 +644,8 @@
 	wpas_dbus_signal_p2p_group_removed(wpa_s, role);
 
 	wpas_dbus_unregister_p2p_group(wpa_s, ssid);
+
+	wpas_hidl_notify_p2p_group_removed(wpa_s, ssid, role);
 }
 
 
@@ -606,6 +653,8 @@
 				const u8 *src, u16 dev_passwd_id, u8 go_intent)
 {
 	wpas_dbus_signal_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
+
+	wpas_hidl_notify_p2p_go_neg_req(wpa_s, src, dev_passwd_id, go_intent);
 }
 
 
@@ -613,6 +662,8 @@
 				      struct p2p_go_neg_results *res)
 {
 	wpas_dbus_signal_p2p_go_neg_resp(wpa_s, res);
+
+	wpas_hidl_notify_p2p_go_neg_completed(wpa_s, res);
 }
 
 
@@ -620,6 +671,8 @@
 				       int status, const u8 *bssid)
 {
 	wpas_dbus_signal_p2p_invitation_result(wpa_s, status, bssid);
+
+	wpas_hidl_notify_p2p_invitation_result(wpa_s, status, bssid);
 }
 
 
@@ -639,6 +692,9 @@
 {
 	wpas_dbus_signal_p2p_sd_response(wpa_s, sa, update_indic,
 					 tlvs, tlvs_len);
+
+	wpas_hidl_notify_p2p_sd_response(wpa_s, sa, update_indic,
+					 tlvs, tlvs_len);
 }
 
 
@@ -664,17 +720,24 @@
 	wpas_dbus_signal_p2p_provision_discovery(wpa_s, dev_addr, request,
 						 status, config_methods,
 						 generated_pin);
+
+	wpas_hidl_notify_p2p_provision_discovery(wpa_s, dev_addr, request,
+						 status, config_methods,
+						 generated_pin);
+
 }
 
 
 void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
-				   struct wpa_ssid *ssid, int network_id,
-				   int client)
+				   struct wpa_ssid *ssid, int persistent,
+				   int client, const u8 *ip)
 {
 	/* Notify a group has been started */
 	wpas_dbus_register_p2p_group(wpa_s, ssid);
 
-	wpas_dbus_signal_p2p_group_started(wpa_s, ssid, client, network_id);
+	wpas_dbus_signal_p2p_group_started(wpa_s, client, persistent, ip);
+
+	wpas_hidl_notify_p2p_group_started(wpa_s, ssid, persistent, client);
 }
 
 
@@ -683,6 +746,8 @@
 {
 	/* Notify a group formation failed */
 	wpas_dbus_signal_p2p_group_formation_failure(wpa_s, reason);
+
+	wpas_hidl_notify_p2p_group_formation_failure(wpa_s, reason);
 }
 
 
@@ -700,6 +765,9 @@
 	/* Notify a P2P Invitation Request */
 	wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
 						 id, op_freq);
+
+	wpas_hidl_notify_p2p_invitation_received(wpa_s, sa, go_dev_addr, bssid,
+						 id, op_freq);
 }
 
 #endif /* CONFIG_P2P */
@@ -722,6 +790,8 @@
 
 	/* Notify listeners a new station has been authorized */
 	wpas_dbus_signal_sta_authorized(wpa_s, sta);
+
+	wpas_hidl_notify_ap_sta_authorized(wpa_s, sta, p2p_dev_addr);
 }
 
 
@@ -740,6 +810,8 @@
 
 	/* Notify listeners a station has been deauthorized */
 	wpas_dbus_signal_sta_deauthorized(wpa_s, sta);
+
+	wpas_hidl_notify_ap_sta_deauthorized(wpa_s, sta, p2p_dev_addr);
 }
 
 
@@ -850,3 +922,53 @@
 	}
 #endif /* CONFIG_P2P */
 }
+
+void wpas_notify_anqp_query_done(struct wpa_supplicant *wpa_s, const u8* bssid,
+				 const char *result,
+				 const struct wpa_bss_anqp *anqp)
+{
+#ifdef CONFIG_INTERWORKING
+	if (!wpa_s || !bssid || !anqp)
+		return;
+
+	wpas_hidl_notify_anqp_query_done(wpa_s, bssid, result, anqp);
+#endif /* CONFIG_INTERWORKING */
+}
+
+void wpas_notify_hs20_icon_query_done(struct wpa_supplicant *wpa_s, const u8* bssid,
+				      const char* file_name, const u8* image,
+				      u32 image_length)
+{
+#ifdef CONFIG_HS20
+	if (!wpa_s || !bssid || !file_name || !image)
+		return;
+
+	wpas_hidl_notify_hs20_icon_query_done(wpa_s, bssid, file_name, image,
+					      image_length);
+#endif /* CONFIG_HS20 */
+}
+
+void wpas_notify_hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+						  const char* url,
+						  u8 osu_method)
+{
+#ifdef CONFIG_HS20
+	if (!wpa_s || !url)
+		return;
+
+	wpas_hidl_notify_hs20_rx_subscription_remediation(wpa_s, url, osu_method);
+#endif /* CONFIG_HS20 */
+}
+
+void wpas_notify_hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s,
+						u8 code, u16 reauth_delay,
+						const char *url)
+{
+#ifdef CONFIG_HS20
+	if (!wpa_s || !url)
+		return;
+
+	wpas_hidl_notify_hs20_rx_deauth_imminent_notice(wpa_s, code, reauth_delay,
+							url);
+#endif /* CONFIG_HS20 */
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index 1b7f04d..9c98524 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -10,6 +10,7 @@
 #define NOTIFY_H
 
 #include "p2p/p2p.h"
+#include "bss.h"
 
 struct wps_credential;
 struct wps_event_m2d;
@@ -24,6 +25,7 @@
 			       enum wpa_states old_state);
 void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s);
 void wpas_notify_assoc_status_code(struct wpa_supplicant *wpa_s);
+void wpas_notify_auth_timeout(struct wpa_supplicant *wpa_s);
 void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
 void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
@@ -88,7 +90,9 @@
 				const u8 *p2p_dev_addr);
 void wpas_notify_p2p_find_stopped(struct wpa_supplicant *wpa_s);
 void wpas_notify_p2p_device_found(struct wpa_supplicant *wpa_s,
-				  const u8 *dev_addr, int new_device);
+				 const u8 *addr, const struct p2p_peer_info *info,
+				 const u8* peer_wfd_device_info, u8 peer_wfd_device_info_len,
+				 int new_device);
 void wpas_notify_p2p_device_lost(struct wpa_supplicant *wpa_s,
 				 const u8 *dev_addr);
 void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
@@ -113,8 +117,8 @@
 					 u16 config_methods,
 					 unsigned int generated_pin);
 void wpas_notify_p2p_group_started(struct wpa_supplicant *wpa_s,
-				   struct wpa_ssid *ssid, int network_id,
-				   int client);
+				   struct wpa_ssid *ssid, int persistent,
+				   int client, const u8 *ip);
 void wpas_notify_p2p_group_formation_failure(struct wpa_supplicant *wpa_s,
 					     const char *reason);
 void wpas_notify_persistent_group_added(struct wpa_supplicant *wpa_s,
@@ -141,5 +145,16 @@
 void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
 					 const u8 *sa, const u8 *go_dev_addr,
 					 const u8 *bssid, int id, int op_freq);
-
+void wpas_notify_anqp_query_done(struct wpa_supplicant *wpa_s, const u8* bssid,
+				 const char* result,
+				 const struct wpa_bss_anqp *anqp);
+void wpas_notify_hs20_icon_query_done(struct wpa_supplicant *wpa_s, const u8* bssid,
+				      const char* file_name, const u8* image,
+				      u32 image_length);
+void wpas_notify_hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+						  const char* url,
+						  u8 osu_method);
+void wpas_notify_hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s,
+						u8 code, u16 reauth_delay,
+						const char *url);
 #endif /* NOTIFY_H */
diff --git a/wpa_supplicant/offchannel.c b/wpa_supplicant/offchannel.c
index 6b3f83c..26d41a4 100644
--- a/wpa_supplicant/offchannel.c
+++ b/wpa_supplicant/offchannel.c
@@ -23,8 +23,29 @@
 {
 	struct wpa_supplicant *iface;
 
-	if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0)
+	if (os_memcmp(src, wpa_s->own_addr, ETH_ALEN) == 0) {
+#ifdef CONFIG_P2P
+		if (wpa_s->p2p_mgmt && wpa_s != wpa_s->parent &&
+		    wpa_s->parent->ap_iface &&
+		    os_memcmp(wpa_s->parent->own_addr,
+			      wpa_s->own_addr, ETH_ALEN) == 0 &&
+		    wpabuf_len(wpa_s->pending_action_tx) >= 2 &&
+		    *wpabuf_head_u8(wpa_s->pending_action_tx) !=
+		    WLAN_ACTION_PUBLIC) {
+			/*
+			 * When P2P Device interface has same MAC address as
+			 * the GO interface, make sure non-Public Action frames
+			 * are sent through the GO interface. The P2P Device
+			 * interface can only send Public Action frames.
+			 */
+			wpa_printf(MSG_DEBUG,
+				   "P2P: Use GO interface %s instead of interface %s for Action TX",
+				   wpa_s->parent->ifname, wpa_s->ifname);
+			return wpa_s->parent;
+		}
+#endif /* CONFIG_P2P */
 		return wpa_s;
+	}
 
 	/*
 	 * Try to find a group interface that matches with the source address.
diff --git a/wpa_supplicant/op_classes.c b/wpa_supplicant/op_classes.c
new file mode 100644
index 0000000..d23b009
--- /dev/null
+++ b/wpa_supplicant/op_classes.c
@@ -0,0 +1,325 @@
+/*
+ * Operating classes
+ * Copyright(c) 2015 Intel Deutschland GmbH
+ * Contact Information:
+ * Intel Linux Wireless <ilw@linux.intel.com>
+ * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+
+
+static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, u8 chan,
+				       unsigned int *flags)
+{
+	int i;
+
+	for (i = 0; i < mode->num_channels; i++) {
+		if (mode->channels[i].chan == chan)
+			break;
+	}
+
+	if (i == mode->num_channels ||
+	    (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED))
+		return NOT_ALLOWED;
+
+	if (flags)
+		*flags = mode->channels[i].flag;
+
+	if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
+		return NO_IR;
+
+	return ALLOWED;
+}
+
+
+static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+	u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
+	size_t i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
+		/*
+		 * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
+		 * so the center channel is 6 channels away from the start/end.
+		 */
+		if (channel >= center_channels[i] - 6 &&
+		    channel <= center_channels[i] + 6)
+			return center_channels[i];
+	}
+
+	return 0;
+}
+
+
+static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+	u8 center_chan;
+	unsigned int i;
+	unsigned int no_ir = 0;
+
+	center_chan = get_center_80mhz(mode, channel);
+	if (!center_chan)
+		return NOT_ALLOWED;
+
+	/* check all the channels are available */
+	for (i = 0; i < 4; i++) {
+		unsigned int flags;
+		u8 adj_chan = center_chan - 6 + i * 4;
+
+		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+			return NOT_ALLOWED;
+
+		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70)) ||
+		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50)) ||
+		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30)) ||
+		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10)))
+			return NOT_ALLOWED;
+
+		if (flags & HOSTAPD_CHAN_NO_IR)
+			no_ir = 1;
+	}
+
+	if (no_ir)
+		return NO_IR;
+
+	return ALLOWED;
+}
+
+
+static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel)
+{
+	u8 center_channels[] = { 50, 114 };
+	unsigned int i;
+
+	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(center_channels); i++) {
+		/*
+		 * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64),
+		 * so the center channel is 14 channels away from the start/end.
+		 */
+		if (channel >= center_channels[i] - 14 &&
+		    channel <= center_channels[i] + 14)
+			return center_channels[i];
+	}
+
+	return 0;
+}
+
+
+static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode,
+				       u8 channel)
+{
+	u8 center_chan;
+	unsigned int i;
+	unsigned int no_ir = 0;
+
+	center_chan = get_center_160mhz(mode, channel);
+	if (!center_chan)
+		return NOT_ALLOWED;
+
+	/* Check all the channels are available */
+	for (i = 0; i < 8; i++) {
+		unsigned int flags;
+		u8 adj_chan = center_chan - 14 + i * 4;
+
+		if (allow_channel(mode, adj_chan, &flags) == NOT_ALLOWED)
+			return NOT_ALLOWED;
+
+		if ((i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_150)) ||
+		    (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_130)) ||
+		    (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_110)) ||
+		    (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_90)) ||
+		    (i == 4 && !(flags & HOSTAPD_CHAN_VHT_90_70)) ||
+		    (i == 5 && !(flags & HOSTAPD_CHAN_VHT_110_50)) ||
+		    (i == 6 && !(flags & HOSTAPD_CHAN_VHT_130_30)) ||
+		    (i == 7 && !(flags & HOSTAPD_CHAN_VHT_150_10)))
+			return NOT_ALLOWED;
+
+		if (flags & HOSTAPD_CHAN_NO_IR)
+			no_ir = 1;
+	}
+
+	if (no_ir)
+		return NO_IR;
+
+	return ALLOWED;
+}
+
+
+enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
+				 u8 bw)
+{
+	unsigned int flag = 0;
+	enum chan_allowed res, res2;
+
+	res2 = res = allow_channel(mode, channel, &flag);
+	if (bw == BW40MINUS) {
+		if (!(flag & HOSTAPD_CHAN_HT40MINUS))
+			return NOT_ALLOWED;
+		res2 = allow_channel(mode, channel - 4, NULL);
+	} else if (bw == BW40PLUS) {
+		if (!(flag & HOSTAPD_CHAN_HT40PLUS))
+			return NOT_ALLOWED;
+		res2 = allow_channel(mode, channel + 4, NULL);
+	} else if (bw == BW80) {
+		/*
+		 * channel is a center channel and as such, not necessarily a
+		 * valid 20 MHz channels. Override earlier allow_channel()
+		 * result and use only the 80 MHz specific version.
+		 */
+		res2 = res = verify_80mhz(mode, channel);
+	} else if (bw == BW160) {
+		/*
+		 * channel is a center channel and as such, not necessarily a
+		 * valid 20 MHz channels. Override earlier allow_channel()
+		 * result and use only the 160 MHz specific version.
+		 */
+		res2 = res = verify_160mhz(mode, channel);
+	} else if (bw == BW80P80) {
+		/*
+		 * channel is a center channel and as such, not necessarily a
+		 * valid 20 MHz channels. Override earlier allow_channel()
+		 * result and use only the 80 MHz specific version.
+		 */
+		res2 = res = verify_80mhz(mode, channel);
+	}
+
+	if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
+		return NOT_ALLOWED;
+
+	if (res == NO_IR || res2 == NO_IR)
+		return NO_IR;
+
+	return ALLOWED;
+}
+
+
+static int wpas_op_class_supported(struct wpa_supplicant *wpa_s,
+				   const struct oper_class_map *op_class)
+{
+	int chan;
+	size_t i;
+	struct hostapd_hw_modes *mode;
+	int found;
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode);
+	if (!mode)
+		return 0;
+
+	if (op_class->op_class == 128) {
+		u8 channels[] = { 42, 58, 106, 122, 138, 155 };
+
+		for (i = 0; i < ARRAY_SIZE(channels); i++) {
+			if (verify_channel(mode, channels[i], op_class->bw) !=
+			    NOT_ALLOWED)
+				return 1;
+		}
+
+		return 0;
+	}
+
+	if (op_class->op_class == 129) {
+		/* Check if either 160 MHz channels is allowed */
+		return verify_channel(mode, 50, op_class->bw) != NOT_ALLOWED ||
+			verify_channel(mode, 114, op_class->bw) != NOT_ALLOWED;
+	}
+
+	if (op_class->op_class == 130) {
+		/* Need at least two non-contiguous 80 MHz segments */
+		found = 0;
+
+		if (verify_channel(mode, 42, op_class->bw) != NOT_ALLOWED ||
+		    verify_channel(mode, 58, op_class->bw) != NOT_ALLOWED)
+			found++;
+		if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED ||
+		    verify_channel(mode, 122, op_class->bw) != NOT_ALLOWED ||
+		    verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED)
+			found++;
+		if (verify_channel(mode, 106, op_class->bw) != NOT_ALLOWED &&
+		    verify_channel(mode, 138, op_class->bw) != NOT_ALLOWED)
+			found++;
+		if (verify_channel(mode, 155, op_class->bw) != NOT_ALLOWED)
+			found++;
+
+		if (found >= 2)
+			return 1;
+
+		return 0;
+	}
+
+	found = 0;
+	for (chan = op_class->min_chan; chan <= op_class->max_chan;
+	     chan += op_class->inc) {
+		if (verify_channel(mode, chan, op_class->bw) != NOT_ALLOWED) {
+			found = 1;
+			break;
+		}
+	}
+
+	return found;
+}
+
+
+size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
+			      size_t len)
+{
+	struct wpabuf *buf;
+	u8 op, current, chan;
+	u8 *ie_len;
+	size_t res;
+
+	/*
+	 * Assume 20 MHz channel for now.
+	 * TODO: Use the secondary channel and VHT channel width that will be
+	 * used after association.
+	 */
+	if (ieee80211_freq_to_channel_ext(freq, 0, VHT_CHANWIDTH_USE_HT,
+					  &current, &chan) == NUM_HOSTAPD_MODES)
+		return 0;
+
+	/*
+	 * Need 3 bytes for EID, length, and current operating class, plus
+	 * 1 byte for every other supported operating class.
+	 */
+	buf = wpabuf_alloc(global_op_class_size + 3);
+	if (!buf)
+		return 0;
+
+	wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES);
+	/* Will set the length later, putting a placeholder */
+	ie_len = wpabuf_put(buf, 1);
+	wpabuf_put_u8(buf, current);
+
+	for (op = 0; global_op_class[op].op_class; op++) {
+		if (wpas_op_class_supported(wpa_s, &global_op_class[op]))
+			wpabuf_put_u8(buf, global_op_class[op].op_class);
+	}
+
+	*ie_len = wpabuf_len(buf) - 2;
+	if (*ie_len < 2 || wpabuf_len(buf) > len) {
+		wpa_printf(MSG_ERROR,
+			   "Failed to add supported operating classes IE");
+		res = 0;
+	} else {
+		os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf));
+		res = wpabuf_len(buf);
+		wpa_hexdump_buf(MSG_DEBUG,
+				"Added supported operating classes IE", buf);
+	}
+
+	wpabuf_free(buf);
+	return res;
+}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index b310885..a341549 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -307,7 +307,14 @@
 		return;
 	}
 
+	if (wpa_s->clear_driver_scan_cache) {
+		wpa_printf(MSG_DEBUG,
+			   "Request driver to clear scan cache due to local BSS flush");
+		params->only_new_results = 1;
+	}
 	ret = wpa_drv_scan(wpa_s, params);
+	if (ret == 0)
+		wpa_s->curr_scan_cookie = params->scan_cookie;
 	wpa_scan_free_params(params);
 	work->ctx = NULL;
 	if (ret) {
@@ -320,6 +327,7 @@
 	os_get_reltime(&wpa_s->scan_trigger_time);
 	wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
 	wpa_s->own_scan_requested = 1;
+	wpa_s->clear_driver_scan_cache = 0;
 	wpa_s->p2p_scan_work = work;
 }
 
@@ -807,7 +815,7 @@
 					      wpa_s->own_addr);
 		} else if (!s && !go_wpa_s) {
 			if (wpas_p2p_add_group_interface(wpa_s,
-							 WPA_IF_P2P_GO) < 0) {
+							 WPA_IF_P2P_GROUP) < 0) {
 				wpa_printf(MSG_ERROR,
 					   "P2P: Failed to allocate a new interface for the group");
 				return P2PS_SETUP_NONE;
@@ -1076,7 +1084,7 @@
 		   "go_dev_addr=" MACSTR,
 		   MAC2STR(bssid), group_capab, MAC2STR(go_dev_addr));
 
-	return group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+	return !!(group_capab & P2P_GROUP_CAPAB_PERSISTENT_GROUP);
 }
 
 
@@ -1301,7 +1309,6 @@
 	int client;
 	int persistent;
 	u8 go_dev_addr[ETH_ALEN];
-	int network_id = -1;
 
 	/*
 	 * This callback is likely called for the main interface. Update wpa_s
@@ -1376,16 +1383,15 @@
 	}
 
 	if (persistent)
-		network_id = wpas_p2p_store_persistent_group(wpa_s->p2pdev,
-							     ssid, go_dev_addr);
+		wpas_p2p_store_persistent_group(wpa_s->p2pdev,
+						ssid, go_dev_addr);
 	else {
 		os_free(wpa_s->global->add_psk);
 		wpa_s->global->add_psk = NULL;
 	}
-	if (network_id < 0 && ssid)
-		network_id = ssid->id;
+
 	if (!client) {
-		wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+		wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 0, NULL);
 		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
 	}
 }
@@ -1750,7 +1756,6 @@
 	struct wpa_supplicant *wpa_s = ctx;
 	struct p2p_go_neg_results *params = data;
 	struct wpa_ssid *ssid;
-	int network_id = -1;
 
 	wpa_s->ap_configured_cb = NULL;
 	wpa_s->ap_configured_cb_ctx = NULL;
@@ -1797,14 +1802,15 @@
 
 		os_get_reltime(&wpa_s->global->p2p_go_wait_client);
 		if (params->persistent_group) {
-			network_id = wpas_p2p_store_persistent_group(
+			wpas_p2p_store_persistent_group(
 				wpa_s->p2pdev, ssid,
 				wpa_s->global->p2p_dev_addr);
 			wpas_p2p_add_psk_list(wpa_s, ssid);
 		}
-		if (network_id < 0)
-			network_id = ssid->id;
-		wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+
+		wpas_notify_p2p_group_started(wpa_s, ssid,
+					      params->persistent_group, 0,
+					      NULL);
 		wpas_p2p_cross_connect_setup(wpa_s);
 		wpas_p2p_set_group_idle_timeout(wpa_s);
 
@@ -1884,7 +1890,7 @@
 	wpa_config_set_network_defaults(ssid);
 	ssid->temporary = 1;
 	ssid->p2p_group = 1;
-	ssid->p2p_persistent_group = params->persistent_group;
+	ssid->p2p_persistent_group = !!params->persistent_group;
 	ssid->mode = group_formation ? WPAS_MODE_P2P_GROUP_FORMATION :
 		WPAS_MODE_P2P_GO;
 	ssid->frequency = params->freq;
@@ -1951,7 +1957,12 @@
 	d = dst->conf;
 	s = src->conf;
 
-#define C(n) if (s->n) d->n = os_strdup(s->n)
+#define C(n)                            \
+do {                                    \
+	if (s->n && !d->n)              \
+		d->n = os_strdup(s->n); \
+} while (0)
+
 	C(device_name);
 	C(manufacturer);
 	C(model_name);
@@ -1979,7 +1990,10 @@
 	d->disable_scan_offload = s->disable_scan_offload;
 	d->passive_scan = s->passive_scan;
 
-	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) {
+	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
+	    !d->wps_nfc_pw_from_config) {
+		wpabuf_free(d->wps_nfc_dh_privkey);
+		wpabuf_free(d->wps_nfc_dh_pubkey);
 		d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
 		d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
 	}
@@ -1987,23 +2001,6 @@
 }
 
 
-static void wpas_p2p_clone_config_dh(struct wpa_supplicant *dst,
-				     const struct wpa_supplicant *src)
-{
-	struct wpa_config *d;
-	const struct wpa_config *s;
-
-	d = dst->conf;
-	s = src->conf;
-
-	if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey &&
-	    !d->wps_nfc_dh_privkey && !d->wps_nfc_dh_pubkey) {
-		d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
-		d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
-	}
-}
-
-
 static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s,
 				      char *ifname, size_t len)
 {
@@ -2255,7 +2252,7 @@
 		group_wpa_s = wpa_s->parent;
 		wpa_s->global->p2p_group_formation = group_wpa_s;
 		if (group_wpa_s != wpa_s)
-			wpas_p2p_clone_config_dh(group_wpa_s, wpa_s);
+			wpas_p2p_clone_config(group_wpa_s, wpa_s);
 	}
 
 	group_wpa_s->p2p_in_provisioning = 1;
@@ -2298,6 +2295,8 @@
 			   const struct p2p_peer_info *info,
 			   int new_device)
 {
+	u8 *wfd_dev_info = NULL;
+	u8 wfd_dev_info_len = 0;
 #ifndef CONFIG_NO_STDOUT_DEBUG
 	struct wpa_supplicant *wpa_s = ctx;
 	char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -2306,6 +2305,12 @@
 #ifdef CONFIG_WIFI_DISPLAY
 	wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems,
 						    WFD_SUBELEM_DEVICE_INFO);
+	if (wfd_dev_info_hex) {
+		wfd_dev_info_len = strlen(wfd_dev_info_hex) / 2;
+		wfd_dev_info = os_zalloc(wfd_dev_info_len);
+		// Only used for notification, so not handling error.
+		hexstr2bin(wfd_dev_info_hex, wfd_dev_info, wfd_dev_info_len);
+	}
 #endif /* CONFIG_WIFI_DISPLAY */
 
 	if (info->p2ps_instance) {
@@ -2372,7 +2377,9 @@
 	os_free(wfd_dev_info_hex);
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-	wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
+	wpas_notify_p2p_device_found(ctx, addr, info, wfd_dev_info,
+				     wfd_dev_info_len, new_device);
+	os_free(wfd_dev_info);
 }
 
 
@@ -3018,12 +3025,31 @@
 			   MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len));
 		if (s) {
 			int go = s->mode == WPAS_MODE_P2P_GO;
+			if (go) {
+				wpa_msg_global(wpa_s, MSG_INFO,
+					       P2P_EVENT_INVITATION_ACCEPTED
+					       "sa=" MACSTR
+					       " persistent=%d freq=%d",
+					       MAC2STR(sa), s->id, op_freq);
+			} else {
+				wpa_msg_global(wpa_s, MSG_INFO,
+					       P2P_EVENT_INVITATION_ACCEPTED
+					       "sa=" MACSTR
+					       " persistent=%d",
+					       MAC2STR(sa), s->id);
+			}
 			wpas_p2p_group_add_persistent(
 				wpa_s, s, go, 0, op_freq, 0, 0, 0, 0, NULL,
 				go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0,
 				1);
 		} else if (bssid) {
 			wpa_s->user_initiated_pd = 0;
+			wpa_msg_global(wpa_s, MSG_INFO,
+				       P2P_EVENT_INVITATION_ACCEPTED
+				       "sa=" MACSTR " go_dev_addr=" MACSTR
+				       " bssid=" MACSTR " unknown-network",
+				       MAC2STR(sa), MAC2STR(go_dev_addr),
+				       MAC2STR(bssid));
 			wpas_p2p_join(wpa_s, bssid, go_dev_addr,
 				      wpa_s->p2p_wps_method, 0, op_freq,
 				      ssid, ssid_len);
@@ -3324,10 +3350,6 @@
 }
 
 
-enum chan_allowed {
-	NOT_ALLOWED, NO_IR, ALLOWED
-};
-
 static int has_channel(struct wpa_global *global,
 		       struct hostapd_hw_modes *mode, u8 chan, int *flags)
 {
@@ -3362,7 +3384,7 @@
 				     u8 channel)
 {
 	u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
-	unsigned int i;
+	size_t i;
 
 	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
 		return 0;
@@ -4996,6 +5018,12 @@
 	params.extra_ies = wpabuf_head(ies);
 	params.extra_ies_len = wpabuf_len(ies);
 
+	if (wpa_s->clear_driver_scan_cache) {
+		wpa_printf(MSG_DEBUG,
+			   "Request driver to clear scan cache due to local BSS flush");
+		params.only_new_results = 1;
+	}
+
 	/*
 	 * Run a scan to update BSS table and start Provision Discovery once
 	 * the new scan results become available.
@@ -5005,6 +5033,7 @@
 		os_get_reltime(&wpa_s->scan_trigger_time);
 		wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
 		wpa_s->own_scan_requested = 1;
+		wpa_s->clear_driver_scan_cache = 0;
 	}
 
 	wpabuf_free(ies);
@@ -5229,9 +5258,11 @@
 		if (!res && max_pref_freq > 0) {
 			*num_pref_freq = max_pref_freq;
 			i = 0;
-			while (wpas_p2p_disallowed_freq(wpa_s->global,
-							pref_freq_list[i]) &&
-			       i < *num_pref_freq) {
+			while (i < *num_pref_freq &&
+			       (!p2p_supported_freq(wpa_s->global->p2p,
+						    pref_freq_list[i]) ||
+				wpas_p2p_disallowed_freq(wpa_s->global,
+							 pref_freq_list[i]))) {
 				wpa_printf(MSG_DEBUG,
 					   "P2P: preferred_freq_list[%d]=%d is disallowed",
 					   i, pref_freq_list[i]);
@@ -5373,6 +5404,9 @@
 			wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0';
 		wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
 			   wpa_s->p2p_pin);
+	} else if (wps_method == WPS_P2PS) {
+		/* Force the P2Ps default PIN to be used */
+		os_strlcpy(wpa_s->p2p_pin, "12345670", sizeof(wpa_s->p2p_pin));
 	} else
 		wpa_s->p2p_pin[0] = '\0';
 
@@ -5591,9 +5625,9 @@
 						 &size, pref_freq_list);
 		if (!res && size > 0) {
 			i = 0;
-			while (wpas_p2p_disallowed_freq(wpa_s->global,
-							pref_freq_list[i]) &&
-			       i < size) {
+			while (i < size &&
+			       wpas_p2p_disallowed_freq(wpa_s->global,
+							pref_freq_list[i])) {
 				wpa_printf(MSG_DEBUG,
 					   "P2P: preferred_freq_list[%d]=%d is disallowed",
 					   i, pref_freq_list[i]);
@@ -5994,7 +6028,7 @@
 			"P2P: Use primary interface for group operations");
 		wpa_s->p2p_first_connection_timeout = 0;
 		if (wpa_s != wpa_s->p2pdev)
-			wpas_p2p_clone_config_dh(wpa_s, wpa_s->p2pdev);
+			wpas_p2p_clone_config(wpa_s, wpa_s->p2pdev);
 		return wpa_s;
 	}
 
@@ -6308,7 +6342,8 @@
 	struct p2p_group *group;
 	struct p2p_group_config *cfg;
 
-	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
+	    !ssid->p2p_group)
 		return NULL;
 
 	cfg = os_zalloc(sizeof(*cfg));
@@ -6621,6 +6656,12 @@
 	if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
 		return -1;
 
+	if (wpa_s->p2p_lo_started) {
+		wpa_printf(MSG_DEBUG,
+			"P2P: Cannot start P2P listen, it is offloaded");
+		return -1;
+	}
+
 	wpa_supplicant_cancel_sched_scan(wpa_s);
 	wpas_p2p_clear_pending_action_tx(wpa_s);
 
@@ -6694,7 +6735,7 @@
 		return 0;
 
 	switch (p2p_probe_req_rx(wpa_s->global->p2p, addr, dst, bssid,
-				 ie, ie_len, rx_freq)) {
+				 ie, ie_len, rx_freq, wpa_s->p2p_lo_started)) {
 	case P2P_PREQ_NOT_P2P:
 		wpas_notify_preq(wpa_s, addr, dst, bssid, ie, ie_len,
 				 ssi_signal);
@@ -6927,7 +6968,6 @@
 {
 	struct wpa_ssid *ssid = wpa_s->current_ssid;
 	u8 go_dev_addr[ETH_ALEN];
-	int network_id = -1;
 	int persistent;
 	int freq;
 	u8 ip[3 * 4];
@@ -6986,11 +7026,10 @@
 			       ip_addr);
 
 	if (persistent)
-		network_id = wpas_p2p_store_persistent_group(wpa_s->p2pdev,
-							     ssid, go_dev_addr);
-	if (network_id < 0)
-		network_id = ssid->id;
-	wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
+		wpas_p2p_store_persistent_group(wpa_s->p2pdev,
+						ssid, go_dev_addr);
+
+	wpas_notify_p2p_group_started(wpa_s, ssid, persistent, 1, ip);
 }
 
 
@@ -8371,17 +8410,17 @@
 		return -1;
 	}
 
-	if (wpa_s->parent->p2p_oob_dev_pw_id !=
+	if (wpa_s->p2pdev->p2p_oob_dev_pw_id !=
 	    DEV_PW_NFC_CONNECTION_HANDOVER &&
-	    !wpa_s->parent->p2p_oob_dev_pw) {
+	    !wpa_s->p2pdev->p2p_oob_dev_pw) {
 		wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
 		return -1;
 	}
 	res = wpas_ap_wps_add_nfc_pw(
-		wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
-		wpa_s->parent->p2p_oob_dev_pw,
-		wpa_s->parent->p2p_peer_oob_pk_hash_known ?
-		wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+		wpa_s, wpa_s->p2pdev->p2p_oob_dev_pw_id,
+		wpa_s->p2pdev->p2p_oob_dev_pw,
+		wpa_s->p2pdev->p2p_peer_oob_pk_hash_known ?
+		wpa_s->p2pdev->p2p_peer_oob_pubkey_hash : NULL);
 	if (res)
 		return res;
 
@@ -8399,16 +8438,16 @@
 
 	wpa_s->global->p2p_invite_group = wpa_s;
 	persistent = ssid->p2p_persistent_group &&
-		wpas_p2p_get_persistent(wpa_s->parent,
+		wpas_p2p_get_persistent(wpa_s->p2pdev,
 					params->peer->p2p_device_addr,
 					ssid->ssid, ssid->ssid_len);
-	wpa_s->parent->pending_invite_ssid_id = -1;
+	wpa_s->p2pdev->pending_invite_ssid_id = -1;
 
 	return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
 			  P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
 			  ssid->ssid, ssid->ssid_len, ssid->frequency,
 			  wpa_s->global->p2p_dev_addr, persistent, 0,
-			  wpa_s->parent->p2p_oob_dev_pw_id);
+			  wpa_s->p2pdev->p2p_oob_dev_pw_id);
 }
 
 
@@ -9197,3 +9236,86 @@
 		wpa_s->ap_iface->bss[0]->p2p_group = NULL;
 	wpas_p2p_group_deinit(wpa_s);
 }
+
+
+int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
+		      unsigned int period, unsigned int interval,
+		      unsigned int count)
+{
+	struct p2p_data *p2p = wpa_s->global->p2p;
+	u8 *device_types;
+	size_t dev_types_len;
+	struct wpabuf *buf;
+	int ret;
+
+	if (wpa_s->p2p_lo_started) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P Listen offload is already started");
+		return 0;
+	}
+
+	if (wpa_s->global->p2p == NULL ||
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_LISTEN_OFFLOAD)) {
+		wpa_printf(MSG_DEBUG, "P2P: Listen offload not supported");
+		return -1;
+	}
+
+	if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+		wpa_printf(MSG_ERROR, "P2P: Input channel not supported: %u",
+			   freq);
+		return -1;
+	}
+
+	/* Get device type */
+	dev_types_len = (wpa_s->conf->num_sec_device_types + 1) *
+		WPS_DEV_TYPE_LEN;
+	device_types = os_malloc(dev_types_len);
+	if (!device_types)
+		return -1;
+	os_memcpy(device_types, wpa_s->conf->device_type, WPS_DEV_TYPE_LEN);
+	os_memcpy(&device_types[WPS_DEV_TYPE_LEN], wpa_s->conf->sec_device_type,
+		  wpa_s->conf->num_sec_device_types * WPS_DEV_TYPE_LEN);
+
+	/* Get Probe Response IE(s) */
+	buf = p2p_build_probe_resp_template(p2p, freq);
+	if (!buf) {
+		os_free(device_types);
+		return -1;
+	}
+
+	ret = wpa_drv_p2p_lo_start(wpa_s, freq, period, interval, count,
+				   device_types, dev_types_len,
+				   wpabuf_mhead_u8(buf), wpabuf_len(buf));
+	if (ret < 0)
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Failed to start P2P listen offload");
+
+	os_free(device_types);
+	wpabuf_free(buf);
+
+	if (ret == 0) {
+		wpa_s->p2p_lo_started = 1;
+
+		/* Stop current P2P listen if any */
+		wpas_stop_listen(wpa_s);
+	}
+
+	return ret;
+}
+
+
+int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s)
+{
+	int ret;
+
+	if (!wpa_s->p2p_lo_started)
+		return 0;
+
+	ret = wpa_drv_p2p_lo_stop(wpa_s);
+	if (ret < 0)
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"P2P: Failed to stop P2P listen offload");
+
+	wpa_s->p2p_lo_started = 0;
+	return ret;
+}
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 6a770d2..63910d1 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -207,6 +207,10 @@
 void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 			 struct wps_event_fail *fail);
 int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+int wpas_p2p_lo_start(struct wpa_supplicant *wpa_s, unsigned int freq,
+		      unsigned int period, unsigned int interval,
+		      unsigned int count);
+int wpas_p2p_lo_stop(struct wpa_supplicant *wpa_s);
 
 #else /* CONFIG_P2P */
 
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
index f4bba98..6ae239f 100644
--- a/wpa_supplicant/preauth_test.c
+++ b/wpa_supplicant/preauth_test.c
@@ -143,7 +143,7 @@
 }
 
 
-static int wpa_supplicant_add_pmkid(void *wpa_s,
+static int wpa_supplicant_add_pmkid(void *wpa_s, void *network_ctx,
 				    const u8 *bssid, const u8 *pmkid)
 {
 	printf("%s - not implemented\n", __func__);
@@ -151,7 +151,7 @@
 }
 
 
-static int wpa_supplicant_remove_pmkid(void *wpa_s,
+static int wpa_supplicant_remove_pmkid(void *wpa_s, void *network_ctx,
 				       const u8 *bssid, const u8 *pmkid)
 {
 	printf("%s - not implemented\n", __func__);
diff --git a/wpa_supplicant/rrm.c b/wpa_supplicant/rrm.c
new file mode 100644
index 0000000..5be917c
--- /dev/null
+++ b/wpa_supplicant/rrm.c
@@ -0,0 +1,1430 @@
+/*
+ * wpa_supplicant - Radio Measurements
+ * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+#include "scan.h"
+#include "p2p_supplicant.h"
+
+
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+	struct rrm_data *rrm = data;
+
+	if (!rrm->notify_neighbor_rep) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Unexpected neighbor report timeout");
+		return;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+	rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+	rrm->notify_neighbor_rep = NULL;
+	rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
+/*
+ * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
+ * @wpa_s: Pointer to wpa_supplicant
+ */
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->rrm.rrm_used = 0;
+
+	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+			     NULL);
+	if (wpa_s->rrm.notify_neighbor_rep)
+		wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+	wpa_s->rrm.next_neighbor_rep_token = 1;
+	wpas_clear_beacon_rep_data(wpa_s);
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+				   const u8 *report, size_t report_len)
+{
+	struct wpabuf *neighbor_rep;
+
+	wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+	if (report_len < 1)
+		return;
+
+	if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Discarding neighbor report with token %d (expected %d)",
+			   report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+		return;
+	}
+
+	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+			     NULL);
+
+	if (!wpa_s->rrm.notify_neighbor_rep) {
+		wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+		return;
+	}
+
+	/* skipping the first byte, which is only an id (dialog token) */
+	neighbor_rep = wpabuf_alloc(report_len - 1);
+	if (!neighbor_rep) {
+		wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+		return;
+	}
+	wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+		   report[0]);
+	wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+				       neighbor_rep);
+	wpa_s->rrm.notify_neighbor_rep = NULL;
+	wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+/* Workaround different, undefined for Windows, error codes used here */
+#define ENOTCONN -1
+#define EOPNOTSUPP -1
+#define ECANCELED -1
+#endif
+
+/* Measurement Request element + Location Subject + Maximum Age subelement */
+#define MEASURE_REQUEST_LCI_LEN (3 + 1 + 4)
+/* Measurement Request element + Location Civic Request */
+#define MEASURE_REQUEST_CIVIC_LEN (3 + 5)
+
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
+ *	  is sent in the request.
+ * @lci: if set, neighbor request will include LCI request
+ * @civic: if set, neighbor request will include civic location request
+ * @cb: Callback function to be called once the requested report arrives, or
+ *	timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ *	In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ *	the requester's responsibility to free it.
+ *	In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+				       const struct wpa_ssid_value *ssid,
+				       int lci, int civic,
+				       void (*cb)(void *ctx,
+						  struct wpabuf *neighbor_rep),
+				       void *cb_ctx)
+{
+	struct wpabuf *buf;
+	const u8 *rrm_ie;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+		wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+		return -ENOTCONN;
+	}
+
+	if (!wpa_s->rrm.rrm_used) {
+		wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+		return -EOPNOTSUPP;
+	}
+
+	rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+				WLAN_EID_RRM_ENABLED_CAPABILITIES);
+	if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+	    !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: No network support for Neighbor Report.");
+		return -EOPNOTSUPP;
+	}
+
+	/* Refuse if there's a live request */
+	if (wpa_s->rrm.notify_neighbor_rep) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Currently handling previous Neighbor Report.");
+		return -EBUSY;
+	}
+
+	/* 3 = action category + action code + dialog token */
+	buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0) +
+			   (lci ? 2 + MEASURE_REQUEST_LCI_LEN : 0) +
+			   (civic ? 2 + MEASURE_REQUEST_CIVIC_LEN : 0));
+	if (buf == NULL) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Failed to allocate Neighbor Report Request");
+		return -ENOMEM;
+	}
+
+	wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+		   (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+		   wpa_s->rrm.next_neighbor_rep_token);
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+	wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+	if (ssid) {
+		wpabuf_put_u8(buf, WLAN_EID_SSID);
+		wpabuf_put_u8(buf, ssid->ssid_len);
+		wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
+	}
+
+	if (lci) {
+		/* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
+		wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+		wpabuf_put_u8(buf, MEASURE_REQUEST_LCI_LEN);
+
+		/*
+		 * Measurement token; nonzero number that is unique among the
+		 * Measurement Request elements in a particular frame.
+		 */
+		wpabuf_put_u8(buf, 1); /* Measurement Token */
+
+		/*
+		 * Parallel, Enable, Request, and Report bits are 0, Duration is
+		 * reserved.
+		 */
+		wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+		wpabuf_put_u8(buf, MEASURE_TYPE_LCI); /* Measurement Type */
+
+		/* IEEE P802.11-REVmc/D5.0 9.4.2.21.10 - LCI request */
+		/* Location Subject */
+		wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+
+		/* Optional Subelements */
+		/*
+		 * IEEE P802.11-REVmc/D5.0 Figure 9-170
+		 * The Maximum Age subelement is required, otherwise the AP can
+		 * send only data that was determined after receiving the
+		 * request. Setting it here to unlimited age.
+		 */
+		wpabuf_put_u8(buf, LCI_REQ_SUBELEM_MAX_AGE);
+		wpabuf_put_u8(buf, 2);
+		wpabuf_put_le16(buf, 0xffff);
+	}
+
+	if (civic) {
+		/* IEEE P802.11-REVmc/D5.0 9.4.2.21 */
+		wpabuf_put_u8(buf, WLAN_EID_MEASURE_REQUEST);
+		wpabuf_put_u8(buf, MEASURE_REQUEST_CIVIC_LEN);
+
+		/*
+		 * Measurement token; nonzero number that is unique among the
+		 * Measurement Request elements in a particular frame.
+		 */
+		wpabuf_put_u8(buf, 2); /* Measurement Token */
+
+		/*
+		 * Parallel, Enable, Request, and Report bits are 0, Duration is
+		 * reserved.
+		 */
+		wpabuf_put_u8(buf, 0); /* Measurement Request Mode */
+		/* Measurement Type */
+		wpabuf_put_u8(buf, MEASURE_TYPE_LOCATION_CIVIC);
+
+		/* IEEE P802.11-REVmc/D5.0 9.4.2.21.14:
+		 * Location Civic request */
+		/* Location Subject */
+		wpabuf_put_u8(buf, LOCATION_SUBJECT_REMOTE);
+		wpabuf_put_u8(buf, 0); /* Civic Location Type: IETF RFC 4776 */
+		/* Location Service Interval Units: Seconds */
+		wpabuf_put_u8(buf, 0);
+		/* Location Service Interval: 0 - Only one report is requested
+		 */
+		wpabuf_put_le16(buf, 0);
+		/* No optional subelements */
+	}
+
+	wpa_s->rrm.next_neighbor_rep_token++;
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Failed to send Neighbor Report Request");
+		wpabuf_free(buf);
+		return -ECANCELED;
+	}
+
+	wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+	wpa_s->rrm.notify_neighbor_rep = cb;
+	eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+			       wpas_rrm_neighbor_rep_timeout_handler,
+			       &wpa_s->rrm, NULL);
+
+	wpabuf_free(buf);
+	return 0;
+}
+
+
+static int wpas_rrm_report_elem(struct wpabuf **buf, u8 token, u8 mode, u8 type,
+				const u8 *data, size_t data_len)
+{
+	if (wpabuf_resize(buf, 5 + data_len))
+		return -1;
+
+	wpabuf_put_u8(*buf, WLAN_EID_MEASURE_REPORT);
+	wpabuf_put_u8(*buf, 3 + data_len);
+	wpabuf_put_u8(*buf, token);
+	wpabuf_put_u8(*buf, mode);
+	wpabuf_put_u8(*buf, type);
+
+	if (data_len)
+		wpabuf_put_data(*buf, data, data_len);
+
+	return 0;
+}
+
+
+static int
+wpas_rrm_build_lci_report(struct wpa_supplicant *wpa_s,
+			  const struct rrm_measurement_request_element *req,
+			  struct wpabuf **buf)
+{
+	u8 subject;
+	u16 max_age = 0;
+	struct os_reltime t, diff;
+	unsigned long diff_l;
+	const u8 *subelem;
+	const u8 *request = req->variable;
+	size_t len = req->len - 3;
+
+	if (len < 1)
+		return -1;
+
+	if (!wpa_s->lci)
+		goto reject;
+
+	subject = *request++;
+	len--;
+
+	wpa_printf(MSG_DEBUG, "Measurement request location subject=%u",
+		   subject);
+
+	if (subject != LOCATION_SUBJECT_REMOTE) {
+		wpa_printf(MSG_INFO,
+			   "Not building LCI report - bad location subject");
+		return 0;
+	}
+
+	/* Subelements are formatted exactly like elements */
+	wpa_hexdump(MSG_DEBUG, "LCI request subelements", request, len);
+	subelem = get_ie(request, len, LCI_REQ_SUBELEM_MAX_AGE);
+	if (subelem && subelem[1] == 2)
+		max_age = WPA_GET_LE16(subelem + 2);
+
+	if (os_get_reltime(&t))
+		goto reject;
+
+	os_reltime_sub(&t, &wpa_s->lci_time, &diff);
+	/* LCI age is calculated in 10th of a second units. */
+	diff_l = diff.sec * 10 + diff.usec / 100000;
+
+	if (max_age != 0xffff && max_age < diff_l)
+		goto reject;
+
+	if (wpas_rrm_report_elem(buf, req->token,
+				 MEASUREMENT_REPORT_MODE_ACCEPT, req->type,
+				 wpabuf_head_u8(wpa_s->lci),
+				 wpabuf_len(wpa_s->lci)) < 0) {
+		wpa_printf(MSG_DEBUG, "Failed to add LCI report element");
+		return -1;
+	}
+
+	return 0;
+
+reject:
+	if (wpas_rrm_report_elem(buf, req->token,
+				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
+				 req->type, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void wpas_rrm_send_msr_report_mpdu(struct wpa_supplicant *wpa_s,
+					  const u8 *data, size_t len)
+{
+	struct wpabuf *report = wpabuf_alloc(len + 3);
+
+	if (!report)
+		return;
+
+	wpabuf_put_u8(report, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(report, WLAN_RRM_RADIO_MEASUREMENT_REPORT);
+	wpabuf_put_u8(report, wpa_s->rrm.token);
+
+	wpabuf_put_data(report, data, len);
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(report), wpabuf_len(report), 0)) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Radio measurement report failed: Sending Action frame failed");
+	}
+
+	wpabuf_free(report);
+}
+
+
+static void wpas_rrm_send_msr_report(struct wpa_supplicant *wpa_s,
+				     struct wpabuf *buf)
+{
+	int len = wpabuf_len(buf);
+	const u8 *pos = wpabuf_head_u8(buf), *next = pos;
+
+#define MPDU_REPORT_LEN (int) (IEEE80211_MAX_MMPDU_SIZE - IEEE80211_HDRLEN - 3)
+
+	while (len) {
+		int send_len = (len > MPDU_REPORT_LEN) ? next - pos : len;
+
+		if (send_len == len ||
+		    (send_len + next[1] + 2) > MPDU_REPORT_LEN) {
+			wpas_rrm_send_msr_report_mpdu(wpa_s, pos, send_len);
+			len -= send_len;
+			pos = next;
+		}
+
+		next += next[1] + 2;
+	}
+#undef MPDU_REPORT_LEN
+}
+
+
+static int wpas_add_channel(u8 op_class, u8 chan, u8 num_primary_channels,
+			    int *freqs)
+{
+	size_t i;
+
+	for (i = 0; i < num_primary_channels; i++) {
+		u8 primary_chan = chan - (2 * num_primary_channels - 2) + i * 4;
+
+		freqs[i] = ieee80211_chan_to_freq(NULL, op_class, primary_chan);
+		/* ieee80211_chan_to_freq() is not really meant for this
+		 * conversion of 20 MHz primary channel numbers for wider VHT
+		 * channels, so handle those as special cases here for now. */
+		if (freqs[i] < 0 &&
+		    (op_class == 128 || op_class == 129 || op_class == 130))
+			freqs[i] = 5000 + 5 * primary_chan;
+		if (freqs[i] < 0) {
+			wpa_printf(MSG_DEBUG,
+				   "Beacon Report: Invalid channel %u",
+				   chan);
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
+static int * wpas_add_channels(const struct oper_class_map *op,
+			       struct hostapd_hw_modes *mode, int active,
+			       const u8 *channels, const u8 size)
+{
+	int *freqs, *next_freq;
+	u8 num_primary_channels, i;
+	u8 num_chans;
+
+	num_chans = channels ? size :
+		(op->max_chan - op->min_chan) / op->inc + 1;
+
+	if (op->bw == BW80 || op->bw == BW80P80)
+		num_primary_channels = 4;
+	else if (op->bw == BW160)
+		num_primary_channels = 8;
+	else
+		num_primary_channels = 1;
+
+	/* one extra place for the zero-terminator */
+	freqs = os_calloc(num_chans * num_primary_channels + 1, sizeof(*freqs));
+	if (!freqs) {
+		wpa_printf(MSG_ERROR,
+			   "Beacon Report: Failed to allocate freqs array");
+		return NULL;
+	}
+
+	next_freq = freqs;
+	for  (i = 0; i < num_chans; i++) {
+		u8 chan = channels ? channels[i] : op->min_chan + i * op->inc;
+		enum chan_allowed res = verify_channel(mode, chan, op->bw);
+
+		if (res == NOT_ALLOWED || (res == NO_IR && active))
+			continue;
+
+		if (wpas_add_channel(op->op_class, chan, num_primary_channels,
+				     next_freq) < 0) {
+			os_free(freqs);
+			return NULL;
+		}
+
+		next_freq += num_primary_channels;
+	}
+
+	if (!freqs[0]) {
+		os_free(freqs);
+		return NULL;
+	}
+
+	return freqs;
+}
+
+
+static int * wpas_op_class_freqs(const struct oper_class_map *op,
+				 struct hostapd_hw_modes *mode, int active)
+{
+	u8 channels_80mhz[] = { 42, 58, 106, 122, 138, 155 };
+	u8 channels_160mhz[] = { 50, 114 };
+
+	/*
+	 * When adding all channels in the operating class, 80 + 80 MHz
+	 * operating classes are like 80 MHz channels because we add all valid
+	 * channels anyway.
+	 */
+	if (op->bw == BW80 || op->bw == BW80P80)
+		return wpas_add_channels(op, mode, active, channels_80mhz,
+					 ARRAY_SIZE(channels_80mhz));
+
+	if (op->bw == BW160)
+		return wpas_add_channels(op, mode, active, channels_160mhz,
+					 ARRAY_SIZE(channels_160mhz));
+
+	return wpas_add_channels(op, mode, active, NULL, 0);
+}
+
+
+static int * wpas_channel_report_freqs(struct wpa_supplicant *wpa_s, int active,
+				       const char *country, const u8 *subelems,
+				       size_t len)
+{
+	int *freqs = NULL, *new_freqs;
+	const u8 *end = subelems + len;
+
+	while (end - subelems > 2) {
+		const struct oper_class_map *op;
+		const u8 *ap_chan_elem, *pos;
+		u8 left;
+		struct hostapd_hw_modes *mode;
+
+		ap_chan_elem = get_ie(subelems, end - subelems,
+				      WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL);
+		if (!ap_chan_elem)
+			break;
+		pos = ap_chan_elem + 2;
+		left = ap_chan_elem[1];
+		if (left < 1)
+			break;
+		subelems = ap_chan_elem + 2 + left;
+
+		op = get_oper_class(country, *pos);
+		if (!op) {
+			wpa_printf(MSG_DEBUG,
+				   "Beacon request: unknown operating class in AP Channel Report subelement %u",
+				   *pos);
+			goto out;
+		}
+		pos++;
+		left--;
+
+		mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode);
+		if (!mode)
+			continue;
+
+		/*
+		 * For 80 + 80 MHz operating classes, this AP Channel Report
+		 * element should be followed by another element specifying
+		 * the second 80 MHz channel. For now just add this 80 MHz
+		 * channel, the second 80 MHz channel will be added when the
+		 * next element is parsed.
+		 * TODO: Verify that this AP Channel Report element is followed
+		 * by a corresponding AP Channel Report element as specified in
+		 * IEEE Std 802.11-2016, 11.11.9.1.
+		 */
+		new_freqs = wpas_add_channels(op, mode, active, pos, left);
+		if (new_freqs)
+			int_array_concat(&freqs, new_freqs);
+
+		os_free(new_freqs);
+	}
+
+	return freqs;
+out:
+	os_free(freqs);
+	return NULL;
+}
+
+
+static int * wpas_beacon_request_freqs(struct wpa_supplicant *wpa_s,
+				       u8 op_class, u8 chan, int active,
+				       const u8 *subelems, size_t len)
+{
+	int *freqs = NULL, *ext_freqs = NULL;
+	struct hostapd_hw_modes *mode;
+	const char *country = NULL;
+	const struct oper_class_map *op;
+	const u8 *elem;
+
+	if (!wpa_s->current_bss)
+		return NULL;
+	elem = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
+	if (elem && elem[1] >= 2)
+		country = (const char *) (elem + 2);
+
+	op = get_oper_class(country, op_class);
+	if (!op) {
+		wpa_printf(MSG_DEBUG,
+			   "Beacon request: invalid operating class %d",
+			   op_class);
+		return NULL;
+	}
+
+	mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op->mode);
+	if (!mode)
+		return NULL;
+
+	switch (chan) {
+	case 0:
+		freqs = wpas_op_class_freqs(op, mode, active);
+		if (!freqs)
+			return NULL;
+		break;
+	case 255:
+		/* freqs will be added from AP channel subelements */
+		break;
+	default:
+		freqs = wpas_add_channels(op, mode, active, &chan, 1);
+		if (!freqs)
+			return NULL;
+		break;
+	}
+
+	ext_freqs = wpas_channel_report_freqs(wpa_s, active, country, subelems,
+					      len);
+	if (ext_freqs) {
+		int_array_concat(&freqs, ext_freqs);
+		os_free(ext_freqs);
+	}
+
+	return freqs;
+}
+
+
+static int wpas_get_op_chan_phy(int freq, const u8 *ies, size_t ies_len,
+				u8 *op_class, u8 *chan, u8 *phy_type)
+{
+	const u8 *ie;
+	int sec_chan = 0, vht = 0;
+	struct ieee80211_ht_operation *ht_oper = NULL;
+	struct ieee80211_vht_operation *vht_oper = NULL;
+	u8 seg0, seg1;
+
+	ie = get_ie(ies, ies_len, WLAN_EID_HT_OPERATION);
+	if (ie && ie[1] >= sizeof(struct ieee80211_ht_operation)) {
+		u8 sec_chan_offset;
+
+		ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
+		sec_chan_offset = ht_oper->ht_param &
+			HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+		if (sec_chan_offset == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+			sec_chan = 1;
+		else if (sec_chan_offset ==
+			 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+			sec_chan = -1;
+	}
+
+	ie = get_ie(ies, ies_len, WLAN_EID_VHT_OPERATION);
+	if (ie && ie[1] >= sizeof(struct ieee80211_vht_operation)) {
+		vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
+
+		switch (vht_oper->vht_op_info_chwidth) {
+		case 1:
+			seg0 = vht_oper->vht_op_info_chan_center_freq_seg0_idx;
+			seg1 = vht_oper->vht_op_info_chan_center_freq_seg1_idx;
+			if (seg1 && abs(seg1 - seg0) == 8)
+				vht = VHT_CHANWIDTH_160MHZ;
+			else if (seg1)
+				vht = VHT_CHANWIDTH_80P80MHZ;
+			else
+				vht = VHT_CHANWIDTH_80MHZ;
+			break;
+		case 2:
+			vht = VHT_CHANWIDTH_160MHZ;
+			break;
+		case 3:
+			vht = VHT_CHANWIDTH_80P80MHZ;
+			break;
+		default:
+			vht = VHT_CHANWIDTH_USE_HT;
+			break;
+		}
+	}
+
+	if (ieee80211_freq_to_channel_ext(freq, sec_chan, vht, op_class,
+					  chan) == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "Cannot determine operating class and channel");
+		return -1;
+	}
+
+	*phy_type = ieee80211_get_phy_type(freq, ht_oper != NULL,
+					   vht_oper != NULL);
+	if (*phy_type == PHY_TYPE_UNSPECIFIED) {
+		wpa_printf(MSG_DEBUG, "Cannot determine phy type");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int wpas_beacon_rep_add_frame_body(struct bitfield *eids,
+					  enum beacon_report_detail detail,
+					  struct wpa_bss *bss, u8 *buf,
+					  size_t buf_len)
+{
+	u8 *ies = (u8 *) (bss + 1);
+	size_t ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len;
+	u8 *pos = buf;
+	int rem_len;
+
+	rem_len = 255 - sizeof(struct rrm_measurement_beacon_report) -
+		sizeof(struct rrm_measurement_report_element) - 2;
+
+	if (detail > BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
+		wpa_printf(MSG_DEBUG,
+			   "Beacon Request: Invalid reporting detail: %d",
+			   detail);
+		return -1;
+	}
+
+	if (detail == BEACON_REPORT_DETAIL_NONE)
+		return 0;
+
+	/*
+	 * Minimal frame body subelement size: EID(1) + length(1) + TSF(8) +
+	 * beacon interval(2) + capabilities(2) = 14 bytes
+	 */
+	if (buf_len < 14)
+		return 0;
+
+	*pos++ = WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY;
+	/* The length will be filled later */
+	pos++;
+	WPA_PUT_LE64(pos, bss->tsf);
+	pos += sizeof(bss->tsf);
+	WPA_PUT_LE16(pos, bss->beacon_int);
+	pos += 2;
+	WPA_PUT_LE16(pos, bss->caps);
+	pos += 2;
+
+	rem_len -= pos - buf;
+
+	/*
+	 * According to IEEE Std 802.11-2016, 9.4.2.22.7, if the reported frame
+	 * body subelement causes the element to exceed the maximum element
+	 * size, the subelement is truncated so that the last IE is a complete
+	 * IE. So even when required to report all IEs, add elements one after
+	 * the other and stop once there is no more room in the measurement
+	 * element.
+	 */
+	while (ies_len > 2 && 2U + ies[1] <= ies_len && rem_len > 0) {
+		if (detail == BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS ||
+		    (eids && bitfield_is_set(eids, ies[0]))) {
+			u8 eid = ies[0], elen = ies[1];
+
+			if ((eid == WLAN_EID_TIM || eid == WLAN_EID_RSN) &&
+			    elen > 4)
+				elen = 4;
+			/*
+			 * TODO: Truncate IBSS DFS element as described in
+			 * IEEE Std 802.11-2016, 9.4.2.22.7.
+			 */
+
+			if (2 + elen > buf + buf_len - pos ||
+			    2 + elen > rem_len)
+				break;
+
+			*pos++ = ies[0];
+			*pos++ = elen;
+			os_memcpy(pos, ies + 2, elen);
+			pos += elen;
+			rem_len -= 2 + elen;
+		}
+
+		ies_len -= 2 + ies[1];
+		ies += 2 + ies[1];
+	}
+
+	/* Now the length is known */
+	buf[1] = pos - buf - 2;
+	return pos - buf;
+}
+
+
+static int wpas_add_beacon_rep(struct wpa_supplicant *wpa_s,
+			       struct wpabuf **wpa_buf, struct wpa_bss *bss,
+			       u64 start, u64 parent_tsf)
+{
+	struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
+	u8 *ie = (u8 *) (bss + 1);
+	size_t ie_len = bss->ie_len + bss->beacon_ie_len;
+	int ret;
+	u8 buf[2000];
+	struct rrm_measurement_beacon_report *rep;
+
+	if (os_memcmp(data->bssid, broadcast_ether_addr, ETH_ALEN) != 0 &&
+	    os_memcmp(data->bssid, bss->bssid, ETH_ALEN) != 0)
+		return 0;
+
+	if (data->ssid_len &&
+	    (data->ssid_len != bss->ssid_len ||
+	     os_memcmp(data->ssid, bss->ssid, bss->ssid_len) != 0))
+		return 0;
+
+	rep = (struct rrm_measurement_beacon_report *) buf;
+	if (wpas_get_op_chan_phy(bss->freq, ie, ie_len, &rep->op_class,
+				 &rep->channel, &rep->report_info) < 0)
+		return 0;
+
+	rep->start_time = host_to_le64(start);
+	rep->duration = host_to_le16(data->scan_params.duration);
+	rep->rcpi = rssi_to_rcpi(bss->level);
+	rep->rsni = 255; /* 255 indicates that RSNI is not available */
+	os_memcpy(rep->bssid, bss->bssid, ETH_ALEN);
+	rep->antenna_id = 0; /* unknown */
+	rep->parent_tsf = host_to_le32(parent_tsf);
+
+	ret = wpas_beacon_rep_add_frame_body(data->eids, data->report_detail,
+					     bss, rep->variable,
+					     sizeof(buf) - sizeof(*rep));
+	if (ret < 0)
+		return -1;
+
+	return wpas_rrm_report_elem(wpa_buf, wpa_s->beacon_rep_data.token,
+				    MEASUREMENT_REPORT_MODE_ACCEPT,
+				    MEASURE_TYPE_BEACON, buf,
+				    ret + sizeof(*rep));
+}
+
+
+static int wpas_beacon_rep_no_results(struct wpa_supplicant *wpa_s,
+				      struct wpabuf **buf)
+{
+	return wpas_rrm_report_elem(buf, wpa_s->beacon_rep_data.token,
+				    MEASUREMENT_REPORT_MODE_ACCEPT,
+				    MEASURE_TYPE_BEACON, NULL, 0);
+}
+
+
+static void wpas_beacon_rep_table(struct wpa_supplicant *wpa_s,
+				  struct wpabuf **buf)
+{
+	size_t i;
+
+	for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+		if (wpas_add_beacon_rep(wpa_s, buf, wpa_s->last_scan_res[i],
+					0, 0) < 0)
+			break;
+	}
+
+	if (!(*buf))
+		wpas_beacon_rep_no_results(wpa_s, buf);
+
+	wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", *buf);
+}
+
+
+static void wpas_rrm_refuse_request(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *buf = NULL;
+
+	if (wpas_rrm_report_elem(&buf, wpa_s->beacon_rep_data.token,
+				 MEASUREMENT_REPORT_MODE_REJECT_REFUSED,
+				 MEASURE_TYPE_BEACON, NULL, 0)) {
+		wpa_printf(MSG_ERROR, "RRM: Memory allocation failed");
+		wpabuf_free(buf);
+		return;
+	}
+
+	wpas_rrm_send_msr_report(wpa_s, buf);
+	wpas_clear_beacon_rep_data(wpa_s);
+	wpabuf_free(buf);
+}
+
+
+static void wpas_rrm_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct wpa_supplicant *wpa_s = eloop_ctx;
+	struct wpa_driver_scan_params *params =
+		&wpa_s->beacon_rep_data.scan_params;
+	u16 prev_duration = params->duration;
+
+	if (!wpa_s->current_bss)
+		return;
+
+	if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL) &&
+	    params->duration) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Cannot set scan duration due to missing driver support");
+		params->duration = 0;
+	}
+	os_get_reltime(&wpa_s->beacon_rep_scan);
+	if (wpa_s->scanning || wpas_p2p_in_progress(wpa_s) ||
+	    wpa_supplicant_trigger_scan(wpa_s, params))
+		wpas_rrm_refuse_request(wpa_s);
+	params->duration = prev_duration;
+}
+
+
+static int wpas_rm_handle_beacon_req_subelem(struct wpa_supplicant *wpa_s,
+					     struct beacon_rep_data *data,
+					     u8 sid, u8 slen, const u8 *subelem)
+{
+	u8 report_info, i;
+
+	switch (sid) {
+	case WLAN_BEACON_REQUEST_SUBELEM_SSID:
+		if (!slen) {
+			wpa_printf(MSG_DEBUG,
+				   "SSID subelement with zero length - wildcard SSID");
+			break;
+		}
+
+		if (slen > SSID_MAX_LEN) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid SSID subelement length: %u", slen);
+			return -1;
+		}
+
+		data->ssid_len = slen;
+		os_memcpy(data->ssid, subelem, data->ssid_len);
+		break;
+	case WLAN_BEACON_REQUEST_SUBELEM_INFO:
+		if (slen != 2) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid reporting information subelement length: %u",
+				   slen);
+			return -1;
+		}
+
+		report_info = subelem[0];
+		if (report_info != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "reporting information=%u is not supported",
+				   report_info);
+			return 0;
+		}
+		break;
+	case WLAN_BEACON_REQUEST_SUBELEM_DETAIL:
+		if (slen != 1) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid reporting detail subelement length: %u",
+				   slen);
+			return -1;
+		}
+
+		data->report_detail = subelem[0];
+		if (data->report_detail >
+		    BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS) {
+			wpa_printf(MSG_DEBUG, "Invalid reporting detail: %u",
+				   subelem[0]);
+			return 0;
+		}
+
+		break;
+	case WLAN_BEACON_REQUEST_SUBELEM_REQUEST:
+		if (data->report_detail !=
+		    BEACON_REPORT_DETAIL_REQUESTED_ONLY) {
+			wpa_printf(MSG_DEBUG,
+				   "Beacon request: request subelement is present but report detail is %u",
+				   data->report_detail);
+			return -1;
+		}
+
+		if (!slen) {
+			wpa_printf(MSG_DEBUG,
+				   "Invalid request subelement length: %u",
+				   slen);
+			return -1;
+		}
+
+		if (data->eids) {
+			wpa_printf(MSG_DEBUG,
+				   "Beacon Request: Request subelement appears more than once");
+			return -1;
+		}
+
+		data->eids = bitfield_alloc(255);
+		if (!data->eids) {
+			wpa_printf(MSG_DEBUG, "Failed to allocate EIDs bitmap");
+			return -1;
+		}
+
+		for (i = 0; i < slen; i++)
+			bitfield_set(data->eids, subelem[i]);
+		break;
+	case WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL:
+		/* Skip - it will be processed when freqs are added */
+		break;
+	default:
+		wpa_printf(MSG_DEBUG,
+			   "Beacon request: Unknown subelement id %u", sid);
+		break;
+	}
+
+	return 1;
+}
+
+
+/**
+ * Returns 0 if the next element can be processed, 1 if some operation was
+ * triggered, and -1 if processing failed (i.e., the element is in invalid
+ * format or an internal error occurred).
+ */
+static int
+wpas_rm_handle_beacon_req(struct wpa_supplicant *wpa_s,
+			  u8 elem_token, int duration_mandatory,
+			  const struct rrm_measurement_beacon_request *req,
+			  size_t len, struct wpabuf **buf)
+{
+	struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
+	struct wpa_driver_scan_params *params = &data->scan_params;
+	const u8 *subelems;
+	size_t elems_len;
+	u16 rand_interval;
+	u32 interval_usec;
+	u32 _rand;
+	int ret = 0, res;
+
+	if (len < sizeof(*req))
+		return -1;
+
+	if (req->mode != BEACON_REPORT_MODE_PASSIVE &&
+	    req->mode != BEACON_REPORT_MODE_ACTIVE &&
+	    req->mode != BEACON_REPORT_MODE_TABLE)
+		return 0;
+
+	subelems = req->variable;
+	elems_len = len - sizeof(*req);
+	rand_interval = le_to_host16(req->rand_interval);
+
+	os_free(params->freqs);
+	os_memset(params, 0, sizeof(*params));
+
+	data->token = elem_token;
+
+	/* default reporting detail is all fixed length fields and all
+	 * elements */
+	data->report_detail = BEACON_REPORT_DETAIL_ALL_FIELDS_AND_ELEMENTS;
+	os_memcpy(data->bssid, req->bssid, ETH_ALEN);
+
+	while (elems_len >= 2) {
+		if (subelems[1] > elems_len - 2) {
+			wpa_printf(MSG_DEBUG,
+				   "Beacon Request: Truncated subelement");
+			ret = -1;
+			goto out;
+		}
+
+		res = wpas_rm_handle_beacon_req_subelem(
+			wpa_s, data, subelems[0], subelems[1], &subelems[2]);
+		if (res != 1) {
+			ret = res;
+			goto out;
+		}
+
+		elems_len -= 2 + subelems[1];
+		subelems += 2 + subelems[1];
+	}
+
+	if (req->mode == BEACON_REPORT_MODE_TABLE) {
+		wpas_beacon_rep_table(wpa_s, buf);
+		goto out;
+	}
+
+	params->freqs = wpas_beacon_request_freqs(
+		wpa_s, req->oper_class, req->channel,
+		req->mode == BEACON_REPORT_MODE_ACTIVE,
+		req->variable, len - sizeof(*req));
+	if (!params->freqs) {
+		wpa_printf(MSG_DEBUG, "Beacon request: No valid channels");
+		goto out;
+	}
+
+	params->duration = le_to_host16(req->duration);
+	params->duration_mandatory = duration_mandatory;
+	if (!params->duration) {
+		wpa_printf(MSG_DEBUG, "Beacon request: Duration is 0");
+		ret = -1;
+		goto out;
+	}
+
+	params->only_new_results = 1;
+
+	if (req->mode == BEACON_REPORT_MODE_ACTIVE) {
+		params->ssids[params->num_ssids].ssid = data->ssid;
+		params->ssids[params->num_ssids++].ssid_len = data->ssid_len;
+	}
+
+	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+		_rand = os_random();
+	interval_usec = (_rand % (rand_interval + 1)) * 1024;
+	eloop_register_timeout(0, interval_usec, wpas_rrm_scan_timeout, wpa_s,
+			       NULL);
+	return 1;
+out:
+	wpas_clear_beacon_rep_data(wpa_s);
+	return ret;
+}
+
+
+static int
+wpas_rrm_handle_msr_req_element(
+	struct wpa_supplicant *wpa_s,
+	const struct rrm_measurement_request_element *req,
+	struct wpabuf **buf)
+{
+	int duration_mandatory;
+
+	wpa_printf(MSG_DEBUG, "Measurement request type %d token %d",
+		   req->type, req->token);
+
+	if (req->mode & MEASUREMENT_REQUEST_MODE_ENABLE) {
+		/* Enable bit is not supported for now */
+		wpa_printf(MSG_DEBUG, "RRM: Enable bit not supported, ignore");
+		return 0;
+	}
+
+	if ((req->mode & MEASUREMENT_REQUEST_MODE_PARALLEL) &&
+	    req->type > MEASURE_TYPE_RPI_HIST) {
+		/* Parallel measurements are not supported for now */
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Parallel measurements are not supported, reject");
+		goto reject;
+	}
+
+	duration_mandatory =
+		!!(req->mode & MEASUREMENT_REQUEST_MODE_DURATION_MANDATORY);
+
+	switch (req->type) {
+	case MEASURE_TYPE_LCI:
+		return wpas_rrm_build_lci_report(wpa_s, req, buf);
+	case MEASURE_TYPE_BEACON:
+		if (duration_mandatory &&
+		    !(wpa_s->drv_rrm_flags &
+		      WPA_DRIVER_FLAGS_SUPPORT_SET_SCAN_DWELL)) {
+			wpa_printf(MSG_DEBUG,
+				   "RRM: Driver does not support dwell time configuration - reject beacon report with mandatory duration");
+			goto reject;
+		}
+		return wpas_rm_handle_beacon_req(wpa_s, req->token,
+						 duration_mandatory,
+						 (const void *) req->variable,
+						 req->len - 3, buf);
+	default:
+		wpa_printf(MSG_INFO,
+			   "RRM: Unsupported radio measurement type %u",
+			   req->type);
+		break;
+	}
+
+reject:
+	if (wpas_rrm_report_elem(buf, req->token,
+				 MEASUREMENT_REPORT_MODE_REJECT_INCAPABLE,
+				 req->type, NULL, 0) < 0) {
+		wpa_printf(MSG_DEBUG, "RRM: Failed to add report element");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static struct wpabuf *
+wpas_rrm_process_msr_req_elems(struct wpa_supplicant *wpa_s, const u8 *pos,
+			       size_t len)
+{
+	struct wpabuf *buf = NULL;
+
+	while (len) {
+		const struct rrm_measurement_request_element *req;
+		int res;
+
+		if (len < 2) {
+			wpa_printf(MSG_DEBUG, "RRM: Truncated element");
+			goto out;
+		}
+
+		req = (const struct rrm_measurement_request_element *) pos;
+		if (req->eid != WLAN_EID_MEASURE_REQUEST) {
+			wpa_printf(MSG_DEBUG,
+				   "RRM: Expected Measurement Request element, but EID is %u",
+				   req->eid);
+			goto out;
+		}
+
+		if (req->len < 3) {
+			wpa_printf(MSG_DEBUG, "RRM: Element length too short");
+			goto out;
+		}
+
+		if (req->len > len - 2) {
+			wpa_printf(MSG_DEBUG, "RRM: Element length too long");
+			goto out;
+		}
+
+		res = wpas_rrm_handle_msr_req_element(wpa_s, req, &buf);
+		if (res < 0)
+			goto out;
+
+		pos += req->len + 2;
+		len -= req->len + 2;
+	}
+
+	return buf;
+
+out:
+	wpabuf_free(buf);
+	return NULL;
+}
+
+
+void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
+					       const u8 *src,
+					       const u8 *frame, size_t len)
+{
+	struct wpabuf *report;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring radio measurement request: Not associated");
+		return;
+	}
+
+	if (!wpa_s->rrm.rrm_used) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring radio measurement request: Not RRM network");
+		return;
+	}
+
+	if (len < 3) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring too short radio measurement request");
+		return;
+	}
+
+	wpa_s->rrm.token = *frame;
+
+	/* Number of repetitions is not supported */
+
+	report = wpas_rrm_process_msr_req_elems(wpa_s, frame + 3, len - 3);
+	if (!report)
+		return;
+
+	wpas_rrm_send_msr_report(wpa_s, report);
+	wpabuf_free(report);
+}
+
+
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+					      const u8 *src,
+					      const u8 *frame, size_t len,
+					      int rssi)
+{
+	struct wpabuf *buf;
+	const struct rrm_link_measurement_request *req;
+	struct rrm_link_measurement_report report;
+
+	if (wpa_s->wpa_state != WPA_COMPLETED) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring link measurement request. Not associated");
+		return;
+	}
+
+	if (!wpa_s->rrm.rrm_used) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Ignoring link measurement request. Not RRM network");
+		return;
+	}
+
+	if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Measurement report failed. TX power insertion not supported");
+		return;
+	}
+
+	req = (const struct rrm_link_measurement_request *) frame;
+	if (len < sizeof(*req)) {
+		wpa_printf(MSG_INFO,
+			   "RRM: Link measurement report failed. Request too short");
+		return;
+	}
+
+	os_memset(&report, 0, sizeof(report));
+	report.dialog_token = req->dialog_token;
+	report.tpc.eid = WLAN_EID_TPC_REPORT;
+	report.tpc.len = 2;
+	/* Note: The driver is expected to update report.tpc.tx_power and
+	 * report.tpc.link_margin subfields when sending out this frame.
+	 * Similarly, the driver would need to update report.rx_ant_id and
+	 * report.tx_ant_id subfields. */
+	report.rsni = 255; /* 255 indicates that RSNI is not available */
+	report.rcpi = rssi_to_rcpi(rssi);
+
+	/* action_category + action_code */
+	buf = wpabuf_alloc(2 + sizeof(report));
+	if (buf == NULL) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Link measurement report failed. Buffer allocation failed");
+		return;
+	}
+
+	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
+	wpabuf_put_data(buf, &report, sizeof(report));
+	wpa_hexdump_buf(MSG_DEBUG, "RRM: Link measurement report", buf);
+
+	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+				wpa_s->own_addr, wpa_s->bssid,
+				wpabuf_head(buf), wpabuf_len(buf), 0)) {
+		wpa_printf(MSG_ERROR,
+			   "RRM: Link measurement report failed. Send action failed");
+	}
+	wpabuf_free(buf);
+}
+
+
+int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
+				 struct wpa_scan_results *scan_res,
+				 struct scan_info *info)
+{
+	size_t i = 0;
+	struct wpabuf *buf = NULL;
+
+	if (!wpa_s->beacon_rep_data.token)
+		return 0;
+
+	if (!wpa_s->current_bss)
+		goto out;
+
+	/* If the measurement was aborted, don't report partial results */
+	if (info->aborted)
+		goto out;
+
+	wpa_printf(MSG_DEBUG, "RRM: TSF BSSID: " MACSTR " current BSS: " MACSTR,
+		   MAC2STR(info->scan_start_tsf_bssid),
+		   MAC2STR(wpa_s->current_bss->bssid));
+	if ((wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
+	    os_memcmp(info->scan_start_tsf_bssid, wpa_s->current_bss->bssid,
+		      ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG,
+			   "RRM: Ignore scan results due to mismatching TSF BSSID");
+		goto out;
+	}
+
+	for (i = 0; i < scan_res->num; i++) {
+		struct wpa_bss *bss =
+			wpa_bss_get_bssid(wpa_s, scan_res->res[i]->bssid);
+
+		if (!bss)
+			continue;
+
+		if ((wpa_s->drv_rrm_flags &
+		     WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT) &&
+		    os_memcmp(scan_res->res[i]->tsf_bssid,
+			      wpa_s->current_bss->bssid, ETH_ALEN) != 0) {
+			wpa_printf(MSG_DEBUG,
+				   "RRM: Ignore scan result for " MACSTR
+				   " due to mismatching TSF BSSID" MACSTR,
+				   MAC2STR(scan_res->res[i]->bssid),
+				   MAC2STR(scan_res->res[i]->tsf_bssid));
+			continue;
+		}
+
+		if (!(wpa_s->drv_rrm_flags &
+		      WPA_DRIVER_FLAGS_SUPPORT_BEACON_REPORT)) {
+			struct os_reltime update_time, diff;
+
+			/* For now, allow 8 ms older results due to some
+			 * unknown issue with cfg80211 BSS table updates during
+			 * a scan with the current BSS.
+			 * TODO: Fix this more properly to avoid having to have
+			 * this type of hacks in place. */
+			calculate_update_time(&scan_res->fetch_time,
+					      scan_res->res[i]->age,
+					      &update_time);
+			os_reltime_sub(&wpa_s->beacon_rep_scan,
+				       &update_time, &diff);
+			if (os_reltime_before(&update_time,
+					      &wpa_s->beacon_rep_scan) &&
+			    (diff.sec || diff.usec >= 8000)) {
+				wpa_printf(MSG_DEBUG,
+					   "RRM: Ignore scan result for " MACSTR
+					   " due to old update (age(ms) %u, calculated age %u.%06u seconds)",
+					   MAC2STR(scan_res->res[i]->bssid),
+					   scan_res->res[i]->age,
+					   (unsigned int) diff.sec,
+					   (unsigned int) diff.usec);
+				continue;
+			}
+		}
+
+		/*
+		 * Don't report results that were not received during the
+		 * current measurement.
+		 */
+		if (info->scan_start_tsf > scan_res->res[i]->parent_tsf)
+			continue;
+
+		if (wpas_add_beacon_rep(wpa_s, &buf, bss, info->scan_start_tsf,
+					scan_res->res[i]->parent_tsf) < 0)
+			break;
+	}
+
+	if (!buf && wpas_beacon_rep_no_results(wpa_s, &buf))
+		goto out;
+
+	wpa_hexdump_buf(MSG_DEBUG, "RRM: Radio Measurement report", buf);
+
+	wpas_rrm_send_msr_report(wpa_s, buf);
+	wpabuf_free(buf);
+
+out:
+	wpas_clear_beacon_rep_data(wpa_s);
+	return 1;
+}
+
+
+void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s)
+{
+	struct beacon_rep_data *data = &wpa_s->beacon_rep_data;
+
+	eloop_cancel_timeout(wpas_rrm_scan_timeout, wpa_s, NULL);
+	bitfield_free(data->eids);
+	os_free(data->scan_params.freqs);
+	os_memset(data, 0, sizeof(*data));
+}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 7f42607..3a100cd 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -36,8 +36,7 @@
 
 	if (wpa_s->current_ssid == NULL) {
 		wpa_s->current_ssid = ssid;
-		if (wpa_s->current_ssid != NULL)
-			wpas_notify_network_changed(wpa_s);
+		wpas_notify_network_changed(wpa_s);
 	}
 	wpa_supplicant_initiate_eapol(wpa_s);
 	wpa_dbg(wpa_s, MSG_DEBUG, "Already associated with a configured "
@@ -60,10 +59,7 @@
 
 		wps = 1;
 		*req_type = wpas_wps_get_req_type(ssid);
-		if (!ssid->eap.phase1)
-			continue;
-
-		if (os_strstr(ssid->eap.phase1, "pbc=1"))
+		if (ssid->eap.phase1 && os_strstr(ssid->eap.phase1, "pbc=1"))
 			return 2;
 	}
 
@@ -166,6 +162,8 @@
 	if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
 		wpa_msg(wpa_s, MSG_INFO,
 			"Failed to assign random MAC address for a scan");
+		wpa_scan_free_params(params);
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
 		radio_work_done(work);
 		return;
 	}
@@ -178,6 +176,17 @@
 		params->only_new_results = 1;
 	}
 	ret = wpa_drv_scan(wpa_s, params);
+	/*
+	 * Store the obtained vendor scan cookie (if any) in wpa_s context.
+	 * The current design is to allow only one scan request on each
+	 * interface, hence having this scan cookie stored in wpa_s context is
+	 * fine for now.
+	 *
+	 * Revisit this logic if concurrent scan operations per interface
+	 * is supported.
+	 */
+	if (ret == 0)
+		wpa_s->curr_scan_cookie = params->scan_cookie;
 	wpa_scan_free_params(params);
 	work->ctx = NULL;
 	if (ret) {
@@ -229,12 +238,11 @@
 	}
 
 	ctx = wpa_scan_clone_params(params);
-	if (ctx == NULL)
-		return -1;
-
-	if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
+	if (!ctx ||
+	    radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
 	{
 		wpa_scan_free_params(ctx);
+		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=-1");
 		return -1;
 	}
 
@@ -266,8 +274,9 @@
 }
 
 
-int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_scan_params *params)
+static int
+wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+				struct wpa_driver_scan_params *params)
 {
 	int ret;
 
@@ -282,7 +291,7 @@
 }
 
 
-int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
 {
 	int ret;
 
@@ -428,6 +437,39 @@
 #endif /* CONFIG_INTERWORKING */
 
 
+void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *default_ies = NULL;
+	u8 ext_capab[18];
+	int ext_capab_len;
+	enum wpa_driver_if_type type = WPA_IF_STATION;
+
+#ifdef CONFIG_P2P
+	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
+		type = WPA_IF_P2P_CLIENT;
+#endif /* CONFIG_P2P */
+
+	wpa_drv_get_ext_capa(wpa_s, type);
+
+	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+					     sizeof(ext_capab));
+	if (ext_capab_len > 0 &&
+	    wpabuf_resize(&default_ies, ext_capab_len) == 0)
+		wpabuf_put_data(default_ies, ext_capab, ext_capab_len);
+
+#ifdef CONFIG_MBO
+	/* Send cellular capabilities for potential MBO STAs */
+	if (wpabuf_resize(&default_ies, 9) == 0)
+		wpas_mbo_scan_ie(wpa_s, default_ies);
+#endif /* CONFIG_MBO */
+
+	if (default_ies)
+		wpa_drv_set_default_scan_ies(wpa_s, wpabuf_head(default_ies),
+					     wpabuf_len(default_ies));
+	wpabuf_free(default_ies);
+}
+
+
 static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
 {
 	struct wpabuf *extra_ie = NULL;
@@ -438,6 +480,13 @@
 	enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_P2P
+	if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+#endif /* CONFIG_P2P */
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
 					     sizeof(ext_capab));
 	if (ext_capab_len > 0 &&
@@ -496,6 +545,13 @@
 		wpas_mbo_scan_ie(wpa_s, extra_ie);
 #endif /* CONFIG_MBO */
 
+	if (wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ]) {
+		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_PROBE_REQ];
+
+		if (wpabuf_resize(&extra_ie, wpabuf_len(buf)) == 0)
+			wpabuf_put_buf(extra_ie, buf);
+	}
+
 	return extra_ie;
 }
 
@@ -575,11 +631,12 @@
 {
 	unsigned int i;
 	struct wpa_ssid *ssid;
+
 	/*
-	 * For devices with |max_ssids| greater than 1, leave the last slot empty
+	 * For devices with max_ssids greater than 1, leave the last slot empty
 	 * for adding the wildcard scan entry.
 	 */
-	max_ssids = (max_ssids == 1) ? max_ssids : max_ssids - 1;
+	max_ssids = max_ssids > 1 ? max_ssids - 1 : max_ssids;
 
 	for (i = 0; i < wpa_s->scan_id_count; i++) {
 		unsigned int j;
@@ -657,10 +714,7 @@
 	size_t max_ssids;
 	int connect_without_scan = 0;
 
-	if (wpa_s->pno || wpa_s->pno_sched_pending) {
-		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
-		return;
-	}
+	wpa_s->ignore_post_flush_scan_res = 0;
 
 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
@@ -722,6 +776,21 @@
 		return;
 	}
 
+	/*
+	 * Don't cancel the scan based on ongoing PNO; defer it. Some scans are
+	 * used for changing modes inside wpa_supplicant (roaming,
+	 * auto-reconnect, etc). Discarding the scan might hurt these processes.
+	 * The normal use case for PNO is to suspend the host immediately after
+	 * starting PNO, so the periodic 100 ms attempts to run the scan do not
+	 * normally happen in practice multiple times, i.e., this is simply
+	 * restarting scanning once the host is woken up and PNO stopped.
+	 */
+	if (wpa_s->pno || wpa_s->pno_sched_pending) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "Defer scan - PNO is in progress");
+		wpa_supplicant_req_scan(wpa_s, 0, 100000);
+		return;
+	}
+
 	if (wpa_s->conf->ap_scan == 2)
 		max_ssids = 1;
 	else {
@@ -835,12 +904,10 @@
 		 * slot for the zero-terminator.
 		 */
 		params.freqs = os_malloc(sizeof(int) * 2);
-		if (params.freqs == NULL) {
-			wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed");
-			return;
+		if (params.freqs) {
+			params.freqs[0] = wpa_s->assoc_freq;
+			params.freqs[1] = 0;
 		}
-		params.freqs[0] = wpa_s->assoc_freq;
-		params.freqs[1] = 0;
 
 		/*
 		 * Reset the reattach flag so that we fall back to full scan if
@@ -1003,7 +1070,8 @@
 	}
 #endif /* CONFIG_P2P */
 
-	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) {
+	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) &&
+	    wpa_s->wpa_state <= WPA_SCANNING) {
 		params.mac_addr_rand = 1;
 		if (wpa_s->mac_addr_scan) {
 			params.mac_addr = wpa_s->mac_addr_scan;
@@ -1181,6 +1249,26 @@
 }
 
 
+static void
+wpa_scan_set_relative_rssi_params(struct wpa_supplicant *wpa_s,
+				  struct wpa_driver_scan_params *params)
+{
+	if (wpa_s->wpa_state != WPA_COMPLETED ||
+	    !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SCHED_SCAN_RELATIVE_RSSI) ||
+	    wpa_s->srp.relative_rssi_set == 0)
+		return;
+
+	params->relative_rssi_set = 1;
+	params->relative_rssi = wpa_s->srp.relative_rssi;
+
+	if (wpa_s->srp.relative_adjust_rssi == 0)
+		return;
+
+	params->relative_adjust_band = wpa_s->srp.relative_adjust_band;
+	params->relative_adjust_rssi = wpa_s->srp.relative_adjust_rssi;
+}
+
+
 /**
  * wpa_supplicant_req_sched_scan - Start a periodic scheduled scan
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1212,6 +1300,8 @@
 	if (max_sched_scan_ssids < 1 || wpa_s->conf->disable_scan_offload)
 		return -1;
 
+	wpa_s->sched_scan_stop_req = 0;
+
 	if (wpa_s->sched_scanning) {
 		wpa_dbg(wpa_s, MSG_DEBUG, "Already sched scanning");
 		return 0;
@@ -1423,7 +1513,8 @@
 
 	wpa_setband_scan_freqs(wpa_s, scan_params);
 
-	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) {
+	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) &&
+	    wpa_s->wpa_state <= WPA_SCANNING) {
 		params.mac_addr_rand = 1;
 		if (wpa_s->mac_addr_sched_scan) {
 			params.mac_addr = wpa_s->mac_addr_sched_scan;
@@ -1432,6 +1523,8 @@
 		}
 	}
 
+	wpa_scan_set_relative_rssi_params(wpa_s, scan_params);
+
 	ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params);
 	wpabuf_free(extra_ie);
 	os_free(params.filter_ssids);
@@ -1510,6 +1603,9 @@
 	if (!wpa_s->sched_scanning)
 		return;
 
+	if (wpa_s->sched_scanning)
+		wpa_s->sched_scan_stop_req = 1;
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling sched scan");
 	eloop_cancel_timeout(wpa_supplicant_sched_scan_timeout, wpa_s, NULL);
 	wpa_supplicant_stop_sched_scan(wpa_s);
@@ -1569,7 +1665,13 @@
  */
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
 {
-	return get_ie((const u8 *) (res + 1), res->ie_len, ie);
+	size_t ie_len = res->ie_len;
+
+	/* Use the Beacon frame IEs if res->ie_len is not available */
+	if (!ie_len)
+		ie_len = res->beacon_ie_len;
+
+	return get_ie((const u8 *) (res + 1), ie_len, ie);
 }
 
 
@@ -1686,10 +1788,12 @@
  * This doc https://supportforums.cisco.com/docs/DOC-12954 says, "the general
  * rule of thumb is that any SNR above 20 is good." This one
  * http://www.cisco.com/en/US/tech/tk722/tk809/technologies_q_and_a_item09186a00805e9a96.shtml#qa23
- * recommends 25 as a minimum SNR for 54 Mbps data rate. 30 is chosen here as a
- * conservative value.
+ * recommends 25 as a minimum SNR for 54 Mbps data rate. The estimates used in
+ * scan_est_throughput() allow even smaller SNR values for the maximum rates
+ * (21 for 54 Mbps, 22 for VHT80 MCS9, 24 for HT40 and HT20 MCS7). Use 25 as a
+ * somewhat conservative value here.
  */
-#define GREAT_SNR 30
+#define GREAT_SNR 25
 
 #define IS_5GHZ(n) (n > 4000)
 
@@ -1737,10 +1841,12 @@
 	}
 
 	/* if SNR is close, decide by max rate or frequency band */
-	if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
-	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+	if (snr_a && snr_b && abs(snr_b - snr_a) < 7) {
 		if (wa->est_throughput != wb->est_throughput)
 			return wb->est_throughput - wa->est_throughput;
+	}
+	if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+	    (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
 		if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
 			return IS_5GHZ(wa->freq) ? -1 : 1;
 	}
@@ -1872,8 +1978,8 @@
 }
 
 
-static void filter_scan_res(struct wpa_supplicant *wpa_s,
-			    struct wpa_scan_results *res)
+void filter_scan_res(struct wpa_supplicant *wpa_s,
+		     struct wpa_scan_results *res)
 {
 	size_t i, j;
 
@@ -1906,7 +2012,7 @@
 #define DEFAULT_NOISE_FLOOR_2GHZ (-89)
 #define DEFAULT_NOISE_FLOOR_5GHZ (-92)
 
-static void scan_snr(struct wpa_scan_res *res)
+void scan_snr(struct wpa_scan_res *res)
 {
 	if (res->flags & WPA_SCAN_NOISE_INVALID) {
 		res->noise = IS_5GHZ(res->freq) ?
@@ -1990,8 +2096,8 @@
 }
 
 
-static void scan_est_throughput(struct wpa_supplicant *wpa_s,
-				struct wpa_scan_res *res)
+void scan_est_throughput(struct wpa_supplicant *wpa_s,
+			 struct wpa_scan_res *res)
 {
 	enum local_hw_capab capab = wpa_s->hw_capab;
 	int rate; /* max legacy rate in 500 kb/s units */
@@ -2128,10 +2234,22 @@
 	}
 #endif /* CONFIG_WPS */
 
-	qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
-	      compar);
+	if (scan_res->res) {
+		qsort(scan_res->res, scan_res->num,
+		      sizeof(struct wpa_scan_res *), compar);
+	}
 	dump_scan_res(scan_res);
 
+	if (wpa_s->ignore_post_flush_scan_res) {
+		/* FLUSH command aborted an ongoing scan and these are the
+		 * results from the aborted scan. Do not process the results to
+		 * maintain flushed state. */
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"Do not update BSS table based on pending post-FLUSH scan results");
+		wpa_s->ignore_post_flush_scan_res = 0;
+		return scan_res;
+	}
+
 	wpa_bss_update_start(wpa_s);
 	for (i = 0; i < scan_res->num; i++)
 		wpa_bss_update_scan_res(wpa_s, scan_res->res[i],
@@ -2256,6 +2374,8 @@
 	params->p2p_probe = src->p2p_probe;
 	params->only_new_results = src->only_new_results;
 	params->low_priority = src->low_priority;
+	params->duration = src->duration;
+	params->duration_mandatory = src->duration_mandatory;
 
 	if (src->sched_scan_plans_num > 0) {
 		params->sched_scan_plans =
@@ -2298,6 +2418,10 @@
 		params->bssid = bssid;
 	}
 
+	params->relative_rssi_set = src->relative_rssi_set;
+	params->relative_rssi = src->relative_rssi;
+	params->relative_adjust_band = src->relative_adjust_band;
+	params->relative_adjust_rssi = src->relative_adjust_rssi;
 	return params;
 
 failed:
@@ -2355,7 +2479,7 @@
 		return 0;
 
 	if ((wpa_s->wpa_state > WPA_SCANNING) &&
-	    (wpa_s->wpa_state <= WPA_COMPLETED)) {
+	    (wpa_s->wpa_state < WPA_COMPLETED)) {
 		wpa_printf(MSG_ERROR, "PNO: In assoc process");
 		return -EAGAIN;
 	}
@@ -2371,6 +2495,13 @@
 		}
 	}
 
+	if (wpa_s->sched_scan_stop_req) {
+		wpa_printf(MSG_DEBUG,
+			   "Schedule PNO after previous sched scan has stopped");
+		wpa_s->pno_sched_pending = 1;
+		return 0;
+	}
+
 	os_memset(&params, 0, sizeof(params));
 
 	num_ssid = num_match_ssid = 0;
@@ -2460,7 +2591,8 @@
 		params.freqs = wpa_s->manual_sched_scan_freqs;
 	}
 
-	if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) {
+	if ((wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) &&
+	    wpa_s->wpa_state <= WPA_SCANNING) {
 		params.mac_addr_rand = 1;
 		if (wpa_s->mac_addr_pno) {
 			params.mac_addr = wpa_s->mac_addr_pno;
@@ -2468,6 +2600,8 @@
 		}
 	}
 
+	wpa_scan_set_relative_rssi_params(wpa_s, &params);
+
 	ret = wpa_supplicant_start_sched_scan(wpa_s, &params);
 	os_free(params.filter_ssids);
 	if (ret == 0)
@@ -2486,6 +2620,7 @@
 		return 0;
 
 	ret = wpa_supplicant_stop_sched_scan(wpa_s);
+	wpa_s->sched_scan_stop_req = 1;
 
 	wpa_s->pno = 0;
 	wpa_s->pno_sched_pending = 0;
@@ -2557,18 +2692,20 @@
 
 int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s)
 {
-	int scan_work = !!wpa_s->scan_work;
+	struct wpa_radio_work *work;
+	struct wpa_radio *radio = wpa_s->radio;
 
-#ifdef CONFIG_P2P
-	scan_work |= !!wpa_s->p2p_scan_work;
-#endif /* CONFIG_P2P */
-
-	if (scan_work && wpa_s->own_scan_running) {
+	dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+		if (work->wpa_s != wpa_s || !work->started ||
+		    (os_strcmp(work->type, "scan") != 0 &&
+		     os_strcmp(work->type, "p2p-scan") != 0))
+			continue;
 		wpa_dbg(wpa_s, MSG_DEBUG, "Abort an ongoing scan");
-		return wpa_drv_abort_scan(wpa_s);
+		return wpa_drv_abort_scan(wpa_s, wpa_s->curr_scan_cookie);
 	}
 
-	return 0;
+	wpa_dbg(wpa_s, MSG_DEBUG, "No ongoing scan/p2p-scan found to abort");
+	return -1;
 }
 
 
@@ -2609,13 +2746,6 @@
 			goto fail;
 		}
 
-		if (!scan_plan->interval) {
-			wpa_printf(MSG_ERROR,
-				   "scan plan %u: Interval cannot be zero",
-				   num);
-			goto fail;
-		}
-
 		if (scan_plan->interval > wpa_s->max_sched_scan_plan_interval) {
 			wpa_printf(MSG_WARNING,
 				   "scan plan %u: Scan interval too long(%u), use the maximum allowed(%u)",
@@ -2689,3 +2819,31 @@
 	wpa_printf(MSG_ERROR, "invalid scan plans list");
 	return -1;
 }
+
+
+/**
+ * wpas_scan_reset_sched_scan - Reset sched_scan state
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel a running scheduled scan and to reset an
+ * internal scan state to continue with a regular scan on the following
+ * wpa_supplicant_req_scan() calls.
+ */
+void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s)
+{
+	wpa_s->normal_scans = 0;
+	if (wpa_s->sched_scanning) {
+		wpa_s->sched_scan_timed_out = 0;
+		wpa_s->prev_sched_ssid = NULL;
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+	}
+}
+
+
+void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s)
+{
+	/* simulate timeout to restart the sched scan */
+	wpa_s->sched_scan_timed_out = 1;
+	wpa_s->prev_sched_ssid = NULL;
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
index 93ec9b3..2aa0a8b 100644
--- a/wpa_supplicant/scan.h
+++ b/wpa_supplicant/scan.h
@@ -39,14 +39,13 @@
 void scan_only_handler(struct wpa_supplicant *wpa_s,
 		       struct wpa_scan_results *scan_res);
 int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
-int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-				    struct wpa_driver_scan_params *params);
-int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
 struct wpa_driver_scan_params *
 wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
 void wpa_scan_free_params(struct wpa_driver_scan_params *params);
 int wpas_start_pno(struct wpa_supplicant *wpa_s);
 int wpas_stop_pno(struct wpa_supplicant *wpa_s);
+void wpas_scan_reset_sched_scan(struct wpa_supplicant *wpa_s);
+void wpas_scan_restart_sched_scan(struct wpa_supplicant *wpa_s);
 
 void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
 				   unsigned int type);
@@ -54,5 +53,11 @@
 				unsigned int type, const u8 *addr,
 				const u8 *mask);
 int wpas_abort_ongoing_scan(struct wpa_supplicant *wpa_s);
+void filter_scan_res(struct wpa_supplicant *wpa_s,
+		     struct wpa_scan_results *res);
+void scan_snr(struct wpa_scan_res *res);
+void scan_est_throughput(struct wpa_supplicant *wpa_s,
+			 struct wpa_scan_res *res);
+void wpa_supplicant_set_default_scan_ies(struct wpa_supplicant *wpa_s);
 
 #endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index a6ace1a..beb9d6e 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -161,9 +161,10 @@
 		return;
 	}
 
-	if (!(wpa_s->drv_rrm_flags &
-	      WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
-	    !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) {
+	if (!((wpa_s->drv_rrm_flags &
+	       WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) &&
+	      (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) &&
+	    !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_SUPPORT_RRM)) {
 		wpa_printf(MSG_DEBUG,
 			   "RRM: Insufficient RRM support in driver - do not use RRM");
 		return;
@@ -186,6 +187,13 @@
 	if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
 		*pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
 
+	*pos |= WLAN_RRM_CAPS_BEACON_REPORT_PASSIVE |
+		WLAN_RRM_CAPS_BEACON_REPORT_ACTIVE |
+		WLAN_RRM_CAPS_BEACON_REPORT_TABLE;
+
+	if (wpa_s->lci)
+		pos[1] |= WLAN_RRM_CAPS_LCI_MEASUREMENT;
+
 	wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2;
 	wpa_s->rrm.rrm_used = 1;
 }
@@ -208,9 +216,6 @@
 	u8 ext_capab[18];
 	int ext_capab_len;
 	int skip_auth;
-#ifdef CONFIG_MBO
-	const u8 *mbo;
-#endif /* CONFIG_MBO */
 
 	if (bss == NULL) {
 		wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
@@ -436,20 +441,15 @@
 
 	sme_auth_handle_rrm(wpa_s, bss);
 
-#ifdef CONFIG_MBO
-	mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
-	if (mbo) {
-		int len;
+	wpa_s->sme.assoc_req_ie_len += wpas_supp_op_class_ie(
+		wpa_s, bss->freq,
+		wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+		sizeof(wpa_s->sme.assoc_req_ie) - wpa_s->sme.assoc_req_ie_len);
 
-		len = wpas_mbo_supp_op_class_ie(
-			wpa_s, bss->freq,
-			wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
-			sizeof(wpa_s->sme.assoc_req_ie) -
-			wpa_s->sme.assoc_req_ie_len);
-		if (len > 0)
-			wpa_s->sme.assoc_req_ie_len += len;
-	}
-#endif /* CONFIG_MBO */
+	if (params.p2p)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
 
 	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
 					     sizeof(ext_capab));
@@ -502,7 +502,7 @@
 	}
 
 #ifdef CONFIG_MBO
-	if (mbo) {
+	if (wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
 		int len;
 
 		len = wpas_mbo_ie(wpa_s, wpa_s->sme.assoc_req_ie +
@@ -534,12 +534,37 @@
 			wpas_connection_failed(wpa_s, bss->bssid);
 			return;
 		}
-		params.sae_data = wpabuf_head(resp);
-		params.sae_data_len = wpabuf_len(resp);
+		params.auth_data = wpabuf_head(resp);
+		params.auth_data_len = wpabuf_len(resp);
 		wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
 	}
 #endif /* CONFIG_SAE */
 
+	old_ssid = wpa_s->current_ssid;
+	wpa_s->current_ssid = ssid;
+	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+	wpa_supplicant_initiate_eapol(wpa_s);
+
+#ifdef CONFIG_FILS
+	/* TODO: FILS operations can in some cases be done between different
+	 * network_ctx (i.e., same credentials can be used with multiple
+	 * networks). */
+	if (params.auth_alg == WPA_AUTH_ALG_OPEN &&
+	    wpa_key_mgmt_fils(ssid->key_mgmt)) {
+		if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+					    ssid, 0) == 0)
+			wpa_printf(MSG_DEBUG,
+				   "SME: Try to use FILS with PMKSA caching");
+		resp = fils_build_auth(wpa_s->wpa);
+		if (resp) {
+			params.auth_alg = WPA_AUTH_ALG_FILS;
+			params.auth_data = wpabuf_head(resp);
+			params.auth_data_len = wpabuf_len(resp);
+			wpa_s->sme.auth_alg = WPA_AUTH_ALG_FILS;
+		}
+	}
+#endif /* CONFIG_FILS */
+
 	wpa_supplicant_cancel_sched_scan(wpa_s);
 	wpa_supplicant_cancel_scan(wpa_s);
 
@@ -549,13 +574,13 @@
 
 	wpa_clear_keys(wpa_s, bss->bssid);
 	wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
-	old_ssid = wpa_s->current_ssid;
-	wpa_s->current_ssid = ssid;
-	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
-	wpa_supplicant_initiate_eapol(wpa_s);
 	if (old_ssid != wpa_s->current_ssid)
 		wpas_notify_network_changed(wpa_s);
 
+#ifdef CONFIG_HS20
+	hs20_configure_frame_filters(wpa_s);
+#endif /* CONFIG_HS20 */
+
 #ifdef CONFIG_P2P
 	/*
 	 * If multi-channel concurrency is not supported, check for any
@@ -637,6 +662,10 @@
 		return;
 	}
 
+	/* Starting new connection, so clear the possibly used WPA IE from the
+	 * previous association. */
+	wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
 	sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
 }
 
@@ -862,10 +891,11 @@
 			}
 		}
 		wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AUTH_REJECT MACSTR
-			" auth_type=%u auth_transaction=%u status_code=%u ie=%s",
+			" auth_type=%u auth_transaction=%u status_code=%u%s%s",
 			MAC2STR(data->auth.peer), data->auth.auth_type,
 			data->auth.auth_transaction, data->auth.status_code,
-			ie_txt);
+			ie_txt ? " ie=" : "",
+			ie_txt ? ie_txt : "");
 		os_free(ie_txt);
 
 		if (data->auth.status_code !=
@@ -903,9 +933,17 @@
 
 #ifdef CONFIG_IEEE80211R
 	if (data->auth.auth_type == WLAN_AUTH_FT) {
+		const u8 *ric_ies = NULL;
+		size_t ric_ies_len = 0;
+
+		if (wpa_s->ric_ies) {
+			ric_ies = wpabuf_head(wpa_s->ric_ies);
+			ric_ies_len = wpabuf_len(wpa_s->ric_ies);
+		}
 		if (wpa_ft_process_response(wpa_s->wpa, data->auth.ies,
 					    data->auth.ies_len, 0,
-					    data->auth.peer, NULL, 0) < 0) {
+					    data->auth.peer,
+					    ric_ies, ric_ies_len) < 0) {
 			wpa_dbg(wpa_s, MSG_DEBUG,
 				"SME: FT Authentication response processing failed");
 			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
@@ -920,6 +958,24 @@
 	}
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_FILS
+	if (data->auth.auth_type == WLAN_AUTH_FILS_SK) {
+		if (fils_process_auth(wpa_s->wpa, data->auth.ies,
+				      data->auth.ies_len) < 0) {
+			wpa_dbg(wpa_s, MSG_DEBUG,
+				"SME: FILS Authentication response processing failed");
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid="
+				MACSTR
+				" reason=%d locally_generated=1",
+				MAC2STR(wpa_s->pending_bssid),
+				WLAN_REASON_DEAUTH_LEAVING);
+			wpas_connection_failed(wpa_s, wpa_s->pending_bssid);
+			wpa_supplicant_mark_disassoc(wpa_s);
+			return;
+		}
+	}
+#endif /* CONFIG_FILS */
+
 	sme_associate(wpa_s, ssid->mode, data->auth.peer,
 		      data->auth.auth_type);
 }
@@ -930,6 +986,9 @@
 {
 	struct wpa_driver_associate_params params;
 	struct ieee802_11_elems elems;
+#ifdef CONFIG_FILS
+	u8 nonces[2 * FILS_NONCE_LEN];
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_HT_OVERRIDES
 	struct ieee80211_ht_capabilities htcaps;
 	struct ieee80211_ht_capabilities htcaps_mask;
@@ -940,6 +999,62 @@
 #endif /* CONFIG_VHT_OVERRIDES */
 
 	os_memset(&params, 0, sizeof(params));
+
+#ifdef CONFIG_FILS
+	if (auth_type == WLAN_AUTH_FILS_SK) {
+		struct wpabuf *buf;
+		const u8 *snonce, *anonce;
+		const unsigned int max_hlp = 20;
+		struct wpabuf *hlp[max_hlp];
+		unsigned int i, num_hlp = 0;
+		struct fils_hlp_req *req;
+
+		dl_list_for_each(req, &wpa_s->fils_hlp_req, struct fils_hlp_req,
+				 list) {
+			hlp[num_hlp] = wpabuf_alloc(2 * ETH_ALEN + 6 +
+					      wpabuf_len(req->pkt));
+			if (!hlp[num_hlp])
+				break;
+			wpabuf_put_data(hlp[num_hlp], req->dst, ETH_ALEN);
+			wpabuf_put_data(hlp[num_hlp], wpa_s->own_addr,
+					ETH_ALEN);
+			wpabuf_put_data(hlp[num_hlp],
+					"\xaa\xaa\x03\x00\x00\x00", 6);
+			wpabuf_put_buf(hlp[num_hlp], req->pkt);
+			num_hlp++;
+			if (num_hlp >= max_hlp)
+				break;
+		}
+
+		buf = fils_build_assoc_req(wpa_s->wpa, &params.fils_kek,
+					   &params.fils_kek_len, &snonce,
+					   &anonce,
+					   (const struct wpabuf **) hlp,
+					   num_hlp);
+		for (i = 0; i < num_hlp; i++)
+			wpabuf_free(hlp[i]);
+		if (!buf)
+			return;
+		/* TODO: Make wpa_s->sme.assoc_req_ie use dynamic allocation */
+		if (wpa_s->sme.assoc_req_ie_len + wpabuf_len(buf) >
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			wpa_printf(MSG_ERROR,
+				   "FILS: Not enough buffer room for own AssocReq elements");
+			wpabuf_free(buf);
+			return;
+		}
+		os_memcpy(wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len,
+			  wpabuf_head(buf), wpabuf_len(buf));
+		wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
+		wpabuf_free(buf);
+
+		os_memcpy(nonces, snonce, FILS_NONCE_LEN);
+		os_memcpy(nonces + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
+		params.fils_nonces = nonces;
+		params.fils_nonces_len = sizeof(nonces);
+	}
+#endif /* CONFIG_FILS */
+
 	params.bssid = bssid;
 	params.ssid = wpa_s->sme.ssid;
 	params.ssid_len = wpa_s->sme.ssid_len;
@@ -1572,8 +1687,10 @@
 	nbuf = os_realloc_array(wpa_s->sme.sa_query_trans_id,
 				wpa_s->sme.sa_query_count + 1,
 				WLAN_SA_QUERY_TR_ID_LEN);
-	if (nbuf == NULL)
+	if (nbuf == NULL) {
+		sme_stop_sa_query(wpa_s);
 		return;
+	}
 	if (wpa_s->sme.sa_query_count == 0) {
 		/* Starting a new SA Query procedure */
 		os_get_reltime(&wpa_s->sme.sa_query_start);
@@ -1584,6 +1701,7 @@
 
 	if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
 		wpa_printf(MSG_DEBUG, "Could not generate SA Query ID");
+		sme_stop_sa_query(wpa_s);
 		return;
 	}
 
diff --git a/wpa_supplicant/systemd/wpa_supplicant.service.in b/wpa_supplicant/systemd/wpa_supplicant.service.in
index ea964ce..bc5d49a 100644
--- a/wpa_supplicant/systemd/wpa_supplicant.service.in
+++ b/wpa_supplicant/systemd/wpa_supplicant.service.in
@@ -5,9 +5,9 @@
 
 [Service]
 Type=dbus
-BusName=fi.epitest.hostap.WPASupplicant
+BusName=@DBUS_INTERFACE@
 ExecStart=@BINDIR@/wpa_supplicant -u
 
 [Install]
 WantedBy=multi-user.target
-Alias=dbus-fi.epitest.hostap.WPASupplicant.service
+Alias=dbus-@DBUS_INTERFACE@.service
diff --git a/wpa_supplicant/wmm_ac.h b/wpa_supplicant/wmm_ac.h
index 5171b16..0d15ad0 100644
--- a/wpa_supplicant/wmm_ac.h
+++ b/wpa_supplicant/wmm_ac.h
@@ -88,7 +88,7 @@
  */
 struct wmm_ac_addts_request {
 	/*
-	 * dialog token - Used to link the recived ADDTS response with this
+	 * dialog token - Used to link the received ADDTS response with this
 	 * saved ADDTS request when ADDTS response is being handled
 	 */
 	u8 dialog_token;
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 7dc1909..bd0b517 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -24,6 +24,7 @@
 #define MAX_TFS_IE_LEN  1024
 #define WNM_MAX_NEIGHBOR_REPORT 10
 
+#define WNM_SCAN_RESULT_AGE 2 /* 2 seconds */
 
 /* get the TFS IE from driver */
 static int ieee80211_11_get_tfs_ie(struct wpa_supplicant *wpa_s, u8 *buf,
@@ -501,7 +502,7 @@
 
 
 static struct wpa_bss *
-compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s, os_time_t age_secs)
 {
 
 	u8 i;
@@ -509,7 +510,7 @@
 	struct wpa_bss *target;
 
 	if (!bss)
-		return 0;
+		return NULL;
 
 	wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
 		   MAC2STR(wpa_s->bssid), bss->level);
@@ -534,6 +535,19 @@
 			continue;
 		}
 
+		if (age_secs) {
+			struct os_reltime now;
+
+			if (os_get_reltime(&now) == 0 &&
+			    os_reltime_expired(&now, &target->last_update,
+					       age_secs)) {
+				wpa_printf(MSG_DEBUG,
+					   "Candidate BSS is more than %ld seconds old",
+					   age_secs);
+				continue;
+			}
+		}
+
 		if (bss->ssid_len != target->ssid_len ||
 		    os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
 			/*
@@ -550,7 +564,7 @@
 
 		if (wpa_s->current_ssid &&
 		    !wpa_scan_res_match(wpa_s, 0, target, wpa_s->current_ssid,
-					1)) {
+					1, 0)) {
 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
 				   " (pref %d) does not match the current network profile",
 				   MAC2STR(nei->bssid),
@@ -744,7 +758,7 @@
 		struct wpa_bss *bss = wpa_s->last_scan_res[i];
 		int res;
 
-		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) {
+		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1, 0)) {
 			res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
 			if (res == -2)
 				continue; /* could not build entry for BSS */
@@ -834,6 +848,41 @@
 }
 
 
+static void wnm_bss_tm_connect(struct wpa_supplicant *wpa_s,
+			       struct wpa_bss *bss, struct wpa_ssid *ssid,
+			       int after_new_scan)
+{
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"WNM: Transition to BSS " MACSTR
+		" based on BSS Transition Management Request (old BSSID "
+		MACSTR " after_new_scan=%d)",
+		MAC2STR(bss->bssid), MAC2STR(wpa_s->bssid), after_new_scan);
+
+	/* Send the BSS Management Response - Accept */
+	if (wpa_s->wnm_reply) {
+		wpa_s->wnm_reply = 0;
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Sending successful BSS Transition Management Response");
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  WNM_BSS_TM_ACCEPT,
+						  0, bss->bssid);
+	}
+
+	if (bss == wpa_s->current_bss) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Already associated with the preferred candidate");
+		wnm_deallocate_memory(wpa_s);
+		return;
+	}
+
+	wpa_s->reassociate = 1;
+	wpa_printf(MSG_DEBUG, "WNM: Issuing connect");
+	wpa_supplicant_connect(wpa_s, bss, ssid);
+	wnm_deallocate_memory(wpa_s);
+}
+
+
 int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
 {
 	struct wpa_bss *bss;
@@ -843,6 +892,8 @@
 	if (!wpa_s->wnm_neighbor_report_elements)
 		return 0;
 
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"WNM: Process scan results for BSS Transition Management");
 	if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
 			      &wpa_s->scan_trigger_time)) {
 		wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
@@ -858,7 +909,7 @@
 	}
 
 	/* Compare the Neighbor Report and scan results */
-	bss = compare_scan_neighbor_results(wpa_s);
+	bss = compare_scan_neighbor_results(wpa_s, 0);
 	if (!bss) {
 		wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
 		status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
@@ -866,25 +917,7 @@
 	}
 
 	/* Associate to the network */
-	/* Send the BSS Management Response - Accept */
-	if (wpa_s->wnm_reply) {
-		wpa_s->wnm_reply = 0;
-		wnm_send_bss_transition_mgmt_resp(wpa_s,
-						  wpa_s->wnm_dialog_token,
-						  WNM_BSS_TM_ACCEPT,
-						  0, bss->bssid);
-	}
-
-	if (bss == wpa_s->current_bss) {
-		wpa_printf(MSG_DEBUG,
-			   "WNM: Already associated with the preferred candidate");
-		wnm_deallocate_memory(wpa_s);
-		return 1;
-	}
-
-	wpa_s->reassociate = 1;
-	wpa_supplicant_connect(wpa_s, bss, ssid);
-	wnm_deallocate_memory(wpa_s);
+	wnm_bss_tm_connect(wpa_s, bss, ssid, 1);
 	return 1;
 
 send_bss_resp_fail:
@@ -1025,6 +1058,79 @@
 }
 
 
+static int wnm_fetch_scan_results(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_scan_results *scan_res;
+	struct wpa_bss *bss;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+	u8 i, found = 0;
+	size_t j;
+
+	wpa_dbg(wpa_s, MSG_DEBUG,
+		"WNM: Fetch current scan results from the driver for checking transition candidates");
+	scan_res = wpa_drv_get_scan_results2(wpa_s);
+	if (!scan_res) {
+		wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Failed to get scan results");
+		return 0;
+	}
+
+	if (scan_res->fetch_time.sec == 0)
+		os_get_reltime(&scan_res->fetch_time);
+
+	filter_scan_res(wpa_s, scan_res);
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		struct neighbor_report *nei;
+
+		nei = &wpa_s->wnm_neighbor_report_elements[i];
+		if (nei->preference_present && nei->preference == 0)
+			continue;
+
+		for (j = 0; j < scan_res->num; j++) {
+			struct wpa_scan_res *res;
+			const u8 *ssid_ie;
+
+			res = scan_res->res[j];
+			if (os_memcmp(nei->bssid, res->bssid, ETH_ALEN) != 0 ||
+			    res->age > WNM_SCAN_RESULT_AGE * 1000)
+				continue;
+			bss = wpa_s->current_bss;
+			ssid_ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
+			if (bss && ssid_ie &&
+			    (bss->ssid_len != ssid_ie[1] ||
+			     os_memcmp(bss->ssid, ssid_ie + 2,
+				       bss->ssid_len) != 0))
+				continue;
+
+			/* Potential candidate found */
+			found = 1;
+			scan_snr(res);
+			scan_est_throughput(wpa_s, res);
+			wpa_bss_update_scan_res(wpa_s, res,
+						&scan_res->fetch_time);
+		}
+	}
+
+	wpa_scan_results_free(scan_res);
+	if (!found) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WNM: No transition candidate matches existing scan results");
+		return 0;
+	}
+
+	bss = compare_scan_neighbor_results(wpa_s, WNM_SCAN_RESULT_AGE);
+	if (!bss) {
+		wpa_dbg(wpa_s, MSG_DEBUG,
+			"WNM: Comparison of scan results against transition candidates did not find matches");
+		return 0;
+	}
+
+	/* Associate to the network */
+	wnm_bss_tm_connect(wpa_s, bss, ssid, 0);
+	return 1;
+}
+
+
 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 					     const u8 *pos, const u8 *end,
 					     int reply)
@@ -1055,6 +1161,19 @@
 		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
 		   wpa_s->wnm_dissoc_timer, valid_int);
 
+#if defined(CONFIG_MBO) && defined(CONFIG_TESTING_OPTIONS)
+	if (wpa_s->reject_btm_req_reason) {
+		wpa_printf(MSG_INFO,
+			   "WNM: Testing - reject BSS Transition Management Request: reject_btm_req_reason=%d",
+			   wpa_s->reject_btm_req_reason);
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  wpa_s->reject_btm_req_reason,
+						  0, NULL);
+		return;
+	}
+#endif /* CONFIG_MBO && CONFIG_TESTING_OPTIONS */
+
 	pos += 5;
 
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
@@ -1157,6 +1276,20 @@
 		wpa_s->wnm_cand_valid_until.usec %= 1000000;
 		os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
 
+		/*
+		 * Fetch the latest scan results from the kernel and check for
+		 * candidates based on those results first. This can help in
+		 * finding more up-to-date information should the driver has
+		 * done some internal scanning operations after the last scan
+		 * result update in wpa_supplicant.
+		 */
+		if (wnm_fetch_scan_results(wpa_s) > 0)
+			return;
+
+		/*
+		 * Try to use previously received scan results, if they are
+		 * recent enough to use for a connection.
+		 */
 		if (wpa_s->last_scan_res_used > 0) {
 			struct os_reltime now;
 
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 36a7a4e..964311c 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,7 @@
 #include <dirent.h>
 #endif /* CONFIG_CTRL_IFACE_UNIX */
 
+#include "common/cli.h"
 #include "common/wpa_ctrl.h"
 #include "utils/common.h"
 #include "utils/eloop.h"
@@ -28,43 +29,7 @@
 
 static const char *const wpa_cli_version =
 "wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2016, Jouni Malinen <j@w1.fi> and contributors";
-
-
-static const char *const wpa_cli_license =
-"This software may be distributed under the terms of the BSD license.\n"
-"See README for more details.\n";
-
-static const char *const wpa_cli_full_license =
-"This software may be distributed under the terms of the BSD license.\n"
-"\n"
-"Redistribution and use in source and binary forms, with or without\n"
-"modification, are permitted provided that the following conditions are\n"
-"met:\n"
-"\n"
-"1. Redistributions of source code must retain the above copyright\n"
-"   notice, this list of conditions and the following disclaimer.\n"
-"\n"
-"2. Redistributions in binary form must reproduce the above copyright\n"
-"   notice, this list of conditions and the following disclaimer in the\n"
-"   documentation and/or other materials provided with the distribution.\n"
-"\n"
-"3. Neither the name(s) of the above-listed copyright holder(s) nor the\n"
-"   names of its contributors may be used to endorse or promote products\n"
-"   derived from this software without specific prior written permission.\n"
-"\n"
-"THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n"
-"\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\n"
-"LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\n"
-"A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\n"
-"OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n"
-"SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\n"
-"LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n"
-"DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n"
-"THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n"
-"(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n"
-"OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
-"\n";
+"Copyright (c) 2004-2017, Jouni Malinen <j@w1.fi> and contributors";
 
 #define VENDOR_ELEM_FRAME_ID \
 	"  0: Probe Req (P2P), 1: Probe Resp (P2P) , 2: Probe Resp (GO), " \
@@ -90,16 +55,14 @@
 static int interactive = 0;
 static char *ifname_prefix = NULL;
 
-struct cli_txt_entry {
-	struct dl_list list;
-	char *txt;
-};
-
 static DEFINE_DL_LIST(bsses); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(p2p_peers); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(p2p_groups); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(ifnames); /* struct cli_txt_entry */
 static DEFINE_DL_LIST(networks); /* struct cli_txt_entry */
+#ifdef CONFIG_AP
+static DEFINE_DL_LIST(stations); /* struct cli_txt_entry */
+#endif /* CONFIG_AP */
 
 
 static void print_help(const char *cmd);
@@ -108,6 +71,7 @@
 static char * wpa_cli_get_default_ifname(void);
 static char ** wpa_list_cmd_list(void);
 static void update_networks(struct wpa_ctrl *ctrl);
+static void update_stations(struct wpa_ctrl *ctrl);
 
 
 static void usage(void)
@@ -130,168 +94,6 @@
 }
 
 
-static void cli_txt_list_free(struct cli_txt_entry *e)
-{
-	dl_list_del(&e->list);
-	os_free(e->txt);
-	os_free(e);
-}
-
-
-static void cli_txt_list_flush(struct dl_list *list)
-{
-	struct cli_txt_entry *e;
-	while ((e = dl_list_first(list, struct cli_txt_entry, list)))
-		cli_txt_list_free(e);
-}
-
-
-static struct cli_txt_entry * cli_txt_list_get(struct dl_list *txt_list,
-					       const char *txt)
-{
-	struct cli_txt_entry *e;
-	dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
-		if (os_strcmp(e->txt, txt) == 0)
-			return e;
-	}
-	return NULL;
-}
-
-
-static void cli_txt_list_del(struct dl_list *txt_list, const char *txt)
-{
-	struct cli_txt_entry *e;
-	e = cli_txt_list_get(txt_list, txt);
-	if (e)
-		cli_txt_list_free(e);
-}
-
-
-static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
-{
-	u8 addr[ETH_ALEN];
-	char buf[18];
-	if (hwaddr_aton(txt, addr) < 0)
-		return;
-	os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
-	cli_txt_list_del(txt_list, buf);
-}
-
-
-#ifdef CONFIG_P2P
-static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt,
-				  int separator)
-{
-	const char *end;
-	char *buf;
-	end = os_strchr(txt, separator);
-	if (end == NULL)
-		end = txt + os_strlen(txt);
-	buf = dup_binstr(txt, end - txt);
-	if (buf == NULL)
-		return;
-	cli_txt_list_del(txt_list, buf);
-	os_free(buf);
-}
-#endif /* CONFIG_P2P */
-
-
-static int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
-{
-	struct cli_txt_entry *e;
-	e = cli_txt_list_get(txt_list, txt);
-	if (e)
-		return 0;
-	e = os_zalloc(sizeof(*e));
-	if (e == NULL)
-		return -1;
-	e->txt = os_strdup(txt);
-	if (e->txt == NULL) {
-		os_free(e);
-		return -1;
-	}
-	dl_list_add(txt_list, &e->list);
-	return 0;
-}
-
-
-#ifdef CONFIG_P2P
-static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
-{
-	u8 addr[ETH_ALEN];
-	char buf[18];
-	if (hwaddr_aton(txt, addr) < 0)
-		return -1;
-	os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
-	return cli_txt_list_add(txt_list, buf);
-}
-#endif /* CONFIG_P2P */
-
-
-static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt,
-				 int separator)
-{
-	const char *end;
-	char *buf;
-	int ret;
-	end = os_strchr(txt, separator);
-	if (end == NULL)
-		end = txt + os_strlen(txt);
-	buf = dup_binstr(txt, end - txt);
-	if (buf == NULL)
-		return -1;
-	ret = cli_txt_list_add(txt_list, buf);
-	os_free(buf);
-	return ret;
-}
-
-
-static char ** cli_txt_list_array(struct dl_list *txt_list)
-{
-	unsigned int i, count = dl_list_len(txt_list);
-	char **res;
-	struct cli_txt_entry *e;
-
-	res = os_calloc(count + 1, sizeof(char *));
-	if (res == NULL)
-		return NULL;
-
-	i = 0;
-	dl_list_for_each(e, txt_list, struct cli_txt_entry, list) {
-		res[i] = os_strdup(e->txt);
-		if (res[i] == NULL)
-			break;
-		i++;
-	}
-
-	return res;
-}
-
-
-static int get_cmd_arg_num(const char *str, int pos)
-{
-	int arg = 0, i;
-
-	for (i = 0; i <= pos; i++) {
-		if (str[i] != ' ') {
-			arg++;
-			while (i <= pos && str[i] != ' ')
-				i++;
-		}
-	}
-
-	if (arg > 0)
-		arg--;
-	return arg;
-}
-
-
-static int str_starts(const char *src, const char *match)
-{
-	return os_strncmp(src, match, os_strlen(match)) == 0;
-}
-
-
 static int wpa_cli_show_event(const char *event)
 {
 	const char *start;
@@ -416,7 +218,7 @@
 }
 
 
-static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd, int print)
 {
 	char buf[4096];
 	size_t len;
@@ -452,42 +254,12 @@
 }
 
 
-static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+static int wpa_ctrl_command(struct wpa_ctrl *ctrl, const char *cmd)
 {
 	return _wpa_ctrl_command(ctrl, cmd, 1);
 }
 
 
-static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
-		     char *argv[])
-{
-	int i, res;
-	char *pos, *end;
-
-	pos = buf;
-	end = buf + buflen;
-
-	res = os_snprintf(pos, end - pos, "%s", cmd);
-	if (os_snprintf_error(end - pos, res))
-		goto fail;
-	pos += res;
-
-	for (i = 0; i < argc; i++) {
-		res = os_snprintf(pos, end - pos, " %s", argv[i]);
-		if (os_snprintf_error(end - pos, res))
-			goto fail;
-		pos += res;
-	}
-
-	buf[buflen - 1] = '\0';
-	return 0;
-
-fail:
-	printf("Too long command\n");
-	return -1;
-}
-
-
 static int wpa_cli_cmd(struct wpa_ctrl *ctrl, const char *cmd, int min_args,
 		       int argc, char *argv[])
 {
@@ -563,6 +335,39 @@
 }
 
 
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+
+static int wpa_cli_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "PMKSA_GET", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "PMKSA_ADD", 8, argc, argv);
+}
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_cli_mesh_cmd_pmksa_get(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_PMKSA_GET", 1, argc, argv);
+}
+
+
+static int wpa_cli_mesh_cmd_pmksa_add(struct wpa_ctrl *ctrl, int argc,
+				      char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "MESH_PMKSA_ADD", 4, argc, argv);
+}
+
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+
 static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	print_help(argc > 0 ? argv[0] : NULL);
@@ -587,7 +392,7 @@
 
 static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
-	printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license);
+	printf("%s\n\n%s\n", wpa_cli_version, cli_full_license);
 	return 0;
 }
 
@@ -683,7 +488,11 @@
 		"tdls_external_control", "osu_dir", "wowlan_triggers",
 		"p2p_search_delay", "mac_addr", "rand_addr_lifetime",
 		"preassoc_mac_addr", "key_mgmt_offload", "passive_scan",
-		"reassoc_same_bss_optim", "wps_priority"
+		"reassoc_same_bss_optim", "wps_priority",
+#ifdef CONFIG_TESTING_OPTIONS
+		"ignore_auth_resp",
+#endif /* CONFIG_TESTING_OPTIONS */
+		"relative_rssi", "relative_band_adjust",
 	};
 	int i, num_fields = ARRAY_SIZE(fields);
 
@@ -711,6 +520,13 @@
 }
 
 
+static int wpa_cli_cmd_driver_flags(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_ctrl_command(ctrl, "DRIVER_FLAGS");
+}
+
+
 static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "GET", 1, argc, argv);
@@ -1608,11 +1424,14 @@
 	"ap_max_inactivity", "dtim_period", "beacon_int",
 #ifdef CONFIG_MACSEC
 	"macsec_policy",
+	"macsec_integ_only",
+	"macsec_port",
+	"mka_priority",
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
 	"update_identifier",
 #endif /* CONFIG_HS20 */
-	"mac_addr", "pbss"
+	"mac_addr", "pbss", "wps_disabled"
 };
 
 
@@ -1817,6 +1636,48 @@
 }
 
 
+static char ** wpa_cli_complete_get_capability(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	const char *fields[] = {
+		"eap", "pairwise", "group", "group_mgmt", "key_mgmt",
+		"proto", "auth_alg", "modes", "channels", "freq",
+#ifdef CONFIG_TDLS
+		"tdls",
+#endif /* CONFIG_TDLS */
+#ifdef CONFIG_ERP
+		"erp",
+#endif /* CONFIG_ERP */
+#ifdef CONFIG_FIPS
+		"fips",
+#endif /* CONFIG_FIPS */
+#ifdef CONFIG_ACS
+		"acs",
+#endif /* CONFIG_ACS */
+	};
+	int i, num_fields = ARRAY_SIZE(fields);
+	char **res = NULL;
+
+	if (arg == 1) {
+		res = os_calloc(num_fields + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		for (i = 0; i < num_fields; i++) {
+			res[i] = os_strdup(fields[i]);
+			if (res[i] == NULL)
+				return res;
+		}
+	}
+	if (arg == 2) {
+		res = os_calloc(1 + 1, sizeof(char *));
+		if (res == NULL)
+			return NULL;
+		res[0] = os_strdup("strict");
+	}
+	return res;
+}
+
+
 static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl)
 {
 	printf("Available interfaces:\n");
@@ -1916,8 +1777,23 @@
 }
 
 
-static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
-				char *addr, size_t addr_len)
+static char ** wpa_cli_complete_sta(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, const char *cmd,
+				char *addr, size_t addr_len, int print)
 {
 	char buf[4096], *pos;
 	size_t len;
@@ -1947,7 +1823,8 @@
 	buf[len] = '\0';
 	if (os_memcmp(buf, "FAIL", 4) == 0)
 		return -1;
-	printf("%s", buf);
+	if (print)
+		printf("%s", buf);
 
 	pos = buf;
 	while (*pos != '\0' && *pos != '\n')
@@ -1962,16 +1839,33 @@
 {
 	char addr[32], cmd[64];
 
-	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr)))
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 1))
 		return 0;
 	do {
 		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
-	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 1) == 0);
 
 	return -1;
 }
 
 
+static int wpa_cli_cmd_list_sta(struct wpa_ctrl *ctrl, int argc,
+				char *argv[])
+{
+	char addr[32], cmd[64];
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return 0;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			printf("%s\n", addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+
+	return 0;
+}
+
+
 static int wpa_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
 				      char *argv[])
 {
@@ -1979,12 +1873,43 @@
 }
 
 
+static char ** wpa_cli_complete_deauthenticate(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
 static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
 {
 	return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
 }
 
+
+static char ** wpa_cli_complete_disassociate(const char *str, int pos)
+{
+	int arg = get_cmd_arg_num(str, pos);
+	char **res = NULL;
+
+	switch (arg) {
+	case 1:
+		res = cli_txt_list_array(&stations);
+		break;
+	}
+
+	return res;
+}
+
+
 static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc,
 				    char *argv[])
 {
@@ -2175,6 +2100,13 @@
 }
 
 
+static int wpa_cli_cmd_p2p_group_member(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_GROUP_MEMBER", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_p2p_prov_disc(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
@@ -2349,7 +2281,7 @@
 }
 
 
-static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, char *cmd,
+static int wpa_ctrl_command_p2p_peer(struct wpa_ctrl *ctrl, const char *cmd,
 				     char *addr, size_t addr_len,
 				     int discovered)
 {
@@ -2885,6 +2817,20 @@
 }
 
 
+static int wpa_cli_cmd_p2p_lo_start(struct wpa_ctrl *ctrl, int argc,
+				    char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_LO_START", 4, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_lo_stop(struct wpa_ctrl *ctrl, int argc,
+				   char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "P2P_LO_STOP", 0, argc, argv);
+}
+
+
 enum wpa_cli_cmd_flags {
 	cli_cmd_flag_none		= 0x00,
 	cli_cmd_flag_sensitive		= 0x01
@@ -2942,6 +2888,9 @@
 	{ "get", wpa_cli_cmd_get, wpa_cli_complete_get,
 	  cli_cmd_flag_none,
 	  "<name> = get information" },
+	{ "driver_flags", wpa_cli_cmd_driver_flags, NULL,
+	  cli_cmd_flag_none,
+	  "= list driver flags" },
 	{ "logon", wpa_cli_cmd_logon, NULL,
 	  cli_cmd_flag_none,
 	  "= IEEE 802.1X EAPOL state machine logon" },
@@ -2954,6 +2903,22 @@
 	{ "pmksa_flush", wpa_cli_cmd_pmksa_flush, NULL,
 	  cli_cmd_flag_none,
 	  "= flush PMKSA cache entries" },
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+	{ "pmksa_get", wpa_cli_cmd_pmksa_get, NULL,
+	  cli_cmd_flag_none,
+	  "<network_id> = fetch all stored PMKSA cache entries" },
+	{ "pmksa_add", wpa_cli_cmd_pmksa_add, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<network_id> <BSSID> <PMKID> <PMK> <reauth_time in seconds> <expiration in seconds> <akmp> <opportunistic> = store PMKSA cache entry from external storage" },
+#ifdef CONFIG_MESH
+	{ "mesh_pmksa_get", wpa_cli_mesh_cmd_pmksa_get, NULL,
+	  cli_cmd_flag_none,
+	  "<peer MAC address | any> = fetch all stored mesh PMKSA cache entries" },
+	{ "mesh_pmksa_add", wpa_cli_mesh_cmd_pmksa_add, NULL,
+	  cli_cmd_flag_sensitive,
+	  "<BSSID> <PMKID> <PMK> <expiration in seconds> = store mesh PMKSA cache entry from external storage" },
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
 	{ "reassociate", wpa_cli_cmd_reassociate, NULL,
 	  cli_cmd_flag_none,
 	  "= force reassociation" },
@@ -2963,30 +2928,30 @@
 	{ "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<BSSID> = force preauthentication" },
-	{ "identity", wpa_cli_cmd_identity, NULL,
+	{ "identity", wpa_cli_cmd_identity, wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> <identity> = configure identity for an SSID" },
-	{ "password", wpa_cli_cmd_password, NULL,
+	{ "password", wpa_cli_cmd_password, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <password> = configure password for an SSID" },
-	{ "new_password", wpa_cli_cmd_new_password, NULL,
-	  cli_cmd_flag_sensitive,
+	{ "new_password", wpa_cli_cmd_new_password,
+	  wpa_cli_complete_network_id, cli_cmd_flag_sensitive,
 	  "<network id> <password> = change password for an SSID" },
-	{ "pin", wpa_cli_cmd_pin, NULL,
+	{ "pin", wpa_cli_cmd_pin, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <pin> = configure pin for an SSID" },
-	{ "otp", wpa_cli_cmd_otp, NULL,
+	{ "otp", wpa_cli_cmd_otp, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <password> = configure one-time-password for an SSID"
 	},
-	{ "passphrase", wpa_cli_cmd_passphrase, NULL,
+	{ "passphrase", wpa_cli_cmd_passphrase, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <passphrase> = configure private key passphrase\n"
 	  "  for an SSID" },
-	{ "sim", wpa_cli_cmd_sim, NULL,
+	{ "sim", wpa_cli_cmd_sim, wpa_cli_complete_network_id,
 	  cli_cmd_flag_sensitive,
 	  "<network id> <pin> = report SIM operation result" },
-	{ "bssid", wpa_cli_cmd_bssid, NULL,
+	{ "bssid", wpa_cli_cmd_bssid, wpa_cli_complete_network_id,
 	  cli_cmd_flag_none,
 	  "<network id> <BSSID> = set preferred BSSID for an SSID" },
 	{ "blacklist", wpa_cli_cmd_blacklist, wpa_cli_complete_bss,
@@ -3069,8 +3034,8 @@
 	{ "bss", wpa_cli_cmd_bss, wpa_cli_complete_bss,
 	  cli_cmd_flag_none,
 	  "<<idx> | <bssid>> = get detailed scan result info" },
-	{ "get_capability", wpa_cli_cmd_get_capability, NULL,
-	  cli_cmd_flag_none,
+	{ "get_capability", wpa_cli_cmd_get_capability,
+	  wpa_cli_complete_get_capability, cli_cmd_flag_none,
 	  "<eap/pairwise/group/key_mgmt/proto/auth_alg/channels/freq/modes> "
 	  "= get capabilities" },
 	{ "reconfigure", wpa_cli_cmd_reconfigure, NULL,
@@ -3185,17 +3150,20 @@
 	  cli_cmd_flag_none,
 	  "<addr> = request RSN authentication with <addr> in IBSS" },
 #ifdef CONFIG_AP
-	{ "sta", wpa_cli_cmd_sta, NULL,
+	{ "sta", wpa_cli_cmd_sta, wpa_cli_complete_sta,
 	  cli_cmd_flag_none,
 	  "<addr> = get information about an associated station (AP)" },
 	{ "all_sta", wpa_cli_cmd_all_sta, NULL,
 	  cli_cmd_flag_none,
 	  "= get information about all associated stations (AP)" },
-	{ "deauthenticate", wpa_cli_cmd_deauthenticate, NULL,
+	{ "list_sta", wpa_cli_cmd_list_sta, NULL,
 	  cli_cmd_flag_none,
+	  "= list all stations (AP)" },
+	{ "deauthenticate", wpa_cli_cmd_deauthenticate,
+	  wpa_cli_complete_deauthenticate, cli_cmd_flag_none,
 	  "<addr> = deauthenticate a station" },
-	{ "disassociate", wpa_cli_cmd_disassociate, NULL,
-	  cli_cmd_flag_none,
+	{ "disassociate", wpa_cli_cmd_disassociate,
+	  wpa_cli_complete_disassociate, cli_cmd_flag_none,
 	  "<addr> = disassociate a station" },
 	{ "chan_switch", wpa_cli_cmd_chanswitch, NULL,
 	  cli_cmd_flag_none,
@@ -3253,6 +3221,9 @@
 	  "<ifname> = remove P2P group interface (terminate group if GO)" },
 	{ "p2p_group_add", wpa_cli_cmd_p2p_group_add, NULL, cli_cmd_flag_none,
 	  "[ht40] = add a new P2P group (local end as GO)" },
+	{ "p2p_group_member", wpa_cli_cmd_p2p_group_member, NULL,
+	  cli_cmd_flag_none,
+	  "<dev_addr> = Get peer interface address on local GO using peer Device Address" },
 	{ "p2p_prov_disc", wpa_cli_cmd_p2p_prov_disc,
 	  wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
 	  "<addr> <method> = request provisioning discovery" },
@@ -3455,8 +3426,7 @@
 	},
 	{ "neighbor_rep_request",
 	  wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none,
-	  "[ssid=<SSID>] = Trigger request to AP for neighboring AP report "
-	  "(with optional given SSID, default: current SSID)"
+	  "[ssid=<SSID>] [lci] [civic] = Trigger request to AP for neighboring AP report (with optional given SSID in hex or enclosed in double quotes, default: current SSID; with optional LCI and location civic request)"
 	},
 	{ "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none,
 	  "= flush ERP keys" },
@@ -3468,6 +3438,12 @@
 	{ "get_pref_freq_list", wpa_cli_cmd_get_pref_freq_list, NULL,
 	  cli_cmd_flag_none,
 	  "<interface type> = retrieve preferred freq list for the specified interface type" },
+	{ "p2p_lo_start", wpa_cli_cmd_p2p_lo_start, NULL,
+	  cli_cmd_flag_none,
+	  "<freq> <period> <interval> <count> = start P2P listen offload" },
+	{ "p2p_lo_stop", wpa_cli_cmd_p2p_lo_stop, NULL,
+	  cli_cmd_flag_none,
+	  "= stop P2P listen offload" },
 	{ NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -3666,12 +3642,6 @@
 }
 
 
-static int str_match(const char *a, const char *b)
-{
-	return os_strncmp(a, b, os_strlen(b)) == 0;
-}
-
-
 static int wpa_cli_exec(const char *program, const char *arg1,
 			const char *arg2)
 {
@@ -3727,7 +3697,7 @@
 			pos = prev;
 	}
 
-	if (str_match(pos, WPA_EVENT_CONNECTED)) {
+	if (str_starts(pos, WPA_EVENT_CONNECTED)) {
 		int new_id = -1;
 		os_unsetenv("WPA_ID");
 		os_unsetenv("WPA_ID_STR");
@@ -3763,44 +3733,48 @@
 			wpa_cli_last_id = new_id;
 			wpa_cli_exec(action_file, ifname, "CONNECTED");
 		}
-	} else if (str_match(pos, WPA_EVENT_DISCONNECTED)) {
+	} else if (str_starts(pos, WPA_EVENT_DISCONNECTED)) {
 		if (wpa_cli_connected) {
 			wpa_cli_connected = 0;
 			wpa_cli_exec(action_file, ifname, "DISCONNECTED");
 		}
-	} else if (str_match(pos, MESH_GROUP_STARTED)) {
+	} else if (str_starts(pos, AP_EVENT_ENABLED)) {
 		wpa_cli_exec(action_file, ctrl_ifname, pos);
-	} else if (str_match(pos, MESH_GROUP_REMOVED)) {
+	} else if (str_starts(pos, AP_EVENT_DISABLED)) {
 		wpa_cli_exec(action_file, ctrl_ifname, pos);
-	} else if (str_match(pos, MESH_PEER_CONNECTED)) {
+	} else if (str_starts(pos, MESH_GROUP_STARTED)) {
 		wpa_cli_exec(action_file, ctrl_ifname, pos);
-	} else if (str_match(pos, MESH_PEER_DISCONNECTED)) {
+	} else if (str_starts(pos, MESH_GROUP_REMOVED)) {
 		wpa_cli_exec(action_file, ctrl_ifname, pos);
-	} else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
+	} else if (str_starts(pos, MESH_PEER_CONNECTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_starts(pos, MESH_PEER_DISCONNECTED)) {
+		wpa_cli_exec(action_file, ctrl_ifname, pos);
+	} else if (str_starts(pos, P2P_EVENT_GROUP_STARTED)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
+	} else if (str_starts(pos, P2P_EVENT_GROUP_REMOVED)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
+	} else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
+	} else if (str_starts(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) {
+	} else if (str_starts(pos, P2P_EVENT_GO_NEG_FAILURE)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, WPS_EVENT_SUCCESS)) {
+	} else if (str_starts(pos, WPS_EVENT_SUCCESS)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, WPS_EVENT_FAIL)) {
+	} else if (str_starts(pos, WPS_EVENT_FAIL)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, AP_STA_CONNECTED)) {
+	} else if (str_starts(pos, AP_STA_CONNECTED)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, AP_STA_DISCONNECTED)) {
+	} else if (str_starts(pos, AP_STA_DISCONNECTED)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, ESS_DISASSOC_IMMINENT)) {
+	} else if (str_starts(pos, ESS_DISASSOC_IMMINENT)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
+	} else if (str_starts(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
+	} else if (str_starts(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
 		wpa_cli_exec(action_file, ifname, pos);
-	} else if (str_match(pos, WPA_EVENT_TERMINATING)) {
+	} else if (str_starts(pos, WPA_EVENT_TERMINATING)) {
 		printf("wpa_supplicant is terminating - stop monitoring\n");
 		wpa_cli_quit = 1;
 	}
@@ -3825,6 +3799,7 @@
 		edit_clear_line();
 		printf("\rConnection to wpa_supplicant re-established\n");
 		edit_redraw();
+		update_stations(ctrl_conn);
 	}
 }
 
@@ -3910,7 +3885,7 @@
 			pos = msg;
 	}
 
-	if (str_match(pos, WPA_EVENT_TERMINATING) && ctrl_conn) {
+	if (str_starts(pos, WPA_EVENT_TERMINATING) && ctrl_conn) {
 		edit_clear_line();
 		printf("\rConnection to wpa_supplicant lost - trying to "
 		       "reconnect\n");
@@ -3961,37 +3936,6 @@
 	}
 }
 
-#define max_args 10
-
-static int tokenize_cmd(char *cmd, char *argv[])
-{
-	char *pos;
-	int argc = 0;
-
-	pos = cmd;
-	for (;;) {
-		while (*pos == ' ')
-			pos++;
-		if (*pos == '\0')
-			break;
-		argv[argc] = pos;
-		argc++;
-		if (argc == max_args)
-			break;
-		if (*pos == '"') {
-			char *pos2 = os_strrchr(pos, '"');
-			if (pos2)
-				pos = pos2 + 1;
-		}
-		while (*pos != '\0' && *pos != ' ')
-			pos++;
-		if (*pos == ' ')
-			*pos++ = '\0';
-	}
-
-	return argc;
-}
-
 
 static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
 {
@@ -4078,7 +4022,7 @@
 	char buf[4096];
 	size_t len = sizeof(buf);
 	int ret;
-	char *cmd = "BSS RANGE=ALL MASK=0x2";
+	const char *cmd = "BSS RANGE=ALL MASK=0x2";
 	char *pos, *end;
 
 	if (ctrl == NULL)
@@ -4109,7 +4053,7 @@
 	char buf[4096];
 	size_t len = sizeof(buf);
 	int ret;
-	char *cmd = "INTERFACES";
+	const char *cmd = "INTERFACES";
 	char *pos, *end;
 	char txt[200];
 
@@ -4141,7 +4085,7 @@
 	char buf[4096];
 	size_t len = sizeof(buf);
 	int ret;
-	char *cmd = "LIST_NETWORKS";
+	const char *cmd = "LIST_NETWORKS";
 	char *pos, *end;
 	int header = 1;
 
@@ -4168,6 +4112,27 @@
 }
 
 
+static void update_stations(struct wpa_ctrl *ctrl)
+{
+#ifdef CONFIG_AP
+	char addr[32], cmd[64];
+
+	if (!ctrl || !interactive)
+		return;
+
+	cli_txt_list_flush(&stations);
+
+	if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr), 0))
+		return;
+	do {
+		if (os_strcmp(addr, "") != 0)
+			cli_txt_list_add(&stations, addr);
+		os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+	} while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr), 0) == 0);
+#endif /* CONFIG_AP */
+}
+
+
 static void try_connection(void *eloop_ctx, void *timeout_ctx)
 {
 	if (ctrl_conn)
@@ -4189,6 +4154,7 @@
 
 	update_bssid_list(ctrl_conn);
 	update_networks(ctrl_conn);
+	update_stations(ctrl_conn);
 
 	if (warning_displayed)
 		printf("Connection established.\n");
@@ -4401,7 +4367,7 @@
 	interactive = (argc == optind) && (action_file == NULL);
 
 	if (interactive)
-		printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
+		printf("%s\n\n%s\n\n", wpa_cli_version, cli_license);
 
 	if (eloop_init())
 		return -1;
@@ -4435,6 +4401,7 @@
 					       "control interface\n");
 				}
 			}
+			update_stations(ctrl_conn);
 		}
 	}
 
diff --git a/wpa_supplicant/wpa_client_include/libwpa_client/qca-vendor.h b/wpa_supplicant/wpa_client_include/libwpa_client/qca-vendor.h
new file mode 120000
index 0000000..c074acd
--- /dev/null
+++ b/wpa_supplicant/wpa_client_include/libwpa_client/qca-vendor.h
@@ -0,0 +1 @@
+../../src/common/qca-vendor.h
\ No newline at end of file
diff --git a/wpa_supplicant/wpa_client_include/libwpa_client/wpa_ctrl.h b/wpa_supplicant/wpa_client_include/libwpa_client/wpa_ctrl.h
new file mode 120000
index 0000000..d1116dc
--- /dev/null
+++ b/wpa_supplicant/wpa_client_include/libwpa_client/wpa_ctrl.h
@@ -0,0 +1 @@
+../../src/common/wpa_ctrl.h
\ No newline at end of file
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
index 9b568f0..adca1cc 100644
--- a/wpa_supplicant/wpa_passphrase.c
+++ b/wpa_supplicant/wpa_passphrase.c
@@ -17,6 +17,7 @@
 	unsigned char psk[32];
 	int i;
 	char *ssid, *passphrase, buf[64], *pos;
+	size_t len;
 
 	if (argc < 2) {
 		printf("usage: wpa_passphrase <ssid> [passphrase]\n"
@@ -47,10 +48,15 @@
 		passphrase = buf;
 	}
 
-	if (os_strlen(passphrase) < 8 || os_strlen(passphrase) > 63) {
+	len = os_strlen(passphrase);
+	if (len < 8 || len > 63) {
 		printf("Passphrase must be 8..63 characters\n");
 		return 1;
 	}
+	if (has_ctrl_char((u8 *) passphrase, len)) {
+		printf("Invalid passphrase character\n");
+		return 1;
+	}
 
 	pbkdf2_sha1(passphrase, (u8 *) ssid, os_strlen(ssid), 4096, psk, 32);
 
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
index 511df4f..46cb95e 100644
--- a/wpa_supplicant/wpa_priv.c
+++ b/wpa_supplicant/wpa_priv.c
@@ -21,6 +21,7 @@
 #include "common/privsep_commands.h"
 #include "common/ieee802_11_defs.h"
 
+#define WPA_PRIV_MAX_L2 3
 
 struct wpa_priv_interface {
 	struct wpa_priv_interface *next;
@@ -35,11 +36,16 @@
 	void *drv_priv;
 	void *drv_global_priv;
 	struct sockaddr_un drv_addr;
+	socklen_t drv_addr_len;
 	int wpas_registered;
 
-	/* TODO: add support for multiple l2 connections */
-	struct l2_packet_data *l2;
-	struct sockaddr_un l2_addr;
+	struct l2_packet_data *l2[WPA_PRIV_MAX_L2];
+	struct sockaddr_un l2_addr[WPA_PRIV_MAX_L2];
+	socklen_t l2_addr_len[WPA_PRIV_MAX_L2];
+	struct wpa_priv_l2 {
+		struct wpa_priv_interface *parent;
+		int idx;
+	} l2_ctx[WPA_PRIV_MAX_L2];
 };
 
 struct wpa_priv_global {
@@ -48,8 +54,10 @@
 
 
 static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
-				  struct sockaddr_un *from)
+				  struct sockaddr_un *from, socklen_t fromlen)
 {
+	int i;
+
 	if (iface->drv_priv) {
 		wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
 		if (iface->driver->deinit)
@@ -62,11 +70,13 @@
 		iface->wpas_registered = 0;
 	}
 
-	if (iface->l2) {
-		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
-			   "instance");
-		l2_packet_deinit(iface->l2);
-		iface->l2 = NULL;
+	for (i = 0; i < WPA_PRIV_MAX_L2; i++) {
+		if (iface->l2[i]) {
+			wpa_printf(MSG_DEBUG,
+				   "Cleaning up forgotten l2_packet instance");
+			l2_packet_deinit(iface->l2[i]);
+			iface->l2[i] = NULL;
+		}
 	}
 
 	if (iface->driver->init2) {
@@ -96,7 +106,8 @@
 	wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface "
 		   "'%s'", iface->driver_name, iface->ifname);
 
-	os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr));
+	os_memcpy(&iface->drv_addr, from, fromlen);
+	iface->drv_addr_len = fromlen;
 	iface->wpas_registered = 1;
 
 	if (iface->driver->set_param &&
@@ -123,18 +134,43 @@
 
 
 static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
-			      char *buf, size_t len)
+			      void *buf, size_t len)
 {
 	struct wpa_driver_scan_params params;
+	struct privsep_cmd_scan *scan;
+	unsigned int i;
+	int freqs[PRIVSEP_MAX_SCAN_FREQS + 1];
 
 	if (iface->drv_priv == NULL)
 		return;
 
+	if (len < sizeof(*scan)) {
+		wpa_printf(MSG_DEBUG, "Invalid scan request");
+		return;
+	}
+
+	scan = buf;
+
 	os_memset(&params, 0, sizeof(params));
-	if (len) {
-		params.ssids[0].ssid = (u8 *) buf;
-		params.ssids[0].ssid_len = len;
-		params.num_ssids = 1;
+	if (scan->num_ssids > WPAS_MAX_SCAN_SSIDS) {
+		wpa_printf(MSG_DEBUG, "Invalid scan request (num_ssids)");
+		return;
+	}
+	params.num_ssids = scan->num_ssids;
+	for (i = 0; i < scan->num_ssids; i++) {
+		params.ssids[i].ssid = scan->ssids[i];
+		params.ssids[i].ssid_len = scan->ssid_lens[i];
+	}
+
+	if (scan->num_freqs > PRIVSEP_MAX_SCAN_FREQS) {
+		wpa_printf(MSG_DEBUG, "Invalid scan request (num_freqs)");
+		return;
+	}
+	if (scan->num_freqs) {
+		for (i = 0; i < scan->num_freqs; i++)
+			freqs[i] = scan->freqs[i];
+		freqs[i] = 0;
+		params.freqs = freqs;
 	}
 
 	if (iface->driver->scan2)
@@ -143,7 +179,8 @@
 
 
 static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
-				       struct sockaddr_un *from)
+				       struct sockaddr_un *from,
+				       socklen_t fromlen)
 {
 	struct wpa_scan_results *res;
 	u8 *buf = NULL, *pos, *end;
@@ -165,7 +202,7 @@
 
 	for (i = 0; i < res->num; i++) {
 		struct wpa_scan_res *r = res->res[i];
-		val = sizeof(*r) + r->ie_len;
+		val = sizeof(*r) + r->ie_len + r->beacon_ie_len;
 		if (end - pos < (int) sizeof(int) + val)
 			break;
 		os_memcpy(pos, &val, sizeof(int));
@@ -174,8 +211,7 @@
 		pos += val;
 	}
 
-	sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
-	       sizeof(*from));
+	sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from, fromlen);
 
 	os_free(buf);
 	wpa_scan_results_free(res);
@@ -184,21 +220,21 @@
 fail:
 	os_free(buf);
 	wpa_scan_results_free(res);
-	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
 }
 
 
 static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface,
-					  struct sockaddr_un *from)
+					  struct sockaddr_un *from,
+					  socklen_t fromlen)
 {
 	if (iface->drv_priv == NULL)
 		return;
 
 	if (iface->driver->get_scan_results2)
-		wpa_priv_get_scan_results2(iface, from);
+		wpa_priv_get_scan_results2(iface, from, fromlen);
 	else
-		sendto(iface->fd, "", 0, 0, (struct sockaddr *) from,
-		       sizeof(*from));
+		sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
 }
 
 
@@ -218,7 +254,7 @@
 	}
 
 	auth = buf;
-	if (sizeof(*auth) + auth->ie_len + auth->sae_data_len > len) {
+	if (sizeof(*auth) + auth->ie_len + auth->auth_data_len > len) {
 		wpa_printf(MSG_DEBUG, "Authentication request overflow");
 		return;
 	}
@@ -244,9 +280,9 @@
 		params.ie = (u8 *) (auth + 1);
 		params.ie_len = auth->ie_len;
 	}
-	if (auth->sae_data_len) {
-		params.sae_data = ((u8 *) (auth + 1)) + auth->ie_len;
-		params.sae_data_len = auth->sae_data_len;
+	if (auth->auth_data_len) {
+		params.auth_data = ((u8 *) (auth + 1)) + auth->ie_len;
+		params.auth_data_len = auth->auth_data_len;
 	}
 
 	res = iface->driver->authenticate(iface->drv_priv, &params);
@@ -303,7 +339,7 @@
 
 
 static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
-				   struct sockaddr_un *from)
+				   struct sockaddr_un *from, socklen_t fromlen)
 {
 	u8 bssid[ETH_ALEN];
 
@@ -315,16 +351,16 @@
 		goto fail;
 
 	sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from,
-	       sizeof(*from));
+	       fromlen);
 	return;
 
 fail:
-	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
 }
 
 
 static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
-				  struct sockaddr_un *from)
+				  struct sockaddr_un *from, socklen_t fromlen)
 {
 	u8 ssid[sizeof(int) + SSID_MAX_LEN];
 	int res;
@@ -335,17 +371,18 @@
 	if (iface->driver->get_ssid == NULL)
 		goto fail;
 
+	os_memset(ssid, 0, sizeof(ssid));
 	res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
 	if (res < 0 || res > SSID_MAX_LEN)
 		goto fail;
 	os_memcpy(ssid, &res, sizeof(int));
 
 	sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from,
-	       sizeof(*from));
+	       fromlen);
 	return;
 
 fail:
-	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
 }
 
 
@@ -378,7 +415,7 @@
 
 
 static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
-				  struct sockaddr_un *from)
+				  struct sockaddr_un *from, socklen_t fromlen)
 {
 	struct wpa_driver_capa capa;
 
@@ -394,18 +431,19 @@
 	capa.extended_capa_mask = NULL;
 	capa.extended_capa_len = 0;
 	sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
-	       sizeof(*from));
+	       fromlen);
 	return;
 
 fail:
-	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+	sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, fromlen);
 }
 
 
 static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
 			   size_t len)
 {
-	struct wpa_priv_interface *iface = ctx;
+	struct wpa_priv_l2 *l2_ctx = ctx;
+	struct wpa_priv_interface *iface = l2_ctx->parent;
 	struct msghdr msg;
 	struct iovec io[2];
 
@@ -417,8 +455,8 @@
 	os_memset(&msg, 0, sizeof(msg));
 	msg.msg_iov = io;
 	msg.msg_iovlen = 2;
-	msg.msg_name = &iface->l2_addr;
-	msg.msg_namelen = sizeof(iface->l2_addr);
+	msg.msg_name = &iface->l2_addr[l2_ctx->idx];
+	msg.msg_namelen = iface->l2_addr_len[l2_ctx->idx];
 
 	if (sendmsg(iface->fd, &msg, 0) < 0) {
 		wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno));
@@ -426,14 +464,23 @@
 }
 
 
+static int wpa_priv_allowed_l2_proto(u16 proto)
+{
+	return proto == ETH_P_EAPOL || proto == ETH_P_RSN_PREAUTH ||
+		proto == ETH_P_80211_ENCAP;
+}
+
+
 static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
 				     struct sockaddr_un *from,
+				     socklen_t fromlen,
 				     void *buf, size_t len)
 {
 	int *reg_cmd = buf;
 	u8 own_addr[ETH_ALEN];
 	int res;
 	u16 proto;
+	int idx;
 
 	if (len != 2 * sizeof(int)) {
 		wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
@@ -442,50 +489,69 @@
 	}
 
 	proto = reg_cmd[0];
-	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH &&
-	    proto != ETH_P_80211_ENCAP) {
+	if (!wpa_priv_allowed_l2_proto(proto)) {
 		wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
 			   "ethertype 0x%x", proto);
 		return;
 	}
 
-	if (iface->l2) {
-		wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
-			   "instance");
-		l2_packet_deinit(iface->l2);
-		iface->l2 = NULL;
+	for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+		if (!iface->l2[idx])
+			break;
+	}
+	if (idx == WPA_PRIV_MAX_L2) {
+		wpa_printf(MSG_DEBUG, "No free l2_packet connection found");
+		return;
 	}
 
-	os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr));
+	os_memcpy(&iface->l2_addr[idx], from, fromlen);
+	iface->l2_addr_len[idx] = fromlen;
 
-	iface->l2 = l2_packet_init(iface->ifname, NULL, proto,
-				   wpa_priv_l2_rx, iface, reg_cmd[1]);
-	if (iface->l2 == NULL) {
+	iface->l2_ctx[idx].idx = idx;
+	iface->l2_ctx[idx].parent = iface;
+	iface->l2[idx] = l2_packet_init(iface->ifname, NULL, proto,
+					wpa_priv_l2_rx, &iface->l2_ctx[idx],
+					reg_cmd[1]);
+	if (!iface->l2[idx]) {
 		wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
 			   "instance for protocol %d", proto);
 		return;
 	}
 
-	if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) {
+	if (l2_packet_get_own_addr(iface->l2[idx], own_addr) < 0) {
 		wpa_printf(MSG_DEBUG, "Failed to get own address from "
 			   "l2_packet");
-		l2_packet_deinit(iface->l2);
-		iface->l2 = NULL;
+		l2_packet_deinit(iface->l2[idx]);
+		iface->l2[idx] = NULL;
 		return;
 	}
 
 	res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
-		     (struct sockaddr *) from, sizeof(*from));
-	wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res);
+		     (struct sockaddr *) from, fromlen);
+	wpa_printf(MSG_DEBUG, "L2 registration[idx=%d]: res=%d", idx, res);
 }
 
 
 static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
-				       struct sockaddr_un *from)
+				       struct sockaddr_un *from,
+				       socklen_t fromlen)
 {
-	if (iface->l2) {
-		l2_packet_deinit(iface->l2);
-		iface->l2 = NULL;
+	int idx;
+
+	for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+		if (iface->l2_addr_len[idx] == fromlen &&
+		    os_memcmp(&iface->l2_addr[idx], from, fromlen) == 0)
+			break;
+	}
+	if (idx == WPA_PRIV_MAX_L2) {
+		wpa_printf(MSG_DEBUG,
+			   "No registered l2_packet socket found for unregister request");
+		return;
+	}
+
+	if (iface->l2[idx]) {
+		l2_packet_deinit(iface->l2[idx]);
+		iface->l2[idx] = NULL;
 	}
 }
 
@@ -493,20 +559,36 @@
 static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
 					      struct sockaddr_un *from)
 {
-	if (iface->l2)
-		l2_packet_notify_auth_start(iface->l2);
+	int idx;
+
+	for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+		if (iface->l2[idx])
+			l2_packet_notify_auth_start(iface->l2[idx]);
+	}
 }
 
 
 static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
-				 struct sockaddr_un *from,
+				 struct sockaddr_un *from, socklen_t fromlen,
 				 void *buf, size_t len)
 {
 	u8 *dst_addr;
 	u16 proto;
 	int res;
+	int idx;
 
-	if (iface->l2 == NULL)
+	for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
+		if (iface->l2_addr_len[idx] == fromlen &&
+		    os_memcmp(&iface->l2_addr[idx], from, fromlen) == 0)
+			break;
+	}
+	if (idx == WPA_PRIV_MAX_L2) {
+		wpa_printf(MSG_DEBUG,
+			   "No registered l2_packet socket found for send request");
+		return;
+	}
+
+	if (iface->l2[idx] == NULL)
 		return;
 
 	if (len < ETH_ALEN + 2) {
@@ -518,15 +600,15 @@
 	dst_addr = buf;
 	os_memcpy(&proto, buf + ETH_ALEN, 2);
 
-	if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+	if (!wpa_priv_allowed_l2_proto(proto)) {
 		wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
 			   "0x%x", proto);
 		return;
 	}
 
-	res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2,
-			     len - ETH_ALEN - 2);
-	wpa_printf(MSG_DEBUG, "L2 send: res=%d", res);
+	res = l2_packet_send(iface->l2[idx], dst_addr, proto,
+			     buf + ETH_ALEN + 2, len - ETH_ALEN - 2);
+	wpa_printf(MSG_DEBUG, "L2 send[idx=%d]: res=%d", idx, res);
 }
 
 
@@ -571,7 +653,7 @@
 
 	switch (cmd) {
 	case PRIVSEP_CMD_REGISTER:
-		wpa_priv_cmd_register(iface, &from);
+		wpa_priv_cmd_register(iface, &from, fromlen);
 		break;
 	case PRIVSEP_CMD_UNREGISTER:
 		wpa_priv_cmd_unregister(iface, &from);
@@ -580,34 +662,35 @@
 		wpa_priv_cmd_scan(iface, cmd_buf, cmd_len);
 		break;
 	case PRIVSEP_CMD_GET_SCAN_RESULTS:
-		wpa_priv_cmd_get_scan_results(iface, &from);
+		wpa_priv_cmd_get_scan_results(iface, &from, fromlen);
 		break;
 	case PRIVSEP_CMD_ASSOCIATE:
 		wpa_priv_cmd_associate(iface, cmd_buf, cmd_len);
 		break;
 	case PRIVSEP_CMD_GET_BSSID:
-		wpa_priv_cmd_get_bssid(iface, &from);
+		wpa_priv_cmd_get_bssid(iface, &from, fromlen);
 		break;
 	case PRIVSEP_CMD_GET_SSID:
-		wpa_priv_cmd_get_ssid(iface, &from);
+		wpa_priv_cmd_get_ssid(iface, &from, fromlen);
 		break;
 	case PRIVSEP_CMD_SET_KEY:
 		wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len);
 		break;
 	case PRIVSEP_CMD_GET_CAPA:
-		wpa_priv_cmd_get_capa(iface, &from);
+		wpa_priv_cmd_get_capa(iface, &from, fromlen);
 		break;
 	case PRIVSEP_CMD_L2_REGISTER:
-		wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len);
+		wpa_priv_cmd_l2_register(iface, &from, fromlen,
+					 cmd_buf, cmd_len);
 		break;
 	case PRIVSEP_CMD_L2_UNREGISTER:
-		wpa_priv_cmd_l2_unregister(iface, &from);
+		wpa_priv_cmd_l2_unregister(iface, &from, fromlen);
 		break;
 	case PRIVSEP_CMD_L2_NOTIFY_AUTH_START:
 		wpa_priv_cmd_l2_notify_auth_start(iface, &from);
 		break;
 	case PRIVSEP_CMD_L2_SEND:
-		wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len);
+		wpa_priv_cmd_l2_send(iface, &from, fromlen, cmd_buf, cmd_len);
 		break;
 	case PRIVSEP_CMD_SET_COUNTRY:
 		pos = cmd_buf;
@@ -625,8 +708,14 @@
 
 static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
 {
-	if (iface->drv_priv && iface->driver->deinit)
-		iface->driver->deinit(iface->drv_priv);
+	int i;
+
+	if (iface->drv_priv) {
+		if (iface->driver->deinit)
+			iface->driver->deinit(iface->drv_priv);
+		if (iface->drv_global_priv)
+			iface->driver->global_deinit(iface->drv_global_priv);
+	}
 
 	if (iface->fd >= 0) {
 		eloop_unregister_read_sock(iface->fd);
@@ -634,8 +723,10 @@
 		unlink(iface->sock_name);
 	}
 
-	if (iface->l2)
-		l2_packet_deinit(iface->l2);
+	for (i = 0; i < WPA_PRIV_MAX_L2; i++) {
+		if (iface->l2[i])
+			l2_packet_deinit(iface->l2[i]);
+	}
 
 	os_free(iface->ifname);
 	os_free(iface->driver_name);
@@ -777,7 +868,7 @@
 	msg.msg_iov = io;
 	msg.msg_iovlen = data ? 2 : 1;
 	msg.msg_name = &iface->drv_addr;
-	msg.msg_namelen = sizeof(iface->drv_addr);
+	msg.msg_namelen = iface->drv_addr_len;
 
 	if (sendmsg(iface->fd, &msg, 0) < 0) {
 		wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
@@ -796,7 +887,7 @@
 	struct privsep_event_auth *auth;
 	u8 *buf, *pos;
 
-	buf = os_malloc(buflen);
+	buf = os_zalloc(buflen);
 	if (buf == NULL)
 		return;
 
@@ -1061,7 +1152,7 @@
 	msg.msg_iov = io;
 	msg.msg_iovlen = 3;
 	msg.msg_name = &iface->drv_addr;
-	msg.msg_namelen = sizeof(iface->drv_addr);
+	msg.msg_namelen = iface->drv_addr_len;
 
 	if (sendmsg(iface->fd, &msg, 0) < 0)
 		wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
@@ -1099,7 +1190,7 @@
 static void usage(void)
 {
 	printf("wpa_priv v" VERSION_STR "\n"
-	       "Copyright (c) 2007-2016, Jouni Malinen <j@w1.fi> and "
+	       "Copyright (c) 2007-2017, Jouni Malinen <j@w1.fi> and "
 	       "contributors\n"
 	       "\n"
 	       "usage:\n"
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 51bb245..9aaedb3 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -62,7 +62,7 @@
 
 const char *const wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2016, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2017, Jouni Malinen <j@w1.fi> and contributors";
 
 const char *const wpa_supplicant_license =
 "This software may be distributed under the terms of the BSD license.\n"
@@ -192,7 +192,9 @@
 {
 	struct wpa_supplicant *wpa_s = eloop_ctx;
 	const u8 *bssid = wpa_s->bssid;
-	if (is_zero_ether_addr(bssid))
+	if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+	    (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+	     wpa_s->wpa_state == WPA_ASSOCIATING))
 		bssid = wpa_s->pending_bssid;
 	wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
 		MAC2STR(bssid));
@@ -200,6 +202,7 @@
 	wpa_sm_notify_disassoc(wpa_s->wpa);
 	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
 	wpa_s->reassociate = 1;
+	wpas_notify_auth_timeout(wpa_s);
 
 	/*
 	 * If we timed out, the AP or the local radio may be busy.
@@ -327,7 +330,12 @@
 
 	eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
 
-	ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#ifdef CONFIG_MACSEC
+	if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE && ssid->mka_psk_set)
+		ieee802_1x_create_preshared_mka(wpa_s, ssid);
+	else
+		ieee802_1x_alloc_kay_sm(wpa_s, ssid);
+#endif /* CONFIG_MACSEC */
 #endif /* IEEE8021X_EAPOL */
 }
 
@@ -413,6 +421,19 @@
 }
 
 
+void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s)
+{
+	struct fils_hlp_req *req;
+
+	while ((req = dl_list_first(&wpa_s->fils_hlp_req, struct fils_hlp_req,
+				    list)) != NULL) {
+		dl_list_del(&req->list);
+		wpabuf_free(req->pkt);
+		os_free(req);
+	}
+}
+
+
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
 	int i;
@@ -432,6 +453,8 @@
 #ifdef CONFIG_TESTING_OPTIONS
 	l2_packet_deinit(wpa_s->l2_test);
 	wpa_s->l2_test = NULL;
+	os_free(wpa_s->get_pref_freq_list_override);
+	wpa_s->get_pref_freq_list_override = NULL;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	if (wpa_s->conf != NULL) {
@@ -552,6 +575,8 @@
 	wpa_s->last_scan_res = NULL;
 
 #ifdef CONFIG_HS20
+	if (wpa_s->drv_priv)
+		wpa_drv_configure_frame_filters(wpa_s, 0);
 	hs20_deinit(wpa_s);
 #endif /* CONFIG_HS20 */
 
@@ -573,6 +598,31 @@
 #endif /* CONFIG_MBO */
 
 	free_bss_tmp_disallowed(wpa_s);
+
+	wpabuf_free(wpa_s->lci);
+	wpa_s->lci = NULL;
+	wpas_clear_beacon_rep_data(wpa_s);
+
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+	{
+		struct external_pmksa_cache *entry;
+
+		while ((entry = dl_list_last(&wpa_s->mesh_external_pmksa_cache,
+					     struct external_pmksa_cache,
+					     list)) != NULL) {
+			dl_list_del(&entry->list);
+			os_free(entry->pmksa_cache);
+			os_free(entry);
+		}
+	}
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
+	wpas_flush_fils_hlp_req(wpa_s);
+
+	wpabuf_free(wpa_s->ric_ies);
+	wpa_s->ric_ies = NULL;
 }
 
 
@@ -1226,6 +1276,22 @@
 		wpa_dbg(wpa_s, MSG_DEBUG,
 			"WPA: using KEY_MGMT 802.1X with Suite B");
 #endif /* CONFIG_SUITEB */
+#ifdef CONFIG_FILS
+#ifdef CONFIG_IEEE80211R
+	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA384) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA384;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA384");
+	} else if (sel & WPA_KEY_MGMT_FT_FILS_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_FILS_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT-FILS-SHA256");
+#endif /* CONFIG_IEEE80211R */
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA384) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA384;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA384");
+	} else if (sel & WPA_KEY_MGMT_FILS_SHA256) {
+		wpa_s->key_mgmt = WPA_KEY_MGMT_FILS_SHA256;
+		wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FILS-SHA256");
+#endif /* CONFIG_FILS */
 #ifdef CONFIG_IEEE80211R
 	} else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
 		wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
@@ -1451,6 +1517,19 @@
 		break;
 	case 6: /* Bits 48-55 */
 		break;
+	case 7: /* Bits 56-63 */
+		break;
+	case 8: /* Bits 64-71 */
+		if (wpa_s->conf->ftm_responder)
+			*pos |= 0x40; /* Bit 70 - FTM responder */
+		if (wpa_s->conf->ftm_initiator)
+			*pos |= 0x80; /* Bit 71 - FTM initiator */
+		break;
+	case 9: /* Bits 72-79 */
+#ifdef CONFIG_FILS
+		*pos |= 0x01;
+#endif /* CONFIG_FILS */
+		break;
 	}
 }
 
@@ -1458,7 +1537,7 @@
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 {
 	u8 *pos = buf;
-	u8 len = 6, i;
+	u8 len = 10, i;
 
 	if (len < wpa_s->extended_capa_len)
 		len = wpa_s->extended_capa_len;
@@ -1655,11 +1734,13 @@
 			wmm_ac_save_tspecs(wpa_s);
 			wpa_s->reassoc_same_bss = 1;
 		}
-	} else if (rand_style > 0) {
+	}
+
+	if (rand_style > 0 && !wpa_s->reassoc_same_ess) {
 		if (wpas_update_random_addr(wpa_s, rand_style) < 0)
 			return;
 		wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-	} else if (wpa_s->mac_addr_changed) {
+	} else if (rand_style == 0 && wpa_s->mac_addr_changed) {
 		if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
 			wpa_msg(wpa_s, MSG_INFO,
 				"Could not restore permanent MAC address");
@@ -1678,6 +1759,13 @@
 #ifdef CONFIG_IBSS_RSN
 	ibss_rsn_deinit(wpa_s->ibss_rsn);
 	wpa_s->ibss_rsn = NULL;
+#else /* CONFIG_IBSS_RSN */
+	if (ssid->mode == WPAS_MODE_IBSS &&
+	    !(ssid->key_mgmt & (WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPA_NONE))) {
+		wpa_msg(wpa_s, MSG_INFO,
+			"IBSS RSN not supported in the build");
+		return;
+	}
 #endif /* CONFIG_IBSS_RSN */
 
 	if (ssid->mode == WPAS_MODE_AP || ssid->mode == WPAS_MODE_P2P_GO ||
@@ -1748,6 +1836,13 @@
 		return;
 	}
 
+#ifdef CONFIG_SME
+	if (ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) {
+		/* Clear possibly set auth_alg, if any, from last attempt. */
+		wpa_s->sme.auth_alg = WPA_AUTH_ALG_OPEN;
+	}
+#endif /* CONFIG_SME */
+
 	wpas_abort_ongoing_scan(wpa_s);
 
 	cwork = os_zalloc(sizeof(*cwork));
@@ -1779,11 +1874,6 @@
 	u8 channel;
 	int i;
 
-#ifdef CONFIG_HT_OVERRIDES
-	if (ssid->disable_ht)
-		return 0;
-#endif /* CONFIG_HT_OVERRIDES */
-
 	hw_mode = ieee80211_freq_to_chan(ssid->frequency, &channel);
 	if (hw_mode == NUM_HOSTAPD_MODES)
 		return 0;
@@ -1868,6 +1958,13 @@
 	if (!mode)
 		return;
 
+#ifdef CONFIG_HT_OVERRIDES
+	if (ssid->disable_ht) {
+		freq->ht_enabled = 0;
+		return;
+	}
+#endif /* CONFIG_HT_OVERRIDES */
+
 	freq->ht_enabled = ht_supported(mode);
 	if (!freq->ht_enabled)
 		return;
@@ -1889,6 +1986,11 @@
 	if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
 		return;
 
+#ifdef CONFIG_HT_OVERRIDES
+	if (ssid->disable_ht40)
+		return;
+#endif /* CONFIG_HT_OVERRIDES */
+
 	/* Check/setup HT40+/HT40- */
 	for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
 		if (ht40plus[j] == channel) {
@@ -1913,22 +2015,16 @@
 
 	freq->channel = pri_chan->chan;
 
-	switch (ht40) {
-	case -1:
+	if (ht40 == -1) {
 		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
 			return;
-		freq->sec_channel_offset = -1;
-		break;
-	case 1:
+	} else {
 		if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
 			return;
-		freq->sec_channel_offset = 1;
-		break;
-	default:
-		break;
 	}
+	freq->sec_channel_offset = ht40;
 
-	if (freq->sec_channel_offset && obss_scan) {
+	if (obss_scan) {
 		struct wpa_scan_results *scan_res;
 
 		scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
@@ -1976,6 +2072,13 @@
 
 	vht_freq = *freq;
 
+#ifdef CONFIG_VHT_OVERRIDES
+	if (ssid->disable_vht) {
+		freq->vht_enabled = 0;
+		return;
+	}
+#endif /* CONFIG_VHT_OVERRIDES */
+
 	vht_freq.vht_enabled = vht_supported(mode);
 	if (!vht_freq.vht_enabled)
 		return;
@@ -2034,6 +2137,16 @@
 			if (chwidth == VHT_CHANWIDTH_80P80MHZ)
 				break;
 		}
+	} else if (ssid->max_oper_chwidth == VHT_CHANWIDTH_160MHZ) {
+		if (freq->freq == 5180) {
+			chwidth = VHT_CHANWIDTH_160MHZ;
+			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+			seg0 = 50;
+		} else if (freq->freq == 5520) {
+			chwidth = VHT_CHANWIDTH_160MHZ;
+			vht_caps |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+			seg0 = 114;
+		}
 	}
 
 	if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
@@ -2074,9 +2187,6 @@
        struct ieee80211_vht_capabilities vhtcaps;
        struct ieee80211_vht_capabilities vhtcaps_mask;
 #endif /* CONFIG_VHT_OVERRIDES */
-#ifdef CONFIG_MBO
-	const u8 *mbo = NULL;
-#endif /* CONFIG_MBO */
 
 	if (deinit) {
 		if (work->started) {
@@ -2141,7 +2251,10 @@
 	} else {
 		wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
 			wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
-		os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+		if (bss)
+			os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+		else
+			os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
 	}
 	if (!wpa_s->pno)
 		wpa_supplicant_cancel_sched_scan(wpa_s);
@@ -2262,21 +2375,12 @@
 	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 #endif /* CONFIG_P2P */
 
-#ifdef CONFIG_MBO
 	if (bss) {
-		mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
-		if (mbo) {
-			int len;
-
-			len = wpas_mbo_supp_op_class_ie(wpa_s, bss->freq,
-							wpa_ie + wpa_ie_len,
-							sizeof(wpa_ie) -
-							wpa_ie_len);
-			if (len > 0)
-				wpa_ie_len += len;
-		}
+		wpa_ie_len += wpas_supp_op_class_ie(wpa_s, bss->freq,
+						    wpa_ie + wpa_ie_len,
+						    sizeof(wpa_ie) -
+						    wpa_ie_len);
 	}
-#endif /* CONFIG_MBO */
 
 	/*
 	 * Workaround: Add Extended Capabilities element only if the AP
@@ -2286,6 +2390,11 @@
 	 * element in all cases, it is justifiable to skip it to avoid
 	 * interoperability issues.
 	 */
+	if (ssid->p2p_group)
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_P2P_CLIENT);
+	else
+		wpa_drv_get_ext_capa(wpa_s, WPA_IF_STATION);
+
 	if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
 		u8 ext_capab[18];
 		int ext_capab_len;
@@ -2319,6 +2428,8 @@
 				wpa_ie_len += wpabuf_len(hs20);
 			}
 			wpabuf_free(hs20);
+
+			hs20_configure_frame_filters(wpa_s);
 		}
 	}
 #endif /* CONFIG_HS20 */
@@ -2348,7 +2459,7 @@
 #endif /* CONFIG_FST */
 
 #ifdef CONFIG_MBO
-	if (mbo) {
+	if (bss && wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE)) {
 		int len;
 
 		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
@@ -2399,12 +2510,14 @@
 	if (bss) {
 		params.ssid = bss->ssid;
 		params.ssid_len = bss->ssid_len;
-		if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
+		if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set ||
+		    wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
 			wpa_printf(MSG_DEBUG, "Limit connection to BSSID "
 				   MACSTR " freq=%u MHz based on scan results "
-				   "(bssid_set=%d)",
+				   "(bssid_set=%d wps=%d)",
 				   MAC2STR(bss->bssid), bss->freq,
-				   ssid->bssid_set);
+				   ssid->bssid_set,
+				   wpa_s->key_mgmt == WPA_KEY_MGMT_WPS);
 			params.bssid = bss->bssid;
 			params.freq.freq = bss->freq;
 		}
@@ -2414,7 +2527,7 @@
 	} else {
 		params.ssid = ssid->ssid;
 		params.ssid_len = ssid->ssid_len;
-		params.pbss = ssid->pbss;
+		params.pbss = (ssid->pbss != 2) ? ssid->pbss : 0;
 	}
 
 	if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
@@ -2611,8 +2724,14 @@
 	}
 	old_ssid = wpa_s->current_ssid;
 	wpa_s->current_ssid = ssid;
-	if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set)
+
+	if (!wpas_driver_bss_selection(wpa_s) || ssid->bssid_set) {
 		wpa_s->current_bss = bss;
+#ifdef CONFIG_HS20
+		hs20_configure_frame_filters(wpa_s);
+#endif /* CONFIG_HS20 */
+	}
+
 	wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
 	wpa_supplicant_initiate_eapol(wpa_s);
 	if (old_ssid != wpa_s->current_ssid)
@@ -2657,12 +2776,12 @@
 		MAC2STR(wpa_s->bssid), MAC2STR(wpa_s->pending_bssid),
 		reason_code, wpa_supplicant_state_txt(wpa_s->wpa_state));
 
-	if (!is_zero_ether_addr(wpa_s->bssid))
-		addr = wpa_s->bssid;
-	else if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
-		 (wpa_s->wpa_state == WPA_AUTHENTICATING ||
-		  wpa_s->wpa_state == WPA_ASSOCIATING))
+	if (!is_zero_ether_addr(wpa_s->pending_bssid) &&
+	    (wpa_s->wpa_state == WPA_AUTHENTICATING ||
+	     wpa_s->wpa_state == WPA_ASSOCIATING))
 		addr = wpa_s->pending_bssid;
+	else if (!is_zero_ether_addr(wpa_s->bssid))
+		addr = wpa_s->bssid;
 	else if (wpa_s->wpa_state == WPA_ASSOCIATING) {
 		/*
 		 * When using driver-based BSS selection, we may not know the
@@ -2717,6 +2836,95 @@
 		wpa_s->reassociate = 1;
 }
 
+/**
+ * wpa_supplicant_add_network - Add a new network.
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: The new network configuration or %NULL if operation failed
+ *
+ * This function performs the following operations:
+ * 1. Adds a new network.
+ * 2. Send network addition notification.
+ * 3. Marks the network disabled.
+ * 4. Set network default parameters.
+ */
+struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_ssid *ssid;
+
+	ssid = wpa_config_add_network(wpa_s->conf);
+	if (!ssid) {
+		return NULL;
+	}
+	wpas_notify_network_added(wpa_s, ssid);
+	ssid->disabled = 1;
+	wpa_config_set_network_defaults(ssid);
+
+	return ssid;
+}
+
+/**
+ * wpa_supplicant_remove_network - Remove a configured network based on id
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @id: Unique network id to search for
+ * Returns: 0 on success, or -1 if the network was not found, -2 if the network
+ * could not be removed
+ *
+ * This function performs the following operations:
+ * 1. Removes the network.
+ * 2. Send network removal notification.
+ * 3. Update internal state machines.
+ * 4. Stop any running sched scans.
+ */
+int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id)
+{
+	struct wpa_ssid *ssid;
+	int was_disabled;
+
+	ssid = wpa_config_get_network(wpa_s->conf, id);
+	if (ssid)
+		wpas_notify_network_removed(wpa_s, ssid);
+	if (ssid == NULL) {
+		return -1;
+	}
+
+	if (wpa_s->last_ssid == ssid)
+		wpa_s->last_ssid = NULL;
+
+	if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
+#ifdef CONFIG_SME
+		wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+		/*
+		 * Invalidate the EAP session cache if the current or
+		 * previously used network is removed.
+		 */
+		eapol_sm_invalidate_cached_session(wpa_s->eapol);
+	}
+
+	if (ssid == wpa_s->current_ssid) {
+		wpa_sm_set_config(wpa_s->wpa, NULL);
+		eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+
+		if (wpa_s->wpa_state >= WPA_AUTHENTICATING)
+			wpa_s->own_disconnect_req = 1;
+		wpa_supplicant_deauthenticate(wpa_s,
+					      WLAN_REASON_DEAUTH_LEAVING);
+	}
+
+	was_disabled = ssid->disabled;
+
+	if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+		return -2;
+	}
+
+	if (!was_disabled && wpa_s->sched_scanning) {
+		wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
+			   "network from filters");
+		wpa_supplicant_cancel_sched_scan(wpa_s);
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	}
+	return 0;
+}
 
 /**
  * wpa_supplicant_enable_network - Mark a configured network as enabled
@@ -2878,6 +3086,7 @@
 	if (wpa_s->connect_without_scan ||
 	    wpa_supplicant_fast_associate(wpa_s) != 1) {
 		wpa_s->scan_req = NORMAL_SCAN_REQ;
+		wpas_scan_reset_sched_scan(wpa_s);
 		wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
 	}
 
@@ -3238,6 +3447,13 @@
 	wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
 	wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (wpa_s->ignore_auth_resp) {
+		wpa_printf(MSG_INFO, "RX EAPOL - ignore_auth_resp active!");
+		return;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #ifdef CONFIG_PEERKEY
 	if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
 	    wpa_s->current_ssid->peerkey &&
@@ -3526,6 +3742,7 @@
 	wpa_s->sched_scanning = 0;
 
 	dl_list_init(&wpa_s->bss_tmp_disallowed);
+	dl_list_init(&wpa_s->fils_hlp_req);
 
 	return wpa_s;
 }
@@ -3553,8 +3770,11 @@
 	wpa_msg(wpa_s, MSG_DEBUG, "set_htcap, ht_mcs -:%s:-", ht_mcs);
 
 	for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++) {
+		long v;
+
 		errno = 0;
-		long v = strtol(tmp, &end, 16);
+		v = strtol(tmp, &end, 16);
+
 		if (errno == 0) {
 			wpa_msg(wpa_s, MSG_DEBUG,
 				"htcap value[%i]: %ld end: %p  tmp: %p",
@@ -3664,18 +3884,10 @@
 				struct ieee80211_ht_capabilities *htcaps_mask,
 				int disabled)
 {
-	/* Masking these out disables HT40 */
-	le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
-				HT_CAP_INFO_SHORT_GI40MHZ);
-
 	wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
 
-	if (disabled)
-		htcaps->ht_capabilities_info &= ~msk;
-	else
-		htcaps->ht_capabilities_info |= msk;
-
-	htcaps_mask->ht_capabilities_info |= msk;
+	set_disable_ht40(htcaps, disabled);
+	set_disable_ht40(htcaps_mask, 0);
 
 	return 0;
 }
@@ -3778,8 +3990,8 @@
 	if (!vhtcaps || !vhtcaps_mask)
 		return;
 
-	vhtcaps->vht_capabilities_info = ssid->vht_capa;
-	vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
+	vhtcaps->vht_capabilities_info = host_to_le32(ssid->vht_capa);
+	vhtcaps_mask->vht_capabilities_info = host_to_le32(ssid->vht_capa_mask);
 
 #ifdef CONFIG_HT_OVERRIDES
 	/* if max ampdu is <= 3, we have to make the HT cap the same */
@@ -3801,15 +4013,17 @@
 #define OVERRIDE_MCS(i)							\
 	if (ssid->vht_tx_mcs_nss_ ##i >= 0) {				\
 		vhtcaps_mask->vht_supported_mcs_set.tx_map |=		\
-			3 << 2 * (i - 1);				\
+			host_to_le16(3 << 2 * (i - 1));			\
 		vhtcaps->vht_supported_mcs_set.tx_map |=		\
-			ssid->vht_tx_mcs_nss_ ##i << 2 * (i - 1);	\
+			host_to_le16(ssid->vht_tx_mcs_nss_ ##i <<	\
+				     2 * (i - 1));			\
 	}								\
 	if (ssid->vht_rx_mcs_nss_ ##i >= 0) {				\
 		vhtcaps_mask->vht_supported_mcs_set.rx_map |=		\
-			3 << 2 * (i - 1);				\
+			host_to_le16(3 << 2 * (i - 1));			\
 		vhtcaps->vht_supported_mcs_set.rx_map |=		\
-			ssid->vht_rx_mcs_nss_ ##i << 2 * (i - 1);	\
+			host_to_le16(ssid->vht_rx_mcs_nss_ ##i <<	\
+				     2 * (i - 1));			\
 	}
 
 	OVERRIDE_MCS(1);
@@ -3949,10 +4163,14 @@
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
-	WPA_ASSERT(os_memcmp(wpa_s->bssid, da, ETH_ALEN) == 0);
+	if (os_memcmp(wpa_s->bssid, da, ETH_ALEN) != 0) {
+		wpa_printf(MSG_INFO, "FST:%s:bssid=" MACSTR " != da=" MACSTR,
+			   __func__, MAC2STR(wpa_s->bssid), MAC2STR(da));
+		return -1;
+	}
 	return wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
-					  wpa_s->own_addr, wpa_s->bssid,
-					  wpabuf_head(data), wpabuf_len(data),
+				   wpa_s->own_addr, wpa_s->bssid,
+				   wpabuf_head(data), wpabuf_len(data),
 				   0);
 }
 
@@ -3981,8 +4199,9 @@
 }
 
 
-const u8 * wpas_fst_get_peer_first(void *ctx, struct fst_get_peer_ctx **get_ctx,
-				   Boolean mb_only)
+static const u8 * wpas_fst_get_peer_first(void *ctx,
+					  struct fst_get_peer_ctx **get_ctx,
+					  Boolean mb_only)
 {
 	struct wpa_supplicant *wpa_s = ctx;
 
@@ -3994,8 +4213,9 @@
 }
 
 
-const u8 * wpas_fst_get_peer_next(void *ctx, struct fst_get_peer_ctx **get_ctx,
-				  Boolean mb_only)
+static const u8 * wpas_fst_get_peer_next(void *ctx,
+					 struct fst_get_peer_ctx **get_ctx,
+					 Boolean mb_only)
 {
 	return NULL;
 }
@@ -4148,6 +4368,20 @@
 }
 
 
+static int radio_work_is_connect(struct wpa_radio_work *work)
+{
+	return os_strcmp(work->type, "sme-connect") == 0 ||
+		os_strcmp(work->type, "connect") == 0;
+}
+
+
+static int radio_work_is_scan(struct wpa_radio_work *work)
+{
+	return os_strcmp(work->type, "scan") == 0 ||
+		os_strcmp(work->type, "p2p-scan") == 0;
+}
+
+
 static struct wpa_radio_work * radio_work_get_next_work(struct wpa_radio *radio)
 {
 	struct wpa_radio_work *active_work = NULL;
@@ -4177,8 +4411,7 @@
 		return NULL;
 	}
 
-	if (os_strcmp(active_work->type, "sme-connect") == 0 ||
-	    os_strcmp(active_work->type, "connect") == 0) {
+	if (radio_work_is_connect(active_work)) {
 		/*
 		 * If the active work is either connect or sme-connect,
 		 * do not parallelize them with other radio works.
@@ -4197,10 +4430,20 @@
 		 * If connect or sme-connect are enqueued, parallelize only
 		 * those operations ahead of them in the queue.
 		 */
-		if (os_strcmp(tmp->type, "connect") == 0 ||
-		    os_strcmp(tmp->type, "sme-connect") == 0)
+		if (radio_work_is_connect(tmp))
 			break;
 
+		/* Serialize parallel scan and p2p_scan operations on the same
+		 * interface since the driver_nl80211 mechanism for tracking
+		 * scan cookies does not yet have support for this. */
+		if (active_work->wpa_s == tmp->wpa_s &&
+		    radio_work_is_scan(active_work) &&
+		    radio_work_is_scan(tmp)) {
+			wpa_dbg(active_work->wpa_s, MSG_DEBUG,
+				"Do not start work '%s' when another work '%s' is already scheduled",
+				tmp->type, active_work->type);
+			continue;
+		}
 		/*
 		 * Check that the radio works are distinct and
 		 * on different bands.
@@ -4796,6 +5039,12 @@
 	if (wpa_bss_init(wpa_s) < 0)
 		return -1;
 
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+#ifdef CONFIG_MESH
+	dl_list_init(&wpa_s->mesh_external_pmksa_cache);
+#endif /* CONFIG_MESH */
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
+
 	/*
 	 * Set Wake-on-WLAN triggers, if configured.
 	 * Note: We don't restore/remove the triggers on shutdown (it doesn't
@@ -4836,6 +5085,8 @@
 	wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
 #endif /* CONFIG_MBO */
 
+	wpa_supplicant_set_default_scan_ies(wpa_s);
+
 	return 0;
 }
 
@@ -5043,13 +5294,15 @@
 		return NULL;
 	}
 
-	if (iface->p2p_mgmt == 0) {
-		/* Notify the control interfaces about new iface */
-		if (wpas_notify_iface_added(wpa_s)) {
-			wpa_supplicant_deinit_iface(wpa_s, 1, 0);
-			return NULL;
-		}
+	/* Notify the control interfaces about new iface */
+	if (wpas_notify_iface_added(wpa_s)) {
+		wpa_supplicant_deinit_iface(wpa_s, 1, 0);
+		return NULL;
+	}
 
+	/* Notify the control interfaces about new networks for non p2p mgmt
+	 * ifaces. */
+	if (iface->p2p_mgmt == 0) {
 		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
 			wpas_notify_network_added(wpa_s, ssid);
 	}
@@ -5095,6 +5348,7 @@
 #ifdef CONFIG_MESH
 	unsigned int mesh_if_created = wpa_s->mesh_if_created;
 	char *ifname = NULL;
+	struct wpa_supplicant *parent = wpa_s->parent;
 #endif /* CONFIG_MESH */
 
 	/* Remove interface from the global list of interfaces */
@@ -5130,7 +5384,7 @@
 
 #ifdef CONFIG_MESH
 	if (mesh_if_created) {
-		wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+		wpa_drv_if_remove(parent, WPA_IF_MESH, ifname);
 		os_free(ifname);
 	}
 #endif /* CONFIG_MESH */
@@ -5660,20 +5914,36 @@
 }
 
 
-#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW)
+#if defined(CONFIG_CTRL_IFACE) || defined(CONFIG_CTRL_IFACE_DBUS_NEW) || defined (CONFIG_CTRL_IFACE_HIDL)
 int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
 					      struct wpa_ssid *ssid,
 					      const char *field,
 					      const char *value)
 {
 #ifdef IEEE8021X_EAPOL
-	struct eap_peer_config *eap = &ssid->eap;
+	enum wpa_ctrl_req_type rtype;
 
 	wpa_printf(MSG_DEBUG, "CTRL_IFACE: response handle field=%s", field);
 	wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: response value",
 			      (const u8 *) value, os_strlen(value));
 
-	switch (wpa_supplicant_ctrl_req_from_string(field)) {
+	rtype = wpa_supplicant_ctrl_req_from_string(field);
+	return wpa_supplicant_ctrl_rsp_handle(wpa_s, ssid, rtype, value);
+#else /* IEEE8021X_EAPOL */
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: IEEE 802.1X not included");
+	return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+
+int wpa_supplicant_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid,
+				   enum wpa_ctrl_req_type rtype,
+				   const char *value)
+{
+#ifdef IEEE8021X_EAPOL
+	struct eap_peer_config *eap = &ssid->eap;
+
+	switch (rtype) {
 	case WPA_CTRL_REQ_EAP_IDENTITY:
 		os_free(eap->identity);
 		eap->identity = (u8 *) os_strdup(value);
@@ -5723,6 +5993,7 @@
 	case WPA_CTRL_REQ_SIM:
 		str_clear_free(eap->external_sim_resp);
 		eap->external_sim_resp = os_strdup(value);
+		eap->pending_req_sim = 0;
 		break;
 	case WPA_CTRL_REQ_PSK_PASSPHRASE:
 		if (wpa_config_set(ssid, "psk", value, 0) < 0)
@@ -5744,7 +6015,7 @@
 			return -1;
 		break;
 	default:
-		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
+		wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown type %d", rtype);
 		return -1;
 	}
 
@@ -5754,7 +6025,7 @@
 	return -1;
 #endif /* IEEE8021X_EAPOL */
 }
-#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW */
+#endif /* CONFIG_CTRL_IFACE || CONFIG_CTRL_IFACE_DBUS_NEW || CONFIG_CTRL_IFACE_HIDL */
 
 
 int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
@@ -5812,6 +6083,19 @@
 			return NO_MGMT_FRAME_PROTECTION;
 		}
 
+		if (ssid &&
+		    (ssid->key_mgmt &
+		     ~(WPA_KEY_MGMT_NONE | WPA_KEY_MGMT_WPS |
+		       WPA_KEY_MGMT_IEEE8021X_NO_WPA)) == 0) {
+			/*
+			 * Do not use the default PMF value for non-RSN networks
+			 * since PMF is available only with RSN and pmf=2
+			 * configuration would otherwise prevent connections to
+			 * all open networks.
+			 */
+			return NO_MGMT_FRAME_PROTECTION;
+		}
+
 		return wpa_s->conf->pmf;
 	}
 
@@ -5969,6 +6253,26 @@
 		wpa_s->reattach = 0;
 }
 
+/**
+ * wpas_request_disconnection - Request disconnection
+ * @wpa_s: Pointer to the network interface
+ *
+ * This function is used to request disconnection from the currently connected
+ * network. This will stop any ongoing scans and initiate deauthentication.
+ */
+void wpas_request_disconnection(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_SME
+	wpa_s->sme.prev_bssid_set = 0;
+#endif /* CONFIG_SME */
+	wpa_s->reassociate = 0;
+	wpa_s->disconnected = 1;
+	wpa_supplicant_cancel_sched_scan(wpa_s);
+	wpa_supplicant_cancel_scan(wpa_s);
+	wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+	eloop_cancel_timeout(wpas_network_reenabled, wpa_s, NULL);
+}
+
 
 void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
 		    struct wpa_used_freq_data *freqs_data,
@@ -6067,271 +6371,6 @@
 }
 
 
-static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
-{
-	struct rrm_data *rrm = data;
-
-	if (!rrm->notify_neighbor_rep) {
-		wpa_printf(MSG_ERROR,
-			   "RRM: Unexpected neighbor report timeout");
-		return;
-	}
-
-	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
-	rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
-
-	rrm->notify_neighbor_rep = NULL;
-	rrm->neighbor_rep_cb_ctx = NULL;
-}
-
-
-/*
- * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
- * @wpa_s: Pointer to wpa_supplicant
- */
-void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
-{
-	wpa_s->rrm.rrm_used = 0;
-
-	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
-			     NULL);
-	if (wpa_s->rrm.notify_neighbor_rep)
-		wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
-	wpa_s->rrm.next_neighbor_rep_token = 1;
-}
-
-
-/*
- * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
- * @wpa_s: Pointer to wpa_supplicant
- * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
- * @report_len: Length of neighbor report buffer
- */
-void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
-				   const u8 *report, size_t report_len)
-{
-	struct wpabuf *neighbor_rep;
-
-	wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
-	if (report_len < 1)
-		return;
-
-	if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: Discarding neighbor report with token %d (expected %d)",
-			   report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
-		return;
-	}
-
-	eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
-			     NULL);
-
-	if (!wpa_s->rrm.notify_neighbor_rep) {
-		wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
-		return;
-	}
-
-	/* skipping the first byte, which is only an id (dialog token) */
-	neighbor_rep = wpabuf_alloc(report_len - 1);
-	if (neighbor_rep == NULL)
-		return;
-	wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
-	wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
-		   report[0]);
-	wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
-				       neighbor_rep);
-	wpa_s->rrm.notify_neighbor_rep = NULL;
-	wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
-}
-
-
-#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
-/* Workaround different, undefined for Windows, error codes used here */
-#define ENOTCONN -1
-#define EOPNOTSUPP -1
-#define ECANCELED -1
-#endif
-
-/**
- * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
- * @wpa_s: Pointer to wpa_supplicant
- * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
- *	  is sent in the request.
- * @cb: Callback function to be called once the requested report arrives, or
- *	timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
- *	In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
- *	the requester's responsibility to free it.
- *	In the latter case NULL will be sent in 'neighbor_rep'.
- * @cb_ctx: Context value to send the callback function
- * Returns: 0 in case of success, negative error code otherwise
- *
- * In case there is a previous request which has not been answered yet, the
- * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
- * Request must contain a callback function.
- */
-int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
-				       const struct wpa_ssid *ssid,
-				       void (*cb)(void *ctx,
-						  struct wpabuf *neighbor_rep),
-				       void *cb_ctx)
-{
-	struct wpabuf *buf;
-	const u8 *rrm_ie;
-
-	if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
-		wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
-		return -ENOTCONN;
-	}
-
-	if (!wpa_s->rrm.rrm_used) {
-		wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
-		return -EOPNOTSUPP;
-	}
-
-	rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
-				WLAN_EID_RRM_ENABLED_CAPABILITIES);
-	if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
-	    !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: No network support for Neighbor Report.");
-		return -EOPNOTSUPP;
-	}
-
-	if (!cb) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: Neighbor Report request must provide a callback.");
-		return -EINVAL;
-	}
-
-	/* Refuse if there's a live request */
-	if (wpa_s->rrm.notify_neighbor_rep) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: Currently handling previous Neighbor Report.");
-		return -EBUSY;
-	}
-
-	/* 3 = action category + action code + dialog token */
-	buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0));
-	if (buf == NULL) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: Failed to allocate Neighbor Report Request");
-		return -ENOMEM;
-	}
-
-	wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
-		   (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
-		   wpa_s->rrm.next_neighbor_rep_token);
-
-	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
-	wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
-	wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
-	if (ssid) {
-		wpabuf_put_u8(buf, WLAN_EID_SSID);
-		wpabuf_put_u8(buf, ssid->ssid_len);
-		wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
-	}
-
-	wpa_s->rrm.next_neighbor_rep_token++;
-
-	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
-				wpa_s->own_addr, wpa_s->bssid,
-				wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
-		wpa_printf(MSG_DEBUG,
-			   "RRM: Failed to send Neighbor Report Request");
-		wpabuf_free(buf);
-		return -ECANCELED;
-	}
-
-	wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
-	wpa_s->rrm.notify_neighbor_rep = cb;
-	eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
-			       wpas_rrm_neighbor_rep_timeout_handler,
-			       &wpa_s->rrm, NULL);
-
-	wpabuf_free(buf);
-	return 0;
-}
-
-
-void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
-					      const u8 *src,
-					      const u8 *frame, size_t len,
-					      int rssi)
-{
-	struct wpabuf *buf;
-	const struct rrm_link_measurement_request *req;
-	struct rrm_link_measurement_report report;
-
-	if (wpa_s->wpa_state != WPA_COMPLETED) {
-		wpa_printf(MSG_INFO,
-			   "RRM: Ignoring link measurement request. Not associated");
-		return;
-	}
-
-	if (!wpa_s->rrm.rrm_used) {
-		wpa_printf(MSG_INFO,
-			   "RRM: Ignoring link measurement request. Not RRM network");
-		return;
-	}
-
-	if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
-		wpa_printf(MSG_INFO,
-			   "RRM: Measurement report failed. TX power insertion not supported");
-		return;
-	}
-
-	req = (const struct rrm_link_measurement_request *) frame;
-	if (len < sizeof(*req)) {
-		wpa_printf(MSG_INFO,
-			   "RRM: Link measurement report failed. Request too short");
-		return;
-	}
-
-	os_memset(&report, 0, sizeof(report));
-	report.tpc.eid = WLAN_EID_TPC_REPORT;
-	report.tpc.len = 2;
-	report.rsni = 255; /* 255 indicates that RSNI is not available */
-	report.dialog_token = req->dialog_token;
-
-	/*
-	 * It's possible to estimate RCPI based on RSSI in dBm. This
-	 * calculation will not reflect the correct value for high rates,
-	 * but it's good enough for Action frames which are transmitted
-	 * with up to 24 Mbps rates.
-	 */
-	if (!rssi)
-		report.rcpi = 255; /* not available */
-	else if (rssi < -110)
-		report.rcpi = 0;
-	else if (rssi > 0)
-		report.rcpi = 220;
-	else
-		report.rcpi = (rssi + 110) * 2;
-
-	/* action_category + action_code */
-	buf = wpabuf_alloc(2 + sizeof(report));
-	if (buf == NULL) {
-		wpa_printf(MSG_ERROR,
-			   "RRM: Link measurement report failed. Buffer allocation failed");
-		return;
-	}
-
-	wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
-	wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
-	wpabuf_put_data(buf, &report, sizeof(report));
-	wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
-		    wpabuf_head(buf), wpabuf_len(buf));
-
-	if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
-				wpa_s->own_addr, wpa_s->bssid,
-				wpabuf_head(buf), wpabuf_len(buf), 0)) {
-		wpa_printf(MSG_ERROR,
-			   "RRM: Link measurement report failed. Send action failed");
-	}
-	wpabuf_free(buf);
-}
-
-
 struct wpa_supplicant *
 wpas_vendor_elem(struct wpa_supplicant *wpa_s, enum wpa_vendor_elem_frame frame)
 {
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index e55b380..6faa7af 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -98,9 +98,7 @@
 #    parameters (e.g., WPA IE generation); this mode can also be used with
 #    non-WPA drivers when using IEEE 802.1X mode; do not try to associate with
 #    APs (i.e., external program needs to control association). This mode must
-#    also be used when using wired Ethernet drivers.
-#    Note: macsec_qca driver is one type of Ethernet driver which implements
-#    macsec feature.
+#    also be used when using wired Ethernet drivers (including MACsec).
 # 2: like 0, but associate with APs using security policy and SSID (but not
 #    BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to
 #    enable operation with hidden SSIDs and optimized roaming; in this mode,
@@ -168,10 +166,13 @@
 fast_reauth=1
 
 # OpenSSL Engine support
-# These options can be used to load OpenSSL engines.
+# These options can be used to load OpenSSL engines in special or legacy
+# modes.
 # The two engines that are supported currently are shown below:
 # They are both from the opensc project (http://www.opensc.org/)
-# By default no engines are loaded.
+# By default the PKCS#11 engine is loaded if the client_cert or
+# private_key option appear to be a PKCS#11 URI, and these options
+# should not need to be used explicitly.
 # make the opensc engine available
 #opensc_engine_path=/usr/lib/opensc/engine_opensc.so
 # make the pkcs11 engine available
@@ -197,7 +198,7 @@
 #load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so
 
 # Driver interface parameters
-# This field can be used to configure arbitrary driver interace parameters. The
+# This field can be used to configure arbitrary driver interface parameters. The
 # format is specific to the selected driver interface. This field is not used
 # in most cases.
 #driver_param="field=value"
@@ -360,10 +361,12 @@
 
 # Protected Management Frames default
 # This parameter can be used to set the default behavior for the ieee80211w
-# parameter. By default, PMF is disabled unless enabled with the global pmf=1/2
-# parameter or with the per-network ieee80211w=1/2 parameter. With pmf=1/2, PMF
-# is enabled/required by default, but can be disabled with the per-network
-# ieee80211w parameter.
+# parameter for RSN networks. By default, PMF is disabled unless enabled with
+# the global pmf=1/2 parameter or with the per-network ieee80211w=1/2 parameter.
+# With pmf=1/2, PMF is enabled/required by default, but can be disabled with the
+# per-network ieee80211w parameter. This global default value does not apply
+# for non-RSN networks (key_mgmt=NONE) since PMF is available only when using
+# RSN.
 #pmf=0
 
 # Enabled SAE finite cyclic groups in preference order
@@ -419,6 +422,15 @@
 # 2 = like 1, but maintain OUI (with local admin bit set)
 #preassoc_mac_addr=0
 
+# MAC address policy for GAS operations
+# 0 = use permanent MAC address
+# 1 = use random MAC address
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#gas_rand_mac_addr=0
+
+# Lifetime of GAS random MAC address in seconds (default: 60)
+#gas_rand_addr_lifetime=60
+
 # Interworking (IEEE 802.11u)
 
 # Enable Interworking
@@ -438,6 +450,28 @@
 #     matching network block
 #auto_interworking=0
 
+# GAS Address3 field behavior
+# 0 = P2P specification (Address3 = AP BSSID); default
+# 1 = IEEE 802.11 standard compliant (Address3 = Wildcard BSSID when
+#     sent to not-associated AP; if associated, AP BSSID)
+#gas_address3=0
+
+# Publish fine timing measurement (FTM) responder functionality in
+# the Extended Capabilities element bit 70.
+# Controls whether FTM responder functionality will be published by AP/STA.
+# Note that actual FTM responder operation is managed outside wpa_supplicant.
+# 0 = Do not publish; default
+# 1 = Publish
+#ftm_responder=0
+
+# Publish fine timing measurement (FTM) initiator functionality in
+# the Extended Capabilities element bit 71.
+# Controls whether FTM initiator functionality will be published by AP/STA.
+# Note that actual FTM initiator operation is managed outside wpa_supplicant.
+# 0 = Do not publish; default
+# 1 = Publish
+#ftm_initiator=0
+
 # credential block
 #
 # Each credential used for automatic network selection is configured as a set
@@ -472,6 +506,10 @@
 #	(EAP-TLS). Full path to the file should be used since working
 #	directory may change when wpa_supplicant is run in the background.
 #
+#	Certificates from PKCS#11 tokens can be referenced by a PKCS#11 URI.
+#
+#	For example: private_key="pkcs11:manufacturer=piv_II;id=%01"
+#
 #	Alternatively, a named configuration blob can be used by setting
 #	this to blob://blob_name.
 #
@@ -482,6 +520,9 @@
 #	used since working directory may change when wpa_supplicant is run
 #	in the background.
 #
+#	Keys in PKCS#11 tokens can be referenced by a PKCS#11 URI.
+#	For example: private_key="pkcs11:manufacturer=piv_II;id=%01"
+#
 #	Windows certificate store can be used by leaving client_cert out and
 #	configuring private_key in one of the following formats:
 #
@@ -643,11 +684,11 @@
 
 # Multi Band Operation (MBO) non-preferred channels
 # A space delimited list of non-preferred channels where each channel is a colon
-# delimited list of values. Reason detail is optional.
+# delimited list of values.
 # Format:
-# non_pref_chan=<oper_class>:<chan>:<preference>:<reason>[:reason_detail]
+# non_pref_chan=<oper_class>:<chan>:<preference>:<reason>
 # Example:
-# non_pref_chan="81:5:10:2:0 81:1:0:2:0 81:9:0:2"
+# non_pref_chan="81:5:10:2 81:1:0:2 81:9:0:2"
 
 # MBO Cellular Data Capabilities
 # 1 = Cellular data connection available
@@ -717,10 +758,14 @@
 # the network will be used instead of this configured value.
 #
 # pbss: Whether to use PBSS. Relevant to IEEE 802.11ad networks only.
+# 0 = do not use PBSS
+# 1 = use PBSS
+# 2 = don't care (not allowed in AP mode)
 # Used together with mode configuration. When mode is AP, it means to start a
 # PCP instead of a regular AP. When mode is infrastructure it means connect
-# to a PCP instead of AP. P2P_GO and P2P_GROUP_FORMATION modes must use PBSS
-# in IEEE 802.11ad network.
+# to a PCP instead of AP. In this mode you can also specify 2 (don't care)
+# which means connect to either PCP or AP.
+# P2P_GO and P2P_GROUP_FORMATION modes must use PBSS in IEEE 802.11ad network.
 # For more details, see IEEE Std 802.11ad-2012.
 #
 # scan_freq: List of frequencies to scan
@@ -784,6 +829,10 @@
 # WPA-EAP-SUITE-B = Suite B 128-bit level
 # WPA-EAP-SUITE-B-192 = Suite B 192-bit level
 # OSEN = Hotspot 2.0 Rel 2 online signup connection
+# FILS-SHA256 = Fast Initial Link Setup with SHA256
+# FILS-SHA384 = Fast Initial Link Setup with SHA384
+# FT-FILS-SHA256 = FT and Fast Initial Link Setup with SHA256
+# FT-FILS-SHA384 = FT and Fast Initial Link Setup with SHA384
 # If not set, this defaults to: WPA-PSK WPA-EAP
 #
 # ieee80211w: whether management frame protection is enabled
@@ -839,17 +888,38 @@
 # bit0 (1): require dynamically generated unicast WEP key
 # bit1 (2): require dynamically generated broadcast WEP key
 # 	(3 = require both keys; default)
-# Note: When using wired authentication (including macsec_qca driver),
+# Note: When using wired authentication (including MACsec drivers),
 # eapol_flags must be set to 0 for the authentication to be completed
 # successfully.
 #
 # macsec_policy: IEEE 802.1X/MACsec options
-# This determines how sessions are secured with MACsec. It is currently
-# applicable only when using the macsec_qca driver interface.
+# This determines how sessions are secured with MACsec (only for MACsec
+# drivers).
 # 0: MACsec not in use (default)
 # 1: MACsec enabled - Should secure, accept key server's advice to
 #    determine whether to use a secure session or not.
 #
+# macsec_integ_only: IEEE 802.1X/MACsec transmit mode
+# This setting applies only when MACsec is in use, i.e.,
+#  - macsec_policy is enabled
+#  - the key server has decided to enable MACsec
+# 0: Encrypt traffic (default)
+# 1: Integrity only
+#
+# macsec_port: IEEE 802.1X/MACsec port
+# Port component of the SCI
+# Range: 1-65534 (default: 1)
+#
+# mka_cak, mka_ckn, and mka_priority: IEEE 802.1X/MACsec pre-shared key mode
+# This allows to configure MACsec with a pre-shared key using a (CAK,CKN) pair.
+# In this mode, instances of wpa_supplicant can act as MACsec peers. The peer
+# with lower priority will become the key server and start distributing SAKs.
+# mka_cak (CAK = Secure Connectivity Association Key) takes a 16-bytes (128 bit)
+# hex-string (32 hex-digits)
+# mka_ckn (CKN = CAK Name) takes a 32-bytes (256 bit) hex-string (64 hex-digits)
+# mka_priority (Priority of MKA Actor) is in 0..255 range with 255 being
+# default priority
+#
 # mixed_cell: This option can be used to configure whether so called mixed
 # cells, i.e., networks that use both plaintext and encryption in the same
 # SSID, are allowed when selecting a BSS from scan results.
@@ -874,9 +944,13 @@
 # wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
 # enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
 #
+# group_rekey: Group rekeying time in seconds. This value, if non-zero, is used
+# as the dot11RSNAConfigGroupRekeyTime parameter when operating in
+# Authenticator role in IBSS, or in AP and mesh modes.
+#
 # Following fields are only used with internal EAP implementation.
 # eap: space-separated list of accepted EAP methods
-#	MD5 = EAP-MD5 (unsecure and does not generate keying material ->
+#	MD5 = EAP-MD5 (insecure and does not generate keying material ->
 #			cannot be used with WPA; to be used as a Phase 2 method
 #			with EAP-PEAP or EAP-TTLS)
 #       MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used
@@ -967,23 +1041,23 @@
 #	automatically converted into DH params.
 # subject_match: Substring to be matched against the subject of the
 #	authentication server certificate. If this string is set, the server
-#	sertificate is only accepted if it contains this string in the subject.
+#	certificate is only accepted if it contains this string in the subject.
 #	The subject string is in following format:
 #	/C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com
-#	Note: Since this is a substring match, this cannot be used securily to
+#	Note: Since this is a substring match, this cannot be used securely to
 #	do a suffix match against a possible domain name in the CN entry. For
 #	such a use case, domain_suffix_match or domain_match should be used
 #	instead.
 # altsubject_match: Semicolon separated string of entries to be matched against
 #	the alternative subject name of the authentication server certificate.
-#	If this string is set, the server sertificate is only accepted if it
+#	If this string is set, the server certificate is only accepted if it
 #	contains one of the entries in an alternative subject name extension.
 #	altSubjectName string is in following format: TYPE:VALUE
 #	Example: EMAIL:server@example.com
 #	Example: DNS:server.example.com;DNS:server2.example.com
 #	Following types are supported: EMAIL, DNS, URI
 # domain_suffix_match: Constraint for server domain name. If set, this FQDN is
-#	used as a suffix match requirement for the AAAserver certificate in
+#	used as a suffix match requirement for the AAA server certificate in
 #	SubjectAltName dNSName element(s). If a matching dNSName is found, this
 #	constraint is met. If no dNSName values are present, this constraint is
 #	matched against SubjectName CN using same suffix match comparison.
@@ -1169,6 +1243,11 @@
 # Beacon interval (default: 100 TU)
 #beacon_int=100
 
+# WPS in AP mode
+# 0 = WPS enabled and configured (default)
+# 1 = WPS disabled
+#wps_disabled=0
+
 # MAC address policy
 # 0 = use permanent MAC address
 # 1 = use random MAC address for each ESS connection
@@ -1231,13 +1310,13 @@
 ##### Fast Session Transfer (FST) support #####################################
 #
 # The options in this section are only available when the build configuration
-# option CONFIG_FST is set while compiling hostapd. They allow this interface
-# to be a part of FST setup.
+# option CONFIG_FST is set while compiling wpa_supplicant. They allow this
+# interface to be a part of FST setup.
 #
 # FST is the transfer of a session from a channel to another channel, in the
 # same or different frequency bands.
 #
-# For detals, see IEEE Std 802.11ad-2012.
+# For details, see IEEE Std 802.11ad-2012.
 
 # Identifier of an FST Group  the interface belongs to.
 #fst_group_id=bond0
@@ -1570,22 +1649,10 @@
 	group=CCMP TKIP
 	identity="user@example.com"
 	ca_cert="/etc/cert/ca.pem"
-	client_cert="/etc/cert/user.pem"
 
-	engine=1
-
-	# The engine configured here must be available. Look at
-	# OpenSSL engine support in the global section.
-	# The key available through the engine must be the private key
-	# matching the client certificate configured above.
-
-	# use the opensc engine
-	#engine_id="opensc"
-	#key_id="45"
-
-	# use the pkcs11 engine
-	engine_id="pkcs11"
-	key_id="id_45"
+	# Certificate and/or key identified by PKCS#11 URI (RFC7512)
+	client_cert="pkcs11:manufacturer=piv_II;id=%01"
+	private_key="pkcs11:manufacturer=piv_II;id=%01"
 
 	# Optional PIN configuration; this can be left out and PIN will be
 	# asked through the control interface
diff --git a/wpa_supplicant/wpa_supplicant_conf.mk b/wpa_supplicant/wpa_supplicant_conf.mk
index 74986ea..4293976 100644
--- a/wpa_supplicant/wpa_supplicant_conf.mk
+++ b/wpa_supplicant/wpa_supplicant_conf.mk
@@ -16,7 +16,7 @@
 LOCAL_MODULE := wpa_supplicant.conf
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/wifi
+LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/wifi
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index c485891..395be3d 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -9,6 +9,7 @@
 #ifndef WPA_SUPPLICANT_I_H
 #define WPA_SUPPLICANT_I_H
 
+#include "utils/bitfield.h"
 #include "utils/list.h"
 #include "common/defs.h"
 #include "common/sae.h"
@@ -44,7 +45,7 @@
 struct ctrl_iface_priv;
 struct ctrl_iface_global_priv;
 struct wpas_dbus_priv;
-struct wpas_binder_priv;
+struct wpas_hidl_priv;
 
 /**
  * struct wpa_interface - Parameters for wpa_supplicant_add_iface()
@@ -265,7 +266,7 @@
 	struct wpa_params params;
 	struct ctrl_iface_global_priv *ctrl_iface;
 	struct wpas_dbus_priv *dbus;
-	struct wpas_binder_priv *binder;
+	struct wpas_hidl_priv *hidl;
 	void **drv_priv;
 	size_t drv_count;
 	struct os_time suspend_time;
@@ -393,11 +394,6 @@
 	u8 uuid[WPS_UUID_LEN];
 };
 
-struct wpa_ssid_value {
-	u8 ssid[SSID_MAX_LEN];
-	size_t ssid_len;
-};
-
 #define WPA_FREQ_USED_BY_INFRA_STATION BIT(0)
 #define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1)
 
@@ -429,6 +425,9 @@
 
 	/* next_neighbor_rep_token - Next request's dialog token */
 	u8 next_neighbor_rep_token;
+
+	/* token - Dialog token of the current radio measurement */
+	u8 token;
 };
 
 enum wpa_supplicant_test_failure {
@@ -451,6 +450,28 @@
 	struct os_reltime disallowed_until;
 };
 
+struct beacon_rep_data {
+	u8 token;
+	struct wpa_driver_scan_params scan_params;
+	u8 ssid[SSID_MAX_LEN];
+	size_t ssid_len;
+	u8 bssid[ETH_ALEN];
+	enum beacon_report_detail report_detail;
+	struct bitfield *eids;
+};
+
+
+struct external_pmksa_cache {
+	struct dl_list list;
+	void *pmksa_cache;
+};
+
+struct fils_hlp_req {
+	struct dl_list list;
+	u8 dst[ETH_ALEN];
+	struct wpabuf *pkt;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -484,9 +505,9 @@
 	char *preq_notify_peer;
 #endif /* CONFIG_AP */
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
-#ifdef CONFIG_CTRL_IFACE_BINDER
-	const void *binder_object_key;
-#endif /* CONFIG_CTRL_IFACE_BINDER */
+#ifdef CONFIG_CTRL_IFACE_HIDL
+	const void *hidl_object_key;
+#endif /* CONFIG_CTRL_IFACE_HIDL */
 	char bridge_ifname[16];
 
 	char *confname;
@@ -579,6 +600,7 @@
 	struct wpa_radio_work *scan_work;
 	int scanning;
 	int sched_scanning;
+	unsigned int sched_scan_stop_req:1;
 	int new_connection;
 
 	int eapol_received; /* number of EAPOL packets received after the
@@ -656,6 +678,12 @@
 	int normal_scans; /* normal scans run before sched_scan */
 	int scan_for_connection; /* whether the scan request was triggered for
 				  * finding a connection */
+	/*
+	 * A unique cookie representing the vendor scan request. This cookie is
+	 * returned from the driver interface. 0 indicates that there is no
+	 * pending vendor scan request.
+	 */
+	u64 curr_scan_cookie;
 #define MAX_SCAN_ID 16
 	int scan_id[MAX_SCAN_ID];
 	unsigned int scan_id_count;
@@ -725,7 +753,7 @@
 		u8 ssid[SSID_MAX_LEN];
 		size_t ssid_len;
 		int freq;
-		u8 assoc_req_ie[200];
+		u8 assoc_req_ie[1500];
 		size_t assoc_req_ie_len;
 		int mfp;
 		int ft_used;
@@ -774,6 +802,10 @@
 	unsigned int mesh_if_created:1;
 	unsigned int mesh_ht_enabled:1;
 	unsigned int mesh_vht_enabled:1;
+#ifdef CONFIG_PMKSA_CACHE_EXTERNAL
+	/* struct external_pmksa_cache::list */
+	struct dl_list mesh_external_pmksa_cache;
+#endif /* CONFIG_PMKSA_CACHE_EXTERNAL */
 #endif /* CONFIG_MESH */
 
 	unsigned int off_channel_freq;
@@ -898,6 +930,7 @@
 
 	unsigned int p2p_go_max_oper_chwidth;
 	unsigned int p2p_go_vht_center_freq2;
+	int p2p_lo_started;
 #endif /* CONFIG_P2P */
 
 	struct wpa_ssid *bgscan_ssid;
@@ -971,6 +1004,8 @@
 
 	/* WLAN_STATUS_* status codes from (Re)Association Response frame. */
 	u16 assoc_status_code;
+	/* Indicates if the previous association request timed out. */
+	u8 assoc_timed_out;
 
 	struct ext_password_data *ext_pw;
 
@@ -984,6 +1019,7 @@
 	unsigned int wmm_ac_supported:1;
 	unsigned int ext_work_in_progress:1;
 	unsigned int own_disconnect_req:1;
+	unsigned int ignore_post_flush_scan_res:1;
 
 #define MAC_ADDR_RAND_SCAN       BIT(0)
 #define MAC_ADDR_RAND_SCHED_SCAN BIT(1)
@@ -1027,7 +1063,11 @@
 	struct l2_packet_data *l2_test;
 	unsigned int extra_roc_dur;
 	enum wpa_supplicant_test_failure test_failure;
+	char *get_pref_freq_list_override;
+	unsigned int reject_btm_req_reason;
 	unsigned int p2p_go_csa_on_inv:1;
+	unsigned int ignore_auth_resp:1;
+	unsigned int ignore_assoc_disallow:1;
 #endif /* CONFIG_TESTING_OPTIONS */
 
 	struct wmm_ac_assoc_data *wmm_ac_assoc_info;
@@ -1038,6 +1078,7 @@
 	u8 last_tspecs_count;
 
 	struct rrm_data rrm;
+	struct beacon_rep_data beacon_rep_data;
 
 #ifdef CONFIG_FST
 	struct fst_iface *fst;
@@ -1051,7 +1092,6 @@
 		enum mbo_non_pref_chan_reason reason;
 		u8 oper_class;
 		u8 chan;
-		u8 reason_detail;
 		u8 preference;
 	} *non_pref_chan;
 	size_t non_pref_chan_num;
@@ -1063,6 +1103,54 @@
 	 * the bss_temp_disallowed list for other purposes as well.
 	 */
 	struct dl_list bss_tmp_disallowed;
+
+	/*
+	 * Content of a measurement report element with type 8 (LCI),
+	 * own location.
+	 */
+	struct wpabuf *lci;
+	struct os_reltime lci_time;
+
+	struct os_reltime beacon_rep_scan;
+
+	/* FILS HLP requests (struct fils_hlp_req) */
+	struct dl_list fils_hlp_req;
+
+	struct sched_scan_relative_params {
+		/**
+		 * relative_rssi_set - Enable relatively preferred BSS reporting
+		 *
+		 * 0 = Disable reporting relatively preferred BSSs
+		 * 1 = Enable reporting relatively preferred BSSs
+		 */
+		int relative_rssi_set;
+
+		/**
+		 * relative_rssi - Relative RSSI for reporting better BSSs
+		 *
+		 * Amount of RSSI by which a BSS should be better than the
+		 * current connected BSS so that the new BSS can be reported
+		 * to user space. This applies to sched_scan operations.
+		 */
+		int relative_rssi;
+
+		/**
+		 * relative_adjust_band - Band in which RSSI is to be adjusted
+		 */
+		enum set_band relative_adjust_band;
+
+		/**
+		 * relative_adjust_rssi - RSSI adjustment
+		 *
+		 * An amount of relative_adjust_rssi should be added to the
+		 * BSSs that belong to the relative_adjust_band while comparing
+		 * with other bands for BSS reporting.
+		 */
+		int relative_adjust_rssi;
+	} srp;
+
+	/* RIC elements for FT protocol */
+	struct wpabuf *ric_ies;
 };
 
 
@@ -1104,6 +1192,8 @@
 void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
 				   int reason_code);
 
+struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id);
 void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
 				   struct wpa_ssid *ssid);
 void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
@@ -1159,6 +1249,7 @@
 int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
 		    size_t ssid_len);
 void wpas_request_connection(struct wpa_supplicant *wpa_s);
+void wpas_request_disconnection(struct wpa_supplicant *wpa_s);
 int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
 int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
 int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
@@ -1168,14 +1259,23 @@
 void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
 				   const u8 *report, size_t report_len);
 int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
-				       const struct wpa_ssid *ssid,
+				       const struct wpa_ssid_value *ssid,
+				       int lci, int civic,
 				       void (*cb)(void *ctx,
 						  struct wpabuf *neighbor_rep),
 				       void *cb_ctx);
+void wpas_rrm_handle_radio_measurement_request(struct wpa_supplicant *wpa_s,
+					       const u8 *src,
+					       const u8 *frame, size_t len);
 void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
 					      const u8 *src,
 					      const u8 *frame, size_t len,
 					      int rssi);
+int wpas_beacon_rep_scan_process(struct wpa_supplicant *wpa_s,
+				 struct wpa_scan_results *scan_res,
+				 struct scan_info *info);
+void wpas_clear_beacon_rep_data(struct wpa_supplicant *wpa_s);
+void wpas_flush_fils_hlp_req(struct wpa_supplicant *wpa_s);
 
 
 /* MBO functions */
@@ -1184,14 +1284,24 @@
 int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
 				  const char *non_pref_chan);
 void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie);
-int wpas_mbo_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
-			      size_t len);
 void wpas_mbo_ie_trans_req(struct wpa_supplicant *wpa_s, const u8 *ie,
 			   size_t len);
 size_t wpas_mbo_ie_bss_trans_reject(struct wpa_supplicant *wpa_s, u8 *pos,
 				    size_t len,
 				    enum mbo_transition_reject_reason reason);
 void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa);
+struct wpabuf * mbo_build_anqp_buf(struct wpa_supplicant *wpa_s,
+				   struct wpa_bss *bss);
+
+/* op_classes.c */
+enum chan_allowed {
+	NOT_ALLOWED, NO_IR, ALLOWED
+};
+
+enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 channel,
+				 u8 bw);
+size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, int freq, u8 *pos,
+			      size_t len);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -1207,6 +1317,20 @@
 					      struct wpa_ssid *ssid,
 					      const char *field,
 					      const char *value);
+/**
+ * wpa_supplicant_ctrl_rsp_handle - Handle a control response
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Pointer to the network block the reply is for
+ * @field: field the response is a reply for
+ * @value: value (ie, password, etc) for @field
+ * Returns: 0 on success, non-zero on error
+ *
+ * Helper function to handle replies to control interface requests.
+ */
+int wpa_supplicant_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
+				   struct wpa_ssid *ssid,
+				   enum wpa_ctrl_req_type rtype,
+				   const char *value);
 
 void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
 			  const struct wpa_ssid *ssid,
@@ -1281,6 +1405,11 @@
 struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 				     int i, struct wpa_bss *bss,
 				     struct wpa_ssid *group,
-				     int only_first_ssid);
+				     int only_first_ssid, int debug_print);
+
+int wpas_ctrl_iface_get_pref_freq_list_override(struct wpa_supplicant *wpa_s,
+						enum wpa_driver_if_type if_type,
+						unsigned int *num,
+						unsigned int *freq_list);
 
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpa_supplicant_template.conf b/wpa_supplicant/wpa_supplicant_template.conf
index f3f2a64..f55227f 100644
--- a/wpa_supplicant/wpa_supplicant_template.conf
+++ b/wpa_supplicant/wpa_supplicant_template.conf
@@ -4,3 +4,4 @@
 ap_scan=1
 fast_reauth=1
 pmf=1
+p2p_add_cli_chan=1
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index f84c8b9..0d753a9 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
 #include "rsn_supp/wpa.h"
 #include "eloop.h"
 #include "config.h"
@@ -513,16 +514,44 @@
 }
 
 
-static int wpa_supplicant_add_pmkid(void *wpa_s,
+static struct wpa_ssid * wpas_get_network_ctx(struct wpa_supplicant *wpa_s,
+					      void *network_ctx)
+{
+	struct wpa_ssid *ssid;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+		if (network_ctx == ssid)
+			return ssid;
+	}
+
+	return NULL;
+}
+
+
+static int wpa_supplicant_add_pmkid(void *_wpa_s, void *network_ctx,
 				    const u8 *bssid, const u8 *pmkid)
 {
+	struct wpa_supplicant *wpa_s = _wpa_s;
+	struct wpa_ssid *ssid;
+
+	ssid = wpas_get_network_ctx(wpa_s, network_ctx);
+	if (ssid)
+		wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_ADDED MACSTR " %d",
+			MAC2STR(bssid), ssid->id);
 	return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
 }
 
 
-static int wpa_supplicant_remove_pmkid(void *wpa_s,
+static int wpa_supplicant_remove_pmkid(void *_wpa_s, void *network_ctx,
 				       const u8 *bssid, const u8 *pmkid)
 {
+	struct wpa_supplicant *wpa_s = _wpa_s;
+	struct wpa_ssid *ssid;
+
+	ssid = wpas_get_network_ctx(wpa_s, network_ctx);
+	if (ssid)
+		wpa_msg(wpa_s, MSG_INFO, PMKSA_CACHE_REMOVED MACSTR " %d",
+			MAC2STR(bssid), ssid->id);
 	return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
 }
 
@@ -865,6 +894,7 @@
 
 
 #ifdef CONFIG_EAP_PROXY
+
 static void wpa_supplicant_eap_proxy_cb(void *ctx)
 {
 	struct wpa_supplicant *wpa_s = ctx;
@@ -880,6 +910,52 @@
 		wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
 	}
 }
+
+
+static void wpa_sm_sim_state_error_handler(struct wpa_supplicant *wpa_s)
+{
+	int i;
+	struct wpa_ssid *ssid;
+	const struct eap_method_type *eap_methods;
+
+	if (!wpa_s->conf)
+		return;
+
+	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)	{
+		eap_methods = ssid->eap.eap_methods;
+		if (!eap_methods)
+			continue;
+
+		for (i = 0; eap_methods[i].method != EAP_TYPE_NONE; i++) {
+			if (eap_methods[i].vendor == EAP_VENDOR_IETF &&
+			    (eap_methods[i].method == EAP_TYPE_SIM ||
+			     eap_methods[i].method == EAP_TYPE_AKA ||
+			     eap_methods[i].method == EAP_TYPE_AKA_PRIME)) {
+				wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+				break;
+			}
+		}
+	}
+}
+
+
+static void
+wpa_supplicant_eap_proxy_notify_sim_status(void *ctx,
+					   enum eap_proxy_sim_state sim_state)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+
+	wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status %u", sim_state);
+	switch (sim_state) {
+	case SIM_STATE_ERROR:
+		wpa_sm_sim_state_error_handler(wpa_s);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "eap_proxy: SIM card status unknown");
+		break;
+	}
+}
+
 #endif /* CONFIG_EAP_PROXY */
 
 
@@ -990,6 +1066,8 @@
 	ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
 #ifdef CONFIG_EAP_PROXY
 	ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb;
+	ctx->eap_proxy_notify_sim_status =
+		wpa_supplicant_eap_proxy_notify_sim_status;
 #endif /* CONFIG_EAP_PROXY */
 	ctx->port_cb = wpa_supplicant_port_cb;
 	ctx->cb = wpa_supplicant_eapol_cb;
@@ -1012,6 +1090,7 @@
 
 
 #ifndef CONFIG_NO_WPA
+
 static void wpa_supplicant_set_rekey_offload(void *ctx,
 					     const u8 *kek, size_t kek_len,
 					     const u8 *kck, size_t kck_len,
@@ -1035,6 +1114,25 @@
 	else
 		return 0;
 }
+
+
+static void wpa_supplicant_fils_hlp_rx(void *ctx, const u8 *dst, const u8 *src,
+				       const u8 *pkt, size_t pkt_len)
+{
+	struct wpa_supplicant *wpa_s = ctx;
+	char *hex;
+	size_t hexlen;
+
+	hexlen = pkt_len * 2 + 1;
+	hex = os_malloc(hexlen);
+	if (!hex)
+		return;
+	wpa_snprintf_hex(hex, hexlen, pkt, pkt_len);
+	wpa_msg(wpa_s, MSG_INFO, FILS_HLP_RX "dst=" MACSTR " src=" MACSTR
+		" frame=%s", MAC2STR(dst), MAC2STR(src), hex);
+	os_free(hex);
+}
+
 #endif /* CONFIG_NO_WPA */
 
 
@@ -1084,6 +1182,7 @@
 #endif /* CONFIG_TDLS */
 	ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
 	ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
+	ctx->fils_hlp_rx = wpa_supplicant_fils_hlp_rx;
 
 	wpa_s->wpa = wpa_sm_init(ctx);
 	if (wpa_s->wpa == NULL) {
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
index 354decf..d087e00 100644
--- a/wpa_supplicant/wpas_kay.c
+++ b/wpa_supplicant/wpas_kay.c
@@ -38,22 +38,33 @@
 }
 
 
+static int wpas_macsec_get_capability(void *priv, enum macsec_cap *cap)
+{
+	return wpa_drv_macsec_get_capability(priv, cap);
+}
+
+
 static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled)
 {
 	return wpa_drv_enable_protect_frames(wpa_s, enabled);
 }
 
 
+static int wpas_enable_encrypt(void *wpa_s, Boolean enabled)
+{
+	return wpa_drv_enable_encrypt(wpa_s, enabled);
+}
+
+
 static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window)
 {
 	return wpa_drv_set_replay_protect(wpa_s, enabled, window);
 }
 
 
-static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs,
-					 size_t cs_len)
+static int wpas_set_current_cipher_suite(void *wpa_s, u64 cs)
 {
-	return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len);
+	return wpa_drv_set_current_cipher_suite(wpa_s, cs);
 }
 
 
@@ -63,30 +74,21 @@
 }
 
 
-static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel,
-				      u8 an, u32 *lowest_pn)
+static int wpas_get_receive_lowest_pn(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn);
+	return wpa_drv_get_receive_lowest_pn(wpa_s, sa);
 }
 
 
-static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel,
-				      u8 an, u32 *next_pn)
+static int wpas_get_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn);
+	return wpa_drv_get_transmit_next_pn(wpa_s, sa);
 }
 
 
-static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel,
-				      u8 an, u32 next_pn)
+static int wpas_set_transmit_next_pn(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn);
-}
-
-
-static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel)
-{
-	return wpa_drv_get_available_receive_sc(wpa_s, channel);
+	return wpa_drv_set_transmit_next_pn(wpa_s, sa);
 }
 
 
@@ -104,81 +106,79 @@
 }
 
 
-static int wpas_create_receive_sc(void *wpa_s, u32 channel,
-				  struct ieee802_1x_mka_sci *sci,
+static int wpas_create_receive_sc(void *wpa_s, struct receive_sc *sc,
 				  enum validate_frames vf,
 				  enum confidentiality_offset co)
 {
-	return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port,
-					 conf_offset_val(co), vf);
+	return wpa_drv_create_receive_sc(wpa_s, sc, conf_offset_val(co), vf);
 }
 
 
-static int wpas_delete_receive_sc(void *wpa_s, u32 channel)
+static int wpas_delete_receive_sc(void *wpa_s, struct receive_sc *sc)
 {
-	return wpa_drv_delete_receive_sc(wpa_s, channel);
+	return wpa_drv_delete_receive_sc(wpa_s, sc);
 }
 
 
-static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an,
-				  u32 lowest_pn, const u8 *sak)
+static int wpas_create_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak);
+	return wpa_drv_create_receive_sa(wpa_s, sa);
 }
 
 
-static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_delete_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_enable_receive_sa(wpa_s, channel, an);
+	return wpa_drv_delete_receive_sa(wpa_s, sa);
 }
 
 
-static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_enable_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_disable_receive_sa(wpa_s, channel, an);
+	return wpa_drv_enable_receive_sa(wpa_s, sa);
 }
 
 
-static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel)
+static int wpas_disable_receive_sa(void *wpa_s, struct receive_sa *sa)
 {
-	return wpa_drv_get_available_transmit_sc(wpa_s, channel);
+	return wpa_drv_disable_receive_sa(wpa_s, sa);
 }
 
 
 static int
-wpas_create_transmit_sc(void *wpa_s, u32 channel,
-			const struct ieee802_1x_mka_sci *sci,
+wpas_create_transmit_sc(void *wpa_s, struct transmit_sc *sc,
 			enum confidentiality_offset co)
 {
-	return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port,
-					  conf_offset_val(co));
+	return wpa_drv_create_transmit_sc(wpa_s, sc, conf_offset_val(co));
 }
 
 
-static int wpas_delete_transmit_sc(void *wpa_s, u32 channel)
+static int wpas_delete_transmit_sc(void *wpa_s, struct transmit_sc *sc)
 {
-	return wpa_drv_delete_transmit_sc(wpa_s, channel);
+	return wpa_drv_delete_transmit_sc(wpa_s, sc);
 }
 
 
-static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an,
-				   u32 next_pn, Boolean confidentiality,
-				   const u8 *sak)
+static int wpas_create_transmit_sa(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn,
-					  confidentiality, sak);
+	return wpa_drv_create_transmit_sa(wpa_s, sa);
 }
 
 
-static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_delete_transmit_sa(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_enable_transmit_sa(wpa_s, channel, an);
+	return wpa_drv_delete_transmit_sa(wpa_s, sa);
 }
 
 
-static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+static int wpas_enable_transmit_sa(void *wpa_s, struct transmit_sa *sa)
 {
-	return wpa_drv_disable_transmit_sa(wpa_s, channel, an);
+	return wpa_drv_enable_transmit_sa(wpa_s, sa);
+}
+
+
+static int wpas_disable_transmit_sa(void *wpa_s, struct transmit_sa *sa)
+{
+	return wpa_drv_disable_transmit_sa(wpa_s, sa);
 }
 
 
@@ -193,7 +193,14 @@
 	if (!ssid || ssid->macsec_policy == 0)
 		return 0;
 
-	policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE;
+	if (ssid->macsec_policy == 1) {
+		if (ssid->macsec_integ_only == 1)
+			policy = SHOULD_SECURE;
+		else
+			policy = SHOULD_ENCRYPT;
+	} else {
+		policy = DO_NOT_SECURE;
+	}
 
 	kay_ctx = os_zalloc(sizeof(*kay_ctx));
 	if (!kay_ctx)
@@ -203,27 +210,30 @@
 
 	kay_ctx->macsec_init = wpas_macsec_init;
 	kay_ctx->macsec_deinit = wpas_macsec_deinit;
+	kay_ctx->macsec_get_capability = wpas_macsec_get_capability;
 	kay_ctx->enable_protect_frames = wpas_enable_protect_frames;
+	kay_ctx->enable_encrypt = wpas_enable_encrypt;
 	kay_ctx->set_replay_protect = wpas_set_replay_protect;
 	kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite;
 	kay_ctx->enable_controlled_port = wpas_enable_controlled_port;
 	kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
 	kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
 	kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
-	kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc;
 	kay_ctx->create_receive_sc = wpas_create_receive_sc;
 	kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
 	kay_ctx->create_receive_sa = wpas_create_receive_sa;
+	kay_ctx->delete_receive_sa = wpas_delete_receive_sa;
 	kay_ctx->enable_receive_sa = wpas_enable_receive_sa;
 	kay_ctx->disable_receive_sa = wpas_disable_receive_sa;
-	kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc;
 	kay_ctx->create_transmit_sc = wpas_create_transmit_sc;
 	kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc;
 	kay_ctx->create_transmit_sa = wpas_create_transmit_sa;
+	kay_ctx->delete_transmit_sa = wpas_delete_transmit_sa;
 	kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa;
 	kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa;
 
-	res = ieee802_1x_kay_init(kay_ctx, policy, wpa_s->ifname,
+	res = ieee802_1x_kay_init(kay_ctx, policy, ssid->macsec_port,
+				  ssid->mka_priority, wpa_s->ifname,
 				  wpa_s->own_addr);
 	if (res == NULL) {
 		os_free(kay_ctx);
@@ -376,3 +386,51 @@
 
 	return res;
 }
+
+
+void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid)
+{
+	struct mka_key *cak;
+	struct mka_key_name *ckn;
+	void *res;
+
+	if ((ssid->mka_psk_set & MKA_PSK_SET) != MKA_PSK_SET)
+		return NULL;
+
+	if (ieee802_1x_alloc_kay_sm(wpa_s, ssid) < 0)
+		return NULL;
+
+	if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE)
+		return NULL;
+
+	ckn = os_zalloc(sizeof(*ckn));
+	if (!ckn)
+		goto dealloc;
+
+	cak = os_zalloc(sizeof(*cak));
+	if (!cak)
+		goto free_ckn;
+
+	cak->len = MACSEC_CAK_LEN;
+	os_memcpy(cak->key, ssid->mka_cak, cak->len);
+
+	ckn->len = MACSEC_CKN_LEN;
+	os_memcpy(ckn->name, ssid->mka_ckn, ckn->len);
+
+	res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0, PSK, FALSE);
+	if (res)
+		return res;
+
+	/* Failed to create MKA */
+	os_free(cak);
+
+	/* fallthrough */
+
+free_ckn:
+	os_free(ckn);
+dealloc:
+	ieee802_1x_dealloc_kay_sm(wpa_s);
+
+	return NULL;
+}
diff --git a/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h
index b7236d0..81f8e0c 100644
--- a/wpa_supplicant/wpas_kay.h
+++ b/wpa_supplicant/wpas_kay.h
@@ -17,6 +17,9 @@
 				      const u8 *peer_addr);
 void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s);
 
+void * ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+				       struct wpa_ssid *ssid);
+
 #else /* CONFIG_MACSEC */
 
 static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
@@ -36,6 +39,13 @@
 {
 }
 
+static inline void *
+ieee802_1x_create_preshared_mka(struct wpa_supplicant *wpa_s,
+				struct wpa_ssid *ssid)
+{
+	return 0;
+}
+
 #endif /* CONFIG_MACSEC */
 
 #endif /* WPAS_KAY_H */
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
index 6af1678..4e37591 100644
--- a/wpa_supplicant/wpas_module_tests.c
+++ b/wpa_supplicant/wpas_module_tests.c
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/module_tests.h"
 #include "wpa_supplicant_i.h"
 #include "blacklist.h"
 
@@ -79,30 +80,18 @@
 		ret = -1;
 
 #ifdef CONFIG_WPS
-	{
-		int wps_module_tests(void);
-		if (wps_module_tests() < 0)
-			ret = -1;
-	}
+	if (wps_module_tests() < 0)
+		ret = -1;
 #endif /* CONFIG_WPS */
 
-	{
-		int utils_module_tests(void);
-		if (utils_module_tests() < 0)
-			ret = -1;
-	}
+	if (utils_module_tests() < 0)
+		ret = -1;
 
-	{
-		int common_module_tests(void);
-		if (common_module_tests() < 0)
-			ret = -1;
-	}
+	if (common_module_tests() < 0)
+		ret = -1;
 
-	{
-		int crypto_module_tests(void);
-		if (crypto_module_tests() < 0)
-			ret = -1;
-	}
+	if (crypto_module_tests() < 0)
+		ret = -1;
 
 	return ret;
 }
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 0860eb4..228269e 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1142,6 +1142,13 @@
 		return -1;
 	ssid->temporary = 1;
 	ssid->p2p_group = p2p_group;
+	/*
+	 * When starting a regular WPS process (not P2P group formation)
+	 * the registrar/final station can be either AP or PCP
+	 * so use a "don't care" value for the pbss flag.
+	 */
+	if (!p2p_group)
+		ssid->pbss = 2;
 #ifdef CONFIG_P2P
 	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
 		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
@@ -1197,6 +1204,13 @@
 	}
 	ssid->temporary = 1;
 	ssid->p2p_group = p2p_group;
+	/*
+	 * When starting a regular WPS process (not P2P group formation)
+	 * the registrar/final station can be either AP or PCP
+	 * so use a "don't care" value for the pbss flag.
+	 */
+	if (!p2p_group)
+		ssid->pbss = 2;
 	if (ssid_val) {
 		ssid->ssid = os_malloc(ssid_len);
 		if (ssid->ssid) {
@@ -2196,7 +2210,7 @@
 #ifdef CONFIG_WPS_NFC
 
 #ifdef CONFIG_WPS_ER
-static struct wpabuf *
+struct wpabuf *
 wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef,
 			      struct wpa_ssid *ssid)
 {
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
index c8fe47e..eb1615a 100644
--- a/wpa_supplicant/wps_supplicant.h
+++ b/wpa_supplicant/wps_supplicant.h
@@ -62,6 +62,8 @@
 					     int ndef, const char *uuid);
 int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
 void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
+struct wpabuf * wpas_wps_network_config_token(struct wpa_supplicant *wpa_s,
+					  int ndef, struct wpa_ssid * ssid);
 struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
 					  int ndef, const char *id_str);
 struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);