aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2009-10-27 15:59:55 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-10-30 16:49:19 -0400
commit4d36ec58239eec44d77839ef6c25108efcbbb58c (patch)
tree7074195389af214ba0081169afb4b64514f2419a
parentb59f04cbf8ab4dce63f0d2ed658624b0ad21c67d (diff)
mac80211: split hardware scan by band
There's currently a very odd bug in mac80211 -- a hardware scan that is done while the hardware is really operating on 2.4 GHz will include CCK rates in the probe request frame, even on 5 GHz (if the driver uses the mac80211 IEs). Vice versa, if the hardware is operating on 5 GHz the 2.4 GHz probe requests will not include CCK rates even though they should. Fix this by splitting up cfg80211 scan requests by band -- recalculating the IEs every time -- and requesting only per-band scans from the driver. Apparently this bug hasn't been a problem yet, but it is imaginable that some older access points get confused if confronted with such behaviour. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/scan.c96
-rw-r--r--net/mac80211/util.c8
3 files changed, 80 insertions, 32 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 60c2822802f0..6365079e6375 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -667,10 +667,9 @@ struct ieee80211_local {
667 unsigned long scanning; 667 unsigned long scanning;
668 struct cfg80211_ssid scan_ssid; 668 struct cfg80211_ssid scan_ssid;
669 struct cfg80211_scan_request *int_scan_req; 669 struct cfg80211_scan_request *int_scan_req;
670 struct cfg80211_scan_request *scan_req; 670 struct cfg80211_scan_request *scan_req, *hw_scan_req;
671 struct ieee80211_channel *scan_channel; 671 struct ieee80211_channel *scan_channel;
672 const u8 *orig_ies; 672 enum ieee80211_band hw_scan_band;
673 int orig_ies_len;
674 int scan_channel_idx; 673 int scan_channel_idx;
675 int scan_ies_len; 674 int scan_ies_len;
676 675
@@ -1050,7 +1049,8 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
1050 u8 *extra, size_t extra_len, const u8 *bssid, 1049 u8 *extra, size_t extra_len, const u8 *bssid,
1051 const u8 *key, u8 key_len, u8 key_idx); 1050 const u8 *key, u8 key_len, u8 key_idx);
1052int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, 1051int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
1053 const u8 *ie, size_t ie_len); 1052 const u8 *ie, size_t ie_len,
1053 enum ieee80211_band band);
1054void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, 1054void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
1055 const u8 *ssid, size_t ssid_len, 1055 const u8 *ssid, size_t ssid_len,
1056 const u8 *ie, size_t ie_len); 1056 const u8 *ie, size_t ie_len);
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 1a643fe5250e..c46ac01e2a8f 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -187,6 +187,39 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb)
187 return RX_QUEUED; 187 return RX_QUEUED;
188} 188}
189 189
190/* return false if no more work */
191static bool ieee80211_prep_hw_scan(struct ieee80211_local *local)
192{
193 struct cfg80211_scan_request *req = local->scan_req;
194 enum ieee80211_band band;
195 int i, ielen, n_chans;
196
197 do {
198 if (local->hw_scan_band == IEEE80211_NUM_BANDS)
199 return false;
200
201 band = local->hw_scan_band;
202 n_chans = 0;
203 for (i = 0; i < req->n_channels; i++) {
204 if (req->channels[i]->band == band) {
205 local->hw_scan_req->channels[n_chans] =
206 req->channels[i];
207 n_chans++;
208 }
209 }
210
211 local->hw_scan_band++;
212 } while (!n_chans);
213
214 local->hw_scan_req->n_channels = n_chans;
215
216 ielen = ieee80211_build_preq_ies(local, (u8 *)local->hw_scan_req->ie,
217 req->ie, req->ie_len, band);
218 local->hw_scan_req->ie_len = ielen;
219
220 return true;
221}
222
190/* 223/*
191 * inform AP that we will go to sleep so that it will buffer the frames 224 * inform AP that we will go to sleep so that it will buffer the frames
192 * while we scan 225 * while we scan
@@ -247,13 +280,6 @@ static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata)
247 } 280 }
248} 281}
249 282
250static void ieee80211_restore_scan_ies(struct ieee80211_local *local)
251{
252 kfree(local->scan_req->ie);
253 local->scan_req->ie = local->orig_ies;
254 local->scan_req->ie_len = local->orig_ies_len;
255}
256
257void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) 283void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
258{ 284{
259 struct ieee80211_local *local = hw_to_local(hw); 285 struct ieee80211_local *local = hw_to_local(hw);
@@ -272,15 +298,22 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted)
272 return; 298 return;
273 } 299 }
274 300
275 if (test_bit(SCAN_HW_SCANNING, &local->scanning)) 301 was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
276 ieee80211_restore_scan_ies(local); 302 if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) {
303 ieee80211_queue_delayed_work(&local->hw,
304 &local->scan_work, 0);
305 mutex_unlock(&local->scan_mtx);
306 return;
307 }
308
309 kfree(local->hw_scan_req);
310 local->hw_scan_req = NULL;
277 311
278 if (local->scan_req != local->int_scan_req) 312 if (local->scan_req != local->int_scan_req)
279 cfg80211_scan_done(local->scan_req, aborted); 313 cfg80211_scan_done(local->scan_req, aborted);
280 local->scan_req = NULL; 314 local->scan_req = NULL;
281 local->scan_sdata = NULL; 315 local->scan_sdata = NULL;
282 316
283 was_hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning);
284 local->scanning = 0; 317 local->scanning = 0;
285 local->scan_channel = NULL; 318 local->scan_channel = NULL;
286 319
@@ -392,19 +425,23 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
392 425
393 if (local->ops->hw_scan) { 426 if (local->ops->hw_scan) {
394 u8 *ies; 427 u8 *ies;
395 int ielen;
396 428
397 ies = kmalloc(2 + IEEE80211_MAX_SSID_LEN + 429 local->hw_scan_req = kmalloc(
398 local->scan_ies_len + req->ie_len, GFP_KERNEL); 430 sizeof(*local->hw_scan_req) +
399 if (!ies) 431 req->n_channels * sizeof(req->channels[0]) +
432 2 + IEEE80211_MAX_SSID_LEN + local->scan_ies_len +
433 req->ie_len, GFP_KERNEL);
434 if (!local->hw_scan_req)
400 return -ENOMEM; 435 return -ENOMEM;
401 436
402 ielen = ieee80211_build_preq_ies(local, ies, 437 local->hw_scan_req->ssids = req->ssids;
403 req->ie, req->ie_len); 438 local->hw_scan_req->n_ssids = req->n_ssids;
404 local->orig_ies = req->ie; 439 ies = (u8 *)local->hw_scan_req +
405 local->orig_ies_len = req->ie_len; 440 sizeof(*local->hw_scan_req) +
406 req->ie = ies; 441 req->n_channels * sizeof(req->channels[0]);
407 req->ie_len = ielen; 442 local->hw_scan_req->ie = ies;
443
444 local->hw_scan_band = 0;
408 } 445 }
409 446
410 local->scan_req = req; 447 local->scan_req = req;
@@ -436,16 +473,17 @@ static int __ieee80211_start_scan(struct ieee80211_sub_if_data *sdata,
436 ieee80211_recalc_idle(local); 473 ieee80211_recalc_idle(local);
437 mutex_unlock(&local->scan_mtx); 474 mutex_unlock(&local->scan_mtx);
438 475
439 if (local->ops->hw_scan) 476 if (local->ops->hw_scan) {
440 rc = drv_hw_scan(local, local->scan_req); 477 WARN_ON(!ieee80211_prep_hw_scan(local));
441 else 478 rc = drv_hw_scan(local, local->hw_scan_req);
479 } else
442 rc = ieee80211_start_sw_scan(local); 480 rc = ieee80211_start_sw_scan(local);
443 481
444 mutex_lock(&local->scan_mtx); 482 mutex_lock(&local->scan_mtx);
445 483
446 if (rc) { 484 if (rc) {
447 if (local->ops->hw_scan) 485 kfree(local->hw_scan_req);
448 ieee80211_restore_scan_ies(local); 486 local->hw_scan_req = NULL;
449 local->scanning = 0; 487 local->scanning = 0;
450 488
451 ieee80211_recalc_idle(local); 489 ieee80211_recalc_idle(local);
@@ -654,6 +692,14 @@ void ieee80211_scan_work(struct work_struct *work)
654 return; 692 return;
655 } 693 }
656 694
695 if (local->hw_scan_req) {
696 int rc = drv_hw_scan(local, local->hw_scan_req);
697 mutex_unlock(&local->scan_mtx);
698 if (rc)
699 ieee80211_scan_completed(&local->hw, true);
700 return;
701 }
702
657 if (local->scan_req && !local->scanning) { 703 if (local->scan_req && !local->scanning) {
658 struct cfg80211_scan_request *req = local->scan_req; 704 struct cfg80211_scan_request *req = local->scan_req;
659 int rc; 705 int rc;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index aeb65b3d2295..aedbaaa067e6 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -872,13 +872,14 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata,
872} 872}
873 873
874int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer, 874int ieee80211_build_preq_ies(struct ieee80211_local *local, u8 *buffer,
875 const u8 *ie, size_t ie_len) 875 const u8 *ie, size_t ie_len,
876 enum ieee80211_band band)
876{ 877{
877 struct ieee80211_supported_band *sband; 878 struct ieee80211_supported_band *sband;
878 u8 *pos, *supp_rates_len, *esupp_rates_len = NULL; 879 u8 *pos, *supp_rates_len, *esupp_rates_len = NULL;
879 int i; 880 int i;
880 881
881 sband = local->hw.wiphy->bands[local->hw.conf.channel->band]; 882 sband = local->hw.wiphy->bands[band];
882 883
883 pos = buffer; 884 pos = buffer;
884 885
@@ -966,7 +967,8 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
966 memcpy(pos, ssid, ssid_len); 967 memcpy(pos, ssid, ssid_len);
967 pos += ssid_len; 968 pos += ssid_len;
968 969
969 skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len)); 970 skb_put(skb, ieee80211_build_preq_ies(local, pos, ie, ie_len,
971 local->hw.conf.channel->band));
970 972
971 ieee80211_tx_skb(sdata, skb, 0); 973 ieee80211_tx_skb(sdata, skb, 0);
972} 974}