aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/mac80211/Makefile2
-rw-r--r--net/mac80211/ieee80211_i.h8
-rw-r--r--net/mac80211/offchannel.c164
-rw-r--r--net/mac80211/scan.c156
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,
981void ieee80211_rx_bss_put(struct ieee80211_local *local, 981void ieee80211_rx_bss_put(struct ieee80211_local *local,
982 struct ieee80211_bss *bss); 982 struct ieee80211_bss *bss);
983 983
984/* off-channel helpers */
985void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local);
986void ieee80211_offchannel_stop_station(struct ieee80211_local *local);
987void ieee80211_offchannel_return(struct ieee80211_local *local,
988 bool enable_beaconing);
989
984/* interface handling */ 990/* interface handling */
985int ieee80211_iface_init(void); 991int ieee80211_iface_init(void);
986void ieee80211_iface_exit(void); 992void 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 */
22static 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 */
55static 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
90void 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
117void 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
138void 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 */
234static 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 */
267static 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
302void ieee80211_scan_completed(struct ieee80211_hw *hw, bool aborted) 230void 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
385static int ieee80211_start_sw_scan(struct ieee80211_local *local) 291static 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,
568static void ieee80211_scan_state_leave_oper_channel(struct ieee80211_local *local, 453static 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
604static void ieee80211_scan_state_enter_oper_channel(struct ieee80211_local *local, 473static 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