]> git.ipfire.org Git - thirdparty/hostap.git/blob - src/ap/ieee802_11_he.c
066a032c0f3978a214818efd343944219a7b57a0
[thirdparty/hostap.git] / src / ap / ieee802_11_he.c
1 /*
2 * hostapd / IEEE 802.11ax HE
3 * Copyright (c) 2016-2017, Qualcomm Atheros, Inc.
4 * Copyright (c) 2019 John Crispin <john@phrozen.org>
5 *
6 * This software may be distributed under the terms of the BSD license.
7 * See README for more details.
8 */
9
10 #include "utils/includes.h"
11
12 #include "utils/common.h"
13 #include "common/ieee802_11_defs.h"
14 #include "hostapd.h"
15 #include "ap_config.h"
16 #include "beacon.h"
17 #include "sta_info.h"
18 #include "ieee802_11.h"
19 #include "dfs.h"
20
21 static u8 ieee80211_he_ppet_size(u8 ppe_thres_hdr, const u8 *phy_cap_info)
22 {
23 u8 sz = 0, ru;
24
25 if ((phy_cap_info[HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX] &
26 HE_PHYCAP_PPE_THRESHOLD_PRESENT_IDX) == 0)
27 return 0;
28
29 ru = (ppe_thres_hdr >> HE_PPE_THRES_RU_INDEX_BITMASK_SHIFT) &
30 HE_PPE_THRES_RU_INDEX_BITMASK_MASK;
31 while (ru) {
32 if (ru & 0x1)
33 sz++;
34 ru >>= 1;
35 }
36
37 sz *= 1 + (ppe_thres_hdr & HE_PPE_THRES_NSS_MASK);
38 sz = (sz * 6) + 7;
39 if (sz % 8)
40 sz += 8;
41 sz /= 8;
42
43 return sz;
44 }
45
46
47 u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid)
48 {
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;
52 u8 *pos = eid;
53 u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
54
55 if (!mode)
56 return eid;
57
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);
61
62 switch (hapd->iface->conf->he_oper_chwidth) {
63 case CHANWIDTH_80P80MHZ:
64 he_oper_chwidth |=
65 HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
66 mcs_nss_size += 4;
67 /* fall through */
68 case CHANWIDTH_160MHZ:
69 he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
70 mcs_nss_size += 4;
71 /* fall through */
72 case CHANWIDTH_80MHZ:
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;
76 mcs_nss_size += 4;
77 break;
78 }
79
80 ie_size += mcs_nss_size + ppet_size;
81
82 *pos++ = WLAN_EID_EXTENSION;
83 *pos++ = 1 + ie_size;
84 *pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
85
86 cap = (struct ieee80211_he_capabilities *) pos;
87 os_memset(cap, 0, sizeof(*cap));
88
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);
94 if (ppet_size)
95 os_memcpy(&cap->optional[mcs_nss_size], mode->he_capab.ppet,
96 ppet_size);
97
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;
101 else
102 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
103 ~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
104
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;
108 else
109 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
110 ~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
111
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;
115 else
116 cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
117 ~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
118
119 cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
120 he_oper_chwidth;
121
122 pos += ie_size;
123
124 return pos;
125 }
126
127
128 u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
129 {
130 struct ieee80211_he_operation *oper;
131 u8 *pos = eid;
132 int oper_size = 6;
133 u32 params = 0;
134
135 if (!hapd->iface->current_mode)
136 return eid;
137
138 *pos++ = WLAN_EID_EXTENSION;
139 *pos++ = 1 + oper_size;
140 *pos++ = WLAN_EID_EXT_HE_OPERATION;
141
142 oper = (struct ieee80211_he_operation *) pos;
143 os_memset(oper, 0, sizeof(*oper));
144
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);
148
149 if (hapd->iface->conf->he_op.he_twt_required)
150 params |= HE_OPERATION_TWT_REQUIRED;
151
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);
155
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);
159
160 /* TODO: conditional MaxBSSID Indicator subfield */
161
162 oper->he_oper_params = host_to_le32(params);
163
164 pos += oper_size;
165
166 return pos;
167 }
168
169
170 u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
171 {
172 struct ieee80211_he_mu_edca_parameter_set *edca;
173 u8 *pos;
174 size_t i;
175
176 pos = (u8 *) &hapd->iface->conf->he_mu_edca;
177 for (i = 0; i < sizeof(*edca); i++) {
178 if (pos[i])
179 break;
180 }
181 if (i == sizeof(*edca))
182 return eid; /* no MU EDCA Parameters configured */
183
184 pos = eid;
185 *pos++ = WLAN_EID_EXTENSION;
186 *pos++ = 1 + sizeof(*edca);
187 *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
188
189 edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
190 os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
191
192 wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
193 pos, sizeof(*edca));
194
195 pos += sizeof(*edca);
196
197 return pos;
198 }
199
200
201 u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
202 {
203 struct ieee80211_spatial_reuse *spr;
204 u8 *pos = eid, *spr_param;
205 u8 sz = 1;
206
207 if (!hapd->iface->conf->spr.sr_control)
208 return eid;
209
210 if (hapd->iface->conf->spr.sr_control &
211 SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
212 sz++;
213
214 if (hapd->iface->conf->spr.sr_control &
215 SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
216 sz += 18;
217
218 *pos++ = WLAN_EID_EXTENSION;
219 *pos++ = 1 + sz;
220 *pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
221
222 spr = (struct ieee80211_spatial_reuse *) pos;
223 os_memset(spr, 0, sizeof(*spr));
224
225 spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
226 pos++;
227 spr_param = spr->params;
228 if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
229 *spr_param++ =
230 hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
231 pos++;
232 }
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;
236 pos += 18;
237 }
238
239 return pos;
240 }
241
242
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,
246 size_t he_capab_len)
247 {
248 if (!he_cap)
249 return;
250
251 if (he_capab_len > sizeof(*neg_he_cap))
252 he_capab_len = sizeof(*neg_he_cap);
253 /* TODO: mask out unsupported features */
254
255 os_memcpy(neg_he_cap, he_cap, he_capab_len);
256 }
257
258
259 static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab)
260 {
261 u16 sta_rx_mcs_set, ap_tx_mcs_set;
262 u8 mcs_count = 0;
263 const u16 *ap_mcs_set, *sta_mcs_set;
264 int i;
265
266 if (!hapd->iface->current_mode)
267 return 1;
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;
271
272 /*
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
276 * band/stream cases.
277 */
278 switch (hapd->iface->conf->he_oper_chwidth) {
279 case CHANWIDTH_80P80MHZ:
280 mcs_count = 3;
281 break;
282 case CHANWIDTH_160MHZ:
283 mcs_count = 2;
284 break;
285 default:
286 mcs_count = 1;
287 break;
288 }
289
290 for (i = 0; i < mcs_count; i++) {
291 int j;
292
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]);
297
298 for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
299 if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
300 continue;
301
302 if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
303 continue;
304
305 return 1;
306 }
307 }
308
309 wpa_printf(MSG_DEBUG,
310 "No matching HE MCS found between AP TX and STA RX");
311
312 return 0;
313 }
314
315
316 u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
317 const u8 *he_capab, size_t he_capab_len)
318 {
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;
326 }
327
328 if (!sta->he_capab) {
329 sta->he_capab =
330 os_zalloc(sizeof(struct ieee80211_he_capabilities));
331 if (!sta->he_capab)
332 return WLAN_STATUS_UNSPECIFIED_FAILURE;
333 }
334
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;
339
340 return WLAN_STATUS_SUCCESS;
341 }