Browse Source

P2P: Apply unsafe frequency rules to available channels

This adds a QCA vendor specific nl80211 event to allow the driver to
indicate a list of frequency ranges that should be avoided due to
interference or possible known co-existance constraints. Such
frequencies are marked as not allowed for P2P use to force groups to be
formed on different channels.

If a P2P GO is operating on a channel that the driver recommended not to
use, a notification about this is sent on the control interface and
upper layer code may decide to tear down the group and optionally
restart it on another channel. As a TODO item, this could also be changed
to use CSA to avoid removing the group.

Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
Arif Hussain 11 years ago
parent
commit
253f2e3795

+ 18 - 0
src/common/qca-vendor.h

@@ -24,10 +24,28 @@
  * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
  *
  * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ *	ranges to avoid to reduce issues due to interference or internal
+ *	co-existence information in the driver. The event data structure is
+ *	defined in struct qca_avoid_freq_list.
  */
 enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
 	QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+	/* subcmds 2..9 not yet allocated */
+	QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
 };
 
+
+struct qca_avoid_freq_range {
+	u32 start_freq;
+	u32 end_freq;
+} STRUCT_PACKED;
+
+struct qca_avoid_freq_list {
+	u32 count;
+	struct qca_avoid_freq_range range[0];
+} STRUCT_PACKED;
+
 #endif /* QCA_VENDOR_H */

+ 3 - 0
src/common/wpa_ctrl.h

@@ -74,6 +74,8 @@ extern "C" {
  * be used again.
  */
 #define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
+/** Frequency ranges that the driver recommends to avoid */
+#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
 /** Available WPS AP with active PBC found in scan results */
@@ -151,6 +153,7 @@ extern "C" {
 
 /* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
 #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
+#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
 
 #define INTERWORKING_AP "INTERWORKING-AP "
 #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "

+ 17 - 1
src/drivers/driver.h

@@ -3147,7 +3147,16 @@ enum wpa_event_type {
 	 * EVENT_SCAN_RESULTS is used to indicate when the scan has been
 	 * completed (either successfully or by getting cancelled).
 	 */
-	EVENT_SCAN_STARTED
+	EVENT_SCAN_STARTED,
+
+	/**
+	 * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+	 *
+	 * This event indicates a set of frequency ranges that should be avoided
+	 * to reduce issues due to interference or internal co-existence
+	 * information in the driver.
+	 */
+	EVENT_AVOID_FREQUENCIES
 };
 
 
@@ -3766,6 +3775,13 @@ union wpa_event_data {
 	struct channel_list_changed {
 		enum reg_change_initiator initiator;
 	} channel_list_changed;
+
+	/**
+	 * freq_range - List of frequency ranges
+	 *
+	 * This is used as the data with EVENT_AVOID_FREQUENCIES.
+	 */
+	struct wpa_freq_range_list freq_range;
 };
 
 /**

+ 1 - 0
src/drivers/driver_common.c

@@ -78,6 +78,7 @@ const char * event_to_string(enum wpa_event_type event)
 	E2S(DFS_NOP_FINISHED);
 	E2S(SURVEY);
 	E2S(SCAN_STARTED);
+	E2S(AVOID_FREQUENCIES);
 	}
 
 	return "UNKNOWN";

+ 50 - 0
src/drivers/driver_nl80211.c

@@ -2717,10 +2717,60 @@ static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
 }
 
 
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+				   const u8 *data, size_t len)
+{
+	u32 i, count;
+	union wpa_event_data event;
+	struct wpa_freq_range *range = NULL;
+	const struct qca_avoid_freq_list *freq_range;
+
+	freq_range = (const struct qca_avoid_freq_list *) data;
+	if (len < sizeof(freq_range->count))
+		return;
+
+	count = freq_range->count;
+	if (len < sizeof(freq_range->count) +
+	    count * sizeof(struct qca_avoid_freq_range)) {
+		wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+			   (unsigned int) len);
+		return;
+	}
+
+	if (count > 0) {
+		range = os_calloc(count, sizeof(struct wpa_freq_range));
+		if (range == NULL)
+			return;
+	}
+
+	os_memset(&event, 0, sizeof(event));
+	for (i = 0; i < count; i++) {
+		unsigned int idx = event.freq_range.num;
+		range[idx].min = freq_range->range[i].start_freq;
+		range[idx].max = freq_range->range[i].end_freq;
+		wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+			   range[idx].min, range[idx].max);
+		if (range[idx].min > range[idx].max) {
+			wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+			continue;
+		}
+		event.freq_range.num++;
+	}
+	event.freq_range.range = range;
+
+	wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+	os_free(range);
+}
+
+
 static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
 				     u32 subcmd, u8 *data, size_t len)
 {
 	switch (subcmd) {
+	case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+		qca_nl80211_avoid_freq(drv, data, len);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Ignore unsupported QCA vendor event %u",

+ 55 - 0
wpa_supplicant/events.c

@@ -2775,6 +2775,58 @@ static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
+					     union wpa_event_data *event)
+{
+#ifdef CONFIG_P2P
+	struct wpa_supplicant *ifs;
+#endif /* CONFIG_P2P */
+	struct wpa_freq_range_list *list;
+	char *str = NULL;
+
+	list = &event->freq_range;
+
+	if (list->num)
+		str = freq_range_list_str(list);
+	wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s",
+		str ? str : "");
+
+#ifdef CONFIG_P2P
+	if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) {
+		wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range",
+			__func__);
+	} else {
+		wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
+		wpas_p2p_update_channel_list(wpa_s);
+	}
+
+	for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+		int freq;
+		if (!ifs->current_ssid ||
+		    !ifs->current_ssid->p2p_group ||
+		    (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+		     ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+			continue;
+
+		freq = ifs->current_ssid->frequency;
+		if (!freq_range_list_includes(list, freq)) {
+			wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
+				freq);
+			continue;
+		}
+
+		wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
+			freq);
+		/* TODO: Consider using CSA or removing the group within
+		 * wpa_supplicant */
+		wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+	}
+#endif /* CONFIG_P2P */
+
+	os_free(str);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
@@ -3266,6 +3318,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 		wpas_wps_start_pbc(wpa_s, NULL, 0);
 #endif /* CONFIG_WPS */
 		break;
+	case EVENT_AVOID_FREQUENCIES:
+		wpa_supplicant_notify_avoid_freq(wpa_s, data);
+		break;
 	case EVENT_CONNECT_FAILED_REASON:
 #ifdef CONFIG_AP
 		if (!wpa_s->ap_iface || !data)

+ 2 - 0
wpa_supplicant/p2p_supplicant.c

@@ -3190,6 +3190,8 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
 static int wpas_p2p_disallowed_freq(struct wpa_global *global,
 				    unsigned int freq)
 {
+	if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
+		return 1;
 	return freq_range_list_includes(&global->p2p_disallow_freq, freq);
 }
 

+ 1 - 0
wpa_supplicant/wpa_supplicant.c

@@ -3927,6 +3927,7 @@ void wpa_supplicant_deinit(struct wpa_global *global)
 	os_free(global->params.override_ctrl_interface);
 
 	os_free(global->p2p_disallow_freq.range);
+	os_free(global->p2p_go_avoid_freq.range);
 	os_free(global->add_psk);
 
 	os_free(global);

+ 1 - 0
wpa_supplicant/wpa_supplicant_i.h

@@ -252,6 +252,7 @@ struct wpa_global {
 	int p2p_disabled;
 	int cross_connection;
 	struct wpa_freq_range_list p2p_disallow_freq;
+	struct wpa_freq_range_list p2p_go_avoid_freq;
 	enum wpa_conc_pref {
 		WPA_CONC_PREF_NOT_SET,
 		WPA_CONC_PREF_STA,