122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul/*
2e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell * hostapd / IEEE 802.11ac VHT
3e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
4e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell *
522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul * This program is free software; you can redistribute it and/or modify
622144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes * it under the terms of BSD license
7b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul *
822144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes * See README and COPYING for more details.
9e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell */
10e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
11e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "utils/includes.h"
12e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
13e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "utils/common.h"
14e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "common/ieee802_11_defs.h"
1522144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes#include "hostapd.h"
16e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "ap_config.h"
17e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "sta_info.h"
1822144ab7552f0799bcfca506bf4ffa7f70a06649Gareth Hughes#include "beacon.h"
19e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell#include "ieee802_11.h"
20e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
21e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
22e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwellu8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
23e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell{
24e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	struct ieee80211_vht_capabilities *cap;
25e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	u8 *pos = eid;
26e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
27e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
28e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	    hapd->conf->disable_11ac)
2946b0988c673b28e072fd0cbf477632a9ab6f9f18Keith Whitwell		return eid;
30e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
31e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	*pos++ = WLAN_EID_VHT_CAP;
320070d398d13759adc519f9bc764ffd39bc88890eBrian Paul	*pos++ = sizeof(*cap);
33cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell
34e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	cap = (struct ieee80211_vht_capabilities *) pos;
35cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell	os_memset(cap, 0, sizeof(*cap));
36b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	cap->vht_capabilities_info = host_to_le32(
37b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		hapd->iface->conf->vht_capab);
38e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
39e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	/* Supported MCS set comes from hw */
40b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	os_memcpy(&cap->vht_supported_mcs_set,
41b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	          hapd->iface->current_mode->vht_mcs_set, 8);
42b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
43b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	pos += sizeof(*cap);
44b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
45b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	return pos;
46b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul}
47b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
48e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
49b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paulu8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
50b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul{
51b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	struct ieee80211_vht_operation *oper;
52b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	u8 *pos = eid;
53b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
54b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
55b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		return eid;
56b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
57b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	*pos++ = WLAN_EID_VHT_OPERATION;
58b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	*pos++ = sizeof(*oper);
59b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
60e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	oper = (struct ieee80211_vht_operation *) pos;
61e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	os_memset(oper, 0, sizeof(*oper));
62e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
63b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	/*
64e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	 * center freq = 5 GHz + (5 * index)
65b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	 * So index 42 gives center freq 5.210 GHz
66b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	 * which is channel 42 in 5G band
67e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	 */
68b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	oper->vht_op_info_chan_center_freq_seg0_idx =
69cd03ed4f54444d96e4e47cdb118a3dfd94d92bb0Keith Whitwell		hapd->iconf->vht_oper_centr_freq_seg0_idx;
70b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	oper->vht_op_info_chan_center_freq_seg1_idx =
71e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell		hapd->iconf->vht_oper_centr_freq_seg1_idx;
72b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
73e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	oper->vht_op_info_chwidth = hapd->iconf->vht_oper_chwidth;
74b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
75b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	/* VHT Basic MCS set comes from hw */
76b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	/* Hard code 1 stream, MCS0-7 is a min Basic VHT MCS rates */
77b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	oper->vht_basic_mcs_set = host_to_le16(0xfffc);
78b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	pos += sizeof(*oper);
79b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
8077df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul	return pos;
81a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul}
82a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul
83b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
84b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paulu16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
85b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		       const u8 *vht_capab, size_t vht_capab_len)
8677df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul{
87b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	/* Disable VHT caps for STAs associated to no-VHT BSSes. */
88b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	if (!vht_capab ||
89b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	    vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
9077df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul	    hapd->conf->disable_11ac) {
91b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		sta->flags &= ~WLAN_STA_VHT;
92b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		os_free(sta->vht_capabilities);
93b7f5e92f1749ce4601a758f66ddc64959f11742bBrian Paul		sta->vht_capabilities = NULL;
94b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		return WLAN_STATUS_SUCCESS;
95b7f5e92f1749ce4601a758f66ddc64959f11742bBrian Paul	}
96b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
97b7f5e92f1749ce4601a758f66ddc64959f11742bBrian Paul	if (sta->vht_capabilities == NULL) {
98b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		sta->vht_capabilities =
99b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul			os_zalloc(sizeof(struct ieee80211_vht_capabilities));
100b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		if (sta->vht_capabilities == NULL)
10177df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul			return WLAN_STATUS_UNSPECIFIED_FAILURE;
102a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul	}
103a670c1280b78e6da3b298b61f623e4c733c6be94Brian Paul
104b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	sta->flags |= WLAN_STA_VHT;
105b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	os_memcpy(sta->vht_capabilities, vht_capab,
106b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		  sizeof(struct ieee80211_vht_capabilities));
10777df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul
108b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	return WLAN_STATUS_SUCCESS;
109b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul}
110b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
11177df88727cb0a423dd5cb41498c2302d9df4fce7Brian Paul
112b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paulu16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
113b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		       const u8 *vht_oper_notif)
114b7f5e92f1749ce4601a758f66ddc64959f11742bBrian Paul{
115b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	if (!vht_oper_notif) {
116b7f5e92f1749ce4601a758f66ddc64959f11742bBrian Paul		sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
117b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul		return WLAN_STATUS_SUCCESS;
118b7f5e92f1749ce4601a758f66ddc64959f11742bBrian Paul	}
119b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
120b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
121e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	sta->vht_opmode = *vht_oper_notif;
122e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	return WLAN_STATUS_SUCCESS;
123e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell}
124e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
125b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul
126b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paulvoid hostapd_get_vht_capab(struct hostapd_data *hapd,
127b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul			   struct ieee80211_vht_capabilities *vht_cap,
128b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul			   struct ieee80211_vht_capabilities *neg_vht_cap)
129b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul{
13022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	u32 cap, own_cap, sym_caps;
13122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
13222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	if (vht_cap == NULL)
13322a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		return;
134e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
135e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
136e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
13722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	own_cap = hapd->iconf->vht_capab;
13822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
13922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	/* mask out symmetric VHT capabilities we don't support */
14022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
141e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	cap &= ~sym_caps | (own_cap & sym_caps);
142e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell
143e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	/* mask out beamformer/beamformee caps if not supported */
14422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
14522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
14622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul			 VHT_CAP_BEAMFORMEE_STS_MAX);
14722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
14822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
14922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
15022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul			 VHT_CAP_SOUNDING_DIMENSION_MAX);
15122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
15222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
15322a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
15422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
15522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
15622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
15722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
15822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	/* mask channel widths we don't support */
159b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
160e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
161e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell		break;
162e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
16322a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
16422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul			cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
16522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul			cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
16622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		}
16722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		break;
16822a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	default:
16922a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
17022a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		break;
17122a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	}
17222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
17322a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
17422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		cap &= ~VHT_CAP_SHORT_GI_160;
17522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
17622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	/*
17722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	 * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
178e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	 * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
179e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell	 */
180b37a084357dd08573b86d6d8c5ba43d65bdc1bd7Brian Paul	if (!(own_cap & VHT_CAP_RXSTBC_MASK))
181e3a051e0538a605551f4d58294c94f5eb00ed07fKeith Whitwell		cap &= ~VHT_CAP_TXSTBC;
18222a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	if (!(own_cap & VHT_CAP_TXSTBC))
18322a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul		cap &= ~VHT_CAP_RXSTBC_MASK;
18422a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul
18522a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul	neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
18622a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul}
18722a47c5251ee7b91dc8f7f4f7dbeb3ad5a117b70Brian Paul