1/*
2 * hostapd / IEEE 802.11ax HE
3 * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
4 * Copyright (c) 2019 John Crispin <john@phrozen.org>
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10#include "utils/includes.h"
11
12#include "utils/common.h"
13#include "common/ieee802_11_defs.h"
14#include "hostapd.h"
15#include "ap_config.h"
16#include "beacon.h"
17#include "sta_info.h"
18#include "ieee802_11.h"
19#include "dfs.h"
20
21static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
22{
23	u8 sz = 0, ru;
24
25	if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
26	     HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
27		return 0;
28
29	ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
30		HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
31	while (ru) {
32		if (ru & 0x1)
33			sz++;
34		ru >>= 1;
35	}
36
37	sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
38	sz = (sz * 6) + 7;
39	if (sz % 8)
40		sz += 8;
41	sz /= 8;
42
43	return sz;
44}
45
46
47u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
48			  enum ieee80211_op_mode opmode)
49{
50	struct ieee80211_he_capabilities *cap;
51	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
52	u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
53	u8 *pos = eid;
54	u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
55
56	if (!mode)
57		return eid;
58
59	ie_size = sizeof(struct ieee80211_he_capabilities);
60	ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
61					   mode->he_capab[opmode].phy_cap);
62
63	switch (hapd->iface->conf->he_oper_chwidth) {
64	case CHANWIDTH_80P80MHZ:
65		he_oper_chwidth |=
66			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
67		mcs_nss_size += 4;
68		/* fall through */
69	case CHANWIDTH_160MHZ:
70		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
71		mcs_nss_size += 4;
72		/* fall through */
73	case CHANWIDTH_80MHZ:
74	case CHANWIDTH_USE_HT:
75		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
76			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
77		mcs_nss_size += 4;
78		break;
79	}
80
81	ie_size += mcs_nss_size + ppet_size;
82
83	*pos++ = WLAN_EID_EXTENSION;
84	*pos++ = 1 + ie_size;
85	*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
86
87	cap = (struct ieee80211_he_capabilities *) pos;
88	os_memset(cap, 0, sizeof(*cap));
89
90	os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
91		  HE_MAX_MAC_CAPAB_SIZE);
92	os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
93		  HE_MAX_PHY_CAPAB_SIZE);
94	os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
95	if (ppet_size)
96		os_memcpy(&cap->optional[mcs_nss_size],
97			  mode->he_capab[opmode].ppet,  ppet_size);
98
99	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
100		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
101			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
102	else
103		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
104			~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
105
106	if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
107		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
108			HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
109	else
110		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
111			~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
112
113	if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
114		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
115			HE_PHYCAP_MU_BEAMFORMER_CAPAB;
116	else
117		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
118			~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
119
120	cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
121		he_oper_chwidth;
122
123	pos += ie_size;
124
125	return pos;
126}
127
128
129u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
130{
131	struct ieee80211_he_operation *oper;
132	u8 *pos = eid;
133	int oper_size = 6;
134	u32 params = 0;
135
136	if (!hapd->iface->current_mode)
137		return eid;
138
139	*pos++ = WLAN_EID_EXTENSION;
140	*pos++ = 1 + oper_size;
141	*pos++ = WLAN_EID_EXT_HE_OPERATION;
142
143	oper = (struct ieee80211_he_operation *) pos;
144	os_memset(oper, 0, sizeof(*oper));
145
146	if (hapd->iface->conf->he_op.he_default_pe_duration)
147		params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
148			   HE_OPERATION_DFLT_PE_DURATION_OFFSET);
149
150	if (hapd->iface->conf->he_op.he_twt_required)
151		params |= HE_OPERATION_TWT_REQUIRED;
152
153	if (hapd->iface->conf->he_op.he_rts_threshold)
154		params |= (hapd->iface->conf->he_op.he_rts_threshold <<
155			   HE_OPERATION_RTS_THRESHOLD_OFFSET);
156
157	if (hapd->iface->conf->he_op.he_bss_color)
158		params |= (hapd->iface->conf->he_op.he_bss_color <<
159			   HE_OPERATION_BSS_COLOR_OFFSET);
160
161	/* HE minimum required basic MCS and NSS for STAs */
162	oper->he_mcs_nss_set =
163		host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
164
165	/* TODO: conditional MaxBSSID Indicator subfield */
166
167	oper->he_oper_params = host_to_le32(params);
168
169	pos += oper_size;
170
171	return pos;
172}
173
174
175u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
176{
177	struct ieee80211_he_mu_edca_parameter_set *edca;
178	u8 *pos;
179	size_t i;
180
181	pos = (u8 *) &hapd->iface->conf->he_mu_edca;
182	for (i = 0; i < sizeof(*edca); i++) {
183		if (pos[i])
184			break;
185	}
186	if (i == sizeof(*edca))
187		return eid; /* no MU EDCA Parameters configured */
188
189	pos = eid;
190	*pos++ = WLAN_EID_EXTENSION;
191	*pos++ = 1 + sizeof(*edca);
192	*pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
193
194	edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
195	os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
196
197	wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
198		    pos, sizeof(*edca));
199
200	pos += sizeof(*edca);
201
202	return pos;
203}
204
205
206u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
207{
208	struct ieee80211_spatial_reuse *spr;
209	u8 *pos = eid, *spr_param;
210	u8 sz = 1;
211
212	if (!hapd->iface->conf->spr.sr_control)
213		return eid;
214
215	if (hapd->iface->conf->spr.sr_control &
216	    SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
217		sz++;
218
219	if (hapd->iface->conf->spr.sr_control &
220	    SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
221		sz += 18;
222
223	*pos++ = WLAN_EID_EXTENSION;
224	*pos++ = 1 + sz;
225	*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
226
227	spr = (struct ieee80211_spatial_reuse *) pos;
228	os_memset(spr, 0, sizeof(*spr));
229
230	spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
231	pos++;
232	spr_param = spr->params;
233	if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
234		*spr_param++ =
235			hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
236		pos++;
237	}
238	if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
239		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
240		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
241		pos += 18;
242	}
243
244	return pos;
245}
246
247
248void hostapd_get_he_capab(struct hostapd_data *hapd,
249			  const struct ieee80211_he_capabilities *he_cap,
250			  struct ieee80211_he_capabilities *neg_he_cap,
251			  size_t he_capab_len)
252{
253	if (!he_cap)
254		return;
255
256	if (he_capab_len > sizeof(*neg_he_cap))
257		he_capab_len = sizeof(*neg_he_cap);
258	/* TODO: mask out unsupported features */
259
260	os_memcpy(neg_he_cap, he_cap, he_capab_len);
261}
262
263
264static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
265			      enum ieee80211_op_mode opmode)
266{
267	u16 sta_rx_mcs_set, ap_tx_mcs_set;
268	u8 mcs_count = 0;
269	const u16 *ap_mcs_set, *sta_mcs_set;
270	int i;
271
272	if (!hapd->iface->current_mode)
273		return 1;
274	ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
275	sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
276			       sta_he_capab)->optional;
277
278	/*
279	 * Disable HE capabilities for STAs for which there is not even a single
280	 * allowed MCS in any supported number of streams, i.e., STA is
281	 * advertising 3 (not supported) as HE MCS rates for all supported
282	 * band/stream cases.
283	 */
284	switch (hapd->iface->conf->he_oper_chwidth) {
285	case CHANWIDTH_80P80MHZ:
286		mcs_count = 3;
287		break;
288	case CHANWIDTH_160MHZ:
289		mcs_count = 2;
290		break;
291	default:
292		mcs_count = 1;
293		break;
294	}
295
296	for (i = 0; i < mcs_count; i++) {
297		int j;
298
299		/* AP Tx MCS map vs. STA Rx MCS map */
300		sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
301		ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
302					     &ap_mcs_set[(i * 2) + 1]);
303
304		for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
305			if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
306				continue;
307
308			if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
309				continue;
310
311			return 1;
312		}
313	}
314
315	wpa_printf(MSG_DEBUG,
316		   "No matching HE MCS found between AP TX and STA RX");
317
318	return 0;
319}
320
321
322u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
323		      enum ieee80211_op_mode opmode, const u8 *he_capab,
324		      size_t he_capab_len)
325{
326	if (!he_capab || !hapd->iconf->ieee80211ax ||
327	    !check_valid_he_mcs(hapd, he_capab, opmode) ||
328	    he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
329		sta->flags &= ~WLAN_STA_HE;
330		os_free(sta->he_capab);
331		sta->he_capab = NULL;
332		return WLAN_STATUS_SUCCESS;
333	}
334
335	if (!sta->he_capab) {
336		sta->he_capab =
337			os_zalloc(sizeof(struct ieee80211_he_capabilities));
338		if (!sta->he_capab)
339			return WLAN_STATUS_UNSPECIFIED_FAILURE;
340	}
341
342	sta->flags |= WLAN_STA_HE;
343	os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
344	os_memcpy(sta->he_capab, he_capab, he_capab_len);
345	sta->he_capab_len = he_capab_len;
346
347	return WLAN_STATUS_SUCCESS;
348}
349