aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/offchannel.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/offchannel.c')
-rw-r--r--net/mac80211/offchannel.c85
1 files changed, 85 insertions, 0 deletions
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 4b564091e51d..b4e52676f3fb 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -14,6 +14,7 @@
14 */ 14 */
15#include <net/mac80211.h> 15#include <net/mac80211.h>
16#include "ieee80211_i.h" 16#include "ieee80211_i.h"
17#include "driver-trace.h"
17 18
18/* 19/*
19 * inform AP that we will go to sleep so that it will buffer the frames 20 * inform AP that we will go to sleep so that it will buffer the frames
@@ -190,3 +191,87 @@ void ieee80211_offchannel_return(struct ieee80211_local *local,
190 } 191 }
191 mutex_unlock(&local->iflist_mtx); 192 mutex_unlock(&local->iflist_mtx);
192} 193}
194
195static 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
226void 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}
234EXPORT_SYMBOL_GPL(ieee80211_ready_on_channel);
235
236static 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
263void 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}
271EXPORT_SYMBOL_GPL(ieee80211_remain_on_channel_expired);
272
273void 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}