diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2009-10-27 15:59:55 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-10-30 16:49:19 -0400 |
commit | 4d36ec58239eec44d77839ef6c25108efcbbb58c (patch) | |
tree | 7074195389af214ba0081169afb4b64514f2419a /net/mac80211/scan.c | |
parent | b59f04cbf8ab4dce63f0d2ed658624b0ad21c67d (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>
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 96 |
1 files changed, 71 insertions, 25 deletions
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 */ | ||
191 | static 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 | ||
250 | static 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 | |||
257 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 283 | void 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; |