• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * hostapd / IEEE 802.11be EHT
3  * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8 
9 #include "utils/includes.h"
10 #include "utils/common.h"
11 #include "hostapd.h"
12 #include "sta_info.h"
13 #include "ieee802_11.h"
14 
15 
ieee80211_eht_ppet_size(u16 ppe_thres_hdr,const u8 * phy_cap_info)16 static u16 ieee80211_eht_ppet_size(u16 ppe_thres_hdr, const u8 *phy_cap_info)
17 {
18 	u8 ru;
19 	u16 sz = 0;
20 
21 	if ((phy_cap_info[EHT_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
22 	     EHT_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
23 		return 0;
24 
25 	ru = (ppe_thres_hdr &
26 	      EHT_PPE_THRES_RU_INDEX_MASK) >> EHT_PPE_THRES_RU_INDEX_SHIFT;
27 	while (ru) {
28 		if (ru & 0x1)
29 			sz++;
30 		ru >>= 1;
31 	}
32 
33 	sz = sz * (1 + ((ppe_thres_hdr & EHT_PPE_THRES_NSS_MASK) >>
34 			EHT_PPE_THRES_NSS_SHIFT));
35 	sz = (sz * 6) + 9;
36 	if (sz % 8)
37 		sz += 8;
38 	sz /= 8;
39 
40 	return sz;
41 }
42 
43 
ieee80211_eht_mcs_set_size(const u8 * he_phy_cap,const u8 * eht_phy_cap)44 static u8 ieee80211_eht_mcs_set_size(const u8 *he_phy_cap,
45 				     const u8 *eht_phy_cap)
46 {
47 	u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
48 
49 	if ((he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
50 	    (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
51 	     HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
52 	     HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
53 	     HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
54 		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
55 
56 	if (he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &
57 	    (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
58 	     HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G))
59 		sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
60 
61 	if (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
62 	    EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK)
63 		sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
64 
65 	return sz;
66 }
67 
68 
hostapd_eid_eht_capab_len(struct hostapd_data * hapd,enum ieee80211_op_mode opmode)69 size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
70 				 enum ieee80211_op_mode opmode)
71 {
72 	struct hostapd_hw_modes *mode;
73 	struct eht_capabilities *eht_cap;
74 	size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
75 
76 	mode = hapd->iface->current_mode;
77 	if (!mode)
78 		return 0;
79 
80 	eht_cap = &mode->eht_capab[opmode];
81 	if (!eht_cap->eht_supported)
82 		return 0;
83 
84 	len += ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
85 					  eht_cap->phy_cap);
86 	len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
87 				       eht_cap->phy_cap);
88 
89 	return len;
90 }
91 
92 
hostapd_eid_eht_capab(struct hostapd_data * hapd,u8 * eid,enum ieee80211_op_mode opmode)93 u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
94 			   enum ieee80211_op_mode opmode)
95 {
96 	struct hostapd_hw_modes *mode;
97 	struct eht_capabilities *eht_cap;
98 	struct ieee80211_eht_capabilities *cap;
99 	size_t mcs_nss_len, ppe_thresh_len;
100 	u8 *pos = eid, *length_pos;
101 
102 	mode = hapd->iface->current_mode;
103 	if (!mode)
104 		return eid;
105 
106 	eht_cap = &mode->eht_capab[opmode];
107 	if (!eht_cap->eht_supported)
108 		return eid;
109 
110 	*pos++ = WLAN_EID_EXTENSION;
111 	length_pos = pos++;
112 	*pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
113 
114 	cap = (struct ieee80211_eht_capabilities *) pos;
115 	os_memset(cap, 0, sizeof(*cap));
116 	cap->mac_cap = host_to_le16(eht_cap->mac_cap);
117 	os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
118 
119 	if (!is_6ghz_op_class(hapd->iconf->op_class))
120 		cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
121 			~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
122 	if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
123 		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
124 			~EHT_PHYCAP_SU_BEAMFORMER;
125 
126 	if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
127 		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
128 			~EHT_PHYCAP_SU_BEAMFORMEE;
129 
130 	if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
131 		cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
132 			~EHT_PHYCAP_MU_BEAMFORMER_MASK;
133 
134 	pos = cap->optional;
135 
136 	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
137 						 eht_cap->phy_cap);
138 	if (mcs_nss_len) {
139 		os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
140 		pos += mcs_nss_len;
141 	}
142 
143 	ppe_thresh_len = ieee80211_eht_ppet_size(
144 				WPA_GET_LE16(&eht_cap->ppet[0]),
145 				eht_cap->phy_cap);
146 	if (ppe_thresh_len) {
147 		os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
148 		pos += ppe_thresh_len;
149 	}
150 
151 	*length_pos = pos - (eid + 2);
152 	return pos;
153 }
154 
155 
hostapd_eid_eht_operation(struct hostapd_data * hapd,u8 * eid)156 u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
157 {
158 	struct hostapd_config *conf = hapd->iconf;
159 	struct ieee80211_eht_operation *oper;
160 	u8 *pos = eid, chwidth, seg0 = 0, seg1 = 0;
161 
162 	if (!hapd->iface->current_mode)
163 		return eid;
164 
165 	*pos++ = WLAN_EID_EXTENSION;
166 	*pos++ = 5;
167 	*pos++ = WLAN_EID_EXT_EHT_OPERATION;
168 
169 	oper = (struct ieee80211_eht_operation *) pos;
170 	oper->oper_params = EHT_OPER_INFO_PRESENT;
171 
172 	if (is_6ghz_op_class(conf->op_class))
173 		chwidth = op_class_to_ch_width(conf->op_class);
174 	else
175 		chwidth = conf->eht_oper_chwidth;
176 
177 	seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
178 
179 	switch (chwidth) {
180 #if 0 /* FIX: Need to clean up CHANWIDTH_* use for protocol vs. internal
181        * needs to be able to define this. */
182 	case CHANWIDTH_320MHZ:
183 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
184 		seg1 = seg0;
185 		if (hapd->iconf->channel < seg0)
186 			seg0 -= 16;
187 		else
188 			seg0 += 16;
189 		break;
190 #endif
191 	case CHANWIDTH_160MHZ:
192 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
193 		seg1 = seg0;
194 		if (hapd->iconf->channel < seg0)
195 			seg0 -= 8;
196 		else
197 			seg0 += 8;
198 		break;
199 	case CHANWIDTH_80MHZ:
200 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
201 		break;
202 	case CHANWIDTH_USE_HT:
203 		if (seg0)
204 			oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
205 		break;
206 	default:
207 		return eid;
208 	}
209 
210 	oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
211 	oper->oper_info.ccfs1 = seg1;
212 
213 	return pos + 4;
214 }
215 
216 
check_valid_eht_mcs_nss(struct hostapd_data * hapd,const u8 * ap_mcs,const u8 * sta_mcs,u8 mcs_count,u8 map_len)217 static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
218 				    const u8 *sta_mcs, u8 mcs_count, u8 map_len)
219 {
220 	unsigned int i, j;
221 
222 	for (i = 0; i < mcs_count; i++) {
223 		ap_mcs += i * 3;
224 		sta_mcs += i * 3;
225 
226 		for (j = 0; j < map_len; j++) {
227 			if (((ap_mcs[j] >> 4) & 0xFF) == 0)
228 				continue;
229 
230 			if ((sta_mcs[j] & 0xFF) == 0)
231 				continue;
232 
233 			return true;
234 		}
235 	}
236 
237 	wpa_printf(MSG_DEBUG,
238 		   "No matching EHT MCS found between AP TX and STA RX");
239 	return false;
240 }
241 
242 
check_valid_eht_mcs(struct hostapd_data * hapd,const u8 * sta_eht_capab,enum ieee80211_op_mode opmode)243 static bool check_valid_eht_mcs(struct hostapd_data *hapd,
244 				const u8 *sta_eht_capab,
245 				enum ieee80211_op_mode opmode)
246 {
247 	struct hostapd_hw_modes *mode;
248 	const struct ieee80211_eht_capabilities *capab;
249 	const u8 *ap_mcs, *sta_mcs;
250 	u8 mcs_count = 1;
251 
252 	mode = hapd->iface->current_mode;
253 	if (!mode)
254 		return true;
255 
256 	ap_mcs = mode->eht_capab[opmode].mcs;
257 	capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
258 	sta_mcs = capab->optional;
259 
260 	if (ieee80211_eht_mcs_set_size(mode->he_capab[opmode].phy_cap,
261 				       mode->eht_capab[opmode].phy_cap) ==
262 	    EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
263 		return check_valid_eht_mcs_nss(
264 			hapd, ap_mcs, sta_mcs, 1,
265 			EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
266 
267 	switch (hapd->iface->conf->eht_oper_chwidth) {
268 	/* TODO: CHANWIDTH_320MHZ */
269 	case CHANWIDTH_80P80MHZ:
270 	case CHANWIDTH_160MHZ:
271 		mcs_count = 2;
272 		break;
273 	}
274 
275 	return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
276 				       EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
277 }
278 
279 
ieee80211_invalid_eht_cap_size(const u8 * he_cap,const u8 * eht_cap,size_t len)280 static bool ieee80211_invalid_eht_cap_size(const u8 *he_cap, const u8 *eht_cap,
281 					   size_t len)
282 {
283 	const struct ieee80211_he_capabilities *he_capab;
284 	struct ieee80211_eht_capabilities *cap;
285 	const u8 *he_phy_cap;
286 	size_t cap_len;
287 	u16 ppe_thres_hdr;
288 
289 	he_capab = (const struct ieee80211_he_capabilities *) he_cap;
290 	he_phy_cap = he_capab->he_phy_capab_info;
291 	cap = (struct ieee80211_eht_capabilities *) eht_cap;
292 	cap_len = sizeof(*cap) - sizeof(cap->optional);
293 	if (len < cap_len)
294 		return true;
295 
296 	cap_len += ieee80211_eht_mcs_set_size(he_phy_cap, cap->phy_cap);
297 	if (len < cap_len)
298 		return true;
299 
300 	ppe_thres_hdr = len > cap_len + 1 ?
301 		WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
302 	cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
303 
304 	return len < cap_len;
305 }
306 
307 
copy_sta_eht_capab(struct hostapd_data * hapd,struct sta_info * sta,enum ieee80211_op_mode opmode,const u8 * he_capab,size_t he_capab_len,const u8 * eht_capab,size_t eht_capab_len)308 u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
309 		       enum ieee80211_op_mode opmode,
310 		       const u8 *he_capab, size_t he_capab_len,
311 		       const u8 *eht_capab, size_t eht_capab_len)
312 {
313 	if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
314 	    !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
315 	    !eht_capab ||
316 	    ieee80211_invalid_eht_cap_size(he_capab, eht_capab,
317 					   eht_capab_len) ||
318 	    !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
319 		sta->flags &= ~WLAN_STA_EHT;
320 		os_free(sta->eht_capab);
321 		sta->eht_capab = NULL;
322 		return WLAN_STATUS_SUCCESS;
323 	}
324 
325 	os_free(sta->eht_capab);
326 	sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
327 	if (!sta->eht_capab) {
328 		sta->eht_capab_len = 0;
329 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
330 	}
331 
332 	sta->flags |= WLAN_STA_EHT;
333 	sta->eht_capab_len = eht_capab_len;
334 
335 	return WLAN_STATUS_SUCCESS;
336 }
337 
338 
hostapd_get_eht_capab(struct hostapd_data * hapd,const struct ieee80211_eht_capabilities * src,struct ieee80211_eht_capabilities * dest,size_t len)339 void hostapd_get_eht_capab(struct hostapd_data *hapd,
340 			   const struct ieee80211_eht_capabilities *src,
341 			   struct ieee80211_eht_capabilities *dest,
342 			   size_t len)
343 {
344 	if (!src || !dest)
345 		return;
346 
347 	if (len > sizeof(*dest))
348 		len = sizeof(*dest);
349 	/* TODO: mask out unsupported features */
350 
351 	os_memset(dest, 0, sizeof(*dest));
352 	os_memcpy(dest, src, len);
353 }
354