diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 88 |
1 files changed, 63 insertions, 25 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 1ef73be76b25..0ea6adae3e06 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -212,6 +212,14 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
212 | if (bss) | 212 | if (bss) |
213 | ieee80211_rx_bss_put(sdata->local, bss); | 213 | ieee80211_rx_bss_put(sdata->local, bss); |
214 | 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 | |||
215 | dev_kfree_skb(skb); | 223 | dev_kfree_skb(skb); |
216 | return RX_QUEUED; | 224 | return RX_QUEUED; |
217 | } | 225 | } |
@@ -293,15 +301,31 @@ static void __ieee80211_scan_completed_finish(struct ieee80211_hw *hw, | |||
293 | bool was_hw_scan) | 301 | bool was_hw_scan) |
294 | { | 302 | { |
295 | struct ieee80211_local *local = hw_to_local(hw); | 303 | struct ieee80211_local *local = hw_to_local(hw); |
304 | bool on_oper_chan; | ||
305 | bool enable_beacons = false; | ||
306 | |||
307 | mutex_lock(&local->mtx); | ||
308 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); | ||
309 | |||
310 | if (was_hw_scan || !on_oper_chan) { | ||
311 | if (WARN_ON(local->scan_channel)) | ||
312 | local->scan_channel = NULL; | ||
313 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
314 | } | ||
296 | 315 | ||
297 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
298 | if (!was_hw_scan) { | 316 | if (!was_hw_scan) { |
317 | bool on_oper_chan2; | ||
299 | ieee80211_configure_filter(local); | 318 | ieee80211_configure_filter(local); |
300 | drv_sw_scan_complete(local); | 319 | drv_sw_scan_complete(local); |
301 | ieee80211_offchannel_return(local, true); | 320 | on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); |
321 | /* We should always be on-channel at this point. */ | ||
322 | WARN_ON(!on_oper_chan2); | ||
323 | if (on_oper_chan2 && (on_oper_chan != on_oper_chan2)) | ||
324 | enable_beacons = true; | ||
325 | |||
326 | ieee80211_offchannel_return(local, enable_beacons, true); | ||
302 | } | 327 | } |
303 | 328 | ||
304 | mutex_lock(&local->mtx); | ||
305 | ieee80211_recalc_idle(local); | 329 | ieee80211_recalc_idle(local); |
306 | mutex_unlock(&local->mtx); | 330 | mutex_unlock(&local->mtx); |
307 | 331 | ||
@@ -341,13 +365,15 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
341 | */ | 365 | */ |
342 | drv_sw_scan_start(local); | 366 | drv_sw_scan_start(local); |
343 | 367 | ||
344 | ieee80211_offchannel_stop_beaconing(local); | ||
345 | |||
346 | local->leave_oper_channel_time = 0; | 368 | local->leave_oper_channel_time = 0; |
347 | local->next_scan_state = SCAN_DECISION; | 369 | local->next_scan_state = SCAN_DECISION; |
348 | local->scan_channel_idx = 0; | 370 | local->scan_channel_idx = 0; |
349 | 371 | ||
350 | drv_flush(local, false); | 372 | /* We always want to use off-channel PS, even if we |
373 | * are not really leaving oper-channel. Don't | ||
374 | * tell the AP though, as long as we are on-channel. | ||
375 | */ | ||
376 | ieee80211_offchannel_enable_all_ps(local, false); | ||
351 | 377 | ||
352 | ieee80211_configure_filter(local); | 378 | ieee80211_configure_filter(local); |
353 | 379 | ||
@@ -487,7 +513,21 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
487 | } | 513 | } |
488 | mutex_unlock(&local->iflist_mtx); | 514 | mutex_unlock(&local->iflist_mtx); |
489 | 515 | ||
490 | if (local->scan_channel) { | 516 | next_chan = local->scan_req->channels[local->scan_channel_idx]; |
517 | |||
518 | if (ieee80211_cfg_on_oper_channel(local)) { | ||
519 | /* We're currently on operating channel. */ | ||
520 | if ((next_chan == local->oper_channel) && | ||
521 | (local->_oper_channel_type == NL80211_CHAN_NO_HT)) | ||
522 | /* We don't need to move off of operating channel. */ | ||
523 | local->next_scan_state = SCAN_SET_CHANNEL; | ||
524 | else | ||
525 | /* | ||
526 | * We do need to leave operating channel, as next | ||
527 | * scan is somewhere else. | ||
528 | */ | ||
529 | local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; | ||
530 | } else { | ||
491 | /* | 531 | /* |
492 | * we're currently scanning a different channel, let's | 532 | * we're currently scanning a different channel, let's |
493 | * see if we can scan another channel without interfering | 533 | * see if we can scan another channel without interfering |
@@ -503,7 +543,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
503 | * | 543 | * |
504 | * Otherwise switch back to the operating channel. | 544 | * Otherwise switch back to the operating channel. |
505 | */ | 545 | */ |
506 | next_chan = local->scan_req->channels[local->scan_channel_idx]; | ||
507 | 546 | ||
508 | bad_latency = time_after(jiffies + | 547 | bad_latency = time_after(jiffies + |
509 | ieee80211_scan_get_channel_time(next_chan), | 548 | ieee80211_scan_get_channel_time(next_chan), |
@@ -521,12 +560,6 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
521 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; | 560 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; |
522 | else | 561 | else |
523 | local->next_scan_state = SCAN_SET_CHANNEL; | 562 | local->next_scan_state = SCAN_SET_CHANNEL; |
524 | } else { | ||
525 | /* | ||
526 | * we're on the operating channel currently, let's | ||
527 | * leave that channel now to scan another one | ||
528 | */ | ||
529 | local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; | ||
530 | } | 563 | } |
531 | 564 | ||
532 | *next_delay = 0; | 565 | *next_delay = 0; |
@@ -535,9 +568,10 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
535 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | 568 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, |
536 | unsigned long *next_delay) | 569 | unsigned long *next_delay) |
537 | { | 570 | { |
538 | ieee80211_offchannel_stop_station(local); | 571 | /* PS will already be in off-channel mode, |
539 | 572 | * we do that once at the beginning of scanning. | |
540 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); | 573 | */ |
574 | ieee80211_offchannel_stop_vifs(local, false); | ||
541 | 575 | ||
542 | /* | 576 | /* |
543 | * What if the nullfunc frames didn't arrive? | 577 | * What if the nullfunc frames didn't arrive? |
@@ -560,15 +594,15 @@ static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *loca | |||
560 | { | 594 | { |
561 | /* switch back to the operating channel */ | 595 | /* switch back to the operating channel */ |
562 | local->scan_channel = NULL; | 596 | local->scan_channel = NULL; |
563 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 597 | if (!ieee80211_cfg_on_oper_channel(local)) |
598 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
564 | 599 | ||
565 | /* | 600 | /* |
566 | * Only re-enable station mode interface now; beaconing will be | 601 | * Re-enable vifs and beaconing. Leave PS |
567 | * re-enabled once the full scan has been completed. | 602 | * in off-channel state..will put that back |
603 | * on-channel at the end of scanning. | ||
568 | */ | 604 | */ |
569 | ieee80211_offchannel_return(local, false); | 605 | ieee80211_offchannel_return(local, true, false); |
570 | |||
571 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); | ||
572 | 606 | ||
573 | *next_delay = HZ / 5; | 607 | *next_delay = HZ / 5; |
574 | local->next_scan_state = SCAN_DECISION; | 608 | local->next_scan_state = SCAN_DECISION; |
@@ -584,8 +618,12 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, | |||
584 | chan = local->scan_req->channels[local->scan_channel_idx]; | 618 | chan = local->scan_req->channels[local->scan_channel_idx]; |
585 | 619 | ||
586 | local->scan_channel = chan; | 620 | local->scan_channel = chan; |
587 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) | 621 | |
588 | skip = 1; | 622 | /* Only call hw-config if we really need to change channels. */ |
623 | if ((chan != local->hw.conf.channel) || | ||
624 | (local->hw.conf.channel_type != NL80211_CHAN_NO_HT)) | ||
625 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) | ||
626 | skip = 1; | ||
589 | 627 | ||
590 | /* advance state machine to next channel/band */ | 628 | /* advance state machine to next channel/band */ |
591 | local->scan_channel_idx++; | 629 | local->scan_channel_idx++; |