blob: ff3b29ec396adc5ada44393dc8814f74ec944cd9 [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;
Johannes Berg0aaffa92010-05-05 15:28:27 +0200170
Johannes Berg0aaffa92010-05-05 15:28:27 +0200171}
Michal Kaziord01a1e62012-06-26 14:37:16 +0200172
173static struct ieee80211_chanctx *
174ieee80211_find_chanctx(struct ieee80211_local *local,
175 struct ieee80211_channel *channel,
176 enum nl80211_channel_type channel_type,
177 enum ieee80211_chanctx_mode mode)
178{
179 struct ieee80211_chanctx *ctx;
180
181 lockdep_assert_held(&local->chanctx_mtx);
182
183 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
184 return NULL;
185 if (WARN_ON(!channel))
186 return NULL;
187
188 list_for_each_entry(ctx, &local->chanctx_list, list) {
189 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
190 continue;
191 if (ctx->conf.channel != channel)
192 continue;
193 if (ctx->conf.channel_type != channel_type)
194 continue;
195
196 return ctx;
197 }
198
199 return NULL;
200}
201
202static struct ieee80211_chanctx *
203ieee80211_new_chanctx(struct ieee80211_local *local,
204 struct ieee80211_channel *channel,
205 enum nl80211_channel_type channel_type,
206 enum ieee80211_chanctx_mode mode)
207{
208 struct ieee80211_chanctx *ctx;
Michal Kazior35f2fce2012-06-26 14:37:20 +0200209 int err;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200210
211 lockdep_assert_held(&local->chanctx_mtx);
212
213 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
214 if (!ctx)
215 return ERR_PTR(-ENOMEM);
216
217 ctx->conf.channel = channel;
218 ctx->conf.channel_type = channel_type;
219 ctx->mode = mode;
220
Michal Kazior35f2fce2012-06-26 14:37:20 +0200221 err = drv_add_chanctx(local, ctx);
222 if (err) {
223 kfree(ctx);
224 return ERR_PTR(err);
225 }
226
Michal Kaziord01a1e62012-06-26 14:37:16 +0200227 list_add(&ctx->list, &local->chanctx_list);
228
229 return ctx;
230}
231
232static void ieee80211_free_chanctx(struct ieee80211_local *local,
233 struct ieee80211_chanctx *ctx)
234{
235 lockdep_assert_held(&local->chanctx_mtx);
236
237 WARN_ON_ONCE(ctx->refcount != 0);
238
Michal Kazior35f2fce2012-06-26 14:37:20 +0200239 drv_remove_chanctx(local, ctx);
240
Michal Kaziord01a1e62012-06-26 14:37:16 +0200241 list_del(&ctx->list);
242 kfree_rcu(ctx, rcu_head);
243}
244
245static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
246 struct ieee80211_chanctx *ctx)
247{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200248 struct ieee80211_local *local = sdata->local;
249 int ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200250
251 lockdep_assert_held(&local->chanctx_mtx);
252
Michal Kazior35f2fce2012-06-26 14:37:20 +0200253 ret = drv_assign_vif_chanctx(local, sdata, ctx);
254 if (ret)
255 return ret;
256
Michal Kaziord01a1e62012-06-26 14:37:16 +0200257 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
258 ctx->refcount++;
259
260 return 0;
261}
262
263static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
264 struct ieee80211_chanctx *ctx)
265{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200266 struct ieee80211_local *local = sdata->local;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200267
268 lockdep_assert_held(&local->chanctx_mtx);
269
270 ctx->refcount--;
271 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200272
273 drv_unassign_vif_chanctx(local, sdata, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200274}
275
276static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
277{
278 struct ieee80211_local *local = sdata->local;
279 struct ieee80211_chanctx_conf *conf;
280 struct ieee80211_chanctx *ctx;
281
282 lockdep_assert_held(&local->chanctx_mtx);
283
284 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
285 lockdep_is_held(&local->chanctx_mtx));
286 if (!conf)
287 return;
288
289 ctx = container_of(conf, struct ieee80211_chanctx, conf);
290
291 ieee80211_unassign_vif_chanctx(sdata, ctx);
292 if (ctx->refcount == 0)
293 ieee80211_free_chanctx(local, ctx);
294}
295
296int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
297 struct ieee80211_channel *channel,
298 enum nl80211_channel_type channel_type,
299 enum ieee80211_chanctx_mode mode)
300{
301 struct ieee80211_local *local = sdata->local;
302 struct ieee80211_chanctx *ctx;
303 int ret;
304
305 mutex_lock(&local->chanctx_mtx);
306 __ieee80211_vif_release_channel(sdata);
307
308 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
309 if (!ctx)
310 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
311 if (IS_ERR(ctx)) {
312 ret = PTR_ERR(ctx);
313 goto out;
314 }
315
316 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
317 if (ret) {
318 /* if assign fails refcount stays the same */
319 if (ctx->refcount == 0)
320 ieee80211_free_chanctx(local, ctx);
321 goto out;
322 }
323
324 out:
325 mutex_unlock(&local->chanctx_mtx);
326 return ret;
327}
328
329void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
330{
331 mutex_lock(&sdata->local->chanctx_mtx);
332 __ieee80211_vif_release_channel(sdata);
333 mutex_unlock(&sdata->local->chanctx_mtx);
334}