xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlo_mgr/src/utils_mlo.c (revision 2888b71da71bce103343119fa1b31f4a0cee07c8)
1 /*
2  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2022 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 	exp_cinfo_len = parsed_payload_len - WLAN_ML_CTRL_SIZE;
232 	if (cinfo_len != exp_cinfo_len) {
233 		mlo_err_rl("ML seq common info len %u doesn't match with expected common info len %u",
234 			   cinfo_len, exp_cinfo_len);
235 		return QDF_STATUS_E_PROTO;
236 	}
237 
238 	if (link_info_len) {
239 		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
240 		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu",
241 			  *link_info_len, parsed_payload_len);
242 	}
243 
244 	if (mlieseqpayloadlen == parsed_payload_len) {
245 		mlo_debug("No Link Info field present");
246 		if (link_info)
247 			*link_info = NULL;
248 		return QDF_STATUS_SUCCESS;
249 	}
250 
251 	if (link_info)
252 		*link_info = mlieseqpayload + parsed_payload_len;
253 
254 	return QDF_STATUS_SUCCESS;
255 }
256 
257 static QDF_STATUS
258 util_parse_prv_multi_link_ctrl(uint8_t *mlieseqpayload,
259 			       qdf_size_t mlieseqpayloadlen,
260 			       uint8_t **link_info,
261 			       qdf_size_t *link_info_len)
262 {
263 	qdf_size_t parsed_payload_len;
264 	uint16_t mlcontrol;
265 	uint16_t presence_bm;
266 	uint16_t cinfo_len = 0;
267 	uint16_t exp_cinfo_len = 0;
268 
269 	/* This helper returns the location(s) and length(s) of (sub)field(s)
270 	 * inferable after parsing the Multi Link element Control field. These
271 	 * location(s) and length(s) is/are in reference to the payload section
272 	 * of the Multi Link element (after defragmentation, if applicable).
273 	 * Here, the payload is the point after the element ID extension of the
274 	 * Multi Link element, and includes the payloads of all subsequent
275 	 * fragments (if any) but not the headers of those fragments.
276 	 *
277 	 * Currently, the helper returns the location and length of the Link
278 	 * Info field in the Multi Link element sequence. Other (sub)field(s)
279 	 * can be added later as required.
280 	 */
281 
282 	if (!mlieseqpayload) {
283 		mlo_err("ML seq payload pointer is NULL");
284 		return QDF_STATUS_E_NULL_VALUE;
285 	}
286 
287 	if (!mlieseqpayloadlen) {
288 		mlo_err("ML seq payload len is 0");
289 		return QDF_STATUS_E_INVAL;
290 	}
291 
292 	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
293 		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
294 			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
295 		return QDF_STATUS_E_PROTO;
296 	}
297 
298 	parsed_payload_len = 0;
299 
300 	qdf_mem_copy(&mlcontrol, mlieseqpayload, WLAN_ML_CTRL_SIZE);
301 	mlcontrol = qdf_le16_to_cpu(mlcontrol);
302 	parsed_payload_len += WLAN_ML_CTRL_SIZE;
303 
304 	presence_bm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
305 				   WLAN_ML_CTRL_PBM_BITS);
306 
307 	if (mlieseqpayloadlen <
308 			(parsed_payload_len + WLAN_ML_PRV_CINFO_LENGTH_SIZE)) {
309 		mlo_err_rl("ML seq payload len %zu insufficient for common info length size %u after parsed payload len %zu.",
310 			   mlieseqpayloadlen,
311 			   WLAN_ML_PRV_CINFO_LENGTH_SIZE,
312 			   parsed_payload_len);
313 		return QDF_STATUS_E_PROTO;
314 	}
315 
316 	cinfo_len = *(mlieseqpayload + parsed_payload_len);
317 	parsed_payload_len += WLAN_ML_PRV_CINFO_LENGTH_SIZE;
318 
319 	/* Check if MLD ID is present */
320 	if (presence_bm & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
321 		if (mlieseqpayloadlen <
322 				(parsed_payload_len +
323 				 WLAN_ML_PRV_CINFO_MLDID_SIZE)) {
324 			mlo_err_rl("ML seq payload len %zu insufficient for MLD ID size %u after parsed payload len %zu.",
325 				   mlieseqpayloadlen,
326 				   WLAN_ML_PRV_CINFO_MLDID_SIZE,
327 				   parsed_payload_len);
328 			return QDF_STATUS_E_PROTO;
329 		}
330 
331 		parsed_payload_len += WLAN_ML_PRV_CINFO_MLDID_SIZE;
332 	}
333 
334 	exp_cinfo_len = parsed_payload_len - WLAN_ML_CTRL_SIZE;
335 	if (cinfo_len != exp_cinfo_len) {
336 		mlo_err_rl("ML seq common info len %u doesn't match with expected common info len %u",
337 			   cinfo_len, exp_cinfo_len);
338 		return QDF_STATUS_E_PROTO;
339 	}
340 
341 	if (link_info_len) {
342 		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
343 		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu",
344 			  *link_info_len, parsed_payload_len);
345 	}
346 
347 	if (mlieseqpayloadlen == parsed_payload_len) {
348 		mlo_debug("No Link Info field present");
349 		if (link_info)
350 			*link_info = NULL;
351 		return QDF_STATUS_SUCCESS;
352 	}
353 
354 	if (link_info)
355 		*link_info = mlieseqpayload + parsed_payload_len;
356 
357 	return QDF_STATUS_SUCCESS;
358 }
359 
360 static QDF_STATUS
361 util_parse_bvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
362 					qdf_size_t subelempayloadlen,
363 					uint8_t *linkid,
364 					uint16_t *beaconinterval,
365 					bool *is_beaconinterval_valid,
366 					uint64_t *tsfoffset,
367 					bool *is_tsfoffset_valid,
368 					bool *is_complete_profile,
369 					bool *is_macaddr_valid,
370 					struct qdf_mac_addr *macaddr,
371 					bool is_staprof_reqd,
372 					uint8_t **staprof,
373 					qdf_size_t *staprof_len)
374 {
375 	qdf_size_t parsed_payload_len = 0;
376 	uint16_t stacontrol;
377 	uint8_t completeprofile;
378 	uint8_t nstrlppresent;
379 	enum wlan_ml_bv_linfo_perstaprof_stactrl_nstrbmsz nstrbmsz;
380 
381 	/* This helper returns the location(s) and where required, the length(s)
382 	 * of (sub)field(s) inferable after parsing the STA Control field in the
383 	 * per-STA profile subelement. These location(s) and length(s) is/are in
384 	 * reference to the payload section of the per-STA profile subelement
385 	 * (after defragmentation, if applicable).  Here, the payload is the
386 	 * point after the subelement length in the subelement, and includes the
387 	 * payloads of all subsequent fragments (if any) but not the headers of
388 	 * those fragments.
389 	 *
390 	 * Currently, the helper returns the link ID, MAC address, and STA
391 	 * profile. More (sub)fields can be added when required.
392 	 */
393 
394 	if (!subelempayload) {
395 		mlo_err("Pointer to subelement payload is NULL");
396 		return QDF_STATUS_E_NULL_VALUE;
397 	}
398 
399 	if (!subelempayloadlen) {
400 		mlo_err("Length of subelement payload is zero");
401 		return QDF_STATUS_E_INVAL;
402 	}
403 
404 	if (subelempayloadlen < WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE) {
405 		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
406 			   subelempayloadlen,
407 			   WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE);
408 		return QDF_STATUS_E_PROTO;
409 	}
410 
411 	parsed_payload_len = 0;
412 
413 	qdf_mem_copy(&stacontrol,
414 		     subelempayload,
415 		     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE);
416 	stacontrol = le16toh(stacontrol);
417 	parsed_payload_len += WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE;
418 
419 	if (linkid) {
420 		*linkid = QDF_GET_BITS(stacontrol,
421 				       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
422 				       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
423 	}
424 
425 	/* Check if this a complete profile */
426 	completeprofile = QDF_GET_BITS(stacontrol,
427 				       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
428 				       WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
429 
430 	if (completeprofile && is_complete_profile)
431 		*is_complete_profile = true;
432 
433 	/* Check STA Info Length */
434 	if (subelempayloadlen <
435 		parsed_payload_len + WLAN_ML_BV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE) {
436 		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.",
437 			   subelempayloadlen,
438 			   WLAN_ML_BV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE,
439 			   parsed_payload_len);
440 		return QDF_STATUS_E_PROTO;
441 	}
442 
443 	parsed_payload_len += WLAN_ML_BV_LINFO_PERSTAPROF_STAINFO_LENGTH_SIZE;
444 
445 	if (is_macaddr_valid)
446 		*is_macaddr_valid = false;
447 
448 	/* Check STA MAC address present bit */
449 	if (QDF_GET_BITS(stacontrol,
450 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
451 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_MACADDRP_BITS)) {
452 		if (subelempayloadlen <
453 				(parsed_payload_len + QDF_MAC_ADDR_SIZE)) {
454 			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.",
455 				   subelempayloadlen,
456 				   QDF_MAC_ADDR_SIZE,
457 				   parsed_payload_len);
458 			return QDF_STATUS_E_PROTO;
459 		}
460 
461 		if (macaddr) {
462 			qdf_mem_copy(macaddr->bytes,
463 				     subelempayload + parsed_payload_len,
464 				     QDF_MAC_ADDR_SIZE);
465 
466 			mlo_nofl_debug("Copied MAC address: " QDF_MAC_ADDR_FMT,
467 				       subelempayload + parsed_payload_len);
468 
469 			if (is_macaddr_valid)
470 				*is_macaddr_valid = true;
471 		}
472 
473 		parsed_payload_len += QDF_MAC_ADDR_SIZE;
474 	}
475 
476 	/* Check Beacon Interval present bit */
477 	if (QDF_GET_BITS(stacontrol,
478 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_IDX,
479 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BCNINTP_BITS)) {
480 		if (subelempayloadlen <
481 				(parsed_payload_len +
482 				 WLAN_BEACONINTERVAL_LEN)) {
483 			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.",
484 				   subelempayloadlen,
485 				   WLAN_BEACONINTERVAL_LEN,
486 				   parsed_payload_len);
487 			return QDF_STATUS_E_PROTO;
488 		}
489 
490 		if (beaconinterval) {
491 			qdf_mem_copy(beaconinterval,
492 				     subelempayload + parsed_payload_len,
493 				     WLAN_BEACONINTERVAL_LEN);
494 			*beaconinterval = qdf_le16_to_cpu(*beaconinterval);
495 
496 			if (is_beaconinterval_valid)
497 				*is_beaconinterval_valid = true;
498 		}
499 		parsed_payload_len += WLAN_BEACONINTERVAL_LEN;
500 	}
501 
502 	/* Check TSF Offset present bit */
503 	if (QDF_GET_BITS(stacontrol,
504 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_TSFOFFSETP_IDX,
505 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_TSFOFFSETP_BITS)) {
506 		if (!completeprofile) {
507 			mlo_err_rl("TSF offset is expected only for complete profiles");
508 			return QDF_STATUS_E_PROTO;
509 		}
510 
511 		if (subelempayloadlen <
512 				(parsed_payload_len +
513 				 WLAN_ML_TSF_OFFSET_SIZE)) {
514 			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.",
515 				   subelempayloadlen,
516 				   WLAN_ML_TSF_OFFSET_SIZE,
517 				   parsed_payload_len);
518 			return QDF_STATUS_E_PROTO;
519 		}
520 
521 		if (tsfoffset) {
522 			qdf_mem_copy(tsfoffset,
523 				     subelempayload + parsed_payload_len,
524 				     WLAN_TIMESTAMP_LEN);
525 			*tsfoffset = qdf_le64_to_cpu(*tsfoffset);
526 
527 			if (is_tsfoffset_valid)
528 				*is_tsfoffset_valid = true;
529 		}
530 
531 		parsed_payload_len += WLAN_ML_TSF_OFFSET_SIZE;
532 	}
533 
534 	/* Check DTIM Info present bit */
535 	if (QDF_GET_BITS(stacontrol,
536 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_IDX,
537 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_DTIMINFOP_BITS)) {
538 		if (subelempayloadlen <
539 				(parsed_payload_len +
540 				 sizeof(struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo))) {
541 			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.",
542 				   subelempayloadlen,
543 				   sizeof(struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo),
544 				   parsed_payload_len);
545 			return QDF_STATUS_E_PROTO;
546 		}
547 
548 		parsed_payload_len +=
549 			sizeof(struct wlan_ml_bv_linfo_perstaprof_stainfo_dtiminfo);
550 	}
551 
552 	/* Check NTSR Link pair present bit */
553 	nstrlppresent =
554 		QDF_GET_BITS(stacontrol,
555 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_IDX,
556 			     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRLINKPRP_BITS);
557 
558 	if (completeprofile && nstrlppresent) {
559 		/* Check NTSR Bitmap Size bit */
560 		nstrbmsz =
561 			QDF_GET_BITS(stacontrol,
562 				     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_IDX,
563 				     WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_BITS);
564 
565 		if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_1_OCTET) {
566 			if (subelempayloadlen <
567 					(parsed_payload_len + 1)) {
568 				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.",
569 					   subelempayloadlen,
570 					   parsed_payload_len);
571 				return QDF_STATUS_E_PROTO;
572 			}
573 
574 			parsed_payload_len += 1;
575 		} else if (nstrbmsz == WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_NSTRBMSZ_2_OCTETS) {
576 			if (subelempayloadlen <
577 					(parsed_payload_len + 2)) {
578 				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.",
579 					   subelempayloadlen,
580 					   parsed_payload_len);
581 				return QDF_STATUS_E_PROTO;
582 			}
583 
584 			parsed_payload_len += 2;
585 		} else {
586 			/* Though an invalid value cannot occur if only 1 bit is
587 			 * used, we check for it in a generic manner in case the
588 			 * number of bits is increased in the future.
589 			 */
590 			mlo_err_rl("Invalid NSTR Bitmap size %u", nstrbmsz);
591 			return QDF_STATUS_E_PROTO;
592 		}
593 	}
594 
595 	/* Check BSS Parameters Change Count Present bit */
596 	if (QDF_GET_BITS(stacontrol,
597 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BSSPARAMCHNGCNTP_IDX,
598 			 WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_BSSPARAMCHNGCNTP_BITS)) {
599 		if (subelempayloadlen <
600 				(parsed_payload_len +
601 				 WLAN_ML_BSSPARAMCHNGCNT_SIZE)) {
602 			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.",
603 				   subelempayloadlen,
604 				   WLAN_ML_BSSPARAMCHNGCNT_SIZE,
605 				   parsed_payload_len);
606 			return QDF_STATUS_E_PROTO;
607 		}
608 
609 		parsed_payload_len += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
610 	}
611 
612 	/* Note: Some implementation versions of hostapd/wpa_supplicant may
613 	 * provide a per-STA profile without STA profile. Let the caller
614 	 * indicate whether a STA profile is required to be found. This may be
615 	 * revisited as upstreaming progresses.
616 	 */
617 	if (!is_staprof_reqd)
618 		return QDF_STATUS_SUCCESS;
619 
620 	if (subelempayloadlen == parsed_payload_len) {
621 		mlo_err_rl("Subelement payload length %zu == parsed payload length %zu. Unable to get STA profile.",
622 			   subelempayloadlen,
623 			   parsed_payload_len);
624 		return QDF_STATUS_E_PROTO;
625 	}
626 
627 	if (staprof_len)
628 		*staprof_len = subelempayloadlen - parsed_payload_len;
629 
630 	if (staprof)
631 		*staprof = subelempayload + parsed_payload_len;
632 
633 	return QDF_STATUS_SUCCESS;
634 }
635 
636 static QDF_STATUS
637 util_parse_prvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
638 					 qdf_size_t subelempayloadlen,
639 					 uint8_t *linkid,
640 					 bool is_staprof_reqd,
641 					 uint8_t **staprof,
642 					 qdf_size_t *staprof_len)
643 {
644 	qdf_size_t parsed_payload_len = 0;
645 	uint16_t stacontrol;
646 	uint8_t completeprofile;
647 
648 	/* This helper returns the location(s) and where required, the length(s)
649 	 * of (sub)field(s) inferable after parsing the STA Control field in the
650 	 * per-STA profile subelement. These location(s) and length(s) is/are in
651 	 * reference to the payload section of the per-STA profile subelement
652 	 * (after defragmentation, if applicable).  Here, the payload is the
653 	 * point after the subelement length in the subelement, and includes the
654 	 * payloads of all subsequent fragments (if any) but not the headers of
655 	 * those fragments.
656 	 *
657 	 * Currently, the helper returns the link ID, MAC address, and STA
658 	 * profile. More (sub)fields can be added when required.
659 	 */
660 
661 	if (!subelempayload) {
662 		mlo_err("Pointer to subelement payload is NULL");
663 		return QDF_STATUS_E_NULL_VALUE;
664 	}
665 
666 	if (!subelempayloadlen) {
667 		mlo_err("Length of subelement payload is zero");
668 		return QDF_STATUS_E_INVAL;
669 	}
670 
671 	if (subelempayloadlen < WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE) {
672 		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
673 			   subelempayloadlen,
674 			   WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE);
675 		return QDF_STATUS_E_PROTO;
676 	}
677 
678 	parsed_payload_len = 0;
679 
680 	qdf_mem_copy(&stacontrol,
681 		     subelempayload,
682 		     WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE);
683 	stacontrol = qdf_le16_to_cpu(stacontrol);
684 	parsed_payload_len += WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_SIZE;
685 
686 	if (linkid) {
687 		*linkid = QDF_GET_BITS(stacontrol,
688 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
689 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
690 	}
691 
692 	/* Check if this a complete profile */
693 	completeprofile = QDF_GET_BITS(stacontrol,
694 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
695 				       WLAN_ML_PRV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
696 
697 	/* Note: Some implementation versions of hostapd/wpa_supplicant may
698 	 * provide a per-STA profile without STA profile. Let the caller
699 	 * indicate whether a STA profile is required to be found. This may be
700 	 * revisited as upstreaming progresses.
701 	 */
702 	if (!is_staprof_reqd)
703 		return QDF_STATUS_SUCCESS;
704 
705 	if (subelempayloadlen == parsed_payload_len) {
706 		mlo_err_rl("Subelement payload length %zu == parsed payload length %zu. Unable to get STA profile.",
707 			   subelempayloadlen,
708 			   parsed_payload_len);
709 		return QDF_STATUS_E_PROTO;
710 	}
711 
712 	if (staprof_len)
713 		*staprof_len = subelempayloadlen - parsed_payload_len;
714 
715 	if (staprof)
716 		*staprof = subelempayload + parsed_payload_len;
717 
718 	return QDF_STATUS_SUCCESS;
719 }
720 
721 static
722 uint8_t *util_get_successorfrag(uint8_t *currie, uint8_t *frame, qdf_size_t len)
723 {
724 	uint8_t *nextie;
725 
726 	if (!currie || !frame || !len)
727 		return NULL;
728 
729 	if ((currie + MIN_IE_LEN) > (frame + len))
730 		return NULL;
731 
732 	/* Check whether there is sufficient space in the frame for the current
733 	 * IE, plus at least another MIN_IE_LEN bytes for the IE header of a
734 	 * fragment (if present) that would come just after the current IE.
735 	 */
736 	if ((currie + MIN_IE_LEN + currie[TAG_LEN_POS] + MIN_IE_LEN) >
737 			(frame + len))
738 		return NULL;
739 
740 	nextie = currie + currie[TAG_LEN_POS] + MIN_IE_LEN;
741 
742 	/* Check whether there is sufficient space in the frame for the next IE
743 	 */
744 	if ((nextie + MIN_IE_LEN + nextie[TAG_LEN_POS]) > (frame + len))
745 		return NULL;
746 
747 	if (nextie[ID_POS] != WLAN_ELEMID_FRAGMENT)
748 		return NULL;
749 
750 	return nextie;
751 }
752 
753 static
754 QDF_STATUS util_parse_partner_info_from_linkinfo(uint8_t *linkinfo,
755 						 qdf_size_t linkinfo_len,
756 						 struct mlo_partner_info *partner_info)
757 {
758 	uint8_t linkid;
759 	struct qdf_mac_addr macaddr;
760 	bool is_macaddr_valid;
761 	uint8_t *linkinfo_currpos;
762 	qdf_size_t linkinfo_remlen;
763 	bool is_subelemfragseq;
764 	uint8_t subelemid;
765 	qdf_size_t subelemseqtotallen;
766 	qdf_size_t subelemseqpayloadlen;
767 	qdf_size_t defragpayload_len;
768 	QDF_STATUS ret;
769 
770 	/* This helper function parses partner info from the per-STA profiles
771 	 * present (if any) in the Link Info field in the payload of a Multi
772 	 * Link element (after defragmentation if required). The caller should
773 	 * pass a copy of the payload so that inline defragmentation of
774 	 * subelements can be carried out if required. The subelement
775 	 * defragmentation (if applicable) in this Control Path helper is
776 	 * required for maintainability, accuracy and eliminating current and
777 	 * future per-field-access multi-level fragment boundary checks and
778 	 * adjustments, given the complex format of Multi Link elements. It is
779 	 * also most likely to be required mainly at the client side.
780 	 */
781 
782 	if (!linkinfo) {
783 		mlo_err("linkinfo is NULL");
784 		return QDF_STATUS_E_NULL_VALUE;
785 	}
786 
787 	if (!linkinfo_len) {
788 		mlo_err("linkinfo_len is zero");
789 		return QDF_STATUS_E_NULL_VALUE;
790 	}
791 
792 	if (!partner_info) {
793 		mlo_err("ML partner info is NULL");
794 		return QDF_STATUS_E_NULL_VALUE;
795 	}
796 
797 	partner_info->num_partner_links = 0;
798 	linkinfo_currpos = linkinfo;
799 	linkinfo_remlen = linkinfo_len;
800 
801 	while (linkinfo_remlen) {
802 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
803 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
804 				   linkinfo_remlen,
805 				   sizeof(struct subelem_header));
806 			return QDF_STATUS_E_PROTO;
807 		}
808 
809 		subelemid = linkinfo_currpos[ID_POS];
810 		is_subelemfragseq = false;
811 		subelemseqtotallen = 0;
812 		subelemseqpayloadlen = 0;
813 
814 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
815 						    linkinfo_currpos,
816 						    linkinfo_remlen,
817 						    &is_subelemfragseq,
818 						    &subelemseqtotallen,
819 						    &subelemseqpayloadlen);
820 		if (QDF_IS_STATUS_ERROR(ret))
821 			return ret;
822 
823 		if (is_subelemfragseq) {
824 			if (!subelemseqpayloadlen) {
825 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
826 				return QDF_STATUS_E_FAILURE;
827 			}
828 
829 			mlo_debug("Subelement fragment sequence found with payload len %zu",
830 				  subelemseqpayloadlen);
831 
832 			ret = wlan_defrag_subelem_fragseq(true,
833 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
834 							  linkinfo_currpos,
835 							  linkinfo_remlen,
836 							  NULL,
837 							  0,
838 							  &defragpayload_len);
839 			if (QDF_IS_STATUS_ERROR(ret))
840 				return ret;
841 
842 			if (defragpayload_len != subelemseqpayloadlen) {
843 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
844 					   defragpayload_len,
845 					   subelemseqpayloadlen);
846 				return QDF_STATUS_E_FAILURE;
847 			}
848 
849 			/* Adjust linkinfo_remlen to reflect removal of all
850 			 * subelement headers except the header of the lead
851 			 * subelement.
852 			 */
853 			linkinfo_remlen -= (subelemseqtotallen -
854 					    subelemseqpayloadlen -
855 					    sizeof(struct subelem_header));
856 		} else {
857 			if (linkinfo_remlen <
858 				(sizeof(struct subelem_header) +
859 				 linkinfo_currpos[TAG_LEN_POS])) {
860 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
861 					   linkinfo_remlen,
862 					   sizeof(struct subelem_header) +
863 					   linkinfo_currpos[TAG_LEN_POS]);
864 				return QDF_STATUS_E_PROTO;
865 			}
866 
867 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
868 		}
869 
870 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
871 			is_macaddr_valid = false;
872 
873 			ret = util_parse_bvmlie_perstaprofile_stactrl(linkinfo_currpos +
874 								      sizeof(struct subelem_header),
875 								      subelemseqpayloadlen,
876 								      &linkid,
877 								      NULL,
878 								      NULL,
879 								      NULL,
880 								      NULL,
881 								      NULL,
882 								      &is_macaddr_valid,
883 								      &macaddr,
884 								      false,
885 								      NULL,
886 								      NULL);
887 			if (QDF_IS_STATUS_ERROR(ret)) {
888 				return ret;
889 			}
890 
891 			if (is_macaddr_valid) {
892 				if (partner_info->num_partner_links >=
893 					QDF_ARRAY_SIZE(partner_info->partner_link_info)) {
894 					mlo_err_rl("Insufficient size %zu of array for partner link info",
895 						   QDF_ARRAY_SIZE(partner_info->partner_link_info));
896 					return QDF_STATUS_E_NOMEM;
897 				}
898 
899 				partner_info->partner_link_info[partner_info->num_partner_links].link_id =
900 					linkid;
901 				qdf_mem_copy(&partner_info->partner_link_info[partner_info->num_partner_links].link_addr,
902 					     &macaddr,
903 					     sizeof(partner_info->partner_link_info[partner_info->num_partner_links].link_addr));
904 
905 				partner_info->num_partner_links++;
906 			} else {
907 				mlo_warn_rl("MAC address not found in STA Info field of per-STA profile with link ID %u",
908 					    linkid);
909 			}
910 		}
911 
912 		linkinfo_remlen -= (sizeof(struct subelem_header) +
913 				    subelemseqpayloadlen);
914 		linkinfo_currpos += (sizeof(struct subelem_header) +
915 				     subelemseqpayloadlen);
916 	}
917 
918 	mlo_debug("Number of ML partner links found=%u",
919 		  partner_info->num_partner_links);
920 
921 	return QDF_STATUS_SUCCESS;
922 }
923 
924 static QDF_STATUS
925 util_parse_probereq_info_from_linkinfo(uint8_t *linkinfo,
926 				       qdf_size_t linkinfo_len,
927 				       struct mlo_probereq_info *probereq_info)
928 {
929 	uint8_t linkid;
930 	uint8_t *linkinfo_currpos;
931 	qdf_size_t linkinfo_remlen;
932 	bool is_subelemfragseq;
933 	uint8_t subelemid;
934 	qdf_size_t subelemseqtotallen;
935 	qdf_size_t subelemseqpayloadlen;
936 	qdf_size_t defragpayload_len;
937 	QDF_STATUS ret;
938 
939 	/* This helper function parses probe request info from the per-STA prof
940 	 * present (if any) in the Link Info field in the payload of a Multi
941 	 * Link element (after defragmentation if required). The caller should
942 	 * pass a copy of the payload so that inline defragmentation of
943 	 * subelements can be carried out if required. The subelement
944 	 * defragmentation (if applicable) in this Control Path helper is
945 	 * required for maintainability, accuracy and eliminating current and
946 	 * future per-field-access multi-level fragment boundary checks and
947 	 * adjustments, given the complex format of Multi Link elements. It is
948 	 * also most likely to be required mainly at the client side.
949 	 */
950 
951 	if (!linkinfo) {
952 		mlo_err("linkinfo is NULL");
953 		return QDF_STATUS_E_NULL_VALUE;
954 	}
955 
956 	if (!linkinfo_len) {
957 		mlo_err("linkinfo_len is zero");
958 		return QDF_STATUS_E_NULL_VALUE;
959 	}
960 
961 	if (!probereq_info) {
962 		mlo_err("ML probe req info is NULL");
963 		return QDF_STATUS_E_NULL_VALUE;
964 	}
965 
966 	probereq_info->num_links = 0;
967 	linkinfo_currpos = linkinfo;
968 	linkinfo_remlen = linkinfo_len;
969 
970 	while (linkinfo_remlen) {
971 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
972 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
973 				   linkinfo_remlen,
974 				   sizeof(struct subelem_header));
975 			return QDF_STATUS_E_PROTO;
976 		}
977 
978 		subelemid = linkinfo_currpos[ID_POS];
979 		is_subelemfragseq = false;
980 		subelemseqtotallen = 0;
981 		subelemseqpayloadlen = 0;
982 
983 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
984 						    linkinfo_currpos,
985 						    linkinfo_remlen,
986 						    &is_subelemfragseq,
987 						    &subelemseqtotallen,
988 						    &subelemseqpayloadlen);
989 		if (QDF_IS_STATUS_ERROR(ret))
990 			return ret;
991 
992 		if (is_subelemfragseq) {
993 			if (!subelemseqpayloadlen) {
994 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
995 				return QDF_STATUS_E_FAILURE;
996 			}
997 
998 			mlo_debug("Subelement fragment sequence found with payload len %zu",
999 				  subelemseqpayloadlen);
1000 
1001 			ret = wlan_defrag_subelem_fragseq(true,
1002 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1003 							  linkinfo_currpos,
1004 							  linkinfo_remlen,
1005 							  NULL,
1006 							  0,
1007 							  &defragpayload_len);
1008 			if (QDF_IS_STATUS_ERROR(ret))
1009 				return ret;
1010 
1011 			if (defragpayload_len != subelemseqpayloadlen) {
1012 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
1013 					   defragpayload_len,
1014 					   subelemseqpayloadlen);
1015 				return QDF_STATUS_E_FAILURE;
1016 			}
1017 
1018 			/* Adjust linkinfo_remlen to reflect removal of all
1019 			 * subelement headers except the header of the lead
1020 			 * subelement.
1021 			 */
1022 			linkinfo_remlen -= (subelemseqtotallen -
1023 					    subelemseqpayloadlen -
1024 					    sizeof(struct subelem_header));
1025 		} else {
1026 			if (linkinfo_remlen <
1027 				(sizeof(struct subelem_header) +
1028 				 linkinfo_currpos[TAG_LEN_POS])) {
1029 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
1030 					   linkinfo_remlen,
1031 					   sizeof(struct subelem_header) +
1032 					   linkinfo_currpos[TAG_LEN_POS]);
1033 				return QDF_STATUS_E_PROTO;
1034 			}
1035 
1036 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
1037 		}
1038 
1039 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
1040 			ret = util_parse_prvmlie_perstaprofile_stactrl(linkinfo_currpos +
1041 								      sizeof(struct subelem_header),
1042 								      subelemseqpayloadlen,
1043 								      &linkid,
1044 								      false,
1045 								      NULL,
1046 								      NULL);
1047 			if (QDF_IS_STATUS_ERROR(ret))
1048 				return ret;
1049 
1050 			if (probereq_info->num_links >=
1051 				QDF_ARRAY_SIZE(probereq_info->link_id)) {
1052 				mlo_err_rl("Insufficient size %zu of array for probe req link id",
1053 					   QDF_ARRAY_SIZE(probereq_info->link_id));
1054 				return QDF_STATUS_E_NOMEM;
1055 			}
1056 
1057 			probereq_info->link_id[probereq_info->num_links] = linkid;
1058 
1059 			probereq_info->num_links++;
1060 		}
1061 
1062 		linkinfo_remlen -= (sizeof(struct subelem_header) +
1063 				    subelemseqpayloadlen);
1064 		linkinfo_currpos += (sizeof(struct subelem_header) +
1065 				     subelemseqpayloadlen);
1066 	}
1067 
1068 	mlo_debug("Number of ML probe request links found=%u",
1069 		  probereq_info->num_links);
1070 
1071 	return QDF_STATUS_SUCCESS;
1072 }
1073 
1074 static
1075 QDF_STATUS util_get_noninheritlists(uint8_t *buff, qdf_size_t buff_len,
1076 				    uint8_t **ninherit_elemlist,
1077 				    qdf_size_t *ninherit_elemlist_len,
1078 				    uint8_t **ninherit_elemextlist,
1079 				    qdf_size_t *ninherit_elemextlist_len)
1080 {
1081 	uint8_t *ninherit_ie;
1082 	qdf_size_t unparsed_len;
1083 
1084 	/* Note: This funtionality provided by this helper may be combined with
1085 	 * other, older non-inheritance parsing helper functionality and exposed
1086 	 * as a common API as part of future efforts once the older
1087 	 * functionality can be made generic.
1088 	 */
1089 
1090 	if (!buff) {
1091 		mlo_err("Pointer to buffer for IEs is NULL");
1092 		return QDF_STATUS_E_NULL_VALUE;
1093 	}
1094 
1095 	if (!buff_len) {
1096 		mlo_err("IE buffer length is zero");
1097 		return QDF_STATUS_E_INVAL;
1098 	}
1099 
1100 	if (!ninherit_elemlist) {
1101 		mlo_err("Pointer to Non-Inheritance element ID list array is NULL");
1102 		return QDF_STATUS_E_NULL_VALUE;
1103 	}
1104 
1105 	if (!ninherit_elemlist_len) {
1106 		mlo_err("Pointer to Non-Inheritance element ID list array length is NULL");
1107 		return QDF_STATUS_E_NULL_VALUE;
1108 	}
1109 
1110 	if (!ninherit_elemextlist) {
1111 		mlo_err("Pointer to Non-Inheritance element ID extension list array is NULL");
1112 		return QDF_STATUS_E_NULL_VALUE;
1113 	}
1114 
1115 	if (!ninherit_elemextlist_len) {
1116 		mlo_err("Pointer to Non-Inheritance element ID extension list array length is NULL");
1117 		return QDF_STATUS_E_NULL_VALUE;
1118 	}
1119 
1120 	ninherit_ie = NULL;
1121 	*ninherit_elemlist_len = 0;
1122 	*ninherit_elemlist = NULL;
1123 	*ninherit_elemextlist_len = 0;
1124 	*ninherit_elemextlist = NULL;
1125 
1126 	ninherit_ie =
1127 		(uint8_t *)util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
1128 					      WLAN_EXTN_ELEMID_NONINHERITANCE,
1129 					      buff,
1130 					      buff_len);
1131 
1132 	if (ninherit_ie) {
1133 		if ((ninherit_ie + TAG_LEN_POS) > (buff + buff_len - 1)) {
1134 			mlo_err_rl("Position of length field of Non-Inheritance element would exceed IE buffer boundary");
1135 			return QDF_STATUS_E_PROTO;
1136 		}
1137 
1138 		if ((ninherit_ie + ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) >
1139 				(buff + buff_len)) {
1140 			mlo_err_rl("Non-Inheritance element with total length %u would exceed IE buffer boundary",
1141 				   ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN);
1142 			return QDF_STATUS_E_PROTO;
1143 		}
1144 
1145 		if ((ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) <
1146 				MIN_NONINHERITANCEELEM_LEN) {
1147 			mlo_err_rl("Non-Inheritance element size %u is smaller than the minimum required %u",
1148 				   ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN,
1149 				   MIN_NONINHERITANCEELEM_LEN);
1150 			return QDF_STATUS_E_PROTO;
1151 		}
1152 
1153 		/* Track the number of unparsed octets, excluding the IE header.
1154 		 */
1155 		unparsed_len = ninherit_ie[TAG_LEN_POS];
1156 
1157 		/* Mark the element ID extension as parsed */
1158 		unparsed_len--;
1159 
1160 		*ninherit_elemlist_len = ninherit_ie[ELEM_ID_LIST_LEN_POS];
1161 		unparsed_len--;
1162 
1163 		/* While checking if the Non-Inheritance element ID list length
1164 		 * exceeds the remaining unparsed IE space, we factor in one
1165 		 * octet for the element extension ID list length and subtract
1166 		 * this from the unparsed IE space.
1167 		 */
1168 		if (*ninherit_elemlist_len > (unparsed_len - 1)) {
1169 			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",
1170 				   *ninherit_elemlist_len, unparsed_len - 1);
1171 
1172 			return QDF_STATUS_E_PROTO;
1173 		}
1174 
1175 		if (*ninherit_elemlist_len != 0) {
1176 			*ninherit_elemlist = ninherit_ie + ELEM_ID_LIST_POS;
1177 			unparsed_len -= *ninherit_elemlist_len;
1178 		}
1179 
1180 		*ninherit_elemextlist_len =
1181 			ninherit_ie[ELEM_ID_LIST_LEN_POS + *ninherit_elemlist_len + 1];
1182 		unparsed_len--;
1183 
1184 		if (*ninherit_elemextlist_len > unparsed_len) {
1185 			mlo_err_rl("Non-Inheritance element ID extension list length %zu exceeds remaining unparsed IE space %zu",
1186 				   *ninherit_elemextlist_len, unparsed_len);
1187 
1188 			return QDF_STATUS_E_PROTO;
1189 		}
1190 
1191 		if (*ninherit_elemextlist_len != 0) {
1192 			*ninherit_elemextlist = ninherit_ie +
1193 				ELEM_ID_LIST_LEN_POS + (*ninherit_elemlist_len)
1194 				+ 2;
1195 			unparsed_len -= *ninherit_elemextlist_len;
1196 		}
1197 
1198 		if (unparsed_len > 0) {
1199 			mlo_err_rl("Unparsed length is %zu, expected 0",
1200 				   unparsed_len);
1201 			return QDF_STATUS_E_PROTO;
1202 		}
1203 	}
1204 
1205 	/* If Non-Inheritance element is not found, we still return success,
1206 	 * with the list lengths kept at zero.
1207 	 */
1208 	mlo_debug("Non-Inheritance element ID list array length=%zu",
1209 		  *ninherit_elemlist_len);
1210 	mlo_debug("Non-Inheritance element ID extension list array length=%zu",
1211 		  *ninherit_elemextlist_len);
1212 
1213 	return QDF_STATUS_SUCCESS;
1214 }
1215 
1216 static
1217 QDF_STATUS util_eval_ie_in_noninheritlist(uint8_t *ie, qdf_size_t total_ie_len,
1218 					  uint8_t *ninherit_elemlist,
1219 					  qdf_size_t ninherit_elemlist_len,
1220 					  uint8_t *ninherit_elemextlist,
1221 					  qdf_size_t ninherit_elemextlist_len,
1222 					  bool *is_in_noninheritlist)
1223 {
1224 	int i;
1225 
1226 	/* Evaluate whether the given IE is in the given Non-Inheritance element
1227 	 * ID list or Non-Inheritance element ID extension list, and update the
1228 	 * result into is_in_noninheritlist. If any list is empty, then the IE
1229 	 * is considered to not be present in that list. Both lists can be
1230 	 * empty.
1231 	 *
1232 	 * If QDF_STATUS_SUCCESS is returned, it means that the evaluation is
1233 	 * successful, and that is_in_noninheritlist contains a valid value
1234 	 * (which could be true or false). If a QDF_STATUS error value is
1235 	 * returned, the value in is_in_noninheritlist is invalid and the caller
1236 	 * should ignore it.
1237 	 */
1238 
1239 	/* Note: The funtionality provided by this helper may be combined with
1240 	 * other, older non-inheritance parsing helper functionality and exposed
1241 	 * as a common API as part of future efforts once the older
1242 	 * functionality can be made generic.
1243 	 */
1244 
1245 	/* Except for is_in_noninheritlist and ie, other pointer arguments are
1246 	 * permitted to be NULL if they are inapplicable. If they are
1247 	 * applicable, they will be checked to ensure they are not NULL.
1248 	 */
1249 
1250 	if (!is_in_noninheritlist) {
1251 		mlo_err("NULL pointer to flag that indicates if element is in a Non-Inheritance list");
1252 		return QDF_STATUS_E_NULL_VALUE;
1253 	}
1254 
1255 	/* If ninherit_elemlist_len and ninherit_elemextlist_len are both zero
1256 	 * as checked soon in this function, we won't be accessing the IE.
1257 	 * However, we still check right-away if the pointer to the IE is
1258 	 * non-NULL and whether the total IE length is sane enough to access the
1259 	 * element ID and if applicable, the element ID extension, since it
1260 	 * doesn't make sense to set the flag in is_in_noninheritlist for a NULL
1261 	 * IE pointer or an IE whose total length is not sane enough to
1262 	 * distinguish the identity of the IE.
1263 	 */
1264 	if (!ie) {
1265 		mlo_err("NULL pointer to IE");
1266 		return QDF_STATUS_E_NULL_VALUE;
1267 	}
1268 
1269 	if (total_ie_len < (ID_POS + 1)) {
1270 		mlo_err("Total IE length %zu is smaller than minimum required to access element ID %u",
1271 			total_ie_len, ID_POS + 1);
1272 		return QDF_STATUS_E_INVAL;
1273 	}
1274 
1275 	if ((ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1276 	    (total_ie_len < (IDEXT_POS + 1))) {
1277 		mlo_err("Total IE length %zu is smaller than minimum required to access element ID extension %u",
1278 			total_ie_len, IDEXT_POS + 1);
1279 		return QDF_STATUS_E_INVAL;
1280 	}
1281 
1282 	*is_in_noninheritlist = false;
1283 
1284 	/* If both the Non-Inheritance element list and Non-Inheritance element
1285 	 * ID extension list are empty, then return success since we can
1286 	 * conclude immediately that the given element does not occur in any
1287 	 * Non-Inheritance list. The is_in_noninheritlist remains set to false
1288 	 * as required.
1289 	 */
1290 	if (!ninherit_elemlist_len && !ninherit_elemextlist_len)
1291 		return QDF_STATUS_SUCCESS;
1292 
1293 	if (ie[ID_POS] != WLAN_ELEMID_EXTN_ELEM) {
1294 		if (!ninherit_elemlist_len)
1295 			return QDF_STATUS_SUCCESS;
1296 
1297 		if (!ninherit_elemlist) {
1298 			mlo_err("NULL pointer to Non-Inheritance element ID list though length of element ID list is %zu",
1299 				ninherit_elemlist_len);
1300 			return QDF_STATUS_E_NULL_VALUE;
1301 		}
1302 
1303 		for (i = 0; i < ninherit_elemlist_len; i++) {
1304 			if (ie[ID_POS] == ninherit_elemlist[i]) {
1305 				*is_in_noninheritlist = true;
1306 				return QDF_STATUS_SUCCESS;
1307 			}
1308 		}
1309 	} else {
1310 		if (!ninherit_elemextlist_len)
1311 			return QDF_STATUS_SUCCESS;
1312 
1313 		if (!ninherit_elemextlist) {
1314 			mlo_err("NULL pointer to Non-Inheritance element ID extension list though length of element ID extension list is %zu",
1315 				ninherit_elemextlist_len);
1316 			return QDF_STATUS_E_NULL_VALUE;
1317 		}
1318 
1319 		for (i = 0; i < ninherit_elemextlist_len; i++) {
1320 			if (ie[IDEXT_POS] == ninherit_elemextlist[i]) {
1321 				*is_in_noninheritlist = true;
1322 				return QDF_STATUS_SUCCESS;
1323 			}
1324 		}
1325 	}
1326 
1327 	return QDF_STATUS_SUCCESS;
1328 }
1329 
1330 static inline
1331 QDF_STATUS util_validate_reportingsta_ie(const uint8_t *reportingsta_ie,
1332 					 const uint8_t *frame_iesection,
1333 					 const qdf_size_t frame_iesection_len)
1334 {
1335 	qdf_size_t reportingsta_ie_size;
1336 
1337 	if (!reportingsta_ie) {
1338 		mlo_err("Pointer to reporting STA IE is NULL");
1339 		return QDF_STATUS_E_NULL_VALUE;
1340 	}
1341 
1342 	if (!frame_iesection) {
1343 		mlo_err("Pointer to start of IE section in reporting frame is NULL");
1344 		return QDF_STATUS_E_NULL_VALUE;
1345 	}
1346 
1347 	if (!frame_iesection_len) {
1348 		mlo_err("Length of IE section in reporting frame is zero");
1349 		return QDF_STATUS_E_INVAL;
1350 	}
1351 
1352 	if ((reportingsta_ie + ID_POS) > (frame_iesection +
1353 			frame_iesection_len - 1)) {
1354 		mlo_err_rl("Position of element ID field of element for reporting STA would exceed frame IE section boundary");
1355 		return QDF_STATUS_E_PROTO;
1356 	}
1357 
1358 	if ((reportingsta_ie + TAG_LEN_POS) > (frame_iesection +
1359 			frame_iesection_len - 1)) {
1360 		mlo_err_rl("Position of length field of element with element ID %u for reporting STA would exceed frame IE section boundary",
1361 			   reportingsta_ie[ID_POS]);
1362 		return QDF_STATUS_E_PROTO;
1363 	}
1364 
1365 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1366 	    ((reportingsta_ie + IDEXT_POS) > (frame_iesection +
1367 				frame_iesection_len - 1))) {
1368 		mlo_err_rl("Position of element ID extension field of element would exceed frame IE section boundary");
1369 		return QDF_STATUS_E_PROTO;
1370 	}
1371 
1372 	reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
1373 
1374 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1375 	    (reportingsta_ie_size < (IDEXT_POS + 1))) {
1376 		mlo_err_rl("Total length %zu of element for reporting STA is smaller than minimum required to access element ID extension %u",
1377 			   reportingsta_ie_size, IDEXT_POS + 1);
1378 		return QDF_STATUS_E_PROTO;
1379 	}
1380 
1381 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
1382 	    (reportingsta_ie_size < (PAYLOAD_START_POS + OUI_LEN))) {
1383 		mlo_err_rl("Total length %zu of element for reporting STA is smaller than minimum required to access vendor EID %u",
1384 			   reportingsta_ie_size, PAYLOAD_START_POS + OUI_LEN);
1385 		return QDF_STATUS_E_PROTO;
1386 	}
1387 
1388 	if ((reportingsta_ie + reportingsta_ie_size) >
1389 			(frame_iesection + frame_iesection_len)) {
1390 		if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
1391 			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",
1392 				   reportingsta_ie_size,
1393 				   reportingsta_ie[ID_POS],
1394 				   reportingsta_ie[IDEXT_POS]);
1395 		} else {
1396 			mlo_err_rl("Total size %zu octets of element with element ID %u for reporting STA would exceed frame IE section boundary",
1397 				   reportingsta_ie_size,
1398 				   reportingsta_ie[ID_POS]);
1399 		}
1400 
1401 		return QDF_STATUS_E_PROTO;
1402 	}
1403 
1404 	return QDF_STATUS_SUCCESS;
1405 }
1406 
1407 static inline
1408 QDF_STATUS util_validate_sta_prof_ie(const uint8_t *sta_prof_ie,
1409 				     const uint8_t *sta_prof_iesection,
1410 				     const qdf_size_t sta_prof_iesection_len)
1411 {
1412 	qdf_size_t sta_prof_ie_size;
1413 
1414 	if (!sta_prof_ie) {
1415 		mlo_err("Pointer to STA profile IE is NULL");
1416 		return QDF_STATUS_E_NULL_VALUE;
1417 	}
1418 
1419 	if (!sta_prof_iesection) {
1420 		mlo_err("Pointer to start of IE section in STA profile is NULL");
1421 		return QDF_STATUS_E_NULL_VALUE;
1422 	}
1423 
1424 	if (!sta_prof_iesection_len) {
1425 		mlo_err("Length of IE section in STA profile is zero");
1426 		return QDF_STATUS_E_INVAL;
1427 	}
1428 
1429 	if ((sta_prof_ie + ID_POS) > (sta_prof_iesection +
1430 			sta_prof_iesection_len - 1)) {
1431 		mlo_err_rl("Position of element ID field of STA profile element would exceed STA profile IE section boundary");
1432 		return QDF_STATUS_E_PROTO;
1433 	}
1434 
1435 	if ((sta_prof_ie + TAG_LEN_POS) > (sta_prof_iesection +
1436 			sta_prof_iesection_len - 1)) {
1437 		mlo_err_rl("Position of length field of element with element ID %u in STA profile would exceed STA profile IE section boundary",
1438 			   sta_prof_ie[ID_POS]);
1439 		return QDF_STATUS_E_PROTO;
1440 	}
1441 
1442 	if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1443 	    ((sta_prof_ie + IDEXT_POS) > (sta_prof_iesection +
1444 				sta_prof_iesection_len - 1))) {
1445 		mlo_err_rl("Position of element ID extension field of element would exceed STA profile IE section boundary");
1446 		return QDF_STATUS_E_PROTO;
1447 	}
1448 
1449 	sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
1450 
1451 	if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1452 	    (sta_prof_ie_size < (IDEXT_POS + 1))) {
1453 		mlo_err_rl("Total length %zu of STA profile element is smaller than minimum required to access element ID extension %u",
1454 			   sta_prof_ie_size, IDEXT_POS + 1);
1455 		return QDF_STATUS_E_PROTO;
1456 	}
1457 
1458 	if ((sta_prof_ie + sta_prof_ie_size) >
1459 			(sta_prof_iesection + sta_prof_iesection_len)) {
1460 		if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
1461 			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",
1462 				   sta_prof_ie_size,
1463 				   sta_prof_ie[ID_POS],
1464 				   sta_prof_ie[IDEXT_POS]);
1465 		} else {
1466 			mlo_err_rl("Total size %zu octets of element with element ID %u in STA profile would exceed STA profile IE section boundary",
1467 				   sta_prof_ie_size,
1468 				   sta_prof_ie[ID_POS]);
1469 		}
1470 
1471 		return QDF_STATUS_E_PROTO;
1472 	}
1473 
1474 	return QDF_STATUS_SUCCESS;
1475 }
1476 
1477 #define MLO_LINKSPECIFIC_ASSOC_REQ_FC0  0x00
1478 #define MLO_LINKSPECIFIC_ASSOC_REQ_FC1  0x00
1479 #define MLO_LINKSPECIFIC_ASSOC_RESP_FC0 0x10
1480 #define MLO_LINKSPECIFIC_ASSOC_RESP_FC1 0x00
1481 #define MLO_LINKSPECIFIC_PROBE_RESP_FC0 0x50
1482 #define MLO_LINKSPECIFIC_PROBE_RESP_FC1 0x00
1483 
1484 static
1485 QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len,
1486 				    uint8_t subtype,
1487 				    struct qdf_mac_addr link_addr,
1488 				    uint8_t *link_frame,
1489 				    qdf_size_t link_frame_maxsize,
1490 				    qdf_size_t *link_frame_len)
1491 {
1492 	/* Please see documentation for util_gen_link_assoc_req() and
1493 	 * util_gen_link_assoc_resp() for information on the inputs to and
1494 	 * output from this helper, since those APIs are essentially wrappers
1495 	 * over this helper.
1496 	 */
1497 
1498 	/* Pointer to Multi-Link element/Multi-Link element fragment sequence */
1499 	uint8_t *mlieseq;
1500 	/* Total length of Multi-Link element sequence (including fragements if
1501 	 * any)
1502 	 */
1503 	qdf_size_t mlieseqlen;
1504 	/* Variant (i.e. type) of the Multi-Link element */
1505 	enum wlan_ml_variant variant;
1506 
1507 	/* Length of the payload of the Multi-Link element (inclusive of
1508 	 * fragment payloads if any) without IE headers and element ID extension
1509 	 */
1510 	qdf_size_t mlieseqpayloadlen;
1511 	/* Pointer to copy of the payload of the Multi-Link element (inclusive
1512 	 * of fragment payloads if any) without IE headers and element ID
1513 	 * extension
1514 	 */
1515 	uint8_t *mlieseqpayload_copy;
1516 
1517 	/* Pointer to start of Link Info within the copy of the payload of the
1518 	 * Multi-Link element
1519 	 */
1520 	uint8_t *link_info;
1521 	/* Length of the Link Info */
1522 	qdf_size_t link_info_len;
1523 
1524 	/* Pointer to the IE section that occurs after the fixed fields in the
1525 	 * original frame for the reporting STA.
1526 	 */
1527 	uint8_t *frame_iesection;
1528 	/* Offset to the start of the IE section in the original frame for the
1529 	 * reporting STA.
1530 	 */
1531 	qdf_size_t frame_iesection_offset;
1532 	/* Total length of the IE section in the original frame for the
1533 	 * reporting STA.
1534 	 */
1535 	qdf_size_t frame_iesection_len;
1536 
1537 	/* Pointer to the IEEE802.11 frame header in the link specific frame
1538 	 * being generated for the reported STA.
1539 	 */
1540 	struct wlan_frame_hdr *link_frame_hdr;
1541 	/* Current position in the link specific frame being generated for the
1542 	 * reported STA.
1543 	 */
1544 	uint8_t *link_frame_currpos;
1545 	/* Current length of the link specific frame being generated for the
1546 	 * reported STA.
1547 	 */
1548 	qdf_size_t link_frame_currlen;
1549 
1550 	/* Pointer to IE for reporting STA */
1551 	const uint8_t *reportingsta_ie;
1552 	/* Total size of IE for reporting STA, inclusive of the element header
1553 	 */
1554 	qdf_size_t reportingsta_ie_size;
1555 
1556 	/* Pointer to current position in STA profile */
1557 	uint8_t *sta_prof_currpos;
1558 	/* Remaining length of STA profile */
1559 	qdf_size_t sta_prof_remlen;
1560 	/* Pointer to start of IE section in STA profile that occurs after fixed
1561 	 * fields.
1562 	 */
1563 	uint8_t *sta_prof_iesection;
1564 	/* Total length of IE section in STA profile */
1565 	qdf_size_t sta_prof_iesection_len;
1566 	/* Pointer to current position being processed in IE section in STA
1567 	 * profile.
1568 	 */
1569 	uint8_t *sta_prof_iesection_currpos;
1570 	/* Remaining length of IE section in STA profile */
1571 	qdf_size_t sta_prof_iesection_remlen;
1572 
1573 	/* Pointer to IE in STA profile, that occurs within IE section */
1574 	uint8_t *sta_prof_ie;
1575 	/* Total size of IE in STA profile, inclusive of the element header */
1576 	qdf_size_t sta_prof_ie_size;
1577 
1578 	/* Pointer to element ID list in Non-Inheritance IE */
1579 	uint8_t *ninherit_elemlist;
1580 	/* Length of element ID list in Non-Inheritance IE */
1581 	qdf_size_t ninherit_elemlist_len;
1582 	/* Pointer to element ID extension list in Non-Inheritance IE */
1583 	uint8_t *ninherit_elemextlist;
1584 	/* Length of element ID extension list in Non-Inheritance IE */
1585 	qdf_size_t ninherit_elemextlist_len;
1586 	/* Whether a given IE is in a non-inheritance list */
1587 	bool is_in_noninheritlist;
1588 
1589 	/* Whether MAC address of reported STA is valid */
1590 	bool is_reportedmacaddr_valid;
1591 	/* MAC address of reported STA */
1592 	struct qdf_mac_addr reportedmacaddr;
1593 
1594 	/* Pointer to per-STA profile */
1595 	uint8_t *persta_prof;
1596 	/* Length of the containing buffer which starts with the per-STA profile
1597 	 */
1598 	qdf_size_t persta_prof_bufflen;
1599 
1600 	/* Other variables for temporary purposes */
1601 
1602 	/* Variable into which API for determining fragment information will
1603 	 * indicate whether the element is the start of a fragment sequence or
1604 	 * not.
1605 	 */
1606 	bool is_elemfragseq;
1607 	/*  De-fragmented payload length returned by API for element
1608 	 *  defragmentation.
1609 	 */
1610 	qdf_size_t defragpayload_len;
1611 	/* Variable into which API for determining fragment information will
1612 	 * indicate whether the subelement is the start of a fragment sequence
1613 	 * or not.
1614 	 */
1615 	bool is_subelemfragseq;
1616 	/* Total length of the subelement fragment sequence, inclusive of
1617 	 * subelement header and the headers of fragments if any.
1618 	 */
1619 	qdf_size_t subelemseqtotallen;
1620 	/* Total length of the subelement fragment sequence payload, excluding
1621 	 * subelement header and fragment headers if any.
1622 	 */
1623 	qdf_size_t subelemseqpayloadlen;
1624 	/* Pointer to Beacon interval in STA info field */
1625 	uint16_t beaconinterval;
1626 	/* Whether Beacon interval value valid */
1627 	bool is_beaconinterval_valid;
1628 	/* TSF timer of the reporting AP */
1629 	uint64_t tsf;
1630 	/* TSF offset of the reproted AP */
1631 	uint64_t tsfoffset;
1632 	/* TSF offset value valid */
1633 	bool is_tsfoffset_valid;
1634 	/* If Complete Profile or not*/
1635 	bool is_completeprofile;
1636 	qdf_size_t tmplen;
1637 	QDF_STATUS ret;
1638 
1639 	if (!frame) {
1640 		mlo_err("Pointer to original frame is NULL");
1641 		return QDF_STATUS_E_NULL_VALUE;
1642 	}
1643 
1644 	if (!frame_len) {
1645 		mlo_err("Length of original frame is zero");
1646 		return QDF_STATUS_E_INVAL;
1647 	}
1648 
1649 	if ((subtype != WLAN_FC0_STYPE_ASSOC_REQ) &&
1650 	    (subtype != WLAN_FC0_STYPE_REASSOC_REQ) &&
1651 	    (subtype != WLAN_FC0_STYPE_ASSOC_RESP) &&
1652 	    (subtype != WLAN_FC0_STYPE_REASSOC_RESP) &&
1653 	    (subtype != WLAN_FC0_STYPE_PROBE_RESP)) {
1654 		mlo_err("802.11 frame subtype %u is invalid", subtype);
1655 		return QDF_STATUS_E_INVAL;
1656 	}
1657 
1658 	if (!link_frame) {
1659 		mlo_err("Pointer to secondary link specific frame is NULL");
1660 		return QDF_STATUS_E_NULL_VALUE;
1661 	}
1662 
1663 	if (!link_frame_maxsize) {
1664 		mlo_err("Maximum size of secondary link specific frame is zero");
1665 		return QDF_STATUS_E_INVAL;
1666 	}
1667 
1668 	if (!link_frame_len) {
1669 		mlo_err("Pointer to populated length of secondary link specific frame is NULL");
1670 		return QDF_STATUS_E_NULL_VALUE;
1671 	}
1672 
1673 	frame_iesection_offset = 0;
1674 
1675 	if (subtype == WLAN_FC0_STYPE_ASSOC_REQ) {
1676 		frame_iesection_offset = WLAN_ASSOC_REQ_IES_OFFSET;
1677 	} else if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
1678 		frame_iesection_offset = WLAN_REASSOC_REQ_IES_OFFSET;
1679 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
1680 		frame_iesection_offset = WLAN_PROBE_RESP_IES_OFFSET;
1681 		qdf_mem_copy(&tsf, frame, WLAN_TIMESTAMP_LEN);
1682 		tsf = qdf_le64_to_cpu(tsf);
1683 	} else {
1684 		/* This is a (re)association response */
1685 		frame_iesection_offset = WLAN_ASSOC_RSP_IES_OFFSET;
1686 	}
1687 
1688 	if (frame_len < frame_iesection_offset) {
1689 		/* The caller is supposed to have confirmed that this is a valid
1690 		 * frame containing a Multi-Link element. Hence we treat this as
1691 		 * a case of invalid argument being passed to us.
1692 		 */
1693 		mlo_err("Frame length %zu is smaller than the IE section offset %zu for subtype %u",
1694 			frame_len, frame_iesection_offset, subtype);
1695 		return QDF_STATUS_E_INVAL;
1696 	}
1697 
1698 	frame_iesection_len = frame_len - frame_iesection_offset;
1699 
1700 	if (frame_iesection_len == 0) {
1701 		/* The caller is supposed to have confirmed that this is a valid
1702 		 * frame containing a Multi-Link element. Hence we treat this as
1703 		 * a case of invalid argument being passed to us.
1704 		 */
1705 		mlo_err("No space left in frame for IE section");
1706 		return QDF_STATUS_E_INVAL;
1707 	}
1708 
1709 	frame_iesection = frame + frame_iesection_offset;
1710 
1711 	mlieseq = NULL;
1712 	mlieseqlen = 0;
1713 
1714 	ret = util_find_mlie(frame_iesection, frame_iesection_len, &mlieseq,
1715 			     &mlieseqlen);
1716 	if (QDF_IS_STATUS_ERROR(ret))
1717 		return ret;
1718 
1719 	if (!mlieseq) {
1720 		/* The caller is supposed to have confirmed that a Multi-Link
1721 		 * element is present in the frame. Hence we treat this as a
1722 		 * case of invalid argument being passed to us.
1723 		 */
1724 		mlo_err("Invalid original frame since no Multi-Link element found");
1725 		return QDF_STATUS_E_INVAL;
1726 	}
1727 
1728 	/* Sanity check the Multi-Link element sequence length */
1729 	if (!mlieseqlen) {
1730 		mlo_err("Length of Multi-Link element sequence is zero. Investigate.");
1731 		return QDF_STATUS_E_FAILURE;
1732 	}
1733 
1734 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
1735 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
1736 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
1737 		return QDF_STATUS_E_PROTO;
1738 	}
1739 
1740 	ret = util_get_mlie_variant(mlieseq, mlieseqlen, (int *)&variant);
1741 	if (QDF_IS_STATUS_ERROR(ret))
1742 		return ret;
1743 
1744 	if (variant != WLAN_ML_VARIANT_BASIC) {
1745 		mlo_err_rl("Unexpected variant %u of Multi-Link element.",
1746 			   variant);
1747 		return QDF_STATUS_E_PROTO;
1748 	}
1749 
1750 	mlieseqpayloadlen = 0;
1751 	tmplen = 0;
1752 	is_elemfragseq = false;
1753 
1754 	ret = wlan_get_elem_fragseq_info(mlieseq,
1755 					 mlieseqlen,
1756 					 &is_elemfragseq,
1757 					 &tmplen,
1758 					 &mlieseqpayloadlen);
1759 	if (QDF_IS_STATUS_ERROR(ret))
1760 		return ret;
1761 
1762 	if (is_elemfragseq) {
1763 		if (tmplen != mlieseqlen) {
1764 			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",
1765 				   tmplen, mlieseqlen);
1766 			return QDF_STATUS_E_FAILURE;
1767 		}
1768 
1769 		if (!mlieseqpayloadlen) {
1770 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
1771 			return QDF_STATUS_E_FAILURE;
1772 		}
1773 
1774 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
1775 			  mlieseqpayloadlen);
1776 	} else {
1777 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
1778 			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",
1779 				   mlieseqlen,
1780 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
1781 			return QDF_STATUS_E_FAILURE;
1782 		}
1783 
1784 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
1785 	}
1786 
1787 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
1788 
1789 	if (!mlieseqpayload_copy) {
1790 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
1791 		return QDF_STATUS_E_NOMEM;
1792 	}
1793 
1794 	if (is_elemfragseq) {
1795 		ret = wlan_defrag_elem_fragseq(false,
1796 					       mlieseq,
1797 					       mlieseqlen,
1798 					       mlieseqpayload_copy,
1799 					       mlieseqpayloadlen,
1800 					       &defragpayload_len);
1801 		if (QDF_IS_STATUS_ERROR(ret)) {
1802 			qdf_mem_free(mlieseqpayload_copy);
1803 			return ret;
1804 		}
1805 
1806 		if (defragpayload_len != mlieseqpayloadlen) {
1807 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
1808 				   defragpayload_len, mlieseqpayloadlen);
1809 			qdf_mem_free(mlieseqpayload_copy);
1810 			return QDF_STATUS_E_FAILURE;
1811 		}
1812 	} else {
1813 		qdf_mem_copy(mlieseqpayload_copy,
1814 			     mlieseq + sizeof(struct ie_header) + 1,
1815 			     mlieseqpayloadlen);
1816 	}
1817 
1818 	link_info = NULL;
1819 	link_info_len = 0;
1820 
1821 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
1822 					 mlieseqpayloadlen,
1823 					 &link_info,
1824 					 &link_info_len);
1825 	if (QDF_IS_STATUS_ERROR(ret)) {
1826 		qdf_mem_free(mlieseqpayload_copy);
1827 		return ret;
1828 	}
1829 
1830 	/* As per the standard, the sender must include Link Info for
1831 	 * association request/response. Throw an error if we are unable to
1832 	 * obtain this.
1833 	 */
1834 	if (!link_info) {
1835 		mlo_err_rl("Unable to successfully obtain Link Info");
1836 		qdf_mem_free(mlieseqpayload_copy);
1837 		return QDF_STATUS_E_PROTO;
1838 	}
1839 
1840 	mlo_debug("Dumping hex of link info after parsing Multi-Link element control");
1841 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_DEBUG,
1842 			   link_info, link_info_len);
1843 
1844 	/* Note: We may have a future change to skip subelements which are not
1845 	 * Per-STA Profile, handle more than two links in MLO, handle cases
1846 	 * where we unexpectedly find more Per-STA Profiles than expected, etc.
1847 	 */
1848 
1849 	persta_prof = link_info;
1850 	persta_prof_bufflen = link_info_len;
1851 
1852 	is_subelemfragseq = false;
1853 	subelemseqtotallen = 0;
1854 	subelemseqpayloadlen = 0;
1855 
1856 	ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1857 					    persta_prof,
1858 					    persta_prof_bufflen,
1859 					    &is_subelemfragseq,
1860 					    &subelemseqtotallen,
1861 					    &subelemseqpayloadlen);
1862 	if (QDF_IS_STATUS_ERROR(ret)) {
1863 		qdf_mem_free(mlieseqpayload_copy);
1864 		return ret;
1865 	}
1866 
1867 	if (is_subelemfragseq) {
1868 		if (!subelemseqpayloadlen) {
1869 			mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
1870 			qdf_mem_free(mlieseqpayload_copy);
1871 			return QDF_STATUS_E_FAILURE;
1872 		}
1873 
1874 		mlo_debug("Subelement fragment sequence found with payload len %zu",
1875 			  subelemseqpayloadlen);
1876 
1877 		ret = wlan_defrag_subelem_fragseq(true,
1878 						  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1879 						  persta_prof,
1880 						  persta_prof_bufflen,
1881 						  NULL,
1882 						  0,
1883 						  &defragpayload_len);
1884 		if (QDF_IS_STATUS_ERROR(ret)) {
1885 			qdf_mem_free(mlieseqpayload_copy);
1886 			return ret;
1887 		}
1888 
1889 		if (defragpayload_len != subelemseqpayloadlen) {
1890 			mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
1891 				   defragpayload_len,
1892 				   subelemseqpayloadlen);
1893 			qdf_mem_free(mlieseqpayload_copy);
1894 			return QDF_STATUS_E_FAILURE;
1895 		}
1896 	} else {
1897 		if (persta_prof_bufflen <
1898 			(sizeof(struct subelem_header) +
1899 			 persta_prof[TAG_LEN_POS])) {
1900 			mlo_err_rl("Length of buffer containing per-STA profile %zu octets is smaller than total size of current subelement %zu octets",
1901 				   persta_prof_bufflen,
1902 				   sizeof(struct subelem_header) +
1903 						persta_prof[TAG_LEN_POS]);
1904 			return QDF_STATUS_E_PROTO;
1905 		}
1906 
1907 		subelemseqpayloadlen = persta_prof[TAG_LEN_POS];
1908 	}
1909 
1910 	sta_prof_remlen = 0;
1911 	sta_prof_currpos = NULL;
1912 	is_reportedmacaddr_valid = false;
1913 	is_beaconinterval_valid = false;
1914 	is_completeprofile = false;
1915 	is_tsfoffset_valid = false;
1916 
1917 	/* Parse per-STA profile */
1918 	ret = util_parse_bvmlie_perstaprofile_stactrl(persta_prof +
1919 						      sizeof(struct subelem_header),
1920 						      subelemseqpayloadlen,
1921 						      NULL,
1922 						      &beaconinterval,
1923 						      &is_beaconinterval_valid,
1924 						      &tsfoffset,
1925 						      &is_tsfoffset_valid,
1926 						      &is_completeprofile,
1927 						      &is_reportedmacaddr_valid,
1928 						      &reportedmacaddr,
1929 						      true,
1930 						      &sta_prof_currpos,
1931 						      &sta_prof_remlen);
1932 	if (QDF_IS_STATUS_ERROR(ret)) {
1933 		qdf_mem_free(mlieseqpayload_copy);
1934 		return ret;
1935 	}
1936 
1937 	if (subtype == WLAN_FC0_STYPE_PROBE_RESP && !is_completeprofile) {
1938 		mlo_err("Complete profile information is not present in per-STA profile of probe response frame");
1939 		return QDF_STATUS_E_NOSUPPORT;
1940 	}
1941 
1942 	/* We double check for a NULL STA Profile, though the helper function
1943 	 * above would have taken care of this. We need to get a non-NULL STA
1944 	 * profile, because we need to get at least the expected fixed fields,
1945 	 * even if there is an (improbable) total inheritance.
1946 	 */
1947 	if (!sta_prof_currpos) {
1948 		mlo_err_rl("STA profile is NULL");
1949 		qdf_mem_free(mlieseqpayload_copy);
1950 		return QDF_STATUS_E_PROTO;
1951 	}
1952 
1953 	/* As per the standard, the sender sets the MAC address in the per-STA
1954 	 * profile in association request/response. Without this, we cannot
1955 	 * generate the link specific frame.
1956 	 */
1957 	if (!is_reportedmacaddr_valid) {
1958 		mlo_err_rl("Unable to get MAC address from per-STA profile");
1959 		qdf_mem_free(mlieseqpayload_copy);
1960 		return QDF_STATUS_E_PROTO;
1961 	}
1962 
1963 	link_frame_currpos = link_frame;
1964 	*link_frame_len = 0;
1965 	link_frame_currlen = 0;
1966 
1967 	if (link_frame_maxsize < WLAN_MAC_HDR_LEN_3A) {
1968 		mlo_err("Insufficent space in link specific frame for 802.11 header. Required: %u octets, available: %zu octets",
1969 			WLAN_MAC_HDR_LEN_3A, link_frame_maxsize);
1970 
1971 		qdf_mem_free(mlieseqpayload_copy);
1972 		return QDF_STATUS_E_NOMEM;
1973 	}
1974 
1975 	link_frame_currpos += WLAN_MAC_HDR_LEN_3A;
1976 	link_frame_currlen += WLAN_MAC_HDR_LEN_3A;
1977 
1978 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
1979 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
1980 		mlo_debug("Populating fixed fields for (re)assoc req in link specific frame");
1981 
1982 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
1983 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
1984 				   sta_prof_remlen,
1985 				   WLAN_CAPABILITYINFO_LEN);
1986 
1987 			qdf_mem_free(mlieseqpayload_copy);
1988 			return QDF_STATUS_E_PROTO;
1989 		}
1990 
1991 		/* Capability information is specific to the link. Copy this
1992 		 * from the STA profile.
1993 		 */
1994 
1995 		if ((link_frame_maxsize - link_frame_currlen) <
1996 				WLAN_CAPABILITYINFO_LEN) {
1997 			mlo_err("Insufficent space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
1998 				WLAN_CAPABILITYINFO_LEN,
1999 				(link_frame_maxsize - link_frame_currlen));
2000 
2001 			qdf_mem_free(mlieseqpayload_copy);
2002 			return QDF_STATUS_E_NOMEM;
2003 		}
2004 
2005 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2006 			     WLAN_CAPABILITYINFO_LEN);
2007 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2008 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2009 		mlo_debug("Added Capablity Info field (%u octets) to link specific frame",
2010 			  WLAN_CAPABILITYINFO_LEN);
2011 
2012 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2013 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2014 
2015 		/* Listen Interval is common between all links. Copy this from
2016 		 * the reporting section of the frame.
2017 		 */
2018 
2019 		if ((link_frame_maxsize - link_frame_currlen) <
2020 				WLAN_LISTENINTERVAL_LEN) {
2021 			mlo_err("Insufficent space in link specific frame for Listen Interval field. Required: %u octets, available: %zu octets",
2022 				WLAN_LISTENINTERVAL_LEN,
2023 				(link_frame_maxsize - link_frame_currlen));
2024 
2025 			qdf_mem_free(mlieseqpayload_copy);
2026 			return QDF_STATUS_E_NOMEM;
2027 		}
2028 
2029 		qdf_mem_copy(link_frame_currpos,
2030 			     frame + WLAN_CAPABILITYINFO_LEN,
2031 			     WLAN_LISTENINTERVAL_LEN);
2032 		link_frame_currpos += WLAN_LISTENINTERVAL_LEN;
2033 		link_frame_currlen += WLAN_LISTENINTERVAL_LEN;
2034 		mlo_debug("Added Listen Interval field (%u octets) to link specific frame",
2035 			  WLAN_LISTENINTERVAL_LEN);
2036 
2037 		if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
2038 			/* Current AP address is common between all links. Copy
2039 			 * this from the reporting section of the frame.
2040 			 */
2041 			if ((link_frame_maxsize - link_frame_currlen) <
2042 				QDF_MAC_ADDR_SIZE) {
2043 				mlo_err("Insufficent space in link specific frame for current AP address. Required: %u octets, available: %zu octets",
2044 					QDF_MAC_ADDR_SIZE,
2045 					(link_frame_maxsize -
2046 						link_frame_currlen));
2047 
2048 				qdf_mem_free(mlieseqpayload_copy);
2049 				return QDF_STATUS_E_NOMEM;
2050 			}
2051 
2052 			qdf_mem_copy(link_frame_currpos,
2053 				     frame + WLAN_CAPABILITYINFO_LEN +
2054 						WLAN_LISTENINTERVAL_LEN,
2055 				     QDF_MAC_ADDR_SIZE);
2056 			link_frame_currpos += QDF_MAC_ADDR_SIZE;
2057 			link_frame_currlen += QDF_MAC_ADDR_SIZE;
2058 			mlo_debug("Reassoc req: Added Current AP address field (%u octets) to link specific frame",
2059 				  QDF_MAC_ADDR_SIZE);
2060 		}
2061 	} else if (subtype == WLAN_FC0_STYPE_ASSOC_RESP ||
2062 		   subtype == WLAN_FC0_STYPE_REASSOC_RESP) {
2063 		/* This is a (re)association response */
2064 		mlo_debug("Populating fixed fields for (re)assoc resp in link specific frame");
2065 
2066 		if (sta_prof_remlen <
2067 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2068 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info + length of Status Code %u",
2069 				   sta_prof_remlen,
2070 				   WLAN_CAPABILITYINFO_LEN +
2071 					WLAN_STATUSCODE_LEN);
2072 
2073 			qdf_mem_free(mlieseqpayload_copy);
2074 			return QDF_STATUS_E_PROTO;
2075 		}
2076 
2077 		/* Capability information and Status Code are specific to the
2078 		 * link. Copy these from the STA profile.
2079 		 */
2080 
2081 		if ((link_frame_maxsize - link_frame_currlen) <
2082 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2083 			mlo_err("Insufficent space in link specific frame for Capability Info and Status Code fields. Required: %u octets, available: %zu octets",
2084 				WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN,
2085 				(link_frame_maxsize - link_frame_currlen));
2086 
2087 			qdf_mem_free(mlieseqpayload_copy);
2088 			return QDF_STATUS_E_NOMEM;
2089 		}
2090 
2091 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2092 			     (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN));
2093 		link_frame_currpos += (WLAN_CAPABILITYINFO_LEN +
2094 						WLAN_STATUSCODE_LEN);
2095 		link_frame_currlen += (WLAN_CAPABILITYINFO_LEN +
2096 				WLAN_STATUSCODE_LEN);
2097 		mlo_debug("Added Capability Info and Status Code fields (%u octets) to link specific frame",
2098 			  WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN);
2099 
2100 		sta_prof_currpos += (WLAN_CAPABILITYINFO_LEN +
2101 				WLAN_STATUSCODE_LEN);
2102 		sta_prof_remlen -= (WLAN_CAPABILITYINFO_LEN +
2103 				WLAN_STATUSCODE_LEN);
2104 
2105 		/* AID is common between all links. Copy this from the original
2106 		 * frame.
2107 		 */
2108 
2109 		if ((link_frame_maxsize - link_frame_currlen) < WLAN_AID_LEN) {
2110 			mlo_err("Insufficent space in link specific frame for AID field. Required: %u octets, available: %zu octets",
2111 				WLAN_AID_LEN,
2112 				(link_frame_maxsize - link_frame_currlen));
2113 
2114 			qdf_mem_free(mlieseqpayload_copy);
2115 			return QDF_STATUS_E_NOMEM;
2116 		}
2117 
2118 		qdf_mem_copy(link_frame_currpos,
2119 			     frame + WLAN_CAPABILITYINFO_LEN +
2120 					WLAN_STATUSCODE_LEN,
2121 			     WLAN_AID_LEN);
2122 		link_frame_currpos += WLAN_AID_LEN;
2123 		link_frame_currlen += WLAN_AID_LEN;
2124 		mlo_debug("Added AID field (%u octets) to link specific frame",
2125 			  WLAN_AID_LEN);
2126 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2127 		/* This is a probe response */
2128 		mlo_debug("Populating fixed fields for probe response in link specific frame");
2129 
2130 		if ((link_frame_maxsize - link_frame_currlen) <
2131 				WLAN_TIMESTAMP_LEN) {
2132 			mlo_err("Insufficent space in link specific frame for Timestamp Info field. Required: %u octets, available: %zu octets",
2133 				WLAN_TIMESTAMP_LEN,
2134 				(link_frame_maxsize - link_frame_currlen));
2135 
2136 			qdf_mem_free(mlieseqpayload_copy);
2137 			return QDF_STATUS_E_NOMEM;
2138 		}
2139 
2140 		/* Per spec 11be_D2.1.1, the TSF Offset subfield of the STA Info
2141 		 * field indicates the offset (Toffset)between the TSF timer of
2142 		 * the reported AP (TA) and the TSF timer of the reporting
2143 		 * AP (TB) and is encoded as a 2s complement signed integer
2144 		 * with units of 2 µs. Toffset is calculated as
2145 		 * Toffset= Floor((TA – TB)/2).
2146 		 */
2147 		if (is_tsfoffset_valid)
2148 			tsf += tsfoffset * 2;
2149 
2150 		qdf_mem_copy(link_frame_currpos, &tsf, WLAN_TIMESTAMP_LEN);
2151 		link_frame_currpos += WLAN_TIMESTAMP_LEN;
2152 		link_frame_currlen += WLAN_TIMESTAMP_LEN;
2153 		mlo_debug("Added Timestamp Info field (%u octets) to link specific frame",
2154 			  WLAN_TIMESTAMP_LEN);
2155 
2156 		if (!is_beaconinterval_valid) {
2157 			mlo_err_rl("Beacon interval information not present in STA info field of per-STA profile");
2158 			qdf_mem_free(mlieseqpayload_copy);
2159 			return QDF_STATUS_E_PROTO;
2160 		}
2161 
2162 		/* Beacon Interval information copy this from
2163 		 * the STA info field.
2164 		 */
2165 		if ((link_frame_maxsize - link_frame_currlen) <
2166 				WLAN_BEACONINTERVAL_LEN) {
2167 			mlo_err("Insufficent space in link specific frame for Beacon Interval Info field. Required: %u octets, available: %zu octets",
2168 				WLAN_BEACONINTERVAL_LEN,
2169 				(link_frame_maxsize - link_frame_currlen));
2170 
2171 			qdf_mem_free(mlieseqpayload_copy);
2172 			return QDF_STATUS_E_NOMEM;
2173 		}
2174 
2175 		qdf_mem_copy(link_frame_currpos, &beaconinterval,
2176 			     WLAN_BEACONINTERVAL_LEN);
2177 		link_frame_currpos += WLAN_BEACONINTERVAL_LEN;
2178 		link_frame_currlen += WLAN_BEACONINTERVAL_LEN;
2179 		mlo_debug("Added Beacon Interval Info field (%u octets) to link specific frame",
2180 			  WLAN_BEACONINTERVAL_LEN);
2181 
2182 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
2183 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
2184 				   sta_prof_remlen,
2185 				   WLAN_CAPABILITYINFO_LEN);
2186 
2187 			qdf_mem_free(mlieseqpayload_copy);
2188 			return QDF_STATUS_E_PROTO;
2189 		}
2190 
2191 		/* Capability information is specific to the link. Copy this
2192 		 * from the STA profile.
2193 		 */
2194 
2195 		if ((link_frame_maxsize - link_frame_currlen) <
2196 				WLAN_CAPABILITYINFO_LEN) {
2197 			mlo_err("Insufficent space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
2198 				WLAN_CAPABILITYINFO_LEN,
2199 				(link_frame_maxsize - link_frame_currlen));
2200 
2201 			qdf_mem_free(mlieseqpayload_copy);
2202 			return QDF_STATUS_E_NOMEM;
2203 		}
2204 
2205 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2206 			     WLAN_CAPABILITYINFO_LEN);
2207 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2208 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2209 		mlo_debug("Added Capablity Info field (%u octets) to link specific frame",
2210 			  WLAN_CAPABILITYINFO_LEN);
2211 
2212 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2213 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2214 	}
2215 
2216 	sta_prof_iesection = sta_prof_currpos;
2217 	sta_prof_iesection_len = sta_prof_remlen;
2218 
2219 	/* Populate non-inheritance lists if applicable */
2220 	ninherit_elemlist_len = 0;
2221 	ninherit_elemlist = NULL;
2222 	ninherit_elemextlist_len = 0;
2223 	ninherit_elemextlist = NULL;
2224 
2225 	ret = util_get_noninheritlists(sta_prof_iesection,
2226 				       sta_prof_iesection_len,
2227 				       &ninherit_elemlist,
2228 				       &ninherit_elemlist_len,
2229 				       &ninherit_elemextlist,
2230 				       &ninherit_elemextlist_len);
2231 	if (QDF_IS_STATUS_ERROR(ret)) {
2232 		qdf_mem_free(mlieseqpayload_copy);
2233 		return ret;
2234 	}
2235 
2236 	/* Go through IEs of the reporting STA, and those in STA profile, merge
2237 	 * them into link_frame (except for elements in the Non-Inheritance
2238 	 * list).
2239 	 *
2240 	 * Note: Currently, only 2-link MLO is supported here. We may have a
2241 	 * future change to expand to more links.
2242 	 */
2243 	reportingsta_ie = util_find_eid(WLAN_ELEMID_SSID, frame_iesection,
2244 					frame_iesection_len);
2245 
2246 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2247 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ) ||
2248 	    (subtype == WLAN_FC0_STYPE_PROBE_RESP)) {
2249 		/* Sanity check that the SSID element is present for the
2250 		 * reporting STA. There is no stipulation in the standard for
2251 		 * the STA profile in this regard, so we do not check the STA
2252 		 * profile for the SSID element.
2253 		 */
2254 		if (!reportingsta_ie) {
2255 			mlo_err_rl("SSID element not found in reporting STA of the frame.");
2256 			qdf_mem_free(mlieseqpayload_copy);
2257 			return QDF_STATUS_E_PROTO;
2258 		}
2259 	} else {
2260 		/* This is a (re)association response. Sanity check that the
2261 		 * SSID element is present neither for the reporting STA nor in
2262 		 * the STA profile.
2263 		 */
2264 		if (reportingsta_ie) {
2265 			mlo_err_rl("SSID element found for reporting STA for (re)association response. It should not be present.");
2266 			qdf_mem_free(mlieseqpayload_copy);
2267 			return QDF_STATUS_E_PROTO;
2268 		}
2269 
2270 		sta_prof_ie = util_find_eid(WLAN_ELEMID_SSID,
2271 					    sta_prof_iesection,
2272 					    sta_prof_iesection_len);
2273 
2274 		if (sta_prof_ie) {
2275 			mlo_err_rl("SSID element found in STA profile for (re)association response. It should not be present.");
2276 			qdf_mem_free(mlieseqpayload_copy);
2277 			return QDF_STATUS_E_PROTO;
2278 		}
2279 	}
2280 
2281 	reportingsta_ie = reportingsta_ie ? reportingsta_ie : frame_iesection;
2282 
2283 	ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection,
2284 					    frame_iesection_len);
2285 	if (QDF_IS_STATUS_ERROR(ret)) {
2286 		qdf_mem_free(mlieseqpayload_copy);
2287 		return ret;
2288 	}
2289 
2290 	reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
2291 
2292 	while (((reportingsta_ie + reportingsta_ie_size) - frame_iesection)
2293 			<= frame_iesection_len) {
2294 		/* Skip Multi-Link element */
2295 		if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
2296 		    (reportingsta_ie[IDEXT_POS] ==
2297 				WLAN_EXTN_ELEMID_MULTI_LINK)) {
2298 			if (((reportingsta_ie + reportingsta_ie_size) -
2299 					frame_iesection) == frame_iesection_len)
2300 				break;
2301 
2302 			reportingsta_ie += reportingsta_ie_size;
2303 
2304 			ret = util_validate_reportingsta_ie(reportingsta_ie,
2305 							    frame_iesection,
2306 							    frame_iesection_len);
2307 			if (QDF_IS_STATUS_ERROR(ret)) {
2308 				qdf_mem_free(mlieseqpayload_copy);
2309 				return ret;
2310 			}
2311 
2312 			reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2313 				MIN_IE_LEN;
2314 
2315 			continue;
2316 		}
2317 
2318 		sta_prof_ie = NULL;
2319 		sta_prof_ie_size = 0;
2320 
2321 		if (sta_prof_iesection_len) {
2322 			if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2323 				sta_prof_ie = (uint8_t *)util_find_extn_eid(reportingsta_ie[ID_POS],
2324 									    reportingsta_ie[IDEXT_POS],
2325 									    sta_prof_iesection,
2326 									    sta_prof_iesection_len);
2327 			} else {
2328 				sta_prof_ie = (uint8_t *)util_find_eid(reportingsta_ie[ID_POS],
2329 								       sta_prof_iesection,
2330 								       sta_prof_iesection_len);
2331 			}
2332 		}
2333 
2334 		if (!sta_prof_ie) {
2335 			/* IE is present for reporting STA, but not in STA
2336 			 * profile.
2337 			 */
2338 
2339 			is_in_noninheritlist = false;
2340 
2341 			ret = util_eval_ie_in_noninheritlist((uint8_t *)reportingsta_ie,
2342 							     reportingsta_ie_size,
2343 							     ninherit_elemlist,
2344 							     ninherit_elemlist_len,
2345 							     ninherit_elemextlist,
2346 							     ninherit_elemextlist_len,
2347 							     &is_in_noninheritlist);
2348 
2349 			if (QDF_IS_STATUS_ERROR(ret)) {
2350 				qdf_mem_free(mlieseqpayload_copy);
2351 				return ret;
2352 			}
2353 
2354 			if (!is_in_noninheritlist) {
2355 				if ((link_frame_currpos +
2356 						reportingsta_ie_size) <=
2357 					(link_frame + link_frame_maxsize)) {
2358 					qdf_mem_copy(link_frame_currpos,
2359 						     reportingsta_ie,
2360 						     reportingsta_ie_size);
2361 
2362 					link_frame_currpos +=
2363 						reportingsta_ie_size;
2364 					link_frame_currlen +=
2365 						reportingsta_ie_size;
2366 
2367 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2368 						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",
2369 							  reportingsta_ie[ID_POS],
2370 							  reportingsta_ie[IDEXT_POS],
2371 							  reportingsta_ie_size);
2372 					} else {
2373 						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",
2374 							  reportingsta_ie[ID_POS],
2375 							  reportingsta_ie_size);
2376 					}
2377 				} else {
2378 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2379 						mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2380 							   reportingsta_ie[ID_POS],
2381 							   reportingsta_ie[IDEXT_POS],
2382 							   reportingsta_ie_size,
2383 							   link_frame_maxsize -
2384 							   link_frame_currlen);
2385 					} else {
2386 						mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2387 							   reportingsta_ie[ID_POS],
2388 							   reportingsta_ie_size,
2389 							   link_frame_maxsize -
2390 							   link_frame_currlen);
2391 					}
2392 
2393 					qdf_mem_free(mlieseqpayload_copy);
2394 					return QDF_STATUS_E_NOMEM;
2395 				}
2396 			} else {
2397 				if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2398 					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.",
2399 						  reportingsta_ie[ID_POS],
2400 						  reportingsta_ie[IDEXT_POS],
2401 						  reportingsta_ie_size);
2402 				} else {
2403 					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.",
2404 						  reportingsta_ie[ID_POS],
2405 						  reportingsta_ie_size);
2406 				}
2407 			}
2408 		} else {
2409 			/* IE is present for reporting STA and also in STA
2410 			 * profile, copy from STA profile and flag the IE in STA
2411 			 * profile as copied (by setting EID field to 0). The
2412 			 * SSID element (with EID 0) is processed first to
2413 			 * enable this. For vendor IE, compare OUI + type +
2414 			 * subType to determine if they are the same IE.
2415 			 */
2416 			/* Note: This may be revisited in a future change, to
2417 			 * adhere to provisions in the standard for multiple
2418 			 * occurrences of a given element ID/extension element
2419 			 * ID.
2420 			 */
2421 
2422 			ret = util_validate_sta_prof_ie(sta_prof_ie,
2423 							sta_prof_iesection,
2424 							sta_prof_iesection_len);
2425 			if (QDF_IS_STATUS_ERROR(ret)) {
2426 				qdf_mem_free(mlieseqpayload_copy);
2427 				return ret;
2428 			}
2429 
2430 			sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] +
2431 				MIN_IE_LEN;
2432 
2433 			sta_prof_iesection_remlen =
2434 				sta_prof_iesection_len -
2435 					(sta_prof_ie - sta_prof_iesection);
2436 
2437 			if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
2438 			    (sta_prof_iesection_remlen >= MIN_VENDOR_TAG_LEN)) {
2439 				/* If Vendor IE also presents in STA profile,
2440 				 * then ignore the Vendor IE which is for
2441 				 * reporting STA. It only needs to copy Vendor
2442 				 * IE from STA profile to link specific frame.
2443 				 * The copy happens when going through the
2444 				 * remaining IEs.
2445 				 */
2446 				;
2447 			} else {
2448 				/* Copy IE from STA profile into link specific
2449 				 * frame.
2450 				 */
2451 				if ((link_frame_currpos + sta_prof_ie_size) <=
2452 					(link_frame + link_frame_maxsize)) {
2453 					qdf_mem_copy(link_frame_currpos,
2454 						     sta_prof_ie,
2455 						     sta_prof_ie_size);
2456 
2457 					link_frame_currpos += sta_prof_ie_size;
2458 					link_frame_currlen +=
2459 						sta_prof_ie_size;
2460 
2461 					if (reportingsta_ie[ID_POS] ==
2462 							WLAN_ELEMID_EXTN_ELEM) {
2463 						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",
2464 							  sta_prof_ie[ID_POS],
2465 							  sta_prof_ie[IDEXT_POS],
2466 							  sta_prof_ie_size);
2467 					} else {
2468 						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",
2469 							  sta_prof_ie[ID_POS],
2470 							  sta_prof_ie_size);
2471 					}
2472 
2473 					sta_prof_ie[0] = 0;
2474 				} else {
2475 					if (sta_prof_ie[ID_POS] ==
2476 							WLAN_ELEMID_EXTN_ELEM) {
2477 						mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2478 							   sta_prof_ie[ID_POS],
2479 							   sta_prof_ie[IDEXT_POS],
2480 							   sta_prof_ie_size,
2481 							   link_frame_maxsize -
2482 							   link_frame_currlen);
2483 					} else {
2484 						mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2485 							   sta_prof_ie[ID_POS],
2486 							   sta_prof_ie_size,
2487 							   link_frame_maxsize -
2488 							   link_frame_currlen);
2489 					}
2490 
2491 					qdf_mem_free(mlieseqpayload_copy);
2492 					return QDF_STATUS_E_NOMEM;
2493 				}
2494 			}
2495 		}
2496 
2497 		if (((reportingsta_ie + reportingsta_ie_size) -
2498 					frame_iesection) == frame_iesection_len)
2499 			break;
2500 
2501 		reportingsta_ie += reportingsta_ie_size;
2502 
2503 		ret = util_validate_reportingsta_ie(reportingsta_ie,
2504 						    frame_iesection,
2505 						    frame_iesection_len);
2506 		if (QDF_IS_STATUS_ERROR(ret)) {
2507 			qdf_mem_free(mlieseqpayload_copy);
2508 			return ret;
2509 		}
2510 
2511 		reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2512 			MIN_IE_LEN;
2513 	}
2514 
2515 	/* Go through the remaining unprocessed IEs in STA profile and copy them
2516 	 * to the link specific frame. The processed ones are marked with 0 in
2517 	 * the first octet. The first octet corresponds to the element ID. In
2518 	 * the case of (re)association request, the element with actual ID
2519 	 * WLAN_ELEMID_SSID(0) has already been copied to the link specific
2520 	 * frame. In the case of (re)association response, it has been verified
2521 	 * that the element with actual ID WLAN_ELEMID_SSID(0) is present
2522 	 * neither for the reporting STA nor in the STA profile.
2523 	 */
2524 	sta_prof_iesection_currpos = sta_prof_iesection;
2525 	sta_prof_iesection_remlen = sta_prof_iesection_len;
2526 
2527 	while (sta_prof_iesection_remlen > 0) {
2528 		sta_prof_ie = sta_prof_iesection_currpos;
2529 		ret = util_validate_sta_prof_ie(sta_prof_ie,
2530 						sta_prof_iesection_currpos,
2531 						sta_prof_iesection_remlen);
2532 		if (QDF_IS_STATUS_ERROR(ret)) {
2533 			qdf_mem_free(mlieseqpayload_copy);
2534 			return ret;
2535 		}
2536 
2537 		sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
2538 
2539 		if (!sta_prof_ie[0]) {
2540 			/* Skip this, since it has already been processed */
2541 			sta_prof_iesection_currpos += sta_prof_ie_size;
2542 			sta_prof_iesection_remlen -= sta_prof_ie_size;
2543 			continue;
2544 		}
2545 
2546 		/* Copy IE from STA profile into link specific frame. */
2547 		if ((link_frame_currpos + sta_prof_ie_size) <=
2548 			(link_frame + link_frame_maxsize)) {
2549 			qdf_mem_copy(link_frame_currpos,
2550 				     sta_prof_ie,
2551 				     sta_prof_ie_size);
2552 
2553 			link_frame_currpos += sta_prof_ie_size;
2554 			link_frame_currlen +=
2555 				sta_prof_ie_size;
2556 
2557 			if (reportingsta_ie[ID_POS] ==
2558 					WLAN_ELEMID_EXTN_ELEM) {
2559 				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",
2560 					  sta_prof_ie[ID_POS],
2561 					  sta_prof_ie[IDEXT_POS],
2562 					  sta_prof_ie_size);
2563 			} else {
2564 				mlo_debug("IE with element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame",
2565 					  sta_prof_ie[ID_POS],
2566 					  sta_prof_ie_size);
2567 			}
2568 
2569 			sta_prof_ie[0] = 0;
2570 		} else {
2571 			if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2572 				mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2573 					   sta_prof_ie[ID_POS],
2574 					   sta_prof_ie[IDEXT_POS],
2575 					   sta_prof_ie_size,
2576 					   link_frame_maxsize -
2577 					   link_frame_currlen);
2578 			} else {
2579 				mlo_err_rl("Insufficent space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2580 					   sta_prof_ie[ID_POS],
2581 					   sta_prof_ie_size,
2582 					   link_frame_maxsize -
2583 					   link_frame_currlen);
2584 			}
2585 
2586 			qdf_mem_free(mlieseqpayload_copy);
2587 			return QDF_STATUS_E_NOMEM;
2588 		}
2589 
2590 		sta_prof_iesection_currpos += sta_prof_ie_size;
2591 		sta_prof_iesection_remlen -= sta_prof_ie_size;
2592 	}
2593 
2594 	/* Copy the link MAC addr */
2595 	link_frame_hdr = (struct wlan_frame_hdr *)link_frame;
2596 
2597 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2598 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
2599 		qdf_mem_copy(link_frame_hdr->i_addr3, &link_addr,
2600 			     QDF_MAC_ADDR_SIZE);
2601 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2602 			     QDF_MAC_ADDR_SIZE);
2603 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2604 			     QDF_MAC_ADDR_SIZE);
2605 
2606 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_REQ_FC0;
2607 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_REQ_FC1;
2608 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2609 		qdf_mem_copy(link_frame_hdr->i_addr3, &link_addr,
2610 			     QDF_MAC_ADDR_SIZE);
2611 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2612 			     QDF_MAC_ADDR_SIZE);
2613 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2614 			     QDF_MAC_ADDR_SIZE);
2615 
2616 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_PROBE_RESP_FC0;
2617 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_PROBE_RESP_FC1;
2618 	} else {
2619 		/* This is a (re)association response */
2620 
2621 		qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
2622 			     QDF_MAC_ADDR_SIZE);
2623 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2624 			     QDF_MAC_ADDR_SIZE);
2625 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2626 			     QDF_MAC_ADDR_SIZE);
2627 
2628 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_RESP_FC0;
2629 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_RESP_FC1;
2630 	}
2631 
2632 	/* Seq num not used so not populated */
2633 
2634 	qdf_mem_free(mlieseqpayload_copy);
2635 
2636 	*link_frame_len = link_frame_currlen;
2637 
2638 	return QDF_STATUS_SUCCESS;
2639 }
2640 
2641 QDF_STATUS
2642 util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2643 			struct qdf_mac_addr link_addr,
2644 			uint8_t *link_frame,
2645 			qdf_size_t link_frame_maxsize,
2646 			qdf_size_t *link_frame_len)
2647 {
2648 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2649 			(isreassoc ? WLAN_FC0_STYPE_REASSOC_REQ :
2650 				WLAN_FC0_STYPE_ASSOC_REQ),
2651 			link_addr, link_frame, link_frame_maxsize,
2652 			link_frame_len);
2653 }
2654 
2655 QDF_STATUS
2656 util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2657 			struct qdf_mac_addr link_addr,
2658 			uint8_t *link_frame,
2659 			qdf_size_t link_frame_maxsize,
2660 			qdf_size_t *link_frame_len)
2661 {
2662 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2663 			(isreassoc ?  WLAN_FC0_STYPE_REASSOC_RESP :
2664 				WLAN_FC0_STYPE_ASSOC_RESP),
2665 			link_addr, link_frame, link_frame_maxsize,
2666 			link_frame_len);
2667 }
2668 
2669 QDF_STATUS
2670 util_gen_link_probe_rsp(uint8_t *frame, qdf_size_t frame_len,
2671 			struct qdf_mac_addr link_addr,
2672 			uint8_t *link_frame,
2673 			qdf_size_t link_frame_maxsize,
2674 			qdf_size_t *link_frame_len)
2675 {
2676 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2677 			 WLAN_FC0_STYPE_PROBE_RESP,
2678 			link_addr, link_frame, link_frame_maxsize,
2679 			link_frame_len);
2680 }
2681 
2682 QDF_STATUS
2683 util_find_mlie(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq,
2684 	       qdf_size_t *mlieseqlen)
2685 {
2686 	uint8_t *bufboundary;
2687 	uint8_t *ieseq;
2688 	qdf_size_t ieseqlen;
2689 	uint8_t *currie;
2690 	uint8_t *successorfrag;
2691 
2692 	if (!buf || !buflen || !mlieseq || !mlieseqlen)
2693 		return QDF_STATUS_E_NULL_VALUE;
2694 
2695 	*mlieseq = NULL;
2696 	*mlieseqlen = 0;
2697 
2698 	/* Find Multi-Link element. In case a fragment sequence is present,
2699 	 * this element will be the leading fragment.
2700 	 */
2701 	ieseq = util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
2702 				   WLAN_EXTN_ELEMID_MULTI_LINK, buf,
2703 				   buflen);
2704 
2705 	/* Even if the element is not found, we have successfully examined the
2706 	 * buffer. The caller will be provided a NULL value for the starting of
2707 	 * the Multi-Link element. Hence, we return success.
2708 	 */
2709 	if (!ieseq)
2710 		return QDF_STATUS_SUCCESS;
2711 
2712 	bufboundary = buf + buflen;
2713 
2714 	if ((ieseq + MIN_IE_LEN) > bufboundary)
2715 		return QDF_STATUS_E_INVAL;
2716 
2717 	ieseqlen = MIN_IE_LEN + ieseq[TAG_LEN_POS];
2718 
2719 	if (ieseqlen < sizeof(struct wlan_ie_multilink))
2720 		return QDF_STATUS_E_PROTO;
2721 
2722 	if ((ieseq + ieseqlen) > bufboundary)
2723 		return QDF_STATUS_E_INVAL;
2724 
2725 	/* In the next sequence of checks, if there is no space in the buffer
2726 	 * for another element after the Multi-Link element/element fragment
2727 	 * sequence, it could indicate an issue since non-MLO EHT elements
2728 	 * would be expected to follow the Multi-Link element/element fragment
2729 	 * sequence. However, this is outside of the purview of this function,
2730 	 * hence we ignore it.
2731 	 */
2732 
2733 	currie = ieseq;
2734 	successorfrag = util_get_successorfrag(currie, buf, buflen);
2735 
2736 	/* Fragmentation definitions as of IEEE802.11be D1.0 and
2737 	 * IEEE802.11REVme D0.2 are applied. Only the case where Multi-Link
2738 	 * element is present in a buffer from the core frame is considered.
2739 	 * Future changes to fragmentation, cases where the Multi-Link element
2740 	 * is present in a subelement, etc. to be reflected here if applicable
2741 	 * as and when the rules evolve.
2742 	 */
2743 	while (successorfrag) {
2744 		/* We should not be seeing a successor fragment if the length
2745 		 * of the current IE is lesser than the max.
2746 		 */
2747 		if (currie[TAG_LEN_POS] != WLAN_MAX_IE_LEN)
2748 			return QDF_STATUS_E_PROTO;
2749 
2750 		if (successorfrag[TAG_LEN_POS] == 0)
2751 			return QDF_STATUS_E_PROTO;
2752 
2753 		ieseqlen +=  (MIN_IE_LEN + successorfrag[TAG_LEN_POS]);
2754 
2755 		currie = successorfrag;
2756 		successorfrag = util_get_successorfrag(currie, buf, buflen);
2757 	}
2758 
2759 	*mlieseq = ieseq;
2760 	*mlieseqlen = ieseqlen;
2761 	return QDF_STATUS_SUCCESS;
2762 }
2763 
2764 QDF_STATUS
2765 util_get_mlie_common_info_len(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2766 			      uint8_t *commoninfo_len)
2767 {
2768 	struct wlan_ie_multilink *mlie_fixed;
2769 	enum wlan_ml_variant variant;
2770 	uint16_t mlcontrol;
2771 
2772 	if (!mlieseq || !mlieseqlen || !commoninfo_len)
2773 		return QDF_STATUS_E_NULL_VALUE;
2774 
2775 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2776 		return QDF_STATUS_E_INVAL;
2777 
2778 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
2779 
2780 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
2781 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
2782 		return QDF_STATUS_E_INVAL;
2783 
2784 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
2785 
2786 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
2787 			       WLAN_ML_CTRL_TYPE_BITS);
2788 
2789 	if (variant != WLAN_ML_VARIANT_BASIC)
2790 		return QDF_STATUS_E_INVAL;
2791 
2792 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
2793 	 * Check if there is sufficient space in the buffer for the Common Info
2794 	 * Length and MLD MAC address.
2795 	 */
2796 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
2797 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
2798 		return QDF_STATUS_E_PROTO;
2799 
2800 	*commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
2801 
2802 	return QDF_STATUS_SUCCESS;
2803 }
2804 
2805 QDF_STATUS
2806 util_get_bvmlie_bssparamchangecnt(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2807 				  bool *bssparamchangecntfound,
2808 				  uint8_t *bssparamchangecnt)
2809 {
2810 	struct wlan_ie_multilink *mlie_fixed;
2811 	enum wlan_ml_variant variant;
2812 	uint16_t mlcontrol;
2813 	uint16_t presencebitmap;
2814 	uint8_t *commoninfo;
2815 	qdf_size_t commoninfolen;
2816 
2817 	if (!mlieseq || !mlieseqlen || !bssparamchangecntfound ||
2818 	    !bssparamchangecnt)
2819 		return QDF_STATUS_E_NULL_VALUE;
2820 
2821 	*bssparamchangecntfound = false;
2822 	*bssparamchangecnt = 0;
2823 
2824 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2825 		return QDF_STATUS_E_INVAL;
2826 
2827 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
2828 
2829 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
2830 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
2831 		return QDF_STATUS_E_INVAL;
2832 
2833 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
2834 
2835 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
2836 			       WLAN_ML_CTRL_TYPE_BITS);
2837 
2838 	if (variant != WLAN_ML_VARIANT_BASIC)
2839 		return QDF_STATUS_E_NOSUPPORT;
2840 
2841 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
2842 				      WLAN_ML_CTRL_PBM_BITS);
2843 
2844 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
2845 	commoninfolen = WLAN_ML_BV_CINFO_LENGTH_SIZE;
2846 
2847 	commoninfolen += QDF_MAC_ADDR_SIZE;
2848 
2849 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
2850 		commoninfolen += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
2851 
2852 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
2853 				mlieseqlen)
2854 			return QDF_STATUS_E_PROTO;
2855 	}
2856 
2857 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
2858 		*bssparamchangecntfound = true;
2859 		*bssparamchangecnt = *(commoninfo + commoninfolen);
2860 	}
2861 
2862 	return QDF_STATUS_SUCCESS;
2863 }
2864 
2865 QDF_STATUS
2866 util_get_mlie_variant(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2867 		      int *variant)
2868 {
2869 	struct wlan_ie_multilink *mlie_fixed;
2870 	enum wlan_ml_variant var;
2871 	uint16_t mlcontrol;
2872 
2873 	if (!mlieseq || !mlieseqlen || !variant)
2874 		return QDF_STATUS_E_NULL_VALUE;
2875 
2876 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2877 		return QDF_STATUS_E_INVAL;
2878 
2879 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
2880 
2881 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
2882 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
2883 		return QDF_STATUS_E_INVAL;
2884 
2885 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
2886 	var = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
2887 			   WLAN_ML_CTRL_TYPE_BITS);
2888 
2889 	if (var >= WLAN_ML_VARIANT_INVALIDSTART)
2890 		return QDF_STATUS_E_PROTO;
2891 
2892 	*variant = var;
2893 	return QDF_STATUS_SUCCESS;
2894 }
2895 
2896 QDF_STATUS
2897 util_get_bvmlie_eml_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2898 			bool *eml_cap_found,
2899 			uint16_t *eml_cap)
2900 {
2901 	struct wlan_ie_multilink *mlie_fixed;
2902 	enum wlan_ml_variant variant;
2903 	uint16_t mlcontrol;
2904 	uint8_t eml_cap_offset;
2905 	uint8_t commoninfo_len;
2906 	uint16_t presencebitmap;
2907 
2908 	if (!mlieseq || !mlieseqlen || !eml_cap_found || !eml_cap)
2909 		return QDF_STATUS_E_NULL_VALUE;
2910 
2911 	*eml_cap = 0;
2912 	*eml_cap_found = false;
2913 
2914 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2915 		return QDF_STATUS_E_INVAL;
2916 
2917 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
2918 
2919 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
2920 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
2921 		return QDF_STATUS_E_INVAL;
2922 
2923 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
2924 
2925 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
2926 			       WLAN_ML_CTRL_TYPE_BITS);
2927 
2928 	if (variant != WLAN_ML_VARIANT_BASIC)
2929 		return QDF_STATUS_E_INVAL;
2930 
2931 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
2932 				      WLAN_ML_CTRL_PBM_BITS);
2933 
2934 	/* eml_cap_offset stores the offset of EML Capabilities within
2935 	 * Common Info
2936 	 */
2937 	eml_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
2938 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
2939 		eml_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
2940 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
2941 		eml_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
2942 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P)
2943 		eml_cap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
2944 
2945 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
2946 		/* Common Info starts at
2947 		 * mlieseq + sizeof(struct wlan_ie_multilink).
2948 		 * Check if there is sufficient space in the buffer for
2949 		 * the Common Info Length.
2950 		 */
2951 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
2952 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
2953 			return QDF_STATUS_E_PROTO;
2954 
2955 		/* Check if the value indicated in the Common Info Length
2956 		 * subfield is sufficient to access the EML capabilities.
2957 		 */
2958 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
2959 		if (commoninfo_len < (eml_cap_offset +
2960 				      WLAN_ML_BV_CINFO_EMLCAP_SIZE))
2961 			return QDF_STATUS_E_PROTO;
2962 
2963 		/* Common Info starts at mlieseq + sizeof(struct
2964 		 * wlan_ie_multilink). Check if there is sufficient space in
2965 		 * Common Info for the EML capability.
2966 		 */
2967 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
2968 				  eml_cap_offset +
2969 				  WLAN_ML_BV_CINFO_EMLCAP_SIZE))
2970 			return QDF_STATUS_E_PROTO;
2971 
2972 		*eml_cap_found = true;
2973 		*eml_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
2974 							 sizeof(struct wlan_ie_multilink) +
2975 							 eml_cap_offset));
2976 	}
2977 	return QDF_STATUS_SUCCESS;
2978 }
2979 
2980 QDF_STATUS
2981 util_get_bvmlie_msd_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2982 			bool *msd_cap_found,
2983 			uint16_t *msd_cap)
2984 {
2985 	struct wlan_ie_multilink *mlie_fixed;
2986 	enum wlan_ml_variant variant;
2987 	uint16_t mlcontrol;
2988 	uint8_t msd_cap_offset;
2989 	uint8_t commoninfo_len;
2990 	uint16_t presencebitmap;
2991 
2992 	if (!mlieseq || !mlieseqlen || !msd_cap_found || !msd_cap)
2993 		return QDF_STATUS_E_NULL_VALUE;
2994 
2995 	*msd_cap = 0;
2996 	*msd_cap_found = false;
2997 
2998 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2999 		return QDF_STATUS_E_INVAL;
3000 
3001 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3002 
3003 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3004 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3005 		return QDF_STATUS_E_INVAL;
3006 
3007 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3008 
3009 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3010 			       WLAN_ML_CTRL_TYPE_BITS);
3011 
3012 	if (variant != WLAN_ML_VARIANT_BASIC)
3013 		return QDF_STATUS_E_INVAL;
3014 
3015 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3016 				      WLAN_ML_CTRL_PBM_BITS);
3017 
3018 	/* msd_cap_offset stores the offset of MSD capabilities within
3019 	 * Common Info
3020 	 */
3021 	msd_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
3022 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
3023 		msd_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3024 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
3025 		msd_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3026 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3027 		/* Common Info starts at
3028 		 * mlieseq + sizeof(struct wlan_ie_multilink).
3029 		 * Check if there is sufficient space in the buffer for
3030 		 * the Common Info Length.
3031 		 */
3032 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3033 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
3034 			return QDF_STATUS_E_PROTO;
3035 
3036 		/* Check if the value indicated in the Common Info Length
3037 		 * subfield is sufficient to access the MSD capabilities.
3038 		 */
3039 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3040 		if (commoninfo_len < (msd_cap_offset +
3041 				      WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3042 			return QDF_STATUS_E_PROTO;
3043 
3044 		/* Common Info starts at mlieseq + sizeof(struct
3045 		 * wlan_ie_multilink). Check if there is sufficient space in
3046 		 * Common Info for the MSD capability.
3047 		 */
3048 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3049 				  msd_cap_offset +
3050 				  WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3051 			return QDF_STATUS_E_PROTO;
3052 
3053 		*msd_cap_found = true;
3054 		*msd_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
3055 							 sizeof(struct wlan_ie_multilink) +
3056 							 msd_cap_offset));
3057 	} else {
3058 		mlo_debug("MSD caps not found in assoc rsp");
3059 	}
3060 
3061 	return QDF_STATUS_SUCCESS;
3062 }
3063 
3064 QDF_STATUS
3065 util_get_bvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3066 			   struct qdf_mac_addr *mldmacaddr)
3067 {
3068 	struct wlan_ie_multilink *mlie_fixed;
3069 	enum wlan_ml_variant variant;
3070 	uint16_t mlcontrol;
3071 	uint8_t commoninfo_len;
3072 
3073 	if (!mlieseq || !mlieseqlen || !mldmacaddr)
3074 		return QDF_STATUS_E_NULL_VALUE;
3075 
3076 	qdf_mem_zero(mldmacaddr, sizeof(*mldmacaddr));
3077 
3078 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3079 		return QDF_STATUS_E_INVAL;
3080 
3081 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3082 
3083 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3084 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3085 		return QDF_STATUS_E_INVAL;
3086 
3087 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3088 
3089 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3090 			       WLAN_ML_CTRL_TYPE_BITS);
3091 
3092 	if (variant != WLAN_ML_VARIANT_BASIC)
3093 		return QDF_STATUS_E_INVAL;
3094 
3095 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
3096 	 * Check if there is sufficient space in the buffer for the Common Info
3097 	 * Length and MLD MAC address.
3098 	 */
3099 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
3100 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
3101 		return QDF_STATUS_E_PROTO;
3102 
3103 	/* Check if the value indicated in the Common Info Length subfield is
3104 	 * sufficient to access the MLD MAC address.
3105 	 */
3106 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3107 	if (commoninfo_len < (WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE))
3108 		return QDF_STATUS_E_PROTO;
3109 
3110 	qdf_mem_copy(mldmacaddr->bytes,
3111 		     mlieseq + sizeof(struct wlan_ie_multilink) +
3112 		     WLAN_ML_BV_CINFO_LENGTH_SIZE,
3113 		     QDF_MAC_ADDR_SIZE);
3114 
3115 	return QDF_STATUS_SUCCESS;
3116 }
3117 
3118 QDF_STATUS
3119 util_get_bvmlie_primary_linkid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3120 			       bool *linkidfound, uint8_t *linkid)
3121 {
3122 	struct wlan_ie_multilink *mlie_fixed;
3123 	enum wlan_ml_variant variant;
3124 	uint16_t mlcontrol;
3125 	uint16_t presencebitmap;
3126 	uint8_t *commoninfo;
3127 	qdf_size_t commoninfolen;
3128 	uint8_t *linkidinfo;
3129 
3130 	if (!mlieseq || !mlieseqlen || !linkidfound || !linkid)
3131 		return QDF_STATUS_E_NULL_VALUE;
3132 
3133 	*linkidfound = false;
3134 	*linkid = 0;
3135 
3136 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3137 		return QDF_STATUS_E_INVAL;
3138 
3139 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3140 
3141 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3142 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3143 		return QDF_STATUS_E_INVAL;
3144 
3145 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3146 
3147 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3148 			       WLAN_ML_CTRL_TYPE_BITS);
3149 
3150 	if (variant != WLAN_ML_VARIANT_BASIC)
3151 		return QDF_STATUS_E_INVAL;
3152 
3153 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3154 				      WLAN_ML_CTRL_PBM_BITS);
3155 
3156 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3157 	commoninfolen = 0;
3158 	commoninfolen += WLAN_ML_BV_CINFO_LENGTH_SIZE;
3159 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3160 			mlieseqlen)
3161 		return QDF_STATUS_E_PROTO;
3162 
3163 	commoninfolen += QDF_MAC_ADDR_SIZE;
3164 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3165 			mlieseqlen)
3166 		return QDF_STATUS_E_PROTO;
3167 
3168 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3169 		linkidinfo = commoninfo + commoninfolen;
3170 		commoninfolen += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3171 
3172 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3173 				mlieseqlen)
3174 			return QDF_STATUS_E_PROTO;
3175 
3176 		*linkidfound = true;
3177 		*linkid = QDF_GET_BITS(linkidinfo[0],
3178 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_IDX,
3179 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_BITS);
3180 	}
3181 
3182 	return QDF_STATUS_SUCCESS;
3183 }
3184 
3185 QDF_STATUS
3186 util_get_bvmlie_mldcap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3187 		       bool *mldcapfound, uint16_t *mldcap)
3188 {
3189 	struct wlan_ie_multilink *mlie_fixed;
3190 	enum wlan_ml_variant variant;
3191 	uint16_t mlcontrol;
3192 	uint16_t presencebitmap;
3193 	uint8_t *commoninfo;
3194 	uint8_t commoninfo_len;
3195 	qdf_size_t mldcap_offset;
3196 
3197 	if (!mlieseq || !mlieseqlen || !mldcapfound || !mldcap)
3198 		return QDF_STATUS_E_NULL_VALUE;
3199 
3200 	*mldcapfound = false;
3201 	*mldcap = 0;
3202 
3203 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3204 		return QDF_STATUS_E_INVAL;
3205 
3206 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3207 
3208 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3209 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3210 		return QDF_STATUS_E_INVAL;
3211 
3212 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3213 
3214 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3215 			       WLAN_ML_CTRL_TYPE_BITS);
3216 
3217 	if (variant != WLAN_ML_VARIANT_BASIC)
3218 		return QDF_STATUS_E_NOSUPPORT;
3219 
3220 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3221 				      WLAN_ML_CTRL_PBM_BITS);
3222 
3223 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3224 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3225 	/* mldcap_offset stores the offset of MLD Capabilities within
3226 	 * Common Info
3227 	 */
3228 	mldcap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE;
3229 	mldcap_offset += QDF_MAC_ADDR_SIZE;
3230 
3231 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3232 		mldcap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3233 
3234 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3235 				mlieseqlen)
3236 			return QDF_STATUS_E_PROTO;
3237 	}
3238 
3239 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
3240 		mldcap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3241 
3242 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3243 				mlieseqlen)
3244 			return QDF_STATUS_E_PROTO;
3245 	}
3246 
3247 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3248 		mldcap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
3249 
3250 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3251 				mlieseqlen)
3252 			return QDF_STATUS_E_PROTO;
3253 	}
3254 
3255 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
3256 		mldcap_offset += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
3257 
3258 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3259 				mlieseqlen)
3260 			return QDF_STATUS_E_PROTO;
3261 	}
3262 
3263 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MLDCAPANDOP_P) {
3264 		/* Check if the value indicated in the Common Info Length
3265 		 * subfield is sufficient to access the MLD capabilities.
3266 		 */
3267 		if (commoninfo_len < (mldcap_offset +
3268 				      WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE))
3269 			return QDF_STATUS_E_PROTO;
3270 
3271 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset +
3272 					WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE) >
3273 				mlieseqlen)
3274 			return QDF_STATUS_E_PROTO;
3275 
3276 		*mldcap = qdf_le16_to_cpu(*((uint16_t *)(commoninfo + mldcap_offset)));
3277 		*mldcapfound = true;
3278 	}
3279 
3280 	return QDF_STATUS_SUCCESS;
3281 }
3282 
3283 QDF_STATUS
3284 util_get_bvmlie_persta_partner_info(uint8_t *mlieseq,
3285 				    qdf_size_t mlieseqlen,
3286 				    struct mlo_partner_info *partner_info)
3287 {
3288 	struct wlan_ie_multilink *mlie_fixed;
3289 	uint16_t mlcontrol;
3290 	enum wlan_ml_variant variant;
3291 	uint8_t *linkinfo;
3292 	qdf_size_t linkinfo_len;
3293 	struct mlo_partner_info pinfo = {0};
3294 	qdf_size_t mlieseqpayloadlen;
3295 	uint8_t *mlieseqpayload_copy;
3296 	bool is_elemfragseq;
3297 	qdf_size_t defragpayload_len;
3298 
3299 	qdf_size_t tmplen;
3300 	QDF_STATUS ret;
3301 
3302 	if (!mlieseq) {
3303 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3304 		return QDF_STATUS_E_NULL_VALUE;
3305 	}
3306 
3307 	if (!mlieseqlen) {
3308 		mlo_err("Length of Multi-Link element sequence is zero");
3309 		return QDF_STATUS_E_INVAL;
3310 	}
3311 
3312 	if (!partner_info) {
3313 		mlo_err("partner_info is NULL");
3314 		return QDF_STATUS_E_NULL_VALUE;
3315 	}
3316 
3317 	partner_info->num_partner_links = 0;
3318 
3319 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3320 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3321 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3322 		return QDF_STATUS_E_INVAL;
3323 	}
3324 
3325 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3326 
3327 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3328 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3329 		mlo_err("The element is not a Multi-Link element");
3330 		return QDF_STATUS_E_INVAL;
3331 	}
3332 
3333 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3334 
3335 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3336 			       WLAN_ML_CTRL_TYPE_BITS);
3337 
3338 	if (variant != WLAN_ML_VARIANT_BASIC) {
3339 		mlo_err("The variant value %u does not correspond to Basic Variant value %u",
3340 			variant, WLAN_ML_VARIANT_BASIC);
3341 		return QDF_STATUS_E_INVAL;
3342 	}
3343 
3344 	mlieseqpayloadlen = 0;
3345 	tmplen = 0;
3346 	is_elemfragseq = false;
3347 
3348 	ret = wlan_get_elem_fragseq_info(mlieseq,
3349 					 mlieseqlen,
3350 					 &is_elemfragseq,
3351 					 &tmplen,
3352 					 &mlieseqpayloadlen);
3353 	if (QDF_IS_STATUS_ERROR(ret))
3354 		return ret;
3355 
3356 	if (is_elemfragseq) {
3357 		if (tmplen != mlieseqlen) {
3358 			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",
3359 				   tmplen, mlieseqlen);
3360 			return QDF_STATUS_E_INVAL;
3361 		}
3362 
3363 		if (!mlieseqpayloadlen) {
3364 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3365 			return QDF_STATUS_E_FAILURE;
3366 		}
3367 
3368 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3369 			  mlieseqpayloadlen);
3370 	} else {
3371 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3372 			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",
3373 				   mlieseqlen,
3374 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3375 			return QDF_STATUS_E_FAILURE;
3376 		}
3377 
3378 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3379 	}
3380 
3381 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3382 
3383 	if (!mlieseqpayload_copy) {
3384 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3385 		return QDF_STATUS_E_NOMEM;
3386 	}
3387 
3388 	if (is_elemfragseq) {
3389 		ret = wlan_defrag_elem_fragseq(false,
3390 					       mlieseq,
3391 					       mlieseqlen,
3392 					       mlieseqpayload_copy,
3393 					       mlieseqpayloadlen,
3394 					       &defragpayload_len);
3395 		if (QDF_IS_STATUS_ERROR(ret)) {
3396 			qdf_mem_free(mlieseqpayload_copy);
3397 			return ret;
3398 		}
3399 
3400 		if (defragpayload_len != mlieseqpayloadlen) {
3401 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3402 				   defragpayload_len, mlieseqpayloadlen);
3403 			qdf_mem_free(mlieseqpayload_copy);
3404 			return QDF_STATUS_E_FAILURE;
3405 		}
3406 	} else {
3407 		qdf_mem_copy(mlieseqpayload_copy,
3408 			     mlieseq + sizeof(struct ie_header) + 1,
3409 			     mlieseqpayloadlen);
3410 	}
3411 
3412 	linkinfo = NULL;
3413 	linkinfo_len = 0;
3414 
3415 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
3416 					 mlieseqpayloadlen,
3417 					 &linkinfo,
3418 					 &linkinfo_len);
3419 	if (QDF_IS_STATUS_ERROR(ret)) {
3420 		qdf_mem_free(mlieseqpayload_copy);
3421 		return ret;
3422 	}
3423 
3424 	/* In case Link Info is absent, the number of partner links will remain
3425 	 * zero.
3426 	 */
3427 	if (!linkinfo) {
3428 		qdf_mem_free(mlieseqpayload_copy);
3429 		return QDF_STATUS_SUCCESS;
3430 	}
3431 
3432 	ret = util_parse_partner_info_from_linkinfo(linkinfo,
3433 						    linkinfo_len,
3434 						    &pinfo);
3435 
3436 	if (QDF_IS_STATUS_ERROR(ret)) {
3437 		qdf_mem_free(mlieseqpayload_copy);
3438 		return ret;
3439 	}
3440 
3441 	qdf_mem_copy(partner_info, &pinfo, sizeof(*partner_info));
3442 
3443 	qdf_mem_free(mlieseqpayload_copy);
3444 
3445 	return QDF_STATUS_SUCCESS;
3446 }
3447 
3448 QDF_STATUS
3449 util_get_prvmlie_persta_link_id(uint8_t *mlieseq,
3450 				qdf_size_t mlieseqlen,
3451 				struct mlo_probereq_info *probereq_info)
3452 {
3453 	struct wlan_ie_multilink *mlie_fixed;
3454 	uint16_t mlcontrol;
3455 	enum wlan_ml_variant variant;
3456 	uint8_t *linkinfo;
3457 	qdf_size_t linkinfo_len;
3458 	struct mlo_probereq_info pinfo = {0};
3459 	qdf_size_t mlieseqpayloadlen;
3460 	uint8_t *mlieseqpayload_copy;
3461 	bool is_elemfragseq;
3462 	qdf_size_t defragpayload_len;
3463 
3464 	qdf_size_t tmplen;
3465 	QDF_STATUS ret;
3466 
3467 	if (!mlieseq) {
3468 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3469 		return QDF_STATUS_E_NULL_VALUE;
3470 	}
3471 
3472 	if (!mlieseqlen) {
3473 		mlo_err("Length of Multi-Link element sequence is zero");
3474 		return QDF_STATUS_E_INVAL;
3475 	}
3476 
3477 	if (!probereq_info) {
3478 		mlo_err("probe request_info is NULL");
3479 		return QDF_STATUS_E_NULL_VALUE;
3480 	}
3481 
3482 	probereq_info->num_links = 0;
3483 
3484 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3485 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3486 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3487 		return QDF_STATUS_E_INVAL;
3488 	}
3489 
3490 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3491 
3492 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3493 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3494 		mlo_err("The element is not a Multi-Link element");
3495 		return QDF_STATUS_E_INVAL;
3496 	}
3497 
3498 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3499 
3500 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3501 			       WLAN_ML_CTRL_TYPE_BITS);
3502 
3503 	if (variant != WLAN_ML_VARIANT_PROBEREQ) {
3504 		mlo_err("The variant value %u does not correspond to Probe Request Variant value %u",
3505 			variant, WLAN_ML_VARIANT_PROBEREQ);
3506 		return QDF_STATUS_E_INVAL;
3507 	}
3508 
3509 	mlieseqpayloadlen = 0;
3510 	tmplen = 0;
3511 	is_elemfragseq = false;
3512 
3513 	ret = wlan_get_elem_fragseq_info(mlieseq,
3514 					 mlieseqlen,
3515 					 &is_elemfragseq,
3516 					 &tmplen,
3517 					 &mlieseqpayloadlen);
3518 	if (QDF_IS_STATUS_ERROR(ret))
3519 		return ret;
3520 
3521 	if (is_elemfragseq) {
3522 		if (tmplen != mlieseqlen) {
3523 			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",
3524 				   tmplen, mlieseqlen);
3525 			return QDF_STATUS_E_INVAL;
3526 		}
3527 
3528 		if (!mlieseqpayloadlen) {
3529 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3530 			return QDF_STATUS_E_FAILURE;
3531 		}
3532 
3533 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3534 			  mlieseqpayloadlen);
3535 	} else {
3536 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3537 			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",
3538 				   mlieseqlen,
3539 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3540 			return QDF_STATUS_E_FAILURE;
3541 		}
3542 
3543 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3544 	}
3545 
3546 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3547 
3548 	if (!mlieseqpayload_copy) {
3549 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3550 		return QDF_STATUS_E_NOMEM;
3551 	}
3552 
3553 	if (is_elemfragseq) {
3554 		ret = wlan_defrag_elem_fragseq(false,
3555 					       mlieseq,
3556 					       mlieseqlen,
3557 					       mlieseqpayload_copy,
3558 					       mlieseqpayloadlen,
3559 					       &defragpayload_len);
3560 		if (QDF_IS_STATUS_ERROR(ret)) {
3561 			qdf_mem_free(mlieseqpayload_copy);
3562 			return ret;
3563 		}
3564 
3565 		if (defragpayload_len != mlieseqpayloadlen) {
3566 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3567 				   defragpayload_len, mlieseqpayloadlen);
3568 			qdf_mem_free(mlieseqpayload_copy);
3569 			return QDF_STATUS_E_FAILURE;
3570 		}
3571 	} else {
3572 		qdf_mem_copy(mlieseqpayload_copy,
3573 			     mlieseq + sizeof(struct ie_header) + 1,
3574 			     mlieseqpayloadlen);
3575 	}
3576 
3577 	linkinfo = NULL;
3578 	linkinfo_len = 0;
3579 	ret = util_parse_prv_multi_link_ctrl(mlieseqpayload_copy,
3580 					     mlieseqpayloadlen,
3581 					     &linkinfo,
3582 					     &linkinfo_len);
3583 	if (QDF_IS_STATUS_ERROR(ret)) {
3584 		qdf_mem_free(mlieseqpayload_copy);
3585 		return ret;
3586 	}
3587 
3588 	/* In case Link Info is absent, the number of links will remain
3589 	 * zero.
3590 	 */
3591 	if (!linkinfo) {
3592 		qdf_mem_free(mlieseqpayload_copy);
3593 		return QDF_STATUS_SUCCESS;
3594 	}
3595 
3596 	ret = util_parse_probereq_info_from_linkinfo(linkinfo,
3597 						     linkinfo_len,
3598 						     &pinfo);
3599 
3600 	if (QDF_IS_STATUS_ERROR(ret)) {
3601 		qdf_mem_free(mlieseqpayload_copy);
3602 		return ret;
3603 	}
3604 
3605 	qdf_mem_copy(probereq_info, &pinfo, sizeof(*probereq_info));
3606 
3607 	qdf_mem_free(mlieseqpayload_copy);
3608 
3609 	return QDF_STATUS_SUCCESS;
3610 }
3611 
3612 QDF_STATUS
3613 util_get_prvmlie_mldid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3614 		       bool *mldidfound, uint8_t *mldid)
3615 {
3616 	struct wlan_ie_multilink *mlie_fixed;
3617 	enum wlan_ml_variant variant;
3618 	uint16_t mlcontrol;
3619 	uint16_t presencebitmap;
3620 	uint8_t *commoninfo;
3621 	qdf_size_t commoninfolen;
3622 
3623 	if (!mlieseq || !mlieseqlen || !mldidfound || !mldid)
3624 		return QDF_STATUS_E_NULL_VALUE;
3625 
3626 	*mldidfound = false;
3627 	*mldid = 0;
3628 
3629 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3630 		return QDF_STATUS_E_INVAL;
3631 
3632 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3633 
3634 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3635 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3636 		return QDF_STATUS_E_INVAL;
3637 
3638 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3639 
3640 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3641 			       WLAN_ML_CTRL_TYPE_BITS);
3642 
3643 	if (variant != WLAN_ML_VARIANT_PROBEREQ)
3644 		return QDF_STATUS_E_NOSUPPORT;
3645 
3646 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3647 				      WLAN_ML_CTRL_PBM_BITS);
3648 
3649 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3650 	commoninfolen = WLAN_ML_PRV_CINFO_LENGTH_SIZE;
3651 
3652 	if (presencebitmap & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
3653 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen +
3654 		     WLAN_ML_PRV_CINFO_MLDID_SIZE) >
3655 		    mlieseqlen)
3656 			return QDF_STATUS_E_PROTO;
3657 
3658 		*mldid = *((uint8_t *)(commoninfo + commoninfolen));
3659 		commoninfolen += WLAN_ML_PRV_CINFO_MLDID_SIZE;
3660 
3661 		*mldidfound = true;
3662 	}
3663 
3664 	return QDF_STATUS_SUCCESS;
3665 }
3666 
3667 #endif
3668