diff options
-rw-r--r-- | net/mac80211/ieee80211_i.h | 13 | ||||
-rw-r--r-- | net/mac80211/main.c | 53 | ||||
-rw-r--r-- | net/mac80211/offchannel.c | 68 | ||||
-rw-r--r-- | net/mac80211/rx.c | 12 | ||||
-rw-r--r-- | net/mac80211/scan.c | 88 | ||||
-rw-r--r-- | net/mac80211/tx.c | 3 | ||||
-rw-r--r-- | net/mac80211/work.c | 66 |
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 { | |||
665 | enum { | 663 | enum { |
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 */ |
1151 | void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local); | 1148 | bool ieee80211_cfg_on_oper_channel(struct ieee80211_local *local); |
1152 | void ieee80211_offchannel_stop_station(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 | void ieee80211_offchannel_return(struct ieee80211_local *local, | 1153 | void ieee80211_offchannel_return(struct ieee80211_local *local, |
1154 | bool enable_beaconing); | 1154 | bool enable_beaconing, |
1155 | bool offchannel_ps_disable); | ||
1155 | void ieee80211_hw_roc_setup(struct ieee80211_local *local); | 1156 | void 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 | */ | ||
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 | int ieee80211_hw_config(struct ieee80211_local *local, u32 changed) | 136 | int 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 | */ |
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 | 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 | ||
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 | 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 | ||
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 | 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 | ||
153 | void ieee80211_offchannel_return(struct ieee80211_local *local, | 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 | 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, | |||
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++; |
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 | } |