dfs.c revision 051af73b8f8014eff33330aead0f36944b3403e6
1051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt/*
2051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * DFS - Dynamic Frequency Selection
3051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * Copyright (c) 2013, Qualcomm Atheros, Inc.
5051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt *
6051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * This software may be distributed under the terms of the BSD license.
7051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * See README for more details.
8051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt */
9051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
10051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt#include "utils/includes.h"
11051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
12051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt#include "utils/common.h"
13051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt#include "common/ieee802_11_defs.h"
14051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt#include "hostapd.h"
15051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt#include "ap_drv_ops.h"
16051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt#include "drivers/driver.h"
17051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt#include "dfs.h"
18051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
19051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
20051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_get_used_n_chans(struct hostapd_data *hapd)
21051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
22051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int n_chans = 1;
23051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
24051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel)
25051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		n_chans = 2;
26051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
27051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (hapd->iconf->ieee80211ac) {
28051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		switch (hapd->iconf->vht_oper_chwidth) {
29051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		case VHT_CHANWIDTH_USE_HT:
30051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
31051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		case VHT_CHANWIDTH_80MHZ:
32051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			n_chans = 4;
33051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
34051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		case VHT_CHANWIDTH_160MHZ:
35051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			n_chans = 8;
36051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
37051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		default:
38051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
39051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
40051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
41051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
42051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return n_chans;
43051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
44051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
45051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
46051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_channel_available(struct hostapd_channel_data *chan)
47051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
48051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (chan->flag & HOSTAPD_CHAN_DISABLED)
49051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
50051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
51051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	    ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
52051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	     HOSTAPD_CHAN_DFS_UNAVAILABLE))
53051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
54051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return 1;
55051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
56051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
57051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
58051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_is_ht40_allowed(struct hostapd_channel_data *chan)
59051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
60051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
61051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			  184, 192 };
62051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	unsigned int i;
63051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
64051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for (i = 0; i < sizeof(allowed) / sizeof(allowed[0]); i++) {
65051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (chan->chan == allowed[i])
66051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			return 1;
67051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
68051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
69051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return 0;
70051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
71051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
72051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
73051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_find_channel(struct hostapd_data *hapd,
74051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			    struct hostapd_channel_data **ret_chan,
75051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			    int idx)
76051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
77051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
78051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *chan, *next_chan;
79051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int i, j, channel_idx = 0, n_chans;
80051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
81051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
82051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	n_chans = dfs_get_used_n_chans(hapd);
83051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
84051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
85051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for (i = 0; i < mode->num_channels; i++) {
86051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		chan = &mode->channels[i];
87051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
88051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Skip not available channels */
89051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (!dfs_channel_available(chan))
90051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			continue;
91051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
92051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Skip HT40/VHT uncompatible channels */
93051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (hapd->iconf->ieee80211n &&
94051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		    hapd->iconf->secondary_channel) {
95051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			if (!dfs_is_ht40_allowed(chan))
96051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				continue;
97051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
98051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			for (j = 1; j < n_chans; j++) {
99051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				next_chan = &mode->channels[i + j];
100051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				if (!dfs_channel_available(next_chan))
101051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt					break;
102051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			}
103051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			if (j != n_chans)
104051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				continue;
105051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
106051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			/* Set HT40+ */
107051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			hapd->iconf->secondary_channel = 1;
108051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
109051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
110051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (ret_chan && idx == channel_idx) {
111051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
112051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			*ret_chan = chan;
113051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			return idx;
114051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
115051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
116051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		channel_idx++;
117051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
118051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return channel_idx;
119051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
120051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
121051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
122051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic void dfs_adjust_vht_center_freq(struct hostapd_data *hapd,
123051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				       struct hostapd_channel_data *chan)
124051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
125051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (!hapd->iconf->ieee80211ac)
126051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return;
127051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
128051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (!chan)
129051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return;
130051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
131051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	switch (hapd->iconf->vht_oper_chwidth) {
132051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case VHT_CHANWIDTH_USE_HT:
133051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 2;
134051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
135051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case VHT_CHANWIDTH_80MHZ:
136051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6;
137051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
138051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case VHT_CHANWIDTH_160MHZ:
139051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		hapd->iconf->vht_oper_centr_freq_seg0_idx =
140051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt						chan->chan + 14;
141051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	default:
142051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
143051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
144051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
145051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
146051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d",
147051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		   hapd->iconf->vht_oper_centr_freq_seg0_idx);
148051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
149051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
150051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
151051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt/* Return start channel idx we will use for mode->channels[idx] */
152051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_get_start_chan_idx(struct hostapd_data *hapd)
153051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
154051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
155051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *chan;
156051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int channel_no = hapd->iconf->channel;
157051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int res = -1, i;
158051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
159051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* HT40- */
160051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel == -1)
161051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		channel_no -= 4;
162051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
163051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* VHT */
164051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (hapd->iconf->ieee80211ac) {
165051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		switch (hapd->iconf->vht_oper_chwidth) {
166051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		case VHT_CHANWIDTH_USE_HT:
167051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
168051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		case VHT_CHANWIDTH_80MHZ:
169051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			channel_no =
170051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				hapd->iconf->vht_oper_centr_freq_seg0_idx - 6;
171051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
172051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		case VHT_CHANWIDTH_160MHZ:
173051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			channel_no =
174051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				hapd->iconf->vht_oper_centr_freq_seg0_idx - 14;
175051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
176051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		default:
177051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			wpa_printf(MSG_INFO,
178051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				   "DFS only VHT20/40/80/160 is supported now");
179051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			channel_no = -1;
180051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
181051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
182051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
183051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
184051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* Get idx */
185051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
186051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for (i = 0; i < mode->num_channels; i++) {
187051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		chan = &mode->channels[i];
188051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (chan->chan == channel_no) {
189051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			res = i;
190051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
191051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
192051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
193051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
194051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (res == -1)
195051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1");
196051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
197051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return res;
198051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
199051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
200051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
201051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt/* At least one channel have radar flag */
202051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_check_chans_radar(struct hostapd_data *hapd, int start_chan_idx,
203051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				 int n_chans)
204051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
205051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *channel;
206051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
207051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int i, res = 0;
208051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
209051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
210051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
211051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for (i = 0; i < n_chans; i++) {
212051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		channel = &mode->channels[start_chan_idx + i];
213051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (channel->flag & HOSTAPD_CHAN_RADAR)
214051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			res++;
215051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
216051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
217051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return res;
218051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
219051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
220051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
221051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt/* All channels available */
222051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_check_chans_available(struct hostapd_data *hapd,
223051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				     int start_chan_idx, int n_chans)
224051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
225051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *channel;
226051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
227051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int i;
228051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
229051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
230051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
231051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for(i = 0; i < n_chans; i++) {
232051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		channel = &mode->channels[start_chan_idx + i];
233051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
234051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		    HOSTAPD_CHAN_DFS_AVAILABLE)
235051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			break;
236051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
237051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
238051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return i == n_chans;
239051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
240051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
241051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
242051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt/* At least one channel unavailable */
243051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_check_chans_unavailable(struct hostapd_data *hapd,
244051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				       int start_chan_idx,
245051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				       int n_chans)
246051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
247051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *channel;
248051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
249051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int i, res = 0;
250051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
251051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
252051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
253051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for(i = 0; i < n_chans; i++) {
254051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		channel = &mode->channels[start_chan_idx + i];
255051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (channel->flag & HOSTAPD_CHAN_DISABLED)
256051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			res++;
257051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
258051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		    HOSTAPD_CHAN_DFS_UNAVAILABLE)
259051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			res++;
260051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
261051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
262051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return res;
263051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
264051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
265051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
266051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic struct hostapd_channel_data * dfs_get_valid_channel(
267051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_data *hapd)
268051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
269051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
270051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *chan = NULL;
271051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int channel_idx, new_channel_idx;
272051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	u32 _rand;
273051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
274051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
275051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
276051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (hapd->iface->current_mode == NULL)
277051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return NULL;
278051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
279051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
280051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (mode->mode != HOSTAPD_MODE_IEEE80211A)
281051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return NULL;
282051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
283051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* get random available channel */
284051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	channel_idx = dfs_find_channel(hapd, NULL, 0);
285051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (channel_idx > 0) {
286051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		os_get_random((u8 *) &_rand, sizeof(_rand));
287051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		new_channel_idx = _rand % channel_idx;
288051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		dfs_find_channel(hapd, &chan, new_channel_idx);
289051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
290051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
291051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* VHT */
292051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	dfs_adjust_vht_center_freq(hapd, chan);
293051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
294051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return chan;
295051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
296051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
297051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
298051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state)
299051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
300051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
301051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *chan = NULL;
302051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int i;
303051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
304051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
305051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (mode == NULL)
306051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
307051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
308051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
309051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
310051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		chan = &hapd->iface->current_mode->channels[i];
311051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (chan->freq == freq) {
312051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			if (chan->flag & HOSTAPD_CHAN_RADAR) {
313051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
314051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				chan->flag |= state;
315051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				return 1; /* Channel found */
316051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			}
317051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
318051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
319051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
320051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return 0;
321051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
322051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
323051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
324051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int set_dfs_state(struct hostapd_data *hapd, int freq, int ht_enabled,
325051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			 int chan_offset, int chan_width, int cf1,
326051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			 int cf2, u32 state)
327051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
328051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int n_chans = 1, i;
329051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
330051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int frequency = freq;
331051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int ret = 0;
332051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
333051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
334051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (mode == NULL)
335051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
336051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
337051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
338051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
339051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
340051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
341051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
342051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* Seems cf1 and chan_width is enough here */
343051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	switch (chan_width) {
344051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_20_NOHT:
345051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_20:
346051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		n_chans = 1;
347051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1;
348051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
349051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_40:
350051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		n_chans = 2;
351051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1 - 10;
352051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
353051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_80:
354051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		n_chans = 4;
355051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1 - 30;
356051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
357051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_160:
358051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		n_chans = 8;
359051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1 - 70;
360051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
361051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	default:
362051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
363051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			   chan_width);
364051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
365051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
366051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
367051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
368051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		   n_chans);
369051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for (i = 0; i < n_chans; i++) {
370051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		ret += set_dfs_state_freq(hapd, frequency, state);
371051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = frequency + 20;
372051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
373051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
374051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return ret;
375051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
376051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
377051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
378051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int dfs_are_channels_overlapped(struct hostapd_data *hapd, int freq,
379051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				       int chan_width, int cf1, int cf2)
380051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
381051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int start_chan_idx;
382051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_hw_modes *mode;
383051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *chan;
384051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int n_chans, i, j, frequency = freq, radar_n_chans = 1;
385051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	u8 radar_chan;
386051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int res = 0;
387051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
388051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (hapd->iface->freq == freq)
389051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		res++;
390051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
391051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* Our configuration */
392051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	mode = hapd->iface->current_mode;
393051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	start_chan_idx = dfs_get_start_chan_idx(hapd);
394051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	n_chans = dfs_get_used_n_chans(hapd);
395051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
396051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* Reported via radar event */
397051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	switch (chan_width) {
398051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_20_NOHT:
399051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_20:
400051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		radar_n_chans = 1;
401051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1;
402051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
403051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_40:
404051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		radar_n_chans = 2;
405051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1 - 10;
406051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
407051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_80:
408051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		radar_n_chans = 4;
409051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1 - 30;
410051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
411051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	case CHAN_WIDTH_160:
412051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		radar_n_chans = 8;
413051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		frequency = cf1 - 70;
414051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
415051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	default:
416051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
417051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			   chan_width);
418051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		break;
419051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
420051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
421051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	ieee80211_freq_to_chan(frequency, &radar_chan);
422051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
423051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	for (i = 0; i < n_chans; i++) {
424051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		chan = &mode->channels[start_chan_idx + i];
425051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		for (j = 0; j < radar_n_chans; j++) {
426051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
427051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				   chan->chan, radar_chan + j * 4);
428051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			if (chan->chan == radar_chan + j * 4)
429051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				res++;
430051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
431051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
432051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
433051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "overlapped: %d", res);
434051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
435051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return res;
436051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
437051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
438051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
439051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt/*
440051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * Main DFS handler
441051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * 1 - continue channel/ap setup
442051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * 0 - channel/ap setup will be continued after CAC
443051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt * -1 - hit critical error
444051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt */
445051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtint hostapd_handle_dfs(struct hostapd_data *hapd)
446051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
447051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *channel;
448051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int res, n_chans, start_chan_idx;
449051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
450051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	do {
451051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Get start (first) channel for current configuration */
452051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		start_chan_idx = dfs_get_start_chan_idx(hapd);
453051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (start_chan_idx == -1)
454051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			return -1;
455051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
456051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Get number of used channels, depend on width */
457051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		n_chans = dfs_get_used_n_chans(hapd);
458051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
459051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Check if any of configured channels require DFS */
460051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		res = dfs_check_chans_radar(hapd, start_chan_idx, n_chans);
461051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_DEBUG,
462051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			   "DFS %d channels required radar detection",
463051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			   res);
464051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (!res)
465051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			return 1;
466051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
467051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Check if all channels are DFS available */
468051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		res = dfs_check_chans_available(hapd, start_chan_idx, n_chans);
469051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_DEBUG,
470051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			   "DFS all channels available, (SKIP CAC): %s",
471051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			   res ? "yes" : "no");
472051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (res)
473051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			return 1;
474051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
475051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Check if any of configured channels is unavailable */
476051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		res = dfs_check_chans_unavailable(hapd, start_chan_idx,
477051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt						  n_chans);
478051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
479051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			   res, res ? "yes": "no");
480051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (res) {
481051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			channel = dfs_get_valid_channel(hapd);
482051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			if (!channel) {
483051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				wpa_printf(MSG_ERROR, "could not get valid channel");
484051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				return -1;
485051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			}
486051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			hapd->iconf->channel = channel->chan;
487051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			hapd->iface->freq = channel->freq;
488051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		}
489051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	} while (res);
490051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
491051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* Finally start CAC */
492051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", hapd->iface->freq);
493051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (hostapd_start_dfs_cac(hapd, hapd->iconf->hw_mode,
494051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iface->freq,
495051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iconf->channel,
496051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iconf->ieee80211n,
497051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iconf->ieee80211ac,
498051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iconf->secondary_channel,
499051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iconf->vht_oper_chwidth,
500051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iconf->vht_oper_centr_freq_seg0_idx,
501051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt				  hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
502051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed");
503051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return -1;
504051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
505051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
506051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return 0;
507051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
508051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
509051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
510051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtint hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq,
511051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			     int ht_enabled, int chan_offset, int chan_width,
512051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			     int cf1, int cf2)
513051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
514051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *channel;
515051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int err = 1;
516051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
517051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (success) {
518051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Complete iface/ap configuration */
519051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		set_dfs_state(hapd, freq, ht_enabled, chan_offset,
520051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			      chan_width, cf1, cf2,
521051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			      HOSTAPD_CHAN_DFS_AVAILABLE);
522051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		hostapd_setup_interface_complete(hapd->iface, 0);
523051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	} else {
524051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		/* Switch to new channel */
525051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		set_dfs_state(hapd, freq, ht_enabled, chan_offset,
526051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			      chan_width, cf1, cf2,
527051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			      HOSTAPD_CHAN_DFS_UNAVAILABLE);
528051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		channel = dfs_get_valid_channel(hapd);
529051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		if (channel) {
530051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			hapd->iconf->channel = channel->chan;
531051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			hapd->iface->freq = channel->freq;
532051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			err = 0;
533051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		} else
534051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			wpa_printf(MSG_ERROR, "No valid channel available");
535051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
536051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		hostapd_setup_interface_complete(hapd->iface, err);
537051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
538051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
539051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return 0;
540051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
541051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
542051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
543051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtstatic int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd)
544051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
545051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	struct hostapd_channel_data *channel;
546051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int err = 1;
547051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
548051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	wpa_printf(MSG_DEBUG, "%s called", __func__);
549051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	channel = dfs_get_valid_channel(hapd);
550051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (channel) {
551051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		hapd->iconf->channel = channel->chan;
552051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		hapd->iface->freq = channel->freq;
553051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		err = 0;
554051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	}
555051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
556051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	hapd->driver->stop_ap(hapd->drv_priv);
557051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
558051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	hostapd_setup_interface_complete(hapd->iface, err);
559051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return 0;
560051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
561051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
562051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
563051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtint hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq,
564051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			       int ht_enabled, int chan_offset, int chan_width,
565051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			       int cf1, int cf2)
566051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
567051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	int res;
568051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
569051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (!hapd->iconf->ieee80211h)
570051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
571051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
572051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* mark radar frequency as invalid */
573051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	res = set_dfs_state(hapd, freq, ht_enabled, chan_offset,
574051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			    chan_width, cf1, cf2,
575051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			    HOSTAPD_CHAN_DFS_UNAVAILABLE);
576051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
577051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* Skip if reported radar event not overlapped our channels */
578051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2);
579051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (!res)
580051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
581051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
582051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* we are working on non-DFS channel - skip event */
583051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	if (res == 0)
584051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		return 0;
585051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
586051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* radar detected while operating, switch the channel. */
587051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	res = hostapd_dfs_start_channel_switch(hapd);
588051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
589051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return res;
590051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
591051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
592051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt
593051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidtint hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq,
594051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			     int ht_enabled, int chan_offset, int chan_width,
595051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt			     int cf1, int cf2)
596051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt{
597051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	/* TODO add correct implementation here */
598051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2,
599051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt		      HOSTAPD_CHAN_DFS_USABLE);
600051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt	return 0;
601051af73b8f8014eff33330aead0f36944b3403e6Dmitry Shmidt}
602