Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[sfrench/cifs-2.6.git] / 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_chan_def_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
109 static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
110                                   int *pri40, int *pri80)
111 {
112         int tmp;
113
114         switch (c->width) {
115         case NL80211_CHAN_WIDTH_40:
116                 *pri40 = c->center_freq1;
117                 *pri80 = 0;
118                 break;
119         case NL80211_CHAN_WIDTH_80:
120         case NL80211_CHAN_WIDTH_80P80:
121                 *pri80 = c->center_freq1;
122                 /* n_P20 */
123                 tmp = (30 + c->chan->center_freq - c->center_freq1)/20;
124                 /* n_P40 */
125                 tmp /= 2;
126                 /* freq_P40 */
127                 *pri40 = c->center_freq1 - 20 + 40 * tmp;
128                 break;
129         case NL80211_CHAN_WIDTH_160:
130                 /* n_P20 */
131                 tmp = (70 + c->chan->center_freq - c->center_freq1)/20;
132                 /* n_P40 */
133                 tmp /= 2;
134                 /* freq_P40 */
135                 *pri40 = c->center_freq1 - 60 + 40 * tmp;
136                 /* n_P80 */
137                 tmp /= 2;
138                 *pri80 = c->center_freq1 - 40 + 80 * tmp;
139                 break;
140         default:
141                 WARN_ON_ONCE(1);
142         }
143 }
144
145 const struct cfg80211_chan_def *
146 cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
147                             const struct cfg80211_chan_def *c2)
148 {
149         u32 c1_pri40, c1_pri80, c2_pri40, c2_pri80;
150
151         /* If they are identical, return */
152         if (cfg80211_chandef_identical(c1, c2))
153                 return c1;
154
155         /* otherwise, must have same control channel */
156         if (c1->chan != c2->chan)
157                 return NULL;
158
159         /*
160          * If they have the same width, but aren't identical,
161          * then they can't be compatible.
162          */
163         if (c1->width == c2->width)
164                 return NULL;
165
166         if (c1->width == NL80211_CHAN_WIDTH_20_NOHT ||
167             c1->width == NL80211_CHAN_WIDTH_20)
168                 return c2;
169
170         if (c2->width == NL80211_CHAN_WIDTH_20_NOHT ||
171             c2->width == NL80211_CHAN_WIDTH_20)
172                 return c1;
173
174         chandef_primary_freqs(c1, &c1_pri40, &c1_pri80);
175         chandef_primary_freqs(c2, &c2_pri40, &c2_pri80);
176
177         if (c1_pri40 != c2_pri40)
178                 return NULL;
179
180         WARN_ON(!c1_pri80 && !c2_pri80);
181         if (c1_pri80 && c2_pri80 && c1_pri80 != c2_pri80)
182                 return NULL;
183
184         if (c1->width > c2->width)
185                 return c1;
186         return c2;
187 }
188 EXPORT_SYMBOL(cfg80211_chandef_compatible);
189
190 bool cfg80211_secondary_chans_ok(struct wiphy *wiphy,
191                                  u32 center_freq, u32 bandwidth,
192                                  u32 prohibited_flags)
193 {
194         struct ieee80211_channel *c;
195         u32 freq;
196
197         for (freq = center_freq - bandwidth/2 + 10;
198              freq <= center_freq + bandwidth/2 - 10;
199              freq += 20) {
200                 c = ieee80211_get_channel(wiphy, freq);
201                 if (!c || c->flags & prohibited_flags)
202                         return false;
203         }
204
205         return true;
206 }
207
208 static bool cfg80211_check_beacon_chans(struct wiphy *wiphy,
209                                         u32 center_freq, u32 bw)
210 {
211         return cfg80211_secondary_chans_ok(wiphy, center_freq, bw,
212                                            IEEE80211_CHAN_DISABLED |
213                                            IEEE80211_CHAN_PASSIVE_SCAN |
214                                            IEEE80211_CHAN_NO_IBSS |
215                                            IEEE80211_CHAN_RADAR);
216 }
217
218 bool cfg80211_reg_can_beacon(struct wiphy *wiphy,
219                              struct cfg80211_chan_def *chandef)
220 {
221         u32 width;
222         bool res;
223
224         trace_cfg80211_reg_can_beacon(wiphy, chandef);
225
226         if (WARN_ON(!cfg80211_chan_def_valid(chandef))) {
227                 trace_cfg80211_return_bool(false);
228                 return false;
229         }
230
231         switch (chandef->width) {
232         case NL80211_CHAN_WIDTH_20_NOHT:
233         case NL80211_CHAN_WIDTH_20:
234                 width = 20;
235                 break;
236         case NL80211_CHAN_WIDTH_40:
237                 width = 40;
238                 break;
239         case NL80211_CHAN_WIDTH_80:
240         case NL80211_CHAN_WIDTH_80P80:
241                 width = 80;
242                 break;
243         case NL80211_CHAN_WIDTH_160:
244                 width = 160;
245                 break;
246         default:
247                 WARN_ON_ONCE(1);
248                 trace_cfg80211_return_bool(false);
249                 return false;
250         }
251
252         res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq1, width);
253
254         if (res && chandef->center_freq2)
255                 res = cfg80211_check_beacon_chans(wiphy, chandef->center_freq2,
256                                                   width);
257
258         trace_cfg80211_return_bool(res);
259         return res;
260 }
261 EXPORT_SYMBOL(cfg80211_reg_can_beacon);
262
263 int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
264                                  struct cfg80211_chan_def *chandef)
265 {
266         if (!rdev->ops->set_monitor_channel)
267                 return -EOPNOTSUPP;
268         if (!cfg80211_has_monitors_only(rdev))
269                 return -EBUSY;
270
271         return rdev_set_monitor_channel(rdev, chandef);
272 }
273
274 void
275 cfg80211_get_chan_state(struct wireless_dev *wdev,
276                         struct ieee80211_channel **chan,
277                         enum cfg80211_chan_mode *chanmode)
278 {
279         *chan = NULL;
280         *chanmode = CHAN_MODE_UNDEFINED;
281
282         ASSERT_WDEV_LOCK(wdev);
283
284         if (wdev->netdev && !netif_running(wdev->netdev))
285                 return;
286
287         switch (wdev->iftype) {
288         case NL80211_IFTYPE_ADHOC:
289                 if (wdev->current_bss) {
290                         *chan = wdev->current_bss->pub.channel;
291                         *chanmode = wdev->ibss_fixed
292                                   ? CHAN_MODE_SHARED
293                                   : CHAN_MODE_EXCLUSIVE;
294                         return;
295                 }
296         case NL80211_IFTYPE_STATION:
297         case NL80211_IFTYPE_P2P_CLIENT:
298                 if (wdev->current_bss) {
299                         *chan = wdev->current_bss->pub.channel;
300                         *chanmode = CHAN_MODE_SHARED;
301                         return;
302                 }
303                 break;
304         case NL80211_IFTYPE_AP:
305         case NL80211_IFTYPE_P2P_GO:
306                 if (wdev->beacon_interval) {
307                         *chan = wdev->channel;
308                         *chanmode = CHAN_MODE_SHARED;
309                 }
310                 return;
311         case NL80211_IFTYPE_MESH_POINT:
312                 if (wdev->mesh_id_len) {
313                         *chan = wdev->channel;
314                         *chanmode = CHAN_MODE_SHARED;
315                 }
316                 return;
317         case NL80211_IFTYPE_MONITOR:
318         case NL80211_IFTYPE_AP_VLAN:
319         case NL80211_IFTYPE_WDS:
320                 /* these interface types don't really have a channel */
321                 return;
322         case NL80211_IFTYPE_P2P_DEVICE:
323                 if (wdev->wiphy->features &
324                                 NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL)
325                         *chanmode = CHAN_MODE_EXCLUSIVE;
326                 return;
327         case NL80211_IFTYPE_UNSPECIFIED:
328         case NUM_NL80211_IFTYPES:
329                 WARN_ON(1);
330         }
331
332         return;
333 }