diff options
Diffstat (limited to 'net/mac80211/offchannel.c')
-rw-r--r-- | net/mac80211/offchannel.c | 164 |
1 files changed, 164 insertions, 0 deletions
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 | } | ||