Make hostapd_set_freq_params() common
[mech_eap.git] / src / common / hw_features_common.c
1 /*
2  * Common hostapd/wpa_supplicant HW features
3  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4  * Copyright (c) 2015, Qualcomm Atheros, Inc.
5  *
6  * This software may be distributed under the terms of the BSD license.
7  * See README for more details.
8  */
9
10 #include "includes.h"
11
12 #include "common.h"
13 #include "defs.h"
14 #include "ieee802_11_defs.h"
15 #include "ieee802_11_common.h"
16 #include "hw_features_common.h"
17
18
19 struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
20                                                   int chan, int *freq)
21 {
22         int i;
23
24         if (freq)
25                 *freq = 0;
26
27         if (!mode)
28                 return NULL;
29
30         for (i = 0; i < mode->num_channels; i++) {
31                 struct hostapd_channel_data *ch = &mode->channels[i];
32                 if (ch->chan == chan) {
33                         if (freq)
34                                 *freq = ch->freq;
35                         return ch;
36                 }
37         }
38
39         return NULL;
40 }
41
42
43 struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
44                                                   int freq, int *chan)
45 {
46         int i;
47
48         if (chan)
49                 *chan = 0;
50
51         if (!mode)
52                 return NULL;
53
54         for (i = 0; i < mode->num_channels; i++) {
55                 struct hostapd_channel_data *ch = &mode->channels[i];
56                 if (ch->freq == freq) {
57                         if (chan)
58                                 *chan = ch->chan;
59                         return ch;
60                 }
61         }
62
63         return NULL;
64 }
65
66
67 int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
68 {
69         int freq;
70
71         hw_get_channel_chan(mode, chan, &freq);
72
73         return freq;
74 }
75
76
77 int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
78 {
79         int chan;
80
81         hw_get_channel_freq(mode, freq, &chan);
82
83         return chan;
84 }
85
86
87 int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
88                               int sec_chan)
89 {
90         int ok, j, first;
91         int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
92                           184, 192 };
93         size_t k;
94
95         if (pri_chan == sec_chan || !sec_chan)
96                 return 1; /* HT40 not used */
97
98         wpa_printf(MSG_DEBUG,
99                    "HT40: control channel: %d  secondary channel: %d",
100                    pri_chan, sec_chan);
101
102         /* Verify that HT40 secondary channel is an allowed 20 MHz
103          * channel */
104         ok = 0;
105         for (j = 0; j < mode->num_channels; j++) {
106                 struct hostapd_channel_data *chan = &mode->channels[j];
107                 if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
108                     chan->chan == sec_chan) {
109                         ok = 1;
110                         break;
111                 }
112         }
113         if (!ok) {
114                 wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
115                            sec_chan);
116                 return 0;
117         }
118
119         /*
120          * Verify that HT40 primary,secondary channel pair is allowed per
121          * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
122          * 2.4 GHz rules allow all cases where the secondary channel fits into
123          * the list of allowed channels (already checked above).
124          */
125         if (mode->mode != HOSTAPD_MODE_IEEE80211A)
126                 return 1;
127
128         first = pri_chan < sec_chan ? pri_chan : sec_chan;
129
130         ok = 0;
131         for (k = 0; k < ARRAY_SIZE(allowed); k++) {
132                 if (first == allowed[k]) {
133                         ok = 1;
134                         break;
135                 }
136         }
137         if (!ok) {
138                 wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
139                            pri_chan, sec_chan);
140                 return 0;
141         }
142
143         return 1;
144 }
145
146
147 void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
148 {
149         struct ieee80211_ht_operation *oper;
150         struct ieee802_11_elems elems;
151
152         *pri_chan = *sec_chan = 0;
153
154         ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
155         if (elems.ht_operation &&
156             elems.ht_operation_len >= sizeof(*oper)) {
157                 oper = (struct ieee80211_ht_operation *) elems.ht_operation;
158                 *pri_chan = oper->primary_chan;
159                 if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
160                         int sec = oper->ht_param &
161                                 HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
162                         if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
163                                 *sec_chan = *pri_chan + 4;
164                         else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
165                                 *sec_chan = *pri_chan - 4;
166                 }
167         }
168 }
169
170
171 int check_40mhz_5g(struct hostapd_hw_modes *mode,
172                    struct wpa_scan_results *scan_res, int pri_chan,
173                    int sec_chan)
174 {
175         int pri_freq, sec_freq, pri_bss, sec_bss;
176         int bss_pri_chan, bss_sec_chan;
177         size_t i;
178         int match;
179
180         if (!mode || !scan_res || !pri_chan || !sec_chan)
181                 return 0;
182
183         if (pri_chan == sec_chan)
184                 return 0;
185
186         pri_freq = hw_get_freq(mode, pri_chan);
187         sec_freq = hw_get_freq(mode, sec_chan);
188
189         /*
190          * Switch PRI/SEC channels if Beacons were detected on selected SEC
191          * channel, but not on selected PRI channel.
192          */
193         pri_bss = sec_bss = 0;
194         for (i = 0; i < scan_res->num; i++) {
195                 struct wpa_scan_res *bss = scan_res->res[i];
196                 if (bss->freq == pri_freq)
197                         pri_bss++;
198                 else if (bss->freq == sec_freq)
199                         sec_bss++;
200         }
201         if (sec_bss && !pri_bss) {
202                 wpa_printf(MSG_INFO,
203                            "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
204                 return 2;
205         }
206
207         /*
208          * Match PRI/SEC channel with any existing HT40 BSS on the same
209          * channels that we are about to use (if already mixed order in
210          * existing BSSes, use own preference).
211          */
212         match = 0;
213         for (i = 0; i < scan_res->num; i++) {
214                 struct wpa_scan_res *bss = scan_res->res[i];
215                 get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
216                 if (pri_chan == bss_pri_chan &&
217                     sec_chan == bss_sec_chan) {
218                         match = 1;
219                         break;
220                 }
221         }
222         if (!match) {
223                 for (i = 0; i < scan_res->num; i++) {
224                         struct wpa_scan_res *bss = scan_res->res[i];
225                         get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
226                         if (pri_chan == bss_sec_chan &&
227                             sec_chan == bss_pri_chan) {
228                                 wpa_printf(MSG_INFO, "Switch own primary and "
229                                            "secondary channel due to BSS "
230                                            "overlap with " MACSTR,
231                                            MAC2STR(bss->bssid));
232                                 return 2;
233                         }
234                 }
235         }
236
237         return 1;
238 }
239
240
241 int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end)
242 {
243         struct ieee802_11_elems elems;
244         struct ieee80211_ht_operation *oper;
245
246         if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
247                 return 0;
248
249         ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
250         if (!elems.ht_capabilities) {
251                 wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
252                            MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
253                 return 1;
254         }
255
256         if (elems.ht_operation &&
257             elems.ht_operation_len >= sizeof(*oper)) {
258                 oper = (struct ieee80211_ht_operation *) elems.ht_operation;
259                 if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
260                         return 0;
261
262                 wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
263                            MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
264                 return 1;
265         }
266         return 0;
267 }
268
269
270 int check_40mhz_2g4(struct hostapd_hw_modes *mode,
271                     struct wpa_scan_results *scan_res, int pri_chan,
272                     int sec_chan)
273 {
274         int pri_freq, sec_freq;
275         int affected_start, affected_end;
276         size_t i;
277
278         if (!mode || !scan_res || !pri_chan || !sec_chan)
279                 return 0;
280
281         if (pri_chan == sec_chan)
282                 return 0;
283
284         pri_freq = hw_get_freq(mode, pri_chan);
285         sec_freq = hw_get_freq(mode, sec_chan);
286
287         affected_start = (pri_freq + sec_freq) / 2 - 25;
288         affected_end = (pri_freq + sec_freq) / 2 + 25;
289         wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
290                    affected_start, affected_end);
291         for (i = 0; i < scan_res->num; i++) {
292                 struct wpa_scan_res *bss = scan_res->res[i];
293                 int pri = bss->freq;
294                 int sec = pri;
295                 struct ieee802_11_elems elems;
296
297                 /* Check for overlapping 20 MHz BSS */
298                 if (check_20mhz_bss(bss, pri_freq, affected_start,
299                                     affected_end)) {
300                         wpa_printf(MSG_DEBUG,
301                                    "Overlapping 20 MHz BSS is found");
302                         return 0;
303                 }
304
305                 get_pri_sec_chan(bss, &pri_chan, &sec_chan);
306
307                 if (sec_chan) {
308                         if (sec_chan < pri_chan)
309                                 sec = pri - 20;
310                         else
311                                 sec = pri + 20;
312                 }
313
314                 if ((pri < affected_start || pri > affected_end) &&
315                     (sec < affected_start || sec > affected_end))
316                         continue; /* not within affected channel range */
317
318                 wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
319                            " freq=%d pri=%d sec=%d",
320                            MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
321
322                 if (sec_chan) {
323                         if (pri_freq != pri || sec_freq != sec) {
324                                 wpa_printf(MSG_DEBUG,
325                                            "40 MHz pri/sec mismatch with BSS "
326                                            MACSTR
327                                            " <%d,%d> (chan=%d%c) vs. <%d,%d>",
328                                            MAC2STR(bss->bssid),
329                                            pri, sec, pri_chan,
330                                            sec > pri ? '+' : '-',
331                                            pri_freq, sec_freq);
332                                 return 0;
333                         }
334                 }
335
336                 ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
337                                        0);
338                 if (elems.ht_capabilities &&
339                     elems.ht_capabilities_len >=
340                     sizeof(struct ieee80211_ht_capabilities)) {
341                         struct ieee80211_ht_capabilities *ht_cap =
342                                 (struct ieee80211_ht_capabilities *)
343                                 elems.ht_capabilities;
344
345                         if (le_to_host16(ht_cap->ht_capabilities_info) &
346                             HT_CAP_INFO_40MHZ_INTOLERANT) {
347                                 wpa_printf(MSG_DEBUG,
348                                            "40 MHz Intolerant is set on channel %d in BSS "
349                                            MACSTR, pri, MAC2STR(bss->bssid));
350                                 return 0;
351                         }
352                 }
353         }
354
355         return 1;
356 }
357
358
359 int hostapd_set_freq_params(struct hostapd_freq_params *data,
360                             enum hostapd_hw_mode mode,
361                             int freq, int channel, int ht_enabled,
362                             int vht_enabled, int sec_channel_offset,
363                             int vht_oper_chwidth, int center_segment0,
364                             int center_segment1, u32 vht_caps)
365 {
366         int tmp;
367
368         os_memset(data, 0, sizeof(*data));
369         data->mode = mode;
370         data->freq = freq;
371         data->channel = channel;
372         data->ht_enabled = ht_enabled;
373         data->vht_enabled = vht_enabled;
374         data->sec_channel_offset = sec_channel_offset;
375         data->center_freq1 = freq + sec_channel_offset * 10;
376         data->center_freq2 = 0;
377         data->bandwidth = sec_channel_offset ? 40 : 20;
378
379         if (data->vht_enabled) switch (vht_oper_chwidth) {
380         case VHT_CHANWIDTH_USE_HT:
381                 if (center_segment1)
382                         return -1;
383                 if (center_segment0 != 0 &&
384                     5000 + center_segment0 * 5 != data->center_freq1 &&
385                     2407 + center_segment0 * 5 != data->center_freq1)
386                         return -1;
387                 break;
388         case VHT_CHANWIDTH_80P80MHZ:
389                 if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
390                         wpa_printf(MSG_ERROR,
391                                    "80+80 channel width is not supported!");
392                         return -1;
393                 }
394                 if (center_segment1 == center_segment0 + 4 ||
395                     center_segment1 == center_segment0 - 4)
396                         return -1;
397                 data->center_freq2 = 5000 + center_segment1 * 5;
398                 /* fall through */
399         case VHT_CHANWIDTH_80MHZ:
400                 data->bandwidth = 80;
401                 if (vht_oper_chwidth == 1 && center_segment1)
402                         return -1;
403                 if (vht_oper_chwidth == 3 && !center_segment1)
404                         return -1;
405                 if (!sec_channel_offset)
406                         return -1;
407                 /* primary 40 part must match the HT configuration */
408                 tmp = (30 + freq - 5000 - center_segment0 * 5) / 20;
409                 tmp /= 2;
410                 if (data->center_freq1 != 5000 +
411                     center_segment0 * 5 - 20 + 40 * tmp)
412                         return -1;
413                 data->center_freq1 = 5000 + center_segment0 * 5;
414                 break;
415         case VHT_CHANWIDTH_160MHZ:
416                 data->bandwidth = 160;
417                 if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
418                                   VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
419                         wpa_printf(MSG_ERROR,
420                                    "160MHZ channel width is not supported!");
421                         return -1;
422                 }
423                 if (center_segment1)
424                         return -1;
425                 if (!sec_channel_offset)
426                         return -1;
427                 /* primary 40 part must match the HT configuration */
428                 tmp = (70 + freq - 5000 - center_segment0 * 5) / 20;
429                 tmp /= 2;
430                 if (data->center_freq1 != 5000 +
431                     center_segment0 * 5 - 60 + 40 * tmp)
432                         return -1;
433                 data->center_freq1 = 5000 + center_segment0 * 5;
434                 break;
435         }
436
437         return 0;
438 }