From 253f2e37957e726677d545f5598aad56d7d33bf5 Mon Sep 17 00:00:00 2001 From: Arif Hussain Date: Fri, 24 Jan 2014 16:14:29 +0200 Subject: [PATCH] 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 --- src/common/qca-vendor.h | 18 ++++++++++ src/common/wpa_ctrl.h | 3 ++ src/drivers/driver.h | 18 +++++++++- src/drivers/driver_common.c | 1 + src/drivers/driver_nl80211.c | 50 ++++++++++++++++++++++++++++ wpa_supplicant/events.c | 55 +++++++++++++++++++++++++++++++ wpa_supplicant/p2p_supplicant.c | 2 ++ wpa_supplicant/wpa_supplicant.c | 1 + wpa_supplicant/wpa_supplicant_i.h | 1 + 9 files changed, 148 insertions(+), 1 deletion(-) diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h index 6e2f0c00f..0d83920a6 100644 --- a/src/common/qca-vendor.h +++ b/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 */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 9bd59a9c9..57c38787d 100644 --- a/src/common/wpa_ctrl.h +++ b/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: */ #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 " diff --git a/src/drivers/driver.h b/src/drivers/driver.h index 4fd16edc9..2f4536ee6 100644 --- a/src/drivers/driver.h +++ b/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; }; /** diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c index 64bdddb2d..3058cd57d 100644 --- a/src/drivers/driver_common.c +++ b/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"; diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c index 008b03191..646d3f830 100644 --- a/src/drivers/driver_nl80211.c +++ b/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", diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index d9449fdb7..d5ac6bfb8 100644 --- a/wpa_supplicant/events.c +++ b/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) diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index 889a949f2..a7eb9aaba 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/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); } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index aa9c32d6d..1bb1a2ce2 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/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); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 078856e8f..68995fc74 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/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,