xref: /openbmc/linux/net/mac80211/offchannel.c (revision 4800cd83)
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 #include "driver-trace.h"
18 
19 /*
20  * inform AP that we will go to sleep so that it will buffer the frames
21  * while we scan
22  */
23 static void ieee80211_offchannel_ps_enable(struct ieee80211_sub_if_data *sdata)
24 {
25 	struct ieee80211_local *local = sdata->local;
26 	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
27 
28 	local->offchannel_ps_enabled = false;
29 
30 	/* FIXME: what to do when local->pspolling is true? */
31 
32 	del_timer_sync(&local->dynamic_ps_timer);
33 	del_timer_sync(&ifmgd->bcn_mon_timer);
34 	del_timer_sync(&ifmgd->conn_mon_timer);
35 
36 	cancel_work_sync(&local->dynamic_ps_enable_work);
37 
38 	if (local->hw.conf.flags & IEEE80211_CONF_PS) {
39 		local->offchannel_ps_enabled = true;
40 		local->hw.conf.flags &= ~IEEE80211_CONF_PS;
41 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
42 	}
43 
44 	if (!(local->offchannel_ps_enabled) ||
45 	    !(local->hw.flags & IEEE80211_HW_PS_NULLFUNC_STACK))
46 		/*
47 		 * If power save was enabled, no need to send a nullfunc
48 		 * frame because AP knows that we are sleeping. But if the
49 		 * hardware is creating the nullfunc frame for power save
50 		 * status (ie. IEEE80211_HW_PS_NULLFUNC_STACK is not
51 		 * enabled) and power save was enabled, the firmware just
52 		 * sent a null frame with power save disabled. So we need
53 		 * to send a new nullfunc frame to inform the AP that we
54 		 * are again sleeping.
55 		 */
56 		ieee80211_send_nullfunc(local, sdata, 1);
57 }
58 
59 /* inform AP that we are awake again, unless power save is enabled */
60 static void ieee80211_offchannel_ps_disable(struct ieee80211_sub_if_data *sdata)
61 {
62 	struct ieee80211_local *local = sdata->local;
63 
64 	if (!local->ps_sdata)
65 		ieee80211_send_nullfunc(local, sdata, 0);
66 	else if (local->offchannel_ps_enabled) {
67 		/*
68 		 * In !IEEE80211_HW_PS_NULLFUNC_STACK case the hardware
69 		 * will send a nullfunc frame with the powersave bit set
70 		 * even though the AP already knows that we are sleeping.
71 		 * This could be avoided by sending a null frame with power
72 		 * save bit disabled before enabling the power save, but
73 		 * this doesn't gain anything.
74 		 *
75 		 * When IEEE80211_HW_PS_NULLFUNC_STACK is enabled, no need
76 		 * to send a nullfunc frame because AP already knows that
77 		 * we are sleeping, let's just enable power save mode in
78 		 * hardware.
79 		 */
80 		local->hw.conf.flags |= IEEE80211_CONF_PS;
81 		ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_PS);
82 	} else if (local->hw.conf.dynamic_ps_timeout > 0) {
83 		/*
84 		 * If IEEE80211_CONF_PS was not set and the dynamic_ps_timer
85 		 * had been running before leaving the operating channel,
86 		 * restart the timer now and send a nullfunc frame to inform
87 		 * the AP that we are awake.
88 		 */
89 		ieee80211_send_nullfunc(local, sdata, 0);
90 		mod_timer(&local->dynamic_ps_timer, jiffies +
91 			  msecs_to_jiffies(local->hw.conf.dynamic_ps_timeout));
92 	}
93 
94 	ieee80211_sta_reset_beacon_monitor(sdata);
95 	ieee80211_sta_reset_conn_monitor(sdata);
96 }
97 
98 void ieee80211_offchannel_stop_beaconing(struct ieee80211_local *local)
99 {
100 	struct ieee80211_sub_if_data *sdata;
101 
102 	mutex_lock(&local->iflist_mtx);
103 	list_for_each_entry(sdata, &local->interfaces, list) {
104 		if (!ieee80211_sdata_running(sdata))
105 			continue;
106 
107 		/* disable beaconing */
108 		if (sdata->vif.type == NL80211_IFTYPE_AP ||
109 		    sdata->vif.type == NL80211_IFTYPE_ADHOC ||
110 		    sdata->vif.type == NL80211_IFTYPE_MESH_POINT)
111 			ieee80211_bss_info_change_notify(
112 				sdata, BSS_CHANGED_BEACON_ENABLED);
113 
114 		/*
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);
126 		}
127 	}
128 	mutex_unlock(&local->iflist_mtx);
129 }
130 
131 void ieee80211_offchannel_stop_station(struct ieee80211_local *local)
132 {
133 	struct ieee80211_sub_if_data *sdata;
134 
135 	/*
136 	 * notify the AP about us leaving the channel and stop all STA interfaces
137 	 */
138 	mutex_lock(&local->iflist_mtx);
139 	list_for_each_entry(sdata, &local->interfaces, list) {
140 		if (!ieee80211_sdata_running(sdata))
141 			continue;
142 
143 		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
144 			set_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
145 			netif_tx_stop_all_queues(sdata->dev);
146 			if (sdata->u.mgd.associated)
147 				ieee80211_offchannel_ps_enable(sdata);
148 		}
149 	}
150 	mutex_unlock(&local->iflist_mtx);
151 }
152 
153 void ieee80211_offchannel_return(struct ieee80211_local *local,
154 				 bool enable_beaconing)
155 {
156 	struct ieee80211_sub_if_data *sdata;
157 
158 	mutex_lock(&local->iflist_mtx);
159 	list_for_each_entry(sdata, &local->interfaces, list) {
160 		if (!ieee80211_sdata_running(sdata))
161 			continue;
162 
163 		/* Tell AP we're back */
164 		if (sdata->vif.type == NL80211_IFTYPE_STATION) {
165 			if (sdata->u.mgd.associated)
166 				ieee80211_offchannel_ps_disable(sdata);
167 		}
168 
169 		if (sdata->vif.type != NL80211_IFTYPE_MONITOR) {
170 			clear_bit(SDATA_STATE_OFFCHANNEL, &sdata->state);
171 			/*
172 			 * This may wake up queues even though the driver
173 			 * currently has them stopped. This is not very
174 			 * likely, since the driver won't have gotten any
175 			 * (or hardly any) new packets while we weren't
176 			 * on the right channel, and even if it happens
177 			 * it will at most lead to queueing up one more
178 			 * packet per queue in mac80211 rather than on
179 			 * the interface qdisc.
180 			 */
181 			netif_tx_wake_all_queues(sdata->dev);
182 		}
183 
184 		/* re-enable beaconing */
185 		if (enable_beaconing &&
186 		    (sdata->vif.type == NL80211_IFTYPE_AP ||
187 		     sdata->vif.type == NL80211_IFTYPE_ADHOC ||
188 		     sdata->vif.type == NL80211_IFTYPE_MESH_POINT))
189 			ieee80211_bss_info_change_notify(
190 				sdata, BSS_CHANGED_BEACON_ENABLED);
191 	}
192 	mutex_unlock(&local->iflist_mtx);
193 }
194 
195 static void ieee80211_hw_roc_start(struct work_struct *work)
196 {
197 	struct ieee80211_local *local =
198 		container_of(work, struct ieee80211_local, hw_roc_start);
199 	struct ieee80211_sub_if_data *sdata;
200 
201 	mutex_lock(&local->mtx);
202 
203 	if (!local->hw_roc_channel) {
204 		mutex_unlock(&local->mtx);
205 		return;
206 	}
207 
208 	ieee80211_recalc_idle(local);
209 
210 	if (local->hw_roc_skb) {
211 		sdata = IEEE80211_DEV_TO_SUB_IF(local->hw_roc_dev);
212 		ieee80211_tx_skb(sdata, local->hw_roc_skb);
213 		local->hw_roc_skb = NULL;
214 	} else {
215 		cfg80211_ready_on_channel(local->hw_roc_dev,
216 					  local->hw_roc_cookie,
217 					  local->hw_roc_channel,
218 					  local->hw_roc_channel_type,
219 					  local->hw_roc_duration,
220 					  GFP_KERNEL);
221 	}
222 
223 	mutex_unlock(&local->mtx);
224 }
225 
226 void ieee80211_ready_on_channel(struct ieee80211_hw *hw)
227 {
228 	struct ieee80211_local *local = hw_to_local(hw);
229 
230 	trace_api_ready_on_channel(local);
231 
232 	ieee80211_queue_work(hw, &local->hw_roc_start);
233 }
234 EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
235 
236 static void ieee80211_hw_roc_done(struct work_struct *work)
237 {
238 	struct ieee80211_local *local =
239 		container_of(work, struct ieee80211_local, hw_roc_done);
240 
241 	mutex_lock(&local->mtx);
242 
243 	if (!local->hw_roc_channel) {
244 		mutex_unlock(&local->mtx);
245 		return;
246 	}
247 
248 	if (!local->hw_roc_for_tx)
249 		cfg80211_remain_on_channel_expired(local->hw_roc_dev,
250 						   local->hw_roc_cookie,
251 						   local->hw_roc_channel,
252 						   local->hw_roc_channel_type,
253 						   GFP_KERNEL);
254 
255 	local->hw_roc_channel = NULL;
256 	local->hw_roc_cookie = 0;
257 
258 	ieee80211_recalc_idle(local);
259 
260 	mutex_unlock(&local->mtx);
261 }
262 
263 void ieee80211_remain_on_channel_expired(struct ieee80211_hw *hw)
264 {
265 	struct ieee80211_local *local = hw_to_local(hw);
266 
267 	trace_api_remain_on_channel_expired(local);
268 
269 	ieee80211_queue_work(hw, &local->hw_roc_done);
270 }
271 EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
272 
273 void ieee80211_hw_roc_setup(struct ieee80211_local *local)
274 {
275 	INIT_WORK(&local->hw_roc_start, ieee80211_hw_roc_start);
276 	INIT_WORK(&local->hw_roc_done, ieee80211_hw_roc_done);
277 }
278