1 /* 2 * hostapd / IEEE 802.11n HT 3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4 * Copyright (c) 2007-2008, Intel Corporation 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 "utils/eloop.h" 14 #include "common/ieee802_11_defs.h" 15 #include "hostapd.h" 16 #include "ap_config.h" 17 #include "sta_info.h" 18 #include "beacon.h" 19 #include "ieee802_11.h" 20 #include "hw_features.h" 21 #include "ap_drv_ops.h" 22 23 hostapd_eid_ht_capabilities(struct hostapd_data * hapd,u8 * eid)24 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) 25 { 26 struct ieee80211_ht_capabilities *cap; 27 u8 *pos = eid; 28 29 if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || 30 hapd->conf->disable_11n || is_6ghz_op_class(hapd->iconf->op_class)) 31 return eid; 32 33 *pos++ = WLAN_EID_HT_CAP; 34 *pos++ = sizeof(*cap); 35 36 cap = (struct ieee80211_ht_capabilities *) pos; 37 os_memset(cap, 0, sizeof(*cap)); 38 cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab); 39 cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params; 40 os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set, 41 16); 42 43 /* TODO: ht_extended_capabilities (now fully disabled) */ 44 /* TODO: tx_bf_capability_info (now fully disabled) */ 45 /* TODO: asel_capabilities (now fully disabled) */ 46 47 pos += sizeof(*cap); 48 49 if (hapd->iconf->obss_interval) { 50 struct ieee80211_obss_scan_parameters *scan_params; 51 52 *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS; 53 *pos++ = sizeof(*scan_params); 54 55 scan_params = (struct ieee80211_obss_scan_parameters *) pos; 56 os_memset(scan_params, 0, sizeof(*scan_params)); 57 scan_params->width_trigger_scan_interval = 58 host_to_le16(hapd->iconf->obss_interval); 59 60 /* Fill in default values for remaining parameters 61 * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */ 62 scan_params->scan_passive_dwell = 63 host_to_le16(20); 64 scan_params->scan_active_dwell = 65 host_to_le16(10); 66 scan_params->scan_passive_total_per_channel = 67 host_to_le16(200); 68 scan_params->scan_active_total_per_channel = 69 host_to_le16(20); 70 scan_params->channel_transition_delay_factor = 71 host_to_le16(5); 72 scan_params->scan_activity_threshold = 73 host_to_le16(25); 74 75 pos += sizeof(*scan_params); 76 } 77 78 return pos; 79 } 80 81 set_ht_param(struct hostapd_data * hapd,struct ieee80211_ht_operation * oper)82 static void set_ht_param(struct hostapd_data *hapd, 83 struct ieee80211_ht_operation *oper) 84 { 85 int secondary_channel = hapd->iconf->secondary_channel; 86 #ifdef CONFIG_IEEE80211BE 87 enum oper_chan_width chwidth = hostapd_get_oper_chwidth(hapd->iconf); 88 u16 bw = 0, punct_bitmap = hostapd_get_punct_bitmap(hapd); 89 u8 offset, chan_bit_pos; 90 91 switch (chwidth) { 92 case CONF_OPER_CHWIDTH_80MHZ: 93 bw = 80; 94 offset = 6; 95 break; 96 case CONF_OPER_CHWIDTH_160MHZ: 97 bw = 160; 98 offset = 14; 99 break; 100 case CONF_OPER_CHWIDTH_320MHZ: 101 bw = 320; 102 offset = 30; 103 break; 104 default: 105 goto no_update; 106 } 107 108 chan_bit_pos = (hapd->iconf->channel - 109 hostapd_get_oper_centr_freq_seg0_idx(hapd->iconf) + 110 offset) / 4; 111 /* Check if secondary channel is punctured */ 112 if (bw >= 80 && punct_bitmap && secondary_channel && 113 (punct_bitmap & BIT(chan_bit_pos + secondary_channel))) 114 return; /* Do not indicate punctured secondary channel for HT */ 115 no_update: 116 #endif /* CONFIG_IEEE80211BE */ 117 118 if (secondary_channel == 1) 119 oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | 120 HT_INFO_HT_PARAM_STA_CHNL_WIDTH; 121 if (secondary_channel == -1) 122 oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | 123 HT_INFO_HT_PARAM_STA_CHNL_WIDTH; 124 } 125 126 hostapd_eid_ht_operation(struct hostapd_data * hapd,u8 * eid)127 u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) 128 { 129 struct ieee80211_ht_operation *oper; 130 u8 *pos = eid; 131 132 if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n || 133 is_6ghz_op_class(hapd->iconf->op_class)) 134 return eid; 135 136 *pos++ = WLAN_EID_HT_OPERATION; 137 *pos++ = sizeof(*oper); 138 139 oper = (struct ieee80211_ht_operation *) pos; 140 os_memset(oper, 0, sizeof(*oper)); 141 142 oper->primary_chan = hapd->iconf->channel; 143 oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); 144 set_ht_param(hapd, oper); 145 146 pos += sizeof(*oper); 147 148 return pos; 149 } 150 151 152 /* 153 op_mode 154 Set to 0 (HT pure) under the following conditions 155 - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or 156 - all STAs in the BSS are 20 MHz HT in 20 MHz BSS 157 Set to 1 (HT non-member protection) if there may be non-HT STAs 158 in both the primary and the secondary channel 159 Set to 2 if only HT STAs are associated in BSS, 160 however and at least one 20 MHz HT STA is associated 161 Set to 3 (HT mixed mode) when one or more non-HT STAs are associated 162 */ hostapd_ht_operation_update(struct hostapd_iface * iface)163 int hostapd_ht_operation_update(struct hostapd_iface *iface) 164 { 165 u16 cur_op_mode, new_op_mode; 166 int op_mode_changes = 0; 167 168 if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) 169 return 0; 170 171 wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", 172 __func__, iface->ht_op_mode); 173 174 if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) 175 && iface->num_sta_ht_no_gf) { 176 iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; 177 op_mode_changes++; 178 } else if ((iface->ht_op_mode & 179 HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) && 180 iface->num_sta_ht_no_gf == 0) { 181 iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; 182 op_mode_changes++; 183 } 184 185 if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && 186 (iface->num_sta_no_ht || iface->olbc_ht)) { 187 iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; 188 op_mode_changes++; 189 } else if ((iface->ht_op_mode & 190 HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && 191 (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { 192 iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; 193 op_mode_changes++; 194 } 195 196 if (iface->num_sta_no_ht) 197 new_op_mode = HT_PROT_NON_HT_MIXED; 198 else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz) 199 new_op_mode = HT_PROT_20MHZ_PROTECTION; 200 else if (iface->olbc_ht) 201 new_op_mode = HT_PROT_NONMEMBER_PROTECTION; 202 else 203 new_op_mode = HT_PROT_NO_PROTECTION; 204 205 cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK; 206 if (cur_op_mode != new_op_mode) { 207 iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK; 208 iface->ht_op_mode |= new_op_mode; 209 op_mode_changes++; 210 } 211 212 wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", 213 __func__, iface->ht_op_mode, op_mode_changes); 214 215 return op_mode_changes; 216 } 217 218 is_40_allowed(struct hostapd_iface * iface,int channel)219 static int is_40_allowed(struct hostapd_iface *iface, int channel) 220 { 221 int pri_freq, sec_freq; 222 int affected_start, affected_end; 223 int pri = 2407 + 5 * channel; 224 225 if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) 226 return 1; 227 228 pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); 229 230 if (iface->conf->secondary_channel > 0) 231 sec_freq = pri_freq + 20; 232 else 233 sec_freq = pri_freq - 20; 234 235 affected_start = (pri_freq + sec_freq) / 2 - 25; 236 affected_end = (pri_freq + sec_freq) / 2 + 25; 237 if ((pri < affected_start || pri > affected_end)) 238 return 1; /* not within affected channel range */ 239 240 wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz", 241 affected_start, affected_end); 242 wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri); 243 return 0; 244 } 245 246 hostapd_2040_coex_action(struct hostapd_data * hapd,const struct ieee80211_mgmt * mgmt,size_t len)247 void hostapd_2040_coex_action(struct hostapd_data *hapd, 248 const struct ieee80211_mgmt *mgmt, size_t len) 249 { 250 struct hostapd_iface *iface = hapd->iface; 251 struct ieee80211_2040_bss_coex_ie *bc_ie; 252 struct ieee80211_2040_intol_chan_report *ic_report; 253 int is_ht40_allowed = 1; 254 int i; 255 const u8 *start = (const u8 *) mgmt; 256 const u8 *data = start + IEEE80211_HDRLEN + 2; 257 struct sta_info *sta; 258 259 wpa_printf(MSG_DEBUG, 260 "HT: Received 20/40 BSS Coexistence Management frame from " 261 MACSTR, MAC2STR(mgmt->sa)); 262 263 hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, 264 HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", 265 mgmt->u.action.u.public_action.action); 266 267 if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { 268 wpa_printf(MSG_DEBUG, 269 "Ignore 20/40 BSS Coexistence Management frame since 40 MHz capability is not enabled"); 270 return; 271 } 272 273 if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) { 274 wpa_printf(MSG_DEBUG, 275 "Ignore too short 20/40 BSS Coexistence Management frame"); 276 return; 277 } 278 279 /* 20/40 BSS Coexistence element */ 280 bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; 281 if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || 282 bc_ie->length < 1) { 283 wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report", 284 bc_ie->element_id, bc_ie->length); 285 return; 286 } 287 if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) { 288 wpa_printf(MSG_DEBUG, 289 "Truncated 20/40 BSS Coexistence element"); 290 return; 291 } 292 data += 2 + bc_ie->length; 293 294 wpa_printf(MSG_DEBUG, 295 "20/40 BSS Coexistence Information field: 0x%x (%s%s%s%s%s%s)", 296 bc_ie->coex_param, 297 (bc_ie->coex_param & BIT(0)) ? "[InfoReq]" : "", 298 (bc_ie->coex_param & BIT(1)) ? "[40MHzIntolerant]" : "", 299 (bc_ie->coex_param & BIT(2)) ? "[20MHzBSSWidthReq]" : "", 300 (bc_ie->coex_param & BIT(3)) ? "[OBSSScanExemptionReq]" : "", 301 (bc_ie->coex_param & BIT(4)) ? 302 "[OBSSScanExemptionGrant]" : "", 303 (bc_ie->coex_param & (BIT(5) | BIT(6) | BIT(7))) ? 304 "[Reserved]" : ""); 305 306 if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { 307 /* Intra-BSS communication prohibiting 20/40 MHz BSS operation 308 */ 309 sta = ap_get_sta(hapd, mgmt->sa); 310 if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { 311 wpa_printf(MSG_DEBUG, 312 "Ignore intra-BSS 20/40 BSS Coexistence Management frame from not-associated STA"); 313 return; 314 } 315 316 hostapd_logger(hapd, mgmt->sa, 317 HOSTAPD_MODULE_IEEE80211, 318 HOSTAPD_LEVEL_DEBUG, 319 "20 MHz BSS width request bit is set in BSS coexistence information field"); 320 is_ht40_allowed = 0; 321 } 322 323 if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { 324 /* Inter-BSS communication prohibiting 20/40 MHz BSS operation 325 */ 326 hostapd_logger(hapd, mgmt->sa, 327 HOSTAPD_MODULE_IEEE80211, 328 HOSTAPD_LEVEL_DEBUG, 329 "40 MHz intolerant bit is set in BSS coexistence information field"); 330 is_ht40_allowed = 0; 331 } 332 333 /* 20/40 BSS Intolerant Channel Report element (zero or more times) */ 334 while (start + len - data >= 3 && 335 data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { 336 u8 ielen = data[1]; 337 338 if (ielen > start + len - data - 2) { 339 wpa_printf(MSG_DEBUG, 340 "Truncated 20/40 BSS Intolerant Channel Report element"); 341 return; 342 } 343 ic_report = (struct ieee80211_2040_intol_chan_report *) data; 344 wpa_printf(MSG_DEBUG, 345 "20/40 BSS Intolerant Channel Report: Operating Class %u", 346 ic_report->op_class); 347 348 /* Go through the channel report to find any BSS there in the 349 * affected channel range */ 350 for (i = 0; i < ielen - 1; i++) { 351 u8 chan = ic_report->variable[i]; 352 353 if (chan == iface->conf->channel) 354 continue; /* matching own primary channel */ 355 if (is_40_allowed(iface, chan)) 356 continue; /* not within affected channels */ 357 hostapd_logger(hapd, mgmt->sa, 358 HOSTAPD_MODULE_IEEE80211, 359 HOSTAPD_LEVEL_DEBUG, 360 "20_40_INTOLERANT channel %d reported", 361 chan); 362 is_ht40_allowed = 0; 363 } 364 365 data += 2 + ielen; 366 } 367 wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", 368 is_ht40_allowed, iface->num_sta_ht40_intolerant); 369 370 if (!is_ht40_allowed && 371 (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { 372 if (iface->conf->secondary_channel) { 373 hostapd_logger(hapd, mgmt->sa, 374 HOSTAPD_MODULE_IEEE80211, 375 HOSTAPD_LEVEL_INFO, 376 "Switching to 20 MHz operation"); 377 iface->conf->secondary_channel = 0; 378 ieee802_11_set_beacons(iface); 379 } 380 if (!iface->num_sta_ht40_intolerant && 381 iface->conf->obss_interval) { 382 unsigned int delay_time; 383 delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * 384 iface->conf->obss_interval; 385 eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface, 386 NULL); 387 eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, 388 hapd->iface, NULL); 389 wpa_printf(MSG_DEBUG, 390 "Reschedule HT 20/40 timeout to occur in %u seconds", 391 delay_time); 392 } 393 } 394 } 395 396 copy_sta_ht_capab(struct hostapd_data * hapd,struct sta_info * sta,const u8 * ht_capab)397 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, 398 const u8 *ht_capab) 399 { 400 /* 401 * Disable HT caps for STAs associated to no-HT BSSes, or for stations 402 * that did not specify a valid WMM IE in the (Re)Association Request 403 * frame. 404 */ 405 if (!ht_capab || !(sta->flags & WLAN_STA_WMM) || 406 !hapd->iconf->ieee80211n || hapd->conf->disable_11n) { 407 sta->flags &= ~WLAN_STA_HT; 408 os_free(sta->ht_capabilities); 409 sta->ht_capabilities = NULL; 410 return WLAN_STATUS_SUCCESS; 411 } 412 413 if (sta->ht_capabilities == NULL) { 414 sta->ht_capabilities = 415 os_zalloc(sizeof(struct ieee80211_ht_capabilities)); 416 if (sta->ht_capabilities == NULL) 417 return WLAN_STATUS_UNSPECIFIED_FAILURE; 418 } 419 420 sta->flags |= WLAN_STA_HT; 421 os_memcpy(sta->ht_capabilities, ht_capab, 422 sizeof(struct ieee80211_ht_capabilities)); 423 424 return WLAN_STATUS_SUCCESS; 425 } 426 427 ht40_intolerant_add(struct hostapd_iface * iface,struct sta_info * sta)428 void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta) 429 { 430 if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) 431 return; 432 433 wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR 434 " in Association Request", MAC2STR(sta->addr)); 435 436 if (sta->ht40_intolerant_set) 437 return; 438 439 sta->ht40_intolerant_set = 1; 440 iface->num_sta_ht40_intolerant++; 441 eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); 442 443 if (iface->conf->secondary_channel && 444 (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { 445 iface->conf->secondary_channel = 0; 446 ieee802_11_set_beacons(iface); 447 } 448 } 449 450 ht40_intolerant_remove(struct hostapd_iface * iface,struct sta_info * sta)451 void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta) 452 { 453 if (!sta->ht40_intolerant_set) 454 return; 455 456 sta->ht40_intolerant_set = 0; 457 iface->num_sta_ht40_intolerant--; 458 459 if (iface->num_sta_ht40_intolerant == 0 && 460 iface->conf->obss_interval && 461 (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && 462 (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { 463 unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * 464 iface->conf->obss_interval; 465 wpa_printf(MSG_DEBUG, 466 "HT: Start 20->40 MHz transition timer (%d seconds)", 467 delay_time); 468 eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); 469 eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, 470 iface, NULL); 471 } 472 } 473 474 update_sta_ht(struct hostapd_data * hapd,struct sta_info * sta)475 static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) 476 { 477 u16 ht_capab; 478 479 ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info); 480 wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: " 481 "0x%04x", MAC2STR(sta->addr), ht_capab); 482 if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) { 483 if (!sta->no_ht_gf_set) { 484 sta->no_ht_gf_set = 1; 485 hapd->iface->num_sta_ht_no_gf++; 486 } 487 wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num " 488 "of non-gf stations %d", 489 __func__, MAC2STR(sta->addr), 490 hapd->iface->num_sta_ht_no_gf); 491 } 492 if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { 493 if (!sta->ht_20mhz_set) { 494 sta->ht_20mhz_set = 1; 495 hapd->iface->num_sta_ht_20mhz++; 496 } 497 wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of " 498 "20MHz HT STAs %d", 499 __func__, MAC2STR(sta->addr), 500 hapd->iface->num_sta_ht_20mhz); 501 } 502 503 if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT) 504 ht40_intolerant_add(hapd->iface, sta); 505 } 506 507 update_sta_no_ht(struct hostapd_data * hapd,struct sta_info * sta)508 static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta) 509 { 510 if (!sta->no_ht_set) { 511 sta->no_ht_set = 1; 512 hapd->iface->num_sta_no_ht++; 513 } 514 if (hapd->iconf->ieee80211n) { 515 wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of " 516 "non-HT stations %d", 517 __func__, MAC2STR(sta->addr), 518 hapd->iface->num_sta_no_ht); 519 } 520 } 521 522 update_ht_state(struct hostapd_data * hapd,struct sta_info * sta)523 int update_ht_state(struct hostapd_data *hapd, struct sta_info *sta) 524 { 525 if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) 526 update_sta_ht(hapd, sta); 527 else 528 update_sta_no_ht(hapd, sta); 529 530 return hostapd_ht_operation_update(hapd->iface); 531 } 532 533 hostapd_get_ht_capab(struct hostapd_data * hapd,struct ieee80211_ht_capabilities * ht_cap,struct ieee80211_ht_capabilities * neg_ht_cap)534 void hostapd_get_ht_capab(struct hostapd_data *hapd, 535 struct ieee80211_ht_capabilities *ht_cap, 536 struct ieee80211_ht_capabilities *neg_ht_cap) 537 { 538 u16 cap; 539 540 if (ht_cap == NULL) 541 return; 542 os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap)); 543 cap = le_to_host16(neg_ht_cap->ht_capabilities_info); 544 545 /* 546 * Mask out HT features we don't support, but don't overwrite 547 * non-symmetric features like STBC and SMPS. Just because 548 * we're not in dynamic SMPS mode the STA might still be. 549 */ 550 cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK | 551 HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK); 552 553 /* 554 * STBC needs to be handled specially 555 * if we don't support RX STBC, mask out TX STBC in the STA's HT caps 556 * if we don't support TX STBC, mask out RX STBC in the STA's HT caps 557 */ 558 if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK)) 559 cap &= ~HT_CAP_INFO_TX_STBC; 560 if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC)) 561 cap &= ~HT_CAP_INFO_RX_STBC_MASK; 562 563 neg_ht_cap->ht_capabilities_info = host_to_le16(cap); 564 } 565 566 ap_ht2040_timeout(void * eloop_data,void * user_data)567 void ap_ht2040_timeout(void *eloop_data, void *user_data) 568 { 569 struct hostapd_iface *iface = eloop_data; 570 571 wpa_printf(MSG_INFO, "Switching to 40 MHz operation"); 572 573 iface->conf->secondary_channel = iface->secondary_ch; 574 ieee802_11_set_beacons(iface); 575 } 576