]>
Commit | Line | Data |
---|---|---|
e76da505 JD |
1 | /* |
2 | * DFS - Dynamic Frequency Selection | |
3 | * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> | |
58b73e3d | 4 | * Copyright (c) 2013, Qualcomm Atheros, Inc. |
e76da505 JD |
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" | |
14 | #include "hostapd.h" | |
e76da505 JD |
15 | #include "ap_drv_ops.h" |
16 | #include "drivers/driver.h" | |
17 | #include "dfs.h" | |
18 | ||
19 | ||
58b73e3d JD |
20 | static int dfs_get_used_n_chans(struct hostapd_data *hapd) |
21 | { | |
22 | int n_chans = 1; | |
23 | ||
24 | if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel) | |
25 | n_chans = 2; | |
26 | ||
27 | if (hapd->iconf->ieee80211ac) { | |
28 | switch (hapd->iconf->vht_oper_chwidth) { | |
29 | case VHT_CHANWIDTH_USE_HT: | |
30 | break; | |
31 | case VHT_CHANWIDTH_80MHZ: | |
32 | n_chans = 4; | |
33 | break; | |
899cc14e JD |
34 | case VHT_CHANWIDTH_160MHZ: |
35 | n_chans = 8; | |
36 | break; | |
58b73e3d JD |
37 | default: |
38 | break; | |
39 | } | |
40 | } | |
41 | ||
42 | return n_chans; | |
43 | } | |
44 | ||
45 | ||
46 | static int dfs_channel_available(struct hostapd_channel_data *chan) | |
47 | { | |
48 | if (chan->flag & HOSTAPD_CHAN_DISABLED) | |
49 | return 0; | |
50 | if ((chan->flag & HOSTAPD_CHAN_RADAR) && | |
51 | ((chan->flag & HOSTAPD_CHAN_DFS_MASK) == | |
52 | HOSTAPD_CHAN_DFS_UNAVAILABLE)) | |
53 | return 0; | |
54 | return 1; | |
55 | } | |
56 | ||
57 | ||
1dc17db3 | 58 | static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) |
58b73e3d | 59 | { |
1dc17db3 JD |
60 | /* |
61 | * The tables contain first valid channel number based on channel width. | |
62 | * We will also choose this first channel as the control one. | |
63 | */ | |
64 | int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, | |
65 | 184, 192 }; | |
66 | /* | |
67 | * VHT80, valid channels based on center frequency: | |
68 | * 42, 58, 106, 122, 138, 155 | |
69 | */ | |
70 | int allowed_80[] = { 36, 52, 100, 116, 132, 149 }; | |
71 | int *allowed = allowed_40; | |
72 | unsigned int i, allowed_no = 0; | |
73 | ||
74 | switch (n_chans) { | |
75 | case 2: | |
76 | allowed = allowed_40; | |
77 | allowed_no = sizeof(allowed_40) / sizeof(allowed_40[0]); | |
78 | break; | |
79 | case 4: | |
80 | allowed = allowed_80; | |
81 | allowed_no = sizeof(allowed_80) / sizeof(allowed_80[0]); | |
82 | break; | |
83 | default: | |
84 | wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); | |
85 | break; | |
86 | } | |
58b73e3d | 87 | |
1dc17db3 | 88 | for (i = 0; i < allowed_no; i++) { |
58b73e3d JD |
89 | if (chan->chan == allowed[i]) |
90 | return 1; | |
91 | } | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | ||
6de0e0c9 JD |
97 | static int dfs_find_channel(struct hostapd_data *hapd, |
98 | struct hostapd_channel_data **ret_chan, | |
99 | int idx) | |
e76da505 JD |
100 | { |
101 | struct hostapd_hw_modes *mode; | |
58b73e3d JD |
102 | struct hostapd_channel_data *chan, *next_chan; |
103 | int i, j, channel_idx = 0, n_chans; | |
e76da505 JD |
104 | |
105 | mode = hapd->iface->current_mode; | |
58b73e3d | 106 | n_chans = dfs_get_used_n_chans(hapd); |
e76da505 | 107 | |
58b73e3d | 108 | wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); |
e76da505 JD |
109 | for (i = 0; i < mode->num_channels; i++) { |
110 | chan = &mode->channels[i]; | |
111 | ||
58b73e3d JD |
112 | /* Skip not available channels */ |
113 | if (!dfs_channel_available(chan)) | |
e76da505 JD |
114 | continue; |
115 | ||
58b73e3d JD |
116 | /* Skip HT40/VHT uncompatible channels */ |
117 | if (hapd->iconf->ieee80211n && | |
118 | hapd->iconf->secondary_channel) { | |
1dc17db3 | 119 | if (!dfs_is_chan_allowed(chan, n_chans)) |
58b73e3d JD |
120 | continue; |
121 | ||
122 | for (j = 1; j < n_chans; j++) { | |
123 | next_chan = &mode->channels[i + j]; | |
124 | if (!dfs_channel_available(next_chan)) | |
125 | break; | |
126 | } | |
127 | if (j != n_chans) | |
128 | continue; | |
129 | ||
130 | /* Set HT40+ */ | |
131 | hapd->iconf->secondary_channel = 1; | |
132 | } | |
e76da505 JD |
133 | |
134 | if (ret_chan && idx == channel_idx) { | |
135 | wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan); | |
136 | *ret_chan = chan; | |
137 | return idx; | |
138 | } | |
58b73e3d | 139 | wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); |
e76da505 JD |
140 | channel_idx++; |
141 | } | |
142 | return channel_idx; | |
143 | } | |
144 | ||
145 | ||
58b73e3d JD |
146 | static void dfs_adjust_vht_center_freq(struct hostapd_data *hapd, |
147 | struct hostapd_channel_data *chan) | |
148 | { | |
149 | if (!hapd->iconf->ieee80211ac) | |
150 | return; | |
151 | ||
152 | if (!chan) | |
153 | return; | |
154 | ||
155 | switch (hapd->iconf->vht_oper_chwidth) { | |
156 | case VHT_CHANWIDTH_USE_HT: | |
345276a6 JD |
157 | if (hapd->iconf->secondary_channel == 1) |
158 | hapd->iconf->vht_oper_centr_freq_seg0_idx = | |
159 | chan->chan + 2; | |
160 | else if (hapd->iconf->secondary_channel == -1) | |
161 | hapd->iconf->vht_oper_centr_freq_seg0_idx = | |
162 | chan->chan - 2; | |
163 | else | |
164 | hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan; | |
58b73e3d JD |
165 | break; |
166 | case VHT_CHANWIDTH_80MHZ: | |
167 | hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6; | |
168 | break; | |
899cc14e JD |
169 | case VHT_CHANWIDTH_160MHZ: |
170 | hapd->iconf->vht_oper_centr_freq_seg0_idx = | |
171 | chan->chan + 14; | |
35f83637 | 172 | break; |
58b73e3d | 173 | default: |
899cc14e | 174 | wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); |
58b73e3d JD |
175 | break; |
176 | } | |
177 | ||
178 | wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d", | |
179 | hapd->iconf->vht_oper_centr_freq_seg0_idx); | |
180 | } | |
181 | ||
182 | ||
183 | /* Return start channel idx we will use for mode->channels[idx] */ | |
184 | static int dfs_get_start_chan_idx(struct hostapd_data *hapd) | |
185 | { | |
186 | struct hostapd_hw_modes *mode; | |
187 | struct hostapd_channel_data *chan; | |
188 | int channel_no = hapd->iconf->channel; | |
189 | int res = -1, i; | |
190 | ||
191 | /* HT40- */ | |
192 | if (hapd->iconf->ieee80211n && hapd->iconf->secondary_channel == -1) | |
193 | channel_no -= 4; | |
194 | ||
195 | /* VHT */ | |
196 | if (hapd->iconf->ieee80211ac) { | |
197 | switch (hapd->iconf->vht_oper_chwidth) { | |
198 | case VHT_CHANWIDTH_USE_HT: | |
199 | break; | |
200 | case VHT_CHANWIDTH_80MHZ: | |
201 | channel_no = | |
202 | hapd->iconf->vht_oper_centr_freq_seg0_idx - 6; | |
203 | break; | |
899cc14e JD |
204 | case VHT_CHANWIDTH_160MHZ: |
205 | channel_no = | |
206 | hapd->iconf->vht_oper_centr_freq_seg0_idx - 14; | |
207 | break; | |
58b73e3d JD |
208 | default: |
209 | wpa_printf(MSG_INFO, | |
899cc14e | 210 | "DFS only VHT20/40/80/160 is supported now"); |
58b73e3d JD |
211 | channel_no = -1; |
212 | break; | |
213 | } | |
214 | } | |
215 | ||
216 | /* Get idx */ | |
217 | mode = hapd->iface->current_mode; | |
218 | for (i = 0; i < mode->num_channels; i++) { | |
219 | chan = &mode->channels[i]; | |
220 | if (chan->chan == channel_no) { | |
221 | res = i; | |
222 | break; | |
223 | } | |
224 | } | |
225 | ||
226 | if (res == -1) | |
227 | wpa_printf(MSG_DEBUG, "DFS chan_idx seems wrong: -1"); | |
228 | ||
229 | return res; | |
230 | } | |
231 | ||
232 | ||
233 | /* At least one channel have radar flag */ | |
234 | static int dfs_check_chans_radar(struct hostapd_data *hapd, int start_chan_idx, | |
235 | int n_chans) | |
236 | { | |
237 | struct hostapd_channel_data *channel; | |
238 | struct hostapd_hw_modes *mode; | |
239 | int i, res = 0; | |
240 | ||
241 | mode = hapd->iface->current_mode; | |
242 | ||
243 | for (i = 0; i < n_chans; i++) { | |
244 | channel = &mode->channels[start_chan_idx + i]; | |
245 | if (channel->flag & HOSTAPD_CHAN_RADAR) | |
246 | res++; | |
247 | } | |
248 | ||
249 | return res; | |
250 | } | |
251 | ||
252 | ||
253 | /* All channels available */ | |
254 | static int dfs_check_chans_available(struct hostapd_data *hapd, | |
255 | int start_chan_idx, int n_chans) | |
256 | { | |
257 | struct hostapd_channel_data *channel; | |
258 | struct hostapd_hw_modes *mode; | |
259 | int i; | |
260 | ||
261 | mode = hapd->iface->current_mode; | |
262 | ||
263 | for(i = 0; i < n_chans; i++) { | |
264 | channel = &mode->channels[start_chan_idx + i]; | |
265 | if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) != | |
266 | HOSTAPD_CHAN_DFS_AVAILABLE) | |
267 | break; | |
268 | } | |
269 | ||
270 | return i == n_chans; | |
271 | } | |
272 | ||
273 | ||
274 | /* At least one channel unavailable */ | |
275 | static int dfs_check_chans_unavailable(struct hostapd_data *hapd, | |
276 | int start_chan_idx, | |
277 | int n_chans) | |
278 | { | |
279 | struct hostapd_channel_data *channel; | |
280 | struct hostapd_hw_modes *mode; | |
281 | int i, res = 0; | |
282 | ||
283 | mode = hapd->iface->current_mode; | |
284 | ||
285 | for(i = 0; i < n_chans; i++) { | |
286 | channel = &mode->channels[start_chan_idx + i]; | |
287 | if (channel->flag & HOSTAPD_CHAN_DISABLED) | |
288 | res++; | |
289 | if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) == | |
290 | HOSTAPD_CHAN_DFS_UNAVAILABLE) | |
291 | res++; | |
292 | } | |
293 | ||
294 | return res; | |
295 | } | |
296 | ||
297 | ||
6de0e0c9 | 298 | static struct hostapd_channel_data * dfs_get_valid_channel( |
e76da505 JD |
299 | struct hostapd_data *hapd) |
300 | { | |
301 | struct hostapd_hw_modes *mode; | |
302 | struct hostapd_channel_data *chan = NULL; | |
303 | int channel_idx, new_channel_idx; | |
304 | u32 _rand; | |
305 | ||
306 | wpa_printf(MSG_DEBUG, "DFS: Selecting random channel"); | |
307 | ||
308 | if (hapd->iface->current_mode == NULL) | |
309 | return NULL; | |
310 | ||
311 | mode = hapd->iface->current_mode; | |
312 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) | |
313 | return NULL; | |
314 | ||
315 | /* get random available channel */ | |
6de0e0c9 | 316 | channel_idx = dfs_find_channel(hapd, NULL, 0); |
e76da505 JD |
317 | if (channel_idx > 0) { |
318 | os_get_random((u8 *) &_rand, sizeof(_rand)); | |
319 | new_channel_idx = _rand % channel_idx; | |
6de0e0c9 | 320 | dfs_find_channel(hapd, &chan, new_channel_idx); |
e76da505 JD |
321 | } |
322 | ||
58b73e3d JD |
323 | /* VHT */ |
324 | dfs_adjust_vht_center_freq(hapd, chan); | |
325 | ||
e76da505 JD |
326 | return chan; |
327 | } | |
328 | ||
329 | ||
58b73e3d | 330 | static int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state) |
e76da505 JD |
331 | { |
332 | struct hostapd_hw_modes *mode; | |
333 | struct hostapd_channel_data *chan = NULL; | |
334 | int i; | |
335 | ||
336 | mode = hapd->iface->current_mode; | |
337 | if (mode == NULL) | |
338 | return 0; | |
339 | ||
58b73e3d | 340 | wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); |
e76da505 JD |
341 | for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { |
342 | chan = &hapd->iface->current_mode->channels[i]; | |
343 | if (chan->freq == freq) { | |
344 | if (chan->flag & HOSTAPD_CHAN_RADAR) { | |
345 | chan->flag &= ~HOSTAPD_CHAN_DFS_MASK; | |
346 | chan->flag |= state; | |
347 | return 1; /* Channel found */ | |
348 | } | |
349 | } | |
350 | } | |
351 | wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq); | |
352 | return 0; | |
353 | } | |
354 | ||
355 | ||
58b73e3d JD |
356 | static int set_dfs_state(struct hostapd_data *hapd, int freq, int ht_enabled, |
357 | int chan_offset, int chan_width, int cf1, | |
358 | int cf2, u32 state) | |
359 | { | |
360 | int n_chans = 1, i; | |
361 | struct hostapd_hw_modes *mode; | |
362 | int frequency = freq; | |
363 | int ret = 0; | |
364 | ||
365 | mode = hapd->iface->current_mode; | |
366 | if (mode == NULL) | |
367 | return 0; | |
368 | ||
369 | if (mode->mode != HOSTAPD_MODE_IEEE80211A) { | |
370 | wpa_printf(MSG_WARNING, "current_mode != IEEE80211A"); | |
371 | return 0; | |
372 | } | |
373 | ||
374 | /* Seems cf1 and chan_width is enough here */ | |
375 | switch (chan_width) { | |
376 | case CHAN_WIDTH_20_NOHT: | |
377 | case CHAN_WIDTH_20: | |
378 | n_chans = 1; | |
379 | frequency = cf1; | |
380 | break; | |
381 | case CHAN_WIDTH_40: | |
382 | n_chans = 2; | |
383 | frequency = cf1 - 10; | |
384 | break; | |
385 | case CHAN_WIDTH_80: | |
386 | n_chans = 4; | |
387 | frequency = cf1 - 30; | |
388 | break; | |
899cc14e JD |
389 | case CHAN_WIDTH_160: |
390 | n_chans = 8; | |
391 | frequency = cf1 - 70; | |
392 | break; | |
58b73e3d JD |
393 | default: |
394 | wpa_printf(MSG_INFO, "DFS chan_width %d not supported", | |
395 | chan_width); | |
396 | break; | |
397 | } | |
398 | ||
399 | wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency, | |
400 | n_chans); | |
401 | for (i = 0; i < n_chans; i++) { | |
402 | ret += set_dfs_state_freq(hapd, frequency, state); | |
403 | frequency = frequency + 20; | |
404 | } | |
405 | ||
406 | return ret; | |
407 | } | |
408 | ||
409 | ||
410 | static int dfs_are_channels_overlapped(struct hostapd_data *hapd, int freq, | |
411 | int chan_width, int cf1, int cf2) | |
412 | { | |
413 | int start_chan_idx; | |
414 | struct hostapd_hw_modes *mode; | |
415 | struct hostapd_channel_data *chan; | |
416 | int n_chans, i, j, frequency = freq, radar_n_chans = 1; | |
417 | u8 radar_chan; | |
418 | int res = 0; | |
419 | ||
58b73e3d JD |
420 | /* Our configuration */ |
421 | mode = hapd->iface->current_mode; | |
422 | start_chan_idx = dfs_get_start_chan_idx(hapd); | |
423 | n_chans = dfs_get_used_n_chans(hapd); | |
424 | ||
5eaf240a JD |
425 | /* Check we are on DFS channel(s) */ |
426 | if (!dfs_check_chans_radar(hapd, start_chan_idx, n_chans)) | |
427 | return 0; | |
428 | ||
58b73e3d JD |
429 | /* Reported via radar event */ |
430 | switch (chan_width) { | |
431 | case CHAN_WIDTH_20_NOHT: | |
432 | case CHAN_WIDTH_20: | |
433 | radar_n_chans = 1; | |
434 | frequency = cf1; | |
435 | break; | |
436 | case CHAN_WIDTH_40: | |
437 | radar_n_chans = 2; | |
438 | frequency = cf1 - 10; | |
439 | break; | |
440 | case CHAN_WIDTH_80: | |
441 | radar_n_chans = 4; | |
442 | frequency = cf1 - 30; | |
443 | break; | |
899cc14e JD |
444 | case CHAN_WIDTH_160: |
445 | radar_n_chans = 8; | |
446 | frequency = cf1 - 70; | |
447 | break; | |
58b73e3d JD |
448 | default: |
449 | wpa_printf(MSG_INFO, "DFS chan_width %d not supported", | |
450 | chan_width); | |
451 | break; | |
452 | } | |
453 | ||
454 | ieee80211_freq_to_chan(frequency, &radar_chan); | |
455 | ||
456 | for (i = 0; i < n_chans; i++) { | |
457 | chan = &mode->channels[start_chan_idx + i]; | |
5eaf240a JD |
458 | if (!(chan->flag & HOSTAPD_CHAN_RADAR)) |
459 | continue; | |
58b73e3d JD |
460 | for (j = 0; j < radar_n_chans; j++) { |
461 | wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d", | |
462 | chan->chan, radar_chan + j * 4); | |
463 | if (chan->chan == radar_chan + j * 4) | |
464 | res++; | |
465 | } | |
466 | } | |
467 | ||
468 | wpa_printf(MSG_DEBUG, "overlapped: %d", res); | |
469 | ||
470 | return res; | |
471 | } | |
472 | ||
473 | ||
e76da505 JD |
474 | /* |
475 | * Main DFS handler | |
476 | * 1 - continue channel/ap setup | |
477 | * 0 - channel/ap setup will be continued after CAC | |
478 | * -1 - hit critical error | |
479 | */ | |
480 | int hostapd_handle_dfs(struct hostapd_data *hapd) | |
481 | { | |
e76da505 | 482 | struct hostapd_channel_data *channel; |
58b73e3d JD |
483 | int res, n_chans, start_chan_idx; |
484 | ||
485 | do { | |
486 | /* Get start (first) channel for current configuration */ | |
487 | start_chan_idx = dfs_get_start_chan_idx(hapd); | |
488 | if (start_chan_idx == -1) | |
489 | return -1; | |
490 | ||
491 | /* Get number of used channels, depend on width */ | |
492 | n_chans = dfs_get_used_n_chans(hapd); | |
493 | ||
494 | /* Check if any of configured channels require DFS */ | |
495 | res = dfs_check_chans_radar(hapd, start_chan_idx, n_chans); | |
496 | wpa_printf(MSG_DEBUG, | |
497 | "DFS %d channels required radar detection", | |
498 | res); | |
499 | if (!res) | |
500 | return 1; | |
501 | ||
502 | /* Check if all channels are DFS available */ | |
503 | res = dfs_check_chans_available(hapd, start_chan_idx, n_chans); | |
504 | wpa_printf(MSG_DEBUG, | |
505 | "DFS all channels available, (SKIP CAC): %s", | |
506 | res ? "yes" : "no"); | |
507 | if (res) | |
508 | return 1; | |
509 | ||
510 | /* Check if any of configured channels is unavailable */ | |
511 | res = dfs_check_chans_unavailable(hapd, start_chan_idx, | |
512 | n_chans); | |
513 | wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s", | |
514 | res, res ? "yes": "no"); | |
515 | if (res) { | |
6de0e0c9 | 516 | channel = dfs_get_valid_channel(hapd); |
e76da505 JD |
517 | if (!channel) { |
518 | wpa_printf(MSG_ERROR, "could not get valid channel"); | |
519 | return -1; | |
520 | } | |
521 | hapd->iconf->channel = channel->chan; | |
522 | hapd->iface->freq = channel->freq; | |
e76da505 | 523 | } |
58b73e3d JD |
524 | } while (res); |
525 | ||
526 | /* Finally start CAC */ | |
527 | wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", hapd->iface->freq); | |
528 | if (hostapd_start_dfs_cac(hapd, hapd->iconf->hw_mode, | |
529 | hapd->iface->freq, | |
530 | hapd->iconf->channel, | |
531 | hapd->iconf->ieee80211n, | |
532 | hapd->iconf->ieee80211ac, | |
533 | hapd->iconf->secondary_channel, | |
534 | hapd->iconf->vht_oper_chwidth, | |
535 | hapd->iconf->vht_oper_centr_freq_seg0_idx, | |
536 | hapd->iconf->vht_oper_centr_freq_seg1_idx)) { | |
537 | wpa_printf(MSG_DEBUG, "DFS start_dfs_cac() failed"); | |
538 | return -1; | |
e76da505 JD |
539 | } |
540 | ||
58b73e3d | 541 | return 0; |
e76da505 JD |
542 | } |
543 | ||
544 | ||
58b73e3d JD |
545 | int hostapd_dfs_complete_cac(struct hostapd_data *hapd, int success, int freq, |
546 | int ht_enabled, int chan_offset, int chan_width, | |
547 | int cf1, int cf2) | |
e76da505 JD |
548 | { |
549 | struct hostapd_channel_data *channel; | |
550 | int err = 1; | |
551 | ||
552 | if (success) { | |
553 | /* Complete iface/ap configuration */ | |
58b73e3d JD |
554 | set_dfs_state(hapd, freq, ht_enabled, chan_offset, |
555 | chan_width, cf1, cf2, | |
556 | HOSTAPD_CHAN_DFS_AVAILABLE); | |
e76da505 JD |
557 | hostapd_setup_interface_complete(hapd->iface, 0); |
558 | } else { | |
559 | /* Switch to new channel */ | |
58b73e3d JD |
560 | set_dfs_state(hapd, freq, ht_enabled, chan_offset, |
561 | chan_width, cf1, cf2, | |
562 | HOSTAPD_CHAN_DFS_UNAVAILABLE); | |
6de0e0c9 | 563 | channel = dfs_get_valid_channel(hapd); |
e76da505 JD |
564 | if (channel) { |
565 | hapd->iconf->channel = channel->chan; | |
566 | hapd->iface->freq = channel->freq; | |
567 | err = 0; | |
568 | } else | |
569 | wpa_printf(MSG_ERROR, "No valid channel available"); | |
570 | ||
571 | hostapd_setup_interface_complete(hapd->iface, err); | |
572 | } | |
573 | ||
574 | return 0; | |
575 | } | |
576 | ||
577 | ||
6de0e0c9 | 578 | static int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd) |
e76da505 JD |
579 | { |
580 | struct hostapd_channel_data *channel; | |
581 | int err = 1; | |
582 | ||
583 | wpa_printf(MSG_DEBUG, "%s called", __func__); | |
6de0e0c9 | 584 | channel = dfs_get_valid_channel(hapd); |
e76da505 JD |
585 | if (channel) { |
586 | hapd->iconf->channel = channel->chan; | |
587 | hapd->iface->freq = channel->freq; | |
588 | err = 0; | |
589 | } | |
590 | ||
591 | hapd->driver->stop_ap(hapd->drv_priv); | |
592 | ||
593 | hostapd_setup_interface_complete(hapd->iface, err); | |
594 | return 0; | |
595 | } | |
58b73e3d JD |
596 | |
597 | ||
598 | int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq, | |
599 | int ht_enabled, int chan_offset, int chan_width, | |
600 | int cf1, int cf2) | |
601 | { | |
602 | int res; | |
603 | ||
604 | if (!hapd->iconf->ieee80211h) | |
605 | return 0; | |
606 | ||
607 | /* mark radar frequency as invalid */ | |
608 | res = set_dfs_state(hapd, freq, ht_enabled, chan_offset, | |
609 | chan_width, cf1, cf2, | |
610 | HOSTAPD_CHAN_DFS_UNAVAILABLE); | |
611 | ||
612 | /* Skip if reported radar event not overlapped our channels */ | |
613 | res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2); | |
614 | if (!res) | |
615 | return 0; | |
616 | ||
617 | /* we are working on non-DFS channel - skip event */ | |
618 | if (res == 0) | |
619 | return 0; | |
620 | ||
621 | /* radar detected while operating, switch the channel. */ | |
6de0e0c9 | 622 | res = hostapd_dfs_start_channel_switch(hapd); |
58b73e3d JD |
623 | |
624 | return res; | |
625 | } | |
626 | ||
627 | ||
628 | int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq, | |
629 | int ht_enabled, int chan_offset, int chan_width, | |
630 | int cf1, int cf2) | |
631 | { | |
632 | /* TODO add correct implementation here */ | |
633 | set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, | |
634 | HOSTAPD_CHAN_DFS_USABLE); | |
635 | return 0; | |
636 | } |