blob: fd556ac05fdbc5c48c365005a0e7deddef79a0ac [file] [log] [blame]
Johannes Berg59bbb6f2009-08-07 17:22:35 +02001/*
2 * This file contains helper code to handle channel
3 * settings and keeping track of what is possible at
4 * any point in time.
5 *
6 * Copyright 2009 Johannes Berg <johannes@sipsolutions.net>
7 */
8
Alexander Simon54858ee5b2011-11-30 16:56:32 +01009#include <linux/export.h>
Johannes Berg59bbb6f2009-08-07 17:22:35 +020010#include <net/cfg80211.h>
11#include "core.h"
Hila Gonene35e4d22012-06-27 17:19:42 +030012#include "rdev-ops.h"
Johannes Berg59bbb6f2009-08-07 17:22:35 +020013
Johannes Berg3d9d1d62012-11-08 23:14:50 +010014void cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
15 struct ieee80211_channel *chan,
16 enum nl80211_channel_type chan_type)
17{
18 if (WARN_ON(!chan))
19 return;
20
21 chandef->chan = chan;
22 chandef->center_freq2 = 0;
23
24 switch (chan_type) {
25 case NL80211_CHAN_NO_HT:
26 chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
27 chandef->center_freq1 = chan->center_freq;
28 break;
29 case NL80211_CHAN_HT20:
30 chandef->width = NL80211_CHAN_WIDTH_20;
31 chandef->center_freq1 = chan->center_freq;
32 break;
33 case NL80211_CHAN_HT40PLUS:
34 chandef->width = NL80211_CHAN_WIDTH_40;
35 chandef->center_freq1 = chan->center_freq + 10;
36 break;
37 case NL80211_CHAN_HT40MINUS:
38 chandef->width = NL80211_CHAN_WIDTH_40;
39 chandef->center_freq1 = chan->center_freq - 10;
40 break;
41 default:
42 WARN_ON(1);
43 }
44}
45EXPORT_SYMBOL(cfg80211_chandef_create);
46
Johannes Berg9f5e8f62012-11-22 16:59:45 +010047bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
Johannes Berg3d9d1d62012-11-08 23:14:50 +010048{
49 u32 control_freq;
50
51 if (!chandef->chan)
52 return false;
53
54 control_freq = chandef->chan->center_freq;
55
56 switch (chandef->width) {
57 case NL80211_CHAN_WIDTH_20:
58 case NL80211_CHAN_WIDTH_20_NOHT:
59 if (chandef->center_freq1 != control_freq)
60 return false;
61 if (chandef->center_freq2)
62 return false;
63 break;
64 case NL80211_CHAN_WIDTH_40:
65 if (chandef->center_freq1 != control_freq + 10 &&
66 chandef->center_freq1 != control_freq - 10)
67 return false;
68 if (chandef->center_freq2)
69 return false;
70 break;
71 case NL80211_CHAN_WIDTH_80P80:
72 if (chandef->center_freq1 != control_freq + 30 &&
73 chandef->center_freq1 != control_freq + 10 &&
74 chandef->center_freq1 != control_freq - 10 &&
75 chandef->center_freq1 != control_freq - 30)
76 return false;
77 if (!chandef->center_freq2)
78 return false;
Johannes Berg9cab3152012-12-14 00:19:08 +010079 /* adjacent is not allowed -- that's a 160 MHz channel */
80 if (chandef->center_freq1 - chandef->center_freq2 == 80 ||
81 chandef->center_freq2 - chandef->center_freq1 == 80)
82 return false;
Johannes Berg3d9d1d62012-11-08 23:14:50 +010083 break;
84 case NL80211_CHAN_WIDTH_80:
85 if (chandef->center_freq1 != control_freq + 30 &&
86 chandef->center_freq1 != control_freq + 10 &&
87 chandef->center_freq1 != control_freq - 10 &&
88 chandef->center_freq1 != control_freq - 30)
89 return false;
90 if (chandef->center_freq2)
91 return false;
92 break;
93 case NL80211_CHAN_WIDTH_160:
94 if (chandef->center_freq1 != control_freq + 70 &&
95 chandef->center_freq1 != control_freq + 50 &&
96 chandef->center_freq1 != control_freq + 30 &&
97 chandef->center_freq1 != control_freq + 10 &&
98 chandef->center_freq1 != control_freq - 10 &&
99 chandef->center_freq1 != control_freq - 30 &&
100 chandef->center_freq1 != control_freq - 50 &&
101 chandef->center_freq1 != control_freq - 70)
102 return false;
103 if (chandef->center_freq2)
104 return false;
105 break;
106 default:
107 return false;
108 }
109
110 return true;
111}
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100112EXPORT_SYMBOL(cfg80211_chandef_valid);
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100113
114static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
115 int *pri40, int *pri80)
116{
117 int tmp;
118
119 switch (c->width) {
120 case NL80211_CHAN_WIDTH_40:
121 *pri40 = c->center_freq1;
122 *pri80 = 0;
123 break;
124 case NL80211_CHAN_WIDTH_80:
125 case NL80211_CHAN_WIDTH_80P80:
126 *pri80 = c->center_freq1;
127 /* n_P20 */
128 tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
129 /* n_P40 */
130 tmp /= 2;
131 /* freq_P40 */
132 *pri40 = c->center_freq1 - 20 + 40 * tmp;
133 break;
134 case NL80211_CHAN_WIDTH_160:
135 /* n_P20 */
136 tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
137 /* n_P40 */
138 tmp /= 2;
139 /* freq_P40 */
140 *pri40 = c->center_freq1 - 60 + 40 * tmp;
141 /* n_P80 */
142 tmp /= 2;
143 *pri80 = c->center_freq1 - 40 + 80 * tmp;
144 break;
145 default:
146 WARN_ON_ONCE(1);
147 }
148}
149
Simon Wunderlich04f39042013-02-08 18:16:19 +0100150static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
151{
152 int width;
153
154 switch (c->width) {
155 case NL80211_CHAN_WIDTH_20:
156 case NL80211_CHAN_WIDTH_20_NOHT:
157 width = 20;
158 break;
159 case NL80211_CHAN_WIDTH_40:
160 width = 40;
161 break;
162 case NL80211_CHAN_WIDTH_80P80:
163 case NL80211_CHAN_WIDTH_80:
164 width = 80;
165 break;
166 case NL80211_CHAN_WIDTH_160:
167 width = 160;
168 break;
169 default:
170 WARN_ON_ONCE(1);
171 return -1;
172 }
173 return width;
174}
175
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100176const struct cfg80211_chan_def *
177cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
178 const struct cfg80211_chan_def *c2)
179{
180 u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
181
182 /* If they are identical, return */
183 if (cfg80211_chandef_identical(c1, c2))
184 return c1;
185
186 /* otherwise, must have same control channel */
187 if (c1->chan != c2->chan)
188 return NULL;
189
190 /*
191 * If they have the same width, but aren't identical,
192 * then they can't be compatible.
193 */
194 if (c1->width == c2->width)
195 return NULL;
196
197 if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
198 c1->width == NL80211_CHAN_WIDTH_20)
199 return c2;
200
201 if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
202 c2->width == NL80211_CHAN_WIDTH_20)
203 return c1;
204
205 chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
206 chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
207
208 if (c1_pri40 != c2_pri40)
209 return NULL;
210
211 WARN_ON(!c1_pri80 && !c2_pri80);
212 if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
213 return NULL;
214
215 if (c1->width > c2->width)
216 return c1;
217 return c2;
218}
219EXPORT_SYMBOL(cfg80211_chandef_compatible);
220
Simon Wunderlich04f39042013-02-08 18:16:19 +0100221static void cfg80211_set_chans_dfs_state(struct wiphy *wiphy, u32 center_freq,
222 u32 bandwidth,
223 enum nl80211_dfs_state dfs_state)
224{
225 struct ieee80211_channel *c;
226 u32 freq;
227
228 for (freq = center_freq - bandwidth/2 + 10;
229 freq <= center_freq + bandwidth/2 - 10;
230 freq += 20) {
231 c = ieee80211_get_channel(wiphy, freq);
232 if (!c || !(c->flags & IEEE80211_CHAN_RADAR))
233 continue;
234
235 c->dfs_state = dfs_state;
236 c->dfs_state_entered = jiffies;
237 }
238}
239
240void cfg80211_set_dfs_state(struct wiphy *wiphy,
241 const struct cfg80211_chan_def *chandef,
242 enum nl80211_dfs_state dfs_state)
243{
244 int width;
245
246 if (WARN_ON(!cfg80211_chandef_valid(chandef)))
247 return;
248
249 width = cfg80211_chandef_get_width(chandef);
250 if (width < 0)
251 return;
252
253 cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq1,
254 width, dfs_state);
255
256 if (!chandef->center_freq2)
257 return;
258 cfg80211_set_chans_dfs_state(wiphy, chandef->center_freq2,
259 width, dfs_state);
260}
261
262static int cfg80211_get_chans_dfs_required(struct wiphy *wiphy,
263 u32 center_freq,
264 u32 bandwidth)
265{
266 struct ieee80211_channel *c;
267 u32 freq;
268
269 for (freq = center_freq - bandwidth/2 + 10;
270 freq <= center_freq + bandwidth/2 - 10;
271 freq += 20) {
272 c = ieee80211_get_channel(wiphy, freq);
273 if (!c)
274 return -EINVAL;
275
276 if (c->flags & IEEE80211_CHAN_RADAR)
277 return 1;
278 }
279 return 0;
280}
281
282
283int cfg80211_chandef_dfs_required(struct wiphy *wiphy,
284 const struct cfg80211_chan_def *chandef)
285{
286 int width;
287 int r;
288
289 if (WARN_ON(!cfg80211_chandef_valid(chandef)))
290 return -EINVAL;
291
292 width = cfg80211_chandef_get_width(chandef);
293 if (width < 0)
294 return -EINVAL;
295
296 r = cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq1,
297 width);
298 if (r)
299 return r;
300
301 if (!chandef->center_freq2)
302 return 0;
303
304 return cfg80211_get_chans_dfs_required(wiphy, chandef->center_freq2,
305 width);
306}
307
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100308static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
309 u32 center_freq, u32 bandwidth,
310 u32 prohibited_flags)
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100311{
312 struct ieee80211_channel *c;
313 u32 freq;
314
315 for (freq = center_freq - bandwidth/2 + 10;
316 freq <= center_freq + bandwidth/2 - 10;
317 freq += 20) {
318 c = ieee80211_get_channel(wiphy, freq);
Simon Wunderlich04f39042013-02-08 18:16:19 +0100319 if (!c)
320 return false;
321
322 /* check for radar flags */
323 if ((prohibited_flags & c->flags & IEEE80211_CHAN_RADAR) &&
324 (c->dfs_state != NL80211_DFS_AVAILABLE))
325 return false;
326
327 /* check for the other flags */
328 if (c->flags & prohibited_flags & ~IEEE80211_CHAN_RADAR)
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100329 return false;
330 }
331
332 return true;
333}
334
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100335bool cfg80211_chandef_usable(struct wiphy *wiphy,
336 const struct cfg80211_chan_def *chandef,
337 u32 prohibited_flags)
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100338{
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100339 struct ieee80211_sta_ht_cap *ht_cap;
340 struct ieee80211_sta_vht_cap *vht_cap;
341 u32 width, control_freq;
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100342
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100343 if (WARN_ON(!cfg80211_chandef_valid(chandef)))
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100344 return false;
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100345
346 ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
347 vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
348
349 control_freq = chandef->chan->center_freq;
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100350
351 switch (chandef->width) {
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100352 case NL80211_CHAN_WIDTH_20:
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100353 if (!ht_cap->ht_supported)
354 return false;
355 case NL80211_CHAN_WIDTH_20_NOHT:
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100356 width = 20;
Mark Mentovai09a02fd2010-11-17 16:34:37 -0500357 break;
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100358 case NL80211_CHAN_WIDTH_40:
359 width = 40;
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100360 if (!ht_cap->ht_supported)
361 return false;
362 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
363 ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
364 return false;
365 if (chandef->center_freq1 < control_freq &&
366 chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
367 return false;
368 if (chandef->center_freq1 > control_freq &&
369 chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
370 return false;
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100371 break;
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100372 case NL80211_CHAN_WIDTH_80P80:
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100373 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
374 return false;
375 case NL80211_CHAN_WIDTH_80:
376 if (!vht_cap->vht_supported)
377 return false;
Johannes Bergc7a6ee22012-12-12 17:50:39 +0100378 prohibited_flags |= IEEE80211_CHAN_NO_80MHZ;
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100379 width = 80;
380 break;
381 case NL80211_CHAN_WIDTH_160:
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100382 if (!vht_cap->vht_supported)
383 return false;
384 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
385 return false;
Johannes Bergc7a6ee22012-12-12 17:50:39 +0100386 prohibited_flags |= IEEE80211_CHAN_NO_160MHZ;
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100387 width = 160;
Mark Mentovai09a02fd2010-11-17 16:34:37 -0500388 break;
Luis R. Rodriguez9236d832010-11-12 16:31:23 -0800389 default:
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100390 WARN_ON_ONCE(1);
Luis R. Rodriguez9236d832010-11-12 16:31:23 -0800391 return false;
Beni Lev4ee3e062012-08-27 12:49:39 +0300392 }
Luis R. Rodriguez9236d832010-11-12 16:31:23 -0800393
Johannes Bergc7a6ee22012-12-12 17:50:39 +0100394 /*
395 * TODO: What if there are only certain 80/160/80+80 MHz channels
396 * allowed by the driver, or only certain combinations?
397 * For 40 MHz the driver can set the NO_HT40 flags, but for
398 * 80/160 MHz and in particular 80+80 MHz this isn't really
399 * feasible and we only have NO_80MHZ/NO_160MHZ so far but
400 * no way to cover 80+80 MHz or more complex restrictions.
401 * Note that such restrictions also need to be advertised to
402 * userspace, for example for P2P channel selection.
403 */
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100404
Johannes Berga6662db2012-12-04 20:49:42 +0100405 if (width > 20)
406 prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
407
Johannes Berg9f5e8f62012-11-22 16:59:45 +0100408 if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
409 width, prohibited_flags))
410 return false;
411
412 if (!chandef->center_freq2)
413 return true;
414 return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
415 width, prohibited_flags);
416}
417EXPORT_SYMBOL(cfg80211_chandef_usable);
418
419bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
420 struct cfg80211_chan_def *chandef)
421{
422 bool res;
423
424 trace_cfg80211_reg_can_beacon(wiphy, chandef);
425
426 res = cfg80211_chandef_usable(wiphy, chandef,
427 IEEE80211_CHAN_DISABLED |
428 IEEE80211_CHAN_PASSIVE_SCAN |
429 IEEE80211_CHAN_NO_IBSS |
430 IEEE80211_CHAN_RADAR);
Johannes Berg3d9d1d62012-11-08 23:14:50 +0100431
432 trace_cfg80211_return_bool(res);
433 return res;
Luis R. Rodriguez9236d832010-11-12 16:31:23 -0800434}
Johannes Berg683b6d32012-11-08 21:25:48 +0100435EXPORT_SYMBOL(cfg80211_reg_can_beacon);
Luis R. Rodriguez9236d832010-11-12 16:31:23 -0800436
Johannes Berge8c9bd52012-06-06 08:18:22 +0200437int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
Johannes Berg683b6d32012-11-08 21:25:48 +0100438 struct cfg80211_chan_def *chandef)
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200439{
Johannes Berge8c9bd52012-06-06 08:18:22 +0200440 if (!rdev->ops->set_monitor_channel)
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200441 return -EOPNOTSUPP;
Michal Kazior4f03c1e2012-06-29 12:47:03 +0200442 if (!cfg80211_has_monitors_only(rdev))
443 return -EBUSY;
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200444
Johannes Berg683b6d32012-11-08 21:25:48 +0100445 return rdev_set_monitor_channel(rdev, chandef);
Johannes Berg59bbb6f2009-08-07 17:22:35 +0200446}
Michal Kazior26ab9a02012-06-29 12:47:00 +0200447
448void
Johannes Berg8e95ea42012-07-10 19:39:02 +0200449cfg80211_get_chan_state(struct wireless_dev *wdev,
Michal Kazior26ab9a02012-06-29 12:47:00 +0200450 struct ieee80211_channel **chan,
451 enum cfg80211_chan_mode *chanmode)
452{
453 *chan = NULL;
454 *chanmode = CHAN_MODE_UNDEFINED;
455
Michal Kazior26ab9a02012-06-29 12:47:00 +0200456 ASSERT_WDEV_LOCK(wdev);
457
Johannes Berg98104fde2012-06-16 00:19:54 +0200458 if (wdev->netdev && !netif_running(wdev->netdev))
Michal Kazior26ab9a02012-06-29 12:47:00 +0200459 return;
460
461 switch (wdev->iftype) {
462 case NL80211_IFTYPE_ADHOC:
463 if (wdev->current_bss) {
464 *chan = wdev->current_bss->pub.channel;
465 *chanmode = wdev->ibss_fixed
466 ? CHAN_MODE_SHARED
467 : CHAN_MODE_EXCLUSIVE;
468 return;
469 }
470 case NL80211_IFTYPE_STATION:
471 case NL80211_IFTYPE_P2P_CLIENT:
472 if (wdev->current_bss) {
473 *chan = wdev->current_bss->pub.channel;
474 *chanmode = CHAN_MODE_SHARED;
475 return;
476 }
477 break;
478 case NL80211_IFTYPE_AP:
479 case NL80211_IFTYPE_P2P_GO:
Simon Wunderlich04f39042013-02-08 18:16:19 +0100480 if (wdev->cac_started) {
481 *chan = wdev->channel;
482 *chanmode = CHAN_MODE_SHARED;
483 } else if (wdev->beacon_interval) {
Felix Fietkauf53594a2012-07-12 16:10:02 +0200484 *chan = wdev->channel;
485 *chanmode = CHAN_MODE_SHARED;
486 }
487 return;
Michal Kazior26ab9a02012-06-29 12:47:00 +0200488 case NL80211_IFTYPE_MESH_POINT:
Felix Fietkauf53594a2012-07-12 16:10:02 +0200489 if (wdev->mesh_id_len) {
490 *chan = wdev->channel;
491 *chanmode = CHAN_MODE_SHARED;
492 }
Michal Kazior26ab9a02012-06-29 12:47:00 +0200493 return;
494 case NL80211_IFTYPE_MONITOR:
495 case NL80211_IFTYPE_AP_VLAN:
496 case NL80211_IFTYPE_WDS:
497 /* these interface types don't really have a channel */
498 return;
Johannes Berg98104fde2012-06-16 00:19:54 +0200499 case NL80211_IFTYPE_P2P_DEVICE:
500 if (wdev->wiphy->features &
501 NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
502 *chanmode = CHAN_MODE_EXCLUSIVE;
503 return;
Michal Kazior26ab9a02012-06-29 12:47:00 +0200504 case NL80211_IFTYPE_UNSPECIFIED:
505 case NUM_NL80211_IFTYPES:
506 WARN_ON(1);
507 }
508
509 return;
510}