vendor/wpa_supplicant: upgrade from 2.1 to 2.9
[dragonfly.git] / contrib / wpa_supplicant / 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) == 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                           enum ieee80211_op_mode opmode)
49 {
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;
53         u8 *pos = eid;
54         u8 ie_size = 0, mcs_nss_size = 0, ppet_size = 0;
55
56         if (!mode)
57                 return eid;
58
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);
62
63         switch (hapd->iface->conf->he_oper_chwidth) {
64         case CHANWIDTH_80P80MHZ:
65                 he_oper_chwidth |=
66                         HE_PHYCAP_CHANNEL_WIDTH_SET_80PLUS80MHZ_IN_5G;
67                 mcs_nss_size += 4;
68                 /* fall through */
69         case CHANWIDTH_160MHZ:
70                 he_oper_chwidth |= HE_PHYCAP_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
71                 mcs_nss_size += 4;
72                 /* fall through */
73         case CHANWIDTH_80MHZ:
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;
77                 mcs_nss_size += 4;
78                 break;
79         }
80
81         ie_size += mcs_nss_size + ppet_size;
82
83         *pos++ = WLAN_EID_EXTENSION;
84         *pos++ = 1 + ie_size;
85         *pos++ = WLAN_EID_EXT_HE_CAPABILITIES;
86
87         cap = (struct ieee80211_he_capabilities *) pos;
88         os_memset(cap, 0, sizeof(*cap));
89
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);
95         if (ppet_size)
96                 os_memcpy(&cap->optional[mcs_nss_size],
97                           mode->he_capab[opmode].ppet,  ppet_size);
98
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;
102         else
103                 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMER_CAPAB_IDX] &=
104                         ~HE_PHYCAP_SU_BEAMFORMER_CAPAB;
105
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;
109         else
110                 cap->he_phy_capab_info[HE_PHYCAP_SU_BEAMFORMEE_CAPAB_IDX] &=
111                         ~HE_PHYCAP_SU_BEAMFORMEE_CAPAB;
112
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;
116         else
117                 cap->he_phy_capab_info[HE_PHYCAP_MU_BEAMFORMER_CAPAB_IDX] &=
118                         ~HE_PHYCAP_MU_BEAMFORMER_CAPAB;
119
120         cap->he_phy_capab_info[HE_PHYCAP_CHANNEL_WIDTH_SET_IDX] &=
121                 he_oper_chwidth;
122
123         pos += ie_size;
124
125         return pos;
126 }
127
128
129 u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
130 {
131         struct ieee80211_he_operation *oper;
132         u8 *pos = eid;
133         int oper_size = 6;
134         u32 params = 0;
135
136         if (!hapd->iface->current_mode)
137                 return eid;
138
139         *pos++ = WLAN_EID_EXTENSION;
140         *pos++ = 1 + oper_size;
141         *pos++ = WLAN_EID_EXT_HE_OPERATION;
142
143         oper = (struct ieee80211_he_operation *) pos;
144         os_memset(oper, 0, sizeof(*oper));
145
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);
149
150         if (hapd->iface->conf->he_op.he_twt_required)
151                 params |= HE_OPERATION_TWT_REQUIRED;
152
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);
156
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);
160
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);
164
165         /* TODO: conditional MaxBSSID Indicator subfield */
166
167         oper->he_oper_params = host_to_le32(params);
168
169         pos += oper_size;
170
171         return pos;
172 }
173
174
175 u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
176 {
177         struct ieee80211_he_mu_edca_parameter_set *edca;
178         u8 *pos;
179         size_t i;
180
181         pos = (u8 *) &hapd->iface->conf->he_mu_edca;
182         for (i = 0; i < sizeof(*edca); i++) {
183                 if (pos[i])
184                         break;
185         }
186         if (i == sizeof(*edca))
187                 return eid; /* no MU EDCA Parameters configured */
188
189         pos = eid;
190         *pos++ = WLAN_EID_EXTENSION;
191         *pos++ = 1 + sizeof(*edca);
192         *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
193
194         edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
195         os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
196
197         wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
198                     pos, sizeof(*edca));
199
200         pos += sizeof(*edca);
201
202         return pos;
203 }
204
205
206 u8 * hostapd_eid_spatial_reuse(struct hostapd_data *hapd, u8 *eid)
207 {
208         struct ieee80211_spatial_reuse *spr;
209         u8 *pos = eid, *spr_param;
210         u8 sz = 1;
211
212         if (!hapd->iface->conf->spr.sr_control)
213                 return eid;
214
215         if (hapd->iface->conf->spr.sr_control &
216             SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT)
217                 sz++;
218
219         if (hapd->iface->conf->spr.sr_control &
220             SPATIAL_REUSE_SRG_INFORMATION_PRESENT)
221                 sz += 18;
222
223         *pos++ = WLAN_EID_EXTENSION;
224         *pos++ = 1 + sz;
225         *pos++ = WLAN_EID_EXT_SPATIAL_REUSE;
226
227         spr = (struct ieee80211_spatial_reuse *) pos;
228         os_memset(spr, 0, sizeof(*spr));
229
230         spr->sr_ctrl = hapd->iface->conf->spr.sr_control;
231         pos++;
232         spr_param = spr->params;
233         if (spr->sr_ctrl & SPATIAL_REUSE_NON_SRG_OFFSET_PRESENT) {
234                 *spr_param++ =
235                         hapd->iface->conf->spr.non_srg_obss_pd_max_offset;
236                 pos++;
237         }
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;
241                 pos += 18;
242         }
243
244         return pos;
245 }
246
247
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,
251                           size_t he_capab_len)
252 {
253         if (!he_cap)
254                 return;
255
256         if (he_capab_len > sizeof(*neg_he_cap))
257                 he_capab_len = sizeof(*neg_he_cap);
258         /* TODO: mask out unsupported features */
259
260         os_memcpy(neg_he_cap, he_cap, he_capab_len);
261 }
262
263
264 static int check_valid_he_mcs(struct hostapd_data *hapd, const u8 *sta_he_capab,
265                               enum ieee80211_op_mode opmode)
266 {
267         u16 sta_rx_mcs_set, ap_tx_mcs_set;
268         u8 mcs_count = 0;
269         const u16 *ap_mcs_set, *sta_mcs_set;
270         int i;
271
272         if (!hapd->iface->current_mode)
273                 return 1;
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;
277
278         /*
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
282          * band/stream cases.
283          */
284         switch (hapd->iface->conf->he_oper_chwidth) {
285         case CHANWIDTH_80P80MHZ:
286                 mcs_count = 3;
287                 break;
288         case CHANWIDTH_160MHZ:
289                 mcs_count = 2;
290                 break;
291         default:
292                 mcs_count = 1;
293                 break;
294         }
295
296         for (i = 0; i < mcs_count; i++) {
297                 int j;
298
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]);
303
304                 for (j = 0; j < HE_NSS_MAX_STREAMS; j++) {
305                         if (((ap_tx_mcs_set >> (j * 2)) & 0x3) == 3)
306                                 continue;
307
308                         if (((sta_rx_mcs_set >> (j * 2)) & 0x3) == 3)
309                                 continue;
310
311                         return 1;
312                 }
313         }
314
315         wpa_printf(MSG_DEBUG,
316                    "No matching HE MCS found between AP TX and STA RX");
317
318         return 0;
319 }
320
321
322 u16 copy_sta_he_capab(struct hostapd_data *hapd, struct sta_info *sta,
323                       enum ieee80211_op_mode opmode, const u8 *he_capab,
324                       size_t he_capab_len)
325 {
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;
333         }
334
335         if (!sta->he_capab) {
336                 sta->he_capab =
337                         os_zalloc(sizeof(struct ieee80211_he_capabilities));
338                 if (!sta->he_capab)
339                         return WLAN_STATUS_UNSPECIFIED_FAILURE;
340         }
341
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;
346
347         return WLAN_STATUS_SUCCESS;
348 }