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