Branch data Line data Source code
1 : : /*
2 : : * hostapd / Hardware feature query and different modes
3 : : * Copyright 2002-2003, Instant802 Networks, Inc.
4 : : * Copyright 2005-2006, Devicescape Software, Inc.
5 : : * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
6 : : *
7 : : * This software may be distributed under the terms of the BSD license.
8 : : * See README for more details.
9 : : */
10 : :
11 : : #include "utils/includes.h"
12 : :
13 : : #include "utils/common.h"
14 : : #include "utils/eloop.h"
15 : : #include "common/ieee802_11_defs.h"
16 : : #include "common/ieee802_11_common.h"
17 : : #include "common/wpa_ctrl.h"
18 : : #include "hostapd.h"
19 : : #include "ap_config.h"
20 : : #include "ap_drv_ops.h"
21 : : #include "acs.h"
22 : : #include "hw_features.h"
23 : :
24 : :
25 : 524 : void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
26 : : size_t num_hw_features)
27 : : {
28 : : size_t i;
29 : :
30 [ + + ]: 524 : if (hw_features == NULL)
31 : 524 : return;
32 : :
33 [ + + ]: 1048 : for (i = 0; i < num_hw_features; i++) {
34 : 786 : os_free(hw_features[i].channels);
35 : 786 : os_free(hw_features[i].rates);
36 : : }
37 : :
38 : 262 : os_free(hw_features);
39 : : }
40 : :
41 : :
42 : : #ifndef CONFIG_NO_STDOUT_DEBUG
43 : 0 : static char * dfs_info(struct hostapd_channel_data *chan)
44 : : {
45 : : static char info[256];
46 : : char *state;
47 : :
48 [ # # # # : 0 : switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
# ]
49 : : case HOSTAPD_CHAN_DFS_UNKNOWN:
50 : 0 : state = "unknown";
51 : 0 : break;
52 : : case HOSTAPD_CHAN_DFS_USABLE:
53 : 0 : state = "usable";
54 : 0 : break;
55 : : case HOSTAPD_CHAN_DFS_UNAVAILABLE:
56 : 0 : state = "unavailable";
57 : 0 : break;
58 : : case HOSTAPD_CHAN_DFS_AVAILABLE:
59 : 0 : state = "available";
60 : 0 : break;
61 : : default:
62 : 0 : return "";
63 : : }
64 : 0 : os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
65 : 0 : info[sizeof(info) - 1] = '\0';
66 : :
67 : 0 : return info;
68 : : }
69 : : #endif /* CONFIG_NO_STDOUT_DEBUG */
70 : :
71 : :
72 : 263 : int hostapd_get_hw_features(struct hostapd_iface *iface)
73 : : {
74 : 263 : struct hostapd_data *hapd = iface->bss[0];
75 : 263 : int ret = 0, i, j;
76 : : u16 num_modes, flags;
77 : : struct hostapd_hw_modes *modes;
78 : :
79 [ + + ]: 263 : if (hostapd_drv_none(hapd))
80 : 1 : return -1;
81 : 262 : modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags);
82 [ - + ]: 262 : if (modes == NULL) {
83 : 0 : hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
84 : : HOSTAPD_LEVEL_DEBUG,
85 : : "Fetching hardware channel/rate support not "
86 : : "supported.");
87 : 0 : return -1;
88 : : }
89 : :
90 : 262 : iface->hw_flags = flags;
91 : :
92 : 262 : hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
93 : 262 : iface->hw_features = modes;
94 : 262 : iface->num_hw_features = num_modes;
95 : :
96 [ + + ]: 1048 : for (i = 0; i < num_modes; i++) {
97 : 786 : struct hostapd_hw_modes *feature = &modes[i];
98 [ - + ][ # # ]: 786 : int dfs_enabled = hapd->iconf->ieee80211h &&
99 : 0 : (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
100 : :
101 : : /* set flag for channels we can use in current regulatory
102 : : * domain */
103 [ + + ]: 14410 : for (j = 0; j < feature->num_channels; j++) {
104 : 13624 : int dfs = 0;
105 : :
106 : : /*
107 : : * Disable all channels that are marked not to allow
108 : : * IBSS operation or active scanning.
109 : : * Use radar channels only if the driver supports DFS.
110 : : */
111 [ + + ]: 13624 : if ((feature->channels[j].flag &
112 [ - + ]: 3930 : HOSTAPD_CHAN_RADAR) && dfs_enabled) {
113 : 0 : dfs = 1;
114 [ + + ]: 13624 : } else if (feature->channels[j].flag &
115 : : (HOSTAPD_CHAN_NO_IBSS |
116 : : HOSTAPD_CHAN_PASSIVE_SCAN |
117 : : HOSTAPD_CHAN_RADAR)) {
118 : 7860 : feature->channels[j].flag |=
119 : : HOSTAPD_CHAN_DISABLED;
120 : : }
121 : :
122 [ + + ]: 13624 : if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
123 : 7860 : continue;
124 : :
125 [ - + ]: 5764 : wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
126 : : "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
127 : 5764 : feature->mode,
128 : 5764 : feature->channels[j].chan,
129 : 5764 : feature->channels[j].freq,
130 : 5764 : feature->channels[j].max_tx_power,
131 : 0 : dfs ? dfs_info(&feature->channels[j]) : "");
132 : : }
133 : : }
134 : :
135 : 263 : return ret;
136 : : }
137 : :
138 : :
139 : 255 : int hostapd_prepare_rates(struct hostapd_iface *iface,
140 : : struct hostapd_hw_modes *mode)
141 : : {
142 : 255 : int i, num_basic_rates = 0;
143 : 255 : int basic_rates_a[] = { 60, 120, 240, -1 };
144 : 255 : int basic_rates_b[] = { 10, 20, -1 };
145 : 255 : int basic_rates_g[] = { 10, 20, 55, 110, -1 };
146 : : int *basic_rates;
147 : :
148 [ + + ]: 255 : if (iface->conf->basic_rates)
149 : 57 : basic_rates = iface->conf->basic_rates;
150 [ - - + - : 198 : else switch (mode->mode) {
- ]
151 : : case HOSTAPD_MODE_IEEE80211A:
152 : 0 : basic_rates = basic_rates_a;
153 : 0 : break;
154 : : case HOSTAPD_MODE_IEEE80211B:
155 : 0 : basic_rates = basic_rates_b;
156 : 0 : break;
157 : : case HOSTAPD_MODE_IEEE80211G:
158 : 198 : basic_rates = basic_rates_g;
159 : 198 : break;
160 : : case HOSTAPD_MODE_IEEE80211AD:
161 : 0 : return 0; /* No basic rates for 11ad */
162 : : default:
163 : 0 : return -1;
164 : : }
165 : :
166 : 255 : i = 0;
167 [ + + ]: 1218 : while (basic_rates[i] >= 0)
168 : 963 : i++;
169 [ + - ]: 255 : if (i)
170 : 255 : i++; /* -1 termination */
171 : 255 : os_free(iface->basic_rates);
172 : 255 : iface->basic_rates = os_malloc(i * sizeof(int));
173 [ + - ]: 255 : if (iface->basic_rates)
174 : 255 : os_memcpy(iface->basic_rates, basic_rates, i * sizeof(int));
175 : :
176 : 255 : os_free(iface->current_rates);
177 : 255 : iface->num_rates = 0;
178 : :
179 : 255 : iface->current_rates =
180 : 255 : os_calloc(mode->num_rates, sizeof(struct hostapd_rate_data));
181 [ - + ]: 255 : if (!iface->current_rates) {
182 : 0 : wpa_printf(MSG_ERROR, "Failed to allocate memory for rate "
183 : : "table.");
184 : 0 : return -1;
185 : : }
186 : :
187 [ + + ]: 3315 : for (i = 0; i < mode->num_rates; i++) {
188 : : struct hostapd_rate_data *rate;
189 : :
190 [ + + + + ]: 3744 : if (iface->conf->supported_rates &&
191 : 684 : !hostapd_rate_found(iface->conf->supported_rates,
192 : 684 : mode->rates[i]))
193 : 228 : continue;
194 : :
195 : 2832 : rate = &iface->current_rates[iface->num_rates];
196 : 2832 : rate->rate = mode->rates[i];
197 [ + + ]: 2832 : if (hostapd_rate_found(basic_rates, rate->rate)) {
198 : 963 : rate->flags |= HOSTAPD_RATE_BASIC;
199 : 963 : num_basic_rates++;
200 : : }
201 : 2832 : wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x",
202 : : iface->num_rates, rate->rate, rate->flags);
203 : 2832 : iface->num_rates++;
204 : : }
205 : :
206 [ + - ][ - + ]: 255 : if ((iface->num_rates == 0 || num_basic_rates == 0) &&
[ # # ]
207 [ # # ]: 0 : (!iface->conf->ieee80211n || !iface->conf->require_ht)) {
208 : 0 : wpa_printf(MSG_ERROR, "No rates remaining in supported/basic "
209 : : "rate sets (%d,%d).",
210 : : iface->num_rates, num_basic_rates);
211 : 0 : return -1;
212 : : }
213 : :
214 : 255 : return 0;
215 : : }
216 : :
217 : :
218 : : #ifdef CONFIG_IEEE80211N
219 : 255 : static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
220 : : {
221 : : int sec_chan, ok, j, first;
222 : 255 : int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
223 : : 184, 192 };
224 : : size_t k;
225 : :
226 [ + + ]: 255 : if (!iface->conf->secondary_channel)
227 : 251 : return 1; /* HT40 not used */
228 : :
229 : 4 : sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
230 : 4 : wpa_printf(MSG_DEBUG, "HT40: control channel: %d "
231 : : "secondary channel: %d",
232 : 4 : iface->conf->channel, sec_chan);
233 : :
234 : : /* Verify that HT40 secondary channel is an allowed 20 MHz
235 : : * channel */
236 : 4 : ok = 0;
237 [ + - ]: 16 : for (j = 0; j < iface->current_mode->num_channels; j++) {
238 : 16 : struct hostapd_channel_data *chan =
239 : 16 : &iface->current_mode->channels[j];
240 [ + - ][ + + ]: 16 : if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
241 : 16 : chan->chan == sec_chan) {
242 : 4 : ok = 1;
243 : 4 : break;
244 : : }
245 : : }
246 [ - + ]: 4 : if (!ok) {
247 : 0 : wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
248 : : sec_chan);
249 : 0 : return 0;
250 : : }
251 : :
252 : : /*
253 : : * Verify that HT40 primary,secondary channel pair is allowed per
254 : : * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
255 : : * 2.4 GHz rules allow all cases where the secondary channel fits into
256 : : * the list of allowed channels (already checked above).
257 : : */
258 [ + - ]: 4 : if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
259 : 4 : return 1;
260 : :
261 [ # # ]: 0 : if (iface->conf->secondary_channel > 0)
262 : 0 : first = iface->conf->channel;
263 : : else
264 : 0 : first = sec_chan;
265 : :
266 : 0 : ok = 0;
267 [ # # ]: 0 : for (k = 0; k < ARRAY_SIZE(allowed); k++) {
268 [ # # ]: 0 : if (first == allowed[k]) {
269 : 0 : ok = 1;
270 : 0 : break;
271 : : }
272 : : }
273 [ # # ]: 0 : if (!ok) {
274 : 0 : wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
275 : 0 : iface->conf->channel,
276 : 0 : iface->conf->secondary_channel);
277 : 0 : return 0;
278 : : }
279 : :
280 : 255 : return 1;
281 : : }
282 : :
283 : :
284 : 0 : static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
285 : : {
286 [ # # ]: 0 : if (iface->conf->secondary_channel > 0) {
287 : 0 : iface->conf->channel += 4;
288 : 0 : iface->conf->secondary_channel = -1;
289 : : } else {
290 : 0 : iface->conf->channel -= 4;
291 : 0 : iface->conf->secondary_channel = 1;
292 : : }
293 : 0 : }
294 : :
295 : :
296 : 0 : static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
297 : : int *pri_chan, int *sec_chan)
298 : : {
299 : : struct ieee80211_ht_operation *oper;
300 : : struct ieee802_11_elems elems;
301 : :
302 : 0 : *pri_chan = *sec_chan = 0;
303 : :
304 : 0 : ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
305 [ # # ][ # # ]: 0 : if (elems.ht_operation &&
306 : 0 : elems.ht_operation_len >= sizeof(*oper)) {
307 : 0 : oper = (struct ieee80211_ht_operation *) elems.ht_operation;
308 : 0 : *pri_chan = oper->control_chan;
309 [ # # ]: 0 : if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
310 : 0 : int sec = oper->ht_param &
311 : : HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
312 [ # # ]: 0 : if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
313 : 0 : *sec_chan = *pri_chan + 4;
314 [ # # ]: 0 : else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
315 : 0 : *sec_chan = *pri_chan - 4;
316 : : }
317 : : }
318 : 0 : }
319 : :
320 : :
321 : 0 : static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
322 : : struct wpa_scan_results *scan_res)
323 : : {
324 : : int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
325 : : int bss_pri_chan, bss_sec_chan;
326 : : size_t i;
327 : : int match;
328 : :
329 : 0 : pri_chan = iface->conf->channel;
330 : 0 : sec_chan = iface->conf->secondary_channel * 4;
331 : 0 : pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
332 [ # # ]: 0 : if (iface->conf->secondary_channel > 0)
333 : 0 : sec_freq = pri_freq + 20;
334 : : else
335 : 0 : sec_freq = pri_freq - 20;
336 : :
337 : : /*
338 : : * Switch PRI/SEC channels if Beacons were detected on selected SEC
339 : : * channel, but not on selected PRI channel.
340 : : */
341 : 0 : pri_bss = sec_bss = 0;
342 [ # # ]: 0 : for (i = 0; i < scan_res->num; i++) {
343 : 0 : struct wpa_scan_res *bss = scan_res->res[i];
344 [ # # ]: 0 : if (bss->freq == pri_freq)
345 : 0 : pri_bss++;
346 [ # # ]: 0 : else if (bss->freq == sec_freq)
347 : 0 : sec_bss++;
348 : : }
349 [ # # ][ # # ]: 0 : if (sec_bss && !pri_bss) {
350 : 0 : wpa_printf(MSG_INFO, "Switch own primary and secondary "
351 : : "channel to get secondary channel with no Beacons "
352 : : "from other BSSes");
353 : 0 : ieee80211n_switch_pri_sec(iface);
354 : : }
355 : :
356 : : /*
357 : : * Match PRI/SEC channel with any existing HT40 BSS on the same
358 : : * channels that we are about to use (if already mixed order in
359 : : * existing BSSes, use own preference).
360 : : */
361 : 0 : match = 0;
362 [ # # ]: 0 : for (i = 0; i < scan_res->num; i++) {
363 : 0 : struct wpa_scan_res *bss = scan_res->res[i];
364 : 0 : ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
365 [ # # ][ # # ]: 0 : if (pri_chan == bss_pri_chan &&
366 : 0 : sec_chan == bss_sec_chan) {
367 : 0 : match = 1;
368 : 0 : break;
369 : : }
370 : : }
371 [ # # ]: 0 : if (!match) {
372 [ # # ]: 0 : for (i = 0; i < scan_res->num; i++) {
373 : 0 : struct wpa_scan_res *bss = scan_res->res[i];
374 : 0 : ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
375 : : &bss_sec_chan);
376 [ # # ][ # # ]: 0 : if (pri_chan == bss_sec_chan &&
377 : 0 : sec_chan == bss_pri_chan) {
378 : 0 : wpa_printf(MSG_INFO, "Switch own primary and "
379 : : "secondary channel due to BSS "
380 : : "overlap with " MACSTR,
381 : 0 : MAC2STR(bss->bssid));
382 : 0 : ieee80211n_switch_pri_sec(iface);
383 : 0 : break;
384 : : }
385 : : }
386 : : }
387 : :
388 : 0 : return 1;
389 : : }
390 : :
391 : :
392 : 4 : static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
393 : : struct wpa_scan_results *scan_res)
394 : : {
395 : : int pri_freq, sec_freq;
396 : : int affected_start, affected_end;
397 : : size_t i;
398 : :
399 : 4 : pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
400 [ + + ]: 4 : if (iface->conf->secondary_channel > 0)
401 : 3 : sec_freq = pri_freq + 20;
402 : : else
403 : 1 : sec_freq = pri_freq - 20;
404 : 4 : affected_start = (pri_freq + sec_freq) / 2 - 25;
405 : 4 : affected_end = (pri_freq + sec_freq) / 2 + 25;
406 : 4 : wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
407 : : affected_start, affected_end);
408 [ - + ]: 4 : for (i = 0; i < scan_res->num; i++) {
409 : 0 : struct wpa_scan_res *bss = scan_res->res[i];
410 : 0 : int pri = bss->freq;
411 : 0 : int sec = pri;
412 : : int sec_chan, pri_chan;
413 : :
414 : 0 : ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
415 : :
416 [ # # ]: 0 : if (sec_chan) {
417 [ # # ]: 0 : if (sec_chan < pri_chan)
418 : 0 : sec = pri - 20;
419 : : else
420 : 0 : sec = pri + 20;
421 : : }
422 : :
423 [ # # ][ # # ]: 0 : if ((pri < affected_start || pri > affected_end) &&
[ # # ]
424 [ # # ]: 0 : (sec < affected_start || sec > affected_end))
425 : 0 : continue; /* not within affected channel range */
426 : :
427 : 0 : wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
428 : : " freq=%d pri=%d sec=%d",
429 : 0 : MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
430 : :
431 [ # # ]: 0 : if (sec_chan) {
432 [ # # ][ # # ]: 0 : if (pri_freq != pri || sec_freq != sec) {
433 [ # # ]: 0 : wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
434 : : "mismatch with BSS " MACSTR
435 : : " <%d,%d> (chan=%d%c) vs. <%d,%d>",
436 : 0 : MAC2STR(bss->bssid),
437 : : pri, sec, pri_chan,
438 : : sec > pri ? '+' : '-',
439 : : pri_freq, sec_freq);
440 : 0 : return 0;
441 : : }
442 : : }
443 : :
444 : : /* TODO: 40 MHz intolerant */
445 : : }
446 : :
447 : 4 : return 1;
448 : : }
449 : :
450 : :
451 : 4 : static void ieee80211n_check_scan(struct hostapd_iface *iface)
452 : : {
453 : : struct wpa_scan_results *scan_res;
454 : : int oper40;
455 : : int res;
456 : :
457 : : /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
458 : : * allowed per IEEE Std 802.11-2012, 10.15.3.2 */
459 : :
460 : 4 : iface->scan_cb = NULL;
461 : :
462 : 4 : scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
463 [ - + ]: 4 : if (scan_res == NULL) {
464 : 0 : hostapd_setup_interface_complete(iface, 1);
465 : 4 : return;
466 : : }
467 : :
468 [ - + ]: 4 : if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
469 : 0 : oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
470 : : else
471 : 4 : oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
472 : 4 : wpa_scan_results_free(scan_res);
473 : :
474 [ - + ]: 4 : if (!oper40) {
475 : 0 : wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
476 : : "channel pri=%d sec=%d based on overlapping BSSes",
477 : 0 : iface->conf->channel,
478 : 0 : iface->conf->channel +
479 : 0 : iface->conf->secondary_channel * 4);
480 : 0 : iface->conf->secondary_channel = 0;
481 : : }
482 : :
483 : 4 : res = ieee80211n_allowed_ht40_channel_pair(iface);
484 : 4 : hostapd_setup_interface_complete(iface, !res);
485 : : }
486 : :
487 : :
488 : 7 : static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
489 : : struct wpa_driver_scan_params *params)
490 : : {
491 : : /* Scan only the affected frequency range */
492 : : int pri_freq, sec_freq;
493 : : int affected_start, affected_end;
494 : : int i, pos;
495 : : struct hostapd_hw_modes *mode;
496 : :
497 [ - + ]: 7 : if (iface->current_mode == NULL)
498 : 0 : return;
499 : :
500 : 7 : pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
501 [ + + ]: 7 : if (iface->conf->secondary_channel > 0)
502 : 6 : sec_freq = pri_freq + 20;
503 : : else
504 : 1 : sec_freq = pri_freq - 20;
505 : 7 : affected_start = (pri_freq + sec_freq) / 2 - 25;
506 : 7 : affected_end = (pri_freq + sec_freq) / 2 + 25;
507 : 7 : wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
508 : : affected_start, affected_end);
509 : :
510 : 7 : mode = iface->current_mode;
511 : 7 : params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
512 [ - + ]: 7 : if (params->freqs == NULL)
513 : 0 : return;
514 : 7 : pos = 0;
515 : :
516 [ + + ]: 105 : for (i = 0; i < mode->num_channels; i++) {
517 : 98 : struct hostapd_channel_data *chan = &mode->channels[i];
518 [ + + ]: 98 : if (chan->flag & HOSTAPD_CHAN_DISABLED)
519 : 21 : continue;
520 [ + - ][ + + ]: 77 : if (chan->freq < affected_start ||
521 : 77 : chan->freq > affected_end)
522 : 21 : continue;
523 : 56 : params->freqs[pos++] = chan->freq;
524 : : }
525 : : }
526 : :
527 : :
528 : 0 : static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
529 : : struct wpa_driver_scan_params *params)
530 : : {
531 : : /* Scan only the affected frequency range */
532 : : int pri_freq;
533 : : int affected_start, affected_end;
534 : : int i, pos;
535 : : struct hostapd_hw_modes *mode;
536 : :
537 [ # # ]: 0 : if (iface->current_mode == NULL)
538 : 0 : return;
539 : :
540 : 0 : pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
541 [ # # ]: 0 : if (iface->conf->secondary_channel > 0) {
542 : 0 : affected_start = pri_freq - 10;
543 : 0 : affected_end = pri_freq + 30;
544 : : } else {
545 : 0 : affected_start = pri_freq - 30;
546 : 0 : affected_end = pri_freq + 10;
547 : : }
548 : 0 : wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
549 : : affected_start, affected_end);
550 : :
551 : 0 : mode = iface->current_mode;
552 : 0 : params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
553 [ # # ]: 0 : if (params->freqs == NULL)
554 : 0 : return;
555 : 0 : pos = 0;
556 : :
557 [ # # ]: 0 : for (i = 0; i < mode->num_channels; i++) {
558 : 0 : struct hostapd_channel_data *chan = &mode->channels[i];
559 [ # # ]: 0 : if (chan->flag & HOSTAPD_CHAN_DISABLED)
560 : 0 : continue;
561 [ # # ][ # # ]: 0 : if (chan->freq < affected_start ||
562 : 0 : chan->freq > affected_end)
563 : 0 : continue;
564 : 0 : params->freqs[pos++] = chan->freq;
565 : : }
566 : : }
567 : :
568 : :
569 : 258 : static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
570 : : {
571 : : struct wpa_driver_scan_params params;
572 : :
573 [ + + ]: 258 : if (!iface->conf->secondary_channel)
574 : 251 : return 0; /* HT40 not used */
575 : :
576 : 7 : hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
577 : 7 : wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
578 : : "40 MHz channel");
579 : 7 : os_memset(¶ms, 0, sizeof(params));
580 [ + - ]: 7 : if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
581 : 7 : ieee80211n_scan_channels_2g4(iface, ¶ms);
582 : : else
583 : 0 : ieee80211n_scan_channels_5g(iface, ¶ms);
584 [ - + ]: 7 : if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) {
585 : 0 : wpa_printf(MSG_ERROR, "Failed to request a scan of "
586 : : "neighboring BSSes");
587 : 0 : os_free(params.freqs);
588 : 0 : return -1;
589 : : }
590 : 7 : os_free(params.freqs);
591 : :
592 : 7 : iface->scan_cb = ieee80211n_check_scan;
593 : 258 : return 1;
594 : : }
595 : :
596 : :
597 : 258 : static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
598 : : {
599 : 258 : u16 hw = iface->current_mode->ht_capab;
600 : 258 : u16 conf = iface->conf->ht_capab;
601 : :
602 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) &&
603 : 0 : !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) {
604 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
605 : : "HT capability [LDPC]");
606 : 0 : return 0;
607 : : }
608 : :
609 [ + + ][ - + ]: 258 : if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
610 : 7 : !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
611 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
612 : : "HT capability [HT40*]");
613 : 0 : return 0;
614 : : }
615 : :
616 [ + - ][ - + ]: 258 : if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
617 : 258 : (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
618 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
619 : : "HT capability [SMPS-*]");
620 : 0 : return 0;
621 : : }
622 : :
623 [ + + ][ - + ]: 258 : if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
624 : 59 : !(hw & HT_CAP_INFO_GREEN_FIELD)) {
625 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
626 : : "HT capability [GF]");
627 : 0 : return 0;
628 : : }
629 : :
630 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) &&
631 : 0 : !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) {
632 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
633 : : "HT capability [SHORT-GI-20]");
634 : 0 : return 0;
635 : : }
636 : :
637 [ + + ][ - + ]: 258 : if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) &&
638 : 59 : !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) {
639 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
640 : : "HT capability [SHORT-GI-40]");
641 : 0 : return 0;
642 : : }
643 : :
644 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) {
645 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
646 : : "HT capability [TX-STBC]");
647 : 0 : return 0;
648 : : }
649 : :
650 [ - + ]: 258 : if ((conf & HT_CAP_INFO_RX_STBC_MASK) >
651 : 258 : (hw & HT_CAP_INFO_RX_STBC_MASK)) {
652 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
653 : : "HT capability [RX-STBC*]");
654 : 0 : return 0;
655 : : }
656 : :
657 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_DELAYED_BA) &&
658 : 0 : !(hw & HT_CAP_INFO_DELAYED_BA)) {
659 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
660 : : "HT capability [DELAYED-BA]");
661 : 0 : return 0;
662 : : }
663 : :
664 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) &&
665 : 0 : !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) {
666 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
667 : : "HT capability [MAX-AMSDU-7935]");
668 : 0 : return 0;
669 : : }
670 : :
671 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) &&
672 : 0 : !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) {
673 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
674 : : "HT capability [DSSS_CCK-40]");
675 : 0 : return 0;
676 : : }
677 : :
678 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
679 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
680 : : "HT capability [PSMP]");
681 : 0 : return 0;
682 : : }
683 : :
684 [ - + ][ # # ]: 258 : if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
685 : 0 : !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
686 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured "
687 : : "HT capability [LSIG-TXOP-PROT]");
688 : 0 : return 0;
689 : : }
690 : :
691 : 258 : return 1;
692 : : }
693 : :
694 : :
695 : : #ifdef CONFIG_IEEE80211AC
696 : :
697 : 4128 : static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name)
698 : : {
699 : 4128 : u32 req_cap = conf & cap;
700 : :
701 : : /*
702 : : * Make sure we support all requested capabilities.
703 : : * NOTE: We assume that 'cap' represents a capability mask,
704 : : * not a discrete value.
705 : : */
706 [ - + ]: 4128 : if ((hw & req_cap) != req_cap) {
707 : 0 : wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]",
708 : : name);
709 : 0 : return 0;
710 : : }
711 : 4128 : return 1;
712 : : }
713 : :
714 : :
715 : 1290 : static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 cap,
716 : : const char *name)
717 : : {
718 : 1290 : u32 hw_max = hw & cap;
719 : 1290 : u32 conf_val = conf & cap;
720 : :
721 [ - + ]: 1290 : if (conf_val > hw_max) {
722 : 0 : int offset = find_first_bit(cap);
723 : 0 : wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
724 : : name, conf_val >> offset, hw_max >> offset);
725 : 0 : return 0;
726 : : }
727 : 1290 : return 1;
728 : : }
729 : :
730 : :
731 : 258 : static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
732 : : {
733 : 258 : u32 hw = iface->current_mode->vht_capab;
734 : 258 : u32 conf = iface->conf->vht_capab;
735 : :
736 : 258 : wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
737 : : hw, conf);
738 : :
739 : : #define VHT_CAP_CHECK(cap) \
740 : : do { \
741 : : if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
742 : : return 0; \
743 : : } while (0)
744 : :
745 : : #define VHT_CAP_CHECK_MAX(cap) \
746 : : do { \
747 : : if (!ieee80211ac_cap_check_max(hw, conf, cap, #cap)) \
748 : : return 0; \
749 : : } while (0)
750 : :
751 [ - + ]: 258 : VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
752 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
753 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
754 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_RXLDPC);
755 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
756 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
757 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_TXSTBC);
758 [ - + ]: 258 : VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
759 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
760 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
761 [ - + ]: 258 : VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
762 [ - + ]: 258 : VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
763 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
764 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
765 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
766 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
767 [ - + ]: 258 : VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT);
768 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
769 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
770 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
771 [ - + ]: 258 : VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
772 : :
773 : : #undef VHT_CAP_CHECK
774 : : #undef VHT_CAP_CHECK_MAX
775 : :
776 : 258 : return 1;
777 : : }
778 : : #endif /* CONFIG_IEEE80211AC */
779 : :
780 : : #endif /* CONFIG_IEEE80211N */
781 : :
782 : :
783 : 258 : int hostapd_check_ht_capab(struct hostapd_iface *iface)
784 : : {
785 : : #ifdef CONFIG_IEEE80211N
786 : : int ret;
787 [ - + ]: 258 : if (!iface->conf->ieee80211n)
788 : 0 : return 0;
789 [ - + ]: 258 : if (!ieee80211n_supported_ht_capab(iface))
790 : 0 : return -1;
791 : : #ifdef CONFIG_IEEE80211AC
792 [ - + ]: 258 : if (!ieee80211ac_supported_vht_capab(iface))
793 : 0 : return -1;
794 : : #endif /* CONFIG_IEEE80211AC */
795 : 258 : ret = ieee80211n_check_40mhz(iface);
796 [ + + ]: 258 : if (ret)
797 : 7 : return ret;
798 [ - + ]: 251 : if (!ieee80211n_allowed_ht40_channel_pair(iface))
799 : 0 : return -1;
800 : : #endif /* CONFIG_IEEE80211N */
801 : :
802 : 258 : return 0;
803 : : }
804 : :
805 : :
806 : 267 : static int hostapd_is_usable_chan(struct hostapd_iface *iface,
807 : : int channel, int primary)
808 : : {
809 : : int i;
810 : : struct hostapd_channel_data *chan;
811 : :
812 [ + + ]: 497 : for (i = 0; i < iface->current_mode->num_channels; i++) {
813 : 495 : chan = &iface->current_mode->channels[i];
814 [ + + ]: 495 : if (chan->chan != channel)
815 : 230 : continue;
816 : :
817 [ + - ]: 265 : if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
818 : 265 : return 1;
819 : :
820 [ # # ][ # # ]: 0 : wpa_printf(MSG_DEBUG,
[ # # ][ # # ]
821 : : "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s%s",
822 : : primary ? "" : "Configured HT40 secondary ",
823 : 0 : i, chan->chan, chan->flag,
824 : 0 : chan->flag & HOSTAPD_CHAN_NO_IBSS ? " NO-IBSS" : "",
825 : 0 : chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN ?
826 : : " PASSIVE-SCAN" : "",
827 : 0 : chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
828 : : }
829 : :
830 : 267 : return 0;
831 : : }
832 : :
833 : :
834 : 260 : static int hostapd_is_usable_chans(struct hostapd_iface *iface)
835 : : {
836 [ + + ]: 260 : if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
837 : 2 : return 0;
838 : :
839 [ + + ]: 258 : if (!iface->conf->secondary_channel)
840 : 251 : return 1;
841 : :
842 : 260 : return hostapd_is_usable_chan(iface, iface->conf->channel +
843 : 7 : iface->conf->secondary_channel * 4, 0);
844 : : }
845 : :
846 : :
847 : : static enum hostapd_chan_status
848 : 265 : hostapd_check_chans(struct hostapd_iface *iface)
849 : : {
850 [ + + ]: 265 : if (iface->conf->channel) {
851 [ + + ]: 260 : if (hostapd_is_usable_chans(iface))
852 : 258 : return HOSTAPD_CHAN_VALID;
853 : : else
854 : 2 : return HOSTAPD_CHAN_INVALID;
855 : : }
856 : :
857 : : /*
858 : : * The user set channel=0 or channel=acs_survey
859 : : * which is used to trigger ACS.
860 : : */
861 : :
862 [ + - ]: 5 : switch (acs_init(iface)) {
863 : : case HOSTAPD_CHAN_ACS:
864 : 5 : return HOSTAPD_CHAN_ACS;
865 : : case HOSTAPD_CHAN_VALID:
866 : : case HOSTAPD_CHAN_INVALID:
867 : : default:
868 : 265 : return HOSTAPD_CHAN_INVALID;
869 : : }
870 : : }
871 : :
872 : :
873 : 2 : static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
874 : : {
875 : 2 : hostapd_logger(iface->bss[0], NULL,
876 : : HOSTAPD_MODULE_IEEE80211,
877 : : HOSTAPD_LEVEL_WARNING,
878 : : "Configured channel (%d) not found from the "
879 : : "channel list of current mode (%d) %s",
880 : 2 : iface->conf->channel,
881 : 2 : iface->current_mode->mode,
882 : 2 : hostapd_hw_mode_txt(iface->current_mode->mode));
883 : 2 : hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
884 : : HOSTAPD_LEVEL_WARNING,
885 : : "Hardware does not support configured channel");
886 : 2 : }
887 : :
888 : :
889 : 3 : int hostapd_acs_completed(struct hostapd_iface *iface, int err)
890 : : {
891 : 3 : int ret = -1;
892 : :
893 [ - + ]: 3 : if (err)
894 : 0 : goto out;
895 : :
896 [ + - - ]: 3 : switch (hostapd_check_chans(iface)) {
897 : : case HOSTAPD_CHAN_VALID:
898 : 3 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
899 : : ACS_EVENT_COMPLETED "freq=%d channel=%d",
900 : 3 : hostapd_hw_get_freq(iface->bss[0],
901 : 3 : iface->conf->channel),
902 : 3 : iface->conf->channel);
903 : 3 : break;
904 : : case HOSTAPD_CHAN_ACS:
905 : 0 : wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
906 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
907 : 0 : hostapd_notify_bad_chans(iface);
908 : 0 : goto out;
909 : : case HOSTAPD_CHAN_INVALID:
910 : : default:
911 : 0 : wpa_printf(MSG_ERROR, "ACS picked unusable channels");
912 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
913 : 0 : hostapd_notify_bad_chans(iface);
914 : 0 : goto out;
915 : : }
916 : :
917 : 3 : ret = hostapd_check_ht_capab(iface);
918 [ - + ]: 3 : if (ret < 0)
919 : 0 : goto out;
920 [ - + ]: 3 : if (ret == 1) {
921 : 0 : wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
922 : 0 : return 0;
923 : : }
924 : :
925 : 3 : ret = 0;
926 : : out:
927 : 3 : return hostapd_setup_interface_complete(iface, ret);
928 : : }
929 : :
930 : :
931 : : /**
932 : : * hostapd_select_hw_mode - Select the hardware mode
933 : : * @iface: Pointer to interface data.
934 : : * Returns: 0 on success, < 0 on failure
935 : : *
936 : : * Sets up the hardware mode, channel, rates, and passive scanning
937 : : * based on the configuration.
938 : : */
939 : 262 : int hostapd_select_hw_mode(struct hostapd_iface *iface)
940 : : {
941 : : int i;
942 : :
943 [ - + ]: 262 : if (iface->num_hw_features < 1)
944 : 0 : return -1;
945 : :
946 : 262 : iface->current_mode = NULL;
947 [ + - ]: 262 : for (i = 0; i < iface->num_hw_features; i++) {
948 : 262 : struct hostapd_hw_modes *mode = &iface->hw_features[i];
949 [ + - ]: 262 : if (mode->mode == iface->conf->hw_mode) {
950 : 262 : iface->current_mode = mode;
951 : 262 : break;
952 : : }
953 : : }
954 : :
955 [ - + ]: 262 : if (iface->current_mode == NULL) {
956 : 0 : wpa_printf(MSG_ERROR, "Hardware does not support configured "
957 : : "mode");
958 : 0 : hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
959 : : HOSTAPD_LEVEL_WARNING,
960 : : "Hardware does not support configured mode "
961 : : "(%d) (hw_mode in hostapd.conf)",
962 : 0 : (int) iface->conf->hw_mode);
963 : 0 : return -2;
964 : : }
965 : :
966 [ + + + ]: 262 : switch (hostapd_check_chans(iface)) {
967 : : case HOSTAPD_CHAN_VALID:
968 : 255 : return 0;
969 : : case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
970 : 5 : return 1;
971 : : case HOSTAPD_CHAN_INVALID:
972 : : default:
973 : 2 : hostapd_notify_bad_chans(iface);
974 : 262 : return -3;
975 : : }
976 : :
977 : : return 0;
978 : : }
979 : :
980 : :
981 : 257 : const char * hostapd_hw_mode_txt(int mode)
982 : : {
983 [ - - + - : 257 : switch (mode) {
- ]
984 : : case HOSTAPD_MODE_IEEE80211A:
985 : 0 : return "IEEE 802.11a";
986 : : case HOSTAPD_MODE_IEEE80211B:
987 : 0 : return "IEEE 802.11b";
988 : : case HOSTAPD_MODE_IEEE80211G:
989 : 257 : return "IEEE 802.11g";
990 : : case HOSTAPD_MODE_IEEE80211AD:
991 : 0 : return "IEEE 802.11ad";
992 : : default:
993 : 257 : return "UNKNOWN";
994 : : }
995 : : }
996 : :
997 : :
998 : 269 : int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
999 : : {
1000 : : int i;
1001 : :
1002 [ - + ]: 269 : if (!hapd->iface->current_mode)
1003 : 0 : return 0;
1004 : :
1005 [ + - ]: 455 : for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
1006 : 455 : struct hostapd_channel_data *ch =
1007 : 455 : &hapd->iface->current_mode->channels[i];
1008 [ + + ]: 455 : if (ch->chan == chan)
1009 : 269 : return ch->freq;
1010 : : }
1011 : :
1012 : 269 : return 0;
1013 : : }
1014 : :
1015 : :
1016 : 1 : int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
1017 : : {
1018 : : int i;
1019 : :
1020 [ - + ]: 1 : if (!hapd->iface->current_mode)
1021 : 0 : return 0;
1022 : :
1023 [ + - ]: 3 : for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
1024 : 3 : struct hostapd_channel_data *ch =
1025 : 3 : &hapd->iface->current_mode->channels[i];
1026 [ + + ]: 3 : if (ch->freq == freq)
1027 : 1 : return ch->chan;
1028 : : }
1029 : :
1030 : 1 : return 0;
1031 : : }
|