Branch data Line data Source code
1 : : /*
2 : : * DFS - Dynamic Frequency Selection
3 : : * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4 : : * Copyright (c) 2013, 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 "utils/includes.h"
11 : :
12 : : #include "utils/common.h"
13 : : #include "common/ieee802_11_defs.h"
14 : : #include "common/wpa_ctrl.h"
15 : : #include "hostapd.h"
16 : : #include "ap_drv_ops.h"
17 : : #include "drivers/driver.h"
18 : : #include "dfs.h"
19 : :
20 : :
21 : 3329 : static int dfs_get_used_n_chans(struct hostapd_iface *iface)
22 : : {
23 : 3329 : int n_chans = 1;
24 : :
25 [ + + ][ + + ]: 3329 : if (iface->conf->ieee80211n && iface->conf->secondary_channel)
26 : 34 : n_chans = 2;
27 : :
28 [ + + ]: 3329 : if (iface->conf->ieee80211ac) {
29 [ + - - - ]: 98 : switch (iface->conf->vht_oper_chwidth) {
30 : : case VHT_CHANWIDTH_USE_HT:
31 : 98 : break;
32 : : case VHT_CHANWIDTH_80MHZ:
33 : 0 : n_chans = 4;
34 : 0 : break;
35 : : case VHT_CHANWIDTH_160MHZ:
36 : 0 : n_chans = 8;
37 : 0 : break;
38 : : default:
39 : 0 : break;
40 : : }
41 : : }
42 : :
43 : 3329 : return n_chans;
44 : : }
45 : :
46 : :
47 : 0 : static int dfs_channel_available(struct hostapd_channel_data *chan,
48 : : int skip_radar)
49 : : {
50 : : /*
51 : : * When radar detection happens, CSA is performed. However, there's no
52 : : * time for CAC, so radar channels must be skipped when finding a new
53 : : * channel for CSA.
54 : : */
55 [ # # ][ # # ]: 0 : if (skip_radar && chan->flag & HOSTAPD_CHAN_RADAR)
56 : 0 : return 0;
57 : :
58 [ # # ]: 0 : if (chan->flag & HOSTAPD_CHAN_DISABLED)
59 : 0 : return 0;
60 [ # # ][ # # ]: 0 : if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
61 : 0 : ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
62 : : HOSTAPD_CHAN_DFS_UNAVAILABLE))
63 : 0 : return 0;
64 : 0 : return 1;
65 : : }
66 : :
67 : :
68 : 0 : static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
69 : : {
70 : : /*
71 : : * The tables contain first valid channel number based on channel width.
72 : : * We will also choose this first channel as the control one.
73 : : */
74 : 0 : int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
75 : : 184, 192 };
76 : : /*
77 : : * VHT80, valid channels based on center frequency:
78 : : * 42, 58, 106, 122, 138, 155
79 : : */
80 : 0 : int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
81 : : /*
82 : : * VHT160 valid channels based on center frequency:
83 : : * 50, 114
84 : : */
85 : 0 : int allowed_160[] = { 36, 100 };
86 : 0 : int *allowed = allowed_40;
87 : 0 : unsigned int i, allowed_no = 0;
88 : :
89 [ # # # # ]: 0 : switch (n_chans) {
90 : : case 2:
91 : 0 : allowed = allowed_40;
92 : 0 : allowed_no = ARRAY_SIZE(allowed_40);
93 : 0 : break;
94 : : case 4:
95 : 0 : allowed = allowed_80;
96 : 0 : allowed_no = ARRAY_SIZE(allowed_80);
97 : 0 : break;
98 : : case 8:
99 : 0 : allowed = allowed_160;
100 : 0 : allowed_no = ARRAY_SIZE(allowed_160);
101 : 0 : break;
102 : : default:
103 : 0 : wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
104 : 0 : break;
105 : : }
106 : :
107 [ # # ]: 0 : for (i = 0; i < allowed_no; i++) {
108 [ # # ]: 0 : if (chan->chan == allowed[i])
109 : 0 : return 1;
110 : : }
111 : :
112 : 0 : return 0;
113 : : }
114 : :
115 : :
116 : 0 : static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
117 : : int first_chan_idx, int num_chans,
118 : : int skip_radar)
119 : : {
120 : : struct hostapd_channel_data *first_chan, *chan;
121 : : int i;
122 : :
123 [ # # ]: 0 : if (first_chan_idx + num_chans >= mode->num_channels)
124 : 0 : return 0;
125 : :
126 : 0 : first_chan = &mode->channels[first_chan_idx];
127 : :
128 [ # # ]: 0 : for (i = 0; i < num_chans; i++) {
129 : 0 : chan = &mode->channels[first_chan_idx + i];
130 : :
131 [ # # ]: 0 : if (first_chan->freq + i * 20 != chan->freq)
132 : 0 : return 0;
133 : :
134 [ # # ]: 0 : if (!dfs_channel_available(chan, skip_radar))
135 : 0 : return 0;
136 : : }
137 : :
138 : 0 : return 1;
139 : : }
140 : :
141 : :
142 : : /*
143 : : * The function assumes HT40+ operation.
144 : : * Make sure to adjust the following variables after calling this:
145 : : * - hapd->secondary_channel
146 : : * - hapd->vht_oper_centr_freq_seg0_idx
147 : : * - hapd->vht_oper_centr_freq_seg1_idx
148 : : */
149 : 0 : static int dfs_find_channel(struct hostapd_iface *iface,
150 : : struct hostapd_channel_data **ret_chan,
151 : : int idx, int skip_radar)
152 : : {
153 : : struct hostapd_hw_modes *mode;
154 : : struct hostapd_channel_data *chan;
155 : 0 : int i, channel_idx = 0, n_chans;
156 : :
157 : 0 : mode = iface->current_mode;
158 : 0 : n_chans = dfs_get_used_n_chans(iface);
159 : :
160 : 0 : wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
161 [ # # ]: 0 : for (i = 0; i < mode->num_channels; i++) {
162 : 0 : chan = &mode->channels[i];
163 : :
164 : : /* Skip HT40/VHT incompatible channels */
165 [ # # ][ # # ]: 0 : if (iface->conf->ieee80211n &&
166 [ # # ]: 0 : iface->conf->secondary_channel &&
167 : 0 : !dfs_is_chan_allowed(chan, n_chans))
168 : 0 : continue;
169 : :
170 : : /* Skip incompatible chandefs */
171 [ # # ]: 0 : if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
172 : 0 : continue;
173 : :
174 [ # # ][ # # ]: 0 : if (ret_chan && idx == channel_idx) {
175 : 0 : wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
176 : 0 : *ret_chan = chan;
177 : 0 : return idx;
178 : : }
179 : 0 : wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
180 : 0 : channel_idx++;
181 : : }
182 : 0 : return channel_idx;
183 : : }
184 : :
185 : :
186 : 0 : static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
187 : : struct hostapd_channel_data *chan,
188 : : int secondary_channel,
189 : : u8 *vht_oper_centr_freq_seg0_idx,
190 : : u8 *vht_oper_centr_freq_seg1_idx)
191 : : {
192 [ # # ]: 0 : if (!iface->conf->ieee80211ac)
193 : 0 : return;
194 : :
195 [ # # ]: 0 : if (!chan)
196 : 0 : return;
197 : :
198 : 0 : *vht_oper_centr_freq_seg1_idx = 0;
199 : :
200 [ # # # # ]: 0 : switch (iface->conf->vht_oper_chwidth) {
201 : : case VHT_CHANWIDTH_USE_HT:
202 [ # # ]: 0 : if (secondary_channel == 1)
203 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan + 2;
204 [ # # ]: 0 : else if (secondary_channel == -1)
205 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan - 2;
206 : : else
207 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan;
208 : 0 : break;
209 : : case VHT_CHANWIDTH_80MHZ:
210 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan + 6;
211 : 0 : break;
212 : : case VHT_CHANWIDTH_160MHZ:
213 : 0 : *vht_oper_centr_freq_seg0_idx = chan->chan + 14;
214 : 0 : break;
215 : : default:
216 : 0 : wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
217 : 0 : *vht_oper_centr_freq_seg0_idx = 0;
218 : 0 : break;
219 : : }
220 : :
221 : 0 : wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
222 : 0 : *vht_oper_centr_freq_seg0_idx,
223 : 0 : *vht_oper_centr_freq_seg1_idx);
224 : : }
225 : :
226 : :
227 : : /* Return start channel idx we will use for mode->channels[idx] */
228 : 3329 : static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
229 : : {
230 : : struct hostapd_hw_modes *mode;
231 : : struct hostapd_channel_data *chan;
232 : 3329 : int channel_no = iface->conf->channel;
233 : 3329 : int res = -1, i;
234 : :
235 : : /* HT40- */
236 [ + + ][ + + ]: 3329 : if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
237 : 11 : channel_no -= 4;
238 : :
239 : : /* VHT */
240 [ + + ]: 3329 : if (iface->conf->ieee80211ac) {
241 [ + - - - ]: 98 : switch (iface->conf->vht_oper_chwidth) {
242 : : case VHT_CHANWIDTH_USE_HT:
243 : 98 : break;
244 : : case VHT_CHANWIDTH_80MHZ:
245 : 0 : channel_no =
246 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx - 6;
247 : 0 : break;
248 : : case VHT_CHANWIDTH_160MHZ:
249 : 0 : channel_no =
250 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx - 14;
251 : 0 : break;
252 : : default:
253 : 0 : wpa_printf(MSG_INFO,
254 : : "DFS only VHT20/40/80/160 is supported now");
255 : 0 : channel_no = -1;
256 : 0 : break;
257 : : }
258 : : }
259 : :
260 : : /* Get idx */
261 : 3329 : mode = iface->current_mode;
262 [ + - ]: 6956 : for (i = 0; i < mode->num_channels; i++) {
263 : 6956 : chan = &mode->channels[i];
264 [ + + ]: 6956 : if (chan->chan == channel_no) {
265 : 3329 : res = i;
266 : 3329 : break;
267 : : }
268 : : }
269 : :
270 [ - + ]: 3329 : if (res == -1)
271 : 0 : wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
272 : :
273 : 3329 : return res;
274 : : }
275 : :
276 : :
277 : : /* At least one channel have radar flag */
278 : 3329 : static int dfs_check_chans_radar(struct hostapd_iface *iface,
279 : : int start_chan_idx, int n_chans)
280 : : {
281 : : struct hostapd_channel_data *channel;
282 : : struct hostapd_hw_modes *mode;
283 : 3329 : int i, res = 0;
284 : :
285 : 3329 : mode = iface->current_mode;
286 : :
287 [ + + ]: 6692 : for (i = 0; i < n_chans; i++) {
288 : 3363 : channel = &mode->channels[start_chan_idx + i];
289 [ - + ]: 3363 : if (channel->flag & HOSTAPD_CHAN_RADAR)
290 : 0 : res++;
291 : : }
292 : :
293 : 3329 : return res;
294 : : }
295 : :
296 : :
297 : : /* All channels available */
298 : 0 : static int dfs_check_chans_available(struct hostapd_iface *iface,
299 : : int start_chan_idx, int n_chans)
300 : : {
301 : : struct hostapd_channel_data *channel;
302 : : struct hostapd_hw_modes *mode;
303 : : int i;
304 : :
305 : 0 : mode = iface->current_mode;
306 : :
307 [ # # ]: 0 : for (i = 0; i < n_chans; i++) {
308 : 0 : channel = &mode->channels[start_chan_idx + i];
309 : :
310 [ # # ]: 0 : if (channel->flag & HOSTAPD_CHAN_DISABLED)
311 : 0 : break;
312 : :
313 [ # # ]: 0 : if (!(channel->flag & HOSTAPD_CHAN_RADAR))
314 : 0 : continue;
315 : :
316 [ # # ]: 0 : if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
317 : : HOSTAPD_CHAN_DFS_AVAILABLE)
318 : 0 : break;
319 : : }
320 : :
321 : 0 : return i == n_chans;
322 : : }
323 : :
324 : :
325 : : /* At least one channel unavailable */
326 : 0 : static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
327 : : int start_chan_idx,
328 : : int n_chans)
329 : : {
330 : : struct hostapd_channel_data *channel;
331 : : struct hostapd_hw_modes *mode;
332 : 0 : int i, res = 0;
333 : :
334 : 0 : mode = iface->current_mode;
335 : :
336 [ # # ]: 0 : for (i = 0; i < n_chans; i++) {
337 : 0 : channel = &mode->channels[start_chan_idx + i];
338 [ # # ]: 0 : if (channel->flag & HOSTAPD_CHAN_DISABLED)
339 : 0 : res++;
340 [ # # ]: 0 : if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
341 : : HOSTAPD_CHAN_DFS_UNAVAILABLE)
342 : 0 : res++;
343 : : }
344 : :
345 : 0 : return res;
346 : : }
347 : :
348 : :
349 : : static struct hostapd_channel_data *
350 : 0 : dfs_get_valid_channel(struct hostapd_iface *iface,
351 : : int *secondary_channel,
352 : : u8 *vht_oper_centr_freq_seg0_idx,
353 : : u8 *vht_oper_centr_freq_seg1_idx,
354 : : int skip_radar)
355 : : {
356 : : struct hostapd_hw_modes *mode;
357 : 0 : struct hostapd_channel_data *chan = NULL;
358 : : int num_available_chandefs;
359 : : int chan_idx;
360 : : u32 _rand;
361 : :
362 : 0 : wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
363 : 0 : *secondary_channel = 0;
364 : 0 : *vht_oper_centr_freq_seg0_idx = 0;
365 : 0 : *vht_oper_centr_freq_seg1_idx = 0;
366 : :
367 [ # # ]: 0 : if (iface->current_mode == NULL)
368 : 0 : return NULL;
369 : :
370 : 0 : mode = iface->current_mode;
371 [ # # ]: 0 : if (mode->mode != HOSTAPD_MODE_IEEE80211A)
372 : 0 : return NULL;
373 : :
374 : : /* Get the count first */
375 : 0 : num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
376 [ # # ]: 0 : if (num_available_chandefs == 0)
377 : 0 : return NULL;
378 : :
379 : 0 : os_get_random((u8 *) &_rand, sizeof(_rand));
380 : 0 : chan_idx = _rand % num_available_chandefs;
381 : 0 : dfs_find_channel(iface, &chan, chan_idx, skip_radar);
382 : :
383 : : /* dfs_find_channel() calculations assume HT40+ */
384 [ # # ]: 0 : if (iface->conf->secondary_channel)
385 : 0 : *secondary_channel = 1;
386 : : else
387 : 0 : *secondary_channel = 0;
388 : :
389 : 0 : dfs_adjust_vht_center_freq(iface, chan,
390 : : *secondary_channel,
391 : : vht_oper_centr_freq_seg0_idx,
392 : : vht_oper_centr_freq_seg1_idx);
393 : :
394 : 0 : return chan;
395 : : }
396 : :
397 : :
398 : 0 : static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
399 : : {
400 : : struct hostapd_hw_modes *mode;
401 : 0 : struct hostapd_channel_data *chan = NULL;
402 : : int i;
403 : :
404 : 0 : mode = iface->current_mode;
405 [ # # ]: 0 : if (mode == NULL)
406 : 0 : return 0;
407 : :
408 : 0 : wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
409 [ # # ]: 0 : for (i = 0; i < iface->current_mode->num_channels; i++) {
410 : 0 : chan = &iface->current_mode->channels[i];
411 [ # # ]: 0 : if (chan->freq == freq) {
412 [ # # ]: 0 : if (chan->flag & HOSTAPD_CHAN_RADAR) {
413 : 0 : chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
414 : 0 : chan->flag |= state;
415 : 0 : return 1; /* Channel found */
416 : : }
417 : : }
418 : : }
419 : 0 : wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
420 : 0 : return 0;
421 : : }
422 : :
423 : :
424 : 0 : static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
425 : : int chan_offset, int chan_width, int cf1,
426 : : int cf2, u32 state)
427 : : {
428 : 0 : int n_chans = 1, i;
429 : : struct hostapd_hw_modes *mode;
430 : 0 : int frequency = freq;
431 : 0 : int ret = 0;
432 : :
433 : 0 : mode = iface->current_mode;
434 [ # # ]: 0 : if (mode == NULL)
435 : 0 : return 0;
436 : :
437 [ # # ]: 0 : if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
438 : 0 : wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
439 : 0 : return 0;
440 : : }
441 : :
442 : : /* Seems cf1 and chan_width is enough here */
443 [ # # # # : 0 : switch (chan_width) {
# ]
444 : : case CHAN_WIDTH_20_NOHT:
445 : : case CHAN_WIDTH_20:
446 : 0 : n_chans = 1;
447 [ # # ]: 0 : if (frequency == 0)
448 : 0 : frequency = cf1;
449 : 0 : break;
450 : : case CHAN_WIDTH_40:
451 : 0 : n_chans = 2;
452 : 0 : frequency = cf1 - 10;
453 : 0 : break;
454 : : case CHAN_WIDTH_80:
455 : 0 : n_chans = 4;
456 : 0 : frequency = cf1 - 30;
457 : 0 : break;
458 : : case CHAN_WIDTH_160:
459 : 0 : n_chans = 8;
460 : 0 : frequency = cf1 - 70;
461 : 0 : break;
462 : : default:
463 : 0 : wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
464 : : chan_width);
465 : 0 : break;
466 : : }
467 : :
468 : 0 : wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
469 : : n_chans);
470 [ # # ]: 0 : for (i = 0; i < n_chans; i++) {
471 : 0 : ret += set_dfs_state_freq(iface, frequency, state);
472 : 0 : frequency = frequency + 20;
473 : : }
474 : :
475 : 0 : return ret;
476 : : }
477 : :
478 : :
479 : 0 : static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
480 : : int chan_width, int cf1, int cf2)
481 : : {
482 : : int start_chan_idx;
483 : : struct hostapd_hw_modes *mode;
484 : : struct hostapd_channel_data *chan;
485 : 0 : int n_chans, i, j, frequency = freq, radar_n_chans = 1;
486 : : u8 radar_chan;
487 : 0 : int res = 0;
488 : :
489 : : /* Our configuration */
490 : 0 : mode = iface->current_mode;
491 : 0 : start_chan_idx = dfs_get_start_chan_idx(iface);
492 : 0 : n_chans = dfs_get_used_n_chans(iface);
493 : :
494 : : /* Check we are on DFS channel(s) */
495 [ # # ]: 0 : if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
496 : 0 : return 0;
497 : :
498 : : /* Reported via radar event */
499 [ # # # # : 0 : switch (chan_width) {
# ]
500 : : case CHAN_WIDTH_20_NOHT:
501 : : case CHAN_WIDTH_20:
502 : 0 : radar_n_chans = 1;
503 [ # # ]: 0 : if (frequency == 0)
504 : 0 : frequency = cf1;
505 : 0 : break;
506 : : case CHAN_WIDTH_40:
507 : 0 : radar_n_chans = 2;
508 : 0 : frequency = cf1 - 10;
509 : 0 : break;
510 : : case CHAN_WIDTH_80:
511 : 0 : radar_n_chans = 4;
512 : 0 : frequency = cf1 - 30;
513 : 0 : break;
514 : : case CHAN_WIDTH_160:
515 : 0 : radar_n_chans = 8;
516 : 0 : frequency = cf1 - 70;
517 : 0 : break;
518 : : default:
519 : 0 : wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
520 : : chan_width);
521 : 0 : break;
522 : : }
523 : :
524 : 0 : ieee80211_freq_to_chan(frequency, &radar_chan);
525 : :
526 [ # # ]: 0 : for (i = 0; i < n_chans; i++) {
527 : 0 : chan = &mode->channels[start_chan_idx + i];
528 [ # # ]: 0 : if (!(chan->flag & HOSTAPD_CHAN_RADAR))
529 : 0 : continue;
530 [ # # ]: 0 : for (j = 0; j < radar_n_chans; j++) {
531 : 0 : wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
532 : 0 : chan->chan, radar_chan + j * 4);
533 [ # # ]: 0 : if (chan->chan == radar_chan + j * 4)
534 : 0 : res++;
535 : : }
536 : : }
537 : :
538 : 0 : wpa_printf(MSG_DEBUG, "overlapped: %d", res);
539 : :
540 : 0 : return res;
541 : : }
542 : :
543 : :
544 : : /*
545 : : * Main DFS handler
546 : : * 1 - continue channel/ap setup
547 : : * 0 - channel/ap setup will be continued after CAC
548 : : * -1 - hit critical error
549 : : */
550 : 411 : int hostapd_handle_dfs(struct hostapd_iface *iface)
551 : : {
552 : : struct hostapd_channel_data *channel;
553 : : int res, n_chans, start_chan_idx;
554 : 411 : int skip_radar = 0;
555 : :
556 : 411 : iface->cac_started = 0;
557 : :
558 : : do {
559 : : /* Get start (first) channel for current configuration */
560 : 411 : start_chan_idx = dfs_get_start_chan_idx(iface);
561 [ - + ]: 411 : if (start_chan_idx == -1)
562 : 0 : return -1;
563 : :
564 : : /* Get number of used channels, depend on width */
565 : 411 : n_chans = dfs_get_used_n_chans(iface);
566 : :
567 : : /* Check if any of configured channels require DFS */
568 : 411 : res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
569 : 411 : wpa_printf(MSG_DEBUG,
570 : : "DFS %d channels required radar detection",
571 : : res);
572 [ + - ]: 411 : if (!res)
573 : 411 : return 1;
574 : :
575 : : /* Check if all channels are DFS available */
576 : 0 : res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
577 [ # # ]: 0 : wpa_printf(MSG_DEBUG,
578 : : "DFS all channels available, (SKIP CAC): %s",
579 : : res ? "yes" : "no");
580 [ # # ]: 0 : if (res)
581 : 0 : return 1;
582 : :
583 : : /* Check if any of configured channels is unavailable */
584 : 0 : res = dfs_check_chans_unavailable(iface, start_chan_idx,
585 : : n_chans);
586 [ # # ]: 0 : wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
587 : : res, res ? "yes": "no");
588 [ # # ]: 0 : if (res) {
589 : 0 : int sec = 0;
590 : 0 : u8 cf1 = 0, cf2 = 0;
591 : :
592 : 0 : channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
593 : : skip_radar);
594 [ # # ]: 0 : if (!channel) {
595 : 0 : wpa_printf(MSG_ERROR, "could not get valid channel");
596 : 0 : return -1;
597 : : }
598 : :
599 : 0 : iface->freq = channel->freq;
600 : 0 : iface->conf->channel = channel->chan;
601 : 0 : iface->conf->secondary_channel = sec;
602 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
603 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
604 : : }
605 [ # # ]: 0 : } while (res);
606 : :
607 : : /* Finally start CAC */
608 : 0 : hostapd_set_state(iface, HAPD_IFACE_DFS);
609 : 0 : wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
610 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
611 : : "freq=%d chan=%d sec_chan=%d",
612 : : iface->freq,
613 : 0 : iface->conf->channel, iface->conf->secondary_channel);
614 [ # # ]: 0 : if (hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
615 : : iface->freq,
616 : 0 : iface->conf->channel,
617 : 0 : iface->conf->ieee80211n,
618 : 0 : iface->conf->ieee80211ac,
619 : 0 : iface->conf->secondary_channel,
620 : 0 : iface->conf->vht_oper_chwidth,
621 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx,
622 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx)) {
623 : 0 : wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
624 : 0 : return -1;
625 : : }
626 : :
627 : 411 : return 0;
628 : : }
629 : :
630 : :
631 : 0 : int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
632 : : int ht_enabled, int chan_offset, int chan_width,
633 : : int cf1, int cf2)
634 : : {
635 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
636 : : "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
637 : : success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
638 : :
639 [ # # ]: 0 : if (success) {
640 : : /* Complete iface/ap configuration */
641 : 0 : set_dfs_state(iface, freq, ht_enabled, chan_offset,
642 : : chan_width, cf1, cf2,
643 : : HOSTAPD_CHAN_DFS_AVAILABLE);
644 : 0 : iface->cac_started = 0;
645 : 0 : hostapd_setup_interface_complete(iface, 0);
646 : : }
647 : :
648 : 0 : return 0;
649 : : }
650 : :
651 : :
652 : 0 : static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
653 : : {
654 : : struct hostapd_channel_data *channel;
655 : : int secondary_channel;
656 : 0 : u8 vht_oper_centr_freq_seg0_idx = 0;
657 : 0 : u8 vht_oper_centr_freq_seg1_idx = 0;
658 : 0 : int skip_radar = 0;
659 : 0 : int err = 1;
660 : :
661 : : /* Radar detected during active CAC */
662 : 0 : iface->cac_started = 0;
663 : 0 : channel = dfs_get_valid_channel(iface, &secondary_channel,
664 : : &vht_oper_centr_freq_seg0_idx,
665 : : &vht_oper_centr_freq_seg1_idx,
666 : : skip_radar);
667 : :
668 [ # # ]: 0 : if (!channel) {
669 : 0 : wpa_printf(MSG_ERROR, "No valid channel available");
670 : 0 : hostapd_setup_interface_complete(iface, err);
671 : 0 : return err;
672 : : }
673 : :
674 : 0 : wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
675 : 0 : channel->chan);
676 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
677 : : "freq=%d chan=%d sec_chan=%d", channel->freq,
678 : 0 : channel->chan, secondary_channel);
679 : :
680 : 0 : iface->freq = channel->freq;
681 : 0 : iface->conf->channel = channel->chan;
682 : 0 : iface->conf->secondary_channel = secondary_channel;
683 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx =
684 : : vht_oper_centr_freq_seg0_idx;
685 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx =
686 : : vht_oper_centr_freq_seg1_idx;
687 : 0 : err = 0;
688 : :
689 : 0 : hostapd_setup_interface_complete(iface, err);
690 : 0 : return err;
691 : : }
692 : :
693 : :
694 : 0 : static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
695 : : {
696 : : struct hostapd_channel_data *channel;
697 : : int secondary_channel;
698 : : u8 vht_oper_centr_freq_seg0_idx;
699 : : u8 vht_oper_centr_freq_seg1_idx;
700 : 0 : int skip_radar = 1;
701 : : struct csa_settings csa_settings;
702 : 0 : struct hostapd_data *hapd = iface->bss[0];
703 : 0 : int err = 1;
704 : :
705 [ # # ][ # # ]: 0 : wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
706 : 0 : __func__, iface->cac_started ? "yes" : "no",
707 : 0 : iface->csa_in_progress ? "yes" : "no");
708 : :
709 : : /* Check if CSA in progress */
710 [ # # ]: 0 : if (iface->csa_in_progress)
711 : 0 : return 0;
712 : :
713 : : /* Check if active CAC */
714 [ # # ]: 0 : if (iface->cac_started)
715 : 0 : return hostapd_dfs_start_channel_switch_cac(iface);
716 : :
717 : : /* Perform channel switch/CSA */
718 : 0 : channel = dfs_get_valid_channel(iface, &secondary_channel,
719 : : &vht_oper_centr_freq_seg0_idx,
720 : : &vht_oper_centr_freq_seg1_idx,
721 : : skip_radar);
722 : :
723 [ # # ]: 0 : if (!channel) {
724 : : /* FIXME: Wait for channel(s) to become available */
725 : 0 : hostapd_disable_iface(iface);
726 : 0 : return err;
727 : : }
728 : :
729 : 0 : wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
730 : 0 : channel->chan);
731 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
732 : : "freq=%d chan=%d sec_chan=%d", channel->freq,
733 : 0 : channel->chan, secondary_channel);
734 : :
735 : : /* Setup CSA request */
736 : 0 : os_memset(&csa_settings, 0, sizeof(csa_settings));
737 : 0 : csa_settings.cs_count = 5;
738 : 0 : csa_settings.block_tx = 1;
739 : 0 : err = hostapd_set_freq_params(&csa_settings.freq_params,
740 : 0 : iface->conf->hw_mode,
741 : : channel->freq,
742 : 0 : channel->chan,
743 : 0 : iface->conf->ieee80211n,
744 : 0 : iface->conf->ieee80211ac,
745 : : secondary_channel,
746 : 0 : iface->conf->vht_oper_chwidth,
747 : : vht_oper_centr_freq_seg0_idx,
748 : : vht_oper_centr_freq_seg1_idx,
749 : 0 : iface->current_mode->vht_capab);
750 : :
751 [ # # ]: 0 : if (err) {
752 : 0 : wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
753 : 0 : hostapd_disable_iface(iface);
754 : 0 : return err;
755 : : }
756 : :
757 : 0 : err = hostapd_switch_channel(hapd, &csa_settings);
758 [ # # ]: 0 : if (err) {
759 : 0 : wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
760 : : err);
761 : 0 : iface->freq = channel->freq;
762 : 0 : iface->conf->channel = channel->chan;
763 : 0 : iface->conf->secondary_channel = secondary_channel;
764 : 0 : iface->conf->vht_oper_centr_freq_seg0_idx =
765 : : vht_oper_centr_freq_seg0_idx;
766 : 0 : iface->conf->vht_oper_centr_freq_seg1_idx =
767 : : vht_oper_centr_freq_seg1_idx;
768 : :
769 : 0 : hostapd_disable_iface(iface);
770 : 0 : hostapd_enable_iface(iface);
771 : 0 : return 0;
772 : : }
773 : :
774 : : /* Channel configuration will be updated once CSA completes and
775 : : * ch_switch_notify event is received */
776 : :
777 : 0 : wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
778 : 0 : return 0;
779 : : }
780 : :
781 : :
782 : 0 : int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
783 : : int ht_enabled, int chan_offset, int chan_width,
784 : : int cf1, int cf2)
785 : : {
786 : : int res;
787 : :
788 [ # # ]: 0 : if (!iface->conf->ieee80211h)
789 : 0 : return 0;
790 : :
791 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
792 : : "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
793 : : freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
794 : :
795 : : /* mark radar frequency as invalid */
796 : 0 : res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
797 : : chan_width, cf1, cf2,
798 : : HOSTAPD_CHAN_DFS_UNAVAILABLE);
799 : :
800 : : /* Skip if reported radar event not overlapped our channels */
801 : 0 : res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
802 [ # # ]: 0 : if (!res)
803 : 0 : return 0;
804 : :
805 : : /* radar detected while operating, switch the channel. */
806 : 0 : res = hostapd_dfs_start_channel_switch(iface);
807 : :
808 : 0 : return res;
809 : : }
810 : :
811 : :
812 : 0 : int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
813 : : int ht_enabled, int chan_offset, int chan_width,
814 : : int cf1, int cf2)
815 : : {
816 : 0 : wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
817 : : "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
818 : : freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
819 : : /* TODO add correct implementation here */
820 : 0 : set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
821 : : cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
822 : 0 : return 0;
823 : : }
824 : :
825 : :
826 : 2919 : int hostapd_is_dfs_required(struct hostapd_iface *iface)
827 : : {
828 : : int n_chans, start_chan_idx;
829 : :
830 [ + + ]: 2919 : if (!iface->current_mode)
831 : 1 : return -1;
832 : :
833 : : /* Get start (first) channel for current configuration */
834 : 2918 : start_chan_idx = dfs_get_start_chan_idx(iface);
835 [ - + ]: 2918 : if (start_chan_idx == -1)
836 : 0 : return -1;
837 : :
838 : : /* Get number of used channels, depend on width */
839 : 2918 : n_chans = dfs_get_used_n_chans(iface);
840 : :
841 : : /* Check if any of configured channels require DFS */
842 : 2919 : return dfs_check_chans_radar(iface, start_chan_idx, n_chans);
843 : : }
|