blob: f84b86028a9cd50bc95c10ec3410c4250c190eb1 [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>
Paul Stewart3117bbdb2012-03-13 07:46:18 -07007#include <net/cfg80211.h>
Johannes Bergf444de02010-05-05 15:25:02 +02008#include "ieee80211_i.h"
Michal Kazior35f2fce2012-06-26 14:37:20 +02009#include "driver-ops.h"
Johannes Bergf444de02010-05-05 15:25:02 +020010
Michal Kazior23a85b452012-06-26 14:37:21 +020011static bool
12ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
13 enum nl80211_channel_type chantype2,
14 enum nl80211_channel_type *compat)
15{
16 /*
17 * start out with chantype1 being the result,
18 * overwriting later if needed
19 */
20 if (compat)
21 *compat = chantype1;
22
23 switch (chantype1) {
Johannes Berg0aaffa92010-05-05 15:28:27 +020024 case NL80211_CHAN_NO_HT:
Michal Kazior23a85b452012-06-26 14:37:21 +020025 if (compat)
26 *compat = chantype2;
27 break;
Johannes Berg0aaffa92010-05-05 15:28:27 +020028 case NL80211_CHAN_HT20:
29 /*
30 * allow any change that doesn't go to no-HT
31 * (if it already is no-HT no change is needed)
32 */
Michal Kazior23a85b452012-06-26 14:37:21 +020033 if (chantype2 == NL80211_CHAN_NO_HT)
Johannes Berg0aaffa92010-05-05 15:28:27 +020034 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020035 if (compat)
36 *compat = chantype2;
Johannes Berg0aaffa92010-05-05 15:28:27 +020037 break;
38 case NL80211_CHAN_HT40PLUS:
39 case NL80211_CHAN_HT40MINUS:
40 /* allow smaller bandwidth and same */
Michal Kazior23a85b452012-06-26 14:37:21 +020041 if (chantype2 == NL80211_CHAN_NO_HT)
Johannes Berg0aaffa92010-05-05 15:28:27 +020042 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020043 if (chantype2 == NL80211_CHAN_HT20)
Johannes Berg0aaffa92010-05-05 15:28:27 +020044 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020045 if (chantype2 == chantype1)
Johannes Berg0aaffa92010-05-05 15:28:27 +020046 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020047 return false;
Johannes Berg0aaffa92010-05-05 15:28:27 +020048 }
49
Michal Kazior23a85b452012-06-26 14:37:21 +020050 return true;
51}
52
Michal Kaziore89a96f2012-06-26 14:37:22 +020053static void ieee80211_change_chantype(struct ieee80211_local *local,
54 struct ieee80211_chanctx *ctx,
55 enum nl80211_channel_type chantype)
56{
57 if (chantype == ctx->conf.channel_type)
58 return;
59
60 ctx->conf.channel_type = chantype;
61 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
Johannes Berg55de9082012-07-26 17:24:39 +020062
63 if (!local->use_chanctx) {
64 local->_oper_channel_type = chantype;
65 ieee80211_hw_config(local, 0);
66 }
Johannes Berg0aaffa92010-05-05 15:28:27 +020067}
Michal Kaziord01a1e62012-06-26 14:37:16 +020068
69static struct ieee80211_chanctx *
70ieee80211_find_chanctx(struct ieee80211_local *local,
71 struct ieee80211_channel *channel,
72 enum nl80211_channel_type channel_type,
73 enum ieee80211_chanctx_mode mode)
74{
75 struct ieee80211_chanctx *ctx;
Michal Kaziore89a96f2012-06-26 14:37:22 +020076 enum nl80211_channel_type compat_type;
Michal Kaziord01a1e62012-06-26 14:37:16 +020077
78 lockdep_assert_held(&local->chanctx_mtx);
79
80 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
81 return NULL;
82 if (WARN_ON(!channel))
83 return NULL;
84
85 list_for_each_entry(ctx, &local->chanctx_list, list) {
Michal Kaziore89a96f2012-06-26 14:37:22 +020086 compat_type = ctx->conf.channel_type;
87
Michal Kaziord01a1e62012-06-26 14:37:16 +020088 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
89 continue;
90 if (ctx->conf.channel != channel)
91 continue;
Michal Kaziore89a96f2012-06-26 14:37:22 +020092 if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type,
93 channel_type,
94 &compat_type))
Michal Kaziord01a1e62012-06-26 14:37:16 +020095 continue;
96
Michal Kaziore89a96f2012-06-26 14:37:22 +020097 ieee80211_change_chantype(local, ctx, compat_type);
98
Michal Kaziord01a1e62012-06-26 14:37:16 +020099 return ctx;
100 }
101
102 return NULL;
103}
104
105static struct ieee80211_chanctx *
106ieee80211_new_chanctx(struct ieee80211_local *local,
107 struct ieee80211_channel *channel,
108 enum nl80211_channel_type channel_type,
109 enum ieee80211_chanctx_mode mode)
110{
111 struct ieee80211_chanctx *ctx;
Michal Kazior35f2fce2012-06-26 14:37:20 +0200112 int err;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200113
114 lockdep_assert_held(&local->chanctx_mtx);
115
116 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
117 if (!ctx)
118 return ERR_PTR(-ENOMEM);
119
120 ctx->conf.channel = channel;
121 ctx->conf.channel_type = channel_type;
Johannes Berg04ecd252012-09-11 14:34:12 +0200122 ctx->conf.rx_chains_static = 1;
123 ctx->conf.rx_chains_dynamic = 1;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200124 ctx->mode = mode;
125
Johannes Berg55de9082012-07-26 17:24:39 +0200126 if (!local->use_chanctx) {
127 local->_oper_channel_type = channel_type;
128 local->_oper_channel = channel;
129 ieee80211_hw_config(local, 0);
130 } else {
131 err = drv_add_chanctx(local, ctx);
132 if (err) {
133 kfree(ctx);
134 return ERR_PTR(err);
135 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200136 }
137
Johannes Berg3448c002012-09-11 17:57:42 +0200138 list_add_rcu(&ctx->list, &local->chanctx_list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200139
140 return ctx;
141}
142
143static void ieee80211_free_chanctx(struct ieee80211_local *local,
144 struct ieee80211_chanctx *ctx)
145{
146 lockdep_assert_held(&local->chanctx_mtx);
147
148 WARN_ON_ONCE(ctx->refcount != 0);
149
Johannes Berg55de9082012-07-26 17:24:39 +0200150 if (!local->use_chanctx) {
151 local->_oper_channel_type = NL80211_CHAN_NO_HT;
152 ieee80211_hw_config(local, 0);
153 } else {
154 drv_remove_chanctx(local, ctx);
155 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200156
Johannes Berg3448c002012-09-11 17:57:42 +0200157 list_del_rcu(&ctx->list);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200158 kfree_rcu(ctx, rcu_head);
159}
160
161static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
162 struct ieee80211_chanctx *ctx)
163{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200164 struct ieee80211_local *local = sdata->local;
165 int ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200166
167 lockdep_assert_held(&local->chanctx_mtx);
168
Michal Kazior35f2fce2012-06-26 14:37:20 +0200169 ret = drv_assign_vif_chanctx(local, sdata, ctx);
170 if (ret)
171 return ret;
172
Michal Kaziord01a1e62012-06-26 14:37:16 +0200173 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
174 ctx->refcount++;
175
176 return 0;
177}
178
Michal Kaziore89a96f2012-06-26 14:37:22 +0200179static enum nl80211_channel_type
180ieee80211_calc_chantype(struct ieee80211_local *local,
181 struct ieee80211_chanctx *ctx)
182{
183 struct ieee80211_chanctx_conf *conf = &ctx->conf;
184 struct ieee80211_sub_if_data *sdata;
185 enum nl80211_channel_type result = NL80211_CHAN_NO_HT;
186
187 lockdep_assert_held(&local->chanctx_mtx);
188
189 rcu_read_lock();
190 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
191 if (!ieee80211_sdata_running(sdata))
192 continue;
193 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
194 continue;
195
196 WARN_ON_ONCE(!ieee80211_channel_types_are_compatible(
197 sdata->vif.bss_conf.channel_type,
198 result, &result));
199 }
200 rcu_read_unlock();
201
202 return result;
203}
204
205static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
206 struct ieee80211_chanctx *ctx)
207{
208 enum nl80211_channel_type chantype;
209
210 lockdep_assert_held(&local->chanctx_mtx);
211
212 chantype = ieee80211_calc_chantype(local, ctx);
213 ieee80211_change_chantype(local, ctx, chantype);
214}
215
Michal Kaziord01a1e62012-06-26 14:37:16 +0200216static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
217 struct ieee80211_chanctx *ctx)
218{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200219 struct ieee80211_local *local = sdata->local;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200220
221 lockdep_assert_held(&local->chanctx_mtx);
222
223 ctx->refcount--;
224 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200225
226 drv_unassign_vif_chanctx(local, sdata, ctx);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200227
Johannes Berg04ecd252012-09-11 14:34:12 +0200228 if (ctx->refcount > 0) {
Michal Kaziore89a96f2012-06-26 14:37:22 +0200229 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
Johannes Berg04ecd252012-09-11 14:34:12 +0200230 ieee80211_recalc_smps_chanctx(local, ctx);
231 }
Michal Kaziord01a1e62012-06-26 14:37:16 +0200232}
233
234static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
235{
236 struct ieee80211_local *local = sdata->local;
237 struct ieee80211_chanctx_conf *conf;
238 struct ieee80211_chanctx *ctx;
239
240 lockdep_assert_held(&local->chanctx_mtx);
241
242 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
243 lockdep_is_held(&local->chanctx_mtx));
244 if (!conf)
245 return;
246
247 ctx = container_of(conf, struct ieee80211_chanctx, conf);
248
249 ieee80211_unassign_vif_chanctx(sdata, ctx);
250 if (ctx->refcount == 0)
251 ieee80211_free_chanctx(local, ctx);
252}
253
Johannes Berg04ecd252012-09-11 14:34:12 +0200254void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
255 struct ieee80211_chanctx *chanctx)
256{
257 struct ieee80211_sub_if_data *sdata;
258 u8 rx_chains_static, rx_chains_dynamic;
259
260 lockdep_assert_held(&local->chanctx_mtx);
261
262 rx_chains_static = 1;
263 rx_chains_dynamic = 1;
264
265 rcu_read_lock();
266 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
267 u8 needed_static, needed_dynamic;
268
269 if (!ieee80211_sdata_running(sdata))
270 continue;
271
272 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
273 &chanctx->conf)
274 continue;
275
276 switch (sdata->vif.type) {
277 case NL80211_IFTYPE_P2P_DEVICE:
278 continue;
279 case NL80211_IFTYPE_STATION:
280 if (!sdata->u.mgd.associated)
281 continue;
282 break;
283 case NL80211_IFTYPE_AP_VLAN:
284 continue;
285 case NL80211_IFTYPE_AP:
286 case NL80211_IFTYPE_ADHOC:
287 case NL80211_IFTYPE_WDS:
288 case NL80211_IFTYPE_MESH_POINT:
289 break;
290 default:
291 WARN_ON_ONCE(1);
292 }
293
294 switch (sdata->smps_mode) {
295 default:
296 WARN_ONCE(1, "Invalid SMPS mode %d\n",
297 sdata->smps_mode);
298 /* fall through */
299 case IEEE80211_SMPS_OFF:
300 needed_static = sdata->needed_rx_chains;
301 needed_dynamic = sdata->needed_rx_chains;
302 break;
303 case IEEE80211_SMPS_DYNAMIC:
304 needed_static = 1;
305 needed_dynamic = sdata->needed_rx_chains;
306 break;
307 case IEEE80211_SMPS_STATIC:
308 needed_static = 1;
309 needed_dynamic = 1;
310 break;
311 }
312
313 rx_chains_static = max(rx_chains_static, needed_static);
314 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
315 }
316 rcu_read_unlock();
317
318 if (!local->use_chanctx) {
319 if (rx_chains_static > 1)
320 local->smps_mode = IEEE80211_SMPS_OFF;
321 else if (rx_chains_dynamic > 1)
322 local->smps_mode = IEEE80211_SMPS_DYNAMIC;
323 else
324 local->smps_mode = IEEE80211_SMPS_STATIC;
325 ieee80211_hw_config(local, 0);
326 }
327
328 if (rx_chains_static == chanctx->conf.rx_chains_static &&
329 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
330 return;
331
332 chanctx->conf.rx_chains_static = rx_chains_static;
333 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
334 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
335}
336
Michal Kaziord01a1e62012-06-26 14:37:16 +0200337int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
338 struct ieee80211_channel *channel,
339 enum nl80211_channel_type channel_type,
340 enum ieee80211_chanctx_mode mode)
341{
342 struct ieee80211_local *local = sdata->local;
343 struct ieee80211_chanctx *ctx;
344 int ret;
345
Johannes Berg55de9082012-07-26 17:24:39 +0200346 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
347
Michal Kaziord01a1e62012-06-26 14:37:16 +0200348 mutex_lock(&local->chanctx_mtx);
349 __ieee80211_vif_release_channel(sdata);
350
351 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
352 if (!ctx)
353 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
354 if (IS_ERR(ctx)) {
355 ret = PTR_ERR(ctx);
356 goto out;
357 }
358
Johannes Berg55de9082012-07-26 17:24:39 +0200359 sdata->vif.bss_conf.channel_type = channel_type;
360
Michal Kaziord01a1e62012-06-26 14:37:16 +0200361 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
362 if (ret) {
363 /* if assign fails refcount stays the same */
364 if (ctx->refcount == 0)
365 ieee80211_free_chanctx(local, ctx);
366 goto out;
367 }
368
Johannes Berg04ecd252012-09-11 14:34:12 +0200369 ieee80211_recalc_smps_chanctx(local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200370 out:
371 mutex_unlock(&local->chanctx_mtx);
372 return ret;
373}
374
375void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
376{
Johannes Berg55de9082012-07-26 17:24:39 +0200377 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
378
Michal Kaziord01a1e62012-06-26 14:37:16 +0200379 mutex_lock(&sdata->local->chanctx_mtx);
380 __ieee80211_vif_release_channel(sdata);
381 mutex_unlock(&sdata->local->chanctx_mtx);
382}
Johannes Berg3448c002012-09-11 17:57:42 +0200383
384void ieee80211_iter_chan_contexts_atomic(
385 struct ieee80211_hw *hw,
386 void (*iter)(struct ieee80211_hw *hw,
387 struct ieee80211_chanctx_conf *chanctx_conf,
388 void *data),
389 void *iter_data)
390{
391 struct ieee80211_local *local = hw_to_local(hw);
392 struct ieee80211_chanctx *ctx;
393
394 rcu_read_lock();
395 list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
396 iter(hw, &ctx->conf, iter_data);
397 rcu_read_unlock();
398}
399EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);