blob: 166165efd8e223fea88f49cd341fafc0117e4690 [file] [log] [blame]
Johannes Bergf444de02010-05-05 15:25:02 +02001/*
2 * mac80211 - channel management
3 */
4
Johannes Berg0aaffa92010-05-05 15:28:27 +02005#include <linux/nl80211.h>
Johannes Berg3448c002012-09-11 17:57:42 +02006#include <linux/export.h>
Johannes Berg4d76d212012-12-11 20:38:41 +01007#include <linux/rtnetlink.h>
Paul Stewart3117bbdb2012-03-13 07:46:18 -07008#include <net/cfg80211.h>
Johannes Bergf444de02010-05-05 15:25:02 +02009#include "ieee80211_i.h"
Michal Kazior35f2fce2012-06-26 14:37:20 +020010#include "driver-ops.h"
Johannes Bergf444de02010-05-05 15:25:02 +020011
Johannes Berg18942d32013-02-07 21:30:37 +010012static void ieee80211_change_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +010013 struct ieee80211_chanctx *ctx,
14 const struct cfg80211_chan_def *chandef)
Michal Kazior23a85b452012-06-26 14:37:21 +020015{
Johannes Berg4bf88532012-11-09 11:39:59 +010016 if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
Michal Kaziore89a96f2012-06-26 14:37:22 +020017 return;
18
Johannes Berg4bf88532012-11-09 11:39:59 +010019 WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
20
21 ctx->conf.def = *chandef;
22 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
Johannes Berg55de9082012-07-26 17:24:39 +020023
24 if (!local->use_chanctx) {
Karl Beldan675a0b02013-03-25 16:26:57 +010025 local->_oper_chandef = *chandef;
Johannes Berg55de9082012-07-26 17:24:39 +020026 ieee80211_hw_config(local, 0);
27 }
Johannes Berg0aaffa92010-05-05 15:28:27 +020028}
Michal Kaziord01a1e62012-06-26 14:37:16 +020029
30static struct ieee80211_chanctx *
31ieee80211_find_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +010032 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +020033 enum ieee80211_chanctx_mode mode)
34{
35 struct ieee80211_chanctx *ctx;
36
37 lockdep_assert_held(&local->chanctx_mtx);
38
39 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
40 return NULL;
Michal Kaziord01a1e62012-06-26 14:37:16 +020041
42 list_for_each_entry(ctx, &local->chanctx_list, list) {
Johannes Berg4bf88532012-11-09 11:39:59 +010043 const struct cfg80211_chan_def *compat;
Michal Kaziore89a96f2012-06-26 14:37:22 +020044
Michal Kaziord01a1e62012-06-26 14:37:16 +020045 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
46 continue;
Johannes Berg4bf88532012-11-09 11:39:59 +010047
48 compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
49 if (!compat)
Michal Kaziord01a1e62012-06-26 14:37:16 +020050 continue;
51
Johannes Berg18942d32013-02-07 21:30:37 +010052 ieee80211_change_chanctx(local, ctx, compat);
Michal Kaziore89a96f2012-06-26 14:37:22 +020053
Michal Kaziord01a1e62012-06-26 14:37:16 +020054 return ctx;
55 }
56
57 return NULL;
58}
59
Simon Wunderliche4746852013-04-08 22:43:16 +020060static bool ieee80211_is_radar_required(struct ieee80211_local *local)
61{
62 struct ieee80211_sub_if_data *sdata;
63
64 rcu_read_lock();
65 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
66 if (sdata->radar_required) {
67 rcu_read_unlock();
68 return true;
69 }
70 }
71 rcu_read_unlock();
72
73 return false;
74}
75
Michal Kaziord01a1e62012-06-26 14:37:16 +020076static struct ieee80211_chanctx *
77ieee80211_new_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +010078 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +020079 enum ieee80211_chanctx_mode mode)
80{
81 struct ieee80211_chanctx *ctx;
Michal Kazior35f2fce2012-06-26 14:37:20 +020082 int err;
Michal Kaziord01a1e62012-06-26 14:37:16 +020083
84 lockdep_assert_held(&local->chanctx_mtx);
85
86 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
87 if (!ctx)
88 return ERR_PTR(-ENOMEM);
89
Johannes Berg4bf88532012-11-09 11:39:59 +010090 ctx->conf.def = *chandef;
Johannes Berg04ecd252012-09-11 14:34:12 +020091 ctx->conf.rx_chains_static = 1;
92 ctx->conf.rx_chains_dynamic = 1;
Michal Kaziord01a1e62012-06-26 14:37:16 +020093 ctx->mode = mode;
Simon Wunderliche4746852013-04-08 22:43:16 +020094 ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
95 if (!local->use_chanctx)
96 local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
Michal Kaziord01a1e62012-06-26 14:37:16 +020097
Johannes Berg55de9082012-07-26 17:24:39 +020098 if (!local->use_chanctx) {
Karl Beldan675a0b02013-03-25 16:26:57 +010099 local->_oper_chandef = *chandef;
Johannes Berg55de9082012-07-26 17:24:39 +0200100 ieee80211_hw_config(local, 0);
101 } else {
102 err = drv_add_chanctx(local, ctx);
103 if (err) {
104 kfree(ctx);
105 return ERR_PTR(err);
106 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200107 }
108
Johannes Berg3448c002012-09-11 17:57:42 +0200109 list_add_rcu(&ctx->list, &local->chanctx_list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200110
Johannes Bergfd0f9792013-02-07 00:14:51 +0100111 mutex_lock(&local->mtx);
112 ieee80211_recalc_idle(local);
113 mutex_unlock(&local->mtx);
114
Michal Kaziord01a1e62012-06-26 14:37:16 +0200115 return ctx;
116}
117
118static void ieee80211_free_chanctx(struct ieee80211_local *local,
119 struct ieee80211_chanctx *ctx)
120{
Simon Wunderliche4746852013-04-08 22:43:16 +0200121 bool check_single_channel = false;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200122 lockdep_assert_held(&local->chanctx_mtx);
123
124 WARN_ON_ONCE(ctx->refcount != 0);
125
Johannes Berg55de9082012-07-26 17:24:39 +0200126 if (!local->use_chanctx) {
Karl Beldan675a0b02013-03-25 16:26:57 +0100127 struct cfg80211_chan_def *chandef = &local->_oper_chandef;
128 chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
129 chandef->center_freq1 = chandef->chan->center_freq;
130 chandef->center_freq2 = 0;
Simon Wunderliche4746852013-04-08 22:43:16 +0200131
132 /* NOTE: Disabling radar is only valid here for
133 * single channel context. To be sure, check it ...
134 */
135 if (local->hw.conf.radar_enabled)
136 check_single_channel = true;
137 local->hw.conf.radar_enabled = false;
138
Johannes Berg55de9082012-07-26 17:24:39 +0200139 ieee80211_hw_config(local, 0);
140 } else {
141 drv_remove_chanctx(local, ctx);
142 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200143
Johannes Berg3448c002012-09-11 17:57:42 +0200144 list_del_rcu(&ctx->list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200145 kfree_rcu(ctx, rcu_head);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100146
Simon Wunderliche4746852013-04-08 22:43:16 +0200147 /* throw a warning if this wasn't the only channel context. */
148 WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
149
Johannes Bergfd0f9792013-02-07 00:14:51 +0100150 mutex_lock(&local->mtx);
151 ieee80211_recalc_idle(local);
152 mutex_unlock(&local->mtx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200153}
154
155static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
156 struct ieee80211_chanctx *ctx)
157{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200158 struct ieee80211_local *local = sdata->local;
159 int ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200160
161 lockdep_assert_held(&local->chanctx_mtx);
162
Michal Kazior35f2fce2012-06-26 14:37:20 +0200163 ret = drv_assign_vif_chanctx(local, sdata, ctx);
164 if (ret)
165 return ret;
166
Michal Kaziord01a1e62012-06-26 14:37:16 +0200167 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
168 ctx->refcount++;
169
Johannes Berg1ea6f9c2012-10-24 10:59:25 +0200170 ieee80211_recalc_txpower(sdata);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100171 sdata->vif.bss_conf.idle = false;
Johannes Berg5bbe754d2013-02-13 13:50:51 +0100172
173 if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
174 sdata->vif.type != NL80211_IFTYPE_MONITOR)
175 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
Johannes Berg1ea6f9c2012-10-24 10:59:25 +0200176
Michal Kaziord01a1e62012-06-26 14:37:16 +0200177 return 0;
178}
179
Johannes Berg4bf88532012-11-09 11:39:59 +0100180static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
181 struct ieee80211_chanctx *ctx)
Michal Kaziore89a96f2012-06-26 14:37:22 +0200182{
183 struct ieee80211_chanctx_conf *conf = &ctx->conf;
184 struct ieee80211_sub_if_data *sdata;
Johannes Berg4bf88532012-11-09 11:39:59 +0100185 const struct cfg80211_chan_def *compat = NULL;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200186
187 lockdep_assert_held(&local->chanctx_mtx);
188
189 rcu_read_lock();
190 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
Johannes Berg4bf88532012-11-09 11:39:59 +0100191
Michal Kaziore89a96f2012-06-26 14:37:22 +0200192 if (!ieee80211_sdata_running(sdata))
193 continue;
194 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
195 continue;
196
Johannes Berg4bf88532012-11-09 11:39:59 +0100197 if (!compat)
198 compat = &sdata->vif.bss_conf.chandef;
199
200 compat = cfg80211_chandef_compatible(
201 &sdata->vif.bss_conf.chandef, compat);
202 if (!compat)
203 break;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200204 }
205 rcu_read_unlock();
206
Johannes Berg4bf88532012-11-09 11:39:59 +0100207 if (WARN_ON_ONCE(!compat))
208 return;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200209
Johannes Berg18942d32013-02-07 21:30:37 +0100210 ieee80211_change_chanctx(local, ctx, compat);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200211}
212
Michal Kaziord01a1e62012-06-26 14:37:16 +0200213static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
214 struct ieee80211_chanctx *ctx)
215{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200216 struct ieee80211_local *local = sdata->local;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200217
218 lockdep_assert_held(&local->chanctx_mtx);
219
220 ctx->refcount--;
221 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200222
Johannes Bergfd0f9792013-02-07 00:14:51 +0100223 sdata->vif.bss_conf.idle = true;
Johannes Berg5bbe754d2013-02-13 13:50:51 +0100224
225 if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
226 sdata->vif.type != NL80211_IFTYPE_MONITOR)
227 ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_IDLE);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100228
Michal Kazior35f2fce2012-06-26 14:37:20 +0200229 drv_unassign_vif_chanctx(local, sdata, ctx);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200230
Johannes Berg04ecd252012-09-11 14:34:12 +0200231 if (ctx->refcount > 0) {
Michal Kaziore89a96f2012-06-26 14:37:22 +0200232 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
Johannes Berg04ecd252012-09-11 14:34:12 +0200233 ieee80211_recalc_smps_chanctx(local, ctx);
Simon Wunderlich164eb022013-02-08 18:16:20 +0100234 ieee80211_recalc_radar_chanctx(local, ctx);
Johannes Berg04ecd252012-09-11 14:34:12 +0200235 }
Michal Kaziord01a1e62012-06-26 14:37:16 +0200236}
237
238static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
239{
240 struct ieee80211_local *local = sdata->local;
241 struct ieee80211_chanctx_conf *conf;
242 struct ieee80211_chanctx *ctx;
243
244 lockdep_assert_held(&local->chanctx_mtx);
245
246 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
247 lockdep_is_held(&local->chanctx_mtx));
248 if (!conf)
249 return;
250
251 ctx = container_of(conf, struct ieee80211_chanctx, conf);
252
253 ieee80211_unassign_vif_chanctx(sdata, ctx);
254 if (ctx->refcount == 0)
255 ieee80211_free_chanctx(local, ctx);
256}
257
Simon Wunderlich164eb022013-02-08 18:16:20 +0100258void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
259 struct ieee80211_chanctx *chanctx)
260{
Simon Wunderliche4746852013-04-08 22:43:16 +0200261 bool radar_enabled;
Simon Wunderlich164eb022013-02-08 18:16:20 +0100262
263 lockdep_assert_held(&local->chanctx_mtx);
264
Simon Wunderliche4746852013-04-08 22:43:16 +0200265 radar_enabled = ieee80211_is_radar_required(local);
Simon Wunderlich164eb022013-02-08 18:16:20 +0100266
267 if (radar_enabled == chanctx->conf.radar_enabled)
268 return;
269
270 chanctx->conf.radar_enabled = radar_enabled;
271 local->radar_detect_enabled = chanctx->conf.radar_enabled;
272
273 if (!local->use_chanctx) {
274 local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
275 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
276 }
277
278 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
279}
280
Johannes Berg04ecd252012-09-11 14:34:12 +0200281void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
282 struct ieee80211_chanctx *chanctx)
283{
284 struct ieee80211_sub_if_data *sdata;
285 u8 rx_chains_static, rx_chains_dynamic;
286
287 lockdep_assert_held(&local->chanctx_mtx);
288
289 rx_chains_static = 1;
290 rx_chains_dynamic = 1;
291
292 rcu_read_lock();
293 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
294 u8 needed_static, needed_dynamic;
295
296 if (!ieee80211_sdata_running(sdata))
297 continue;
298
299 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
300 &chanctx->conf)
301 continue;
302
303 switch (sdata->vif.type) {
304 case NL80211_IFTYPE_P2P_DEVICE:
305 continue;
306 case NL80211_IFTYPE_STATION:
307 if (!sdata->u.mgd.associated)
308 continue;
309 break;
310 case NL80211_IFTYPE_AP_VLAN:
311 continue;
312 case NL80211_IFTYPE_AP:
313 case NL80211_IFTYPE_ADHOC:
314 case NL80211_IFTYPE_WDS:
315 case NL80211_IFTYPE_MESH_POINT:
316 break;
317 default:
318 WARN_ON_ONCE(1);
319 }
320
321 switch (sdata->smps_mode) {
322 default:
323 WARN_ONCE(1, "Invalid SMPS mode %d\n",
324 sdata->smps_mode);
325 /* fall through */
326 case IEEE80211_SMPS_OFF:
327 needed_static = sdata->needed_rx_chains;
328 needed_dynamic = sdata->needed_rx_chains;
329 break;
330 case IEEE80211_SMPS_DYNAMIC:
331 needed_static = 1;
332 needed_dynamic = sdata->needed_rx_chains;
333 break;
334 case IEEE80211_SMPS_STATIC:
335 needed_static = 1;
336 needed_dynamic = 1;
337 break;
338 }
339
340 rx_chains_static = max(rx_chains_static, needed_static);
341 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
342 }
343 rcu_read_unlock();
344
345 if (!local->use_chanctx) {
346 if (rx_chains_static > 1)
347 local->smps_mode = IEEE80211_SMPS_OFF;
348 else if (rx_chains_dynamic > 1)
349 local->smps_mode = IEEE80211_SMPS_DYNAMIC;
350 else
351 local->smps_mode = IEEE80211_SMPS_STATIC;
352 ieee80211_hw_config(local, 0);
353 }
354
355 if (rx_chains_static == chanctx->conf.rx_chains_static &&
356 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
357 return;
358
359 chanctx->conf.rx_chains_static = rx_chains_static;
360 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
361 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
362}
363
Michal Kaziord01a1e62012-06-26 14:37:16 +0200364int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
Johannes Berg4bf88532012-11-09 11:39:59 +0100365 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +0200366 enum ieee80211_chanctx_mode mode)
367{
368 struct ieee80211_local *local = sdata->local;
369 struct ieee80211_chanctx *ctx;
370 int ret;
371
Johannes Berg55de9082012-07-26 17:24:39 +0200372 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
373
Michal Kaziord01a1e62012-06-26 14:37:16 +0200374 mutex_lock(&local->chanctx_mtx);
375 __ieee80211_vif_release_channel(sdata);
376
Johannes Berg4bf88532012-11-09 11:39:59 +0100377 ctx = ieee80211_find_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200378 if (!ctx)
Johannes Berg4bf88532012-11-09 11:39:59 +0100379 ctx = ieee80211_new_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200380 if (IS_ERR(ctx)) {
381 ret = PTR_ERR(ctx);
382 goto out;
383 }
384
Johannes Berg4bf88532012-11-09 11:39:59 +0100385 sdata->vif.bss_conf.chandef = *chandef;
Johannes Berg55de9082012-07-26 17:24:39 +0200386
Michal Kaziord01a1e62012-06-26 14:37:16 +0200387 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
388 if (ret) {
389 /* if assign fails refcount stays the same */
390 if (ctx->refcount == 0)
391 ieee80211_free_chanctx(local, ctx);
392 goto out;
393 }
394
Johannes Berg04ecd252012-09-11 14:34:12 +0200395 ieee80211_recalc_smps_chanctx(local, ctx);
Simon Wunderlich164eb022013-02-08 18:16:20 +0100396 ieee80211_recalc_radar_chanctx(local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200397 out:
398 mutex_unlock(&local->chanctx_mtx);
399 return ret;
400}
401
Johannes Berg2c9b7352013-02-07 21:37:29 +0100402int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
403 const struct cfg80211_chan_def *chandef,
404 u32 *changed)
405{
406 struct ieee80211_local *local = sdata->local;
407 struct ieee80211_chanctx_conf *conf;
408 struct ieee80211_chanctx *ctx;
409 int ret;
410
411 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
412 IEEE80211_CHAN_DISABLED))
413 return -EINVAL;
414
415 mutex_lock(&local->chanctx_mtx);
416 if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
417 ret = 0;
418 goto out;
419 }
420
421 if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
422 sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
423 ret = -EINVAL;
424 goto out;
425 }
426
427 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
428 lockdep_is_held(&local->chanctx_mtx));
429 if (!conf) {
430 ret = -EINVAL;
431 goto out;
432 }
433
434 ctx = container_of(conf, struct ieee80211_chanctx, conf);
435 if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
436 ret = -EINVAL;
437 goto out;
438 }
439
440 sdata->vif.bss_conf.chandef = *chandef;
441
442 ieee80211_recalc_chanctx_chantype(local, ctx);
443
444 *changed |= BSS_CHANGED_BANDWIDTH;
445 ret = 0;
446 out:
447 mutex_unlock(&local->chanctx_mtx);
448 return ret;
449}
450
Michal Kaziord01a1e62012-06-26 14:37:16 +0200451void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
452{
Johannes Berg55de9082012-07-26 17:24:39 +0200453 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
454
Michal Kaziord01a1e62012-06-26 14:37:16 +0200455 mutex_lock(&sdata->local->chanctx_mtx);
456 __ieee80211_vif_release_channel(sdata);
457 mutex_unlock(&sdata->local->chanctx_mtx);
458}
Johannes Berg3448c002012-09-11 17:57:42 +0200459
Johannes Berg4d76d212012-12-11 20:38:41 +0100460void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
461{
462 struct ieee80211_local *local = sdata->local;
463 struct ieee80211_sub_if_data *ap;
464 struct ieee80211_chanctx_conf *conf;
465
466 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
467 return;
468
469 ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
470
471 mutex_lock(&local->chanctx_mtx);
472
473 conf = rcu_dereference_protected(ap->vif.chanctx_conf,
474 lockdep_is_held(&local->chanctx_mtx));
475 rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
476 mutex_unlock(&local->chanctx_mtx);
477}
478
Johannes Berg1f4ac5a2013-02-08 12:07:44 +0100479void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
480 bool clear)
481{
482 struct ieee80211_local *local = sdata->local;
483 struct ieee80211_sub_if_data *vlan;
484 struct ieee80211_chanctx_conf *conf;
485
486 ASSERT_RTNL();
487
488 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
489 return;
490
491 mutex_lock(&local->chanctx_mtx);
492
493 /*
494 * Check that conf exists, even when clearing this function
495 * must be called with the AP's channel context still there
496 * as it would otherwise cause VLANs to have an invalid
497 * channel context pointer for a while, possibly pointing
498 * to a channel context that has already been freed.
499 */
500 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
501 lockdep_is_held(&local->chanctx_mtx));
502 WARN_ON(!conf);
503
504 if (clear)
505 conf = NULL;
506
507 list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
508 rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
509
510 mutex_unlock(&local->chanctx_mtx);
511}
512
Johannes Berg3448c002012-09-11 17:57:42 +0200513void ieee80211_iter_chan_contexts_atomic(
514 struct ieee80211_hw *hw,
515 void (*iter)(struct ieee80211_hw *hw,
516 struct ieee80211_chanctx_conf *chanctx_conf,
517 void *data),
518 void *iter_data)
519{
520 struct ieee80211_local *local = hw_to_local(hw);
521 struct ieee80211_chanctx *ctx;
522
523 rcu_read_lock();
524 list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
Johannes Berg8a61af62012-12-13 17:42:30 +0100525 if (ctx->driver_present)
526 iter(hw, &ctx->conf, iter_data);
Johannes Berg3448c002012-09-11 17:57:42 +0200527 rcu_read_unlock();
528}
529EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);