Commit b23b025fe246f3acc2988eb6d400df34c27cb8ae

Authored by Ben Greear
Committed by John W. Linville
1 parent cb8d61de2d

mac80211: Optimize scans on current operating channel.

This should decrease un-necessary flushes, on/off channel work,
and channel changes in cases where the only scanned channel is
the current operating channel.

* Removes SCAN_OFF_CHANNEL flag, uses SDATA_STATE_OFFCHANNEL
  and is-scanning flags instead.

* Add helper method to determine if we are currently configured
  for the operating channel.

* Do no blindly go off/on channel in work.c  Instead, only call
  appropriate on/off code when we really need to change channels.
  Always enable offchannel-ps mode when starting work,
  and disable it when we are done.

* Consolidate ieee80211_offchannel_stop_station and
  ieee80211_offchannel_stop_beaconing, call it
  ieee80211_offchannel_stop_vifs instead.

* Accept non-beacon frames when scanning on operating channel.

* Scan state machine optimized to minimize on/off channel
  transitions.  Also, when going on-channel, go ahead and
  re-enable beaconing.  We're going to be there for 200ms,
  so seems like some useful beaconing could happen.
  Always enable offchannel-ps mode when starting software
  scan, and disable it when we are done.

* Grab local->mtx earlier in __ieee80211_scan_completed_finish
  so that we are protected when calling hw_config(), etc.

* Pass probe-responses up the stack if scanning on local
  channel, so that mlme can take a look.

Signed-off-by: Ben Greear <greearb@candelatech.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>

Showing 7 changed files with 214 additions and 89 deletions Side-by-side Diff

net/mac80211/ieee80211_i.h
... ... @@ -655,8 +655,6 @@
655 655 * well be on the operating channel
656 656 * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to
657 657 * determine if we are on the operating channel or not
658   - * @SCAN_OFF_CHANNEL: We're off our operating channel for scanning,
659   - * gets only set in conjunction with SCAN_SW_SCANNING
660 658 * @SCAN_COMPLETED: Set for our scan work function when the driver reported
661 659 * that the scan completed.
662 660 * @SCAN_ABORTED: Set for our scan work function when the driver reported
... ... @@ -665,7 +663,6 @@
665 663 enum {
666 664 SCAN_SW_SCANNING,
667 665 SCAN_HW_SCANNING,
668   - SCAN_OFF_CHANNEL,
669 666 SCAN_COMPLETED,
670 667 SCAN_ABORTED,
671 668 };
672 669  
... ... @@ -1148,10 +1145,14 @@
1148 1145 struct ieee80211_bss *bss);
1149 1146  
1150 1147 /* off-channel helpers */
1151   -void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local);
1152   -void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
  1148 +bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local);
  1149 +void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
  1150 + bool tell_ap);
  1151 +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
  1152 + bool offchannel_ps_enable);
1153 1153 void ieee80211_offchannel_return(struct ieee80211_local *local,
1154   - bool enable_beaconing);
  1154 + bool enable_beaconing,
  1155 + bool offchannel_ps_disable);
1155 1156 void ieee80211_hw_roc_setup(struct ieee80211_local *local);
1156 1157  
1157 1158 /* interface handling */
... ... @@ -98,6 +98,41 @@
98 98 ieee80211_configure_filter(local);
99 99 }
100 100  
  101 +/*
  102 + * Returns true if we are logically configured to be on
  103 + * the operating channel AND the hardware-conf is currently
  104 + * configured on the operating channel. Compares channel-type
  105 + * as well.
  106 + */
  107 +bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local)
  108 +{
  109 + struct ieee80211_channel *chan, *scan_chan;
  110 + enum nl80211_channel_type channel_type;
  111 +
  112 + /* This logic needs to match logic in ieee80211_hw_config */
  113 + if (local->scan_channel) {
  114 + chan = local->scan_channel;
  115 + channel_type = NL80211_CHAN_NO_HT;
  116 + } else if (local->tmp_channel) {
  117 + chan = scan_chan = local->tmp_channel;
  118 + channel_type = local->tmp_channel_type;
  119 + } else {
  120 + chan = local->oper_channel;
  121 + channel_type = local->_oper_channel_type;
  122 + }
  123 +
  124 + if (chan != local->oper_channel ||
  125 + channel_type != local->_oper_channel_type)
  126 + return false;
  127 +
  128 + /* Check current hardware-config against oper_channel. */
  129 + if ((local->oper_channel != local->hw.conf.channel) ||
  130 + (local->_oper_channel_type != local->hw.conf.channel_type))
  131 + return false;
  132 +
  133 + return true;
  134 +}
  135 +
101 136 int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
102 137 {
103 138 struct ieee80211_channel *chan, *scan_chan;
104 139  
105 140  
106 141  
107 142  
... ... @@ -110,21 +145,27 @@
110 145  
111 146 scan_chan = local->scan_channel;
112 147  
  148 + /* If this off-channel logic ever changes, ieee80211_on_oper_channel
  149 + * may need to change as well.
  150 + */
113 151 offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
114 152 if (scan_chan) {
115 153 chan = scan_chan;
116 154 channel_type = NL80211_CHAN_NO_HT;
117   - local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
118   - } else if (local->tmp_channel &&
119   - local->oper_channel != local->tmp_channel) {
  155 + } else if (local->tmp_channel) {
120 156 chan = scan_chan = local->tmp_channel;
121 157 channel_type = local->tmp_channel_type;
122   - local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
123 158 } else {
124 159 chan = local->oper_channel;
125 160 channel_type = local->_oper_channel_type;
126   - local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
127 161 }
  162 +
  163 + if (chan != local->oper_channel ||
  164 + channel_type != local->_oper_channel_type)
  165 + local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
  166 + else
  167 + local->hw.conf.flags &= ~IEEE80211_CONF_OFFCHANNEL;
  168 +
128 169 offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
129 170  
130 171 if (offchannel_flag || chan != local->hw.conf.channel ||
... ... @@ -231,7 +272,7 @@
231 272  
232 273 if (changed & BSS_CHANGED_BEACON_ENABLED) {
233 274 if (local->quiescing || !ieee80211_sdata_running(sdata) ||
234   - test_bit(SCAN_SW_SCANNING, &local->scanning)) {
  275 + test_bit(SDATA_STATE_OFFCHANNEL, &sdata->state)) {
235 276 sdata->vif.bss_conf.enable_beacon = false;
236 277 } else {
237 278 /*
net/mac80211/offchannel.c
... ... @@ -17,10 +17,14 @@
17 17 #include "driver-trace.h"
18 18  
19 19 /*
20   - * inform AP that we will go to sleep so that it will buffer the frames
21   - * while we scan
  20 + * Tell our hardware to disable PS.
  21 + * Optionally inform AP that we will go to sleep so that it will buffer
  22 + * the frames while we are doing off-channel work. This is optional
  23 + * because we *may* be doing work on-operating channel, and want our
  24 + * hardware unconditionally awake, but still let the AP send us normal frames.
22 25 */
23   -static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
  26 +static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata,
  27 + bool tell_ap)
24 28 {
25 29 struct ieee80211_local *local = sdata->local;
26 30 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
... ... @@ -41,8 +45,8 @@
41 45 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
42 46 }
43 47  
44   - if (!(local->offchannel_ps_enabled) ||
45   - !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
  48 + if (tell_ap && (!local->offchannel_ps_enabled ||
  49 + !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)))
46 50 /*
47 51 * If power save was enabled, no need to send a nullfunc
48 52 * frame because AP knows that we are sleeping. But if the
... ... @@ -77,6 +81,9 @@
77 81 * we are sleeping, let's just enable power save mode in
78 82 * hardware.
79 83 */
  84 + /* TODO: Only set hardware if CONF_PS changed?
  85 + * TODO: Should we set offchannel_ps_enabled to false?
  86 + */
80 87 local->hw.conf.flags |= IEEE80211_CONF_PS;
81 88 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
82 89 } else if (local->hw.conf.dynamic_ps_timeout > 0) {
83 90  
84 91  
85 92  
86 93  
87 94  
88 95  
89 96  
90 97  
... ... @@ -95,63 +102,61 @@
95 102 ieee80211_sta_reset_conn_monitor(sdata);
96 103 }
97 104  
98   -void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
  105 +void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
  106 + bool offchannel_ps_enable)
99 107 {
100 108 struct ieee80211_sub_if_data *sdata;
101 109  
  110 + /*
  111 + * notify the AP about us leaving the channel and stop all
  112 + * STA interfaces.
  113 + */
102 114 mutex_lock(&local->iflist_mtx);
103 115 list_for_each_entry(sdata, &local->interfaces, list) {
104 116 if (!ieee80211_sdata_running(sdata))
105 117 continue;
106 118  
107   - /* disable beaconing */
  119 + if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
  120 + set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
  121 +
  122 + /* Check to see if we should disable beaconing. */
108 123 if (sdata->vif.type == NL80211_IFTYPE_AP ||
109 124 sdata->vif.type == NL80211_IFTYPE_ADHOC ||
110 125 sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
111 126 ieee80211_bss_info_change_notify(
112 127 sdata, BSS_CHANGED_BEACON_ENABLED);
113 128  
114   - /*
115   - * only handle non-STA interfaces here, STA interfaces
116   - * are handled in ieee80211_offchannel_stop_station(),
117   - * e.g., from the background scan state machine.
118   - *
119   - * In addition, do not stop monitor interface to allow it to be
120   - * used from user space controlled off-channel operations.
121   - */
122   - if (sdata->vif.type != NL80211_IFTYPE_STATION &&
123   - sdata->vif.type != NL80211_IFTYPE_MONITOR) {
124   - set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
  129 + if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
125 130 netif_tx_stop_all_queues(sdata->dev);
  131 + if (offchannel_ps_enable &&
  132 + (sdata->vif.type == NL80211_IFTYPE_STATION) &&
  133 + sdata->u.mgd.associated)
  134 + ieee80211_offchannel_ps_enable(sdata, true);
126 135 }
127 136 }
128 137 mutex_unlock(&local->iflist_mtx);
129 138 }
130 139  
131   -void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
  140 +void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
  141 + bool tell_ap)
132 142 {
133 143 struct ieee80211_sub_if_data *sdata;
134 144  
135   - /*
136   - * notify the AP about us leaving the channel and stop all STA interfaces
137   - */
138 145 mutex_lock(&local->iflist_mtx);
139 146 list_for_each_entry(sdata, &local->interfaces, list) {
140 147 if (!ieee80211_sdata_running(sdata))
141 148 continue;
142 149  
143   - if (sdata->vif.type == NL80211_IFTYPE_STATION) {
144   - set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
145   - netif_tx_stop_all_queues(sdata->dev);
146   - if (sdata->u.mgd.associated)
147   - ieee80211_offchannel_ps_enable(sdata);
148   - }
  150 + if (sdata->vif.type == NL80211_IFTYPE_STATION &&
  151 + sdata->u.mgd.associated)
  152 + ieee80211_offchannel_ps_enable(sdata, tell_ap);
149 153 }
150 154 mutex_unlock(&local->iflist_mtx);
151 155 }
152 156  
153 157 void ieee80211_offchannel_return(struct ieee80211_local *local,
154   - bool enable_beaconing)
  158 + bool enable_beaconing,
  159 + bool offchannel_ps_disable)
155 160 {
156 161 struct ieee80211_sub_if_data *sdata;
157 162  
... ... @@ -161,7 +166,8 @@
161 166 continue;
162 167  
163 168 /* Tell AP we're back */
164   - if (sdata->vif.type == NL80211_IFTYPE_STATION) {
  169 + if (offchannel_ps_disable &&
  170 + sdata->vif.type == NL80211_IFTYPE_STATION) {
165 171 if (sdata->u.mgd.associated)
166 172 ieee80211_offchannel_ps_disable(sdata);
167 173 }
... ... @@ -181,7 +187,7 @@
181 187 netif_tx_wake_all_queues(sdata->dev);
182 188 }
183 189  
184   - /* re-enable beaconing */
  190 + /* Check to see if we should re-enable beaconing */
185 191 if (enable_beaconing &&
186 192 (sdata->vif.type == NL80211_IFTYPE_AP ||
187 193 sdata->vif.type == NL80211_IFTYPE_ADHOC ||
... ... @@ -409,16 +409,10 @@
409 409 if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN)))
410 410 return RX_CONTINUE;
411 411  
412   - if (test_bit(SCAN_HW_SCANNING, &local->scanning))
  412 + if (test_bit(SCAN_HW_SCANNING, &local->scanning) ||
  413 + test_bit(SCAN_SW_SCANNING, &local->scanning))
413 414 return ieee80211_scan_rx(rx->sdata, skb);
414 415  
415   - if (test_bit(SCAN_SW_SCANNING, &local->scanning)) {
416   - /* drop all the other packets during a software scan anyway */
417   - if (ieee80211_scan_rx(rx->sdata, skb) != RX_QUEUED)
418   - dev_kfree_skb(skb);
419   - return RX_QUEUED;
420   - }
421   -
422 416 /* scanning finished during invoking of handlers */
423 417 I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
424 418 return RX_DROP_UNUSABLE;
... ... @@ -2793,7 +2787,7 @@
2793 2787 local->dot11ReceivedFragmentCount++;
2794 2788  
2795 2789 if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) ||
2796   - test_bit(SCAN_OFF_CHANNEL, &local->scanning)))
  2790 + test_bit(SCAN_SW_SCANNING, &local->scanning)))
2797 2791 status->rx_flags |= IEEE80211_RX_IN_SCAN;
2798 2792  
2799 2793 if (ieee80211_is_mgmt(fc))
... ... @@ -212,6 +212,14 @@
212 212 if (bss)
213 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 223 dev_kfree_skb(skb);
216 224 return RX_QUEUED;
217 225 }
218 226  
219 227  
220 228  
221 229  
... ... @@ -293,15 +301,31 @@
293 301 bool was_hw_scan)
294 302 {
295 303 struct ieee80211_local *local = hw_to_local(hw);
  304 + bool on_oper_chan;
  305 + bool enable_beacons = false;
296 306  
297   - ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
  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 + }
  315 +
298 316 if (!was_hw_scan) {
  317 + bool on_oper_chan2;
299 318 ieee80211_configure_filter(local);
300 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 329 ieee80211_recalc_idle(local);
306 330 mutex_unlock(&local->mtx);
307 331  
308 332  
... ... @@ -341,13 +365,15 @@
341 365 */
342 366 drv_sw_scan_start(local);
343 367  
344   - ieee80211_offchannel_stop_beaconing(local);
345   -
346 368 local->leave_oper_channel_time = 0;
347 369 local->next_scan_state = SCAN_DECISION;
348 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 378 ieee80211_configure_filter(local);
353 379  
... ... @@ -487,7 +513,21 @@
487 513 }
488 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 532 * we're currently scanning a different channel, let's
493 533 * see if we can scan another channel without interfering
... ... @@ -503,7 +543,6 @@
503 543 *
504 544 * Otherwise switch back to the operating channel.
505 545 */
506   - next_chan = local->scan_req->channels[local->scan_channel_idx];
507 546  
508 547 bad_latency = time_after(jiffies +
509 548 ieee80211_scan_get_channel_time(next_chan),
... ... @@ -521,12 +560,6 @@
521 560 local->next_scan_state = SCAN_ENTER_OPER_CHANNEL;
522 561 else
523 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 565 *next_delay = 0;
533 566  
... ... @@ -535,10 +568,11 @@
535 568 static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local,
536 569 unsigned long *next_delay)
537 570 {
538   - ieee80211_offchannel_stop_station(local);
  571 + /* PS will already be in off-channel mode,
  572 + * we do that once at the beginning of scanning.
  573 + */
  574 + ieee80211_offchannel_stop_vifs(local, false);
539 575  
540   - __set_bit(SCAN_OFF_CHANNEL, &local->scanning);
541   -
542 576 /*
543 577 * What if the nullfunc frames didn't arrive?
544 578 */
545 579  
546 580  
547 581  
... ... @@ -560,16 +594,16 @@
560 594 {
561 595 /* switch back to the operating channel */
562 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
567   - * re-enabled once the full scan has been completed.
  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.
568 604 */
569   - ieee80211_offchannel_return(local, false);
  605 + ieee80211_offchannel_return(local, true, false);
570 606  
571   - __clear_bit(SCAN_OFF_CHANNEL, &local->scanning);
572   -
573 607 *next_delay = HZ / 5;
574 608 local->next_scan_state = SCAN_DECISION;
575 609 }
... ... @@ -584,8 +618,12 @@
584 618 chan = local->scan_req->channels[local->scan_channel_idx];
585 619  
586 620 local->scan_channel = chan;
587   - if (ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL))
588   - skip = 1;
  621 +
  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 628 /* advance state machine to next channel/band */
591 629 local->scan_channel_idx++;
... ... @@ -257,7 +257,8 @@
257 257 if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
258 258 return TX_CONTINUE;
259 259  
260   - if (unlikely(test_bit(SCAN_OFF_CHANNEL, &tx->local->scanning)) &&
  260 + if (unlikely(test_bit(SCAN_SW_SCANNING, &tx->local->scanning)) &&
  261 + test_bit(SDATA_STATE_OFFCHANNEL, &tx->sdata->state) &&
261 262 !ieee80211_is_probe_req(hdr->frame_control) &&
262 263 !ieee80211_is_nullfunc(hdr->frame_control))
263 264 /*
... ... @@ -924,18 +924,44 @@
924 924 }
925 925  
926 926 if (!started && !local->tmp_channel) {
927   - /*
928   - * TODO: could optimize this by leaving the
929   - * station vifs in awake mode if they
930   - * happen to be on the same channel as
931   - * the requested channel
932   - */
933   - ieee80211_offchannel_stop_beaconing(local);
934   - ieee80211_offchannel_stop_station(local);
  927 + bool on_oper_chan;
  928 + bool tmp_chan_changed = false;
  929 + bool on_oper_chan2;
  930 + on_oper_chan = ieee80211_cfg_on_oper_channel(local);
  931 + if (local->tmp_channel)
  932 + if ((local->tmp_channel != wk->chan) ||
  933 + (local->tmp_channel_type != wk->chan_type))
  934 + tmp_chan_changed = true;
935 935  
936 936 local->tmp_channel = wk->chan;
937 937 local->tmp_channel_type = wk->chan_type;
938   - ieee80211_hw_config(local, 0);
  938 + /*
  939 + * Leave the station vifs in awake mode if they
  940 + * happen to be on the same channel as
  941 + * the requested channel.
  942 + */
  943 + on_oper_chan2 = ieee80211_cfg_on_oper_channel(local);
  944 + if (on_oper_chan != on_oper_chan2) {
  945 + if (on_oper_chan2) {
  946 + /* going off oper channel, PS too */
  947 + ieee80211_offchannel_stop_vifs(local,
  948 + true);
  949 + ieee80211_hw_config(local, 0);
  950 + } else {
  951 + /* going on channel, but leave PS
  952 + * off-channel. */
  953 + ieee80211_hw_config(local, 0);
  954 + ieee80211_offchannel_return(local,
  955 + true,
  956 + false);
  957 + }
  958 + } else if (tmp_chan_changed)
  959 + /* Still off-channel, but on some other
  960 + * channel, so update hardware.
  961 + * PS should already be off-channel.
  962 + */
  963 + ieee80211_hw_config(local, 0);
  964 +
939 965 started = true;
940 966 wk->timeout = jiffies;
941 967 }
942 968  
... ... @@ -1011,9 +1037,27 @@
1011 1037 }
1012 1038  
1013 1039 if (!remain_off_channel && local->tmp_channel) {
  1040 + bool on_oper_chan = ieee80211_cfg_on_oper_channel(local);
1014 1041 local->tmp_channel = NULL;
1015   - ieee80211_hw_config(local, 0);
1016   - ieee80211_offchannel_return(local, true);
  1042 + /* If tmp_channel wasn't operating channel, then
  1043 + * we need to go back on-channel.
  1044 + * NOTE: If we can ever be here while scannning,
  1045 + * or if the hw_config() channel config logic changes,
  1046 + * then we may need to do a more thorough check to see if
  1047 + * we still need to do a hardware config. Currently,
  1048 + * we cannot be here while scanning, however.
  1049 + */
  1050 + if (ieee80211_cfg_on_oper_channel(local) && !on_oper_chan)
  1051 + ieee80211_hw_config(local, 0);
  1052 +
  1053 + /* At the least, we need to disable offchannel_ps,
  1054 + * so just go ahead and run the entire offchannel
  1055 + * return logic here. We *could* skip enabling
  1056 + * beaconing if we were already on-oper-channel
  1057 + * as a future optimization.
  1058 + */
  1059 + ieee80211_offchannel_return(local, true, true);
  1060 +
1017 1061 /* give connection some time to breathe */
1018 1062 run_again(local, jiffies + HZ/2);
1019 1063 }