1 /* 2 * Management of interference due to incumbent signals in 6 GHz band 3 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9 #include "utils/includes.h" 10 #include "utils/common.h" 11 #include "common/hw_features_common.h" 12 #include "common/ieee802_11_common.h" 13 #include "hostapd.h" 14 #include "ap_drv_ops.h" 15 #include "beacon.h" 16 #include "interference.h" 17 18 19 #define BASE_6GHZ_FREQ 5950 20 /* Channel 2 (op class 136) has a center frequency of 5935 MHz. */ 21 #define BASE_6GHZ_CH2_FREQ 5935 22 23 enum chan_seg { 24 SEG_PRI20 = 0x1, 25 SEG_SEC20 = 0x2, 26 SEG_SEC40_LOW = 0x4, 27 SEG_SEC40_UP = 0x8, 28 SEG_SEC40 = 0xC, 29 SEG_SEC80_LOW = 0x10, 30 SEG_SEC80_LOW_UP = 0x20, 31 SEG_SEC80_UP_LOW = 0x40, 32 SEG_SEC80_UP = 0x80, 33 SEG_SEC80 = 0xF0, 34 SEG_SEC160_LOW = 0x0100, 35 SEG_SEC160_LOW_UP = 0x0200, 36 SEG_SEC160_LOW_UP_UP = 0x0400, 37 SEG_SEC160_LOW_UP_UP_UP = 0x0800, 38 SEG_SEC160_UP_LOW_LOW_LOW = 0x1000, 39 SEG_SEC160_UP_LOW_LOW = 0x2000, 40 SEG_SEC160_UP_LOW = 0x4000, 41 SEG_SEC160_UP = 0x8000, 42 SEG_SEC160 = 0xFF00, 43 }; 44 45 46 static bool is_chan_disabled(struct hostapd_hw_modes *mode, int chan_num) 47 { 48 struct hostapd_channel_data *chan; 49 int i; 50 51 for (i = 0; i < mode->num_channels; i++) { 52 chan = &mode->channels[i]; 53 if (chan->chan == chan_num) 54 return !!(chan->flag & HOSTAPD_CHAN_DISABLED); 55 } 56 57 return true; 58 } 59 60 61 /* 62 * chan_range_available - Check whether the channel can operate in the given 63 * bandwidth in 6 GHz 64 * @first_chan_idx - Channel index of the first 20 MHz channel in a segment 65 * @num_chans - Number of 20 MHz channels needed for the operating bandwidth 66 * @allowed_arr - Range of channels allowed in the given bandwidth 67 * @allowed_arr_size - Number of channels allowed in the given bandwidth 68 * Returns: Whether the channel can operation in the given bandwidth 69 */ 70 static bool chan_range_available(struct hostapd_hw_modes *mode, 71 unsigned int first_chan_idx, 72 unsigned int num_chans, 73 const int *allowed_arr, 74 unsigned int allowed_arr_size) 75 { 76 struct hostapd_channel_data *first_chan; 77 unsigned int i; 78 79 first_chan = &mode->channels[first_chan_idx]; 80 81 if (!chan_pri_allowed(first_chan)) { 82 wpa_printf(MSG_DEBUG, "Intf: Primary channel %d not allowed", 83 first_chan->chan); 84 return false; 85 } 86 87 /* 20 MHz channel, so no need to check the range */ 88 if (num_chans == 1) 89 return true; 90 91 if (allowed_arr) { 92 for (i = 0; i < allowed_arr_size; i++) { 93 if (first_chan->chan == allowed_arr[i]) 94 break; 95 } 96 if (i == allowed_arr_size) 97 return false; 98 } 99 100 /* 101 * Check whether all the 20 MHz channels in the given operating range 102 * are enabled. 103 */ 104 for (i = 1; i < num_chans; i++) { 105 if (is_chan_disabled(mode, first_chan->chan + i * 4)) 106 return false; 107 } 108 109 return true; 110 } 111 112 113 static bool is_in_chanlist(struct hostapd_iface *iface, 114 struct hostapd_channel_data *chan) 115 { 116 if (!iface->conf->acs_ch_list.num) 117 return true; 118 119 return freq_range_list_includes(&iface->conf->acs_ch_list, chan->chan); 120 } 121 122 123 static int get_center_freq_6g(int chan_idx, enum chan_width chan_width, 124 int *center_freq) 125 { 126 if (!center_freq) 127 return -1; 128 129 *center_freq = 0; 130 131 switch (chan_width) { 132 case CHAN_WIDTH_20: 133 if (chan_idx == 2) { 134 *center_freq = BASE_6GHZ_CH2_FREQ; 135 break; 136 } 137 if (chan_idx >= 1 && chan_idx <= 233) 138 *center_freq = ((chan_idx / 4) * 4 + 1) * 5 + 139 BASE_6GHZ_FREQ; 140 break; 141 case CHAN_WIDTH_40: 142 if (chan_idx >= 1 && chan_idx <= 229) 143 *center_freq = ((chan_idx / 8) * 8 + 3) * 5 + 144 BASE_6GHZ_FREQ; 145 break; 146 case CHAN_WIDTH_80: 147 if (chan_idx >= 1 && chan_idx <= 221) 148 *center_freq = ((chan_idx / 16) * 16 + 7) * 5 + 149 BASE_6GHZ_FREQ; 150 break; 151 case CHAN_WIDTH_160: 152 if (chan_idx >= 1 && chan_idx <= 221) 153 *center_freq = ((chan_idx / 32) * 32 + 15) * 5 + 154 BASE_6GHZ_FREQ; 155 break; 156 case CHAN_WIDTH_320: 157 if (chan_idx >= 1 && chan_idx <= 221) 158 *center_freq = ((chan_idx / 32) * 32 + 31) * 5 + 159 BASE_6GHZ_FREQ; 160 break; 161 default: 162 break; 163 } 164 165 if (*center_freq == 0) 166 return -1; 167 168 return 0; 169 } 170 171 172 static bool is_interference_in_chanlist(int freq_start, int freq_end, 173 const int *interference_freqs) 174 { 175 int i; 176 177 for (i = freq_start; i <= freq_end; i += 20) { 178 if (int_array_includes(interference_freqs, i)) 179 return true; 180 } 181 return false; 182 } 183 184 185 186 static const int * get_allowed_channel_array(int num_chans, unsigned int *size) 187 { 188 static const int allowed_40_6g[] = { 1, 9, 17, 25, 33, 41, 49, 57, 65, 189 73, 81, 89, 97, 105, 113, 121, 129, 190 137, 145, 153, 161, 169, 177, 185, 191 193, 201, 209, 217, 225, 233 }; 192 static const int allowed_80_6g[] = { 1, 17, 33, 49, 65, 81, 97, 113, 193 129, 145, 161, 177, 194 193, 209 }; 195 static const int allowed_160_6g[] = { 1, 33, 65, 97, 129, 161, 193 }; 196 static const int allowed_320_6g[] = { 1, 65, 129, 33, 97, 161 }; 197 198 switch (num_chans) { 199 case 2: 200 *size = ARRAY_SIZE(allowed_40_6g); 201 return allowed_40_6g; 202 case 4: 203 *size = ARRAY_SIZE(allowed_80_6g); 204 return allowed_80_6g; 205 case 8: 206 *size = ARRAY_SIZE(allowed_160_6g); 207 return allowed_160_6g; 208 case 16: 209 *size = ARRAY_SIZE(allowed_320_6g); 210 return allowed_320_6g; 211 default: 212 *size = 0; 213 return NULL; 214 } 215 } 216 217 218 /** 219 * intf_find_channel_list - Find the list of channels that can operate with 220 * channel width chan_width and not present within the range of current 221 * operating range. 222 * @chan_width - Channel width to be checked 223 * @chandef_list - Pointer array to hold the list of valid available chandef 224 * Returns: The total number of available chandefs that support the provided 225 * bandwidth 226 */ 227 static int intf_find_channel_list(struct hostapd_iface *iface, int chan_width, 228 struct hostapd_channel_data **chandef_list, 229 const int *interference_freqs) 230 { 231 struct hostapd_hw_modes *mode = iface->current_mode; 232 int new_center_freq, new_start_freq, new_end_freq; 233 const int *allowed_arr = NULL; 234 unsigned int allowed_arr_size = 0; 235 struct hostapd_channel_data *chan; 236 int i; 237 unsigned int channel_idx = 0, n_chans; 238 int ret; 239 240 switch (chan_width) { 241 case CHAN_WIDTH_20_NOHT: 242 case CHAN_WIDTH_20: 243 n_chans = 1; 244 break; 245 case CHAN_WIDTH_40: 246 n_chans = 2; 247 break; 248 case CHAN_WIDTH_80: 249 n_chans = 4; 250 break; 251 case CHAN_WIDTH_80P80: 252 case CHAN_WIDTH_160: 253 n_chans = 8; 254 break; 255 case CHAN_WIDTH_320: 256 n_chans = 16; 257 break; 258 default: 259 n_chans = 1; 260 break; 261 } 262 263 allowed_arr = get_allowed_channel_array(n_chans, &allowed_arr_size); 264 265 for (i = 0; i < mode->num_channels; i++) { 266 chan = &mode->channels[i]; 267 268 if (!chan_in_current_hw_info(iface->current_hw_info, chan)) { 269 wpa_printf(MSG_DEBUG, 270 "Intf: Channel %d (%d) is not under current hardware index", 271 chan->freq, chan->chan); 272 continue; 273 } 274 275 /* Skip incompatible chandefs */ 276 if (!chan_range_available(mode, i, n_chans, 277 allowed_arr, allowed_arr_size)) { 278 wpa_printf(MSG_DEBUG, 279 "Intf: Range not available for chan %d (%d)", 280 chan->freq, chan->chan); 281 continue; 282 } 283 284 if (!is_in_chanlist(iface, chan)) { 285 wpa_printf(MSG_DEBUG, 286 "Intf: Channel %d (%d) not in chanlist", 287 chan->freq, chan->chan); 288 continue; 289 } 290 291 ret = get_center_freq_6g(chan->chan, chan_width, 292 &new_center_freq); 293 if (ret) { 294 wpa_printf(MSG_INFO, 295 "Intf: Couldn't find center freq for chan %d chan_width %d", 296 chan->chan, chan_width); 297 return 0; 298 } 299 300 new_start_freq = (new_center_freq - 301 channel_width_to_int(chan_width) / 2) + 10; 302 new_end_freq = (new_center_freq + 303 channel_width_to_int(chan_width) / 2) - 10; 304 305 if (is_interference_in_chanlist(new_start_freq, new_end_freq, 306 interference_freqs)) { 307 wpa_printf(MSG_DEBUG, 308 "Intf: Found frequency which has interference in channel (%d)", 309 chan->chan); 310 continue; 311 } 312 313 wpa_printf(MSG_DEBUG, 314 "Intf: Adding channel %d (%d) to valid chandef list", 315 chan->freq, chan->chan); 316 chandef_list[channel_idx] = chan; 317 channel_idx++; 318 } 319 320 return channel_idx; 321 } 322 323 324 static enum chan_width downgrade_bandwidth(enum chan_width chan_width) 325 { 326 switch (chan_width) { 327 case CHAN_WIDTH_320: 328 return CHAN_WIDTH_160; 329 case CHAN_WIDTH_160: 330 return CHAN_WIDTH_80; 331 case CHAN_WIDTH_80: 332 return CHAN_WIDTH_40; 333 case CHAN_WIDTH_40: 334 return CHAN_WIDTH_20; 335 default: 336 return CHAN_WIDTH_20_NOHT; 337 } 338 } 339 340 341 /* 342 * hostapd_incumbt_sig_intf_detected - Incumbent signal interference is detected 343 * in the operating channel. The interference channel information is available 344 * as a bitmap(chan_bw_interference_bitmap). If interference has occurred in 345 * the primary channel, do a complete channel switch to a different channel; 346 * otherwise, reduce the operating bandwidth and continue AP operation using 347 * the same primary channel. 348 */ 349 int hostapd_incumbt_sig_intf_detected(struct hostapd_iface *iface, int freq, 350 int chan_width, int cf1, int cf2, 351 u32 chan_bw_interference_bitmap) 352 { 353 struct csa_settings settings; 354 struct hostapd_channel_data *chan_data = NULL; 355 struct hostapd_channel_data *chan_temp; 356 struct hostapd_channel_data **available_chandef_list = NULL; 357 int ret = 0; 358 unsigned int i; 359 u32 _rand; 360 u32 chan_idx; 361 int num_available_chandefs = 0; 362 enum chan_width new_chan_width; 363 int new_center_freq; 364 int current_start_freq; 365 int temp_width; 366 struct hostapd_hw_modes *mode = iface->current_mode; 367 int *interference_freqs = NULL; 368 int primary_chan_bit = -1; 369 int segment_freq; 370 int start_freq = (cf1 - channel_width_to_int(chan_width) / 2) + 10; 371 u8 channel_no; 372 unsigned int num_segments; 373 int new_bw; 374 375 wpa_printf(MSG_DEBUG, 376 "Intf: input freq=%d, chan_width=%d, cf1=%d, cf2=%d, chan_bw_interference_bitmap=0x%x", 377 freq, chan_width, cf1, cf2, chan_bw_interference_bitmap); 378 379 num_segments = channel_width_to_int(chan_width) / 20; 380 if (!num_segments) { 381 wpa_printf(MSG_INFO, "Intf: Invalid channel width %d", 382 chan_width); 383 return -1; 384 } 385 386 /* Determine which 20 MHz segment contains the primary channel */ 387 for (i = 0; i < num_segments; i++) { 388 segment_freq = start_freq + i * 20; 389 if (segment_freq == freq) { 390 primary_chan_bit = i; 391 break; 392 } 393 } 394 395 /* Check whether interference has occurred in primary 20 MHz channel */ 396 if (primary_chan_bit >= 0 && 397 (chan_bw_interference_bitmap & BIT(primary_chan_bit))) { 398 available_chandef_list = os_calloc( 399 mode->num_channels, 400 sizeof(struct hostapd_channel_data *)); 401 if (!available_chandef_list) { 402 wpa_printf(MSG_ERROR, 403 "Intf: Failed to allocate available_chandef_list"); 404 return -1; 405 } 406 407 /* Store frequencies with interference in interference_freqs */ 408 current_start_freq = 409 (cf1 - channel_width_to_int(chan_width) / 2) + 10; 410 for (i = 0; i < num_segments; i++) { 411 if (chan_bw_interference_bitmap & BIT(i)) { 412 wpa_printf(MSG_DEBUG, 413 "Intf: Found incumbent signal interference in frequency %d", 414 current_start_freq + 20 * i); 415 int_array_add_unique(&interference_freqs, 416 current_start_freq + 417 20 * i); 418 if (!interference_freqs) { 419 wpa_printf(MSG_ERROR, 420 "Intf: Failed to store interference frequencies"); 421 ret = -1; 422 goto exit; 423 } 424 } 425 } 426 427 /* Find a random channel to be switched */ 428 temp_width = chan_width; 429 430 while (temp_width > CHAN_WIDTH_20_NOHT) { 431 num_available_chandefs = 432 intf_find_channel_list(iface, temp_width, 433 available_chandef_list, 434 interference_freqs); 435 if (num_available_chandefs > 0) 436 break; 437 wpa_printf(MSG_DEBUG, "Intf: Downgrading bandwidth"); 438 temp_width = downgrade_bandwidth(temp_width); 439 } 440 441 if (num_available_chandefs == 0) { 442 wpa_printf(MSG_INFO, "Intf: No available_chandefs"); 443 goto exit; 444 } 445 446 if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0) 447 _rand = os_random(); 448 449 chan_idx = _rand % num_available_chandefs; 450 chan_data = available_chandef_list[chan_idx]; 451 new_chan_width = temp_width; 452 453 wpa_printf(MSG_DEBUG, "Intf: Got random channel %d (%d)", 454 chan_data->freq, chan_data->chan); 455 } else { 456 /* 457 * Interference is not present in the primary 20 MHz, so 458 * reduce bandwidth. 459 */ 460 u8 seg0 = hostapd_get_oper_centr_freq_seg0_idx(iface->conf); 461 u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(iface->conf); 462 enum oper_chan_width oper_chwidth; 463 int j; 464 465 for (j = 0; j < mode->num_channels; j++) { 466 chan_temp = &mode->channels[j]; 467 if (chan_temp->freq == freq) 468 chan_data = chan_temp; 469 } 470 if (!chan_data) { 471 wpa_printf(MSG_INFO, "Intf: No channel found"); 472 goto exit; 473 } 474 475 oper_chwidth = chan_width_to_oper_chwidth(chan_width); 476 477 if (chan_width > CHAN_WIDTH_40) { 478 punct_update_legacy_bw(chan_bw_interference_bitmap, 479 iface->conf->channel, 480 &oper_chwidth, &seg0, &seg1); 481 if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ) 482 new_chan_width = CHAN_WIDTH_160; 483 else if (oper_chwidth == CONF_OPER_CHWIDTH_80MHZ) 484 new_chan_width = CHAN_WIDTH_80; 485 else if (seg0 == 0) 486 new_chan_width = CHAN_WIDTH_20; 487 else 488 new_chan_width = CHAN_WIDTH_40; 489 } else { 490 new_chan_width = CHAN_WIDTH_20; 491 } 492 } 493 494 if (new_chan_width > CHAN_WIDTH_20) { 495 ret = get_center_freq_6g(chan_data->chan, new_chan_width, 496 &new_center_freq); 497 if (ret) { 498 wpa_printf(MSG_ERROR, 499 "Intf: Couldn't find center freq for chan : %d chan_width : %d", 500 chan_data->chan, new_chan_width); 501 goto exit; 502 } 503 } else { 504 new_center_freq = chan_data->freq; 505 } 506 507 ieee80211_freq_to_chan(chan_data->freq, &channel_no); 508 509 os_memset(&settings, 0, sizeof(settings)); 510 settings.cs_count = 5; 511 settings.freq_params.freq = chan_data->freq; 512 513 switch (new_chan_width) { 514 case CHAN_WIDTH_40: 515 new_bw = 40; 516 break; 517 case CHAN_WIDTH_80P80: 518 case CHAN_WIDTH_80: 519 new_bw = 80; 520 break; 521 case CHAN_WIDTH_160: 522 new_bw = 160; 523 break; 524 case CHAN_WIDTH_320: 525 new_bw = 320; 526 break; 527 default: 528 new_bw = 20; 529 break; 530 } 531 532 settings.freq_params.bandwidth = new_bw; 533 settings.freq_params.channel = channel_no; 534 settings.freq_params.center_freq1 = new_center_freq; 535 settings.freq_params.ht_enabled = iface->conf->ieee80211n; 536 settings.freq_params.vht_enabled = iface->conf->ieee80211ac; 537 settings.freq_params.he_enabled = iface->conf->ieee80211ax; 538 settings.freq_params.eht_enabled = iface->conf->ieee80211be; 539 wpa_printf(MSG_DEBUG, 540 "Intf: channel=%u, freq=%d, bw=%d, center_freq1=%d", 541 settings.freq_params.channel, 542 settings.freq_params.freq, 543 settings.freq_params.bandwidth, 544 settings.freq_params.center_freq1); 545 546 if (chan_data->freq == iface->freq) { 547 if (hostapd_change_config_freq(iface->bss[0], iface->conf, 548 &settings.freq_params, NULL)) { 549 wpa_printf(MSG_INFO, 550 "Intf: Failed to update bandwidth"); 551 ret = -1; 552 goto exit; 553 } 554 if (hostapd_set_freq( 555 iface->bss[0], iface->conf->hw_mode, iface->freq, 556 iface->conf->channel, iface->conf->enable_edmg, 557 iface->conf->edmg_channel, iface->conf->ieee80211n, 558 iface->conf->ieee80211ac, 559 iface->conf->ieee80211ax, 560 iface->conf->ieee80211be, 561 iface->conf->secondary_channel, 562 hostapd_get_oper_chwidth(iface->conf), 563 hostapd_get_oper_centr_freq_seg0_idx(iface->conf), 564 hostapd_get_oper_centr_freq_seg1_idx(iface->conf))) { 565 wpa_printf(MSG_ERROR, 566 "Intf: Failed to apply bandwidth update"); 567 ret = -1; 568 goto exit; 569 } 570 ieee802_11_set_beacons(iface); 571 goto exit; 572 } 573 574 /* Channel and bandwidth have been decided, triggering channel switch */ 575 for (i = 0; i < iface->num_bss; i++) { 576 /* Save CHAN_SWITCH VHT and HE config */ 577 hostapd_chan_switch_config(iface->bss[i], 578 &settings.freq_params); 579 580 ret = hostapd_switch_channel(iface->bss[i], &settings); 581 if (ret) { 582 wpa_printf(MSG_ERROR, 583 "Intf: Channel switch failed"); 584 break; 585 } 586 } 587 588 exit: 589 os_free(available_chandef_list); 590 os_free(interference_freqs); 591 return ret; 592 } 593