Cumulative patch from commit 538922a628d4f5403b9a96b171a59235bcb3d921

538922a dbus: Add boolean AllowRoam option to Scan() method options dictionary
c6f5dec Don't start second scan when changing scan interval
cd3b070 nl80211: Fix DFS radar event parsing
2b72df6 nl80211: Free BSS structure even if netdev does not exists
41cc50d nl80211: Update send_action_cookie on AP-offchannel-TX path
313424d GAS: Add support for multiple pending queries for the same destination
cbc5484 GAS: Do not start new scan operation during an ongoing GAS query
c377514 GAS: Delay GAS query Tx while scanning/connecting
24c694b GAS: Delay GAS query Tx while another query is in progress
7255983 WPS: Clear after_wps from number of new locations
73b54d6 P2P: Fix Operating Channel in Invitation Request for operating group
dc46fd6 P2P: Cancel offchannel TX wait on Invitation Response RX
0c92963 D-Bus: Clean up debug print for P2P invitation result
8d82c21 P2P: Fix PD retry channel on join-a-group case
d285888 P2P: Add GO BSS entry details to debug log on join-a-group
512629a P2P: Accept Invitation Response non-success without Channel List
e241b1b eap_proxy: Fix IMSI fetch for home vs. visited network determination
db13605 EAP-AKA/AKA' peer: Allow external USIM processing to be used
569ccf7 EAP-SIM peer: Allow external SIM processing to be used
84dc137 hlr_auc_gw: Add GSM-AUTH-REQ command
a5d44ac EAP peer: Add framework for external SIM/USIM processing
7e8bc7d eapol_test: Initialize BSS lists
bceb843 Send CTRL-RSP command response before processing EAPOL update
b607796 eapol_test: Fix external EAP request mechanism
94de082 eapol_test: Initialize wpa_s->global to fix ctrl_iface
f07bba3 Android: Add dfs.c into build
0cf0af2 WNM: Set Disassoc Imminent flag in ESS Disassoc Imminent frame
f47c145 Interworking: Add required_roaming_consortium parameter for credentials
a83e574 GAS: Update timeout from TX status handler
e88060e HTTP server: Allow TCP socket to be reused
9bc3386 Add test option for specifying hardcoded BSS Load element
9c7e43a Define BSS Load element id
56f5af4 Interworking: Add support for QoS Mapping functionality for the STA
850e1c2 atheros: Add support for QoS Mapping configuration
c551700 Interworking: Add support for QoS Mapping functionality for the AP
ac1bc54 Interworking: Add domain_suffix_match for credentials
463c8ff Interworking: Add support for multiple home FQDNs
01f809c Add AAA server domain name suffix matching constraint
be7963b OpenSSL: Fix code indentation in OCSP processing
899cc14 hostapd: Add support for DFS with 160 MHz channel width
6de0e0c Mark DFS functions static and rename them
58b73e3 hostapd: DFS with 40/80 MHz channel width support
846de15 DFS: Add more parameters to radar events
04e8003 nl80211: Use struct hostapd_freq_params with start_dfs_cac
72c753d hostapd: Split hostapd_set_freq to helper function
e76da50 hostapd: Add AP DFS support

Change-Id: Ie9ed4662ba6d81e6d8b14bccb29ffa192becf0f2
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/wpa_supplicant/gas_query.c b/wpa_supplicant/gas_query.c
index 06a97d3..f9885e3 100644
--- a/wpa_supplicant/gas_query.c
+++ b/wpa_supplicant/gas_query.c
@@ -1,7 +1,7 @@
 /*
  * Generic advertisement service (GAS) query
  * Copyright (c) 2009, Atheros Communications
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -21,6 +21,8 @@
 
 /** GAS query timeout in seconds */
 #define GAS_QUERY_TIMEOUT_PERIOD 2
+/** Retry period for GAS query requests in milliseconds */
+#define GAS_SERVICE_RETRY_PERIOD_MS 500
 
 
 /**
@@ -35,6 +37,7 @@
 	unsigned int offchannel_tx_started:1;
 	int freq;
 	u16 status_code;
+	struct wpabuf *req;
 	struct wpabuf *adv_proto;
 	struct wpabuf *resp;
 	void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
@@ -50,11 +53,13 @@
 struct gas_query {
 	struct wpa_supplicant *wpa_s;
 	struct dl_list pending; /* struct gas_query_pending */
+	struct gas_query_pending *current;
 };
 
 
 static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
 static void gas_query_timeout(void *eloop_data, void *user_ctx);
+static void gas_service_timeout(void *eloop_data, void *user_ctx);
 
 
 /**
@@ -81,13 +86,17 @@
 			   struct gas_query_pending *query,
 			   enum gas_query_result result)
 {
+	if (gas->current == query)
+		gas->current = NULL;
 	if (query->offchannel_tx_started)
 		offchannel_send_action_done(gas->wpa_s);
 	eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
 	eloop_cancel_timeout(gas_query_timeout, gas, query);
+	eloop_cancel_timeout(gas_service_timeout, gas, query);
 	dl_list_del(&query->list);
 	query->cb(query->ctx, query->addr, query->dialog_token, result,
 		  query->adv_proto, query->resp, query->status_code);
+	wpabuf_free(query->req);
 	wpabuf_free(query->adv_proto);
 	wpabuf_free(query->resp);
 	os_free(query);
@@ -138,6 +147,44 @@
 }
 
 
+static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
+				unsigned int freq, const u8 *dst,
+				const u8 *src, const u8 *bssid,
+				const u8 *data, size_t data_len,
+				enum offchannel_send_action_result result)
+{
+	struct gas_query_pending *query;
+	struct gas_query *gas = wpa_s->gas;
+
+	if (gas->current == NULL) {
+		wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
+			   MACSTR " result=%d - no query in progress",
+			   freq, MAC2STR(dst), result);
+		return;
+	}
+
+	query = gas->current;
+
+	wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+		   " result=%d query=%p dialog_token=%u",
+		   freq, MAC2STR(dst), result, query, query->dialog_token);
+	if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+		return;
+	}
+
+	if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
+		eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+				       gas_query_timeout, gas, query);
+	}
+	if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
+		eloop_cancel_timeout(gas_query_timeout, gas, query);
+		eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
+	}
+}
+
+
 static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
 			struct wpabuf *req)
 {
@@ -148,7 +195,7 @@
 	res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
 				     gas->wpa_s->own_addr, query->addr,
 				     wpabuf_head(req), wpabuf_len(req), 1000,
-				     NULL, 0);
+				     gas_query_tx_status, 0);
 	if (res == 0)
 		query->offchannel_tx_started = 1;
 	return res;
@@ -426,12 +473,50 @@
 	struct gas_query *gas = eloop_data;
 	struct gas_query_pending *query = user_ctx;
 
-	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
-		   MAC2STR(query->addr));
+	wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+		   " dialog token %u",
+		   MAC2STR(query->addr), query->dialog_token);
 	gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
 }
 
 
+static void gas_service_timeout(void *eloop_data, void *user_ctx)
+{
+	struct gas_query *gas = eloop_data;
+	struct wpa_supplicant *wpa_s = gas->wpa_s;
+	struct gas_query_pending *query = user_ctx;
+	int conn;
+
+	conn = wpas_wpa_is_in_progress(wpa_s, 1);
+	if (conn || wpa_s->scanning || gas->current) {
+		wpa_printf(MSG_DEBUG, "GAS: Delaying GAS query Tx while another operation is in progress:%s%s%s",
+			   conn ? " connection" : "",
+			   wpa_s->scanning ? " scanning" : "",
+			   gas->current ? " gas_query" : "");
+		eloop_register_timeout(
+			GAS_SERVICE_RETRY_PERIOD_MS / 1000,
+			(GAS_SERVICE_RETRY_PERIOD_MS % 1000) * 1000,
+			gas_service_timeout, gas, query);
+		return;
+	}
+
+	if (gas_query_tx(gas, query, query->req) < 0) {
+		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+			   MACSTR, MAC2STR(query->addr));
+		dl_list_del(&query->list);
+		wpabuf_free(query->req);
+		os_free(query);
+		return;
+	}
+	gas->current = query;
+
+	wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+		   query->dialog_token);
+	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+			       gas_query_timeout, gas, query);
+}
+
+
 static int gas_query_dialog_token_available(struct gas_query *gas,
 					    const u8 *dst, u8 dialog_token)
 {
@@ -451,7 +536,8 @@
  * @gas: GAS query data from gas_query_init()
  * @dst: Destination MAC address for the query
  * @freq: Frequency (in MHz) for the channel on which to send the query
- * @req: GAS query payload
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ *	return)
  * @cb: Callback function for reporting GAS query result and response
  * @ctx: Context pointer to use with the @cb call
  * Returns: dialog token (>= 0) on success or -1 on failure
@@ -490,22 +576,15 @@
 	query->freq = freq;
 	query->cb = cb;
 	query->ctx = ctx;
+	query->req = req;
 	dl_list_add(&gas->pending, &query->list);
 
 	*(wpabuf_mhead_u8(req) + 2) = dialog_token;
 
 	wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
 		   " dialog_token %u", MAC2STR(dst), dialog_token);
-	if (gas_query_tx(gas, query, req) < 0) {
-		wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
-			   MACSTR, MAC2STR(query->addr));
-		dl_list_del(&query->list);
-		os_free(query);
-		return -1;
-	}
 
-	eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
-			       gas, query);
+	eloop_register_timeout(0, 0, gas_service_timeout, gas, query);
 
 	return dialog_token;
 }
@@ -526,3 +605,9 @@
 		gas_query_done(gas, query, GAS_QUERY_CANCELLED);
 
 }
+
+
+int gas_query_in_progress(struct gas_query *gas)
+{
+	return gas->current != NULL;
+}