]> git.ipfire.org Git - thirdparty/hostap.git/blame - src/ap/dfs.c
hostapd: Make chan_switch command per-interface not per-BSS
[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
7450c128
BG
291 if (res == -1) {
292 wpa_printf(MSG_DEBUG,
293 "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
294 mode->num_channels, channel_no, iface->conf->channel,
295 iface->conf->ieee80211n,
296 iface->conf->secondary_channel,
297 iface->conf->vht_oper_chwidth);
298
299 for (i = 0; i < mode->num_channels; i++) {
300 wpa_printf(MSG_DEBUG, "Available channel: %d",
301 mode->channels[i].chan);
302 }
303 }
58b73e3d
JD
304
305 return res;
306}
307
308
309/* At least one channel have radar flag */
dc036d9e
JM
310static int dfs_check_chans_radar(struct hostapd_iface *iface,
311 int start_chan_idx, int n_chans)
58b73e3d
JD
312{
313 struct hostapd_channel_data *channel;
314 struct hostapd_hw_modes *mode;
315 int i, res = 0;
316
dc036d9e 317 mode = iface->current_mode;
58b73e3d
JD
318
319 for (i = 0; i < n_chans; i++) {
320 channel = &mode->channels[start_chan_idx + i];
321 if (channel->flag & HOSTAPD_CHAN_RADAR)
322 res++;
323 }
324
325 return res;
326}
327
328
329/* All channels available */
dc036d9e 330static int dfs_check_chans_available(struct hostapd_iface *iface,
58b73e3d
JD
331 int start_chan_idx, int n_chans)
332{
333 struct hostapd_channel_data *channel;
334 struct hostapd_hw_modes *mode;
335 int i;
336
dc036d9e 337 mode = iface->current_mode;
58b73e3d 338
8c9cb81f 339 for (i = 0; i < n_chans; i++) {
58b73e3d 340 channel = &mode->channels[start_chan_idx + i];
b8058a69
JD
341
342 if (channel->flag & HOSTAPD_CHAN_DISABLED)
343 break;
344
345 if (!(channel->flag & HOSTAPD_CHAN_RADAR))
346 continue;
347
58b73e3d
JD
348 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
349 HOSTAPD_CHAN_DFS_AVAILABLE)
350 break;
351 }
352
353 return i == n_chans;
354}
355
356
357/* At least one channel unavailable */
dc036d9e 358static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
58b73e3d
JD
359 int start_chan_idx,
360 int n_chans)
361{
362 struct hostapd_channel_data *channel;
363 struct hostapd_hw_modes *mode;
364 int i, res = 0;
365
dc036d9e 366 mode = iface->current_mode;
58b73e3d 367
8c9cb81f 368 for (i = 0; i < n_chans; i++) {
58b73e3d
JD
369 channel = &mode->channels[start_chan_idx + i];
370 if (channel->flag & HOSTAPD_CHAN_DISABLED)
371 res++;
372 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
373 HOSTAPD_CHAN_DFS_UNAVAILABLE)
374 res++;
375 }
376
377 return res;
378}
379
380
32595da6 381static struct hostapd_channel_data *
dc036d9e 382dfs_get_valid_channel(struct hostapd_iface *iface,
32595da6
MK
383 int *secondary_channel,
384 u8 *vht_oper_centr_freq_seg0_idx,
b72f949b
MK
385 u8 *vht_oper_centr_freq_seg1_idx,
386 int skip_radar)
e76da505
JD
387{
388 struct hostapd_hw_modes *mode;
389 struct hostapd_channel_data *chan = NULL;
32595da6
MK
390 int num_available_chandefs;
391 int chan_idx;
e76da505
JD
392 u32 _rand;
393
394 wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
3d91a047
JM
395 *secondary_channel = 0;
396 *vht_oper_centr_freq_seg0_idx = 0;
397 *vht_oper_centr_freq_seg1_idx = 0;
e76da505 398
dc036d9e 399 if (iface->current_mode == NULL)
e76da505
JD
400 return NULL;
401
dc036d9e 402 mode = iface->current_mode;
e76da505
JD
403 if (mode->mode != HOSTAPD_MODE_IEEE80211A)
404 return NULL;
405
32595da6 406 /* Get the count first */
b72f949b 407 num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
32595da6
MK
408 if (num_available_chandefs == 0)
409 return NULL;
e76da505 410
32595da6
MK
411 os_get_random((u8 *) &_rand, sizeof(_rand));
412 chan_idx = _rand % num_available_chandefs;
b72f949b 413 dfs_find_channel(iface, &chan, chan_idx, skip_radar);
32595da6
MK
414
415 /* dfs_find_channel() calculations assume HT40+ */
dc036d9e 416 if (iface->conf->secondary_channel)
32595da6
MK
417 *secondary_channel = 1;
418 else
419 *secondary_channel = 0;
420
dc036d9e 421 dfs_adjust_vht_center_freq(iface, chan,
eed65aad 422 *secondary_channel,
32595da6
MK
423 vht_oper_centr_freq_seg0_idx,
424 vht_oper_centr_freq_seg1_idx);
58b73e3d 425
e76da505
JD
426 return chan;
427}
428
429
dc036d9e 430static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
e76da505
JD
431{
432 struct hostapd_hw_modes *mode;
433 struct hostapd_channel_data *chan = NULL;
434 int i;
435
dc036d9e 436 mode = iface->current_mode;
e76da505
JD
437 if (mode == NULL)
438 return 0;
439
58b73e3d 440 wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
dc036d9e
JM
441 for (i = 0; i < iface->current_mode->num_channels; i++) {
442 chan = &iface->current_mode->channels[i];
e76da505
JD
443 if (chan->freq == freq) {
444 if (chan->flag & HOSTAPD_CHAN_RADAR) {
445 chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
446 chan->flag |= state;
447 return 1; /* Channel found */
448 }
449 }
450 }
451 wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
452 return 0;
453}
454
455
dc036d9e 456static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
58b73e3d
JD
457 int chan_offset, int chan_width, int cf1,
458 int cf2, u32 state)
459{
460 int n_chans = 1, i;
461 struct hostapd_hw_modes *mode;
462 int frequency = freq;
463 int ret = 0;
464
dc036d9e 465 mode = iface->current_mode;
58b73e3d
JD
466 if (mode == NULL)
467 return 0;
468
469 if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
470 wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
471 return 0;
472 }
473
474 /* Seems cf1 and chan_width is enough here */
475 switch (chan_width) {
476 case CHAN_WIDTH_20_NOHT:
477 case CHAN_WIDTH_20:
478 n_chans = 1;
bb337dda
JM
479 if (frequency == 0)
480 frequency = cf1;
58b73e3d
JD
481 break;
482 case CHAN_WIDTH_40:
483 n_chans = 2;
484 frequency = cf1 - 10;
485 break;
486 case CHAN_WIDTH_80:
487 n_chans = 4;
488 frequency = cf1 - 30;
489 break;
899cc14e
JD
490 case CHAN_WIDTH_160:
491 n_chans = 8;
492 frequency = cf1 - 70;
493 break;
58b73e3d
JD
494 default:
495 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
496 chan_width);
497 break;
498 }
499
500 wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
501 n_chans);
502 for (i = 0; i < n_chans; i++) {
dc036d9e 503 ret += set_dfs_state_freq(iface, frequency, state);
58b73e3d
JD
504 frequency = frequency + 20;
505 }
506
507 return ret;
508}
509
510
dc036d9e 511static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
58b73e3d
JD
512 int chan_width, int cf1, int cf2)
513{
514 int start_chan_idx;
515 struct hostapd_hw_modes *mode;
516 struct hostapd_channel_data *chan;
517 int n_chans, i, j, frequency = freq, radar_n_chans = 1;
518 u8 radar_chan;
519 int res = 0;
520
58b73e3d 521 /* Our configuration */
dc036d9e
JM
522 mode = iface->current_mode;
523 start_chan_idx = dfs_get_start_chan_idx(iface);
524 n_chans = dfs_get_used_n_chans(iface);
58b73e3d 525
5eaf240a 526 /* Check we are on DFS channel(s) */
dc036d9e 527 if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
5eaf240a
JD
528 return 0;
529
58b73e3d
JD
530 /* Reported via radar event */
531 switch (chan_width) {
532 case CHAN_WIDTH_20_NOHT:
533 case CHAN_WIDTH_20:
534 radar_n_chans = 1;
bb337dda
JM
535 if (frequency == 0)
536 frequency = cf1;
58b73e3d
JD
537 break;
538 case CHAN_WIDTH_40:
539 radar_n_chans = 2;
540 frequency = cf1 - 10;
541 break;
542 case CHAN_WIDTH_80:
543 radar_n_chans = 4;
544 frequency = cf1 - 30;
545 break;
899cc14e
JD
546 case CHAN_WIDTH_160:
547 radar_n_chans = 8;
548 frequency = cf1 - 70;
549 break;
58b73e3d
JD
550 default:
551 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
552 chan_width);
553 break;
554 }
555
556 ieee80211_freq_to_chan(frequency, &radar_chan);
557
558 for (i = 0; i < n_chans; i++) {
559 chan = &mode->channels[start_chan_idx + i];
5eaf240a
JD
560 if (!(chan->flag & HOSTAPD_CHAN_RADAR))
561 continue;
58b73e3d
JD
562 for (j = 0; j < radar_n_chans; j++) {
563 wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
564 chan->chan, radar_chan + j * 4);
565 if (chan->chan == radar_chan + j * 4)
566 res++;
567 }
568 }
569
570 wpa_printf(MSG_DEBUG, "overlapped: %d", res);
571
572 return res;
573}
574
575
bbbacbf2
JD
576static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
577 int start_chan_idx, int n_chans)
578{
579 struct hostapd_channel_data *channel;
580 struct hostapd_hw_modes *mode;
581 int i;
582 unsigned int cac_time_ms = 0;
583
584 mode = iface->current_mode;
585
586 for (i = 0; i < n_chans; i++) {
587 channel = &mode->channels[start_chan_idx + i];
588 if (!(channel->flag & HOSTAPD_CHAN_RADAR))
589 continue;
590 if (channel->dfs_cac_ms > cac_time_ms)
591 cac_time_ms = channel->dfs_cac_ms;
592 }
593
594 return cac_time_ms;
595}
596
597
e76da505
JD
598/*
599 * Main DFS handler
600 * 1 - continue channel/ap setup
601 * 0 - channel/ap setup will be continued after CAC
602 * -1 - hit critical error
603 */
dc036d9e 604int hostapd_handle_dfs(struct hostapd_iface *iface)
e76da505 605{
e76da505 606 struct hostapd_channel_data *channel;
58b73e3d 607 int res, n_chans, start_chan_idx;
b72f949b 608 int skip_radar = 0;
58b73e3d 609
dc036d9e 610 iface->cac_started = 0;
954e71d2 611
58b73e3d
JD
612 do {
613 /* Get start (first) channel for current configuration */
dc036d9e 614 start_chan_idx = dfs_get_start_chan_idx(iface);
58b73e3d
JD
615 if (start_chan_idx == -1)
616 return -1;
617
618 /* Get number of used channels, depend on width */
dc036d9e 619 n_chans = dfs_get_used_n_chans(iface);
58b73e3d 620
bbbacbf2
JD
621 /* Setup CAC time */
622 iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
623 n_chans);
624
58b73e3d 625 /* Check if any of configured channels require DFS */
dc036d9e 626 res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
58b73e3d
JD
627 wpa_printf(MSG_DEBUG,
628 "DFS %d channels required radar detection",
629 res);
630 if (!res)
631 return 1;
632
633 /* Check if all channels are DFS available */
dc036d9e 634 res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
58b73e3d
JD
635 wpa_printf(MSG_DEBUG,
636 "DFS all channels available, (SKIP CAC): %s",
637 res ? "yes" : "no");
638 if (res)
639 return 1;
640
641 /* Check if any of configured channels is unavailable */
dc036d9e 642 res = dfs_check_chans_unavailable(iface, start_chan_idx,
58b73e3d
JD
643 n_chans);
644 wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
645 res, res ? "yes": "no");
646 if (res) {
5479ff90
MS
647 int sec = 0;
648 u8 cf1 = 0, cf2 = 0;
32595da6 649
b72f949b
MK
650 channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
651 skip_radar);
e76da505
JD
652 if (!channel) {
653 wpa_printf(MSG_ERROR, "could not get valid channel");
654 return -1;
655 }
32595da6 656
dc036d9e
JM
657 iface->freq = channel->freq;
658 iface->conf->channel = channel->chan;
659 iface->conf->secondary_channel = sec;
660 iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
661 iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
e76da505 662 }
58b73e3d
JD
663 } while (res);
664
665 /* Finally start CAC */
dc036d9e
JM
666 hostapd_set_state(iface, HAPD_IFACE_DFS);
667 wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
668 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
bbbacbf2 669 "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
dc036d9e 670 iface->freq,
1f374834
JD
671 iface->conf->channel, iface->conf->secondary_channel,
672 iface->conf->vht_oper_chwidth,
673 iface->conf->vht_oper_centr_freq_seg0_idx,
bbbacbf2
JD
674 iface->conf->vht_oper_centr_freq_seg1_idx,
675 iface->dfs_cac_ms / 1000);
1f374834
JD
676
677 res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
678 iface->freq,
679 iface->conf->channel,
680 iface->conf->ieee80211n,
681 iface->conf->ieee80211ac,
682 iface->conf->secondary_channel,
683 iface->conf->vht_oper_chwidth,
684 iface->conf->vht_oper_centr_freq_seg0_idx,
685 iface->conf->vht_oper_centr_freq_seg1_idx);
686
687 if (res) {
688 wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
58b73e3d 689 return -1;
e76da505
JD
690 }
691
58b73e3d 692 return 0;
e76da505
JD
693}
694
695
dc036d9e 696int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
58b73e3d
JD
697 int ht_enabled, int chan_offset, int chan_width,
698 int cf1, int cf2)
e76da505 699{
dc036d9e 700 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
186c9059
JM
701 "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
702 success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
703
e76da505
JD
704 if (success) {
705 /* Complete iface/ap configuration */
dc036d9e 706 set_dfs_state(iface, freq, ht_enabled, chan_offset,
58b73e3d
JD
707 chan_width, cf1, cf2,
708 HOSTAPD_CHAN_DFS_AVAILABLE);
dc036d9e
JM
709 iface->cac_started = 0;
710 hostapd_setup_interface_complete(iface, 0);
e76da505
JD
711 }
712
713 return 0;
714}
715
716
f7154cee 717static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
e76da505
JD
718{
719 struct hostapd_channel_data *channel;
f7154cee 720 int secondary_channel;
5479ff90
MS
721 u8 vht_oper_centr_freq_seg0_idx = 0;
722 u8 vht_oper_centr_freq_seg1_idx = 0;
f7154cee 723 int skip_radar = 0;
e76da505 724 int err = 1;
f7154cee
JD
725
726 /* Radar detected during active CAC */
727 iface->cac_started = 0;
728 channel = dfs_get_valid_channel(iface, &secondary_channel,
729 &vht_oper_centr_freq_seg0_idx,
730 &vht_oper_centr_freq_seg1_idx,
731 skip_radar);
732
733 if (!channel) {
734 wpa_printf(MSG_ERROR, "No valid channel available");
735 hostapd_setup_interface_complete(iface, err);
736 return err;
737 }
738
739 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
740 channel->chan);
741 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
742 "freq=%d chan=%d sec_chan=%d", channel->freq,
743 channel->chan, secondary_channel);
744
745 iface->freq = channel->freq;
746 iface->conf->channel = channel->chan;
747 iface->conf->secondary_channel = secondary_channel;
748 iface->conf->vht_oper_centr_freq_seg0_idx =
749 vht_oper_centr_freq_seg0_idx;
750 iface->conf->vht_oper_centr_freq_seg1_idx =
751 vht_oper_centr_freq_seg1_idx;
752 err = 0;
753
754 hostapd_setup_interface_complete(iface, err);
755 return err;
756}
757
758
6782b684
MK
759static int hostapd_csa_in_progress(struct hostapd_iface *iface)
760{
761 unsigned int i;
762 for (i = 0; i < iface->num_bss; i++)
763 if (iface->bss[i]->csa_in_progress)
764 return 1;
765 return 0;
766}
767
768
f7154cee
JD
769static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
770{
771 struct hostapd_channel_data *channel;
32595da6
MK
772 int secondary_channel;
773 u8 vht_oper_centr_freq_seg0_idx;
774 u8 vht_oper_centr_freq_seg1_idx;
b72f949b 775 int skip_radar = 1;
f7154cee
JD
776 struct csa_settings csa_settings;
777 struct hostapd_data *hapd = iface->bss[0];
778 int err = 1;
779
25592b23
JD
780 wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
781 __func__, iface->cac_started ? "yes" : "no",
6782b684 782 hostapd_csa_in_progress(iface) ? "yes" : "no");
25592b23
JD
783
784 /* Check if CSA in progress */
6782b684 785 if (hostapd_csa_in_progress(iface))
25592b23 786 return 0;
f7154cee
JD
787
788 /* Check if active CAC */
789 if (iface->cac_started)
790 return hostapd_dfs_start_channel_switch_cac(iface);
e76da505 791
f7154cee 792 /* Perform channel switch/CSA */
dc036d9e 793 channel = dfs_get_valid_channel(iface, &secondary_channel,
32595da6 794 &vht_oper_centr_freq_seg0_idx,
b72f949b
MK
795 &vht_oper_centr_freq_seg1_idx,
796 skip_radar);
813d4bac 797
f7154cee 798 if (!channel) {
e0700512
SW
799 /*
800 * If there is no channel to switch immediately to, check if
801 * there is another channel where we can switch even if it
802 * requires to perform a CAC first.
803 */
804 skip_radar = 0;
805 channel = dfs_get_valid_channel(iface, &secondary_channel,
806 &vht_oper_centr_freq_seg0_idx,
807 &vht_oper_centr_freq_seg1_idx,
808 skip_radar);
809 if (!channel) {
810 /* FIXME: Wait for channel(s) to become available */
811 hostapd_disable_iface(iface);
812 return err;
813 }
814
815 iface->freq = channel->freq;
816 iface->conf->channel = channel->chan;
817 iface->conf->secondary_channel = secondary_channel;
818 iface->conf->vht_oper_centr_freq_seg0_idx =
819 vht_oper_centr_freq_seg0_idx;
820 iface->conf->vht_oper_centr_freq_seg1_idx =
821 vht_oper_centr_freq_seg1_idx;
822
f7154cee 823 hostapd_disable_iface(iface);
e0700512
SW
824 hostapd_enable_iface(iface);
825 return 0;
f7154cee
JD
826 }
827
828 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
829 channel->chan);
830 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
831 "freq=%d chan=%d sec_chan=%d", channel->freq,
832 channel->chan, secondary_channel);
833
834 /* Setup CSA request */
835 os_memset(&csa_settings, 0, sizeof(csa_settings));
836 csa_settings.cs_count = 5;
837 csa_settings.block_tx = 1;
838 err = hostapd_set_freq_params(&csa_settings.freq_params,
839 iface->conf->hw_mode,
840 channel->freq,
841 channel->chan,
842 iface->conf->ieee80211n,
843 iface->conf->ieee80211ac,
844 secondary_channel,
845 iface->conf->vht_oper_chwidth,
846 vht_oper_centr_freq_seg0_idx,
847 vht_oper_centr_freq_seg1_idx,
848 iface->current_mode->vht_capab);
849
850 if (err) {
851 wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
852 hostapd_disable_iface(iface);
853 return err;
854 }
855
856 err = hostapd_switch_channel(hapd, &csa_settings);
857 if (err) {
858 wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
859 err);
dc036d9e
JM
860 iface->freq = channel->freq;
861 iface->conf->channel = channel->chan;
862 iface->conf->secondary_channel = secondary_channel;
863 iface->conf->vht_oper_centr_freq_seg0_idx =
864 vht_oper_centr_freq_seg0_idx;
865 iface->conf->vht_oper_centr_freq_seg1_idx =
866 vht_oper_centr_freq_seg1_idx;
813d4bac 867
dc036d9e 868 hostapd_disable_iface(iface);
f7154cee
JD
869 hostapd_enable_iface(iface);
870 return 0;
2e946249 871 }
e76da505 872
f7154cee
JD
873 /* Channel configuration will be updated once CSA completes and
874 * ch_switch_notify event is received */
875
876 wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
e76da505
JD
877 return 0;
878}
58b73e3d
JD
879
880
dc036d9e 881int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
58b73e3d
JD
882 int ht_enabled, int chan_offset, int chan_width,
883 int cf1, int cf2)
884{
885 int res;
886
dc036d9e 887 if (!iface->conf->ieee80211h)
58b73e3d
JD
888 return 0;
889
dc036d9e 890 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
186c9059
JM
891 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
892 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
893
58b73e3d 894 /* mark radar frequency as invalid */
dc036d9e 895 res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
58b73e3d
JD
896 chan_width, cf1, cf2,
897 HOSTAPD_CHAN_DFS_UNAVAILABLE);
898
899 /* Skip if reported radar event not overlapped our channels */
dc036d9e 900 res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
58b73e3d
JD
901 if (!res)
902 return 0;
903
58b73e3d 904 /* radar detected while operating, switch the channel. */
dc036d9e 905 res = hostapd_dfs_start_channel_switch(iface);
58b73e3d
JD
906
907 return res;
908}
909
910
dc036d9e 911int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
58b73e3d
JD
912 int ht_enabled, int chan_offset, int chan_width,
913 int cf1, int cf2)
914{
dc036d9e 915 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
186c9059
JM
916 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
917 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
58b73e3d 918 /* TODO add correct implementation here */
dc036d9e
JM
919 set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
920 cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
58b73e3d
JD
921 return 0;
922}
3d7ad2f6
C
923
924
925int hostapd_is_dfs_required(struct hostapd_iface *iface)
926{
927 int n_chans, start_chan_idx;
928
8a0f3bf6
MP
929 if (!iface->conf->ieee80211h || !iface->current_mode ||
930 iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
931 return 0;
3d7ad2f6
C
932
933 /* Get start (first) channel for current configuration */
934 start_chan_idx = dfs_get_start_chan_idx(iface);
935 if (start_chan_idx == -1)
936 return -1;
937
938 /* Get number of used channels, depend on width */
939 n_chans = dfs_get_used_n_chans(iface);
940
941 /* Check if any of configured channels require DFS */
942 return dfs_check_chans_radar(iface, start_chan_idx, n_chans);
943}