Commit b23b025fe246f3acc2988eb6d400df34c27cb8ae
Committed by
John W. Linville
1 parent
cb8d61de2d
Exists in
master
and in
4 other branches
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 */ |
net/mac80211/main.c
... | ... | @@ -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 || |
net/mac80211/rx.c
... | ... | @@ -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)) |
net/mac80211/scan.c
... | ... | @@ -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++; |
net/mac80211/tx.c
... | ... | @@ -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 | /* |
net/mac80211/work.c
... | ... | @@ -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 | } |