xref: /freebsd/contrib/wpa/src/ap/ieee802_11_he.c (revision dd41de95a84d979615a2ef11df6850622bf6184e)
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 
21 static 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 
47 u8 * 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 
129 u8 * 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 
175 u8 * 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 
206 u8 * 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 
248 void 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 
264 static 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 
322 u16 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