1e5b75505Sopenharmony_ci/*
2e5b75505Sopenharmony_ci * DFS - Dynamic Frequency Selection
3e5b75505Sopenharmony_ci * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4e5b75505Sopenharmony_ci * Copyright (c) 2013-2017, Qualcomm Atheros, Inc.
5e5b75505Sopenharmony_ci *
6e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license.
7e5b75505Sopenharmony_ci * See README for more details.
8e5b75505Sopenharmony_ci */
9e5b75505Sopenharmony_ci
10e5b75505Sopenharmony_ci#include "utils/includes.h"
11e5b75505Sopenharmony_ci
12e5b75505Sopenharmony_ci#include "utils/common.h"
13e5b75505Sopenharmony_ci#include "common/ieee802_11_defs.h"
14e5b75505Sopenharmony_ci#include "common/hw_features_common.h"
15e5b75505Sopenharmony_ci#include "common/wpa_ctrl.h"
16e5b75505Sopenharmony_ci#include "hostapd.h"
17e5b75505Sopenharmony_ci#include "ap_drv_ops.h"
18e5b75505Sopenharmony_ci#include "drivers/driver.h"
19e5b75505Sopenharmony_ci#include "dfs.h"
20e5b75505Sopenharmony_ci
21e5b75505Sopenharmony_ci
22e5b75505Sopenharmony_cistatic int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
23e5b75505Sopenharmony_ci{
24e5b75505Sopenharmony_ci	int n_chans = 1;
25e5b75505Sopenharmony_ci
26e5b75505Sopenharmony_ci	*seg1 = 0;
27e5b75505Sopenharmony_ci
28e5b75505Sopenharmony_ci	if (iface->conf->ieee80211n && iface->conf->secondary_channel)
29e5b75505Sopenharmony_ci		n_chans = 2;
30e5b75505Sopenharmony_ci
31e5b75505Sopenharmony_ci	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
32e5b75505Sopenharmony_ci		switch (hostapd_get_oper_chwidth(iface->conf)) {
33e5b75505Sopenharmony_ci		case CHANWIDTH_USE_HT:
34e5b75505Sopenharmony_ci			break;
35e5b75505Sopenharmony_ci		case CHANWIDTH_80MHZ:
36e5b75505Sopenharmony_ci			n_chans = 4;
37e5b75505Sopenharmony_ci			break;
38e5b75505Sopenharmony_ci		case CHANWIDTH_160MHZ:
39e5b75505Sopenharmony_ci#ifdef CONFIG_P2P_160M
40e5b75505Sopenharmony_ci/*only need to check CH36,40,44,48 the 4 channels for P2P 160M*/
41e5b75505Sopenharmony_ci			n_chans = 4;
42e5b75505Sopenharmony_ci#else
43e5b75505Sopenharmony_ci			n_chans = 8;
44e5b75505Sopenharmony_ci#endif
45e5b75505Sopenharmony_ci			break;
46e5b75505Sopenharmony_ci		case CHANWIDTH_80P80MHZ:
47e5b75505Sopenharmony_ci			n_chans = 4;
48e5b75505Sopenharmony_ci			*seg1 = 4;
49e5b75505Sopenharmony_ci			break;
50e5b75505Sopenharmony_ci		default:
51e5b75505Sopenharmony_ci			break;
52e5b75505Sopenharmony_ci		}
53e5b75505Sopenharmony_ci	}
54e5b75505Sopenharmony_ci
55e5b75505Sopenharmony_ci	return n_chans;
56e5b75505Sopenharmony_ci}
57e5b75505Sopenharmony_ci
58e5b75505Sopenharmony_ci
59e5b75505Sopenharmony_cistatic int dfs_channel_available(struct hostapd_channel_data *chan,
60e5b75505Sopenharmony_ci				 int skip_radar)
61e5b75505Sopenharmony_ci{
62e5b75505Sopenharmony_ci	/*
63e5b75505Sopenharmony_ci	 * When radar detection happens, CSA is performed. However, there's no
64e5b75505Sopenharmony_ci	 * time for CAC, so radar channels must be skipped when finding a new
65e5b75505Sopenharmony_ci	 * channel for CSA, unless they are available for immediate use.
66e5b75505Sopenharmony_ci	 */
67e5b75505Sopenharmony_ci	if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
68e5b75505Sopenharmony_ci	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
69e5b75505Sopenharmony_ci	     HOSTAPD_CHAN_DFS_AVAILABLE))
70e5b75505Sopenharmony_ci		return 0;
71e5b75505Sopenharmony_ci
72e5b75505Sopenharmony_ci	if (chan->flag & HOSTAPD_CHAN_DISABLED)
73e5b75505Sopenharmony_ci		return 0;
74e5b75505Sopenharmony_ci	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
75e5b75505Sopenharmony_ci	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
76e5b75505Sopenharmony_ci	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
77e5b75505Sopenharmony_ci		return 0;
78e5b75505Sopenharmony_ci	return 1;
79e5b75505Sopenharmony_ci}
80e5b75505Sopenharmony_ci
81e5b75505Sopenharmony_ci
82e5b75505Sopenharmony_cistatic int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
83e5b75505Sopenharmony_ci{
84e5b75505Sopenharmony_ci	/*
85e5b75505Sopenharmony_ci	 * The tables contain first valid channel number based on channel width.
86e5b75505Sopenharmony_ci	 * We will also choose this first channel as the control one.
87e5b75505Sopenharmony_ci	 */
88e5b75505Sopenharmony_ci	int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
89e5b75505Sopenharmony_ci			     165, 173, 184, 192 };
90e5b75505Sopenharmony_ci	/*
91e5b75505Sopenharmony_ci	 * VHT80, valid channels based on center frequency:
92e5b75505Sopenharmony_ci	 * 42, 58, 106, 122, 138, 155, 171
93e5b75505Sopenharmony_ci	 */
94e5b75505Sopenharmony_ci	int allowed_80[] = { 36, 52, 100, 116, 132, 149, 165 };
95e5b75505Sopenharmony_ci	/*
96e5b75505Sopenharmony_ci	 * VHT160 valid channels based on center frequency:
97e5b75505Sopenharmony_ci	 * 50, 114, 163
98e5b75505Sopenharmony_ci	 */
99e5b75505Sopenharmony_ci	int allowed_160[] = { 36, 100, 149 };
100e5b75505Sopenharmony_ci	int *allowed = allowed_40;
101e5b75505Sopenharmony_ci	unsigned int i, allowed_no = 0;
102e5b75505Sopenharmony_ci
103e5b75505Sopenharmony_ci	switch (n_chans) {
104e5b75505Sopenharmony_ci	case 2:
105e5b75505Sopenharmony_ci		allowed = allowed_40;
106e5b75505Sopenharmony_ci		allowed_no = ARRAY_SIZE(allowed_40);
107e5b75505Sopenharmony_ci		break;
108e5b75505Sopenharmony_ci	case 4:
109e5b75505Sopenharmony_ci		allowed = allowed_80;
110e5b75505Sopenharmony_ci		allowed_no = ARRAY_SIZE(allowed_80);
111e5b75505Sopenharmony_ci		break;
112e5b75505Sopenharmony_ci	case 8:
113e5b75505Sopenharmony_ci		allowed = allowed_160;
114e5b75505Sopenharmony_ci		allowed_no = ARRAY_SIZE(allowed_160);
115e5b75505Sopenharmony_ci		break;
116e5b75505Sopenharmony_ci	default:
117e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
118e5b75505Sopenharmony_ci		break;
119e5b75505Sopenharmony_ci	}
120e5b75505Sopenharmony_ci
121e5b75505Sopenharmony_ci	for (i = 0; i < allowed_no; i++) {
122e5b75505Sopenharmony_ci		if (chan->chan == allowed[i])
123e5b75505Sopenharmony_ci			return 1;
124e5b75505Sopenharmony_ci	}
125e5b75505Sopenharmony_ci
126e5b75505Sopenharmony_ci	return 0;
127e5b75505Sopenharmony_ci}
128e5b75505Sopenharmony_ci
129e5b75505Sopenharmony_ci
130e5b75505Sopenharmony_cistatic struct hostapd_channel_data *
131e5b75505Sopenharmony_cidfs_get_chan_data(struct hostapd_hw_modes *mode, int freq, int first_chan_idx)
132e5b75505Sopenharmony_ci{
133e5b75505Sopenharmony_ci	int i;
134e5b75505Sopenharmony_ci
135e5b75505Sopenharmony_ci	for (i = first_chan_idx; i < mode->num_channels; i++) {
136e5b75505Sopenharmony_ci		if (mode->channels[i].freq == freq)
137e5b75505Sopenharmony_ci			return &mode->channels[i];
138e5b75505Sopenharmony_ci	}
139e5b75505Sopenharmony_ci
140e5b75505Sopenharmony_ci	return NULL;
141e5b75505Sopenharmony_ci}
142e5b75505Sopenharmony_ci
143e5b75505Sopenharmony_ci
144e5b75505Sopenharmony_cistatic int dfs_chan_range_available(struct hostapd_hw_modes *mode,
145e5b75505Sopenharmony_ci				    int first_chan_idx, int num_chans,
146e5b75505Sopenharmony_ci				    int skip_radar)
147e5b75505Sopenharmony_ci{
148e5b75505Sopenharmony_ci	struct hostapd_channel_data *first_chan, *chan;
149e5b75505Sopenharmony_ci	int i;
150e5b75505Sopenharmony_ci	u32 bw = num_chan_to_bw(num_chans);
151e5b75505Sopenharmony_ci
152e5b75505Sopenharmony_ci	if (first_chan_idx + num_chans > mode->num_channels) {
153e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
154e5b75505Sopenharmony_ci			   "DFS: some channels in range not defined");
155e5b75505Sopenharmony_ci		return 0;
156e5b75505Sopenharmony_ci	}
157e5b75505Sopenharmony_ci
158e5b75505Sopenharmony_ci	first_chan = &mode->channels[first_chan_idx];
159e5b75505Sopenharmony_ci
160e5b75505Sopenharmony_ci	/* hostapd DFS implementation assumes the first channel as primary.
161e5b75505Sopenharmony_ci	 * If it's not allowed to use the first channel as primary, decline the
162e5b75505Sopenharmony_ci	 * whole channel range. */
163e5b75505Sopenharmony_ci	if (!chan_pri_allowed(first_chan)) {
164e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "DFS: primary chanenl not allowed");
165e5b75505Sopenharmony_ci		return 0;
166e5b75505Sopenharmony_ci	}
167e5b75505Sopenharmony_ci
168e5b75505Sopenharmony_ci	for (i = 0; i < num_chans; i++) {
169e5b75505Sopenharmony_ci		chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
170e5b75505Sopenharmony_ci					 first_chan_idx);
171e5b75505Sopenharmony_ci		if (!chan) {
172e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "DFS: no channel data for %d",
173e5b75505Sopenharmony_ci				   first_chan->freq + i * 20);
174e5b75505Sopenharmony_ci			return 0;
175e5b75505Sopenharmony_ci		}
176e5b75505Sopenharmony_ci
177e5b75505Sopenharmony_ci		/* HT 40 MHz secondary channel availability checked only for
178e5b75505Sopenharmony_ci		 * primary channel */
179e5b75505Sopenharmony_ci		if (!chan_bw_allowed(chan, bw, 1, !i)) {
180e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "DFS: bw now allowed for %d",
181e5b75505Sopenharmony_ci				   first_chan->freq + i * 20);
182e5b75505Sopenharmony_ci			return 0;
183e5b75505Sopenharmony_ci		}
184e5b75505Sopenharmony_ci
185e5b75505Sopenharmony_ci		if (!dfs_channel_available(chan, skip_radar)) {
186e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "DFS: channel not available %d",
187e5b75505Sopenharmony_ci				   first_chan->freq + i * 20);
188e5b75505Sopenharmony_ci			return 0;
189e5b75505Sopenharmony_ci		}
190e5b75505Sopenharmony_ci	}
191e5b75505Sopenharmony_ci
192e5b75505Sopenharmony_ci	return 1;
193e5b75505Sopenharmony_ci}
194e5b75505Sopenharmony_ci
195e5b75505Sopenharmony_ci
196e5b75505Sopenharmony_cistatic int is_in_chanlist(struct hostapd_iface *iface,
197e5b75505Sopenharmony_ci			  struct hostapd_channel_data *chan)
198e5b75505Sopenharmony_ci{
199e5b75505Sopenharmony_ci	if (!iface->conf->acs_ch_list.num)
200e5b75505Sopenharmony_ci		return 1;
201e5b75505Sopenharmony_ci
202e5b75505Sopenharmony_ci	return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan);
203e5b75505Sopenharmony_ci}
204e5b75505Sopenharmony_ci
205e5b75505Sopenharmony_ci
206e5b75505Sopenharmony_ci/*
207e5b75505Sopenharmony_ci * The function assumes HT40+ operation.
208e5b75505Sopenharmony_ci * Make sure to adjust the following variables after calling this:
209e5b75505Sopenharmony_ci *  - hapd->secondary_channel
210e5b75505Sopenharmony_ci *  - hapd->vht/he_oper_centr_freq_seg0_idx
211e5b75505Sopenharmony_ci *  - hapd->vht/he_oper_centr_freq_seg1_idx
212e5b75505Sopenharmony_ci */
213e5b75505Sopenharmony_cistatic int dfs_find_channel(struct hostapd_iface *iface,
214e5b75505Sopenharmony_ci			    struct hostapd_channel_data **ret_chan,
215e5b75505Sopenharmony_ci			    int idx, int skip_radar)
216e5b75505Sopenharmony_ci{
217e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
218e5b75505Sopenharmony_ci	struct hostapd_channel_data *chan;
219e5b75505Sopenharmony_ci	int i, channel_idx = 0, n_chans, n_chans1;
220e5b75505Sopenharmony_ci
221e5b75505Sopenharmony_ci	mode = iface->current_mode;
222e5b75505Sopenharmony_ci	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
223e5b75505Sopenharmony_ci
224e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
225e5b75505Sopenharmony_ci	for (i = 0; i < mode->num_channels; i++) {
226e5b75505Sopenharmony_ci		chan = &mode->channels[i];
227e5b75505Sopenharmony_ci
228e5b75505Sopenharmony_ci		/* Skip HT40/VHT incompatible channels */
229e5b75505Sopenharmony_ci		if (iface->conf->ieee80211n &&
230e5b75505Sopenharmony_ci		    iface->conf->secondary_channel &&
231e5b75505Sopenharmony_ci		    (!dfs_is_chan_allowed(chan, n_chans) ||
232e5b75505Sopenharmony_ci		     !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P))) {
233e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG,
234e5b75505Sopenharmony_ci				   "DFS: channel %d (%d) is incompatible",
235e5b75505Sopenharmony_ci				   chan->freq, chan->chan);
236e5b75505Sopenharmony_ci			continue;
237e5b75505Sopenharmony_ci		}
238e5b75505Sopenharmony_ci
239e5b75505Sopenharmony_ci		/* Skip incompatible chandefs */
240e5b75505Sopenharmony_ci		if (!dfs_chan_range_available(mode, i, n_chans, skip_radar)) {
241e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG,
242e5b75505Sopenharmony_ci				   "DFS: range not available for %d (%d)",
243e5b75505Sopenharmony_ci				   chan->freq, chan->chan);
244e5b75505Sopenharmony_ci			continue;
245e5b75505Sopenharmony_ci		}
246e5b75505Sopenharmony_ci
247e5b75505Sopenharmony_ci		if (!is_in_chanlist(iface, chan)) {
248e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG,
249e5b75505Sopenharmony_ci				   "DFS: channel %d (%d) not in chanlist",
250e5b75505Sopenharmony_ci				   chan->freq, chan->chan);
251e5b75505Sopenharmony_ci			continue;
252e5b75505Sopenharmony_ci		}
253e5b75505Sopenharmony_ci
254e5b75505Sopenharmony_ci		if (chan->max_tx_power < iface->conf->min_tx_power)
255e5b75505Sopenharmony_ci			continue;
256e5b75505Sopenharmony_ci
257e5b75505Sopenharmony_ci		if (ret_chan && idx == channel_idx) {
258e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "Selected channel %d (%d)",
259e5b75505Sopenharmony_ci				   chan->freq, chan->chan);
260e5b75505Sopenharmony_ci			*ret_chan = chan;
261e5b75505Sopenharmony_ci			return idx;
262e5b75505Sopenharmony_ci		}
263e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "Adding channel %d (%d)",
264e5b75505Sopenharmony_ci			   chan->freq, chan->chan);
265e5b75505Sopenharmony_ci		channel_idx++;
266e5b75505Sopenharmony_ci	}
267e5b75505Sopenharmony_ci	return channel_idx;
268e5b75505Sopenharmony_ci}
269e5b75505Sopenharmony_ci
270e5b75505Sopenharmony_ci
271e5b75505Sopenharmony_cistatic void dfs_adjust_center_freq(struct hostapd_iface *iface,
272e5b75505Sopenharmony_ci				   struct hostapd_channel_data *chan,
273e5b75505Sopenharmony_ci				   int secondary_channel,
274e5b75505Sopenharmony_ci				   int sec_chan_idx_80p80,
275e5b75505Sopenharmony_ci				   u8 *oper_centr_freq_seg0_idx,
276e5b75505Sopenharmony_ci				   u8 *oper_centr_freq_seg1_idx)
277e5b75505Sopenharmony_ci{
278e5b75505Sopenharmony_ci	if (!iface->conf->ieee80211ac && !iface->conf->ieee80211ax)
279e5b75505Sopenharmony_ci		return;
280e5b75505Sopenharmony_ci
281e5b75505Sopenharmony_ci	if (!chan)
282e5b75505Sopenharmony_ci		return;
283e5b75505Sopenharmony_ci
284e5b75505Sopenharmony_ci	*oper_centr_freq_seg1_idx = 0;
285e5b75505Sopenharmony_ci
286e5b75505Sopenharmony_ci	switch (hostapd_get_oper_chwidth(iface->conf)) {
287e5b75505Sopenharmony_ci	case CHANWIDTH_USE_HT:
288e5b75505Sopenharmony_ci		if (secondary_channel == 1)
289e5b75505Sopenharmony_ci			*oper_centr_freq_seg0_idx = chan->chan + 2;
290e5b75505Sopenharmony_ci		else if (secondary_channel == -1)
291e5b75505Sopenharmony_ci			*oper_centr_freq_seg0_idx = chan->chan - 2;
292e5b75505Sopenharmony_ci		else
293e5b75505Sopenharmony_ci			*oper_centr_freq_seg0_idx = chan->chan;
294e5b75505Sopenharmony_ci		break;
295e5b75505Sopenharmony_ci	case CHANWIDTH_80MHZ:
296e5b75505Sopenharmony_ci		*oper_centr_freq_seg0_idx = chan->chan + 6;
297e5b75505Sopenharmony_ci		break;
298e5b75505Sopenharmony_ci	case CHANWIDTH_160MHZ:
299e5b75505Sopenharmony_ci		*oper_centr_freq_seg0_idx = chan->chan + 14;
300e5b75505Sopenharmony_ci		break;
301e5b75505Sopenharmony_ci	case CHANWIDTH_80P80MHZ:
302e5b75505Sopenharmony_ci		*oper_centr_freq_seg0_idx = chan->chan + 6;
303e5b75505Sopenharmony_ci		*oper_centr_freq_seg1_idx = sec_chan_idx_80p80 + 6;
304e5b75505Sopenharmony_ci		break;
305e5b75505Sopenharmony_ci
306e5b75505Sopenharmony_ci	default:
307e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO,
308e5b75505Sopenharmony_ci			   "DFS: Unsupported channel width configuration");
309e5b75505Sopenharmony_ci		*oper_centr_freq_seg0_idx = 0;
310e5b75505Sopenharmony_ci		break;
311e5b75505Sopenharmony_ci	}
312e5b75505Sopenharmony_ci
313e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
314e5b75505Sopenharmony_ci		   *oper_centr_freq_seg0_idx,
315e5b75505Sopenharmony_ci		   *oper_centr_freq_seg1_idx);
316e5b75505Sopenharmony_ci}
317e5b75505Sopenharmony_ci
318e5b75505Sopenharmony_ci
319e5b75505Sopenharmony_ci/* Return start channel idx we will use for mode->channels[idx] */
320e5b75505Sopenharmony_cistatic int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start)
321e5b75505Sopenharmony_ci{
322e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
323e5b75505Sopenharmony_ci	struct hostapd_channel_data *chan;
324e5b75505Sopenharmony_ci	int channel_no = iface->conf->channel;
325e5b75505Sopenharmony_ci	int res = -1, i;
326e5b75505Sopenharmony_ci	int chan_seg1 = -1;
327e5b75505Sopenharmony_ci
328e5b75505Sopenharmony_ci	*seg1_start = -1;
329e5b75505Sopenharmony_ci
330e5b75505Sopenharmony_ci	/* HT40- */
331e5b75505Sopenharmony_ci	if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
332e5b75505Sopenharmony_ci		channel_no -= 4;
333e5b75505Sopenharmony_ci
334e5b75505Sopenharmony_ci	/* VHT/HE */
335e5b75505Sopenharmony_ci	if (iface->conf->ieee80211ac || iface->conf->ieee80211ax) {
336e5b75505Sopenharmony_ci		switch (hostapd_get_oper_chwidth(iface->conf)) {
337e5b75505Sopenharmony_ci		case CHANWIDTH_USE_HT:
338e5b75505Sopenharmony_ci			break;
339e5b75505Sopenharmony_ci		case CHANWIDTH_80MHZ:
340e5b75505Sopenharmony_ci			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
341e5b75505Sopenharmony_ci				iface->conf) - 6;
342e5b75505Sopenharmony_ci			break;
343e5b75505Sopenharmony_ci		case CHANWIDTH_160MHZ:
344e5b75505Sopenharmony_ci			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
345e5b75505Sopenharmony_ci				iface->conf) - 14;
346e5b75505Sopenharmony_ci			break;
347e5b75505Sopenharmony_ci		case CHANWIDTH_80P80MHZ:
348e5b75505Sopenharmony_ci			channel_no = hostapd_get_oper_centr_freq_seg0_idx(
349e5b75505Sopenharmony_ci				iface->conf) - 6;
350e5b75505Sopenharmony_ci			chan_seg1 = hostapd_get_oper_centr_freq_seg1_idx(
351e5b75505Sopenharmony_ci				iface->conf) - 6;
352e5b75505Sopenharmony_ci			break;
353e5b75505Sopenharmony_ci		default:
354e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO,
355e5b75505Sopenharmony_ci				   "DFS only VHT20/40/80/160/80+80 is supported now");
356e5b75505Sopenharmony_ci			channel_no = -1;
357e5b75505Sopenharmony_ci			break;
358e5b75505Sopenharmony_ci		}
359e5b75505Sopenharmony_ci	}
360e5b75505Sopenharmony_ci
361e5b75505Sopenharmony_ci	/* Get idx */
362e5b75505Sopenharmony_ci	mode = iface->current_mode;
363e5b75505Sopenharmony_ci	for (i = 0; i < mode->num_channels; i++) {
364e5b75505Sopenharmony_ci		chan = &mode->channels[i];
365e5b75505Sopenharmony_ci		if (chan->chan == channel_no) {
366e5b75505Sopenharmony_ci			res = i;
367e5b75505Sopenharmony_ci			break;
368e5b75505Sopenharmony_ci		}
369e5b75505Sopenharmony_ci	}
370e5b75505Sopenharmony_ci
371e5b75505Sopenharmony_ci	if (res != -1 && chan_seg1 > -1) {
372e5b75505Sopenharmony_ci		int found = 0;
373e5b75505Sopenharmony_ci
374e5b75505Sopenharmony_ci		/* Get idx for seg1 */
375e5b75505Sopenharmony_ci		mode = iface->current_mode;
376e5b75505Sopenharmony_ci		for (i = 0; i < mode->num_channels; i++) {
377e5b75505Sopenharmony_ci			chan = &mode->channels[i];
378e5b75505Sopenharmony_ci			if (chan->chan == chan_seg1) {
379e5b75505Sopenharmony_ci				*seg1_start = i;
380e5b75505Sopenharmony_ci				found = 1;
381e5b75505Sopenharmony_ci				break;
382e5b75505Sopenharmony_ci			}
383e5b75505Sopenharmony_ci		}
384e5b75505Sopenharmony_ci		if (!found)
385e5b75505Sopenharmony_ci			res = -1;
386e5b75505Sopenharmony_ci	}
387e5b75505Sopenharmony_ci
388e5b75505Sopenharmony_ci	if (res == -1) {
389e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
390e5b75505Sopenharmony_ci			   "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
391e5b75505Sopenharmony_ci			   mode->num_channels, channel_no, iface->conf->channel,
392e5b75505Sopenharmony_ci			   iface->conf->ieee80211n,
393e5b75505Sopenharmony_ci			   iface->conf->secondary_channel,
394e5b75505Sopenharmony_ci			   hostapd_get_oper_chwidth(iface->conf));
395e5b75505Sopenharmony_ci
396e5b75505Sopenharmony_ci		for (i = 0; i < mode->num_channels; i++) {
397e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "Available channel: %d",
398e5b75505Sopenharmony_ci				   mode->channels[i].chan);
399e5b75505Sopenharmony_ci		}
400e5b75505Sopenharmony_ci	}
401e5b75505Sopenharmony_ci
402e5b75505Sopenharmony_ci	return res;
403e5b75505Sopenharmony_ci}
404e5b75505Sopenharmony_ci
405e5b75505Sopenharmony_ci
406e5b75505Sopenharmony_ci/* At least one channel have radar flag */
407e5b75505Sopenharmony_cistatic int dfs_check_chans_radar(struct hostapd_iface *iface,
408e5b75505Sopenharmony_ci				 int start_chan_idx, int n_chans)
409e5b75505Sopenharmony_ci{
410e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
411e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
412e5b75505Sopenharmony_ci	int i, res = 0;
413e5b75505Sopenharmony_ci
414e5b75505Sopenharmony_ci	mode = iface->current_mode;
415e5b75505Sopenharmony_ci
416e5b75505Sopenharmony_ci	for (i = 0; i < n_chans; i++) {
417e5b75505Sopenharmony_ci		channel = &mode->channels[start_chan_idx + i];
418e5b75505Sopenharmony_ci		if (channel->flag & HOSTAPD_CHAN_RADAR)
419e5b75505Sopenharmony_ci			res++;
420e5b75505Sopenharmony_ci	}
421e5b75505Sopenharmony_ci
422e5b75505Sopenharmony_ci	return res;
423e5b75505Sopenharmony_ci}
424e5b75505Sopenharmony_ci
425e5b75505Sopenharmony_ci
426e5b75505Sopenharmony_ci/* All channels available */
427e5b75505Sopenharmony_cistatic int dfs_check_chans_available(struct hostapd_iface *iface,
428e5b75505Sopenharmony_ci				     int start_chan_idx, int n_chans)
429e5b75505Sopenharmony_ci{
430e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
431e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
432e5b75505Sopenharmony_ci	int i;
433e5b75505Sopenharmony_ci
434e5b75505Sopenharmony_ci	mode = iface->current_mode;
435e5b75505Sopenharmony_ci
436e5b75505Sopenharmony_ci	for (i = 0; i < n_chans; i++) {
437e5b75505Sopenharmony_ci		channel = &mode->channels[start_chan_idx + i];
438e5b75505Sopenharmony_ci
439e5b75505Sopenharmony_ci		if (channel->flag & HOSTAPD_CHAN_DISABLED)
440e5b75505Sopenharmony_ci			break;
441e5b75505Sopenharmony_ci
442e5b75505Sopenharmony_ci		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
443e5b75505Sopenharmony_ci			continue;
444e5b75505Sopenharmony_ci
445e5b75505Sopenharmony_ci		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
446e5b75505Sopenharmony_ci		    HOSTAPD_CHAN_DFS_AVAILABLE)
447e5b75505Sopenharmony_ci			break;
448e5b75505Sopenharmony_ci	}
449e5b75505Sopenharmony_ci
450e5b75505Sopenharmony_ci	return i == n_chans;
451e5b75505Sopenharmony_ci}
452e5b75505Sopenharmony_ci
453e5b75505Sopenharmony_ci
454e5b75505Sopenharmony_ci/* At least one channel unavailable */
455e5b75505Sopenharmony_cistatic int dfs_check_chans_unavailable(struct hostapd_iface *iface,
456e5b75505Sopenharmony_ci				       int start_chan_idx,
457e5b75505Sopenharmony_ci				       int n_chans)
458e5b75505Sopenharmony_ci{
459e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
460e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
461e5b75505Sopenharmony_ci	int i, res = 0;
462e5b75505Sopenharmony_ci
463e5b75505Sopenharmony_ci	mode = iface->current_mode;
464e5b75505Sopenharmony_ci
465e5b75505Sopenharmony_ci	for (i = 0; i < n_chans; i++) {
466e5b75505Sopenharmony_ci		channel = &mode->channels[start_chan_idx + i];
467e5b75505Sopenharmony_ci		if (channel->flag & HOSTAPD_CHAN_DISABLED)
468e5b75505Sopenharmony_ci			res++;
469e5b75505Sopenharmony_ci		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
470e5b75505Sopenharmony_ci		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
471e5b75505Sopenharmony_ci			res++;
472e5b75505Sopenharmony_ci	}
473e5b75505Sopenharmony_ci
474e5b75505Sopenharmony_ci	return res;
475e5b75505Sopenharmony_ci}
476e5b75505Sopenharmony_ci
477e5b75505Sopenharmony_ci
478e5b75505Sopenharmony_cistatic struct hostapd_channel_data *
479e5b75505Sopenharmony_cidfs_get_valid_channel(struct hostapd_iface *iface,
480e5b75505Sopenharmony_ci		      int *secondary_channel,
481e5b75505Sopenharmony_ci		      u8 *oper_centr_freq_seg0_idx,
482e5b75505Sopenharmony_ci		      u8 *oper_centr_freq_seg1_idx,
483e5b75505Sopenharmony_ci		      int skip_radar)
484e5b75505Sopenharmony_ci{
485e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
486e5b75505Sopenharmony_ci	struct hostapd_channel_data *chan = NULL;
487e5b75505Sopenharmony_ci	struct hostapd_channel_data *chan2 = NULL;
488e5b75505Sopenharmony_ci	int num_available_chandefs;
489e5b75505Sopenharmony_ci	int chan_idx, chan_idx2;
490e5b75505Sopenharmony_ci	int sec_chan_idx_80p80 = -1;
491e5b75505Sopenharmony_ci	int i;
492e5b75505Sopenharmony_ci	u32 _rand;
493e5b75505Sopenharmony_ci
494e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
495e5b75505Sopenharmony_ci	*secondary_channel = 0;
496e5b75505Sopenharmony_ci	*oper_centr_freq_seg0_idx = 0;
497e5b75505Sopenharmony_ci	*oper_centr_freq_seg1_idx = 0;
498e5b75505Sopenharmony_ci
499e5b75505Sopenharmony_ci	if (iface->current_mode == NULL)
500e5b75505Sopenharmony_ci		return NULL;
501e5b75505Sopenharmony_ci
502e5b75505Sopenharmony_ci	mode = iface->current_mode;
503e5b75505Sopenharmony_ci	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
504e5b75505Sopenharmony_ci		return NULL;
505e5b75505Sopenharmony_ci
506e5b75505Sopenharmony_ci	/* Get the count first */
507e5b75505Sopenharmony_ci	num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
508e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS: num_available_chandefs=%d",
509e5b75505Sopenharmony_ci		   num_available_chandefs);
510e5b75505Sopenharmony_ci	if (num_available_chandefs == 0)
511e5b75505Sopenharmony_ci		return NULL;
512e5b75505Sopenharmony_ci
513e5b75505Sopenharmony_ci	if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
514e5b75505Sopenharmony_ci		return NULL;
515e5b75505Sopenharmony_ci	chan_idx = _rand % num_available_chandefs;
516e5b75505Sopenharmony_ci	dfs_find_channel(iface, &chan, chan_idx, skip_radar);
517e5b75505Sopenharmony_ci	if (!chan) {
518e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "DFS: no random channel found");
519e5b75505Sopenharmony_ci		return NULL;
520e5b75505Sopenharmony_ci	}
521e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS: got random channel %d (%d)",
522e5b75505Sopenharmony_ci		   chan->freq, chan->chan);
523e5b75505Sopenharmony_ci
524e5b75505Sopenharmony_ci	/* dfs_find_channel() calculations assume HT40+ */
525e5b75505Sopenharmony_ci	if (iface->conf->secondary_channel)
526e5b75505Sopenharmony_ci		*secondary_channel = 1;
527e5b75505Sopenharmony_ci	else
528e5b75505Sopenharmony_ci		*secondary_channel = 0;
529e5b75505Sopenharmony_ci
530e5b75505Sopenharmony_ci	/* Get secondary channel for HT80P80 */
531e5b75505Sopenharmony_ci	if (hostapd_get_oper_chwidth(iface->conf) == CHANWIDTH_80P80MHZ) {
532e5b75505Sopenharmony_ci		if (num_available_chandefs <= 1) {
533e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR,
534e5b75505Sopenharmony_ci				   "only 1 valid chan, can't support 80+80");
535e5b75505Sopenharmony_ci			return NULL;
536e5b75505Sopenharmony_ci		}
537e5b75505Sopenharmony_ci
538e5b75505Sopenharmony_ci		/*
539e5b75505Sopenharmony_ci		 * Loop all channels except channel1 to find a valid channel2
540e5b75505Sopenharmony_ci		 * that is not adjacent to channel1.
541e5b75505Sopenharmony_ci		 */
542e5b75505Sopenharmony_ci		for (i = 0; i < num_available_chandefs - 1; i++) {
543e5b75505Sopenharmony_ci			/* start from chan_idx + 1, end when chan_idx - 1 */
544e5b75505Sopenharmony_ci			chan_idx2 = (chan_idx + 1 + i) % num_available_chandefs;
545e5b75505Sopenharmony_ci			dfs_find_channel(iface, &chan2, chan_idx2, skip_radar);
546e5b75505Sopenharmony_ci			if (chan2 && abs(chan2->chan - chan->chan) > 12) {
547e5b75505Sopenharmony_ci				/* two channels are not adjacent */
548e5b75505Sopenharmony_ci				sec_chan_idx_80p80 = chan2->chan;
549e5b75505Sopenharmony_ci				wpa_printf(MSG_DEBUG,
550e5b75505Sopenharmony_ci					   "DFS: got second chan: %d (%d)",
551e5b75505Sopenharmony_ci					   chan2->freq, chan2->chan);
552e5b75505Sopenharmony_ci				break;
553e5b75505Sopenharmony_ci			}
554e5b75505Sopenharmony_ci		}
555e5b75505Sopenharmony_ci
556e5b75505Sopenharmony_ci		/* Check if we got a valid secondary channel which is not
557e5b75505Sopenharmony_ci		 * adjacent to the first channel.
558e5b75505Sopenharmony_ci		 */
559e5b75505Sopenharmony_ci		if (sec_chan_idx_80p80 == -1) {
560e5b75505Sopenharmony_ci			wpa_printf(MSG_INFO,
561e5b75505Sopenharmony_ci				   "DFS: failed to get chan2 for 80+80");
562e5b75505Sopenharmony_ci			return NULL;
563e5b75505Sopenharmony_ci		}
564e5b75505Sopenharmony_ci	}
565e5b75505Sopenharmony_ci
566e5b75505Sopenharmony_ci	dfs_adjust_center_freq(iface, chan,
567e5b75505Sopenharmony_ci			       *secondary_channel,
568e5b75505Sopenharmony_ci			       sec_chan_idx_80p80,
569e5b75505Sopenharmony_ci			       oper_centr_freq_seg0_idx,
570e5b75505Sopenharmony_ci			       oper_centr_freq_seg1_idx);
571e5b75505Sopenharmony_ci
572e5b75505Sopenharmony_ci	return chan;
573e5b75505Sopenharmony_ci}
574e5b75505Sopenharmony_ci
575e5b75505Sopenharmony_ci
576e5b75505Sopenharmony_cistatic int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
577e5b75505Sopenharmony_ci{
578e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
579e5b75505Sopenharmony_ci	struct hostapd_channel_data *chan = NULL;
580e5b75505Sopenharmony_ci	int i;
581e5b75505Sopenharmony_ci
582e5b75505Sopenharmony_ci	mode = iface->current_mode;
583e5b75505Sopenharmony_ci	if (mode == NULL)
584e5b75505Sopenharmony_ci		return 0;
585e5b75505Sopenharmony_ci
586e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
587e5b75505Sopenharmony_ci	for (i = 0; i < iface->current_mode->num_channels; i++) {
588e5b75505Sopenharmony_ci		chan = &iface->current_mode->channels[i];
589e5b75505Sopenharmony_ci		if (chan->freq == freq) {
590e5b75505Sopenharmony_ci			if (chan->flag & HOSTAPD_CHAN_RADAR) {
591e5b75505Sopenharmony_ci				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
592e5b75505Sopenharmony_ci				chan->flag |= state;
593e5b75505Sopenharmony_ci				return 1; /* Channel found */
594e5b75505Sopenharmony_ci			}
595e5b75505Sopenharmony_ci		}
596e5b75505Sopenharmony_ci	}
597e5b75505Sopenharmony_ci	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
598e5b75505Sopenharmony_ci	return 0;
599e5b75505Sopenharmony_ci}
600e5b75505Sopenharmony_ci
601e5b75505Sopenharmony_ci
602e5b75505Sopenharmony_cistatic int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
603e5b75505Sopenharmony_ci			 int chan_offset, int chan_width, int cf1,
604e5b75505Sopenharmony_ci			 int cf2, u32 state)
605e5b75505Sopenharmony_ci{
606e5b75505Sopenharmony_ci	int n_chans = 1, i;
607e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
608e5b75505Sopenharmony_ci	int frequency = freq;
609e5b75505Sopenharmony_ci	int frequency2 = 0;
610e5b75505Sopenharmony_ci	int ret = 0;
611e5b75505Sopenharmony_ci
612e5b75505Sopenharmony_ci	mode = iface->current_mode;
613e5b75505Sopenharmony_ci	if (mode == NULL)
614e5b75505Sopenharmony_ci		return 0;
615e5b75505Sopenharmony_ci
616e5b75505Sopenharmony_ci	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
617e5b75505Sopenharmony_ci		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
618e5b75505Sopenharmony_ci		return 0;
619e5b75505Sopenharmony_ci	}
620e5b75505Sopenharmony_ci
621e5b75505Sopenharmony_ci	/* Seems cf1 and chan_width is enough here */
622e5b75505Sopenharmony_ci	switch (chan_width) {
623e5b75505Sopenharmony_ci	case CHAN_WIDTH_20_NOHT:
624e5b75505Sopenharmony_ci	case CHAN_WIDTH_20:
625e5b75505Sopenharmony_ci		n_chans = 1;
626e5b75505Sopenharmony_ci		if (frequency == 0)
627e5b75505Sopenharmony_ci			frequency = cf1;
628e5b75505Sopenharmony_ci		break;
629e5b75505Sopenharmony_ci	case CHAN_WIDTH_40:
630e5b75505Sopenharmony_ci		n_chans = 2;
631e5b75505Sopenharmony_ci		frequency = cf1 - 10;
632e5b75505Sopenharmony_ci		break;
633e5b75505Sopenharmony_ci	case CHAN_WIDTH_80:
634e5b75505Sopenharmony_ci		n_chans = 4;
635e5b75505Sopenharmony_ci		frequency = cf1 - 30;
636e5b75505Sopenharmony_ci		break;
637e5b75505Sopenharmony_ci	case CHAN_WIDTH_80P80:
638e5b75505Sopenharmony_ci		n_chans = 4;
639e5b75505Sopenharmony_ci		frequency = cf1 - 30;
640e5b75505Sopenharmony_ci		frequency2 = cf2 - 30;
641e5b75505Sopenharmony_ci		break;
642e5b75505Sopenharmony_ci	case CHAN_WIDTH_160:
643e5b75505Sopenharmony_ci		n_chans = 8;
644e5b75505Sopenharmony_ci		frequency = cf1 - 70;
645e5b75505Sopenharmony_ci		break;
646e5b75505Sopenharmony_ci	default:
647e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
648e5b75505Sopenharmony_ci			   chan_width);
649e5b75505Sopenharmony_ci		break;
650e5b75505Sopenharmony_ci	}
651e5b75505Sopenharmony_ci
652e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
653e5b75505Sopenharmony_ci		   n_chans);
654e5b75505Sopenharmony_ci	for (i = 0; i < n_chans; i++) {
655e5b75505Sopenharmony_ci		ret += set_dfs_state_freq(iface, frequency, state);
656e5b75505Sopenharmony_ci		frequency = frequency + 20;
657e5b75505Sopenharmony_ci
658e5b75505Sopenharmony_ci		if (chan_width == CHAN_WIDTH_80P80) {
659e5b75505Sopenharmony_ci			ret += set_dfs_state_freq(iface, frequency2, state);
660e5b75505Sopenharmony_ci			frequency2 = frequency2 + 20;
661e5b75505Sopenharmony_ci		}
662e5b75505Sopenharmony_ci	}
663e5b75505Sopenharmony_ci
664e5b75505Sopenharmony_ci	return ret;
665e5b75505Sopenharmony_ci}
666e5b75505Sopenharmony_ci
667e5b75505Sopenharmony_ci
668e5b75505Sopenharmony_cistatic int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
669e5b75505Sopenharmony_ci				       int chan_width, int cf1, int cf2)
670e5b75505Sopenharmony_ci{
671e5b75505Sopenharmony_ci	int start_chan_idx, start_chan_idx1;
672e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
673e5b75505Sopenharmony_ci	struct hostapd_channel_data *chan;
674e5b75505Sopenharmony_ci	int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1;
675e5b75505Sopenharmony_ci	u8 radar_chan;
676e5b75505Sopenharmony_ci	int res = 0;
677e5b75505Sopenharmony_ci
678e5b75505Sopenharmony_ci	/* Our configuration */
679e5b75505Sopenharmony_ci	mode = iface->current_mode;
680e5b75505Sopenharmony_ci	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
681e5b75505Sopenharmony_ci	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
682e5b75505Sopenharmony_ci
683e5b75505Sopenharmony_ci	/* Check we are on DFS channel(s) */
684e5b75505Sopenharmony_ci	if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
685e5b75505Sopenharmony_ci		return 0;
686e5b75505Sopenharmony_ci
687e5b75505Sopenharmony_ci	/* Reported via radar event */
688e5b75505Sopenharmony_ci	switch (chan_width) {
689e5b75505Sopenharmony_ci	case CHAN_WIDTH_20_NOHT:
690e5b75505Sopenharmony_ci	case CHAN_WIDTH_20:
691e5b75505Sopenharmony_ci		radar_n_chans = 1;
692e5b75505Sopenharmony_ci		if (frequency == 0)
693e5b75505Sopenharmony_ci			frequency = cf1;
694e5b75505Sopenharmony_ci		break;
695e5b75505Sopenharmony_ci	case CHAN_WIDTH_40:
696e5b75505Sopenharmony_ci		radar_n_chans = 2;
697e5b75505Sopenharmony_ci		frequency = cf1 - 10;
698e5b75505Sopenharmony_ci		break;
699e5b75505Sopenharmony_ci	case CHAN_WIDTH_80:
700e5b75505Sopenharmony_ci		radar_n_chans = 4;
701e5b75505Sopenharmony_ci		frequency = cf1 - 30;
702e5b75505Sopenharmony_ci		break;
703e5b75505Sopenharmony_ci	case CHAN_WIDTH_160:
704e5b75505Sopenharmony_ci		radar_n_chans = 8;
705e5b75505Sopenharmony_ci		frequency = cf1 - 70;
706e5b75505Sopenharmony_ci		break;
707e5b75505Sopenharmony_ci	default:
708e5b75505Sopenharmony_ci		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
709e5b75505Sopenharmony_ci			   chan_width);
710e5b75505Sopenharmony_ci		break;
711e5b75505Sopenharmony_ci	}
712e5b75505Sopenharmony_ci
713e5b75505Sopenharmony_ci	ieee80211_freq_to_chan(frequency, &radar_chan);
714e5b75505Sopenharmony_ci
715e5b75505Sopenharmony_ci	for (i = 0; i < n_chans; i++) {
716e5b75505Sopenharmony_ci		chan = &mode->channels[start_chan_idx + i];
717e5b75505Sopenharmony_ci		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
718e5b75505Sopenharmony_ci			continue;
719e5b75505Sopenharmony_ci		for (j = 0; j < radar_n_chans; j++) {
720e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
721e5b75505Sopenharmony_ci				   chan->chan, radar_chan + j * 4);
722e5b75505Sopenharmony_ci			if (chan->chan == radar_chan + j * 4)
723e5b75505Sopenharmony_ci				res++;
724e5b75505Sopenharmony_ci		}
725e5b75505Sopenharmony_ci	}
726e5b75505Sopenharmony_ci
727e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
728e5b75505Sopenharmony_ci
729e5b75505Sopenharmony_ci	return res;
730e5b75505Sopenharmony_ci}
731e5b75505Sopenharmony_ci
732e5b75505Sopenharmony_ci
733e5b75505Sopenharmony_cistatic unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
734e5b75505Sopenharmony_ci				     int start_chan_idx, int n_chans)
735e5b75505Sopenharmony_ci{
736e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
737e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode;
738e5b75505Sopenharmony_ci	int i;
739e5b75505Sopenharmony_ci	unsigned int cac_time_ms = 0;
740e5b75505Sopenharmony_ci
741e5b75505Sopenharmony_ci	mode = iface->current_mode;
742e5b75505Sopenharmony_ci
743e5b75505Sopenharmony_ci	for (i = 0; i < n_chans; i++) {
744e5b75505Sopenharmony_ci		channel = &mode->channels[start_chan_idx + i];
745e5b75505Sopenharmony_ci		if (!(channel->flag & HOSTAPD_CHAN_RADAR))
746e5b75505Sopenharmony_ci			continue;
747e5b75505Sopenharmony_ci		if (channel->dfs_cac_ms > cac_time_ms)
748e5b75505Sopenharmony_ci			cac_time_ms = channel->dfs_cac_ms;
749e5b75505Sopenharmony_ci	}
750e5b75505Sopenharmony_ci
751e5b75505Sopenharmony_ci	return cac_time_ms;
752e5b75505Sopenharmony_ci}
753e5b75505Sopenharmony_ci
754e5b75505Sopenharmony_ci
755e5b75505Sopenharmony_ci/*
756e5b75505Sopenharmony_ci * Main DFS handler
757e5b75505Sopenharmony_ci * 1 - continue channel/ap setup
758e5b75505Sopenharmony_ci * 0 - channel/ap setup will be continued after CAC
759e5b75505Sopenharmony_ci * -1 - hit critical error
760e5b75505Sopenharmony_ci */
761e5b75505Sopenharmony_ciint hostapd_handle_dfs(struct hostapd_iface *iface)
762e5b75505Sopenharmony_ci{
763e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
764e5b75505Sopenharmony_ci	int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
765e5b75505Sopenharmony_ci	int skip_radar = 0;
766e5b75505Sopenharmony_ci
767e5b75505Sopenharmony_ci	if (is_6ghz_freq(iface->freq))
768e5b75505Sopenharmony_ci		return 1;
769e5b75505Sopenharmony_ci
770e5b75505Sopenharmony_ci	if (!iface->current_mode) {
771e5b75505Sopenharmony_ci		/*
772e5b75505Sopenharmony_ci		 * This can happen with drivers that do not provide mode
773e5b75505Sopenharmony_ci		 * information and as such, cannot really use hostapd for DFS.
774e5b75505Sopenharmony_ci		 */
775e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
776e5b75505Sopenharmony_ci			   "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
777e5b75505Sopenharmony_ci		return 1;
778e5b75505Sopenharmony_ci	}
779e5b75505Sopenharmony_ci
780e5b75505Sopenharmony_ci	iface->cac_started = 0;
781e5b75505Sopenharmony_ci
782e5b75505Sopenharmony_ci	do {
783e5b75505Sopenharmony_ci		/* Get start (first) channel for current configuration */
784e5b75505Sopenharmony_ci		start_chan_idx = dfs_get_start_chan_idx(iface,
785e5b75505Sopenharmony_ci							&start_chan_idx1);
786e5b75505Sopenharmony_ci		if (start_chan_idx == -1)
787e5b75505Sopenharmony_ci			return -1;
788e5b75505Sopenharmony_ci
789e5b75505Sopenharmony_ci		/* Get number of used channels, depend on width */
790e5b75505Sopenharmony_ci		n_chans = dfs_get_used_n_chans(iface, &n_chans1);
791e5b75505Sopenharmony_ci
792e5b75505Sopenharmony_ci		/* Setup CAC time */
793e5b75505Sopenharmony_ci		iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
794e5b75505Sopenharmony_ci						     n_chans);
795e5b75505Sopenharmony_ci
796e5b75505Sopenharmony_ci		/* Check if any of configured channels require DFS */
797e5b75505Sopenharmony_ci		res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
798e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
799e5b75505Sopenharmony_ci			   "DFS %d channels required radar detection",
800e5b75505Sopenharmony_ci			   res);
801e5b75505Sopenharmony_ci		if (!res)
802e5b75505Sopenharmony_ci			return 1;
803e5b75505Sopenharmony_ci
804e5b75505Sopenharmony_ci		/* Check if all channels are DFS available */
805e5b75505Sopenharmony_ci		res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
806e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
807e5b75505Sopenharmony_ci			   "DFS all channels available, (SKIP CAC): %s",
808e5b75505Sopenharmony_ci			   res ? "yes" : "no");
809e5b75505Sopenharmony_ci		if (res)
810e5b75505Sopenharmony_ci			return 1;
811e5b75505Sopenharmony_ci
812e5b75505Sopenharmony_ci		/* Check if any of configured channels is unavailable */
813e5b75505Sopenharmony_ci		res = dfs_check_chans_unavailable(iface, start_chan_idx,
814e5b75505Sopenharmony_ci						  n_chans);
815e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
816e5b75505Sopenharmony_ci			   res, res ? "yes": "no");
817e5b75505Sopenharmony_ci		if (res) {
818e5b75505Sopenharmony_ci			int sec = 0;
819e5b75505Sopenharmony_ci			u8 cf1 = 0, cf2 = 0;
820e5b75505Sopenharmony_ci
821e5b75505Sopenharmony_ci			channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
822e5b75505Sopenharmony_ci							skip_radar);
823e5b75505Sopenharmony_ci			if (!channel) {
824e5b75505Sopenharmony_ci				wpa_printf(MSG_ERROR, "could not get valid channel");
825e5b75505Sopenharmony_ci				hostapd_set_state(iface, HAPD_IFACE_DFS);
826e5b75505Sopenharmony_ci				return 0;
827e5b75505Sopenharmony_ci			}
828e5b75505Sopenharmony_ci
829e5b75505Sopenharmony_ci			iface->freq = channel->freq;
830e5b75505Sopenharmony_ci			iface->conf->channel = channel->chan;
831e5b75505Sopenharmony_ci			iface->conf->secondary_channel = sec;
832e5b75505Sopenharmony_ci			hostapd_set_oper_centr_freq_seg0_idx(iface->conf, cf1);
833e5b75505Sopenharmony_ci			hostapd_set_oper_centr_freq_seg1_idx(iface->conf, cf2);
834e5b75505Sopenharmony_ci		}
835e5b75505Sopenharmony_ci	} while (res);
836e5b75505Sopenharmony_ci
837e5b75505Sopenharmony_ci	/* Finally start CAC */
838e5b75505Sopenharmony_ci	hostapd_set_state(iface, HAPD_IFACE_DFS);
839e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
840e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
841e5b75505Sopenharmony_ci		"freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
842e5b75505Sopenharmony_ci		iface->freq,
843e5b75505Sopenharmony_ci		iface->conf->channel, iface->conf->secondary_channel,
844e5b75505Sopenharmony_ci		hostapd_get_oper_chwidth(iface->conf),
845e5b75505Sopenharmony_ci		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
846e5b75505Sopenharmony_ci		hostapd_get_oper_centr_freq_seg1_idx(iface->conf),
847e5b75505Sopenharmony_ci		iface->dfs_cac_ms / 1000);
848e5b75505Sopenharmony_ci
849e5b75505Sopenharmony_ci	res = hostapd_start_dfs_cac(
850e5b75505Sopenharmony_ci		iface, iface->conf->hw_mode, iface->freq, iface->conf->channel,
851e5b75505Sopenharmony_ci		iface->conf->ieee80211n, iface->conf->ieee80211ac,
852e5b75505Sopenharmony_ci		iface->conf->ieee80211ax,
853e5b75505Sopenharmony_ci		iface->conf->secondary_channel,
854e5b75505Sopenharmony_ci		hostapd_get_oper_chwidth(iface->conf),
855e5b75505Sopenharmony_ci		hostapd_get_oper_centr_freq_seg0_idx(iface->conf),
856e5b75505Sopenharmony_ci		hostapd_get_oper_centr_freq_seg1_idx(iface->conf));
857e5b75505Sopenharmony_ci
858e5b75505Sopenharmony_ci	if (res) {
859e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
860e5b75505Sopenharmony_ci		return -1;
861e5b75505Sopenharmony_ci	}
862e5b75505Sopenharmony_ci
863e5b75505Sopenharmony_ci	return 0;
864e5b75505Sopenharmony_ci}
865e5b75505Sopenharmony_ci
866e5b75505Sopenharmony_ci
867e5b75505Sopenharmony_ciint hostapd_is_dfs_chan_available(struct hostapd_iface *iface)
868e5b75505Sopenharmony_ci{
869e5b75505Sopenharmony_ci	int n_chans, n_chans1, start_chan_idx, start_chan_idx1;
870e5b75505Sopenharmony_ci
871e5b75505Sopenharmony_ci	/* Get the start (first) channel for current configuration */
872e5b75505Sopenharmony_ci	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
873e5b75505Sopenharmony_ci	if (start_chan_idx < 0)
874e5b75505Sopenharmony_ci		return 0;
875e5b75505Sopenharmony_ci
876e5b75505Sopenharmony_ci	/* Get the number of used channels, depending on width */
877e5b75505Sopenharmony_ci	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
878e5b75505Sopenharmony_ci
879e5b75505Sopenharmony_ci	/* Check if all channels are DFS available */
880e5b75505Sopenharmony_ci	return dfs_check_chans_available(iface, start_chan_idx, n_chans);
881e5b75505Sopenharmony_ci}
882e5b75505Sopenharmony_ci
883e5b75505Sopenharmony_ci
884e5b75505Sopenharmony_ciint hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
885e5b75505Sopenharmony_ci			     int ht_enabled, int chan_offset, int chan_width,
886e5b75505Sopenharmony_ci			     int cf1, int cf2)
887e5b75505Sopenharmony_ci{
888e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
889e5b75505Sopenharmony_ci		"success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
890e5b75505Sopenharmony_ci		success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
891e5b75505Sopenharmony_ci
892e5b75505Sopenharmony_ci	if (success) {
893e5b75505Sopenharmony_ci		/* Complete iface/ap configuration */
894e5b75505Sopenharmony_ci		if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
895e5b75505Sopenharmony_ci			/* Complete AP configuration for the first bring up. */
896e5b75505Sopenharmony_ci			if (iface->state != HAPD_IFACE_ENABLED)
897e5b75505Sopenharmony_ci				hostapd_setup_interface_complete(iface, 0);
898e5b75505Sopenharmony_ci			else
899e5b75505Sopenharmony_ci				iface->cac_started = 0;
900e5b75505Sopenharmony_ci		} else {
901e5b75505Sopenharmony_ci			set_dfs_state(iface, freq, ht_enabled, chan_offset,
902e5b75505Sopenharmony_ci				      chan_width, cf1, cf2,
903e5b75505Sopenharmony_ci				      HOSTAPD_CHAN_DFS_AVAILABLE);
904e5b75505Sopenharmony_ci			/*
905e5b75505Sopenharmony_ci			 * Just mark the channel available when CAC completion
906e5b75505Sopenharmony_ci			 * event is received in enabled state. CAC result could
907e5b75505Sopenharmony_ci			 * have been propagated from another radio having the
908e5b75505Sopenharmony_ci			 * same regulatory configuration. When CAC completion is
909e5b75505Sopenharmony_ci			 * received during non-HAPD_IFACE_ENABLED state, make
910e5b75505Sopenharmony_ci			 * sure the configured channel is available because this
911e5b75505Sopenharmony_ci			 * CAC completion event could have been propagated from
912e5b75505Sopenharmony_ci			 * another radio.
913e5b75505Sopenharmony_ci			 */
914e5b75505Sopenharmony_ci			if (iface->state != HAPD_IFACE_ENABLED &&
915e5b75505Sopenharmony_ci			    hostapd_is_dfs_chan_available(iface)) {
916e5b75505Sopenharmony_ci				hostapd_setup_interface_complete(iface, 0);
917e5b75505Sopenharmony_ci				iface->cac_started = 0;
918e5b75505Sopenharmony_ci			}
919e5b75505Sopenharmony_ci		}
920e5b75505Sopenharmony_ci	}
921e5b75505Sopenharmony_ci
922e5b75505Sopenharmony_ci	return 0;
923e5b75505Sopenharmony_ci}
924e5b75505Sopenharmony_ci
925e5b75505Sopenharmony_ci
926e5b75505Sopenharmony_ciint hostapd_dfs_pre_cac_expired(struct hostapd_iface *iface, int freq,
927e5b75505Sopenharmony_ci				int ht_enabled, int chan_offset, int chan_width,
928e5b75505Sopenharmony_ci				int cf1, int cf2)
929e5b75505Sopenharmony_ci{
930e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_PRE_CAC_EXPIRED
931e5b75505Sopenharmony_ci		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
932e5b75505Sopenharmony_ci		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
933e5b75505Sopenharmony_ci
934e5b75505Sopenharmony_ci	/* Proceed only if DFS is not offloaded to the driver */
935e5b75505Sopenharmony_ci	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
936e5b75505Sopenharmony_ci		return 0;
937e5b75505Sopenharmony_ci
938e5b75505Sopenharmony_ci	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
939e5b75505Sopenharmony_ci		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
940e5b75505Sopenharmony_ci
941e5b75505Sopenharmony_ci	return 0;
942e5b75505Sopenharmony_ci}
943e5b75505Sopenharmony_ci
944e5b75505Sopenharmony_ci
945e5b75505Sopenharmony_cistatic struct hostapd_channel_data *
946e5b75505Sopenharmony_cidfs_downgrade_bandwidth(struct hostapd_iface *iface, int *secondary_channel,
947e5b75505Sopenharmony_ci			u8 *oper_centr_freq_seg0_idx,
948e5b75505Sopenharmony_ci			u8 *oper_centr_freq_seg1_idx, int *skip_radar)
949e5b75505Sopenharmony_ci{
950e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
951e5b75505Sopenharmony_ci
952e5b75505Sopenharmony_ci	for (;;) {
953e5b75505Sopenharmony_ci		channel = dfs_get_valid_channel(iface, secondary_channel,
954e5b75505Sopenharmony_ci						oper_centr_freq_seg0_idx,
955e5b75505Sopenharmony_ci						oper_centr_freq_seg1_idx,
956e5b75505Sopenharmony_ci						*skip_radar);
957e5b75505Sopenharmony_ci		if (channel) {
958e5b75505Sopenharmony_ci			wpa_printf(MSG_DEBUG, "DFS: Selected channel: %d",
959e5b75505Sopenharmony_ci				   channel->chan);
960e5b75505Sopenharmony_ci			return channel;
961e5b75505Sopenharmony_ci		}
962e5b75505Sopenharmony_ci
963e5b75505Sopenharmony_ci		if (*skip_radar) {
964e5b75505Sopenharmony_ci			*skip_radar = 0;
965e5b75505Sopenharmony_ci		} else {
966e5b75505Sopenharmony_ci			int oper_chwidth;
967e5b75505Sopenharmony_ci
968e5b75505Sopenharmony_ci			oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
969e5b75505Sopenharmony_ci			if (oper_chwidth == CHANWIDTH_USE_HT)
970e5b75505Sopenharmony_ci				break;
971e5b75505Sopenharmony_ci			*skip_radar = 1;
972e5b75505Sopenharmony_ci			hostapd_set_oper_chwidth(iface->conf, oper_chwidth - 1);
973e5b75505Sopenharmony_ci		}
974e5b75505Sopenharmony_ci	}
975e5b75505Sopenharmony_ci
976e5b75505Sopenharmony_ci	wpa_printf(MSG_INFO,
977e5b75505Sopenharmony_ci		   "%s: no DFS channels left, waiting for NOP to finish",
978e5b75505Sopenharmony_ci		   __func__);
979e5b75505Sopenharmony_ci	return NULL;
980e5b75505Sopenharmony_ci}
981e5b75505Sopenharmony_ci
982e5b75505Sopenharmony_ci
983e5b75505Sopenharmony_cistatic int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
984e5b75505Sopenharmony_ci{
985e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
986e5b75505Sopenharmony_ci	int secondary_channel;
987e5b75505Sopenharmony_ci	u8 oper_centr_freq_seg0_idx = 0;
988e5b75505Sopenharmony_ci	u8 oper_centr_freq_seg1_idx = 0;
989e5b75505Sopenharmony_ci	int skip_radar = 0;
990e5b75505Sopenharmony_ci	int err = 1;
991e5b75505Sopenharmony_ci
992e5b75505Sopenharmony_ci	/* Radar detected during active CAC */
993e5b75505Sopenharmony_ci	iface->cac_started = 0;
994e5b75505Sopenharmony_ci	channel = dfs_get_valid_channel(iface, &secondary_channel,
995e5b75505Sopenharmony_ci					&oper_centr_freq_seg0_idx,
996e5b75505Sopenharmony_ci					&oper_centr_freq_seg1_idx,
997e5b75505Sopenharmony_ci					skip_radar);
998e5b75505Sopenharmony_ci
999e5b75505Sopenharmony_ci	if (!channel) {
1000e5b75505Sopenharmony_ci		channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
1001e5b75505Sopenharmony_ci						  &oper_centr_freq_seg0_idx,
1002e5b75505Sopenharmony_ci						  &oper_centr_freq_seg1_idx,
1003e5b75505Sopenharmony_ci						  &skip_radar);
1004e5b75505Sopenharmony_ci		if (!channel) {
1005e5b75505Sopenharmony_ci			wpa_printf(MSG_ERROR, "No valid channel available");
1006e5b75505Sopenharmony_ci			return err;
1007e5b75505Sopenharmony_ci		}
1008e5b75505Sopenharmony_ci	}
1009e5b75505Sopenharmony_ci
1010e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
1011e5b75505Sopenharmony_ci		   channel->chan);
1012e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
1013e5b75505Sopenharmony_ci		"freq=%d chan=%d sec_chan=%d", channel->freq,
1014e5b75505Sopenharmony_ci		channel->chan, secondary_channel);
1015e5b75505Sopenharmony_ci
1016e5b75505Sopenharmony_ci	iface->freq = channel->freq;
1017e5b75505Sopenharmony_ci	iface->conf->channel = channel->chan;
1018e5b75505Sopenharmony_ci	iface->conf->secondary_channel = secondary_channel;
1019e5b75505Sopenharmony_ci	hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
1020e5b75505Sopenharmony_ci					     oper_centr_freq_seg0_idx);
1021e5b75505Sopenharmony_ci	hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
1022e5b75505Sopenharmony_ci					     oper_centr_freq_seg1_idx);
1023e5b75505Sopenharmony_ci	err = 0;
1024e5b75505Sopenharmony_ci
1025e5b75505Sopenharmony_ci	hostapd_setup_interface_complete(iface, err);
1026e5b75505Sopenharmony_ci	return err;
1027e5b75505Sopenharmony_ci}
1028e5b75505Sopenharmony_ci
1029e5b75505Sopenharmony_ci
1030e5b75505Sopenharmony_cistatic int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
1031e5b75505Sopenharmony_ci{
1032e5b75505Sopenharmony_ci	struct hostapd_channel_data *channel;
1033e5b75505Sopenharmony_ci	int secondary_channel;
1034e5b75505Sopenharmony_ci	u8 oper_centr_freq_seg0_idx;
1035e5b75505Sopenharmony_ci	u8 oper_centr_freq_seg1_idx;
1036e5b75505Sopenharmony_ci	u8 new_vht_oper_chwidth;
1037e5b75505Sopenharmony_ci	int skip_radar = 1;
1038e5b75505Sopenharmony_ci	struct csa_settings csa_settings;
1039e5b75505Sopenharmony_ci	unsigned int i;
1040e5b75505Sopenharmony_ci	int err = 1;
1041e5b75505Sopenharmony_ci	struct hostapd_hw_modes *cmode = iface->current_mode;
1042e5b75505Sopenharmony_ci	u8 current_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
1043e5b75505Sopenharmony_ci	int ieee80211_mode = IEEE80211_MODE_AP;
1044e5b75505Sopenharmony_ci
1045e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
1046e5b75505Sopenharmony_ci		   __func__, iface->cac_started ? "yes" : "no",
1047e5b75505Sopenharmony_ci		   hostapd_csa_in_progress(iface) ? "yes" : "no");
1048e5b75505Sopenharmony_ci
1049e5b75505Sopenharmony_ci	/* Check if CSA in progress */
1050e5b75505Sopenharmony_ci	if (hostapd_csa_in_progress(iface))
1051e5b75505Sopenharmony_ci		return 0;
1052e5b75505Sopenharmony_ci
1053e5b75505Sopenharmony_ci	/* Check if active CAC */
1054e5b75505Sopenharmony_ci	if (iface->cac_started)
1055e5b75505Sopenharmony_ci		return hostapd_dfs_start_channel_switch_cac(iface);
1056e5b75505Sopenharmony_ci
1057e5b75505Sopenharmony_ci	/*
1058e5b75505Sopenharmony_ci	 * Allow selection of DFS channel in ETSI to comply with
1059e5b75505Sopenharmony_ci	 * uniform spreading.
1060e5b75505Sopenharmony_ci	 */
1061e5b75505Sopenharmony_ci	if (iface->dfs_domain == HOSTAPD_DFS_REGION_ETSI)
1062e5b75505Sopenharmony_ci		skip_radar = 0;
1063e5b75505Sopenharmony_ci
1064e5b75505Sopenharmony_ci	/* Perform channel switch/CSA */
1065e5b75505Sopenharmony_ci	channel = dfs_get_valid_channel(iface, &secondary_channel,
1066e5b75505Sopenharmony_ci					&oper_centr_freq_seg0_idx,
1067e5b75505Sopenharmony_ci					&oper_centr_freq_seg1_idx,
1068e5b75505Sopenharmony_ci					skip_radar);
1069e5b75505Sopenharmony_ci
1070e5b75505Sopenharmony_ci	if (!channel) {
1071e5b75505Sopenharmony_ci		/*
1072e5b75505Sopenharmony_ci		 * If there is no channel to switch immediately to, check if
1073e5b75505Sopenharmony_ci		 * there is another channel where we can switch even if it
1074e5b75505Sopenharmony_ci		 * requires to perform a CAC first.
1075e5b75505Sopenharmony_ci		 */
1076e5b75505Sopenharmony_ci		skip_radar = 0;
1077e5b75505Sopenharmony_ci		channel = dfs_downgrade_bandwidth(iface, &secondary_channel,
1078e5b75505Sopenharmony_ci						  &oper_centr_freq_seg0_idx,
1079e5b75505Sopenharmony_ci						  &oper_centr_freq_seg1_idx,
1080e5b75505Sopenharmony_ci						  &skip_radar);
1081e5b75505Sopenharmony_ci		if (!channel) {
1082e5b75505Sopenharmony_ci			/*
1083e5b75505Sopenharmony_ci			 * Toggle interface state to enter DFS state
1084e5b75505Sopenharmony_ci			 * until NOP is finished.
1085e5b75505Sopenharmony_ci			 */
1086e5b75505Sopenharmony_ci			hostapd_disable_iface(iface);
1087e5b75505Sopenharmony_ci			hostapd_enable_iface(iface);
1088e5b75505Sopenharmony_ci			return 0;
1089e5b75505Sopenharmony_ci		}
1090e5b75505Sopenharmony_ci
1091e5b75505Sopenharmony_ci		if (!skip_radar) {
1092e5b75505Sopenharmony_ci			iface->freq = channel->freq;
1093e5b75505Sopenharmony_ci			iface->conf->channel = channel->chan;
1094e5b75505Sopenharmony_ci			iface->conf->secondary_channel = secondary_channel;
1095e5b75505Sopenharmony_ci			hostapd_set_oper_centr_freq_seg0_idx(
1096e5b75505Sopenharmony_ci				iface->conf, oper_centr_freq_seg0_idx);
1097e5b75505Sopenharmony_ci			hostapd_set_oper_centr_freq_seg1_idx(
1098e5b75505Sopenharmony_ci				iface->conf, oper_centr_freq_seg1_idx);
1099e5b75505Sopenharmony_ci
1100e5b75505Sopenharmony_ci			hostapd_disable_iface(iface);
1101e5b75505Sopenharmony_ci			hostapd_enable_iface(iface);
1102e5b75505Sopenharmony_ci			return 0;
1103e5b75505Sopenharmony_ci		}
1104e5b75505Sopenharmony_ci	}
1105e5b75505Sopenharmony_ci
1106e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
1107e5b75505Sopenharmony_ci		   channel->chan);
1108e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
1109e5b75505Sopenharmony_ci		"freq=%d chan=%d sec_chan=%d", channel->freq,
1110e5b75505Sopenharmony_ci		channel->chan, secondary_channel);
1111e5b75505Sopenharmony_ci
1112e5b75505Sopenharmony_ci	new_vht_oper_chwidth = hostapd_get_oper_chwidth(iface->conf);
1113e5b75505Sopenharmony_ci	hostapd_set_oper_chwidth(iface->conf, current_vht_oper_chwidth);
1114e5b75505Sopenharmony_ci
1115e5b75505Sopenharmony_ci	/* Setup CSA request */
1116e5b75505Sopenharmony_ci	os_memset(&csa_settings, 0, sizeof(csa_settings));
1117e5b75505Sopenharmony_ci	csa_settings.cs_count = 5;
1118e5b75505Sopenharmony_ci	csa_settings.block_tx = 1;
1119e5b75505Sopenharmony_ci#ifdef CONFIG_MESH
1120e5b75505Sopenharmony_ci	if (iface->mconf)
1121e5b75505Sopenharmony_ci		ieee80211_mode = IEEE80211_MODE_MESH;
1122e5b75505Sopenharmony_ci#endif /* CONFIG_MESH */
1123e5b75505Sopenharmony_ci	err = hostapd_set_freq_params(&csa_settings.freq_params,
1124e5b75505Sopenharmony_ci				      iface->conf->hw_mode,
1125e5b75505Sopenharmony_ci				      channel->freq,
1126e5b75505Sopenharmony_ci				      channel->chan,
1127e5b75505Sopenharmony_ci				      iface->conf->enable_edmg,
1128e5b75505Sopenharmony_ci				      iface->conf->edmg_channel,
1129e5b75505Sopenharmony_ci				      iface->conf->ieee80211n,
1130e5b75505Sopenharmony_ci				      iface->conf->ieee80211ac,
1131e5b75505Sopenharmony_ci				      iface->conf->ieee80211ax,
1132e5b75505Sopenharmony_ci				      secondary_channel,
1133e5b75505Sopenharmony_ci				      new_vht_oper_chwidth,
1134e5b75505Sopenharmony_ci				      oper_centr_freq_seg0_idx,
1135e5b75505Sopenharmony_ci				      oper_centr_freq_seg1_idx,
1136e5b75505Sopenharmony_ci				      cmode->vht_capab,
1137e5b75505Sopenharmony_ci				      &cmode->he_capab[ieee80211_mode]);
1138e5b75505Sopenharmony_ci
1139e5b75505Sopenharmony_ci	if (err) {
1140e5b75505Sopenharmony_ci		wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
1141e5b75505Sopenharmony_ci		hostapd_disable_iface(iface);
1142e5b75505Sopenharmony_ci		return err;
1143e5b75505Sopenharmony_ci	}
1144e5b75505Sopenharmony_ci
1145e5b75505Sopenharmony_ci	for (i = 0; i < iface->num_bss; i++) {
1146e5b75505Sopenharmony_ci		err = hostapd_switch_channel(iface->bss[i], &csa_settings);
1147e5b75505Sopenharmony_ci		if (err)
1148e5b75505Sopenharmony_ci			break;
1149e5b75505Sopenharmony_ci	}
1150e5b75505Sopenharmony_ci
1151e5b75505Sopenharmony_ci	if (err) {
1152e5b75505Sopenharmony_ci		wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
1153e5b75505Sopenharmony_ci			   err);
1154e5b75505Sopenharmony_ci		iface->freq = channel->freq;
1155e5b75505Sopenharmony_ci		iface->conf->channel = channel->chan;
1156e5b75505Sopenharmony_ci		iface->conf->secondary_channel = secondary_channel;
1157e5b75505Sopenharmony_ci		hostapd_set_oper_chwidth(iface->conf, new_vht_oper_chwidth);
1158e5b75505Sopenharmony_ci		hostapd_set_oper_centr_freq_seg0_idx(iface->conf,
1159e5b75505Sopenharmony_ci						     oper_centr_freq_seg0_idx);
1160e5b75505Sopenharmony_ci		hostapd_set_oper_centr_freq_seg1_idx(iface->conf,
1161e5b75505Sopenharmony_ci						     oper_centr_freq_seg1_idx);
1162e5b75505Sopenharmony_ci
1163e5b75505Sopenharmony_ci		hostapd_disable_iface(iface);
1164e5b75505Sopenharmony_ci		hostapd_enable_iface(iface);
1165e5b75505Sopenharmony_ci		return 0;
1166e5b75505Sopenharmony_ci	}
1167e5b75505Sopenharmony_ci
1168e5b75505Sopenharmony_ci	/* Channel configuration will be updated once CSA completes and
1169e5b75505Sopenharmony_ci	 * ch_switch_notify event is received */
1170e5b75505Sopenharmony_ci
1171e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
1172e5b75505Sopenharmony_ci	return 0;
1173e5b75505Sopenharmony_ci}
1174e5b75505Sopenharmony_ci
1175e5b75505Sopenharmony_ci
1176e5b75505Sopenharmony_ciint hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
1177e5b75505Sopenharmony_ci			       int ht_enabled, int chan_offset, int chan_width,
1178e5b75505Sopenharmony_ci			       int cf1, int cf2)
1179e5b75505Sopenharmony_ci{
1180e5b75505Sopenharmony_ci	int res;
1181e5b75505Sopenharmony_ci
1182e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
1183e5b75505Sopenharmony_ci		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
1184e5b75505Sopenharmony_ci		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
1185e5b75505Sopenharmony_ci
1186e5b75505Sopenharmony_ci	/* Proceed only if DFS is not offloaded to the driver */
1187e5b75505Sopenharmony_ci	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
1188e5b75505Sopenharmony_ci		return 0;
1189e5b75505Sopenharmony_ci
1190e5b75505Sopenharmony_ci	if (!iface->conf->ieee80211h)
1191e5b75505Sopenharmony_ci		return 0;
1192e5b75505Sopenharmony_ci
1193e5b75505Sopenharmony_ci	/* mark radar frequency as invalid */
1194e5b75505Sopenharmony_ci	res = set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
1195e5b75505Sopenharmony_ci			    cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
1196e5b75505Sopenharmony_ci	if (!res)
1197e5b75505Sopenharmony_ci		return 0;
1198e5b75505Sopenharmony_ci
1199e5b75505Sopenharmony_ci	/* Skip if reported radar event not overlapped our channels */
1200e5b75505Sopenharmony_ci	res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
1201e5b75505Sopenharmony_ci	if (!res)
1202e5b75505Sopenharmony_ci		return 0;
1203e5b75505Sopenharmony_ci
1204e5b75505Sopenharmony_ci	/* radar detected while operating, switch the channel. */
1205e5b75505Sopenharmony_ci	res = hostapd_dfs_start_channel_switch(iface);
1206e5b75505Sopenharmony_ci
1207e5b75505Sopenharmony_ci	return res;
1208e5b75505Sopenharmony_ci}
1209e5b75505Sopenharmony_ci
1210e5b75505Sopenharmony_ci
1211e5b75505Sopenharmony_ciint hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
1212e5b75505Sopenharmony_ci			     int ht_enabled, int chan_offset, int chan_width,
1213e5b75505Sopenharmony_ci			     int cf1, int cf2)
1214e5b75505Sopenharmony_ci{
1215e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
1216e5b75505Sopenharmony_ci		"freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
1217e5b75505Sopenharmony_ci		freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
1218e5b75505Sopenharmony_ci
1219e5b75505Sopenharmony_ci	/* Proceed only if DFS is not offloaded to the driver */
1220e5b75505Sopenharmony_ci	if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
1221e5b75505Sopenharmony_ci		return 0;
1222e5b75505Sopenharmony_ci
1223e5b75505Sopenharmony_ci	/* TODO add correct implementation here */
1224e5b75505Sopenharmony_ci	set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
1225e5b75505Sopenharmony_ci		      cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
1226e5b75505Sopenharmony_ci
1227e5b75505Sopenharmony_ci	/* Handle cases where all channels were initially unavailable */
1228e5b75505Sopenharmony_ci	if (iface->state == HAPD_IFACE_DFS && !iface->cac_started)
1229e5b75505Sopenharmony_ci		hostapd_handle_dfs(iface);
1230e5b75505Sopenharmony_ci
1231e5b75505Sopenharmony_ci	return 0;
1232e5b75505Sopenharmony_ci}
1233e5b75505Sopenharmony_ci
1234e5b75505Sopenharmony_ci
1235e5b75505Sopenharmony_ciint hostapd_is_dfs_required(struct hostapd_iface *iface)
1236e5b75505Sopenharmony_ci{
1237e5b75505Sopenharmony_ci	int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
1238e5b75505Sopenharmony_ci
1239e5b75505Sopenharmony_ci	if ((!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
1240e5b75505Sopenharmony_ci	     !iface->conf->ieee80211h) ||
1241e5b75505Sopenharmony_ci	    !iface->current_mode ||
1242e5b75505Sopenharmony_ci	    iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
1243e5b75505Sopenharmony_ci		return 0;
1244e5b75505Sopenharmony_ci
1245e5b75505Sopenharmony_ci	/* Get start (first) channel for current configuration */
1246e5b75505Sopenharmony_ci	start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
1247e5b75505Sopenharmony_ci	if (start_chan_idx == -1)
1248e5b75505Sopenharmony_ci		return -1;
1249e5b75505Sopenharmony_ci
1250e5b75505Sopenharmony_ci	/* Get number of used channels, depend on width */
1251e5b75505Sopenharmony_ci	n_chans = dfs_get_used_n_chans(iface, &n_chans1);
1252e5b75505Sopenharmony_ci
1253e5b75505Sopenharmony_ci	/* Check if any of configured channels require DFS */
1254e5b75505Sopenharmony_ci	res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
1255e5b75505Sopenharmony_ci	if (res)
1256e5b75505Sopenharmony_ci		return res;
1257e5b75505Sopenharmony_ci	if (start_chan_idx1 >= 0 && n_chans1 > 0)
1258e5b75505Sopenharmony_ci		res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
1259e5b75505Sopenharmony_ci	return res;
1260e5b75505Sopenharmony_ci}
1261e5b75505Sopenharmony_ci
1262e5b75505Sopenharmony_ci
1263e5b75505Sopenharmony_ciint hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
1264e5b75505Sopenharmony_ci			  int ht_enabled, int chan_offset, int chan_width,
1265e5b75505Sopenharmony_ci			  int cf1, int cf2)
1266e5b75505Sopenharmony_ci{
1267e5b75505Sopenharmony_ci	/* This is called when the driver indicates that an offloaded DFS has
1268e5b75505Sopenharmony_ci	 * started CAC. */
1269e5b75505Sopenharmony_ci	hostapd_set_state(iface, HAPD_IFACE_DFS);
1270e5b75505Sopenharmony_ci	/* TODO: How to check CAC time for ETSI weather channels? */
1271e5b75505Sopenharmony_ci	iface->dfs_cac_ms = 60000;
1272e5b75505Sopenharmony_ci	wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
1273e5b75505Sopenharmony_ci		"freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
1274e5b75505Sopenharmony_ci		"seg1=%d cac_time=%ds",
1275e5b75505Sopenharmony_ci		freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2,
1276e5b75505Sopenharmony_ci		iface->dfs_cac_ms / 1000);
1277e5b75505Sopenharmony_ci	iface->cac_started = 1;
1278e5b75505Sopenharmony_ci	os_get_reltime(&iface->dfs_cac_start);
1279e5b75505Sopenharmony_ci	return 0;
1280e5b75505Sopenharmony_ci}
1281e5b75505Sopenharmony_ci
1282e5b75505Sopenharmony_ci
1283e5b75505Sopenharmony_ci/*
1284e5b75505Sopenharmony_ci * Main DFS handler for offloaded case.
1285e5b75505Sopenharmony_ci * 2 - continue channel/AP setup for non-DFS channel
1286e5b75505Sopenharmony_ci * 1 - continue channel/AP setup for DFS channel
1287e5b75505Sopenharmony_ci * 0 - channel/AP setup will be continued after CAC
1288e5b75505Sopenharmony_ci * -1 - hit critical error
1289e5b75505Sopenharmony_ci */
1290e5b75505Sopenharmony_ciint hostapd_handle_dfs_offload(struct hostapd_iface *iface)
1291e5b75505Sopenharmony_ci{
1292e5b75505Sopenharmony_ci	int dfs_res;
1293e5b75505Sopenharmony_ci
1294e5b75505Sopenharmony_ci	wpa_printf(MSG_EXCESSIVE, "%s: iface->cac_started: %d",
1295e5b75505Sopenharmony_ci		   __func__, iface->cac_started);
1296e5b75505Sopenharmony_ci
1297e5b75505Sopenharmony_ci	/*
1298e5b75505Sopenharmony_ci	 * If DFS has already been started, then we are being called from a
1299e5b75505Sopenharmony_ci	 * callback to continue AP/channel setup. Reset the CAC start flag and
1300e5b75505Sopenharmony_ci	 * return.
1301e5b75505Sopenharmony_ci	 */
1302e5b75505Sopenharmony_ci	if (iface->cac_started) {
1303e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
1304e5b75505Sopenharmony_ci			   __func__, iface->cac_started);
1305e5b75505Sopenharmony_ci		iface->cac_started = 0;
1306e5b75505Sopenharmony_ci		return 1;
1307e5b75505Sopenharmony_ci	}
1308e5b75505Sopenharmony_ci
1309e5b75505Sopenharmony_ci	dfs_res = hostapd_is_dfs_required(iface);
1310e5b75505Sopenharmony_ci	if (dfs_res > 0) {
1311e5b75505Sopenharmony_ci		wpa_printf(MSG_DEBUG,
1312e5b75505Sopenharmony_ci			   "%s: freq %d MHz requires DFS for %d chans",
1313e5b75505Sopenharmony_ci			   __func__, iface->freq, dfs_res);
1314e5b75505Sopenharmony_ci#ifdef CONFIG_P2P_160M
1315e5b75505Sopenharmony_ci/* if P2P 160M return 2 -continue channel/AP setup for non-DFS channel*/
1316e5b75505Sopenharmony_ci		return 2;
1317e5b75505Sopenharmony_ci#else
1318e5b75505Sopenharmony_ci		return 0;
1319e5b75505Sopenharmony_ci#endif
1320e5b75505Sopenharmony_ci	}
1321e5b75505Sopenharmony_ci
1322e5b75505Sopenharmony_ci	wpa_printf(MSG_EXCESSIVE,
1323e5b75505Sopenharmony_ci		   "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
1324e5b75505Sopenharmony_ci		   __func__, iface->freq);
1325e5b75505Sopenharmony_ci	return 2;
1326e5b75505Sopenharmony_ci}
1327e5b75505Sopenharmony_ci
1328e5b75505Sopenharmony_ci
1329e5b75505Sopenharmony_ciint hostapd_is_dfs_overlap(struct hostapd_iface *iface, enum chan_width width,
1330e5b75505Sopenharmony_ci			   int center_freq)
1331e5b75505Sopenharmony_ci{
1332e5b75505Sopenharmony_ci	struct hostapd_channel_data *chan;
1333e5b75505Sopenharmony_ci	struct hostapd_hw_modes *mode = iface->current_mode;
1334e5b75505Sopenharmony_ci	int half_width;
1335e5b75505Sopenharmony_ci	int res = 0;
1336e5b75505Sopenharmony_ci	int i;
1337e5b75505Sopenharmony_ci
1338e5b75505Sopenharmony_ci	if (!iface->conf->ieee80211h || !mode ||
1339e5b75505Sopenharmony_ci	    mode->mode != HOSTAPD_MODE_IEEE80211A)
1340e5b75505Sopenharmony_ci		return 0;
1341e5b75505Sopenharmony_ci
1342e5b75505Sopenharmony_ci	switch (width) {
1343e5b75505Sopenharmony_ci	case CHAN_WIDTH_20_NOHT:
1344e5b75505Sopenharmony_ci	case CHAN_WIDTH_20:
1345e5b75505Sopenharmony_ci		half_width = 10;
1346e5b75505Sopenharmony_ci		break;
1347e5b75505Sopenharmony_ci	case CHAN_WIDTH_40:
1348e5b75505Sopenharmony_ci		half_width = 20;
1349e5b75505Sopenharmony_ci		break;
1350e5b75505Sopenharmony_ci	case CHAN_WIDTH_80:
1351e5b75505Sopenharmony_ci	case CHAN_WIDTH_80P80:
1352e5b75505Sopenharmony_ci		half_width = 40;
1353e5b75505Sopenharmony_ci		break;
1354e5b75505Sopenharmony_ci	case CHAN_WIDTH_160:
1355e5b75505Sopenharmony_ci		half_width = 80;
1356e5b75505Sopenharmony_ci		break;
1357e5b75505Sopenharmony_ci	default:
1358e5b75505Sopenharmony_ci		wpa_printf(MSG_WARNING, "DFS chanwidth %d not supported",
1359e5b75505Sopenharmony_ci			   width);
1360e5b75505Sopenharmony_ci		return 0;
1361e5b75505Sopenharmony_ci	}
1362e5b75505Sopenharmony_ci
1363e5b75505Sopenharmony_ci	for (i = 0; i < mode->num_channels; i++) {
1364e5b75505Sopenharmony_ci		chan = &mode->channels[i];
1365e5b75505Sopenharmony_ci
1366e5b75505Sopenharmony_ci		if (!(chan->flag & HOSTAPD_CHAN_RADAR))
1367e5b75505Sopenharmony_ci			continue;
1368e5b75505Sopenharmony_ci
1369e5b75505Sopenharmony_ci		if ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
1370e5b75505Sopenharmony_ci		    HOSTAPD_CHAN_DFS_AVAILABLE)
1371e5b75505Sopenharmony_ci			continue;
1372e5b75505Sopenharmony_ci
1373e5b75505Sopenharmony_ci		if (center_freq - chan->freq < half_width &&
1374e5b75505Sopenharmony_ci		    chan->freq - center_freq < half_width)
1375e5b75505Sopenharmony_ci			res++;
1376e5b75505Sopenharmony_ci	}
1377e5b75505Sopenharmony_ci
1378e5b75505Sopenharmony_ci	wpa_printf(MSG_DEBUG, "DFS CAC required: (%d, %d): in range: %s",
1379e5b75505Sopenharmony_ci		   center_freq - half_width, center_freq + half_width,
1380e5b75505Sopenharmony_ci		   res ? "yes" : "no");
1381e5b75505Sopenharmony_ci
1382e5b75505Sopenharmony_ci	return res;
1383e5b75505Sopenharmony_ci}
1384