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