Accumulative patch from commit c6ccf12d3f6f35e295f9522dded535da8af6ab98

c6ccf12 P2P: Use preferred channel list during GO creation
6d956c4 P2P: Re-select channel in invitation case with peer info
010b5f9 P2P: Fix p2p_pref_chan setting from configuration file
d7692b8 dbus: Terminate cleanly on messagebus shutdown
e2396a6 WNM: Enable CONFIG_WNM in Android.mk
65bcd0a WNM: Add sending of BSS Transition Management Query
e27d20b WNM: Add neighbor report processing for BSS Transition Management
f3e907a WPS: Clear connection failure counts on WPS success
170f566 NFC: Connect using learnt credential after NFC Tag read
0af2db7 edit: Fix libreadline history clearing with WPA_TRACE

Change-Id: I0599ea2179869ae8c250ff6b887bb16324fc02f4
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index f782c86..0848590 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -549,6 +549,14 @@
 					 * Entries (optional) */
 					u8 variable[0];
 				} STRUCT_PACKED bss_tm_resp;
+				struct {
+					u8 action; /* 6 */
+					u8 dialog_token;
+					u8 query_reason;
+					/* BSS Transition Candidate List
+					 * Entries (optional) */
+					u8 variable[0];
+				} STRUCT_PACKED bss_tm_query;
 			} u;
 		} STRUCT_PACKED action;
 	} u;
@@ -1049,6 +1057,15 @@
 #define WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED BIT(3)
 #define WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT BIT(4)
 
+#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_MEASUREMENT_PILOT          66
+#define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES   70
+#define WNM_NEIGHBOR_MULTIPLE_BSSID             71
+
 /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
 #define WLAN_20_40_BSS_COEX_INFO_REQ            BIT(0)
 #define WLAN_20_40_BSS_COEX_40MHZ_INTOL         BIT(1)
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 28a0a1d..b197893 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -1648,6 +1648,15 @@
  */
 int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
 
+/**
+ * p2p_get_pref_freq - Get channel from preferred channel list
+ * @p2p: P2P module context from p2p_init()
+ * @channels: List of channels
+ * Returns: Preferred channel
+ */
+unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
+			       const struct p2p_channels *channels);
+
 void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
 
 /**
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 05822c6..5016f1b 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -322,6 +322,14 @@
 				status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
 				goto fail;
 			}
+		} else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+			   !p2p->cfg->cfg_op_channel) {
+			wpa_msg(p2p->cfg->msg_ctx, MSG_DEBUG,
+				"P2P: Try to reselect channel selection with "
+				"peer information received; "
+				"previously selected op_class %u channel %u",
+				p2p->op_reg_class, p2p->op_channel);
+			p2p_reselect_channel(p2p, &intersection);
 		}
 
 		op_freq = p2p_channel_to_freq(p2p->op_reg_class,
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index 69bd14b..0da2682 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -243,3 +243,31 @@
 	return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
 				     op_channel);
 }
+
+
+unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
+			       const struct p2p_channels *channels)
+{
+	unsigned int i;
+	int freq = 0;
+
+	if (channels == NULL) {
+		if (p2p->cfg->num_pref_chan) {
+			freq = p2p_channel_to_freq(
+				p2p->cfg->pref_chan[0].op_class,
+				p2p->cfg->pref_chan[0].chan);
+			if (freq < 0)
+				freq = 0;
+		}
+		return freq;
+	}
+
+	for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
+		freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class,
+					   p2p->cfg->pref_chan[i].chan);
+		if (p2p_channels_includes_freq(channels, freq))
+			return freq;
+	}
+
+	return 0;
+}
diff --git a/src/utils/edit_readline.c b/src/utils/edit_readline.c
index add26fa..c2a5bca 100644
--- a/src/utils/edit_readline.c
+++ b/src/utils/edit_readline.c
@@ -167,9 +167,9 @@
 			if (filter_cb && filter_cb(edit_cb_ctx, p)) {
 				h = remove_history(where_history());
 				if (h) {
-					os_free(h->line);
+					free(h->line);
 					free(h->data);
-					os_free(h);
+					free(h);
 				} else
 					next_history();
 			} else
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
index 8c06334..e8bfe0c 100644
--- a/wpa_supplicant/ctrl_iface.c
+++ b/wpa_supplicant/ctrl_iface.c
@@ -4994,6 +4994,19 @@
 	return ret;
 }
 
+
+static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd)
+{
+	int query_reason;
+
+	query_reason = atoi(cmd);
+
+	wpa_printf(MSG_DEBUG, "CTRL_IFACE: WNM_BSS_QUERY query_reason=%d",
+		   query_reason);
+
+	return wnm_send_bss_transition_mgmt_query(wpa_s, query_reason);
+}
+
 #endif /* CONFIG_WNM */
 
 
@@ -5631,6 +5644,9 @@
 	} else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
 		if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
 			reply_len = -1;
+	} else if (os_strncmp(buf, "WNM_BSS_QUERY ", 10) == 0) {
+		if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 10))
+				reply_len = -1;
 #endif /* CONFIG_WNM */
 	} else if (os_strcmp(buf, "FLUSH") == 0) {
 		wpa_supplicant_ctrl_iface_flush(wpa_s);
diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c
index 5d0e31e..6caf740 100644
--- a/wpa_supplicant/dbus/dbus_common.c
+++ b/wpa_supplicant/dbus/dbus_common.c
@@ -17,6 +17,7 @@
 #include "dbus_common_i.h"
 #include "dbus_new.h"
 #include "dbus_old.h"
+#include "../wpa_supplicant_i.h"
 
 
 #ifndef SIGPOLL
@@ -257,6 +258,22 @@
 }
 
 
+static DBusHandlerResult disconnect_filter(DBusConnection *conn,
+                                           DBusMessage *message, void *data)
+{
+	struct wpas_dbus_priv *priv = data;
+
+	if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
+	                           "Disconnected")) {
+		wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating");
+		dbus_connection_set_exit_on_disconnect(conn, FALSE);
+		wpa_supplicant_terminate_proc(priv->global);
+		return DBUS_HANDLER_RESULT_HANDLED;
+	} else
+		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
 static int wpas_dbus_init_common(struct wpas_dbus_priv *priv)
 {
 	DBusError error;
@@ -265,7 +282,10 @@
 	/* Get a reference to the system bus */
 	dbus_error_init(&error);
 	priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
-	if (!priv->con) {
+	if (priv->con) {
+		dbus_connection_add_filter(priv->con, disconnect_filter, priv,
+		                           NULL);
+	} else {
 		wpa_printf(MSG_ERROR, "dbus: Could not acquire the system "
 			   "bus: %s - %s", error.name, error.message);
 		ret = -1;
@@ -304,6 +324,9 @@
 						    NULL, NULL, NULL);
 		dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
 						      NULL, NULL, NULL);
+		dbus_connection_remove_filter(priv->con, disconnect_filter,
+					      priv);
+
 		dbus_connection_unref(priv->con);
 	}
 
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index f1e15ff..b760e7f 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -3103,6 +3103,12 @@
 		wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
 			   "%d:%d", p2p.op_reg_class, p2p.op_channel);
 	}
+
+	if (wpa_s->conf->p2p_pref_chan && wpa_s->conf->num_p2p_pref_chan) {
+		p2p.pref_chan = wpa_s->conf->p2p_pref_chan;
+		p2p.num_pref_chan = wpa_s->conf->num_p2p_pref_chan;
+	}
+
 	if (wpa_s->conf->country[0] && wpa_s->conf->country[1]) {
 		os_memcpy(p2p.country, wpa_s->conf->country, 2);
 		p2p.country[2] = 0x04;
@@ -4084,6 +4090,7 @@
 {
 	u8 bssid[ETH_ALEN];
 	int res;
+	unsigned int pref_freq;
 
 	os_memset(params, 0, sizeof(*params));
 	params->role_go = 1;
@@ -4140,6 +4147,11 @@
 		params->freq = wpa_s->best_5_freq;
 		wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
 			   "channel %d MHz", params->freq);
+	} else if ((pref_freq = p2p_get_pref_freq(wpa_s->global->p2p,
+						  channels))) {
+		params->freq = pref_freq;
+		wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
+			   "channels", params->freq);
 	} else {
 		int chan;
 		for (chan = 0; chan < 11; chan++) {
diff --git a/wpa_supplicant/wnm_sta.c b/wpa_supplicant/wnm_sta.c
index 4d9e453..7de96c5 100644
--- a/wpa_supplicant/wnm_sta.c
+++ b/wpa_supplicant/wnm_sta.c
@@ -14,8 +14,12 @@
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "scan.h"
+#include "ctrl_iface.h"
+#include "bss.h"
+#include "wnm_sta.h"
 
 #define MAX_TFS_IE_LEN  1024
+#define WNM_MAX_NEIGHBOR_REPORT 10
 
 
 /* get the TFS IE from driver */
@@ -294,6 +298,199 @@
 }
 
 
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
+{
+	int i;
+
+	for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+		os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
+		os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
+	}
+
+	os_free(wpa_s->wnm_neighbor_report_elements);
+	wpa_s->wnm_neighbor_report_elements = NULL;
+}
+
+
+static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
+					   u8 id, u8 elen, const u8 *pos)
+{
+	switch (id) {
+	case WNM_NEIGHBOR_TSF:
+		if (elen < 2 + 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
+			break;
+		}
+		rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
+		if (rep->tsf_info == NULL)
+			break;
+		rep->tsf_info->present = 1;
+		os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
+		os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
+		break;
+	case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
+		if (elen < 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short condensed "
+				   "country string");
+			break;
+		}
+		rep->con_coun_str =
+			os_zalloc(sizeof(struct condensed_country_string));
+		if (rep->con_coun_str == NULL)
+			break;
+		rep->con_coun_str->present = 1;
+		os_memcpy(rep->con_coun_str->country_string, pos, 2);
+		break;
+	case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
+		if (elen < 1) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short BSS transition "
+				   "candidate");
+			break;
+		}
+		rep->bss_tran_can =
+			os_zalloc(sizeof(struct bss_transition_candidate));
+		if (rep->bss_tran_can == NULL)
+			break;
+		rep->bss_tran_can->present = 1;
+		rep->bss_tran_can->preference = pos[0];
+		break;
+	case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
+		if (elen < 12) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
+				   "duration");
+			break;
+		}
+		rep->bss_term_dur =
+			os_zalloc(sizeof(struct bss_termination_duration));
+		if (rep->bss_term_dur == NULL)
+			break;
+		rep->bss_term_dur->present = 1;
+		os_memcpy(rep->bss_term_dur->duration, pos, 12);
+		break;
+	case WNM_NEIGHBOR_BEARING:
+		if (elen < 8) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short neighbor "
+				   "bearing");
+			break;
+		}
+		rep->bearing = os_zalloc(sizeof(struct bearing));
+		if (rep->bearing == NULL)
+			break;
+		rep->bearing->present = 1;
+		os_memcpy(rep->bearing->bearing, pos, 8);
+		break;
+	case WNM_NEIGHBOR_MEASUREMENT_PILOT:
+		if (elen < 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
+				   "pilot");
+			break;
+		}
+		rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
+		if (rep->meas_pilot == NULL)
+			break;
+		rep->meas_pilot->present = 1;
+		rep->meas_pilot->measurement_pilot = pos[0];
+		rep->meas_pilot->num_vendor_specific = pos[1];
+		os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
+		break;
+	case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
+		if (elen < 4) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
+				   "capabilities");
+			break;
+		}
+		rep->rrm_cap =
+			os_zalloc(sizeof(struct rrm_enabled_capabilities));
+		if (rep->rrm_cap == NULL)
+			break;
+		rep->rrm_cap->present = 1;
+		os_memcpy(rep->rrm_cap->capabilities, pos, 4);
+		break;
+	case WNM_NEIGHBOR_MULTIPLE_BSSID:
+		if (elen < 2) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
+			break;
+		}
+		rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
+		if (rep->mul_bssid == NULL)
+			break;
+		rep->mul_bssid->present = 1;
+		rep->mul_bssid->max_bssid_indicator = pos[0];
+		rep->mul_bssid->num_vendor_specific = pos[1];
+		os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
+		break;
+	}
+}
+
+
+static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
+				      const u8 *pos, u8 len,
+				      struct neighbor_report *rep)
+{
+	u8 left = len;
+
+	if (left < 13) {
+		wpa_printf(MSG_DEBUG, "WNM: Too short neighbor report");
+		return;
+	}
+
+	os_memcpy(rep->bssid, pos, ETH_ALEN);
+	os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
+	rep->regulatory_class = *(pos + 10);
+	rep->channel_number = *(pos + 11);
+	rep->phy_type = *(pos + 12);
+
+	pos += 13;
+	left -= 13;
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		wnm_parse_neighbor_report_elem(rep, id, elen, pos);
+		left -= 2 + elen;
+		pos += elen;
+	}
+}
+
+
+static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
+					 struct wpa_scan_results *scan_res,
+					 struct neighbor_report *neigh_rep,
+					 u8 num_neigh_rep, u8 *bssid_to_connect)
+{
+
+	u8 i, j;
+
+	if (scan_res == NULL || num_neigh_rep == 0)
+		return 0;
+
+	for (i = 0; i < num_neigh_rep; i++) {
+		for (j = 0; j < scan_res->num; j++) {
+			/* Check for a better RSSI AP */
+			if (os_memcmp(scan_res->res[j]->bssid,
+				      neigh_rep[i].bssid, ETH_ALEN) == 0 &&
+			    scan_res->res[j]->level >
+			    wpa_s->current_bss->level) {
+				/* Got a BSSID with better RSSI value */
+				os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
+					  ETH_ALEN);
+				return 1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+
 static void wnm_send_bss_transition_mgmt_resp(struct wpa_supplicant *wpa_s,
 					      u8 dialog_token, u8 status,
 					      u8 delay, const u8 *target_bssid)
@@ -332,29 +529,90 @@
 }
 
 
+void wnm_scan_response(struct wpa_supplicant *wpa_s,
+		       struct wpa_scan_results *scan_res)
+{
+	u8 bssid[ETH_ALEN];
+
+	if (scan_res == NULL) {
+		wpa_printf(MSG_ERROR, "Scan result is NULL");
+		goto send_bss_resp_fail;
+	}
+
+	/* Compare the Neighbor Report and scan results */
+	if (compare_scan_neighbor_results(wpa_s, scan_res,
+					  wpa_s->wnm_neighbor_report_elements,
+					  wpa_s->wnm_num_neighbor_report,
+					  bssid) == 1) {
+		/* Associate to the network */
+		struct wpa_bss *bss;
+		struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+		bss = wpa_bss_get_bssid(wpa_s, bssid);
+		if (!bss) {
+			wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
+				   "BSS table");
+			goto send_bss_resp_fail;
+		}
+
+		/* Send the BSS Management Response - Accept */
+		if (wpa_s->wnm_reply) {
+			wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  0, /* Accept */
+						  0, NULL);
+		}
+
+		wpa_s->reassociate = 1;
+		wpa_supplicant_connect(wpa_s, bss, ssid);
+		wnm_deallocate_memory(wpa_s);
+		return;
+	}
+
+	/* Send reject response for all the failures */
+send_bss_resp_fail:
+	wnm_deallocate_memory(wpa_s);
+	if (wpa_s->wnm_reply) {
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
+						  1 /* Reject - unspecified */,
+						  0, NULL);
+	}
+	return;
+}
+
+
 static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 					     const u8 *pos, const u8 *end,
 					     int reply)
 {
-	u8 dialog_token;
-	u8 mode;
-	u16 disassoc_timer;
-
 	if (pos + 5 > end)
 		return;
 
-	dialog_token = pos[0];
-	mode = pos[1];
-	disassoc_timer = WPA_GET_LE16(pos + 2);
+	wpa_s->wnm_dialog_token = pos[0];
+	wpa_s->wnm_mode = pos[1];
+	wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
+	wpa_s->wnm_validity_interval = pos[4];
+	wpa_s->wnm_reply = reply;
 
 	wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
 		   "dialog_token=%u request_mode=0x%x "
 		   "disassoc_timer=%u validity_interval=%u",
-		   dialog_token, mode, disassoc_timer, pos[4]);
+		   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
+		   wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
+
 	pos += 5;
-	if (mode & 0x08)
+
+	if (wpa_s->wnm_mode & 0x08) {
+		if (pos + 12 > end) {
+			wpa_printf(MSG_DEBUG, "WNM: Too short BSS TM Request");
+			return;
+		}
+		os_memcpy(wpa_s->wnm_bss_termination_duration, pos, 12);
 		pos += 12; /* BSS Termination Duration */
-	if (mode & 0x10) {
+	}
+
+	if (wpa_s->wnm_mode & 0x10) {
 		char url[256];
 		if (pos + 1 > end || pos + 1 + pos[0] > end) {
 			wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
@@ -363,14 +621,15 @@
 		}
 		os_memcpy(url, pos + 1, pos[0]);
 		url[pos[0]] = '\0';
+		pos += 1 + pos[0];
 		wpa_msg(wpa_s, MSG_INFO, "WNM: ESS Disassociation Imminent - "
 			"session_info_url=%s", url);
 	}
 
-	if (mode & 0x04) {
+	if (wpa_s->wnm_mode & 0x04) {
 		wpa_msg(wpa_s, MSG_INFO, "WNM: Disassociation Imminent - "
-			"Disassociation Timer %u", disassoc_timer);
-		if (disassoc_timer && !wpa_s->scanning) {
+			"Disassociation Timer %u", wpa_s->wnm_dissoc_timer);
+		if (wpa_s->wnm_dissoc_timer && !wpa_s->scanning) {
 			/* TODO: mark current BSS less preferred for
 			 * selection */
 			wpa_printf(MSG_DEBUG, "Trying to find another BSS");
@@ -378,15 +637,85 @@
 		}
 	}
 
-	if (reply) {
-		/* TODO: add support for reporting Accept */
-		wnm_send_bss_transition_mgmt_resp(wpa_s, dialog_token,
+	if (wpa_s->wnm_mode & 0x01) {
+		wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
+		wpa_s->wnm_num_neighbor_report = 0;
+		os_free(wpa_s->wnm_neighbor_report_elements);
+		wpa_s->wnm_neighbor_report_elements = os_zalloc(
+			WNM_MAX_NEIGHBOR_REPORT *
+			sizeof(struct neighbor_report));
+		if (wpa_s->wnm_neighbor_report_elements == NULL)
+			return;
+
+		while (pos + 2 <= end &&
+		       wpa_s->wnm_num_neighbor_report < WNM_MAX_NEIGHBOR_REPORT)
+		{
+			u8 tag = *pos++;
+			u8 len = *pos++;
+
+			wpa_printf(MSG_DEBUG, "WNM: Neighbor report tag %u",
+				   tag);
+			if (pos + len > end) {
+				wpa_printf(MSG_DEBUG, "WNM: Truncated request");
+				return;
+			}
+			wnm_parse_neighbor_report(
+				wpa_s, pos, len,
+				&wpa_s->wnm_neighbor_report_elements[
+					wpa_s->wnm_num_neighbor_report]);
+
+			pos += len;
+			wpa_s->wnm_num_neighbor_report++;
+		}
+
+		wpa_s->scan_res_handler = wnm_scan_response;
+		wpa_supplicant_req_scan(wpa_s, 0, 0);
+	} else if (reply) {
+		wpa_msg(wpa_s, MSG_INFO, "WNM: BSS Transition Management "
+			"Request Mode is zero");
+		wnm_send_bss_transition_mgmt_resp(wpa_s,
+						  wpa_s->wnm_dialog_token,
 						  1 /* Reject - unspecified */,
 						  0, NULL);
 	}
 }
 
 
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+				       u8 query_reason)
+{
+	u8 buf[1000], *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);
+
+	mgmt = (struct ieee80211_mgmt *) buf;
+	os_memset(&buf, 0, sizeof(buf));
+	os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+	os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+	os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+	mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+					   WLAN_FC_STYPE_ACTION);
+	mgmt->u.action.category = WLAN_ACTION_WNM;
+	mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
+	mgmt->u.action.u.bss_tm_query.dialog_token = 0;
+	mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
+	pos = mgmt->u.action.u.bss_tm_query.variable;
+
+	len = pos - (u8 *) &mgmt->u.action.category;
+
+	ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+				  wpa_s->own_addr, wpa_s->bssid,
+				  &mgmt->u.action.category, len, 0);
+
+	return ret;
+}
+
+
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
 			      struct rx_action *action)
 {
@@ -418,6 +747,7 @@
 		ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
 		break;
 	default:
+		wpa_printf(MSG_ERROR, "WNM: Unknown request");
 		break;
 	}
 }
diff --git a/wpa_supplicant/wnm_sta.h b/wpa_supplicant/wnm_sta.h
index 3f9d88b..2933926 100644
--- a/wpa_supplicant/wnm_sta.h
+++ b/wpa_supplicant/wnm_sta.h
@@ -12,10 +12,79 @@
 struct rx_action;
 struct wpa_supplicant;
 
+struct tsf_info {
+	u8 present;
+	u8 tsf_offset[2];
+	u8 beacon_interval[2];
+};
+
+struct condensed_country_string {
+	u8 present;
+	u8 country_string[2];
+};
+
+struct bss_transition_candidate {
+	u8 present;
+	u8 preference;
+};
+
+struct bss_termination_duration {
+	u8 present;
+	u8 duration[12];
+};
+
+struct bearing {
+	u8 present;
+	u8 bearing[8];
+};
+
+struct measurement_pilot {
+	u8 present;
+	u8 measurement_pilot;
+	u8 num_vendor_specific;
+	u8 vendor_specific[255];
+};
+
+struct rrm_enabled_capabilities {
+	u8 present;
+	u8 capabilities[4];
+};
+
+struct multiple_bssid {
+	u8 present;
+	u8 max_bssid_indicator;
+	u8 num_vendor_specific;
+	u8 vendor_specific[255];
+};
+
+struct neighbor_report {
+	u8 bssid[ETH_ALEN];
+	u8 bssid_information[4];
+	u8 regulatory_class;
+	u8 channel_number;
+	u8 phy_type;
+	struct tsf_info *tsf_info;
+	struct condensed_country_string *con_coun_str;
+	struct bss_transition_candidate *bss_tran_can;
+	struct bss_termination_duration *bss_term_dur;
+	struct bearing *bearing;
+	struct measurement_pilot *meas_pilot;
+	struct rrm_enabled_capabilities *rrm_cap;
+	struct multiple_bssid *mul_bssid;
+};
+
+
 int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
 				 u8 action, u16 intval, struct wpabuf *tfs_req);
 
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
 			      struct rx_action *action);
 
+void wnm_scan_response(struct wpa_supplicant *wpa_s,
+		       struct wpa_scan_results *scan_res);
+
+int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
+				       u8 query_reason);
+void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
+
 #endif /* WNM_STA_H */
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index b66dcd0..61d4b61 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -2315,6 +2315,12 @@
 	return wpa_cli_cmd(ctrl, "WNM_SLEEP", 0, argc, argv);
 }
 
+
+static int wpa_cli_cmd_wnm_bss_query(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+	return wpa_cli_cmd(ctrl, "WNM_BSS_QUERY", 1, argc, argv);
+}
+
 #endif /* CONFIG_WNM */
 
 
@@ -2798,6 +2804,8 @@
 #ifdef CONFIG_WNM
 	{ "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" },
 #endif /* CONFIG_WNM */
 	{ "raw", wpa_cli_cmd_raw, NULL, cli_cmd_flag_sensitive,
 	  "<params..> = Sent unprocessed command" },
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index dbdaf19..1a7c0ff 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -49,6 +49,7 @@
 #include "scan.h"
 #include "offchannel.h"
 #include "hs20_supplicant.h"
+#include "wnm_sta.h"
 
 const char *wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
@@ -470,6 +471,9 @@
 	wpa_s->disallow_aps_ssid = NULL;
 
 	wnm_bss_keep_alive_deinit(wpa_s);
+#ifdef CONFIG_WNM
+	wnm_deallocate_memory(wpa_s);
+#endif /* CONFIG_WNM */
 
 	ext_password_deinit(wpa_s->ext_pw);
 	wpa_s->ext_pw = NULL;
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7559a75..a2a189b 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -690,6 +690,17 @@
 	u8 last_gas_dialog_token;
 
 	unsigned int no_keep_alive:1;
+
+#ifdef CONFIG_WNM
+	u8 wnm_dialog_token;
+	u8 wnm_reply;
+	u8 wnm_num_neighbor_report;
+	u8 wnm_mode;
+	u16 wnm_dissoc_timer;
+	u8 wnm_validity_interval;
+	u8 wnm_bss_termination_duration[12];
+	struct neighbor_report *wnm_neighbor_report_elements;
+#endif /* CONFIG_WNM */
 };
 
 
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
index b376fb0..d73e023 100644
--- a/wpa_supplicant/wps_supplicant.c
+++ b/wpa_supplicant/wps_supplicant.c
@@ -648,6 +648,9 @@
 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
 	wpa_s->wps_success = 1;
 	wpas_notify_wps_event_success(wpa_s);
+	if (wpa_s->current_ssid)
+		wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
+	wpa_s->extra_blacklist_count = 0;
 
 	/*
 	 * Enable the networks disabled during wpas_wps_reassoc after 10
@@ -964,21 +967,10 @@
 }
 
 
-static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
-			     struct wpa_ssid *selected, const u8 *bssid)
+static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
+				  struct wpa_ssid *selected)
 {
 	struct wpa_ssid *ssid;
-	struct wpa_bss *bss;
-
-	wpa_s->after_wps = 0;
-	wpa_s->known_wps_freq = 0;
-	if (bssid) {
-		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
-		if (bss && bss->freq > 0) {
-			wpa_s->known_wps_freq = 1;
-			wpa_s->wps_freq = bss->freq;
-		}
-	}
 
 	if (wpa_s->current_ssid)
 		wpa_supplicant_deauthenticate(
@@ -1006,6 +998,26 @@
 		}
 		ssid = ssid->next;
 	}
+}
+
+
+static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
+			     struct wpa_ssid *selected, const u8 *bssid)
+{
+	struct wpa_bss *bss;
+
+	wpa_s->after_wps = 0;
+	wpa_s->known_wps_freq = 0;
+	if (bssid) {
+		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
+		if (bss && bss->freq > 0) {
+			wpa_s->known_wps_freq = 1;
+			wpa_s->wps_freq = bss->freq;
+		}
+	}
+
+	wpas_wps_temp_disable(wpa_s, selected);
+
 	wpa_s->disconnected = 0;
 	wpa_s->reassociate = 1;
 	wpa_s->scan_runs = 0;
@@ -2087,6 +2099,15 @@
 {
 	wpa_s->wps_ap_channel = 0;
 
+	/*
+	 * Disable existing networks temporarily to allow the newly learned
+	 * credential to be preferred. Enable the temporarily disabled networks
+	 * after 10 seconds.
+	 */
+	wpas_wps_temp_disable(wpa_s, NULL);
+	eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
+			       NULL);
+
 	if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
 		return -1;