• 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(enum hostapd_hw_mode mode,u8 opclass,u8 he_oper_chwidth,const u8 * he_phy_cap,const u8 * eht_phy_cap)44 static u8 ieee80211_eht_mcs_set_size(enum hostapd_hw_mode mode, u8 opclass,
45 				     u8 he_oper_chwidth, const u8 *he_phy_cap,
46 				     const u8 *eht_phy_cap)
47 {
48 	u8 sz = EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
49 	bool band24, band5, band6;
50 	u8 he_phy_cap_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
51 
52 	switch (he_oper_chwidth) {
53 	case CONF_OPER_CHWIDTH_80P80MHZ:
54 		he_phy_cap_chwidth |=
55 			HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
56 		/* fall through */
57 	case CONF_OPER_CHWIDTH_160MHZ:
58 		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
59 		/* fall through */
60 	case CONF_OPER_CHWIDTH_80MHZ:
61 	case CONF_OPER_CHWIDTH_USE_HT:
62 		he_phy_cap_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
63 			HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
64 		break;
65 	}
66 
67 	he_phy_cap_chwidth &= he_phy_cap[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX];
68 
69 	band24 = mode == HOSTAPD_MODE_IEEE80211B ||
70 		mode == HOSTAPD_MODE_IEEE80211G ||
71 		mode == NUM_HOSTAPD_MODES;
72 	band5 = mode == HOSTAPD_MODE_IEEE80211A ||
73 		mode == NUM_HOSTAPD_MODES;
74 	band6 = is_6ghz_op_class(opclass);
75 
76 	if (band24 &&
77 	    (he_phy_cap_chwidth & HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G) == 0)
78 		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
79 
80 	if (band5 &&
81 	    (he_phy_cap_chwidth &
82 	     (HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
83 	      HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
84 	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)) == 0)
85 		return EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY;
86 
87 	if (band5 &&
88 	    (he_phy_cap_chwidth &
89 	     (HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G |
90 	      HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G)))
91 	    sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
92 
93 	if (band6 &&
94 	    (eht_phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &
95 	     EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK))
96 		sz += EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS;
97 
98 	return sz;
99 }
100 
101 
hostapd_eid_eht_capab_len(struct hostapd_data * hapd,enum ieee80211_op_mode opmode)102 size_t hostapd_eid_eht_capab_len(struct hostapd_data *hapd,
103 				 enum ieee80211_op_mode opmode)
104 {
105 	struct hostapd_hw_modes *mode;
106 	struct eht_capabilities *eht_cap;
107 	size_t len = 3 + 2 + EHT_PHY_CAPAB_LEN;
108 
109 	mode = hapd->iface->current_mode;
110 	if (!mode)
111 		return 0;
112 
113 	eht_cap = &mode->eht_capab[opmode];
114 	if (!eht_cap->eht_supported)
115 		return 0;
116 
117 	len += ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
118 					  hapd->iconf->he_oper_chwidth,
119 					  mode->he_capab[opmode].phy_cap,
120 					  eht_cap->phy_cap);
121 	len += ieee80211_eht_ppet_size(WPA_GET_LE16(&eht_cap->ppet[0]),
122 				       eht_cap->phy_cap);
123 
124 	return len;
125 }
126 
127 
hostapd_eid_eht_capab(struct hostapd_data * hapd,u8 * eid,enum ieee80211_op_mode opmode)128 u8 * hostapd_eid_eht_capab(struct hostapd_data *hapd, u8 *eid,
129 			   enum ieee80211_op_mode opmode)
130 {
131 	struct hostapd_hw_modes *mode;
132 	struct eht_capabilities *eht_cap;
133 	struct ieee80211_eht_capabilities *cap;
134 	size_t mcs_nss_len, ppe_thresh_len;
135 	u8 *pos = eid, *length_pos;
136 
137 	mode = hapd->iface->current_mode;
138 	if (!mode)
139 		return eid;
140 
141 	eht_cap = &mode->eht_capab[opmode];
142 	if (!eht_cap->eht_supported)
143 		return eid;
144 
145 	*pos++ = WLAN_EID_EXTENSION;
146 	length_pos = pos++;
147 	*pos++ = WLAN_EID_EXT_EHT_CAPABILITIES;
148 
149 	cap = (struct ieee80211_eht_capabilities *) pos;
150 	os_memset(cap, 0, sizeof(*cap));
151 	cap->mac_cap = host_to_le16(eht_cap->mac_cap);
152 	os_memcpy(cap->phy_cap, eht_cap->phy_cap, EHT_PHY_CAPAB_LEN);
153 
154 	if (!is_6ghz_op_class(hapd->iconf->op_class))
155 		cap->phy_cap[EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_IDX] &=
156 			~EHT_PHYCAP_320MHZ_IN_6GHZ_SUPPORT_MASK;
157 	if (!hapd->iface->conf->eht_phy_capab.su_beamformer)
158 		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMER_IDX] &=
159 			~EHT_PHYCAP_SU_BEAMFORMER;
160 
161 	if (!hapd->iface->conf->eht_phy_capab.su_beamformee)
162 		cap->phy_cap[EHT_PHYCAP_SU_BEAMFORMEE_IDX] &=
163 			~EHT_PHYCAP_SU_BEAMFORMEE;
164 
165 	if (!hapd->iface->conf->eht_phy_capab.mu_beamformer)
166 		cap->phy_cap[EHT_PHYCAP_MU_BEAMFORMER_IDX] &=
167 			~EHT_PHYCAP_MU_BEAMFORMER_MASK;
168 
169 	pos = cap->optional;
170 
171 	mcs_nss_len = ieee80211_eht_mcs_set_size(mode->mode,
172 						 hapd->iconf->op_class,
173 						 hapd->iconf->he_oper_chwidth,
174 						 mode->he_capab[opmode].phy_cap,
175 						 eht_cap->phy_cap);
176 	if (mcs_nss_len) {
177 		os_memcpy(pos, eht_cap->mcs, mcs_nss_len);
178 		pos += mcs_nss_len;
179 	}
180 
181 	ppe_thresh_len = ieee80211_eht_ppet_size(
182 				WPA_GET_LE16(&eht_cap->ppet[0]),
183 				eht_cap->phy_cap);
184 	if (ppe_thresh_len) {
185 		os_memcpy(pos, eht_cap->ppet, ppe_thresh_len);
186 		pos += ppe_thresh_len;
187 	}
188 
189 	*length_pos = pos - (eid + 2);
190 	return pos;
191 }
192 
193 
hostapd_eid_eht_operation(struct hostapd_data * hapd,u8 * eid)194 u8 * hostapd_eid_eht_operation(struct hostapd_data *hapd, u8 *eid)
195 {
196 	struct hostapd_config *conf = hapd->iconf;
197 	struct ieee80211_eht_operation *oper;
198 	u8 *pos = eid, seg0 = 0, seg1 = 0;
199 	enum oper_chan_width chwidth;
200 	size_t elen = 1 + 4 + 3;
201 
202 	if (!hapd->iface->current_mode)
203 		return eid;
204 
205 	if (hapd->iconf->punct_bitmap)
206 		elen += EHT_OPER_DISABLED_SUBCHAN_BITMAP_SIZE;
207 
208 	*pos++ = WLAN_EID_EXTENSION;
209 	*pos++ = 1 + elen;
210 	*pos++ = WLAN_EID_EXT_EHT_OPERATION;
211 
212 	oper = (struct ieee80211_eht_operation *) pos;
213 	oper->oper_params = EHT_OPER_INFO_PRESENT;
214 
215 	/* TODO: Fill in appropriate EHT-MCS max Nss information */
216 	oper->basic_eht_mcs_nss_set[0] = 0x11;
217 	oper->basic_eht_mcs_nss_set[1] = 0x00;
218 	oper->basic_eht_mcs_nss_set[2] = 0x00;
219 	oper->basic_eht_mcs_nss_set[3] = 0x00;
220 
221 	if (is_6ghz_op_class(conf->op_class))
222 		chwidth = op_class_to_ch_width(conf->op_class);
223 	else
224 		chwidth = conf->eht_oper_chwidth;
225 
226 	seg0 = hostapd_get_oper_centr_freq_seg0_idx(conf);
227 
228 	switch (chwidth) {
229 	case CONF_OPER_CHWIDTH_320MHZ:
230 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_320MHZ;
231 		seg1 = seg0;
232 		if (hapd->iconf->channel < seg0)
233 			seg0 -= 16;
234 		else
235 			seg0 += 16;
236 		break;
237 	case CONF_OPER_CHWIDTH_160MHZ:
238 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_160MHZ;
239 		seg1 = seg0;
240 		if (hapd->iconf->channel < seg0)
241 			seg0 -= 8;
242 		else
243 			seg0 += 8;
244 		break;
245 	case CONF_OPER_CHWIDTH_80MHZ:
246 		oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_80MHZ;
247 		break;
248 	case CONF_OPER_CHWIDTH_USE_HT:
249 		if (seg0)
250 			oper->oper_info.control |= EHT_OPER_CHANNEL_WIDTH_40MHZ;
251 		break;
252 	default:
253 		return eid;
254 	}
255 
256 	oper->oper_info.ccfs0 = seg0 ? seg0 : hapd->iconf->channel;
257 	oper->oper_info.ccfs1 = seg1;
258 
259 	if (hapd->iconf->punct_bitmap) {
260 		oper->oper_params |= EHT_OPER_DISABLED_SUBCHAN_BITMAP_PRESENT;
261 		oper->oper_info.disabled_chan_bitmap =
262 			host_to_le16(hapd->iconf->punct_bitmap);
263 	}
264 
265 	return pos + elen;
266 }
267 
268 
check_valid_eht_mcs_nss(struct hostapd_data * hapd,const u8 * ap_mcs,const u8 * sta_mcs,u8 mcs_count,u8 map_len)269 static bool check_valid_eht_mcs_nss(struct hostapd_data *hapd, const u8 *ap_mcs,
270 				    const u8 *sta_mcs, u8 mcs_count, u8 map_len)
271 {
272 	unsigned int i, j;
273 
274 	for (i = 0; i < mcs_count; i++) {
275 		ap_mcs += i * 3;
276 		sta_mcs += i * 3;
277 
278 		for (j = 0; j < map_len; j++) {
279 			if (((ap_mcs[j] >> 4) & 0xFF) == 0)
280 				continue;
281 
282 			if ((sta_mcs[j] & 0xFF) == 0)
283 				continue;
284 
285 			return true;
286 		}
287 	}
288 
289 	wpa_printf(MSG_DEBUG,
290 		   "No matching EHT MCS found between AP TX and STA RX");
291 	return false;
292 }
293 
294 
check_valid_eht_mcs(struct hostapd_data * hapd,const u8 * sta_eht_capab,enum ieee80211_op_mode opmode)295 static bool check_valid_eht_mcs(struct hostapd_data *hapd,
296 				const u8 *sta_eht_capab,
297 				enum ieee80211_op_mode opmode)
298 {
299 	struct hostapd_hw_modes *mode;
300 	const struct ieee80211_eht_capabilities *capab;
301 	const u8 *ap_mcs, *sta_mcs;
302 	u8 mcs_count = 1;
303 
304 	mode = hapd->iface->current_mode;
305 	if (!mode)
306 		return true;
307 
308 	ap_mcs = mode->eht_capab[opmode].mcs;
309 	capab = (const struct ieee80211_eht_capabilities *) sta_eht_capab;
310 	sta_mcs = capab->optional;
311 
312 	if (ieee80211_eht_mcs_set_size(mode->mode, hapd->iconf->op_class,
313 				       hapd->iconf->he_oper_chwidth,
314 				       mode->he_capab[opmode].phy_cap,
315 				       mode->eht_capab[opmode].phy_cap) ==
316 	    EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY)
317 		return check_valid_eht_mcs_nss(
318 			hapd, ap_mcs, sta_mcs, 1,
319 			EHT_PHYCAP_MCS_NSS_LEN_20MHZ_ONLY);
320 
321 	switch (hapd->iface->conf->eht_oper_chwidth) {
322 	case CONF_OPER_CHWIDTH_320MHZ:
323 		mcs_count++;
324 		/* fall through */
325 	case CONF_OPER_CHWIDTH_80P80MHZ:
326 	case CONF_OPER_CHWIDTH_160MHZ:
327 		mcs_count++;
328 		break;
329 	default:
330 		break;
331 	}
332 
333 	return check_valid_eht_mcs_nss(hapd, ap_mcs, sta_mcs, mcs_count,
334 				       EHT_PHYCAP_MCS_NSS_LEN_20MHZ_PLUS);
335 }
336 
337 
ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,u8 opclass,u8 he_oper_chwidth,const u8 * he_cap,const u8 * eht_cap,size_t len)338 static bool ieee80211_invalid_eht_cap_size(enum hostapd_hw_mode mode,
339 					   u8 opclass, u8 he_oper_chwidth,
340 					   const u8 *he_cap, const u8 *eht_cap,
341 					   size_t len)
342 {
343 	const struct ieee80211_he_capabilities *he_capab;
344 	struct ieee80211_eht_capabilities *cap;
345 	const u8 *he_phy_cap;
346 	size_t cap_len;
347 	u16 ppe_thres_hdr;
348 
349 	he_capab = (const struct ieee80211_he_capabilities *) he_cap;
350 	he_phy_cap = he_capab->he_phy_capab_info;
351 	cap = (struct ieee80211_eht_capabilities *) eht_cap;
352 	cap_len = sizeof(*cap) - sizeof(cap->optional);
353 	if (len < cap_len)
354 		return true;
355 
356 	cap_len += ieee80211_eht_mcs_set_size(mode, opclass, he_oper_chwidth,
357 					      he_phy_cap, cap->phy_cap);
358 	if (len < cap_len)
359 		return true;
360 
361 	ppe_thres_hdr = len > cap_len + 1 ?
362 		WPA_GET_LE16(&eht_cap[cap_len]) : 0x01ff;
363 	cap_len += ieee80211_eht_ppet_size(ppe_thres_hdr, cap->phy_cap);
364 
365 	return len < cap_len;
366 }
367 
368 
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)369 u16 copy_sta_eht_capab(struct hostapd_data *hapd, struct sta_info *sta,
370 		       enum ieee80211_op_mode opmode,
371 		       const u8 *he_capab, size_t he_capab_len,
372 		       const u8 *eht_capab, size_t eht_capab_len)
373 {
374 	struct hostapd_hw_modes *c_mode = hapd->iface->current_mode;
375 	enum hostapd_hw_mode mode = c_mode ? c_mode->mode : NUM_HOSTAPD_MODES;
376 
377 	if (!hapd->iconf->ieee80211be || hapd->conf->disable_11be ||
378 	    !he_capab || he_capab_len < IEEE80211_HE_CAPAB_MIN_LEN ||
379 	    !eht_capab ||
380 	    ieee80211_invalid_eht_cap_size(mode, hapd->iconf->op_class,
381 					   hapd->iconf->he_oper_chwidth,
382 					   he_capab, eht_capab,
383 					   eht_capab_len) ||
384 	    !check_valid_eht_mcs(hapd, eht_capab, opmode)) {
385 		sta->flags &= ~WLAN_STA_EHT;
386 		os_free(sta->eht_capab);
387 		sta->eht_capab = NULL;
388 		return WLAN_STATUS_SUCCESS;
389 	}
390 
391 	os_free(sta->eht_capab);
392 	sta->eht_capab = os_memdup(eht_capab, eht_capab_len);
393 	if (!sta->eht_capab) {
394 		sta->eht_capab_len = 0;
395 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
396 	}
397 
398 	sta->flags |= WLAN_STA_EHT;
399 	sta->eht_capab_len = eht_capab_len;
400 
401 	return WLAN_STATUS_SUCCESS;
402 }
403 
404 
hostapd_get_eht_capab(struct hostapd_data * hapd,const struct ieee80211_eht_capabilities * src,struct ieee80211_eht_capabilities * dest,size_t len)405 void hostapd_get_eht_capab(struct hostapd_data *hapd,
406 			   const struct ieee80211_eht_capabilities *src,
407 			   struct ieee80211_eht_capabilities *dest,
408 			   size_t len)
409 {
410 	if (!src || !dest)
411 		return;
412 
413 	if (len > sizeof(*dest))
414 		len = sizeof(*dest);
415 	/* TODO: mask out unsupported features */
416 
417 	os_memset(dest, 0, sizeof(*dest));
418 	os_memcpy(dest, src, len);
419 }
420