blob: 0f262ceb243cbda139a701dd470e40fbc07ac156 [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
576/*
577 * Main DFS handler
578 * 1 - continue channel/ap setup
579 * 0 - channel/ap setup will be continued after CAC
580 * -1 - hit critical error
581 */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800582int hostapd_handle_dfs(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700583{
584 struct hostapd_channel_data *channel;
585 int res, n_chans, start_chan_idx;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800586 int skip_radar = 0;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700587
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800588 iface->cac_started = 0;
589
Dmitry Shmidt051af732013-10-22 13:52:46 -0700590 do {
591 /* Get start (first) channel for current configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800592 start_chan_idx = dfs_get_start_chan_idx(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700593 if (start_chan_idx == -1)
594 return -1;
595
596 /* Get number of used channels, depend on width */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800597 n_chans = dfs_get_used_n_chans(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700598
599 /* Check if any of configured channels require DFS */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800600 res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700601 wpa_printf(MSG_DEBUG,
602 "DFS %d channels required radar detection",
603 res);
604 if (!res)
605 return 1;
606
607 /* Check if all channels are DFS available */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800608 res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700609 wpa_printf(MSG_DEBUG,
610 "DFS all channels available, (SKIP CAC): %s",
611 res ? "yes" : "no");
612 if (res)
613 return 1;
614
615 /* Check if any of configured channels is unavailable */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800616 res = dfs_check_chans_unavailable(iface, start_chan_idx,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700617 n_chans);
618 wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
619 res, res ? "yes": "no");
620 if (res) {
Dmitry Shmidt96be6222014-02-13 10:16:51 -0800621 int sec = 0;
622 u8 cf1 = 0, cf2 = 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800623
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800624 channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
625 skip_radar);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700626 if (!channel) {
627 wpa_printf(MSG_ERROR, "could not get valid channel");
628 return -1;
629 }
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800630
631 iface->freq = channel->freq;
632 iface->conf->channel = channel->chan;
633 iface->conf->secondary_channel = sec;
634 iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
635 iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700636 }
637 } while (res);
638
639 /* Finally start CAC */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800640 hostapd_set_state(iface, HAPD_IFACE_DFS);
641 wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
642 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800643 "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d",
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800644 iface->freq,
Dmitry Shmidta38abf92014-03-06 13:38:44 -0800645 iface->conf->channel, iface->conf->secondary_channel,
646 iface->conf->vht_oper_chwidth,
647 iface->conf->vht_oper_centr_freq_seg0_idx,
648 iface->conf->vht_oper_centr_freq_seg1_idx);
649
650 res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
651 iface->freq,
652 iface->conf->channel,
653 iface->conf->ieee80211n,
654 iface->conf->ieee80211ac,
655 iface->conf->secondary_channel,
656 iface->conf->vht_oper_chwidth,
657 iface->conf->vht_oper_centr_freq_seg0_idx,
658 iface->conf->vht_oper_centr_freq_seg1_idx);
659
660 if (res) {
661 wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700662 return -1;
663 }
664
665 return 0;
666}
667
668
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800669int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700670 int ht_enabled, int chan_offset, int chan_width,
671 int cf1, int cf2)
672{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800673 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
674 "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
675 success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
676
Dmitry Shmidt051af732013-10-22 13:52:46 -0700677 if (success) {
678 /* Complete iface/ap configuration */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800679 set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700680 chan_width, cf1, cf2,
681 HOSTAPD_CHAN_DFS_AVAILABLE);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800682 iface->cac_started = 0;
683 hostapd_setup_interface_complete(iface, 0);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700684 }
685
686 return 0;
687}
688
689
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800690static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700691{
692 struct hostapd_channel_data *channel;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800693 int secondary_channel;
Dmitry Shmidt96be6222014-02-13 10:16:51 -0800694 u8 vht_oper_centr_freq_seg0_idx = 0;
695 u8 vht_oper_centr_freq_seg1_idx = 0;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800696 int skip_radar = 0;
697 int err = 1;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700698
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800699 /* Radar detected during active CAC */
700 iface->cac_started = 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800701 channel = dfs_get_valid_channel(iface, &secondary_channel,
702 &vht_oper_centr_freq_seg0_idx,
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800703 &vht_oper_centr_freq_seg1_idx,
704 skip_radar);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800705
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800706 if (!channel) {
707 wpa_printf(MSG_ERROR, "No valid channel available");
708 hostapd_setup_interface_complete(iface, err);
709 return err;
710 }
711
712 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
713 channel->chan);
714 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
715 "freq=%d chan=%d sec_chan=%d", channel->freq,
716 channel->chan, secondary_channel);
717
718 iface->freq = channel->freq;
719 iface->conf->channel = channel->chan;
720 iface->conf->secondary_channel = secondary_channel;
721 iface->conf->vht_oper_centr_freq_seg0_idx =
722 vht_oper_centr_freq_seg0_idx;
723 iface->conf->vht_oper_centr_freq_seg1_idx =
724 vht_oper_centr_freq_seg1_idx;
725 err = 0;
726
727 hostapd_setup_interface_complete(iface, err);
728 return err;
729}
730
731
732static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
733{
734 struct hostapd_channel_data *channel;
735 int secondary_channel;
736 u8 vht_oper_centr_freq_seg0_idx;
737 u8 vht_oper_centr_freq_seg1_idx;
738 int skip_radar = 1;
739 struct csa_settings csa_settings;
740 struct hostapd_data *hapd = iface->bss[0];
741 int err = 1;
742
Dmitry Shmidt344abd32014-01-14 13:17:00 -0800743 wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
744 __func__, iface->cac_started ? "yes" : "no",
745 iface->csa_in_progress ? "yes" : "no");
746
747 /* Check if CSA in progress */
748 if (iface->csa_in_progress)
749 return 0;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800750
751 /* Check if active CAC */
752 if (iface->cac_started)
753 return hostapd_dfs_start_channel_switch_cac(iface);
754
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800755 /* Perform channel switch/CSA */
756 channel = dfs_get_valid_channel(iface, &secondary_channel,
757 &vht_oper_centr_freq_seg0_idx,
758 &vht_oper_centr_freq_seg1_idx,
759 skip_radar);
760
761 if (!channel) {
Dmitry Shmidt98660862014-03-11 17:26:21 -0700762 /*
763 * If there is no channel to switch immediately to, check if
764 * there is another channel where we can switch even if it
765 * requires to perform a CAC first.
766 */
767 skip_radar = 0;
768 channel = dfs_get_valid_channel(iface, &secondary_channel,
769 &vht_oper_centr_freq_seg0_idx,
770 &vht_oper_centr_freq_seg1_idx,
771 skip_radar);
772 if (!channel) {
773 /* FIXME: Wait for channel(s) to become available */
774 hostapd_disable_iface(iface);
775 return err;
776 }
777
778 iface->freq = channel->freq;
779 iface->conf->channel = channel->chan;
780 iface->conf->secondary_channel = secondary_channel;
781 iface->conf->vht_oper_centr_freq_seg0_idx =
782 vht_oper_centr_freq_seg0_idx;
783 iface->conf->vht_oper_centr_freq_seg1_idx =
784 vht_oper_centr_freq_seg1_idx;
785
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800786 hostapd_disable_iface(iface);
Dmitry Shmidt98660862014-03-11 17:26:21 -0700787 hostapd_enable_iface(iface);
788 return 0;
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800789 }
790
791 wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
792 channel->chan);
793 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
794 "freq=%d chan=%d sec_chan=%d", channel->freq,
795 channel->chan, secondary_channel);
796
797 /* Setup CSA request */
798 os_memset(&csa_settings, 0, sizeof(csa_settings));
799 csa_settings.cs_count = 5;
800 csa_settings.block_tx = 1;
801 err = hostapd_set_freq_params(&csa_settings.freq_params,
802 iface->conf->hw_mode,
803 channel->freq,
804 channel->chan,
805 iface->conf->ieee80211n,
806 iface->conf->ieee80211ac,
807 secondary_channel,
808 iface->conf->vht_oper_chwidth,
809 vht_oper_centr_freq_seg0_idx,
810 vht_oper_centr_freq_seg1_idx,
811 iface->current_mode->vht_capab);
812
813 if (err) {
814 wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
815 hostapd_disable_iface(iface);
816 return err;
817 }
818
819 err = hostapd_switch_channel(hapd, &csa_settings);
820 if (err) {
821 wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
822 err);
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800823 iface->freq = channel->freq;
824 iface->conf->channel = channel->chan;
825 iface->conf->secondary_channel = secondary_channel;
826 iface->conf->vht_oper_centr_freq_seg0_idx =
827 vht_oper_centr_freq_seg0_idx;
828 iface->conf->vht_oper_centr_freq_seg1_idx =
829 vht_oper_centr_freq_seg1_idx;
Dmitry Shmidt051af732013-10-22 13:52:46 -0700830
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800831 hostapd_disable_iface(iface);
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800832 hostapd_enable_iface(iface);
833 return 0;
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800834 }
835
Dmitry Shmidt04f534e2013-12-09 15:50:16 -0800836 /* Channel configuration will be updated once CSA completes and
837 * ch_switch_notify event is received */
838
839 wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
Dmitry Shmidt051af732013-10-22 13:52:46 -0700840 return 0;
841}
842
843
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800844int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700845 int ht_enabled, int chan_offset, int chan_width,
846 int cf1, int cf2)
847{
848 int res;
849
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800850 if (!iface->conf->ieee80211h)
Dmitry Shmidt051af732013-10-22 13:52:46 -0700851 return 0;
852
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800853 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
854 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
855 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
856
Dmitry Shmidt051af732013-10-22 13:52:46 -0700857 /* mark radar frequency as invalid */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800858 res = set_dfs_state(iface, freq, ht_enabled, chan_offset,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700859 chan_width, cf1, cf2,
860 HOSTAPD_CHAN_DFS_UNAVAILABLE);
861
862 /* Skip if reported radar event not overlapped our channels */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800863 res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700864 if (!res)
865 return 0;
866
Dmitry Shmidt051af732013-10-22 13:52:46 -0700867 /* radar detected while operating, switch the channel. */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800868 res = hostapd_dfs_start_channel_switch(iface);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700869
870 return res;
871}
872
873
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800874int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
Dmitry Shmidt051af732013-10-22 13:52:46 -0700875 int ht_enabled, int chan_offset, int chan_width,
876 int cf1, int cf2)
877{
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800878 wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
879 "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
880 freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700881 /* TODO add correct implementation here */
Dmitry Shmidtcce06662013-11-04 18:44:24 -0800882 set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
883 cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
Dmitry Shmidt051af732013-10-22 13:52:46 -0700884 return 0;
885}
Dmitry Shmidtf21452a2014-02-26 10:55:25 -0800886
887
888int hostapd_is_dfs_required(struct hostapd_iface *iface)
889{
890 int n_chans, start_chan_idx;
891
892 if (!iface->current_mode)
893 return -1;
894
895 /* Get start (first) channel for current configuration */
896 start_chan_idx = dfs_get_start_chan_idx(iface);
897 if (start_chan_idx == -1)
898 return -1;
899
900 /* Get number of used channels, depend on width */
901 n_chans = dfs_get_used_n_chans(iface);
902
903 /* Check if any of configured channels require DFS */
904 return dfs_check_chans_radar(iface, start_chan_idx, n_chans);
905}