xref: /freebsd/contrib/wpa/src/ap/ieee802_11_he.c (revision a90b9d0159070121c221b966469c3e36d912bf82)
185732ac8SCy Schubert /*
285732ac8SCy Schubert  * hostapd / IEEE 802.11ax HE
385732ac8SCy Schubert  * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
4206b73d0SCy Schubert  * Copyright (c) 2019 John Crispin <john@phrozen.org>
585732ac8SCy Schubert  *
685732ac8SCy Schubert  * This software may be distributed under the terms of the BSD license.
785732ac8SCy Schubert  * See README for more details.
885732ac8SCy Schubert  */
985732ac8SCy Schubert 
1085732ac8SCy Schubert #include "utils/includes.h"
1185732ac8SCy Schubert 
1285732ac8SCy Schubert #include "utils/common.h"
1385732ac8SCy Schubert #include "common/ieee802_11_defs.h"
14c1d255d3SCy Schubert #include "common/ieee802_11_common.h"
15*a90b9d01SCy Schubert #include "common/hw_features_common.h"
1685732ac8SCy Schubert #include "hostapd.h"
1785732ac8SCy Schubert #include "ap_config.h"
1885732ac8SCy Schubert #include "beacon.h"
19206b73d0SCy Schubert #include "sta_info.h"
2085732ac8SCy Schubert #include "ieee802_11.h"
2185732ac8SCy Schubert #include "dfs.h"
2285732ac8SCy Schubert 
ieee80211_he_ppet_size(u8 ppe_thres_hdr,const u8 * phy_cap_info)23206b73d0SCy Schubert static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
24206b73d0SCy Schubert {
25206b73d0SCy Schubert 	u8 sz = 0, ru;
26206b73d0SCy Schubert 
27206b73d0SCy Schubert 	if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
28206b73d0SCy Schubert 	     HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
29206b73d0SCy Schubert 		return 0;
30206b73d0SCy Schubert 
31206b73d0SCy Schubert 	ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
32206b73d0SCy Schubert 		HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
33*a90b9d01SCy Schubert 	/* Count the number of 1 bits in RU Index Bitmask */
34206b73d0SCy Schubert 	while (ru) {
35206b73d0SCy Schubert 		if (ru & 0x1)
36206b73d0SCy Schubert 			sz++;
37206b73d0SCy Schubert 		ru >>= 1;
38206b73d0SCy Schubert 	}
39206b73d0SCy Schubert 
40*a90b9d01SCy Schubert 	/* fixed header of 3 (NSTS) + 4 (RU Index Bitmask) = 7 bits */
41*a90b9d01SCy Schubert 	/* 6 * (NSTS + 1) bits for bit 1 in RU Index Bitmask */
42206b73d0SCy Schubert 	sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
43206b73d0SCy Schubert 	sz = (sz * 6) + 7;
44*a90b9d01SCy Schubert 	/* PPE Pad to count the number of needed full octets */
45*a90b9d01SCy Schubert 	sz = (sz + 7) / 8;
46206b73d0SCy Schubert 
47206b73d0SCy Schubert 	return sz;
48206b73d0SCy Schubert }
49206b73d0SCy Schubert 
50206b73d0SCy Schubert 
ieee80211_he_mcs_set_size(const u8 * phy_cap_info)51c1d255d3SCy Schubert static u8 ieee80211_he_mcs_set_size(const u8 *phy_cap_info)
52c1d255d3SCy Schubert {
53c1d255d3SCy Schubert 	u8 sz = 4;
54c1d255d3SCy Schubert 
55c1d255d3SCy Schubert 	if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
56c1d255d3SCy Schubert 	    HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)
57c1d255d3SCy Schubert 		sz += 4;
58c1d255d3SCy Schubert 	if (phy_cap_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
59c1d255d3SCy Schubert 	    HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G)
60c1d255d3SCy Schubert 		sz += 4;
61c1d255d3SCy Schubert 
62c1d255d3SCy Schubert 	return sz;
63c1d255d3SCy Schubert }
64c1d255d3SCy Schubert 
65c1d255d3SCy Schubert 
ieee80211_invalid_he_cap_size(const u8 * buf,size_t len)66c1d255d3SCy Schubert static int ieee80211_invalid_he_cap_size(const u8 *buf, size_t len)
67c1d255d3SCy Schubert {
68c1d255d3SCy Schubert 	struct ieee80211_he_capabilities *cap;
69c1d255d3SCy Schubert 	size_t cap_len;
70*a90b9d01SCy Schubert 	u8 ppe_thres_hdr;
71c1d255d3SCy Schubert 
72c1d255d3SCy Schubert 	cap = (struct ieee80211_he_capabilities *) buf;
73c1d255d3SCy Schubert 	cap_len = sizeof(*cap) - sizeof(cap->optional);
74c1d255d3SCy Schubert 	if (len < cap_len)
75c1d255d3SCy Schubert 		return 1;
76c1d255d3SCy Schubert 
77c1d255d3SCy Schubert 	cap_len += ieee80211_he_mcs_set_size(cap->he_phy_capab_info);
78c1d255d3SCy Schubert 	if (len < cap_len)
79c1d255d3SCy Schubert 		return 1;
80c1d255d3SCy Schubert 
81*a90b9d01SCy Schubert 	ppe_thres_hdr = len > cap_len ? buf[cap_len] : 0xff;
82*a90b9d01SCy Schubert 	cap_len += ieee80211_he_ppet_size(ppe_thres_hdr,
83*a90b9d01SCy Schubert 					  cap->he_phy_capab_info);
84c1d255d3SCy Schubert 
85*a90b9d01SCy Schubert 	return len < cap_len;
86c1d255d3SCy Schubert }
87c1d255d3SCy Schubert 
88c1d255d3SCy Schubert 
hostapd_eid_he_capab(struct hostapd_data * hapd,u8 * eid,enum ieee80211_op_mode opmode)89206b73d0SCy Schubert u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
90206b73d0SCy Schubert 			  enum ieee80211_op_mode opmode)
9185732ac8SCy Schubert {
9285732ac8SCy Schubert 	struct ieee80211_he_capabilities *cap;
93206b73d0SCy Schubert 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
94206b73d0SCy Schubert 	u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
9585732ac8SCy Schubert 	u8 *pos = eid;
96c1d255d3SCy Schubert 	u8 ie_size = 0, mcs_nss_size = 4, ppet_size = 0;
9785732ac8SCy Schubert 
98206b73d0SCy Schubert 	if (!mode)
9985732ac8SCy Schubert 		return eid;
10085732ac8SCy Schubert 
101c1d255d3SCy Schubert 	ie_size = sizeof(*cap) - sizeof(cap->optional);
102206b73d0SCy Schubert 	ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
103206b73d0SCy Schubert 					   mode->he_capab[opmode].phy_cap);
104206b73d0SCy Schubert 
105206b73d0SCy Schubert 	switch (hapd->iface->conf->he_oper_chwidth) {
106*a90b9d01SCy Schubert 	case CONF_OPER_CHWIDTH_80P80MHZ:
107206b73d0SCy Schubert 		he_oper_chwidth |=
108206b73d0SCy Schubert 			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
109206b73d0SCy Schubert 		mcs_nss_size += 4;
110206b73d0SCy Schubert 		/* fall through */
111*a90b9d01SCy Schubert 	case CONF_OPER_CHWIDTH_160MHZ:
112206b73d0SCy Schubert 		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
113206b73d0SCy Schubert 		mcs_nss_size += 4;
114206b73d0SCy Schubert 		/* fall through */
115*a90b9d01SCy Schubert 	case CONF_OPER_CHWIDTH_80MHZ:
116*a90b9d01SCy Schubert 	case CONF_OPER_CHWIDTH_USE_HT:
117206b73d0SCy Schubert 		he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
118206b73d0SCy Schubert 			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
119206b73d0SCy Schubert 		break;
120*a90b9d01SCy Schubert 	default:
121*a90b9d01SCy Schubert 		break;
122206b73d0SCy Schubert 	}
123206b73d0SCy Schubert 
124206b73d0SCy Schubert 	ie_size += mcs_nss_size + ppet_size;
125206b73d0SCy Schubert 
12685732ac8SCy Schubert 	*pos++ = WLAN_EID_EXTENSION;
127206b73d0SCy Schubert 	*pos++ = 1 + ie_size;
12885732ac8SCy Schubert 	*pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
12985732ac8SCy Schubert 
13085732ac8SCy Schubert 	cap = (struct ieee80211_he_capabilities *) pos;
13185732ac8SCy Schubert 	os_memset(cap, 0, sizeof(*cap));
13285732ac8SCy Schubert 
133206b73d0SCy Schubert 	os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
134206b73d0SCy Schubert 		  HE_MAX_MAC_CAPAB_SIZE);
135206b73d0SCy Schubert 	os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
136206b73d0SCy Schubert 		  HE_MAX_PHY_CAPAB_SIZE);
137206b73d0SCy Schubert 	os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
138206b73d0SCy Schubert 	if (ppet_size)
139206b73d0SCy Schubert 		os_memcpy(&cap->optional[mcs_nss_size],
140206b73d0SCy Schubert 			  mode->he_capab[opmode].ppet,  ppet_size);
141206b73d0SCy Schubert 
14285732ac8SCy Schubert 	if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
14385732ac8SCy Schubert 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
14485732ac8SCy Schubert 			HE_PHYCAP_SU_BEAMFORMER_CAPAB;
145206b73d0SCy Schubert 	else
146206b73d0SCy Schubert 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
147206b73d0SCy Schubert 			~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
14885732ac8SCy Schubert 
14985732ac8SCy Schubert 	if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
15085732ac8SCy Schubert 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
15185732ac8SCy Schubert 			HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
152206b73d0SCy Schubert 	else
153206b73d0SCy Schubert 		cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
154206b73d0SCy Schubert 			~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
15585732ac8SCy Schubert 
15685732ac8SCy Schubert 	if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
15785732ac8SCy Schubert 		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
15885732ac8SCy Schubert 			HE_PHYCAP_MU_BEAMFORMER_CAPAB;
159206b73d0SCy Schubert 	else
160206b73d0SCy Schubert 		cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
161206b73d0SCy Schubert 			~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
16285732ac8SCy Schubert 
163206b73d0SCy Schubert 	cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
164206b73d0SCy Schubert 		he_oper_chwidth;
165206b73d0SCy Schubert 
166206b73d0SCy Schubert 	pos += ie_size;
16785732ac8SCy Schubert 
16885732ac8SCy Schubert 	return pos;
16985732ac8SCy Schubert }
17085732ac8SCy Schubert 
17185732ac8SCy Schubert 
hostapd_eid_he_operation(struct hostapd_data * hapd,u8 * eid)17285732ac8SCy Schubert u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
17385732ac8SCy Schubert {
17485732ac8SCy Schubert 	struct ieee80211_he_operation *oper;
17585732ac8SCy Schubert 	u8 *pos = eid;
176206b73d0SCy Schubert 	int oper_size = 6;
177206b73d0SCy Schubert 	u32 params = 0;
17885732ac8SCy Schubert 
17985732ac8SCy Schubert 	if (!hapd->iface->current_mode)
18085732ac8SCy Schubert 		return eid;
18185732ac8SCy Schubert 
182c1d255d3SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class))
183c1d255d3SCy Schubert 		oper_size += 5;
184c1d255d3SCy Schubert 
18585732ac8SCy Schubert 	*pos++ = WLAN_EID_EXTENSION;
186206b73d0SCy Schubert 	*pos++ = 1 + oper_size;
18785732ac8SCy Schubert 	*pos++ = WLAN_EID_EXT_HE_OPERATION;
18885732ac8SCy Schubert 
18985732ac8SCy Schubert 	oper = (struct ieee80211_he_operation *) pos;
19085732ac8SCy Schubert 	os_memset(oper, 0, sizeof(*oper));
19185732ac8SCy Schubert 
19285732ac8SCy Schubert 	if (hapd->iface->conf->he_op.he_default_pe_duration)
193206b73d0SCy Schubert 		params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
19485732ac8SCy Schubert 			   HE_OPERATION_DFLT_PE_DURATION_OFFSET);
19585732ac8SCy Schubert 
19685732ac8SCy Schubert 	if (hapd->iface->conf->he_op.he_twt_required)
197206b73d0SCy Schubert 		params |= HE_OPERATION_TWT_REQUIRED;
19885732ac8SCy Schubert 
19985732ac8SCy Schubert 	if (hapd->iface->conf->he_op.he_rts_threshold)
200206b73d0SCy Schubert 		params |= (hapd->iface->conf->he_op.he_rts_threshold <<
20185732ac8SCy Schubert 			   HE_OPERATION_RTS_THRESHOLD_OFFSET);
20285732ac8SCy Schubert 
2034b72b91aSCy Schubert 	if (hapd->iface->conf->he_op.he_er_su_disable)
2044b72b91aSCy Schubert 		params |= HE_OPERATION_ER_SU_DISABLE;
2054b72b91aSCy Schubert 
206*a90b9d01SCy Schubert 	if (hapd->iface->conf->he_op.he_bss_color_disabled ||
207*a90b9d01SCy Schubert 	    hapd->cca_in_progress)
208c1d255d3SCy Schubert 		params |= HE_OPERATION_BSS_COLOR_DISABLED;
209c1d255d3SCy Schubert 	if (hapd->iface->conf->he_op.he_bss_color_partial)
210c1d255d3SCy Schubert 		params |= HE_OPERATION_BSS_COLOR_PARTIAL;
211c1d255d3SCy Schubert 	params |= hapd->iface->conf->he_op.he_bss_color <<
212c1d255d3SCy Schubert 		HE_OPERATION_BSS_COLOR_OFFSET;
213206b73d0SCy Schubert 
214206b73d0SCy Schubert 	/* HE minimum required basic MCS and NSS for STAs */
215206b73d0SCy Schubert 	oper->he_mcs_nss_set =
216206b73d0SCy Schubert 		host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
217206b73d0SCy Schubert 
21885732ac8SCy Schubert 	/* TODO: conditional MaxBSSID Indicator subfield */
21985732ac8SCy Schubert 
220c1d255d3SCy Schubert 	pos += 6; /* skip the fixed part */
221206b73d0SCy Schubert 
222c1d255d3SCy Schubert 	if (is_6ghz_op_class(hapd->iconf->op_class)) {
223*a90b9d01SCy Schubert 		enum oper_chan_width oper_chwidth =
224*a90b9d01SCy Schubert 			hostapd_get_oper_chwidth(hapd->iconf);
225*a90b9d01SCy Schubert 		u8 seg0 = hapd->iconf->he_oper_centr_freq_seg0_idx;
226c1d255d3SCy Schubert 		u8 seg1 = hostapd_get_oper_centr_freq_seg1_idx(hapd->iconf);
227*a90b9d01SCy Schubert 		u8 control;
228*a90b9d01SCy Schubert #ifdef CONFIG_IEEE80211BE
229*a90b9d01SCy Schubert 		u16 punct_bitmap = hostapd_get_punct_bitmap(hapd);
230*a90b9d01SCy Schubert 
231*a90b9d01SCy Schubert 		if (punct_bitmap) {
232*a90b9d01SCy Schubert 			punct_update_legacy_bw(punct_bitmap,
233*a90b9d01SCy Schubert 					       hapd->iconf->channel,
234*a90b9d01SCy Schubert 					       &oper_chwidth, &seg0, &seg1);
235*a90b9d01SCy Schubert 		}
236*a90b9d01SCy Schubert #endif /* CONFIG_IEEE80211BE */
237c1d255d3SCy Schubert 
238c1d255d3SCy Schubert 		if (!seg0)
239c1d255d3SCy Schubert 			seg0 = hapd->iconf->channel;
240c1d255d3SCy Schubert 
241c1d255d3SCy Schubert 		params |= HE_OPERATION_6GHZ_OPER_INFO;
242c1d255d3SCy Schubert 
243c1d255d3SCy Schubert 		/* 6 GHz Operation Information field
244*a90b9d01SCy Schubert 		 * IEEE Std 802.11ax-2021, 9.4.2.249 HE Operation element,
245c1d255d3SCy Schubert 		 * Figure 9-788k
246c1d255d3SCy Schubert 		 */
247c1d255d3SCy Schubert 		*pos++ = hapd->iconf->channel; /* Primary Channel */
248c1d255d3SCy Schubert 
249*a90b9d01SCy Schubert 		/* Control:
250*a90b9d01SCy Schubert 		 *	bits 0-1: Channel Width
251*a90b9d01SCy Schubert 		 *	bit 2: Duplicate Beacon
252*a90b9d01SCy Schubert 		 *	bits 3-5: Regulatory Info
253*a90b9d01SCy Schubert 		 */
254*a90b9d01SCy Schubert 		/* Channel Width */
255c1d255d3SCy Schubert 		if (seg1)
256*a90b9d01SCy Schubert 			control = 3;
257c1d255d3SCy Schubert 		else
258*a90b9d01SCy Schubert 			control = center_idx_to_bw_6ghz(seg0);
259*a90b9d01SCy Schubert 
260*a90b9d01SCy Schubert 		control |= hapd->iconf->he_6ghz_reg_pwr_type <<
261*a90b9d01SCy Schubert 			HE_6GHZ_OPER_INFO_CTRL_REG_INFO_SHIFT;
262*a90b9d01SCy Schubert 
263*a90b9d01SCy Schubert 		*pos++ = control;
264c1d255d3SCy Schubert 
265c1d255d3SCy Schubert 		/* Channel Center Freq Seg0/Seg1 */
266*a90b9d01SCy Schubert 		if (oper_chwidth == CONF_OPER_CHWIDTH_160MHZ ||
267*a90b9d01SCy Schubert 		    oper_chwidth == CONF_OPER_CHWIDTH_320MHZ) {
268c1d255d3SCy Schubert 			/*
269c1d255d3SCy Schubert 			 * Seg 0 indicates the channel center frequency index of
270c1d255d3SCy Schubert 			 * the 160 MHz channel.
271c1d255d3SCy Schubert 			 */
272c1d255d3SCy Schubert 			seg1 = seg0;
273c1d255d3SCy Schubert 			if (hapd->iconf->channel < seg0)
274c1d255d3SCy Schubert 				seg0 -= 8;
275c1d255d3SCy Schubert 			else
276c1d255d3SCy Schubert 				seg0 += 8;
277c1d255d3SCy Schubert 		}
278c1d255d3SCy Schubert 
279c1d255d3SCy Schubert 		*pos++ = seg0;
280c1d255d3SCy Schubert 		*pos++ = seg1;
281c1d255d3SCy Schubert 		/* Minimum Rate */
282c1d255d3SCy Schubert 		*pos++ = 6; /* TODO: what should be set here? */
283c1d255d3SCy Schubert 	}
284c1d255d3SCy Schubert 
285c1d255d3SCy Schubert 	oper->he_oper_params = host_to_le32(params);
28685732ac8SCy Schubert 
28785732ac8SCy Schubert 	return pos;
28885732ac8SCy Schubert }
2894bc52338SCy Schubert 
2904bc52338SCy Schubert 
hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data * hapd,u8 * eid)2914bc52338SCy Schubert u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
2924bc52338SCy Schubert {
2934bc52338SCy Schubert 	struct ieee80211_he_mu_edca_parameter_set *edca;
2944bc52338SCy Schubert 	u8 *pos;
2954bc52338SCy Schubert 	size_t i;
2964bc52338SCy Schubert 
2974bc52338SCy Schubert 	pos = (u8 *) &hapd->iface->conf->he_mu_edca;
2984bc52338SCy Schubert 	for (i = 0; i < sizeof(*edca); i++) {
2994bc52338SCy Schubert 		if (pos[i])
3004bc52338SCy Schubert 			break;
3014bc52338SCy Schubert 	}
3024bc52338SCy Schubert 	if (i == sizeof(*edca))
3034bc52338SCy Schubert 		return eid; /* no MU EDCA Parameters configured */
3044bc52338SCy Schubert 
3054bc52338SCy Schubert 	pos = eid;
3064bc52338SCy Schubert 	*pos++ = WLAN_EID_EXTENSION;
3074bc52338SCy Schubert 	*pos++ = 1 + sizeof(*edca);
3084bc52338SCy Schubert 	*pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
3094bc52338SCy Schubert 
3104bc52338SCy Schubert 	edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
3114bc52338SCy Schubert 	os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
3124bc52338SCy Schubert 
3134bc52338SCy Schubert 	wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
3144bc52338SCy Schubert 		    pos, sizeof(*edca));
3154bc52338SCy Schubert 
3164bc52338SCy Schubert 	pos += sizeof(*edca);
3174bc52338SCy Schubert 
3184bc52338SCy Schubert 	return pos;
3194bc52338SCy Schubert }
320206b73d0SCy Schubert 
321206b73d0SCy Schubert 
hostapd_eid_spatial_reuse(struct hostapd_data * hapd,u8 * eid)322206b73d0SCy Schubert u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
323206b73d0SCy Schubert {
324206b73d0SCy Schubert 	struct ieee80211_spatial_reuse *spr;
325206b73d0SCy Schubert 	u8 *pos = eid, *spr_param;
326206b73d0SCy Schubert 	u8 sz = 1;
327206b73d0SCy Schubert 
328206b73d0SCy Schubert 	if (!hapd->iface->conf->spr.sr_control)
329206b73d0SCy Schubert 		return eid;
330206b73d0SCy Schubert 
331206b73d0SCy Schubert 	if (hapd->iface->conf->spr.sr_control &
332206b73d0SCy Schubert 	    SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
333206b73d0SCy Schubert 		sz++;
334206b73d0SCy Schubert 
335206b73d0SCy Schubert 	if (hapd->iface->conf->spr.sr_control &
336206b73d0SCy Schubert 	    SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
337206b73d0SCy Schubert 		sz += 18;
338206b73d0SCy Schubert 
339206b73d0SCy Schubert 	*pos++ = WLAN_EID_EXTENSION;
340206b73d0SCy Schubert 	*pos++ = 1 + sz;
341206b73d0SCy Schubert 	*pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
342206b73d0SCy Schubert 
343206b73d0SCy Schubert 	spr = (struct ieee80211_spatial_reuse *) pos;
344206b73d0SCy Schubert 	os_memset(spr, 0, sizeof(*spr));
345206b73d0SCy Schubert 
346206b73d0SCy Schubert 	spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
347206b73d0SCy Schubert 	pos++;
348206b73d0SCy Schubert 	spr_param = spr->params;
349206b73d0SCy Schubert 	if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
350206b73d0SCy Schubert 		*spr_param++ =
351206b73d0SCy Schubert 			hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
352206b73d0SCy Schubert 		pos++;
353206b73d0SCy Schubert 	}
354206b73d0SCy Schubert 	if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
355206b73d0SCy Schubert 		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
356206b73d0SCy Schubert 		*spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
357c1d255d3SCy Schubert 		os_memcpy(spr_param,
358c1d255d3SCy Schubert 			  hapd->iface->conf->spr.srg_bss_color_bitmap, 8);
359c1d255d3SCy Schubert 		spr_param += 8;
360c1d255d3SCy Schubert 		os_memcpy(spr_param,
361c1d255d3SCy Schubert 			  hapd->iface->conf->spr.srg_partial_bssid_bitmap, 8);
362206b73d0SCy Schubert 		pos += 18;
363206b73d0SCy Schubert 	}
364206b73d0SCy Schubert 
365206b73d0SCy Schubert 	return pos;
366206b73d0SCy Schubert }
367206b73d0SCy Schubert 
368206b73d0SCy Schubert 
hostapd_eid_he_6ghz_band_cap(struct hostapd_data * hapd,u8 * eid)369c1d255d3SCy Schubert u8 * hostapd_eid_he_6ghz_band_cap(struct hostapd_data *hapd, u8 *eid)
370c1d255d3SCy Schubert {
371c1d255d3SCy Schubert 	struct hostapd_config *conf = hapd->iface->conf;
372c1d255d3SCy Schubert 	struct hostapd_hw_modes *mode = hapd->iface->current_mode;
373c1d255d3SCy Schubert 	struct he_capabilities *he_cap;
374c1d255d3SCy Schubert 	struct ieee80211_he_6ghz_band_cap *cap;
375c1d255d3SCy Schubert 	u16 capab;
376c1d255d3SCy Schubert 	u8 *pos;
377c1d255d3SCy Schubert 
378c1d255d3SCy Schubert 	if (!mode || !is_6ghz_op_class(hapd->iconf->op_class) ||
379c1d255d3SCy Schubert 	    !is_6ghz_freq(hapd->iface->freq))
380c1d255d3SCy Schubert 		return eid;
381c1d255d3SCy Schubert 
382c1d255d3SCy Schubert 	he_cap = &mode->he_capab[IEEE80211_MODE_AP];
383c1d255d3SCy Schubert 	capab = he_cap->he_6ghz_capa & HE_6GHZ_BAND_CAP_MIN_MPDU_START;
384c1d255d3SCy Schubert 	capab |= (conf->he_6ghz_max_ampdu_len_exp <<
385c1d255d3SCy Schubert 		  HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_SHIFT) &
386c1d255d3SCy Schubert 		HE_6GHZ_BAND_CAP_MAX_AMPDU_LEN_EXP_MASK;
387c1d255d3SCy Schubert 	capab |= (conf->he_6ghz_max_mpdu <<
388c1d255d3SCy Schubert 		  HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_SHIFT) &
389c1d255d3SCy Schubert 		HE_6GHZ_BAND_CAP_MAX_MPDU_LEN_MASK;
390c1d255d3SCy Schubert 	capab |= HE_6GHZ_BAND_CAP_SMPS_DISABLED;
391c1d255d3SCy Schubert 	if (conf->he_6ghz_rx_ant_pat)
392c1d255d3SCy Schubert 		capab |= HE_6GHZ_BAND_CAP_RX_ANTPAT_CONS;
393c1d255d3SCy Schubert 	if (conf->he_6ghz_tx_ant_pat)
394c1d255d3SCy Schubert 		capab |= HE_6GHZ_BAND_CAP_TX_ANTPAT_CONS;
395c1d255d3SCy Schubert 
396c1d255d3SCy Schubert 	pos = eid;
397c1d255d3SCy Schubert 	*pos++ = WLAN_EID_EXTENSION;
398c1d255d3SCy Schubert 	*pos++ = 1 + sizeof(*cap);
399c1d255d3SCy Schubert 	*pos++ = WLAN_EID_EXT_HE_6GHZ_BAND_CAP;
400c1d255d3SCy Schubert 
401c1d255d3SCy Schubert 	cap = (struct ieee80211_he_6ghz_band_cap *) pos;
402c1d255d3SCy Schubert 	cap->capab = host_to_le16(capab);
403c1d255d3SCy Schubert 	pos += sizeof(*cap);
404c1d255d3SCy Schubert 
405c1d255d3SCy Schubert 	return pos;
406c1d255d3SCy Schubert }
407c1d255d3SCy Schubert 
408c1d255d3SCy Schubert 
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)409206b73d0SCy Schubert void hostapd_get_he_capab(struct hostapd_data *hapd,
410206b73d0SCy Schubert 			  const struct ieee80211_he_capabilities *he_cap,
411206b73d0SCy Schubert 			  struct ieee80211_he_capabilities *neg_he_cap,
412206b73d0SCy Schubert 			  size_t he_capab_len)
413206b73d0SCy Schubert {
414206b73d0SCy Schubert 	if (!he_cap)
415206b73d0SCy Schubert 		return;
416206b73d0SCy Schubert 
417206b73d0SCy Schubert 	if (he_capab_len > sizeof(*neg_he_cap))
418206b73d0SCy Schubert 		he_capab_len = sizeof(*neg_he_cap);
419206b73d0SCy Schubert 	/* TODO: mask out unsupported features */
420206b73d0SCy Schubert 
421206b73d0SCy Schubert 	os_memcpy(neg_he_cap, he_cap, he_capab_len);
422206b73d0SCy Schubert }
423206b73d0SCy Schubert 
424206b73d0SCy Schubert 
check_valid_he_mcs(struct hostapd_data * hapd,const u8 * sta_he_capab,enum ieee80211_op_mode opmode)425206b73d0SCy Schubert static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
426206b73d0SCy Schubert 			      enum ieee80211_op_mode opmode)
427206b73d0SCy Schubert {
428206b73d0SCy Schubert 	u16 sta_rx_mcs_set, ap_tx_mcs_set;
429206b73d0SCy Schubert 	u8 mcs_count = 0;
430206b73d0SCy Schubert 	const u16 *ap_mcs_set, *sta_mcs_set;
431206b73d0SCy Schubert 	int i;
432206b73d0SCy Schubert 
433206b73d0SCy Schubert 	if (!hapd->iface->current_mode)
434206b73d0SCy Schubert 		return 1;
435206b73d0SCy Schubert 	ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
436206b73d0SCy Schubert 	sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
437206b73d0SCy Schubert 			       sta_he_capab)->optional;
438206b73d0SCy Schubert 
439206b73d0SCy Schubert 	/*
440206b73d0SCy Schubert 	 * Disable HE capabilities for STAs for which there is not even a single
441206b73d0SCy Schubert 	 * allowed MCS in any supported number of streams, i.e., STA is
442206b73d0SCy Schubert 	 * advertising 3 (not supported) as HE MCS rates for all supported
443206b73d0SCy Schubert 	 * band/stream cases.
444206b73d0SCy Schubert 	 */
445206b73d0SCy Schubert 	switch (hapd->iface->conf->he_oper_chwidth) {
446*a90b9d01SCy Schubert 	case CONF_OPER_CHWIDTH_80P80MHZ:
447206b73d0SCy Schubert 		mcs_count = 3;
448206b73d0SCy Schubert 		break;
449*a90b9d01SCy Schubert 	case CONF_OPER_CHWIDTH_160MHZ:
450206b73d0SCy Schubert 		mcs_count = 2;
451206b73d0SCy Schubert 		break;
452206b73d0SCy Schubert 	default:
453206b73d0SCy Schubert 		mcs_count = 1;
454206b73d0SCy Schubert 		break;
455206b73d0SCy Schubert 	}
456206b73d0SCy Schubert 
457206b73d0SCy Schubert 	for (i = 0; i < mcs_count; i++) {
458206b73d0SCy Schubert 		int j;
459206b73d0SCy Schubert 
460206b73d0SCy Schubert 		/* AP Tx MCS map vs. STA Rx MCS map */
461206b73d0SCy Schubert 		sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
462206b73d0SCy Schubert 		ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
463206b73d0SCy Schubert 					     &ap_mcs_set[(i * 2) + 1]);
464206b73d0SCy Schubert 
465206b73d0SCy Schubert 		for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
466206b73d0SCy Schubert 			if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
467206b73d0SCy Schubert 				continue;
468206b73d0SCy Schubert 
469206b73d0SCy Schubert 			if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
470206b73d0SCy Schubert 				continue;
471206b73d0SCy Schubert 
472206b73d0SCy Schubert 			return 1;
473206b73d0SCy Schubert 		}
474206b73d0SCy Schubert 	}
475206b73d0SCy Schubert 
476206b73d0SCy Schubert 	wpa_printf(MSG_DEBUG,
477206b73d0SCy Schubert 		   "No matching HE MCS found between AP TX and STA RX");
478206b73d0SCy Schubert 
479206b73d0SCy Schubert 	return 0;
480206b73d0SCy Schubert }
481206b73d0SCy Schubert 
482206b73d0SCy Schubert 
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)483206b73d0SCy Schubert u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
484206b73d0SCy Schubert 		      enum ieee80211_op_mode opmode, const u8 *he_capab,
485206b73d0SCy Schubert 		      size_t he_capab_len)
486206b73d0SCy Schubert {
487c1d255d3SCy Schubert 	if (!he_capab || !(sta->flags & WLAN_STA_WMM) ||
488c1d255d3SCy Schubert 	    !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax ||
489206b73d0SCy Schubert 	    !check_valid_he_mcs(hapd, he_capab, opmode) ||
490c1d255d3SCy Schubert 	    ieee80211_invalid_he_cap_size(he_capab, he_capab_len) ||
491206b73d0SCy Schubert 	    he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
492206b73d0SCy Schubert 		sta->flags &= ~WLAN_STA_HE;
493206b73d0SCy Schubert 		os_free(sta->he_capab);
494206b73d0SCy Schubert 		sta->he_capab = NULL;
495206b73d0SCy Schubert 		return WLAN_STATUS_SUCCESS;
496206b73d0SCy Schubert 	}
497206b73d0SCy Schubert 
498206b73d0SCy Schubert 	if (!sta->he_capab) {
499206b73d0SCy Schubert 		sta->he_capab =
500206b73d0SCy Schubert 			os_zalloc(sizeof(struct ieee80211_he_capabilities));
501206b73d0SCy Schubert 		if (!sta->he_capab)
502206b73d0SCy Schubert 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
503206b73d0SCy Schubert 	}
504206b73d0SCy Schubert 
505206b73d0SCy Schubert 	sta->flags |= WLAN_STA_HE;
506206b73d0SCy Schubert 	os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
507206b73d0SCy Schubert 	os_memcpy(sta->he_capab, he_capab, he_capab_len);
508206b73d0SCy Schubert 	sta->he_capab_len = he_capab_len;
509206b73d0SCy Schubert 
510206b73d0SCy Schubert 	return WLAN_STATUS_SUCCESS;
511206b73d0SCy Schubert }
512c1d255d3SCy Schubert 
513c1d255d3SCy Schubert 
copy_sta_he_6ghz_capab(struct hostapd_data * hapd,struct sta_info * sta,const u8 * he_6ghz_capab)514c1d255d3SCy Schubert u16 copy_sta_he_6ghz_capab(struct hostapd_data *hapd, struct sta_info *sta,
515c1d255d3SCy Schubert 			   const u8 *he_6ghz_capab)
516c1d255d3SCy Schubert {
517c1d255d3SCy Schubert 	if (!he_6ghz_capab || !hapd->iconf->ieee80211ax ||
518c1d255d3SCy Schubert 	    hapd->conf->disable_11ax ||
519c1d255d3SCy Schubert 	    !is_6ghz_op_class(hapd->iconf->op_class)) {
520c1d255d3SCy Schubert 		sta->flags &= ~WLAN_STA_6GHZ;
521c1d255d3SCy Schubert 		os_free(sta->he_6ghz_capab);
522c1d255d3SCy Schubert 		sta->he_6ghz_capab = NULL;
523c1d255d3SCy Schubert 		return WLAN_STATUS_SUCCESS;
524c1d255d3SCy Schubert 	}
525c1d255d3SCy Schubert 
526c1d255d3SCy Schubert 	if (!sta->he_6ghz_capab) {
527c1d255d3SCy Schubert 		sta->he_6ghz_capab =
528c1d255d3SCy Schubert 			os_zalloc(sizeof(struct ieee80211_he_6ghz_band_cap));
529c1d255d3SCy Schubert 		if (!sta->he_6ghz_capab)
530c1d255d3SCy Schubert 			return WLAN_STATUS_UNSPECIFIED_FAILURE;
531c1d255d3SCy Schubert 	}
532c1d255d3SCy Schubert 
533c1d255d3SCy Schubert 	sta->flags |= WLAN_STA_6GHZ;
534c1d255d3SCy Schubert 	os_memcpy(sta->he_6ghz_capab, he_6ghz_capab,
535c1d255d3SCy Schubert 		  sizeof(struct ieee80211_he_6ghz_band_cap));
536c1d255d3SCy Schubert 
537c1d255d3SCy Schubert 	return WLAN_STATUS_SUCCESS;
538c1d255d3SCy Schubert }
539c1d255d3SCy Schubert 
540c1d255d3SCy Schubert 
hostapd_get_he_twt_responder(struct hostapd_data * hapd,enum ieee80211_op_mode mode)541c1d255d3SCy Schubert int hostapd_get_he_twt_responder(struct hostapd_data *hapd,
542c1d255d3SCy Schubert 				 enum ieee80211_op_mode mode)
543c1d255d3SCy Schubert {
544c1d255d3SCy Schubert 	u8 *mac_cap;
545c1d255d3SCy Schubert 
546c1d255d3SCy Schubert 	if (!hapd->iface->current_mode ||
547*a90b9d01SCy Schubert 	    !hapd->iface->current_mode->he_capab[mode].he_supported ||
548*a90b9d01SCy Schubert 	    !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
549c1d255d3SCy Schubert 		return 0;
550c1d255d3SCy Schubert 
551c1d255d3SCy Schubert 	mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
552c1d255d3SCy Schubert 
553c1d255d3SCy Schubert 	return !!(mac_cap[HE_MAC_CAPAB_0] & HE_MACCAP_TWT_RESPONDER) &&
554c1d255d3SCy Schubert 		hapd->iface->conf->he_op.he_twt_responder;
555c1d255d3SCy Schubert }
556*a90b9d01SCy Schubert 
557*a90b9d01SCy Schubert 
hostapd_eid_cca(struct hostapd_data * hapd,u8 * eid)558*a90b9d01SCy Schubert u8 * hostapd_eid_cca(struct hostapd_data *hapd, u8 *eid)
559*a90b9d01SCy Schubert {
560*a90b9d01SCy Schubert 	if (!hapd->cca_in_progress)
561*a90b9d01SCy Schubert 		return eid;
562*a90b9d01SCy Schubert 
563*a90b9d01SCy Schubert 	/* BSS Color Change Announcement element */
564*a90b9d01SCy Schubert 	*eid++ = WLAN_EID_EXTENSION;
565*a90b9d01SCy Schubert 	*eid++ = 3;
566*a90b9d01SCy Schubert 	*eid++ = WLAN_EID_EXT_COLOR_CHANGE_ANNOUNCEMENT;
567*a90b9d01SCy Schubert 	*eid++ = hapd->cca_count; /* Color Switch Countdown */
568*a90b9d01SCy Schubert 	*eid++ = hapd->cca_color; /* New BSS Color Information */
569*a90b9d01SCy Schubert 
570*a90b9d01SCy Schubert 	return eid;
571*a90b9d01SCy Schubert }
572