blob: 3fb18810cf43ff45adab6588ec35eaae7ae40abe [file] [log] [blame]
Dmitry Shmidt051af732013-10-22 13:52:46 -07001/*
2 * DFS - Dynamic Frequency Selection
3 * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
4 * Copyright (c) 2013, Qualcomm Atheros, Inc.
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10#include "utils/includes.h"
11
12#include "utils/common.h"
13#include "common/ieee802_11_defs.h"
Dmitry Shmidtcce06662013-11-04 18:44:24 -080014#include "common/wpa_ctrl.h"
Dmitry Shmidt051af732013-10-22 13:52:46 -070015#include "hostapd.h"
16#include "ap_drv_ops.h"
17#include "drivers/driver.h"
18#include "dfs.h"
19
20
Dmitry Shmidtcce06662013-11-04 18:44:24 -080021static int dfs_get_used_n_chans(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -070022{
23 int n_chans = 1;
24
Dmitry Shmidtcce06662013-11-04 18:44:24 -080025 if (iface->conf->ieee80211n && iface->conf->secondary_channel)
Dmitry Shmidt051af732013-10-22 13:52:46 -070026 n_chans = 2;
27
Dmitry Shmidtcce06662013-11-04 18:44:24 -080028 if (iface->conf->ieee80211ac) {
29 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -070030 case VHT_CHANWIDTH_USE_HT:
31 break;
32 case VHT_CHANWIDTH_80MHZ:
33 n_chans = 4;
34 break;
35 case VHT_CHANWIDTH_160MHZ:
36 n_chans = 8;
37 break;
38 default:
39 break;
40 }
41 }
42
43 return n_chans;
44}
45
46
Dmitry Shmidt04f534e2013-12-09 15:50:16 -080047static int dfs_channel_available(struct hostapd_channel_data *chan,
48 int skip_radar)
Dmitry Shmidt051af732013-10-22 13:52:46 -070049{
Dmitry Shmidt04f534e2013-12-09 15:50:16 -080050 /*
51 * When radar detection happens, CSA is performed. However, there's no
52 * time for CAC, so radar channels must be skipped when finding a new
Dmitry Shmidt98660862014-03-11 17:26:21 -070053 * channel for CSA, unless they are available for immediate use.
Dmitry Shmidt04f534e2013-12-09 15:50:16 -080054 */
Dmitry Shmidt98660862014-03-11 17:26:21 -070055 if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
56 ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
57 HOSTAPD_CHAN_DFS_AVAILABLE))
Dmitry Shmidt04f534e2013-12-09 15:50:16 -080058 return 0;
59
Dmitry Shmidt051af732013-10-22 13:52:46 -070060 if (chan->flag & HOSTAPD_CHAN_DISABLED)
61 return 0;
62 if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
63 ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
64 HOSTAPD_CHAN_DFS_UNAVAILABLE))
65 return 0;
66 return 1;
67}
68
69
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070070static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
Dmitry Shmidt051af732013-10-22 13:52:46 -070071{
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070072 /*
73 * The tables contain first valid channel number based on channel width.
74 * We will also choose this first channel as the control one.
75 */
76 int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
77 184, 192 };
78 /*
79 * VHT80, valid channels based on center frequency:
80 * 42, 58, 106, 122, 138, 155
81 */
82 int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
Dmitry Shmidtf21452a2014-02-26 10:55:25 -080083 /*
84 * VHT160 valid channels based on center frequency:
85 * 50, 114
86 */
87 int allowed_160[] = { 36, 100 };
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070088 int *allowed = allowed_40;
89 unsigned int i, allowed_no = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -070090
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -070091 switch (n_chans) {
92 case 2:
93 allowed = allowed_40;
94 allowed_no = ARRAY_SIZE(allowed_40);
95 break;
96 case 4:
97 allowed = allowed_80;
98 allowed_no = ARRAY_SIZE(allowed_80);
99 break;
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800100 case 8:
101 allowed = allowed_160;
102 allowed_no = ARRAY_SIZE(allowed_160);
103 break;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700104 default:
105 wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
106 break;
107 }
108
109 for (i = 0; i < allowed_no; i++) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700110 if (chan->chan == allowed[i])
111 return 1;
112 }
113
114 return 0;
115}
116
117
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800118static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800119 int first_chan_idx, int num_chans,
120 int skip_radar)
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800121{
122 struct hostapd_channel_data *first_chan, *chan;
123 int i;
124
125 if (first_chan_idx + num_chans >= mode->num_channels)
126 return 0;
127
128 first_chan = &mode->channels[first_chan_idx];
129
130 for (i = 0; i < num_chans; i++) {
131 chan = &mode->channels[first_chan_idx + i];
132
133 if (first_chan->freq + i * 20 != chan->freq)
134 return 0;
135
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800136 if (!dfs_channel_available(chan, skip_radar))
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800137 return 0;
138 }
139
140 return 1;
141}
142
143
Dmitry Shmidt98660862014-03-11 17:26:21 -0700144static int is_in_chanlist(struct hostapd_iface *iface,
145 struct hostapd_channel_data *chan)
146{
147 int *entry;
148
149 if (!iface->conf->chanlist)
150 return 1;
151
152 for (entry = iface->conf->chanlist; *entry != -1; entry++) {
153 if (*entry == chan->chan)
154 return 1;
155 }
156 return 0;
157}
158
159
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800160/*
161 * The function assumes HT40+ operation.
162 * Make sure to adjust the following variables after calling this:
163 * - hapd->secondary_channel
164 * - hapd->vht_oper_centr_freq_seg0_idx
165 * - hapd->vht_oper_centr_freq_seg1_idx
166 */
167static int dfs_find_channel(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700168 struct hostapd_channel_data **ret_chan,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800169 int idx, int skip_radar)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700170{
171 struct hostapd_hw_modes *mode;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800172 struct hostapd_channel_data *chan;
173 int i, channel_idx = 0, n_chans;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700174
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800175 mode = iface->current_mode;
176 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700177
178 wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
179 for (i = 0; i < mode->num_channels; i++) {
180 chan = &mode->channels[i];
181
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800182 /* Skip HT40/VHT incompatible channels */
183 if (iface->conf->ieee80211n &&
184 iface->conf->secondary_channel &&
185 !dfs_is_chan_allowed(chan, n_chans))
Dmitry Shmidt051af732013-10-22 13:52:46 -0700186 continue;
187
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800188 /* Skip incompatible chandefs */
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800189 if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800190 continue;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700191
Dmitry Shmidt98660862014-03-11 17:26:21 -0700192 if (!is_in_chanlist(iface, chan))
193 continue;
194
Dmitry Shmidt051af732013-10-22 13:52:46 -0700195 if (ret_chan && idx == channel_idx) {
196 wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
197 *ret_chan = chan;
198 return idx;
199 }
200 wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
201 channel_idx++;
202 }
203 return channel_idx;
204}
205
206
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800207static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
208 struct hostapd_channel_data *chan,
Dmitry Shmidt344abd32014-01-14 13:17:00 -0800209 int secondary_channel,
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800210 u8 *vht_oper_centr_freq_seg0_idx,
211 u8 *vht_oper_centr_freq_seg1_idx)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700212{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800213 if (!iface->conf->ieee80211ac)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700214 return;
215
216 if (!chan)
217 return;
218
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800219 *vht_oper_centr_freq_seg1_idx = 0;
220
221 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700222 case VHT_CHANWIDTH_USE_HT:
Dmitry Shmidt344abd32014-01-14 13:17:00 -0800223 if (secondary_channel == 1)
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800224 *vht_oper_centr_freq_seg0_idx = chan->chan + 2;
Dmitry Shmidt344abd32014-01-14 13:17:00 -0800225 else if (secondary_channel == -1)
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800226 *vht_oper_centr_freq_seg0_idx = chan->chan - 2;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700227 else
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800228 *vht_oper_centr_freq_seg0_idx = chan->chan;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700229 break;
230 case VHT_CHANWIDTH_80MHZ:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800231 *vht_oper_centr_freq_seg0_idx = chan->chan + 6;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700232 break;
233 case VHT_CHANWIDTH_160MHZ:
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800234 *vht_oper_centr_freq_seg0_idx = chan->chan + 14;
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700235 break;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700236 default:
237 wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
Dmitry Shmidt7d5c8f22014-03-03 13:53:28 -0800238 *vht_oper_centr_freq_seg0_idx = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700239 break;
240 }
241
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800242 wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
243 *vht_oper_centr_freq_seg0_idx,
244 *vht_oper_centr_freq_seg1_idx);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700245}
246
247
248/* Return start channel idx we will use for mode->channels[idx] */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800249static int dfs_get_start_chan_idx(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700250{
251 struct hostapd_hw_modes *mode;
252 struct hostapd_channel_data *chan;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800253 int channel_no = iface->conf->channel;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700254 int res = -1, i;
255
256 /* HT40- */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800257 if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700258 channel_no -= 4;
259
260 /* VHT */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800261 if (iface->conf->ieee80211ac) {
262 switch (iface->conf->vht_oper_chwidth) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700263 case VHT_CHANWIDTH_USE_HT:
264 break;
265 case VHT_CHANWIDTH_80MHZ:
266 channel_no =
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800267 iface->conf->vht_oper_centr_freq_seg0_idx - 6;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700268 break;
269 case VHT_CHANWIDTH_160MHZ:
270 channel_no =
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800271 iface->conf->vht_oper_centr_freq_seg0_idx - 14;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700272 break;
273 default:
274 wpa_printf(MSG_INFO,
275 "DFS only VHT20/40/80/160 is supported now");
276 channel_no = -1;
277 break;
278 }
279 }
280
281 /* Get idx */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800282 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700283 for (i = 0; i < mode->num_channels; i++) {
284 chan = &mode->channels[i];
285 if (chan->chan == channel_no) {
286 res = i;
287 break;
288 }
289 }
290
Dmitry Shmidt98660862014-03-11 17:26:21 -0700291 if (res == -1) {
292 wpa_printf(MSG_DEBUG,
293 "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
294 mode->num_channels, channel_no, iface->conf->channel,
295 iface->conf->ieee80211n,
296 iface->conf->secondary_channel,
297 iface->conf->vht_oper_chwidth);
298
299 for (i = 0; i < mode->num_channels; i++) {
300 wpa_printf(MSG_DEBUG, "Available channel: %d",
301 mode->channels[i].chan);
302 }
303 }
Dmitry Shmidt051af732013-10-22 13:52:46 -0700304
305 return res;
306}
307
308
309/* At least one channel have radar flag */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800310static int dfs_check_chans_radar(struct hostapd_iface *iface,
311 int start_chan_idx, int n_chans)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700312{
313 struct hostapd_channel_data *channel;
314 struct hostapd_hw_modes *mode;
315 int i, res = 0;
316
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800317 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700318
319 for (i = 0; i < n_chans; i++) {
320 channel = &mode->channels[start_chan_idx + i];
321 if (channel->flag & HOSTAPD_CHAN_RADAR)
322 res++;
323 }
324
325 return res;
326}
327
328
329/* All channels available */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800330static int dfs_check_chans_available(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700331 int start_chan_idx, int n_chans)
332{
333 struct hostapd_channel_data *channel;
334 struct hostapd_hw_modes *mode;
335 int i;
336
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800337 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700338
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800339 for (i = 0; i < n_chans; i++) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700340 channel = &mode->channels[start_chan_idx + i];
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800341
342 if (channel->flag & HOSTAPD_CHAN_DISABLED)
343 break;
344
345 if (!(channel->flag & HOSTAPD_CHAN_RADAR))
346 continue;
347
Dmitry Shmidt051af732013-10-22 13:52:46 -0700348 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
349 HOSTAPD_CHAN_DFS_AVAILABLE)
350 break;
351 }
352
353 return i == n_chans;
354}
355
356
357/* At least one channel unavailable */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800358static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700359 int start_chan_idx,
360 int n_chans)
361{
362 struct hostapd_channel_data *channel;
363 struct hostapd_hw_modes *mode;
364 int i, res = 0;
365
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800366 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700367
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800368 for (i = 0; i < n_chans; i++) {
Dmitry Shmidt051af732013-10-22 13:52:46 -0700369 channel = &mode->channels[start_chan_idx + i];
370 if (channel->flag & HOSTAPD_CHAN_DISABLED)
371 res++;
372 if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
373 HOSTAPD_CHAN_DFS_UNAVAILABLE)
374 res++;
375 }
376
377 return res;
378}
379
380
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800381static struct hostapd_channel_data *
382dfs_get_valid_channel(struct hostapd_iface *iface,
383 int *secondary_channel,
384 u8 *vht_oper_centr_freq_seg0_idx,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800385 u8 *vht_oper_centr_freq_seg1_idx,
386 int skip_radar)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700387{
388 struct hostapd_hw_modes *mode;
389 struct hostapd_channel_data *chan = NULL;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800390 int num_available_chandefs;
391 int chan_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700392 u32 _rand;
393
394 wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
Dmitry Shmidt7d5c8f22014-03-03 13:53:28 -0800395 *secondary_channel = 0;
396 *vht_oper_centr_freq_seg0_idx = 0;
397 *vht_oper_centr_freq_seg1_idx = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700398
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800399 if (iface->current_mode == NULL)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700400 return NULL;
401
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800402 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700403 if (mode->mode != HOSTAPD_MODE_IEEE80211A)
404 return NULL;
405
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800406 /* Get the count first */
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800407 num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800408 if (num_available_chandefs == 0)
409 return NULL;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700410
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800411 os_get_random((u8 *) &_rand, sizeof(_rand));
412 chan_idx = _rand % num_available_chandefs;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800413 dfs_find_channel(iface, &chan, chan_idx, skip_radar);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800414
415 /* dfs_find_channel() calculations assume HT40+ */
416 if (iface->conf->secondary_channel)
417 *secondary_channel = 1;
418 else
419 *secondary_channel = 0;
420
421 dfs_adjust_vht_center_freq(iface, chan,
Dmitry Shmidt344abd32014-01-14 13:17:00 -0800422 *secondary_channel,
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800423 vht_oper_centr_freq_seg0_idx,
424 vht_oper_centr_freq_seg1_idx);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700425
426 return chan;
427}
428
429
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800430static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700431{
432 struct hostapd_hw_modes *mode;
433 struct hostapd_channel_data *chan = NULL;
434 int i;
435
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800436 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700437 if (mode == NULL)
438 return 0;
439
440 wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800441 for (i = 0; i < iface->current_mode->num_channels; i++) {
442 chan = &iface->current_mode->channels[i];
Dmitry Shmidt051af732013-10-22 13:52:46 -0700443 if (chan->freq == freq) {
444 if (chan->flag & HOSTAPD_CHAN_RADAR) {
445 chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
446 chan->flag |= state;
447 return 1; /* Channel found */
448 }
449 }
450 }
451 wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
452 return 0;
453}
454
455
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800456static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700457 int chan_offset, int chan_width, int cf1,
458 int cf2, u32 state)
459{
460 int n_chans = 1, i;
461 struct hostapd_hw_modes *mode;
462 int frequency = freq;
463 int ret = 0;
464
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800465 mode = iface->current_mode;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700466 if (mode == NULL)
467 return 0;
468
469 if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
470 wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
471 return 0;
472 }
473
474 /* Seems cf1 and chan_width is enough here */
475 switch (chan_width) {
476 case CHAN_WIDTH_20_NOHT:
477 case CHAN_WIDTH_20:
478 n_chans = 1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800479 if (frequency == 0)
480 frequency = cf1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700481 break;
482 case CHAN_WIDTH_40:
483 n_chans = 2;
484 frequency = cf1 - 10;
485 break;
486 case CHAN_WIDTH_80:
487 n_chans = 4;
488 frequency = cf1 - 30;
489 break;
490 case CHAN_WIDTH_160:
491 n_chans = 8;
492 frequency = cf1 - 70;
493 break;
494 default:
495 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
496 chan_width);
497 break;
498 }
499
500 wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
501 n_chans);
502 for (i = 0; i < n_chans; i++) {
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800503 ret += set_dfs_state_freq(iface, frequency, state);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700504 frequency = frequency + 20;
505 }
506
507 return ret;
508}
509
510
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800511static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700512 int chan_width, int cf1, int cf2)
513{
514 int start_chan_idx;
515 struct hostapd_hw_modes *mode;
516 struct hostapd_channel_data *chan;
517 int n_chans, i, j, frequency = freq, radar_n_chans = 1;
518 u8 radar_chan;
519 int res = 0;
520
Dmitry Shmidt051af732013-10-22 13:52:46 -0700521 /* Our configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800522 mode = iface->current_mode;
523 start_chan_idx = dfs_get_start_chan_idx(iface);
524 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700525
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700526 /* Check we are on DFS channel(s) */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800527 if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700528 return 0;
529
Dmitry Shmidt051af732013-10-22 13:52:46 -0700530 /* Reported via radar event */
531 switch (chan_width) {
532 case CHAN_WIDTH_20_NOHT:
533 case CHAN_WIDTH_20:
534 radar_n_chans = 1;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800535 if (frequency == 0)
536 frequency = cf1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700537 break;
538 case CHAN_WIDTH_40:
539 radar_n_chans = 2;
540 frequency = cf1 - 10;
541 break;
542 case CHAN_WIDTH_80:
543 radar_n_chans = 4;
544 frequency = cf1 - 30;
545 break;
546 case CHAN_WIDTH_160:
547 radar_n_chans = 8;
548 frequency = cf1 - 70;
549 break;
550 default:
551 wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
552 chan_width);
553 break;
554 }
555
556 ieee80211_freq_to_chan(frequency, &radar_chan);
557
558 for (i = 0; i < n_chans; i++) {
559 chan = &mode->channels[start_chan_idx + i];
Dmitry Shmidt68d0e3e2013-10-28 17:59:21 -0700560 if (!(chan->flag & HOSTAPD_CHAN_RADAR))
561 continue;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700562 for (j = 0; j < radar_n_chans; j++) {
563 wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
564 chan->chan, radar_chan + j * 4);
565 if (chan->chan == radar_chan + j * 4)
566 res++;
567 }
568 }
569
570 wpa_printf(MSG_DEBUG, "overlapped: %d", res);
571
572 return res;
573}
574
575
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700576static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
577 int start_chan_idx, int n_chans)
578{
579 struct hostapd_channel_data *channel;
580 struct hostapd_hw_modes *mode;
581 int i;
582 unsigned int cac_time_ms = 0;
583
584 mode = iface->current_mode;
585
586 for (i = 0; i < n_chans; i++) {
587 channel = &mode->channels[start_chan_idx + i];
588 if (!(channel->flag & HOSTAPD_CHAN_RADAR))
589 continue;
590 if (channel->dfs_cac_ms > cac_time_ms)
591 cac_time_ms = channel->dfs_cac_ms;
592 }
593
594 return cac_time_ms;
595}
596
597
Dmitry Shmidt051af732013-10-22 13:52:46 -0700598/*
599 * Main DFS handler
600 * 1 - continue channel/ap setup
601 * 0 - channel/ap setup will be continued after CAC
602 * -1 - hit critical error
603 */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800604int hostapd_handle_dfs(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700605{
606 struct hostapd_channel_data *channel;
607 int res, n_chans, start_chan_idx;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800608 int skip_radar = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700609
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800610 iface->cac_started = 0;
611
Dmitry Shmidt051af732013-10-22 13:52:46 -0700612 do {
613 /* Get start (first) channel for current configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800614 start_chan_idx = dfs_get_start_chan_idx(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700615 if (start_chan_idx == -1)
616 return -1;
617
618 /* Get number of used channels, depend on width */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800619 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700620
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700621 /* Setup CAC time */
622 iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
623 n_chans);
624
Dmitry Shmidt051af732013-10-22 13:52:46 -0700625 /* Check if any of configured channels require DFS */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800626 res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700627 wpa_printf(MSG_DEBUG,
628 "DFS %d channels required radar detection",
629 res);
630 if (!res)
631 return 1;
632
633 /* Check if all channels are DFS available */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800634 res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700635 wpa_printf(MSG_DEBUG,
636 "DFS all channels available, (SKIP CAC): %s",
637 res ? "yes" : "no");
638 if (res)
639 return 1;
640
641 /* Check if any of configured channels is unavailable */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800642 res = dfs_check_chans_unavailable(iface, start_chan_idx,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700643 n_chans);
644 wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
645 res, res ? "yes": "no");
646 if (res) {
Dmitry Shmidt96be6222014-02-13 10:16:51 -0800647 int sec = 0;
648 u8 cf1 = 0, cf2 = 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800649
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800650 channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
651 skip_radar);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700652 if (!channel) {
653 wpa_printf(MSG_ERROR, "could not get valid channel");
654 return -1;
655 }
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800656
657 iface->freq = channel->freq;
658 iface->conf->channel = channel->chan;
659 iface->conf->secondary_channel = sec;
660 iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
661 iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700662 }
663 } while (res);
664
665 /* Finally start CAC */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800666 hostapd_set_state(iface, HAPD_IFACE_DFS);
667 wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
668 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700669 "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800670 iface->freq,
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800671 iface->conf->channel, iface->conf->secondary_channel,
672 iface->conf->vht_oper_chwidth,
673 iface->conf->vht_oper_centr_freq_seg0_idx,
Dmitry Shmidtdf5a7e42014-04-02 12:59:59 -0700674 iface->conf->vht_oper_centr_freq_seg1_idx,
675 iface->dfs_cac_ms / 1000);
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800676
677 res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
678 iface->freq,
679 iface->conf->channel,
680 iface->conf->ieee80211n,
681 iface->conf->ieee80211ac,
682 iface->conf->secondary_channel,
683 iface->conf->vht_oper_chwidth,
684 iface->conf->vht_oper_centr_freq_seg0_idx,
685 iface->conf->vht_oper_centr_freq_seg1_idx);
686
687 if (res) {
688 wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700689 return -1;
690 }
691
692 return 0;
693}
694
695
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800696int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700697 int ht_enabled, int chan_offset, int chan_width,
698 int cf1, int cf2)
699{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800700 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
701 "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
702 success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
703
Dmitry Shmidt051af732013-10-22 13:52:46 -0700704 if (success) {
705 /* Complete iface/ap configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800706 set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700707 chan_width, cf1, cf2,
708 HOSTAPD_CHAN_DFS_AVAILABLE);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800709 iface->cac_started = 0;
710 hostapd_setup_interface_complete(iface, 0);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700711 }
712
713 return 0;
714}
715
716
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800717static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700718{
719 struct hostapd_channel_data *channel;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800720 int secondary_channel;
Dmitry Shmidt96be6222014-02-13 10:16:51 -0800721 u8 vht_oper_centr_freq_seg0_idx = 0;
722 u8 vht_oper_centr_freq_seg1_idx = 0;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800723 int skip_radar = 0;
724 int err = 1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700725
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800726 /* Radar detected during active CAC */
727 iface->cac_started = 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800728 channel = dfs_get_valid_channel(iface, &secondary_channel,
729 &vht_oper_centr_freq_seg0_idx,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800730 &vht_oper_centr_freq_seg1_idx,
731 skip_radar);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800732
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800733 if (!channel) {
734 wpa_printf(MSG_ERROR, "No valid channel available");
735 hostapd_setup_interface_complete(iface, err);
736 return err;
737 }
738
739 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
740 channel->chan);
741 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
742 "freq=%d chan=%d sec_chan=%d", channel->freq,
743 channel->chan, secondary_channel);
744
745 iface->freq = channel->freq;
746 iface->conf->channel = channel->chan;
747 iface->conf->secondary_channel = secondary_channel;
748 iface->conf->vht_oper_centr_freq_seg0_idx =
749 vht_oper_centr_freq_seg0_idx;
750 iface->conf->vht_oper_centr_freq_seg1_idx =
751 vht_oper_centr_freq_seg1_idx;
752 err = 0;
753
754 hostapd_setup_interface_complete(iface, err);
755 return err;
756}
757
758
759static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
760{
761 struct hostapd_channel_data *channel;
762 int secondary_channel;
763 u8 vht_oper_centr_freq_seg0_idx;
764 u8 vht_oper_centr_freq_seg1_idx;
765 int skip_radar = 1;
766 struct csa_settings csa_settings;
767 struct hostapd_data *hapd = iface->bss[0];
768 int err = 1;
769
Dmitry Shmidt344abd32014-01-14 13:17:00 -0800770 wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
771 __func__, iface->cac_started ? "yes" : "no",
772 iface->csa_in_progress ? "yes" : "no");
773
774 /* Check if CSA in progress */
775 if (iface->csa_in_progress)
776 return 0;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800777
778 /* Check if active CAC */
779 if (iface->cac_started)
780 return hostapd_dfs_start_channel_switch_cac(iface);
781
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800782 /* Perform channel switch/CSA */
783 channel = dfs_get_valid_channel(iface, &secondary_channel,
784 &vht_oper_centr_freq_seg0_idx,
785 &vht_oper_centr_freq_seg1_idx,
786 skip_radar);
787
788 if (!channel) {
Dmitry Shmidt98660862014-03-11 17:26:21 -0700789 /*
790 * If there is no channel to switch immediately to, check if
791 * there is another channel where we can switch even if it
792 * requires to perform a CAC first.
793 */
794 skip_radar = 0;
795 channel = dfs_get_valid_channel(iface, &secondary_channel,
796 &vht_oper_centr_freq_seg0_idx,
797 &vht_oper_centr_freq_seg1_idx,
798 skip_radar);
799 if (!channel) {
800 /* FIXME: Wait for channel(s) to become available */
801 hostapd_disable_iface(iface);
802 return err;
803 }
804
805 iface->freq = channel->freq;
806 iface->conf->channel = channel->chan;
807 iface->conf->secondary_channel = secondary_channel;
808 iface->conf->vht_oper_centr_freq_seg0_idx =
809 vht_oper_centr_freq_seg0_idx;
810 iface->conf->vht_oper_centr_freq_seg1_idx =
811 vht_oper_centr_freq_seg1_idx;
812
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800813 hostapd_disable_iface(iface);
Dmitry Shmidt98660862014-03-11 17:26:21 -0700814 hostapd_enable_iface(iface);
815 return 0;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800816 }
817
818 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
819 channel->chan);
820 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
821 "freq=%d chan=%d sec_chan=%d", channel->freq,
822 channel->chan, secondary_channel);
823
824 /* Setup CSA request */
825 os_memset(&csa_settings, 0, sizeof(csa_settings));
826 csa_settings.cs_count = 5;
827 csa_settings.block_tx = 1;
828 err = hostapd_set_freq_params(&csa_settings.freq_params,
829 iface->conf->hw_mode,
830 channel->freq,
831 channel->chan,
832 iface->conf->ieee80211n,
833 iface->conf->ieee80211ac,
834 secondary_channel,
835 iface->conf->vht_oper_chwidth,
836 vht_oper_centr_freq_seg0_idx,
837 vht_oper_centr_freq_seg1_idx,
838 iface->current_mode->vht_capab);
839
840 if (err) {
841 wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
842 hostapd_disable_iface(iface);
843 return err;
844 }
845
846 err = hostapd_switch_channel(hapd, &csa_settings);
847 if (err) {
848 wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
849 err);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800850 iface->freq = channel->freq;
851 iface->conf->channel = channel->chan;
852 iface->conf->secondary_channel = secondary_channel;
853 iface->conf->vht_oper_centr_freq_seg0_idx =
854 vht_oper_centr_freq_seg0_idx;
855 iface->conf->vht_oper_centr_freq_seg1_idx =
856 vht_oper_centr_freq_seg1_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700857
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800858 hostapd_disable_iface(iface);
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800859 hostapd_enable_iface(iface);
860 return 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800861 }
862
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800863 /* Channel configuration will be updated once CSA completes and
864 * ch_switch_notify event is received */
865
866 wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
Dmitry Shmidt051af732013-10-22 13:52:46 -0700867 return 0;
868}
869
870
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800871int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700872 int ht_enabled, int chan_offset, int chan_width,
873 int cf1, int cf2)
874{
875 int res;
876
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800877 if (!iface->conf->ieee80211h)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700878 return 0;
879
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800880 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
881 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
882 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
883
Dmitry Shmidt051af732013-10-22 13:52:46 -0700884 /* mark radar frequency as invalid */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800885 res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700886 chan_width, cf1, cf2,
887 HOSTAPD_CHAN_DFS_UNAVAILABLE);
888
889 /* Skip if reported radar event not overlapped our channels */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800890 res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700891 if (!res)
892 return 0;
893
Dmitry Shmidt051af732013-10-22 13:52:46 -0700894 /* radar detected while operating, switch the channel. */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800895 res = hostapd_dfs_start_channel_switch(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700896
897 return res;
898}
899
900
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800901int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700902 int ht_enabled, int chan_offset, int chan_width,
903 int cf1, int cf2)
904{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800905 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
906 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
907 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700908 /* TODO add correct implementation here */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800909 set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
910 cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700911 return 0;
912}
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800913
914
915int hostapd_is_dfs_required(struct hostapd_iface *iface)
916{
917 int n_chans, start_chan_idx;
918
919 if (!iface->current_mode)
920 return -1;
921
922 /* Get start (first) channel for current configuration */
923 start_chan_idx = dfs_get_start_chan_idx(iface);
924 if (start_chan_idx == -1)
925 return -1;
926
927 /* Get number of used channels, depend on width */
928 n_chans = dfs_get_used_n_chans(iface);
929
930 /* Check if any of configured channels require DFS */
931 return dfs_check_chans_radar(iface, start_chan_idx, n_chans);
932}