diff options
Diffstat (limited to 'net/mac80211/offchannel.c')
-rw-r--r-- | net/mac80211/offchannel.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c new file mode 100644 index 000000000000..c36b1911987a --- /dev/null +++ b/net/mac80211/offchannel.c | |||
@@ -0,0 +1,170 @@ | |||
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 | * In addition, do not stop monitor interface to allow it to be | ||
112 | * used from user space controlled off-channel operations. | ||
113 | */ | ||
114 | if (sdata->vif.type != NL80211_IFTYPE_STATION && | ||
115 | sdata->vif.type != NL80211_IFTYPE_MONITOR) | ||
116 | netif_tx_stop_all_queues(sdata->dev); | ||
117 | } | ||
118 | mutex_unlock(&local->iflist_mtx); | ||
119 | } | ||
120 | |||
121 | void ieee80211_offchannel_stop_station(struct ieee80211_local *local) | ||
122 | { | ||
123 | struct ieee80211_sub_if_data *sdata; | ||
124 | |||
125 | /* | ||
126 | * notify the AP about us leaving the channel and stop all STA interfaces | ||
127 | */ | ||
128 | mutex_lock(&local->iflist_mtx); | ||
129 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
130 | if (!ieee80211_sdata_running(sdata)) | ||
131 | continue; | ||
132 | |||
133 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
134 | netif_tx_stop_all_queues(sdata->dev); | ||
135 | if (sdata->u.mgd.associated) | ||
136 | ieee80211_offchannel_ps_enable(sdata); | ||
137 | } | ||
138 | } | ||
139 | mutex_unlock(&local->iflist_mtx); | ||
140 | } | ||
141 | |||
142 | void ieee80211_offchannel_return(struct ieee80211_local *local, | ||
143 | bool enable_beaconing) | ||
144 | { | ||
145 | struct ieee80211_sub_if_data *sdata; | ||
146 | |||
147 | mutex_lock(&local->iflist_mtx); | ||
148 | list_for_each_entry(sdata, &local->interfaces, list) { | ||
149 | if (!ieee80211_sdata_running(sdata)) | ||
150 | continue; | ||
151 | |||
152 | /* Tell AP we're back */ | ||
153 | if (sdata->vif.type == NL80211_IFTYPE_STATION) { | ||
154 | if (sdata->u.mgd.associated) | ||
155 | ieee80211_offchannel_ps_disable(sdata); | ||
156 | } | ||
157 | |||
158 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR) | ||
159 | netif_tx_wake_all_queues(sdata->dev); | ||
160 | |||
161 | /* re-enable beaconing */ | ||
162 | if (enable_beaconing && | ||
163 | (sdata->vif.type == NL80211_IFTYPE_AP || | ||
164 | sdata->vif.type == NL80211_IFTYPE_ADHOC || | ||
165 | sdata->vif.type == NL80211_IFTYPE_MESH_POINT)) | ||
166 | ieee80211_bss_info_change_notify( | ||
167 | sdata, BSS_CHANGED_BEACON_ENABLED); | ||
168 | } | ||
169 | mutex_unlock(&local->iflist_mtx); | ||
170 | } | ||