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_IDX
) == 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
)
49 struct ieee80211_he_capabilities
*cap
;
50 struct hostapd_hw_modes
*mode
= hapd
->iface
->current_mode
;
51 u8 he_oper_chwidth
= ~HE_PHYCAP_CHANNEL_WIDTH_MASK
;
53 u8 ie_size
= 0, mcs_nss_size
= 0, ppet_size
= 0;
58 ie_size
= sizeof(struct ieee80211_he_capabilities
);
59 ppet_size
= ieee80211_he_ppet_size(mode
->he_capab
.ppet
[0],
60 mode
->he_capab
.phy_cap
);
62 switch (hapd
->iface
->conf
->he_oper_chwidth
) {
63 case CHANWIDTH_80P80MHZ
:
65 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G
;
68 case CHANWIDTH_160MHZ
:
69 he_oper_chwidth
|= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G
;
73 case CHANWIDTH_USE_HT
:
74 he_oper_chwidth
|= HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_IN_2G
|
75 HE_PHYCAP_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G
;
80 ie_size
+= mcs_nss_size
+ ppet_size
;
82 *pos
++ = WLAN_EID_EXTENSION
;
84 *pos
++ = WLAN_EID_EXT_HE_CAPABILITIES
;
86 cap
= (struct ieee80211_he_capabilities
*) pos
;
87 os_memset(cap
, 0, sizeof(*cap
));
89 os_memcpy(cap
->he_mac_capab_info
, mode
->he_capab
.mac_cap
,
90 HE_MAX_MAC_CAPAB_SIZE
);
91 os_memcpy(cap
->he_phy_capab_info
, mode
->he_capab
.phy_cap
,
92 HE_MAX_PHY_CAPAB_SIZE
);
93 os_memcpy(cap
->optional
, mode
->he_capab
.mcs
, mcs_nss_size
);
95 os_memcpy(&cap
->optional
[mcs_nss_size
], mode
->he_capab
.ppet
,
98 if (hapd
->iface
->conf
->he_phy_capab
.he_su_beamformer
)
99 cap
->he_phy_capab_info
[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX
] |=
100 HE_PHYCAP_SU_BEAMFORMER_CAPAB
;
102 cap
->he_phy_capab_info
[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX
] &=
103 ~HE_PHYCAP_SU_BEAMFORMER_CAPAB
;
105 if (hapd
->iface
->conf
->he_phy_capab
.he_su_beamformee
)
106 cap
->he_phy_capab_info
[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX
] |=
107 HE_PHYCAP_SU_BEAMFORMEE_CAPAB
;
109 cap
->he_phy_capab_info
[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX
] &=
110 ~HE_PHYCAP_SU_BEAMFORMEE_CAPAB
;
112 if (hapd
->iface
->conf
->he_phy_capab
.he_mu_beamformer
)
113 cap
->he_phy_capab_info
[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX
] |=
114 HE_PHYCAP_MU_BEAMFORMER_CAPAB
;
116 cap
->he_phy_capab_info
[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX
] &=
117 ~HE_PHYCAP_MU_BEAMFORMER_CAPAB
;
119 cap
->he_phy_capab_info
[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX
] &=
128 u8
* hostapd_eid_he_operation(struct hostapd_data
*hapd
, u8
*eid
)
130 struct ieee80211_he_operation
*oper
;
135 if (!hapd
->iface
->current_mode
)
138 *pos
++ = WLAN_EID_EXTENSION
;
139 *pos
++ = 1 + oper_size
;
140 *pos
++ = WLAN_EID_EXT_HE_OPERATION
;
142 oper
= (struct ieee80211_he_operation
*) pos
;
143 os_memset(oper
, 0, sizeof(*oper
));
145 if (hapd
->iface
->conf
->he_op
.he_default_pe_duration
)
146 params
|= (hapd
->iface
->conf
->he_op
.he_default_pe_duration
<<
147 HE_OPERATION_DFLT_PE_DURATION_OFFSET
);
149 if (hapd
->iface
->conf
->he_op
.he_twt_required
)
150 params
|= HE_OPERATION_TWT_REQUIRED
;
152 if (hapd
->iface
->conf
->he_op
.he_rts_threshold
)
153 params
|= (hapd
->iface
->conf
->he_op
.he_rts_threshold
<<
154 HE_OPERATION_RTS_THRESHOLD_OFFSET
);
156 if (hapd
->iface
->conf
->he_op
.he_bss_color
)
157 params
|= (hapd
->iface
->conf
->he_op
.he_bss_color
<<
158 HE_OPERATION_BSS_COLOR_OFFSET
);
160 /* TODO: conditional MaxBSSID Indicator subfield */
162 oper
->he_oper_params
= host_to_le32(params
);
170 u8
* hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data
*hapd
, u8
*eid
)
172 struct ieee80211_he_mu_edca_parameter_set
*edca
;
176 pos
= (u8
*) &hapd
->iface
->conf
->he_mu_edca
;
177 for (i
= 0; i
< sizeof(*edca
); i
++) {
181 if (i
== sizeof(*edca
))
182 return eid
; /* no MU EDCA Parameters configured */
185 *pos
++ = WLAN_EID_EXTENSION
;
186 *pos
++ = 1 + sizeof(*edca
);
187 *pos
++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS
;
189 edca
= (struct ieee80211_he_mu_edca_parameter_set
*) pos
;
190 os_memcpy(edca
, &hapd
->iface
->conf
->he_mu_edca
, sizeof(*edca
));
192 wpa_hexdump(MSG_DEBUG
, "HE: MU EDCA Parameter Set element",
195 pos
+= sizeof(*edca
);
201 u8
* hostapd_eid_spatial_reuse(struct hostapd_data
*hapd
, u8
*eid
)
203 struct ieee80211_spatial_reuse
*spr
;
204 u8
*pos
= eid
, *spr_param
;
207 if (!hapd
->iface
->conf
->spr
.sr_control
)
210 if (hapd
->iface
->conf
->spr
.sr_control
&
211 SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT
)
214 if (hapd
->iface
->conf
->spr
.sr_control
&
215 SPATIAL_REUSE_SRG_INFORMATION_PRESENT
)
218 *pos
++ = WLAN_EID_EXTENSION
;
220 *pos
++ = WLAN_EID_EXT_SPATIAL_REUSE
;
222 spr
= (struct ieee80211_spatial_reuse
*) pos
;
223 os_memset(spr
, 0, sizeof(*spr
));
225 spr
->sr_ctrl
= hapd
->iface
->conf
->spr
.sr_control
;
227 spr_param
= spr
->params
;
228 if (spr
->sr_ctrl
& SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT
) {
230 hapd
->iface
->conf
->spr
.non_srg_obss_pd_max_offset
;
233 if (spr
->sr_ctrl
& SPATIAL_REUSE_SRG_INFORMATION_PRESENT
) {
234 *spr_param
++ = hapd
->iface
->conf
->spr
.srg_obss_pd_min_offset
;
235 *spr_param
++ = hapd
->iface
->conf
->spr
.srg_obss_pd_max_offset
;
243 void hostapd_get_he_capab(struct hostapd_data
*hapd
,
244 const struct ieee80211_he_capabilities
*he_cap
,
245 struct ieee80211_he_capabilities
*neg_he_cap
,
251 if (he_capab_len
> sizeof(*neg_he_cap
))
252 he_capab_len
= sizeof(*neg_he_cap
);
253 /* TODO: mask out unsupported features */
255 os_memcpy(neg_he_cap
, he_cap
, he_capab_len
);
259 static int check_valid_he_mcs(struct hostapd_data
*hapd
, const u8
*sta_he_capab
)
261 u16 sta_rx_mcs_set
, ap_tx_mcs_set
;
263 const u16
*ap_mcs_set
, *sta_mcs_set
;
266 if (!hapd
->iface
->current_mode
)
268 ap_mcs_set
= (u16
*) hapd
->iface
->current_mode
->he_capab
.mcs
;
269 sta_mcs_set
= (u16
*) ((const struct ieee80211_he_capabilities
*)
270 sta_he_capab
)->optional
;
273 * Disable HE capabilities for STAs for which there is not even a single
274 * allowed MCS in any supported number of streams, i.e., STA is
275 * advertising 3 (not supported) as HE MCS rates for all supported
278 switch (hapd
->iface
->conf
->he_oper_chwidth
) {
279 case CHANWIDTH_80P80MHZ
:
282 case CHANWIDTH_160MHZ
:
290 for (i
= 0; i
< mcs_count
; i
++) {
293 /* AP Tx MCS map vs. STA Rx MCS map */
294 sta_rx_mcs_set
= WPA_GET_LE16((const u8
*) &sta_mcs_set
[i
* 2]);
295 ap_tx_mcs_set
= WPA_GET_LE16((const u8
*)
296 &ap_mcs_set
[(i
* 2) + 1]);
298 for (j
= 0; j
< HE_NSS_MAX_STREAMS
; j
++) {
299 if (((ap_tx_mcs_set
>> (j
* 2)) & 0x3) == 3)
302 if (((sta_rx_mcs_set
>> (j
* 2)) & 0x3) == 3)
309 wpa_printf(MSG_DEBUG
,
310 "No matching HE MCS found between AP TX and STA RX");
316 u16
copy_sta_he_capab(struct hostapd_data
*hapd
, struct sta_info
*sta
,
317 const u8
*he_capab
, size_t he_capab_len
)
319 if (!he_capab
|| !hapd
->iconf
->ieee80211ax
||
320 !check_valid_he_mcs(hapd
, he_capab
) ||
321 he_capab_len
> sizeof(struct ieee80211_he_capabilities
)) {
322 sta
->flags
&= ~WLAN_STA_HE
;
323 os_free(sta
->he_capab
);
324 sta
->he_capab
= NULL
;
325 return WLAN_STATUS_SUCCESS
;
328 if (!sta
->he_capab
) {
330 os_zalloc(sizeof(struct ieee80211_he_capabilities
));
332 return WLAN_STATUS_UNSPECIFIED_FAILURE
;
335 sta
->flags
|= WLAN_STA_HE
;
336 os_memset(sta
->he_capab
, 0, sizeof(struct ieee80211_he_capabilities
));
337 os_memcpy(sta
->he_capab
, he_capab
, he_capab_len
);
338 sta
->he_capab_len
= he_capab_len
;
340 return WLAN_STATUS_SUCCESS
;