blob: a2b06d40aebf53be340398ef7e22319b27731cb2 [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
Johannes Berg1ea6f9c2012-10-24 10:59:25 +0200176 ieee80211_recalc_txpower(sdata);
177
Michal Kaziord01a1e62012-06-26 14:37:16 +0200178 return 0;
179}
180
Michal Kaziore89a96f2012-06-26 14:37:22 +0200181static enum nl80211_channel_type
182ieee80211_calc_chantype(struct ieee80211_local *local,
183 struct ieee80211_chanctx *ctx)
184{
185 struct ieee80211_chanctx_conf *conf = &ctx->conf;
186 struct ieee80211_sub_if_data *sdata;
187 enum nl80211_channel_type result = NL80211_CHAN_NO_HT;
188
189 lockdep_assert_held(&local->chanctx_mtx);
190
191 rcu_read_lock();
192 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
193 if (!ieee80211_sdata_running(sdata))
194 continue;
195 if (rcu_access_pointer(sdata->vif.chanctx_conf) != conf)
196 continue;
197
198 WARN_ON_ONCE(!ieee80211_channel_types_are_compatible(
199 sdata->vif.bss_conf.channel_type,
200 result, &result));
201 }
202 rcu_read_unlock();
203
204 return result;
205}
206
207static void ieee80211_recalc_chanctx_chantype(struct ieee80211_local *local,
208 struct ieee80211_chanctx *ctx)
209{
210 enum nl80211_channel_type chantype;
211
212 lockdep_assert_held(&local->chanctx_mtx);
213
214 chantype = ieee80211_calc_chantype(local, ctx);
215 ieee80211_change_chantype(local, ctx, chantype);
216}
217
Michal Kaziord01a1e62012-06-26 14:37:16 +0200218static void ieee80211_unassign_vif_chanctx(struct ieee80211_sub_if_data *sdata,
219 struct ieee80211_chanctx *ctx)
220{
Michal Kazior35f2fce2012-06-26 14:37:20 +0200221 struct ieee80211_local *local = sdata->local;
Michal Kaziord01a1e62012-06-26 14:37:16 +0200222
223 lockdep_assert_held(&local->chanctx_mtx);
224
225 ctx->refcount--;
226 rcu_assign_pointer(sdata->vif.chanctx_conf, NULL);
Michal Kazior35f2fce2012-06-26 14:37:20 +0200227
228 drv_unassign_vif_chanctx(local, sdata, ctx);
Michal Kaziore89a96f2012-06-26 14:37:22 +0200229
Johannes Berg04ecd252012-09-11 14:34:12 +0200230 if (ctx->refcount > 0) {
Michal Kaziore89a96f2012-06-26 14:37:22 +0200231 ieee80211_recalc_chanctx_chantype(sdata->local, ctx);
Johannes Berg04ecd252012-09-11 14:34:12 +0200232 ieee80211_recalc_smps_chanctx(local, ctx);
233 }
Michal Kaziord01a1e62012-06-26 14:37:16 +0200234}
235
236static void __ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
237{
238 struct ieee80211_local *local = sdata->local;
239 struct ieee80211_chanctx_conf *conf;
240 struct ieee80211_chanctx *ctx;
241
242 lockdep_assert_held(&local->chanctx_mtx);
243
244 conf = rcu_dereference_protected(sdata->vif.chanctx_conf,
245 lockdep_is_held(&local->chanctx_mtx));
246 if (!conf)
247 return;
248
249 ctx = container_of(conf, struct ieee80211_chanctx, conf);
250
251 ieee80211_unassign_vif_chanctx(sdata, ctx);
252 if (ctx->refcount == 0)
253 ieee80211_free_chanctx(local, ctx);
254}
255
Johannes Berg04ecd252012-09-11 14:34:12 +0200256void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
257 struct ieee80211_chanctx *chanctx)
258{
259 struct ieee80211_sub_if_data *sdata;
260 u8 rx_chains_static, rx_chains_dynamic;
261
262 lockdep_assert_held(&local->chanctx_mtx);
263
264 rx_chains_static = 1;
265 rx_chains_dynamic = 1;
266
267 rcu_read_lock();
268 list_for_each_entry_rcu(sdata, &local->interfaces, list) {
269 u8 needed_static, needed_dynamic;
270
271 if (!ieee80211_sdata_running(sdata))
272 continue;
273
274 if (rcu_access_pointer(sdata->vif.chanctx_conf) !=
275 &chanctx->conf)
276 continue;
277
278 switch (sdata->vif.type) {
279 case NL80211_IFTYPE_P2P_DEVICE:
280 continue;
281 case NL80211_IFTYPE_STATION:
282 if (!sdata->u.mgd.associated)
283 continue;
284 break;
285 case NL80211_IFTYPE_AP_VLAN:
286 continue;
287 case NL80211_IFTYPE_AP:
288 case NL80211_IFTYPE_ADHOC:
289 case NL80211_IFTYPE_WDS:
290 case NL80211_IFTYPE_MESH_POINT:
291 break;
292 default:
293 WARN_ON_ONCE(1);
294 }
295
296 switch (sdata->smps_mode) {
297 default:
298 WARN_ONCE(1, "Invalid SMPS mode %d\n",
299 sdata->smps_mode);
300 /* fall through */
301 case IEEE80211_SMPS_OFF:
302 needed_static = sdata->needed_rx_chains;
303 needed_dynamic = sdata->needed_rx_chains;
304 break;
305 case IEEE80211_SMPS_DYNAMIC:
306 needed_static = 1;
307 needed_dynamic = sdata->needed_rx_chains;
308 break;
309 case IEEE80211_SMPS_STATIC:
310 needed_static = 1;
311 needed_dynamic = 1;
312 break;
313 }
314
315 rx_chains_static = max(rx_chains_static, needed_static);
316 rx_chains_dynamic = max(rx_chains_dynamic, needed_dynamic);
317 }
318 rcu_read_unlock();
319
320 if (!local->use_chanctx) {
321 if (rx_chains_static > 1)
322 local->smps_mode = IEEE80211_SMPS_OFF;
323 else if (rx_chains_dynamic > 1)
324 local->smps_mode = IEEE80211_SMPS_DYNAMIC;
325 else
326 local->smps_mode = IEEE80211_SMPS_STATIC;
327 ieee80211_hw_config(local, 0);
328 }
329
330 if (rx_chains_static == chanctx->conf.rx_chains_static &&
331 rx_chains_dynamic == chanctx->conf.rx_chains_dynamic)
332 return;
333
334 chanctx->conf.rx_chains_static = rx_chains_static;
335 chanctx->conf.rx_chains_dynamic = rx_chains_dynamic;
336 drv_change_chanctx(local, chanctx, IEEE80211_CHANCTX_CHANGE_RX_CHAINS);
337}
338
Michal Kaziord01a1e62012-06-26 14:37:16 +0200339int ieee80211_vif_use_channel(struct ieee80211_sub_if_data *sdata,
340 struct ieee80211_channel *channel,
341 enum nl80211_channel_type channel_type,
342 enum ieee80211_chanctx_mode mode)
343{
344 struct ieee80211_local *local = sdata->local;
345 struct ieee80211_chanctx *ctx;
346 int ret;
347
Johannes Berg55de9082012-07-26 17:24:39 +0200348 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
349
Michal Kaziord01a1e62012-06-26 14:37:16 +0200350 mutex_lock(&local->chanctx_mtx);
351 __ieee80211_vif_release_channel(sdata);
352
353 ctx = ieee80211_find_chanctx(local, channel, channel_type, mode);
354 if (!ctx)
355 ctx = ieee80211_new_chanctx(local, channel, channel_type, mode);
356 if (IS_ERR(ctx)) {
357 ret = PTR_ERR(ctx);
358 goto out;
359 }
360
Johannes Berg55de9082012-07-26 17:24:39 +0200361 sdata->vif.bss_conf.channel_type = channel_type;
362
Michal Kaziord01a1e62012-06-26 14:37:16 +0200363 ret = ieee80211_assign_vif_chanctx(sdata, ctx);
364 if (ret) {
365 /* if assign fails refcount stays the same */
366 if (ctx->refcount == 0)
367 ieee80211_free_chanctx(local, ctx);
368 goto out;
369 }
370
Johannes Berg04ecd252012-09-11 14:34:12 +0200371 ieee80211_recalc_smps_chanctx(local, ctx);
Michal Kaziord01a1e62012-06-26 14:37:16 +0200372 out:
373 mutex_unlock(&local->chanctx_mtx);
374 return ret;
375}
376
377void ieee80211_vif_release_channel(struct ieee80211_sub_if_data *sdata)
378{
Johannes Berg55de9082012-07-26 17:24:39 +0200379 WARN_ON(sdata->dev && netif_carrier_ok(sdata->dev));
380
Michal Kaziord01a1e62012-06-26 14:37:16 +0200381 mutex_lock(&sdata->local->chanctx_mtx);
382 __ieee80211_vif_release_channel(sdata);
383 mutex_unlock(&sdata->local->chanctx_mtx);
384}
Johannes Berg3448c002012-09-11 17:57:42 +0200385
386void ieee80211_iter_chan_contexts_atomic(
387 struct ieee80211_hw *hw,
388 void (*iter)(struct ieee80211_hw *hw,
389 struct ieee80211_chanctx_conf *chanctx_conf,
390 void *data),
391 void *iter_data)
392{
393 struct ieee80211_local *local = hw_to_local(hw);
394 struct ieee80211_chanctx *ctx;
395
396 rcu_read_lock();
397 list_for_each_entry_rcu(ctx, &local->chanctx_list, list)
398 iter(hw, &ctx->conf, iter_data);
399 rcu_read_unlock();
400}
401EXPORT_SYMBOL_GPL(ieee80211_iter_chan_contexts_atomic);