blob: 1a8dee42e546c0f3e2e1fc67fa76a72a7caf3bcd [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>
Paul Stewart3117bbdb2012-03-13 07:46:18 -07006#include <net/cfg80211.h>
Johannes Bergf444de02010-05-05 15:25:02 +02007#include "ieee80211_i.h"
Michal Kazior35f2fce2012-06-26 14:37:20 +02008#include "driver-ops.h"
Johannes Bergf444de02010-05-05 15:25:02 +02009
Johannes Berg368a07d2010-05-28 14:26:23 +020010static enum ieee80211_chan_mode
Johannes Bergf444de02010-05-05 15:25:02 +020011__ieee80211_get_channel_mode(struct ieee80211_local *local,
12 struct ieee80211_sub_if_data *ignore)
13{
14 struct ieee80211_sub_if_data *sdata;
15
Johannes Berg46a5eba2010-09-15 13:28:15 +020016 lockdep_assert_held(&local->iflist_mtx);
Johannes Bergf444de02010-05-05 15:25:02 +020017
18 list_for_each_entry(sdata, &local->interfaces, list) {
19 if (sdata == ignore)
20 continue;
21
22 if (!ieee80211_sdata_running(sdata))
23 continue;
24
Johannes Berge9980e62012-01-09 13:57:36 +010025 switch (sdata->vif.type) {
26 case NL80211_IFTYPE_MONITOR:
Johannes Bergf444de02010-05-05 15:25:02 +020027 continue;
Johannes Berge9980e62012-01-09 13:57:36 +010028 case NL80211_IFTYPE_STATION:
29 if (!sdata->u.mgd.associated)
30 continue;
31 break;
32 case NL80211_IFTYPE_ADHOC:
Johannes Bergf444de02010-05-05 15:25:02 +020033 if (!sdata->u.ibss.ssid_len)
34 continue;
35 if (!sdata->u.ibss.fixed_channel)
36 return CHAN_MODE_HOPPING;
Johannes Berge9980e62012-01-09 13:57:36 +010037 break;
38 case NL80211_IFTYPE_AP_VLAN:
39 /* will also have _AP interface */
Johannes Bergf444de02010-05-05 15:25:02 +020040 continue;
Johannes Berge9980e62012-01-09 13:57:36 +010041 case NL80211_IFTYPE_AP:
42 if (!sdata->u.ap.beacon)
43 continue;
44 break;
Thomas Pedersenbe0f4232012-05-13 22:24:08 -070045 case NL80211_IFTYPE_MESH_POINT:
46 if (!sdata->wdev.mesh_id_len)
47 continue;
48 break;
Johannes Berge9980e62012-01-09 13:57:36 +010049 default:
50 break;
51 }
Johannes Bergf444de02010-05-05 15:25:02 +020052
53 return CHAN_MODE_FIXED;
54 }
55
56 return CHAN_MODE_UNDEFINED;
57}
58
59enum ieee80211_chan_mode
60ieee80211_get_channel_mode(struct ieee80211_local *local,
61 struct ieee80211_sub_if_data *ignore)
62{
63 enum ieee80211_chan_mode mode;
64
65 mutex_lock(&local->iflist_mtx);
66 mode = __ieee80211_get_channel_mode(local, ignore);
67 mutex_unlock(&local->iflist_mtx);
68
69 return mode;
70}
Johannes Berg0aaffa92010-05-05 15:28:27 +020071
Michal Kazior23a85b452012-06-26 14:37:21 +020072static enum nl80211_channel_type
73ieee80211_get_superchan(struct ieee80211_local *local,
74 struct ieee80211_sub_if_data *sdata)
Johannes Berg0aaffa92010-05-05 15:28:27 +020075{
Johannes Berg0aaffa92010-05-05 15:28:27 +020076 enum nl80211_channel_type superchan = NL80211_CHAN_NO_HT;
Michal Kazior23a85b452012-06-26 14:37:21 +020077 struct ieee80211_sub_if_data *tmp;
Johannes Berg0aaffa92010-05-05 15:28:27 +020078
79 mutex_lock(&local->iflist_mtx);
Johannes Berg0aaffa92010-05-05 15:28:27 +020080 list_for_each_entry(tmp, &local->interfaces, list) {
81 if (tmp == sdata)
82 continue;
83
84 if (!ieee80211_sdata_running(tmp))
85 continue;
86
87 switch (tmp->vif.bss_conf.channel_type) {
88 case NL80211_CHAN_NO_HT:
89 case NL80211_CHAN_HT20:
Felix Fietkau9db372f2011-03-11 21:45:51 +010090 if (superchan > tmp->vif.bss_conf.channel_type)
91 break;
92
Johannes Berg0aaffa92010-05-05 15:28:27 +020093 superchan = tmp->vif.bss_conf.channel_type;
94 break;
95 case NL80211_CHAN_HT40PLUS:
96 WARN_ON(superchan == NL80211_CHAN_HT40MINUS);
97 superchan = NL80211_CHAN_HT40PLUS;
98 break;
99 case NL80211_CHAN_HT40MINUS:
100 WARN_ON(superchan == NL80211_CHAN_HT40PLUS);
101 superchan = NL80211_CHAN_HT40MINUS;
102 break;
103 }
104 }
Michal Kazior23a85b452012-06-26 14:37:21 +0200105 mutex_unlock(&local->iflist_mtx);
Johannes Berg0aaffa92010-05-05 15:28:27 +0200106
Michal Kazior23a85b452012-06-26 14:37:21 +0200107 return superchan;
108}
109
110static bool
111ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
112 enum nl80211_channel_type chantype2,
113 enum nl80211_channel_type *compat)
114{
115 /*
116 * start out with chantype1 being the result,
117 * overwriting later if needed
118 */
119 if (compat)
120 *compat = chantype1;
121
122 switch (chantype1) {
Johannes Berg0aaffa92010-05-05 15:28:27 +0200123 case NL80211_CHAN_NO_HT:
Michal Kazior23a85b452012-06-26 14:37:21 +0200124 if (compat)
125 *compat = chantype2;
126 break;
Johannes Berg0aaffa92010-05-05 15:28:27 +0200127 case NL80211_CHAN_HT20:
128 /*
129 * allow any change that doesn't go to no-HT
130 * (if it already is no-HT no change is needed)
131 */
Michal Kazior23a85b452012-06-26 14:37:21 +0200132 if (chantype2 == NL80211_CHAN_NO_HT)
Johannes Berg0aaffa92010-05-05 15:28:27 +0200133 break;
Michal Kazior23a85b452012-06-26 14:37:21 +0200134 if (compat)
135 *compat = chantype2;
Johannes Berg0aaffa92010-05-05 15:28:27 +0200136 break;
137 case NL80211_CHAN_HT40PLUS:
138 case NL80211_CHAN_HT40MINUS:
139 /* allow smaller bandwidth and same */
Michal Kazior23a85b452012-06-26 14:37:21 +0200140 if (chantype2 == NL80211_CHAN_NO_HT)
Johannes Berg0aaffa92010-05-05 15:28:27 +0200141 break;
Michal Kazior23a85b452012-06-26 14:37:21 +0200142 if (chantype2 == NL80211_CHAN_HT20)
Johannes Berg0aaffa92010-05-05 15:28:27 +0200143 break;
Michal Kazior23a85b452012-06-26 14:37:21 +0200144 if (chantype2 == chantype1)
Johannes Berg0aaffa92010-05-05 15:28:27 +0200145 break;
Michal Kazior23a85b452012-06-26 14:37:21 +0200146 return false;
Johannes Berg0aaffa92010-05-05 15:28:27 +0200147 }
148
Michal Kazior23a85b452012-06-26 14:37:21 +0200149 return true;
150}
151
152bool ieee80211_set_channel_type(struct ieee80211_local *local,
153 struct ieee80211_sub_if_data *sdata,
154 enum nl80211_channel_type chantype)
155{
156 enum nl80211_channel_type superchan;
157 enum nl80211_channel_type compatchan;
158
159 superchan = ieee80211_get_superchan(local, sdata);
160 if (!ieee80211_channel_types_are_compatible(superchan, chantype,
161 &compatchan))
162 return false;
163
164 local->_oper_channel_type = compatchan;
Johannes Berg0aaffa92010-05-05 15:28:27 +0200165
166 if (sdata)
167 sdata->vif.bss_conf.channel_type = chantype;
168
Michal Kazior23a85b452012-06-26 14:37:21 +0200169 return true;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200170}
Johannes Berg0aaffa92010-05-05 15:28:27 +0200171
Michal Kaziore89a96f2012-06-26 14:37:22 +0200172static void ieee80211_change_chantype(struct ieee80211_local *local,
173 struct ieee80211_chanctx *ctx,
174 enum nl80211_channel_type chantype)
175{
176 if (chantype == ctx->conf.channel_type)
177 return;
178
179 ctx->conf.channel_type = chantype;
180 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
Johannes Berg0aaffa92010-05-05 15:28:27 +0200181}
Michal Kaziord01a1e62012-06-26 14:37:16 +0200182
183static struct ieee80211_chanctx *
184ieee80211_find_chanctx(struct ieee80211_local *local,
185 struct ieee80211_channel *channel,
186 enum nl80211_channel_type channel_type,
187 enum ieee80211_chanctx_mode mode)
188{
189 struct ieee80211_chanctx *ctx;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200190 enum nl80211_channel_type compat_type;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200191
192 lockdep_assert_held(&local->chanctx_mtx);
193
194 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
195 return NULL;
196 if (WARN_ON(!channel))
197 return NULL;
198
199 list_for_each_entry(ctx, &local->chanctx_list, list) {
Michal Kaziore89a96f2012-06-26 14:37:22 +0200200 compat_type = ctx->conf.channel_type;
201
Michal Kaziord01a1e62012-06-26 14:37:16 +0200202 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
203 continue;
204 if (ctx->conf.channel != channel)
205 continue;
Michal Kaziore89a96f2012-06-26 14:37:22 +0200206 if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type,
207 channel_type,
208 &compat_type))
Michal Kaziord01a1e62012-06-26 14:37:16 +0200209 continue;
210
Michal Kaziore89a96f2012-06-26 14:37:22 +0200211 ieee80211_change_chantype(local, ctx, compat_type);
212
Michal Kaziord01a1e62012-06-26 14:37:16 +0200213 return ctx;
214 }
215
216 return NULL;
217}
218
219static struct ieee80211_chanctx *
220ieee80211_new_chanctx(struct ieee80211_local *local,
221 struct ieee80211_channel *channel,
222 enum nl80211_channel_type channel_type,
223 enum ieee80211_chanctx_mode mode)
224{
225 struct ieee80211_chanctx *ctx;
Michal Kazior35f2fce2012-06-26 14:37:20 +0200226 int err;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200227
228 lockdep_assert_held(&local->chanctx_mtx);
229
230 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
231 if (!ctx)
232 return ERR_PTR(-ENOMEM);
233
234 ctx->conf.channel = channel;
235 ctx->conf.channel_type = channel_type;
236 ctx->mode = mode;
237
Michal Kazior35f2fce2012-06-26 14:37:20 +0200238 err = drv_add_chanctx(local, ctx);
239 if (err) {
240 kfree(ctx);
241 return ERR_PTR(err);
242 }
243
Michal Kaziord01a1e62012-06-26 14:37:16 +0200244 list_add(&ctx->list, &local->chanctx_list);
245
246 return ctx;
247}
248
249static void ieee80211_free_chanctx(struct ieee80211_local *local,
250 struct ieee80211_chanctx *ctx)
251{
252 lockdep_assert_held(&local->chanctx_mtx);
253
254 WARN_ON_ONCE(ctx->refcount != 0);
255
Michal Kazior35f2fce2012-06-26 14:37:20 +0200256 drv_remove_chanctx(local, ctx);
257
Michal Kaziord01a1e62012-06-26 14:37:16 +0200258 list_del(&ctx->list);
259 kfree_rcu(ctx, rcu_head);
260}
261
262static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
263 struct ieee80211_chanctx *ctx)
264{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200265 struct ieee80211_local *local = sdata->local;
266 int ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200267
268 lockdep_assert_held(&local->chanctx_mtx);
269
Michal Kazior35f2fce2012-06-26 14:37:20 +0200270 ret = drv_assign_vif_chanctx(local, sdata, ctx);
271 if (ret)
272 return ret;
273
Michal Kaziord01a1e62012-06-26 14:37:16 +0200274 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
275 ctx->refcount++;
276
277 return 0;
278}
279
Michal Kaziore89a96f2012-06-26 14:37:22 +0200280static enum nl80211_channel_type
281ieee80211_calc_chantype(struct ieee80211_local *local,
282 struct ieee80211_chanctx *ctx)
283{
284 struct ieee80211_chanctx_conf *conf = &ctx->conf;
285 struct ieee80211_sub_if_data *sdata;
286 enum nl80211_channel_type result = NL80211_CHAN_NO_HT;
287
288 lockdep_assert_held(&local->chanctx_mtx);
289
290 rcu_read_lock();
291 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
292 if (!ieee80211_sdata_running(sdata))
293 continue;
294 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
295 continue;
296
297 WARN_ON_ONCE(!ieee80211_channel_types_are_compatible(
298 sdata->vif.bss_conf.channel_type,
299 result, &result));
300 }
301 rcu_read_unlock();
302
303 return result;
304}
305
306static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
307 struct ieee80211_chanctx *ctx)
308{
309 enum nl80211_channel_type chantype;
310
311 lockdep_assert_held(&local->chanctx_mtx);
312
313 chantype = ieee80211_calc_chantype(local, ctx);
314 ieee80211_change_chantype(local, ctx, chantype);
315}
316
Michal Kaziord01a1e62012-06-26 14:37:16 +0200317static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
318 struct ieee80211_chanctx *ctx)
319{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200320 struct ieee80211_local *local = sdata->local;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200321
322 lockdep_assert_held(&local->chanctx_mtx);
323
324 ctx->refcount--;
325 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200326
327 drv_unassign_vif_chanctx(local, sdata, ctx);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200328
329 if (ctx->refcount > 0)
330 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200331}
332
333static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
334{
335 struct ieee80211_local *local = sdata->local;
336 struct ieee80211_chanctx_conf *conf;
337 struct ieee80211_chanctx *ctx;
338
339 lockdep_assert_held(&local->chanctx_mtx);
340
341 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
342 lockdep_is_held(&local->chanctx_mtx));
343 if (!conf)
344 return;
345
346 ctx = container_of(conf, struct ieee80211_chanctx, conf);
347
348 ieee80211_unassign_vif_chanctx(sdata, ctx);
349 if (ctx->refcount == 0)
350 ieee80211_free_chanctx(local, ctx);
351}
352
353int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
354 struct ieee80211_channel *channel,
355 enum nl80211_channel_type channel_type,
356 enum ieee80211_chanctx_mode mode)
357{
358 struct ieee80211_local *local = sdata->local;
359 struct ieee80211_chanctx *ctx;
360 int ret;
361
362 mutex_lock(&local->chanctx_mtx);
363 __ieee80211_vif_release_channel(sdata);
364
365 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
366 if (!ctx)
367 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
368 if (IS_ERR(ctx)) {
369 ret = PTR_ERR(ctx);
370 goto out;
371 }
372
373 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
374 if (ret) {
375 /* if assign fails refcount stays the same */
376 if (ctx->refcount == 0)
377 ieee80211_free_chanctx(local, ctx);
378 goto out;
379 }
380
381 out:
382 mutex_unlock(&local->chanctx_mtx);
383 return ret;
384}
385
386void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
387{
388 mutex_lock(&sdata->local->chanctx_mtx);
389 __ieee80211_vif_release_channel(sdata);
390 mutex_unlock(&sdata->local->chanctx_mtx);
391}