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