P2P: Handle frequency conflicts on station mode association requests

Signed-hostap: Jithu Jance <jithu@broadcom.com>

diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index d13ba02..0813a79 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -62,6 +62,8 @@ extern "C" {
 #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
 /** A BSS entry was removed (followed by BSS entry id and BSSID) */
 #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
+/** Notification of frequency conflict */
+#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
 
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c
index a6298a7..5ed7295 100644
--- a/wpa_supplicant/p2p_supplicant.c
+++ b/wpa_supplicant/p2p_supplicant.c
@@ -68,6 +68,55 @@ static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
 
 
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq)
+{
+	struct wpa_supplicant *iface;
+	int res;
+
+	for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+		if (iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE ||
+		    iface->current_ssid == NULL ||
+		    iface->current_ssid->frequency == freq)
+			continue;
+
+		if (iface->p2p_group_interface == P2P_GROUP_INTERFACE_GO) {
+			/*
+			 * Try to see whether we can move the GO. If it is not
+			 * possible, remove the GO interface.
+			 */
+			if (wpa_drv_switch_channel(iface, freq) == 0) {
+				wpa_dbg(iface, MSG_DEBUG, "P2P: GO moved to "
+					"freq %d", freq);
+				iface->current_ssid->frequency = freq;
+				continue;
+			}
+		}
+
+		/*
+		 * If GO cannot be moved or if the conflicting interface is a
+		 * P2P Client, remove the interface depending up on the
+		 * connection priority.
+		 */
+		res = wpas_is_interface_prioritized(wpa_s, iface);
+		if (res > 0) {
+			/*
+			 * Newly requested connection has priority over
+			 * existing P2P connection. So remove the interface.
+			 */
+			wpas_p2p_disconnect(iface);
+		} else if (res < 0) {
+			/*
+			 * No priority set. Notify the application to take
+			 * action.
+			 */
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
+
 static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
 				      struct wpa_scan_results *scan_res)
 {
diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h
index 605741d..7194681 100644
--- a/wpa_supplicant/p2p_supplicant.h
+++ b/wpa_supplicant/p2p_supplicant.h
@@ -31,6 +31,8 @@ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 				   unsigned int freq, unsigned int duration);
 void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 					  unsigned int freq);
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
+					int freq);
 int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 		       int freq);
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 4a7a5be..9d2fe95 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -1110,6 +1110,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 	struct wpa_driver_capa capa;
 	int assoc_failed = 0;
 	struct wpa_ssid *old_ssid;
+	int freq = 0;
 #ifdef CONFIG_HT_OVERRIDES
 	struct ieee80211_ht_capabilities htcaps;
 	struct ieee80211_ht_capabilities htcaps_mask;
@@ -1423,6 +1424,35 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
 	wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
 
+	/*
+	 * If multichannel concurrency is not supported, check for any
+	 * frequency conflict and take appropriate action.
+	 */
+	if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) &&
+	    (freq = wpa_drv_shared_freq(wpa_s)) > 0 && freq != params.freq) {
+		wpa_msg(wpa_s, MSG_INFO, "Shared interface with conflicting "
+			"frequency found (%d != %d)", freq, params.freq);
+#ifdef CONFIG_P2P
+		if (wpas_p2p_handle_frequency_conflicts(wpa_s, params.freq) <
+		    0) {
+			/*
+			 * Handling conflicts failed. Disable the current
+			 * connection request and notify the upper layers to
+			 * take appropriate action.
+			 */
+			wpa_printf(MSG_DEBUG, "Prioritize param not set. "
+				   "Notifying upper layers to handle the "
+				   "case");
+			wpa_supplicant_disable_network(wpa_s, ssid);
+			wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_FREQ_CONFLICT
+				" ssid=%s bssid=" MACSTR,
+				wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+				MAC2STR(wpa_s->pending_bssid));
+			os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+		}
+#endif /* CONFIG_P2P */
+	}
+
 	ret = wpa_drv_associate(wpa_s, &params);
 	if (ret < 0) {
 		wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
