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