Home | History | Annotate | Download | only in ap
      1 /*
      2  * hostapd / IEEE 802.11ac VHT
      3  * Copyright (c) 2002-2009, Jouni Malinen <j (at) 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 
     22 u8 * 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 
     61 u8 * 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 
     93 static 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 
    134 u16 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 
    162 u16 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 
    194 no_capab:
    195 	sta->flags &= ~WLAN_STA_VENDOR_VHT;
    196 	return WLAN_STATUS_SUCCESS;
    197 }
    198 
    199 
    200 u8 * 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 
    222 u16 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 
    236 void 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