aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/mac80211/ieee80211_i.h13
-rw-r--r--net/mac80211/main.c53
-rw-r--r--net/mac80211/offchannel.c68
-rw-r--r--net/mac80211/rx.c12
-rw-r--r--net/mac80211/scan.c88
-rw-r--r--net/mac80211/tx.c3
-rw-r--r--net/mac80211/work.c66
7 files changed, 214 insertions, 89 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f71ed31d176a..44eea1af1553 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -655,8 +655,6 @@ struct tpt_led_trigger {
655 * well be on the operating channel 655 * well be on the operating channel
656 * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to 656 * @SCAN_HW_SCANNING: The hardware is scanning for us, we have no way to
657 * determine if we are on the operating channel or not 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 * @SCAN_COMPLETED: Set for our scan work function when the driver reported 658 * @SCAN_COMPLETED: Set for our scan work function when the driver reported
661 * that the scan completed. 659 * that the scan completed.
662 * @SCAN_ABORTED: Set for our scan work function when the driver reported 660 * @SCAN_ABORTED: Set for our scan work function when the driver reported
@@ -665,7 +663,6 @@ struct tpt_led_trigger {
665enum { 663enum {
666 SCAN_SW_SCANNING, 664 SCAN_SW_SCANNING,
667 SCAN_HW_SCANNING, 665 SCAN_HW_SCANNING,
668 SCAN_OFF_CHANNEL,
669 SCAN_COMPLETED, 666 SCAN_COMPLETED,
670 SCAN_ABORTED, 667 SCAN_ABORTED,
671}; 668};
@@ -1148,10 +1145,14 @@ void ieee80211_rx_bss_put(struct ieee80211_local *local,
1148 struct ieee80211_bss *bss); 1145 struct ieee80211_bss *bss);
1149 1146
1150/* off-channel helpers */ 1147/* off-channel helpers */
1151void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local); 1148bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local);
1152void ieee80211_offchannel_stop_station(struct ieee80211_local *local); 1149void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
1150 bool tell_ap);
1151void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
1152 bool offchannel_ps_enable);
1153void ieee80211_offchannel_return(struct ieee80211_local *local, 1153void ieee80211_offchannel_return(struct ieee80211_local *local,
1154 bool enable_beaconing); 1154 bool enable_beaconing,
1155 bool offchannel_ps_disable);
1155void ieee80211_hw_roc_setup(struct ieee80211_local *local); 1156void ieee80211_hw_roc_setup(struct ieee80211_local *local);
1156 1157
1157/* interface handling */ 1158/* interface handling */
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 09a27449f3fd..c155c0b69426 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -98,6 +98,41 @@ static void ieee80211_reconfig_filter(struct work_struct *work)
98 ieee80211_configure_filter(local); 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 */
107bool 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
101int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) 136int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
102{ 137{
103 struct ieee80211_channel *chan, *scan_chan; 138 struct ieee80211_channel *chan, *scan_chan;
@@ -110,21 +145,27 @@ int ieee80211_hw_config(struct ieee80211_local *local, u32 changed)
110 145
111 scan_chan = local->scan_channel; 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 offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; 151 offchannel_flag = local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
114 if (scan_chan) { 152 if (scan_chan) {
115 chan = scan_chan; 153 chan = scan_chan;
116 channel_type = NL80211_CHAN_NO_HT; 154 channel_type = NL80211_CHAN_NO_HT;
117 local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL; 155 } else if (local->tmp_channel) {
118 } else if (local->tmp_channel &&
119 local->oper_channel != local->tmp_channel) {
120 chan = scan_chan = local->tmp_channel; 156 chan = scan_chan = local->tmp_channel;
121 channel_type = local->tmp_channel_type; 157 channel_type = local->tmp_channel_type;
122 local->hw.conf.flags |= IEEE80211_CONF_OFFCHANNEL;
123 } else { 158 } else {
124 chan = local->oper_channel; 159 chan = local->oper_channel;
125 channel_type = local->_oper_channel_type; 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 offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL; 169 offchannel_flag ^= local->hw.conf.flags & IEEE80211_CONF_OFFCHANNEL;
129 170
130 if (offchannel_flag || chan != local->hw.conf.channel || 171 if (offchannel_flag || chan != local->hw.conf.channel ||
@@ -231,7 +272,7 @@ void ieee80211_bss_info_change_notify(struct ieee80211_sub_if_data *sdata,
231 272
232 if (changed & BSS_CHANGED_BEACON_ENABLED) { 273 if (changed & BSS_CHANGED_BEACON_ENABLED) {
233 if (local->quiescing || !ieee80211_sdata_running(sdata) || 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 sdata->vif.bss_conf.enable_beacon = false; 276 sdata->vif.bss_conf.enable_beacon = false;
236 } else { 277 } else {
237 /* 278 /*
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index b4e52676f3fb..13427b194ced 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -17,10 +17,14 @@
17#include "driver-trace.h" 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 20 * Tell our hardware to disable PS.
21 * while we scan 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 */
23static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) 26static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata,
27 bool tell_ap)
24{ 28{
25 struct ieee80211_local *local = sdata->local; 29 struct ieee80211_local *local = sdata->local;
26 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; 30 struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
@@ -41,8 +45,8 @@ static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
41 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 45 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
42 } 46 }
43 47
44 if (!(local->offchannel_ps_enabled) || 48 if (tell_ap && (!local->offchannel_ps_enabled ||
45 !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) 49 !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)))
46 /* 50 /*
47 * If power save was enabled, no need to send a nullfunc 51 * If power save was enabled, no need to send a nullfunc
48 * frame because AP knows that we are sleeping. But if the 52 * frame because AP knows that we are sleeping. But if the
@@ -77,6 +81,9 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
77 * we are sleeping, let's just enable power save mode in 81 * we are sleeping, let's just enable power save mode in
78 * hardware. 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 local->hw.conf.flags |= IEEE80211_CONF_PS; 87 local->hw.conf.flags |= IEEE80211_CONF_PS;
81 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); 88 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
82 } else if (local->hw.conf.dynamic_ps_timeout > 0) { 89 } else if (local->hw.conf.dynamic_ps_timeout > 0) {
@@ -95,63 +102,61 @@ static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
95 ieee80211_sta_reset_conn_monitor(sdata); 102 ieee80211_sta_reset_conn_monitor(sdata);
96} 103}
97 104
98void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) 105void ieee80211_offchannel_stop_vifs(struct ieee80211_local *local,
106 bool offchannel_ps_enable)
99{ 107{
100 struct ieee80211_sub_if_data *sdata; 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 mutex_lock(&local->iflist_mtx); 114 mutex_lock(&local->iflist_mtx);
103 list_for_each_entry(sdata, &local->interfaces, list) { 115 list_for_each_entry(sdata, &local->interfaces, list) {
104 if (!ieee80211_sdata_running(sdata)) 116 if (!ieee80211_sdata_running(sdata))
105 continue; 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 if (sdata->vif.type == NL80211_IFTYPE_AP || 123 if (sdata->vif.type == NL80211_IFTYPE_AP ||
109 sdata->vif.type == NL80211_IFTYPE_ADHOC || 124 sdata->vif.type == NL80211_IFTYPE_ADHOC ||
110 sdata->vif.type == NL80211_IFTYPE_MESH_POINT) 125 sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
111 ieee80211_bss_info_change_notify( 126 ieee80211_bss_info_change_notify(
112 sdata, BSS_CHANGED_BEACON_ENABLED); 127 sdata, BSS_CHANGED_BEACON_ENABLED);
113 128
114 /* 129 if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
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);
125 netif_tx_stop_all_queues(sdata->dev); 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 mutex_unlock(&local->iflist_mtx); 137 mutex_unlock(&local->iflist_mtx);
129} 138}
130 139
131void ieee80211_offchannel_stop_station(struct ieee80211_local *local) 140void ieee80211_offchannel_enable_all_ps(struct ieee80211_local *local,
141 bool tell_ap)
132{ 142{
133 struct ieee80211_sub_if_data *sdata; 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 mutex_lock(&local->iflist_mtx); 145 mutex_lock(&local->iflist_mtx);
139 list_for_each_entry(sdata, &local->interfaces, list) { 146 list_for_each_entry(sdata, &local->interfaces, list) {
140 if (!ieee80211_sdata_running(sdata)) 147 if (!ieee80211_sdata_running(sdata))
141 continue; 148 continue;
142 149
143 if (sdata->vif.type == NL80211_IFTYPE_STATION) { 150 if (sdata->vif.type == NL80211_IFTYPE_STATION &&
144 set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state); 151 sdata->u.mgd.associated)
145 netif_tx_stop_all_queues(sdata->dev); 152 ieee80211_offchannel_ps_enable(sdata, tell_ap);
146 if (sdata->u.mgd.associated)
147 ieee80211_offchannel_ps_enable(sdata);
148 }
149 } 153 }
150 mutex_unlock(&local->iflist_mtx); 154 mutex_unlock(&local->iflist_mtx);
151} 155}
152 156
153void ieee80211_offchannel_return(struct ieee80211_local *local, 157void ieee80211_offchannel_return(struct ieee80211_local *local,
154 bool enable_beaconing) 158 bool enable_beaconing,
159 bool offchannel_ps_disable)
155{ 160{
156 struct ieee80211_sub_if_data *sdata; 161 struct ieee80211_sub_if_data *sdata;
157 162
@@ -161,7 +166,8 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
161 continue; 166 continue;
162 167
163 /* Tell AP we're back */ 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 if (sdata->u.mgd.associated) 171 if (sdata->u.mgd.associated)
166 ieee80211_offchannel_ps_disable(sdata); 172 ieee80211_offchannel_ps_disable(sdata);
167 } 173 }
@@ -181,7 +187,7 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
181 netif_tx_wake_all_queues(sdata->dev); 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 if (enable_beaconing && 191 if (enable_beaconing &&
186 (sdata->vif.type == NL80211_IFTYPE_AP || 192 (sdata->vif.type == NL80211_IFTYPE_AP ||
187 sdata->vif.type == NL80211_IFTYPE_ADHOC || 193 sdata->vif.type == NL80211_IFTYPE_ADHOC ||
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 753ffc41c1aa..b5f59ed24000 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -409,16 +409,10 @@ ieee80211_rx_h_passive_scan(struct ieee80211_rx_data *rx)
409 if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN))) 409 if (likely(!(status->rx_flags & IEEE80211_RX_IN_SCAN)))
410 return RX_CONTINUE; 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 return ieee80211_scan_rx(rx->sdata, skb); 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 /* scanning finished during invoking of handlers */ 416 /* scanning finished during invoking of handlers */
423 I802_DEBUG_INC(local->rx_handlers_drop_passive_scan); 417 I802_DEBUG_INC(local->rx_handlers_drop_passive_scan);
424 return RX_DROP_UNUSABLE; 418 return RX_DROP_UNUSABLE;
@@ -2793,7 +2787,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
2793 local->dot11ReceivedFragmentCount++; 2787 local->dot11ReceivedFragmentCount++;
2794 2788
2795 if (unlikely(test_bit(SCAN_HW_SCANNING, &local->scanning) || 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 status->rx_flags |= IEEE80211_RX_IN_SCAN; 2791 status->rx_flags |= IEEE80211_RX_IN_SCAN;
2798 2792
2799 if (ieee80211_is_mgmt(fc)) 2793 if (ieee80211_is_mgmt(fc))
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,
535static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, 568static 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++;
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index bf67a223cd60..291516807fc4 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -257,7 +257,8 @@ ieee80211_tx_h_check_assoc(struct ieee80211_tx_data *tx)
257 if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED)) 257 if (unlikely(info->flags & IEEE80211_TX_CTL_INJECTED))
258 return TX_CONTINUE; 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 !ieee80211_is_probe_req(hdr->frame_control) && 262 !ieee80211_is_probe_req(hdr->frame_control) &&
262 !ieee80211_is_nullfunc(hdr->frame_control)) 263 !ieee80211_is_nullfunc(hdr->frame_control))
263 /* 264 /*
diff --git a/net/mac80211/work.c b/net/mac80211/work.c
index 36305e0d06ef..6bf787a5b38a 100644
--- a/net/mac80211/work.c
+++ b/net/mac80211/work.c
@@ -924,18 +924,44 @@ static void ieee80211_work_work(struct work_struct *work)
924 } 924 }
925 925
926 if (!started && !local->tmp_channel) { 926 if (!started && !local->tmp_channel) {
927 /* 927 bool on_oper_chan;
928 * TODO: could optimize this by leaving the 928 bool tmp_chan_changed = false;
929 * station vifs in awake mode if they 929 bool on_oper_chan2;
930 * happen to be on the same channel as 930 on_oper_chan = ieee80211_cfg_on_oper_channel(local);
931 * the requested channel 931 if (local->tmp_channel)
932 */ 932 if ((local->tmp_channel != wk->chan) ||
933 ieee80211_offchannel_stop_beaconing(local); 933 (local->tmp_channel_type != wk->chan_type))
934 ieee80211_offchannel_stop_station(local); 934 tmp_chan_changed = true;
935 935
936 local->tmp_channel = wk->chan; 936 local->tmp_channel = wk->chan;
937 local->tmp_channel_type = wk->chan_type; 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 started = true; 965 started = true;
940 wk->timeout = jiffies; 966 wk->timeout = jiffies;
941 } 967 }
@@ -1011,9 +1037,27 @@ static void ieee80211_work_work(struct work_struct *work)
1011 } 1037 }
1012 1038
1013 if (!remain_off_channel && local->tmp_channel) { 1039 if (!remain_off_channel && local->tmp_channel) {
1040 bool on_oper_chan = ieee80211_cfg_on_oper_channel(local);
1014 local->tmp_channel = NULL; 1041 local->tmp_channel = NULL;
1015 ieee80211_hw_config(local, 0); 1042 /* If tmp_channel wasn't operating channel, then
1016 ieee80211_offchannel_return(local, true); 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 /* give connection some time to breathe */ 1061 /* give connection some time to breathe */
1018 run_again(local, jiffies + HZ/2); 1062 run_again(local, jiffies + HZ/2);
1019 } 1063 }