dfs.c revision 04f534e89ed127da4077485376f24debc50d80d5
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
21static 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
47static 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
68static 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
107static 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 */
140static 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
177static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
178				       struct hostapd_channel_data *chan,
179				       u8 *vht_oper_centr_freq_seg0_idx,
180				       u8 *vht_oper_centr_freq_seg1_idx)
181{
182	if (!iface->conf->ieee80211ac)
183		return;
184
185	if (!chan)
186		return;
187
188	*vht_oper_centr_freq_seg1_idx = 0;
189
190	switch (iface->conf->vht_oper_chwidth) {
191	case VHT_CHANWIDTH_USE_HT:
192		if (iface->conf->secondary_channel == 1)
193			*vht_oper_centr_freq_seg0_idx = chan->chan + 2;
194		else if (iface->conf->secondary_channel == -1)
195			*vht_oper_centr_freq_seg0_idx = chan->chan - 2;
196		else
197			*vht_oper_centr_freq_seg0_idx = chan->chan;
198		break;
199	case VHT_CHANWIDTH_80MHZ:
200		*vht_oper_centr_freq_seg0_idx = chan->chan + 6;
201		break;
202	case VHT_CHANWIDTH_160MHZ:
203		*vht_oper_centr_freq_seg0_idx = chan->chan + 14;
204		break;
205	default:
206		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
207		break;
208	}
209
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);
213}
214
215
216/* Return start channel idx we will use for mode->channels[idx] */
217static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
218{
219	struct hostapd_hw_modes *mode;
220	struct hostapd_channel_data *chan;
221	int channel_no = iface->conf->channel;
222	int res = -1, i;
223
224	/* HT40- */
225	if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
226		channel_no -= 4;
227
228	/* VHT */
229	if (iface->conf->ieee80211ac) {
230		switch (iface->conf->vht_oper_chwidth) {
231		case VHT_CHANWIDTH_USE_HT:
232			break;
233		case VHT_CHANWIDTH_80MHZ:
234			channel_no =
235				iface->conf->vht_oper_centr_freq_seg0_idx - 6;
236			break;
237		case VHT_CHANWIDTH_160MHZ:
238			channel_no =
239				iface->conf->vht_oper_centr_freq_seg0_idx - 14;
240			break;
241		default:
242			wpa_printf(MSG_INFO,
243				   "DFS only VHT20/40/80/160 is supported now");
244			channel_no = -1;
245			break;
246		}
247	}
248
249	/* Get idx */
250	mode = iface->current_mode;
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 */
267static int dfs_check_chans_radar(struct hostapd_iface *iface,
268				 int start_chan_idx, int n_chans)
269{
270	struct hostapd_channel_data *channel;
271	struct hostapd_hw_modes *mode;
272	int i, res = 0;
273
274	mode = iface->current_mode;
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 */
287static int dfs_check_chans_available(struct hostapd_iface *iface,
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
294	mode = iface->current_mode;
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 */
308static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
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
316	mode = iface->current_mode;
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
331static struct hostapd_channel_data *
332dfs_get_valid_channel(struct hostapd_iface *iface,
333		      int *secondary_channel,
334		      u8 *vht_oper_centr_freq_seg0_idx,
335		      u8 *vht_oper_centr_freq_seg1_idx,
336		      int skip_radar)
337{
338	struct hostapd_hw_modes *mode;
339	struct hostapd_channel_data *chan = NULL;
340	int num_available_chandefs;
341	int chan_idx;
342	u32 _rand;
343
344	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
345
346	if (iface->current_mode == NULL)
347		return NULL;
348
349	mode = iface->current_mode;
350	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
351		return NULL;
352
353	/* Get the count first */
354	num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
355	if (num_available_chandefs == 0)
356		return NULL;
357
358	os_get_random((u8 *) &_rand, sizeof(_rand));
359	chan_idx = _rand % num_available_chandefs;
360	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
361
362	/* dfs_find_channel() calculations assume HT40+ */
363	if (iface->conf->secondary_channel)
364		*secondary_channel = 1;
365	else
366		*secondary_channel = 0;
367
368	dfs_adjust_vht_center_freq(iface, chan,
369				   vht_oper_centr_freq_seg0_idx,
370				   vht_oper_centr_freq_seg1_idx);
371
372	return chan;
373}
374
375
376static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
377{
378	struct hostapd_hw_modes *mode;
379	struct hostapd_channel_data *chan = NULL;
380	int i;
381
382	mode = iface->current_mode;
383	if (mode == NULL)
384		return 0;
385
386	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
387	for (i = 0; i < iface->current_mode->num_channels; i++) {
388		chan = &iface->current_mode->channels[i];
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
402static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
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
411	mode = iface->current_mode;
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;
425		if (frequency == 0)
426			frequency = cf1;
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;
436	case CHAN_WIDTH_160:
437		n_chans = 8;
438		frequency = cf1 - 70;
439		break;
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++) {
449		ret += set_dfs_state_freq(iface, frequency, state);
450		frequency = frequency + 20;
451	}
452
453	return ret;
454}
455
456
457static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
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
467	/* Our configuration */
468	mode = iface->current_mode;
469	start_chan_idx = dfs_get_start_chan_idx(iface);
470	n_chans = dfs_get_used_n_chans(iface);
471
472	/* Check we are on DFS channel(s) */
473	if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
474		return 0;
475
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;
481		if (frequency == 0)
482			frequency = cf1;
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;
492	case CHAN_WIDTH_160:
493		radar_n_chans = 8;
494		frequency = cf1 - 70;
495		break;
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];
506		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
507			continue;
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
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 */
528int hostapd_handle_dfs(struct hostapd_iface *iface)
529{
530	struct hostapd_channel_data *channel;
531	int res, n_chans, start_chan_idx;
532	int skip_radar = 0;
533
534	iface->cac_started = 0;
535
536	do {
537		/* Get start (first) channel for current configuration */
538		start_chan_idx = dfs_get_start_chan_idx(iface);
539		if (start_chan_idx == -1)
540			return -1;
541
542		/* Get number of used channels, depend on width */
543		n_chans = dfs_get_used_n_chans(iface);
544
545		/* Check if any of configured channels require DFS */
546		res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
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 */
554		res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
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 */
562		res = dfs_check_chans_unavailable(iface, start_chan_idx,
563						  n_chans);
564		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
565			   res, res ? "yes": "no");
566		if (res) {
567			int sec;
568			u8 cf1, cf2;
569
570			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
571							skip_radar);
572			if (!channel) {
573				wpa_printf(MSG_ERROR, "could not get valid channel");
574				return -1;
575			}
576
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;
582		}
583	} while (res);
584
585	/* Finally start CAC */
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
589		"freq=%d chan=%d sec_chan=%d",
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)) {
601		wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
602		return -1;
603	}
604
605	return 0;
606}
607
608
609int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
610			     int ht_enabled, int chan_offset, int chan_width,
611			     int cf1, int cf2)
612{
613	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
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
617	if (success) {
618		/* Complete iface/ap configuration */
619		set_dfs_state(iface, freq, ht_enabled, chan_offset,
620			      chan_width, cf1, cf2,
621			      HOSTAPD_CHAN_DFS_AVAILABLE);
622		iface->cac_started = 0;
623		hostapd_setup_interface_complete(iface, 0);
624	}
625
626	return 0;
627}
628
629
630static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
631{
632	struct hostapd_channel_data *channel;
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;
637	int err = 1;
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;
675	int secondary_channel;
676	u8 vht_oper_centr_freq_seg0_idx;
677	u8 vht_oper_centr_freq_seg1_idx;
678	int skip_radar = 1;
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);
689
690
691	/* Perform channel switch/CSA */
692	channel = dfs_get_valid_channel(iface, &secondary_channel,
693					&vht_oper_centr_freq_seg0_idx,
694					&vht_oper_centr_freq_seg1_idx,
695					skip_radar);
696
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);
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;
742
743		hostapd_disable_iface(iface);
744		hostapd_enable_iface(iface);
745		return 0;
746	}
747
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");
752	return 0;
753}
754
755
756int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
757			       int ht_enabled, int chan_offset, int chan_width,
758			       int cf1, int cf2)
759{
760	int res;
761
762	if (!iface->conf->ieee80211h)
763		return 0;
764
765	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
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
769	/* mark radar frequency as invalid */
770	res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
771			    chan_width, cf1, cf2,
772			    HOSTAPD_CHAN_DFS_UNAVAILABLE);
773
774	/* Skip if reported radar event not overlapped our channels */
775	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
776	if (!res)
777		return 0;
778
779	/* radar detected while operating, switch the channel. */
780	res = hostapd_dfs_start_channel_switch(iface);
781
782	return res;
783}
784
785
786int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
787			     int ht_enabled, int chan_offset, int chan_width,
788			     int cf1, int cf2)
789{
790	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
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);
793	/* TODO add correct implementation here */
794	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
795		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
796	return 0;
797}
798