xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlo_mgr/src/utils_mlo.c (revision 932f2d2bb2b411c64156731c8b8d1180032e2573)
1 /*
2  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*
19  * DOC: contains MLO manager util api's
20  */
21 #include <wlan_cmn.h>
22 #include <wlan_mlo_mgr_sta.h>
23 #include <wlan_cm_public_struct.h>
24 #include <wlan_mlo_mgr_main.h>
25 #include <wlan_cm_api.h>
26 #include "wlan_scan_api.h"
27 #include "qdf_types.h"
28 #include "utils_mlo.h"
29 #include "wlan_mlo_mgr_cmn.h"
30 #include "wlan_utility.h"
31 
32 #ifdef WLAN_FEATURE_11BE_MLO
33 
34 static uint8_t *util_find_eid(uint8_t eid, uint8_t *frame, qdf_size_t len)
35 {
36 	if (!frame)
37 		return NULL;
38 
39 	while (len >= MIN_IE_LEN && len >= frame[TAG_LEN_POS] + MIN_IE_LEN) {
40 		if (frame[ID_POS] == eid)
41 			return frame;
42 
43 		len -= frame[TAG_LEN_POS] + MIN_IE_LEN;
44 		frame += frame[TAG_LEN_POS] + MIN_IE_LEN;
45 	}
46 
47 	return NULL;
48 }
49 
50 static
51 uint8_t *util_find_extn_eid(uint8_t eid, uint8_t extn_eid,
52 			    uint8_t *frame, qdf_size_t len)
53 {
54 	if (!frame)
55 		return NULL;
56 
57 	while (len > MIN_IE_LEN && len >= frame[TAG_LEN_POS] + MIN_IE_LEN) {
58 		if ((frame[ID_POS] == eid) &&
59 		    (frame[ELEM_ID_EXTN_POS] == extn_eid))
60 			return frame;
61 
62 		len -= frame[TAG_LEN_POS] + MIN_IE_LEN;
63 		frame += frame[TAG_LEN_POS] + MIN_IE_LEN;
64 	}
65 	return NULL;
66 }
67 
68 static QDF_STATUS
69 util_parse_multi_link_ctrl(uint8_t *mlieseqpayload,
70 			   qdf_size_t mlieseqpayloadlen,
71 			   uint8_t **link_info,
72 			   qdf_size_t *link_info_len)
73 {
74 	qdf_size_t parsed_payload_len;
75 	uint16_t mlcontrol;
76 	uint16_t presence_bm;
77 	uint16_t cinfo_len = 0;
78 	uint16_t exp_cinfo_len = 0;
79 
80 	/* This helper returns the location(s) and length(s) of (sub)field(s)
81 	 * inferable after parsing the Multi Link element Control field. These
82 	 * location(s) and length(s) is/are in reference to the payload section
83 	 * of the Multi Link element (after defragmentation, if applicable).
84 	 * Here, the payload is the point after the element ID extension of the
85 	 * Multi Link element, and includes the payloads of all subsequent
86 	 * fragments (if any) but not the headers of those fragments.
87 	 *
88 	 * Currently, the helper returns the location and length of the Link
89 	 * Info field in the Multi Link element sequence. Other (sub)field(s)
90 	 * can be added later as required.
91 	 */
92 
93 	if (!mlieseqpayload) {
94 		mlo_err("ML seq payload pointer is NULL");
95 		return QDF_STATUS_E_NULL_VALUE;
96 	}
97 
98 	if (!mlieseqpayloadlen) {
99 		mlo_err("ML seq payload len is 0");
100 		return QDF_STATUS_E_INVAL;
101 	}
102 
103 	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
104 		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
105 			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
106 		return QDF_STATUS_E_PROTO;
107 	}
108 
109 	parsed_payload_len = 0;
110 
111 	qdf_mem_copy(&mlcontrol, mlieseqpayload, WLAN_ML_CTRL_SIZE);
112 	mlcontrol = qdf_le16_to_cpu(mlcontrol);
113 	parsed_payload_len += WLAN_ML_CTRL_SIZE;
114 
115 	presence_bm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
116 				   WLAN_ML_CTRL_PBM_BITS);
117 
118 	if (mlieseqpayloadlen <
119 			(parsed_payload_len + WLAN_ML_BV_CINFO_LENGTH_SIZE)) {
120 		mlo_err_rl("ML seq payload len %zu insufficient for common info length size %u after parsed payload len %zu.",
121 			   mlieseqpayloadlen,
122 			   WLAN_ML_BV_CINFO_LENGTH_SIZE,
123 			   parsed_payload_len);
124 		return QDF_STATUS_E_PROTO;
125 	}
126 
127 	cinfo_len = *(mlieseqpayload + parsed_payload_len);
128 	parsed_payload_len += WLAN_ML_BV_CINFO_LENGTH_SIZE;
129 
130 	if (mlieseqpayloadlen <
131 			(parsed_payload_len + QDF_MAC_ADDR_SIZE)) {
132 		mlo_err_rl("ML seq payload len %zu insufficient for MAC address size %u after parsed payload len %zu.",
133 			   mlieseqpayloadlen,
134 			   QDF_MAC_ADDR_SIZE,
135 			   parsed_payload_len);
136 		return QDF_STATUS_E_PROTO;
137 	}
138 
139 	parsed_payload_len += QDF_MAC_ADDR_SIZE;
140 
141 	/* Check if Link ID info is present */
142 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
143 		if (mlieseqpayloadlen <
144 				(parsed_payload_len +
145 				 WLAN_ML_BV_CINFO_LINKIDINFO_SIZE)) {
146 			mlo_err_rl("ML seq payload len %zu insufficient for Link ID info size %u after parsed payload len %zu.",
147 				   mlieseqpayloadlen,
148 				   WLAN_ML_BV_CINFO_LINKIDINFO_SIZE,
149 				   parsed_payload_len);
150 			return QDF_STATUS_E_PROTO;
151 		}
152 
153 		parsed_payload_len += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
154 	}
155 
156 	/* Check if BSS parameter change count is present */
157 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
158 		if (mlieseqpayloadlen <
159 				(parsed_payload_len +
160 				 WLAN_ML_BSSPARAMCHNGCNT_SIZE)) {
161 			mlo_err_rl("ML seq payload len %zu insufficient for BSS parameter change count size %u after parsed payload len %zu.",
162 				   mlieseqpayloadlen,
163 				   WLAN_ML_BSSPARAMCHNGCNT_SIZE,
164 				   parsed_payload_len);
165 			return QDF_STATUS_E_PROTO;
166 		}
167 
168 		parsed_payload_len += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
169 	}
170 
171 	/* Check if Medium Sync Delay Info is present */
172 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
173 		if (mlieseqpayloadlen <
174 				(parsed_payload_len +
175 				 WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE)) {
176 			mlo_err_rl("ML seq payload len %zu insufficient for Medium Sync Delay Info size %u after parsed payload len %zu.",
177 				   mlieseqpayloadlen,
178 				   WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE,
179 				   parsed_payload_len);
180 			return QDF_STATUS_E_PROTO;
181 		}
182 
183 		parsed_payload_len += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
184 	}
185 
186 	/* Check if EML cap is present */
187 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
188 		if (mlieseqpayloadlen <
189 				(parsed_payload_len +
190 				 WLAN_ML_BV_CINFO_EMLCAP_SIZE)) {
191 			mlo_err_rl("ML seq payload len %zu insufficient for EML cap size %u after parsed payload len %zu.",
192 				   mlieseqpayloadlen,
193 				   WLAN_ML_BV_CINFO_EMLCAP_SIZE,
194 				   parsed_payload_len);
195 			return QDF_STATUS_E_PROTO;
196 		}
197 
198 		parsed_payload_len += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
199 	}
200 
201 	/* Check if MLD cap is present */
202 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_MLDCAPANDOP_P) {
203 		if (mlieseqpayloadlen <
204 				(parsed_payload_len +
205 				 WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE)) {
206 			mlo_err_rl("ML seq payload len %zu insufficient for MLD cap size %u after parsed payload len %zu.",
207 				   mlieseqpayloadlen,
208 				   WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE,
209 				   parsed_payload_len);
210 			return QDF_STATUS_E_PROTO;
211 		}
212 
213 		parsed_payload_len += WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE;
214 	}
215 
216 	/* Check if MLD ID is present */
217 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_MLDID_P) {
218 		if (mlieseqpayloadlen <
219 				(parsed_payload_len +
220 				 WLAN_ML_BV_CINFO_MLDID_SIZE)) {
221 			mlo_err_rl("ML seq payload len %zu insufficient for MLD ID size %u after parsed payload len %zu.",
222 				   mlieseqpayloadlen,
223 				   WLAN_ML_BV_CINFO_MLDID_SIZE,
224 				   parsed_payload_len);
225 			return QDF_STATUS_E_PROTO;
226 		}
227 
228 		parsed_payload_len += WLAN_ML_BV_CINFO_MLDID_SIZE;
229 	}
230 
231 	/* Check if Ext MLD CAP OP is present */
232 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_EXT_MLDCAPANDOP_P) {
233 		if (mlieseqpayloadlen <
234 				(parsed_payload_len +
235 				 WLAN_ML_BV_CINFO_EXT_MLDCAPANDOP_SIZE)) {
236 			mlo_err_rl("ML seq payload len %zu insufficient for Ext MLD CAP OP size %u after parsed payload len %zu.",
237 				   mlieseqpayloadlen,
238 				   WLAN_ML_BV_CINFO_EXT_MLDCAPANDOP_SIZE,
239 				   parsed_payload_len);
240 			return QDF_STATUS_E_PROTO;
241 		}
242 
243 		parsed_payload_len += WLAN_ML_BV_CINFO_EXT_MLDCAPANDOP_SIZE;
244 	}
245 
246 	exp_cinfo_len = parsed_payload_len - WLAN_ML_CTRL_SIZE;
247 	if (cinfo_len >= exp_cinfo_len) {
248 		/* If common info length received is greater,
249 		 *  skip through the additional bytes
250 		 */
251 		parsed_payload_len += (cinfo_len - exp_cinfo_len);
252 		mlo_debug("ML seq common info len %u, parsed payload length %zu, expected common info len %u",
253 			  cinfo_len, parsed_payload_len, exp_cinfo_len);
254 	} else {
255 		/* If cinfo_len < exp_cinfo_len return error */
256 		mlo_err_rl("ML seq common info len %u doesn't match with expected common info len %u",
257 			   cinfo_len, exp_cinfo_len);
258 		return QDF_STATUS_E_PROTO;
259 	}
260 
261 	if (link_info_len) {
262 		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
263 		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu",
264 			  *link_info_len, parsed_payload_len);
265 	}
266 
267 	if (mlieseqpayloadlen == parsed_payload_len) {
268 		mlo_debug("No Link Info field present");
269 		if (link_info)
270 			*link_info = NULL;
271 		return QDF_STATUS_SUCCESS;
272 	}
273 
274 	if (link_info)
275 		*link_info = mlieseqpayload + parsed_payload_len;
276 
277 	return QDF_STATUS_SUCCESS;
278 }
279 
280 static QDF_STATUS
281 util_parse_prv_multi_link_ctrl(uint8_t *mlieseqpayload,
282 			       qdf_size_t mlieseqpayloadlen,
283 			       uint8_t **link_info,
284 			       qdf_size_t *link_info_len)
285 {
286 	qdf_size_t parsed_payload_len;
287 	uint16_t mlcontrol;
288 	uint16_t presence_bm;
289 	uint16_t cinfo_len = 0;
290 	uint16_t exp_cinfo_len = 0;
291 
292 	/* This helper returns the location(s) and length(s) of (sub)field(s)
293 	 * inferable after parsing the Multi Link element Control field. These
294 	 * location(s) and length(s) is/are in reference to the payload section
295 	 * of the Multi Link element (after defragmentation, if applicable).
296 	 * Here, the payload is the point after the element ID extension of the
297 	 * Multi Link element, and includes the payloads of all subsequent
298 	 * fragments (if any) but not the headers of those fragments.
299 	 *
300 	 * Currently, the helper returns the location and length of the Link
301 	 * Info field in the Multi Link element sequence. Other (sub)field(s)
302 	 * can be added later as required.
303 	 */
304 
305 	if (!mlieseqpayload) {
306 		mlo_err("ML seq payload pointer is NULL");
307 		return QDF_STATUS_E_NULL_VALUE;
308 	}
309 
310 	if (!mlieseqpayloadlen) {
311 		mlo_err("ML seq payload len is 0");
312 		return QDF_STATUS_E_INVAL;
313 	}
314 
315 	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
316 		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
317 			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
318 		return QDF_STATUS_E_PROTO;
319 	}
320 
321 	parsed_payload_len = 0;
322 
323 	qdf_mem_copy(&mlcontrol, mlieseqpayload, WLAN_ML_CTRL_SIZE);
324 	mlcontrol = qdf_le16_to_cpu(mlcontrol);
325 	parsed_payload_len += WLAN_ML_CTRL_SIZE;
326 
327 	presence_bm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
328 				   WLAN_ML_CTRL_PBM_BITS);
329 
330 	if (mlieseqpayloadlen <
331 			(parsed_payload_len + WLAN_ML_PRV_CINFO_LENGTH_SIZE)) {
332 		mlo_err_rl("ML seq payload len %zu insufficient for common info length size %u after parsed payload len %zu.",
333 			   mlieseqpayloadlen,
334 			   WLAN_ML_PRV_CINFO_LENGTH_SIZE,
335 			   parsed_payload_len);
336 		return QDF_STATUS_E_PROTO;
337 	}
338 
339 	cinfo_len = *(mlieseqpayload + parsed_payload_len);
340 	parsed_payload_len += WLAN_ML_PRV_CINFO_LENGTH_SIZE;
341 
342 	/* Check if MLD ID is present */
343 	if (presence_bm & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
344 		if (mlieseqpayloadlen <
345 				(parsed_payload_len +
346 				 WLAN_ML_PRV_CINFO_MLDID_SIZE)) {
347 			mlo_err_rl("ML seq payload len %zu insufficient for MLD ID size %u after parsed payload len %zu.",
348 				   mlieseqpayloadlen,
349 				   WLAN_ML_PRV_CINFO_MLDID_SIZE,
350 				   parsed_payload_len);
351 			return QDF_STATUS_E_PROTO;
352 		}
353 
354 		parsed_payload_len += WLAN_ML_PRV_CINFO_MLDID_SIZE;
355 	}
356 
357 	exp_cinfo_len = parsed_payload_len - WLAN_ML_CTRL_SIZE;
358 	if (cinfo_len != exp_cinfo_len) {
359 		mlo_err_rl("ML seq common info len %u doesn't match with expected common info len %u",
360 			   cinfo_len, exp_cinfo_len);
361 		return QDF_STATUS_E_PROTO;
362 	}
363 
364 	if (link_info_len) {
365 		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
366 		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu",
367 			  *link_info_len, parsed_payload_len);
368 	}
369 
370 	if (mlieseqpayloadlen == parsed_payload_len) {
371 		mlo_debug("No Link Info field present");
372 		if (link_info)
373 			*link_info = NULL;
374 		return QDF_STATUS_SUCCESS;
375 	}
376 
377 	if (link_info)
378 		*link_info = mlieseqpayload + parsed_payload_len;
379 
380 	return QDF_STATUS_SUCCESS;
381 }
382 
383 static QDF_STATUS
384 util_parse_bvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
385 					qdf_size_t subelempayloadlen,
386 					uint8_t *linkid,
387 					uint16_t *beaconinterval,
388 					bool *is_beaconinterval_valid,
389 					uint64_t *tsfoffset,
390 					bool *is_tsfoffset_valid,
391 					bool *is_complete_profile,
392 					bool *is_macaddr_valid,
393 					struct qdf_mac_addr *macaddr,
394 					bool is_staprof_reqd,
395 					uint8_t **staprof,
396 					qdf_size_t *staprof_len,
397 					struct mlo_nstr_info *nstr_info,
398 					bool *is_nstrlp_present)
399 {
400 	qdf_size_t parsed_payload_len = 0;
401 	uint16_t stacontrol;
402 	uint8_t completeprofile;
403 	uint8_t nstrlppresent;
404 	enum wlan_ml_bv_linfo_perstaprof_stactrl_nstrbmsz nstrbmsz;
405 	qdf_size_t nstrlpoffset = 0;
406 	uint8_t link_id;
407 
408 	/* This helper returns the location(s) and where required, the length(s)
409 	 * of (sub)field(s) inferable after parsing the STA Control field in the
410 	 * per-STA profile subelement. These location(s) and length(s) is/are in
411 	 * reference to the payload section of the per-STA profile subelement
412 	 * (after defragmentation, if applicable).  Here, the payload is the
413 	 * point after the subelement length in the subelement, and includes the
414 	 * payloads of all subsequent fragments (if any) but not the headers of
415 	 * those fragments.
416 	 *
417 	 * Currently, the helper returns the link ID, MAC address, and STA
418 	 * profile. More (sub)fields can be added when required.
419 	 */
420 
421 	if (!subelempayload) {
422 		mlo_err("Pointer to subelement payload is NULL");
423 		return QDF_STATUS_E_NULL_VALUE;
424 	}
425 
426 	if (!subelempayloadlen) {
427 		mlo_err("Length of subelement payload is zero");
428 		return QDF_STATUS_E_INVAL;
429 	}
430 
431 	if (subelempayloadlen < WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE) {
432 		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
433 			   subelempayloadlen,
434 			   WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE);
435 		return QDF_STATUS_E_PROTO;
436 	}
437 
438 	parsed_payload_len = 0;
439 
440 	qdf_mem_copy(&stacontrol,
441 		     subelempayload,
442 		     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE);
443 	stacontrol = le16toh(stacontrol);
444 	parsed_payload_len += WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE;
445 
446 	link_id = QDF_GET_BITS(stacontrol,
447 			       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
448 			       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
449 	if (linkid)
450 		*linkid = link_id;
451 
452 	/* Check if this a complete profile */
453 	completeprofile = QDF_GET_BITS(stacontrol,
454 				       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
455 				       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
456 
457 	if (completeprofile && is_complete_profile)
458 		*is_complete_profile = true;
459 
460 	/* Check STA Info Length */
461 	if (subelempayloadlen <
462 		parsed_payload_len + WLAN_ML_BV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE) {
463 		mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain STA Info Length of size %u octets after parsed payload length of %zu octets.",
464 			   subelempayloadlen,
465 			   WLAN_ML_BV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE,
466 			   parsed_payload_len);
467 		return QDF_STATUS_E_PROTO;
468 	}
469 
470 	parsed_payload_len += WLAN_ML_BV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE;
471 
472 	if (is_macaddr_valid)
473 		*is_macaddr_valid = false;
474 
475 	/* Check STA MAC address present bit */
476 	if (QDF_GET_BITS(stacontrol,
477 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
478 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_BITS)) {
479 		if (subelempayloadlen <
480 				(parsed_payload_len + QDF_MAC_ADDR_SIZE)) {
481 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain MAC address of size %u octets after parsed payload length of %zu octets.",
482 				   subelempayloadlen,
483 				   QDF_MAC_ADDR_SIZE,
484 				   parsed_payload_len);
485 			return QDF_STATUS_E_PROTO;
486 		}
487 
488 		if (macaddr) {
489 			qdf_mem_copy(macaddr->bytes,
490 				     subelempayload + parsed_payload_len,
491 				     QDF_MAC_ADDR_SIZE);
492 
493 			mlo_nofl_debug("Copied MAC address: " QDF_MAC_ADDR_FMT,
494 				       QDF_MAC_ADDR_REF(macaddr->bytes));
495 
496 			if (is_macaddr_valid)
497 				*is_macaddr_valid = true;
498 		}
499 
500 		parsed_payload_len += QDF_MAC_ADDR_SIZE;
501 	}
502 
503 	/* Check Beacon Interval present bit */
504 	if (QDF_GET_BITS(stacontrol,
505 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_IDX,
506 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_BITS)) {
507 		if (subelempayloadlen <
508 				(parsed_payload_len +
509 				 WLAN_BEACONINTERVAL_LEN)) {
510 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain Beacon Interval of size %u octets after parsed payload length of %zu octets.",
511 				   subelempayloadlen,
512 				   WLAN_BEACONINTERVAL_LEN,
513 				   parsed_payload_len);
514 			return QDF_STATUS_E_PROTO;
515 		}
516 
517 		if (beaconinterval) {
518 			qdf_mem_copy(beaconinterval,
519 				     subelempayload + parsed_payload_len,
520 				     WLAN_BEACONINTERVAL_LEN);
521 			*beaconinterval = qdf_le16_to_cpu(*beaconinterval);
522 
523 			if (is_beaconinterval_valid)
524 				*is_beaconinterval_valid = true;
525 		}
526 		parsed_payload_len += WLAN_BEACONINTERVAL_LEN;
527 	}
528 
529 	/* Check TSF Offset present bit */
530 	if (QDF_GET_BITS(stacontrol,
531 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_TSFOFFSETP_IDX,
532 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_TSFOFFSETP_BITS)) {
533 		if (!completeprofile) {
534 			mlo_err_rl("TSF offset is expected only for complete profiles");
535 			return QDF_STATUS_E_PROTO;
536 		}
537 
538 		if (subelempayloadlen <
539 				(parsed_payload_len +
540 				 WLAN_ML_TSF_OFFSET_SIZE)) {
541 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain TSF Offset of size %u octets after parsed payload length of %zu octets.",
542 				   subelempayloadlen,
543 				   WLAN_ML_TSF_OFFSET_SIZE,
544 				   parsed_payload_len);
545 			return QDF_STATUS_E_PROTO;
546 		}
547 
548 		if (tsfoffset) {
549 			qdf_mem_copy(tsfoffset,
550 				     subelempayload + parsed_payload_len,
551 				     WLAN_TIMESTAMP_LEN);
552 			*tsfoffset = qdf_le64_to_cpu(*tsfoffset);
553 
554 			if (is_tsfoffset_valid)
555 				*is_tsfoffset_valid = true;
556 		}
557 
558 		parsed_payload_len += WLAN_ML_TSF_OFFSET_SIZE;
559 	}
560 
561 	/* Check DTIM Info present bit */
562 	if (QDF_GET_BITS(stacontrol,
563 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_IDX,
564 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_BITS)) {
565 		if (subelempayloadlen <
566 				(parsed_payload_len +
567 				 sizeof(struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo))) {
568 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain DTIM Info of size %zu octets after parsed payload length of %zu octets.",
569 				   subelempayloadlen,
570 				   sizeof(struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo),
571 				   parsed_payload_len);
572 			return QDF_STATUS_E_PROTO;
573 		}
574 
575 		parsed_payload_len +=
576 			sizeof(struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo);
577 	}
578 
579 	/* Check NTSR Link pair present bit */
580 	nstrlppresent =
581 		QDF_GET_BITS(stacontrol,
582 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_IDX,
583 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_BITS);
584 
585 	if (completeprofile && nstrlppresent) {
586 		nstrlpoffset = parsed_payload_len;
587 		/* Check NTSR Bitmap Size bit */
588 		nstrbmsz =
589 			QDF_GET_BITS(stacontrol,
590 				     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_IDX,
591 				     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_BITS);
592 
593 		if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_1_OCTET) {
594 			if (subelempayloadlen <
595 					(parsed_payload_len + 1)) {
596 				mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain NTSR Bitmap of size 1 octet after parsed payload length of %zu octets.",
597 					   subelempayloadlen,
598 					   parsed_payload_len);
599 				return QDF_STATUS_E_PROTO;
600 			}
601 
602 			parsed_payload_len += 1;
603 		} else if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_2_OCTETS) {
604 			if (subelempayloadlen <
605 					(parsed_payload_len + 2)) {
606 				mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain NTSR Bitmap  of size 2 octets after parsed payload length of %zu octets.",
607 					   subelempayloadlen,
608 					   parsed_payload_len);
609 				return QDF_STATUS_E_PROTO;
610 			}
611 
612 			parsed_payload_len += 2;
613 		} else {
614 			/* Though an invalid value cannot occur if only 1 bit is
615 			 * used, we check for it in a generic manner in case the
616 			 * number of bits is increased in the future.
617 			 */
618 			mlo_err_rl("Invalid NSTR Bitmap size %u", nstrbmsz);
619 			return QDF_STATUS_E_PROTO;
620 		}
621 		if (nstr_info) {
622 			nstr_info->nstr_lp_present = nstrlppresent;
623 			nstr_info->nstr_bmp_size = nstrbmsz;
624 			*is_nstrlp_present = true;
625 			nstr_info->link_id = link_id;
626 
627 			if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_1_OCTET) {
628 				nstr_info->nstr_lp_bitmap =
629 					*(uint8_t *)(subelempayload + nstrlpoffset);
630 			} else if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_2_OCTETS) {
631 				nstr_info->nstr_lp_bitmap =
632 					qdf_le16_to_cpu(*(uint16_t *)(subelempayload + nstrlpoffset));
633 			}
634 		}
635 	}
636 
637 	/* Check BSS Parameters Change Count Present bit */
638 	if (QDF_GET_BITS(stacontrol,
639 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BSSPARAMCHNGCNTP_IDX,
640 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BSSPARAMCHNGCNTP_BITS)) {
641 		if (subelempayloadlen <
642 				(parsed_payload_len +
643 				 WLAN_ML_BSSPARAMCHNGCNT_SIZE)) {
644 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain BSS Parameters Change Count of size %u octets after parsed payload length of %zu octets.",
645 				   subelempayloadlen,
646 				   WLAN_ML_BSSPARAMCHNGCNT_SIZE,
647 				   parsed_payload_len);
648 			return QDF_STATUS_E_PROTO;
649 		}
650 
651 		parsed_payload_len += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
652 	}
653 
654 	/* Note: Some implementation versions of hostapd/wpa_supplicant may
655 	 * provide a per-STA profile without STA profile. Let the caller
656 	 * indicate whether a STA profile is required to be found. This may be
657 	 * revisited as upstreaming progresses.
658 	 */
659 	if (!is_staprof_reqd)
660 		return QDF_STATUS_SUCCESS;
661 
662 	if (subelempayloadlen == parsed_payload_len) {
663 		mlo_err_rl("Subelement payload length %zu == parsed payload length %zu. Unable to get STA profile.",
664 			   subelempayloadlen,
665 			   parsed_payload_len);
666 		return QDF_STATUS_E_PROTO;
667 	}
668 
669 	if (staprof_len)
670 		*staprof_len = subelempayloadlen - parsed_payload_len;
671 
672 	if (staprof)
673 		*staprof = subelempayload + parsed_payload_len;
674 
675 	return QDF_STATUS_SUCCESS;
676 }
677 
678 static QDF_STATUS
679 util_parse_prvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
680 					 qdf_size_t subelempayloadlen,
681 					 uint8_t *linkid,
682 					 bool is_staprof_reqd,
683 					 uint8_t **staprof,
684 					 qdf_size_t *staprof_len)
685 {
686 	qdf_size_t parsed_payload_len = 0;
687 	uint16_t stacontrol;
688 	uint8_t completeprofile;
689 
690 	/* This helper returns the location(s) and where required, the length(s)
691 	 * of (sub)field(s) inferable after parsing the STA Control field in the
692 	 * per-STA profile subelement. These location(s) and length(s) is/are in
693 	 * reference to the payload section of the per-STA profile subelement
694 	 * (after defragmentation, if applicable).  Here, the payload is the
695 	 * point after the subelement length in the subelement, and includes the
696 	 * payloads of all subsequent fragments (if any) but not the headers of
697 	 * those fragments.
698 	 *
699 	 * Currently, the helper returns the link ID, MAC address, and STA
700 	 * profile. More (sub)fields can be added when required.
701 	 */
702 
703 	if (!subelempayload) {
704 		mlo_err("Pointer to subelement payload is NULL");
705 		return QDF_STATUS_E_NULL_VALUE;
706 	}
707 
708 	if (!subelempayloadlen) {
709 		mlo_err("Length of subelement payload is zero");
710 		return QDF_STATUS_E_INVAL;
711 	}
712 
713 	if (subelempayloadlen < WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE) {
714 		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
715 			   subelempayloadlen,
716 			   WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE);
717 		return QDF_STATUS_E_PROTO;
718 	}
719 
720 	parsed_payload_len = 0;
721 
722 	qdf_mem_copy(&stacontrol,
723 		     subelempayload,
724 		     WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE);
725 	stacontrol = qdf_le16_to_cpu(stacontrol);
726 	parsed_payload_len += WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE;
727 
728 	if (linkid) {
729 		*linkid = QDF_GET_BITS(stacontrol,
730 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
731 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
732 	}
733 
734 	/* Check if this a complete profile */
735 	completeprofile = QDF_GET_BITS(stacontrol,
736 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
737 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
738 
739 	/* Note: Some implementation versions of hostapd/wpa_supplicant may
740 	 * provide a per-STA profile without STA profile. Let the caller
741 	 * indicate whether a STA profile is required to be found. This may be
742 	 * revisited as upstreaming progresses.
743 	 */
744 	if (!is_staprof_reqd)
745 		return QDF_STATUS_SUCCESS;
746 
747 	if (subelempayloadlen == parsed_payload_len) {
748 		mlo_err_rl("Subelement payload length %zu == parsed payload length %zu. Unable to get STA profile.",
749 			   subelempayloadlen,
750 			   parsed_payload_len);
751 		return QDF_STATUS_E_PROTO;
752 	}
753 
754 	if (staprof_len)
755 		*staprof_len = subelempayloadlen - parsed_payload_len;
756 
757 	if (staprof)
758 		*staprof = subelempayload + parsed_payload_len;
759 
760 	return QDF_STATUS_SUCCESS;
761 }
762 
763 static
764 uint8_t *util_get_successorfrag(uint8_t *currie, uint8_t *frame, qdf_size_t len)
765 {
766 	uint8_t *nextie;
767 
768 	if (!currie || !frame || !len)
769 		return NULL;
770 
771 	if ((currie + MIN_IE_LEN) > (frame + len))
772 		return NULL;
773 
774 	/* Check whether there is sufficient space in the frame for the current
775 	 * IE, plus at least another MIN_IE_LEN bytes for the IE header of a
776 	 * fragment (if present) that would come just after the current IE.
777 	 */
778 	if ((currie + MIN_IE_LEN + currie[TAG_LEN_POS] + MIN_IE_LEN) >
779 			(frame + len))
780 		return NULL;
781 
782 	nextie = currie + currie[TAG_LEN_POS] + MIN_IE_LEN;
783 
784 	/* Check whether there is sufficient space in the frame for the next IE
785 	 */
786 	if ((nextie + MIN_IE_LEN + nextie[TAG_LEN_POS]) > (frame + len))
787 		return NULL;
788 
789 	if (nextie[ID_POS] != WLAN_ELEMID_FRAGMENT)
790 		return NULL;
791 
792 	return nextie;
793 }
794 
795 static
796 QDF_STATUS util_parse_partner_info_from_linkinfo(uint8_t *linkinfo,
797 						 qdf_size_t linkinfo_len,
798 						 struct mlo_partner_info *partner_info)
799 {
800 	uint8_t linkid;
801 	struct qdf_mac_addr macaddr;
802 	struct mlo_nstr_info nstr_info = {0};
803 	bool is_macaddr_valid;
804 	uint8_t *linkinfo_currpos;
805 	qdf_size_t linkinfo_remlen;
806 	bool is_subelemfragseq;
807 	uint8_t subelemid;
808 	qdf_size_t subelemseqtotallen;
809 	qdf_size_t subelemseqpayloadlen;
810 	qdf_size_t defragpayload_len;
811 	QDF_STATUS ret;
812 	bool is_nstrlp_present = false;
813 
814 	/* This helper function parses partner info from the per-STA profiles
815 	 * present (if any) in the Link Info field in the payload of a Multi
816 	 * Link element (after defragmentation if required). The caller should
817 	 * pass a copy of the payload so that inline defragmentation of
818 	 * subelements can be carried out if required. The subelement
819 	 * defragmentation (if applicable) in this Control Path helper is
820 	 * required for maintainability, accuracy and eliminating current and
821 	 * future per-field-access multi-level fragment boundary checks and
822 	 * adjustments, given the complex format of Multi Link elements. It is
823 	 * also most likely to be required mainly at the client side.
824 	 */
825 
826 	if (!linkinfo) {
827 		mlo_err("linkinfo is NULL");
828 		return QDF_STATUS_E_NULL_VALUE;
829 	}
830 
831 	if (!linkinfo_len) {
832 		mlo_err("linkinfo_len is zero");
833 		return QDF_STATUS_E_NULL_VALUE;
834 	}
835 
836 	if (!partner_info) {
837 		mlo_err("ML partner info is NULL");
838 		return QDF_STATUS_E_NULL_VALUE;
839 	}
840 
841 	partner_info->num_partner_links = 0;
842 	linkinfo_currpos = linkinfo;
843 	linkinfo_remlen = linkinfo_len;
844 
845 	while (linkinfo_remlen) {
846 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
847 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
848 				   linkinfo_remlen,
849 				   sizeof(struct subelem_header));
850 			return QDF_STATUS_E_PROTO;
851 		}
852 
853 		subelemid = linkinfo_currpos[ID_POS];
854 		is_subelemfragseq = false;
855 		subelemseqtotallen = 0;
856 		subelemseqpayloadlen = 0;
857 
858 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
859 						    linkinfo_currpos,
860 						    linkinfo_remlen,
861 						    &is_subelemfragseq,
862 						    &subelemseqtotallen,
863 						    &subelemseqpayloadlen);
864 		if (QDF_IS_STATUS_ERROR(ret))
865 			return ret;
866 
867 		if (is_subelemfragseq) {
868 			if (!subelemseqpayloadlen) {
869 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
870 				return QDF_STATUS_E_FAILURE;
871 			}
872 
873 			mlo_debug("Subelement fragment sequence found with payload len %zu",
874 				  subelemseqpayloadlen);
875 
876 			ret = wlan_defrag_subelem_fragseq(true,
877 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
878 							  linkinfo_currpos,
879 							  linkinfo_remlen,
880 							  NULL,
881 							  0,
882 							  &defragpayload_len);
883 			if (QDF_IS_STATUS_ERROR(ret))
884 				return ret;
885 
886 			if (defragpayload_len != subelemseqpayloadlen) {
887 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
888 					   defragpayload_len,
889 					   subelemseqpayloadlen);
890 				return QDF_STATUS_E_FAILURE;
891 			}
892 
893 			/* Adjust linkinfo_remlen to reflect removal of all
894 			 * subelement headers except the header of the lead
895 			 * subelement.
896 			 */
897 			linkinfo_remlen -= (subelemseqtotallen -
898 					    subelemseqpayloadlen -
899 					    sizeof(struct subelem_header));
900 		} else {
901 			if (linkinfo_remlen <
902 				(sizeof(struct subelem_header) +
903 				 linkinfo_currpos[TAG_LEN_POS])) {
904 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
905 					   linkinfo_remlen,
906 					   sizeof(struct subelem_header) +
907 					   linkinfo_currpos[TAG_LEN_POS]);
908 				return QDF_STATUS_E_PROTO;
909 			}
910 
911 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
912 		}
913 
914 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
915 			is_macaddr_valid = false;
916 
917 			ret = util_parse_bvmlie_perstaprofile_stactrl(linkinfo_currpos +
918 								      sizeof(struct subelem_header),
919 								      subelemseqpayloadlen,
920 								      &linkid,
921 								      NULL,
922 								      NULL,
923 								      NULL,
924 								      NULL,
925 								      NULL,
926 								      &is_macaddr_valid,
927 								      &macaddr,
928 								      false,
929 								      NULL,
930 								      NULL,
931 								      &nstr_info,
932 								      &is_nstrlp_present);
933 			if (QDF_IS_STATUS_ERROR(ret)) {
934 				return ret;
935 			}
936 
937 			if (is_nstrlp_present) {
938 				if (partner_info->num_nstr_info_links >=
939 					QDF_ARRAY_SIZE(partner_info->nstr_info)) {
940 					mlo_err_rl("Insufficient size %zu of array for nstr link info",
941 						   QDF_ARRAY_SIZE(partner_info->nstr_info));
942 					return QDF_STATUS_E_NOMEM;
943 				}
944 				qdf_mem_copy(&partner_info->nstr_info[partner_info->num_nstr_info_links],
945 					     &nstr_info, sizeof(nstr_info));
946 				partner_info->num_nstr_info_links++;
947 				is_nstrlp_present = false;
948 			}
949 
950 			if (is_macaddr_valid) {
951 				if (partner_info->num_partner_links >=
952 					QDF_ARRAY_SIZE(partner_info->partner_link_info)) {
953 					mlo_err_rl("Insufficient size %zu of array for partner link info",
954 						   QDF_ARRAY_SIZE(partner_info->partner_link_info));
955 					return QDF_STATUS_E_NOMEM;
956 				}
957 
958 				partner_info->partner_link_info[partner_info->num_partner_links].link_id =
959 					linkid;
960 				qdf_mem_copy(&partner_info->partner_link_info[partner_info->num_partner_links].link_addr,
961 					     &macaddr,
962 					     sizeof(partner_info->partner_link_info[partner_info->num_partner_links].link_addr));
963 
964 				partner_info->num_partner_links++;
965 			} else {
966 				mlo_warn_rl("MAC address not found in STA Info field of per-STA profile with link ID %u",
967 					    linkid);
968 			}
969 		}
970 
971 		linkinfo_remlen -= (sizeof(struct subelem_header) +
972 				    subelemseqpayloadlen);
973 		linkinfo_currpos += (sizeof(struct subelem_header) +
974 				     subelemseqpayloadlen);
975 	}
976 
977 	mlo_debug("Number of ML partner links found=%u",
978 		  partner_info->num_partner_links);
979 
980 	return QDF_STATUS_SUCCESS;
981 }
982 
983 static QDF_STATUS
984 util_parse_probereq_info_from_linkinfo(uint8_t *linkinfo,
985 				       qdf_size_t linkinfo_len,
986 				       struct mlo_probereq_info *probereq_info)
987 {
988 	uint8_t linkid;
989 	uint8_t *linkinfo_currpos;
990 	qdf_size_t linkinfo_remlen;
991 	bool is_subelemfragseq;
992 	uint8_t subelemid;
993 	qdf_size_t subelemseqtotallen;
994 	qdf_size_t subelemseqpayloadlen;
995 	qdf_size_t defragpayload_len;
996 	QDF_STATUS ret;
997 
998 	/* This helper function parses probe request info from the per-STA prof
999 	 * present (if any) in the Link Info field in the payload of a Multi
1000 	 * Link element (after defragmentation if required). The caller should
1001 	 * pass a copy of the payload so that inline defragmentation of
1002 	 * subelements can be carried out if required. The subelement
1003 	 * defragmentation (if applicable) in this Control Path helper is
1004 	 * required for maintainability, accuracy and eliminating current and
1005 	 * future per-field-access multi-level fragment boundary checks and
1006 	 * adjustments, given the complex format of Multi Link elements. It is
1007 	 * also most likely to be required mainly at the client side.
1008 	 */
1009 
1010 	if (!linkinfo) {
1011 		mlo_err("linkinfo is NULL");
1012 		return QDF_STATUS_E_NULL_VALUE;
1013 	}
1014 
1015 	if (!linkinfo_len) {
1016 		mlo_err("linkinfo_len is zero");
1017 		return QDF_STATUS_E_NULL_VALUE;
1018 	}
1019 
1020 	if (!probereq_info) {
1021 		mlo_err("ML probe req info is NULL");
1022 		return QDF_STATUS_E_NULL_VALUE;
1023 	}
1024 
1025 	probereq_info->num_links = 0;
1026 	linkinfo_currpos = linkinfo;
1027 	linkinfo_remlen = linkinfo_len;
1028 
1029 	while (linkinfo_remlen) {
1030 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
1031 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
1032 				   linkinfo_remlen,
1033 				   sizeof(struct subelem_header));
1034 			return QDF_STATUS_E_PROTO;
1035 		}
1036 
1037 		subelemid = linkinfo_currpos[ID_POS];
1038 		is_subelemfragseq = false;
1039 		subelemseqtotallen = 0;
1040 		subelemseqpayloadlen = 0;
1041 
1042 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1043 						    linkinfo_currpos,
1044 						    linkinfo_remlen,
1045 						    &is_subelemfragseq,
1046 						    &subelemseqtotallen,
1047 						    &subelemseqpayloadlen);
1048 		if (QDF_IS_STATUS_ERROR(ret))
1049 			return ret;
1050 
1051 		if (is_subelemfragseq) {
1052 			if (!subelemseqpayloadlen) {
1053 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
1054 				return QDF_STATUS_E_FAILURE;
1055 			}
1056 
1057 			mlo_debug("Subelement fragment sequence found with payload len %zu",
1058 				  subelemseqpayloadlen);
1059 
1060 			ret = wlan_defrag_subelem_fragseq(true,
1061 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1062 							  linkinfo_currpos,
1063 							  linkinfo_remlen,
1064 							  NULL,
1065 							  0,
1066 							  &defragpayload_len);
1067 			if (QDF_IS_STATUS_ERROR(ret))
1068 				return ret;
1069 
1070 			if (defragpayload_len != subelemseqpayloadlen) {
1071 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
1072 					   defragpayload_len,
1073 					   subelemseqpayloadlen);
1074 				return QDF_STATUS_E_FAILURE;
1075 			}
1076 
1077 			/* Adjust linkinfo_remlen to reflect removal of all
1078 			 * subelement headers except the header of the lead
1079 			 * subelement.
1080 			 */
1081 			linkinfo_remlen -= (subelemseqtotallen -
1082 					    subelemseqpayloadlen -
1083 					    sizeof(struct subelem_header));
1084 		} else {
1085 			if (linkinfo_remlen <
1086 				(sizeof(struct subelem_header) +
1087 				 linkinfo_currpos[TAG_LEN_POS])) {
1088 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
1089 					   linkinfo_remlen,
1090 					   sizeof(struct subelem_header) +
1091 					   linkinfo_currpos[TAG_LEN_POS]);
1092 				return QDF_STATUS_E_PROTO;
1093 			}
1094 
1095 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
1096 		}
1097 
1098 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
1099 			ret = util_parse_prvmlie_perstaprofile_stactrl(linkinfo_currpos +
1100 								      sizeof(struct subelem_header),
1101 								      subelemseqpayloadlen,
1102 								      &linkid,
1103 								      false,
1104 								      NULL,
1105 								      NULL);
1106 			if (QDF_IS_STATUS_ERROR(ret))
1107 				return ret;
1108 
1109 			if (probereq_info->num_links >=
1110 				QDF_ARRAY_SIZE(probereq_info->link_id)) {
1111 				mlo_err_rl("Insufficient size %zu of array for probe req link id",
1112 					   QDF_ARRAY_SIZE(probereq_info->link_id));
1113 				return QDF_STATUS_E_NOMEM;
1114 			}
1115 
1116 			probereq_info->link_id[probereq_info->num_links] = linkid;
1117 
1118 			probereq_info->num_links++;
1119 			mlo_debug("LINK ID requested is = %u", linkid);
1120 		}
1121 
1122 		linkinfo_remlen -= (sizeof(struct subelem_header) +
1123 				    subelemseqpayloadlen);
1124 		linkinfo_currpos += (sizeof(struct subelem_header) +
1125 				     subelemseqpayloadlen);
1126 	}
1127 
1128 	mlo_debug("Number of ML probe request links found=%u",
1129 		  probereq_info->num_links);
1130 
1131 	return QDF_STATUS_SUCCESS;
1132 }
1133 
1134 static
1135 QDF_STATUS util_get_noninheritlists(uint8_t *buff, qdf_size_t buff_len,
1136 				    uint8_t **ninherit_elemlist,
1137 				    qdf_size_t *ninherit_elemlist_len,
1138 				    uint8_t **ninherit_elemextlist,
1139 				    qdf_size_t *ninherit_elemextlist_len)
1140 {
1141 	uint8_t *ninherit_ie;
1142 	qdf_size_t unparsed_len;
1143 
1144 	/* Note: This functionality provided by this helper may be combined with
1145 	 * other, older non-inheritance parsing helper functionality and exposed
1146 	 * as a common API as part of future efforts once the older
1147 	 * functionality can be made generic.
1148 	 */
1149 
1150 	if (!buff) {
1151 		mlo_err("Pointer to buffer for IEs is NULL");
1152 		return QDF_STATUS_E_NULL_VALUE;
1153 	}
1154 
1155 	if (!buff_len) {
1156 		mlo_err("IE buffer length is zero");
1157 		return QDF_STATUS_E_INVAL;
1158 	}
1159 
1160 	if (!ninherit_elemlist) {
1161 		mlo_err("Pointer to Non-Inheritance element ID list array is NULL");
1162 		return QDF_STATUS_E_NULL_VALUE;
1163 	}
1164 
1165 	if (!ninherit_elemlist_len) {
1166 		mlo_err("Pointer to Non-Inheritance element ID list array length is NULL");
1167 		return QDF_STATUS_E_NULL_VALUE;
1168 	}
1169 
1170 	if (!ninherit_elemextlist) {
1171 		mlo_err("Pointer to Non-Inheritance element ID extension list array is NULL");
1172 		return QDF_STATUS_E_NULL_VALUE;
1173 	}
1174 
1175 	if (!ninherit_elemextlist_len) {
1176 		mlo_err("Pointer to Non-Inheritance element ID extension list array length is NULL");
1177 		return QDF_STATUS_E_NULL_VALUE;
1178 	}
1179 
1180 	ninherit_ie = NULL;
1181 	*ninherit_elemlist_len = 0;
1182 	*ninherit_elemlist = NULL;
1183 	*ninherit_elemextlist_len = 0;
1184 	*ninherit_elemextlist = NULL;
1185 
1186 	ninherit_ie =
1187 		(uint8_t *)util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
1188 					      WLAN_EXTN_ELEMID_NONINHERITANCE,
1189 					      buff,
1190 					      buff_len);
1191 
1192 	if (ninherit_ie) {
1193 		if ((ninherit_ie + TAG_LEN_POS) > (buff + buff_len - 1)) {
1194 			mlo_err_rl("Position of length field of Non-Inheritance element would exceed IE buffer boundary");
1195 			return QDF_STATUS_E_PROTO;
1196 		}
1197 
1198 		if ((ninherit_ie + ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) >
1199 				(buff + buff_len)) {
1200 			mlo_err_rl("Non-Inheritance element with total length %u would exceed IE buffer boundary",
1201 				   ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN);
1202 			return QDF_STATUS_E_PROTO;
1203 		}
1204 
1205 		if ((ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) <
1206 				MIN_NONINHERITANCEELEM_LEN) {
1207 			mlo_err_rl("Non-Inheritance element size %u is smaller than the minimum required %u",
1208 				   ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN,
1209 				   MIN_NONINHERITANCEELEM_LEN);
1210 			return QDF_STATUS_E_PROTO;
1211 		}
1212 
1213 		/* Track the number of unparsed octets, excluding the IE header.
1214 		 */
1215 		unparsed_len = ninherit_ie[TAG_LEN_POS];
1216 
1217 		/* Mark the element ID extension as parsed */
1218 		unparsed_len--;
1219 
1220 		*ninherit_elemlist_len = ninherit_ie[ELEM_ID_LIST_LEN_POS];
1221 		unparsed_len--;
1222 
1223 		/* While checking if the Non-Inheritance element ID list length
1224 		 * exceeds the remaining unparsed IE space, we factor in one
1225 		 * octet for the element extension ID list length and subtract
1226 		 * this from the unparsed IE space.
1227 		 */
1228 		if (*ninherit_elemlist_len > (unparsed_len - 1)) {
1229 			mlo_err_rl("Non-Inheritance element ID list length %zu exceeds remaining unparsed IE space, minus an octet for element extension ID list length %zu",
1230 				   *ninherit_elemlist_len, unparsed_len - 1);
1231 
1232 			return QDF_STATUS_E_PROTO;
1233 		}
1234 
1235 		if (*ninherit_elemlist_len != 0) {
1236 			*ninherit_elemlist = ninherit_ie + ELEM_ID_LIST_POS;
1237 			unparsed_len -= *ninherit_elemlist_len;
1238 		}
1239 
1240 		*ninherit_elemextlist_len =
1241 			ninherit_ie[ELEM_ID_LIST_LEN_POS + *ninherit_elemlist_len + 1];
1242 		unparsed_len--;
1243 
1244 		if (*ninherit_elemextlist_len > unparsed_len) {
1245 			mlo_err_rl("Non-Inheritance element ID extension list length %zu exceeds remaining unparsed IE space %zu",
1246 				   *ninherit_elemextlist_len, unparsed_len);
1247 
1248 			return QDF_STATUS_E_PROTO;
1249 		}
1250 
1251 		if (*ninherit_elemextlist_len != 0) {
1252 			*ninherit_elemextlist = ninherit_ie +
1253 				ELEM_ID_LIST_LEN_POS + (*ninherit_elemlist_len)
1254 				+ 2;
1255 			unparsed_len -= *ninherit_elemextlist_len;
1256 		}
1257 
1258 		if (unparsed_len > 0) {
1259 			mlo_err_rl("Unparsed length is %zu, expected 0",
1260 				   unparsed_len);
1261 			return QDF_STATUS_E_PROTO;
1262 		}
1263 	}
1264 
1265 	/* If Non-Inheritance element is not found, we still return success,
1266 	 * with the list lengths kept at zero.
1267 	 */
1268 	mlo_debug("Non-Inheritance element ID list array length=%zu",
1269 		  *ninherit_elemlist_len);
1270 	mlo_debug("Non-Inheritance element ID extension list array length=%zu",
1271 		  *ninherit_elemextlist_len);
1272 
1273 	return QDF_STATUS_SUCCESS;
1274 }
1275 
1276 static
1277 QDF_STATUS util_eval_ie_in_noninheritlist(uint8_t *ie, qdf_size_t total_ie_len,
1278 					  uint8_t *ninherit_elemlist,
1279 					  qdf_size_t ninherit_elemlist_len,
1280 					  uint8_t *ninherit_elemextlist,
1281 					  qdf_size_t ninherit_elemextlist_len,
1282 					  bool *is_in_noninheritlist)
1283 {
1284 	int i;
1285 
1286 	/* Evaluate whether the given IE is in the given Non-Inheritance element
1287 	 * ID list or Non-Inheritance element ID extension list, and update the
1288 	 * result into is_in_noninheritlist. If any list is empty, then the IE
1289 	 * is considered to not be present in that list. Both lists can be
1290 	 * empty.
1291 	 *
1292 	 * If QDF_STATUS_SUCCESS is returned, it means that the evaluation is
1293 	 * successful, and that is_in_noninheritlist contains a valid value
1294 	 * (which could be true or false). If a QDF_STATUS error value is
1295 	 * returned, the value in is_in_noninheritlist is invalid and the caller
1296 	 * should ignore it.
1297 	 */
1298 
1299 	/* Note: The functionality provided by this helper may be combined with
1300 	 * other, older non-inheritance parsing helper functionality and exposed
1301 	 * as a common API as part of future efforts once the older
1302 	 * functionality can be made generic.
1303 	 */
1304 
1305 	/* Except for is_in_noninheritlist and ie, other pointer arguments are
1306 	 * permitted to be NULL if they are inapplicable. If they are
1307 	 * applicable, they will be checked to ensure they are not NULL.
1308 	 */
1309 
1310 	if (!is_in_noninheritlist) {
1311 		mlo_err("NULL pointer to flag that indicates if element is in a Non-Inheritance list");
1312 		return QDF_STATUS_E_NULL_VALUE;
1313 	}
1314 
1315 	/* If ninherit_elemlist_len and ninherit_elemextlist_len are both zero
1316 	 * as checked soon in this function, we won't be accessing the IE.
1317 	 * However, we still check right-away if the pointer to the IE is
1318 	 * non-NULL and whether the total IE length is sane enough to access the
1319 	 * element ID and if applicable, the element ID extension, since it
1320 	 * doesn't make sense to set the flag in is_in_noninheritlist for a NULL
1321 	 * IE pointer or an IE whose total length is not sane enough to
1322 	 * distinguish the identity of the IE.
1323 	 */
1324 	if (!ie) {
1325 		mlo_err("NULL pointer to IE");
1326 		return QDF_STATUS_E_NULL_VALUE;
1327 	}
1328 
1329 	if (total_ie_len < (ID_POS + 1)) {
1330 		mlo_err("Total IE length %zu is smaller than minimum required to access element ID %u",
1331 			total_ie_len, ID_POS + 1);
1332 		return QDF_STATUS_E_INVAL;
1333 	}
1334 
1335 	if ((ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1336 	    (total_ie_len < (IDEXT_POS + 1))) {
1337 		mlo_err("Total IE length %zu is smaller than minimum required to access element ID extension %u",
1338 			total_ie_len, IDEXT_POS + 1);
1339 		return QDF_STATUS_E_INVAL;
1340 	}
1341 
1342 	*is_in_noninheritlist = false;
1343 
1344 	/* If both the Non-Inheritance element list and Non-Inheritance element
1345 	 * ID extension list are empty, then return success since we can
1346 	 * conclude immediately that the given element does not occur in any
1347 	 * Non-Inheritance list. The is_in_noninheritlist remains set to false
1348 	 * as required.
1349 	 */
1350 	if (!ninherit_elemlist_len && !ninherit_elemextlist_len)
1351 		return QDF_STATUS_SUCCESS;
1352 
1353 	if (ie[ID_POS] != WLAN_ELEMID_EXTN_ELEM) {
1354 		if (!ninherit_elemlist_len)
1355 			return QDF_STATUS_SUCCESS;
1356 
1357 		if (!ninherit_elemlist) {
1358 			mlo_err("NULL pointer to Non-Inheritance element ID list though length of element ID list is %zu",
1359 				ninherit_elemlist_len);
1360 			return QDF_STATUS_E_NULL_VALUE;
1361 		}
1362 
1363 		for (i = 0; i < ninherit_elemlist_len; i++) {
1364 			if (ie[ID_POS] == ninherit_elemlist[i]) {
1365 				*is_in_noninheritlist = true;
1366 				return QDF_STATUS_SUCCESS;
1367 			}
1368 		}
1369 	} else {
1370 		if (!ninherit_elemextlist_len)
1371 			return QDF_STATUS_SUCCESS;
1372 
1373 		if (!ninherit_elemextlist) {
1374 			mlo_err("NULL pointer to Non-Inheritance element ID extension list though length of element ID extension list is %zu",
1375 				ninherit_elemextlist_len);
1376 			return QDF_STATUS_E_NULL_VALUE;
1377 		}
1378 
1379 		for (i = 0; i < ninherit_elemextlist_len; i++) {
1380 			if (ie[IDEXT_POS] == ninherit_elemextlist[i]) {
1381 				*is_in_noninheritlist = true;
1382 				return QDF_STATUS_SUCCESS;
1383 			}
1384 		}
1385 	}
1386 
1387 	return QDF_STATUS_SUCCESS;
1388 }
1389 
1390 static inline
1391 QDF_STATUS util_validate_reportingsta_ie(const uint8_t *reportingsta_ie,
1392 					 const uint8_t *frame_iesection,
1393 					 const qdf_size_t frame_iesection_len)
1394 {
1395 	qdf_size_t reportingsta_ie_size;
1396 
1397 	if (!reportingsta_ie) {
1398 		mlo_err("Pointer to reporting STA IE is NULL");
1399 		return QDF_STATUS_E_NULL_VALUE;
1400 	}
1401 
1402 	if (!frame_iesection) {
1403 		mlo_err("Pointer to start of IE section in reporting frame is NULL");
1404 		return QDF_STATUS_E_NULL_VALUE;
1405 	}
1406 
1407 	if (!frame_iesection_len) {
1408 		mlo_err("Length of IE section in reporting frame is zero");
1409 		return QDF_STATUS_E_INVAL;
1410 	}
1411 
1412 	if ((reportingsta_ie + ID_POS) > (frame_iesection +
1413 			frame_iesection_len - 1)) {
1414 		mlo_err_rl("Position of element ID field of element for reporting STA would exceed frame IE section boundary");
1415 		return QDF_STATUS_E_PROTO;
1416 	}
1417 
1418 	if ((reportingsta_ie + TAG_LEN_POS) > (frame_iesection +
1419 			frame_iesection_len - 1)) {
1420 		mlo_err_rl("Position of length field of element with element ID %u for reporting STA would exceed frame IE section boundary",
1421 			   reportingsta_ie[ID_POS]);
1422 		return QDF_STATUS_E_PROTO;
1423 	}
1424 
1425 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1426 	    ((reportingsta_ie + IDEXT_POS) > (frame_iesection +
1427 				frame_iesection_len - 1))) {
1428 		mlo_err_rl("Position of element ID extension field of element would exceed frame IE section boundary");
1429 		return QDF_STATUS_E_PROTO;
1430 	}
1431 
1432 	reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
1433 
1434 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1435 	    (reportingsta_ie_size < (IDEXT_POS + 1))) {
1436 		mlo_err_rl("Total length %zu of element for reporting STA is smaller than minimum required to access element ID extension %u",
1437 			   reportingsta_ie_size, IDEXT_POS + 1);
1438 		return QDF_STATUS_E_PROTO;
1439 	}
1440 
1441 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
1442 	    (reportingsta_ie_size < (PAYLOAD_START_POS + OUI_LEN))) {
1443 		mlo_err_rl("Total length %zu of element for reporting STA is smaller than minimum required to access vendor EID %u",
1444 			   reportingsta_ie_size, PAYLOAD_START_POS + OUI_LEN);
1445 		return QDF_STATUS_E_PROTO;
1446 	}
1447 
1448 	if ((reportingsta_ie + reportingsta_ie_size) >
1449 			(frame_iesection + frame_iesection_len)) {
1450 		if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
1451 			mlo_err_rl("Total size %zu octets of element with element ID %u element ID extension %u for reporting STA would exceed frame IE section boundary",
1452 				   reportingsta_ie_size,
1453 				   reportingsta_ie[ID_POS],
1454 				   reportingsta_ie[IDEXT_POS]);
1455 		} else {
1456 			mlo_err_rl("Total size %zu octets of element with element ID %u for reporting STA would exceed frame IE section boundary",
1457 				   reportingsta_ie_size,
1458 				   reportingsta_ie[ID_POS]);
1459 		}
1460 
1461 		return QDF_STATUS_E_PROTO;
1462 	}
1463 
1464 	return QDF_STATUS_SUCCESS;
1465 }
1466 
1467 static inline
1468 QDF_STATUS util_validate_sta_prof_ie(const uint8_t *sta_prof_ie,
1469 				     const uint8_t *sta_prof_iesection,
1470 				     const qdf_size_t sta_prof_iesection_len)
1471 {
1472 	qdf_size_t sta_prof_ie_size;
1473 
1474 	if (!sta_prof_ie) {
1475 		mlo_err("Pointer to STA profile IE is NULL");
1476 		return QDF_STATUS_E_NULL_VALUE;
1477 	}
1478 
1479 	if (!sta_prof_iesection) {
1480 		mlo_err("Pointer to start of IE section in STA profile is NULL");
1481 		return QDF_STATUS_E_NULL_VALUE;
1482 	}
1483 
1484 	if (!sta_prof_iesection_len) {
1485 		mlo_err("Length of IE section in STA profile is zero");
1486 		return QDF_STATUS_E_INVAL;
1487 	}
1488 
1489 	if ((sta_prof_ie + ID_POS) > (sta_prof_iesection +
1490 			sta_prof_iesection_len - 1)) {
1491 		mlo_err_rl("Position of element ID field of STA profile element would exceed STA profile IE section boundary");
1492 		return QDF_STATUS_E_PROTO;
1493 	}
1494 
1495 	if ((sta_prof_ie + TAG_LEN_POS) > (sta_prof_iesection +
1496 			sta_prof_iesection_len - 1)) {
1497 		mlo_err_rl("Position of length field of element with element ID %u in STA profile would exceed STA profile IE section boundary",
1498 			   sta_prof_ie[ID_POS]);
1499 		return QDF_STATUS_E_PROTO;
1500 	}
1501 
1502 	if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1503 	    ((sta_prof_ie + IDEXT_POS) > (sta_prof_iesection +
1504 				sta_prof_iesection_len - 1))) {
1505 		mlo_err_rl("Position of element ID extension field of element would exceed STA profile IE section boundary");
1506 		return QDF_STATUS_E_PROTO;
1507 	}
1508 
1509 	sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
1510 
1511 	if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1512 	    (sta_prof_ie_size < (IDEXT_POS + 1))) {
1513 		mlo_err_rl("Total length %zu of STA profile element is smaller than minimum required to access element ID extension %u",
1514 			   sta_prof_ie_size, IDEXT_POS + 1);
1515 		return QDF_STATUS_E_PROTO;
1516 	}
1517 
1518 	if ((sta_prof_ie + sta_prof_ie_size) >
1519 			(sta_prof_iesection + sta_prof_iesection_len)) {
1520 		if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
1521 			mlo_err_rl("Total size %zu octets of element with element ID %u element ID extension %u in STA profile would exceed STA profile IE section boundary",
1522 				   sta_prof_ie_size,
1523 				   sta_prof_ie[ID_POS],
1524 				   sta_prof_ie[IDEXT_POS]);
1525 		} else {
1526 			mlo_err_rl("Total size %zu octets of element with element ID %u in STA profile would exceed STA profile IE section boundary",
1527 				   sta_prof_ie_size,
1528 				   sta_prof_ie[ID_POS]);
1529 		}
1530 
1531 		return QDF_STATUS_E_PROTO;
1532 	}
1533 
1534 	return QDF_STATUS_SUCCESS;
1535 }
1536 
1537 #ifdef CONN_MGR_ADV_FEATURE
1538 /**
1539  * util_add_mlie_for_prb_rsp_gen - Add the basic variant Multi-Link element
1540  * when generating link specific probe response.
1541  * @reportingsta_ie: Pointer to the reportingsta ie
1542  * @reportingsta_ie_len: Length for reporting sta ie
1543  * @plink_frame_currpos: Pointer to Link frame current pos
1544  * @plink_frame_currlen: Current length of link frame.
1545  * @link_frame_maxsize: Maximum size of the frame to be generated
1546  * @linkid: Link Id value
1547  *
1548  * Add the basic variant Multi-Link element when
1549  * generating link specific probe response.
1550  *
1551  * Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
1552  * the reason for error in the case of failure
1553  */
1554 static QDF_STATUS
1555 util_add_mlie_for_prb_rsp_gen(const uint8_t *reportingsta_ie,
1556 			      qdf_size_t reportingsta_ie_len,
1557 			      uint8_t **plink_frame_currpos,
1558 			      qdf_size_t *plink_frame_currlen,
1559 			      qdf_size_t link_frame_maxsize,
1560 			      uint8_t linkid)
1561 {
1562 	uint8_t mlie_len = 0;
1563 	uint8_t common_info_len = 0;
1564 	struct wlan_ie_multilink ml_ie_ff;
1565 	uint16_t mlcontrol;
1566 	uint16_t presencebm;
1567 	uint8_t *mlie_frame = NULL;
1568 	uint8_t link_id_offset = sizeof(struct wlan_ie_multilink) +
1569 				QDF_MAC_ADDR_SIZE +
1570 				WLAN_ML_BV_CINFO_LENGTH_SIZE;
1571 	uint8_t *link_frame_currpos = *plink_frame_currpos;
1572 	qdf_size_t link_frame_currlen = *plink_frame_currlen;
1573 	QDF_STATUS status = QDF_STATUS_SUCCESS;
1574 
1575 	status = util_get_mlie_common_info_len((uint8_t *)reportingsta_ie,
1576 					       reportingsta_ie_len,
1577 					       &common_info_len);
1578 	if (QDF_IS_STATUS_ERROR(status)) {
1579 		mlo_err("Failed while parsing the common info length");
1580 		return status;
1581 	}
1582 
1583 	/* common info len + bvmlie fixed fields */
1584 	mlie_len = common_info_len + sizeof(struct wlan_ie_multilink);
1585 
1586 	mlo_debug_rl("mlie_len %d, common_info_len %d, link_id_offset %d",
1587 		     mlie_len,
1588 		     common_info_len,
1589 		     link_id_offset);
1590 
1591 	/*
1592 	 * Validate the buffer available before copying ML IE.
1593 	 * Incase if mlie_len is modified at later place, move this validation
1594 	 * there to make sure no buffer overflow happens.
1595 	 */
1596 	if ((link_frame_maxsize - link_frame_currlen) < mlie_len) {
1597 		mlo_err("Insufficient space in link specific frame for ML IE. Required: %u octets, available: %zu octets",
1598 			mlie_len, (link_frame_maxsize - link_frame_currlen));
1599 		return QDF_STATUS_E_NOMEM;
1600 	}
1601 
1602 	mlie_frame = qdf_mem_malloc(mlie_len);
1603 	if (!mlie_frame)
1604 		return QDF_STATUS_E_NOMEM;
1605 
1606 	/* Copy ml ie fixed fields */
1607 	qdf_mem_copy(&ml_ie_ff,
1608 		     reportingsta_ie,
1609 		     sizeof(struct wlan_ie_multilink));
1610 
1611 	ml_ie_ff.elem_len = mlie_len - sizeof(struct ie_header);
1612 
1613 	mlcontrol = qdf_le16_to_cpu(ml_ie_ff.mlcontrol);
1614 	presencebm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
1615 				  WLAN_ML_CTRL_PBM_BITS);
1616 	qdf_set_bit(WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P,
1617 		    (unsigned long *)&presencebm);
1618 
1619 	QDF_SET_BITS(ml_ie_ff.mlcontrol,
1620 		     WLAN_ML_CTRL_PBM_IDX,
1621 		     WLAN_ML_CTRL_PBM_BITS,
1622 		     presencebm);
1623 
1624 	qdf_mem_copy(mlie_frame,
1625 		     &ml_ie_ff,
1626 		     sizeof(struct wlan_ie_multilink));
1627 
1628 	qdf_mem_copy(mlie_frame + sizeof(struct wlan_ie_multilink),
1629 		     reportingsta_ie + sizeof(struct wlan_ie_multilink),
1630 		     mlie_len - sizeof(struct wlan_ie_multilink));
1631 
1632 	if (linkid == 0xFF) {
1633 		qdf_mem_free(mlie_frame);
1634 		mlo_err("Link id is invalid");
1635 		return QDF_STATUS_E_INVAL;
1636 	}
1637 	mlie_frame[link_id_offset] = (mlie_frame[link_id_offset] & ~0x0f) |
1638 				   (linkid & 0x0f);
1639 	qdf_mem_copy(link_frame_currpos,
1640 		     mlie_frame,
1641 		     mlie_len);
1642 
1643 	mlo_debug("Add mlie for link id %d", linkid);
1644 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
1645 			   mlie_frame, mlie_len);
1646 
1647 	link_frame_currpos += mlie_len;
1648 	link_frame_currlen += mlie_len;
1649 	*plink_frame_currpos = link_frame_currpos;
1650 	*plink_frame_currlen = link_frame_currlen;
1651 	qdf_mem_free(mlie_frame);
1652 
1653 	return QDF_STATUS_SUCCESS;
1654 }
1655 #else
1656 static QDF_STATUS
1657 util_add_mlie_for_prb_rsp_gen(const uint8_t *reportingsta_ie,
1658 			      qdf_size_t reportingsta_ie_len,
1659 			      uint8_t **plink_frame_currpos,
1660 			      qdf_size_t *plink_frame_currlen,
1661 			      qdf_size_t link_frame_maxsize,
1662 			      uint8_t linkid)
1663 {
1664 	return QDF_STATUS_SUCCESS;
1665 }
1666 #endif
1667 
1668 /**
1669  * util_find_bvmlie_persta_prof_for_linkid() - get per sta profile per link id
1670  * @req_link_id: link id
1671  * @linkinfo: the pointer of link info
1672  * @linkinfo_len: the length of link info
1673  * @persta_prof_frame: the pointer to store the address of sta profile
1674  * @persta_prof_len: the sta profile length
1675  *
1676  * This helper function parses partner info from the per-STA profiles
1677  * present (if any) in the Link Info field in the payload of a Multi
1678  * Link element (after defragmentation if required). The caller should
1679  * pass a copy of the payload so that inline defragmentation of
1680  * subelements can be carried out if required. The subelement
1681  * defragmentation (if applicable) in this Control Path helper is
1682  * required for maintainability, accuracy and eliminating current and
1683  * future per-field-access multi-level fragment boundary checks and
1684  * adjustments, given the complex format of Multi Link elements. It is
1685  * also most likely to be required mainly at the client side.
1686  *
1687  * Return: QDF_STATUS
1688  */
1689 static QDF_STATUS
1690 util_find_bvmlie_persta_prof_for_linkid(uint8_t req_link_id,
1691 					uint8_t *linkinfo,
1692 					qdf_size_t linkinfo_len,
1693 					uint8_t **persta_prof_frame,
1694 					qdf_size_t *persta_prof_len)
1695 {
1696 	uint8_t linkid;
1697 	struct qdf_mac_addr macaddr;
1698 	bool is_macaddr_valid;
1699 	uint8_t *linkinfo_currpos;
1700 	qdf_size_t linkinfo_remlen;
1701 	bool is_subelemfragseq;
1702 	uint8_t subelemid;
1703 	qdf_size_t subelemseqtotallen;
1704 	qdf_size_t subelemseqpayloadlen;
1705 	qdf_size_t defragpayload_len;
1706 	QDF_STATUS ret;
1707 
1708 	if (!linkinfo) {
1709 		mlo_err("linkinfo is NULL");
1710 		return QDF_STATUS_E_NULL_VALUE;
1711 	}
1712 
1713 	if (!linkinfo_len) {
1714 		mlo_err("linkinfo_len is zero");
1715 		return QDF_STATUS_E_NULL_VALUE;
1716 	}
1717 
1718 	if (!persta_prof_frame) {
1719 		mlo_err("Pointer to per-STA prof frame is NULL");
1720 		return QDF_STATUS_E_NULL_VALUE;
1721 	}
1722 
1723 	if (!persta_prof_len) {
1724 		mlo_err("Length to per-STA prof frame is 0");
1725 		return QDF_STATUS_E_NULL_VALUE;
1726 	}
1727 
1728 	linkinfo_currpos = linkinfo;
1729 	linkinfo_remlen = linkinfo_len;
1730 
1731 	while (linkinfo_remlen) {
1732 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
1733 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
1734 				   linkinfo_remlen,
1735 				   sizeof(struct subelem_header));
1736 			return QDF_STATUS_E_PROTO;
1737 		}
1738 
1739 		subelemid = linkinfo_currpos[ID_POS];
1740 		is_subelemfragseq = false;
1741 		subelemseqtotallen = 0;
1742 		subelemseqpayloadlen = 0;
1743 
1744 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1745 						    linkinfo_currpos,
1746 						    linkinfo_remlen,
1747 						    &is_subelemfragseq,
1748 						    &subelemseqtotallen,
1749 						    &subelemseqpayloadlen);
1750 		if (QDF_IS_STATUS_ERROR(ret))
1751 			return ret;
1752 
1753 		if (is_subelemfragseq) {
1754 			if (!subelemseqpayloadlen) {
1755 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
1756 				return QDF_STATUS_E_FAILURE;
1757 			}
1758 
1759 			mlo_debug("Subelement fragment sequence found with payload len %zu",
1760 				  subelemseqpayloadlen);
1761 
1762 			ret = wlan_defrag_subelem_fragseq(true,
1763 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1764 							  linkinfo_currpos,
1765 							  linkinfo_remlen,
1766 							  NULL,
1767 							  0,
1768 							  &defragpayload_len);
1769 			if (QDF_IS_STATUS_ERROR(ret))
1770 				return ret;
1771 
1772 			if (defragpayload_len != subelemseqpayloadlen) {
1773 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
1774 					   defragpayload_len,
1775 					   subelemseqpayloadlen);
1776 				return QDF_STATUS_E_FAILURE;
1777 			}
1778 
1779 			/* Adjust linkinfo_remlen to reflect removal of all
1780 			 * subelement headers except the header of the lead
1781 			 * subelement.
1782 			 */
1783 			linkinfo_remlen -= (subelemseqtotallen -
1784 					    subelemseqpayloadlen -
1785 					    sizeof(struct subelem_header));
1786 		} else {
1787 			if (linkinfo_remlen <
1788 				(sizeof(struct subelem_header) +
1789 				 linkinfo_currpos[TAG_LEN_POS])) {
1790 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
1791 					   linkinfo_remlen,
1792 					   sizeof(struct subelem_header) +
1793 					   linkinfo_currpos[TAG_LEN_POS]);
1794 				return QDF_STATUS_E_PROTO;
1795 			}
1796 
1797 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
1798 		}
1799 
1800 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
1801 			is_macaddr_valid = false;
1802 
1803 			ret = util_parse_bvmlie_perstaprofile_stactrl(linkinfo_currpos +
1804 								      sizeof(struct subelem_header),
1805 								      subelemseqpayloadlen,
1806 								      &linkid,
1807 								      NULL,
1808 								      NULL,
1809 								      NULL,
1810 								      NULL,
1811 								      NULL,
1812 								      &is_macaddr_valid,
1813 								      &macaddr,
1814 								      false,
1815 								      NULL,
1816 								      NULL,
1817 								      NULL,
1818 								      NULL);
1819 			if (QDF_IS_STATUS_ERROR(ret))
1820 				return ret;
1821 
1822 			if (req_link_id == linkid) {
1823 				mlo_debug("Found requested per-STA prof for linkid %u, len %zu",
1824 					  linkid, subelemseqpayloadlen);
1825 				QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO,
1826 						   QDF_TRACE_LEVEL_DEBUG,
1827 						   linkinfo_currpos,
1828 						   subelemseqpayloadlen +
1829 						   sizeof(struct subelem_header));
1830 				*persta_prof_frame = linkinfo_currpos;
1831 				*persta_prof_len = subelemseqpayloadlen;
1832 				return QDF_STATUS_SUCCESS;
1833 			}
1834 		}
1835 
1836 		linkinfo_remlen -= (sizeof(struct subelem_header) +
1837 				    subelemseqpayloadlen);
1838 		linkinfo_currpos += (sizeof(struct subelem_header) +
1839 				     subelemseqpayloadlen);
1840 	}
1841 
1842 	return QDF_STATUS_E_PROTO;
1843 }
1844 
1845 static
1846 QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len,
1847 				    uint8_t subtype,
1848 				    uint8_t req_link_id,
1849 				    struct qdf_mac_addr link_addr,
1850 				    uint8_t *link_frame,
1851 				    qdf_size_t link_frame_maxsize,
1852 				    qdf_size_t *link_frame_len)
1853 {
1854 	/* Please see documentation for util_gen_link_assoc_req() and
1855 	 * util_gen_link_assoc_resp() for information on the inputs to and
1856 	 * output from this helper, since those APIs are essentially wrappers
1857 	 * over this helper.
1858 	 */
1859 
1860 	/* Pointer to Multi-Link element/Multi-Link element fragment sequence */
1861 	uint8_t *mlieseq;
1862 	/* Total length of Multi-Link element sequence (including fragments if
1863 	 * any)
1864 	 */
1865 	qdf_size_t mlieseqlen;
1866 	/* Variant (i.e. type) of the Multi-Link element */
1867 	enum wlan_ml_variant variant;
1868 
1869 	/* Length of the payload of the Multi-Link element (inclusive of
1870 	 * fragment payloads if any) without IE headers and element ID extension
1871 	 */
1872 	qdf_size_t mlieseqpayloadlen;
1873 	/* Pointer to copy of the payload of the Multi-Link element (inclusive
1874 	 * of fragment payloads if any) without IE headers and element ID
1875 	 * extension
1876 	 */
1877 	uint8_t *mlieseqpayload_copy;
1878 
1879 	/* Pointer to start of Link Info within the copy of the payload of the
1880 	 * Multi-Link element
1881 	 */
1882 	uint8_t *link_info;
1883 	/* Length of the Link Info */
1884 	qdf_size_t link_info_len;
1885 
1886 	/* Pointer to the IE section that occurs after the fixed fields in the
1887 	 * original frame for the reporting STA.
1888 	 */
1889 	uint8_t *frame_iesection;
1890 	/* Offset to the start of the IE section in the original frame for the
1891 	 * reporting STA.
1892 	 */
1893 	qdf_size_t frame_iesection_offset;
1894 	/* Total length of the IE section in the original frame for the
1895 	 * reporting STA.
1896 	 */
1897 	qdf_size_t frame_iesection_len;
1898 
1899 	/* Pointer to the IEEE802.11 frame header in the link specific frame
1900 	 * being generated for the reported STA.
1901 	 */
1902 	struct wlan_frame_hdr *link_frame_hdr;
1903 	/* Current position in the link specific frame being generated for the
1904 	 * reported STA.
1905 	 */
1906 	uint8_t *link_frame_currpos;
1907 	/* Current length of the link specific frame being generated for the
1908 	 * reported STA.
1909 	 */
1910 	qdf_size_t link_frame_currlen;
1911 
1912 	/* Pointer to IE for reporting STA */
1913 	const uint8_t *reportingsta_ie;
1914 	/* Total size of IE for reporting STA, inclusive of the element header
1915 	 */
1916 	qdf_size_t reportingsta_ie_size;
1917 
1918 	/* Pointer to current position in STA profile */
1919 	uint8_t *sta_prof_currpos;
1920 	/* Remaining length of STA profile */
1921 	qdf_size_t sta_prof_remlen;
1922 	/* Pointer to start of IE section in STA profile that occurs after fixed
1923 	 * fields.
1924 	 */
1925 	uint8_t *sta_prof_iesection;
1926 	/* Total length of IE section in STA profile */
1927 	qdf_size_t sta_prof_iesection_len;
1928 	/* Pointer to current position being processed in IE section in STA
1929 	 * profile.
1930 	 */
1931 	uint8_t *sta_prof_iesection_currpos;
1932 	/* Remaining length of IE section in STA profile */
1933 	qdf_size_t sta_prof_iesection_remlen;
1934 
1935 	/* Pointer to IE in STA profile, that occurs within IE section */
1936 	uint8_t *sta_prof_ie;
1937 	/* Total size of IE in STA profile, inclusive of the element header */
1938 	qdf_size_t sta_prof_ie_size;
1939 
1940 	/* Pointer to element ID list in Non-Inheritance IE */
1941 	uint8_t *ninherit_elemlist;
1942 	/* Length of element ID list in Non-Inheritance IE */
1943 	qdf_size_t ninherit_elemlist_len;
1944 	/* Pointer to element ID extension list in Non-Inheritance IE */
1945 	uint8_t *ninherit_elemextlist;
1946 	/* Length of element ID extension list in Non-Inheritance IE */
1947 	qdf_size_t ninherit_elemextlist_len;
1948 	/* Whether a given IE is in a non-inheritance list */
1949 	bool is_in_noninheritlist;
1950 
1951 	/* Whether MAC address of reported STA is valid */
1952 	bool is_reportedmacaddr_valid;
1953 	/* MAC address of reported STA */
1954 	struct qdf_mac_addr reportedmacaddr;
1955 
1956 	/* Pointer to per-STA profile */
1957 	uint8_t *persta_prof;
1958 	/* Length of the containing buffer which starts with the per-STA profile
1959 	 */
1960 	qdf_size_t persta_prof_bufflen;
1961 
1962 	/* Other variables for temporary purposes */
1963 
1964 	/* Variable into which API for determining fragment information will
1965 	 * indicate whether the element is the start of a fragment sequence or
1966 	 * not.
1967 	 */
1968 	bool is_elemfragseq;
1969 	/*  De-fragmented payload length returned by API for element
1970 	 *  defragmentation.
1971 	 */
1972 	qdf_size_t defragpayload_len;
1973 	/* Pointer to Beacon interval in STA info field */
1974 	uint16_t beaconinterval;
1975 	/* Whether Beacon interval value valid */
1976 	bool is_beaconinterval_valid;
1977 	/* TSF timer of the reporting AP */
1978 	uint64_t tsf;
1979 	/* TSF offset of the reproted AP */
1980 	uint64_t tsfoffset;
1981 	/* TSF offset value valid */
1982 	bool is_tsfoffset_valid;
1983 	/* If Complete Profile or not*/
1984 	bool is_completeprofile;
1985 	qdf_size_t tmplen;
1986 	QDF_STATUS ret;
1987 	uint8_t linkid = 0xFF;
1988 
1989 	if (!frame) {
1990 		mlo_err("Pointer to original frame is NULL");
1991 		return QDF_STATUS_E_NULL_VALUE;
1992 	}
1993 
1994 	if (!frame_len) {
1995 		mlo_err("Length of original frame is zero");
1996 		return QDF_STATUS_E_INVAL;
1997 	}
1998 
1999 	if ((subtype != WLAN_FC0_STYPE_ASSOC_REQ) &&
2000 	    (subtype != WLAN_FC0_STYPE_REASSOC_REQ) &&
2001 	    (subtype != WLAN_FC0_STYPE_ASSOC_RESP) &&
2002 	    (subtype != WLAN_FC0_STYPE_REASSOC_RESP) &&
2003 	    (subtype != WLAN_FC0_STYPE_PROBE_RESP)) {
2004 		mlo_err("802.11 frame subtype %u is invalid", subtype);
2005 		return QDF_STATUS_E_INVAL;
2006 	}
2007 
2008 	if (!link_frame) {
2009 		mlo_err("Pointer to secondary link specific frame is NULL");
2010 		return QDF_STATUS_E_NULL_VALUE;
2011 	}
2012 
2013 	if (!link_frame_maxsize) {
2014 		mlo_err("Maximum size of secondary link specific frame is zero");
2015 		return QDF_STATUS_E_INVAL;
2016 	}
2017 
2018 	if (!link_frame_len) {
2019 		mlo_err("Pointer to populated length of secondary link specific frame is NULL");
2020 		return QDF_STATUS_E_NULL_VALUE;
2021 	}
2022 
2023 	frame_iesection_offset = 0;
2024 
2025 	if (subtype == WLAN_FC0_STYPE_ASSOC_REQ) {
2026 		frame_iesection_offset = WLAN_ASSOC_REQ_IES_OFFSET;
2027 	} else if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
2028 		frame_iesection_offset = WLAN_REASSOC_REQ_IES_OFFSET;
2029 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2030 		frame_iesection_offset = WLAN_PROBE_RESP_IES_OFFSET;
2031 		qdf_mem_copy(&tsf, frame, WLAN_TIMESTAMP_LEN);
2032 		tsf = qdf_le64_to_cpu(tsf);
2033 	} else {
2034 		/* This is a (re)association response */
2035 		frame_iesection_offset = WLAN_ASSOC_RSP_IES_OFFSET;
2036 	}
2037 
2038 	if (frame_len < frame_iesection_offset) {
2039 		/* The caller is supposed to have confirmed that this is a valid
2040 		 * frame containing a Multi-Link element. Hence we treat this as
2041 		 * a case of invalid argument being passed to us.
2042 		 */
2043 		mlo_err("Frame length %zu is smaller than the IE section offset %zu for subtype %u",
2044 			frame_len, frame_iesection_offset, subtype);
2045 		return QDF_STATUS_E_INVAL;
2046 	}
2047 
2048 	frame_iesection_len = frame_len - frame_iesection_offset;
2049 
2050 	if (frame_iesection_len == 0) {
2051 		/* The caller is supposed to have confirmed that this is a valid
2052 		 * frame containing a Multi-Link element. Hence we treat this as
2053 		 * a case of invalid argument being passed to us.
2054 		 */
2055 		mlo_err("No space left in frame for IE section");
2056 		return QDF_STATUS_E_INVAL;
2057 	}
2058 
2059 	frame_iesection = frame + frame_iesection_offset;
2060 
2061 	mlieseq = NULL;
2062 	mlieseqlen = 0;
2063 
2064 	ret = util_find_mlie(frame_iesection, frame_iesection_len, &mlieseq,
2065 			     &mlieseqlen);
2066 	if (QDF_IS_STATUS_ERROR(ret))
2067 		return ret;
2068 
2069 	if (!mlieseq) {
2070 		/* The caller is supposed to have confirmed that a Multi-Link
2071 		 * element is present in the frame. Hence we treat this as a
2072 		 * case of invalid argument being passed to us.
2073 		 */
2074 		mlo_err("Invalid original frame since no Multi-Link element found");
2075 		return QDF_STATUS_E_INVAL;
2076 	}
2077 
2078 	/* Sanity check the Multi-Link element sequence length */
2079 	if (!mlieseqlen) {
2080 		mlo_err("Length of Multi-Link element sequence is zero. Investigate.");
2081 		return QDF_STATUS_E_FAILURE;
2082 	}
2083 
2084 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
2085 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
2086 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
2087 		return QDF_STATUS_E_PROTO;
2088 	}
2089 
2090 	ret = util_get_mlie_variant(mlieseq, mlieseqlen, (int *)&variant);
2091 	if (QDF_IS_STATUS_ERROR(ret))
2092 		return ret;
2093 
2094 	if (variant != WLAN_ML_VARIANT_BASIC) {
2095 		mlo_err_rl("Unexpected variant %u of Multi-Link element.",
2096 			   variant);
2097 		return QDF_STATUS_E_PROTO;
2098 	}
2099 
2100 	mlieseqpayloadlen = 0;
2101 	tmplen = 0;
2102 	is_elemfragseq = false;
2103 
2104 	ret = wlan_get_elem_fragseq_info(mlieseq,
2105 					 mlieseqlen,
2106 					 &is_elemfragseq,
2107 					 &tmplen,
2108 					 &mlieseqpayloadlen);
2109 	if (QDF_IS_STATUS_ERROR(ret))
2110 		return ret;
2111 
2112 	if (is_elemfragseq) {
2113 		if (tmplen != mlieseqlen) {
2114 			mlo_err_rl("Mismatch in values of element fragment sequence total length. Val per frag info determination: %zu octets, val per Multi-Link element search: %zu octets",
2115 				   tmplen, mlieseqlen);
2116 			return QDF_STATUS_E_FAILURE;
2117 		}
2118 
2119 		if (!mlieseqpayloadlen) {
2120 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
2121 			return QDF_STATUS_E_FAILURE;
2122 		}
2123 
2124 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
2125 			  mlieseqpayloadlen);
2126 	} else {
2127 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
2128 			mlo_err_rl("Expected presence of valid fragment sequence since Multi-Link element sequence length %zu octets is larger than frag threshold of %zu octets, however no valid fragment sequence found",
2129 				   mlieseqlen,
2130 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
2131 			return QDF_STATUS_E_FAILURE;
2132 		}
2133 
2134 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
2135 	}
2136 
2137 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
2138 
2139 	if (!mlieseqpayload_copy) {
2140 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
2141 		return QDF_STATUS_E_NOMEM;
2142 	}
2143 
2144 	if (is_elemfragseq) {
2145 		ret = wlan_defrag_elem_fragseq(false,
2146 					       mlieseq,
2147 					       mlieseqlen,
2148 					       mlieseqpayload_copy,
2149 					       mlieseqpayloadlen,
2150 					       &defragpayload_len);
2151 		if (QDF_IS_STATUS_ERROR(ret)) {
2152 			qdf_mem_free(mlieseqpayload_copy);
2153 			return ret;
2154 		}
2155 
2156 		if (defragpayload_len != mlieseqpayloadlen) {
2157 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
2158 				   defragpayload_len, mlieseqpayloadlen);
2159 			qdf_mem_free(mlieseqpayload_copy);
2160 			return QDF_STATUS_E_FAILURE;
2161 		}
2162 	} else {
2163 		qdf_mem_copy(mlieseqpayload_copy,
2164 			     mlieseq + sizeof(struct ie_header) + 1,
2165 			     mlieseqpayloadlen);
2166 	}
2167 
2168 	link_info = NULL;
2169 	link_info_len = 0;
2170 
2171 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
2172 					 mlieseqpayloadlen,
2173 					 &link_info,
2174 					 &link_info_len);
2175 	if (QDF_IS_STATUS_ERROR(ret)) {
2176 		qdf_mem_free(mlieseqpayload_copy);
2177 		return ret;
2178 	}
2179 
2180 	/* As per the standard, the sender must include Link Info for
2181 	 * association request/response. Throw an error if we are unable to
2182 	 * obtain this.
2183 	 */
2184 	if (!link_info) {
2185 		mlo_err_rl("Unable to successfully obtain Link Info");
2186 		qdf_mem_free(mlieseqpayload_copy);
2187 		return QDF_STATUS_E_PROTO;
2188 	}
2189 
2190 	mlo_debug("Dumping hex of link info after parsing Multi-Link element control");
2191 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_DEBUG,
2192 			   link_info, link_info_len);
2193 
2194 	/* Note: We may have a future change to skip subelements which are not
2195 	 * Per-STA Profile, handle more than two links in MLO, handle cases
2196 	 * where we unexpectedly find more Per-STA Profiles than expected, etc.
2197 	 */
2198 
2199 	persta_prof = NULL;
2200 	persta_prof_bufflen = 0;
2201 
2202 	ret = util_find_bvmlie_persta_prof_for_linkid(req_link_id,
2203 						      link_info,
2204 						      link_info_len,
2205 						      &persta_prof,
2206 						      &persta_prof_bufflen);
2207 
2208 	if (QDF_IS_STATUS_ERROR(ret)) {
2209 		mlo_err_rl("Per STA profile not found for link id %d",
2210 			   req_link_id);
2211 		qdf_mem_free(mlieseqpayload_copy);
2212 		return ret;
2213 	}
2214 
2215 	sta_prof_remlen = 0;
2216 	sta_prof_currpos = NULL;
2217 	is_reportedmacaddr_valid = false;
2218 	is_beaconinterval_valid = false;
2219 	is_completeprofile = false;
2220 	is_tsfoffset_valid = false;
2221 
2222 	/* Parse per-STA profile */
2223 	ret = util_parse_bvmlie_perstaprofile_stactrl(persta_prof +
2224 						      sizeof(struct subelem_header),
2225 						      persta_prof_bufflen,
2226 						      &linkid,
2227 						      &beaconinterval,
2228 						      &is_beaconinterval_valid,
2229 						      &tsfoffset,
2230 						      &is_tsfoffset_valid,
2231 						      &is_completeprofile,
2232 						      &is_reportedmacaddr_valid,
2233 						      &reportedmacaddr,
2234 						      true,
2235 						      &sta_prof_currpos,
2236 						      &sta_prof_remlen,
2237 						      NULL,
2238 						      NULL);
2239 	if (QDF_IS_STATUS_ERROR(ret)) {
2240 		qdf_mem_free(mlieseqpayload_copy);
2241 		return ret;
2242 	}
2243 
2244 	if (subtype == WLAN_FC0_STYPE_PROBE_RESP && !is_completeprofile) {
2245 		mlo_err("Complete profile information is not present in per-STA profile of probe response frame");
2246 		return QDF_STATUS_E_NOSUPPORT;
2247 	}
2248 
2249 	/* We double check for a NULL STA Profile, though the helper function
2250 	 * above would have taken care of this. We need to get a non-NULL STA
2251 	 * profile, because we need to get at least the expected fixed fields,
2252 	 * even if there is an (improbable) total inheritance.
2253 	 */
2254 	if (!sta_prof_currpos) {
2255 		mlo_err_rl("STA profile is NULL");
2256 		qdf_mem_free(mlieseqpayload_copy);
2257 		return QDF_STATUS_E_PROTO;
2258 	}
2259 
2260 	/* As per the standard, the sender sets the MAC address in the per-STA
2261 	 * profile in association request/response. Without this, we cannot
2262 	 * generate the link specific frame.
2263 	 */
2264 	if (!is_reportedmacaddr_valid) {
2265 		mlo_err_rl("Unable to get MAC address from per-STA profile");
2266 		qdf_mem_free(mlieseqpayload_copy);
2267 		return QDF_STATUS_E_PROTO;
2268 	}
2269 
2270 	link_frame_currpos = link_frame;
2271 	*link_frame_len = 0;
2272 	link_frame_currlen = 0;
2273 
2274 	if (link_frame_maxsize < WLAN_MAC_HDR_LEN_3A) {
2275 		mlo_err("Insufficient space in link specific frame for 802.11 header. Required: %u octets, available: %zu octets",
2276 			WLAN_MAC_HDR_LEN_3A, link_frame_maxsize);
2277 
2278 		qdf_mem_free(mlieseqpayload_copy);
2279 		return QDF_STATUS_E_NOMEM;
2280 	}
2281 
2282 	link_frame_currpos += WLAN_MAC_HDR_LEN_3A;
2283 	link_frame_currlen += WLAN_MAC_HDR_LEN_3A;
2284 
2285 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2286 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
2287 		mlo_debug("Populating fixed fields for (re)assoc req in link specific frame");
2288 
2289 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
2290 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
2291 				   sta_prof_remlen,
2292 				   WLAN_CAPABILITYINFO_LEN);
2293 
2294 			qdf_mem_free(mlieseqpayload_copy);
2295 			return QDF_STATUS_E_PROTO;
2296 		}
2297 
2298 		/* Capability information is specific to the link. Copy this
2299 		 * from the STA profile.
2300 		 */
2301 
2302 		if ((link_frame_maxsize - link_frame_currlen) <
2303 				WLAN_CAPABILITYINFO_LEN) {
2304 			mlo_err("Insufficient space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
2305 				WLAN_CAPABILITYINFO_LEN,
2306 				(link_frame_maxsize - link_frame_currlen));
2307 
2308 			qdf_mem_free(mlieseqpayload_copy);
2309 			return QDF_STATUS_E_NOMEM;
2310 		}
2311 
2312 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2313 			     WLAN_CAPABILITYINFO_LEN);
2314 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2315 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2316 		mlo_debug("Added Capability Info field (%u octets) to link specific frame",
2317 			  WLAN_CAPABILITYINFO_LEN);
2318 
2319 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2320 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2321 
2322 		/* Listen Interval is common between all links. Copy this from
2323 		 * the reporting section of the frame.
2324 		 */
2325 
2326 		if ((link_frame_maxsize - link_frame_currlen) <
2327 				WLAN_LISTENINTERVAL_LEN) {
2328 			mlo_err("Insufficient space in link specific frame for Listen Interval field. Required: %u octets, available: %zu octets",
2329 				WLAN_LISTENINTERVAL_LEN,
2330 				(link_frame_maxsize - link_frame_currlen));
2331 
2332 			qdf_mem_free(mlieseqpayload_copy);
2333 			return QDF_STATUS_E_NOMEM;
2334 		}
2335 
2336 		qdf_mem_copy(link_frame_currpos,
2337 			     frame + WLAN_CAPABILITYINFO_LEN,
2338 			     WLAN_LISTENINTERVAL_LEN);
2339 		link_frame_currpos += WLAN_LISTENINTERVAL_LEN;
2340 		link_frame_currlen += WLAN_LISTENINTERVAL_LEN;
2341 		mlo_debug("Added Listen Interval field (%u octets) to link specific frame",
2342 			  WLAN_LISTENINTERVAL_LEN);
2343 
2344 		if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
2345 			/* Current AP address is common between all links. Copy
2346 			 * this from the reporting section of the frame.
2347 			 */
2348 			if ((link_frame_maxsize - link_frame_currlen) <
2349 				QDF_MAC_ADDR_SIZE) {
2350 				mlo_err("Insufficient space in link specific frame for current AP address. Required: %u octets, available: %zu octets",
2351 					QDF_MAC_ADDR_SIZE,
2352 					(link_frame_maxsize -
2353 						link_frame_currlen));
2354 
2355 				qdf_mem_free(mlieseqpayload_copy);
2356 				return QDF_STATUS_E_NOMEM;
2357 			}
2358 
2359 			qdf_mem_copy(link_frame_currpos,
2360 				     frame + WLAN_CAPABILITYINFO_LEN +
2361 						WLAN_LISTENINTERVAL_LEN,
2362 				     QDF_MAC_ADDR_SIZE);
2363 			link_frame_currpos += QDF_MAC_ADDR_SIZE;
2364 			link_frame_currlen += QDF_MAC_ADDR_SIZE;
2365 			mlo_debug("Reassoc req: Added Current AP address field (%u octets) to link specific frame",
2366 				  QDF_MAC_ADDR_SIZE);
2367 		}
2368 	} else if (subtype == WLAN_FC0_STYPE_ASSOC_RESP ||
2369 		   subtype == WLAN_FC0_STYPE_REASSOC_RESP) {
2370 		/* This is a (re)association response */
2371 		mlo_debug("Populating fixed fields for (re)assoc resp in link specific frame");
2372 
2373 		if (sta_prof_remlen <
2374 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2375 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info + length of Status Code %u",
2376 				   sta_prof_remlen,
2377 				   WLAN_CAPABILITYINFO_LEN +
2378 					WLAN_STATUSCODE_LEN);
2379 
2380 			qdf_mem_free(mlieseqpayload_copy);
2381 			return QDF_STATUS_E_PROTO;
2382 		}
2383 
2384 		/* Capability information and Status Code are specific to the
2385 		 * link. Copy these from the STA profile.
2386 		 */
2387 
2388 		if ((link_frame_maxsize - link_frame_currlen) <
2389 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2390 			mlo_err("Insufficient space in link specific frame for Capability Info and Status Code fields. Required: %u octets, available: %zu octets",
2391 				WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN,
2392 				(link_frame_maxsize - link_frame_currlen));
2393 
2394 			qdf_mem_free(mlieseqpayload_copy);
2395 			return QDF_STATUS_E_NOMEM;
2396 		}
2397 
2398 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2399 			     (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN));
2400 		link_frame_currpos += (WLAN_CAPABILITYINFO_LEN +
2401 						WLAN_STATUSCODE_LEN);
2402 		link_frame_currlen += (WLAN_CAPABILITYINFO_LEN +
2403 				WLAN_STATUSCODE_LEN);
2404 		mlo_debug("Added Capability Info and Status Code fields (%u octets) to link specific frame",
2405 			  WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN);
2406 
2407 		sta_prof_currpos += (WLAN_CAPABILITYINFO_LEN +
2408 				WLAN_STATUSCODE_LEN);
2409 		sta_prof_remlen -= (WLAN_CAPABILITYINFO_LEN +
2410 				WLAN_STATUSCODE_LEN);
2411 
2412 		/* AID is common between all links. Copy this from the original
2413 		 * frame.
2414 		 */
2415 
2416 		if ((link_frame_maxsize - link_frame_currlen) < WLAN_AID_LEN) {
2417 			mlo_err("Insufficient space in link specific frame for AID field. Required: %u octets, available: %zu octets",
2418 				WLAN_AID_LEN,
2419 				(link_frame_maxsize - link_frame_currlen));
2420 
2421 			qdf_mem_free(mlieseqpayload_copy);
2422 			return QDF_STATUS_E_NOMEM;
2423 		}
2424 
2425 		qdf_mem_copy(link_frame_currpos,
2426 			     frame + WLAN_CAPABILITYINFO_LEN +
2427 					WLAN_STATUSCODE_LEN,
2428 			     WLAN_AID_LEN);
2429 		link_frame_currpos += WLAN_AID_LEN;
2430 		link_frame_currlen += WLAN_AID_LEN;
2431 		mlo_debug("Added AID field (%u octets) to link specific frame",
2432 			  WLAN_AID_LEN);
2433 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2434 		/* This is a probe response */
2435 		mlo_debug("Populating fixed fields for probe response in link specific frame");
2436 
2437 		if ((link_frame_maxsize - link_frame_currlen) <
2438 				WLAN_TIMESTAMP_LEN) {
2439 			mlo_err("Insufficient space in link specific frame for Timestamp Info field. Required: %u octets, available: %zu octets",
2440 				WLAN_TIMESTAMP_LEN,
2441 				(link_frame_maxsize - link_frame_currlen));
2442 
2443 			qdf_mem_free(mlieseqpayload_copy);
2444 			return QDF_STATUS_E_NOMEM;
2445 		}
2446 
2447 		/* Per spec 11be_D2.1.1, the TSF Offset subfield of the STA Info
2448 		 * field indicates the offset (Toffset)between the TSF timer of
2449 		 * the reported AP (TA) and the TSF timer of the reporting
2450 		 * AP (TB) and is encoded as a 2s complement signed integer
2451 		 * with units of 2 µs. Toffset is calculated as
2452 		 * Toffset= Floor((TA – TB)/2).
2453 		 */
2454 		if (is_tsfoffset_valid)
2455 			tsf += tsfoffset * 2;
2456 
2457 		qdf_mem_copy(link_frame_currpos, &tsf, WLAN_TIMESTAMP_LEN);
2458 		link_frame_currpos += WLAN_TIMESTAMP_LEN;
2459 		link_frame_currlen += WLAN_TIMESTAMP_LEN;
2460 		mlo_debug("Added Timestamp Info field (%u octets) to link specific frame",
2461 			  WLAN_TIMESTAMP_LEN);
2462 
2463 		if (!is_beaconinterval_valid) {
2464 			mlo_err_rl("Beacon interval information not present in STA info field of per-STA profile");
2465 			qdf_mem_free(mlieseqpayload_copy);
2466 			return QDF_STATUS_E_PROTO;
2467 		}
2468 
2469 		/* Beacon Interval information copy this from
2470 		 * the STA info field.
2471 		 */
2472 		if ((link_frame_maxsize - link_frame_currlen) <
2473 				WLAN_BEACONINTERVAL_LEN) {
2474 			mlo_err("Insufficient space in link specific frame for Beacon Interval Info field. Required: %u octets, available: %zu octets",
2475 				WLAN_BEACONINTERVAL_LEN,
2476 				(link_frame_maxsize - link_frame_currlen));
2477 
2478 			qdf_mem_free(mlieseqpayload_copy);
2479 			return QDF_STATUS_E_NOMEM;
2480 		}
2481 
2482 		qdf_mem_copy(link_frame_currpos, &beaconinterval,
2483 			     WLAN_BEACONINTERVAL_LEN);
2484 		link_frame_currpos += WLAN_BEACONINTERVAL_LEN;
2485 		link_frame_currlen += WLAN_BEACONINTERVAL_LEN;
2486 		mlo_debug("Added Beacon Interval Info field (%u octets) to link specific frame",
2487 			  WLAN_BEACONINTERVAL_LEN);
2488 
2489 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
2490 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
2491 				   sta_prof_remlen,
2492 				   WLAN_CAPABILITYINFO_LEN);
2493 
2494 			qdf_mem_free(mlieseqpayload_copy);
2495 			return QDF_STATUS_E_PROTO;
2496 		}
2497 
2498 		/* Capability information is specific to the link. Copy this
2499 		 * from the STA profile.
2500 		 */
2501 
2502 		if ((link_frame_maxsize - link_frame_currlen) <
2503 				WLAN_CAPABILITYINFO_LEN) {
2504 			mlo_err("Insufficient space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
2505 				WLAN_CAPABILITYINFO_LEN,
2506 				(link_frame_maxsize - link_frame_currlen));
2507 
2508 			qdf_mem_free(mlieseqpayload_copy);
2509 			return QDF_STATUS_E_NOMEM;
2510 		}
2511 
2512 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2513 			     WLAN_CAPABILITYINFO_LEN);
2514 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2515 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2516 		mlo_debug("Added Capability Info field (%u octets) to link specific frame",
2517 			  WLAN_CAPABILITYINFO_LEN);
2518 
2519 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2520 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2521 	}
2522 
2523 	sta_prof_iesection = sta_prof_currpos;
2524 	sta_prof_iesection_len = sta_prof_remlen;
2525 
2526 	/* Populate non-inheritance lists if applicable */
2527 	ninherit_elemlist_len = 0;
2528 	ninherit_elemlist = NULL;
2529 	ninherit_elemextlist_len = 0;
2530 	ninherit_elemextlist = NULL;
2531 
2532 	ret = util_get_noninheritlists(sta_prof_iesection,
2533 				       sta_prof_iesection_len,
2534 				       &ninherit_elemlist,
2535 				       &ninherit_elemlist_len,
2536 				       &ninherit_elemextlist,
2537 				       &ninherit_elemextlist_len);
2538 	if (QDF_IS_STATUS_ERROR(ret)) {
2539 		qdf_mem_free(mlieseqpayload_copy);
2540 		return ret;
2541 	}
2542 
2543 	/* Go through IEs of the reporting STA, and those in STA profile, merge
2544 	 * them into link_frame (except for elements in the Non-Inheritance
2545 	 * list).
2546 	 *
2547 	 * Note: Currently, only 2-link MLO is supported here. We may have a
2548 	 * future change to expand to more links.
2549 	 */
2550 	reportingsta_ie = util_find_eid(WLAN_ELEMID_SSID, frame_iesection,
2551 					frame_iesection_len);
2552 
2553 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2554 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ) ||
2555 	    (subtype == WLAN_FC0_STYPE_PROBE_RESP)) {
2556 		/* Sanity check that the SSID element is present for the
2557 		 * reporting STA. There is no stipulation in the standard for
2558 		 * the STA profile in this regard, so we do not check the STA
2559 		 * profile for the SSID element.
2560 		 */
2561 		if (!reportingsta_ie) {
2562 			mlo_err_rl("SSID element not found in reporting STA of the frame.");
2563 			qdf_mem_free(mlieseqpayload_copy);
2564 			return QDF_STATUS_E_PROTO;
2565 		}
2566 	} else {
2567 		/* This is a (re)association response. Sanity check that the
2568 		 * SSID element is present neither for the reporting STA nor in
2569 		 * the STA profile.
2570 		 */
2571 		if (reportingsta_ie) {
2572 			mlo_err_rl("SSID element found for reporting STA for (re)association response. It should not be present.");
2573 			qdf_mem_free(mlieseqpayload_copy);
2574 			return QDF_STATUS_E_PROTO;
2575 		}
2576 
2577 		sta_prof_ie = util_find_eid(WLAN_ELEMID_SSID,
2578 					    sta_prof_iesection,
2579 					    sta_prof_iesection_len);
2580 
2581 		if (sta_prof_ie) {
2582 			mlo_err_rl("SSID element found in STA profile for (re)association response. It should not be present.");
2583 			qdf_mem_free(mlieseqpayload_copy);
2584 			return QDF_STATUS_E_PROTO;
2585 		}
2586 	}
2587 
2588 	reportingsta_ie = reportingsta_ie ? reportingsta_ie : frame_iesection;
2589 
2590 	ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection,
2591 					    frame_iesection_len);
2592 	if (QDF_IS_STATUS_ERROR(ret)) {
2593 		qdf_mem_free(mlieseqpayload_copy);
2594 		return ret;
2595 	}
2596 
2597 	reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
2598 
2599 	while (((reportingsta_ie + reportingsta_ie_size) - frame_iesection)
2600 			<= frame_iesection_len) {
2601 		/* Skip Multi-Link element */
2602 		if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
2603 		    (reportingsta_ie[IDEXT_POS] ==
2604 				WLAN_EXTN_ELEMID_MULTI_LINK)) {
2605 			if (((reportingsta_ie + reportingsta_ie_size) -
2606 					frame_iesection) == frame_iesection_len)
2607 				break;
2608 
2609 			/* Add BV ML IE for link specific probe response */
2610 			if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2611 				ret = util_add_mlie_for_prb_rsp_gen(
2612 					reportingsta_ie,
2613 					reportingsta_ie[TAG_LEN_POS],
2614 					&link_frame_currpos,
2615 					&link_frame_currlen,
2616 					link_frame_maxsize,
2617 					linkid);
2618 				if (QDF_IS_STATUS_ERROR(ret)) {
2619 					qdf_mem_free(mlieseqpayload_copy);
2620 					return ret;
2621 				}
2622 			}
2623 			reportingsta_ie += reportingsta_ie_size;
2624 
2625 			ret = util_validate_reportingsta_ie(reportingsta_ie,
2626 							    frame_iesection,
2627 							    frame_iesection_len);
2628 			if (QDF_IS_STATUS_ERROR(ret)) {
2629 				qdf_mem_free(mlieseqpayload_copy);
2630 				return ret;
2631 			}
2632 
2633 			reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2634 				MIN_IE_LEN;
2635 
2636 			continue;
2637 		}
2638 
2639 		sta_prof_ie = NULL;
2640 		sta_prof_ie_size = 0;
2641 
2642 		if (sta_prof_iesection_len) {
2643 			if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2644 				sta_prof_ie = (uint8_t *)util_find_extn_eid(reportingsta_ie[ID_POS],
2645 									    reportingsta_ie[IDEXT_POS],
2646 									    sta_prof_iesection,
2647 									    sta_prof_iesection_len);
2648 			} else {
2649 				sta_prof_ie = (uint8_t *)util_find_eid(reportingsta_ie[ID_POS],
2650 								       sta_prof_iesection,
2651 								       sta_prof_iesection_len);
2652 			}
2653 		}
2654 
2655 		if (!sta_prof_ie) {
2656 			/* IE is present for reporting STA, but not in STA
2657 			 * profile.
2658 			 */
2659 
2660 			is_in_noninheritlist = false;
2661 
2662 			ret = util_eval_ie_in_noninheritlist((uint8_t *)reportingsta_ie,
2663 							     reportingsta_ie_size,
2664 							     ninherit_elemlist,
2665 							     ninherit_elemlist_len,
2666 							     ninherit_elemextlist,
2667 							     ninherit_elemextlist_len,
2668 							     &is_in_noninheritlist);
2669 
2670 			if (QDF_IS_STATUS_ERROR(ret)) {
2671 				qdf_mem_free(mlieseqpayload_copy);
2672 				return ret;
2673 			}
2674 
2675 			if (!is_in_noninheritlist) {
2676 				if ((link_frame_currpos +
2677 						reportingsta_ie_size) <=
2678 					(link_frame + link_frame_maxsize)) {
2679 					qdf_mem_copy(link_frame_currpos,
2680 						     reportingsta_ie,
2681 						     reportingsta_ie_size);
2682 
2683 					link_frame_currpos +=
2684 						reportingsta_ie_size;
2685 					link_frame_currlen +=
2686 						reportingsta_ie_size;
2687 
2688 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2689 						mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) present for reporting STA but not in STA profile. Copied IE from reporting frame to link specific frame",
2690 							  reportingsta_ie[ID_POS],
2691 							  reportingsta_ie[IDEXT_POS],
2692 							  reportingsta_ie_size);
2693 					} else {
2694 						mlo_debug("IE with element ID : %u (%zu octets) present for reporting STA but not in STA profile. Copied IE from reporting frame to link specific frame",
2695 							  reportingsta_ie[ID_POS],
2696 							  reportingsta_ie_size);
2697 					}
2698 				} else {
2699 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2700 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2701 							   reportingsta_ie[ID_POS],
2702 							   reportingsta_ie[IDEXT_POS],
2703 							   reportingsta_ie_size,
2704 							   link_frame_maxsize -
2705 							   link_frame_currlen);
2706 					} else {
2707 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2708 							   reportingsta_ie[ID_POS],
2709 							   reportingsta_ie_size,
2710 							   link_frame_maxsize -
2711 							   link_frame_currlen);
2712 					}
2713 
2714 					qdf_mem_free(mlieseqpayload_copy);
2715 					return QDF_STATUS_E_NOMEM;
2716 				}
2717 			} else {
2718 				if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2719 					mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) present for reporting STA but not in STA profile. However it is in Non-Inheritance list, hence ignoring.",
2720 						  reportingsta_ie[ID_POS],
2721 						  reportingsta_ie[IDEXT_POS],
2722 						  reportingsta_ie_size);
2723 				} else {
2724 					mlo_debug("IE with element ID : %u (%zu octets) present for reporting STA but not in STA profile. However it is in Non-Inheritance list, hence ignoring.",
2725 						  reportingsta_ie[ID_POS],
2726 						  reportingsta_ie_size);
2727 				}
2728 			}
2729 		} else {
2730 			/* IE is present for reporting STA and also in STA
2731 			 * profile, copy from STA profile and flag the IE in STA
2732 			 * profile as copied (by setting EID field to 0). The
2733 			 * SSID element (with EID 0) is processed first to
2734 			 * enable this. For vendor IE, compare OUI + type +
2735 			 * subType to determine if they are the same IE.
2736 			 */
2737 			/* Note: This may be revisited in a future change, to
2738 			 * adhere to provisions in the standard for multiple
2739 			 * occurrences of a given element ID/extension element
2740 			 * ID.
2741 			 */
2742 
2743 			ret = util_validate_sta_prof_ie(sta_prof_ie,
2744 							sta_prof_iesection,
2745 							sta_prof_iesection_len);
2746 			if (QDF_IS_STATUS_ERROR(ret)) {
2747 				qdf_mem_free(mlieseqpayload_copy);
2748 				return ret;
2749 			}
2750 
2751 			sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] +
2752 				MIN_IE_LEN;
2753 
2754 			sta_prof_iesection_remlen =
2755 				sta_prof_iesection_len -
2756 					(sta_prof_ie - sta_prof_iesection);
2757 
2758 			if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
2759 			    (sta_prof_iesection_remlen >= MIN_VENDOR_TAG_LEN)) {
2760 				/* If Vendor IE also presents in STA profile,
2761 				 * then ignore the Vendor IE which is for
2762 				 * reporting STA. It only needs to copy Vendor
2763 				 * IE from STA profile to link specific frame.
2764 				 * The copy happens when going through the
2765 				 * remaining IEs.
2766 				 */
2767 				;
2768 			} else {
2769 				/* Copy IE from STA profile into link specific
2770 				 * frame.
2771 				 */
2772 				if ((link_frame_currpos + sta_prof_ie_size) <=
2773 					(link_frame + link_frame_maxsize)) {
2774 					qdf_mem_copy(link_frame_currpos,
2775 						     sta_prof_ie,
2776 						     sta_prof_ie_size);
2777 
2778 					link_frame_currpos += sta_prof_ie_size;
2779 					link_frame_currlen +=
2780 						sta_prof_ie_size;
2781 
2782 					if (reportingsta_ie[ID_POS] ==
2783 							WLAN_ELEMID_EXTN_ELEM) {
2784 						mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) for reporting STA also present in STA profile. Copied IE from STA profile to link specific frame",
2785 							  sta_prof_ie[ID_POS],
2786 							  sta_prof_ie[IDEXT_POS],
2787 							  sta_prof_ie_size);
2788 					} else {
2789 						mlo_debug("IE with element ID : %u (%zu octets) for reporting STA also present in STA profile. Copied IE from STA profile to link specific frame",
2790 							  sta_prof_ie[ID_POS],
2791 							  sta_prof_ie_size);
2792 					}
2793 
2794 					sta_prof_ie[0] = 0;
2795 				} else {
2796 					if (sta_prof_ie[ID_POS] ==
2797 							WLAN_ELEMID_EXTN_ELEM) {
2798 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2799 							   sta_prof_ie[ID_POS],
2800 							   sta_prof_ie[IDEXT_POS],
2801 							   sta_prof_ie_size,
2802 							   link_frame_maxsize -
2803 							   link_frame_currlen);
2804 					} else {
2805 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2806 							   sta_prof_ie[ID_POS],
2807 							   sta_prof_ie_size,
2808 							   link_frame_maxsize -
2809 							   link_frame_currlen);
2810 					}
2811 
2812 					qdf_mem_free(mlieseqpayload_copy);
2813 					return QDF_STATUS_E_NOMEM;
2814 				}
2815 			}
2816 		}
2817 
2818 		if (((reportingsta_ie + reportingsta_ie_size) -
2819 					frame_iesection) == frame_iesection_len)
2820 			break;
2821 
2822 		reportingsta_ie += reportingsta_ie_size;
2823 
2824 		ret = util_validate_reportingsta_ie(reportingsta_ie,
2825 						    frame_iesection,
2826 						    frame_iesection_len);
2827 		if (QDF_IS_STATUS_ERROR(ret)) {
2828 			qdf_mem_free(mlieseqpayload_copy);
2829 			return ret;
2830 		}
2831 
2832 		reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2833 			MIN_IE_LEN;
2834 	}
2835 
2836 	/* Go through the remaining unprocessed IEs in STA profile and copy them
2837 	 * to the link specific frame. The processed ones are marked with 0 in
2838 	 * the first octet. The first octet corresponds to the element ID. In
2839 	 * the case of (re)association request, the element with actual ID
2840 	 * WLAN_ELEMID_SSID(0) has already been copied to the link specific
2841 	 * frame. In the case of (re)association response, it has been verified
2842 	 * that the element with actual ID WLAN_ELEMID_SSID(0) is present
2843 	 * neither for the reporting STA nor in the STA profile.
2844 	 */
2845 	sta_prof_iesection_currpos = sta_prof_iesection;
2846 	sta_prof_iesection_remlen = sta_prof_iesection_len;
2847 
2848 	while (sta_prof_iesection_remlen > 0) {
2849 		sta_prof_ie = sta_prof_iesection_currpos;
2850 		ret = util_validate_sta_prof_ie(sta_prof_ie,
2851 						sta_prof_iesection_currpos,
2852 						sta_prof_iesection_remlen);
2853 		if (QDF_IS_STATUS_ERROR(ret)) {
2854 			qdf_mem_free(mlieseqpayload_copy);
2855 			return ret;
2856 		}
2857 
2858 		sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
2859 
2860 		if (!sta_prof_ie[0]) {
2861 			/* Skip this, since it has already been processed */
2862 			sta_prof_iesection_currpos += sta_prof_ie_size;
2863 			sta_prof_iesection_remlen -= sta_prof_ie_size;
2864 			continue;
2865 		}
2866 
2867 		/* Copy IE from STA profile into link specific frame. */
2868 		if ((link_frame_currpos + sta_prof_ie_size) <=
2869 			(link_frame + link_frame_maxsize)) {
2870 			qdf_mem_copy(link_frame_currpos,
2871 				     sta_prof_ie,
2872 				     sta_prof_ie_size);
2873 
2874 			link_frame_currpos += sta_prof_ie_size;
2875 			link_frame_currlen +=
2876 				sta_prof_ie_size;
2877 
2878 			if (reportingsta_ie[ID_POS] ==
2879 					WLAN_ELEMID_EXTN_ELEM) {
2880 				mlo_debug("IE with element ID : %u extension element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame",
2881 					  sta_prof_ie[ID_POS],
2882 					  sta_prof_ie[IDEXT_POS],
2883 					  sta_prof_ie_size);
2884 			} else {
2885 				mlo_debug("IE with element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame",
2886 					  sta_prof_ie[ID_POS],
2887 					  sta_prof_ie_size);
2888 			}
2889 
2890 			sta_prof_ie[0] = 0;
2891 		} else {
2892 			if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2893 				mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2894 					   sta_prof_ie[ID_POS],
2895 					   sta_prof_ie[IDEXT_POS],
2896 					   sta_prof_ie_size,
2897 					   link_frame_maxsize -
2898 					   link_frame_currlen);
2899 			} else {
2900 				mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2901 					   sta_prof_ie[ID_POS],
2902 					   sta_prof_ie_size,
2903 					   link_frame_maxsize -
2904 					   link_frame_currlen);
2905 			}
2906 
2907 			qdf_mem_free(mlieseqpayload_copy);
2908 			return QDF_STATUS_E_NOMEM;
2909 		}
2910 
2911 		sta_prof_iesection_currpos += sta_prof_ie_size;
2912 		sta_prof_iesection_remlen -= sta_prof_ie_size;
2913 	}
2914 
2915 	/* Copy the link MAC addr */
2916 	link_frame_hdr = (struct wlan_frame_hdr *)link_frame;
2917 
2918 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2919 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
2920 		qdf_mem_copy(link_frame_hdr->i_addr3, &link_addr,
2921 			     QDF_MAC_ADDR_SIZE);
2922 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2923 			     QDF_MAC_ADDR_SIZE);
2924 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2925 			     QDF_MAC_ADDR_SIZE);
2926 
2927 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_REQ_FC0;
2928 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_REQ_FC1;
2929 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2930 		qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
2931 			     QDF_MAC_ADDR_SIZE);
2932 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2933 			     QDF_MAC_ADDR_SIZE);
2934 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2935 			     QDF_MAC_ADDR_SIZE);
2936 
2937 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_PROBE_RESP_FC0;
2938 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_PROBE_RESP_FC1;
2939 	} else {
2940 		/* This is a (re)association response */
2941 
2942 		qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
2943 			     QDF_MAC_ADDR_SIZE);
2944 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2945 			     QDF_MAC_ADDR_SIZE);
2946 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2947 			     QDF_MAC_ADDR_SIZE);
2948 
2949 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_RESP_FC0;
2950 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_RESP_FC1;
2951 	}
2952 
2953 	mlo_debug("subtype:%u addr3:" QDF_MAC_ADDR_FMT " addr2:"
2954 		  QDF_MAC_ADDR_FMT " addr1:" QDF_MAC_ADDR_FMT,
2955 		  subtype,
2956 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr3),
2957 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr2),
2958 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr1));
2959 
2960 	/* Seq num not used so not populated */
2961 
2962 	qdf_mem_free(mlieseqpayload_copy);
2963 
2964 	*link_frame_len = link_frame_currlen;
2965 
2966 	return QDF_STATUS_SUCCESS;
2967 }
2968 
2969 QDF_STATUS
2970 util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2971 			uint8_t link_id,
2972 			struct qdf_mac_addr link_addr,
2973 			uint8_t *link_frame,
2974 			qdf_size_t link_frame_maxsize,
2975 			qdf_size_t *link_frame_len)
2976 {
2977 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2978 			(isreassoc ? WLAN_FC0_STYPE_REASSOC_REQ :
2979 				WLAN_FC0_STYPE_ASSOC_REQ),
2980 			link_id, link_addr, link_frame,
2981 			link_frame_maxsize, link_frame_len);
2982 }
2983 
2984 QDF_STATUS
2985 util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2986 			uint8_t link_id,
2987 			struct qdf_mac_addr link_addr,
2988 			uint8_t *link_frame,
2989 			qdf_size_t link_frame_maxsize,
2990 			qdf_size_t *link_frame_len)
2991 {
2992 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2993 			(isreassoc ?  WLAN_FC0_STYPE_REASSOC_RESP :
2994 				WLAN_FC0_STYPE_ASSOC_RESP),
2995 			link_id, link_addr, link_frame,
2996 			link_frame_maxsize, link_frame_len);
2997 }
2998 
2999 QDF_STATUS
3000 util_gen_link_probe_rsp(uint8_t *frame, qdf_size_t frame_len,
3001 			uint8_t link_id,
3002 			struct qdf_mac_addr link_addr,
3003 			uint8_t *link_frame,
3004 			qdf_size_t link_frame_maxsize,
3005 			qdf_size_t *link_frame_len)
3006 {
3007 	return util_gen_link_reqrsp_cmn(frame, frame_len,
3008 			 WLAN_FC0_STYPE_PROBE_RESP, link_id,
3009 			link_addr, link_frame, link_frame_maxsize,
3010 			link_frame_len);
3011 }
3012 
3013 QDF_STATUS
3014 util_find_mlie(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq,
3015 	       qdf_size_t *mlieseqlen)
3016 {
3017 	uint8_t *bufboundary;
3018 	uint8_t *ieseq;
3019 	qdf_size_t ieseqlen;
3020 	uint8_t *currie;
3021 	uint8_t *successorfrag;
3022 
3023 	if (!buf || !buflen || !mlieseq || !mlieseqlen)
3024 		return QDF_STATUS_E_NULL_VALUE;
3025 
3026 	*mlieseq = NULL;
3027 	*mlieseqlen = 0;
3028 
3029 	/* Find Multi-Link element. In case a fragment sequence is present,
3030 	 * this element will be the leading fragment.
3031 	 */
3032 	ieseq = util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
3033 				   WLAN_EXTN_ELEMID_MULTI_LINK, buf,
3034 				   buflen);
3035 
3036 	/* Even if the element is not found, we have successfully examined the
3037 	 * buffer. The caller will be provided a NULL value for the starting of
3038 	 * the Multi-Link element. Hence, we return success.
3039 	 */
3040 	if (!ieseq)
3041 		return QDF_STATUS_SUCCESS;
3042 
3043 	bufboundary = buf + buflen;
3044 
3045 	if ((ieseq + MIN_IE_LEN) > bufboundary)
3046 		return QDF_STATUS_E_INVAL;
3047 
3048 	ieseqlen = MIN_IE_LEN + ieseq[TAG_LEN_POS];
3049 
3050 	if (ieseqlen < sizeof(struct wlan_ie_multilink))
3051 		return QDF_STATUS_E_PROTO;
3052 
3053 	if ((ieseq + ieseqlen) > bufboundary)
3054 		return QDF_STATUS_E_INVAL;
3055 
3056 	/* In the next sequence of checks, if there is no space in the buffer
3057 	 * for another element after the Multi-Link element/element fragment
3058 	 * sequence, it could indicate an issue since non-MLO EHT elements
3059 	 * would be expected to follow the Multi-Link element/element fragment
3060 	 * sequence. However, this is outside of the purview of this function,
3061 	 * hence we ignore it.
3062 	 */
3063 
3064 	currie = ieseq;
3065 	successorfrag = util_get_successorfrag(currie, buf, buflen);
3066 
3067 	/* Fragmentation definitions as of IEEE802.11be D1.0 and
3068 	 * IEEE802.11REVme D0.2 are applied. Only the case where Multi-Link
3069 	 * element is present in a buffer from the core frame is considered.
3070 	 * Future changes to fragmentation, cases where the Multi-Link element
3071 	 * is present in a subelement, etc. to be reflected here if applicable
3072 	 * as and when the rules evolve.
3073 	 */
3074 	while (successorfrag) {
3075 		/* We should not be seeing a successor fragment if the length
3076 		 * of the current IE is lesser than the max.
3077 		 */
3078 		if (currie[TAG_LEN_POS] != WLAN_MAX_IE_LEN)
3079 			return QDF_STATUS_E_PROTO;
3080 
3081 		if (successorfrag[TAG_LEN_POS] == 0)
3082 			return QDF_STATUS_E_PROTO;
3083 
3084 		ieseqlen +=  (MIN_IE_LEN + successorfrag[TAG_LEN_POS]);
3085 
3086 		currie = successorfrag;
3087 		successorfrag = util_get_successorfrag(currie, buf, buflen);
3088 	}
3089 
3090 	*mlieseq = ieseq;
3091 	*mlieseqlen = ieseqlen;
3092 	return QDF_STATUS_SUCCESS;
3093 }
3094 
3095 QDF_STATUS
3096 util_find_mlie_by_variant(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq,
3097 			  qdf_size_t *mlieseqlen, int variant)
3098 {
3099 	uint8_t *ieseq;
3100 	qdf_size_t ieseqlen;
3101 	QDF_STATUS status;
3102 	int ml_variant;
3103 	qdf_size_t buf_parsed_len;
3104 
3105 	if (!buf || !buflen || !mlieseq || !mlieseqlen)
3106 		return QDF_STATUS_E_NULL_VALUE;
3107 
3108 	if (variant >= WLAN_ML_VARIANT_INVALIDSTART)
3109 		return QDF_STATUS_E_PROTO;
3110 
3111 	ieseq = NULL;
3112 	ieseqlen = 0;
3113 	*mlieseq = NULL;
3114 	*mlieseqlen = 0;
3115 	buf_parsed_len = 0;
3116 
3117 	while (buflen > buf_parsed_len) {
3118 		status = util_find_mlie(buf + buf_parsed_len,
3119 					buflen - buf_parsed_len,
3120 					&ieseq, &ieseqlen);
3121 
3122 		if (QDF_IS_STATUS_ERROR(status))
3123 			return status;
3124 
3125 		/* Even if the element is not found, we have successfully
3126 		 * examined the buffer. The caller will be provided a NULL value
3127 		 * for the starting of the Multi-Link element. Hence, we return
3128 		 * success.
3129 		 */
3130 		if (!ieseq)
3131 			return QDF_STATUS_SUCCESS;
3132 
3133 		status = util_get_mlie_variant(ieseq, ieseqlen,
3134 					       &ml_variant);
3135 		if (QDF_IS_STATUS_ERROR(status)) {
3136 			mlo_err("Unable to get Multi-link element variant");
3137 			return status;
3138 		}
3139 
3140 		if (ml_variant == variant) {
3141 			*mlieseq = ieseq;
3142 			*mlieseqlen = ieseqlen;
3143 			return QDF_STATUS_SUCCESS;
3144 		}
3145 
3146 		buf_parsed_len = ieseq + ieseqlen - buf;
3147 	}
3148 
3149 	return QDF_STATUS_E_INVAL;
3150 }
3151 
3152 QDF_STATUS
3153 util_get_mlie_common_info_len(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3154 			      uint8_t *commoninfo_len)
3155 {
3156 	struct wlan_ie_multilink *mlie_fixed;
3157 	enum wlan_ml_variant variant;
3158 	uint16_t mlcontrol;
3159 
3160 	if (!mlieseq || !mlieseqlen || !commoninfo_len)
3161 		return QDF_STATUS_E_NULL_VALUE;
3162 
3163 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3164 		return QDF_STATUS_E_INVAL;
3165 
3166 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3167 
3168 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3169 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3170 		return QDF_STATUS_E_INVAL;
3171 
3172 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3173 
3174 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3175 			       WLAN_ML_CTRL_TYPE_BITS);
3176 
3177 	if (variant != WLAN_ML_VARIANT_BASIC)
3178 		return QDF_STATUS_E_INVAL;
3179 
3180 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
3181 	 * Check if there is sufficient space in the buffer for the Common Info
3182 	 * Length and MLD MAC address.
3183 	 */
3184 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
3185 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
3186 		return QDF_STATUS_E_PROTO;
3187 
3188 	*commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3189 
3190 	return QDF_STATUS_SUCCESS;
3191 }
3192 
3193 QDF_STATUS
3194 util_get_bvmlie_bssparamchangecnt(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3195 				  bool *bssparamchangecntfound,
3196 				  uint8_t *bssparamchangecnt)
3197 {
3198 	struct wlan_ie_multilink *mlie_fixed;
3199 	enum wlan_ml_variant variant;
3200 	uint16_t mlcontrol;
3201 	uint16_t presencebitmap;
3202 	uint8_t *commoninfo;
3203 	uint8_t commoninfolen;
3204 	qdf_size_t mldcap_offset;
3205 
3206 	if (!mlieseq || !mlieseqlen || !bssparamchangecntfound ||
3207 	    !bssparamchangecnt)
3208 		return QDF_STATUS_E_NULL_VALUE;
3209 
3210 	*bssparamchangecntfound = false;
3211 	*bssparamchangecnt = 0;
3212 
3213 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3214 		return QDF_STATUS_E_INVAL;
3215 
3216 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3217 
3218 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3219 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3220 		return QDF_STATUS_E_INVAL;
3221 
3222 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3223 
3224 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3225 			       WLAN_ML_CTRL_TYPE_BITS);
3226 
3227 	if (variant != WLAN_ML_VARIANT_BASIC)
3228 		return QDF_STATUS_E_NOSUPPORT;
3229 
3230 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3231 				      WLAN_ML_CTRL_PBM_BITS);
3232 
3233 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3234 	commoninfolen = *(mlieseq + sizeof(struct wlan_ie_multilink));
3235 
3236 	mldcap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE;
3237 
3238 	mldcap_offset += QDF_MAC_ADDR_SIZE;
3239 
3240 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3241 		mldcap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3242 
3243 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3244 				mlieseqlen)
3245 			return QDF_STATUS_E_PROTO;
3246 	}
3247 
3248 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
3249 		if (commoninfolen < (mldcap_offset +
3250 				     WLAN_ML_BSSPARAMCHNGCNT_SIZE))
3251 			return QDF_STATUS_E_PROTO;
3252 
3253 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset +
3254 				WLAN_ML_BSSPARAMCHNGCNT_SIZE) >
3255 				mlieseqlen)
3256 			return QDF_STATUS_E_PROTO;
3257 		*bssparamchangecntfound = true;
3258 		*bssparamchangecnt = *(commoninfo + mldcap_offset);
3259 	}
3260 
3261 	return QDF_STATUS_SUCCESS;
3262 }
3263 
3264 QDF_STATUS
3265 util_get_mlie_variant(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3266 		      int *variant)
3267 {
3268 	struct wlan_ie_multilink *mlie_fixed;
3269 	enum wlan_ml_variant var;
3270 	uint16_t mlcontrol;
3271 
3272 	if (!mlieseq || !mlieseqlen || !variant)
3273 		return QDF_STATUS_E_NULL_VALUE;
3274 
3275 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3276 		return QDF_STATUS_E_INVAL;
3277 
3278 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3279 
3280 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3281 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3282 		return QDF_STATUS_E_INVAL;
3283 
3284 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3285 	var = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3286 			   WLAN_ML_CTRL_TYPE_BITS);
3287 
3288 	if (var >= WLAN_ML_VARIANT_INVALIDSTART)
3289 		return QDF_STATUS_E_PROTO;
3290 
3291 	*variant = var;
3292 	return QDF_STATUS_SUCCESS;
3293 }
3294 
3295 QDF_STATUS
3296 util_get_bvmlie_eml_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3297 			bool *eml_cap_found,
3298 			uint16_t *eml_cap)
3299 {
3300 	struct wlan_ie_multilink *mlie_fixed;
3301 	enum wlan_ml_variant variant;
3302 	uint16_t mlcontrol;
3303 	uint8_t eml_cap_offset;
3304 	uint8_t commoninfo_len;
3305 	uint16_t presencebitmap;
3306 
3307 	if (!mlieseq || !mlieseqlen || !eml_cap_found || !eml_cap)
3308 		return QDF_STATUS_E_NULL_VALUE;
3309 
3310 	*eml_cap = 0;
3311 	*eml_cap_found = false;
3312 
3313 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3314 		return QDF_STATUS_E_INVAL;
3315 
3316 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3317 
3318 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3319 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3320 		return QDF_STATUS_E_INVAL;
3321 
3322 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3323 
3324 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3325 			       WLAN_ML_CTRL_TYPE_BITS);
3326 
3327 	if (variant != WLAN_ML_VARIANT_BASIC)
3328 		return QDF_STATUS_E_INVAL;
3329 
3330 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3331 				      WLAN_ML_CTRL_PBM_BITS);
3332 
3333 	/* eml_cap_offset stores the offset of EML Capabilities within
3334 	 * Common Info
3335 	 */
3336 	eml_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
3337 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
3338 		eml_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3339 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
3340 		eml_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3341 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P)
3342 		eml_cap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
3343 
3344 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
3345 		/* Common Info starts at
3346 		 * mlieseq + sizeof(struct wlan_ie_multilink).
3347 		 * Check if there is sufficient space in the buffer for
3348 		 * the Common Info Length.
3349 		 */
3350 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3351 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
3352 			return QDF_STATUS_E_PROTO;
3353 
3354 		/* Check if the value indicated in the Common Info Length
3355 		 * subfield is sufficient to access the EML capabilities.
3356 		 */
3357 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3358 		if (commoninfo_len < (eml_cap_offset +
3359 				      WLAN_ML_BV_CINFO_EMLCAP_SIZE))
3360 			return QDF_STATUS_E_PROTO;
3361 
3362 		/* Common Info starts at mlieseq + sizeof(struct
3363 		 * wlan_ie_multilink). Check if there is sufficient space in
3364 		 * Common Info for the EML capability.
3365 		 */
3366 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3367 				  eml_cap_offset +
3368 				  WLAN_ML_BV_CINFO_EMLCAP_SIZE))
3369 			return QDF_STATUS_E_PROTO;
3370 
3371 		*eml_cap_found = true;
3372 		*eml_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
3373 							 sizeof(struct wlan_ie_multilink) +
3374 							 eml_cap_offset));
3375 	}
3376 	return QDF_STATUS_SUCCESS;
3377 }
3378 
3379 QDF_STATUS
3380 util_get_bvmlie_msd_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3381 			bool *msd_cap_found,
3382 			uint16_t *msd_cap)
3383 {
3384 	struct wlan_ie_multilink *mlie_fixed;
3385 	enum wlan_ml_variant variant;
3386 	uint16_t mlcontrol;
3387 	uint8_t msd_cap_offset;
3388 	uint8_t commoninfo_len;
3389 	uint16_t presencebitmap;
3390 
3391 	if (!mlieseq || !mlieseqlen || !msd_cap_found || !msd_cap)
3392 		return QDF_STATUS_E_NULL_VALUE;
3393 
3394 	*msd_cap = 0;
3395 	*msd_cap_found = false;
3396 
3397 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3398 		return QDF_STATUS_E_INVAL;
3399 
3400 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3401 
3402 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3403 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3404 		return QDF_STATUS_E_INVAL;
3405 
3406 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3407 
3408 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3409 			       WLAN_ML_CTRL_TYPE_BITS);
3410 
3411 	if (variant != WLAN_ML_VARIANT_BASIC)
3412 		return QDF_STATUS_E_INVAL;
3413 
3414 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3415 				      WLAN_ML_CTRL_PBM_BITS);
3416 
3417 	/* msd_cap_offset stores the offset of MSD capabilities within
3418 	 * Common Info
3419 	 */
3420 	msd_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
3421 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
3422 		msd_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3423 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
3424 		msd_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3425 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3426 		/* Common Info starts at
3427 		 * mlieseq + sizeof(struct wlan_ie_multilink).
3428 		 * Check if there is sufficient space in the buffer for
3429 		 * the Common Info Length.
3430 		 */
3431 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3432 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
3433 			return QDF_STATUS_E_PROTO;
3434 
3435 		/* Check if the value indicated in the Common Info Length
3436 		 * subfield is sufficient to access the MSD capabilities.
3437 		 */
3438 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3439 		if (commoninfo_len < (msd_cap_offset +
3440 				      WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3441 			return QDF_STATUS_E_PROTO;
3442 
3443 		/* Common Info starts at mlieseq + sizeof(struct
3444 		 * wlan_ie_multilink). Check if there is sufficient space in
3445 		 * Common Info for the MSD capability.
3446 		 */
3447 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3448 				  msd_cap_offset +
3449 				  WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3450 			return QDF_STATUS_E_PROTO;
3451 
3452 		*msd_cap_found = true;
3453 		*msd_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
3454 							 sizeof(struct wlan_ie_multilink) +
3455 							 msd_cap_offset));
3456 	} else {
3457 		mlo_debug("MSD caps not found in assoc rsp");
3458 	}
3459 
3460 	return QDF_STATUS_SUCCESS;
3461 }
3462 
3463 QDF_STATUS
3464 util_get_bvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3465 			   struct qdf_mac_addr *mldmacaddr)
3466 {
3467 	struct wlan_ie_multilink *mlie_fixed;
3468 	enum wlan_ml_variant variant;
3469 	uint16_t mlcontrol;
3470 	uint8_t commoninfo_len;
3471 
3472 	if (!mlieseq || !mlieseqlen || !mldmacaddr)
3473 		return QDF_STATUS_E_NULL_VALUE;
3474 
3475 	qdf_mem_zero(mldmacaddr, sizeof(*mldmacaddr));
3476 
3477 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3478 		return QDF_STATUS_E_INVAL;
3479 
3480 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3481 
3482 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3483 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3484 		return QDF_STATUS_E_INVAL;
3485 
3486 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3487 
3488 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3489 			       WLAN_ML_CTRL_TYPE_BITS);
3490 
3491 	if (variant != WLAN_ML_VARIANT_BASIC)
3492 		return QDF_STATUS_E_INVAL;
3493 
3494 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
3495 	 * Check if there is sufficient space in the buffer for the Common Info
3496 	 * Length and MLD MAC address.
3497 	 */
3498 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
3499 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
3500 		return QDF_STATUS_E_PROTO;
3501 
3502 	/* Check if the value indicated in the Common Info Length subfield is
3503 	 * sufficient to access the MLD MAC address.
3504 	 */
3505 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3506 	if (commoninfo_len < (WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE))
3507 		return QDF_STATUS_E_PROTO;
3508 
3509 	qdf_mem_copy(mldmacaddr->bytes,
3510 		     mlieseq + sizeof(struct wlan_ie_multilink) +
3511 		     WLAN_ML_BV_CINFO_LENGTH_SIZE,
3512 		     QDF_MAC_ADDR_SIZE);
3513 
3514 	return QDF_STATUS_SUCCESS;
3515 }
3516 
3517 QDF_STATUS
3518 util_get_bvmlie_primary_linkid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3519 			       bool *linkidfound, uint8_t *linkid)
3520 {
3521 	struct wlan_ie_multilink *mlie_fixed;
3522 	enum wlan_ml_variant variant;
3523 	uint16_t mlcontrol;
3524 	uint16_t presencebitmap;
3525 	uint8_t *commoninfo;
3526 	qdf_size_t commoninfolen;
3527 	uint8_t *linkidinfo;
3528 
3529 	if (!mlieseq || !mlieseqlen || !linkidfound || !linkid)
3530 		return QDF_STATUS_E_NULL_VALUE;
3531 
3532 	*linkidfound = false;
3533 	*linkid = 0;
3534 
3535 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3536 		return QDF_STATUS_E_INVAL;
3537 
3538 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3539 
3540 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3541 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3542 		return QDF_STATUS_E_INVAL;
3543 
3544 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3545 
3546 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3547 			       WLAN_ML_CTRL_TYPE_BITS);
3548 
3549 	if (variant != WLAN_ML_VARIANT_BASIC)
3550 		return QDF_STATUS_E_INVAL;
3551 
3552 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3553 				      WLAN_ML_CTRL_PBM_BITS);
3554 
3555 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3556 	commoninfolen = 0;
3557 	commoninfolen += WLAN_ML_BV_CINFO_LENGTH_SIZE;
3558 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3559 			mlieseqlen)
3560 		return QDF_STATUS_E_PROTO;
3561 
3562 	commoninfolen += QDF_MAC_ADDR_SIZE;
3563 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3564 			mlieseqlen)
3565 		return QDF_STATUS_E_PROTO;
3566 
3567 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3568 		linkidinfo = commoninfo + commoninfolen;
3569 		commoninfolen += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3570 
3571 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3572 				mlieseqlen)
3573 			return QDF_STATUS_E_PROTO;
3574 
3575 		*linkidfound = true;
3576 		*linkid = QDF_GET_BITS(linkidinfo[0],
3577 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_IDX,
3578 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_BITS);
3579 	}
3580 
3581 	return QDF_STATUS_SUCCESS;
3582 }
3583 
3584 QDF_STATUS
3585 util_get_bvmlie_mldcap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3586 		       bool *mldcapfound, uint16_t *mldcap)
3587 {
3588 	struct wlan_ie_multilink *mlie_fixed;
3589 	enum wlan_ml_variant variant;
3590 	uint16_t mlcontrol;
3591 	uint16_t presencebitmap;
3592 	uint8_t *commoninfo;
3593 	uint8_t commoninfo_len;
3594 	qdf_size_t mldcap_offset;
3595 
3596 	if (!mlieseq || !mlieseqlen || !mldcapfound || !mldcap)
3597 		return QDF_STATUS_E_NULL_VALUE;
3598 
3599 	*mldcapfound = false;
3600 	*mldcap = 0;
3601 
3602 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3603 		return QDF_STATUS_E_INVAL;
3604 
3605 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3606 
3607 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3608 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3609 		return QDF_STATUS_E_INVAL;
3610 
3611 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3612 
3613 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3614 			       WLAN_ML_CTRL_TYPE_BITS);
3615 
3616 	if (variant != WLAN_ML_VARIANT_BASIC)
3617 		return QDF_STATUS_E_NOSUPPORT;
3618 
3619 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3620 				      WLAN_ML_CTRL_PBM_BITS);
3621 
3622 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3623 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3624 	/* mldcap_offset stores the offset of MLD Capabilities within
3625 	 * Common Info
3626 	 */
3627 	mldcap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE;
3628 	mldcap_offset += QDF_MAC_ADDR_SIZE;
3629 
3630 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3631 		mldcap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3632 
3633 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3634 				mlieseqlen)
3635 			return QDF_STATUS_E_PROTO;
3636 	}
3637 
3638 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
3639 		mldcap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3640 
3641 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3642 				mlieseqlen)
3643 			return QDF_STATUS_E_PROTO;
3644 	}
3645 
3646 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3647 		mldcap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
3648 
3649 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3650 				mlieseqlen)
3651 			return QDF_STATUS_E_PROTO;
3652 	}
3653 
3654 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
3655 		mldcap_offset += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
3656 
3657 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3658 				mlieseqlen)
3659 			return QDF_STATUS_E_PROTO;
3660 	}
3661 
3662 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MLDCAPANDOP_P) {
3663 		/* Check if the value indicated in the Common Info Length
3664 		 * subfield is sufficient to access the MLD capabilities.
3665 		 */
3666 		if (commoninfo_len < (mldcap_offset +
3667 				      WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE))
3668 			return QDF_STATUS_E_PROTO;
3669 
3670 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset +
3671 					WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE) >
3672 				mlieseqlen)
3673 			return QDF_STATUS_E_PROTO;
3674 
3675 		*mldcap = qdf_le16_to_cpu(*((uint16_t *)(commoninfo + mldcap_offset)));
3676 		*mldcapfound = true;
3677 	}
3678 
3679 	return QDF_STATUS_SUCCESS;
3680 }
3681 
3682 QDF_STATUS
3683 util_get_bvmlie_persta_partner_info(uint8_t *mlieseq,
3684 				    qdf_size_t mlieseqlen,
3685 				    struct mlo_partner_info *partner_info)
3686 {
3687 	struct wlan_ie_multilink *mlie_fixed;
3688 	uint16_t mlcontrol;
3689 	enum wlan_ml_variant variant;
3690 	uint8_t *linkinfo;
3691 	qdf_size_t linkinfo_len;
3692 	struct mlo_partner_info pinfo = {0};
3693 	qdf_size_t mlieseqpayloadlen;
3694 	uint8_t *mlieseqpayload_copy;
3695 	bool is_elemfragseq;
3696 	qdf_size_t defragpayload_len;
3697 
3698 	qdf_size_t tmplen;
3699 	QDF_STATUS ret;
3700 
3701 	if (!mlieseq) {
3702 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3703 		return QDF_STATUS_E_NULL_VALUE;
3704 	}
3705 
3706 	if (!mlieseqlen) {
3707 		mlo_err("Length of Multi-Link element sequence is zero");
3708 		return QDF_STATUS_E_INVAL;
3709 	}
3710 
3711 	if (!partner_info) {
3712 		mlo_err("partner_info is NULL");
3713 		return QDF_STATUS_E_NULL_VALUE;
3714 	}
3715 
3716 	partner_info->num_partner_links = 0;
3717 
3718 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3719 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3720 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3721 		return QDF_STATUS_E_INVAL;
3722 	}
3723 
3724 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3725 
3726 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3727 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3728 		mlo_err("The element is not a Multi-Link element");
3729 		return QDF_STATUS_E_INVAL;
3730 	}
3731 
3732 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3733 
3734 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3735 			       WLAN_ML_CTRL_TYPE_BITS);
3736 
3737 	if (variant != WLAN_ML_VARIANT_BASIC) {
3738 		mlo_err("The variant value %u does not correspond to Basic Variant value %u",
3739 			variant, WLAN_ML_VARIANT_BASIC);
3740 		return QDF_STATUS_E_INVAL;
3741 	}
3742 
3743 	mlieseqpayloadlen = 0;
3744 	tmplen = 0;
3745 	is_elemfragseq = false;
3746 
3747 	ret = wlan_get_elem_fragseq_info(mlieseq,
3748 					 mlieseqlen,
3749 					 &is_elemfragseq,
3750 					 &tmplen,
3751 					 &mlieseqpayloadlen);
3752 	if (QDF_IS_STATUS_ERROR(ret))
3753 		return ret;
3754 
3755 	if (is_elemfragseq) {
3756 		if (tmplen != mlieseqlen) {
3757 			mlo_err_rl("Mismatch in values of element fragment sequence total length. Val per frag info determination: %zu octets, val passed as arg: %zu octets",
3758 				   tmplen, mlieseqlen);
3759 			return QDF_STATUS_E_INVAL;
3760 		}
3761 
3762 		if (!mlieseqpayloadlen) {
3763 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3764 			return QDF_STATUS_E_FAILURE;
3765 		}
3766 
3767 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3768 			  mlieseqpayloadlen);
3769 	} else {
3770 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3771 			mlo_err_rl("Expected presence of valid fragment sequence since Multi-Link element sequence length %zu octets is larger than frag threshold of %zu octets, however no valid fragment sequence found",
3772 				   mlieseqlen,
3773 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3774 			return QDF_STATUS_E_FAILURE;
3775 		}
3776 
3777 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3778 	}
3779 
3780 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3781 
3782 	if (!mlieseqpayload_copy) {
3783 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3784 		return QDF_STATUS_E_NOMEM;
3785 	}
3786 
3787 	if (is_elemfragseq) {
3788 		ret = wlan_defrag_elem_fragseq(false,
3789 					       mlieseq,
3790 					       mlieseqlen,
3791 					       mlieseqpayload_copy,
3792 					       mlieseqpayloadlen,
3793 					       &defragpayload_len);
3794 		if (QDF_IS_STATUS_ERROR(ret)) {
3795 			qdf_mem_free(mlieseqpayload_copy);
3796 			return ret;
3797 		}
3798 
3799 		if (defragpayload_len != mlieseqpayloadlen) {
3800 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3801 				   defragpayload_len, mlieseqpayloadlen);
3802 			qdf_mem_free(mlieseqpayload_copy);
3803 			return QDF_STATUS_E_FAILURE;
3804 		}
3805 	} else {
3806 		qdf_mem_copy(mlieseqpayload_copy,
3807 			     mlieseq + sizeof(struct ie_header) + 1,
3808 			     mlieseqpayloadlen);
3809 	}
3810 
3811 	linkinfo = NULL;
3812 	linkinfo_len = 0;
3813 
3814 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
3815 					 mlieseqpayloadlen,
3816 					 &linkinfo,
3817 					 &linkinfo_len);
3818 	if (QDF_IS_STATUS_ERROR(ret)) {
3819 		qdf_mem_free(mlieseqpayload_copy);
3820 		return ret;
3821 	}
3822 
3823 	/*
3824 	 * If Probe Request variant Multi-Link element in the Multi-Link probe
3825 	 * request does not include any per-STA profile, then all APs affiliated
3826 	 * with the same AP MLD as the AP identified in the Addr 1 or Addr 3
3827 	 * field or AP MLD ID of the Multi-Link probe request are requested
3828 	 * APs return success here
3829 	 */
3830 	if (!linkinfo) {
3831 		qdf_mem_free(mlieseqpayload_copy);
3832 		return QDF_STATUS_SUCCESS;
3833 	}
3834 
3835 	ret = util_parse_partner_info_from_linkinfo(linkinfo,
3836 						    linkinfo_len,
3837 						    &pinfo);
3838 
3839 	if (QDF_IS_STATUS_ERROR(ret)) {
3840 		qdf_mem_free(mlieseqpayload_copy);
3841 		return ret;
3842 	}
3843 
3844 	qdf_mem_copy(partner_info, &pinfo, sizeof(*partner_info));
3845 
3846 	qdf_mem_free(mlieseqpayload_copy);
3847 
3848 	return QDF_STATUS_SUCCESS;
3849 }
3850 
3851 QDF_STATUS
3852 util_get_prvmlie_persta_link_id(uint8_t *mlieseq,
3853 				qdf_size_t mlieseqlen,
3854 				struct mlo_probereq_info *probereq_info)
3855 {
3856 	struct wlan_ie_multilink *mlie_fixed;
3857 	uint16_t mlcontrol;
3858 	enum wlan_ml_variant variant;
3859 	uint8_t *linkinfo;
3860 	qdf_size_t linkinfo_len;
3861 	qdf_size_t mlieseqpayloadlen;
3862 	uint8_t *mlieseqpayload_copy;
3863 	bool is_elemfragseq;
3864 	qdf_size_t defragpayload_len;
3865 
3866 	qdf_size_t tmplen;
3867 	QDF_STATUS ret;
3868 
3869 	if (!mlieseq) {
3870 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3871 		return QDF_STATUS_E_NULL_VALUE;
3872 	}
3873 
3874 	if (!mlieseqlen) {
3875 		mlo_err("Length of Multi-Link element sequence is zero");
3876 		return QDF_STATUS_E_INVAL;
3877 	}
3878 
3879 	if (!probereq_info) {
3880 		mlo_err("probe request_info is NULL");
3881 		return QDF_STATUS_E_NULL_VALUE;
3882 	}
3883 
3884 	probereq_info->num_links = 0;
3885 
3886 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3887 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3888 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3889 		return QDF_STATUS_E_INVAL;
3890 	}
3891 
3892 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3893 
3894 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3895 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3896 		mlo_err("The element is not a Multi-Link element");
3897 		return QDF_STATUS_E_INVAL;
3898 	}
3899 
3900 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3901 
3902 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3903 			       WLAN_ML_CTRL_TYPE_BITS);
3904 
3905 	if (variant != WLAN_ML_VARIANT_PROBEREQ) {
3906 		mlo_err("The variant value %u does not correspond to Probe Request Variant value %u",
3907 			variant, WLAN_ML_VARIANT_PROBEREQ);
3908 		return QDF_STATUS_E_INVAL;
3909 	}
3910 
3911 	mlieseqpayloadlen = 0;
3912 	tmplen = 0;
3913 	is_elemfragseq = false;
3914 
3915 	ret = wlan_get_elem_fragseq_info(mlieseq,
3916 					 mlieseqlen,
3917 					 &is_elemfragseq,
3918 					 &tmplen,
3919 					 &mlieseqpayloadlen);
3920 	if (QDF_IS_STATUS_ERROR(ret))
3921 		return ret;
3922 
3923 	if (is_elemfragseq) {
3924 		if (tmplen != mlieseqlen) {
3925 			mlo_err_rl("Mismatch in values of element fragment sequence total length. Val per frag info determination: %zu octets, val passed as arg: %zu octets",
3926 				   tmplen, mlieseqlen);
3927 			return QDF_STATUS_E_INVAL;
3928 		}
3929 
3930 		if (!mlieseqpayloadlen) {
3931 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3932 			return QDF_STATUS_E_FAILURE;
3933 		}
3934 
3935 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3936 			  mlieseqpayloadlen);
3937 	} else {
3938 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3939 			mlo_err_rl("Expected presence of valid fragment sequence since Multi-Link element sequence length %zu octets is larger than frag threshold of %zu octets, however no valid fragment sequence found",
3940 				   mlieseqlen,
3941 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3942 			return QDF_STATUS_E_FAILURE;
3943 		}
3944 
3945 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3946 	}
3947 
3948 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3949 
3950 	if (!mlieseqpayload_copy) {
3951 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3952 		return QDF_STATUS_E_NOMEM;
3953 	}
3954 
3955 	if (is_elemfragseq) {
3956 		ret = wlan_defrag_elem_fragseq(false,
3957 					       mlieseq,
3958 					       mlieseqlen,
3959 					       mlieseqpayload_copy,
3960 					       mlieseqpayloadlen,
3961 					       &defragpayload_len);
3962 		if (QDF_IS_STATUS_ERROR(ret)) {
3963 			qdf_mem_free(mlieseqpayload_copy);
3964 			return ret;
3965 		}
3966 
3967 		if (defragpayload_len != mlieseqpayloadlen) {
3968 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3969 				   defragpayload_len, mlieseqpayloadlen);
3970 			qdf_mem_free(mlieseqpayload_copy);
3971 			return QDF_STATUS_E_FAILURE;
3972 		}
3973 	} else {
3974 		qdf_mem_copy(mlieseqpayload_copy,
3975 			     mlieseq + sizeof(struct ie_header) + 1,
3976 			     mlieseqpayloadlen);
3977 	}
3978 
3979 	linkinfo = NULL;
3980 	linkinfo_len = 0;
3981 	ret = util_parse_prv_multi_link_ctrl(mlieseqpayload_copy,
3982 					     mlieseqpayloadlen,
3983 					     &linkinfo,
3984 					     &linkinfo_len);
3985 	if (QDF_IS_STATUS_ERROR(ret)) {
3986 		qdf_mem_free(mlieseqpayload_copy);
3987 		return ret;
3988 	}
3989 
3990 	/* In case Link Info is absent, the number of links will remain
3991 	 * zero.
3992 	 */
3993 	if (!linkinfo) {
3994 		mlo_debug("No link info present");
3995 		qdf_mem_free(mlieseqpayload_copy);
3996 		return QDF_STATUS_SUCCESS;
3997 	}
3998 
3999 	ret = util_parse_probereq_info_from_linkinfo(linkinfo,
4000 						     linkinfo_len,
4001 						     probereq_info);
4002 
4003 	if (QDF_IS_STATUS_ERROR(ret)) {
4004 		qdf_mem_free(mlieseqpayload_copy);
4005 		return ret;
4006 	}
4007 
4008 	qdf_mem_free(mlieseqpayload_copy);
4009 
4010 	return QDF_STATUS_SUCCESS;
4011 }
4012 
4013 QDF_STATUS
4014 util_get_prvmlie_mldid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
4015 		       bool *mldidfound, uint8_t *mldid)
4016 {
4017 	struct wlan_ie_multilink *mlie_fixed;
4018 	enum wlan_ml_variant variant;
4019 	uint16_t mlcontrol;
4020 	uint16_t presencebitmap;
4021 	uint8_t *commoninfo;
4022 	qdf_size_t commoninfolen;
4023 
4024 	if (!mlieseq || !mlieseqlen || !mldidfound || !mldid)
4025 		return QDF_STATUS_E_NULL_VALUE;
4026 
4027 	*mldidfound = false;
4028 	*mldid = 0;
4029 
4030 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
4031 		return QDF_STATUS_E_INVAL;
4032 
4033 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
4034 
4035 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
4036 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
4037 		return QDF_STATUS_E_INVAL;
4038 
4039 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
4040 
4041 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
4042 			       WLAN_ML_CTRL_TYPE_BITS);
4043 
4044 	if (variant != WLAN_ML_VARIANT_PROBEREQ)
4045 		return QDF_STATUS_E_NOSUPPORT;
4046 
4047 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
4048 				      WLAN_ML_CTRL_PBM_BITS);
4049 
4050 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
4051 	commoninfolen = WLAN_ML_PRV_CINFO_LENGTH_SIZE;
4052 
4053 	if (presencebitmap & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
4054 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen +
4055 		     WLAN_ML_PRV_CINFO_MLDID_SIZE) >
4056 		    mlieseqlen)
4057 			return QDF_STATUS_E_PROTO;
4058 
4059 		*mldid = *((uint8_t *)(commoninfo + commoninfolen));
4060 		commoninfolen += WLAN_ML_PRV_CINFO_MLDID_SIZE;
4061 
4062 		*mldidfound = true;
4063 	}
4064 
4065 	return QDF_STATUS_SUCCESS;
4066 }
4067 
4068 QDF_STATUS util_get_rvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
4069 				      struct qdf_mac_addr *mldmacaddr,
4070 				      bool *is_mldmacaddr_found)
4071 {
4072 	struct wlan_ie_multilink *mlie_fixed;
4073 	enum wlan_ml_variant variant;
4074 	uint16_t mlcontrol;
4075 	uint16_t presencebitmap;
4076 	qdf_size_t rv_cinfo_len;
4077 
4078 	if (!mlieseq || !mlieseqlen || !mldmacaddr || !is_mldmacaddr_found)
4079 		return QDF_STATUS_E_NULL_VALUE;
4080 
4081 	*is_mldmacaddr_found = false;
4082 	qdf_mem_zero(mldmacaddr, sizeof(*mldmacaddr));
4083 
4084 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
4085 		return QDF_STATUS_E_INVAL;
4086 
4087 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
4088 
4089 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
4090 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
4091 		return QDF_STATUS_E_INVAL;
4092 
4093 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
4094 
4095 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
4096 			       WLAN_ML_CTRL_TYPE_BITS);
4097 
4098 	if (variant != WLAN_ML_VARIANT_RECONFIG)
4099 		return QDF_STATUS_E_INVAL;
4100 
4101 	/* ML Reconfig Common Info Length field present */
4102 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_RV_CINFO_LENGTH_SIZE) >
4103 	    mlieseqlen)
4104 		return QDF_STATUS_E_PROTO;
4105 
4106 	rv_cinfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
4107 
4108 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
4109 				      WLAN_ML_CTRL_PBM_BITS);
4110 
4111 	/* Check if MLD mac address is present */
4112 	if (presencebitmap & WLAN_ML_RV_CTRL_PBM_MLDMACADDR_P) {
4113 		/* Check if the value indicated in the Common Info Length
4114 		 * subfield is sufficient to access the MLD MAC address.
4115 		 */
4116 		if (rv_cinfo_len < (WLAN_ML_RV_CINFO_LENGTH_SIZE +
4117 				    QDF_MAC_ADDR_SIZE))
4118 			return QDF_STATUS_E_PROTO;
4119 
4120 		if ((sizeof(struct wlan_ie_multilink) +
4121 		     WLAN_ML_RV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE) >
4122 			mlieseqlen)
4123 			return QDF_STATUS_E_PROTO;
4124 
4125 		qdf_mem_copy(mldmacaddr->bytes,
4126 			     mlieseq + sizeof(struct wlan_ie_multilink) +
4127 			     WLAN_ML_RV_CINFO_LENGTH_SIZE,
4128 			     QDF_MAC_ADDR_SIZE);
4129 		*is_mldmacaddr_found = true;
4130 	}
4131 
4132 	return QDF_STATUS_SUCCESS;
4133 }
4134 
4135 static QDF_STATUS
4136 util_parse_rv_multi_link_ctrl(uint8_t *mlieseqpayload,
4137 			      qdf_size_t mlieseqpayloadlen,
4138 			      uint8_t **link_info,
4139 			      qdf_size_t *link_info_len)
4140 {
4141 	qdf_size_t parsed_payload_len, rv_cinfo_len;
4142 	uint16_t mlcontrol;
4143 	uint16_t presence_bm;
4144 
4145 	/* This helper returns the location(s) and length(s) of (sub)field(s)
4146 	 * inferable after parsing the Multi Link element Control field. These
4147 	 * location(s) and length(s) is/are in reference to the payload section
4148 	 * of the Multi Link element (after defragmentation, if applicable).
4149 	 * Here, the payload is the point after the element ID extension of the
4150 	 * Multi Link element, and includes the payloads of all subsequent
4151 	 * fragments (if any) but not the headers of those fragments.
4152 	 *
4153 	 * Currently, the helper returns the location and length of the Link
4154 	 * Info field in the Multi Link element sequence. Other (sub)field(s)
4155 	 * can be added later as required.
4156 	 */
4157 	if (!mlieseqpayload) {
4158 		mlo_err("ML seq payload pointer is NULL");
4159 		return QDF_STATUS_E_NULL_VALUE;
4160 	}
4161 
4162 	if (!mlieseqpayloadlen) {
4163 		mlo_err("ML seq payload len is 0");
4164 		return QDF_STATUS_E_INVAL;
4165 	}
4166 
4167 	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
4168 		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
4169 			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
4170 		return QDF_STATUS_E_PROTO;
4171 	}
4172 
4173 	parsed_payload_len = 0;
4174 
4175 	qdf_mem_copy(&mlcontrol, mlieseqpayload, WLAN_ML_CTRL_SIZE);
4176 	mlcontrol = qdf_le16_to_cpu(mlcontrol);
4177 	parsed_payload_len += WLAN_ML_CTRL_SIZE;
4178 
4179 	if (mlieseqpayloadlen <
4180 			(parsed_payload_len + WLAN_ML_RV_CINFO_LENGTH_SIZE)) {
4181 		mlo_err_rl("ML rv seq payload len %zu insufficient for common info length size %u after parsed payload len %zu.",
4182 			   mlieseqpayloadlen,
4183 			   WLAN_ML_RV_CINFO_LENGTH_SIZE,
4184 			   parsed_payload_len);
4185 		return QDF_STATUS_E_PROTO;
4186 	}
4187 
4188 	rv_cinfo_len = *(mlieseqpayload + parsed_payload_len);
4189 	parsed_payload_len += WLAN_ML_RV_CINFO_LENGTH_SIZE;
4190 
4191 	presence_bm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
4192 				   WLAN_ML_CTRL_PBM_BITS);
4193 
4194 	/* Check if MLD MAC address is present */
4195 	if (presence_bm & WLAN_ML_RV_CTRL_PBM_MLDMACADDR_P) {
4196 		/* Check if the value indicated in the Common Info Length
4197 		 * subfield is sufficient to access the MLD MAC address.
4198 		 * Note: In D3.0, MLD MAC address will not be present
4199 		 * in ML Reconfig IE. But for code completeness, we
4200 		 * should have below code to sanity check.
4201 		 */
4202 		if (rv_cinfo_len < (WLAN_ML_RV_CINFO_LENGTH_SIZE +
4203 				    QDF_MAC_ADDR_SIZE)) {
4204 			mlo_err_rl("ML rv Common Info Length %zu insufficient to access MLD MAC addr size %u.",
4205 				   rv_cinfo_len,
4206 				   QDF_MAC_ADDR_SIZE);
4207 			return QDF_STATUS_E_PROTO;
4208 		}
4209 
4210 		if (mlieseqpayloadlen <
4211 				(parsed_payload_len +
4212 				 QDF_MAC_ADDR_SIZE)) {
4213 			mlo_err_rl("ML seq payload len %zu insufficient for MLD MAC size %u after parsed payload len %zu.",
4214 				   mlieseqpayloadlen,
4215 				   QDF_MAC_ADDR_SIZE,
4216 				   parsed_payload_len);
4217 			return QDF_STATUS_E_PROTO;
4218 		}
4219 
4220 		parsed_payload_len += QDF_MAC_ADDR_SIZE;
4221 	}
4222 
4223 	/* At present, we only handle MAC address field in common info field.
4224 	 * To be compatible with future spec updating, if new items are added
4225 	 * to common info, below log will highlight the spec change.
4226 	 */
4227 	if (rv_cinfo_len != (parsed_payload_len - WLAN_ML_CTRL_SIZE))
4228 		mlo_debug_rl("ML rv seq common info len %zu doesn't match with expected common info len %zu",
4229 			     rv_cinfo_len,
4230 			     parsed_payload_len - WLAN_ML_CTRL_SIZE);
4231 
4232 	if (mlieseqpayloadlen < (WLAN_ML_CTRL_SIZE + rv_cinfo_len)) {
4233 		mlo_err_rl("ML seq payload len %zu insufficient for rv link info after parsed mutli-link control %u and indicated Common Info length %zu",
4234 			   mlieseqpayloadlen,
4235 			   WLAN_ML_CTRL_SIZE,
4236 			   rv_cinfo_len);
4237 		return QDF_STATUS_E_PROTO;
4238 	}
4239 	/* Update parsed_payload_len to reflect the actual bytes in common info
4240 	 * field to be compatible with future spec updating.
4241 	 */
4242 	parsed_payload_len = WLAN_ML_CTRL_SIZE + rv_cinfo_len;
4243 
4244 	if (link_info_len) {
4245 		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
4246 		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu, rv_cinfo_len %zu ",
4247 			  *link_info_len, parsed_payload_len, rv_cinfo_len);
4248 	}
4249 
4250 	if (mlieseqpayloadlen == parsed_payload_len) {
4251 		mlo_debug("No Link Info field present");
4252 		if (link_info)
4253 			*link_info = NULL;
4254 
4255 		return QDF_STATUS_SUCCESS;
4256 	}
4257 
4258 	if (link_info)
4259 		*link_info = mlieseqpayload + parsed_payload_len;
4260 
4261 	return QDF_STATUS_SUCCESS;
4262 }
4263 
4264 static QDF_STATUS
4265 util_parse_rvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
4266 					qdf_size_t subelempayloadlen,
4267 					uint8_t *linkid,
4268 					bool *is_macaddr_valid,
4269 					struct qdf_mac_addr *macaddr,
4270 					bool *is_ap_removal_timer_valid,
4271 					uint16_t *ap_removal_timer)
4272 {
4273 	qdf_size_t parsed_payload_len = 0, sta_info_len;
4274 	qdf_size_t parsed_sta_info_len;
4275 	uint16_t stacontrol;
4276 	uint8_t completeprofile;
4277 
4278 	/* This helper returns the location(s) and where required, the length(s)
4279 	 * of (sub)field(s) inferable after parsing the STA Control field in the
4280 	 * per-STA profile subelement. These location(s) and length(s) is/are in
4281 	 * reference to the payload section of the per-STA profile subelement
4282 	 * (after defragmentation, if applicable).  Here, the payload is the
4283 	 * point after the subelement length in the subelement, and includes the
4284 	 * payloads of all subsequent fragments (if any) but not the headers of
4285 	 * those fragments.
4286 	 *
4287 	 * Currently, the helper returns the link ID, MAC address, AP removal
4288 	 * timer and STA profile. More (sub)fields can be added when required.
4289 	 */
4290 	if (!subelempayload) {
4291 		mlo_err("Pointer to subelement payload is NULL");
4292 		return QDF_STATUS_E_NULL_VALUE;
4293 	}
4294 
4295 	if (!subelempayloadlen) {
4296 		mlo_err("Length of subelement payload is zero");
4297 		return QDF_STATUS_E_INVAL;
4298 	}
4299 
4300 	if (subelempayloadlen < WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE) {
4301 		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
4302 			   subelempayloadlen,
4303 			   WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE);
4304 		return QDF_STATUS_E_PROTO;
4305 	}
4306 
4307 	parsed_payload_len = 0;
4308 	qdf_mem_copy(&stacontrol,
4309 		     subelempayload,
4310 		     WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE);
4311 
4312 	stacontrol = qdf_le16_to_cpu(stacontrol);
4313 	parsed_payload_len += WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE;
4314 
4315 	if (linkid)
4316 		*linkid = QDF_GET_BITS(stacontrol,
4317 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
4318 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
4319 
4320 	/* Check if this a complete profile */
4321 	completeprofile = QDF_GET_BITS(stacontrol,
4322 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
4323 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
4324 
4325 	if (is_macaddr_valid)
4326 		*is_macaddr_valid = false;
4327 	if (subelempayloadlen < parsed_payload_len +
4328 		WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE) {
4329 		mlo_err_rl("Length of subelement payload %zu octets not sufficient for sta info length of size %u octets after parsed payload length of %zu octets.",
4330 			   subelempayloadlen, WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE,
4331 			   parsed_payload_len);
4332 		return QDF_STATUS_E_PROTO;
4333 	}
4334 
4335 	sta_info_len = *(subelempayload + parsed_payload_len);
4336 	parsed_payload_len += WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE;
4337 	parsed_sta_info_len = WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE;
4338 
4339 	/* Check STA MAC address present bit */
4340 	if (QDF_GET_BITS(stacontrol,
4341 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_STAMACADDRP_IDX,
4342 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_STAMACADDRP_BITS)) {
4343 		if (sta_info_len < (parsed_sta_info_len + QDF_MAC_ADDR_SIZE)) {
4344 			mlo_err_rl("Length of sta info len %zu octets not sufficient to contain MAC address of size %u octets after parsed sta info length of %zu octets.",
4345 				   sta_info_len, QDF_MAC_ADDR_SIZE,
4346 				   parsed_sta_info_len);
4347 			return QDF_STATUS_E_PROTO;
4348 		}
4349 
4350 		if (subelempayloadlen <
4351 				(parsed_payload_len + QDF_MAC_ADDR_SIZE)) {
4352 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain MAC address of size %u octets after parsed payload length of %zu octets.",
4353 				   subelempayloadlen, QDF_MAC_ADDR_SIZE,
4354 				   parsed_payload_len);
4355 			return QDF_STATUS_E_PROTO;
4356 		}
4357 
4358 		if (macaddr) {
4359 			qdf_mem_copy(macaddr->bytes,
4360 				     subelempayload + parsed_payload_len,
4361 				     QDF_MAC_ADDR_SIZE);
4362 			mlo_nofl_debug("Copied MAC address: " QDF_MAC_ADDR_FMT,
4363 				       QDF_MAC_ADDR_REF(macaddr->bytes));
4364 
4365 			if (is_macaddr_valid)
4366 				*is_macaddr_valid = true;
4367 		}
4368 
4369 		parsed_payload_len += QDF_MAC_ADDR_SIZE;
4370 		parsed_sta_info_len += QDF_MAC_ADDR_SIZE;
4371 	}
4372 
4373 	/* Check AP Removal timer present bit */
4374 	if (QDF_GET_BITS(stacontrol,
4375 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_APREMOVALTIMERP_IDX,
4376 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_APREMOVALTIMERP_BITS)) {
4377 		if (sta_info_len <
4378 				(parsed_sta_info_len +
4379 				 WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_APREMOVALTIMER_SIZE)) {
4380 			mlo_err_rl("Length of sta info len %zu octets not sufficient to contain AP removal timer of size %u octets after parsed sta info length of %zu octets.",
4381 				   sta_info_len, WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_APREMOVALTIMER_SIZE,
4382 				   parsed_sta_info_len);
4383 			return QDF_STATUS_E_PROTO;
4384 		}
4385 
4386 		if (subelempayloadlen <
4387 				(parsed_payload_len +
4388 				 WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_APREMOVALTIMER_SIZE)) {
4389 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain AP removal timer of size %u octets after parsed payload length of %zu octets.",
4390 				   subelempayloadlen,
4391 				   WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_APREMOVALTIMER_SIZE,
4392 				   parsed_payload_len);
4393 			return QDF_STATUS_E_PROTO;
4394 		}
4395 
4396 		if (ap_removal_timer) {
4397 			qdf_mem_copy(ap_removal_timer,
4398 				     subelempayload + parsed_payload_len,
4399 				     WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_APREMOVALTIMER_SIZE);
4400 
4401 			if (is_ap_removal_timer_valid)
4402 				*is_ap_removal_timer_valid = true;
4403 		}
4404 
4405 		parsed_payload_len +=
4406 			WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_APREMOVALTIMER_SIZE;
4407 		parsed_sta_info_len += WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_APREMOVALTIMER_SIZE;
4408 	}
4409 	/* At present, we only handle link MAC address field and ap removal
4410 	 * timer tbtt field parsing. To be compatible with future spec
4411 	 * updating, if new items are added to sta info, below log will
4412 	 * highlight the spec change.
4413 	 */
4414 	if (sta_info_len != (parsed_payload_len -
4415 			     WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE))
4416 		mlo_debug_rl("Length of sta info len %zu octets not match parsed payload length of %zu octets.",
4417 			     sta_info_len,
4418 			     parsed_payload_len -
4419 			     WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE);
4420 
4421 	return QDF_STATUS_SUCCESS;
4422 }
4423 
4424 static QDF_STATUS
4425 util_parse_rv_info_from_linkinfo(uint8_t *linkinfo,
4426 				 qdf_size_t linkinfo_len,
4427 				 struct ml_rv_info *reconfig_info)
4428 {
4429 	uint8_t linkid;
4430 	uint8_t *linkinfo_currpos;
4431 	qdf_size_t linkinfo_remlen;
4432 	bool is_subelemfragseq;
4433 	uint8_t subelemid;
4434 	qdf_size_t subelemseqtotallen;
4435 	qdf_size_t subelemseqpayloadlen;
4436 	qdf_size_t defragpayload_len;
4437 	QDF_STATUS ret;
4438 	struct qdf_mac_addr mac_addr;
4439 	bool is_macaddr_valid;
4440 	bool is_ap_removal_timer_valid;
4441 	uint16_t ap_removal_timer;
4442 
4443 	/* This helper function parses probe request info from the per-STA prof
4444 	 * present (if any) in the Link Info field in the payload of a Multi
4445 	 * Link element (after defragmentation if required). The caller should
4446 	 * pass a copy of the payload so that inline defragmentation of
4447 	 * subelements can be carried out if required. The subelement
4448 	 * defragmentation (if applicable) in this Control Path helper is
4449 	 * required for maintainability, accuracy and eliminating current and
4450 	 * future per-field-access multi-level fragment boundary checks and
4451 	 * adjustments, given the complex format of Multi Link elements. It is
4452 	 * also most likely to be required mainly at the client side.
4453 	 * Fragmentation is currently unlikely to be required for subelements
4454 	 * in Reconfiguration variant Multi-Link elements, but it should be
4455 	 * handled in order to be future ready.
4456 	 */
4457 	if (!linkinfo) {
4458 		mlo_err("linkinfo is NULL");
4459 		return QDF_STATUS_E_NULL_VALUE;
4460 	}
4461 
4462 	if (!linkinfo_len) {
4463 		mlo_err("linkinfo_len is zero");
4464 		return QDF_STATUS_E_NULL_VALUE;
4465 	}
4466 
4467 	if (!reconfig_info) {
4468 		mlo_err("ML reconfig info is NULL");
4469 		return QDF_STATUS_E_NULL_VALUE;
4470 	}
4471 
4472 	reconfig_info->num_links = 0;
4473 	linkinfo_currpos = linkinfo;
4474 	linkinfo_remlen = linkinfo_len;
4475 
4476 	while (linkinfo_remlen) {
4477 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
4478 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
4479 				   linkinfo_remlen,
4480 				   sizeof(struct subelem_header));
4481 			return QDF_STATUS_E_PROTO;
4482 		}
4483 
4484 		subelemid = linkinfo_currpos[ID_POS];
4485 		is_subelemfragseq = false;
4486 		subelemseqtotallen = 0;
4487 		subelemseqpayloadlen = 0;
4488 
4489 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
4490 						    linkinfo_currpos,
4491 						    linkinfo_remlen,
4492 						    &is_subelemfragseq,
4493 						    &subelemseqtotallen,
4494 						    &subelemseqpayloadlen);
4495 		if (QDF_IS_STATUS_ERROR(ret))
4496 			return ret;
4497 
4498 		if (qdf_unlikely(is_subelemfragseq)) {
4499 			if (!subelemseqpayloadlen) {
4500 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
4501 				return QDF_STATUS_E_FAILURE;
4502 			}
4503 
4504 			mlo_debug("Subelement fragment sequence found with payload len %zu",
4505 				  subelemseqpayloadlen);
4506 
4507 			ret = wlan_defrag_subelem_fragseq(true,
4508 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
4509 							  linkinfo_currpos,
4510 							  linkinfo_remlen,
4511 							  NULL,
4512 							  0,
4513 							  &defragpayload_len);
4514 
4515 			if (QDF_IS_STATUS_ERROR(ret))
4516 				return ret;
4517 
4518 			if (defragpayload_len != subelemseqpayloadlen) {
4519 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
4520 					   defragpayload_len,
4521 					   subelemseqpayloadlen);
4522 				return QDF_STATUS_E_FAILURE;
4523 			}
4524 
4525 			/* Adjust linkinfo_remlen to reflect removal of all
4526 			 * subelement headers except the header of the lead
4527 			 * subelement.
4528 			 */
4529 			linkinfo_remlen -= (subelemseqtotallen -
4530 					    subelemseqpayloadlen -
4531 					    sizeof(struct subelem_header));
4532 		} else {
4533 			if (linkinfo_remlen <
4534 				(sizeof(struct subelem_header) +
4535 				linkinfo_currpos[TAG_LEN_POS])) {
4536 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
4537 					   linkinfo_remlen,
4538 					   sizeof(struct subelem_header) +
4539 					   linkinfo_currpos[TAG_LEN_POS]);
4540 				return QDF_STATUS_E_PROTO;
4541 			}
4542 
4543 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
4544 		}
4545 
4546 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
4547 			struct ml_rv_partner_link_info *link_info;
4548 			is_macaddr_valid = false;
4549 			is_ap_removal_timer_valid = false;
4550 			ret = util_parse_rvmlie_perstaprofile_stactrl(linkinfo_currpos +
4551 								      sizeof(struct subelem_header),
4552 								      subelemseqpayloadlen,
4553 								      &linkid,
4554 								      &is_macaddr_valid,
4555 								      &mac_addr,
4556 								      &is_ap_removal_timer_valid,
4557 								      &ap_removal_timer);
4558 			if (QDF_IS_STATUS_ERROR(ret))
4559 				return ret;
4560 			if (reconfig_info->num_links >=
4561 						WLAN_UMAC_MLO_MAX_VDEVS) {
4562 				mlo_err("num_link %d invalid",
4563 					reconfig_info->num_links);
4564 				return QDF_STATUS_E_INVAL;
4565 			}
4566 			link_info =
4567 			&reconfig_info->link_info[reconfig_info->num_links];
4568 			link_info->link_id = linkid;
4569 			link_info->is_ap_removal_timer_p = is_ap_removal_timer_valid;
4570 			if (is_macaddr_valid)
4571 				qdf_copy_macaddr(&link_info->link_mac_addr,
4572 						 &mac_addr);
4573 
4574 			if (is_ap_removal_timer_valid)
4575 				link_info->ap_removal_timer = ap_removal_timer;
4576 			else
4577 				mlo_warn_rl("AP removal timer not found in STA Info field of per-STA profile with link ID %u",
4578 					    linkid);
4579 
4580 			mlo_debug("Per-STA Profile Link ID: %u AP removal timer present: %d AP removal timer: %u",
4581 				  link_info->link_id,
4582 				  link_info->is_ap_removal_timer_p,
4583 				  link_info->ap_removal_timer);
4584 
4585 			reconfig_info->num_links++;
4586 		}
4587 
4588 		linkinfo_remlen -= (sizeof(struct subelem_header) +
4589 				    subelemseqpayloadlen);
4590 		linkinfo_currpos += (sizeof(struct subelem_header) +
4591 				     subelemseqpayloadlen);
4592 	}
4593 
4594 	mlo_debug("Number of ML probe request links found=%u",
4595 		  reconfig_info->num_links);
4596 
4597 	return QDF_STATUS_SUCCESS;
4598 }
4599 
4600 QDF_STATUS util_get_rvmlie_persta_link_info(uint8_t *mlieseq,
4601 					    qdf_size_t mlieseqlen,
4602 					    struct ml_rv_info *reconfig_info)
4603 {
4604 	struct wlan_ie_multilink *mlie_fixed;
4605 	uint16_t mlcontrol;
4606 	enum wlan_ml_variant variant;
4607 	uint8_t *linkinfo;
4608 	qdf_size_t linkinfo_len;
4609 	struct ml_rv_info rinfo = {0};
4610 	qdf_size_t mlieseqpayloadlen;
4611 	uint8_t *mlieseqpayload_copy;
4612 	bool is_elemfragseq;
4613 	qdf_size_t defragpayload_len;
4614 	qdf_size_t tmplen;
4615 	QDF_STATUS ret;
4616 
4617 	if (!mlieseq) {
4618 		mlo_err("Pointer to Multi-Link element sequence is NULL");
4619 		return QDF_STATUS_E_NULL_VALUE;
4620 	}
4621 
4622 	if (!mlieseqlen) {
4623 		mlo_err("Length of Multi-Link element sequence is zero");
4624 		return QDF_STATUS_E_INVAL;
4625 	}
4626 
4627 	if (!reconfig_info) {
4628 		mlo_err("reconfig_info is NULL");
4629 		return QDF_STATUS_E_NULL_VALUE;
4630 	}
4631 
4632 	reconfig_info->num_links = 0;
4633 
4634 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
4635 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
4636 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
4637 		return QDF_STATUS_E_INVAL;
4638 	}
4639 
4640 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
4641 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
4642 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK) {
4643 		mlo_err("The element is not a Multi-Link element");
4644 		return QDF_STATUS_E_INVAL;
4645 	}
4646 
4647 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
4648 
4649 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
4650 			       WLAN_ML_CTRL_TYPE_BITS);
4651 
4652 	if (variant != WLAN_ML_VARIANT_RECONFIG) {
4653 		mlo_err("The variant value %u does not correspond to Reconfig Variant value %u",
4654 			variant, WLAN_ML_VARIANT_RECONFIG);
4655 		return QDF_STATUS_E_INVAL;
4656 	}
4657 
4658 	mlieseqpayloadlen = 0;
4659 	tmplen = 0;
4660 	is_elemfragseq = false;
4661 
4662 	ret = wlan_get_elem_fragseq_info(mlieseq,
4663 					 mlieseqlen,
4664 					 &is_elemfragseq,
4665 					 &tmplen,
4666 					 &mlieseqpayloadlen);
4667 
4668 	if (QDF_IS_STATUS_ERROR(ret))
4669 		return ret;
4670 
4671 	if (qdf_unlikely(is_elemfragseq)) {
4672 		if (tmplen != mlieseqlen) {
4673 			mlo_err_rl("Mismatch in values of element fragment sequence total length. Val per frag info determination: %zu octets, val passed as arg: %zu octets",
4674 				   tmplen, mlieseqlen);
4675 			return QDF_STATUS_E_INVAL;
4676 		}
4677 
4678 		if (!mlieseqpayloadlen) {
4679 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
4680 			return QDF_STATUS_E_FAILURE;
4681 		}
4682 
4683 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
4684 			  mlieseqpayloadlen);
4685 	} else {
4686 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
4687 			mlo_err_rl("Expected presence of valid fragment sequence since Multi-Link element sequence length %zu octets is larger than frag threshold of %zu octets, however no valid fragment sequence found",
4688 				   mlieseqlen,
4689 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
4690 			return QDF_STATUS_E_FAILURE;
4691 		}
4692 
4693 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
4694 	}
4695 
4696 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
4697 
4698 	if (!mlieseqpayload_copy) {
4699 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
4700 		return QDF_STATUS_E_NOMEM;
4701 	}
4702 
4703 	if (qdf_unlikely(is_elemfragseq)) {
4704 		ret = wlan_defrag_elem_fragseq(false,
4705 					       mlieseq,
4706 					       mlieseqlen,
4707 					       mlieseqpayload_copy,
4708 					       mlieseqpayloadlen,
4709 					       &defragpayload_len);
4710 		if (QDF_IS_STATUS_ERROR(ret)) {
4711 			qdf_mem_free(mlieseqpayload_copy);
4712 			return ret;
4713 		}
4714 
4715 		if (defragpayload_len != mlieseqpayloadlen) {
4716 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
4717 				   defragpayload_len, mlieseqpayloadlen);
4718 			qdf_mem_free(mlieseqpayload_copy);
4719 			return QDF_STATUS_E_FAILURE;
4720 		}
4721 	} else {
4722 		qdf_mem_copy(mlieseqpayload_copy,
4723 			     mlieseq + sizeof(struct ie_header) + 1,
4724 			     mlieseqpayloadlen);
4725 	}
4726 
4727 	linkinfo = NULL;
4728 	linkinfo_len = 0;
4729 
4730 	ret = util_parse_rv_multi_link_ctrl(mlieseqpayload_copy,
4731 					    mlieseqpayloadlen,
4732 					    &linkinfo,
4733 					    &linkinfo_len);
4734 	if (QDF_IS_STATUS_ERROR(ret)) {
4735 		qdf_mem_free(mlieseqpayload_copy);
4736 		return ret;
4737 	}
4738 
4739 	/* In case Link Info is absent, the number of links will remain
4740 	 * zero.
4741 	 */
4742 	if (!linkinfo) {
4743 		qdf_mem_free(mlieseqpayload_copy);
4744 		return QDF_STATUS_SUCCESS;
4745 	}
4746 
4747 	ret = util_parse_rv_info_from_linkinfo(linkinfo, linkinfo_len, &rinfo);
4748 	if (QDF_IS_STATUS_ERROR(ret)) {
4749 		qdf_mem_free(mlieseqpayload_copy);
4750 		return ret;
4751 	}
4752 
4753 	qdf_mem_copy(reconfig_info, &rinfo, sizeof(*reconfig_info));
4754 	qdf_mem_free(mlieseqpayload_copy);
4755 
4756 	return QDF_STATUS_SUCCESS;
4757 }
4758 
4759 static QDF_STATUS
4760 util_parse_pa_multi_link_ctrl(uint8_t *mlieseqpayload,
4761 			      qdf_size_t mlieseqpayloadlen,
4762 			      uint8_t **link_info,
4763 			      qdf_size_t *link_info_len)
4764 {
4765 	qdf_size_t parsed_payload_len;
4766 
4767 	/* This helper returns the location(s) and length(s) of (sub)field(s)
4768 	 * inferable after parsing the Multi Link element Control field. These
4769 	 * location(s) and length(s) is/are in reference to the payload section
4770 	 * of the Multi Link element (after defragmentation, if applicable).
4771 	 * Here, the payload is the point after the element ID extension of the
4772 	 * Multi Link element, and includes the payloads of all subsequent
4773 	 * fragments (if any) but not the headers of those fragments.
4774 	 *
4775 	 * Currently, the helper returns the location and length of the Link
4776 	 * Info field in the Multi Link element sequence. Other (sub)field(s)
4777 	 * can be added later as required.
4778 	 */
4779 	if (!mlieseqpayload) {
4780 		mlo_err("ML seq payload pointer is NULL");
4781 		return QDF_STATUS_E_NULL_VALUE;
4782 	}
4783 
4784 	if (!mlieseqpayloadlen) {
4785 		mlo_err("ML seq payload len is 0");
4786 		return QDF_STATUS_E_INVAL;
4787 	}
4788 
4789 	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
4790 		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
4791 			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
4792 		return QDF_STATUS_E_PROTO;
4793 	}
4794 
4795 	parsed_payload_len = 0;
4796 
4797 	parsed_payload_len += WLAN_ML_CTRL_SIZE;
4798 
4799 	if (mlieseqpayloadlen <
4800 		(parsed_payload_len +
4801 		WLAN_ML_PAV_CINFO_LENGTH_MAX)) {
4802 		mlo_err_rl("ML seq payload len %zu insufficient for MLD cmn size %u after parsed payload len %zu.",
4803 			   mlieseqpayloadlen,
4804 			   WLAN_ML_PAV_CINFO_LENGTH_MAX,
4805 			   parsed_payload_len);
4806 		return QDF_STATUS_E_PROTO;
4807 	}
4808 
4809 	parsed_payload_len += QDF_MAC_ADDR_SIZE + WLAN_ML_PAV_CINFO_LENGTH_SIZE;
4810 
4811 	if (link_info_len) {
4812 		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
4813 		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu",
4814 			  *link_info_len, parsed_payload_len);
4815 	}
4816 
4817 	if (mlieseqpayloadlen == parsed_payload_len) {
4818 		mlo_debug("No Link Info field present");
4819 		if (link_info)
4820 			*link_info = NULL;
4821 
4822 		return QDF_STATUS_SUCCESS;
4823 	}
4824 
4825 	if (link_info)
4826 		*link_info = mlieseqpayload + parsed_payload_len;
4827 
4828 	return QDF_STATUS_SUCCESS;
4829 }
4830 
4831 static QDF_STATUS
4832 util_parse_pamlie_perstaprofile_stactrl(uint8_t *subelempayload,
4833 					qdf_size_t subelempayloadlen,
4834 					struct ml_pa_partner_link_info *pa_link_info)
4835 {
4836 	qdf_size_t parsed_payload_len = 0;
4837 	uint16_t stacontrol;
4838 	struct ie_header *ie;
4839 	struct extn_ie_header *extn_ie;
4840 
4841 	/* This helper returns the location(s) and where required, the length(s)
4842 	 * of (sub)field(s) inferable after parsing the STA Control field in the
4843 	 * per-STA profile subelement. These location(s) and length(s) is/are in
4844 	 * reference to the payload section of the per-STA profile subelement
4845 	 * (after defragmentation, if applicable).  Here, the payload is the
4846 	 * point after the subelement length in the subelement, and includes the
4847 	 * payloads of all subsequent fragments (if any) but not the headers of
4848 	 * those fragments.
4849 	 *
4850 	 * Currently, the helper returns the priority access link information
4851 	 * for all parner links.
4852 	 */
4853 	if (!subelempayload) {
4854 		mlo_err("Pointer to subelement payload is NULL");
4855 		return QDF_STATUS_E_NULL_VALUE;
4856 	}
4857 
4858 	if (!subelempayloadlen) {
4859 		mlo_err("Length of subelement payload is zero");
4860 		return QDF_STATUS_E_INVAL;
4861 	}
4862 
4863 	if (subelempayloadlen < WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE) {
4864 		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
4865 			   subelempayloadlen,
4866 			   WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE);
4867 		return QDF_STATUS_E_PROTO;
4868 	}
4869 
4870 	parsed_payload_len = 0;
4871 	qdf_mem_copy(&stacontrol,
4872 		     subelempayload,
4873 		     WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE);
4874 
4875 	stacontrol = qdf_le16_to_cpu(stacontrol);
4876 	parsed_payload_len += WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE;
4877 
4878 	subelempayload += WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_SIZE;
4879 
4880 	pa_link_info->link_id =
4881 		QDF_GET_BITS(stacontrol,
4882 			     WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
4883 			     WLAN_ML_PAV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
4884 
4885 	pa_link_info->edca_ie_present = false;
4886 	pa_link_info->ven_wme_ie_present = false;
4887 	pa_link_info->muedca_ie_present = false;
4888 
4889 	do {
4890 		if (subelempayloadlen <
4891 			(parsed_payload_len +
4892 			sizeof(struct ie_header))) {
4893 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain min ie length %zu after parsed payload length of %zu octets",
4894 				   subelempayloadlen,
4895 				   sizeof(struct ie_header),
4896 				   parsed_payload_len);
4897 			return QDF_STATUS_E_PROTO;
4898 		}
4899 
4900 		ie = (struct ie_header *)subelempayload;
4901 
4902 		if (subelempayloadlen <
4903 			(parsed_payload_len +
4904 			(sizeof(struct ie_header) + ie->ie_len))) {
4905 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain ie length %zu after parsed payload length of %zu octets",
4906 				   subelempayloadlen,
4907 				   sizeof(struct ie_header) + ie->ie_len,
4908 				   parsed_payload_len);
4909 			return QDF_STATUS_E_PROTO;
4910 		}
4911 
4912 		switch (ie->ie_id) {
4913 		case WLAN_ELEMID_EDCAPARMS:
4914 			if (pa_link_info->ven_wme_ie_present) {
4915 				/* WME parameters already present
4916 				 * use that one instead of EDCA */
4917 				break;
4918 			}
4919 			if (ie->ie_len == (sizeof(struct edca_ie) -
4920 			    sizeof(struct ie_header))) {
4921 				pa_link_info->edca_ie_present = true;
4922 				qdf_mem_copy(&pa_link_info->edca,
4923 					     subelempayload,
4924 					     sizeof(struct edca_ie));
4925 			} else {
4926 				epcs_debug("Invalid edca length %d in PAV IE",
4927 					   ie->ie_len);
4928 			}
4929 			break;
4930 		case WLAN_ELEMID_VENDOR:
4931 			if (is_wme_param((uint8_t *)ie) &&
4932 			    (ie->ie_len == WLAN_VENDOR_WME_IE_LEN)) {
4933 				pa_link_info->ven_wme_ie_present = true;
4934 				qdf_mem_copy(&pa_link_info->ven_wme_ie_bytes,
4935 					     subelempayload,
4936 					     sizeof(WLAN_VENDOR_WME_IE_LEN +
4937 						    sizeof(struct ie_header)));
4938 				pa_link_info->edca_ie_present = false;
4939 			} else {
4940 				epcs_debug("Unrelated Venfor IE reecived ie_id %d ie_len %d",
4941 					   ie->ie_id,
4942 					   ie->ie_len);
4943 			}
4944 			break;
4945 		case WLAN_ELEMID_EXTN_ELEM:
4946 			extn_ie = (struct extn_ie_header *)ie;
4947 			switch (extn_ie->ie_extn_id) {
4948 			case WLAN_EXTN_ELEMID_MUEDCA:
4949 				if (extn_ie->ie_len == WLAN_MAX_MUEDCA_IE_LEN) {
4950 					pa_link_info->muedca_ie_present = true;
4951 					qdf_mem_copy(&pa_link_info->muedca,
4952 						     subelempayload,
4953 						     sizeof(struct muedca_ie));
4954 				} else {
4955 					epcs_debug("Invalid muedca length %d in PAV IE",
4956 						   ie->ie_len);
4957 				}
4958 				break;
4959 			default:
4960 				epcs_debug("Unrelated Extn IE reecived ie_id %d ie_len %d extid %d IN PAV IE",
4961 					   ie->ie_id,
4962 					   ie->ie_len,
4963 					   extn_ie->ie_extn_id);
4964 				break;
4965 			}
4966 			break;
4967 		default:
4968 			epcs_debug("Unrelated IE reecived ie_id %d ie_len %d in PAV IE",
4969 				   ie->ie_id,
4970 				   ie->ie_len);
4971 			break;
4972 		}
4973 		subelempayload += ie->ie_len + sizeof(struct ie_header);
4974 		parsed_payload_len += ie->ie_len + sizeof(struct ie_header);
4975 	} while (parsed_payload_len < subelempayloadlen);
4976 
4977 	if (parsed_payload_len != subelempayloadlen)
4978 		epcs_debug("Error in processing per sta profile of PA ML IE %zu %zu",
4979 			   parsed_payload_len,
4980 			   subelempayloadlen);
4981 
4982 	epcs_debug("Link id %d presence of edca %d muedca %d wme %d",
4983 		   pa_link_info->link_id,
4984 		   pa_link_info->edca_ie_present,
4985 		   pa_link_info->muedca_ie_present,
4986 		   pa_link_info->ven_wme_ie_present);
4987 
4988 	return QDF_STATUS_SUCCESS;
4989 }
4990 
4991 static QDF_STATUS
4992 util_parse_pa_info_from_linkinfo(uint8_t *linkinfo,
4993 				 qdf_size_t linkinfo_len,
4994 				 struct ml_pa_info *pa_info)
4995 {
4996 	uint8_t *linkinfo_currpos;
4997 	qdf_size_t linkinfo_remlen;
4998 	bool is_subelemfragseq;
4999 	uint8_t subelemid;
5000 	qdf_size_t subelemseqtotallen;
5001 	qdf_size_t subelemseqpayloadlen;
5002 	qdf_size_t defragpayload_len;
5003 	QDF_STATUS ret;
5004 
5005 	/* This helper function parses priority access info from the per-STA
5006 	 * prof present (if any) in the Link Info field in the payload of a
5007 	 * Multi Link element (after defragmentation if required). The caller
5008 	 * should pass a copy of the payload so that inline defragmentation of
5009 	 * subelements can be carried out if required. The subelement
5010 	 * defragmentation (if applicable) in this Control Path helper is
5011 	 * required for maintainability, accuracy and eliminating current and
5012 	 * future per-field-access multi-level fragment boundary checks and
5013 	 * adjustments, given the complex format of Multi Link elements. It is
5014 	 * also most likely to be required mainly at the client side.
5015 	 * Fragmentation is currently unlikely to be required for subelements
5016 	 * in Reconfiguration variant Multi-Link elements, but it should be
5017 	 * handled in order to be future ready.
5018 	 */
5019 	if (!linkinfo) {
5020 		mlo_err("linkinfo is NULL");
5021 		return QDF_STATUS_E_NULL_VALUE;
5022 	}
5023 
5024 	if (!linkinfo_len) {
5025 		mlo_err("linkinfo_len is zero");
5026 		return QDF_STATUS_E_NULL_VALUE;
5027 	}
5028 
5029 	if (!pa_info) {
5030 		mlo_err("ML pa info is NULL");
5031 		return QDF_STATUS_E_NULL_VALUE;
5032 	}
5033 
5034 	pa_info->num_links = 0;
5035 	linkinfo_currpos = linkinfo;
5036 	linkinfo_remlen = linkinfo_len;
5037 
5038 	while (linkinfo_remlen) {
5039 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
5040 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
5041 				   linkinfo_remlen,
5042 				   sizeof(struct subelem_header));
5043 			return QDF_STATUS_E_PROTO;
5044 		}
5045 
5046 		subelemid = linkinfo_currpos[ID_POS];
5047 		is_subelemfragseq = false;
5048 		subelemseqtotallen = 0;
5049 		subelemseqpayloadlen = 0;
5050 
5051 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
5052 						    linkinfo_currpos,
5053 						    linkinfo_remlen,
5054 						    &is_subelemfragseq,
5055 						    &subelemseqtotallen,
5056 						    &subelemseqpayloadlen);
5057 		if (QDF_IS_STATUS_ERROR(ret))
5058 			return ret;
5059 
5060 		if (qdf_unlikely(is_subelemfragseq)) {
5061 			if (!subelemseqpayloadlen) {
5062 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
5063 				return QDF_STATUS_E_FAILURE;
5064 			}
5065 
5066 			mlo_debug("Subelement fragment sequence found with payload len %zu",
5067 				  subelemseqpayloadlen);
5068 
5069 			ret = wlan_defrag_subelem_fragseq(true,
5070 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
5071 							  linkinfo_currpos,
5072 							  linkinfo_remlen,
5073 							  NULL,
5074 							  0,
5075 							  &defragpayload_len);
5076 
5077 			if (QDF_IS_STATUS_ERROR(ret))
5078 				return ret;
5079 
5080 			if (defragpayload_len != subelemseqpayloadlen) {
5081 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
5082 					   defragpayload_len,
5083 					   subelemseqpayloadlen);
5084 				return QDF_STATUS_E_FAILURE;
5085 			}
5086 
5087 			/* Adjust linkinfo_remlen to reflect removal of all
5088 			 * subelement headers except the header of the lead
5089 			 * subelement.
5090 			 */
5091 			linkinfo_remlen -= (subelemseqtotallen -
5092 					    subelemseqpayloadlen -
5093 					    sizeof(struct subelem_header));
5094 		} else {
5095 			if (linkinfo_remlen <
5096 				(sizeof(struct subelem_header) +
5097 				linkinfo_currpos[TAG_LEN_POS])) {
5098 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
5099 					   linkinfo_remlen,
5100 					   sizeof(struct subelem_header) +
5101 					   linkinfo_currpos[TAG_LEN_POS]);
5102 				return QDF_STATUS_E_PROTO;
5103 			}
5104 
5105 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
5106 		}
5107 
5108 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
5109 			struct ml_pa_partner_link_info *link_info =
5110 					&pa_info->link_info[pa_info->num_links];
5111 			ret = util_parse_pamlie_perstaprofile_stactrl(linkinfo_currpos +
5112 								      sizeof(struct subelem_header),
5113 								      subelemseqpayloadlen,
5114 								      link_info);
5115 			if (QDF_IS_STATUS_ERROR(ret))
5116 				return ret;
5117 		}
5118 
5119 		pa_info->num_links++;
5120 
5121 		linkinfo_remlen -= (sizeof(struct subelem_header) +
5122 				    subelemseqpayloadlen);
5123 		linkinfo_currpos += (sizeof(struct subelem_header) +
5124 				     subelemseqpayloadlen);
5125 	}
5126 
5127 	mlo_debug("Number of ML probe request links found=%u",
5128 		  pa_info->num_links);
5129 
5130 	return QDF_STATUS_SUCCESS;
5131 }
5132 
5133 QDF_STATUS
5134 util_get_pav_mlie_link_info(uint8_t *mlieseq,
5135 			    qdf_size_t mlieseqlen,
5136 			    struct ml_pa_info *pa_info)
5137 {
5138 	struct wlan_ie_multilink *mlie_fixed;
5139 	uint16_t mlcontrol;
5140 	enum wlan_ml_variant variant;
5141 	uint8_t *linkinfo;
5142 	qdf_size_t linkinfo_len;
5143 	struct ml_pa_info painfo = {0};
5144 	qdf_size_t mlieseqpayloadlen;
5145 	uint8_t *mlieseqpayload_copy;
5146 	bool is_elemfragseq;
5147 	qdf_size_t defragpayload_len;
5148 	qdf_size_t tmplen;
5149 	QDF_STATUS ret;
5150 
5151 	if (!mlieseq) {
5152 		mlo_err("Pointer to Multi-Link element sequence is NULL");
5153 		return QDF_STATUS_E_NULL_VALUE;
5154 	}
5155 
5156 	if (!mlieseqlen) {
5157 		mlo_err("Length of Multi-Link element sequence is zero");
5158 		return QDF_STATUS_E_INVAL;
5159 	}
5160 
5161 	if (!pa_info) {
5162 		mlo_err("pa_info is NULL");
5163 		return QDF_STATUS_E_NULL_VALUE;
5164 	}
5165 
5166 	pa_info->num_links = 0;
5167 
5168 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
5169 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
5170 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
5171 		return QDF_STATUS_E_INVAL;
5172 	}
5173 
5174 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
5175 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
5176 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK) {
5177 		mlo_err("The element is not a Multi-Link element");
5178 		return QDF_STATUS_E_INVAL;
5179 	}
5180 
5181 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
5182 
5183 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
5184 			       WLAN_ML_CTRL_TYPE_BITS);
5185 
5186 	if (variant != WLAN_ML_VARIANT_PRIORITYACCESS) {
5187 		mlo_err("The variant value %u does not correspond to priority access Variant value %u",
5188 			variant, WLAN_ML_VARIANT_PRIORITYACCESS);
5189 		return QDF_STATUS_E_INVAL;
5190 	}
5191 
5192 	mlieseqpayloadlen = 0;
5193 	tmplen = 0;
5194 	is_elemfragseq = false;
5195 
5196 	ret = wlan_get_elem_fragseq_info(mlieseq,
5197 					 mlieseqlen,
5198 					 &is_elemfragseq,
5199 					 &tmplen,
5200 					 &mlieseqpayloadlen);
5201 
5202 	if (QDF_IS_STATUS_ERROR(ret))
5203 		return ret;
5204 
5205 	if (qdf_unlikely(is_elemfragseq)) {
5206 		if (tmplen != mlieseqlen) {
5207 			mlo_err_rl("Mismatch in values of element fragment sequence total length. Val per frag info determination: %zu octets, val passed as arg: %zu octets",
5208 				   tmplen, mlieseqlen);
5209 			return QDF_STATUS_E_INVAL;
5210 		}
5211 
5212 		if (!mlieseqpayloadlen) {
5213 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
5214 			return QDF_STATUS_E_FAILURE;
5215 		}
5216 
5217 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
5218 			  mlieseqpayloadlen);
5219 	} else {
5220 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
5221 			mlo_err_rl("Expected presence of valid fragment sequence since Multi-Link element sequence length %zu octets is larger than frag threshold of %zu octets, however no valid fragment sequence found",
5222 				   mlieseqlen,
5223 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
5224 			return QDF_STATUS_E_FAILURE;
5225 		}
5226 
5227 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
5228 	}
5229 
5230 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
5231 
5232 	if (!mlieseqpayload_copy) {
5233 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
5234 		return QDF_STATUS_E_NOMEM;
5235 	}
5236 
5237 	if (qdf_unlikely(is_elemfragseq)) {
5238 		ret = wlan_defrag_elem_fragseq(false,
5239 					       mlieseq,
5240 					       mlieseqlen,
5241 					       mlieseqpayload_copy,
5242 					       mlieseqpayloadlen,
5243 					       &defragpayload_len);
5244 		if (QDF_IS_STATUS_ERROR(ret)) {
5245 			qdf_mem_free(mlieseqpayload_copy);
5246 			return ret;
5247 		}
5248 
5249 		if (defragpayload_len != mlieseqpayloadlen) {
5250 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
5251 				   defragpayload_len, mlieseqpayloadlen);
5252 			qdf_mem_free(mlieseqpayload_copy);
5253 			return QDF_STATUS_E_FAILURE;
5254 		}
5255 	} else {
5256 		qdf_mem_copy(mlieseqpayload_copy,
5257 			     mlieseq + sizeof(struct ie_header) + 1,
5258 			     mlieseqpayloadlen);
5259 	}
5260 
5261 	linkinfo = NULL;
5262 	linkinfo_len = 0;
5263 
5264 	ret = util_parse_pa_multi_link_ctrl(mlieseqpayload_copy,
5265 					    mlieseqpayloadlen,
5266 					    &linkinfo,
5267 					    &linkinfo_len);
5268 	if (QDF_IS_STATUS_ERROR(ret)) {
5269 		qdf_mem_free(mlieseqpayload_copy);
5270 		return ret;
5271 	}
5272 
5273 	/* In case Link Info is absent, the number of links will remain
5274 	 * zero.
5275 	 */
5276 	if (!linkinfo) {
5277 		qdf_mem_free(mlieseqpayload_copy);
5278 		return QDF_STATUS_SUCCESS;
5279 	}
5280 
5281 	mlo_debug("Dumping hex of link info after parsing Multi-Link element control");
5282 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_ERROR,
5283 			   linkinfo, linkinfo_len);
5284 
5285 	ret = util_parse_pa_info_from_linkinfo(linkinfo, linkinfo_len, &painfo);
5286 	if (QDF_IS_STATUS_ERROR(ret)) {
5287 		qdf_mem_free(mlieseqpayload_copy);
5288 		return ret;
5289 	}
5290 
5291 	qdf_mem_copy(pa_info, &painfo, sizeof(painfo));
5292 	qdf_mem_free(mlieseqpayload_copy);
5293 
5294 	return QDF_STATUS_SUCCESS;
5295 }
5296 
5297 #endif
5298 
5299 #ifdef WLAN_FEATURE_11BE
5300 
5301 QDF_STATUS util_add_bw_ind(struct wlan_ie_bw_ind *bw_ind, uint8_t ccfs0,
5302 			   uint8_t ccfs1, enum phy_ch_width ch_width,
5303 			   uint16_t puncture_bitmap, int *bw_ind_len)
5304 {
5305 	uint8_t bw_ind_width;
5306 
5307 	if (!bw_ind) {
5308 		mlo_err("Pointer to bandwidth indiaction element is NULL");
5309 		return QDF_STATUS_E_NULL_VALUE;
5310 	}
5311 
5312 	if (!bw_ind_len) {
5313 		mlo_err("Length of bandwidth indaication element is Zero");
5314 		return QDF_STATUS_E_INVAL;
5315 	}
5316 
5317 	switch (ch_width) {
5318 	case CH_WIDTH_20MHZ:
5319 		bw_ind_width = IEEE80211_11BEOP_CHWIDTH_20;
5320 		break;
5321 	case CH_WIDTH_40MHZ:
5322 		bw_ind_width = IEEE80211_11BEOP_CHWIDTH_40;
5323 		break;
5324 	case CH_WIDTH_80MHZ:
5325 		bw_ind_width = IEEE80211_11BEOP_CHWIDTH_80;
5326 		break;
5327 	case CH_WIDTH_160MHZ:
5328 		bw_ind_width = IEEE80211_11BEOP_CHWIDTH_160;
5329 		break;
5330 	case CH_WIDTH_320MHZ:
5331 		bw_ind_width = IEEE80211_11BEOP_CHWIDTH_320;
5332 		break;
5333 	default:
5334 		bw_ind_width = IEEE80211_11BEOP_CHWIDTH_20;
5335 	}
5336 
5337 	bw_ind->elem_id = WLAN_ELEMID_EXTN_ELEM;
5338 	*bw_ind_len = WLAN_BW_IND_IE_MAX_LEN;
5339 	bw_ind->elem_len = WLAN_BW_IND_IE_MAX_LEN - WLAN_IE_HDR_LEN;
5340 	bw_ind->elem_id_extn = WLAN_EXTN_ELEMID_BW_IND;
5341 	bw_ind->ccfs0 = ccfs0;
5342 	bw_ind->ccfs1 = ccfs1;
5343 	QDF_SET_BITS(bw_ind->control, BW_IND_CHAN_WIDTH_IDX,
5344 		     BW_IND_CHAN_WIDTH_BITS, bw_ind_width);
5345 
5346 	if (puncture_bitmap) {
5347 		bw_ind->disabled_sub_chan_bitmap[0] =
5348 			QDF_GET_BITS(puncture_bitmap, 0, 8);
5349 		bw_ind->disabled_sub_chan_bitmap[1] =
5350 			QDF_GET_BITS(puncture_bitmap, 8, 8);
5351 		QDF_SET_BITS(bw_ind->bw_ind_param,
5352 			     BW_IND_PARAM_DISABLED_SC_BITMAP_PRESENT_IDX,
5353 			     BW_IND_PARAM_DISABLED_SC_BITMAP_PRESENT_BITS, 1);
5354 	} else {
5355 		QDF_SET_BITS(bw_ind->bw_ind_param,
5356 			     BW_IND_PARAM_DISABLED_SC_BITMAP_PRESENT_IDX,
5357 			     BW_IND_PARAM_DISABLED_SC_BITMAP_PRESENT_BITS, 0);
5358 		bw_ind->elem_len -=
5359 			QDF_ARRAY_SIZE(bw_ind->disabled_sub_chan_bitmap);
5360 		*bw_ind_len -=
5361 			QDF_ARRAY_SIZE(bw_ind->disabled_sub_chan_bitmap);
5362 	}
5363 
5364 	return QDF_STATUS_SUCCESS;
5365 }
5366 
5367 QDF_STATUS util_parse_bw_ind(struct wlan_ie_bw_ind *bw_ind, uint8_t *ccfs0,
5368 			     uint8_t *ccfs1, enum phy_ch_width *ch_width,
5369 			     uint16_t *puncture_bitmap)
5370 {
5371 	uint8_t bw_ind_width;
5372 
5373 	if (!bw_ind) {
5374 		mlo_err("Pointer to bandwidth indiaction element is NULL");
5375 		return QDF_STATUS_E_NULL_VALUE;
5376 	}
5377 
5378 	*ccfs0 = bw_ind->ccfs0;
5379 	*ccfs1 = bw_ind->ccfs1;
5380 	bw_ind_width = QDF_GET_BITS(bw_ind->control, BW_IND_CHAN_WIDTH_IDX,
5381 				    BW_IND_CHAN_WIDTH_BITS);
5382 
5383 	switch (bw_ind_width) {
5384 	case IEEE80211_11BEOP_CHWIDTH_20:
5385 		*ch_width = CH_WIDTH_20MHZ;
5386 		break;
5387 	case IEEE80211_11BEOP_CHWIDTH_40:
5388 		*ch_width = CH_WIDTH_40MHZ;
5389 		break;
5390 	case IEEE80211_11BEOP_CHWIDTH_80:
5391 		*ch_width = CH_WIDTH_80MHZ;
5392 		break;
5393 	case IEEE80211_11BEOP_CHWIDTH_160:
5394 		*ch_width = CH_WIDTH_160MHZ;
5395 		break;
5396 	case IEEE80211_11BEOP_CHWIDTH_320:
5397 		*ch_width = CH_WIDTH_320MHZ;
5398 		break;
5399 	default:
5400 		*ch_width = CH_WIDTH_20MHZ;
5401 	}
5402 
5403 	if (QDF_GET_BITS(bw_ind->bw_ind_param,
5404 			 BW_IND_PARAM_DISABLED_SC_BITMAP_PRESENT_IDX,
5405 			 BW_IND_PARAM_DISABLED_SC_BITMAP_PRESENT_BITS)) {
5406 		QDF_SET_BITS(*puncture_bitmap, 0, 8,
5407 			     bw_ind->disabled_sub_chan_bitmap[0]);
5408 		QDF_SET_BITS(*puncture_bitmap, 8, 8,
5409 			     bw_ind->disabled_sub_chan_bitmap[1]);
5410 	} else {
5411 		*puncture_bitmap = 0;
5412 	}
5413 
5414 	return QDF_STATUS_SUCCESS;
5415 }
5416 #endif
5417