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