aboutsummaryrefslogtreecommitdiffstats
path: root/wpa_supplicant
diff options
context:
space:
mode:
authorRajkumar Manoharan <rmanohar@qca.qualcomm.com>2012-05-03 09:13:43 (GMT)
committerJouni Malinen <j@w1.fi>2012-05-03 12:55:38 (GMT)
commitc3701c66a5f57c95b7353688429149e904a9d1df (patch)
tree6cb4261fcd75e7e80b3108cbaacf89af3c89fae6 /wpa_supplicant
parent73cdd917a33ebaaed8dca43e16eb5432eb20ccba (diff)
downloadhostap-c3701c66a5f57c95b7353688429149e904a9d1df.zip
hostap-c3701c66a5f57c95b7353688429149e904a9d1df.tar.gz
hostap-c3701c66a5f57c95b7353688429149e904a9d1df.tar.bz2
Add handling of OBSS scan requests and 20/40 BSS coex reports
Add support for HT STA to report 40 MHz intolerance to the associated AP. A HT station generates a report (20/40 BSS coexistence) of channel list if it finds a non-HT capable AP or a HT AP which prohibits 40 MHz transmission (i.e., 40 MHz intolerant bit is set in HT capabilities IE) from the scan results. Parse the OBSS scan parameter from Beacon or Probe Response frames and schedule periodic scan to generate 20/40 coexistence channel report if requested to do so. This patch decodes Scan Interval alone from the OBSS Scan Parameters element and triggers scan on timeout. Signed-off-by: Rajkumar Manoharan <rmanohar@qca.qualcomm.com> Signed-hostap: Jouni Malinen <jouni@qca.qualcomm.com>
Diffstat (limited to 'wpa_supplicant')
-rw-r--r--wpa_supplicant/events.c5
-rw-r--r--wpa_supplicant/sme.c216
-rw-r--r--wpa_supplicant/sme.h13
-rw-r--r--wpa_supplicant/wpa_supplicant.c3
-rw-r--r--wpa_supplicant/wpa_supplicant_i.h3
5 files changed, 240 insertions, 0 deletions
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index da9cf2b..26bb4c4 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1077,6 +1077,11 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
wpas_notify_scan_done(wpa_s, 1);
+ if (sme_proc_obss_scan(wpa_s) > 0) {
+ wpa_scan_results_free(scan_res);
+ return 0;
+ }
+
if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
wpa_scan_results_free(scan_res);
return 0;
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index b366847..0051ded 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -32,6 +32,7 @@
static void sme_auth_timer(void *eloop_ctx, void *timeout_ctx);
static void sme_assoc_timer(void *eloop_ctx, void *timeout_ctx);
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx);
#ifdef CONFIG_IEEE80211W
static void sme_stop_sa_query(struct wpa_supplicant *wpa_s);
#endif /* CONFIG_IEEE80211W */
@@ -606,6 +607,221 @@ void sme_deinit(struct wpa_supplicant *wpa_s)
eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
+ eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
+ const u8 *chan_list, u8 num_channels,
+ u8 num_intol)
+{
+ struct ieee80211_2040_bss_coex_ie *bc_ie;
+ struct ieee80211_2040_intol_chan_report *ic_report;
+ struct wpabuf *buf;
+
+ wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR,
+ MAC2STR(wpa_s->bssid));
+
+ buf = wpabuf_alloc(2 + /* action.category + action_code */
+ sizeof(struct ieee80211_2040_bss_coex_ie) +
+ sizeof(struct ieee80211_2040_intol_chan_report) +
+ num_channels);
+ if (buf == NULL)
+ return;
+
+ wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
+ wpabuf_put_u8(buf, WLAN_PA_20_40_BSS_COEX);
+
+ bc_ie = wpabuf_put(buf, sizeof(*bc_ie));
+ bc_ie->element_id = WLAN_EID_20_40_BSS_COEXISTENCE;
+ bc_ie->length = 1;
+ if (num_intol)
+ bc_ie->coex_param |= WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ;
+
+ if (num_channels > 0) {
+ ic_report = wpabuf_put(buf, sizeof(*ic_report));
+ ic_report->element_id = WLAN_EID_20_40_BSS_INTOLERANT;
+ ic_report->length = num_channels + 1;
+ ic_report->op_class = 0;
+ os_memcpy(wpabuf_put(buf, num_channels), chan_list,
+ num_channels);
+ }
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "SME: Failed to send 20/40 BSS Coexistence frame");
+ }
+
+ wpabuf_free(buf);
+}
+
+
+/**
+ * enum wpas_band - Frequency band
+ * @WPAS_BAND_2GHZ: 2.4 GHz ISM band
+ * @WPAS_BAND_5GHZ: around 5 GHz band (4.9 - 5.7 GHz)
+ */
+enum wpas_band {
+ WPAS_BAND_2GHZ,
+ WPAS_BAND_5GHZ,
+ WPAS_BAND_INVALID
+};
+
+/**
+ * freq_to_channel - Convert frequency into channel info
+ * @channel: Buffer for returning channel number
+ * Returns: Band (2 or 5 GHz)
+ */
+static enum wpas_band freq_to_channel(int freq, u8 *channel)
+{
+ enum wpas_band band = (freq <= 2484) ? WPAS_BAND_2GHZ : WPAS_BAND_5GHZ;
+ u8 chan = 0;
+
+ if (freq >= 2412 && freq <= 2472)
+ chan = (freq - 2407) / 5;
+ else if (freq == 2484)
+ chan = 14;
+ else if (freq >= 5180 && freq <= 5805)
+ chan = (freq - 5000) / 5;
+
+ *channel = chan;
+ return band;
+}
+
+
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bss *bss;
+ const u8 *ie;
+ u16 ht_cap;
+ u8 chan_list[P2P_MAX_CHANNELS], channel;
+ u8 num_channels = 0, num_intol = 0, i;
+
+ if (!wpa_s->sme.sched_obss_scan)
+ return 0;
+
+ wpa_s->sme.sched_obss_scan = 0;
+ if (!wpa_s->current_bss || wpa_s->wpa_state != WPA_COMPLETED)
+ return 1;
+
+ /*
+ * Check whether AP uses regulatory triplet or channel triplet in
+ * country info. Right now the operating class of the BSS channel
+ * width trigger event is "unknown" (IEEE Std 802.11-2012 10.15.12),
+ * based on the assumption that operating class triplet is not used in
+ * beacon frame. If the First Channel Number/Operating Extension
+ * Identifier octet has a positive integer value of 201 or greater,
+ * then its operating class triplet.
+ *
+ * TODO: If Supported Operating Classes element is present in beacon
+ * frame, have to lookup operating class in Annex E and fill them in
+ * 2040 coex frame.
+ */
+ ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_COUNTRY);
+ if (ie && (ie[1] >= 6) && (ie[5] >= 201))
+ return 1;
+
+ os_memset(chan_list, 0, sizeof(chan_list));
+
+ dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+ /* Skip other band bss */
+ if (freq_to_channel(bss->freq, &channel) != WPAS_BAND_2GHZ)
+ continue;
+
+ ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
+ ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
+
+ if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
+ /* Check whether the channel is already considered */
+ for (i = 0; i < num_channels; i++) {
+ if (channel == chan_list[i])
+ break;
+ }
+ if (i != num_channels)
+ continue;
+
+ if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+ num_intol++;
+
+ chan_list[num_channels++] = channel;
+ }
+ }
+
+ sme_send_2040_bss_coex(wpa_s, chan_list, num_channels, num_intol);
+ return 1;
+}
+
+
+static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+ struct wpa_driver_scan_params params;
+
+ if (!wpa_s->current_bss) {
+ wpa_printf(MSG_DEBUG, "SME OBSS: Ignore scan request");
+ return;
+ }
+
+ os_memset(&params, 0, sizeof(params));
+ /* TODO: 2.4 GHz channels only */
+ wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
+
+ if (wpa_supplicant_trigger_scan(wpa_s, &params))
+ wpa_printf(MSG_DEBUG, "SME OBSS: Failed to trigger scan");
+ else
+ wpa_s->sme.sched_obss_scan = 1;
+
+ eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+ sme_obss_scan_timeout, wpa_s, NULL);
+}
+
+
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
+{
+ const u8 *ie;
+ struct wpa_bss *bss = wpa_s->current_bss;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
+ wpa_s->sme.sched_obss_scan = 0;
+ if (!enable)
+ return;
+
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) || ssid == NULL ||
+ ssid->mode != IEEE80211_MODE_INFRA)
+ return; /* Not using station SME in wpa_supplicant */
+
+ if (!wpa_s->hw.modes ||
+ !(wpa_s->hw.modes->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ return; /* Driver does not support HT40 */
+
+ if (bss == NULL || bss->freq < 2400 || bss->freq > 2500)
+ return; /* Not associated on 2.4 GHz band */
+
+ /* Check whether AP supports HT40 */
+ ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_CAP);
+ if (!ie || ie[1] < 2 ||
+ !(WPA_GET_LE16(ie + 2) & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+ return; /* AP does not support HT40 */
+
+ ie = wpa_bss_get_ie(wpa_s->current_bss,
+ WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS);
+ if (!ie || ie[1] < 14)
+ return; /* AP does not request OBSS scans */
+
+ wpa_s->sme.obss_scan_int = WPA_GET_LE16(ie + 6);
+ if (wpa_s->sme.obss_scan_int < 10) {
+ wpa_printf(MSG_DEBUG, "SME: Invalid OBSS Scan Interval %u "
+ "replaced with the minimum 10 sec",
+ wpa_s->sme.obss_scan_int);
+ wpa_s->sme.obss_scan_int = 10;
+ }
+ wpa_printf(MSG_DEBUG, "SME: OBSS Scan Interval %u sec",
+ wpa_s->sme.obss_scan_int);
+ eloop_register_timeout(wpa_s->sme.obss_scan_int, 0,
+ sme_obss_scan_timeout, wpa_s, NULL);
}
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
index 33530bb..a7cc507 100644
--- a/wpa_supplicant/sme.h
+++ b/wpa_supplicant/sme.h
@@ -35,6 +35,9 @@ void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
const u8 *prev_pending_bssid);
void sme_deinit(struct wpa_supplicant *wpa_s);
+int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
+void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable);
+
#else /* CONFIG_SME */
static inline void sme_authenticate(struct wpa_supplicant *wpa_s,
@@ -95,6 +98,16 @@ static inline void sme_deinit(struct wpa_supplicant *wpa_s)
{
}
+static inline int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
+{
+ return 0;
+}
+
+static inline void sme_sched_obss_scan(struct wpa_supplicant *wpa_s,
+ int enable)
+{
+}
+
#endif /* CONFIG_SME */
#endif /* SME_H */
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 8c8de26..1473e4b 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -607,6 +607,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
#ifdef CONFIG_P2P
wpas_p2p_completed(wpa_s);
#endif /* CONFIG_P2P */
+
+ sme_sched_obss_scan(wpa_s, 1);
} else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
state == WPA_ASSOCIATED) {
wpa_s->new_connection = 1;
@@ -614,6 +616,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
#ifndef IEEE8021X_EAPOL
wpa_drv_set_supp_port(wpa_s, 0);
#endif /* IEEE8021X_EAPOL */
+ sme_sched_obss_scan(wpa_s, 0);
}
wpa_s->wpa_state = state;
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 2795004..7067eec 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -419,6 +419,8 @@ struct wpa_supplicant {
* sa_query_count octets of pending
* SA Query transaction identifiers */
struct os_time sa_query_start;
+ u8 sched_obss_scan;
+ u16 obss_scan_int;
} sme;
#endif /* CONFIG_SME */
@@ -634,6 +636,7 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_proc_40mhz_intolerant(struct wpa_supplicant *wpa_s);
/**
* wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response