diff options
Diffstat (limited to 'net/mac80211/scan.c')
-rw-r--r-- | net/mac80211/scan.c | 199 |
1 files changed, 77 insertions, 122 deletions
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 105436dbb90d..81863031e0a3 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -213,12 +213,7 @@ ieee80211_scan_rx(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb) | |||
213 | if (bss) | 213 | if (bss) |
214 | ieee80211_rx_bss_put(sdata->local, bss); | 214 | ieee80211_rx_bss_put(sdata->local, bss); |
215 | 215 | ||
216 | /* If we are on-operating-channel, and this packet is for the | 216 | if (channel == sdata->local->oper_channel) |
217 | * current channel, pass the pkt on up the stack so that | ||
218 | * the rest of the stack can make use of it. | ||
219 | */ | ||
220 | if (ieee80211_cfg_on_oper_channel(sdata->local) | ||
221 | && (channel == sdata->local->oper_channel)) | ||
222 | return RX_CONTINUE; | 217 | return RX_CONTINUE; |
223 | 218 | ||
224 | dev_kfree_skb(skb); | 219 | dev_kfree_skb(skb); |
@@ -264,8 +259,6 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
264 | bool was_hw_scan) | 259 | bool was_hw_scan) |
265 | { | 260 | { |
266 | struct ieee80211_local *local = hw_to_local(hw); | 261 | struct ieee80211_local *local = hw_to_local(hw); |
267 | bool on_oper_chan; | ||
268 | bool enable_beacons = false; | ||
269 | 262 | ||
270 | lockdep_assert_held(&local->mtx); | 263 | lockdep_assert_held(&local->mtx); |
271 | 264 | ||
@@ -298,25 +291,13 @@ static void __ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted, | |||
298 | local->scanning = 0; | 291 | local->scanning = 0; |
299 | local->scan_channel = NULL; | 292 | local->scan_channel = NULL; |
300 | 293 | ||
301 | on_oper_chan = ieee80211_cfg_on_oper_channel(local); | 294 | /* Set power back to normal operating levels. */ |
302 | 295 | ieee80211_hw_config(local, 0); | |
303 | if (was_hw_scan || !on_oper_chan) | ||
304 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
305 | else | ||
306 | /* Set power back to normal operating levels. */ | ||
307 | ieee80211_hw_config(local, 0); | ||
308 | 296 | ||
309 | if (!was_hw_scan) { | 297 | if (!was_hw_scan) { |
310 | bool on_oper_chan2; | ||
311 | ieee80211_configure_filter(local); | 298 | ieee80211_configure_filter(local); |
312 | drv_sw_scan_complete(local); | 299 | drv_sw_scan_complete(local); |
313 | on_oper_chan2 = ieee80211_cfg_on_oper_channel(local); | 300 | ieee80211_offchannel_return(local, true, true); |
314 | /* We should always be on-channel at this point. */ | ||
315 | WARN_ON(!on_oper_chan2); | ||
316 | if (on_oper_chan2 && (on_oper_chan != on_oper_chan2)) | ||
317 | enable_beacons = true; | ||
318 | |||
319 | ieee80211_offchannel_return(local, enable_beacons, true); | ||
320 | } | 301 | } |
321 | 302 | ||
322 | ieee80211_recalc_idle(local); | 303 | ieee80211_recalc_idle(local); |
@@ -361,11 +342,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
361 | local->next_scan_state = SCAN_DECISION; | 342 | local->next_scan_state = SCAN_DECISION; |
362 | local->scan_channel_idx = 0; | 343 | local->scan_channel_idx = 0; |
363 | 344 | ||
364 | /* We always want to use off-channel PS, even if we | 345 | ieee80211_offchannel_stop_vifs(local, true); |
365 | * are not really leaving oper-channel. Don't | ||
366 | * tell the AP though, as long as we are on-channel. | ||
367 | */ | ||
368 | ieee80211_offchannel_enable_all_ps(local, false); | ||
369 | 346 | ||
370 | ieee80211_configure_filter(local); | 347 | ieee80211_configure_filter(local); |
371 | 348 | ||
@@ -373,8 +350,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
373 | ieee80211_hw_config(local, 0); | 350 | ieee80211_hw_config(local, 0); |
374 | 351 | ||
375 | ieee80211_queue_delayed_work(&local->hw, | 352 | ieee80211_queue_delayed_work(&local->hw, |
376 | &local->scan_work, | 353 | &local->scan_work, 0); |
377 | IEEE80211_CHANNEL_TIME); | ||
378 | 354 | ||
379 | return 0; | 355 | return 0; |
380 | } | 356 | } |
@@ -510,96 +486,39 @@ static void ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
510 | 486 | ||
511 | next_chan = local->scan_req->channels[local->scan_channel_idx]; | 487 | next_chan = local->scan_req->channels[local->scan_channel_idx]; |
512 | 488 | ||
513 | if (ieee80211_cfg_on_oper_channel(local)) { | ||
514 | /* We're currently on operating channel. */ | ||
515 | if (next_chan == local->oper_channel) | ||
516 | /* We don't need to move off of operating channel. */ | ||
517 | local->next_scan_state = SCAN_SET_CHANNEL; | ||
518 | else | ||
519 | /* | ||
520 | * We do need to leave operating channel, as next | ||
521 | * scan is somewhere else. | ||
522 | */ | ||
523 | local->next_scan_state = SCAN_LEAVE_OPER_CHANNEL; | ||
524 | } else { | ||
525 | /* | ||
526 | * we're currently scanning a different channel, let's | ||
527 | * see if we can scan another channel without interfering | ||
528 | * with the current traffic situation. | ||
529 | * | ||
530 | * Since we don't know if the AP has pending frames for us | ||
531 | * we can only check for our tx queues and use the current | ||
532 | * pm_qos requirements for rx. Hence, if no tx traffic occurs | ||
533 | * at all we will scan as many channels in a row as the pm_qos | ||
534 | * latency allows us to. Additionally we also check for the | ||
535 | * currently negotiated listen interval to prevent losing | ||
536 | * frames unnecessarily. | ||
537 | * | ||
538 | * Otherwise switch back to the operating channel. | ||
539 | */ | ||
540 | |||
541 | bad_latency = time_after(jiffies + | ||
542 | ieee80211_scan_get_channel_time(next_chan), | ||
543 | local->leave_oper_channel_time + | ||
544 | usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); | ||
545 | |||
546 | listen_int_exceeded = time_after(jiffies + | ||
547 | ieee80211_scan_get_channel_time(next_chan), | ||
548 | local->leave_oper_channel_time + | ||
549 | usecs_to_jiffies(min_beacon_int * 1024) * | ||
550 | local->hw.conf.listen_interval); | ||
551 | |||
552 | if (associated && ( !tx_empty || bad_latency || | ||
553 | listen_int_exceeded)) | ||
554 | local->next_scan_state = SCAN_ENTER_OPER_CHANNEL; | ||
555 | else | ||
556 | local->next_scan_state = SCAN_SET_CHANNEL; | ||
557 | } | ||
558 | |||
559 | *next_delay = 0; | ||
560 | } | ||
561 | |||
562 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | ||
563 | unsigned long *next_delay) | ||
564 | { | ||
565 | /* PS will already be in off-channel mode, | ||
566 | * we do that once at the beginning of scanning. | ||
567 | */ | ||
568 | ieee80211_offchannel_stop_vifs(local, false); | ||
569 | |||
570 | /* | 489 | /* |
571 | * What if the nullfunc frames didn't arrive? | 490 | * we're currently scanning a different channel, let's |
491 | * see if we can scan another channel without interfering | ||
492 | * with the current traffic situation. | ||
493 | * | ||
494 | * Since we don't know if the AP has pending frames for us | ||
495 | * we can only check for our tx queues and use the current | ||
496 | * pm_qos requirements for rx. Hence, if no tx traffic occurs | ||
497 | * at all we will scan as many channels in a row as the pm_qos | ||
498 | * latency allows us to. Additionally we also check for the | ||
499 | * currently negotiated listen interval to prevent losing | ||
500 | * frames unnecessarily. | ||
501 | * | ||
502 | * Otherwise switch back to the operating channel. | ||
572 | */ | 503 | */ |
573 | drv_flush(local, false); | ||
574 | if (local->ops->flush) | ||
575 | *next_delay = 0; | ||
576 | else | ||
577 | *next_delay = HZ / 10; | ||
578 | 504 | ||
579 | /* remember when we left the operating channel */ | 505 | bad_latency = time_after(jiffies + |
580 | local->leave_oper_channel_time = jiffies; | 506 | ieee80211_scan_get_channel_time(next_chan), |
507 | local->leave_oper_channel_time + | ||
508 | usecs_to_jiffies(pm_qos_request(PM_QOS_NETWORK_LATENCY))); | ||
581 | 509 | ||
582 | /* advance to the next channel to be scanned */ | 510 | listen_int_exceeded = time_after(jiffies + |
583 | local->next_scan_state = SCAN_SET_CHANNEL; | 511 | ieee80211_scan_get_channel_time(next_chan), |
584 | } | 512 | local->leave_oper_channel_time + |
585 | 513 | usecs_to_jiffies(min_beacon_int * 1024) * | |
586 | static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, | 514 | local->hw.conf.listen_interval); |
587 | unsigned long *next_delay) | ||
588 | { | ||
589 | /* switch back to the operating channel */ | ||
590 | local->scan_channel = NULL; | ||
591 | if (!ieee80211_cfg_on_oper_channel(local)) | ||
592 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
593 | 515 | ||
594 | /* | 516 | if (associated && (!tx_empty || bad_latency || listen_int_exceeded)) |
595 | * Re-enable vifs and beaconing. Leave PS | 517 | local->next_scan_state = SCAN_SUSPEND; |
596 | * in off-channel state..will put that back | 518 | else |
597 | * on-channel at the end of scanning. | 519 | local->next_scan_state = SCAN_SET_CHANNEL; |
598 | */ | ||
599 | ieee80211_offchannel_return(local, true, false); | ||
600 | 520 | ||
601 | *next_delay = HZ / 5; | 521 | *next_delay = 0; |
602 | local->next_scan_state = SCAN_DECISION; | ||
603 | } | 522 | } |
604 | 523 | ||
605 | static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, | 524 | static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, |
@@ -613,10 +532,8 @@ static void ieee80211_scan_state_set_channel(struct ieee80211_local *local, | |||
613 | 532 | ||
614 | local->scan_channel = chan; | 533 | local->scan_channel = chan; |
615 | 534 | ||
616 | /* Only call hw-config if we really need to change channels. */ | 535 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) |
617 | if (chan != local->hw.conf.channel) | 536 | skip = 1; |
618 | if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL)) | ||
619 | skip = 1; | ||
620 | 537 | ||
621 | /* advance state machine to next channel/band */ | 538 | /* advance state machine to next channel/band */ |
622 | local->scan_channel_idx++; | 539 | local->scan_channel_idx++; |
@@ -673,6 +590,44 @@ static void ieee80211_scan_state_send_probe(struct ieee80211_local *local, | |||
673 | local->next_scan_state = SCAN_DECISION; | 590 | local->next_scan_state = SCAN_DECISION; |
674 | } | 591 | } |
675 | 592 | ||
593 | static void ieee80211_scan_state_suspend(struct ieee80211_local *local, | ||
594 | unsigned long *next_delay) | ||
595 | { | ||
596 | /* switch back to the operating channel */ | ||
597 | local->scan_channel = NULL; | ||
598 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | ||
599 | |||
600 | /* | ||
601 | * Re-enable vifs and beaconing. Leave PS | ||
602 | * in off-channel state..will put that back | ||
603 | * on-channel at the end of scanning. | ||
604 | */ | ||
605 | ieee80211_offchannel_return(local, true, false); | ||
606 | |||
607 | *next_delay = HZ / 5; | ||
608 | /* afterwards, resume scan & go to next channel */ | ||
609 | local->next_scan_state = SCAN_RESUME; | ||
610 | } | ||
611 | |||
612 | static void ieee80211_scan_state_resume(struct ieee80211_local *local, | ||
613 | unsigned long *next_delay) | ||
614 | { | ||
615 | /* PS already is in off-channel mode */ | ||
616 | ieee80211_offchannel_stop_vifs(local, false); | ||
617 | |||
618 | if (local->ops->flush) { | ||
619 | drv_flush(local, false); | ||
620 | *next_delay = 0; | ||
621 | } else | ||
622 | *next_delay = HZ / 10; | ||
623 | |||
624 | /* remember when we left the operating channel */ | ||
625 | local->leave_oper_channel_time = jiffies; | ||
626 | |||
627 | /* advance to the next channel to be scanned */ | ||
628 | local->next_scan_state = SCAN_DECISION; | ||
629 | } | ||
630 | |||
676 | void ieee80211_scan_work(struct work_struct *work) | 631 | void ieee80211_scan_work(struct work_struct *work) |
677 | { | 632 | { |
678 | struct ieee80211_local *local = | 633 | struct ieee80211_local *local = |
@@ -743,11 +698,11 @@ void ieee80211_scan_work(struct work_struct *work) | |||
743 | case SCAN_SEND_PROBE: | 698 | case SCAN_SEND_PROBE: |
744 | ieee80211_scan_state_send_probe(local, &next_delay); | 699 | ieee80211_scan_state_send_probe(local, &next_delay); |
745 | break; | 700 | break; |
746 | case SCAN_LEAVE_OPER_CHANNEL: | 701 | case SCAN_SUSPEND: |
747 | ieee80211_scan_state_leave_oper_channel(local, &next_delay); | 702 | ieee80211_scan_state_suspend(local, &next_delay); |
748 | break; | 703 | break; |
749 | case SCAN_ENTER_OPER_CHANNEL: | 704 | case SCAN_RESUME: |
750 | ieee80211_scan_state_enter_oper_channel(local, &next_delay); | 705 | ieee80211_scan_state_resume(local, &next_delay); |
751 | break; | 706 | break; |
752 | } | 707 | } |
753 | } while (next_delay == 0); | 708 | } while (next_delay == 0); |