Cumulative patch from commit 077dcfb8c48d2509a6e116c0de3ad57d2fbfe4fe

077dcfb AP: Debug print management frame TX result
ca911d6 MBO: Parse non-preferred channel list on the AP
3f48274 WNM: Fix a memory leak on AP error path
f5ca176 VLAN: Fix vlan_compare() for tagged VLANs
1260564 hostapd_cli: Add support for RAW command
940491c MBO: Mandate use of PMF for WPA2+MBO association (STA)
4c57228 MBO: Mandate use of PMF for WPA2+MBO association (AP)
8dd49f0 MBO: Update STA cellular data capability based on WNM Notification
6332aaf MBO: Track STA cellular data capability from association request
f3cb7a6 WNM: Minimal processing for WNM Notification Request frames on AP
e578343 MBO: Indicate WNM-Notification support on AP when MBO is enabled
990b7b6 Simplify hostapd_build_ap_extra_ies() with helper functions
d010048 MBO: Expire non-matching bss_tmp_disallowed entries as part of check
f4c74e1 MBO: Parse MBO IE in ieee802_11_parse_elems()
016082e MBO: Send WNM-Notification when cellular capabilities change
c0e2a17 hostapd: Add MBO IE to BSS Transition Management Request frame
fb9a1c3 hostapd: Add MBO IE to Beacon, Probe Response, Association Response
c484b19 Move Hotspot 2.0 element in (Re)Association Request frames
a0c38e5 Re-order elements in (Re)Association Request frames
9a493fa WNM: Add candidate list to BSS transition query
84d1c0f WNM: Add candidate list to BSS transition response
cf11ab7 utils: Derive phy type by frequency and bandwidth
c8082d2 MBO: Add MBO IE to BSS Transition Management Response frame
dd59990 MBO: Parse MBO IE in BSS Transition Management Request frames
5e57ba2 MBO: Add Supported Operating Classes element to Association Request
7d46f58 MBO: Add global operating class definitions
cb06cf3 MBO: Prevent association to APs that explicitly disallow this
c5d193d MBO: Add cellular capability to MBO IE
2d5b861 MBO: Send MBO WNM-Notification Request frames to notify changes
92c6e2e MBO: Implement MBO non-preferred channel report in Association Request
facf2c7 MBO: Add non-preferred channel configuration in wpa_supplicant
425dd78 MBO: Add Multi Band Operation definitions
a159958 ndis: Use the new get_ie() helper to avoid duplicated code
231b04b utils: Share a single helper function to get IE by ID
ea69d97 wpa_supplicant: Share a single get_mode() implementation
75cc211 VLAN: Check vlan_desc validity in a failure debug print
43022ab Use 64-bit TX/RX byte counters for statistics
3f81ac0 AP: Set STA assoc flag in the driver before sending Assoc Resp frame
bb598c3 AP: Add support for full station state
dc55b6b nl80211: Add support for full station state operations
5558b99 EAP-FAST peer: Remove fixed return value from eap_fast_parse_phase1()
4b16c15 EAP-pwd server: Use os_get_random() for unpredictable token
239952b DFS: Remove the os_random() fallback
98a516e WPS: Use only os_get_random() for PIN generation
f441e5a Use os_get_random() for Shared Key authentication challenge
8c676b5 Add RADIUS Service-Type attribute with a value of Framed
09d96de mesh: Drop Authentication frames from BLOCKED STA
70c9396 SAE: Fix PMKID calculation for PMKSA cache
1492fbb Print Acct-Session-Id and Acct-Multi-Session-Id 64-bit values
e21ceca kqueue: Use 0 instead of NULL for udata
640b0b9 ctype functions require an unsigned char
a5a3efc Fix compile on NetBSD for vlan
a084c24 wired: Fix compile on NetBSD for wired driver
634e2e2 Add CONFIG_ELOOP_KQUEUE to defconfig
99a94f5 nl80211: Avoid wpa_printf %s call with NULL pointer in set_param()
ba91e92 wpa_supplicant: Parse ifname argument from DATA_TEST_CONFIG
8be640b VLAN: Add per-STA vif option
d0bdc96 VLAN: Actually add tagged VLANs to AP_VLAN
f9c0018 VLAN: Factor out per-vid code in newlink/dellink
8e44c19 radius: Add tagged VLAN parsing
1889af2 VLAN: Separate station grouping and uplink configuration
3a583e0 OpenSSL: Fix PKCS#12 parsing of extra certificates with OpenSSL 1.0.1
ddd0032 wpa_cli: Clean up logical operation
24c382a TDLS: Clean up os_memcmp use
6136d43 trace: Free symbols on program exit
8bcf8de OpenSSL: Fix memory leak in PKCS12 additional certificate parsing
03e3ddf OpenSSL: Fix memory leak in HMAC_CTX compatibility wrapper function
d9a0f69 OpenSSL: Fix memory leak in OCSP parsing
29bc76e OpenSSL: Do not use library init/deinit functions with 1.1.0
0f09637 OpenSSL: Fix memory leak in subjectAltName parsing
e60913b curl: Fix memory leak in subjectAltName parsing
6014890 OpenSSL: Fix memory leak with EVP_CIPHER_CTX_new()
99a1735 rfkill: Fix a memory leak
1f1e599 OpenSSL: Fix memory leak on error path
b907491 wpa_supplicant: Basic support for PBSS/PCP
86b5c40 nl80211: Basic support for PBSS/PCP
afa453a Sync with mac80211-next.git include/uapi/linux/nl80211.h
d1d8a2b EAP peer: Simplify buildNotify return
1314bc1 Clean up EAP peer PCSC identity functions

Change-Id: I9db475a2a4ebc88d2ee024319ed59a850636bb16
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/Android.mk b/wpa_supplicant/Android.mk
index 95690bf..8f4cc9c 100644
--- a/wpa_supplicant/Android.mk
+++ b/wpa_supplicant/Android.mk
@@ -805,6 +805,9 @@
 ifdef CONFIG_WNM
 OBJS += src/ap/wnm_ap.c
 endif
+ifdef CONFIG_MBO
+OBJS += src/ap/mbo_ap.c
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += src/ap/ctrl_iface_ap.c
 endif
@@ -821,6 +824,11 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.c
+L_CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_AP_MLME
 OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 234a0bf..28ae172 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -848,6 +848,9 @@
 ifdef CONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
 endif
+ifdef CONFIG_MBO
+OBJS += ../src/ap/mbo_ap.o
+endif
 ifdef CONFIG_CTRL_IFACE
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
@@ -864,6 +867,11 @@
 endif
 endif
 
+ifdef CONFIG_MBO
+OBJS += mbo.o
+CFLAGS += -DCONFIG_MBO
+endif
+
 ifdef NEED_AP_MLME
 OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
diff --git a/wpa_supplicant/android.config b/wpa_supplicant/android.config
index e9c0a01..190bc5a 100644
--- a/wpa_supplicant/android.config
+++ b/wpa_supplicant/android.config
@@ -482,4 +482,7 @@
 # Enable Fast Session Transfer (FST)
 #CONFIG_FST=y
 
+# Support Multi Band Operation
+#CONFIG_MBO=y
+
 include $(wildcard $(LOCAL_PATH)/android_config_*.inc)
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index 98b9596..60f8c0d 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -453,6 +453,8 @@
 			wpabuf_dup(wpa_s->conf->ap_vendor_elements);
 	}
 
+	bss->pbss = ssid->pbss;
+
 	return 0;
 }
 
@@ -913,7 +915,10 @@
 		return -1;
 
 	if (pin == NULL) {
-		unsigned int rpin = wps_generate_pin();
+		unsigned int rpin;
+
+		if (wps_generate_pin(&rpin) < 0)
+			return -1;
 		ret_len = os_snprintf(buf, buflen, "%08d", rpin);
 		if (os_snprintf_error(buflen, ret_len))
 			return -1;
@@ -979,7 +984,8 @@
 	if (wpa_s->ap_iface == NULL)
 		return NULL;
 	hapd = wpa_s->ap_iface->bss[0];
-	pin = wps_generate_pin();
+	if (wps_generate_pin(&pin) < 0)
+		return NULL;
 	os_snprintf(pin_txt, sizeof(pin_txt), "%08u", pin);
 	os_free(hapd->conf->ap_pin);
 	hapd->conf->ap_pin = os_strdup(pin_txt);
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
index 24cc986..a83ca10 100644
--- a/wpa_supplicant/bss.c
+++ b/wpa_supplicant/bss.c
@@ -1019,20 +1019,7 @@
  */
 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
 {
-	const u8 *end, *pos;
-
-	pos = (const u8 *) (bss + 1);
-	end = pos + bss->ie_len;
-
-	while (end - pos > 1) {
-		if (2 + pos[1] > end - pos)
-			break;
-		if (pos[0] == ie)
-			return pos;
-		pos += 2 + pos[1];
-	}
-
-	return NULL;
+	return get_ie((const u8 *) (bss + 1), bss->ie_len, ie);
 }
 
 
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
index 4a782af..f7f72f3 100644
--- a/wpa_supplicant/bss.h
+++ b/wpa_supplicant/bss.h
@@ -148,6 +148,17 @@
 	return bss->freq > 45000;
 }
 
+/**
+ * Test whether a BSS is a PBSS.
+ * This checks whether a BSS is a DMG-band PBSS. PBSS is used for P2P DMG
+ * network.
+ */
+static inline int bss_is_pbss(struct wpa_bss *bss)
+{
+	return bss_is_dmg(bss) &&
+		(bss->caps & IEEE80211_CAP_DMG_MASK) == IEEE80211_CAP_DMG_PBSS;
+}
+
 static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level)
 {
 	if (bss != NULL && new_level < 0)
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 85717e9..eff1043 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -1980,6 +1980,7 @@
 	{ INT(update_identifier) },
 #endif /* CONFIG_HS20 */
 	{ INT_RANGE(mac_addr, 0, 2) },
+	{ INT_RANGE(pbss, 0, 1) },
 };
 
 #undef OFFSET
@@ -2285,6 +2286,9 @@
 	os_free(config->wowlan_triggers);
 	os_free(config->fst_group_id);
 	os_free(config->sched_scan_plans);
+#ifdef CONFIG_MBO
+	os_free(config->non_pref_chan);
+#endif /* CONFIG_MBO */
 
 	os_free(config);
 }
@@ -3557,6 +3561,10 @@
 	config->cert_in_cb = DEFAULT_CERT_IN_CB;
 	config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
 
+#ifdef CONFIG_MBO
+	config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
+#endif /* CONFIG_MBO */
+
 	if (ctrl_interface)
 		config->ctrl_interface = os_strdup(ctrl_interface);
 	if (driver_param)
@@ -4264,6 +4272,11 @@
 #endif /* CONFIG_FST */
 	{ INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
 	{ STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
+#ifdef CONFIG_MBO
+	{ STR(non_pref_chan), 0 },
+	{ INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
+		    MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
+#endif /*CONFIG_MBO */
 };
 
 #undef FUNC
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
index 86f940d..9a13f5f 100644
--- a/wpa_supplicant/config.h
+++ b/wpa_supplicant/config.h
@@ -40,6 +40,7 @@
 #define DEFAULT_CERT_IN_CB 1
 #define DEFAULT_P2P_GO_CTWINDOW 0
 #define DEFAULT_WPA_RSC_RELAXATION 1
+#define DEFAULT_MBO_CELL_CAPA MBO_CELL_CAPA_NOT_SUPPORTED
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -1275,6 +1276,21 @@
 	 * format: <interval:iterations> <interval2:iterations2> ... <interval>
 	 */
 	 char *sched_scan_plans;
+
+#ifdef CONFIG_MBO
+	/**
+	 * non_pref_chan - Non-preferred channels list, separated by spaces.
+	 *
+	 * format: op_class:chan:preference:reason<:detail>
+	 * Detail is optional.
+	 */
+	char *non_pref_chan;
+
+	/**
+	 * mbo_cell_capa - Cellular capabilities for MBO
+	 */
+	enum mbo_cellular_capa mbo_cell_capa;
+#endif /* CONFIG_MBO */
 };
 
 
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index c9ba8b7..38061f1 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -755,6 +755,7 @@
 	INT(peerkey);
 	INT(mixed_cell);
 	INT(max_oper_chwidth);
+	INT(pbss);
 #ifdef CONFIG_IEEE80211W
 	write_int(f, "ieee80211w", ssid->ieee80211w,
 		  MGMT_FRAME_PROTECTION_DEFAULT);
@@ -1326,6 +1327,14 @@
 
 	if (config->sched_scan_plans)
 		fprintf(f, "sched_scan_plans=%s\n", config->sched_scan_plans);
+
+#ifdef CONFIG_MBO
+	if (config->non_pref_chan)
+		fprintf(f, "non_pref_chan=%s\n", config->non_pref_chan);
+	if (config->mbo_cell_capa != DEFAULT_MBO_CELL_CAPA)
+		fprintf(f, "mbo_cell_capa=%u\n", config->mbo_cell_capa);
+#endif /* CONFIG_MBO */
+
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index b296826..eb7b87b 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -360,6 +360,15 @@
 	} mode;
 
 	/**
+	 * pbss - Whether to use PBSS. Relevant to DMG networks only.
+	 * 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.
+	 */
+	int pbss;
+
+	/**
 	 * disabled - Whether this network is currently disabled
 	 *
 	 * 0 = this network can be used (default).
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index b3d6246..f585b92 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -490,6 +490,12 @@
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 	} else if (os_strcasecmp(cmd, "setband") == 0) {
 		ret = wpas_ctrl_set_band(wpa_s, value);
+#ifdef CONFIG_MBO
+	} else if (os_strcasecmp(cmd, "non_pref_chan") == 0) {
+		ret = wpas_mbo_update_non_pref_chan(wpa_s, value);
+	} else if (os_strcasecmp(cmd, "mbo_cell_capa") == 0) {
+		wpas_mbo_update_cell_capa(wpa_s, atoi(value));
+#endif /* CONFIG_MBO */
 	} else {
 		value[-1] = '=';
 		ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -956,7 +962,8 @@
 	if (os_strcmp(cmd, "any") == 0)
 		_bssid = NULL;
 	else if (os_strcmp(cmd, "get") == 0) {
-		ret = wps_generate_pin();
+		if (wps_generate_pin((unsigned int *) &ret) < 0)
+			return -1;
 		goto done;
 	} else if (hwaddr_aton(cmd, bssid)) {
 		wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
@@ -4712,7 +4719,7 @@
 			return -1;
 		}
 
-		if (isblank(*last)) {
+		if (isblank((unsigned char) *last)) {
 			i++;
 			break;
 		}
@@ -6719,14 +6726,27 @@
 
 static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
 {
-	int query_reason;
+	int query_reason, list = 0;
 
 	query_reason = atoi(cmd);
 
-	wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d",
-		   query_reason);
+	cmd = os_strchr(cmd, ' ');
+	if (cmd) {
+		cmd++;
+		if (os_strncmp(cmd, "list", 4) == 0) {
+			list = 1;
+		} else {
+			wpa_printf(MSG_DEBUG, "WNM Query: Invalid option %s",
+				   cmd);
+			return -1;
+		}
+	}
 
-	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason);
+	wpa_printf(MSG_DEBUG,
+		   "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d%s",
+		   query_reason, list ? " candidate list" : "");
+
+	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason, list);
 }
 
 #endif /* CONFIG_WNM */
@@ -6917,13 +6937,13 @@
 
 	/* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
 	vendor_id = strtoul(cmd, &pos, 16);
-	if (!isblank(*pos))
+	if (!isblank((unsigned char) *pos))
 		return -EINVAL;
 
 	subcmd = strtoul(pos, &pos, 10);
 
 	if (*pos != '\0') {
-		if (!isblank(*pos++))
+		if (!isblank((unsigned char) *pos++))
 			return -EINVAL;
 		data_len = os_strlen(pos);
 	}
@@ -7729,6 +7749,8 @@
 					    char *cmd)
 {
 	int enabled = atoi(cmd);
+	char *pos;
+	const char *ifname;
 
 	if (!enabled) {
 		if (wpa_s->l2_test) {
@@ -7742,7 +7764,13 @@
 	if (wpa_s->l2_test)
 		return 0;
 
-	wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr,
+	pos = os_strstr(cmd, " ifname=");
+	if (pos)
+		ifname = pos + 8;
+	else
+		ifname = wpa_s->ifname;
+
+	wpa_s->l2_test = l2_packet_init(ifname, wpa_s->own_addr,
 					ETHERTYPE_IP, wpas_data_test_rx,
 					wpa_s, 1);
 	if (wpa_s->l2_test == NULL)
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 8b1d121..79632e6 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -270,6 +270,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
@@ -539,3 +542,6 @@
 # For more details refer to:
 # http://wireless.kernel.org/en/users/Documentation/acs
 #CONFIG_ACS=y
+
+# Support Multi Band Operation
+#CONFIG_MBO=y
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
index 1aede79..6548bd1 100644
--- a/wpa_supplicant/eapol_test.c
+++ b/wpa_supplicant/eapol_test.c
@@ -258,6 +258,13 @@
 		goto fail;
 	}
 
+	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_SERVICE_TYPE) &&
+	    !radius_msg_add_attr_int32(msg, RADIUS_ATTR_SERVICE_TYPE,
+				       RADIUS_SERVICE_TYPE_FRAMED)) {
+		printf("Could not add Service-Type\n");
+		goto fail;
+	}
+
 	os_snprintf(buf, sizeof(buf), "%s", e->connect_info);
 	if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) &&
 	    !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 3c60cc1..ecb22e2 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -574,6 +574,16 @@
 				"   skip RSN IE - no mgmt frame protection enabled but AP requires it");
 			break;
 		}
+#ifdef CONFIG_MBO
+		if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+		    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");
+			break;
+		}
+#endif /* CONFIG_MBO */
 
 		wpa_dbg(wpa_s, MSG_DEBUG, "   selected based on RSN IE");
 		return 1;
@@ -816,10 +826,10 @@
 }
 
 
-static 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)
+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)
 {
 	u8 wpa_ie_len, rsn_ie_len;
 	int wpa;
@@ -827,6 +837,9 @@
 	const u8 *ie;
 	struct wpa_ssid *ssid;
 	int osen;
+#ifdef CONFIG_MBO
+	const u8 *assoc_disallow;
+#endif /* CONFIG_MBO */
 
 	ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
 	wpa_ie_len = ie ? ie[1] : 0;
@@ -988,8 +1001,14 @@
 			continue;
 		}
 
-		if (!bss_is_ess(bss)) {
-			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - not ESS network");
+		if (!bss_is_ess(bss) && !bss_is_pbss(bss)) {
+			wpa_dbg(wpa_s, MSG_DEBUG, "   skip - neither ESS nor PBSS network");
+			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));
 			continue;
 		}
 
@@ -1058,6 +1077,22 @@
 				(unsigned int) diff.usec);
 			continue;
 		}
+#ifdef CONFIG_MBO
+		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)",
+				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");
+			continue;
+		}
+#endif /* CONFIG_MBO */
 
 		/* Matching configuration found */
 		return ssid;
@@ -2526,7 +2561,8 @@
 	    !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
 	    !disallowed_ssid(wpa_s, fast_reconnect->ssid,
 			     fast_reconnect->ssid_len) &&
-	    !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
+	    !wpas_temp_disabled(wpa_s, fast_reconnect_ssid) &&
+	    !wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect->bssid)) {
 #ifndef CONFIG_NO_SCAN_PROCESSING
 		wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
 		if (wpa_supplicant_connect(wpa_s, fast_reconnect,
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index d9d0ae7..c00db31 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -230,7 +230,7 @@
 	wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
 	wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
 	wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
-	wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL);
+	wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL, NULL);
 
 	peer->supp_ie_len = sizeof(peer->supp_ie);
 	if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
diff --git a/wpa_supplicant/mbo.c b/wpa_supplicant/mbo.c
new file mode 100644
index 0000000..3292e67
--- /dev/null
+++ b/wpa_supplicant/mbo.c
@@ -0,0 +1,771 @@
+/*
+ * wpa_supplicant - MBO
+ *
+ * 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_defs.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+
+/* type + length + oui + oui type */
+#define MBO_IE_HEADER 6
+
+
+static int wpas_mbo_validate_non_pref_chan(u8 oper_class, u8 chan, u8 reason)
+{
+	if (reason > MBO_NON_PREF_CHAN_REASON_INT_INTERFERENCE)
+		return -1;
+
+	/* Only checking the validity of the channel and oper_class */
+	if (ieee80211_chan_to_freq(NULL, oper_class, chan) == -1)
+		return -1;
+
+	return 0;
+}
+
+
+const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr)
+{
+	const u8 *mbo, *end;
+
+	if (!bss)
+		return NULL;
+
+	mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+	if (!mbo)
+		return NULL;
+
+	end = mbo + 2 + mbo[1];
+	mbo += MBO_IE_HEADER;
+
+	return get_ie(mbo, end - mbo, attr);
+}
+
+
+static void wpas_mbo_non_pref_chan_attr_body(struct wpa_supplicant *wpa_s,
+					     struct wpabuf *mbo,
+					     u8 start, u8 end)
+{
+	u8 i;
+
+	wpabuf_put_u8(mbo, wpa_s->non_pref_chan[start].oper_class);
+
+	for (i = start; i < end; i++)
+		wpabuf_put_u8(mbo, wpa_s->non_pref_chan[i].chan);
+
+	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;
+
+	if (size + 2 > wpabuf_tailroom(mbo))
+		return;
+
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
+	wpabuf_put_u8(mbo, size); /* Length */
+
+	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
+}
+
+
+static void wpas_mbo_non_pref_chan_subelem_hdr(struct wpabuf *mbo, u8 len)
+{
+	wpabuf_put_u8(mbo, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(mbo, len); /* Length */
+	wpabuf_put_be24(mbo, OUI_WFA);
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_NON_PREF_CHAN_REPORT);
+}
+
+
+static void wpas_mbo_non_pref_chan_subelement(struct wpa_supplicant *wpa_s,
+					      struct wpabuf *mbo, u8 start,
+					      u8 end)
+{
+	size_t size = end - start + 8;
+
+	if (size + 2 > wpabuf_tailroom(mbo))
+		return;
+
+	wpas_mbo_non_pref_chan_subelem_hdr(mbo, size);
+	wpas_mbo_non_pref_chan_attr_body(wpa_s, mbo, start, end);
+}
+
+
+static void wpas_mbo_non_pref_chan_attrs(struct wpa_supplicant *wpa_s,
+					 struct wpabuf *mbo, int subelement)
+{
+	u8 i, start = 0;
+	struct wpa_mbo_non_pref_channel *start_pref;
+
+	if (!wpa_s->non_pref_chan || !wpa_s->non_pref_chan_num) {
+		if (subelement)
+			wpas_mbo_non_pref_chan_subelem_hdr(mbo, 4);
+		return;
+	}
+	start_pref = &wpa_s->non_pref_chan[0];
+
+	for (i = 1; i <= wpa_s->non_pref_chan_num; i++) {
+		struct wpa_mbo_non_pref_channel *non_pref = NULL;
+
+		if (i < wpa_s->non_pref_chan_num)
+			non_pref = &wpa_s->non_pref_chan[i];
+		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,
+								  start, i);
+			else
+				wpas_mbo_non_pref_chan_attr(wpa_s, mbo, start,
+							    i);
+
+			if (!non_pref)
+				return;
+
+			start = i;
+			start_pref = non_pref;
+		}
+	}
+}
+
+
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+{
+	struct wpabuf *mbo;
+	int res;
+
+	if (len < MBO_IE_HEADER + 3 + 7)
+		return 0;
+
+	/* Leave room for the MBO IE header */
+	mbo = wpabuf_alloc(len - MBO_IE_HEADER);
+	if (!mbo)
+		return 0;
+
+	/* Add non-preferred channels attribute */
+	wpas_mbo_non_pref_chan_attrs(wpa_s, mbo, 0);
+
+	/*
+	 * Send cellular capabilities attribute even if AP does not advertise
+	 * cellular capabilities.
+	 */
+	wpabuf_put_u8(mbo, MBO_ATTR_ID_CELL_DATA_CAPA);
+	wpabuf_put_u8(mbo, 1);
+	wpabuf_put_u8(mbo, wpa_s->conf->mbo_cell_capa);
+
+	res = mbo_add_ie(buf, len, wpabuf_head_u8(mbo), wpabuf_len(mbo));
+	if (!res)
+		wpa_printf(MSG_ERROR, "Failed to add MBO IE");
+
+	wpabuf_free(mbo);
+	return res;
+}
+
+
+static void wpas_mbo_send_wnm_notification(struct wpa_supplicant *wpa_s,
+					   const u8 *data, size_t len)
+{
+	struct wpabuf *buf;
+	int res;
+
+	/*
+	 * Send WNM-Notification Request frame only in case of a change in
+	 * non-preferred channels list during association, if the AP supports
+	 * MBO.
+	 */
+	if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_bss ||
+	    !wpa_bss_get_vendor_ie(wpa_s->current_bss, MBO_IE_VENDOR_TYPE))
+		return;
+
+	buf = wpabuf_alloc(4 + len);
+	if (!buf)
+		return;
+
+	wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+	wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+	wpa_s->mbo_wnm_token++;
+	if (wpa_s->mbo_wnm_token == 0)
+		wpa_s->mbo_wnm_token++;
+	wpabuf_put_u8(buf, wpa_s->mbo_wnm_token);
+	wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC); /* Type */
+
+	wpabuf_put_data(buf, data, len);
+
+	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  wpabuf_head(buf), wpabuf_len(buf), 0);
+	if (res < 0)
+		wpa_printf(MSG_DEBUG,
+			   "Failed to send WNM-Notification Request frame with non-preferred channel list");
+
+	wpabuf_free(buf);
+}
+
+
+static void wpas_mbo_non_pref_chan_changed(struct wpa_supplicant *wpa_s)
+{
+	struct wpabuf *buf;
+
+	buf = wpabuf_alloc(512);
+	if (!buf)
+		return;
+
+	wpas_mbo_non_pref_chan_attrs(wpa_s, buf, 1);
+	wpas_mbo_send_wnm_notification(wpa_s, wpabuf_head_u8(buf),
+				       wpabuf_len(buf));
+	wpabuf_free(buf);
+}
+
+
+static int wpa_non_pref_chan_is_eq(struct wpa_mbo_non_pref_channel *a,
+				   struct wpa_mbo_non_pref_channel *b)
+{
+	return a->oper_class == b->oper_class && a->chan == b->chan;
+}
+
+
+/*
+ * wpa_non_pref_chan_cmp - Compare two channels for sorting
+ *
+ * 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.
+ */
+static int wpa_non_pref_chan_cmp(const void *_a, const void *_b)
+{
+	const struct wpa_mbo_non_pref_channel *a = _a, *b = _b;
+
+	if (a->oper_class != b->oper_class)
+		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;
+}
+
+
+int wpas_mbo_update_non_pref_chan(struct wpa_supplicant *wpa_s,
+				  const char *non_pref_chan)
+{
+	char *cmd, *token, *context = NULL;
+	struct wpa_mbo_non_pref_channel *chans = NULL, *tmp_chans;
+	size_t num = 0, size = 0;
+	unsigned i;
+
+	wpa_printf(MSG_DEBUG, "MBO: Update non-preferred channels, non_pref_chan=%s",
+		   non_pref_chan ? non_pref_chan : "N/A");
+
+	/*
+	 * The shortest channel configuration is 10 characters - commas, 3
+	 * colons, and 4 values that one of them (oper_class) is 2 digits or
+	 * more.
+	 */
+	if (!non_pref_chan || os_strlen(non_pref_chan) < 10)
+		goto update;
+
+	cmd = os_strdup(non_pref_chan);
+	if (!cmd)
+		return -1;
+
+	while ((token = str_token(cmd, " ", &context))) {
+		struct wpa_mbo_non_pref_channel *chan;
+		int ret;
+		unsigned int _oper_class;
+		unsigned int _chan;
+		unsigned int _preference;
+		unsigned int _reason;
+		unsigned int _reason_detail;
+
+		if (num == size) {
+			size = size ? size * 2 : 1;
+			tmp_chans = os_realloc_array(chans, size,
+						     sizeof(*chans));
+			if (!tmp_chans) {
+				wpa_printf(MSG_ERROR,
+					   "Couldn't reallocate non_pref_chan");
+				goto fail;
+			}
+			chans = tmp_chans;
+		}
+
+		chan = &chans[num];
+
+		ret = sscanf(token, "%u:%u:%u:%u:%u", &_oper_class,
+			     &_chan, &_preference, &_reason,
+			     &_reason_detail);
+		if ((ret != 4 && ret != 5) ||
+		    _oper_class > 255 || _chan > 255 ||
+		    _preference > 255 || _reason > 65535 ||
+		    (ret == 5 && _reason_detail > 255)) {
+			wpa_printf(MSG_ERROR, "Invalid non-pref chan input %s",
+				   token);
+			goto fail;
+		}
+		chan->oper_class = _oper_class;
+		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)) {
+			wpa_printf(MSG_ERROR,
+				   "Invalid non_pref_chan: oper class %d chan %d reason %d",
+				   chan->oper_class, chan->chan, chan->reason);
+			goto fail;
+		}
+
+		for (i = 0; i < num; i++)
+			if (wpa_non_pref_chan_is_eq(chan, &chans[i]))
+				break;
+		if (i != num) {
+			wpa_printf(MSG_ERROR,
+				   "oper class %d chan %d is duplicated",
+				   chan->oper_class, chan->chan);
+			goto fail;
+		}
+
+		num++;
+	}
+
+	os_free(cmd);
+
+	if (chans) {
+		qsort(chans, num, sizeof(struct wpa_mbo_non_pref_channel),
+		      wpa_non_pref_chan_cmp);
+	}
+
+update:
+	os_free(wpa_s->non_pref_chan);
+	wpa_s->non_pref_chan = chans;
+	wpa_s->non_pref_chan_num = num;
+	wpas_mbo_non_pref_chan_changed(wpa_s);
+
+	return 0;
+
+fail:
+	os_free(chans);
+	os_free(cmd);
+	return -1;
+}
+
+
+void wpas_mbo_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ie)
+{
+	wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+	wpabuf_put_u8(ie, 7);
+	wpabuf_put_be24(ie, OUI_WFA);
+	wpabuf_put_u8(ie, MBO_OUI_TYPE);
+
+	wpabuf_put_u8(ie, MBO_ATTR_ID_CELL_DATA_CAPA);
+	wpabuf_put_u8(ie, 1);
+	wpabuf_put_u8(ie, wpa_s->conf->mbo_cell_capa);
+}
+
+
+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)
+{
+	const u8 *pos, *cell_pref = NULL, *reason = NULL;
+	u8 id, elen;
+	u16 disallowed_sec = 0;
+
+	if (len <= 4 || WPA_GET_BE24(mbo_ie) != OUI_WFA ||
+	    mbo_ie[3] != MBO_OUI_TYPE)
+		return;
+
+	pos = mbo_ie + 4;
+	len -= 4;
+
+	while (len >= 2) {
+		id = *pos++;
+		elen = *pos++;
+		len -= 2;
+
+		if (elen > len)
+			goto fail;
+
+		switch (id) {
+		case MBO_ATTR_ID_CELL_DATA_PREF:
+			if (elen != 1)
+				goto fail;
+
+			if (wpa_s->conf->mbo_cell_capa ==
+			    MBO_CELL_CAPA_AVAILABLE)
+				cell_pref = pos;
+			else
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Station does not support Cellular data connection");
+			break;
+		case MBO_ATTR_ID_TRANSITION_REASON:
+			if (elen != 1)
+				goto fail;
+
+			reason = pos;
+			break;
+		case MBO_ATTR_ID_ASSOC_RETRY_DELAY:
+			if (elen != 2)
+				goto fail;
+
+			if (wpa_s->wnm_mode &
+			    WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) {
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Unexpected association retry delay, BSS is terminating");
+				goto fail;
+			} else if (wpa_s->wnm_mode &
+				   WNM_BSS_TM_REQ_DISASSOC_IMMINENT) {
+				disallowed_sec = WPA_GET_LE16(pos);
+			} else {
+				wpa_printf(MSG_DEBUG,
+					   "MBO: Association retry delay attribute not in disassoc imminent mode");
+			}
+
+			break;
+		case MBO_ATTR_ID_AP_CAPA_IND:
+		case MBO_ATTR_ID_NON_PREF_CHAN_REPORT:
+		case MBO_ATTR_ID_CELL_DATA_CAPA:
+		case MBO_ATTR_ID_ASSOC_DISALLOW:
+		case MBO_ATTR_ID_TRANSITION_REJECT_REASON:
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Attribute %d should not be included in BTM Request frame",
+				   id);
+			break;
+		default:
+			wpa_printf(MSG_DEBUG, "MBO: Unknown attribute id %u",
+				   id);
+			return;
+		}
+
+		pos += elen;
+		len -= elen;
+	}
+
+	if (cell_pref)
+		wpa_msg(wpa_s, MSG_INFO, MBO_CELL_PREFERENCE "preference=%u",
+			*cell_pref);
+
+	if (reason)
+		wpa_msg(wpa_s, MSG_INFO, MBO_TRANSITION_REASON "reason=%u",
+			*reason);
+
+	if (disallowed_sec && wpa_s->current_bss)
+		wpa_bss_tmp_disallow(wpa_s, wpa_s->current_bss->bssid,
+				     disallowed_sec);
+
+	return;
+fail:
+	wpa_printf(MSG_DEBUG, "MBO IE parsing failed (id=%u len=%u left=%zu)",
+		   id, elen, 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)
+{
+	u8 reject_attr[3];
+
+	reject_attr[0] = MBO_ATTR_ID_TRANSITION_REJECT_REASON;
+	reject_attr[1] = 1;
+	reject_attr[2] = reason;
+
+	return mbo_add_ie(pos, len, reject_attr, sizeof(reject_attr));
+}
+
+
+void wpas_mbo_update_cell_capa(struct wpa_supplicant *wpa_s, u8 mbo_cell_capa)
+{
+	u8 cell_capa[7];
+
+	if (wpa_s->conf->mbo_cell_capa == mbo_cell_capa) {
+		wpa_printf(MSG_DEBUG,
+			   "MBO: Cellular capability already set to %u",
+			   mbo_cell_capa);
+		return;
+	}
+
+	wpa_s->conf->mbo_cell_capa = mbo_cell_capa;
+
+	cell_capa[0] = WLAN_EID_VENDOR_SPECIFIC;
+	cell_capa[1] = 5; /* Length */
+	WPA_PUT_BE24(cell_capa + 2, OUI_WFA);
+	cell_capa[5] = MBO_ATTR_ID_CELL_DATA_CAPA;
+	cell_capa[6] = mbo_cell_capa;
+
+	wpas_mbo_send_wnm_notification(wpa_s, cell_capa, 7);
+}
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index 9b36b63..d274667 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -1898,6 +1898,8 @@
 		 */
 		ssid->pairwise_cipher = WPA_CIPHER_GCMP;
 		ssid->group_cipher = WPA_CIPHER_GCMP;
+		/* P2P GO in 60 GHz is always a PCP (PBSS) */
+		ssid->pbss = 1;
 	}
 	if (os_strlen(params->passphrase) > 0) {
 		ssid->passphrase = os_strdup(params->passphrase);
@@ -2576,7 +2578,13 @@
 	params[sizeof(params) - 1] = '\0';
 
 	if (config_methods & WPS_CONFIG_DISPLAY) {
-		generated_pin = wps_generate_pin();
+		if (wps_generate_pin(&generated_pin) < 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
+			wpas_notify_p2p_provision_discovery(
+				wpa_s, peer, 0 /* response */,
+				P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
+			return;
+		}
 		wpas_prov_disc_local_display(wpa_s, peer, params,
 					     generated_pin);
 	} else if (config_methods & WPS_CONFIG_KEYPAD)
@@ -2621,7 +2629,13 @@
 	if (config_methods & WPS_CONFIG_DISPLAY)
 		wpas_prov_disc_local_keypad(wpa_s, peer, params);
 	else if (config_methods & WPS_CONFIG_KEYPAD) {
-		generated_pin = wps_generate_pin();
+		if (wps_generate_pin(&generated_pin) < 0) {
+			wpa_printf(MSG_DEBUG, "P2P: Could not generate PIN");
+			wpas_notify_p2p_provision_discovery(
+				wpa_s, peer, 0 /* response */,
+				P2P_PROV_DISC_INFO_UNAVAILABLE, 0, 0);
+			return;
+		}
 		wpas_prov_disc_local_display(wpa_s, peer, params,
 					     generated_pin);
 	} else if (config_methods & WPS_CONFIG_PUSHBUTTON)
@@ -3277,21 +3291,6 @@
 }
 
 
-static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
-					  u16 num_modes,
-					  enum hostapd_hw_mode mode)
-{
-	u16 i;
-
-	for (i = 0; i < num_modes; i++) {
-		if (modes[i].mode == mode)
-			return &modes[i];
-	}
-
-	return NULL;
-}
-
-
 enum chan_allowed {
 	NOT_ALLOWED, NO_IR, ALLOWED
 };
@@ -3325,45 +3324,6 @@
 }
 
 
-struct p2p_oper_class_map {
-	enum hostapd_hw_mode mode;
-	u8 op_class;
-	u8 min_chan;
-	u8 max_chan;
-	u8 inc;
-	enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160, BW160, BW80P80 } bw;
-};
-
-static const struct p2p_oper_class_map op_class[] = {
-	{ HOSTAPD_MODE_IEEE80211G, 81, 1, 13, 1, BW20 },
-#if 0 /* Do not enable HT40 on 2 GHz for now */
-	{ HOSTAPD_MODE_IEEE80211G, 83, 1, 9, 1, BW40PLUS },
-	{ HOSTAPD_MODE_IEEE80211G, 84, 5, 13, 1, BW40MINUS },
-#endif
-	{ HOSTAPD_MODE_IEEE80211A, 115, 36, 48, 4, BW20 },
-	{ HOSTAPD_MODE_IEEE80211A, 124, 149, 161, 4, BW20 },
-	{ HOSTAPD_MODE_IEEE80211A, 125, 149, 169, 4, BW20 },
-	{ HOSTAPD_MODE_IEEE80211A, 116, 36, 44, 8, BW40PLUS },
-	{ HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
-	{ HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
-	{ HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
-
-	/*
-	 * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
-	 * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
-	 * 80 MHz, but currently use the following definition for simplicity
-	 * (these center frequencies are not actual channels, which makes
-	 * has_channel() fail). wpas_p2p_verify_80mhz() should take care of
-	 * removing invalid channels.
-	 */
-	{ HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
-	{ HOSTAPD_MODE_IEEE80211A, 130, 36, 161, 4, BW80P80 },
-	{ HOSTAPD_MODE_IEEE80211A, 129, 50, 114, 16, BW160 },
-	{ HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 },
-	{ -1, 0, 0, 0, 0, BW20 }
-};
-
-
 static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s,
 				     struct hostapd_hw_modes *mode,
 				     u8 channel)
@@ -3540,11 +3500,14 @@
 
 	cla = cli_cla = 0;
 
-	for (op = 0; op_class[op].op_class; op++) {
-		const struct p2p_oper_class_map *o = &op_class[op];
+	for (op = 0; global_op_class[op].op_class; op++) {
+		const struct oper_class_map *o = &global_op_class[op];
 		u8 ch;
 		struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
 
+		if (o->p2p == NO_P2P_SUPP)
+			continue;
+
 		mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
 		if (mode == NULL)
 			continue;
@@ -3599,10 +3562,13 @@
 	int op;
 	enum chan_allowed ret;
 
-	for (op = 0; op_class[op].op_class; op++) {
-		const struct p2p_oper_class_map *o = &op_class[op];
+	for (op = 0; global_op_class[op].op_class; op++) {
+		const struct oper_class_map *o = &global_op_class[op];
 		u8 ch;
 
+		if (o->p2p == NO_P2P_SUPP)
+			continue;
+
 		for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
 			if (o->mode != HOSTAPD_MODE_IEEE80211A ||
 			    (o->bw != BW40PLUS && o->bw != BW40MINUS) ||
@@ -5364,7 +5330,8 @@
 	if (pin)
 		os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
 	else if (wps_method == WPS_PIN_DISPLAY) {
-		ret = wps_generate_pin();
+		if (wps_generate_pin((unsigned int *) &ret) < 0)
+			return -1;
 		res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin),
 				  "%08d", ret);
 		if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res))
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
index 3c3f9e0..c333e57 100644
--- a/wpa_supplicant/scan.c
+++ b/wpa_supplicant/scan.c
@@ -490,6 +490,12 @@
 		wpabuf_put_buf(extra_ie, wpa_s->fst_ies);
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_MBO
+	/* Send cellular capabilities for potential MBO STAs */
+	if (wpabuf_resize(&extra_ie, 9) == 0)
+		wpas_mbo_scan_ie(wpa_s, extra_ie);
+#endif /* CONFIG_MBO */
+
 	return extra_ie;
 }
 
@@ -521,21 +527,6 @@
 #endif /* CONFIG_P2P */
 
 
-static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
-					  u16 num_modes,
-					  enum hostapd_hw_mode mode)
-{
-	u16 i;
-
-	for (i = 0; i < num_modes; i++) {
-		if (modes[i].mode == mode)
-			return &modes[i];
-	}
-
-	return NULL;
-}
-
-
 static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
 					enum hostapd_hw_mode band,
 					struct wpa_driver_scan_params *params)
@@ -1550,20 +1541,7 @@
  */
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
 {
-	const u8 *end, *pos;
-
-	pos = (const u8 *) (res + 1);
-	end = pos + res->ie_len;
-
-	while (end - pos > 1) {
-		if (2 + pos[1] > end - pos)
-			break;
-		if (pos[0] == ie)
-			return pos;
-		pos += 2 + pos[1];
-	}
-
-	return NULL;
+	return get_ie((const u8 *) (res + 1), res->ie_len, ie);
 }
 
 
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 2a3a728..f09534d 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -208,6 +208,9 @@
 	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 "
@@ -416,9 +419,55 @@
 	}
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_FST
+	if (wpa_s->fst_ies) {
+		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
+
+		if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
+		    sizeof(wpa_s->sme.assoc_req_ie)) {
+			os_memcpy(wpa_s->sme.assoc_req_ie +
+				  wpa_s->sme.assoc_req_ie_len,
+				  wpabuf_head(wpa_s->fst_ies),
+				  fst_ies_len);
+			wpa_s->sme.assoc_req_ie_len += fst_ies_len;
+		}
+	}
+#endif /* CONFIG_FST */
+
+	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;
+
+		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 */
+
+	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+					     sizeof(ext_capab));
+	if (ext_capab_len > 0) {
+		u8 *pos = wpa_s->sme.assoc_req_ie;
+		if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+			pos += 2 + pos[1];
+		os_memmove(pos + ext_capab_len, pos,
+			   wpa_s->sme.assoc_req_ie_len -
+			   (pos - wpa_s->sme.assoc_req_ie));
+		wpa_s->sme.assoc_req_ie_len += ext_capab_len;
+		os_memcpy(pos, ext_capab, ext_capab_len);
+	}
+
 #ifdef CONFIG_HS20
 	if (is_hs20_network(wpa_s, ssid, bss)) {
 		struct wpabuf *hs20;
+
 		hs20 = wpabuf_alloc(20);
 		if (hs20) {
 			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
@@ -438,34 +487,6 @@
 	}
 #endif /* CONFIG_HS20 */
 
-#ifdef CONFIG_FST
-	if (wpa_s->fst_ies) {
-		int fst_ies_len = wpabuf_len(wpa_s->fst_ies);
-
-		if (wpa_s->sme.assoc_req_ie_len + fst_ies_len <=
-		    sizeof(wpa_s->sme.assoc_req_ie)) {
-			os_memcpy(wpa_s->sme.assoc_req_ie +
-				  wpa_s->sme.assoc_req_ie_len,
-				  wpabuf_head(wpa_s->fst_ies),
-				  fst_ies_len);
-			wpa_s->sme.assoc_req_ie_len += fst_ies_len;
-		}
-	}
-#endif /* CONFIG_FST */
-
-	ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
-					     sizeof(ext_capab));
-	if (ext_capab_len > 0) {
-		u8 *pos = wpa_s->sme.assoc_req_ie;
-		if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
-			pos += 2 + pos[1];
-		os_memmove(pos + ext_capab_len, pos,
-			   wpa_s->sme.assoc_req_ie_len -
-			   (pos - wpa_s->sme.assoc_req_ie));
-		wpa_s->sme.assoc_req_ie_len += ext_capab_len;
-		os_memcpy(pos, ext_capab, ext_capab_len);
-	}
-
 	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
 		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
 		size_t len;
@@ -480,7 +501,18 @@
 		}
 	}
 
-	sme_auth_handle_rrm(wpa_s, bss);
+#ifdef CONFIG_MBO
+	if (mbo) {
+		int len;
+
+		len = wpas_mbo_ie(wpa_s, 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 */
 
 #ifdef CONFIG_SAE
 	if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
@@ -814,7 +846,7 @@
 		wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
 			   "4-way handshake");
 		wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
-			       wpa_s->pending_bssid);
+			       wpa_s->sme.sae.pmkid, wpa_s->pending_bssid);
 	}
 #endif /* CONFIG_SAE */
 
@@ -1322,21 +1354,6 @@
 }
 
 
-static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
-					  u16 num_modes,
-					  enum hostapd_hw_mode mode)
-{
-	u16 i;
-
-	for (i = 0; i < num_modes; i++) {
-		if (modes[i].mode == mode)
-			return &modes[i];
-	}
-
-	return NULL;
-}
-
-
 static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s,
 				     struct wpa_driver_scan_params *params)
 {
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index ffc9052..0dc41dc 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -546,6 +546,14 @@
 			continue;
 		}
 
+		if (wpa_is_bss_tmp_disallowed(wpa_s, target->bssid)) {
+			wpa_printf(MSG_DEBUG,
+				   "MBO: Candidate BSS " MACSTR
+				   " retry delay is not over yet",
+				   MAC2STR(nei->bssid));
+			continue;
+		}
+
 		if (target->level < bss->level && target->level < -80) {
 			wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
 				   " (pref %d) does not have sufficient signal level (%d)",
@@ -567,12 +575,190 @@
 }
 
 
+static int wpa_bss_ies_eq(struct wpa_bss *a, struct wpa_bss *b, u8 eid)
+{
+	const u8 *ie_a, *ie_b;
+
+	if (!a || !b)
+		return 0;
+
+	ie_a = wpa_bss_get_ie(a, eid);
+	ie_b = wpa_bss_get_ie(b, eid);
+
+	if (!ie_a || !ie_b || ie_a[1] != ie_b[1])
+		return 0;
+
+	return os_memcmp(ie_a, ie_b, ie_a[1]) == 0;
+}
+
+
+static u32 wnm_get_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+	u32 info = 0;
+
+	info |= NEI_REP_BSSID_INFO_AP_UNKNOWN_REACH;
+
+	/*
+	 * Leave the security and key scope bits unset to indicate that the
+	 * security information is not available.
+	 */
+
+	if (bss->caps & WLAN_CAPABILITY_SPECTRUM_MGMT)
+		info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+	if (bss->caps & WLAN_CAPABILITY_QOS)
+		info |= NEI_REP_BSSID_INFO_QOS;
+	if (bss->caps & WLAN_CAPABILITY_APSD)
+		info |= NEI_REP_BSSID_INFO_APSD;
+	if (bss->caps & WLAN_CAPABILITY_RADIO_MEASUREMENT)
+		info |= NEI_REP_BSSID_INFO_RM;
+	if (bss->caps & WLAN_CAPABILITY_DELAYED_BLOCK_ACK)
+		info |= NEI_REP_BSSID_INFO_DELAYED_BA;
+	if (bss->caps & WLAN_CAPABILITY_IMM_BLOCK_ACK)
+		info |= NEI_REP_BSSID_INFO_IMM_BA;
+	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_MOBILITY_DOMAIN))
+		info |= NEI_REP_BSSID_INFO_MOBILITY_DOMAIN;
+	if (wpa_bss_ies_eq(bss, wpa_s->current_bss, WLAN_EID_HT_CAP))
+		info |= NEI_REP_BSSID_INFO_HT;
+
+	return info;
+}
+
+
+static int wnm_add_nei_rep(u8 *buf, size_t len, const u8 *bssid, u32 bss_info,
+			   u8 op_class, u8 chan, u8 phy_type, u8 pref)
+{
+	u8 *pos = buf;
+
+	if (len < 18) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Not enough room for Neighbor Report element");
+		return -1;
+	}
+
+	*pos++ = WLAN_EID_NEIGHBOR_REPORT;
+	/* length: 13 for basic neighbor report + 3 for preference subelement */
+	*pos++ = 16;
+	os_memcpy(pos, bssid, ETH_ALEN);
+	pos += ETH_ALEN;
+	WPA_PUT_LE32(pos, bss_info);
+	pos += 4;
+	*pos++ = op_class;
+	*pos++ = chan;
+	*pos++ = phy_type;
+	*pos++ = WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE;
+	*pos++ = 1;
+	*pos++ = pref;
+	return pos - buf;
+}
+
+
+static int wnm_nei_rep_add_bss(struct wpa_supplicant *wpa_s,
+			       struct wpa_bss *bss, u8 *buf, size_t len,
+			       u8 pref)
+{
+	const u8 *ie;
+	u8 op_class, chan;
+	int sec_chan = 0, vht = 0;
+	enum phy_type phy_type;
+	u32 info;
+	struct ieee80211_ht_operation *ht_oper = NULL;
+	struct ieee80211_vht_operation *vht_oper = NULL;
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_HT_OPERATION);
+	if (ie && ie[1] >= 2) {
+		ht_oper = (struct ieee80211_ht_operation *) (ie + 2);
+
+		if (ht_oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+			sec_chan = 1;
+		else if (ht_oper->ht_param &
+			 HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+			sec_chan = -1;
+	}
+
+	ie = wpa_bss_get_ie(bss, WLAN_EID_VHT_OPERATION);
+	if (ie && ie[1] >= 1) {
+		vht_oper = (struct ieee80211_vht_operation *) (ie + 2);
+
+		if (vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80MHZ ||
+		    vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_160MHZ ||
+		    vht_oper->vht_op_info_chwidth == VHT_CHANWIDTH_80P80MHZ)
+			vht = vht_oper->vht_op_info_chwidth;
+	}
+
+	if (ieee80211_freq_to_channel_ext(bss->freq, sec_chan, vht, &op_class,
+					  &chan) == NUM_HOSTAPD_MODES) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Cannot determine operating class and channel");
+		return -2;
+	}
+
+	phy_type = ieee80211_get_phy_type(bss->freq, (ht_oper != NULL),
+					  (vht_oper != NULL));
+	if (phy_type == PHY_TYPE_UNSPECIFIED) {
+		wpa_printf(MSG_DEBUG,
+			   "WNM: Cannot determine BSS phy type for Neighbor Report");
+		return -2;
+	}
+
+	info = wnm_get_bss_info(wpa_s, bss);
+
+	return wnm_add_nei_rep(buf, len, bss->bssid, info, op_class, chan,
+			       phy_type, pref);
+}
+
+
+static int wnm_add_cand_list(struct wpa_supplicant *wpa_s, u8 *buf, size_t len)
+{
+	u8 *pos = buf;
+	unsigned int i, pref = 255;
+	struct os_reltime now;
+	struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+	if (!ssid)
+		return 0;
+
+	/*
+	 * TODO: Define when scan results are no longer valid for the candidate
+	 * list.
+	 */
+	os_get_reltime(&now);
+	if (os_reltime_expired(&now, &wpa_s->last_scan, 10))
+		return 0;
+
+	wpa_printf(MSG_DEBUG,
+		   "WNM: Add candidate list to BSS Transition Management Response frame");
+	for (i = 0; i < wpa_s->last_scan_res_used && pref; i++) {
+		struct wpa_bss *bss = wpa_s->last_scan_res[i];
+		int res;
+
+		if (wpa_scan_res_match(wpa_s, i, bss, ssid, 1)) {
+			res = wnm_nei_rep_add_bss(wpa_s, bss, pos, len, pref--);
+			if (res == -2)
+				continue; /* could not build entry for BSS */
+			if (res < 0)
+				break; /* no more room for candidates */
+			if (pref == 1)
+				break;
+
+			pos += res;
+			len -= res;
+		}
+	}
+
+	wpa_hexdump(MSG_DEBUG,
+		    "WNM: BSS Transition Management Response candidate list",
+		    buf, pos - buf);
+
+	return pos - buf;
+}
+
+
 static void wnm_send_bss_transition_mgmt_resp(
 	struct wpa_supplicant *wpa_s, u8 dialog_token,
 	enum bss_trans_mgmt_status_code status, u8 delay,
 	const u8 *target_bssid)
 {
-	u8 buf[1000], *pos;
+	u8 buf[2000], *pos;
 	struct ieee80211_mgmt *mgmt;
 	size_t len;
 	int res;
@@ -612,6 +798,17 @@
 		pos += ETH_ALEN;
 	}
 
+	if (status == WNM_BSS_TM_ACCEPT)
+		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+
+#ifdef CONFIG_MBO
+	if (status != WNM_BSS_TM_ACCEPT) {
+		pos += wpas_mbo_ie_bss_trans_reject(
+			wpa_s, pos, buf + sizeof(buf) - pos,
+			MBO_TRANSITION_REJECT_REASON_UNSPECIFIED);
+	}
+#endif /* CONFIG_MBO */
+
 	len = pos - (u8 *) &mgmt->u.action.category;
 
 	res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
@@ -821,6 +1018,9 @@
 {
 	unsigned int beacon_int;
 	u8 valid_int;
+#ifdef CONFIG_MBO
+	const u8 *vendor;
+#endif /* CONFIG_MBO */
 
 	if (end - pos < 5)
 		return;
@@ -881,6 +1081,12 @@
 		}
 	}
 
+#ifdef CONFIG_MBO
+	vendor = get_ie(pos, end - pos, WLAN_EID_VENDOR_SPECIFIC);
+	if (vendor)
+		wpas_mbo_ie_trans_req(wpa_s, vendor + 2, vendor[1]);
+#endif /* CONFIG_MBO */
+
 	if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
 		unsigned int valid_ms;
 
@@ -959,16 +1165,17 @@
 
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason)
+				       u8 query_reason, int cand_list)
 {
-	u8 buf[1000], *pos;
+	u8 buf[2000], *pos;
 	struct ieee80211_mgmt *mgmt;
 	size_t len;
 	int ret;
 
 	wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Query to "
-		   MACSTR " query_reason=%u",
-		   MAC2STR(wpa_s->bssid), query_reason);
+		   MACSTR " query_reason=%u%s",
+		   MAC2STR(wpa_s->bssid), query_reason,
+		   cand_list ? " candidate list" : "");
 
 	mgmt = (struct ieee80211_mgmt *) buf;
 	os_memset(&buf, 0, sizeof(buf));
@@ -983,6 +1190,9 @@
 	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
 	pos = mgmt->u.action.u.bss_tm_query.variable;
 
+	if (cand_list)
+		pos += wnm_add_cand_list(wpa_s, pos, buf + sizeof(buf) - pos);
+
 	len = pos - (u8 *) &mgmt->u.action.category;
 
 	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 8de4348..81d8153 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -56,7 +56,7 @@
 			      const struct ieee80211_mgmt *mgmt, size_t len);
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
-				       u8 query_reason);
+				       u8 query_reason, int cand_list);
 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 
 
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 265f72c..f8bf6bd 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -1612,7 +1612,7 @@
 #ifdef CONFIG_HS20
 	"update_identifier",
 #endif /* CONFIG_HS20 */
-	"mac_addr"
+	"mac_addr", "pbss"
 };
 
 
@@ -3418,7 +3418,7 @@
 	{ "wnm_sleep", wpa_cli_cmd_wnm_sleep, NULL, cli_cmd_flag_none,
 	  "<enter/exit> [interval=#] = enter/exit WNM-Sleep mode" },
 	{ "wnm_bss_query", wpa_cli_cmd_wnm_bss_query, NULL, cli_cmd_flag_none,
-	  "<query reason> = Send BSS Transition Management Query" },
+	  "<query reason> [list] = Send BSS Transition Management Query" },
 #endif /* CONFIG_WNM */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
@@ -4152,7 +4152,7 @@
 	if (ctrl_ifname == NULL)
 		ctrl_ifname = wpa_cli_get_default_ifname();
 
-	if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
+	if (wpa_cli_open_connection(ctrl_ifname, 1)) {
 		if (!warning_displayed) {
 			printf("Could not connect to wpa_supplicant: "
 			       "%s - re-trying\n",
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index c3c1f14..dbfc34e 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -397,6 +397,18 @@
 }
 
 
+static void free_bss_tmp_disallowed(struct wpa_supplicant *wpa_s)
+{
+	struct wpa_bss_tmp_disallowed *bss, *prev;
+
+	dl_list_for_each_safe(bss, prev, &wpa_s->bss_tmp_disallowed,
+			      struct wpa_bss_tmp_disallowed, list) {
+		dl_list_del(&bss->list);
+		os_free(bss);
+	}
+}
+
+
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
 	int i;
@@ -549,6 +561,14 @@
 	wpa_s->sched_scan_plans_num = 0;
 	os_free(wpa_s->sched_scan_plans);
 	wpa_s->sched_scan_plans = NULL;
+
+#ifdef CONFIG_MBO
+	wpa_s->non_pref_chan_num = 0;
+	os_free(wpa_s->non_pref_chan);
+	wpa_s->non_pref_chan = NULL;
+#endif /* CONFIG_MBO */
+
+	free_bss_tmp_disallowed(wpa_s);
 }
 
 
@@ -1293,7 +1313,8 @@
 		int psk_set = 0;
 
 		if (ssid->psk_set) {
-			wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
+			wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL,
+				       NULL);
 			psk_set = 1;
 		}
 #ifndef CONFIG_NO_PBKDF2
@@ -1304,7 +1325,7 @@
 				    4096, psk, PMK_LEN);
 		        wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
 					psk, PMK_LEN);
-			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+			wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL, NULL);
 			psk_set = 1;
 			os_memset(psk, 0, sizeof(psk));
 		}
@@ -1342,7 +1363,8 @@
 				wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
 						"external passphrase)",
 						psk, PMK_LEN);
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+					       NULL);
 				psk_set = 1;
 				os_memset(psk, 0, sizeof(psk));
 			} else
@@ -1355,7 +1377,8 @@
 					ext_password_free(pw);
 					return -1;
 				}
-				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+				wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL,
+					       NULL);
 				psk_set = 1;
 				os_memset(psk, 0, sizeof(psk));
 			} else {
@@ -1418,6 +1441,9 @@
 		if (wpa_s->conf->hs20)
 			*pos |= 0x40; /* Bit 46 - WNM-Notification */
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+		*pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_MBO */
 		break;
 	case 6: /* Bits 48-55 */
 		break;
@@ -2041,6 +2067,9 @@
        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) {
@@ -2225,25 +2254,21 @@
 	os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 #endif /* CONFIG_P2P */
 
-#ifdef CONFIG_HS20
-	if (is_hs20_network(wpa_s, ssid, bss)) {
-		struct wpabuf *hs20;
-		hs20 = wpabuf_alloc(20);
-		if (hs20) {
-			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
-			size_t len;
+#ifdef CONFIG_MBO
+	if (bss) {
+		mbo = wpa_bss_get_vendor_ie(bss, MBO_IE_VENDOR_TYPE);
+		if (mbo) {
+			int len;
 
-			wpas_hs20_add_indication(hs20, pps_mo_id);
-			len = sizeof(wpa_ie) - wpa_ie_len;
-			if (wpabuf_len(hs20) <= len) {
-				os_memcpy(wpa_ie + wpa_ie_len,
-					  wpabuf_head(hs20), wpabuf_len(hs20));
-				wpa_ie_len += wpabuf_len(hs20);
-			}
-			wpabuf_free(hs20);
+			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;
 		}
 	}
-#endif /* CONFIG_HS20 */
+#endif /* CONFIG_MBO */
 
 	/*
 	 * Workaround: Add Extended Capabilities element only if the AP
@@ -2269,6 +2294,27 @@
 		}
 	}
 
+#ifdef CONFIG_HS20
+	if (is_hs20_network(wpa_s, ssid, bss)) {
+		struct wpabuf *hs20;
+
+		hs20 = wpabuf_alloc(20);
+		if (hs20) {
+			int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+			size_t len;
+
+			wpas_hs20_add_indication(hs20, pps_mo_id);
+			len = sizeof(wpa_ie) - wpa_ie_len;
+			if (wpabuf_len(hs20) <= len) {
+				os_memcpy(wpa_ie + wpa_ie_len,
+					  wpabuf_head(hs20), wpabuf_len(hs20));
+				wpa_ie_len += wpabuf_len(hs20);
+			}
+			wpabuf_free(hs20);
+		}
+	}
+#endif /* CONFIG_HS20 */
+
 	if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
 		struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
 		size_t len;
@@ -2293,6 +2339,17 @@
 	}
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_MBO
+	if (mbo) {
+		int len;
+
+		len = wpas_mbo_ie(wpa_s, wpa_ie + wpa_ie_len,
+				  sizeof(wpa_ie) - wpa_ie_len);
+		if (len >= 0)
+			wpa_ie_len += len;
+	}
+#endif /* CONFIG_MBO */
+
 	wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
 	use_crypt = 1;
 	cipher_pairwise = wpa_s->pairwise_cipher;
@@ -2345,9 +2402,11 @@
 		}
 		params.bssid_hint = bss->bssid;
 		params.freq_hint = bss->freq;
+		params.pbss = bss_is_pbss(bss);
 	} else {
 		params.ssid = ssid->ssid;
 		params.ssid_len = ssid->ssid_len;
+		params.pbss = ssid->pbss;
 	}
 
 	if (ssid->mode == WPAS_MODE_IBSS && ssid->bssid_set &&
@@ -3453,6 +3512,8 @@
 	wpa_s->parent = parent ? parent : wpa_s;
 	wpa_s->sched_scanning = 0;
 
+	dl_list_init(&wpa_s->bss_tmp_disallowed);
+
 	return wpa_s;
 }
 
@@ -4759,6 +4820,9 @@
 #ifdef CONFIG_HS20
 	hs20_init(wpa_s);
 #endif /* CONFIG_HS20 */
+#ifdef CONFIG_MBO
+	wpas_mbo_update_non_pref_chan(wpa_s, wpa_s->conf->non_pref_chan);
+#endif /* CONFIG_MBO */
 
 	return 0;
 }
@@ -6247,3 +6311,92 @@
 
 	return -1;
 }
+
+
+struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+				   u16 num_modes, enum hostapd_hw_mode mode)
+{
+	u16 i;
+
+	for (i = 0; i < num_modes; i++) {
+		if (modes[i].mode == mode)
+			return &modes[i];
+	}
+
+	return NULL;
+}
+
+
+static struct
+wpa_bss_tmp_disallowed * wpas_get_disallowed_bss(struct wpa_supplicant *wpa_s,
+						 const u8 *bssid)
+{
+	struct wpa_bss_tmp_disallowed *bss;
+
+	dl_list_for_each(bss, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (os_memcmp(bssid, bss->bssid, ETH_ALEN) == 0)
+			return bss;
+	}
+
+	return NULL;
+}
+
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  unsigned int sec)
+{
+	struct wpa_bss_tmp_disallowed *bss;
+	struct os_reltime until;
+
+	os_get_reltime(&until);
+	until.sec += sec;
+
+	bss = wpas_get_disallowed_bss(wpa_s, bssid);
+	if (bss) {
+		bss->disallowed_until = until;
+		return;
+	}
+
+	bss = os_malloc(sizeof(*bss));
+	if (!bss) {
+		wpa_printf(MSG_DEBUG,
+			   "Failed to allocate memory for temp disallow BSS");
+		return;
+	}
+
+	bss->disallowed_until = until;
+	os_memcpy(bss->bssid, bssid, ETH_ALEN);
+	dl_list_add(&wpa_s->bss_tmp_disallowed, &bss->list);
+}
+
+
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+	struct wpa_bss_tmp_disallowed *bss = NULL, *tmp, *prev;
+	struct os_reltime now, age;
+
+	os_get_reltime(&now);
+
+	dl_list_for_each_safe(tmp, prev, &wpa_s->bss_tmp_disallowed,
+			 struct wpa_bss_tmp_disallowed, list) {
+		if (!os_reltime_before(&now, &tmp->disallowed_until)) {
+			/* This BSS is not disallowed anymore */
+			dl_list_del(&tmp->list);
+			os_free(tmp);
+			continue;
+		}
+		if (os_memcmp(bssid, tmp->bssid, ETH_ALEN) == 0) {
+			bss = tmp;
+			break;
+		}
+	}
+	if (!bss)
+		return 0;
+
+	os_reltime_sub(&bss->disallowed_until, &now, &age);
+	wpa_printf(MSG_DEBUG,
+		   "BSS " MACSTR " disabled for %ld.%0ld seconds",
+		   MAC2STR(bss->bssid), age.sec, age.usec);
+	return 1;
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index f3e913a..e55b380 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -641,6 +641,20 @@
 # Example:
 # sched_scan_plans=10:100 20:200 30
 
+# 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.
+# Format:
+# non_pref_chan=<oper_class>:<chan>:<preference>:<reason>[:reason_detail]
+# Example:
+# non_pref_chan="81:5:10:2:0 81:1:0:2:0 81:9:0:2"
+
+# MBO Cellular Data Capabilities
+# 1 = Cellular data connection available
+# 2 = Cellular data connection not available
+# 3 = Not cellular capable (default)
+#mbo_cell_capa=3
+
 # network block
 #
 # Each network (usually AP's sharing the same SSID) is configured as a separate
@@ -702,6 +716,13 @@
 # an IBSS network with the configured SSID is already present, the frequency of
 # the network will be used instead of this configured value.
 #
+# pbss: Whether to use PBSS. Relevant to IEEE 802.11ad networks only.
+# 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.
+# For more details, see IEEE Std 802.11ad-2012.
+#
 # scan_freq: List of frequencies to scan
 # Space-separated list of frequencies in MHz to scan when searching for this
 # BSS. If the subset of channels used by the network is known, this option can
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7b74f38..0e3e42a 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -434,6 +434,12 @@
 	size_t image_len;
 };
 
+struct wpa_bss_tmp_disallowed {
+	struct dl_list list;
+	u8 bssid[ETH_ALEN];
+	struct os_reltime disallowed_until;
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -1016,6 +1022,25 @@
 	const struct wpabuf *fst_ies;
 	struct wpabuf *received_mb_ies;
 #endif /* CONFIG_FST */
+
+#ifdef CONFIG_MBO
+	/* Multiband operation non-preferred channel */
+	struct wpa_mbo_non_pref_channel {
+		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;
+	u8 mbo_wnm_token;
+#endif /* CONFIG_MBO */
+
+	/*
+	 * This should be under CONFIG_MBO, but it is left out to allow using
+	 * the bss_temp_disallowed list for other purposes as well.
+	 */
+	struct dl_list bss_tmp_disallowed;
 };
 
 
@@ -1128,6 +1153,22 @@
 					      const u8 *frame, size_t len,
 					      int rssi);
 
+
+/* MBO functions */
+int wpas_mbo_ie(struct wpa_supplicant *wpa_s, u8 *buf, size_t len);
+const u8 * wpas_mbo_get_bss_attr(struct wpa_bss *bss, enum mbo_attr_id attr);
+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);
+
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1206,4 +1247,16 @@
 
 int wpas_sched_scan_plans_set(struct wpa_supplicant *wpa_s, const char *cmd);
 
+struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
+				   u16 num_modes, enum hostapd_hw_mode mode);
+
+void wpa_bss_tmp_disallow(struct wpa_supplicant *wpa_s, const u8 *bssid,
+			  unsigned int sec);
+int wpa_is_bss_tmp_disallowed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+
+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);
+
 #endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index 5c674b2..07452ad 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -1149,6 +1149,10 @@
 			ssid->ssid_len = wpa_s->go_params->ssid_len;
 			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
 				  ssid->ssid_len);
+			if (wpa_s->go_params->freq > 56160) {
+				/* P2P in 60 GHz uses PBSS */
+				ssid->pbss = 1;
+			}
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 					  "SSID", ssid->ssid, ssid->ssid_len);
 		}
@@ -1216,6 +1220,10 @@
 			ssid->ssid_len = wpa_s->go_params->ssid_len;
 			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
 				  ssid->ssid_len);
+			if (wpa_s->go_params->freq > 56160) {
+				/* P2P in 60 GHz uses PBSS */
+				ssid->pbss = 1;
+			}
 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
 					  "SSID", ssid->ssid, ssid->ssid_len);
 		}
@@ -1228,7 +1236,10 @@
 		os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
 			    dev_pw_id, hash);
 	} else {
-		rpin = wps_generate_pin();
+		if (wps_generate_pin(&rpin) < 0) {
+			wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN");
+			return -1;
+		}
 		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
 			    rpin, dev_pw_id, hash);
 	}