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