]> Pileus Git - ~andy/linux/blob - net/wireless/chan.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/sage/ceph...
[~andy/linux] / net / wireless / chan.c
1 /*
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
9 #include <linux/export.h>
10 #include <net/cfg80211.h>
11 #include "core.h"
12 #include "rdev-ops.h"
13
14 void 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 }
45 EXPORT_SYMBOL(cfg80211_chandef_create);
46
47 bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
48 {
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;
79                 break;
80         case NL80211_CHAN_WIDTH_80:
81                 if (chandef->center_freq1 != control_freq + 30 &&
82                     chandef->center_freq1 != control_freq + 10 &&
83                     chandef->center_freq1 != control_freq - 10 &&
84                     chandef->center_freq1 != control_freq - 30)
85                         return false;
86                 if (chandef->center_freq2)
87                         return false;
88                 break;
89         case NL80211_CHAN_WIDTH_160:
90                 if (chandef->center_freq1 != control_freq + 70 &&
91                     chandef->center_freq1 != control_freq + 50 &&
92                     chandef->center_freq1 != control_freq + 30 &&
93                     chandef->center_freq1 != control_freq + 10 &&
94                     chandef->center_freq1 != control_freq - 10 &&
95                     chandef->center_freq1 != control_freq - 30 &&
96                     chandef->center_freq1 != control_freq - 50 &&
97                     chandef->center_freq1 != control_freq - 70)
98                         return false;
99                 if (chandef->center_freq2)
100                         return false;
101                 break;
102         default:
103                 return false;
104         }
105
106         return true;
107 }
108 EXPORT_SYMBOL(cfg80211_chandef_valid);
109
110 static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
111                                   int *pri40, int *pri80)
112 {
113         int tmp;
114
115         switch (c->width) {
116         case NL80211_CHAN_WIDTH_40:
117                 *pri40 = c->center_freq1;
118                 *pri80 = 0;
119                 break;
120         case NL80211_CHAN_WIDTH_80:
121         case NL80211_CHAN_WIDTH_80P80:
122                 *pri80 = c->center_freq1;
123                 /* n_P20 */
124                 tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
125                 /* n_P40 */
126                 tmp /= 2;
127                 /* freq_P40 */
128                 *pri40 = c->center_freq1 - 20 + 40 * tmp;
129                 break;
130         case NL80211_CHAN_WIDTH_160:
131                 /* n_P20 */
132                 tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
133                 /* n_P40 */
134                 tmp /= 2;
135                 /* freq_P40 */
136                 *pri40 = c->center_freq1 - 60 + 40 * tmp;
137                 /* n_P80 */
138                 tmp /= 2;
139                 *pri80 = c->center_freq1 - 40 + 80 * tmp;
140                 break;
141         default:
142                 WARN_ON_ONCE(1);
143         }
144 }
145
146 const struct cfg80211_chan_def *
147 cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
148                             const struct cfg80211_chan_def *c2)
149 {
150         u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
151
152         /* If they are identical, return */
153         if (cfg80211_chandef_identical(c1, c2))
154                 return c1;
155
156         /* otherwise, must have same control channel */
157         if (c1->chan != c2->chan)
158                 return NULL;
159
160         /*
161          * If they have the same width, but aren't identical,
162          * then they can't be compatible.
163          */
164         if (c1->width == c2->width)
165                 return NULL;
166
167         if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
168             c1->width == NL80211_CHAN_WIDTH_20)
169                 return c2;
170
171         if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
172             c2->width == NL80211_CHAN_WIDTH_20)
173                 return c1;
174
175         chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
176         chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
177
178         if (c1_pri40 != c2_pri40)
179                 return NULL;
180
181         WARN_ON(!c1_pri80 && !c2_pri80);
182         if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
183                 return NULL;
184
185         if (c1->width > c2->width)
186                 return c1;
187         return c2;
188 }
189 EXPORT_SYMBOL(cfg80211_chandef_compatible);
190
191 static bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
192                                         u32 center_freq, u32 bandwidth,
193                                         u32 prohibited_flags)
194 {
195         struct ieee80211_channel *c;
196         u32 freq;
197
198         for (freq = center_freq - bandwidth/2 + 10;
199              freq <= center_freq + bandwidth/2 - 10;
200              freq += 20) {
201                 c = ieee80211_get_channel(wiphy, freq);
202                 if (!c || c->flags & prohibited_flags)
203                         return false;
204         }
205
206         return true;
207 }
208
209 bool cfg80211_chandef_usable(struct wiphy *wiphy,
210                              const struct cfg80211_chan_def *chandef,
211                              u32 prohibited_flags)
212 {
213         struct ieee80211_sta_ht_cap *ht_cap;
214         struct ieee80211_sta_vht_cap *vht_cap;
215         u32 width, control_freq;
216
217         if (WARN_ON(!cfg80211_chandef_valid(chandef)))
218                 return false;
219
220         ht_cap = &wiphy->bands[chandef->chan->band]->ht_cap;
221         vht_cap = &wiphy->bands[chandef->chan->band]->vht_cap;
222
223         control_freq = chandef->chan->center_freq;
224
225         switch (chandef->width) {
226         case NL80211_CHAN_WIDTH_20:
227                 if (!ht_cap->ht_supported)
228                         return false;
229         case NL80211_CHAN_WIDTH_20_NOHT:
230                 width = 20;
231                 break;
232         case NL80211_CHAN_WIDTH_40:
233                 width = 40;
234                 if (!ht_cap->ht_supported)
235                         return false;
236                 if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
237                     ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
238                         return false;
239                 if (chandef->center_freq1 < control_freq &&
240                     chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
241                         return false;
242                 if (chandef->center_freq1 > control_freq &&
243                     chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
244                         return false;
245                 break;
246         case NL80211_CHAN_WIDTH_80P80:
247                 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))
248                         return false;
249         case NL80211_CHAN_WIDTH_80:
250                 if (!vht_cap->vht_supported)
251                         return false;
252                 width = 80;
253                 break;
254         case NL80211_CHAN_WIDTH_160:
255                 if (!vht_cap->vht_supported)
256                         return false;
257                 if (!(vht_cap->cap & IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ))
258                         return false;
259                 width = 160;
260                 break;
261         default:
262                 WARN_ON_ONCE(1);
263                 return false;
264         }
265
266         /* TODO: missing regulatory check on 80/160 bandwidth */
267
268         if (width > 20)
269                 prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
270
271         if (!cfg80211_secondary_chans_ok(wiphy, chandef->center_freq1,
272                                          width, prohibited_flags))
273                 return false;
274
275         if (!chandef->center_freq2)
276                 return true;
277         return cfg80211_secondary_chans_ok(wiphy, chandef->center_freq2,
278                                            width, prohibited_flags);
279 }
280 EXPORT_SYMBOL(cfg80211_chandef_usable);
281
282 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
283                              struct cfg80211_chan_def *chandef)
284 {
285         bool res;
286
287         trace_cfg80211_reg_can_beacon(wiphy, chandef);
288
289         res = cfg80211_chandef_usable(wiphy, chandef,
290                                       IEEE80211_CHAN_DISABLED |
291                                       IEEE80211_CHAN_PASSIVE_SCAN |
292                                       IEEE80211_CHAN_NO_IBSS |
293                                       IEEE80211_CHAN_RADAR);
294
295         trace_cfg80211_return_bool(res);
296         return res;
297 }
298 EXPORT_SYMBOL(cfg80211_reg_can_beacon);
299
300 int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
301                                  struct cfg80211_chan_def *chandef)
302 {
303         if (!rdev->ops->set_monitor_channel)
304                 return -EOPNOTSUPP;
305         if (!cfg80211_has_monitors_only(rdev))
306                 return -EBUSY;
307
308         return rdev_set_monitor_channel(rdev, chandef);
309 }
310
311 void
312 cfg80211_get_chan_state(struct wireless_dev *wdev,
313                         struct ieee80211_channel **chan,
314                         enum cfg80211_chan_mode *chanmode)
315 {
316         *chan = NULL;
317         *chanmode = CHAN_MODE_UNDEFINED;
318
319         ASSERT_WDEV_LOCK(wdev);
320
321         if (wdev->netdev && !netif_running(wdev->netdev))
322                 return;
323
324         switch (wdev->iftype) {
325         case NL80211_IFTYPE_ADHOC:
326                 if (wdev->current_bss) {
327                         *chan = wdev->current_bss->pub.channel;
328                         *chanmode = wdev->ibss_fixed
329                                   ? CHAN_MODE_SHARED
330                                   : CHAN_MODE_EXCLUSIVE;
331                         return;
332                 }
333         case NL80211_IFTYPE_STATION:
334         case NL80211_IFTYPE_P2P_CLIENT:
335                 if (wdev->current_bss) {
336                         *chan = wdev->current_bss->pub.channel;
337                         *chanmode = CHAN_MODE_SHARED;
338                         return;
339                 }
340                 break;
341         case NL80211_IFTYPE_AP:
342         case NL80211_IFTYPE_P2P_GO:
343                 if (wdev->beacon_interval) {
344                         *chan = wdev->channel;
345                         *chanmode = CHAN_MODE_SHARED;
346                 }
347                 return;
348         case NL80211_IFTYPE_MESH_POINT:
349                 if (wdev->mesh_id_len) {
350                         *chan = wdev->channel;
351                         *chanmode = CHAN_MODE_SHARED;
352                 }
353                 return;
354         case NL80211_IFTYPE_MONITOR:
355         case NL80211_IFTYPE_AP_VLAN:
356         case NL80211_IFTYPE_WDS:
357                 /* these interface types don't really have a channel */
358                 return;
359         case NL80211_IFTYPE_P2P_DEVICE:
360                 if (wdev->wiphy->features &
361                                 NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
362                         *chanmode = CHAN_MODE_EXCLUSIVE;
363                 return;
364         case NL80211_IFTYPE_UNSPECIFIED:
365         case NUM_NL80211_IFTYPES:
366                 WARN_ON(1);
367         }
368
369         return;
370 }