blob: bfaa486d928ca9bed29854ac00963a7cd37c8779 [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
Michal Kazior23a85b452012-06-26 14:37:21 +020010static bool
11ieee80211_channel_types_are_compatible(enum nl80211_channel_type chantype1,
12 enum nl80211_channel_type chantype2,
13 enum nl80211_channel_type *compat)
14{
15 /*
16 * start out with chantype1 being the result,
17 * overwriting later if needed
18 */
19 if (compat)
20 *compat = chantype1;
21
22 switch (chantype1) {
Johannes Berg0aaffa92010-05-05 15:28:27 +020023 case NL80211_CHAN_NO_HT:
Michal Kazior23a85b452012-06-26 14:37:21 +020024 if (compat)
25 *compat = chantype2;
26 break;
Johannes Berg0aaffa92010-05-05 15:28:27 +020027 case NL80211_CHAN_HT20:
28 /*
29 * allow any change that doesn't go to no-HT
30 * (if it already is no-HT no change is needed)
31 */
Michal Kazior23a85b452012-06-26 14:37:21 +020032 if (chantype2 == NL80211_CHAN_NO_HT)
Johannes Berg0aaffa92010-05-05 15:28:27 +020033 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020034 if (compat)
35 *compat = chantype2;
Johannes Berg0aaffa92010-05-05 15:28:27 +020036 break;
37 case NL80211_CHAN_HT40PLUS:
38 case NL80211_CHAN_HT40MINUS:
39 /* allow smaller bandwidth and same */
Michal Kazior23a85b452012-06-26 14:37:21 +020040 if (chantype2 == NL80211_CHAN_NO_HT)
Johannes Berg0aaffa92010-05-05 15:28:27 +020041 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020042 if (chantype2 == NL80211_CHAN_HT20)
Johannes Berg0aaffa92010-05-05 15:28:27 +020043 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020044 if (chantype2 == chantype1)
Johannes Berg0aaffa92010-05-05 15:28:27 +020045 break;
Michal Kazior23a85b452012-06-26 14:37:21 +020046 return false;
Johannes Berg0aaffa92010-05-05 15:28:27 +020047 }
48
Michal Kazior23a85b452012-06-26 14:37:21 +020049 return true;
50}
51
Michal Kaziore89a96f2012-06-26 14:37:22 +020052static void ieee80211_change_chantype(struct ieee80211_local *local,
53 struct ieee80211_chanctx *ctx,
54 enum nl80211_channel_type chantype)
55{
56 if (chantype == ctx->conf.channel_type)
57 return;
58
59 ctx->conf.channel_type = chantype;
60 drv_change_chanctx(local, ctx, IEEE80211_CHANCTX_CHANGE_CHANNEL_TYPE);
Johannes Berg55de9082012-07-26 17:24:39 +020061
62 if (!local->use_chanctx) {
63 local->_oper_channel_type = chantype;
64 ieee80211_hw_config(local, 0);
65 }
Johannes Berg0aaffa92010-05-05 15:28:27 +020066}
Michal Kaziord01a1e62012-06-26 14:37:16 +020067
68static struct ieee80211_chanctx *
69ieee80211_find_chanctx(struct ieee80211_local *local,
70 struct ieee80211_channel *channel,
71 enum nl80211_channel_type channel_type,
72 enum ieee80211_chanctx_mode mode)
73{
74 struct ieee80211_chanctx *ctx;
Michal Kaziore89a96f2012-06-26 14:37:22 +020075 enum nl80211_channel_type compat_type;
Michal Kaziord01a1e62012-06-26 14:37:16 +020076
77 lockdep_assert_held(&local->chanctx_mtx);
78
79 if (mode == IEEE80211_CHANCTX_EXCLUSIVE)
80 return NULL;
81 if (WARN_ON(!channel))
82 return NULL;
83
84 list_for_each_entry(ctx, &local->chanctx_list, list) {
Michal Kaziore89a96f2012-06-26 14:37:22 +020085 compat_type = ctx->conf.channel_type;
86
Michal Kaziord01a1e62012-06-26 14:37:16 +020087 if (ctx->mode == IEEE80211_CHANCTX_EXCLUSIVE)
88 continue;
89 if (ctx->conf.channel != channel)
90 continue;
Michal Kaziore89a96f2012-06-26 14:37:22 +020091 if (!ieee80211_channel_types_are_compatible(ctx->conf.channel_type,
92 channel_type,
93 &compat_type))
Michal Kaziord01a1e62012-06-26 14:37:16 +020094 continue;
95
Michal Kaziore89a96f2012-06-26 14:37:22 +020096 ieee80211_change_chantype(local, ctx, compat_type);
97
Michal Kaziord01a1e62012-06-26 14:37:16 +020098 return ctx;
99 }
100
101 return NULL;
102}
103
104static struct ieee80211_chanctx *
105ieee80211_new_chanctx(struct ieee80211_local *local,
106 struct ieee80211_channel *channel,
107 enum nl80211_channel_type channel_type,
108 enum ieee80211_chanctx_mode mode)
109{
110 struct ieee80211_chanctx *ctx;
Michal Kazior35f2fce2012-06-26 14:37:20 +0200111 int err;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200112
113 lockdep_assert_held(&local->chanctx_mtx);
114
115 ctx = kzalloc(sizeof(*ctx) + local->hw.chanctx_data_size, GFP_KERNEL);
116 if (!ctx)
117 return ERR_PTR(-ENOMEM);
118
119 ctx->conf.channel = channel;
120 ctx->conf.channel_type = channel_type;
Johannes Berg04ecd252012-09-11 14:34:12 +0200121 ctx->conf.rx_chains_static = 1;
122 ctx->conf.rx_chains_dynamic = 1;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200123 ctx->mode = mode;
124
Johannes Berg55de9082012-07-26 17:24:39 +0200125 if (!local->use_chanctx) {
126 local->_oper_channel_type = channel_type;
127 local->_oper_channel = channel;
128 ieee80211_hw_config(local, 0);
129 } else {
130 err = drv_add_chanctx(local, ctx);
131 if (err) {
132 kfree(ctx);
133 return ERR_PTR(err);
134 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200135 }
136
Michal Kaziord01a1e62012-06-26 14:37:16 +0200137 list_add(&ctx->list, &local->chanctx_list);
138
139 return ctx;
140}
141
142static void ieee80211_free_chanctx(struct ieee80211_local *local,
143 struct ieee80211_chanctx *ctx)
144{
145 lockdep_assert_held(&local->chanctx_mtx);
146
147 WARN_ON_ONCE(ctx->refcount != 0);
148
Johannes Berg55de9082012-07-26 17:24:39 +0200149 if (!local->use_chanctx) {
150 local->_oper_channel_type = NL80211_CHAN_NO_HT;
151 ieee80211_hw_config(local, 0);
152 } else {
153 drv_remove_chanctx(local, ctx);
154 }
Michal Kazior35f2fce2012-06-26 14:37:20 +0200155
Michal Kaziord01a1e62012-06-26 14:37:16 +0200156 list_del(&ctx->list);
157 kfree_rcu(ctx, rcu_head);
158}
159
160static int ieee80211_assign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
161 struct ieee80211_chanctx *ctx)
162{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200163 struct ieee80211_local *local = sdata->local;
164 int ret;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200165
166 lockdep_assert_held(&local->chanctx_mtx);
167
Michal Kazior35f2fce2012-06-26 14:37:20 +0200168 ret = drv_assign_vif_chanctx(local, sdata, ctx);
169 if (ret)
170 return ret;
171
Michal Kaziord01a1e62012-06-26 14:37:16 +0200172 rcu_assign_pointer(sdata->vif.chanctx_conf, &ctx->conf);
173 ctx->refcount++;
174
175 return 0;
176}
177
Michal Kaziore89a96f2012-06-26 14:37:22 +0200178static enum nl80211_channel_type
179ieee80211_calc_chantype(struct ieee80211_local *local,
180 struct ieee80211_chanctx *ctx)
181{
182 struct ieee80211_chanctx_conf *conf = &ctx->conf;
183 struct ieee80211_sub_if_data *sdata;
184 enum nl80211_channel_type result = NL80211_CHAN_NO_HT;
185
186 lockdep_assert_held(&local->chanctx_mtx);
187
188 rcu_read_lock();
189 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
190 if (!ieee80211_sdata_running(sdata))
191 continue;
192 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
193 continue;
194
195 WARN_ON_ONCE(!ieee80211_channel_types_are_compatible(
196 sdata->vif.bss_conf.channel_type,
197 result, &result));
198 }
199 rcu_read_unlock();
200
201 return result;
202}
203
204static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
205 struct ieee80211_chanctx *ctx)
206{
207 enum nl80211_channel_type chantype;
208
209 lockdep_assert_held(&local->chanctx_mtx);
210
211 chantype = ieee80211_calc_chantype(local, ctx);
212 ieee80211_change_chantype(local, ctx, chantype);
213}
214
Michal Kaziord01a1e62012-06-26 14:37:16 +0200215static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
216 struct ieee80211_chanctx *ctx)
217{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200218 struct ieee80211_local *local = sdata->local;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200219
220 lockdep_assert_held(&local->chanctx_mtx);
221
222 ctx->refcount--;
223 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200224
225 drv_unassign_vif_chanctx(local, sdata, ctx);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200226
Johannes Berg04ecd252012-09-11 14:34:12 +0200227 if (ctx->refcount > 0) {
Michal Kaziore89a96f2012-06-26 14:37:22 +0200228 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
Johannes Berg04ecd252012-09-11 14:34:12 +0200229 ieee80211_recalc_smps_chanctx(local, ctx);
230 }
Michal Kaziord01a1e62012-06-26 14:37:16 +0200231}
232
233static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
234{
235 struct ieee80211_local *local = sdata->local;
236 struct ieee80211_chanctx_conf *conf;
237 struct ieee80211_chanctx *ctx;
238
239 lockdep_assert_held(&local->chanctx_mtx);
240
241 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
242 lockdep_is_held(&local->chanctx_mtx));
243 if (!conf)
244 return;
245
246 ctx = container_of(conf, struct ieee80211_chanctx, conf);
247
248 ieee80211_unassign_vif_chanctx(sdata, ctx);
249 if (ctx->refcount == 0)
250 ieee80211_free_chanctx(local, ctx);
251}
252
Johannes Berg04ecd252012-09-11 14:34:12 +0200253void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
254 struct ieee80211_chanctx *chanctx)
255{
256 struct ieee80211_sub_if_data *sdata;
257 u8 rx_chains_static, rx_chains_dynamic;
258
259 lockdep_assert_held(&local->chanctx_mtx);
260
261 rx_chains_static = 1;
262 rx_chains_dynamic = 1;
263
264 rcu_read_lock();
265 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
266 u8 needed_static, needed_dynamic;
267
268 if (!ieee80211_sdata_running(sdata))
269 continue;
270
271 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
272 &chanctx->conf)
273 continue;
274
275 switch (sdata->vif.type) {
276 case NL80211_IFTYPE_P2P_DEVICE:
277 continue;
278 case NL80211_IFTYPE_STATION:
279 if (!sdata->u.mgd.associated)
280 continue;
281 break;
282 case NL80211_IFTYPE_AP_VLAN:
283 continue;
284 case NL80211_IFTYPE_AP:
285 case NL80211_IFTYPE_ADHOC:
286 case NL80211_IFTYPE_WDS:
287 case NL80211_IFTYPE_MESH_POINT:
288 break;
289 default:
290 WARN_ON_ONCE(1);
291 }
292
293 switch (sdata->smps_mode) {
294 default:
295 WARN_ONCE(1, "Invalid SMPS mode %d\n",
296 sdata->smps_mode);
297 /* fall through */
298 case IEEE80211_SMPS_OFF:
299 needed_static = sdata->needed_rx_chains;
300 needed_dynamic = sdata->needed_rx_chains;
301 break;
302 case IEEE80211_SMPS_DYNAMIC:
303 needed_static = 1;
304 needed_dynamic = sdata->needed_rx_chains;
305 break;
306 case IEEE80211_SMPS_STATIC:
307 needed_static = 1;
308 needed_dynamic = 1;
309 break;
310 }
311
312 rx_chains_static = max(rx_chains_static, needed_static);
313 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
314 }
315 rcu_read_unlock();
316
317 if (!local->use_chanctx) {
318 if (rx_chains_static > 1)
319 local->smps_mode = IEEE80211_SMPS_OFF;
320 else if (rx_chains_dynamic > 1)
321 local->smps_mode = IEEE80211_SMPS_DYNAMIC;
322 else
323 local->smps_mode = IEEE80211_SMPS_STATIC;
324 ieee80211_hw_config(local, 0);
325 }
326
327 if (rx_chains_static == chanctx->conf.rx_chains_static &&
328 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
329 return;
330
331 chanctx->conf.rx_chains_static = rx_chains_static;
332 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
333 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
334}
335
Michal Kaziord01a1e62012-06-26 14:37:16 +0200336int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
337 struct ieee80211_channel *channel,
338 enum nl80211_channel_type channel_type,
339 enum ieee80211_chanctx_mode mode)
340{
341 struct ieee80211_local *local = sdata->local;
342 struct ieee80211_chanctx *ctx;
343 int ret;
344
Johannes Berg55de9082012-07-26 17:24:39 +0200345 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
346
Michal Kaziord01a1e62012-06-26 14:37:16 +0200347 mutex_lock(&local->chanctx_mtx);
348 __ieee80211_vif_release_channel(sdata);
349
350 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
351 if (!ctx)
352 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
353 if (IS_ERR(ctx)) {
354 ret = PTR_ERR(ctx);
355 goto out;
356 }
357
Johannes Berg55de9082012-07-26 17:24:39 +0200358 sdata->vif.bss_conf.channel_type = channel_type;
359
Michal Kaziord01a1e62012-06-26 14:37:16 +0200360 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
361 if (ret) {
362 /* if assign fails refcount stays the same */
363 if (ctx->refcount == 0)
364 ieee80211_free_chanctx(local, ctx);
365 goto out;
366 }
367
Johannes Berg04ecd252012-09-11 14:34:12 +0200368 ieee80211_recalc_smps_chanctx(local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200369 out:
370 mutex_unlock(&local->chanctx_mtx);
371 return ret;
372}
373
374void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
375{
Johannes Berg55de9082012-07-26 17:24:39 +0200376 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
377
Michal Kaziord01a1e62012-06-26 14:37:16 +0200378 mutex_lock(&sdata->local->chanctx_mtx);
379 __ieee80211_vif_release_channel(sdata);
380 mutex_unlock(&sdata->local->chanctx_mtx);
381}