1214501Srpaulo/* 2214501Srpaulo * hostapd / IEEE 802.11n HT 3214501Srpaulo * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4214501Srpaulo * Copyright (c) 2007-2008, Intel Corporation 5214501Srpaulo * 6281806Srpaulo * This software may be distributed under the terms of the BSD license. 7281806Srpaulo * See README for more details. 8214501Srpaulo */ 9214501Srpaulo 10214501Srpaulo#include "utils/includes.h" 11214501Srpaulo 12214501Srpaulo#include "utils/common.h" 13281806Srpaulo#include "utils/eloop.h" 14214501Srpaulo#include "common/ieee802_11_defs.h" 15214501Srpaulo#include "hostapd.h" 16214501Srpaulo#include "ap_config.h" 17214501Srpaulo#include "sta_info.h" 18214501Srpaulo#include "beacon.h" 19214501Srpaulo#include "ieee802_11.h" 20281806Srpaulo#include "hw_features.h" 21281806Srpaulo#include "ap_drv_ops.h" 22214501Srpaulo 23214501Srpaulo 24214501Srpaulou8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) 25214501Srpaulo{ 26214501Srpaulo struct ieee80211_ht_capabilities *cap; 27214501Srpaulo u8 *pos = eid; 28214501Srpaulo 29252726Srpaulo if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode || 30252726Srpaulo hapd->conf->disable_11n) 31214501Srpaulo return eid; 32214501Srpaulo 33214501Srpaulo *pos++ = WLAN_EID_HT_CAP; 34214501Srpaulo *pos++ = sizeof(*cap); 35214501Srpaulo 36214501Srpaulo cap = (struct ieee80211_ht_capabilities *) pos; 37214501Srpaulo os_memset(cap, 0, sizeof(*cap)); 38214501Srpaulo cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab); 39214501Srpaulo cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params; 40214501Srpaulo os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set, 41214501Srpaulo 16); 42214501Srpaulo 43214501Srpaulo /* TODO: ht_extended_capabilities (now fully disabled) */ 44214501Srpaulo /* TODO: tx_bf_capability_info (now fully disabled) */ 45214501Srpaulo /* TODO: asel_capabilities (now fully disabled) */ 46214501Srpaulo 47214501Srpaulo pos += sizeof(*cap); 48214501Srpaulo 49281806Srpaulo if (hapd->iconf->obss_interval) { 50281806Srpaulo struct ieee80211_obss_scan_parameters *scan_params; 51281806Srpaulo 52281806Srpaulo *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS; 53281806Srpaulo *pos++ = sizeof(*scan_params); 54281806Srpaulo 55281806Srpaulo scan_params = (struct ieee80211_obss_scan_parameters *) pos; 56281806Srpaulo os_memset(scan_params, 0, sizeof(*scan_params)); 57281806Srpaulo scan_params->width_trigger_scan_interval = 58281806Srpaulo host_to_le16(hapd->iconf->obss_interval); 59281806Srpaulo 60281806Srpaulo /* Fill in default values for remaining parameters 61281806Srpaulo * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */ 62281806Srpaulo scan_params->scan_passive_dwell = 63281806Srpaulo host_to_le16(20); 64281806Srpaulo scan_params->scan_active_dwell = 65281806Srpaulo host_to_le16(10); 66281806Srpaulo scan_params->scan_passive_total_per_channel = 67281806Srpaulo host_to_le16(200); 68281806Srpaulo scan_params->scan_active_total_per_channel = 69281806Srpaulo host_to_le16(20); 70281806Srpaulo scan_params->channel_transition_delay_factor = 71281806Srpaulo host_to_le16(5); 72281806Srpaulo scan_params->scan_activity_threshold = 73281806Srpaulo host_to_le16(25); 74281806Srpaulo 75281806Srpaulo pos += sizeof(*scan_params); 76281806Srpaulo } 77281806Srpaulo 78214501Srpaulo return pos; 79214501Srpaulo} 80214501Srpaulo 81214501Srpaulo 82214501Srpaulou8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) 83214501Srpaulo{ 84214501Srpaulo struct ieee80211_ht_operation *oper; 85214501Srpaulo u8 *pos = eid; 86214501Srpaulo 87252726Srpaulo if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n) 88214501Srpaulo return eid; 89214501Srpaulo 90214501Srpaulo *pos++ = WLAN_EID_HT_OPERATION; 91214501Srpaulo *pos++ = sizeof(*oper); 92214501Srpaulo 93214501Srpaulo oper = (struct ieee80211_ht_operation *) pos; 94214501Srpaulo os_memset(oper, 0, sizeof(*oper)); 95214501Srpaulo 96281806Srpaulo oper->primary_chan = hapd->iconf->channel; 97214501Srpaulo oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); 98214501Srpaulo if (hapd->iconf->secondary_channel == 1) 99214501Srpaulo oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | 100281806Srpaulo HT_INFO_HT_PARAM_STA_CHNL_WIDTH; 101214501Srpaulo if (hapd->iconf->secondary_channel == -1) 102214501Srpaulo oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | 103281806Srpaulo HT_INFO_HT_PARAM_STA_CHNL_WIDTH; 104214501Srpaulo 105214501Srpaulo pos += sizeof(*oper); 106214501Srpaulo 107214501Srpaulo return pos; 108214501Srpaulo} 109214501Srpaulo 110214501Srpaulo 111214501Srpaulo/* 112214501Srpauloop_mode 113214501SrpauloSet to 0 (HT pure) under the followign conditions 114214501Srpaulo - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or 115214501Srpaulo - all STAs in the BSS are 20 MHz HT in 20 MHz BSS 116214501SrpauloSet to 1 (HT non-member protection) if there may be non-HT STAs 117214501Srpaulo in both the primary and the secondary channel 118214501SrpauloSet to 2 if only HT STAs are associated in BSS, 119214501Srpaulo however and at least one 20 MHz HT STA is associated 120214501SrpauloSet to 3 (HT mixed mode) when one or more non-HT STAs are associated 121214501Srpaulo*/ 122214501Srpauloint hostapd_ht_operation_update(struct hostapd_iface *iface) 123214501Srpaulo{ 124214501Srpaulo u16 cur_op_mode, new_op_mode; 125214501Srpaulo int op_mode_changes = 0; 126214501Srpaulo 127214501Srpaulo if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) 128214501Srpaulo return 0; 129214501Srpaulo 130214501Srpaulo wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", 131214501Srpaulo __func__, iface->ht_op_mode); 132214501Srpaulo 133281806Srpaulo if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) 134214501Srpaulo && iface->num_sta_ht_no_gf) { 135281806Srpaulo iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; 136214501Srpaulo op_mode_changes++; 137214501Srpaulo } else if ((iface->ht_op_mode & 138281806Srpaulo HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) && 139214501Srpaulo iface->num_sta_ht_no_gf == 0) { 140281806Srpaulo iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT; 141214501Srpaulo op_mode_changes++; 142214501Srpaulo } 143214501Srpaulo 144281806Srpaulo if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && 145214501Srpaulo (iface->num_sta_no_ht || iface->olbc_ht)) { 146281806Srpaulo iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; 147214501Srpaulo op_mode_changes++; 148214501Srpaulo } else if ((iface->ht_op_mode & 149281806Srpaulo HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) && 150214501Srpaulo (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { 151281806Srpaulo iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT; 152214501Srpaulo op_mode_changes++; 153214501Srpaulo } 154214501Srpaulo 155252726Srpaulo if (iface->num_sta_no_ht) 156281806Srpaulo new_op_mode = HT_PROT_NON_HT_MIXED; 157281806Srpaulo else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz) 158281806Srpaulo new_op_mode = HT_PROT_20MHZ_PROTECTION; 159214501Srpaulo else if (iface->olbc_ht) 160281806Srpaulo new_op_mode = HT_PROT_NONMEMBER_PROTECTION; 161214501Srpaulo else 162281806Srpaulo new_op_mode = HT_PROT_NO_PROTECTION; 163214501Srpaulo 164281806Srpaulo cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK; 165214501Srpaulo if (cur_op_mode != new_op_mode) { 166281806Srpaulo iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK; 167214501Srpaulo iface->ht_op_mode |= new_op_mode; 168214501Srpaulo op_mode_changes++; 169214501Srpaulo } 170214501Srpaulo 171214501Srpaulo wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", 172214501Srpaulo __func__, iface->ht_op_mode, op_mode_changes); 173214501Srpaulo 174214501Srpaulo return op_mode_changes; 175214501Srpaulo} 176214501Srpaulo 177214501Srpaulo 178281806Srpaulostatic int is_40_allowed(struct hostapd_iface *iface, int channel) 179281806Srpaulo{ 180281806Srpaulo int pri_freq, sec_freq; 181281806Srpaulo int affected_start, affected_end; 182281806Srpaulo int pri = 2407 + 5 * channel; 183281806Srpaulo 184281806Srpaulo if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) 185281806Srpaulo return 1; 186281806Srpaulo 187281806Srpaulo pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); 188281806Srpaulo 189281806Srpaulo if (iface->conf->secondary_channel > 0) 190281806Srpaulo sec_freq = pri_freq + 20; 191281806Srpaulo else 192281806Srpaulo sec_freq = pri_freq - 20; 193281806Srpaulo 194281806Srpaulo affected_start = (pri_freq + sec_freq) / 2 - 25; 195281806Srpaulo affected_end = (pri_freq + sec_freq) / 2 + 25; 196281806Srpaulo if ((pri < affected_start || pri > affected_end)) 197281806Srpaulo return 1; /* not within affected channel range */ 198281806Srpaulo 199281806Srpaulo wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz", 200281806Srpaulo affected_start, affected_end); 201281806Srpaulo wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri); 202281806Srpaulo return 0; 203281806Srpaulo} 204281806Srpaulo 205281806Srpaulo 206281806Srpaulovoid hostapd_2040_coex_action(struct hostapd_data *hapd, 207281806Srpaulo const struct ieee80211_mgmt *mgmt, size_t len) 208281806Srpaulo{ 209281806Srpaulo struct hostapd_iface *iface = hapd->iface; 210281806Srpaulo struct ieee80211_2040_bss_coex_ie *bc_ie; 211281806Srpaulo struct ieee80211_2040_intol_chan_report *ic_report; 212289549Srpaulo int is_ht40_allowed = 1; 213281806Srpaulo int i; 214281806Srpaulo const u8 *start = (const u8 *) mgmt; 215281806Srpaulo const u8 *data = start + IEEE80211_HDRLEN + 2; 216281806Srpaulo 217281806Srpaulo hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, 218281806Srpaulo HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d", 219281806Srpaulo mgmt->u.action.u.public_action.action); 220281806Srpaulo 221281806Srpaulo if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) 222281806Srpaulo return; 223281806Srpaulo 224281806Srpaulo if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie)) 225281806Srpaulo return; 226281806Srpaulo 227281806Srpaulo bc_ie = (struct ieee80211_2040_bss_coex_ie *) data; 228281806Srpaulo if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE || 229281806Srpaulo bc_ie->length < 1) { 230281806Srpaulo wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report", 231281806Srpaulo bc_ie->element_id, bc_ie->length); 232281806Srpaulo return; 233281806Srpaulo } 234281806Srpaulo if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length) 235281806Srpaulo return; 236281806Srpaulo data += 2 + bc_ie->length; 237281806Srpaulo 238281806Srpaulo wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x", 239281806Srpaulo bc_ie->coex_param); 240281806Srpaulo if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) { 241281806Srpaulo hostapd_logger(hapd, mgmt->sa, 242281806Srpaulo HOSTAPD_MODULE_IEEE80211, 243281806Srpaulo HOSTAPD_LEVEL_DEBUG, 244281806Srpaulo "20 MHz BSS width request bit is set in BSS coexistence information field"); 245289549Srpaulo is_ht40_allowed = 0; 246281806Srpaulo } 247281806Srpaulo 248281806Srpaulo if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) { 249281806Srpaulo hostapd_logger(hapd, mgmt->sa, 250281806Srpaulo HOSTAPD_MODULE_IEEE80211, 251281806Srpaulo HOSTAPD_LEVEL_DEBUG, 252281806Srpaulo "40 MHz intolerant bit is set in BSS coexistence information field"); 253289549Srpaulo is_ht40_allowed = 0; 254281806Srpaulo } 255281806Srpaulo 256281806Srpaulo if (start + len - data >= 3 && 257281806Srpaulo data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) { 258281806Srpaulo u8 ielen = data[1]; 259281806Srpaulo 260281806Srpaulo if (ielen > start + len - data - 2) 261281806Srpaulo return; 262281806Srpaulo ic_report = (struct ieee80211_2040_intol_chan_report *) data; 263281806Srpaulo wpa_printf(MSG_DEBUG, 264281806Srpaulo "20/40 BSS Intolerant Channel Report: Operating Class %u", 265281806Srpaulo ic_report->op_class); 266281806Srpaulo 267281806Srpaulo /* Go through the channel report to find any BSS there in the 268281806Srpaulo * affected channel range */ 269281806Srpaulo for (i = 0; i < ielen - 1; i++) { 270281806Srpaulo u8 chan = ic_report->variable[i]; 271281806Srpaulo 272281806Srpaulo if (is_40_allowed(iface, chan)) 273281806Srpaulo continue; 274281806Srpaulo hostapd_logger(hapd, mgmt->sa, 275281806Srpaulo HOSTAPD_MODULE_IEEE80211, 276281806Srpaulo HOSTAPD_LEVEL_DEBUG, 277281806Srpaulo "20_40_INTOLERANT channel %d reported", 278281806Srpaulo chan); 279289549Srpaulo is_ht40_allowed = 0; 280281806Srpaulo } 281281806Srpaulo } 282289549Srpaulo wpa_printf(MSG_DEBUG, "is_ht40_allowed=%d num_sta_ht40_intolerant=%d", 283289549Srpaulo is_ht40_allowed, iface->num_sta_ht40_intolerant); 284281806Srpaulo 285289549Srpaulo if (!is_ht40_allowed && 286281806Srpaulo (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { 287281806Srpaulo if (iface->conf->secondary_channel) { 288281806Srpaulo hostapd_logger(hapd, mgmt->sa, 289281806Srpaulo HOSTAPD_MODULE_IEEE80211, 290281806Srpaulo HOSTAPD_LEVEL_INFO, 291281806Srpaulo "Switching to 20 MHz operation"); 292281806Srpaulo iface->conf->secondary_channel = 0; 293281806Srpaulo ieee802_11_set_beacons(iface); 294281806Srpaulo } 295281806Srpaulo if (!iface->num_sta_ht40_intolerant && 296281806Srpaulo iface->conf->obss_interval) { 297281806Srpaulo unsigned int delay_time; 298281806Srpaulo delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * 299281806Srpaulo iface->conf->obss_interval; 300281806Srpaulo eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface, 301281806Srpaulo NULL); 302281806Srpaulo eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, 303281806Srpaulo hapd->iface, NULL); 304281806Srpaulo wpa_printf(MSG_DEBUG, 305281806Srpaulo "Reschedule HT 20/40 timeout to occur in %u seconds", 306281806Srpaulo delay_time); 307281806Srpaulo } 308281806Srpaulo } 309281806Srpaulo} 310281806Srpaulo 311281806Srpaulo 312252726Srpaulou16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta, 313289549Srpaulo const u8 *ht_capab) 314214501Srpaulo{ 315289549Srpaulo /* 316289549Srpaulo * Disable HT caps for STAs associated to no-HT BSSes, or for stations 317289549Srpaulo * that did not specify a valid WMM IE in the (Re)Association Request 318289549Srpaulo * frame. 319289549Srpaulo */ 320214501Srpaulo if (!ht_capab || 321289549Srpaulo !(sta->flags & WLAN_STA_WMM) || hapd->conf->disable_11n) { 322214501Srpaulo sta->flags &= ~WLAN_STA_HT; 323214501Srpaulo os_free(sta->ht_capabilities); 324214501Srpaulo sta->ht_capabilities = NULL; 325214501Srpaulo return WLAN_STATUS_SUCCESS; 326214501Srpaulo } 327214501Srpaulo 328214501Srpaulo if (sta->ht_capabilities == NULL) { 329214501Srpaulo sta->ht_capabilities = 330214501Srpaulo os_zalloc(sizeof(struct ieee80211_ht_capabilities)); 331214501Srpaulo if (sta->ht_capabilities == NULL) 332214501Srpaulo return WLAN_STATUS_UNSPECIFIED_FAILURE; 333214501Srpaulo } 334214501Srpaulo 335214501Srpaulo sta->flags |= WLAN_STA_HT; 336214501Srpaulo os_memcpy(sta->ht_capabilities, ht_capab, 337214501Srpaulo sizeof(struct ieee80211_ht_capabilities)); 338214501Srpaulo 339214501Srpaulo return WLAN_STATUS_SUCCESS; 340214501Srpaulo} 341214501Srpaulo 342214501Srpaulo 343281806Srpaulovoid ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta) 344281806Srpaulo{ 345281806Srpaulo if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) 346281806Srpaulo return; 347281806Srpaulo 348281806Srpaulo wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR 349281806Srpaulo " in Association Request", MAC2STR(sta->addr)); 350281806Srpaulo 351281806Srpaulo if (sta->ht40_intolerant_set) 352281806Srpaulo return; 353281806Srpaulo 354281806Srpaulo sta->ht40_intolerant_set = 1; 355281806Srpaulo iface->num_sta_ht40_intolerant++; 356281806Srpaulo eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); 357281806Srpaulo 358281806Srpaulo if (iface->conf->secondary_channel && 359281806Srpaulo (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { 360281806Srpaulo iface->conf->secondary_channel = 0; 361281806Srpaulo ieee802_11_set_beacons(iface); 362281806Srpaulo } 363281806Srpaulo} 364281806Srpaulo 365281806Srpaulo 366281806Srpaulovoid ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta) 367281806Srpaulo{ 368281806Srpaulo if (!sta->ht40_intolerant_set) 369281806Srpaulo return; 370281806Srpaulo 371281806Srpaulo sta->ht40_intolerant_set = 0; 372281806Srpaulo iface->num_sta_ht40_intolerant--; 373281806Srpaulo 374281806Srpaulo if (iface->num_sta_ht40_intolerant == 0 && 375281806Srpaulo (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && 376281806Srpaulo (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) { 377281806Srpaulo unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR * 378281806Srpaulo iface->conf->obss_interval; 379281806Srpaulo wpa_printf(MSG_DEBUG, 380281806Srpaulo "HT: Start 20->40 MHz transition timer (%d seconds)", 381281806Srpaulo delay_time); 382281806Srpaulo eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL); 383281806Srpaulo eloop_register_timeout(delay_time, 0, ap_ht2040_timeout, 384281806Srpaulo iface, NULL); 385281806Srpaulo } 386281806Srpaulo} 387281806Srpaulo 388281806Srpaulo 389214501Srpaulostatic void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) 390214501Srpaulo{ 391214501Srpaulo u16 ht_capab; 392214501Srpaulo 393214501Srpaulo ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info); 394214501Srpaulo wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: " 395214501Srpaulo "0x%04x", MAC2STR(sta->addr), ht_capab); 396214501Srpaulo if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) { 397214501Srpaulo if (!sta->no_ht_gf_set) { 398214501Srpaulo sta->no_ht_gf_set = 1; 399214501Srpaulo hapd->iface->num_sta_ht_no_gf++; 400214501Srpaulo } 401214501Srpaulo wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num " 402214501Srpaulo "of non-gf stations %d", 403214501Srpaulo __func__, MAC2STR(sta->addr), 404214501Srpaulo hapd->iface->num_sta_ht_no_gf); 405214501Srpaulo } 406214501Srpaulo if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { 407214501Srpaulo if (!sta->ht_20mhz_set) { 408214501Srpaulo sta->ht_20mhz_set = 1; 409214501Srpaulo hapd->iface->num_sta_ht_20mhz++; 410214501Srpaulo } 411214501Srpaulo wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of " 412214501Srpaulo "20MHz HT STAs %d", 413214501Srpaulo __func__, MAC2STR(sta->addr), 414214501Srpaulo hapd->iface->num_sta_ht_20mhz); 415214501Srpaulo } 416281806Srpaulo 417281806Srpaulo if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT) 418281806Srpaulo ht40_intolerant_add(hapd->iface, sta); 419214501Srpaulo} 420214501Srpaulo 421214501Srpaulo 422214501Srpaulostatic void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta) 423214501Srpaulo{ 424214501Srpaulo if (!sta->no_ht_set) { 425214501Srpaulo sta->no_ht_set = 1; 426214501Srpaulo hapd->iface->num_sta_no_ht++; 427214501Srpaulo } 428214501Srpaulo if (hapd->iconf->ieee80211n) { 429214501Srpaulo wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of " 430214501Srpaulo "non-HT stations %d", 431214501Srpaulo __func__, MAC2STR(sta->addr), 432214501Srpaulo hapd->iface->num_sta_no_ht); 433214501Srpaulo } 434214501Srpaulo} 435214501Srpaulo 436214501Srpaulo 437214501Srpaulovoid update_ht_state(struct hostapd_data *hapd, struct sta_info *sta) 438214501Srpaulo{ 439214501Srpaulo if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) 440214501Srpaulo update_sta_ht(hapd, sta); 441214501Srpaulo else 442214501Srpaulo update_sta_no_ht(hapd, sta); 443214501Srpaulo 444214501Srpaulo if (hostapd_ht_operation_update(hapd->iface) > 0) 445214501Srpaulo ieee802_11_set_beacons(hapd->iface); 446214501Srpaulo} 447214501Srpaulo 448214501Srpaulo 449214501Srpaulovoid hostapd_get_ht_capab(struct hostapd_data *hapd, 450214501Srpaulo struct ieee80211_ht_capabilities *ht_cap, 451214501Srpaulo struct ieee80211_ht_capabilities *neg_ht_cap) 452214501Srpaulo{ 453214501Srpaulo u16 cap; 454214501Srpaulo 455214501Srpaulo if (ht_cap == NULL) 456214501Srpaulo return; 457214501Srpaulo os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap)); 458214501Srpaulo cap = le_to_host16(neg_ht_cap->ht_capabilities_info); 459214501Srpaulo 460214501Srpaulo /* 461252726Srpaulo * Mask out HT features we don't support, but don't overwrite 462252726Srpaulo * non-symmetric features like STBC and SMPS. Just because 463252726Srpaulo * we're not in dynamic SMPS mode the STA might still be. 464252726Srpaulo */ 465252726Srpaulo cap &= (hapd->iconf->ht_capab | HT_CAP_INFO_RX_STBC_MASK | 466252726Srpaulo HT_CAP_INFO_TX_STBC | HT_CAP_INFO_SMPS_MASK); 467252726Srpaulo 468252726Srpaulo /* 469214501Srpaulo * STBC needs to be handled specially 470214501Srpaulo * if we don't support RX STBC, mask out TX STBC in the STA's HT caps 471214501Srpaulo * if we don't support TX STBC, mask out RX STBC in the STA's HT caps 472214501Srpaulo */ 473214501Srpaulo if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK)) 474214501Srpaulo cap &= ~HT_CAP_INFO_TX_STBC; 475214501Srpaulo if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC)) 476214501Srpaulo cap &= ~HT_CAP_INFO_RX_STBC_MASK; 477214501Srpaulo 478214501Srpaulo neg_ht_cap->ht_capabilities_info = host_to_le16(cap); 479214501Srpaulo} 480281806Srpaulo 481281806Srpaulo 482281806Srpaulovoid ap_ht2040_timeout(void *eloop_data, void *user_data) 483281806Srpaulo{ 484281806Srpaulo struct hostapd_iface *iface = eloop_data; 485281806Srpaulo 486281806Srpaulo wpa_printf(MSG_INFO, "Switching to 40 MHz operation"); 487281806Srpaulo 488281806Srpaulo iface->conf->secondary_channel = iface->secondary_ch; 489281806Srpaulo ieee802_11_set_beacons(iface); 490281806Srpaulo} 491