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