dfs.c revision 68d0e3ed07847339aedfac8e02f50db68c702e52
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 "hostapd.h" 15#include "ap_drv_ops.h" 16#include "drivers/driver.h" 17#include "dfs.h" 18 19 20static 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; 34 case VHT_CHANWIDTH_160MHZ: 35 n_chans = 8; 36 break; 37 default: 38 break; 39 } 40 } 41 42 return n_chans; 43} 44 45 46static 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 58static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans) 59{ 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 = ARRAY_SIZE(allowed_40); 78 break; 79 case 4: 80 allowed = allowed_80; 81 allowed_no = ARRAY_SIZE(allowed_80); 82 break; 83 default: 84 wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans); 85 break; 86 } 87 88 for (i = 0; i < allowed_no; i++) { 89 if (chan->chan == allowed[i]) 90 return 1; 91 } 92 93 return 0; 94} 95 96 97static int dfs_find_channel(struct hostapd_data *hapd, 98 struct hostapd_channel_data **ret_chan, 99 int idx) 100{ 101 struct hostapd_hw_modes *mode; 102 struct hostapd_channel_data *chan, *next_chan; 103 int i, j, channel_idx = 0, n_chans; 104 105 mode = hapd->iface->current_mode; 106 n_chans = dfs_get_used_n_chans(hapd); 107 108 wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans); 109 for (i = 0; i < mode->num_channels; i++) { 110 chan = &mode->channels[i]; 111 112 /* Skip not available channels */ 113 if (!dfs_channel_available(chan)) 114 continue; 115 116 /* Skip HT40/VHT uncompatible channels */ 117 if (hapd->iconf->ieee80211n && 118 hapd->iconf->secondary_channel) { 119 if (!dfs_is_chan_allowed(chan, n_chans)) 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 } 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 } 139 wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan); 140 channel_idx++; 141 } 142 return channel_idx; 143} 144 145 146static 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: 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; 165 break; 166 case VHT_CHANWIDTH_80MHZ: 167 hapd->iconf->vht_oper_centr_freq_seg0_idx = chan->chan + 6; 168 break; 169 case VHT_CHANWIDTH_160MHZ: 170 hapd->iconf->vht_oper_centr_freq_seg0_idx = 171 chan->chan + 14; 172 break; 173 default: 174 wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now"); 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] */ 184static 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; 204 case VHT_CHANWIDTH_160MHZ: 205 channel_no = 206 hapd->iconf->vht_oper_centr_freq_seg0_idx - 14; 207 break; 208 default: 209 wpa_printf(MSG_INFO, 210 "DFS only VHT20/40/80/160 is supported now"); 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 */ 234static 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 */ 254static 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 */ 275static 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 298static struct hostapd_channel_data * dfs_get_valid_channel( 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 */ 316 channel_idx = dfs_find_channel(hapd, NULL, 0); 317 if (channel_idx > 0) { 318 os_get_random((u8 *) &_rand, sizeof(_rand)); 319 new_channel_idx = _rand % channel_idx; 320 dfs_find_channel(hapd, &chan, new_channel_idx); 321 } 322 323 /* VHT */ 324 dfs_adjust_vht_center_freq(hapd, chan); 325 326 return chan; 327} 328 329 330static int set_dfs_state_freq(struct hostapd_data *hapd, int freq, u32 state) 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 340 wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq); 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 356static 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; 389 case CHAN_WIDTH_160: 390 n_chans = 8; 391 frequency = cf1 - 70; 392 break; 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 410static 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 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 425 /* Check we are on DFS channel(s) */ 426 if (!dfs_check_chans_radar(hapd, start_chan_idx, n_chans)) 427 return 0; 428 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; 444 case CHAN_WIDTH_160: 445 radar_n_chans = 8; 446 frequency = cf1 - 70; 447 break; 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]; 458 if (!(chan->flag & HOSTAPD_CHAN_RADAR)) 459 continue; 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 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 */ 480int hostapd_handle_dfs(struct hostapd_data *hapd) 481{ 482 struct hostapd_channel_data *channel; 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) { 516 channel = dfs_get_valid_channel(hapd); 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; 523 } 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; 539 } 540 541 return 0; 542} 543 544 545int 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) 548{ 549 if (success) { 550 /* Complete iface/ap configuration */ 551 set_dfs_state(hapd, freq, ht_enabled, chan_offset, 552 chan_width, cf1, cf2, 553 HOSTAPD_CHAN_DFS_AVAILABLE); 554 hapd->cac_started = 0; 555 hostapd_setup_interface_complete(hapd->iface, 0); 556 } 557 558 return 0; 559} 560 561 562static int hostapd_dfs_start_channel_switch(struct hostapd_data *hapd) 563{ 564 struct hostapd_channel_data *channel; 565 int err = 1; 566 567 wpa_printf(MSG_DEBUG, "%s called", __func__); 568 channel = dfs_get_valid_channel(hapd); 569 if (channel) { 570 hapd->iconf->channel = channel->chan; 571 hapd->iface->freq = channel->freq; 572 err = 0; 573 } 574 575 if (!hapd->cac_started) { 576 wpa_printf(MSG_DEBUG, "DFS radar detected"); 577 hapd->driver->stop_ap(hapd->drv_priv); 578 } else { 579 wpa_printf(MSG_DEBUG, "DFS radar detected during CAC"); 580 hapd->cac_started = 0; 581 } 582 583 hostapd_setup_interface_complete(hapd->iface, err); 584 return 0; 585} 586 587 588int hostapd_dfs_radar_detected(struct hostapd_data *hapd, int freq, 589 int ht_enabled, int chan_offset, int chan_width, 590 int cf1, int cf2) 591{ 592 int res; 593 594 if (!hapd->iconf->ieee80211h) 595 return 0; 596 597 /* mark radar frequency as invalid */ 598 res = set_dfs_state(hapd, freq, ht_enabled, chan_offset, 599 chan_width, cf1, cf2, 600 HOSTAPD_CHAN_DFS_UNAVAILABLE); 601 602 /* Skip if reported radar event not overlapped our channels */ 603 res = dfs_are_channels_overlapped(hapd, freq, chan_width, cf1, cf2); 604 if (!res) 605 return 0; 606 607 /* radar detected while operating, switch the channel. */ 608 res = hostapd_dfs_start_channel_switch(hapd); 609 610 return res; 611} 612 613 614int hostapd_dfs_nop_finished(struct hostapd_data *hapd, int freq, 615 int ht_enabled, int chan_offset, int chan_width, 616 int cf1, int cf2) 617{ 618 /* TODO add correct implementation here */ 619 set_dfs_state(hapd, freq, ht_enabled, chan_offset, chan_width, cf1, cf2, 620 HOSTAPD_CHAN_DFS_USABLE); 621 return 0; 622} 623