1 /* 2 * Common hostapd/wpa_supplicant HW features 3 * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> 4 * Copyright (c) 2015, 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 "includes.h" 11 12 #include "common.h" 13 #include "defs.h" 14 #include "ieee802_11_defs.h" 15 #include "ieee802_11_common.h" 16 #include "hw_features_common.h" 17 18 19 struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode, 20 int chan, int *freq) 21 { 22 int i; 23 24 if (freq) 25 *freq = 0; 26 27 if (!mode) 28 return NULL; 29 30 for (i = 0; i < mode->num_channels; i++) { 31 struct hostapd_channel_data *ch = &mode->channels[i]; 32 if (ch->chan == chan) { 33 if (freq) 34 *freq = ch->freq; 35 return ch; 36 } 37 } 38 39 return NULL; 40 } 41 42 43 struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode, 44 int freq, int *chan) 45 { 46 int i; 47 48 if (chan) 49 *chan = 0; 50 51 if (!mode) 52 return NULL; 53 54 for (i = 0; i < mode->num_channels; i++) { 55 struct hostapd_channel_data *ch = &mode->channels[i]; 56 if (ch->freq == freq) { 57 if (chan) 58 *chan = ch->chan; 59 return ch; 60 } 61 } 62 63 return NULL; 64 } 65 66 67 int hw_get_freq(struct hostapd_hw_modes *mode, int chan) 68 { 69 int freq; 70 71 hw_get_channel_chan(mode, chan, &freq); 72 73 return freq; 74 } 75 76 77 int hw_get_chan(struct hostapd_hw_modes *mode, int freq) 78 { 79 int chan; 80 81 hw_get_channel_freq(mode, freq, &chan); 82 83 return chan; 84 } 85 86 87 int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan, 88 int sec_chan) 89 { 90 int ok, first; 91 int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140, 92 149, 157, 165, 184, 192 }; 93 size_t k; 94 struct hostapd_channel_data *p_chan, *s_chan; 95 const int ht40_plus = pri_chan < sec_chan; 96 97 p_chan = hw_get_channel_chan(mode, pri_chan, NULL); 98 if (!p_chan) 99 return 0; 100 101 if (pri_chan == sec_chan || !sec_chan) { 102 if (chan_pri_allowed(p_chan)) 103 return 1; /* HT40 not used */ 104 105 wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary", 106 pri_chan); 107 return 0; 108 } 109 110 s_chan = hw_get_channel_chan(mode, sec_chan, NULL); 111 if (!s_chan) 112 return 0; 113 114 wpa_printf(MSG_DEBUG, 115 "HT40: control channel: %d secondary channel: %d", 116 pri_chan, sec_chan); 117 118 /* Verify that HT40 secondary channel is an allowed 20 MHz 119 * channel */ 120 if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) || 121 (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) || 122 (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) { 123 wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", 124 sec_chan); 125 return 0; 126 } 127 128 /* 129 * Verify that HT40 primary,secondary channel pair is allowed per 130 * IEEE 802.11n Annex J. This is only needed for 5 GHz band since 131 * 2.4 GHz rules allow all cases where the secondary channel fits into 132 * the list of allowed channels (already checked above). 133 */ 134 if (mode->mode != HOSTAPD_MODE_IEEE80211A) 135 return 1; 136 137 first = pri_chan < sec_chan ? pri_chan : sec_chan; 138 139 ok = 0; 140 for (k = 0; k < ARRAY_SIZE(allowed); k++) { 141 if (first == allowed[k]) { 142 ok = 1; 143 break; 144 } 145 } 146 if (!ok) { 147 wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", 148 pri_chan, sec_chan); 149 return 0; 150 } 151 152 return 1; 153 } 154 155 156 void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan) 157 { 158 struct ieee80211_ht_operation *oper; 159 struct ieee802_11_elems elems; 160 161 *pri_chan = *sec_chan = 0; 162 163 ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 164 if (elems.ht_operation) { 165 oper = (struct ieee80211_ht_operation *) elems.ht_operation; 166 *pri_chan = oper->primary_chan; 167 if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) { 168 int sec = oper->ht_param & 169 HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK; 170 if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) 171 *sec_chan = *pri_chan + 4; 172 else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) 173 *sec_chan = *pri_chan - 4; 174 } 175 } 176 } 177 178 179 int check_40mhz_5g(struct hostapd_hw_modes *mode, 180 struct wpa_scan_results *scan_res, int pri_chan, 181 int sec_chan) 182 { 183 int pri_freq, sec_freq, pri_bss, sec_bss; 184 int bss_pri_chan, bss_sec_chan; 185 size_t i; 186 int match; 187 188 if (!mode || !scan_res || !pri_chan || !sec_chan || 189 pri_chan == sec_chan) 190 return 0; 191 192 pri_freq = hw_get_freq(mode, pri_chan); 193 sec_freq = hw_get_freq(mode, sec_chan); 194 195 /* 196 * Switch PRI/SEC channels if Beacons were detected on selected SEC 197 * channel, but not on selected PRI channel. 198 */ 199 pri_bss = sec_bss = 0; 200 for (i = 0; i < scan_res->num; i++) { 201 struct wpa_scan_res *bss = scan_res->res[i]; 202 if (bss->freq == pri_freq) 203 pri_bss++; 204 else if (bss->freq == sec_freq) 205 sec_bss++; 206 } 207 if (sec_bss && !pri_bss) { 208 wpa_printf(MSG_INFO, 209 "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes"); 210 return 2; 211 } 212 213 /* 214 * Match PRI/SEC channel with any existing HT40 BSS on the same 215 * channels that we are about to use (if already mixed order in 216 * existing BSSes, use own preference). 217 */ 218 match = 0; 219 for (i = 0; i < scan_res->num; i++) { 220 struct wpa_scan_res *bss = scan_res->res[i]; 221 get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 222 if (pri_chan == bss_pri_chan && 223 sec_chan == bss_sec_chan) { 224 match = 1; 225 break; 226 } 227 } 228 if (!match) { 229 for (i = 0; i < scan_res->num; i++) { 230 struct wpa_scan_res *bss = scan_res->res[i]; 231 get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); 232 if (pri_chan == bss_sec_chan && 233 sec_chan == bss_pri_chan) { 234 wpa_printf(MSG_INFO, "Switch own primary and " 235 "secondary channel due to BSS " 236 "overlap with " MACSTR, 237 MAC2STR(bss->bssid)); 238 return 2; 239 } 240 } 241 } 242 243 return 1; 244 } 245 246 247 static int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, 248 int end) 249 { 250 struct ieee802_11_elems elems; 251 struct ieee80211_ht_operation *oper; 252 253 if (bss->freq < start || bss->freq > end || bss->freq == pri_freq) 254 return 0; 255 256 ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); 257 if (!elems.ht_capabilities) { 258 wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: " 259 MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 260 return 1; 261 } 262 263 if (elems.ht_operation) { 264 oper = (struct ieee80211_ht_operation *) elems.ht_operation; 265 if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK) 266 return 0; 267 268 wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: " 269 MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq); 270 return 1; 271 } 272 return 0; 273 } 274 275 276 int check_40mhz_2g4(struct hostapd_hw_modes *mode, 277 struct wpa_scan_results *scan_res, int pri_chan, 278 int sec_chan) 279 { 280 int pri_freq, sec_freq; 281 int affected_start, affected_end; 282 size_t i; 283 284 if (!mode || !scan_res || !pri_chan || !sec_chan || 285 pri_chan == sec_chan) 286 return 0; 287 288 pri_freq = hw_get_freq(mode, pri_chan); 289 sec_freq = hw_get_freq(mode, sec_chan); 290 291 affected_start = (pri_freq + sec_freq) / 2 - 25; 292 affected_end = (pri_freq + sec_freq) / 2 + 25; 293 wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", 294 affected_start, affected_end); 295 for (i = 0; i < scan_res->num; i++) { 296 struct wpa_scan_res *bss = scan_res->res[i]; 297 int pri = bss->freq; 298 int sec = pri; 299 struct ieee802_11_elems elems; 300 301 /* Check for overlapping 20 MHz BSS */ 302 if (check_20mhz_bss(bss, pri_freq, affected_start, 303 affected_end)) { 304 wpa_printf(MSG_DEBUG, 305 "Overlapping 20 MHz BSS is found"); 306 return 0; 307 } 308 309 get_pri_sec_chan(bss, &pri_chan, &sec_chan); 310 311 if (sec_chan) { 312 if (sec_chan < pri_chan) 313 sec = pri - 20; 314 else 315 sec = pri + 20; 316 } 317 318 if ((pri < affected_start || pri > affected_end) && 319 (sec < affected_start || sec > affected_end)) 320 continue; /* not within affected channel range */ 321 322 wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR 323 " freq=%d pri=%d sec=%d", 324 MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); 325 326 if (sec_chan) { 327 if (pri_freq != pri || sec_freq != sec) { 328 wpa_printf(MSG_DEBUG, 329 "40 MHz pri/sec mismatch with BSS " 330 MACSTR 331 " <%d,%d> (chan=%d%c) vs. <%d,%d>", 332 MAC2STR(bss->bssid), 333 pri, sec, pri_chan, 334 sec > pri ? '+' : '-', 335 pri_freq, sec_freq); 336 return 0; 337 } 338 } 339 340 ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 341 0); 342 if (elems.ht_capabilities) { 343 struct ieee80211_ht_capabilities *ht_cap = 344 (struct ieee80211_ht_capabilities *) 345 elems.ht_capabilities; 346 347 if (le_to_host16(ht_cap->ht_capabilities_info) & 348 HT_CAP_INFO_40MHZ_INTOLERANT) { 349 wpa_printf(MSG_DEBUG, 350 "40 MHz Intolerant is set on channel %d in BSS " 351 MACSTR, pri, MAC2STR(bss->bssid)); 352 return 0; 353 } 354 } 355 } 356 357 return 1; 358 } 359 360 361 int hostapd_set_freq_params(struct hostapd_freq_params *data, 362 enum hostapd_hw_mode mode, 363 int freq, int channel, int ht_enabled, 364 int vht_enabled, int sec_channel_offset, 365 int vht_oper_chwidth, int center_segment0, 366 int center_segment1, u32 vht_caps) 367 { 368 os_memset(data, 0, sizeof(*data)); 369 data->mode = mode; 370 data->freq = freq; 371 data->channel = channel; 372 data->ht_enabled = ht_enabled; 373 data->vht_enabled = vht_enabled; 374 data->sec_channel_offset = sec_channel_offset; 375 data->center_freq1 = freq + sec_channel_offset * 10; 376 data->center_freq2 = 0; 377 data->bandwidth = sec_channel_offset ? 40 : 20; 378 379 if (data->vht_enabled) switch (vht_oper_chwidth) { 380 case VHT_CHANWIDTH_USE_HT: 381 if (center_segment1 || 382 (center_segment0 != 0 && 383 5000 + center_segment0 * 5 != data->center_freq1 && 384 2407 + center_segment0 * 5 != data->center_freq1)) 385 return -1; 386 break; 387 case VHT_CHANWIDTH_80P80MHZ: 388 if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) { 389 wpa_printf(MSG_ERROR, 390 "80+80 channel width is not supported!"); 391 return -1; 392 } 393 if (center_segment1 == center_segment0 + 4 || 394 center_segment1 == center_segment0 - 4) 395 return -1; 396 data->center_freq2 = 5000 + center_segment1 * 5; 397 /* fall through */ 398 case VHT_CHANWIDTH_80MHZ: 399 data->bandwidth = 80; 400 if ((vht_oper_chwidth == VHT_CHANWIDTH_80MHZ && 401 center_segment1) || 402 (vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ && 403 !center_segment1) || 404 !sec_channel_offset) 405 return -1; 406 if (!center_segment0) { 407 if (channel <= 48) 408 center_segment0 = 42; 409 else if (channel <= 64) 410 center_segment0 = 58; 411 else if (channel <= 112) 412 center_segment0 = 106; 413 else if (channel <= 128) 414 center_segment0 = 122; 415 else if (channel <= 144) 416 center_segment0 = 138; 417 else if (channel <= 161) 418 center_segment0 = 155; 419 data->center_freq1 = 5000 + center_segment0 * 5; 420 } else { 421 /* 422 * Note: HT/VHT config and params are coupled. Check if 423 * HT40 channel band is in VHT80 Pri channel band 424 * configuration. 425 */ 426 if (center_segment0 == channel + 6 || 427 center_segment0 == channel + 2 || 428 center_segment0 == channel - 2 || 429 center_segment0 == channel - 6) 430 data->center_freq1 = 5000 + center_segment0 * 5; 431 else 432 return -1; 433 } 434 break; 435 case VHT_CHANWIDTH_160MHZ: 436 data->bandwidth = 160; 437 if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ | 438 VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) { 439 wpa_printf(MSG_ERROR, 440 "160MHZ channel width is not supported!"); 441 return -1; 442 } 443 if (center_segment1) 444 return -1; 445 if (!sec_channel_offset) 446 return -1; 447 /* 448 * Note: HT/VHT config and params are coupled. Check if 449 * HT40 channel band is in VHT160 channel band configuration. 450 */ 451 if (center_segment0 == channel + 14 || 452 center_segment0 == channel + 10 || 453 center_segment0 == channel + 6 || 454 center_segment0 == channel + 2 || 455 center_segment0 == channel - 2 || 456 center_segment0 == channel - 6 || 457 center_segment0 == channel - 10 || 458 center_segment0 == channel - 14) 459 data->center_freq1 = 5000 + center_segment0 * 5; 460 else 461 return -1; 462 break; 463 } 464 465 return 0; 466 } 467 468 469 void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps, 470 int disabled) 471 { 472 /* Masking these out disables HT40 */ 473 le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET | 474 HT_CAP_INFO_SHORT_GI40MHZ); 475 476 if (disabled) 477 htcaps->ht_capabilities_info &= ~msk; 478 else 479 htcaps->ht_capabilities_info |= msk; 480 } 481 482 483 #ifdef CONFIG_IEEE80211AC 484 485 static int _ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, 486 const char *name) 487 { 488 u32 req_cap = conf & cap; 489 490 /* 491 * Make sure we support all requested capabilities. 492 * NOTE: We assume that 'cap' represents a capability mask, 493 * not a discrete value. 494 */ 495 if ((hw & req_cap) != req_cap) { 496 wpa_printf(MSG_ERROR, 497 "Driver does not support configured VHT capability [%s]", 498 name); 499 return 0; 500 } 501 return 1; 502 } 503 504 505 static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask, 506 unsigned int shift, 507 const char *name) 508 { 509 u32 hw_max = hw & mask; 510 u32 conf_val = conf & mask; 511 512 if (conf_val > hw_max) { 513 wpa_printf(MSG_ERROR, 514 "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)", 515 name, conf_val >> shift, hw_max >> shift); 516 return 0; 517 } 518 return 1; 519 } 520 521 522 int ieee80211ac_cap_check(u32 hw, u32 conf) 523 { 524 #define VHT_CAP_CHECK(cap) \ 525 do { \ 526 if (!_ieee80211ac_cap_check(hw, conf, cap, #cap)) \ 527 return 0; \ 528 } while (0) 529 530 #define VHT_CAP_CHECK_MAX(cap) \ 531 do { \ 532 if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \ 533 #cap)) \ 534 return 0; \ 535 } while (0) 536 537 VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK); 538 VHT_CAP_CHECK_MAX(VHT_CAP_SUPP_CHAN_WIDTH_MASK); 539 VHT_CAP_CHECK(VHT_CAP_RXLDPC); 540 VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80); 541 VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160); 542 VHT_CAP_CHECK(VHT_CAP_TXSTBC); 543 VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK); 544 VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE); 545 VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE); 546 VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX); 547 VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX); 548 VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE); 549 VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE); 550 VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS); 551 VHT_CAP_CHECK(VHT_CAP_HTC_VHT); 552 VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX); 553 VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB); 554 VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB); 555 VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN); 556 VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN); 557 558 #undef VHT_CAP_CHECK 559 #undef VHT_CAP_CHECK_MAX 560 561 return 1; 562 } 563 564 #endif /* CONFIG_IEEE80211AC */ 565 566 567 u32 num_chan_to_bw(int num_chans) 568 { 569 switch (num_chans) { 570 case 2: 571 case 4: 572 case 8: 573 return num_chans * 20; 574 default: 575 return 20; 576 } 577 } 578 579 580 /* check if BW is applicable for channel */ 581 int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw, 582 int ht40_plus, int pri) 583 { 584 u32 bw_mask; 585 586 switch (bw) { 587 case 20: 588 bw_mask = HOSTAPD_CHAN_WIDTH_20; 589 break; 590 case 40: 591 /* HT 40 MHz support declared only for primary channel, 592 * just skip 40 MHz secondary checking */ 593 if (pri && ht40_plus) 594 bw_mask = HOSTAPD_CHAN_WIDTH_40P; 595 else if (pri && !ht40_plus) 596 bw_mask = HOSTAPD_CHAN_WIDTH_40M; 597 else 598 bw_mask = 0; 599 break; 600 case 80: 601 bw_mask = HOSTAPD_CHAN_WIDTH_80; 602 break; 603 case 160: 604 bw_mask = HOSTAPD_CHAN_WIDTH_160; 605 break; 606 default: 607 bw_mask = 0; 608 break; 609 } 610 611 return (chan->allowed_bw & bw_mask) == bw_mask; 612 } 613 614 615 /* check if channel is allowed to be used as primary */ 616 int chan_pri_allowed(const struct hostapd_channel_data *chan) 617 { 618 return !(chan->flag & HOSTAPD_CHAN_DISABLED) && 619 (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20); 620 } 621