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