diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 214 |
1 files changed, 151 insertions, 63 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 9270771702f..169da0742c8 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c @@ -13,6 +13,7 @@ */ #include <linux/if_arp.h> +#include <linux/etherdevice.h> #include <linux/rtnetlink.h> #include <linux/pm_qos.h> #include <net/sch_generic.h> @@ -28,20 +29,6 @@ #define IEEE80211_CHANNEL_TIME (HZ / 33) #define IEEE80211_PASSIVE_CHANNEL_TIME (HZ / 8) -struct ieee80211_bss * -ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, - u8 *ssid, u8 ssid_len) -{ - struct cfg80211_bss *cbss; - - cbss = cfg80211_get_bss(local->hw.wiphy, - ieee80211_get_channel(local->hw.wiphy, freq), - bssid, ssid, ssid_len, 0, 0); - if (!cbss) - return NULL; - return (void *)cbss->priv; -} - static void ieee80211_rx_bss_free(struct cfg80211_bss *cbss) { struct ieee80211_bss *bss = (void *)cbss->priv; @@ -103,16 +90,35 @@ ieee80211_bss_info_update(struct ieee80211_local *local, cbss->free_priv = ieee80211_rx_bss_free; bss = (void *)cbss->priv; + if (elems->parse_error) { + if (beacon) + bss->corrupt_data |= IEEE80211_BSS_CORRUPT_BEACON; + else + bss->corrupt_data |= IEEE80211_BSS_CORRUPT_PROBE_RESP; + } else { + if (beacon) + bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_BEACON; + else + bss->corrupt_data &= ~IEEE80211_BSS_CORRUPT_PROBE_RESP; + } + /* save the ERP value so that it is available at association time */ - if (elems->erp_info && elems->erp_info_len >= 1) { + if (elems->erp_info && elems->erp_info_len >= 1 && + (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_ERP))) { bss->erp_value = elems->erp_info[0]; bss->has_erp_value = true; + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_ERP; } - if (elems->tim) { + if (elems->tim && (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_DTIM))) { struct ieee80211_tim_ie *tim_ie = (struct ieee80211_tim_ie *)elems->tim; bss->dtim_period = tim_ie->dtim_period; + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_DTIM; } /* If the beacon had no TIM IE, or it was invalid, use 1 */ @@ -120,26 +126,38 @@ ieee80211_bss_info_update(struct ieee80211_local *local, bss->dtim_period = 1; /* replace old supported rates if we get new values */ - srlen = 0; - if (elems->supp_rates) { - clen = IEEE80211_MAX_SUPP_RATES; - if (clen > elems->supp_rates_len) - clen = elems->supp_rates_len; - memcpy(bss->supp_rates, elems->supp_rates, clen); - srlen += clen; - } - if (elems->ext_supp_rates) { - clen = IEEE80211_MAX_SUPP_RATES - srlen; - if (clen > elems->ext_supp_rates_len) - clen = elems->ext_supp_rates_len; - memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen); - srlen += clen; + if (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_RATES)) { + srlen = 0; + if (elems->supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES; + if (clen > elems->supp_rates_len) + clen = elems->supp_rates_len; + memcpy(bss->supp_rates, elems->supp_rates, clen); + srlen += clen; + } + if (elems->ext_supp_rates) { + clen = IEEE80211_MAX_SUPP_RATES - srlen; + if (clen > elems->ext_supp_rates_len) + clen = elems->ext_supp_rates_len; + memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, + clen); + srlen += clen; + } + if (srlen) { + bss->supp_rates_len = srlen; + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_RATES; + } } - if (srlen) - bss->supp_rates_len = srlen; - bss->wmm_used = elems->wmm_param || elems->wmm_info; - bss->uapsd_supported = is_uapsd_supported(elems); + if (!elems->parse_error || + !(bss->valid_data & IEEE80211_BSS_VALID_WMM)) { + bss->wmm_used = elems->wmm_param || elems->wmm_info; + bss->uapsd_supported = is_uapsd_supported(elems); + if (!elems->parse_error) + bss->valid_data |= IEEE80211_BSS_VALID_WMM; + } if (!beacon) bss->last_probe_resp = jiffies; @@ -176,7 +194,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) presp = ieee80211_is_probe_resp(fc); if (presp) { /* ignore ProbeResp to foreign address */ - if (memcmp(mgmt->da, sdata->vif.addr, ETH_ALEN)) + if (!ether_addr_equal(mgmt->da, sdata->vif.addr)) return RX_DROP_MONITOR; presp = true; @@ -338,7 +356,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) */ drv_sw_scan_start(local); - local->leave_oper_channel_time = 0; + local->leave_oper_channel_time = jiffies; local->next_scan_state = SCAN_DECISION; local->scan_channel_idx = 0; @@ -355,6 +373,57 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) return 0; } +static bool ieee80211_can_scan(struct ieee80211_local *local, + struct ieee80211_sub_if_data *sdata) +{ + if (!list_empty(&local->work_list)) + return false; + + if (sdata->vif.type == NL80211_IFTYPE_STATION && + sdata->u.mgd.flags & (IEEE80211_STA_BEACON_POLL | + IEEE80211_STA_CONNECTION_POLL)) + return false; + + return true; +} + +void ieee80211_run_deferred_scan(struct ieee80211_local *local) +{ + lockdep_assert_held(&local->mtx); + + if (!local->scan_req || local->scanning) + return; + + if (!ieee80211_can_scan(local, local->scan_sdata)) + return; + + ieee80211_queue_delayed_work(&local->hw, &local->scan_work, + round_jiffies_relative(0)); +} + +static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, + unsigned long *next_delay) +{ + int i; + struct ieee80211_sub_if_data *sdata = local->scan_sdata; + enum ieee80211_band band = local->hw.conf.channel->band; + + for (i = 0; i < local->scan_req->n_ssids; i++) + ieee80211_send_probe_req( + sdata, NULL, + local->scan_req->ssids[i].ssid, + local->scan_req->ssids[i].ssid_len, + local->scan_req->ie, local->scan_req->ie_len, + local->scan_req->rates[band], false, + local->scan_req->no_cck); + + /* + * After sending probe requests, wait for probe responses + * on the channel. + */ + *next_delay = IEEE80211_CHANNEL_TIME; + local->next_scan_state = SCAN_DECISION; +} static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, struct cfg80211_scan_request *req) @@ -367,7 +436,7 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, if (local->scan_req) return -EBUSY; - if (!list_empty(&local->work_list)) { + if (!ieee80211_can_scan(local, sdata)) { /* wait for the work to finish/time out */ local->scan_req = req; local->scan_sdata = sdata; @@ -406,10 +475,47 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata, local->scan_req = req; local->scan_sdata = sdata; - if (local->ops->hw_scan) + if (local->ops->hw_scan) { __set_bit(SCAN_HW_SCANNING, &local->scanning); - else + } else if ((req->n_channels == 1) && + (req->channels[0]->center_freq == + local->hw.conf.channel->center_freq)) { + + /* If we are scanning only on the current channel, then + * we do not need to stop normal activities + */ + unsigned long next_delay; + + __set_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning); + + ieee80211_recalc_idle(local); + + /* Notify driver scan is starting, keep order of operations + * same as normal software scan, in case that matters. */ + drv_sw_scan_start(local); + + ieee80211_configure_filter(local); /* accept probe-responses */ + + /* We need to ensure power level is at max for scanning. */ + ieee80211_hw_config(local, 0); + + if ((req->channels[0]->flags & + IEEE80211_CHAN_PASSIVE_SCAN) || + !local->scan_req->n_ssids) { + next_delay = IEEE80211_PASSIVE_CHANNEL_TIME; + } else { + ieee80211_scan_state_send_probe(local, &next_delay); + next_delay = IEEE80211_CHANNEL_TIME; + } + + /* Now, just wait a bit and we are all done! */ + ieee80211_queue_delayed_work(&local->hw, &local->scan_work, + next_delay); + return 0; + } else { + /* Do normal software scan */ __set_bit(SCAN_SW_SCANNING, &local->scanning); + } ieee80211_recalc_idle(local); @@ -566,30 +672,6 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, local->next_scan_state = SCAN_SEND_PROBE; } -static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, - unsigned long *next_delay) -{ - int i; - struct ieee80211_sub_if_data *sdata = local->scan_sdata; - enum ieee80211_band band = local->hw.conf.channel->band; - - for (i = 0; i < local->scan_req->n_ssids; i++) - ieee80211_send_probe_req( - sdata, NULL, - local->scan_req->ssids[i].ssid, - local->scan_req->ssids[i].ssid_len, - local->scan_req->ie, local->scan_req->ie_len, - local->scan_req->rates[band], false, - local->scan_req->no_cck); - - /* - * After sending probe requests, wait for probe responses - * on the channel. - */ - *next_delay = IEEE80211_CHANNEL_TIME; - local->next_scan_state = SCAN_DECISION; -} - static void ieee80211_scan_state_suspend(struct ieee80211_local *local, unsigned long *next_delay) { @@ -640,6 +722,12 @@ void ieee80211_scan_work(struct work_struct *work) sdata = local->scan_sdata; + /* When scanning on-channel, the first-callback means completed. */ + if (test_bit(SCAN_ONCHANNEL_SCANNING, &local->scanning)) { + aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); + goto out_complete; + } + if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); goto out_complete; |