1/* 2 * hostapd / IEEE 802.11ac VHT 3 * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of BSD license 7 * 8 * See README and COPYING for more details. 9 */ 10 11#include "utils/includes.h" 12 13#include "utils/common.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 21 22u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid) 23{ 24 struct ieee80211_vht_capabilities *cap; 25 struct hostapd_hw_modes *mode = hapd->iface->current_mode; 26 u8 *pos = eid; 27 28 if (!mode) 29 return eid; 30 31 if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht && 32 mode->vht_capab == 0 && hapd->iface->hw_features) { 33 int i; 34 35 for (i = 0; i < hapd->iface->num_hw_features; i++) { 36 if (hapd->iface->hw_features[i].mode == 37 HOSTAPD_MODE_IEEE80211A) { 38 mode = &hapd->iface->hw_features[i]; 39 break; 40 } 41 } 42 } 43 44 *pos++ = WLAN_EID_VHT_CAP; 45 *pos++ = sizeof(*cap); 46 47 cap = (struct ieee80211_vht_capabilities *) pos; 48 os_memset(cap, 0, sizeof(*cap)); 49 cap->vht_capabilities_info = host_to_le32( 50 hapd->iface->conf->vht_capab); 51 52 /* Supported MCS set comes from hw */ 53 os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8); 54 55 pos += sizeof(*cap); 56 57 return pos; 58} 59 60 61u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid) 62{ 63 struct ieee80211_vht_operation *oper; 64 u8 *pos = eid; 65 66 *pos++ = WLAN_EID_VHT_OPERATION; 67 *pos++ = sizeof(*oper); 68 69 oper = (struct ieee80211_vht_operation *) pos; 70 os_memset(oper, 0, sizeof(*oper)); 71 72 /* 73 * center freq = 5 GHz + (5 * index) 74 * So index 42 gives center freq 5.210 GHz 75 * which is channel 42 in 5G band 76 */ 77 oper->vht_op_info_chan_center_freq_seg0_idx = 78 hapd->iconf->vht_oper_centr_freq_seg0_idx; 79 oper->vht_op_info_chan_center_freq_seg1_idx = 80 hapd->iconf->vht_oper_centr_freq_seg1_idx; 81 82 oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth; 83 84 /* VHT Basic MCS set comes from hw */ 85 /* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */ 86 oper->vht_basic_mcs_set = host_to_le16(0xfffc); 87 pos += sizeof(*oper); 88 89 return pos; 90} 91 92 93static int check_valid_vht_mcs(struct hostapd_hw_modes *mode, 94 const u8 *sta_vht_capab) 95{ 96 const struct ieee80211_vht_capabilities *vht_cap; 97 struct ieee80211_vht_capabilities ap_vht_cap; 98 u16 sta_rx_mcs_set, ap_tx_mcs_set; 99 int i; 100 101 if (!mode) 102 return 1; 103 104 /* 105 * Disable VHT caps for STAs for which there is not even a single 106 * allowed MCS in any supported number of streams, i.e., STA is 107 * advertising 3 (not supported) as VHT MCS rates for all supported 108 * stream cases. 109 */ 110 os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set, 111 sizeof(ap_vht_cap.vht_supported_mcs_set)); 112 vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab; 113 114 /* AP Tx MCS map vs. STA Rx MCS map */ 115 sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map); 116 ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map); 117 118 for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) { 119 if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3) 120 continue; 121 122 if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3) 123 continue; 124 125 return 1; 126 } 127 128 wpa_printf(MSG_DEBUG, 129 "No matching VHT MCS found between AP TX and STA RX"); 130 return 0; 131} 132 133 134u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta, 135 const u8 *vht_capab) 136{ 137 /* Disable VHT caps for STAs associated to no-VHT BSSes. */ 138 if (!vht_capab || 139 hapd->conf->disable_11ac || 140 !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) { 141 sta->flags &= ~WLAN_STA_VHT; 142 os_free(sta->vht_capabilities); 143 sta->vht_capabilities = NULL; 144 return WLAN_STATUS_SUCCESS; 145 } 146 147 if (sta->vht_capabilities == NULL) { 148 sta->vht_capabilities = 149 os_zalloc(sizeof(struct ieee80211_vht_capabilities)); 150 if (sta->vht_capabilities == NULL) 151 return WLAN_STATUS_UNSPECIFIED_FAILURE; 152 } 153 154 sta->flags |= WLAN_STA_VHT; 155 os_memcpy(sta->vht_capabilities, vht_capab, 156 sizeof(struct ieee80211_vht_capabilities)); 157 158 return WLAN_STATUS_SUCCESS; 159} 160 161 162u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta, 163 const u8 *ie, size_t len) 164{ 165 const u8 *vht_capab; 166 unsigned int vht_capab_len; 167 168 if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) || 169 hapd->conf->disable_11ac) 170 goto no_capab; 171 172 /* The VHT Capabilities element embedded in vendor VHT */ 173 vht_capab = ie + 5; 174 if (vht_capab[0] != WLAN_EID_VHT_CAP) 175 goto no_capab; 176 vht_capab_len = vht_capab[1]; 177 if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) || 178 (int) vht_capab_len > ie + len - vht_capab - 2) 179 goto no_capab; 180 vht_capab += 2; 181 182 if (sta->vht_capabilities == NULL) { 183 sta->vht_capabilities = 184 os_zalloc(sizeof(struct ieee80211_vht_capabilities)); 185 if (sta->vht_capabilities == NULL) 186 return WLAN_STATUS_UNSPECIFIED_FAILURE; 187 } 188 189 sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT; 190 os_memcpy(sta->vht_capabilities, vht_capab, 191 sizeof(struct ieee80211_vht_capabilities)); 192 return WLAN_STATUS_SUCCESS; 193 194no_capab: 195 sta->flags &= ~WLAN_STA_VENDOR_VHT; 196 return WLAN_STATUS_SUCCESS; 197} 198 199 200u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid) 201{ 202 u8 *pos = eid; 203 204 if (!hapd->iface->current_mode) 205 return eid; 206 207 *pos++ = WLAN_EID_VENDOR_SPECIFIC; 208 *pos++ = (5 + /* The Vendor OUI, type and subtype */ 209 2 + sizeof(struct ieee80211_vht_capabilities) + 210 2 + sizeof(struct ieee80211_vht_operation)); 211 212 WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE); 213 pos += 4; 214 *pos++ = VENDOR_VHT_SUBTYPE; 215 pos = hostapd_eid_vht_capabilities(hapd, pos); 216 pos = hostapd_eid_vht_operation(hapd, pos); 217 218 return pos; 219} 220 221 222u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta, 223 const u8 *vht_oper_notif) 224{ 225 if (!vht_oper_notif) { 226 sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED; 227 return WLAN_STATUS_SUCCESS; 228 } 229 230 sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED; 231 sta->vht_opmode = *vht_oper_notif; 232 return WLAN_STATUS_SUCCESS; 233} 234 235 236void hostapd_get_vht_capab(struct hostapd_data *hapd, 237 struct ieee80211_vht_capabilities *vht_cap, 238 struct ieee80211_vht_capabilities *neg_vht_cap) 239{ 240 u32 cap, own_cap, sym_caps; 241 242 if (vht_cap == NULL) 243 return; 244 os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap)); 245 246 cap = le_to_host32(neg_vht_cap->vht_capabilities_info); 247 own_cap = hapd->iconf->vht_capab; 248 249 /* mask out symmetric VHT capabilities we don't support */ 250 sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160; 251 cap &= ~sym_caps | (own_cap & sym_caps); 252 253 /* mask out beamformer/beamformee caps if not supported */ 254 if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE)) 255 cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE | 256 VHT_CAP_BEAMFORMEE_STS_MAX); 257 258 if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE)) 259 cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE | 260 VHT_CAP_SOUNDING_DIMENSION_MAX); 261 262 if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE)) 263 cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE; 264 265 if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE)) 266 cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE; 267 268 /* mask channel widths we don't support */ 269 switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) { 270 case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ: 271 break; 272 case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ: 273 if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) { 274 cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ; 275 cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; 276 } 277 break; 278 default: 279 cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK; 280 break; 281 } 282 283 if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK)) 284 cap &= ~VHT_CAP_SHORT_GI_160; 285 286 /* 287 * if we don't support RX STBC, mask out TX STBC in the STA's HT caps 288 * if we don't support TX STBC, mask out RX STBC in the STA's HT caps 289 */ 290 if (!(own_cap & VHT_CAP_RXSTBC_MASK)) 291 cap &= ~VHT_CAP_TXSTBC; 292 if (!(own_cap & VHT_CAP_TXSTBC)) 293 cap &= ~VHT_CAP_RXSTBC_MASK; 294 295 neg_vht_cap->vht_capabilities_info = host_to_le32(cap); 296} 297