xref: /dflybsd-src/contrib/wpa_supplicant/src/ap/dfs.c (revision 3a84a4273475ed07d0ab1c2dfeffdfedef35d9cd)
13ff40c12SJohn Marino /*
23ff40c12SJohn Marino  * DFS - Dynamic Frequency Selection
33ff40c12SJohn Marino  * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4*a1157835SDaniel Fojt  * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
53ff40c12SJohn Marino  *
63ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
73ff40c12SJohn Marino  * See README for more details.
83ff40c12SJohn Marino  */
93ff40c12SJohn Marino 
103ff40c12SJohn Marino #include "utils/includes.h"
113ff40c12SJohn Marino 
123ff40c12SJohn Marino #include "utils/common.h"
133ff40c12SJohn Marino #include "common/ieee802_11_defs.h"
14*a1157835SDaniel Fojt #include "common/hw_features_common.h"
153ff40c12SJohn Marino #include "common/wpa_ctrl.h"
163ff40c12SJohn Marino #include "hostapd.h"
173ff40c12SJohn Marino #include "ap_drv_ops.h"
183ff40c12SJohn Marino #include "drivers/driver.h"
193ff40c12SJohn Marino #include "dfs.h"
203ff40c12SJohn Marino 
213ff40c12SJohn Marino 
dfs_get_used_n_chans(struct hostapd_iface * iface,int * seg1)22*a1157835SDaniel Fojt static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
233ff40c12SJohn Marino {
243ff40c12SJohn Marino 	int n_chans = 1;
253ff40c12SJohn Marino 
26*a1157835SDaniel Fojt 	*seg1 = 0;
27*a1157835SDaniel Fojt 
283ff40c12SJohn Marino 	if (iface->conf->ieee80211n && iface->conf->secondary_channel)
293ff40c12SJohn Marino 		n_chans = 2;
303ff40c12SJohn Marino 
31*a1157835SDaniel Fojt 	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
32*a1157835SDaniel Fojt 		switch (hostapd_get_oper_chwidth(iface->conf)) {
33*a1157835SDaniel Fojt 		case CHANWIDTH_USE_HT:
343ff40c12SJohn Marino 			break;
35*a1157835SDaniel Fojt 		case CHANWIDTH_80MHZ:
363ff40c12SJohn Marino 			n_chans = 4;
373ff40c12SJohn Marino 			break;
38*a1157835SDaniel Fojt 		case CHANWIDTH_160MHZ:
393ff40c12SJohn Marino 			n_chans = 8;
403ff40c12SJohn Marino 			break;
41*a1157835SDaniel Fojt 		case CHANWIDTH_80P80MHZ:
42*a1157835SDaniel Fojt 			n_chans = 4;
43*a1157835SDaniel Fojt 			*seg1 = 4;
44*a1157835SDaniel Fojt 			break;
453ff40c12SJohn Marino 		default:
463ff40c12SJohn Marino 			break;
473ff40c12SJohn Marino 		}
483ff40c12SJohn Marino 	}
493ff40c12SJohn Marino 
503ff40c12SJohn Marino 	return n_chans;
513ff40c12SJohn Marino }
523ff40c12SJohn Marino 
533ff40c12SJohn Marino 
dfs_channel_available(struct hostapd_channel_data * chan,int skip_radar)543ff40c12SJohn Marino static int dfs_channel_available(struct hostapd_channel_data *chan,
553ff40c12SJohn Marino 				 int skip_radar)
563ff40c12SJohn Marino {
573ff40c12SJohn Marino 	/*
583ff40c12SJohn Marino 	 * When radar detection happens, CSA is performed. However, there's no
593ff40c12SJohn Marino 	 * time for CAC, so radar channels must be skipped when finding a new
60*a1157835SDaniel Fojt 	 * channel for CSA, unless they are available for immediate use.
613ff40c12SJohn Marino 	 */
62*a1157835SDaniel Fojt 	if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
63*a1157835SDaniel Fojt 	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
64*a1157835SDaniel Fojt 	     HOSTAPD_CHAN_DFS_AVAILABLE))
653ff40c12SJohn Marino 		return 0;
663ff40c12SJohn Marino 
673ff40c12SJohn Marino 	if (chan->flag & HOSTAPD_CHAN_DISABLED)
683ff40c12SJohn Marino 		return 0;
693ff40c12SJohn Marino 	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
703ff40c12SJohn Marino 	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
713ff40c12SJohn Marino 	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
723ff40c12SJohn Marino 		return 0;
733ff40c12SJohn Marino 	return 1;
743ff40c12SJohn Marino }
753ff40c12SJohn Marino 
763ff40c12SJohn Marino 
dfs_is_chan_allowed(struct hostapd_channel_data * chan,int n_chans)773ff40c12SJohn Marino static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
783ff40c12SJohn Marino {
793ff40c12SJohn Marino 	/*
803ff40c12SJohn Marino 	 * The tables contain first valid channel number based on channel width.
813ff40c12SJohn Marino 	 * We will also choose this first channel as the control one.
823ff40c12SJohn Marino 	 */
833ff40c12SJohn Marino 	int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
843ff40c12SJohn Marino 			     184, 192 };
853ff40c12SJohn Marino 	/*
863ff40c12SJohn Marino 	 * VHT80, valid channels based on center frequency:
873ff40c12SJohn Marino 	 * 42, 58, 106, 122, 138, 155
883ff40c12SJohn Marino 	 */
893ff40c12SJohn Marino 	int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
90*a1157835SDaniel Fojt 	/*
91*a1157835SDaniel Fojt 	 * VHT160 valid channels based on center frequency:
92*a1157835SDaniel Fojt 	 * 50, 114
93*a1157835SDaniel Fojt 	 */
94*a1157835SDaniel Fojt 	int allowed_160[] = { 36, 100 };
953ff40c12SJohn Marino 	int *allowed = allowed_40;
963ff40c12SJohn Marino 	unsigned int i, allowed_no = 0;
973ff40c12SJohn Marino 
983ff40c12SJohn Marino 	switch (n_chans) {
993ff40c12SJohn Marino 	case 2:
1003ff40c12SJohn Marino 		allowed = allowed_40;
1013ff40c12SJohn Marino 		allowed_no = ARRAY_SIZE(allowed_40);
1023ff40c12SJohn Marino 		break;
1033ff40c12SJohn Marino 	case 4:
1043ff40c12SJohn Marino 		allowed = allowed_80;
1053ff40c12SJohn Marino 		allowed_no = ARRAY_SIZE(allowed_80);
1063ff40c12SJohn Marino 		break;
107*a1157835SDaniel Fojt 	case 8:
108*a1157835SDaniel Fojt 		allowed = allowed_160;
109*a1157835SDaniel Fojt 		allowed_no = ARRAY_SIZE(allowed_160);
110*a1157835SDaniel Fojt 		break;
1113ff40c12SJohn Marino 	default:
1123ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
1133ff40c12SJohn Marino 		break;
1143ff40c12SJohn Marino 	}
1153ff40c12SJohn Marino 
1163ff40c12SJohn Marino 	for (i = 0; i < allowed_no; i++) {
1173ff40c12SJohn Marino 		if (chan->chan == allowed[i])
1183ff40c12SJohn Marino 			return 1;
1193ff40c12SJohn Marino 	}
1203ff40c12SJohn Marino 
1213ff40c12SJohn Marino 	return 0;
1223ff40c12SJohn Marino }
1233ff40c12SJohn Marino 
1243ff40c12SJohn Marino 
125*a1157835SDaniel Fojt static struct hostapd_channel_data *
dfs_get_chan_data(struct hostapd_hw_modes * mode,int freq,int first_chan_idx)126*a1157835SDaniel Fojt dfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
127*a1157835SDaniel Fojt {
128*a1157835SDaniel Fojt 	int i;
129*a1157835SDaniel Fojt 
130*a1157835SDaniel Fojt 	for (i = first_chan_idx; i < mode->num_channels; i++) {
131*a1157835SDaniel Fojt 		if (mode->channels[i].freq == freq)
132*a1157835SDaniel Fojt 			return &mode->channels[i];
133*a1157835SDaniel Fojt 	}
134*a1157835SDaniel Fojt 
135*a1157835SDaniel Fojt 	return NULL;
136*a1157835SDaniel Fojt }
137*a1157835SDaniel Fojt 
138*a1157835SDaniel Fojt 
dfs_chan_range_available(struct hostapd_hw_modes * mode,int first_chan_idx,int num_chans,int skip_radar)1393ff40c12SJohn Marino static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
1403ff40c12SJohn Marino 				    int first_chan_idx, int num_chans,
1413ff40c12SJohn Marino 				    int skip_radar)
1423ff40c12SJohn Marino {
1433ff40c12SJohn Marino 	struct hostapd_channel_data *first_chan, *chan;
1443ff40c12SJohn Marino 	int i;
145*a1157835SDaniel Fojt 	u32 bw = num_chan_to_bw(num_chans);
1463ff40c12SJohn Marino 
147*a1157835SDaniel Fojt 	if (first_chan_idx + num_chans > mode->num_channels)
1483ff40c12SJohn Marino 		return 0;
1493ff40c12SJohn Marino 
1503ff40c12SJohn Marino 	first_chan = &mode->channels[first_chan_idx];
1513ff40c12SJohn Marino 
152*a1157835SDaniel Fojt 	/* hostapd DFS implementation assumes the first channel as primary.
153*a1157835SDaniel Fojt 	 * If it's not allowed to use the first channel as primary, decline the
154*a1157835SDaniel Fojt 	 * whole channel range. */
155*a1157835SDaniel Fojt 	if (!chan_pri_allowed(first_chan))
156*a1157835SDaniel Fojt 		return 0;
1573ff40c12SJohn Marino 
158*a1157835SDaniel Fojt 	for (i = 0; i < num_chans; i++) {
159*a1157835SDaniel Fojt 		chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
160*a1157835SDaniel Fojt 					 first_chan_idx);
161*a1157835SDaniel Fojt 		if (!chan)
162*a1157835SDaniel Fojt 			return 0;
163*a1157835SDaniel Fojt 
164*a1157835SDaniel Fojt 		/* HT 40 MHz secondary channel availability checked only for
165*a1157835SDaniel Fojt 		 * primary channel */
166*a1157835SDaniel Fojt 		if (!chan_bw_allowed(chan, bw, 1, !i))
1673ff40c12SJohn Marino 			return 0;
1683ff40c12SJohn Marino 
1693ff40c12SJohn Marino 		if (!dfs_channel_available(chan, skip_radar))
1703ff40c12SJohn Marino 			return 0;
1713ff40c12SJohn Marino 	}
1723ff40c12SJohn Marino 
1733ff40c12SJohn Marino 	return 1;
1743ff40c12SJohn Marino }
1753ff40c12SJohn Marino 
1763ff40c12SJohn Marino 
is_in_chanlist(struct hostapd_iface * iface,struct hostapd_channel_data * chan)177*a1157835SDaniel Fojt static int is_in_chanlist(struct hostapd_iface *iface,
178*a1157835SDaniel Fojt 			  struct hostapd_channel_data *chan)
179*a1157835SDaniel Fojt {
180*a1157835SDaniel Fojt 	if (!iface->conf->acs_ch_list.num)
181*a1157835SDaniel Fojt 		return 1;
182*a1157835SDaniel Fojt 
183*a1157835SDaniel Fojt 	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
184*a1157835SDaniel Fojt }
185*a1157835SDaniel Fojt 
186*a1157835SDaniel Fojt 
1873ff40c12SJohn Marino /*
1883ff40c12SJohn Marino  * The function assumes HT40+ operation.
1893ff40c12SJohn Marino  * Make sure to adjust the following variables after calling this:
1903ff40c12SJohn Marino  *  - hapd->secondary_channel
191*a1157835SDaniel Fojt  *  - hapd->vht/he_oper_centr_freq_seg0_idx
192*a1157835SDaniel Fojt  *  - hapd->vht/he_oper_centr_freq_seg1_idx
1933ff40c12SJohn Marino  */
dfs_find_channel(struct hostapd_iface * iface,struct hostapd_channel_data ** ret_chan,int idx,int skip_radar)1943ff40c12SJohn Marino static int dfs_find_channel(struct hostapd_iface *iface,
1953ff40c12SJohn Marino 			    struct hostapd_channel_data **ret_chan,
1963ff40c12SJohn Marino 			    int idx, int skip_radar)
1973ff40c12SJohn Marino {
1983ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
1993ff40c12SJohn Marino 	struct hostapd_channel_data *chan;
200*a1157835SDaniel Fojt 	int i, channel_idx = 0, n_chans, n_chans1;
2013ff40c12SJohn Marino 
2023ff40c12SJohn Marino 	mode = iface->current_mode;
203*a1157835SDaniel Fojt 	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
2043ff40c12SJohn Marino 
2053ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
2063ff40c12SJohn Marino 	for (i = 0; i < mode->num_channels; i++) {
2073ff40c12SJohn Marino 		chan = &mode->channels[i];
2083ff40c12SJohn Marino 
2093ff40c12SJohn Marino 		/* Skip HT40/VHT incompatible channels */
2103ff40c12SJohn Marino 		if (iface->conf->ieee80211n &&
2113ff40c12SJohn Marino 		    iface->conf->secondary_channel &&
212*a1157835SDaniel Fojt 		    (!dfs_is_chan_allowed(chan, n_chans) ||
213*a1157835SDaniel Fojt 		     !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)))
2143ff40c12SJohn Marino 			continue;
2153ff40c12SJohn Marino 
2163ff40c12SJohn Marino 		/* Skip incompatible chandefs */
2173ff40c12SJohn Marino 		if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
2183ff40c12SJohn Marino 			continue;
2193ff40c12SJohn Marino 
220*a1157835SDaniel Fojt 		if (!is_in_chanlist(iface, chan))
221*a1157835SDaniel Fojt 			continue;
222*a1157835SDaniel Fojt 
2233ff40c12SJohn Marino 		if (ret_chan && idx == channel_idx) {
2243ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
2253ff40c12SJohn Marino 			*ret_chan = chan;
2263ff40c12SJohn Marino 			return idx;
2273ff40c12SJohn Marino 		}
2283ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
2293ff40c12SJohn Marino 		channel_idx++;
2303ff40c12SJohn Marino 	}
2313ff40c12SJohn Marino 	return channel_idx;
2323ff40c12SJohn Marino }
2333ff40c12SJohn Marino 
2343ff40c12SJohn Marino 
dfs_adjust_center_freq(struct hostapd_iface * iface,struct hostapd_channel_data * chan,int secondary_channel,u8 * oper_centr_freq_seg0_idx,u8 * oper_centr_freq_seg1_idx)235*a1157835SDaniel Fojt static void dfs_adjust_center_freq(struct hostapd_iface *iface,
2363ff40c12SJohn Marino 				   struct hostapd_channel_data *chan,
2373ff40c12SJohn Marino 				   int secondary_channel,
238*a1157835SDaniel Fojt 				   u8 *oper_centr_freq_seg0_idx,
239*a1157835SDaniel Fojt 				   u8 *oper_centr_freq_seg1_idx)
2403ff40c12SJohn Marino {
241*a1157835SDaniel Fojt 	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
2423ff40c12SJohn Marino 		return;
2433ff40c12SJohn Marino 
2443ff40c12SJohn Marino 	if (!chan)
2453ff40c12SJohn Marino 		return;
2463ff40c12SJohn Marino 
247*a1157835SDaniel Fojt 	*oper_centr_freq_seg1_idx = 0;
2483ff40c12SJohn Marino 
249*a1157835SDaniel Fojt 	switch (hostapd_get_oper_chwidth(iface->conf)) {
250*a1157835SDaniel Fojt 	case CHANWIDTH_USE_HT:
2513ff40c12SJohn Marino 		if (secondary_channel == 1)
252*a1157835SDaniel Fojt 			*oper_centr_freq_seg0_idx = chan->chan + 2;
2533ff40c12SJohn Marino 		else if (secondary_channel == -1)
254*a1157835SDaniel Fojt 			*oper_centr_freq_seg0_idx = chan->chan - 2;
2553ff40c12SJohn Marino 		else
256*a1157835SDaniel Fojt 			*oper_centr_freq_seg0_idx = chan->chan;
2573ff40c12SJohn Marino 		break;
258*a1157835SDaniel Fojt 	case CHANWIDTH_80MHZ:
259*a1157835SDaniel Fojt 		*oper_centr_freq_seg0_idx = chan->chan + 6;
2603ff40c12SJohn Marino 		break;
261*a1157835SDaniel Fojt 	case CHANWIDTH_160MHZ:
262*a1157835SDaniel Fojt 		*oper_centr_freq_seg0_idx = chan->chan + 14;
2633ff40c12SJohn Marino 		break;
2643ff40c12SJohn Marino 	default:
2653ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
266*a1157835SDaniel Fojt 		*oper_centr_freq_seg0_idx = 0;
2673ff40c12SJohn Marino 		break;
2683ff40c12SJohn Marino 	}
2693ff40c12SJohn Marino 
2703ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
271*a1157835SDaniel Fojt 		   *oper_centr_freq_seg0_idx,
272*a1157835SDaniel Fojt 		   *oper_centr_freq_seg1_idx);
2733ff40c12SJohn Marino }
2743ff40c12SJohn Marino 
2753ff40c12SJohn Marino 
2763ff40c12SJohn Marino /* Return start channel idx we will use for mode->channels[idx] */
dfs_get_start_chan_idx(struct hostapd_iface * iface,int * seg1_start)277*a1157835SDaniel Fojt static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start)
2783ff40c12SJohn Marino {
2793ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
2803ff40c12SJohn Marino 	struct hostapd_channel_data *chan;
2813ff40c12SJohn Marino 	int channel_no = iface->conf->channel;
2823ff40c12SJohn Marino 	int res = -1, i;
283*a1157835SDaniel Fojt 	int chan_seg1 = -1;
284*a1157835SDaniel Fojt 
285*a1157835SDaniel Fojt 	*seg1_start = -1;
2863ff40c12SJohn Marino 
2873ff40c12SJohn Marino 	/* HT40- */
2883ff40c12SJohn Marino 	if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
2893ff40c12SJohn Marino 		channel_no -= 4;
2903ff40c12SJohn Marino 
291*a1157835SDaniel Fojt 	/* VHT/HE */
292*a1157835SDaniel Fojt 	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
293*a1157835SDaniel Fojt 		switch (hostapd_get_oper_chwidth(iface->conf)) {
294*a1157835SDaniel Fojt 		case CHANWIDTH_USE_HT:
2953ff40c12SJohn Marino 			break;
296*a1157835SDaniel Fojt 		case CHANWIDTH_80MHZ:
297*a1157835SDaniel Fojt 			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
298*a1157835SDaniel Fojt 				iface->conf) - 6;
2993ff40c12SJohn Marino 			break;
300*a1157835SDaniel Fojt 		case CHANWIDTH_160MHZ:
301*a1157835SDaniel Fojt 			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
302*a1157835SDaniel Fojt 				iface->conf) - 14;
303*a1157835SDaniel Fojt 			break;
304*a1157835SDaniel Fojt 		case CHANWIDTH_80P80MHZ:
305*a1157835SDaniel Fojt 			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
306*a1157835SDaniel Fojt 				iface->conf) - 6;
307*a1157835SDaniel Fojt 			chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
308*a1157835SDaniel Fojt 				iface->conf) - 6;
3093ff40c12SJohn Marino 			break;
3103ff40c12SJohn Marino 		default:
3113ff40c12SJohn Marino 			wpa_printf(MSG_INFO,
312*a1157835SDaniel Fojt 				   "DFS only VHT20/40/80/160/80+80 is supported now");
3133ff40c12SJohn Marino 			channel_no = -1;
3143ff40c12SJohn Marino 			break;
3153ff40c12SJohn Marino 		}
3163ff40c12SJohn Marino 	}
3173ff40c12SJohn Marino 
3183ff40c12SJohn Marino 	/* Get idx */
3193ff40c12SJohn Marino 	mode = iface->current_mode;
3203ff40c12SJohn Marino 	for (i = 0; i < mode->num_channels; i++) {
3213ff40c12SJohn Marino 		chan = &mode->channels[i];
3223ff40c12SJohn Marino 		if (chan->chan == channel_no) {
3233ff40c12SJohn Marino 			res = i;
3243ff40c12SJohn Marino 			break;
3253ff40c12SJohn Marino 		}
3263ff40c12SJohn Marino 	}
3273ff40c12SJohn Marino 
328*a1157835SDaniel Fojt 	if (res != -1 && chan_seg1 > -1) {
329*a1157835SDaniel Fojt 		int found = 0;
330*a1157835SDaniel Fojt 
331*a1157835SDaniel Fojt 		/* Get idx for seg1 */
332*a1157835SDaniel Fojt 		mode = iface->current_mode;
333*a1157835SDaniel Fojt 		for (i = 0; i < mode->num_channels; i++) {
334*a1157835SDaniel Fojt 			chan = &mode->channels[i];
335*a1157835SDaniel Fojt 			if (chan->chan == chan_seg1) {
336*a1157835SDaniel Fojt 				*seg1_start = i;
337*a1157835SDaniel Fojt 				found = 1;
338*a1157835SDaniel Fojt 				break;
339*a1157835SDaniel Fojt 			}
340*a1157835SDaniel Fojt 		}
341*a1157835SDaniel Fojt 		if (!found)
342*a1157835SDaniel Fojt 			res = -1;
343*a1157835SDaniel Fojt 	}
344*a1157835SDaniel Fojt 
345*a1157835SDaniel Fojt 	if (res == -1) {
346*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
347*a1157835SDaniel Fojt 			   "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
348*a1157835SDaniel Fojt 			   mode->num_channels, channel_no, iface->conf->channel,
349*a1157835SDaniel Fojt 			   iface->conf->ieee80211n,
350*a1157835SDaniel Fojt 			   iface->conf->secondary_channel,
351*a1157835SDaniel Fojt 			   hostapd_get_oper_chwidth(iface->conf));
352*a1157835SDaniel Fojt 
353*a1157835SDaniel Fojt 		for (i = 0; i < mode->num_channels; i++) {
354*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG, "Available channel: %d",
355*a1157835SDaniel Fojt 				   mode->channels[i].chan);
356*a1157835SDaniel Fojt 		}
357*a1157835SDaniel Fojt 	}
3583ff40c12SJohn Marino 
3593ff40c12SJohn Marino 	return res;
3603ff40c12SJohn Marino }
3613ff40c12SJohn Marino 
3623ff40c12SJohn Marino 
3633ff40c12SJohn Marino /* At least one channel have radar flag */
dfs_check_chans_radar(struct hostapd_iface * iface,int start_chan_idx,int n_chans)3643ff40c12SJohn Marino static int dfs_check_chans_radar(struct hostapd_iface *iface,
3653ff40c12SJohn Marino 				 int start_chan_idx, int n_chans)
3663ff40c12SJohn Marino {
3673ff40c12SJohn Marino 	struct hostapd_channel_data *channel;
3683ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
3693ff40c12SJohn Marino 	int i, res = 0;
3703ff40c12SJohn Marino 
3713ff40c12SJohn Marino 	mode = iface->current_mode;
3723ff40c12SJohn Marino 
3733ff40c12SJohn Marino 	for (i = 0; i < n_chans; i++) {
3743ff40c12SJohn Marino 		channel = &mode->channels[start_chan_idx + i];
3753ff40c12SJohn Marino 		if (channel->flag & HOSTAPD_CHAN_RADAR)
3763ff40c12SJohn Marino 			res++;
3773ff40c12SJohn Marino 	}
3783ff40c12SJohn Marino 
3793ff40c12SJohn Marino 	return res;
3803ff40c12SJohn Marino }
3813ff40c12SJohn Marino 
3823ff40c12SJohn Marino 
3833ff40c12SJohn Marino /* All channels available */
dfs_check_chans_available(struct hostapd_iface * iface,int start_chan_idx,int n_chans)3843ff40c12SJohn Marino static int dfs_check_chans_available(struct hostapd_iface *iface,
3853ff40c12SJohn Marino 				     int start_chan_idx, int n_chans)
3863ff40c12SJohn Marino {
3873ff40c12SJohn Marino 	struct hostapd_channel_data *channel;
3883ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
3893ff40c12SJohn Marino 	int i;
3903ff40c12SJohn Marino 
3913ff40c12SJohn Marino 	mode = iface->current_mode;
3923ff40c12SJohn Marino 
3933ff40c12SJohn Marino 	for (i = 0; i < n_chans; i++) {
3943ff40c12SJohn Marino 		channel = &mode->channels[start_chan_idx + i];
395*a1157835SDaniel Fojt 
396*a1157835SDaniel Fojt 		if (channel->flag & HOSTAPD_CHAN_DISABLED)
397*a1157835SDaniel Fojt 			break;
398*a1157835SDaniel Fojt 
399*a1157835SDaniel Fojt 		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
400*a1157835SDaniel Fojt 			continue;
401*a1157835SDaniel Fojt 
4023ff40c12SJohn Marino 		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
4033ff40c12SJohn Marino 		    HOSTAPD_CHAN_DFS_AVAILABLE)
4043ff40c12SJohn Marino 			break;
4053ff40c12SJohn Marino 	}
4063ff40c12SJohn Marino 
4073ff40c12SJohn Marino 	return i == n_chans;
4083ff40c12SJohn Marino }
4093ff40c12SJohn Marino 
4103ff40c12SJohn Marino 
4113ff40c12SJohn Marino /* At least one channel unavailable */
dfs_check_chans_unavailable(struct hostapd_iface * iface,int start_chan_idx,int n_chans)4123ff40c12SJohn Marino static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
4133ff40c12SJohn Marino 				       int start_chan_idx,
4143ff40c12SJohn Marino 				       int n_chans)
4153ff40c12SJohn Marino {
4163ff40c12SJohn Marino 	struct hostapd_channel_data *channel;
4173ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
4183ff40c12SJohn Marino 	int i, res = 0;
4193ff40c12SJohn Marino 
4203ff40c12SJohn Marino 	mode = iface->current_mode;
4213ff40c12SJohn Marino 
4223ff40c12SJohn Marino 	for (i = 0; i < n_chans; i++) {
4233ff40c12SJohn Marino 		channel = &mode->channels[start_chan_idx + i];
4243ff40c12SJohn Marino 		if (channel->flag & HOSTAPD_CHAN_DISABLED)
4253ff40c12SJohn Marino 			res++;
4263ff40c12SJohn Marino 		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
4273ff40c12SJohn Marino 		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
4283ff40c12SJohn Marino 			res++;
4293ff40c12SJohn Marino 	}
4303ff40c12SJohn Marino 
4313ff40c12SJohn Marino 	return res;
4323ff40c12SJohn Marino }
4333ff40c12SJohn Marino 
4343ff40c12SJohn Marino 
4353ff40c12SJohn Marino static struct hostapd_channel_data *
dfs_get_valid_channel(struct hostapd_iface * iface,int * secondary_channel,u8 * oper_centr_freq_seg0_idx,u8 * oper_centr_freq_seg1_idx,int skip_radar)4363ff40c12SJohn Marino dfs_get_valid_channel(struct hostapd_iface *iface,
4373ff40c12SJohn Marino 		      int *secondary_channel,
438*a1157835SDaniel Fojt 		      u8 *oper_centr_freq_seg0_idx,
439*a1157835SDaniel Fojt 		      u8 *oper_centr_freq_seg1_idx,
4403ff40c12SJohn Marino 		      int skip_radar)
4413ff40c12SJohn Marino {
4423ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
4433ff40c12SJohn Marino 	struct hostapd_channel_data *chan = NULL;
4443ff40c12SJohn Marino 	int num_available_chandefs;
4453ff40c12SJohn Marino 	int chan_idx;
4463ff40c12SJohn Marino 	u32 _rand;
4473ff40c12SJohn Marino 
4483ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
449*a1157835SDaniel Fojt 	*secondary_channel = 0;
450*a1157835SDaniel Fojt 	*oper_centr_freq_seg0_idx = 0;
451*a1157835SDaniel Fojt 	*oper_centr_freq_seg1_idx = 0;
4523ff40c12SJohn Marino 
4533ff40c12SJohn Marino 	if (iface->current_mode == NULL)
4543ff40c12SJohn Marino 		return NULL;
4553ff40c12SJohn Marino 
4563ff40c12SJohn Marino 	mode = iface->current_mode;
4573ff40c12SJohn Marino 	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
4583ff40c12SJohn Marino 		return NULL;
4593ff40c12SJohn Marino 
4603ff40c12SJohn Marino 	/* Get the count first */
4613ff40c12SJohn Marino 	num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
4623ff40c12SJohn Marino 	if (num_available_chandefs == 0)
4633ff40c12SJohn Marino 		return NULL;
4643ff40c12SJohn Marino 
465*a1157835SDaniel Fojt 	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
466*a1157835SDaniel Fojt 		return NULL;
4673ff40c12SJohn Marino 	chan_idx = _rand % num_available_chandefs;
4683ff40c12SJohn Marino 	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
4693ff40c12SJohn Marino 
4703ff40c12SJohn Marino 	/* dfs_find_channel() calculations assume HT40+ */
4713ff40c12SJohn Marino 	if (iface->conf->secondary_channel)
4723ff40c12SJohn Marino 		*secondary_channel = 1;
4733ff40c12SJohn Marino 	else
4743ff40c12SJohn Marino 		*secondary_channel = 0;
4753ff40c12SJohn Marino 
476*a1157835SDaniel Fojt 	dfs_adjust_center_freq(iface, chan,
4773ff40c12SJohn Marino 			       *secondary_channel,
478*a1157835SDaniel Fojt 			       oper_centr_freq_seg0_idx,
479*a1157835SDaniel Fojt 			       oper_centr_freq_seg1_idx);
4803ff40c12SJohn Marino 
4813ff40c12SJohn Marino 	return chan;
4823ff40c12SJohn Marino }
4833ff40c12SJohn Marino 
4843ff40c12SJohn Marino 
set_dfs_state_freq(struct hostapd_iface * iface,int freq,u32 state)4853ff40c12SJohn Marino static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
4863ff40c12SJohn Marino {
4873ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
4883ff40c12SJohn Marino 	struct hostapd_channel_data *chan = NULL;
4893ff40c12SJohn Marino 	int i;
4903ff40c12SJohn Marino 
4913ff40c12SJohn Marino 	mode = iface->current_mode;
4923ff40c12SJohn Marino 	if (mode == NULL)
4933ff40c12SJohn Marino 		return 0;
4943ff40c12SJohn Marino 
4953ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
4963ff40c12SJohn Marino 	for (i = 0; i < iface->current_mode->num_channels; i++) {
4973ff40c12SJohn Marino 		chan = &iface->current_mode->channels[i];
4983ff40c12SJohn Marino 		if (chan->freq == freq) {
4993ff40c12SJohn Marino 			if (chan->flag & HOSTAPD_CHAN_RADAR) {
5003ff40c12SJohn Marino 				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
5013ff40c12SJohn Marino 				chan->flag |= state;
5023ff40c12SJohn Marino 				return 1; /* Channel found */
5033ff40c12SJohn Marino 			}
5043ff40c12SJohn Marino 		}
5053ff40c12SJohn Marino 	}
5063ff40c12SJohn Marino 	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
5073ff40c12SJohn Marino 	return 0;
5083ff40c12SJohn Marino }
5093ff40c12SJohn Marino 
5103ff40c12SJohn Marino 
set_dfs_state(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2,u32 state)5113ff40c12SJohn Marino static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
5123ff40c12SJohn Marino 			 int chan_offset, int chan_width, int cf1,
5133ff40c12SJohn Marino 			 int cf2, u32 state)
5143ff40c12SJohn Marino {
5153ff40c12SJohn Marino 	int n_chans = 1, i;
5163ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
5173ff40c12SJohn Marino 	int frequency = freq;
5183ff40c12SJohn Marino 	int ret = 0;
5193ff40c12SJohn Marino 
5203ff40c12SJohn Marino 	mode = iface->current_mode;
5213ff40c12SJohn Marino 	if (mode == NULL)
5223ff40c12SJohn Marino 		return 0;
5233ff40c12SJohn Marino 
5243ff40c12SJohn Marino 	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
5253ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
5263ff40c12SJohn Marino 		return 0;
5273ff40c12SJohn Marino 	}
5283ff40c12SJohn Marino 
5293ff40c12SJohn Marino 	/* Seems cf1 and chan_width is enough here */
5303ff40c12SJohn Marino 	switch (chan_width) {
5313ff40c12SJohn Marino 	case CHAN_WIDTH_20_NOHT:
5323ff40c12SJohn Marino 	case CHAN_WIDTH_20:
5333ff40c12SJohn Marino 		n_chans = 1;
5343ff40c12SJohn Marino 		if (frequency == 0)
5353ff40c12SJohn Marino 			frequency = cf1;
5363ff40c12SJohn Marino 		break;
5373ff40c12SJohn Marino 	case CHAN_WIDTH_40:
5383ff40c12SJohn Marino 		n_chans = 2;
5393ff40c12SJohn Marino 		frequency = cf1 - 10;
5403ff40c12SJohn Marino 		break;
5413ff40c12SJohn Marino 	case CHAN_WIDTH_80:
5423ff40c12SJohn Marino 		n_chans = 4;
5433ff40c12SJohn Marino 		frequency = cf1 - 30;
5443ff40c12SJohn Marino 		break;
5453ff40c12SJohn Marino 	case CHAN_WIDTH_160:
5463ff40c12SJohn Marino 		n_chans = 8;
5473ff40c12SJohn Marino 		frequency = cf1 - 70;
5483ff40c12SJohn Marino 		break;
5493ff40c12SJohn Marino 	default:
5503ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
5513ff40c12SJohn Marino 			   chan_width);
5523ff40c12SJohn Marino 		break;
5533ff40c12SJohn Marino 	}
5543ff40c12SJohn Marino 
5553ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
5563ff40c12SJohn Marino 		   n_chans);
5573ff40c12SJohn Marino 	for (i = 0; i < n_chans; i++) {
5583ff40c12SJohn Marino 		ret += set_dfs_state_freq(iface, frequency, state);
5593ff40c12SJohn Marino 		frequency = frequency + 20;
5603ff40c12SJohn Marino 	}
5613ff40c12SJohn Marino 
5623ff40c12SJohn Marino 	return ret;
5633ff40c12SJohn Marino }
5643ff40c12SJohn Marino 
5653ff40c12SJohn Marino 
dfs_are_channels_overlapped(struct hostapd_iface * iface,int freq,int chan_width,int cf1,int cf2)5663ff40c12SJohn Marino static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
5673ff40c12SJohn Marino 				       int chan_width, int cf1, int cf2)
5683ff40c12SJohn Marino {
569*a1157835SDaniel Fojt 	int start_chan_idx, start_chan_idx1;
5703ff40c12SJohn Marino 	struct hostapd_hw_modes *mode;
5713ff40c12SJohn Marino 	struct hostapd_channel_data *chan;
572*a1157835SDaniel Fojt 	int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1;
5733ff40c12SJohn Marino 	u8 radar_chan;
5743ff40c12SJohn Marino 	int res = 0;
5753ff40c12SJohn Marino 
5763ff40c12SJohn Marino 	/* Our configuration */
5773ff40c12SJohn Marino 	mode = iface->current_mode;
578*a1157835SDaniel Fojt 	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
579*a1157835SDaniel Fojt 	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
5803ff40c12SJohn Marino 
5813ff40c12SJohn Marino 	/* Check we are on DFS channel(s) */
5823ff40c12SJohn Marino 	if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
5833ff40c12SJohn Marino 		return 0;
5843ff40c12SJohn Marino 
5853ff40c12SJohn Marino 	/* Reported via radar event */
5863ff40c12SJohn Marino 	switch (chan_width) {
5873ff40c12SJohn Marino 	case CHAN_WIDTH_20_NOHT:
5883ff40c12SJohn Marino 	case CHAN_WIDTH_20:
5893ff40c12SJohn Marino 		radar_n_chans = 1;
5903ff40c12SJohn Marino 		if (frequency == 0)
5913ff40c12SJohn Marino 			frequency = cf1;
5923ff40c12SJohn Marino 		break;
5933ff40c12SJohn Marino 	case CHAN_WIDTH_40:
5943ff40c12SJohn Marino 		radar_n_chans = 2;
5953ff40c12SJohn Marino 		frequency = cf1 - 10;
5963ff40c12SJohn Marino 		break;
5973ff40c12SJohn Marino 	case CHAN_WIDTH_80:
5983ff40c12SJohn Marino 		radar_n_chans = 4;
5993ff40c12SJohn Marino 		frequency = cf1 - 30;
6003ff40c12SJohn Marino 		break;
6013ff40c12SJohn Marino 	case CHAN_WIDTH_160:
6023ff40c12SJohn Marino 		radar_n_chans = 8;
6033ff40c12SJohn Marino 		frequency = cf1 - 70;
6043ff40c12SJohn Marino 		break;
6053ff40c12SJohn Marino 	default:
6063ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
6073ff40c12SJohn Marino 			   chan_width);
6083ff40c12SJohn Marino 		break;
6093ff40c12SJohn Marino 	}
6103ff40c12SJohn Marino 
6113ff40c12SJohn Marino 	ieee80211_freq_to_chan(frequency, &radar_chan);
6123ff40c12SJohn Marino 
6133ff40c12SJohn Marino 	for (i = 0; i < n_chans; i++) {
6143ff40c12SJohn Marino 		chan = &mode->channels[start_chan_idx + i];
6153ff40c12SJohn Marino 		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
6163ff40c12SJohn Marino 			continue;
6173ff40c12SJohn Marino 		for (j = 0; j < radar_n_chans; j++) {
6183ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
6193ff40c12SJohn Marino 				   chan->chan, radar_chan + j * 4);
6203ff40c12SJohn Marino 			if (chan->chan == radar_chan + j * 4)
6213ff40c12SJohn Marino 				res++;
6223ff40c12SJohn Marino 		}
6233ff40c12SJohn Marino 	}
6243ff40c12SJohn Marino 
6253ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
6263ff40c12SJohn Marino 
6273ff40c12SJohn Marino 	return res;
6283ff40c12SJohn Marino }
6293ff40c12SJohn Marino 
6303ff40c12SJohn Marino 
dfs_get_cac_time(struct hostapd_iface * iface,int start_chan_idx,int n_chans)631*a1157835SDaniel Fojt static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
632*a1157835SDaniel Fojt 				     int start_chan_idx, int n_chans)
633*a1157835SDaniel Fojt {
634*a1157835SDaniel Fojt 	struct hostapd_channel_data *channel;
635*a1157835SDaniel Fojt 	struct hostapd_hw_modes *mode;
636*a1157835SDaniel Fojt 	int i;
637*a1157835SDaniel Fojt 	unsigned int cac_time_ms = 0;
638*a1157835SDaniel Fojt 
639*a1157835SDaniel Fojt 	mode = iface->current_mode;
640*a1157835SDaniel Fojt 
641*a1157835SDaniel Fojt 	for (i = 0; i < n_chans; i++) {
642*a1157835SDaniel Fojt 		channel = &mode->channels[start_chan_idx + i];
643*a1157835SDaniel Fojt 		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
644*a1157835SDaniel Fojt 			continue;
645*a1157835SDaniel Fojt 		if (channel->dfs_cac_ms > cac_time_ms)
646*a1157835SDaniel Fojt 			cac_time_ms = channel->dfs_cac_ms;
647*a1157835SDaniel Fojt 	}
648*a1157835SDaniel Fojt 
649*a1157835SDaniel Fojt 	return cac_time_ms;
650*a1157835SDaniel Fojt }
651*a1157835SDaniel Fojt 
652*a1157835SDaniel Fojt 
6533ff40c12SJohn Marino /*
6543ff40c12SJohn Marino  * Main DFS handler
6553ff40c12SJohn Marino  * 1 - continue channel/ap setup
6563ff40c12SJohn Marino  * 0 - channel/ap setup will be continued after CAC
6573ff40c12SJohn Marino  * -1 - hit critical error
6583ff40c12SJohn Marino  */
hostapd_handle_dfs(struct hostapd_iface * iface)6593ff40c12SJohn Marino int hostapd_handle_dfs(struct hostapd_iface *iface)
6603ff40c12SJohn Marino {
6613ff40c12SJohn Marino 	struct hostapd_channel_data *channel;
662*a1157835SDaniel Fojt 	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
6633ff40c12SJohn Marino 	int skip_radar = 0;
6643ff40c12SJohn Marino 
665*a1157835SDaniel Fojt 	if (!iface->current_mode) {
666*a1157835SDaniel Fojt 		/*
667*a1157835SDaniel Fojt 		 * This can happen with drivers that do not provide mode
668*a1157835SDaniel Fojt 		 * information and as such, cannot really use hostapd for DFS.
669*a1157835SDaniel Fojt 		 */
670*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
671*a1157835SDaniel Fojt 			   "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
672*a1157835SDaniel Fojt 		return 1;
673*a1157835SDaniel Fojt 	}
674*a1157835SDaniel Fojt 
6753ff40c12SJohn Marino 	iface->cac_started = 0;
6763ff40c12SJohn Marino 
6773ff40c12SJohn Marino 	do {
6783ff40c12SJohn Marino 		/* Get start (first) channel for current configuration */
679*a1157835SDaniel Fojt 		start_chan_idx = dfs_get_start_chan_idx(iface,
680*a1157835SDaniel Fojt 							&start_chan_idx1);
6813ff40c12SJohn Marino 		if (start_chan_idx == -1)
6823ff40c12SJohn Marino 			return -1;
6833ff40c12SJohn Marino 
6843ff40c12SJohn Marino 		/* Get number of used channels, depend on width */
685*a1157835SDaniel Fojt 		n_chans = dfs_get_used_n_chans(iface, &n_chans1);
686*a1157835SDaniel Fojt 
687*a1157835SDaniel Fojt 		/* Setup CAC time */
688*a1157835SDaniel Fojt 		iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
689*a1157835SDaniel Fojt 						     n_chans);
6903ff40c12SJohn Marino 
6913ff40c12SJohn Marino 		/* Check if any of configured channels require DFS */
6923ff40c12SJohn Marino 		res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
6933ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG,
6943ff40c12SJohn Marino 			   "DFS %d channels required radar detection",
6953ff40c12SJohn Marino 			   res);
6963ff40c12SJohn Marino 		if (!res)
6973ff40c12SJohn Marino 			return 1;
6983ff40c12SJohn Marino 
6993ff40c12SJohn Marino 		/* Check if all channels are DFS available */
7003ff40c12SJohn Marino 		res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
7013ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG,
7023ff40c12SJohn Marino 			   "DFS all channels available, (SKIP CAC): %s",
7033ff40c12SJohn Marino 			   res ? "yes" : "no");
7043ff40c12SJohn Marino 		if (res)
7053ff40c12SJohn Marino 			return 1;
7063ff40c12SJohn Marino 
7073ff40c12SJohn Marino 		/* Check if any of configured channels is unavailable */
7083ff40c12SJohn Marino 		res = dfs_check_chans_unavailable(iface, start_chan_idx,
7093ff40c12SJohn Marino 						  n_chans);
7103ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
7113ff40c12SJohn Marino 			   res, res ? "yes": "no");
7123ff40c12SJohn Marino 		if (res) {
713*a1157835SDaniel Fojt 			int sec = 0;
714*a1157835SDaniel Fojt 			u8 cf1 = 0, cf2 = 0;
7153ff40c12SJohn Marino 
7163ff40c12SJohn Marino 			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
7173ff40c12SJohn Marino 							skip_radar);
7183ff40c12SJohn Marino 			if (!channel) {
7193ff40c12SJohn Marino 				wpa_printf(MSG_ERROR, "could not get valid channel");
720*a1157835SDaniel Fojt 				hostapd_set_state(iface, HAPD_IFACE_DFS);
721*a1157835SDaniel Fojt 				return 0;
7223ff40c12SJohn Marino 			}
7233ff40c12SJohn Marino 
7243ff40c12SJohn Marino 			iface->freq = channel->freq;
7253ff40c12SJohn Marino 			iface->conf->channel = channel->chan;
7263ff40c12SJohn Marino 			iface->conf->secondary_channel = sec;
727*a1157835SDaniel Fojt 			hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
728*a1157835SDaniel Fojt 			hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
7293ff40c12SJohn Marino 		}
7303ff40c12SJohn Marino 	} while (res);
7313ff40c12SJohn Marino 
7323ff40c12SJohn Marino 	/* Finally start CAC */
7333ff40c12SJohn Marino 	hostapd_set_state(iface, HAPD_IFACE_DFS);
7343ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
7353ff40c12SJohn Marino 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
736*a1157835SDaniel Fojt 		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
7373ff40c12SJohn Marino 		iface->freq,
738*a1157835SDaniel Fojt 		iface->conf->channel, iface->conf->secondary_channel,
739*a1157835SDaniel Fojt 		hostapd_get_oper_chwidth(iface->conf),
740*a1157835SDaniel Fojt 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
741*a1157835SDaniel Fojt 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
742*a1157835SDaniel Fojt 		iface->dfs_cac_ms / 1000);
743*a1157835SDaniel Fojt 
744*a1157835SDaniel Fojt 	res = hostapd_start_dfs_cac(
745*a1157835SDaniel Fojt 		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
746*a1157835SDaniel Fojt 		iface->conf->ieee80211n, iface->conf->ieee80211ac,
747*a1157835SDaniel Fojt 		iface->conf->ieee80211ax,
7483ff40c12SJohn Marino 		iface->conf->secondary_channel,
749*a1157835SDaniel Fojt 		hostapd_get_oper_chwidth(iface->conf),
750*a1157835SDaniel Fojt 		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
751*a1157835SDaniel Fojt 		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
752*a1157835SDaniel Fojt 
753*a1157835SDaniel Fojt 	if (res) {
754*a1157835SDaniel Fojt 		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
7553ff40c12SJohn Marino 		return -1;
7563ff40c12SJohn Marino 	}
7573ff40c12SJohn Marino 
7583ff40c12SJohn Marino 	return 0;
7593ff40c12SJohn Marino }
7603ff40c12SJohn Marino 
7613ff40c12SJohn Marino 
hostapd_config_dfs_chan_available(struct hostapd_iface * iface)762*a1157835SDaniel Fojt static int hostapd_config_dfs_chan_available(struct hostapd_iface *iface)
763*a1157835SDaniel Fojt {
764*a1157835SDaniel Fojt 	int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
765*a1157835SDaniel Fojt 
766*a1157835SDaniel Fojt 	/* Get the start (first) channel for current configuration */
767*a1157835SDaniel Fojt 	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
768*a1157835SDaniel Fojt 	if (start_chan_idx < 0)
769*a1157835SDaniel Fojt 		return 0;
770*a1157835SDaniel Fojt 
771*a1157835SDaniel Fojt 	/* Get the number of used channels, depending on width */
772*a1157835SDaniel Fojt 	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
773*a1157835SDaniel Fojt 
774*a1157835SDaniel Fojt 	/* Check if all channels are DFS available */
775*a1157835SDaniel Fojt 	return dfs_check_chans_available(iface, start_chan_idx, n_chans);
776*a1157835SDaniel Fojt }
777*a1157835SDaniel Fojt 
778*a1157835SDaniel Fojt 
hostapd_dfs_complete_cac(struct hostapd_iface * iface,int success,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)7793ff40c12SJohn Marino int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
7803ff40c12SJohn Marino 			     int ht_enabled, int chan_offset, int chan_width,
7813ff40c12SJohn Marino 			     int cf1, int cf2)
7823ff40c12SJohn Marino {
7833ff40c12SJohn Marino 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
7843ff40c12SJohn Marino 		"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
7853ff40c12SJohn Marino 		success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
7863ff40c12SJohn Marino 
7873ff40c12SJohn Marino 	if (success) {
7883ff40c12SJohn Marino 		/* Complete iface/ap configuration */
789*a1157835SDaniel Fojt 		if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
790*a1157835SDaniel Fojt 			/* Complete AP configuration for the first bring up. */
791*a1157835SDaniel Fojt 			if (iface->state != HAPD_IFACE_ENABLED)
792*a1157835SDaniel Fojt 				hostapd_setup_interface_complete(iface, 0);
793*a1157835SDaniel Fojt 			else
794*a1157835SDaniel Fojt 				iface->cac_started = 0;
795*a1157835SDaniel Fojt 		} else {
7963ff40c12SJohn Marino 			set_dfs_state(iface, freq, ht_enabled, chan_offset,
7973ff40c12SJohn Marino 				      chan_width, cf1, cf2,
7983ff40c12SJohn Marino 				      HOSTAPD_CHAN_DFS_AVAILABLE);
799*a1157835SDaniel Fojt 			/*
800*a1157835SDaniel Fojt 			 * Just mark the channel available when CAC completion
801*a1157835SDaniel Fojt 			 * event is received in enabled state. CAC result could
802*a1157835SDaniel Fojt 			 * have been propagated from another radio having the
803*a1157835SDaniel Fojt 			 * same regulatory configuration. When CAC completion is
804*a1157835SDaniel Fojt 			 * received during non-HAPD_IFACE_ENABLED state, make
805*a1157835SDaniel Fojt 			 * sure the configured channel is available because this
806*a1157835SDaniel Fojt 			 * CAC completion event could have been propagated from
807*a1157835SDaniel Fojt 			 * another radio.
808*a1157835SDaniel Fojt 			 */
809*a1157835SDaniel Fojt 			if (iface->state != HAPD_IFACE_ENABLED &&
810*a1157835SDaniel Fojt 			    hostapd_config_dfs_chan_available(iface)) {
8113ff40c12SJohn Marino 				hostapd_setup_interface_complete(iface, 0);
812*a1157835SDaniel Fojt 				iface->cac_started = 0;
8133ff40c12SJohn Marino 			}
814*a1157835SDaniel Fojt 		}
815*a1157835SDaniel Fojt 	}
816*a1157835SDaniel Fojt 
817*a1157835SDaniel Fojt 	return 0;
818*a1157835SDaniel Fojt }
819*a1157835SDaniel Fojt 
820*a1157835SDaniel Fojt 
hostapd_dfs_pre_cac_expired(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)821*a1157835SDaniel Fojt int hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
822*a1157835SDaniel Fojt 				int ht_enabled, int chan_offset, int chan_width,
823*a1157835SDaniel Fojt 				int cf1, int cf2)
824*a1157835SDaniel Fojt {
825*a1157835SDaniel Fojt 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
826*a1157835SDaniel Fojt 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
827*a1157835SDaniel Fojt 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
828*a1157835SDaniel Fojt 
829*a1157835SDaniel Fojt 	/* Proceed only if DFS is not offloaded to the driver */
830*a1157835SDaniel Fojt 	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
831*a1157835SDaniel Fojt 		return 0;
832*a1157835SDaniel Fojt 
833*a1157835SDaniel Fojt 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
834*a1157835SDaniel Fojt 		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
8353ff40c12SJohn Marino 
8363ff40c12SJohn Marino 	return 0;
8373ff40c12SJohn Marino }
8383ff40c12SJohn Marino 
8393ff40c12SJohn Marino 
hostapd_dfs_start_channel_switch_cac(struct hostapd_iface * iface)8403ff40c12SJohn Marino static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
8413ff40c12SJohn Marino {
8423ff40c12SJohn Marino 	struct hostapd_channel_data *channel;
8433ff40c12SJohn Marino 	int secondary_channel;
844*a1157835SDaniel Fojt 	u8 oper_centr_freq_seg0_idx = 0;
845*a1157835SDaniel Fojt 	u8 oper_centr_freq_seg1_idx = 0;
8463ff40c12SJohn Marino 	int skip_radar = 0;
8473ff40c12SJohn Marino 	int err = 1;
8483ff40c12SJohn Marino 
8493ff40c12SJohn Marino 	/* Radar detected during active CAC */
8503ff40c12SJohn Marino 	iface->cac_started = 0;
8513ff40c12SJohn Marino 	channel = dfs_get_valid_channel(iface, &secondary_channel,
852*a1157835SDaniel Fojt 					&oper_centr_freq_seg0_idx,
853*a1157835SDaniel Fojt 					&oper_centr_freq_seg1_idx,
8543ff40c12SJohn Marino 					skip_radar);
8553ff40c12SJohn Marino 
8563ff40c12SJohn Marino 	if (!channel) {
8573ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "No valid channel available");
8583ff40c12SJohn Marino 		return err;
8593ff40c12SJohn Marino 	}
8603ff40c12SJohn Marino 
8613ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
8623ff40c12SJohn Marino 		   channel->chan);
8633ff40c12SJohn Marino 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
8643ff40c12SJohn Marino 		"freq=%d chan=%d sec_chan=%d", channel->freq,
8653ff40c12SJohn Marino 		channel->chan, secondary_channel);
8663ff40c12SJohn Marino 
8673ff40c12SJohn Marino 	iface->freq = channel->freq;
8683ff40c12SJohn Marino 	iface->conf->channel = channel->chan;
8693ff40c12SJohn Marino 	iface->conf->secondary_channel = secondary_channel;
870*a1157835SDaniel Fojt 	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
871*a1157835SDaniel Fojt 					     oper_centr_freq_seg0_idx);
872*a1157835SDaniel Fojt 	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
873*a1157835SDaniel Fojt 					     oper_centr_freq_seg1_idx);
8743ff40c12SJohn Marino 	err = 0;
8753ff40c12SJohn Marino 
8763ff40c12SJohn Marino 	hostapd_setup_interface_complete(iface, err);
8773ff40c12SJohn Marino 	return err;
8783ff40c12SJohn Marino }
8793ff40c12SJohn Marino 
8803ff40c12SJohn Marino 
hostapd_dfs_start_channel_switch(struct hostapd_iface * iface)8813ff40c12SJohn Marino static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
8823ff40c12SJohn Marino {
8833ff40c12SJohn Marino 	struct hostapd_channel_data *channel;
8843ff40c12SJohn Marino 	int secondary_channel;
885*a1157835SDaniel Fojt 	u8 oper_centr_freq_seg0_idx;
886*a1157835SDaniel Fojt 	u8 oper_centr_freq_seg1_idx;
8873ff40c12SJohn Marino 	int skip_radar = 1;
8883ff40c12SJohn Marino 	struct csa_settings csa_settings;
889*a1157835SDaniel Fojt 	unsigned int i;
8903ff40c12SJohn Marino 	int err = 1;
891*a1157835SDaniel Fojt 	struct hostapd_hw_modes *cmode = iface->current_mode;
8923ff40c12SJohn Marino 
8933ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
8943ff40c12SJohn Marino 		   __func__, iface->cac_started ? "yes" : "no",
895*a1157835SDaniel Fojt 		   hostapd_csa_in_progress(iface) ? "yes" : "no");
8963ff40c12SJohn Marino 
8973ff40c12SJohn Marino 	/* Check if CSA in progress */
898*a1157835SDaniel Fojt 	if (hostapd_csa_in_progress(iface))
8993ff40c12SJohn Marino 		return 0;
9003ff40c12SJohn Marino 
9013ff40c12SJohn Marino 	/* Check if active CAC */
9023ff40c12SJohn Marino 	if (iface->cac_started)
9033ff40c12SJohn Marino 		return hostapd_dfs_start_channel_switch_cac(iface);
9043ff40c12SJohn Marino 
905*a1157835SDaniel Fojt 	/*
906*a1157835SDaniel Fojt 	 * Allow selection of DFS channel in ETSI to comply with
907*a1157835SDaniel Fojt 	 * uniform spreading.
908*a1157835SDaniel Fojt 	 */
909*a1157835SDaniel Fojt 	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
910*a1157835SDaniel Fojt 		skip_radar = 0;
911*a1157835SDaniel Fojt 
9123ff40c12SJohn Marino 	/* Perform channel switch/CSA */
9133ff40c12SJohn Marino 	channel = dfs_get_valid_channel(iface, &secondary_channel,
914*a1157835SDaniel Fojt 					&oper_centr_freq_seg0_idx,
915*a1157835SDaniel Fojt 					&oper_centr_freq_seg1_idx,
9163ff40c12SJohn Marino 					skip_radar);
9173ff40c12SJohn Marino 
9183ff40c12SJohn Marino 	if (!channel) {
919*a1157835SDaniel Fojt 		/*
920*a1157835SDaniel Fojt 		 * If there is no channel to switch immediately to, check if
921*a1157835SDaniel Fojt 		 * there is another channel where we can switch even if it
922*a1157835SDaniel Fojt 		 * requires to perform a CAC first.
923*a1157835SDaniel Fojt 		 */
924*a1157835SDaniel Fojt 		skip_radar = 0;
925*a1157835SDaniel Fojt 		channel = dfs_get_valid_channel(iface, &secondary_channel,
926*a1157835SDaniel Fojt 						&oper_centr_freq_seg0_idx,
927*a1157835SDaniel Fojt 						&oper_centr_freq_seg1_idx,
928*a1157835SDaniel Fojt 						skip_radar);
929*a1157835SDaniel Fojt 		if (!channel) {
930*a1157835SDaniel Fojt 			wpa_printf(MSG_INFO,
931*a1157835SDaniel Fojt 				   "%s: no DFS channels left, waiting for NOP to finish",
932*a1157835SDaniel Fojt 				   __func__);
9333ff40c12SJohn Marino 			return err;
9343ff40c12SJohn Marino 		}
9353ff40c12SJohn Marino 
936*a1157835SDaniel Fojt 		iface->freq = channel->freq;
937*a1157835SDaniel Fojt 		iface->conf->channel = channel->chan;
938*a1157835SDaniel Fojt 		iface->conf->secondary_channel = secondary_channel;
939*a1157835SDaniel Fojt 		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
940*a1157835SDaniel Fojt 						     oper_centr_freq_seg0_idx);
941*a1157835SDaniel Fojt 		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
942*a1157835SDaniel Fojt 						     oper_centr_freq_seg1_idx);
943*a1157835SDaniel Fojt 
944*a1157835SDaniel Fojt 		hostapd_disable_iface(iface);
945*a1157835SDaniel Fojt 		hostapd_enable_iface(iface);
946*a1157835SDaniel Fojt 		return 0;
947*a1157835SDaniel Fojt 	}
948*a1157835SDaniel Fojt 
9493ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
9503ff40c12SJohn Marino 		   channel->chan);
9513ff40c12SJohn Marino 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
9523ff40c12SJohn Marino 		"freq=%d chan=%d sec_chan=%d", channel->freq,
9533ff40c12SJohn Marino 		channel->chan, secondary_channel);
9543ff40c12SJohn Marino 
9553ff40c12SJohn Marino 	/* Setup CSA request */
9563ff40c12SJohn Marino 	os_memset(&csa_settings, 0, sizeof(csa_settings));
9573ff40c12SJohn Marino 	csa_settings.cs_count = 5;
9583ff40c12SJohn Marino 	csa_settings.block_tx = 1;
9593ff40c12SJohn Marino 	err = hostapd_set_freq_params(&csa_settings.freq_params,
9603ff40c12SJohn Marino 				      iface->conf->hw_mode,
9613ff40c12SJohn Marino 				      channel->freq,
9623ff40c12SJohn Marino 				      channel->chan,
9633ff40c12SJohn Marino 				      iface->conf->ieee80211n,
9643ff40c12SJohn Marino 				      iface->conf->ieee80211ac,
965*a1157835SDaniel Fojt 				      iface->conf->ieee80211ax,
9663ff40c12SJohn Marino 				      secondary_channel,
967*a1157835SDaniel Fojt 				      hostapd_get_oper_chwidth(iface->conf),
968*a1157835SDaniel Fojt 				      oper_centr_freq_seg0_idx,
969*a1157835SDaniel Fojt 				      oper_centr_freq_seg1_idx,
970*a1157835SDaniel Fojt 				      cmode->vht_capab,
971*a1157835SDaniel Fojt 				      &cmode->he_capab[IEEE80211_MODE_AP]);
9723ff40c12SJohn Marino 
9733ff40c12SJohn Marino 	if (err) {
9743ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
9753ff40c12SJohn Marino 		hostapd_disable_iface(iface);
9763ff40c12SJohn Marino 		return err;
9773ff40c12SJohn Marino 	}
9783ff40c12SJohn Marino 
979*a1157835SDaniel Fojt 	for (i = 0; i < iface->num_bss; i++) {
980*a1157835SDaniel Fojt 		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
981*a1157835SDaniel Fojt 		if (err)
982*a1157835SDaniel Fojt 			break;
983*a1157835SDaniel Fojt 	}
984*a1157835SDaniel Fojt 
9853ff40c12SJohn Marino 	if (err) {
9863ff40c12SJohn Marino 		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
9873ff40c12SJohn Marino 			   err);
9883ff40c12SJohn Marino 		iface->freq = channel->freq;
9893ff40c12SJohn Marino 		iface->conf->channel = channel->chan;
9903ff40c12SJohn Marino 		iface->conf->secondary_channel = secondary_channel;
991*a1157835SDaniel Fojt 		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
992*a1157835SDaniel Fojt 						     oper_centr_freq_seg0_idx);
993*a1157835SDaniel Fojt 		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
994*a1157835SDaniel Fojt 						     oper_centr_freq_seg1_idx);
9953ff40c12SJohn Marino 
9963ff40c12SJohn Marino 		hostapd_disable_iface(iface);
9973ff40c12SJohn Marino 		hostapd_enable_iface(iface);
9983ff40c12SJohn Marino 		return 0;
9993ff40c12SJohn Marino 	}
10003ff40c12SJohn Marino 
10013ff40c12SJohn Marino 	/* Channel configuration will be updated once CSA completes and
10023ff40c12SJohn Marino 	 * ch_switch_notify event is received */
10033ff40c12SJohn Marino 
10043ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
10053ff40c12SJohn Marino 	return 0;
10063ff40c12SJohn Marino }
10073ff40c12SJohn Marino 
10083ff40c12SJohn Marino 
hostapd_dfs_radar_detected(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)10093ff40c12SJohn Marino int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
10103ff40c12SJohn Marino 			       int ht_enabled, int chan_offset, int chan_width,
10113ff40c12SJohn Marino 			       int cf1, int cf2)
10123ff40c12SJohn Marino {
10133ff40c12SJohn Marino 	int res;
10143ff40c12SJohn Marino 
10153ff40c12SJohn Marino 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
10163ff40c12SJohn Marino 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
10173ff40c12SJohn Marino 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
10183ff40c12SJohn Marino 
1019*a1157835SDaniel Fojt 	/* Proceed only if DFS is not offloaded to the driver */
1020*a1157835SDaniel Fojt 	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
1021*a1157835SDaniel Fojt 		return 0;
1022*a1157835SDaniel Fojt 
1023*a1157835SDaniel Fojt 	if (!iface->conf->ieee80211h)
1024*a1157835SDaniel Fojt 		return 0;
1025*a1157835SDaniel Fojt 
10263ff40c12SJohn Marino 	/* mark radar frequency as invalid */
1027*a1157835SDaniel Fojt 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
1028*a1157835SDaniel Fojt 		      cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
10293ff40c12SJohn Marino 
10303ff40c12SJohn Marino 	/* Skip if reported radar event not overlapped our channels */
10313ff40c12SJohn Marino 	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
10323ff40c12SJohn Marino 	if (!res)
10333ff40c12SJohn Marino 		return 0;
10343ff40c12SJohn Marino 
10353ff40c12SJohn Marino 	/* radar detected while operating, switch the channel. */
10363ff40c12SJohn Marino 	res = hostapd_dfs_start_channel_switch(iface);
10373ff40c12SJohn Marino 
10383ff40c12SJohn Marino 	return res;
10393ff40c12SJohn Marino }
10403ff40c12SJohn Marino 
10413ff40c12SJohn Marino 
hostapd_dfs_nop_finished(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)10423ff40c12SJohn Marino int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
10433ff40c12SJohn Marino 			     int ht_enabled, int chan_offset, int chan_width,
10443ff40c12SJohn Marino 			     int cf1, int cf2)
10453ff40c12SJohn Marino {
10463ff40c12SJohn Marino 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
10473ff40c12SJohn Marino 		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
10483ff40c12SJohn Marino 		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
1049*a1157835SDaniel Fojt 
1050*a1157835SDaniel Fojt 	/* Proceed only if DFS is not offloaded to the driver */
1051*a1157835SDaniel Fojt 	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
1052*a1157835SDaniel Fojt 		return 0;
1053*a1157835SDaniel Fojt 
10543ff40c12SJohn Marino 	/* TODO add correct implementation here */
10553ff40c12SJohn Marino 	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
10563ff40c12SJohn Marino 		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
1057*a1157835SDaniel Fojt 
1058*a1157835SDaniel Fojt 	/* Handle cases where all channels were initially unavailable */
1059*a1157835SDaniel Fojt 	if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
1060*a1157835SDaniel Fojt 		hostapd_handle_dfs(iface);
1061*a1157835SDaniel Fojt 
10623ff40c12SJohn Marino 	return 0;
10633ff40c12SJohn Marino }
1064*a1157835SDaniel Fojt 
1065*a1157835SDaniel Fojt 
hostapd_is_dfs_required(struct hostapd_iface * iface)1066*a1157835SDaniel Fojt int hostapd_is_dfs_required(struct hostapd_iface *iface)
1067*a1157835SDaniel Fojt {
1068*a1157835SDaniel Fojt 	int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
1069*a1157835SDaniel Fojt 
1070*a1157835SDaniel Fojt 	if (!iface->conf->ieee80211h || !iface->current_mode ||
1071*a1157835SDaniel Fojt 	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
1072*a1157835SDaniel Fojt 		return 0;
1073*a1157835SDaniel Fojt 
1074*a1157835SDaniel Fojt 	/* Get start (first) channel for current configuration */
1075*a1157835SDaniel Fojt 	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
1076*a1157835SDaniel Fojt 	if (start_chan_idx == -1)
1077*a1157835SDaniel Fojt 		return -1;
1078*a1157835SDaniel Fojt 
1079*a1157835SDaniel Fojt 	/* Get number of used channels, depend on width */
1080*a1157835SDaniel Fojt 	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
1081*a1157835SDaniel Fojt 
1082*a1157835SDaniel Fojt 	/* Check if any of configured channels require DFS */
1083*a1157835SDaniel Fojt 	res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
1084*a1157835SDaniel Fojt 	if (res)
1085*a1157835SDaniel Fojt 		return res;
1086*a1157835SDaniel Fojt 	if (start_chan_idx1 >= 0 && n_chans1 > 0)
1087*a1157835SDaniel Fojt 		res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
1088*a1157835SDaniel Fojt 	return res;
1089*a1157835SDaniel Fojt }
1090*a1157835SDaniel Fojt 
1091*a1157835SDaniel Fojt 
hostapd_dfs_start_cac(struct hostapd_iface * iface,int freq,int ht_enabled,int chan_offset,int chan_width,int cf1,int cf2)1092*a1157835SDaniel Fojt int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
1093*a1157835SDaniel Fojt 			  int ht_enabled, int chan_offset, int chan_width,
1094*a1157835SDaniel Fojt 			  int cf1, int cf2)
1095*a1157835SDaniel Fojt {
1096*a1157835SDaniel Fojt 	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
1097*a1157835SDaniel Fojt 		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
1098*a1157835SDaniel Fojt 		"seg1=%d cac_time=%ds",
1099*a1157835SDaniel Fojt 		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60);
1100*a1157835SDaniel Fojt 	iface->cac_started = 1;
1101*a1157835SDaniel Fojt 	return 0;
1102*a1157835SDaniel Fojt }
1103*a1157835SDaniel Fojt 
1104*a1157835SDaniel Fojt 
1105*a1157835SDaniel Fojt /*
1106*a1157835SDaniel Fojt  * Main DFS handler for offloaded case.
1107*a1157835SDaniel Fojt  * 2 - continue channel/AP setup for non-DFS channel
1108*a1157835SDaniel Fojt  * 1 - continue channel/AP setup for DFS channel
1109*a1157835SDaniel Fojt  * 0 - channel/AP setup will be continued after CAC
1110*a1157835SDaniel Fojt  * -1 - hit critical error
1111*a1157835SDaniel Fojt  */
hostapd_handle_dfs_offload(struct hostapd_iface * iface)1112*a1157835SDaniel Fojt int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
1113*a1157835SDaniel Fojt {
1114*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
1115*a1157835SDaniel Fojt 		   __func__, iface->cac_started);
1116*a1157835SDaniel Fojt 
1117*a1157835SDaniel Fojt 	/*
1118*a1157835SDaniel Fojt 	 * If DFS has already been started, then we are being called from a
1119*a1157835SDaniel Fojt 	 * callback to continue AP/channel setup. Reset the CAC start flag and
1120*a1157835SDaniel Fojt 	 * return.
1121*a1157835SDaniel Fojt 	 */
1122*a1157835SDaniel Fojt 	if (iface->cac_started) {
1123*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
1124*a1157835SDaniel Fojt 			   __func__, iface->cac_started);
1125*a1157835SDaniel Fojt 		iface->cac_started = 0;
1126*a1157835SDaniel Fojt 		return 1;
1127*a1157835SDaniel Fojt 	}
1128*a1157835SDaniel Fojt 
1129*a1157835SDaniel Fojt 	if (ieee80211_is_dfs(iface->freq, iface->hw_features,
1130*a1157835SDaniel Fojt 			     iface->num_hw_features)) {
1131*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
1132*a1157835SDaniel Fojt 			   __func__, iface->freq);
1133*a1157835SDaniel Fojt 		return 0;
1134*a1157835SDaniel Fojt 	}
1135*a1157835SDaniel Fojt 
1136*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG,
1137*a1157835SDaniel Fojt 		   "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
1138*a1157835SDaniel Fojt 		   __func__, iface->freq);
1139*a1157835SDaniel Fojt 	return 2;
1140*a1157835SDaniel Fojt }
1141