diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 138 |
1 files changed, 83 insertions, 55 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index fb274db77e3c..489b6ad200d4 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -196,7 +196,8 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
196 | ieee802_11_parse_elems(elements, skb->len - baselen, &elems); | 196 | ieee802_11_parse_elems(elements, skb->len - baselen, &elems); |
197 | 197 | ||
198 | if (elems.ds_params && elems.ds_params_len == 1) | 198 | if (elems.ds_params && elems.ds_params_len == 1) |
199 | freq = ieee80211_channel_to_frequency(elems.ds_params[0]); | 199 | freq = ieee80211_channel_to_frequency(elems.ds_params[0], |
200 | rx_status->band); | ||
200 | else | 201 | else |
201 | freq = rx_status->freq; | 202 | freq = rx_status->freq; |
202 | 203 | ||
@@ -211,6 +212,14 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
211 | if (bss) | 212 | if (bss) |
212 | ieee80211_rx_bss_put(sdata->local, bss); | 213 | ieee80211_rx_bss_put(sdata->local, bss); |
213 | 214 | ||
215 | /* If we are on-operating-channel, and this packet is for the | ||
216 | * current channel, pass the pkt on up the stack so that | ||
217 | * the rest of the stack can make use of it. | ||
218 | */ | ||
219 | if (ieee80211_cfg_on_oper_channel(sdata->local) | ||
220 | && (channel == sdata->local->oper_channel)) | ||
221 | return RX_CONTINUE; | ||
222 | |||
214 | dev_kfree_skb(skb); | 223 | dev_kfree_skb(skb); |
215 | return RX_QUEUED; | 224 | return RX_QUEUED; |
216 | } | 225 | } |
@@ -249,10 +258,12 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
249 | return true; | 258 | return true; |
250 | } | 259 | } |
251 | 260 | ||
252 | static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | 261 | static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, |
253 | bool was_hw_scan) | 262 | bool was_hw_scan) |
254 | { | 263 | { |
255 | struct ieee80211_local *local = hw_to_local(hw); | 264 | struct ieee80211_local *local = hw_to_local(hw); |
265 | bool on_oper_chan; | ||
266 | bool enable_beacons = false; | ||
256 | 267 | ||
257 | lockdep_assert_held(&local->mtx); | 268 | lockdep_assert_held(&local->mtx); |
258 | 269 | ||
@@ -266,12 +277,12 @@ static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
266 | aborted = true; | 277 | aborted = true; |
267 | 278 | ||
268 | if (WARN_ON(!local->scan_req)) | 279 | if (WARN_ON(!local->scan_req)) |
269 | return false; | 280 | return; |
270 | 281 | ||
271 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { | 282 | if (was_hw_scan && !aborted && ieee80211_prep_hw_scan(local)) { |
272 | int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req); | 283 | int rc = drv_hw_scan(local, local->scan_sdata, local->hw_scan_req); |
273 | if (rc == 0) | 284 | if (rc == 0) |
274 | return false; | 285 | return; |
275 | } | 286 | } |
276 | 287 | ||
277 | kfree(local->hw_scan_req); | 288 | kfree(local->hw_scan_req); |
@@ -285,24 +296,28 @@ static bool __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
285 | local->scanning = 0; | 296 | local->scanning = 0; |
286 | local->scan_channel = NULL; | 297 | local->scan_channel = NULL; |
287 | 298 | ||
288 | return true; | 299 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); |
289 | } | ||
290 | 300 | ||
291 | static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, | 301 | if (was_hw_scan || !on_oper_chan) |
292 | bool was_hw_scan) | 302 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
293 | { | 303 | else |
294 | struct ieee80211_local *local = hw_to_local(hw); | 304 | /* Set power back to normal operating levels. */ |
305 | ieee80211_hw_config(local, 0); | ||
295 | 306 | ||
296 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
297 | if (!was_hw_scan) { | 307 | if (!was_hw_scan) { |
308 | bool on_oper_chan2; | ||
298 | ieee80211_configure_filter(local); | 309 | ieee80211_configure_filter(local); |
299 | drv_sw_scan_complete(local); | 310 | drv_sw_scan_complete(local); |
300 | ieee80211_offchannel_return(local, true); | 311 | on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); |
312 | /* We should always be on-channel at this point. */ | ||
313 | WARN_ON(!on_oper_chan2); | ||
314 | if (on_oper_chan2 && (on_oper_chan != on_oper_chan2)) | ||
315 | enable_beacons = true; | ||
316 | |||
317 | ieee80211_offchannel_return(local, enable_beacons, true); | ||
301 | } | 318 | } |
302 | 319 | ||
303 | mutex_lock(&local->mtx); | ||
304 | ieee80211_recalc_idle(local); | 320 | ieee80211_recalc_idle(local); |
305 | mutex_unlock(&local->mtx); | ||
306 | 321 | ||
307 | ieee80211_mlme_notify_scan_completed(local); | 322 | ieee80211_mlme_notify_scan_completed(local); |
308 | ieee80211_ibss_notify_scan_completed(local); | 323 | ieee80211_ibss_notify_scan_completed(local); |
@@ -340,16 +355,21 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
340 | */ | 355 | */ |
341 | drv_sw_scan_start(local); | 356 | drv_sw_scan_start(local); |
342 | 357 | ||
343 | ieee80211_offchannel_stop_beaconing(local); | ||
344 | |||
345 | local->leave_oper_channel_time = 0; | 358 | local->leave_oper_channel_time = 0; |
346 | local->next_scan_state = SCAN_DECISION; | 359 | local->next_scan_state = SCAN_DECISION; |
347 | local->scan_channel_idx = 0; | 360 | local->scan_channel_idx = 0; |
348 | 361 | ||
349 | drv_flush(local, false); | 362 | /* We always want to use off-channel PS, even if we |
363 | * are not really leaving oper-channel. Don't | ||
364 | * tell the AP though, as long as we are on-channel. | ||
365 | */ | ||
366 | ieee80211_offchannel_enable_all_ps(local, false); | ||
350 | 367 | ||
351 | ieee80211_configure_filter(local); | 368 | ieee80211_configure_filter(local); |
352 | 369 | ||
370 | /* We need to set power level at maximum rate for scanning. */ | ||
371 | ieee80211_hw_config(local, 0); | ||
372 | |||
353 | ieee80211_queue_delayed_work(&local->hw, | 373 | ieee80211_queue_delayed_work(&local->hw, |
354 | &local->scan_work, | 374 | &local->scan_work, |
355 | IEEE80211_CHANNEL_TIME); | 375 | IEEE80211_CHANNEL_TIME); |
@@ -486,7 +506,20 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
486 | } | 506 | } |
487 | mutex_unlock(&local->iflist_mtx); | 507 | mutex_unlock(&local->iflist_mtx); |
488 | 508 | ||
489 | if (local->scan_channel) { | 509 | next_chan = local->scan_req->channels[local->scan_channel_idx]; |
510 | |||
511 | if (ieee80211_cfg_on_oper_channel(local)) { | ||
512 | /* We're currently on operating channel. */ | ||
513 | if (next_chan == local->oper_channel) | ||
514 | /* We don't need to move off of operating channel. */ | ||
515 | local->next_scan_state = SCAN_SET_CHANNEL; | ||
516 | else | ||
517 | /* | ||
518 | * We do need to leave operating channel, as next | ||
519 | * scan is somewhere else. | ||
520 | */ | ||
521 | local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; | ||
522 | } else { | ||
490 | /* | 523 | /* |
491 | * we're currently scanning a different channel, let's | 524 | * we're currently scanning a different channel, let's |
492 | * see if we can scan another channel without interfering | 525 | * see if we can scan another channel without interfering |
@@ -502,7 +535,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
502 | * | 535 | * |
503 | * Otherwise switch back to the operating channel. | 536 | * Otherwise switch back to the operating channel. |
504 | */ | 537 | */ |
505 | next_chan = local->scan_req->channels[local->scan_channel_idx]; | ||
506 | 538 | ||
507 | bad_latency = time_after(jiffies + | 539 | bad_latency = time_after(jiffies + |
508 | ieee80211_scan_get_channel_time(next_chan), | 540 | ieee80211_scan_get_channel_time(next_chan), |
@@ -520,12 +552,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
520 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; | 552 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; |
521 | else | 553 | else |
522 | local->next_scan_state = SCAN_SET_CHANNEL; | 554 | local->next_scan_state = SCAN_SET_CHANNEL; |
523 | } else { | ||
524 | /* | ||
525 | * we're on the operating channel currently, let's | ||
526 | * leave that channel now to scan another one | ||
527 | */ | ||
528 | local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; | ||
529 | } | 555 | } |
530 | 556 | ||
531 | *next_delay = 0; | 557 | *next_delay = 0; |
@@ -534,9 +560,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
534 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | 560 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, |
535 | unsigned long *next_delay) | 561 | unsigned long *next_delay) |
536 | { | 562 | { |
537 | ieee80211_offchannel_stop_station(local); | 563 | /* PS will already be in off-channel mode, |
538 | 564 | * we do that once at the beginning of scanning. | |
539 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); | 565 | */ |
566 | ieee80211_offchannel_stop_vifs(local, false); | ||
540 | 567 | ||
541 | /* | 568 | /* |
542 | * What if the nullfunc frames didn't arrive? | 569 | * What if the nullfunc frames didn't arrive? |
@@ -559,15 +586,15 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca | |||
559 | { | 586 | { |
560 | /* switch back to the operating channel */ | 587 | /* switch back to the operating channel */ |
561 | local->scan_channel = NULL; | 588 | local->scan_channel = NULL; |
562 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 589 | if (!ieee80211_cfg_on_oper_channel(local)) |
590 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
563 | 591 | ||
564 | /* | 592 | /* |
565 | * Only re-enable station mode interface now; beaconing will be | 593 | * Re-enable vifs and beaconing. Leave PS |
566 | * re-enabled once the full scan has been completed. | 594 | * in off-channel state..will put that back |
595 | * on-channel at the end of scanning. | ||
567 | */ | 596 | */ |
568 | ieee80211_offchannel_return(local, false); | 597 | ieee80211_offchannel_return(local, true, false); |
569 | |||
570 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); | ||
571 | 598 | ||
572 | *next_delay = HZ / 5; | 599 | *next_delay = HZ / 5; |
573 | local->next_scan_state = SCAN_DECISION; | 600 | local->next_scan_state = SCAN_DECISION; |
@@ -583,8 +610,11 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, | |||
583 | chan = local->scan_req->channels[local->scan_channel_idx]; | 610 | chan = local->scan_req->channels[local->scan_channel_idx]; |
584 | 611 | ||
585 | local->scan_channel = chan; | 612 | local->scan_channel = chan; |
586 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) | 613 | |
587 | skip = 1; | 614 | /* Only call hw-config if we really need to change channels. */ |
615 | if (chan != local->hw.conf.channel) | ||
616 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) | ||
617 | skip = 1; | ||
588 | 618 | ||
589 | /* advance state machine to next channel/band */ | 619 | /* advance state machine to next channel/band */ |
590 | local->scan_channel_idx++; | 620 | local->scan_channel_idx++; |
@@ -642,12 +672,14 @@ void ieee80211_scan_work(struct work_struct *work) | |||
642 | { | 672 | { |
643 | struct ieee80211_local *local = | 673 | struct ieee80211_local *local = |
644 | container_of(work, struct ieee80211_local, scan_work.work); | 674 | container_of(work, struct ieee80211_local, scan_work.work); |
645 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | 675 | struct ieee80211_sub_if_data *sdata; |
646 | unsigned long next_delay = 0; | 676 | unsigned long next_delay = 0; |
647 | bool aborted, hw_scan, finish; | 677 | bool aborted, hw_scan; |
648 | 678 | ||
649 | mutex_lock(&local->mtx); | 679 | mutex_lock(&local->mtx); |
650 | 680 | ||
681 | sdata = local->scan_sdata; | ||
682 | |||
651 | if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { | 683 | if (test_and_clear_bit(SCAN_COMPLETED, &local->scanning)) { |
652 | aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); | 684 | aborted = test_and_clear_bit(SCAN_ABORTED, &local->scanning); |
653 | goto out_complete; | 685 | goto out_complete; |
@@ -711,17 +743,11 @@ void ieee80211_scan_work(struct work_struct *work) | |||
711 | } while (next_delay == 0); | 743 | } while (next_delay == 0); |
712 | 744 | ||
713 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); | 745 | ieee80211_queue_delayed_work(&local->hw, &local->scan_work, next_delay); |
714 | mutex_unlock(&local->mtx); | 746 | goto out; |
715 | return; | ||
716 | 747 | ||
717 | out_complete: | 748 | out_complete: |
718 | hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); | 749 | hw_scan = test_bit(SCAN_HW_SCANNING, &local->scanning); |
719 | finish = __ieee80211_scan_completed(&local->hw, aborted, hw_scan); | 750 | __ieee80211_scan_completed(&local->hw, aborted, hw_scan); |
720 | mutex_unlock(&local->mtx); | ||
721 | if (finish) | ||
722 | __ieee80211_scan_completed_finish(&local->hw, hw_scan); | ||
723 | return; | ||
724 | |||
725 | out: | 751 | out: |
726 | mutex_unlock(&local->mtx); | 752 | mutex_unlock(&local->mtx); |
727 | } | 753 | } |
@@ -791,7 +817,6 @@ int ieee80211_request_internal_scan(struct ieee80211_sub_if_data *sdata, | |||
791 | void ieee80211_scan_cancel(struct ieee80211_local *local) | 817 | void ieee80211_scan_cancel(struct ieee80211_local *local) |
792 | { | 818 | { |
793 | bool abortscan; | 819 | bool abortscan; |
794 | bool finish = false; | ||
795 | 820 | ||
796 | /* | 821 | /* |
797 | * We are only canceling software scan, or deferred scan that was not | 822 | * We are only canceling software scan, or deferred scan that was not |
@@ -811,14 +836,17 @@ void ieee80211_scan_cancel(struct ieee80211_local *local) | |||
811 | 836 | ||
812 | mutex_lock(&local->mtx); | 837 | mutex_lock(&local->mtx); |
813 | abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); | 838 | abortscan = local->scan_req && !test_bit(SCAN_HW_SCANNING, &local->scanning); |
814 | if (abortscan) | ||
815 | finish = __ieee80211_scan_completed(&local->hw, true, false); | ||
816 | mutex_unlock(&local->mtx); | ||
817 | |||
818 | if (abortscan) { | 839 | if (abortscan) { |
819 | /* The scan is canceled, but stop work from being pending */ | 840 | /* |
820 | cancel_delayed_work_sync(&local->scan_work); | 841 | * The scan is canceled, but stop work from being pending. |
842 | * | ||
843 | * If the work is currently running, it must be blocked on | ||
844 | * the mutex, but we'll set scan_sdata = NULL and it'll | ||
845 | * simply exit once it acquires the mutex. | ||
846 | */ | ||
847 | cancel_delayed_work(&local->scan_work); | ||
848 | /* and clean up */ | ||
849 | __ieee80211_scan_completed(&local->hw, true, false); | ||
821 | } | 850 | } |
822 | if (finish) | 851 | mutex_unlock(&local->mtx); |
823 | __ieee80211_scan_completed_finish(&local->hw, false); | ||
824 | } | 852 | } |