blob: 247133fe36bce630068c7af36fec8bb96bd2c9ac [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
434static void ieee80211_free_chanctx(struct ieee80211_local *local,
435 struct ieee80211_chanctx *ctx)
436{
Simon Wunderliche4746852013-04-08 22:43:16 +0200437 bool check_single_channel = false;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200438 lockdep_assert_held(&local->chanctx_mtx);
439
440 WARN_ON_ONCE(ctx->refcount != 0);
441
Johannes Berg55de9082012-07-26 17:24:39 +0200442 if (!local->use_chanctx) {
Karl Beldan675a0b02013-03-25 16:26:57 +0100443 struct cfg80211_chan_def *chandef = &local->_oper_chandef;
444 chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
445 chandef->center_freq1 = chandef->chan->center_freq;
446 chandef->center_freq2 = 0;
Simon Wunderliche4746852013-04-08 22:43:16 +0200447
448 /* NOTE: Disabling radar is only valid here for
449 * single channel context. To be sure, check it ...
450 */
451 if (local->hw.conf.radar_enabled)
452 check_single_channel = true;
453 local->hw.conf.radar_enabled = false;
454
Johannes Berg55de9082012-07-26 17:24:39 +0200455 ieee80211_hw_config(local, 0);
456 } else {
457 drv_remove_chanctx(local, ctx);
458 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200459
Johannes Berg3448c002012-09-11 17:57:42 +0200460 list_del_rcu(&ctx->list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200461 kfree_rcu(ctx, rcu_head);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100462
Simon Wunderliche4746852013-04-08 22:43:16 +0200463 /* throw a warning if this wasn't the only channel context. */
464 WARN_ON(check_single_channel && !list_empty(&local->chanctx_list));
465
Johannes Bergfd0f9792013-02-07 00:14:51 +0100466 ieee80211_recalc_idle(local);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200467}
468
Johannes Berg4bf88532012-11-09 11:39:59 +0100469static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
470 struct ieee80211_chanctx *ctx)
Michal Kaziore89a96f2012-06-26 14:37:22 +0200471{
472 struct ieee80211_chanctx_conf *conf = &ctx->conf;
473 struct ieee80211_sub_if_data *sdata;
Johannes Berg4bf88532012-11-09 11:39:59 +0100474 const struct cfg80211_chan_def *compat = NULL;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200475
476 lockdep_assert_held(&local->chanctx_mtx);
477
478 rcu_read_lock();
479 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
Johannes Berg4bf88532012-11-09 11:39:59 +0100480
Michal Kaziore89a96f2012-06-26 14:37:22 +0200481 if (!ieee80211_sdata_running(sdata))
482 continue;
483 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
484 continue;
485
Johannes Berg4bf88532012-11-09 11:39:59 +0100486 if (!compat)
487 compat = &sdata->vif.bss_conf.chandef;
488
489 compat = cfg80211_chandef_compatible(
490 &sdata->vif.bss_conf.chandef, compat);
491 if (!compat)
492 break;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200493 }
494 rcu_read_unlock();
495
Johannes Berg4bf88532012-11-09 11:39:59 +0100496 if (WARN_ON_ONCE(!compat))
497 return;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200498
Johannes Berg18942d32013-02-07 21:30:37 +0100499 ieee80211_change_chanctx(local, ctx, compat);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200500}
501
Johannes Berg367bbd12013-12-18 09:36:09 +0100502static void ieee80211_recalc_radar_chanctx(struct ieee80211_local *local,
503 struct ieee80211_chanctx *chanctx)
504{
505 bool radar_enabled;
506
507 lockdep_assert_held(&local->chanctx_mtx);
Johannes Berg34a37402013-12-18 09:43:33 +0100508 /* for setting local->radar_detect_enabled */
509 lockdep_assert_held(&local->mtx);
Johannes Berg367bbd12013-12-18 09:36:09 +0100510
511 radar_enabled = ieee80211_is_radar_required(local);
512
513 if (radar_enabled == chanctx->conf.radar_enabled)
514 return;
515
516 chanctx->conf.radar_enabled = radar_enabled;
517 local->radar_detect_enabled = chanctx->conf.radar_enabled;
518
519 if (!local->use_chanctx) {
520 local->hw.conf.radar_enabled = chanctx->conf.radar_enabled;
521 ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
522 }
523
524 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RADAR);
525}
526
Luciano Coelho77eeba92014-03-11 18:24:12 +0200527static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
528 struct ieee80211_chanctx *new_ctx)
Michal Kaziord01a1e62012-06-26 14:37:16 +0200529{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200530 struct ieee80211_local *local = sdata->local;
Luciano Coelho77eeba92014-03-11 18:24:12 +0200531 struct ieee80211_chanctx_conf *conf;
532 struct ieee80211_chanctx *curr_ctx = NULL;
533 int ret = 0;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200534
Luciano Coelho77eeba92014-03-11 18:24:12 +0200535 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
536 lockdep_is_held(&local->chanctx_mtx));
Michal Kaziord01a1e62012-06-26 14:37:16 +0200537
Luciano Coelho77eeba92014-03-11 18:24:12 +0200538 if (conf) {
539 curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200540
Luciano Coelho77eeba92014-03-11 18:24:12 +0200541 curr_ctx->refcount--;
542 drv_unassign_vif_chanctx(local, sdata, curr_ctx);
543 conf = NULL;
Michal Kazior484298a2014-04-09 15:29:26 +0200544 list_del(&sdata->assigned_chanctx_list);
Luciano Coelho77eeba92014-03-11 18:24:12 +0200545 }
546
547 if (new_ctx) {
548 ret = drv_assign_vif_chanctx(local, sdata, new_ctx);
549 if (ret)
550 goto out;
551
552 new_ctx->refcount++;
553 conf = &new_ctx->conf;
Michal Kazior484298a2014-04-09 15:29:26 +0200554 list_add(&sdata->assigned_chanctx_list,
555 &new_ctx->assigned_vifs);
Luciano Coelho77eeba92014-03-11 18:24:12 +0200556 }
557
558out:
559 rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
560
561 sdata->vif.bss_conf.idle = !conf;
562
563 if (curr_ctx && curr_ctx->refcount > 0) {
564 ieee80211_recalc_chanctx_chantype(local, curr_ctx);
565 ieee80211_recalc_smps_chanctx(local, curr_ctx);
566 ieee80211_recalc_radar_chanctx(local, curr_ctx);
567 ieee80211_recalc_chanctx_min_def(local, curr_ctx);
568 }
569
570 if (new_ctx && new_ctx->refcount > 0) {
571 ieee80211_recalc_txpower(sdata);
572 ieee80211_recalc_chanctx_min_def(local, new_ctx);
573 }
Johannes Berg5bbe754d2013-02-13 13:50:51 +0100574
575 if (sdata->vif.type != NL80211_IFTYPE_P2P_DEVICE &&
576 sdata->vif.type != NL80211_IFTYPE_MONITOR)
Luciano Coelho77eeba92014-03-11 18:24:12 +0200577 ieee80211_bss_info_change_notify(sdata,
578 BSS_CHANGED_IDLE);
Johannes Bergfd0f9792013-02-07 00:14:51 +0100579
Luciano Coelho77eeba92014-03-11 18:24:12 +0200580 return ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200581}
582
583static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
584{
585 struct ieee80211_local *local = sdata->local;
586 struct ieee80211_chanctx_conf *conf;
587 struct ieee80211_chanctx *ctx;
588
589 lockdep_assert_held(&local->chanctx_mtx);
590
591 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
592 lockdep_is_held(&local->chanctx_mtx));
593 if (!conf)
594 return;
595
596 ctx = container_of(conf, struct ieee80211_chanctx, conf);
597
Luciano Coelho11335a552013-10-30 13:09:39 +0200598 if (sdata->reserved_chanctx)
599 ieee80211_vif_unreserve_chanctx(sdata);
600
Luciano Coelho77eeba92014-03-11 18:24:12 +0200601 ieee80211_assign_vif_chanctx(sdata, NULL);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200602 if (ctx->refcount == 0)
603 ieee80211_free_chanctx(local, ctx);
604}
605
Johannes Berg04ecd252012-09-11 14:34:12 +0200606void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
607 struct ieee80211_chanctx *chanctx)
608{
609 struct ieee80211_sub_if_data *sdata;
610 u8 rx_chains_static, rx_chains_dynamic;
611
612 lockdep_assert_held(&local->chanctx_mtx);
613
614 rx_chains_static = 1;
615 rx_chains_dynamic = 1;
616
617 rcu_read_lock();
618 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
619 u8 needed_static, needed_dynamic;
620
621 if (!ieee80211_sdata_running(sdata))
622 continue;
623
624 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
625 &chanctx->conf)
626 continue;
627
628 switch (sdata->vif.type) {
629 case NL80211_IFTYPE_P2P_DEVICE:
630 continue;
631 case NL80211_IFTYPE_STATION:
632 if (!sdata->u.mgd.associated)
633 continue;
634 break;
635 case NL80211_IFTYPE_AP_VLAN:
636 continue;
637 case NL80211_IFTYPE_AP:
638 case NL80211_IFTYPE_ADHOC:
639 case NL80211_IFTYPE_WDS:
640 case NL80211_IFTYPE_MESH_POINT:
641 break;
642 default:
643 WARN_ON_ONCE(1);
644 }
645
646 switch (sdata->smps_mode) {
647 default:
648 WARN_ONCE(1, "Invalid SMPS mode %d\n",
649 sdata->smps_mode);
650 /* fall through */
651 case IEEE80211_SMPS_OFF:
652 needed_static = sdata->needed_rx_chains;
653 needed_dynamic = sdata->needed_rx_chains;
654 break;
655 case IEEE80211_SMPS_DYNAMIC:
656 needed_static = 1;
657 needed_dynamic = sdata->needed_rx_chains;
658 break;
659 case IEEE80211_SMPS_STATIC:
660 needed_static = 1;
661 needed_dynamic = 1;
662 break;
663 }
664
665 rx_chains_static = max(rx_chains_static, needed_static);
666 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
667 }
668 rcu_read_unlock();
669
670 if (!local->use_chanctx) {
671 if (rx_chains_static > 1)
672 local->smps_mode = IEEE80211_SMPS_OFF;
673 else if (rx_chains_dynamic > 1)
674 local->smps_mode = IEEE80211_SMPS_DYNAMIC;
675 else
676 local->smps_mode = IEEE80211_SMPS_STATIC;
677 ieee80211_hw_config(local, 0);
678 }
679
680 if (rx_chains_static == chanctx->conf.rx_chains_static &&
681 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
682 return;
683
684 chanctx->conf.rx_chains_static = rx_chains_static;
685 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
686 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
687}
688
Michal Kaziord01a1e62012-06-26 14:37:16 +0200689int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
Johannes Berg4bf88532012-11-09 11:39:59 +0100690 const struct cfg80211_chan_def *chandef,
Michal Kaziord01a1e62012-06-26 14:37:16 +0200691 enum ieee80211_chanctx_mode mode)
692{
693 struct ieee80211_local *local = sdata->local;
694 struct ieee80211_chanctx *ctx;
Luciano Coelho73de86a2014-02-13 11:31:59 +0200695 u8 radar_detect_width = 0;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200696 int ret;
697
Johannes Berg34a37402013-12-18 09:43:33 +0100698 lockdep_assert_held(&local->mtx);
699
Johannes Berg55de9082012-07-26 17:24:39 +0200700 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
701
Michal Kaziord01a1e62012-06-26 14:37:16 +0200702 mutex_lock(&local->chanctx_mtx);
Luciano Coelho73de86a2014-02-13 11:31:59 +0200703
704 ret = cfg80211_chandef_dfs_required(local->hw.wiphy,
705 chandef,
706 sdata->wdev.iftype);
707 if (ret < 0)
708 goto out;
709 if (ret > 0)
710 radar_detect_width = BIT(chandef->width);
711
712 sdata->radar_required = ret;
713
714 ret = ieee80211_check_combinations(sdata, chandef, mode,
715 radar_detect_width);
716 if (ret < 0)
717 goto out;
718
Michal Kaziord01a1e62012-06-26 14:37:16 +0200719 __ieee80211_vif_release_channel(sdata);
720
Johannes Berg4bf88532012-11-09 11:39:59 +0100721 ctx = ieee80211_find_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200722 if (!ctx)
Johannes Berg4bf88532012-11-09 11:39:59 +0100723 ctx = ieee80211_new_chanctx(local, chandef, mode);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200724 if (IS_ERR(ctx)) {
725 ret = PTR_ERR(ctx);
726 goto out;
727 }
728
Johannes Berg4bf88532012-11-09 11:39:59 +0100729 sdata->vif.bss_conf.chandef = *chandef;
Johannes Berg55de9082012-07-26 17:24:39 +0200730
Michal Kaziord01a1e62012-06-26 14:37:16 +0200731 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
732 if (ret) {
733 /* if assign fails refcount stays the same */
734 if (ctx->refcount == 0)
735 ieee80211_free_chanctx(local, ctx);
736 goto out;
737 }
738
Johannes Berg04ecd252012-09-11 14:34:12 +0200739 ieee80211_recalc_smps_chanctx(local, ctx);
Simon Wunderlich164eb022013-02-08 18:16:20 +0100740 ieee80211_recalc_radar_chanctx(local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200741 out:
742 mutex_unlock(&local->chanctx_mtx);
743 return ret;
744}
745
Luciano Coelho33ffd952014-01-30 22:08:16 +0200746static int __ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
747 struct ieee80211_chanctx *ctx,
748 u32 *changed)
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200749{
750 struct ieee80211_local *local = sdata->local;
Luciano Coelho33787fc2013-11-11 20:34:54 +0200751 const struct cfg80211_chan_def *chandef = &sdata->csa_chandef;
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200752 u32 chanctx_changed = 0;
753
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200754 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
755 IEEE80211_CHAN_DISABLED))
756 return -EINVAL;
757
Luciano Coelho33ffd952014-01-30 22:08:16 +0200758 if (ctx->refcount != 1)
759 return -EINVAL;
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200760
761 if (sdata->vif.bss_conf.chandef.width != chandef->width) {
762 chanctx_changed = IEEE80211_CHANCTX_CHANGE_WIDTH;
763 *changed |= BSS_CHANGED_BANDWIDTH;
764 }
765
766 sdata->vif.bss_conf.chandef = *chandef;
767 ctx->conf.def = *chandef;
768
769 chanctx_changed |= IEEE80211_CHANCTX_CHANGE_CHANNEL;
770 drv_change_chanctx(local, ctx, chanctx_changed);
771
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200772 ieee80211_recalc_chanctx_chantype(local, ctx);
773 ieee80211_recalc_smps_chanctx(local, ctx);
774 ieee80211_recalc_radar_chanctx(local, ctx);
Eliad Peller21f659b2013-11-11 20:14:01 +0200775 ieee80211_recalc_chanctx_min_def(local, ctx);
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200776
Luciano Coelho33ffd952014-01-30 22:08:16 +0200777 return 0;
778}
779
780int ieee80211_vif_change_channel(struct ieee80211_sub_if_data *sdata,
781 u32 *changed)
782{
783 struct ieee80211_local *local = sdata->local;
784 struct ieee80211_chanctx_conf *conf;
785 struct ieee80211_chanctx *ctx;
786 int ret;
787
788 lockdep_assert_held(&local->mtx);
789
790 /* should never be called if not performing a channel switch. */
791 if (WARN_ON(!sdata->vif.csa_active))
792 return -EINVAL;
793
794 mutex_lock(&local->chanctx_mtx);
795 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
796 lockdep_is_held(&local->chanctx_mtx));
797 if (!conf) {
798 ret = -EINVAL;
799 goto out;
800 }
801
802 ctx = container_of(conf, struct ieee80211_chanctx, conf);
803
804 ret = __ieee80211_vif_change_channel(sdata, ctx, changed);
Simon Wunderlich73da7d52013-07-11 16:09:06 +0200805 out:
806 mutex_unlock(&local->chanctx_mtx);
807 return ret;
808}
809
Luciano Coelho11335a552013-10-30 13:09:39 +0200810static void
811__ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
812 bool clear)
813{
814 struct ieee80211_local *local = sdata->local;
815 struct ieee80211_sub_if_data *vlan;
816 struct ieee80211_chanctx_conf *conf;
817
818 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP))
819 return;
820
821 lockdep_assert_held(&local->mtx);
822
823 /* Check that conf exists, even when clearing this function
824 * must be called with the AP's channel context still there
825 * as it would otherwise cause VLANs to have an invalid
826 * channel context pointer for a while, possibly pointing
827 * to a channel context that has already been freed.
828 */
829 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
830 lockdep_is_held(&local->chanctx_mtx));
831 WARN_ON(!conf);
832
833 if (clear)
834 conf = NULL;
835
836 list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
837 rcu_assign_pointer(vlan->vif.chanctx_conf, conf);
838}
839
840void ieee80211_vif_copy_chanctx_to_vlans(struct ieee80211_sub_if_data *sdata,
841 bool clear)
842{
843 struct ieee80211_local *local = sdata->local;
844
845 mutex_lock(&local->chanctx_mtx);
846
847 __ieee80211_vif_copy_chanctx_to_vlans(sdata, clear);
848
849 mutex_unlock(&local->chanctx_mtx);
850}
851
852int ieee80211_vif_unreserve_chanctx(struct ieee80211_sub_if_data *sdata)
853{
Michal Kaziore3afb922014-04-09 15:29:27 +0200854 struct ieee80211_chanctx *ctx = sdata->reserved_chanctx;
855
Luciano Coelho11335a552013-10-30 13:09:39 +0200856 lockdep_assert_held(&sdata->local->chanctx_mtx);
857
Michal Kaziore3afb922014-04-09 15:29:27 +0200858 if (WARN_ON(!ctx))
Luciano Coelho11335a552013-10-30 13:09:39 +0200859 return -EINVAL;
860
Michal Kaziore3afb922014-04-09 15:29:27 +0200861 list_del(&sdata->reserved_chanctx_list);
Luciano Coelho11335a552013-10-30 13:09:39 +0200862 sdata->reserved_chanctx = NULL;
863
Michal Kaziore3afb922014-04-09 15:29:27 +0200864 if (--ctx->refcount == 0)
865 ieee80211_free_chanctx(sdata->local, ctx);
866
Luciano Coelho11335a552013-10-30 13:09:39 +0200867 return 0;
868}
869
870int ieee80211_vif_reserve_chanctx(struct ieee80211_sub_if_data *sdata,
871 const struct cfg80211_chan_def *chandef,
Michal Kazior09332482014-04-09 15:29:25 +0200872 enum ieee80211_chanctx_mode mode,
873 bool radar_required)
Luciano Coelho11335a552013-10-30 13:09:39 +0200874{
875 struct ieee80211_local *local = sdata->local;
876 struct ieee80211_chanctx_conf *conf;
877 struct ieee80211_chanctx *new_ctx, *curr_ctx;
878 int ret = 0;
879
880 mutex_lock(&local->chanctx_mtx);
881
882 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
883 lockdep_is_held(&local->chanctx_mtx));
884 if (!conf) {
885 ret = -EINVAL;
886 goto out;
887 }
888
889 curr_ctx = container_of(conf, struct ieee80211_chanctx, conf);
890
Michal Kazior13f348a2014-04-09 15:29:29 +0200891 new_ctx = ieee80211_find_reservation_chanctx(local, chandef, mode);
Luciano Coelho11335a552013-10-30 13:09:39 +0200892 if (!new_ctx) {
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200893 if (curr_ctx->refcount == 1 &&
894 (local->hw.flags & IEEE80211_HW_CHANGE_RUNNING_CHANCTX)) {
895 /* if we're the only users of the chanctx and
896 * the driver supports changing a running
897 * context, reserve our current context
898 */
899 new_ctx = curr_ctx;
Michal Kaziorc2b90ad2014-04-09 15:29:24 +0200900 } else if (ieee80211_can_create_new_chanctx(local)) {
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200901 /* create a new context and reserve it */
902 new_ctx = ieee80211_new_chanctx(local, chandef, mode);
903 if (IS_ERR(new_ctx)) {
904 ret = PTR_ERR(new_ctx);
905 goto out;
906 }
Michal Kaziorc2b90ad2014-04-09 15:29:24 +0200907 } else {
908 ret = -EBUSY;
909 goto out;
Luciano Coelho11335a552013-10-30 13:09:39 +0200910 }
911 }
912
Michal Kaziore3afb922014-04-09 15:29:27 +0200913 list_add(&sdata->reserved_chanctx_list, &new_ctx->reserved_vifs);
Luciano Coelho11335a552013-10-30 13:09:39 +0200914 new_ctx->refcount++;
915 sdata->reserved_chanctx = new_ctx;
916 sdata->reserved_chandef = *chandef;
Michal Kazior09332482014-04-09 15:29:25 +0200917 sdata->reserved_radar_required = radar_required;
Luciano Coelho11335a552013-10-30 13:09:39 +0200918out:
919 mutex_unlock(&local->chanctx_mtx);
920 return ret;
921}
922
923int ieee80211_vif_use_reserved_context(struct ieee80211_sub_if_data *sdata,
924 u32 *changed)
925{
926 struct ieee80211_local *local = sdata->local;
927 struct ieee80211_chanctx *ctx;
928 struct ieee80211_chanctx *old_ctx;
929 struct ieee80211_chanctx_conf *conf;
930 int ret;
931 u32 tmp_changed = *changed;
932
933 /* TODO: need to recheck if the chandef is usable etc.? */
934
935 lockdep_assert_held(&local->mtx);
936
937 mutex_lock(&local->chanctx_mtx);
938
939 ctx = sdata->reserved_chanctx;
940 if (WARN_ON(!ctx)) {
941 ret = -EINVAL;
942 goto out;
943 }
944
945 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
946 lockdep_is_held(&local->chanctx_mtx));
947 if (!conf) {
948 ret = -EINVAL;
949 goto out;
950 }
951
952 old_ctx = container_of(conf, struct ieee80211_chanctx, conf);
953
954 if (sdata->vif.bss_conf.chandef.width != sdata->reserved_chandef.width)
955 tmp_changed |= BSS_CHANGED_BANDWIDTH;
956
957 sdata->vif.bss_conf.chandef = sdata->reserved_chandef;
958
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200959 /* unref our reservation */
Luciano Coelho11335a552013-10-30 13:09:39 +0200960 ctx->refcount--;
961 sdata->reserved_chanctx = NULL;
Michal Kazior09332482014-04-09 15:29:25 +0200962 sdata->radar_required = sdata->reserved_radar_required;
Michal Kaziore3afb922014-04-09 15:29:27 +0200963 list_del(&sdata->reserved_chanctx_list);
Luciano Coelho11335a552013-10-30 13:09:39 +0200964
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200965 if (old_ctx == ctx) {
966 /* This is our own context, just change it */
967 ret = __ieee80211_vif_change_channel(sdata, old_ctx,
968 &tmp_changed);
969 if (ret)
970 goto out;
971 } else {
972 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
973 if (old_ctx->refcount == 0)
974 ieee80211_free_chanctx(local, old_ctx);
975 if (ret) {
976 /* if assign fails refcount stays the same */
977 if (ctx->refcount == 0)
978 ieee80211_free_chanctx(local, ctx);
979 goto out;
980 }
Luciano Coelho11335a552013-10-30 13:09:39 +0200981
Luciano Coelho5d52ee82014-02-27 14:33:47 +0200982 if (sdata->vif.type == NL80211_IFTYPE_AP)
983 __ieee80211_vif_copy_chanctx_to_vlans(sdata, false);
984 }
Luciano Coelho11335a552013-10-30 13:09:39 +0200985
986 *changed = tmp_changed;
987
988 ieee80211_recalc_chanctx_chantype(local, ctx);
989 ieee80211_recalc_smps_chanctx(local, ctx);
990 ieee80211_recalc_radar_chanctx(local, ctx);
991 ieee80211_recalc_chanctx_min_def(local, ctx);
992out:
993 mutex_unlock(&local->chanctx_mtx);
994 return ret;
995}
996
Johannes Berg2c9b7352013-02-07 21:37:29 +0100997int ieee80211_vif_change_bandwidth(struct ieee80211_sub_if_data *sdata,
998 const struct cfg80211_chan_def *chandef,
999 u32 *changed)
1000{
1001 struct ieee80211_local *local = sdata->local;
1002 struct ieee80211_chanctx_conf *conf;
1003 struct ieee80211_chanctx *ctx;
1004 int ret;
1005
1006 if (!cfg80211_chandef_usable(sdata->local->hw.wiphy, chandef,
1007 IEEE80211_CHAN_DISABLED))
1008 return -EINVAL;
1009
1010 mutex_lock(&local->chanctx_mtx);
1011 if (cfg80211_chandef_identical(chandef, &sdata->vif.bss_conf.chandef)) {
1012 ret = 0;
1013 goto out;
1014 }
1015
1016 if (chandef->width == NL80211_CHAN_WIDTH_20_NOHT ||
1017 sdata->vif.bss_conf.chandef.width == NL80211_CHAN_WIDTH_20_NOHT) {
1018 ret = -EINVAL;
1019 goto out;
1020 }
1021
1022 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
1023 lockdep_is_held(&local->chanctx_mtx));
1024 if (!conf) {
1025 ret = -EINVAL;
1026 goto out;
1027 }
1028
1029 ctx = container_of(conf, struct ieee80211_chanctx, conf);
1030 if (!cfg80211_chandef_compatible(&conf->def, chandef)) {
1031 ret = -EINVAL;
1032 goto out;
1033 }
1034
1035 sdata->vif.bss_conf.chandef = *chandef;
1036
1037 ieee80211_recalc_chanctx_chantype(local, ctx);
1038
1039 *changed |= BSS_CHANGED_BANDWIDTH;
1040 ret = 0;
1041 out:
1042 mutex_unlock(&local->chanctx_mtx);
1043 return ret;
1044}
1045
Michal Kaziord01a1e62012-06-26 14:37:16 +02001046void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
1047{
Johannes Berg55de9082012-07-26 17:24:39 +02001048 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
1049
Johannes Berg34a37402013-12-18 09:43:33 +01001050 lockdep_assert_held(&sdata->local->mtx);
1051
Michal Kaziord01a1e62012-06-26 14:37:16 +02001052 mutex_lock(&sdata->local->chanctx_mtx);
1053 __ieee80211_vif_release_channel(sdata);
1054 mutex_unlock(&sdata->local->chanctx_mtx);
1055}
Johannes Berg3448c002012-09-11 17:57:42 +02001056
Johannes Berg4d76d212012-12-11 20:38:41 +01001057void ieee80211_vif_vlan_copy_chanctx(struct ieee80211_sub_if_data *sdata)
1058{
1059 struct ieee80211_local *local = sdata->local;
1060 struct ieee80211_sub_if_data *ap;
1061 struct ieee80211_chanctx_conf *conf;
1062
1063 if (WARN_ON(sdata->vif.type != NL80211_IFTYPE_AP_VLAN || !sdata->bss))
1064 return;
1065
1066 ap = container_of(sdata->bss, struct ieee80211_sub_if_data, u.ap);
1067
1068 mutex_lock(&local->chanctx_mtx);
1069
1070 conf = rcu_dereference_protected(ap->vif.chanctx_conf,
1071 lockdep_is_held(&local->chanctx_mtx));
1072 rcu_assign_pointer(sdata->vif.chanctx_conf, conf);
1073 mutex_unlock(&local->chanctx_mtx);
1074}
1075
Johannes Berg3448c002012-09-11 17:57:42 +02001076void ieee80211_iter_chan_contexts_atomic(
1077 struct ieee80211_hw *hw,
1078 void (*iter)(struct ieee80211_hw *hw,
1079 struct ieee80211_chanctx_conf *chanctx_conf,
1080 void *data),
1081 void *iter_data)
1082{
1083 struct ieee80211_local *local = hw_to_local(hw);
1084 struct ieee80211_chanctx *ctx;
1085
1086 rcu_read_lock();
1087 list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
Johannes Berg8a61af62012-12-13 17:42:30 +01001088 if (ctx->driver_present)
1089 iter(hw, &ctx->conf, iter_data);
Johannes Berg3448c002012-09-11 17:57:42 +02001090 rcu_read_unlock();
1091}
1092EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);