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