diff options
-rw-r--r-- | net/mac80211/Makefile | 2 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 8 | ||||
-rw-r--r-- | net/mac80211/offchannel.c | 164 | ||||
-rw-r--r-- | net/mac80211/scan.c | 156 |
4 files changed, 178 insertions, 152 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 5a1f57df7cd6..04420291e7ad 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -6,7 +6,7 @@ mac80211-y := \ | |||
6 | sta_info.o \ | 6 | sta_info.o \ |
7 | wep.o \ | 7 | wep.o \ |
8 | wpa.o \ | 8 | wpa.o \ |
9 | scan.o \ | 9 | scan.o offchannel.o \ |
10 | ht.o agg-tx.o agg-rx.o \ | 10 | ht.o agg-tx.o agg-rx.o \ |
11 | ibss.o \ | 11 | ibss.o \ |
12 | mlme.o work.o \ | 12 | mlme.o work.o \ |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index de068ad6223b..fd912eb5309e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -786,7 +786,7 @@ struct ieee80211_local { | |||
786 | unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ | 786 | unsigned int wmm_acm; /* bit field of ACM bits (BIT(802.1D tag)) */ |
787 | 787 | ||
788 | bool pspolling; | 788 | bool pspolling; |
789 | bool scan_ps_enabled; | 789 | bool offchannel_ps_enabled; |
790 | /* | 790 | /* |
791 | * PS can only be enabled when we have exactly one managed | 791 | * PS can only be enabled when we have exactly one managed |
792 | * interface (and monitors) in PS, this then points there. | 792 | * interface (and monitors) in PS, this then points there. |
@@ -981,6 +981,12 @@ ieee80211_rx_bss_get(struct ieee80211_local *local, u8 *bssid, int freq, | |||
981 | void ieee80211_rx_bss_put(struct ieee80211_local *local, | 981 | void ieee80211_rx_bss_put(struct ieee80211_local *local, |
982 | struct ieee80211_bss *bss); | 982 | struct ieee80211_bss *bss); |
983 | 983 | ||
984 | /* off-channel helpers */ | ||
985 | void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local); | ||
986 | void ieee80211_offchannel_stop_station(struct ieee80211_local *local); | ||
987 | void ieee80211_offchannel_return(struct ieee80211_local *local, | ||
988 | bool enable_beaconing); | ||
989 | |||
984 | /* interface handling */ | 990 | /* interface handling */ |
985 | int ieee80211_iface_init(void); | 991 | int ieee80211_iface_init(void); |
986 | void ieee80211_iface_exit(void); | 992 | void ieee80211_iface_exit(void); |
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c new file mode 100644 index 000000000000..2cd880e444d1 --- /dev/null +++ b/net/mac80211/offchannel.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* | ||
2 | * Off-channel operation helpers | ||
3 | * | ||
4 | * Copyright 2003, Jouni Malinen <jkmaline@cc.hut.fi> | ||
5 | * Copyright 2004, Instant802 Networks, Inc. | ||
6 | * Copyright 2005, Devicescape Software, Inc. | ||
7 | * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> | ||
8 | * Copyright 2007, Michael Wu <flamingice@sourmilk.net> | ||
9 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | #include <net/mac80211.h> | ||
16 | #include "ieee80211_i.h" | ||
17 | |||
18 | /* | ||
19 | * inform AP that we will go to sleep so that it will buffer the frames | ||
20 | * while we scan | ||
21 | */ | ||
22 | static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata) | ||
23 | { | ||
24 | struct ieee80211_local *local = sdata->local; | ||
25 | |||
26 | local->offchannel_ps_enabled = false; | ||
27 | |||
28 | /* FIXME: what to do when local->pspolling is true? */ | ||
29 | |||
30 | del_timer_sync(&local->dynamic_ps_timer); | ||
31 | cancel_work_sync(&local->dynamic_ps_enable_work); | ||
32 | |||
33 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { | ||
34 | local->offchannel_ps_enabled = true; | ||
35 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; | ||
36 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
37 | } | ||
38 | |||
39 | if (!(local->offchannel_ps_enabled) || | ||
40 | !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) | ||
41 | /* | ||
42 | * If power save was enabled, no need to send a nullfunc | ||
43 | * frame because AP knows that we are sleeping. But if the | ||
44 | * hardware is creating the nullfunc frame for power save | ||
45 | * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not | ||
46 | * enabled) and power save was enabled, the firmware just | ||
47 | * sent a null frame with power save disabled. So we need | ||
48 | * to send a new nullfunc frame to inform the AP that we | ||
49 | * are again sleeping. | ||
50 | */ | ||
51 | ieee80211_send_nullfunc(local, sdata, 1); | ||
52 | } | ||
53 | |||
54 | /* inform AP that we are awake again, unless power save is enabled */ | ||
55 | static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata) | ||
56 | { | ||
57 | struct ieee80211_local *local = sdata->local; | ||
58 | |||
59 | if (!local->ps_sdata) | ||
60 | ieee80211_send_nullfunc(local, sdata, 0); | ||
61 | else if (local->offchannel_ps_enabled) { | ||
62 | /* | ||
63 | * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware | ||
64 | * will send a nullfunc frame with the powersave bit set | ||
65 | * even though the AP already knows that we are sleeping. | ||
66 | * This could be avoided by sending a null frame with power | ||
67 | * save bit disabled before enabling the power save, but | ||
68 | * this doesn't gain anything. | ||
69 | * | ||
70 | * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need | ||
71 | * to send a nullfunc frame because AP already knows that | ||
72 | * we are sleeping, let's just enable power save mode in | ||
73 | * hardware. | ||
74 | */ | ||
75 | local->hw.conf.flags |= IEEE80211_CONF_PS; | ||
76 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
77 | } else if (local->hw.conf.dynamic_ps_timeout > 0) { | ||
78 | /* | ||
79 | * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer | ||
80 | * had been running before leaving the operating channel, | ||
81 | * restart the timer now and send a nullfunc frame to inform | ||
82 | * the AP that we are awake. | ||
83 | */ | ||
84 | ieee80211_send_nullfunc(local, sdata, 0); | ||
85 | mod_timer(&local->dynamic_ps_timer, jiffies + | ||
86 | msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local) | ||
91 | { | ||
92 | struct ieee80211_sub_if_data *sdata; | ||
93 | |||
94 | mutex_lock(&local->iflist_mtx); | ||
95 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
96 | if (!ieee80211_sdata_running(sdata)) | ||
97 | continue; | ||
98 | |||
99 | /* disable beaconing */ | ||
100 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
101 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
102 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
103 | ieee80211_bss_info_change_notify( | ||
104 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
105 | |||
106 | /* | ||
107 | * only handle non-STA interfaces here, STA interfaces | ||
108 | * are handled in ieee80211_offchannel_stop_station(), | ||
109 | * e.g., from the background scan state machine | ||
110 | */ | ||
111 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
112 | netif_stop_queue(sdata->dev); | ||
113 | } | ||
114 | mutex_unlock(&local->iflist_mtx); | ||
115 | } | ||
116 | |||
117 | void ieee80211_offchannel_stop_station(struct ieee80211_local *local) | ||
118 | { | ||
119 | struct ieee80211_sub_if_data *sdata; | ||
120 | |||
121 | /* | ||
122 | * notify the AP about us leaving the channel and stop all STA interfaces | ||
123 | */ | ||
124 | mutex_lock(&local->iflist_mtx); | ||
125 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
126 | if (!ieee80211_sdata_running(sdata)) | ||
127 | continue; | ||
128 | |||
129 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
130 | netif_stop_queue(sdata->dev); | ||
131 | if (sdata->u.mgd.associated) | ||
132 | ieee80211_offchannel_ps_enable(sdata); | ||
133 | } | ||
134 | } | ||
135 | mutex_unlock(&local->iflist_mtx); | ||
136 | } | ||
137 | |||
138 | void ieee80211_offchannel_return(struct ieee80211_local *local, | ||
139 | bool enable_beaconing) | ||
140 | { | ||
141 | struct ieee80211_sub_if_data *sdata; | ||
142 | |||
143 | mutex_lock(&local->iflist_mtx); | ||
144 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
145 | if (!ieee80211_sdata_running(sdata)) | ||
146 | continue; | ||
147 | |||
148 | /* Tell AP we're back */ | ||
149 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
150 | if (sdata->u.mgd.associated) | ||
151 | ieee80211_offchannel_ps_disable(sdata); | ||
152 | netif_wake_queue(sdata->dev); | ||
153 | } | ||
154 | |||
155 | /* re-enable beaconing */ | ||
156 | if (enable_beaconing && | ||
157 | (sdata->vif.type == NL80211_IFTYPE_AP || | ||
158 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
159 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT)) | ||
160 | ieee80211_bss_info_change_notify( | ||
161 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
162 | } | ||
163 | mutex_unlock(&local->iflist_mtx); | ||
164 | } | ||
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c index 2a2d7f6005af..365f40975511 100644 --- a/net/mac80211/scan.c +++ b/net/mac80211/scan.c | |||
@@ -227,82 +227,9 @@ static bool ieee80211_prep_hw_scan(struct ieee80211_local *local) | |||
227 | return true; | 227 | return true; |
228 | } | 228 | } |
229 | 229 | ||
230 | /* | ||
231 | * inform AP that we will go to sleep so that it will buffer the frames | ||
232 | * while we scan | ||
233 | */ | ||
234 | static void ieee80211_scan_ps_enable(struct ieee80211_sub_if_data *sdata) | ||
235 | { | ||
236 | struct ieee80211_local *local = sdata->local; | ||
237 | |||
238 | local->scan_ps_enabled = false; | ||
239 | |||
240 | /* FIXME: what to do when local->pspolling is true? */ | ||
241 | |||
242 | del_timer_sync(&local->dynamic_ps_timer); | ||
243 | cancel_work_sync(&local->dynamic_ps_enable_work); | ||
244 | |||
245 | if (local->hw.conf.flags & IEEE80211_CONF_PS) { | ||
246 | local->scan_ps_enabled = true; | ||
247 | local->hw.conf.flags &= ~IEEE80211_CONF_PS; | ||
248 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
249 | } | ||
250 | |||
251 | if (!(local->scan_ps_enabled) || | ||
252 | !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK)) | ||
253 | /* | ||
254 | * If power save was enabled, no need to send a nullfunc | ||
255 | * frame because AP knows that we are sleeping. But if the | ||
256 | * hardware is creating the nullfunc frame for power save | ||
257 | * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not | ||
258 | * enabled) and power save was enabled, the firmware just | ||
259 | * sent a null frame with power save disabled. So we need | ||
260 | * to send a new nullfunc frame to inform the AP that we | ||
261 | * are again sleeping. | ||
262 | */ | ||
263 | ieee80211_send_nullfunc(local, sdata, 1); | ||
264 | } | ||
265 | |||
266 | /* inform AP that we are awake again, unless power save is enabled */ | ||
267 | static void ieee80211_scan_ps_disable(struct ieee80211_sub_if_data *sdata) | ||
268 | { | ||
269 | struct ieee80211_local *local = sdata->local; | ||
270 | |||
271 | if (!local->ps_sdata) | ||
272 | ieee80211_send_nullfunc(local, sdata, 0); | ||
273 | else if (local->scan_ps_enabled) { | ||
274 | /* | ||
275 | * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware | ||
276 | * will send a nullfunc frame with the powersave bit set | ||
277 | * even though the AP already knows that we are sleeping. | ||
278 | * This could be avoided by sending a null frame with power | ||
279 | * save bit disabled before enabling the power save, but | ||
280 | * this doesn't gain anything. | ||
281 | * | ||
282 | * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need | ||
283 | * to send a nullfunc frame because AP already knows that | ||
284 | * we are sleeping, let's just enable power save mode in | ||
285 | * hardware. | ||
286 | */ | ||
287 | local->hw.conf.flags |= IEEE80211_CONF_PS; | ||
288 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS); | ||
289 | } else if (local->hw.conf.dynamic_ps_timeout > 0) { | ||
290 | /* | ||
291 | * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer | ||
292 | * had been running before leaving the operating channel, | ||
293 | * restart the timer now and send a nullfunc frame to inform | ||
294 | * the AP that we are awake. | ||
295 | */ | ||
296 | ieee80211_send_nullfunc(local, sdata, 0); | ||
297 | mod_timer(&local->dynamic_ps_timer, jiffies + | ||
298 | msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout)); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | 230 | void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) |
303 | { | 231 | { |
304 | struct ieee80211_local *local = hw_to_local(hw); | 232 | struct ieee80211_local *local = hw_to_local(hw); |
305 | struct ieee80211_sub_if_data *sdata; | ||
306 | bool was_hw_scan; | 233 | bool was_hw_scan; |
307 | 234 | ||
308 | mutex_lock(&local->scan_mtx); | 235 | mutex_lock(&local->scan_mtx); |
@@ -351,28 +278,7 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) | |||
351 | 278 | ||
352 | drv_sw_scan_complete(local); | 279 | drv_sw_scan_complete(local); |
353 | 280 | ||
354 | mutex_lock(&local->iflist_mtx); | 281 | ieee80211_offchannel_return(local, true); |
355 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
356 | if (!ieee80211_sdata_running(sdata)) | ||
357 | continue; | ||
358 | |||
359 | /* Tell AP we're back */ | ||
360 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
361 | if (sdata->u.mgd.associated) { | ||
362 | ieee80211_scan_ps_disable(sdata); | ||
363 | netif_wake_queue(sdata->dev); | ||
364 | } | ||
365 | } else | ||
366 | netif_wake_queue(sdata->dev); | ||
367 | |||
368 | /* re-enable beaconing */ | ||
369 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
370 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
371 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
372 | ieee80211_bss_info_change_notify( | ||
373 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
374 | } | ||
375 | mutex_unlock(&local->iflist_mtx); | ||
376 | 282 | ||
377 | done: | 283 | done: |
378 | ieee80211_recalc_idle(local); | 284 | ieee80211_recalc_idle(local); |
@@ -384,8 +290,6 @@ EXPORT_SYMBOL(ieee80211_scan_completed); | |||
384 | 290 | ||
385 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) | 291 | static int ieee80211_start_sw_scan(struct ieee80211_local *local) |
386 | { | 292 | { |
387 | struct ieee80211_sub_if_data *sdata; | ||
388 | |||
389 | /* | 293 | /* |
390 | * Hardware/driver doesn't support hw_scan, so use software | 294 | * Hardware/driver doesn't support hw_scan, so use software |
391 | * scanning instead. First send a nullfunc frame with power save | 295 | * scanning instead. First send a nullfunc frame with power save |
@@ -401,26 +305,7 @@ static int ieee80211_start_sw_scan(struct ieee80211_local *local) | |||
401 | */ | 305 | */ |
402 | drv_sw_scan_start(local); | 306 | drv_sw_scan_start(local); |
403 | 307 | ||
404 | mutex_lock(&local->iflist_mtx); | 308 | ieee80211_offchannel_stop_beaconing(local); |
405 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
406 | if (!ieee80211_sdata_running(sdata)) | ||
407 | continue; | ||
408 | |||
409 | /* disable beaconing */ | ||
410 | if (sdata->vif.type == NL80211_IFTYPE_AP || | ||
411 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
412 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT) | ||
413 | ieee80211_bss_info_change_notify( | ||
414 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
415 | |||
416 | /* | ||
417 | * only handle non-STA interfaces here, STA interfaces | ||
418 | * are handled in the scan state machine | ||
419 | */ | ||
420 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
421 | netif_stop_queue(sdata->dev); | ||
422 | } | ||
423 | mutex_unlock(&local->iflist_mtx); | ||
424 | 309 | ||
425 | local->next_scan_state = SCAN_DECISION; | 310 | local->next_scan_state = SCAN_DECISION; |
426 | local->scan_channel_idx = 0; | 311 | local->scan_channel_idx = 0; |
@@ -568,23 +453,7 @@ static int ieee80211_scan_state_decision(struct ieee80211_local *local, | |||
568 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, | 453 | static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, |
569 | unsigned long *next_delay) | 454 | unsigned long *next_delay) |
570 | { | 455 | { |
571 | struct ieee80211_sub_if_data *sdata; | 456 | ieee80211_offchannel_stop_station(local); |
572 | |||
573 | /* | ||
574 | * notify the AP about us leaving the channel and stop all STA interfaces | ||
575 | */ | ||
576 | mutex_lock(&local->iflist_mtx); | ||
577 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
578 | if (!ieee80211_sdata_running(sdata)) | ||
579 | continue; | ||
580 | |||
581 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
582 | netif_stop_queue(sdata->dev); | ||
583 | if (sdata->u.mgd.associated) | ||
584 | ieee80211_scan_ps_enable(sdata); | ||
585 | } | ||
586 | } | ||
587 | mutex_unlock(&local->iflist_mtx); | ||
588 | 457 | ||
589 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); | 458 | __set_bit(SCAN_OFF_CHANNEL, &local->scanning); |
590 | 459 | ||
@@ -604,28 +473,15 @@ static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *loca | |||
604 | static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, | 473 | static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, |
605 | unsigned long *next_delay) | 474 | unsigned long *next_delay) |
606 | { | 475 | { |
607 | struct ieee80211_sub_if_data *sdata = local->scan_sdata; | ||
608 | |||
609 | /* switch back to the operating channel */ | 476 | /* switch back to the operating channel */ |
610 | local->scan_channel = NULL; | 477 | local->scan_channel = NULL; |
611 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 478 | ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
612 | 479 | ||
613 | /* | 480 | /* |
614 | * notify the AP about us being back and restart all STA interfaces | 481 | * Only re-enable station mode interface now; beaconing will be |
482 | * re-enabled once the full scan has been completed. | ||
615 | */ | 483 | */ |
616 | mutex_lock(&local->iflist_mtx); | 484 | ieee80211_offchannel_return(local, false); |
617 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
618 | if (!ieee80211_sdata_running(sdata)) | ||
619 | continue; | ||
620 | |||
621 | /* Tell AP we're back */ | ||
622 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
623 | if (sdata->u.mgd.associated) | ||
624 | ieee80211_scan_ps_disable(sdata); | ||
625 | netif_wake_queue(sdata->dev); | ||
626 | } | ||
627 | } | ||
628 | mutex_unlock(&local->iflist_mtx); | ||
629 | 485 | ||
630 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); | 486 | __clear_bit(SCAN_OFF_CHANNEL, &local->scanning); |
631 | 487 | ||