diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 1b506a7af..fa08234e6 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2895,6 +2895,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, conf->vht_oper_centr_freq_seg1_idx = atoi(pos); } else if (os_strcmp(buf, "vendor_vht") == 0) { bss->vendor_vht = atoi(pos); + } else if (os_strcmp(buf, "use_sta_nsts") == 0) { + bss->use_sta_nsts = atoi(pos); #endif /* CONFIG_IEEE80211AC */ } else if (os_strcmp(buf, "max_listen_interval") == 0) { bss->max_listen_interval = atoi(pos); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index a310c0576..4fc32786f 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -685,6 +685,13 @@ wmm_ac_vo_acm=0 # #vht_oper_centr_freq_seg1_idx=159 +# Workaround to use station's nsts capability in (Re)Association Response frame +# This may be needed with some deployed devices as an interoperability +# workaround for beamforming if the AP's capability is greater than the +# station's capability. This is disabled by default and can be enabled by +# setting use_sta_nsts=1. +#use_sta_nsts=0 + ##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 64daf4c81..b16286b58 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -582,6 +582,7 @@ struct hostapd_bss_config { u8 radio_measurements[RRM_CAPABILITIES_IE_LEN]; int vendor_vht; + int use_sta_nsts; char *no_probe_resp_if_seen_on; char *no_auth_if_seen_on; diff --git a/src/ap/beacon.c b/src/ap/beacon.c index 0570ab721..202abe6de 100644 --- a/src/ap/beacon.c +++ b/src/ap/beacon.c @@ -485,7 +485,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); pos = hostapd_eid_txpower_envelope(hapd, pos); pos = hostapd_eid_wb_chsw_wrapper(hapd, pos); @@ -1105,7 +1105,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - tailpos = hostapd_eid_vht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_vht_capabilities(hapd, tailpos, 0); tailpos = hostapd_eid_vht_operation(hapd, tailpos); tailpos = hostapd_eid_txpower_envelope(hapd, tailpos); tailpos = hostapd_eid_wb_chsw_wrapper(hapd, tailpos); diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index eed54836e..2ecd78f25 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1944,7 +1944,23 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, #ifdef CONFIG_IEEE80211AC if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) { - p = hostapd_eid_vht_capabilities(hapd, p); + u32 nsts = 0, sta_nsts; + + if (hapd->conf->use_sta_nsts && sta->vht_capabilities) { + struct ieee80211_vht_capabilities *capa; + + nsts = (hapd->iface->conf->vht_capab >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + capa = sta->vht_capabilities; + sta_nsts = (le_to_host32(capa->vht_capabilities_info) >> + VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + + if (nsts < sta_nsts) + nsts = 0; + else + nsts = sta_nsts; + } + p = hostapd_eid_vht_capabilities(hapd, p, nsts); p = hostapd_eid_vht_operation(hapd, p); } #endif /* CONFIG_IEEE80211AC */ diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h index 71b3b497a..0327dec2a 100644 --- a/src/ap/ieee802_11.h +++ b/src/ap/ieee802_11.h @@ -50,7 +50,7 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid); -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts); u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid); u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid); diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c index 08418980a..f30f63bc5 100644 --- a/src/ap/ieee802_11_vht.c +++ b/src/ap/ieee802_11_vht.c @@ -20,7 +20,7 @@ #include "dfs.h" -u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) +u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid, u32 nsts) { struct ieee80211_vht_capabilities *cap; struct hostapd_hw_modes *mode = hapd->iface->current_mode; @@ -50,6 +50,18 @@ u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) cap->vht_capabilities_info = host_to_le32( hapd->iface->conf->vht_capab); + if (nsts != 0) { + u32 hapd_nsts; + + hapd_nsts = le_to_host32(cap->vht_capabilities_info); + hapd_nsts = (hapd_nsts >> VHT_CAP_BEAMFORMEE_STS_OFFSET) & 7; + cap->vht_capabilities_info &= + ~(host_to_le32(hapd_nsts << + VHT_CAP_BEAMFORMEE_STS_OFFSET)); + cap->vht_capabilities_info |= + host_to_le32(nsts << VHT_CAP_BEAMFORMEE_STS_OFFSET); + } + /* Supported MCS set comes from hw */ os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); @@ -398,7 +410,7 @@ u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid) WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); pos += 4; *pos++ = VENDOR_VHT_SUBTYPE; - pos = hostapd_eid_vht_capabilities(hapd, pos); + pos = hostapd_eid_vht_capabilities(hapd, pos, 0); pos = hostapd_eid_vht_operation(hapd, pos); return pos; diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c index 7ffdefe21..d14c7e3b2 100644 --- a/wpa_supplicant/mesh_mpm.c +++ b/wpa_supplicant/mesh_mpm.c @@ -348,7 +348,7 @@ static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s, if (type != PLINK_CLOSE && wpa_s->mesh_vht_enabled) { u8 vht_capa_oper[2 + 12 + 2 + 5]; - pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper); + pos = hostapd_eid_vht_capabilities(bss, vht_capa_oper, 0); pos = hostapd_eid_vht_operation(bss, pos); wpabuf_put_data(buf, vht_capa_oper, pos - vht_capa_oper); }