1 /*
2 * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /*
21 *
22 * This file lim_prop_exts_utils.cc contains the utility functions
23 * to populate, parse proprietary extensions required to
24 * support ANI feature set.
25 *
26 * Author: Chandra Modumudi
27 * Date: 11/27/02
28 * History:-
29 * Date Modified by Modification Information
30 * --------------------------------------------------------------------
31 *
32 */
33 #include "ani_global.h"
34 #include "wni_cfg.h"
35 #include "sir_common.h"
36 #include "sir_debug.h"
37 #include "utils_api.h"
38 #include "lim_api.h"
39 #include "lim_types.h"
40 #include "lim_utils.h"
41 #include "lim_assoc_utils.h"
42 #include "lim_prop_exts_utils.h"
43 #include "lim_ser_des_utils.h"
44 #include "lim_trace.h"
45 #include "lim_ft_defs.h"
46 #include "lim_session.h"
47 #include "wma.h"
48 #include "wlan_utility.h"
49
50 #ifdef FEATURE_WLAN_ESE
51 /**
52 * get_local_power_constraint_probe_response() - extracts local constraint
53 * from probe response
54 * @beacon_struct: beacon structure
55 * @local_constraint: local constraint pointer
56 * @session: A pointer to session entry.
57 *
58 * Return: None
59 */
get_local_power_constraint_probe_response(tpSirProbeRespBeacon beacon_struct,int8_t * local_constraint,struct pe_session * session)60 static void get_local_power_constraint_probe_response(
61 tpSirProbeRespBeacon beacon_struct,
62 int8_t *local_constraint,
63 struct pe_session *session)
64 {
65 if (beacon_struct->eseTxPwr.present)
66 *local_constraint =
67 beacon_struct->eseTxPwr.power_limit;
68 }
69
70 /**
71 * get_ese_version_ie_probe_response() - extracts ESE version IE
72 * from probe response
73 * @mac_ctx: MAC context
74 * @beacon_struct: beacon structure
75 * @session: A pointer to session entry.
76 *
77 * Return: None
78 */
get_ese_version_ie_probe_response(struct mac_context * mac_ctx,tpSirProbeRespBeacon beacon_struct,struct pe_session * session)79 static void get_ese_version_ie_probe_response(struct mac_context *mac_ctx,
80 tpSirProbeRespBeacon beacon_struct,
81 struct pe_session *session)
82 {
83 if (mac_ctx->mlme_cfg->lfr.ese_enabled)
84 session->is_ese_version_ie_present =
85 beacon_struct->is_ese_ver_ie_present;
86 }
87 #else
get_local_power_constraint_probe_response(tpSirProbeRespBeacon beacon_struct,int8_t * local_constraint,struct pe_session * session)88 static void get_local_power_constraint_probe_response(
89 tpSirProbeRespBeacon beacon_struct,
90 int8_t *local_constraint,
91 struct pe_session *session)
92 {
93
94 }
95
get_ese_version_ie_probe_response(struct mac_context * mac_ctx,tpSirProbeRespBeacon beacon_struct,struct pe_session * session)96 static inline void get_ese_version_ie_probe_response(struct mac_context *mac_ctx,
97 tpSirProbeRespBeacon beacon_struct,
98 struct pe_session *session)
99 {
100 }
101 #endif
102
103 #ifdef WLAN_FEATURE_11AX
lim_extract_he_op(struct pe_session * session,tSirProbeRespBeacon * beacon_struct)104 static void lim_extract_he_op(struct pe_session *session,
105 tSirProbeRespBeacon *beacon_struct)
106 {
107 uint8_t fw_vht_ch_wd;
108 uint8_t ap_bcon_ch_width;
109 uint8_t center_freq_diff;
110
111 if (!session->he_capable)
112 return;
113 if (!beacon_struct->he_op.present) {
114 return;
115 }
116 qdf_mem_copy(&session->he_op, &beacon_struct->he_op,
117 sizeof(session->he_op));
118 if (!session->he_6ghz_band)
119 return;
120 if (!session->he_op.oper_info_6g_present) {
121 session->ap_defined_power_type_6g = REG_CURRENT_MAX_AP_TYPE;
122 return;
123 }
124 session->ch_width = session->he_op.oper_info_6g.info.ch_width;
125 session->ch_center_freq_seg0 =
126 session->he_op.oper_info_6g.info.center_freq_seg0;
127 session->ch_center_freq_seg1 =
128 session->he_op.oper_info_6g.info.center_freq_seg1;
129 session->ap_defined_power_type_6g =
130 session->he_op.oper_info_6g.info.reg_info;
131 if (session->ap_defined_power_type_6g < REG_INDOOR_AP ||
132 session->ap_defined_power_type_6g > REG_MAX_SUPP_AP_TYPE) {
133 session->ap_defined_power_type_6g = REG_CURRENT_MAX_AP_TYPE;
134 pe_debug("AP power type invalid, defaulting to MAX_AP_TYPE");
135 }
136
137 pe_debug("6G op info: ch_wd %d cntr_freq_seg0 %d cntr_freq_seg1 %d",
138 session->ch_width, session->ch_center_freq_seg0,
139 session->ch_center_freq_seg1);
140
141 if (!session->ch_center_freq_seg1)
142 return;
143
144 fw_vht_ch_wd = wma_get_vht_ch_width();
145 if (fw_vht_ch_wd <= WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) {
146 session->ch_width = CH_WIDTH_80MHZ;
147 session->ch_center_freq_seg1 = 0;
148 return;
149 }
150 center_freq_diff = abs(session->ch_center_freq_seg1 -
151 session->ch_center_freq_seg0);
152 if (center_freq_diff == 8) {
153 ap_bcon_ch_width = CH_WIDTH_160MHZ;
154 } else if (center_freq_diff > 16) {
155 ap_bcon_ch_width = CH_WIDTH_80P80MHZ;
156 } else {
157 session->ch_width = CH_WIDTH_80MHZ;
158 session->ch_center_freq_seg1 = 0;
159 return;
160 }
161
162 if ((ap_bcon_ch_width == CH_WIDTH_80P80MHZ) &&
163 (fw_vht_ch_wd != WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ)) {
164 session->ch_width = CH_WIDTH_80MHZ;
165 session->ch_center_freq_seg1 = 0;
166 }
167 }
168
lim_validate_he160_mcs_map(struct mac_context * mac_ctx,uint16_t peer_rx,uint16_t peer_tx,uint8_t nss)169 static bool lim_validate_he160_mcs_map(struct mac_context *mac_ctx,
170 uint16_t peer_rx, uint16_t peer_tx,
171 uint8_t nss)
172 {
173 uint16_t rx_he_mcs_map;
174 uint16_t tx_he_mcs_map;
175 uint16_t he_mcs_map;
176
177 he_mcs_map = *((uint16_t *)mac_ctx->mlme_cfg->he_caps.dot11_he_cap.
178 tx_he_mcs_map_160);
179 rx_he_mcs_map = HE_INTERSECT_MCS(peer_rx, he_mcs_map);
180
181 he_mcs_map = *((uint16_t *)mac_ctx->mlme_cfg->he_caps.dot11_he_cap.
182 rx_he_mcs_map_160);
183 tx_he_mcs_map = HE_INTERSECT_MCS(peer_tx, he_mcs_map);
184
185 if (nss == NSS_1x1_MODE) {
186 rx_he_mcs_map |= HE_MCS_INV_MSK_4_NSS(1);
187 tx_he_mcs_map |= HE_MCS_INV_MSK_4_NSS(1);
188 } else if (nss == NSS_2x2_MODE) {
189 rx_he_mcs_map |= (HE_MCS_INV_MSK_4_NSS(1) &
190 HE_MCS_INV_MSK_4_NSS(2));
191 tx_he_mcs_map |= (HE_MCS_INV_MSK_4_NSS(1) &
192 HE_MCS_INV_MSK_4_NSS(2));
193 }
194
195 return ((rx_he_mcs_map != HE_MCS_ALL_DISABLED) &&
196 (tx_he_mcs_map != HE_MCS_ALL_DISABLED));
197 }
198
lim_check_is_he_mcs_valid(struct pe_session * session,tSirProbeRespBeacon * beacon_struct)199 static void lim_check_is_he_mcs_valid(struct pe_session *session,
200 tSirProbeRespBeacon *beacon_struct)
201 {
202 uint8_t i;
203 uint16_t mcs_map;
204
205 if (!session->he_capable || !beacon_struct->he_cap.present)
206 return;
207
208 mcs_map = beacon_struct->he_cap.rx_he_mcs_map_lt_80;
209 for (i = 0; i < session->nss; i++) {
210 if (((mcs_map >> (i * 2)) & 0x3) != 0x3)
211 return;
212 }
213 session->he_capable = false;
214 if (session->vhtCapability)
215 session->dot11mode = MLME_DOT11_MODE_11AC;
216 else
217 session->dot11mode = MLME_DOT11_MODE_11N;
218 pe_err("vdev %d: Invalid LT80 MCS map 0x%x with NSS %d, falback to dot11mode %d",
219 session->vdev_id, mcs_map, session->nss, session->dot11mode);
220 }
221
lim_update_he_bw_cap_mcs(struct pe_session * session,tSirProbeRespBeacon * beacon)222 void lim_update_he_bw_cap_mcs(struct pe_session *session,
223 tSirProbeRespBeacon *beacon)
224 {
225 uint8_t is_80mhz;
226 uint8_t sta_prefer_80mhz_over_160mhz;
227
228 if (!session->he_capable)
229 return;
230
231 sta_prefer_80mhz_over_160mhz =
232 session->mac_ctx->mlme_cfg->sta.sta_prefer_80mhz_over_160mhz;
233 if ((session->opmode == QDF_STA_MODE ||
234 session->opmode == QDF_P2P_CLIENT_MODE) &&
235 beacon && beacon->he_cap.present) {
236 if (!beacon->he_cap.chan_width_2) {
237 is_80mhz = 1;
238 } else if (beacon->he_cap.chan_width_2 &&
239 !lim_validate_he160_mcs_map(session->mac_ctx,
240 *((uint16_t *)beacon->he_cap.rx_he_mcs_map_160),
241 *((uint16_t *)beacon->he_cap.tx_he_mcs_map_160),
242 session->nss)) {
243 is_80mhz = 1;
244 if (session->ch_width == CH_WIDTH_160MHZ) {
245 pe_debug("HE160 Rx/Tx MCS is not valid, falling back to 80MHz");
246 session->ch_width = CH_WIDTH_80MHZ;
247 }
248 } else if (sta_prefer_80mhz_over_160mhz ==
249 STA_PREFER_BW_80MHZ) {
250 is_80mhz = 1;
251 if (session->ch_width == CH_WIDTH_160MHZ) {
252 pe_debug("STA preferred HE80 over HE160, falling back to 80MHz");
253 session->ch_width = CH_WIDTH_80MHZ;
254 }
255 } else {
256 is_80mhz = 0;
257 }
258 } else {
259 is_80mhz = 1;
260 }
261
262 if (session->ch_width <= CH_WIDTH_80MHZ && is_80mhz) {
263 session->he_config.chan_width_2 = 0;
264 session->he_config.chan_width_3 = 0;
265 } else if (session->ch_width == CH_WIDTH_160MHZ) {
266 session->he_config.chan_width_3 = 0;
267 }
268 /* Reset the > 20MHz caps for 20MHz connection */
269 if (session->ch_width == CH_WIDTH_20MHZ) {
270 session->he_config.chan_width_0 = 0;
271 session->he_config.chan_width_1 = 0;
272 session->he_config.chan_width_2 = 0;
273 session->he_config.chan_width_3 = 0;
274 session->he_config.chan_width_4 = 0;
275 session->he_config.chan_width_5 = 0;
276 session->he_config.chan_width_6 = 0;
277 session->he_config.he_ppdu_20_in_40Mhz_2G = 0;
278 session->he_config.he_ppdu_20_in_160_80p80Mhz = 0;
279 session->he_config.he_ppdu_80_in_160_80p80Mhz = 0;
280 }
281 if (WLAN_REG_IS_24GHZ_CH_FREQ(session->curr_op_freq)) {
282 session->he_config.chan_width_1 = 0;
283 session->he_config.chan_width_2 = 0;
284 session->he_config.chan_width_3 = 0;
285 session->he_config.chan_width_5 = 0;
286 session->he_config.chan_width_6 = 0;
287 } else {
288 session->he_config.chan_width_0 = 0;
289 session->he_config.chan_width_4 = 0;
290 session->he_config.chan_width_6 = 0;
291 }
292 if (!session->he_config.chan_width_2) {
293 session->he_config.bfee_sts_gt_80 = 0;
294 session->he_config.num_sounding_gt_80 = 0;
295 session->he_config.he_ppdu_20_in_160_80p80Mhz = 0;
296 session->he_config.he_ppdu_80_in_160_80p80Mhz = 0;
297 *(uint16_t *)session->he_config.rx_he_mcs_map_160 =
298 HE_MCS_ALL_DISABLED;
299 *(uint16_t *)session->he_config.tx_he_mcs_map_160 =
300 HE_MCS_ALL_DISABLED;
301 }
302 if (!session->he_config.chan_width_3) {
303 *(uint16_t *)session->he_config.rx_he_mcs_map_80_80 =
304 HE_MCS_ALL_DISABLED;
305 *(uint16_t *)session->he_config.tx_he_mcs_map_80_80 =
306 HE_MCS_ALL_DISABLED;
307 }
308 }
309
lim_update_he_mcs_12_13_map(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id,uint16_t he_mcs_12_13_map)310 void lim_update_he_mcs_12_13_map(struct wlan_objmgr_psoc *psoc,
311 uint8_t vdev_id, uint16_t he_mcs_12_13_map)
312 {
313 struct wlan_objmgr_vdev *vdev;
314
315 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
316 WLAN_LEGACY_MAC_ID);
317 if (!vdev) {
318 pe_err("vdev not found for id: %d", vdev_id);
319 return;
320 }
321 wlan_vdev_obj_lock(vdev);
322 wlan_vdev_mlme_set_he_mcs_12_13_map(vdev, he_mcs_12_13_map);
323 wlan_vdev_obj_unlock(vdev);
324 wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID);
325 }
326 #else
lim_extract_he_op(struct pe_session * session,tSirProbeRespBeacon * beacon_struct)327 static inline void lim_extract_he_op(struct pe_session *session,
328 tSirProbeRespBeacon *beacon_struct)
329 {}
lim_check_is_he_mcs_valid(struct pe_session * session,tSirProbeRespBeacon * beacon_struct)330 static void lim_check_is_he_mcs_valid(struct pe_session *session,
331 tSirProbeRespBeacon *beacon_struct)
332 {
333 }
334
lim_update_he_mcs_12_13_map(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id,uint16_t he_mcs_12_13_map)335 void lim_update_he_mcs_12_13_map(struct wlan_objmgr_psoc *psoc,
336 uint8_t vdev_id, uint16_t he_mcs_12_13_map)
337 {
338 }
339 #endif
340
341 #ifdef WLAN_FEATURE_11BE
lim_extract_eht_op(struct pe_session * session,tSirProbeRespBeacon * beacon_struct)342 void lim_extract_eht_op(struct pe_session *session,
343 tSirProbeRespBeacon *beacon_struct)
344 {
345 uint32_t max_eht_bw;
346
347 if (!session->eht_capable)
348 return;
349
350 if (!beacon_struct->eht_op.present)
351 return;
352
353 if (!beacon_struct->eht_op.eht_op_information_present)
354 return;
355
356 qdf_mem_copy(&session->eht_op, &beacon_struct->eht_op,
357 sizeof(session->eht_op));
358
359 max_eht_bw = wma_get_eht_ch_width();
360
361 if (session->eht_op.channel_width == WLAN_EHT_CHWIDTH_320) {
362 if (max_eht_bw == WNI_CFG_EHT_CHANNEL_WIDTH_320MHZ) {
363 session->ch_width = CH_WIDTH_320MHZ;
364 } else if (max_eht_bw == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) {
365 session->ch_width = CH_WIDTH_160MHZ;
366 } else {
367 session->ch_width = CH_WIDTH_80MHZ;
368 session->ch_center_freq_seg1 = 0;
369 }
370 } else if (session->eht_op.channel_width == WLAN_EHT_CHWIDTH_160) {
371 if (max_eht_bw >= WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) {
372 session->ch_width = CH_WIDTH_160MHZ;
373 } else {
374 session->ch_width = CH_WIDTH_80MHZ;
375 session->ch_center_freq_seg1 = 0;
376 }
377 } else if (session->eht_op.channel_width == WLAN_EHT_CHWIDTH_80) {
378 session->ch_width = CH_WIDTH_80MHZ;
379 session->ch_center_freq_seg1 = 0;
380 } else if (session->eht_op.channel_width == WLAN_EHT_CHWIDTH_40) {
381 session->ch_width = CH_WIDTH_40MHZ;
382 session->ch_center_freq_seg1 = 0;
383 } else {
384 session->ch_width = CH_WIDTH_20MHZ;
385 session->ch_center_freq_seg1 = 0;
386 }
387
388 session->ch_center_freq_seg0 = session->eht_op.ccfs0;
389 session->ch_center_freq_seg1 = session->eht_op.ccfs1;
390 }
391
lim_update_eht_bw_cap_mcs(struct pe_session * session,tSirProbeRespBeacon * beacon)392 void lim_update_eht_bw_cap_mcs(struct pe_session *session,
393 tSirProbeRespBeacon *beacon)
394 {
395 if (!session->eht_capable)
396 return;
397
398 if ((session->opmode == QDF_STA_MODE ||
399 session->opmode == QDF_P2P_CLIENT_MODE) &&
400 beacon && beacon->eht_cap.present) {
401 if (!beacon->eht_cap.support_320mhz_6ghz)
402 session->eht_config.support_320mhz_6ghz = 0;
403 if (!beacon->eht_cap.support_320mhz_6ghz ||
404 !beacon->eht_cap.su_beamformer)
405 session->eht_config.num_sounding_dim_320mhz = 0;
406 }
407 }
408 #endif
409
410 #ifdef WLAN_FEATURE_11BE_MLO
lim_objmgr_update_emlsr_caps(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id,tpSirAssocRsp assoc_rsp)411 void lim_objmgr_update_emlsr_caps(struct wlan_objmgr_psoc *psoc,
412 uint8_t vdev_id, tpSirAssocRsp assoc_rsp)
413 {
414 struct wlan_objmgr_vdev *vdev;
415 bool ap_emlsr_cap = false;
416
417 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
418 WLAN_LEGACY_MAC_ID);
419 if (!vdev) {
420 pe_err("vdev not found for id: %d", vdev_id);
421 return;
422 }
423
424 /* Check for assoc link vdev to extract emlsr cap from assoc rsp */
425 if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev)) {
426 ap_emlsr_cap =
427 assoc_rsp->mlo_ie.mlo_ie.eml_capabilities_info.emlsr_support;
428
429 if (!(wlan_vdev_mlme_cap_get(vdev, WLAN_VDEV_C_EMLSR_CAP) &&
430 ap_emlsr_cap)) {
431 if (!wlan_vdev_mlme_cap_get(vdev, WLAN_VDEV_C_EMLSR_CAP)
432 && ap_emlsr_cap)
433 pe_debug("No eMLSR STA supp but recvd EML caps in assc rsp");
434 else
435 pe_debug("EML caps not present in assoc rsp");
436 wlan_vdev_obj_lock(vdev);
437 wlan_vdev_mlme_cap_clear(vdev, WLAN_VDEV_C_EMLSR_CAP);
438 wlan_vdev_obj_unlock(vdev);
439 } else {
440 pe_debug("EML caps present in assoc rsp");
441 }
442 } else {
443 pe_debug("no change required for link vdev");
444 }
445
446 wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID);
447 }
448 #endif
449
lim_objmgr_update_vdev_nss(struct wlan_objmgr_psoc * psoc,uint8_t vdev_id,uint8_t nss)450 void lim_objmgr_update_vdev_nss(struct wlan_objmgr_psoc *psoc,
451 uint8_t vdev_id, uint8_t nss)
452 {
453 struct wlan_objmgr_vdev *vdev;
454
455 vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
456 WLAN_LEGACY_MAC_ID);
457 if (!vdev) {
458 pe_err("vdev not found for id: %d", vdev_id);
459 return;
460 }
461 wlan_vdev_obj_lock(vdev);
462 wlan_vdev_mlme_set_nss(vdev, nss);
463 wlan_vdev_obj_unlock(vdev);
464 wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID);
465 }
466
467 #ifdef WLAN_ADAPTIVE_11R
468 /**
469 * lim_extract_adaptive_11r_cap() - check if the AP has adaptive 11r
470 * IE
471 * @ie: Pointer to the IE
472 * @ie_len: ie Length
473 *
474 * Return: True if adaptive 11r IE is present
475 */
lim_extract_adaptive_11r_cap(uint8_t * ie,uint16_t ie_len)476 static bool lim_extract_adaptive_11r_cap(uint8_t *ie, uint16_t ie_len)
477 {
478 const uint8_t *adaptive_ie;
479 uint8_t data;
480 bool adaptive_11r;
481
482 adaptive_ie = wlan_get_vendor_ie_ptr_from_oui(LIM_ADAPTIVE_11R_OUI,
483 LIM_ADAPTIVE_11R_OUI_SIZE,
484 ie, ie_len);
485 if (!adaptive_ie)
486 return false;
487
488 if ((adaptive_ie[1] < (OUI_LENGTH + 1)) ||
489 (adaptive_ie[1] > MAX_ADAPTIVE_11R_IE_LEN))
490 return false;
491
492 data = *(adaptive_ie + OUI_LENGTH + 2);
493 adaptive_11r = (data & 0x1) ? true : false;
494
495 return adaptive_11r;
496 }
497
498 #else
lim_extract_adaptive_11r_cap(uint8_t * ie,uint16_t ie_len)499 static inline bool lim_extract_adaptive_11r_cap(uint8_t *ie, uint16_t ie_len)
500 {
501 return false;
502 }
503 #endif
504
505 #ifdef WLAN_FEATURE_11AX
lim_check_peer_ldpc_and_update(struct pe_session * session,tSirProbeRespBeacon * beacon_struct)506 static void lim_check_peer_ldpc_and_update(struct pe_session *session,
507 tSirProbeRespBeacon *beacon_struct)
508 {
509 /*
510 * In 2.4G if AP supports HE till MCS 0-9 we can associate
511 * with HE mode instead downgrading to 11ac
512 */
513 if (session->he_capable &&
514 WLAN_REG_IS_24GHZ_CH_FREQ(session->curr_op_freq) &&
515 beacon_struct->he_cap.present &&
516 lim_check_he_80_mcs11_supp(session, &beacon_struct->he_cap) &&
517 !beacon_struct->he_cap.ldpc_coding) {
518 session->he_capable = false;
519 pe_err("LDPC check failed for HE operation");
520 if (session->vhtCapability) {
521 session->dot11mode = MLME_DOT11_MODE_11AC;
522 pe_debug("Update dot11mode to 11ac");
523 } else {
524 session->dot11mode = MLME_DOT11_MODE_11N;
525 pe_debug("Update dot11mode to 11N");
526 }
527 }
528 }
529 #else
lim_check_peer_ldpc_and_update(struct pe_session * session,tSirProbeRespBeacon * beacon_struct)530 static void lim_check_peer_ldpc_and_update(struct pe_session *session,
531 tSirProbeRespBeacon *beacon_struct)
532 {}
533 #endif
534
535 static
lim_update_ch_width_for_p2p_client(struct mac_context * mac,struct pe_session * session,uint32_t ch_freq)536 void lim_update_ch_width_for_p2p_client(struct mac_context *mac,
537 struct pe_session *session,
538 uint32_t ch_freq)
539 {
540 struct ch_params ch_params = {0};
541
542 if (session->dot11mode < MLME_DOT11_MODE_11AC)
543 return;
544 /*
545 * Some IOT AP's/P2P-GO's (e.g. make: Wireless-AC 9560160MHz as P2P GO),
546 * send beacon with 20mhz and assoc resp with 80mhz and
547 * after assoc resp, next beacon also has 80mhz.
548 * Connection is expected to happen in better possible
549 * bandwidth(80MHz in this case).
550 * Start the vdev with max supported ch_width in order to support this.
551 * It'll be downgraded to appropriate ch_width or the same would be
552 * continued based on assoc resp.
553 * Restricting this check for p2p client and 5G only and this may be
554 * extended to STA based on wider testing results with multiple AP's.
555 * Limit it to 80MHz as 80+80 is channel specific and 160MHz is not
556 * supported in p2p.
557 */
558 ch_params.ch_width = CH_WIDTH_80MHZ;
559
560 wlan_reg_set_channel_params_for_pwrmode(mac->pdev, ch_freq, 0,
561 &ch_params,
562 REG_CURRENT_PWR_MODE);
563 if (ch_params.ch_width == CH_WIDTH_20MHZ)
564 ch_params.sec_ch_offset = PHY_SINGLE_CHANNEL_CENTERED;
565
566 session->htSupportedChannelWidthSet = ch_params.sec_ch_offset ? 1 : 0;
567 session->htRecommendedTxWidthSet = session->htSupportedChannelWidthSet;
568 session->htSecondaryChannelOffset = ch_params.sec_ch_offset;
569 session->ch_width = ch_params.ch_width;
570 session->ch_center_freq_seg0 = ch_params.center_freq_seg0;
571 session->ch_center_freq_seg1 = ch_params.center_freq_seg1;
572 pe_debug("Start P2P_CLI in ch freq %d max supported ch_width: %u cbmode: %u seg0: %u, seg1: %u",
573 ch_freq, ch_params.ch_width, ch_params.sec_ch_offset,
574 session->ch_center_freq_seg0, session->ch_center_freq_seg1);
575 }
576
lim_extract_ap_capability(struct mac_context * mac_ctx,uint8_t * p_ie,uint16_t ie_len,uint8_t * qos_cap,uint8_t * uapsd,int8_t * local_constraint,struct pe_session * session,bool * is_pwr_constraint)577 void lim_extract_ap_capability(struct mac_context *mac_ctx, uint8_t *p_ie,
578 uint16_t ie_len, uint8_t *qos_cap,
579 uint8_t *uapsd, int8_t *local_constraint,
580 struct pe_session *session,
581 bool *is_pwr_constraint)
582 {
583 tSirProbeRespBeacon *beacon_struct;
584 uint8_t ap_bcon_ch_width;
585 bool new_ch_width_dfn = false;
586 tDot11fIEVHTOperation *vht_op;
587 uint8_t fw_vht_ch_wd;
588 uint8_t vht_ch_wd;
589 uint8_t center_freq_diff;
590 struct s_ext_cap *ext_cap;
591 uint8_t chan_center_freq_seg1;
592 tDot11fIEVHTCaps *vht_caps;
593 uint8_t channel = 0;
594 uint8_t sta_prefer_80mhz_over_160mhz;
595 struct mlme_vht_capabilities_info *mlme_vht_cap;
596 QDF_STATUS status;
597
598 beacon_struct = qdf_mem_malloc(sizeof(tSirProbeRespBeacon));
599 if (!beacon_struct)
600 return;
601
602 *qos_cap = 0;
603 *uapsd = 0;
604 sta_prefer_80mhz_over_160mhz =
605 session->mac_ctx->mlme_cfg->sta.sta_prefer_80mhz_over_160mhz;
606
607 status = sir_parse_beacon_ie(mac_ctx, beacon_struct, p_ie,
608 (uint32_t)ie_len);
609 if (QDF_IS_STATUS_ERROR(status)) {
610 pe_err("sir_parse_beacon_ie failed to parse beacon");
611 qdf_mem_free(beacon_struct);
612 return;
613 }
614
615 mlme_vht_cap = &mac_ctx->mlme_cfg->vht_caps.vht_cap_info;
616 if (beacon_struct->wmeInfoPresent ||
617 beacon_struct->wmeEdcaPresent ||
618 beacon_struct->HTCaps.present)
619 LIM_BSS_CAPS_SET(WME, *qos_cap);
620
621 if (LIM_BSS_CAPS_GET(WME, *qos_cap) && beacon_struct->wsmCapablePresent)
622 LIM_BSS_CAPS_SET(WSM, *qos_cap);
623
624 if (beacon_struct->HTCaps.present)
625 mac_ctx->lim.htCapabilityPresentInBeacon = 1;
626 else
627 mac_ctx->lim.htCapabilityPresentInBeacon = 0;
628
629 vht_op = &beacon_struct->VHTOperation;
630 vht_caps = &beacon_struct->VHTCaps;
631 if (IS_BSS_VHT_CAPABLE(beacon_struct->VHTCaps) && vht_op->present &&
632 session->vhtCapability) {
633 session->vhtCapabilityPresentInBeacon = 1;
634
635 if (((beacon_struct->Vendor1IEPresent &&
636 beacon_struct->vendor_vht_ie.present &&
637 beacon_struct->Vendor3IEPresent)) &&
638 (((beacon_struct->VHTCaps.txMCSMap & VHT_MCS_3x3_MASK) ==
639 VHT_MCS_3x3_MASK) &&
640 ((beacon_struct->VHTCaps.txMCSMap & VHT_MCS_2x2_MASK) !=
641 VHT_MCS_2x2_MASK)))
642 session->vht_config.su_beam_formee = 0;
643 } else {
644 session->vhtCapabilityPresentInBeacon = 0;
645 }
646
647 if (session->vhtCapabilityPresentInBeacon == 1 &&
648 !session->htSupportedChannelWidthSet) {
649 if (!mac_ctx->mlme_cfg->vht_caps.vht_cap_info.enable_txbf_20mhz)
650 session->vht_config.su_beam_formee = 0;
651
652 if (session->opmode == QDF_P2P_CLIENT_MODE &&
653 !wlan_reg_is_24ghz_ch_freq(beacon_struct->chan_freq) &&
654 mac_ctx->roam.configParam.channelBondingMode5GHz)
655 lim_update_ch_width_for_p2p_client(
656 mac_ctx, session,
657 beacon_struct->chan_freq);
658
659 } else if (session->vhtCapabilityPresentInBeacon && vht_op->chanWidth) {
660 /* If VHT is supported min 80 MHz support is must */
661 ap_bcon_ch_width = vht_op->chanWidth;
662 if (vht_caps->vht_extended_nss_bw_cap) {
663 if (!vht_caps->extended_nss_bw_supp)
664 chan_center_freq_seg1 =
665 vht_op->chan_center_freq_seg1;
666 else
667 chan_center_freq_seg1 =
668 beacon_struct->HTInfo.chan_center_freq_seg2;
669 } else {
670 chan_center_freq_seg1 = vht_op->chan_center_freq_seg1;
671 }
672 if (chan_center_freq_seg1 &&
673 (ap_bcon_ch_width == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ)) {
674 new_ch_width_dfn = true;
675 if (chan_center_freq_seg1 >
676 vht_op->chan_center_freq_seg0)
677 center_freq_diff = chan_center_freq_seg1 -
678 vht_op->chan_center_freq_seg0;
679 else
680 center_freq_diff = vht_op->chan_center_freq_seg0 -
681 chan_center_freq_seg1;
682 if (center_freq_diff == 8)
683 ap_bcon_ch_width =
684 WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ;
685 else if (center_freq_diff > 16)
686 ap_bcon_ch_width =
687 WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ;
688 else
689 ap_bcon_ch_width =
690 WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ;
691 }
692
693 fw_vht_ch_wd = wma_get_vht_ch_width();
694 vht_ch_wd = QDF_MIN(fw_vht_ch_wd, ap_bcon_ch_width);
695
696 if ((vht_ch_wd > WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) &&
697 (ap_bcon_ch_width ==
698 WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ) &&
699 mlme_vht_cap->restricted_80p80_bw_supp) {
700 if ((chan_center_freq_seg1 == 138 &&
701 vht_op->chan_center_freq_seg0 == 155) ||
702 (vht_op->chan_center_freq_seg0 == 138 &&
703 chan_center_freq_seg1 == 155))
704 vht_ch_wd =
705 WNI_CFG_VHT_CHANNEL_WIDTH_80_PLUS_80MHZ;
706 else
707 vht_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ;
708 }
709 /*
710 * If the supported channel width is greater than 80MHz and
711 * AP supports Nss > 1 in 160MHz mode then connect the STA
712 * in 2x2 80MHz mode instead of connecting in 160MHz mode.
713 */
714 if (vht_ch_wd > WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) {
715 if (sta_prefer_80mhz_over_160mhz == STA_PREFER_BW_80MHZ)
716 vht_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ;
717 else if ((sta_prefer_80mhz_over_160mhz ==
718 STA_PREFER_BW_VHT80MHZ) &&
719 (!(IS_VHT_NSS_1x1(beacon_struct->VHTCaps.txMCSMap)) &&
720 (!IS_VHT_NSS_1x1(beacon_struct->VHTCaps.rxMCSMap))))
721 vht_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ;
722 }
723 /*
724 * VHT OP IE old definition:
725 * vht_op->chan_center_freq_seg0: center freq of 80MHz/160MHz/
726 * primary 80 in 80+80MHz.
727 *
728 * vht_op->chan_center_freq_seg1: center freq of secondary 80
729 * in 80+80MHz.
730 *
731 * VHT OP IE NEW definition:
732 * vht_op->chan_center_freq_seg0: center freq of 80MHz/primary
733 * 80 in 80+80MHz/center freq of the 80 MHz channel segment
734 * that contains the primary channel in 160MHz mode.
735 *
736 * vht_op->chan_center_freq_seg1: center freq of secondary 80
737 * in 80+80MHz/center freq of 160MHz.
738 */
739 session->ch_center_freq_seg0 = vht_op->chan_center_freq_seg0;
740 session->ch_center_freq_seg1 = chan_center_freq_seg1;
741 channel = wlan_reg_freq_to_chan(mac_ctx->pdev,
742 beacon_struct->chan_freq);
743 if (vht_ch_wd == WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) {
744 /* DUT or AP supports only 160MHz */
745 if (ap_bcon_ch_width ==
746 WNI_CFG_VHT_CHANNEL_WIDTH_160MHZ) {
747 /* AP is in 160MHz mode */
748 if (!new_ch_width_dfn) {
749 session->ch_center_freq_seg1 =
750 vht_op->chan_center_freq_seg0;
751 session->ch_center_freq_seg0 =
752 lim_get_80Mhz_center_channel(channel);
753 }
754 } else {
755 /* DUT supports only 160MHz and AP is
756 * in 80+80 mode
757 */
758 vht_ch_wd = WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ;
759 session->ch_center_freq_seg1 = 0;
760 session->ch_center_freq_seg0 =
761 lim_get_80Mhz_center_channel(channel);
762 }
763 } else if (vht_ch_wd == WNI_CFG_VHT_CHANNEL_WIDTH_80MHZ) {
764 /* DUT or AP supports only 80MHz */
765 session->ch_center_freq_seg0 =
766 lim_get_80Mhz_center_channel(channel);
767 session->ch_center_freq_seg1 = 0;
768 }
769 session->ch_width = vht_ch_wd + 1;
770 session->ap_ch_width = session->ch_width;
771 }
772
773 if (session->vhtCapability && session->vhtCapabilityPresentInBeacon &&
774 beacon_struct->ext_cap.present) {
775 ext_cap = (struct s_ext_cap *)beacon_struct->ext_cap.bytes;
776 session->gLimOperatingMode.present =
777 ext_cap->oper_mode_notification;
778 if (ext_cap->oper_mode_notification) {
779 uint8_t self_nss = 0;
780
781 if (!wlan_reg_is_24ghz_ch_freq(session->curr_op_freq))
782 self_nss = mac_ctx->vdev_type_nss_5g.sta;
783 else
784 self_nss = mac_ctx->vdev_type_nss_2g.sta;
785
786 if (CH_WIDTH_160MHZ > session->ch_width)
787 session->gLimOperatingMode.chanWidth =
788 session->ch_width;
789 else
790 session->gLimOperatingMode.chanWidth =
791 CH_WIDTH_160MHZ;
792 /** Populate vdev nss in OMN ie of assoc requse for
793 * WFA CERT test scenario.
794 */
795 if (ext_cap->beacon_protection_enable &&
796 session->opmode == QDF_STA_MODE &&
797 !session->nss_forced_1x1 &&
798 lim_get_nss_supported_by_ap(
799 &beacon_struct->VHTCaps,
800 &beacon_struct->HTCaps,
801 &beacon_struct->he_cap) == NSS_1x1_MODE)
802 session->gLimOperatingMode.rxNSS = self_nss - 1;
803 else
804 session->gLimOperatingMode.rxNSS =
805 session->nss - 1;
806 } else {
807 pe_err("AP does not support op_mode rx");
808 }
809 }
810
811 lim_check_is_he_mcs_valid(session, beacon_struct);
812 lim_check_peer_ldpc_and_update(session, beacon_struct);
813 lim_extract_he_op(session, beacon_struct);
814 lim_extract_eht_op(session, beacon_struct);
815 if (!mac_ctx->usr_eht_testbed_cfg)
816 lim_update_he_bw_cap_mcs(session, beacon_struct);
817 lim_update_eht_bw_cap_mcs(session, beacon_struct);
818 /* Extract the UAPSD flag from WMM Parameter element */
819 if (beacon_struct->wmeEdcaPresent)
820 *uapsd = beacon_struct->edcaParams.qosInfo.uapsd;
821
822 if (mac_ctx->mlme_cfg->sta.allow_tpc_from_ap) {
823 if (beacon_struct->powerConstraintPresent) {
824 *local_constraint =
825 beacon_struct->localPowerConstraint.
826 localPowerConstraints;
827 *is_pwr_constraint = true;
828 } else {
829 get_local_power_constraint_probe_response(
830 beacon_struct, local_constraint, session);
831 *is_pwr_constraint = false;
832 }
833 }
834
835 get_ese_version_ie_probe_response(mac_ctx, beacon_struct, session);
836
837 session->country_info_present = false;
838 /* Initializing before first use */
839 if (beacon_struct->countryInfoPresent)
840 session->country_info_present = true;
841 /* Check if Extended caps are present in probe resp or not */
842 if (beacon_struct->ext_cap.present)
843 session->is_ext_caps_present = true;
844 /* Update HS 2.0 Information Element */
845 if (beacon_struct->hs20vendor_ie.present) {
846 pe_debug("HS20 Indication Element Present, rel#: %u id: %u",
847 beacon_struct->hs20vendor_ie.release_num,
848 beacon_struct->hs20vendor_ie.hs_id_present);
849 qdf_mem_copy(&session->hs20vendor_ie,
850 &beacon_struct->hs20vendor_ie,
851 sizeof(tDot11fIEhs20vendor_ie) -
852 sizeof(beacon_struct->hs20vendor_ie.hs_id));
853 if (beacon_struct->hs20vendor_ie.hs_id_present)
854 qdf_mem_copy(&session->hs20vendor_ie.hs_id,
855 &beacon_struct->hs20vendor_ie.hs_id,
856 sizeof(beacon_struct->hs20vendor_ie.hs_id));
857 }
858
859 lim_objmgr_update_vdev_nss(mac_ctx->psoc, session->smeSessionId,
860 session->nss);
861
862 session->is_adaptive_11r_connection =
863 lim_extract_adaptive_11r_cap(p_ie, ie_len);
864 qdf_mem_free(beacon_struct);
865 return;
866 } /****** end lim_extract_ap_capability() ******/
867
868 /**
869 * lim_get_htcb_state
870 *
871 ***FUNCTION:
872 * This routing provides the translation of Airgo Enum to HT enum for determining
873 * secondary channel offset.
874 * Airgo Enum is required for backward compatibility purposes.
875 *
876 *
877 ***NOTE:
878 *
879 * @param mac - Pointer to Global MAC structure
880 * @return The corresponding HT enumeration
881 */
lim_get_htcb_state(ePhyChanBondState aniCBMode)882 ePhyChanBondState lim_get_htcb_state(ePhyChanBondState aniCBMode)
883 {
884 switch (aniCBMode) {
885 case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_LOW:
886 case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_CENTERED:
887 case PHY_QUADRUPLE_CHANNEL_20MHZ_HIGH_40MHZ_HIGH:
888 case PHY_DOUBLE_CHANNEL_HIGH_PRIMARY:
889 return PHY_DOUBLE_CHANNEL_HIGH_PRIMARY;
890 case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_LOW:
891 case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_CENTERED:
892 case PHY_QUADRUPLE_CHANNEL_20MHZ_LOW_40MHZ_HIGH:
893 case PHY_DOUBLE_CHANNEL_LOW_PRIMARY:
894 return PHY_DOUBLE_CHANNEL_LOW_PRIMARY;
895 case PHY_QUADRUPLE_CHANNEL_20MHZ_CENTERED_40MHZ_CENTERED:
896 return PHY_SINGLE_CHANNEL_CENTERED;
897 default:
898 return PHY_SINGLE_CHANNEL_CENTERED;
899 }
900 }
901