2 * hostapd / IEEE 802.11ax HE
3 * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
4 * Copyright (c) 2019 John Crispin <john@phrozen.org>
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
10 #include "utils/includes.h"
12 #include "utils/common.h"
13 #include "common/ieee802_11_defs.h"
15 #include "ap_config.h"
18 #include "ieee802_11.h"
21 static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
25 if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
26 HE_PHYCAP_PPE_THRESHOLD_PRESENT) == 0)
29 ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
30 HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
37 sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
47 u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid,
48 enum ieee80211_op_mode opmode)
50 struct ieee80211_he_capabilities *cap;
51 struct hostapd_hw_modes *mode = hapd->iface->current_mode;
52 u8 he_oper_chwidth = ~HE_PHYCAP_CHANNEL_WIDTH_MASK;
54 u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
59 ie_size = sizeof(struct ieee80211_he_capabilities);
60 ppet_size = ieee80211_he_ppet_size(mode->he_capab[opmode].ppet[0],
61 mode->he_capab[opmode].phy_cap);
63 switch (hapd->iface->conf->he_oper_chwidth) {
64 case CHANWIDTH_80P80MHZ:
66 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
69 case CHANWIDTH_160MHZ:
70 he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
74 case CHANWIDTH_USE_HT:
75 he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G |
76 HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G;
81 ie_size += mcs_nss_size + ppet_size;
83 *pos++ = WLAN_EID_EXTENSION;
85 *pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
87 cap = (struct ieee80211_he_capabilities *) pos;
88 os_memset(cap, 0, sizeof(*cap));
90 os_memcpy(cap->he_mac_capab_info, mode->he_capab[opmode].mac_cap,
91 HE_MAX_MAC_CAPAB_SIZE);
92 os_memcpy(cap->he_phy_capab_info, mode->he_capab[opmode].phy_cap,
93 HE_MAX_PHY_CAPAB_SIZE);
94 os_memcpy(cap->optional, mode->he_capab[opmode].mcs, mcs_nss_size);
96 os_memcpy(&cap->optional[mcs_nss_size],
97 mode->he_capab[opmode].ppet, ppet_size);
99 if (hapd->iface->conf->he_phy_capab.he_su_beamformer)
100 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] |=
101 HE_PHYCAP_SU_BEAMFORMER_CAPAB;
103 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
104 ~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
106 if (hapd->iface->conf->he_phy_capab.he_su_beamformee)
107 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] |=
108 HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
110 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
111 ~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
113 if (hapd->iface->conf->he_phy_capab.he_mu_beamformer)
114 cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] |=
115 HE_PHYCAP_MU_BEAMFORMER_CAPAB;
117 cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
118 ~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
120 cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
129 u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
131 struct ieee80211_he_operation *oper;
136 if (!hapd->iface->current_mode)
139 *pos++ = WLAN_EID_EXTENSION;
140 *pos++ = 1 + oper_size;
141 *pos++ = WLAN_EID_EXT_HE_OPERATION;
143 oper = (struct ieee80211_he_operation *) pos;
144 os_memset(oper, 0, sizeof(*oper));
146 if (hapd->iface->conf->he_op.he_default_pe_duration)
147 params |= (hapd->iface->conf->he_op.he_default_pe_duration <<
148 HE_OPERATION_DFLT_PE_DURATION_OFFSET);
150 if (hapd->iface->conf->he_op.he_twt_required)
151 params |= HE_OPERATION_TWT_REQUIRED;
153 if (hapd->iface->conf->he_op.he_rts_threshold)
154 params |= (hapd->iface->conf->he_op.he_rts_threshold <<
155 HE_OPERATION_RTS_THRESHOLD_OFFSET);
157 if (hapd->iface->conf->he_op.he_bss_color)
158 params |= (hapd->iface->conf->he_op.he_bss_color <<
159 HE_OPERATION_BSS_COLOR_OFFSET);
161 /* HE minimum required basic MCS and NSS for STAs */
162 oper->he_mcs_nss_set =
163 host_to_le16(hapd->iface->conf->he_op.he_basic_mcs_nss_set);
165 /* TODO: conditional MaxBSSID Indicator subfield */
167 oper->he_oper_params = host_to_le32(params);
175 u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
177 struct ieee80211_he_mu_edca_parameter_set *edca;
181 pos = (u8 *) &hapd->iface->conf->he_mu_edca;
182 for (i = 0; i < sizeof(*edca); i++) {
186 if (i == sizeof(*edca))
187 return eid; /* no MU EDCA Parameters configured */
190 *pos++ = WLAN_EID_EXTENSION;
191 *pos++ = 1 + sizeof(*edca);
192 *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
194 edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
195 os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
197 wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
200 pos += sizeof(*edca);
206 u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
208 struct ieee80211_spatial_reuse *spr;
209 u8 *pos = eid, *spr_param;
212 if (!hapd->iface->conf->spr.sr_control)
215 if (hapd->iface->conf->spr.sr_control &
216 SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
219 if (hapd->iface->conf->spr.sr_control &
220 SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
223 *pos++ = WLAN_EID_EXTENSION;
225 *pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
227 spr = (struct ieee80211_spatial_reuse *) pos;
228 os_memset(spr, 0, sizeof(*spr));
230 spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
232 spr_param = spr->params;
233 if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
235 hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
238 if (spr->sr_ctrl & SPATIAL_REUSE_SRG_INFORMATION_PRESENT) {
239 *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_min_offset;
240 *spr_param++ = hapd->iface->conf->spr.srg_obss_pd_max_offset;
248 void hostapd_get_he_capab(struct hostapd_data *hapd,
249 const struct ieee80211_he_capabilities *he_cap,
250 struct ieee80211_he_capabilities *neg_he_cap,
256 if (he_capab_len > sizeof(*neg_he_cap))
257 he_capab_len = sizeof(*neg_he_cap);
258 /* TODO: mask out unsupported features */
260 os_memcpy(neg_he_cap, he_cap, he_capab_len);
264 static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
265 enum ieee80211_op_mode opmode)
267 u16 sta_rx_mcs_set, ap_tx_mcs_set;
269 const u16 *ap_mcs_set, *sta_mcs_set;
272 if (!hapd->iface->current_mode)
274 ap_mcs_set = (u16 *) hapd->iface->current_mode->he_capab[opmode].mcs;
275 sta_mcs_set = (u16 *) ((const struct ieee80211_he_capabilities *)
276 sta_he_capab)->optional;
279 * Disable HE capabilities for STAs for which there is not even a single
280 * allowed MCS in any supported number of streams, i.e., STA is
281 * advertising 3 (not supported) as HE MCS rates for all supported
284 switch (hapd->iface->conf->he_oper_chwidth) {
285 case CHANWIDTH_80P80MHZ:
288 case CHANWIDTH_160MHZ:
296 for (i = 0; i < mcs_count; i++) {
299 /* AP Tx MCS map vs. STA Rx MCS map */
300 sta_rx_mcs_set = WPA_GET_LE16((const u8 *) &sta_mcs_set[i * 2]);
301 ap_tx_mcs_set = WPA_GET_LE16((const u8 *)
302 &ap_mcs_set[(i * 2) + 1]);
304 for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
305 if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
308 if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
315 wpa_printf(MSG_DEBUG,
316 "No matching HE MCS found between AP TX and STA RX");
322 u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
323 enum ieee80211_op_mode opmode, const u8 *he_capab,
326 if (!he_capab || !hapd->iconf->ieee80211ax ||
327 !check_valid_he_mcs(hapd, he_capab, opmode) ||
328 he_capab_len > sizeof(struct ieee80211_he_capabilities)) {
329 sta->flags &= ~WLAN_STA_HE;
330 os_free(sta->he_capab);
331 sta->he_capab = NULL;
332 return WLAN_STATUS_SUCCESS;
335 if (!sta->he_capab) {
337 os_zalloc(sizeof(struct ieee80211_he_capabilities));
339 return WLAN_STATUS_UNSPECIFIED_FAILURE;
342 sta->flags |= WLAN_STA_HE;
343 os_memset(sta->he_capab, 0, sizeof(struct ieee80211_he_capabilities));
344 os_memcpy(sta->he_capab, he_capab, he_capab_len);
345 sta->he_capab_len = he_capab_len;
347 return WLAN_STATUS_SUCCESS;