Cumulative patch from commit 107a89448c8a127e9131bc9288b9093720b87781

107a894 P2P: Clean up wpas_p2p_setup_freqs() debug message
7d669cf P2P: Clean up wpas_p2p_init_go_params()
384bdd0 P2P: Prefer VHT channels for initial pick
a30d50b P2P: Prefer HT40 channels for initial pick
f240710 P2P: Prefer 5 GHz channels for initial pick
b2d4aaa P2P: Select VHT channel at random instead of using the first entry
ee8f6ea P2P: Select HT40 channel at random instead of using the first entry
5576663 P2P: Move random channel selection into a helper function
b17d89b P2P: Fix channel selection for MCC case
f86d282 Handle legacy scan interruption of sched_scan/PNO
334bf36 Add chan_switch to ctrl interface of wpa_supplicant and hostapd
bf281c1 Add AP channel switch mechanism
e44a384 Move AP parameter filling into a helper function
1c4ffa8 nl80211: Add channel switch implementation
dcca221 wpa_supplicant: Update channel switch driver interface
e1925bd eloop: Remove eloop_none.c
3505743 Sync with wireless-testing.git include/uapi/linux/nl80211.h
f6c2b8c Android: Add state message sent after 'status' command
6b49907 Android: Add SSID in supplicant change event
22cf7d7 SCARD: Clean up SIM/USIM selection
cea97a0 dbus_new: Add DBus TDLS methods
cc4952a Request new scan only for the original interface
fa58da2 Remove old mechanism of skipping scan request rescheduling
e2f5a98 Deplete scan request if new time is less than remaining
dcd25d4 P2P: Use negotiated channel from invitation for persistent GO
800d587 TDLS: Provide external control to specify the peers for setup
795baf7 hostapd: Filter channel list updated events after country code change
146fa9b nl80211: Do not force interface down on deinit
49b4b20 nl80211: Do not for station mode on hostapd deinit
04eff7d nl80211: Remove build time condition on HOSTAPD define
0d547d5 nl80211: Share more code between hostapd and wpa_supplicant
7b7de4c nl80211: Remove read-only last_freq/last_freq_ht

Change-Id: Idba1eb8d63480fbc30eee11d0d8e189ccc49ddd3
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b09fbac..acd77f2 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1236,6 +1236,9 @@
 static void p2p_prepare_channel_best(struct p2p_data *p2p)
 {
 	u8 op_class, op_channel;
+	const int op_classes_5ghz[] = { 115, 124, 0 };
+	const int op_classes_ht40[] = { 116, 117, 126, 127, 0 };
+	const int op_classes_vht[] = { 128, 0 };
 
 	p2p_dbg(p2p, "Prepare channel best");
 
@@ -1267,6 +1270,21 @@
 		p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference");
 		p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class;
 		p2p->op_channel = p2p->cfg->pref_chan[0].chan;
+	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference",
+			p2p->op_reg_class, p2p->op_channel);
+	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference",
+			p2p->op_reg_class, p2p->op_channel);
+	} else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz,
+				      &p2p->op_reg_class, &p2p->op_channel) ==
+		   0) {
+		p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference",
+			p2p->op_reg_class, p2p->op_channel);
 	} else {
 		p2p_dbg(p2p, "Select pre-configured channel as operating channel preference");
 		p2p->op_reg_class = p2p->cfg->op_reg_class;
@@ -3381,7 +3399,8 @@
 			if (p2p->cfg->invitation_result)
 				p2p->cfg->invitation_result(
 					p2p->cfg->cb_ctx, -1, NULL, NULL,
-					p2p->invite_peer->info.p2p_device_addr);
+					p2p->invite_peer->info.p2p_device_addr,
+					0);
 		}
 		p2p_set_state(p2p, P2P_IDLE);
 	}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 6e4a850..22d0c58 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -758,6 +758,7 @@
 	 * @bssid: P2P Group BSSID or %NULL if not received
 	 * @channels: Available operating channels for the group
 	 * @addr: Peer address
+	 * @freq: Frequency (in MHz) indicated during invitation or 0
 	 *
 	 * This callback is used to indicate result of an Invitation procedure
 	 * started with a call to p2p_invite(). The indicated status code is
@@ -767,7 +768,7 @@
 	 */
 	void (*invitation_result)(void *ctx, int status, const u8 *bssid,
 				  const struct p2p_channels *channels,
-				  const u8 *addr);
+				  const u8 *addr, int freq);
 
 	/**
 	 * go_connected - Check whether we are connected to a GO
diff --git a/src/p2p/p2p_go_neg.c b/src/p2p/p2p_go_neg.c
index 7eaa8dc..325b832 100644
--- a/src/p2p/p2p_go_neg.c
+++ b/src/p2p/p2p_go_neg.c
@@ -348,6 +348,9 @@
 	int freq;
 	u8 op_reg_class, op_channel;
 	unsigned int i;
+	const int op_classes_5ghz[] = { 115, 124, 0 };
+	const int op_classes_ht40[] = { 116, 117, 126, 127, 0 };
+	const int op_classes_vht[] = { 128, 0 };
 
 	if (p2p->own_freq_preference > 0 &&
 	    p2p_freq_to_channel(p2p->own_freq_preference,
@@ -413,49 +416,27 @@
 	}
 
 	/* Try a channel where we might be able to use VHT */
-	for (i = 0; i < intersection->reg_classes; i++) {
-		struct p2p_reg_class *c = &intersection->reg_class[i];
-		if (c->reg_class == 128) {
-			p2p_dbg(p2p, "Pick possible VHT channel (reg_class %u channel %u) from intersection",
-				c->reg_class, c->channel[0]);
-			p2p->op_reg_class = c->reg_class;
-			p2p->op_channel = c->channel[0];
-			return;
-		}
+	if (p2p_channel_select(intersection, op_classes_vht,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
 	}
 
 	/* Try a channel where we might be able to use HT40 */
-	for (i = 0; i < intersection->reg_classes; i++) {
-		struct p2p_reg_class *c = &intersection->reg_class[i];
-		if (c->reg_class == 116 || c->reg_class == 117 ||
-		    c->reg_class == 126 || c->reg_class == 127) {
-			p2p_dbg(p2p, "Pick possible HT40 channel (reg_class %u channel %u) from intersection",
-				c->reg_class, c->channel[0]);
-			p2p->op_reg_class = c->reg_class;
-			p2p->op_channel = c->channel[0];
-			return;
-		}
+	if (p2p_channel_select(intersection, op_classes_ht40,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
 	}
 
 	/* Prefer a 5 GHz channel */
-	for (i = 0; i < intersection->reg_classes; i++) {
-		struct p2p_reg_class *c = &intersection->reg_class[i];
-		if ((c->reg_class == 115 || c->reg_class == 124) &&
-		    c->channels) {
-			unsigned int r;
-
-			/*
-			 * Pick one of the available channels in the operating
-			 * class at random.
-			 */
-			os_get_random((u8 *) &r, sizeof(r));
-			r %= c->channels;
-			p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection",
-				c->reg_class, c->channel[r]);
-			p2p->op_reg_class = c->reg_class;
-			p2p->op_channel = c->channel[r];
-			return;
-		}
+	if (p2p_channel_select(intersection, op_classes_5ghz,
+			       &p2p->op_reg_class, &p2p->op_channel) == 0) {
+		p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection",
+			p2p->op_reg_class, p2p->op_channel);
+		return;
 	}
 
 	/*
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index e4ec6de..e980be6 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -589,6 +589,8 @@
 			  u8 channel);
 void p2p_channels_dump(struct p2p_data *p2p, const char *title,
 		       const struct p2p_channels *chan);
+int p2p_channel_select(struct p2p_channels *chans, const int *classes,
+		       u8 *op_class, u8 *op_channel);
 
 /* p2p_parse.c */
 int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index 203445b..2734386 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -431,9 +431,15 @@
 		channels = &intersection;
 	}
 
-	if (p2p->cfg->invitation_result)
+	if (p2p->cfg->invitation_result) {
+		int freq = p2p_channel_to_freq(p2p->op_reg_class,
+					       p2p->op_channel);
+		if (freq < 0)
+			freq = 0;
 		p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
-					    msg.group_bssid, channels, sa);
+					    msg.group_bssid, channels, sa,
+					    freq);
+	}
 
 	p2p_parse_free(&msg);
 
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index a3dcdeb..a579509 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -439,3 +439,36 @@
 
 	p2p_dbg(p2p, "%s:%s", title, buf);
 }
+
+
+int p2p_channel_select(struct p2p_channels *chans, const int *classes,
+		       u8 *op_class, u8 *op_channel)
+{
+	unsigned int i, j, r;
+
+	for (i = 0; i < chans->reg_classes; i++) {
+		struct p2p_reg_class *c = &chans->reg_class[i];
+
+		if (c->channels == 0)
+			continue;
+
+		for (j = 0; classes[j]; j++) {
+			if (c->reg_class == classes[j])
+				break;
+		}
+		if (!classes[j])
+			continue;
+
+		/*
+		 * Pick one of the available channels in the operating class at
+		 * random.
+		 */
+		os_get_random((u8 *) &r, sizeof(r));
+		r %= c->channels;
+		*op_class = c->reg_class;
+		*op_channel = c->channel[r];
+		return 0;
+	}
+
+	return -1;
+}