blob: e4166b3af5d46e70c286b540c3a12e294d1a1af4 [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
Michal Kaziorc2b90ad2014-04-09 15:29:24 +020012static int ieee80211_num_chanctx(struct ieee80211_local *local)
13{
14 struct ieee80211_chanctx *ctx;
15 int num = 0;
16
17 lockdep_assert_held(&local->chanctx_mtx);
18
19 list_for_each_entry(ctx, &local->chanctx_list, list)
20 num++;
21
22 return num;
23}
24
25static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local)
26{
27 lockdep_assert_held(&local->chanctx_mtx);
28 return ieee80211_num_chanctx(local) < ieee80211_max_num_channels(local);
29}
30
Michal Kazior02881572014-04-09 15:29:28 +020031static const struct cfg80211_chan_def *
32ieee80211_chanctx_reserved_chandef(struct ieee80211_local *local,
33 struct ieee80211_chanctx *ctx,
34 const struct cfg80211_chan_def *compat)
35{
36 struct ieee80211_sub_if_data *sdata;
37
38 lockdep_assert_held(&local->chanctx_mtx);
39
40 list_for_each_entry(sdata, &ctx->reserved_vifs,
41 reserved_chanctx_list) {
42 if (!compat)
43 compat = &sdata->reserved_chandef;
44
45 compat = cfg80211_chandef_compatible(&sdata->reserved_chandef,
46 compat);
47 if (!compat)
48 break;
49 }
50
51 return compat;
52}
53
Michal Kazior13f348a2014-04-09 15:29:29 +020054static const struct cfg80211_chan_def *
55ieee80211_chanctx_non_reserved_chandef(struct ieee80211_local *local,
56 struct ieee80211_chanctx *ctx,
57 const struct cfg80211_chan_def *compat)
58{
59 struct ieee80211_sub_if_data *sdata;
60
61 lockdep_assert_held(&local->chanctx_mtx);
62
63 list_for_each_entry(sdata, &ctx->assigned_vifs,
64 assigned_chanctx_list) {
65 if (sdata->reserved_chanctx != NULL)
66 continue;
67
68 if (!compat)
69 compat = &sdata->vif.bss_conf.chandef;
70
71 compat = cfg80211_chandef_compatible(
72 &sdata->vif.bss_conf.chandef, compat);
73 if (!compat)
74 break;
75 }
76
77 return compat;
78}
79
80static const struct cfg80211_chan_def *
81ieee80211_chanctx_combined_chandef(struct ieee80211_local *local,
82 struct ieee80211_chanctx *ctx,
83 const struct cfg80211_chan_def *compat)
84{
85 lockdep_assert_held(&local->chanctx_mtx);
86
87 compat = ieee80211_chanctx_reserved_chandef(local, ctx, compat);
88 if (!compat)
89 return NULL;
90
91 compat = ieee80211_chanctx_non_reserved_chandef(local, ctx, compat);
92 if (!compat)
93 return NULL;
94
95 return compat;
96}
97
98static bool
99ieee80211_chanctx_can_reserve_chandef(struct ieee80211_local *local,
100 struct ieee80211_chanctx *ctx,
101 const struct cfg80211_chan_def *def)
102{
103 lockdep_assert_held(&local->chanctx_mtx);
104
105 if (ieee80211_chanctx_combined_chandef(local, ctx, def))
106 return true;
107
108 if (!list_empty(&ctx->reserved_vifs) &&
109 ieee80211_chanctx_reserved_chandef(local, ctx, def))
110 return true;
111
112 return false;
113}
114
115static struct ieee80211_chanctx *
116ieee80211_find_reservation_chanctx(struct ieee80211_local *local,
117 const struct cfg80211_chan_def *chandef,
118 enum ieee80211_chanctx_mode mode)
119{
120 struct ieee80211_chanctx *ctx;
121
122 lockdep_assert_held(&local->chanctx_mtx);
123
124 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
125 return NULL;
126
127 list_for_each_entry(ctx, &local->chanctx_list, list) {
128 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
129 continue;
130
131 if (!ieee80211_chanctx_can_reserve_chandef(local, ctx,
132 chandef))
133 continue;
134
135 return ctx;
136 }
137
138 return NULL;
139}
140
Eliad Peller21f659b2013-11-11 20:14:01 +0200141static enum nl80211_chan_width ieee80211_get_sta_bw(struct ieee80211_sta *sta)
142{
143 switch (sta->bandwidth) {
144 case IEEE80211_STA_RX_BW_20:
145 if (sta->ht_cap.ht_supported)
146 return NL80211_CHAN_WIDTH_20;
147 else
148 return NL80211_CHAN_WIDTH_20_NOHT;
149 case IEEE80211_STA_RX_BW_40:
150 return NL80211_CHAN_WIDTH_40;
151 case IEEE80211_STA_RX_BW_80:
152 return NL80211_CHAN_WIDTH_80;
153 case IEEE80211_STA_RX_BW_160:
154 /*
155 * This applied for both 160 and 80+80. since we use
156 * the returned value to consider degradation of
157 * ctx->conf.min_def, we have to make sure to take
158 * the bigger one (NL80211_CHAN_WIDTH_160).
159 * Otherwise we might try degrading even when not
160 * needed, as the max required sta_bw returned (80+80)
161 * might be smaller than the configured bw (160).
162 */
163 return NL80211_CHAN_WIDTH_160;
164 default:
165 WARN_ON(1);
166 return NL80211_CHAN_WIDTH_20;
167 }
168}
169
170static enum nl80211_chan_width
171ieee80211_get_max_required_bw(struct ieee80211_sub_if_data *sdata)
172{
173 enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
174 struct sta_info *sta;
175
176 rcu_read_lock();
177 list_for_each_entry_rcu(sta, &sdata->local->sta_list, list) {
178 if (sdata != sta->sdata &&
179 !(sta->sdata->bss && sta->sdata->bss == sdata->bss))
180 continue;
181
182 if (!sta->uploaded)
183 continue;
184
185 max_bw = max(max_bw, ieee80211_get_sta_bw(&sta->sta));
186 }
187 rcu_read_unlock();
188
189 return max_bw;
190}
191
192static enum nl80211_chan_width
193ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
194 struct ieee80211_chanctx_conf *conf)
195{
196 struct ieee80211_sub_if_data *sdata;
197 enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
198
199 rcu_read_lock();
200 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
201 struct ieee80211_vif *vif = &sdata->vif;
202 enum nl80211_chan_width width = NL80211_CHAN_WIDTH_20_NOHT;
203
204 if (!ieee80211_sdata_running(sdata))
205 continue;
206
207 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
208 continue;
209
210 switch (vif->type) {
211 case NL80211_IFTYPE_AP:
212 case NL80211_IFTYPE_AP_VLAN:
213 width = ieee80211_get_max_required_bw(sdata);
214 break;
215 case NL80211_IFTYPE_P2P_DEVICE:
216 continue;
217 case NL80211_IFTYPE_STATION:
218 case NL80211_IFTYPE_ADHOC:
219 case NL80211_IFTYPE_WDS:
220 case NL80211_IFTYPE_MESH_POINT:
221 width = vif->bss_conf.chandef.width;
222 break;
223 case NL80211_IFTYPE_UNSPECIFIED:
224 case NUM_NL80211_IFTYPES:
225 case NL80211_IFTYPE_MONITOR:
226 case NL80211_IFTYPE_P2P_CLIENT:
227 case NL80211_IFTYPE_P2P_GO:
228 WARN_ON_ONCE(1);
229 }
230 max_bw = max(max_bw, width);
231 }
Eliad Peller1c37a722014-03-03 13:37:14 +0200232
233 /* use the configured bandwidth in case of monitor interface */
234 sdata = rcu_dereference(local->monitor_sdata);
235 if (sdata && rcu_access_pointer(sdata->vif.chanctx_conf) == conf)
236 max_bw = max(max_bw, conf->def.width);
237
Eliad Peller21f659b2013-11-11 20:14:01 +0200238 rcu_read_unlock();
239
240 return max_bw;
241}
242
243/*
244 * recalc the min required chan width of the channel context, which is
245 * the max of min required widths of all the interfaces bound to this
246 * channel context.
247 */
248void ieee80211_recalc_chanctx_min_def(struct ieee80211_local *local,
249 struct ieee80211_chanctx *ctx)
250{
251 enum nl80211_chan_width max_bw;
252 struct cfg80211_chan_def min_def;
253
254 lockdep_assert_held(&local->chanctx_mtx);
255
256 /* don't optimize 5MHz, 10MHz, and radar_enabled confs */
257 if (ctx->conf.def.width == NL80211_CHAN_WIDTH_5 ||
258 ctx->conf.def.width == NL80211_CHAN_WIDTH_10 ||
259 ctx->conf.radar_enabled) {
260 ctx->conf.min_def = ctx->conf.def;
261 return;
262 }
263
264 max_bw = ieee80211_get_chanctx_max_required_bw(local, &ctx->conf);
265
266 /* downgrade chandef up to max_bw */
267 min_def = ctx->conf.def;
268 while (min_def.width > max_bw)
269 ieee80211_chandef_downgrade(&min_def);
270
271 if (cfg80211_chandef_identical(&ctx->conf.min_def, &min_def))
272 return;
273
274 ctx->conf.min_def = min_def;
275 if (!ctx->driver_present)
276 return;
277
278 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_MIN_WIDTH);
279}
280
Johannes Berg18942d32013-02-07 21:30:37 +0100281static void ieee80211_change_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +0100282 struct ieee80211_chanctx *ctx,
283 const struct cfg80211_chan_def *chandef)
Michal Kazior23a85b452012-06-26 14:37:21 +0200284{
Johannes Berg4bf88532012-11-09 11:39:59 +0100285 if (cfg80211_chandef_identical(&ctx->conf.def, chandef))
Michal Kaziore89a96f2012-06-26 14:37:22 +0200286 return;
287
Johannes Berg4bf88532012-11-09 11:39:59 +0100288 WARN_ON(!cfg80211_chandef_compatible(&ctx->conf.def, chandef));
289
290 ctx->conf.def = *chandef;
291 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_WIDTH);
Eliad Peller21f659b2013-11-11 20:14:01 +0200292 ieee80211_recalc_chanctx_min_def(local, ctx);
Johannes Berg55de9082012-07-26 17:24:39 +0200293
294 if (!local->use_chanctx) {
Karl Beldan675a0b02013-03-25 16:26:57 +0100295 local->_oper_chandef = *chandef;
Johannes Berg55de9082012-07-26 17:24:39 +0200296 ieee80211_hw_config(local, 0);
297 }
Johannes Berg0aaffa92010-05-05 15:28:27 +0200298}
Michal Kaziord01a1e62012-06-26 14:37:16 +0200299
300static struct ieee80211_chanctx *
301ieee80211_find_chanctx(struct ieee80211_local *local,
Johannes Berg4bf88532012-11-09 11:39:59 +0100302 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +0200303 enum ieee80211_chanctx_mode mode)
304{
305 struct ieee80211_chanctx *ctx;
306
307 lockdep_assert_held(&local->chanctx_mtx);
308
309 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
310 return NULL;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200311
312 list_for_each_entry(ctx, &local->chanctx_list, list) {
Johannes Berg4bf88532012-11-09 11:39:59 +0100313 const struct cfg80211_chan_def *compat;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200314
Michal Kazior02881572014-04-09 15:29:28 +0200315 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
Michal Kaziord01a1e62012-06-26 14:37:16 +0200316 continue;
Johannes Berg4bf88532012-11-09 11:39:59 +0100317
318 compat = cfg80211_chandef_compatible(&ctx->conf.def, chandef);
319 if (!compat)
Michal Kaziord01a1e62012-06-26 14:37:16 +0200320 continue;
321
Michal Kazior02881572014-04-09 15:29:28 +0200322 compat = ieee80211_chanctx_reserved_chandef(local, ctx,
323 compat);
324 if (!compat)
325 continue;
326
Johannes Berg18942d32013-02-07 21:30:37 +0100327 ieee80211_change_chanctx(local, ctx, compat);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200328
Michal Kaziord01a1e62012-06-26 14:37:16 +0200329 return ctx;
330 }
331
332 return NULL;
333}
334
Simon Wunderliche4746852013-04-08 22:43:16 +0200335static bool ieee80211_is_radar_required(struct ieee80211_local *local)
336{
337 struct ieee80211_sub_if_data *sdata;
338
Michal Kaziorcc901de2014-01-29 07:56:20 +0100339 lockdep_assert_held(&local->mtx);
340
Simon Wunderliche4746852013-04-08 22:43:16 +0200341 rcu_read_lock();
342 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
343 if (sdata->radar_required) {
344 rcu_read_unlock();
345 return true;
346 }
347 }
348 rcu_read_unlock();
349
350 return false;
351}
352
Michal Kaziord01a1e62012-06-26 14:37:16 +0200353static struct ieee80211_chanctx *
Michal Kaziored68ebc2014-04-09 15:29:30 +0200354ieee80211_alloc_chanctx(struct ieee80211_local *local,
355 const struct cfg80211_chan_def *chandef,
356 enum ieee80211_chanctx_mode mode)
Michal Kaziord01a1e62012-06-26 14:37:16 +0200357{
358 struct ieee80211_chanctx *ctx;
359
360 lockdep_assert_held(&local->chanctx_mtx);
361
362 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
363 if (!ctx)
Michal Kaziored68ebc2014-04-09 15:29:30 +0200364 return NULL;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200365
Michal Kazior484298a2014-04-09 15:29:26 +0200366 INIT_LIST_HEAD(&ctx->assigned_vifs);
Michal Kaziore3afb922014-04-09 15:29:27 +0200367 INIT_LIST_HEAD(&ctx->reserved_vifs);
Johannes Berg4bf88532012-11-09 11:39:59 +0100368 ctx->conf.def = *chandef;
Johannes Berg04ecd252012-09-11 14:34:12 +0200369 ctx->conf.rx_chains_static = 1;
370 ctx->conf.rx_chains_dynamic = 1;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200371 ctx->mode = mode;
Simon Wunderliche4746852013-04-08 22:43:16 +0200372 ctx->conf.radar_enabled = ieee80211_is_radar_required(local);
Eliad Peller21f659b2013-11-11 20:14:01 +0200373 ieee80211_recalc_chanctx_min_def(local, ctx);
Michal Kaziored68ebc2014-04-09 15:29:30 +0200374
375 return ctx;
376}
377
378static int ieee80211_add_chanctx(struct ieee80211_local *local,
379 struct ieee80211_chanctx *ctx)
380{
381 u32 changed;
382 int err;
383
384 lockdep_assert_held(&local->mtx);
385 lockdep_assert_held(&local->chanctx_mtx);
386
Simon Wunderliche4746852013-04-08 22:43:16 +0200387 if (!local->use_chanctx)
388 local->hw.conf.radar_enabled = ctx->conf.radar_enabled;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200389
Johannes Berg382a1032013-03-22 22:30:09 +0100390 /* turn idle off *before* setting channel -- some drivers need that */
391 changed = ieee80211_idle_off(local);
392 if (changed)
393 ieee80211_hw_config(local, changed);
394
Johannes Berg55de9082012-07-26 17:24:39 +0200395 if (!local->use_chanctx) {
Michal Kaziored68ebc2014-04-09 15:29:30 +0200396 local->_oper_chandef = ctx->conf.def;
Johannes Berg55de9082012-07-26 17:24:39 +0200397 ieee80211_hw_config(local, 0);
398 } else {
399 err = drv_add_chanctx(local, ctx);
400 if (err) {
Johannes Berg382a1032013-03-22 22:30:09 +0100401 ieee80211_recalc_idle(local);
Michal Kaziored68ebc2014-04-09 15:29:30 +0200402 return err;
Johannes Berg55de9082012-07-26 17:24:39 +0200403 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200404 }
405
Michal Kaziored68ebc2014-04-09 15:29:30 +0200406 return 0;
407}
Michal Kaziord01a1e62012-06-26 14:37:16 +0200408
Michal Kaziored68ebc2014-04-09 15:29:30 +0200409static struct ieee80211_chanctx *
410ieee80211_new_chanctx(struct ieee80211_local *local,
411 const struct cfg80211_chan_def *chandef,
412 enum ieee80211_chanctx_mode mode)
413{
414 struct ieee80211_chanctx *ctx;
415 int err;
416
417 lockdep_assert_held(&local->mtx);
418 lockdep_assert_held(&local->chanctx_mtx);
419
420 ctx = ieee80211_alloc_chanctx(local, chandef, mode);
421 if (!ctx)
422 return ERR_PTR(-ENOMEM);
423
424 err = ieee80211_add_chanctx(local, ctx);
425 if (err) {
426 kfree(ctx);
427 return ERR_PTR(err);
428 }
429
430 list_add_rcu(&ctx->list, &local->chanctx_list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200431 return ctx;
432}
433
Michal Kazior1f0d54c2014-04-09 15:29:31 +0200434static void ieee80211_del_chanctx(struct ieee80211_local *local,
435 struct ieee80211_chanctx *ctx)
Michal Kaziord01a1e62012-06-26 14:37:16 +0200436{
437 lockdep_assert_held(&local->chanctx_mtx);
438
Johannes Berg55de9082012-07-26 17:24:39 +0200439 if (!local->use_chanctx) {
Karl Beldan675a0b02013-03-25 16:26:57 +0100440 struct cfg80211_chan_def *chandef = &local->_oper_chandef;
441 chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
442 chandef->center_freq1 = chandef->chan->center_freq;
443 chandef->center_freq2 = 0;
Simon Wunderliche4746852013-04-08 22:43:16 +0200444
445 /* NOTE: Disabling radar is only valid here for
446 * single channel context. To be sure, check it ...
447 */
Michal Kazior1f0d54c2014-04-09 15:29:31 +0200448 WARN_ON(local->hw.conf.radar_enabled &&
449 !list_empty(&local->chanctx_list));
450
Simon Wunderliche4746852013-04-08 22:43:16 +0200451 local->hw.conf.radar_enabled = false;
452
Johannes Berg55de9082012-07-26 17:24:39 +0200453 ieee80211_hw_config(local, 0);
454 } else {
455 drv_remove_chanctx(local, ctx);
456 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200457
Johannes Bergfd0f9792013-02-07 00:14:51 +0100458 ieee80211_recalc_idle(local);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200459}
460
Michal Kazior1f0d54c2014-04-09 15:29:31 +0200461static void ieee80211_free_chanctx(struct ieee80211_local *local,
462 struct ieee80211_chanctx *ctx)
463{
464 lockdep_assert_held(&local->chanctx_mtx);
465
466 WARN_ON_ONCE(ctx->refcount != 0);
467
468 list_del_rcu(&ctx->list);
469 ieee80211_del_chanctx(local, ctx);
470 kfree_rcu(ctx, rcu_head);
471}
472
Johannes Berg4bf88532012-11-09 11:39:59 +0100473static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
474 struct ieee80211_chanctx *ctx)
Michal Kaziore89a96f2012-06-26 14:37:22 +0200475{
476 struct ieee80211_chanctx_conf *conf = &ctx->conf;
477 struct ieee80211_sub_if_data *sdata;
Johannes Berg4bf88532012-11-09 11:39:59 +0100478 const struct cfg80211_chan_def *compat = NULL;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200479
480 lockdep_assert_held(&local->chanctx_mtx);
481
482 rcu_read_lock();
483 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
Johannes Berg4bf88532012-11-09 11:39:59 +0100484
Michal Kaziore89a96f2012-06-26 14:37:22 +0200485 if (!ieee80211_sdata_running(sdata))
486 continue;
487 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
488 continue;
489
Johannes Berg4bf88532012-11-09 11:39:59 +0100490 if (!compat)
491 compat = &sdata->vif.bss_conf.chandef;
492
493 compat = cfg80211_chandef_compatible(
494 &sdata->vif.bss_conf.chandef, compat);
495 if (!compat)
496 break;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200497 }
498 rcu_read_unlock();
499
Johannes Berg4bf88532012-11-09 11:39:59 +0100500 if (WARN_ON_ONCE(!compat))
501 return;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200502
Johannes Berg18942d32013-02-07 21:30:37 +0100503 ieee80211_change_chanctx(local, ctx, compat);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200504}
505
Johannes Berg367bbd12013-12-18 09:36:09 +0100506static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
507 struct ieee80211_chanctx *chanctx)
508{
509 bool radar_enabled;
510
511 lockdep_assert_held(&local->chanctx_mtx);
Johannes Berg34a37402013-12-18 09:43:33 +0100512 /* for setting local->radar_detect_enabled */
513 lockdep_assert_held(&local->mtx);
Johannes Berg367bbd12013-12-18 09:36:09 +0100514
515 radar_enabled = ieee80211_is_radar_required(local);
516
517 if (radar_enabled == chanctx->conf.radar_enabled)
518 return;
519
520 chanctx->conf.radar_enabled = radar_enabled;
521 local->radar_detect_enabled = chanctx->conf.radar_enabled;
522
523 if (!local->use_chanctx) {
524 local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
525 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
526 }
527
528 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
529}
530
Luciano Coelho77eeba92014-03-11 18:24:12 +0200531static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
532 struct ieee80211_chanctx *new_ctx)
Michal Kaziord01a1e62012-06-26 14:37:16 +0200533{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200534 struct ieee80211_local *local = sdata->local;
Luciano Coelho77eeba92014-03-11 18:24:12 +0200535 struct ieee80211_chanctx_conf *conf;
536 struct ieee80211_chanctx *curr_ctx = NULL;
537 int ret = 0;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200538
Luciano Coelho77eeba92014-03-11 18:24:12 +0200539 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
540 lockdep_is_held(&local->chanctx_mtx));
Michal Kaziord01a1e62012-06-26 14:37:16 +0200541
Luciano Coelho77eeba92014-03-11 18:24:12 +0200542 if (conf) {
543 curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200544
Luciano Coelho77eeba92014-03-11 18:24:12 +0200545 curr_ctx->refcount--;
546 drv_unassign_vif_chanctx(local, sdata, curr_ctx);
547 conf = NULL;
Michal Kazior484298a2014-04-09 15:29:26 +0200548 list_del(&sdata->assigned_chanctx_list);
Luciano Coelho77eeba92014-03-11 18:24:12 +0200549 }
550
551 if (new_ctx) {
552 ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
553 if (ret)
554 goto out;
555
556 new_ctx->refcount++;
557 conf = &new_ctx->conf;
Michal Kazior484298a2014-04-09 15:29:26 +0200558 list_add(&sdata->assigned_chanctx_list,
559 &new_ctx->assigned_vifs);
Luciano Coelho77eeba92014-03-11 18:24:12 +0200560 }
561
562out:
563 rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
564
565 sdata->vif.bss_conf.idle = !conf;
566
567 if (curr_ctx && curr_ctx->refcount > 0) {
568 ieee80211_recalc_chanctx_chantype(local, curr_ctx);
569 ieee80211_recalc_smps_chanctx(local, curr_ctx);
570 ieee80211_recalc_radar_chanctx(local, curr_ctx);
571 ieee80211_recalc_chanctx_min_def(local, curr_ctx);
572 }
573
574 if (new_ctx && new_ctx->refcount > 0) {
575 ieee80211_recalc_txpower(sdata);
576 ieee80211_recalc_chanctx_min_def(local, new_ctx);
577 }
Johannes Berg5bbe754d2013-02-13 13:50:51 +0100578
579 if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
580 sdata->vif.type != NL80211_IFTYPE_MONITOR)
Luciano Coelho77eeba92014-03-11 18:24:12 +0200581 ieee80211_bss_info_change_notify(sdata,
582 BSS_CHANGED_IDLE);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100583
Luciano Coelho77eeba92014-03-11 18:24:12 +0200584 return ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200585}
586
587static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
588{
589 struct ieee80211_local *local = sdata->local;
590 struct ieee80211_chanctx_conf *conf;
591 struct ieee80211_chanctx *ctx;
592
593 lockdep_assert_held(&local->chanctx_mtx);
594
595 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
596 lockdep_is_held(&local->chanctx_mtx));
597 if (!conf)
598 return;
599
600 ctx = container_of(conf, struct ieee80211_chanctx, conf);
601
Luciano Coelho11335a52013-10-30 13:09:39 +0200602 if (sdata->reserved_chanctx)
603 ieee80211_vif_unreserve_chanctx(sdata);
604
Luciano Coelho77eeba92014-03-11 18:24:12 +0200605 ieee80211_assign_vif_chanctx(sdata, NULL);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200606 if (ctx->refcount == 0)
607 ieee80211_free_chanctx(local, ctx);
608}
609
Johannes Berg04ecd252012-09-11 14:34:12 +0200610void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
611 struct ieee80211_chanctx *chanctx)
612{
613 struct ieee80211_sub_if_data *sdata;
614 u8 rx_chains_static, rx_chains_dynamic;
615
616 lockdep_assert_held(&local->chanctx_mtx);
617
618 rx_chains_static = 1;
619 rx_chains_dynamic = 1;
620
621 rcu_read_lock();
622 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
623 u8 needed_static, needed_dynamic;
624
625 if (!ieee80211_sdata_running(sdata))
626 continue;
627
628 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
629 &chanctx->conf)
630 continue;
631
632 switch (sdata->vif.type) {
633 case NL80211_IFTYPE_P2P_DEVICE:
634 continue;
635 case NL80211_IFTYPE_STATION:
636 if (!sdata->u.mgd.associated)
637 continue;
638 break;
639 case NL80211_IFTYPE_AP_VLAN:
640 continue;
641 case NL80211_IFTYPE_AP:
642 case NL80211_IFTYPE_ADHOC:
643 case NL80211_IFTYPE_WDS:
644 case NL80211_IFTYPE_MESH_POINT:
645 break;
646 default:
647 WARN_ON_ONCE(1);
648 }
649
650 switch (sdata->smps_mode) {
651 default:
652 WARN_ONCE(1, "Invalid SMPS mode %d\n",
653 sdata->smps_mode);
654 /* fall through */
655 case IEEE80211_SMPS_OFF:
656 needed_static = sdata->needed_rx_chains;
657 needed_dynamic = sdata->needed_rx_chains;
658 break;
659 case IEEE80211_SMPS_DYNAMIC:
660 needed_static = 1;
661 needed_dynamic = sdata->needed_rx_chains;
662 break;
663 case IEEE80211_SMPS_STATIC:
664 needed_static = 1;
665 needed_dynamic = 1;
666 break;
667 }
668
669 rx_chains_static = max(rx_chains_static, needed_static);
670 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
671 }
672 rcu_read_unlock();
673
674 if (!local->use_chanctx) {
675 if (rx_chains_static > 1)
676 local->smps_mode = IEEE80211_SMPS_OFF;
677 else if (rx_chains_dynamic > 1)
678 local->smps_mode = IEEE80211_SMPS_DYNAMIC;
679 else
680 local->smps_mode = IEEE80211_SMPS_STATIC;
681 ieee80211_hw_config(local, 0);
682 }
683
684 if (rx_chains_static == chanctx->conf.rx_chains_static &&
685 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
686 return;
687
688 chanctx->conf.rx_chains_static = rx_chains_static;
689 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
690 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
691}
692
Michal Kaziord01a1e62012-06-26 14:37:16 +0200693int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
Johannes Berg4bf88532012-11-09 11:39:59 +0100694 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +0200695 enum ieee80211_chanctx_mode mode)
696{
697 struct ieee80211_local *local = sdata->local;
698 struct ieee80211_chanctx *ctx;
Luciano Coelho73de86a2014-02-13 11:31:59 +0200699 u8 radar_detect_width = 0;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200700 int ret;
701
Johannes Berg34a37402013-12-18 09:43:33 +0100702 lockdep_assert_held(&local->mtx);
703
Johannes Berg55de9082012-07-26 17:24:39 +0200704 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
705
Michal Kaziord01a1e62012-06-26 14:37:16 +0200706 mutex_lock(&local->chanctx_mtx);
Luciano Coelho73de86a2014-02-13 11:31:59 +0200707
708 ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
709 chandef,
710 sdata->wdev.iftype);
711 if (ret < 0)
712 goto out;
713 if (ret > 0)
714 radar_detect_width = BIT(chandef->width);
715
716 sdata->radar_required = ret;
717
718 ret = ieee80211_check_combinations(sdata, chandef, mode,
719 radar_detect_width);
720 if (ret < 0)
721 goto out;
722
Michal Kaziord01a1e62012-06-26 14:37:16 +0200723 __ieee80211_vif_release_channel(sdata);
724
Johannes Berg4bf88532012-11-09 11:39:59 +0100725 ctx = ieee80211_find_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200726 if (!ctx)
Johannes Berg4bf88532012-11-09 11:39:59 +0100727 ctx = ieee80211_new_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200728 if (IS_ERR(ctx)) {
729 ret = PTR_ERR(ctx);
730 goto out;
731 }
732
Johannes Berg4bf88532012-11-09 11:39:59 +0100733 sdata->vif.bss_conf.chandef = *chandef;
Johannes Berg55de9082012-07-26 17:24:39 +0200734
Michal Kaziord01a1e62012-06-26 14:37:16 +0200735 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
736 if (ret) {
737 /* if assign fails refcount stays the same */
738 if (ctx->refcount == 0)
739 ieee80211_free_chanctx(local, ctx);
740 goto out;
741 }
742
Johannes Berg04ecd252012-09-11 14:34:12 +0200743 ieee80211_recalc_smps_chanctx(local, ctx);
Simon Wunderlich164eb022013-02-08 18:16:20 +0100744 ieee80211_recalc_radar_chanctx(local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200745 out:
746 mutex_unlock(&local->chanctx_mtx);
747 return ret;
748}
749
Luciano Coelho33ffd952014-01-30 22:08:16 +0200750static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
751 struct ieee80211_chanctx *ctx,
752 u32 *changed)
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200753{
754 struct ieee80211_local *local = sdata->local;
Luciano Coelho33787fc2013-11-11 20:34:54 +0200755 const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200756 u32 chanctx_changed = 0;
757
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200758 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
759 IEEE80211_CHAN_DISABLED))
760 return -EINVAL;
761
Luciano Coelho33ffd952014-01-30 22:08:16 +0200762 if (ctx->refcount != 1)
763 return -EINVAL;
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200764
765 if (sdata->vif.bss_conf.chandef.width != chandef->width) {
766 chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
767 *changed |= BSS_CHANGED_BANDWIDTH;
768 }
769
770 sdata->vif.bss_conf.chandef = *chandef;
771 ctx->conf.def = *chandef;
772
773 chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
774 drv_change_chanctx(local, ctx, chanctx_changed);
775
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200776 ieee80211_recalc_chanctx_chantype(local, ctx);
777 ieee80211_recalc_smps_chanctx(local, ctx);
778 ieee80211_recalc_radar_chanctx(local, ctx);
Eliad Peller21f659b2013-11-11 20:14:01 +0200779 ieee80211_recalc_chanctx_min_def(local, ctx);
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200780
Luciano Coelho33ffd952014-01-30 22:08:16 +0200781 return 0;
782}
783
784int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
785 u32 *changed)
786{
787 struct ieee80211_local *local = sdata->local;
788 struct ieee80211_chanctx_conf *conf;
789 struct ieee80211_chanctx *ctx;
790 int ret;
791
792 lockdep_assert_held(&local->mtx);
793
794 /* should never be called if not performing a channel switch. */
795 if (WARN_ON(!sdata->vif.csa_active))
796 return -EINVAL;
797
798 mutex_lock(&local->chanctx_mtx);
799 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
800 lockdep_is_held(&local->chanctx_mtx));
801 if (!conf) {
802 ret = -EINVAL;
803 goto out;
804 }
805
806 ctx = container_of(conf, struct ieee80211_chanctx, conf);
807
808 ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200809 out:
810 mutex_unlock(&local->chanctx_mtx);
811 return ret;
812}
813
Luciano Coelho11335a52013-10-30 13:09:39 +0200814static void
815__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
816 bool clear)
817{
818 struct ieee80211_local *local = sdata->local;
819 struct ieee80211_sub_if_data *vlan;
820 struct ieee80211_chanctx_conf *conf;
821
822 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
823 return;
824
825 lockdep_assert_held(&local->mtx);
826
827 /* Check that conf exists, even when clearing this function
828 * must be called with the AP's channel context still there
829 * as it would otherwise cause VLANs to have an invalid
830 * channel context pointer for a while, possibly pointing
831 * to a channel context that has already been freed.
832 */
833 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
834 lockdep_is_held(&local->chanctx_mtx));
835 WARN_ON(!conf);
836
837 if (clear)
838 conf = NULL;
839
840 list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
841 rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
842}
843
844void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
845 bool clear)
846{
847 struct ieee80211_local *local = sdata->local;
848
849 mutex_lock(&local->chanctx_mtx);
850
851 __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
852
853 mutex_unlock(&local->chanctx_mtx);
854}
855
856int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
857{
Michal Kaziore3afb922014-04-09 15:29:27 +0200858 struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
859
Luciano Coelho11335a52013-10-30 13:09:39 +0200860 lockdep_assert_held(&sdata->local->chanctx_mtx);
861
Michal Kaziore3afb922014-04-09 15:29:27 +0200862 if (WARN_ON(!ctx))
Luciano Coelho11335a52013-10-30 13:09:39 +0200863 return -EINVAL;
864
Michal Kaziore3afb922014-04-09 15:29:27 +0200865 list_del(&sdata->reserved_chanctx_list);
Luciano Coelho11335a52013-10-30 13:09:39 +0200866 sdata->reserved_chanctx = NULL;
867
Michal Kaziore3afb922014-04-09 15:29:27 +0200868 if (--ctx->refcount == 0)
869 ieee80211_free_chanctx(sdata->local, ctx);
870
Luciano Coelho11335a52013-10-30 13:09:39 +0200871 return 0;
872}
873
874int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
875 const struct cfg80211_chan_def *chandef,
Michal Kazior09332482014-04-09 15:29:25 +0200876 enum ieee80211_chanctx_mode mode,
877 bool radar_required)
Luciano Coelho11335a52013-10-30 13:09:39 +0200878{
879 struct ieee80211_local *local = sdata->local;
880 struct ieee80211_chanctx_conf *conf;
881 struct ieee80211_chanctx *new_ctx, *curr_ctx;
882 int ret = 0;
883
884 mutex_lock(&local->chanctx_mtx);
885
886 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
887 lockdep_is_held(&local->chanctx_mtx));
888 if (!conf) {
889 ret = -EINVAL;
890 goto out;
891 }
892
893 curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
894
Michal Kazior13f348a2014-04-09 15:29:29 +0200895 new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
Luciano Coelho11335a52013-10-30 13:09:39 +0200896 if (!new_ctx) {
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200897 if (curr_ctx->refcount == 1 &&
898 (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
899 /* if we're the only users of the chanctx and
900 * the driver supports changing a running
901 * context, reserve our current context
902 */
903 new_ctx = curr_ctx;
Michal Kaziorc2b90ad2014-04-09 15:29:24 +0200904 } else if (ieee80211_can_create_new_chanctx(local)) {
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200905 /* create a new context and reserve it */
906 new_ctx = ieee80211_new_chanctx(local, chandef, mode);
907 if (IS_ERR(new_ctx)) {
908 ret = PTR_ERR(new_ctx);
909 goto out;
910 }
Michal Kaziorc2b90ad2014-04-09 15:29:24 +0200911 } else {
912 ret = -EBUSY;
913 goto out;
Luciano Coelho11335a52013-10-30 13:09:39 +0200914 }
915 }
916
Michal Kaziore3afb922014-04-09 15:29:27 +0200917 list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
Luciano Coelho11335a52013-10-30 13:09:39 +0200918 new_ctx->refcount++;
919 sdata->reserved_chanctx = new_ctx;
920 sdata->reserved_chandef = *chandef;
Michal Kazior09332482014-04-09 15:29:25 +0200921 sdata->reserved_radar_required = radar_required;
Luciano Coelho11335a52013-10-30 13:09:39 +0200922out:
923 mutex_unlock(&local->chanctx_mtx);
924 return ret;
925}
926
927int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
928 u32 *changed)
929{
930 struct ieee80211_local *local = sdata->local;
931 struct ieee80211_chanctx *ctx;
932 struct ieee80211_chanctx *old_ctx;
933 struct ieee80211_chanctx_conf *conf;
934 int ret;
935 u32 tmp_changed = *changed;
936
937 /* TODO: need to recheck if the chandef is usable etc.? */
938
939 lockdep_assert_held(&local->mtx);
940
941 mutex_lock(&local->chanctx_mtx);
942
943 ctx = sdata->reserved_chanctx;
944 if (WARN_ON(!ctx)) {
945 ret = -EINVAL;
946 goto out;
947 }
948
949 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
950 lockdep_is_held(&local->chanctx_mtx));
951 if (!conf) {
952 ret = -EINVAL;
953 goto out;
954 }
955
956 old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
957
958 if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
959 tmp_changed |= BSS_CHANGED_BANDWIDTH;
960
961 sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
962
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200963 /* unref our reservation */
Luciano Coelho11335a52013-10-30 13:09:39 +0200964 ctx->refcount--;
965 sdata->reserved_chanctx = NULL;
Michal Kazior09332482014-04-09 15:29:25 +0200966 sdata->radar_required = sdata->reserved_radar_required;
Michal Kaziore3afb922014-04-09 15:29:27 +0200967 list_del(&sdata->reserved_chanctx_list);
Luciano Coelho11335a52013-10-30 13:09:39 +0200968
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200969 if (old_ctx == ctx) {
970 /* This is our own context, just change it */
971 ret = __ieee80211_vif_change_channel(sdata, old_ctx,
972 &tmp_changed);
973 if (ret)
974 goto out;
975 } else {
976 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
977 if (old_ctx->refcount == 0)
978 ieee80211_free_chanctx(local, old_ctx);
979 if (ret) {
980 /* if assign fails refcount stays the same */
981 if (ctx->refcount == 0)
982 ieee80211_free_chanctx(local, ctx);
983 goto out;
984 }
Luciano Coelho11335a52013-10-30 13:09:39 +0200985
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200986 if (sdata->vif.type == NL80211_IFTYPE_AP)
987 __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
988 }
Luciano Coelho11335a52013-10-30 13:09:39 +0200989
990 *changed = tmp_changed;
991
992 ieee80211_recalc_chanctx_chantype(local, ctx);
993 ieee80211_recalc_smps_chanctx(local, ctx);
994 ieee80211_recalc_radar_chanctx(local, ctx);
995 ieee80211_recalc_chanctx_min_def(local, ctx);
996out:
997 mutex_unlock(&local->chanctx_mtx);
998 return ret;
999}
1000
Johannes Berg2c9b7352013-02-07 21:37:29 +01001001int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
1002 const struct cfg80211_chan_def *chandef,
1003 u32 *changed)
1004{
1005 struct ieee80211_local *local = sdata->local;
1006 struct ieee80211_chanctx_conf *conf;
1007 struct ieee80211_chanctx *ctx;
1008 int ret;
1009
1010 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
1011 IEEE80211_CHAN_DISABLED))
1012 return -EINVAL;
1013
1014 mutex_lock(&local->chanctx_mtx);
1015 if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
1016 ret = 0;
1017 goto out;
1018 }
1019
1020 if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
1021 sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
1022 ret = -EINVAL;
1023 goto out;
1024 }
1025
1026 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1027 lockdep_is_held(&local->chanctx_mtx));
1028 if (!conf) {
1029 ret = -EINVAL;
1030 goto out;
1031 }
1032
1033 ctx = container_of(conf, struct ieee80211_chanctx, conf);
1034 if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
1035 ret = -EINVAL;
1036 goto out;
1037 }
1038
1039 sdata->vif.bss_conf.chandef = *chandef;
1040
1041 ieee80211_recalc_chanctx_chantype(local, ctx);
1042
1043 *changed |= BSS_CHANGED_BANDWIDTH;
1044 ret = 0;
1045 out:
1046 mutex_unlock(&local->chanctx_mtx);
1047 return ret;
1048}
1049
Michal Kaziord01a1e62012-06-26 14:37:16 +02001050void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
1051{
Johannes Berg55de9082012-07-26 17:24:39 +02001052 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
1053
Johannes Berg34a37402013-12-18 09:43:33 +01001054 lockdep_assert_held(&sdata->local->mtx);
1055
Michal Kaziord01a1e62012-06-26 14:37:16 +02001056 mutex_lock(&sdata->local->chanctx_mtx);
1057 __ieee80211_vif_release_channel(sdata);
1058 mutex_unlock(&sdata->local->chanctx_mtx);
1059}
Johannes Berg3448c002012-09-11 17:57:42 +02001060
Johannes Berg4d76d212012-12-11 20:38:41 +01001061void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
1062{
1063 struct ieee80211_local *local = sdata->local;
1064 struct ieee80211_sub_if_data *ap;
1065 struct ieee80211_chanctx_conf *conf;
1066
1067 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
1068 return;
1069
1070 ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
1071
1072 mutex_lock(&local->chanctx_mtx);
1073
1074 conf = rcu_dereference_protected(ap->vif.chanctx_conf,
1075 lockdep_is_held(&local->chanctx_mtx));
1076 rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
1077 mutex_unlock(&local->chanctx_mtx);
1078}
1079
Johannes Berg3448c002012-09-11 17:57:42 +02001080void ieee80211_iter_chan_contexts_atomic(
1081 struct ieee80211_hw *hw,
1082 void (*iter)(struct ieee80211_hw *hw,
1083 struct ieee80211_chanctx_conf *chanctx_conf,
1084 void *data),
1085 void *iter_data)
1086{
1087 struct ieee80211_local *local = hw_to_local(hw);
1088 struct ieee80211_chanctx *ctx;
1089
1090 rcu_read_lock();
1091 list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
Johannes Berg8a61af62012-12-13 17:42:30 +01001092 if (ctx->driver_present)
1093 iter(hw, &ctx->conf, iter_data);
Johannes Berg3448c002012-09-11 17:57:42 +02001094 rcu_read_unlock();
1095}
1096EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);