xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlo_mgr/src/utils_mlo.c (revision 8b3dca18206e1a0461492f082fa6e270b092c035)
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 functionality 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 functionality 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 #ifdef CONN_MGR_ADV_FEATURE
1478 /**
1479  * util_add_mlie_for_prb_rsp_gen - Add the basic variant Multi-Link element
1480  * when generating link specific probe response.
1481  * @reportingsta_ie: Pointer to the reportingsta ie
1482  * @reportingsta_ie_len: Length for reporting sta ie
1483  * @plink_frame_currpos: Pointer to Link frame current pos
1484  * @plink_frame_currlen: Current length of link frame.
1485  * @linkid: Link Id value
1486  *
1487  * Add the basic variant Multi-Link element when
1488  * generating link specific probe response.
1489  *
1490  * Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
1491  * the reason for error in the case of failure
1492  */
1493 static QDF_STATUS
1494 util_add_mlie_for_prb_rsp_gen(const uint8_t *reportingsta_ie,
1495 			      qdf_size_t reportingsta_ie_len,
1496 			      uint8_t **plink_frame_currpos,
1497 			      qdf_size_t *plink_frame_currlen,
1498 			      uint8_t linkid)
1499 {
1500 	uint8_t mlie_len = 0;
1501 	uint8_t common_info_len = 0;
1502 	struct wlan_ie_multilink ml_ie_ff;
1503 	uint16_t mlcontrol;
1504 	uint16_t presencebm;
1505 	uint8_t *mlie_frame = NULL;
1506 	uint8_t link_id_offset = sizeof(struct wlan_ie_multilink) +
1507 				QDF_MAC_ADDR_SIZE +
1508 				WLAN_ML_BV_CINFO_LENGTH_SIZE;
1509 	uint8_t *link_frame_currpos = *plink_frame_currpos;
1510 	qdf_size_t link_frame_currlen = *plink_frame_currlen;
1511 	QDF_STATUS status = QDF_STATUS_SUCCESS;
1512 
1513 	status = util_get_mlie_common_info_len((uint8_t *)reportingsta_ie,
1514 					       reportingsta_ie_len,
1515 					       &common_info_len);
1516 	if (QDF_IS_STATUS_ERROR(status)) {
1517 		mlo_err("Failed while parsing the common info length");
1518 		return status;
1519 	}
1520 
1521 	/* common info len + bvmlie fixed fields */
1522 	mlie_len = common_info_len + sizeof(struct wlan_ie_multilink);
1523 
1524 	mlo_debug_rl("mlie_len %d, common_info_len %d, link_id_offset %d",
1525 		     mlie_len,
1526 		     common_info_len,
1527 		     link_id_offset);
1528 
1529 	mlie_frame = qdf_mem_malloc(mlie_len);
1530 	if (!mlie_frame)
1531 		return QDF_STATUS_E_NOMEM;
1532 
1533 	/* Copy ml ie fixed fields */
1534 	qdf_mem_copy(&ml_ie_ff,
1535 		     reportingsta_ie,
1536 		     sizeof(struct wlan_ie_multilink));
1537 
1538 	ml_ie_ff.elem_len = mlie_len - sizeof(struct ie_header);
1539 
1540 	mlcontrol = qdf_le16_to_cpu(ml_ie_ff.mlcontrol);
1541 	presencebm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
1542 				  WLAN_ML_CTRL_PBM_BITS);
1543 	qdf_set_bit(WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P,
1544 		    (unsigned long *)&presencebm);
1545 
1546 	QDF_SET_BITS(ml_ie_ff.mlcontrol,
1547 		     WLAN_ML_CTRL_PBM_IDX,
1548 		     WLAN_ML_CTRL_PBM_BITS,
1549 		     presencebm);
1550 
1551 	qdf_mem_copy(mlie_frame,
1552 		     &ml_ie_ff,
1553 		     sizeof(struct wlan_ie_multilink));
1554 
1555 	qdf_mem_copy(mlie_frame + sizeof(struct wlan_ie_multilink),
1556 		     reportingsta_ie + sizeof(struct wlan_ie_multilink),
1557 		     mlie_len - sizeof(struct wlan_ie_multilink));
1558 
1559 	if (linkid == 0xFF) {
1560 		qdf_mem_free(mlie_frame);
1561 		mlo_err("Link id is invalid");
1562 		return QDF_STATUS_E_INVAL;
1563 	}
1564 	mlie_frame[link_id_offset] = (mlie_frame[link_id_offset] & ~0x0f) |
1565 				   (linkid & 0x0f);
1566 	qdf_mem_copy(link_frame_currpos,
1567 		     mlie_frame,
1568 		     mlie_len);
1569 
1570 	mlo_debug("Add mlie for link id %d", linkid);
1571 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
1572 			   mlie_frame, mlie_len);
1573 
1574 	link_frame_currpos += mlie_len;
1575 	link_frame_currlen += mlie_len;
1576 	*plink_frame_currpos = link_frame_currpos;
1577 	*plink_frame_currlen = link_frame_currlen;
1578 	qdf_mem_free(mlie_frame);
1579 
1580 	return QDF_STATUS_SUCCESS;
1581 }
1582 #else
1583 static QDF_STATUS
1584 util_add_mlie_for_prb_rsp_gen(const uint8_t *reportingsta_ie,
1585 			      qdf_size_t reportingsta_ie_len,
1586 			      uint8_t **plink_frame_currpos,
1587 			      qdf_size_t *plink_frame_currlen,
1588 			      uint8_t linkid)
1589 {
1590 	return QDF_STATUS_SUCCESS;
1591 }
1592 #endif
1593 
1594 #define MLO_LINKSPECIFIC_ASSOC_REQ_FC0  0x00
1595 #define MLO_LINKSPECIFIC_ASSOC_REQ_FC1  0x00
1596 #define MLO_LINKSPECIFIC_ASSOC_RESP_FC0 0x10
1597 #define MLO_LINKSPECIFIC_ASSOC_RESP_FC1 0x00
1598 #define MLO_LINKSPECIFIC_PROBE_RESP_FC0 0x50
1599 #define MLO_LINKSPECIFIC_PROBE_RESP_FC1 0x00
1600 
1601 static
1602 QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len,
1603 				    uint8_t subtype,
1604 				    struct qdf_mac_addr link_addr,
1605 				    uint8_t *link_frame,
1606 				    qdf_size_t link_frame_maxsize,
1607 				    qdf_size_t *link_frame_len)
1608 {
1609 	/* Please see documentation for util_gen_link_assoc_req() and
1610 	 * util_gen_link_assoc_resp() for information on the inputs to and
1611 	 * output from this helper, since those APIs are essentially wrappers
1612 	 * over this helper.
1613 	 */
1614 
1615 	/* Pointer to Multi-Link element/Multi-Link element fragment sequence */
1616 	uint8_t *mlieseq;
1617 	/* Total length of Multi-Link element sequence (including fragments if
1618 	 * any)
1619 	 */
1620 	qdf_size_t mlieseqlen;
1621 	/* Variant (i.e. type) of the Multi-Link element */
1622 	enum wlan_ml_variant variant;
1623 
1624 	/* Length of the payload of the Multi-Link element (inclusive of
1625 	 * fragment payloads if any) without IE headers and element ID extension
1626 	 */
1627 	qdf_size_t mlieseqpayloadlen;
1628 	/* Pointer to copy of the payload of the Multi-Link element (inclusive
1629 	 * of fragment payloads if any) without IE headers and element ID
1630 	 * extension
1631 	 */
1632 	uint8_t *mlieseqpayload_copy;
1633 
1634 	/* Pointer to start of Link Info within the copy of the payload of the
1635 	 * Multi-Link element
1636 	 */
1637 	uint8_t *link_info;
1638 	/* Length of the Link Info */
1639 	qdf_size_t link_info_len;
1640 
1641 	/* Pointer to the IE section that occurs after the fixed fields in the
1642 	 * original frame for the reporting STA.
1643 	 */
1644 	uint8_t *frame_iesection;
1645 	/* Offset to the start of the IE section in the original frame for the
1646 	 * reporting STA.
1647 	 */
1648 	qdf_size_t frame_iesection_offset;
1649 	/* Total length of the IE section in the original frame for the
1650 	 * reporting STA.
1651 	 */
1652 	qdf_size_t frame_iesection_len;
1653 
1654 	/* Pointer to the IEEE802.11 frame header in the link specific frame
1655 	 * being generated for the reported STA.
1656 	 */
1657 	struct wlan_frame_hdr *link_frame_hdr;
1658 	/* Current position in the link specific frame being generated for the
1659 	 * reported STA.
1660 	 */
1661 	uint8_t *link_frame_currpos;
1662 	/* Current length of the link specific frame being generated for the
1663 	 * reported STA.
1664 	 */
1665 	qdf_size_t link_frame_currlen;
1666 
1667 	/* Pointer to IE for reporting STA */
1668 	const uint8_t *reportingsta_ie;
1669 	/* Total size of IE for reporting STA, inclusive of the element header
1670 	 */
1671 	qdf_size_t reportingsta_ie_size;
1672 
1673 	/* Pointer to current position in STA profile */
1674 	uint8_t *sta_prof_currpos;
1675 	/* Remaining length of STA profile */
1676 	qdf_size_t sta_prof_remlen;
1677 	/* Pointer to start of IE section in STA profile that occurs after fixed
1678 	 * fields.
1679 	 */
1680 	uint8_t *sta_prof_iesection;
1681 	/* Total length of IE section in STA profile */
1682 	qdf_size_t sta_prof_iesection_len;
1683 	/* Pointer to current position being processed in IE section in STA
1684 	 * profile.
1685 	 */
1686 	uint8_t *sta_prof_iesection_currpos;
1687 	/* Remaining length of IE section in STA profile */
1688 	qdf_size_t sta_prof_iesection_remlen;
1689 
1690 	/* Pointer to IE in STA profile, that occurs within IE section */
1691 	uint8_t *sta_prof_ie;
1692 	/* Total size of IE in STA profile, inclusive of the element header */
1693 	qdf_size_t sta_prof_ie_size;
1694 
1695 	/* Pointer to element ID list in Non-Inheritance IE */
1696 	uint8_t *ninherit_elemlist;
1697 	/* Length of element ID list in Non-Inheritance IE */
1698 	qdf_size_t ninherit_elemlist_len;
1699 	/* Pointer to element ID extension list in Non-Inheritance IE */
1700 	uint8_t *ninherit_elemextlist;
1701 	/* Length of element ID extension list in Non-Inheritance IE */
1702 	qdf_size_t ninherit_elemextlist_len;
1703 	/* Whether a given IE is in a non-inheritance list */
1704 	bool is_in_noninheritlist;
1705 
1706 	/* Whether MAC address of reported STA is valid */
1707 	bool is_reportedmacaddr_valid;
1708 	/* MAC address of reported STA */
1709 	struct qdf_mac_addr reportedmacaddr;
1710 
1711 	/* Pointer to per-STA profile */
1712 	uint8_t *persta_prof;
1713 	/* Length of the containing buffer which starts with the per-STA profile
1714 	 */
1715 	qdf_size_t persta_prof_bufflen;
1716 
1717 	/* Other variables for temporary purposes */
1718 
1719 	/* Variable into which API for determining fragment information will
1720 	 * indicate whether the element is the start of a fragment sequence or
1721 	 * not.
1722 	 */
1723 	bool is_elemfragseq;
1724 	/*  De-fragmented payload length returned by API for element
1725 	 *  defragmentation.
1726 	 */
1727 	qdf_size_t defragpayload_len;
1728 	/* Variable into which API for determining fragment information will
1729 	 * indicate whether the subelement is the start of a fragment sequence
1730 	 * or not.
1731 	 */
1732 	bool is_subelemfragseq;
1733 	/* Total length of the subelement fragment sequence, inclusive of
1734 	 * subelement header and the headers of fragments if any.
1735 	 */
1736 	qdf_size_t subelemseqtotallen;
1737 	/* Total length of the subelement fragment sequence payload, excluding
1738 	 * subelement header and fragment headers if any.
1739 	 */
1740 	qdf_size_t subelemseqpayloadlen;
1741 	/* Pointer to Beacon interval in STA info field */
1742 	uint16_t beaconinterval;
1743 	/* Whether Beacon interval value valid */
1744 	bool is_beaconinterval_valid;
1745 	/* TSF timer of the reporting AP */
1746 	uint64_t tsf;
1747 	/* TSF offset of the reproted AP */
1748 	uint64_t tsfoffset;
1749 	/* TSF offset value valid */
1750 	bool is_tsfoffset_valid;
1751 	/* If Complete Profile or not*/
1752 	bool is_completeprofile;
1753 	qdf_size_t tmplen;
1754 	QDF_STATUS ret;
1755 	uint8_t linkid = 0xFF;
1756 
1757 	if (!frame) {
1758 		mlo_err("Pointer to original frame is NULL");
1759 		return QDF_STATUS_E_NULL_VALUE;
1760 	}
1761 
1762 	if (!frame_len) {
1763 		mlo_err("Length of original frame is zero");
1764 		return QDF_STATUS_E_INVAL;
1765 	}
1766 
1767 	if ((subtype != WLAN_FC0_STYPE_ASSOC_REQ) &&
1768 	    (subtype != WLAN_FC0_STYPE_REASSOC_REQ) &&
1769 	    (subtype != WLAN_FC0_STYPE_ASSOC_RESP) &&
1770 	    (subtype != WLAN_FC0_STYPE_REASSOC_RESP) &&
1771 	    (subtype != WLAN_FC0_STYPE_PROBE_RESP)) {
1772 		mlo_err("802.11 frame subtype %u is invalid", subtype);
1773 		return QDF_STATUS_E_INVAL;
1774 	}
1775 
1776 	if (!link_frame) {
1777 		mlo_err("Pointer to secondary link specific frame is NULL");
1778 		return QDF_STATUS_E_NULL_VALUE;
1779 	}
1780 
1781 	if (!link_frame_maxsize) {
1782 		mlo_err("Maximum size of secondary link specific frame is zero");
1783 		return QDF_STATUS_E_INVAL;
1784 	}
1785 
1786 	if (!link_frame_len) {
1787 		mlo_err("Pointer to populated length of secondary link specific frame is NULL");
1788 		return QDF_STATUS_E_NULL_VALUE;
1789 	}
1790 
1791 	frame_iesection_offset = 0;
1792 
1793 	if (subtype == WLAN_FC0_STYPE_ASSOC_REQ) {
1794 		frame_iesection_offset = WLAN_ASSOC_REQ_IES_OFFSET;
1795 	} else if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
1796 		frame_iesection_offset = WLAN_REASSOC_REQ_IES_OFFSET;
1797 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
1798 		frame_iesection_offset = WLAN_PROBE_RESP_IES_OFFSET;
1799 		qdf_mem_copy(&tsf, frame, WLAN_TIMESTAMP_LEN);
1800 		tsf = qdf_le64_to_cpu(tsf);
1801 	} else {
1802 		/* This is a (re)association response */
1803 		frame_iesection_offset = WLAN_ASSOC_RSP_IES_OFFSET;
1804 	}
1805 
1806 	if (frame_len < frame_iesection_offset) {
1807 		/* The caller is supposed to have confirmed that this is a valid
1808 		 * frame containing a Multi-Link element. Hence we treat this as
1809 		 * a case of invalid argument being passed to us.
1810 		 */
1811 		mlo_err("Frame length %zu is smaller than the IE section offset %zu for subtype %u",
1812 			frame_len, frame_iesection_offset, subtype);
1813 		return QDF_STATUS_E_INVAL;
1814 	}
1815 
1816 	frame_iesection_len = frame_len - frame_iesection_offset;
1817 
1818 	if (frame_iesection_len == 0) {
1819 		/* The caller is supposed to have confirmed that this is a valid
1820 		 * frame containing a Multi-Link element. Hence we treat this as
1821 		 * a case of invalid argument being passed to us.
1822 		 */
1823 		mlo_err("No space left in frame for IE section");
1824 		return QDF_STATUS_E_INVAL;
1825 	}
1826 
1827 	frame_iesection = frame + frame_iesection_offset;
1828 
1829 	mlieseq = NULL;
1830 	mlieseqlen = 0;
1831 
1832 	ret = util_find_mlie(frame_iesection, frame_iesection_len, &mlieseq,
1833 			     &mlieseqlen);
1834 	if (QDF_IS_STATUS_ERROR(ret))
1835 		return ret;
1836 
1837 	if (!mlieseq) {
1838 		/* The caller is supposed to have confirmed that a Multi-Link
1839 		 * element is present in the frame. Hence we treat this as a
1840 		 * case of invalid argument being passed to us.
1841 		 */
1842 		mlo_err("Invalid original frame since no Multi-Link element found");
1843 		return QDF_STATUS_E_INVAL;
1844 	}
1845 
1846 	/* Sanity check the Multi-Link element sequence length */
1847 	if (!mlieseqlen) {
1848 		mlo_err("Length of Multi-Link element sequence is zero. Investigate.");
1849 		return QDF_STATUS_E_FAILURE;
1850 	}
1851 
1852 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
1853 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
1854 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
1855 		return QDF_STATUS_E_PROTO;
1856 	}
1857 
1858 	ret = util_get_mlie_variant(mlieseq, mlieseqlen, (int *)&variant);
1859 	if (QDF_IS_STATUS_ERROR(ret))
1860 		return ret;
1861 
1862 	if (variant != WLAN_ML_VARIANT_BASIC) {
1863 		mlo_err_rl("Unexpected variant %u of Multi-Link element.",
1864 			   variant);
1865 		return QDF_STATUS_E_PROTO;
1866 	}
1867 
1868 	mlieseqpayloadlen = 0;
1869 	tmplen = 0;
1870 	is_elemfragseq = false;
1871 
1872 	ret = wlan_get_elem_fragseq_info(mlieseq,
1873 					 mlieseqlen,
1874 					 &is_elemfragseq,
1875 					 &tmplen,
1876 					 &mlieseqpayloadlen);
1877 	if (QDF_IS_STATUS_ERROR(ret))
1878 		return ret;
1879 
1880 	if (is_elemfragseq) {
1881 		if (tmplen != mlieseqlen) {
1882 			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",
1883 				   tmplen, mlieseqlen);
1884 			return QDF_STATUS_E_FAILURE;
1885 		}
1886 
1887 		if (!mlieseqpayloadlen) {
1888 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
1889 			return QDF_STATUS_E_FAILURE;
1890 		}
1891 
1892 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
1893 			  mlieseqpayloadlen);
1894 	} else {
1895 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
1896 			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",
1897 				   mlieseqlen,
1898 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
1899 			return QDF_STATUS_E_FAILURE;
1900 		}
1901 
1902 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
1903 	}
1904 
1905 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
1906 
1907 	if (!mlieseqpayload_copy) {
1908 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
1909 		return QDF_STATUS_E_NOMEM;
1910 	}
1911 
1912 	if (is_elemfragseq) {
1913 		ret = wlan_defrag_elem_fragseq(false,
1914 					       mlieseq,
1915 					       mlieseqlen,
1916 					       mlieseqpayload_copy,
1917 					       mlieseqpayloadlen,
1918 					       &defragpayload_len);
1919 		if (QDF_IS_STATUS_ERROR(ret)) {
1920 			qdf_mem_free(mlieseqpayload_copy);
1921 			return ret;
1922 		}
1923 
1924 		if (defragpayload_len != mlieseqpayloadlen) {
1925 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
1926 				   defragpayload_len, mlieseqpayloadlen);
1927 			qdf_mem_free(mlieseqpayload_copy);
1928 			return QDF_STATUS_E_FAILURE;
1929 		}
1930 	} else {
1931 		qdf_mem_copy(mlieseqpayload_copy,
1932 			     mlieseq + sizeof(struct ie_header) + 1,
1933 			     mlieseqpayloadlen);
1934 	}
1935 
1936 	link_info = NULL;
1937 	link_info_len = 0;
1938 
1939 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
1940 					 mlieseqpayloadlen,
1941 					 &link_info,
1942 					 &link_info_len);
1943 	if (QDF_IS_STATUS_ERROR(ret)) {
1944 		qdf_mem_free(mlieseqpayload_copy);
1945 		return ret;
1946 	}
1947 
1948 	/* As per the standard, the sender must include Link Info for
1949 	 * association request/response. Throw an error if we are unable to
1950 	 * obtain this.
1951 	 */
1952 	if (!link_info) {
1953 		mlo_err_rl("Unable to successfully obtain Link Info");
1954 		qdf_mem_free(mlieseqpayload_copy);
1955 		return QDF_STATUS_E_PROTO;
1956 	}
1957 
1958 	mlo_debug("Dumping hex of link info after parsing Multi-Link element control");
1959 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_DEBUG,
1960 			   link_info, link_info_len);
1961 
1962 	/* Note: We may have a future change to skip subelements which are not
1963 	 * Per-STA Profile, handle more than two links in MLO, handle cases
1964 	 * where we unexpectedly find more Per-STA Profiles than expected, etc.
1965 	 */
1966 
1967 	persta_prof = link_info;
1968 	persta_prof_bufflen = link_info_len;
1969 
1970 	is_subelemfragseq = false;
1971 	subelemseqtotallen = 0;
1972 	subelemseqpayloadlen = 0;
1973 
1974 	ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1975 					    persta_prof,
1976 					    persta_prof_bufflen,
1977 					    &is_subelemfragseq,
1978 					    &subelemseqtotallen,
1979 					    &subelemseqpayloadlen);
1980 	if (QDF_IS_STATUS_ERROR(ret)) {
1981 		qdf_mem_free(mlieseqpayload_copy);
1982 		return ret;
1983 	}
1984 
1985 	if (is_subelemfragseq) {
1986 		if (!subelemseqpayloadlen) {
1987 			mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
1988 			qdf_mem_free(mlieseqpayload_copy);
1989 			return QDF_STATUS_E_FAILURE;
1990 		}
1991 
1992 		mlo_debug("Subelement fragment sequence found with payload len %zu",
1993 			  subelemseqpayloadlen);
1994 
1995 		ret = wlan_defrag_subelem_fragseq(true,
1996 						  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1997 						  persta_prof,
1998 						  persta_prof_bufflen,
1999 						  NULL,
2000 						  0,
2001 						  &defragpayload_len);
2002 		if (QDF_IS_STATUS_ERROR(ret)) {
2003 			qdf_mem_free(mlieseqpayload_copy);
2004 			return ret;
2005 		}
2006 
2007 		if (defragpayload_len != subelemseqpayloadlen) {
2008 			mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
2009 				   defragpayload_len,
2010 				   subelemseqpayloadlen);
2011 			qdf_mem_free(mlieseqpayload_copy);
2012 			return QDF_STATUS_E_FAILURE;
2013 		}
2014 	} else {
2015 		if (persta_prof_bufflen <
2016 			(sizeof(struct subelem_header) +
2017 			 persta_prof[TAG_LEN_POS])) {
2018 			mlo_err_rl("Length of buffer containing per-STA profile %zu octets is smaller than total size of current subelement %zu octets",
2019 				   persta_prof_bufflen,
2020 				   sizeof(struct subelem_header) +
2021 						persta_prof[TAG_LEN_POS]);
2022 			return QDF_STATUS_E_PROTO;
2023 		}
2024 
2025 		subelemseqpayloadlen = persta_prof[TAG_LEN_POS];
2026 	}
2027 
2028 	sta_prof_remlen = 0;
2029 	sta_prof_currpos = NULL;
2030 	is_reportedmacaddr_valid = false;
2031 	is_beaconinterval_valid = false;
2032 	is_completeprofile = false;
2033 	is_tsfoffset_valid = false;
2034 
2035 	/* Parse per-STA profile */
2036 	ret = util_parse_bvmlie_perstaprofile_stactrl(persta_prof +
2037 						      sizeof(struct subelem_header),
2038 						      subelemseqpayloadlen,
2039 						      &linkid,
2040 						      &beaconinterval,
2041 						      &is_beaconinterval_valid,
2042 						      &tsfoffset,
2043 						      &is_tsfoffset_valid,
2044 						      &is_completeprofile,
2045 						      &is_reportedmacaddr_valid,
2046 						      &reportedmacaddr,
2047 						      true,
2048 						      &sta_prof_currpos,
2049 						      &sta_prof_remlen);
2050 	if (QDF_IS_STATUS_ERROR(ret)) {
2051 		qdf_mem_free(mlieseqpayload_copy);
2052 		return ret;
2053 	}
2054 
2055 	if (subtype == WLAN_FC0_STYPE_PROBE_RESP && !is_completeprofile) {
2056 		mlo_err("Complete profile information is not present in per-STA profile of probe response frame");
2057 		return QDF_STATUS_E_NOSUPPORT;
2058 	}
2059 
2060 	/* We double check for a NULL STA Profile, though the helper function
2061 	 * above would have taken care of this. We need to get a non-NULL STA
2062 	 * profile, because we need to get at least the expected fixed fields,
2063 	 * even if there is an (improbable) total inheritance.
2064 	 */
2065 	if (!sta_prof_currpos) {
2066 		mlo_err_rl("STA profile is NULL");
2067 		qdf_mem_free(mlieseqpayload_copy);
2068 		return QDF_STATUS_E_PROTO;
2069 	}
2070 
2071 	/* As per the standard, the sender sets the MAC address in the per-STA
2072 	 * profile in association request/response. Without this, we cannot
2073 	 * generate the link specific frame.
2074 	 */
2075 	if (!is_reportedmacaddr_valid) {
2076 		mlo_err_rl("Unable to get MAC address from per-STA profile");
2077 		qdf_mem_free(mlieseqpayload_copy);
2078 		return QDF_STATUS_E_PROTO;
2079 	}
2080 
2081 	link_frame_currpos = link_frame;
2082 	*link_frame_len = 0;
2083 	link_frame_currlen = 0;
2084 
2085 	if (link_frame_maxsize < WLAN_MAC_HDR_LEN_3A) {
2086 		mlo_err("Insufficient space in link specific frame for 802.11 header. Required: %u octets, available: %zu octets",
2087 			WLAN_MAC_HDR_LEN_3A, link_frame_maxsize);
2088 
2089 		qdf_mem_free(mlieseqpayload_copy);
2090 		return QDF_STATUS_E_NOMEM;
2091 	}
2092 
2093 	link_frame_currpos += WLAN_MAC_HDR_LEN_3A;
2094 	link_frame_currlen += WLAN_MAC_HDR_LEN_3A;
2095 
2096 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2097 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
2098 		mlo_debug("Populating fixed fields for (re)assoc req in link specific frame");
2099 
2100 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
2101 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
2102 				   sta_prof_remlen,
2103 				   WLAN_CAPABILITYINFO_LEN);
2104 
2105 			qdf_mem_free(mlieseqpayload_copy);
2106 			return QDF_STATUS_E_PROTO;
2107 		}
2108 
2109 		/* Capability information is specific to the link. Copy this
2110 		 * from the STA profile.
2111 		 */
2112 
2113 		if ((link_frame_maxsize - link_frame_currlen) <
2114 				WLAN_CAPABILITYINFO_LEN) {
2115 			mlo_err("Insufficient space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
2116 				WLAN_CAPABILITYINFO_LEN,
2117 				(link_frame_maxsize - link_frame_currlen));
2118 
2119 			qdf_mem_free(mlieseqpayload_copy);
2120 			return QDF_STATUS_E_NOMEM;
2121 		}
2122 
2123 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2124 			     WLAN_CAPABILITYINFO_LEN);
2125 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2126 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2127 		mlo_debug("Added Capability Info field (%u octets) to link specific frame",
2128 			  WLAN_CAPABILITYINFO_LEN);
2129 
2130 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2131 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2132 
2133 		/* Listen Interval is common between all links. Copy this from
2134 		 * the reporting section of the frame.
2135 		 */
2136 
2137 		if ((link_frame_maxsize - link_frame_currlen) <
2138 				WLAN_LISTENINTERVAL_LEN) {
2139 			mlo_err("Insufficient space in link specific frame for Listen Interval field. Required: %u octets, available: %zu octets",
2140 				WLAN_LISTENINTERVAL_LEN,
2141 				(link_frame_maxsize - link_frame_currlen));
2142 
2143 			qdf_mem_free(mlieseqpayload_copy);
2144 			return QDF_STATUS_E_NOMEM;
2145 		}
2146 
2147 		qdf_mem_copy(link_frame_currpos,
2148 			     frame + WLAN_CAPABILITYINFO_LEN,
2149 			     WLAN_LISTENINTERVAL_LEN);
2150 		link_frame_currpos += WLAN_LISTENINTERVAL_LEN;
2151 		link_frame_currlen += WLAN_LISTENINTERVAL_LEN;
2152 		mlo_debug("Added Listen Interval field (%u octets) to link specific frame",
2153 			  WLAN_LISTENINTERVAL_LEN);
2154 
2155 		if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
2156 			/* Current AP address is common between all links. Copy
2157 			 * this from the reporting section of the frame.
2158 			 */
2159 			if ((link_frame_maxsize - link_frame_currlen) <
2160 				QDF_MAC_ADDR_SIZE) {
2161 				mlo_err("Insufficient space in link specific frame for current AP address. Required: %u octets, available: %zu octets",
2162 					QDF_MAC_ADDR_SIZE,
2163 					(link_frame_maxsize -
2164 						link_frame_currlen));
2165 
2166 				qdf_mem_free(mlieseqpayload_copy);
2167 				return QDF_STATUS_E_NOMEM;
2168 			}
2169 
2170 			qdf_mem_copy(link_frame_currpos,
2171 				     frame + WLAN_CAPABILITYINFO_LEN +
2172 						WLAN_LISTENINTERVAL_LEN,
2173 				     QDF_MAC_ADDR_SIZE);
2174 			link_frame_currpos += QDF_MAC_ADDR_SIZE;
2175 			link_frame_currlen += QDF_MAC_ADDR_SIZE;
2176 			mlo_debug("Reassoc req: Added Current AP address field (%u octets) to link specific frame",
2177 				  QDF_MAC_ADDR_SIZE);
2178 		}
2179 	} else if (subtype == WLAN_FC0_STYPE_ASSOC_RESP ||
2180 		   subtype == WLAN_FC0_STYPE_REASSOC_RESP) {
2181 		/* This is a (re)association response */
2182 		mlo_debug("Populating fixed fields for (re)assoc resp in link specific frame");
2183 
2184 		if (sta_prof_remlen <
2185 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2186 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info + length of Status Code %u",
2187 				   sta_prof_remlen,
2188 				   WLAN_CAPABILITYINFO_LEN +
2189 					WLAN_STATUSCODE_LEN);
2190 
2191 			qdf_mem_free(mlieseqpayload_copy);
2192 			return QDF_STATUS_E_PROTO;
2193 		}
2194 
2195 		/* Capability information and Status Code are specific to the
2196 		 * link. Copy these from the STA profile.
2197 		 */
2198 
2199 		if ((link_frame_maxsize - link_frame_currlen) <
2200 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2201 			mlo_err("Insufficient space in link specific frame for Capability Info and Status Code fields. Required: %u octets, available: %zu octets",
2202 				WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN,
2203 				(link_frame_maxsize - link_frame_currlen));
2204 
2205 			qdf_mem_free(mlieseqpayload_copy);
2206 			return QDF_STATUS_E_NOMEM;
2207 		}
2208 
2209 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2210 			     (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN));
2211 		link_frame_currpos += (WLAN_CAPABILITYINFO_LEN +
2212 						WLAN_STATUSCODE_LEN);
2213 		link_frame_currlen += (WLAN_CAPABILITYINFO_LEN +
2214 				WLAN_STATUSCODE_LEN);
2215 		mlo_debug("Added Capability Info and Status Code fields (%u octets) to link specific frame",
2216 			  WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN);
2217 
2218 		sta_prof_currpos += (WLAN_CAPABILITYINFO_LEN +
2219 				WLAN_STATUSCODE_LEN);
2220 		sta_prof_remlen -= (WLAN_CAPABILITYINFO_LEN +
2221 				WLAN_STATUSCODE_LEN);
2222 
2223 		/* AID is common between all links. Copy this from the original
2224 		 * frame.
2225 		 */
2226 
2227 		if ((link_frame_maxsize - link_frame_currlen) < WLAN_AID_LEN) {
2228 			mlo_err("Insufficient space in link specific frame for AID field. Required: %u octets, available: %zu octets",
2229 				WLAN_AID_LEN,
2230 				(link_frame_maxsize - link_frame_currlen));
2231 
2232 			qdf_mem_free(mlieseqpayload_copy);
2233 			return QDF_STATUS_E_NOMEM;
2234 		}
2235 
2236 		qdf_mem_copy(link_frame_currpos,
2237 			     frame + WLAN_CAPABILITYINFO_LEN +
2238 					WLAN_STATUSCODE_LEN,
2239 			     WLAN_AID_LEN);
2240 		link_frame_currpos += WLAN_AID_LEN;
2241 		link_frame_currlen += WLAN_AID_LEN;
2242 		mlo_debug("Added AID field (%u octets) to link specific frame",
2243 			  WLAN_AID_LEN);
2244 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2245 		/* This is a probe response */
2246 		mlo_debug("Populating fixed fields for probe response in link specific frame");
2247 
2248 		if ((link_frame_maxsize - link_frame_currlen) <
2249 				WLAN_TIMESTAMP_LEN) {
2250 			mlo_err("Insufficient space in link specific frame for Timestamp Info field. Required: %u octets, available: %zu octets",
2251 				WLAN_TIMESTAMP_LEN,
2252 				(link_frame_maxsize - link_frame_currlen));
2253 
2254 			qdf_mem_free(mlieseqpayload_copy);
2255 			return QDF_STATUS_E_NOMEM;
2256 		}
2257 
2258 		/* Per spec 11be_D2.1.1, the TSF Offset subfield of the STA Info
2259 		 * field indicates the offset (Toffset)between the TSF timer of
2260 		 * the reported AP (TA) and the TSF timer of the reporting
2261 		 * AP (TB) and is encoded as a 2s complement signed integer
2262 		 * with units of 2 µs. Toffset is calculated as
2263 		 * Toffset= Floor((TA – TB)/2).
2264 		 */
2265 		if (is_tsfoffset_valid)
2266 			tsf += tsfoffset * 2;
2267 
2268 		qdf_mem_copy(link_frame_currpos, &tsf, WLAN_TIMESTAMP_LEN);
2269 		link_frame_currpos += WLAN_TIMESTAMP_LEN;
2270 		link_frame_currlen += WLAN_TIMESTAMP_LEN;
2271 		mlo_debug("Added Timestamp Info field (%u octets) to link specific frame",
2272 			  WLAN_TIMESTAMP_LEN);
2273 
2274 		if (!is_beaconinterval_valid) {
2275 			mlo_err_rl("Beacon interval information not present in STA info field of per-STA profile");
2276 			qdf_mem_free(mlieseqpayload_copy);
2277 			return QDF_STATUS_E_PROTO;
2278 		}
2279 
2280 		/* Beacon Interval information copy this from
2281 		 * the STA info field.
2282 		 */
2283 		if ((link_frame_maxsize - link_frame_currlen) <
2284 				WLAN_BEACONINTERVAL_LEN) {
2285 			mlo_err("Insufficient space in link specific frame for Beacon Interval Info field. Required: %u octets, available: %zu octets",
2286 				WLAN_BEACONINTERVAL_LEN,
2287 				(link_frame_maxsize - link_frame_currlen));
2288 
2289 			qdf_mem_free(mlieseqpayload_copy);
2290 			return QDF_STATUS_E_NOMEM;
2291 		}
2292 
2293 		qdf_mem_copy(link_frame_currpos, &beaconinterval,
2294 			     WLAN_BEACONINTERVAL_LEN);
2295 		link_frame_currpos += WLAN_BEACONINTERVAL_LEN;
2296 		link_frame_currlen += WLAN_BEACONINTERVAL_LEN;
2297 		mlo_debug("Added Beacon Interval Info field (%u octets) to link specific frame",
2298 			  WLAN_BEACONINTERVAL_LEN);
2299 
2300 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
2301 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
2302 				   sta_prof_remlen,
2303 				   WLAN_CAPABILITYINFO_LEN);
2304 
2305 			qdf_mem_free(mlieseqpayload_copy);
2306 			return QDF_STATUS_E_PROTO;
2307 		}
2308 
2309 		/* Capability information is specific to the link. Copy this
2310 		 * from the STA profile.
2311 		 */
2312 
2313 		if ((link_frame_maxsize - link_frame_currlen) <
2314 				WLAN_CAPABILITYINFO_LEN) {
2315 			mlo_err("Insufficient space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
2316 				WLAN_CAPABILITYINFO_LEN,
2317 				(link_frame_maxsize - link_frame_currlen));
2318 
2319 			qdf_mem_free(mlieseqpayload_copy);
2320 			return QDF_STATUS_E_NOMEM;
2321 		}
2322 
2323 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2324 			     WLAN_CAPABILITYINFO_LEN);
2325 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2326 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2327 		mlo_debug("Added Capability Info field (%u octets) to link specific frame",
2328 			  WLAN_CAPABILITYINFO_LEN);
2329 
2330 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2331 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2332 	}
2333 
2334 	sta_prof_iesection = sta_prof_currpos;
2335 	sta_prof_iesection_len = sta_prof_remlen;
2336 
2337 	/* Populate non-inheritance lists if applicable */
2338 	ninherit_elemlist_len = 0;
2339 	ninherit_elemlist = NULL;
2340 	ninherit_elemextlist_len = 0;
2341 	ninherit_elemextlist = NULL;
2342 
2343 	ret = util_get_noninheritlists(sta_prof_iesection,
2344 				       sta_prof_iesection_len,
2345 				       &ninherit_elemlist,
2346 				       &ninherit_elemlist_len,
2347 				       &ninherit_elemextlist,
2348 				       &ninherit_elemextlist_len);
2349 	if (QDF_IS_STATUS_ERROR(ret)) {
2350 		qdf_mem_free(mlieseqpayload_copy);
2351 		return ret;
2352 	}
2353 
2354 	/* Go through IEs of the reporting STA, and those in STA profile, merge
2355 	 * them into link_frame (except for elements in the Non-Inheritance
2356 	 * list).
2357 	 *
2358 	 * Note: Currently, only 2-link MLO is supported here. We may have a
2359 	 * future change to expand to more links.
2360 	 */
2361 	reportingsta_ie = util_find_eid(WLAN_ELEMID_SSID, frame_iesection,
2362 					frame_iesection_len);
2363 
2364 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2365 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ) ||
2366 	    (subtype == WLAN_FC0_STYPE_PROBE_RESP)) {
2367 		/* Sanity check that the SSID element is present for the
2368 		 * reporting STA. There is no stipulation in the standard for
2369 		 * the STA profile in this regard, so we do not check the STA
2370 		 * profile for the SSID element.
2371 		 */
2372 		if (!reportingsta_ie) {
2373 			mlo_err_rl("SSID element not found in reporting STA of the frame.");
2374 			qdf_mem_free(mlieseqpayload_copy);
2375 			return QDF_STATUS_E_PROTO;
2376 		}
2377 	} else {
2378 		/* This is a (re)association response. Sanity check that the
2379 		 * SSID element is present neither for the reporting STA nor in
2380 		 * the STA profile.
2381 		 */
2382 		if (reportingsta_ie) {
2383 			mlo_err_rl("SSID element found for reporting STA for (re)association response. It should not be present.");
2384 			qdf_mem_free(mlieseqpayload_copy);
2385 			return QDF_STATUS_E_PROTO;
2386 		}
2387 
2388 		sta_prof_ie = util_find_eid(WLAN_ELEMID_SSID,
2389 					    sta_prof_iesection,
2390 					    sta_prof_iesection_len);
2391 
2392 		if (sta_prof_ie) {
2393 			mlo_err_rl("SSID element found in STA profile for (re)association response. It should not be present.");
2394 			qdf_mem_free(mlieseqpayload_copy);
2395 			return QDF_STATUS_E_PROTO;
2396 		}
2397 	}
2398 
2399 	reportingsta_ie = reportingsta_ie ? reportingsta_ie : frame_iesection;
2400 
2401 	ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection,
2402 					    frame_iesection_len);
2403 	if (QDF_IS_STATUS_ERROR(ret)) {
2404 		qdf_mem_free(mlieseqpayload_copy);
2405 		return ret;
2406 	}
2407 
2408 	reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
2409 
2410 	while (((reportingsta_ie + reportingsta_ie_size) - frame_iesection)
2411 			<= frame_iesection_len) {
2412 		/* Skip Multi-Link element */
2413 		if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
2414 		    (reportingsta_ie[IDEXT_POS] ==
2415 				WLAN_EXTN_ELEMID_MULTI_LINK)) {
2416 			if (((reportingsta_ie + reportingsta_ie_size) -
2417 					frame_iesection) == frame_iesection_len)
2418 				break;
2419 
2420 			/* Add BV ML IE for link specific probe response */
2421 			if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2422 				ret = util_add_mlie_for_prb_rsp_gen(reportingsta_ie,
2423 								    reportingsta_ie[TAG_LEN_POS],
2424 								    &link_frame_currpos,
2425 								    &link_frame_currlen,
2426 								    linkid);
2427 				if (QDF_IS_STATUS_ERROR(ret)) {
2428 					qdf_mem_free(mlieseqpayload_copy);
2429 					return ret;
2430 				}
2431 			}
2432 			reportingsta_ie += reportingsta_ie_size;
2433 
2434 			ret = util_validate_reportingsta_ie(reportingsta_ie,
2435 							    frame_iesection,
2436 							    frame_iesection_len);
2437 			if (QDF_IS_STATUS_ERROR(ret)) {
2438 				qdf_mem_free(mlieseqpayload_copy);
2439 				return ret;
2440 			}
2441 
2442 			reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2443 				MIN_IE_LEN;
2444 
2445 			continue;
2446 		}
2447 
2448 		sta_prof_ie = NULL;
2449 		sta_prof_ie_size = 0;
2450 
2451 		if (sta_prof_iesection_len) {
2452 			if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2453 				sta_prof_ie = (uint8_t *)util_find_extn_eid(reportingsta_ie[ID_POS],
2454 									    reportingsta_ie[IDEXT_POS],
2455 									    sta_prof_iesection,
2456 									    sta_prof_iesection_len);
2457 			} else {
2458 				sta_prof_ie = (uint8_t *)util_find_eid(reportingsta_ie[ID_POS],
2459 								       sta_prof_iesection,
2460 								       sta_prof_iesection_len);
2461 			}
2462 		}
2463 
2464 		if (!sta_prof_ie) {
2465 			/* IE is present for reporting STA, but not in STA
2466 			 * profile.
2467 			 */
2468 
2469 			is_in_noninheritlist = false;
2470 
2471 			ret = util_eval_ie_in_noninheritlist((uint8_t *)reportingsta_ie,
2472 							     reportingsta_ie_size,
2473 							     ninherit_elemlist,
2474 							     ninherit_elemlist_len,
2475 							     ninherit_elemextlist,
2476 							     ninherit_elemextlist_len,
2477 							     &is_in_noninheritlist);
2478 
2479 			if (QDF_IS_STATUS_ERROR(ret)) {
2480 				qdf_mem_free(mlieseqpayload_copy);
2481 				return ret;
2482 			}
2483 
2484 			if (!is_in_noninheritlist) {
2485 				if ((link_frame_currpos +
2486 						reportingsta_ie_size) <=
2487 					(link_frame + link_frame_maxsize)) {
2488 					qdf_mem_copy(link_frame_currpos,
2489 						     reportingsta_ie,
2490 						     reportingsta_ie_size);
2491 
2492 					link_frame_currpos +=
2493 						reportingsta_ie_size;
2494 					link_frame_currlen +=
2495 						reportingsta_ie_size;
2496 
2497 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2498 						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",
2499 							  reportingsta_ie[ID_POS],
2500 							  reportingsta_ie[IDEXT_POS],
2501 							  reportingsta_ie_size);
2502 					} else {
2503 						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",
2504 							  reportingsta_ie[ID_POS],
2505 							  reportingsta_ie_size);
2506 					}
2507 				} else {
2508 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2509 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2510 							   reportingsta_ie[ID_POS],
2511 							   reportingsta_ie[IDEXT_POS],
2512 							   reportingsta_ie_size,
2513 							   link_frame_maxsize -
2514 							   link_frame_currlen);
2515 					} else {
2516 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2517 							   reportingsta_ie[ID_POS],
2518 							   reportingsta_ie_size,
2519 							   link_frame_maxsize -
2520 							   link_frame_currlen);
2521 					}
2522 
2523 					qdf_mem_free(mlieseqpayload_copy);
2524 					return QDF_STATUS_E_NOMEM;
2525 				}
2526 			} else {
2527 				if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2528 					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.",
2529 						  reportingsta_ie[ID_POS],
2530 						  reportingsta_ie[IDEXT_POS],
2531 						  reportingsta_ie_size);
2532 				} else {
2533 					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.",
2534 						  reportingsta_ie[ID_POS],
2535 						  reportingsta_ie_size);
2536 				}
2537 			}
2538 		} else {
2539 			/* IE is present for reporting STA and also in STA
2540 			 * profile, copy from STA profile and flag the IE in STA
2541 			 * profile as copied (by setting EID field to 0). The
2542 			 * SSID element (with EID 0) is processed first to
2543 			 * enable this. For vendor IE, compare OUI + type +
2544 			 * subType to determine if they are the same IE.
2545 			 */
2546 			/* Note: This may be revisited in a future change, to
2547 			 * adhere to provisions in the standard for multiple
2548 			 * occurrences of a given element ID/extension element
2549 			 * ID.
2550 			 */
2551 
2552 			ret = util_validate_sta_prof_ie(sta_prof_ie,
2553 							sta_prof_iesection,
2554 							sta_prof_iesection_len);
2555 			if (QDF_IS_STATUS_ERROR(ret)) {
2556 				qdf_mem_free(mlieseqpayload_copy);
2557 				return ret;
2558 			}
2559 
2560 			sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] +
2561 				MIN_IE_LEN;
2562 
2563 			sta_prof_iesection_remlen =
2564 				sta_prof_iesection_len -
2565 					(sta_prof_ie - sta_prof_iesection);
2566 
2567 			if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
2568 			    (sta_prof_iesection_remlen >= MIN_VENDOR_TAG_LEN)) {
2569 				/* If Vendor IE also presents in STA profile,
2570 				 * then ignore the Vendor IE which is for
2571 				 * reporting STA. It only needs to copy Vendor
2572 				 * IE from STA profile to link specific frame.
2573 				 * The copy happens when going through the
2574 				 * remaining IEs.
2575 				 */
2576 				;
2577 			} else {
2578 				/* Copy IE from STA profile into link specific
2579 				 * frame.
2580 				 */
2581 				if ((link_frame_currpos + sta_prof_ie_size) <=
2582 					(link_frame + link_frame_maxsize)) {
2583 					qdf_mem_copy(link_frame_currpos,
2584 						     sta_prof_ie,
2585 						     sta_prof_ie_size);
2586 
2587 					link_frame_currpos += sta_prof_ie_size;
2588 					link_frame_currlen +=
2589 						sta_prof_ie_size;
2590 
2591 					if (reportingsta_ie[ID_POS] ==
2592 							WLAN_ELEMID_EXTN_ELEM) {
2593 						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",
2594 							  sta_prof_ie[ID_POS],
2595 							  sta_prof_ie[IDEXT_POS],
2596 							  sta_prof_ie_size);
2597 					} else {
2598 						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",
2599 							  sta_prof_ie[ID_POS],
2600 							  sta_prof_ie_size);
2601 					}
2602 
2603 					sta_prof_ie[0] = 0;
2604 				} else {
2605 					if (sta_prof_ie[ID_POS] ==
2606 							WLAN_ELEMID_EXTN_ELEM) {
2607 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2608 							   sta_prof_ie[ID_POS],
2609 							   sta_prof_ie[IDEXT_POS],
2610 							   sta_prof_ie_size,
2611 							   link_frame_maxsize -
2612 							   link_frame_currlen);
2613 					} else {
2614 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2615 							   sta_prof_ie[ID_POS],
2616 							   sta_prof_ie_size,
2617 							   link_frame_maxsize -
2618 							   link_frame_currlen);
2619 					}
2620 
2621 					qdf_mem_free(mlieseqpayload_copy);
2622 					return QDF_STATUS_E_NOMEM;
2623 				}
2624 			}
2625 		}
2626 
2627 		if (((reportingsta_ie + reportingsta_ie_size) -
2628 					frame_iesection) == frame_iesection_len)
2629 			break;
2630 
2631 		reportingsta_ie += reportingsta_ie_size;
2632 
2633 		ret = util_validate_reportingsta_ie(reportingsta_ie,
2634 						    frame_iesection,
2635 						    frame_iesection_len);
2636 		if (QDF_IS_STATUS_ERROR(ret)) {
2637 			qdf_mem_free(mlieseqpayload_copy);
2638 			return ret;
2639 		}
2640 
2641 		reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2642 			MIN_IE_LEN;
2643 	}
2644 
2645 	/* Go through the remaining unprocessed IEs in STA profile and copy them
2646 	 * to the link specific frame. The processed ones are marked with 0 in
2647 	 * the first octet. The first octet corresponds to the element ID. In
2648 	 * the case of (re)association request, the element with actual ID
2649 	 * WLAN_ELEMID_SSID(0) has already been copied to the link specific
2650 	 * frame. In the case of (re)association response, it has been verified
2651 	 * that the element with actual ID WLAN_ELEMID_SSID(0) is present
2652 	 * neither for the reporting STA nor in the STA profile.
2653 	 */
2654 	sta_prof_iesection_currpos = sta_prof_iesection;
2655 	sta_prof_iesection_remlen = sta_prof_iesection_len;
2656 
2657 	while (sta_prof_iesection_remlen > 0) {
2658 		sta_prof_ie = sta_prof_iesection_currpos;
2659 		ret = util_validate_sta_prof_ie(sta_prof_ie,
2660 						sta_prof_iesection_currpos,
2661 						sta_prof_iesection_remlen);
2662 		if (QDF_IS_STATUS_ERROR(ret)) {
2663 			qdf_mem_free(mlieseqpayload_copy);
2664 			return ret;
2665 		}
2666 
2667 		sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
2668 
2669 		if (!sta_prof_ie[0]) {
2670 			/* Skip this, since it has already been processed */
2671 			sta_prof_iesection_currpos += sta_prof_ie_size;
2672 			sta_prof_iesection_remlen -= sta_prof_ie_size;
2673 			continue;
2674 		}
2675 
2676 		/* Copy IE from STA profile into link specific frame. */
2677 		if ((link_frame_currpos + sta_prof_ie_size) <=
2678 			(link_frame + link_frame_maxsize)) {
2679 			qdf_mem_copy(link_frame_currpos,
2680 				     sta_prof_ie,
2681 				     sta_prof_ie_size);
2682 
2683 			link_frame_currpos += sta_prof_ie_size;
2684 			link_frame_currlen +=
2685 				sta_prof_ie_size;
2686 
2687 			if (reportingsta_ie[ID_POS] ==
2688 					WLAN_ELEMID_EXTN_ELEM) {
2689 				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",
2690 					  sta_prof_ie[ID_POS],
2691 					  sta_prof_ie[IDEXT_POS],
2692 					  sta_prof_ie_size);
2693 			} else {
2694 				mlo_debug("IE with element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame",
2695 					  sta_prof_ie[ID_POS],
2696 					  sta_prof_ie_size);
2697 			}
2698 
2699 			sta_prof_ie[0] = 0;
2700 		} else {
2701 			if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2702 				mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u extension element ID : %u. Required: %zu octets, available: %zu octets",
2703 					   sta_prof_ie[ID_POS],
2704 					   sta_prof_ie[IDEXT_POS],
2705 					   sta_prof_ie_size,
2706 					   link_frame_maxsize -
2707 					   link_frame_currlen);
2708 			} else {
2709 				mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2710 					   sta_prof_ie[ID_POS],
2711 					   sta_prof_ie_size,
2712 					   link_frame_maxsize -
2713 					   link_frame_currlen);
2714 			}
2715 
2716 			qdf_mem_free(mlieseqpayload_copy);
2717 			return QDF_STATUS_E_NOMEM;
2718 		}
2719 
2720 		sta_prof_iesection_currpos += sta_prof_ie_size;
2721 		sta_prof_iesection_remlen -= sta_prof_ie_size;
2722 	}
2723 
2724 	/* Copy the link MAC addr */
2725 	link_frame_hdr = (struct wlan_frame_hdr *)link_frame;
2726 
2727 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2728 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
2729 		qdf_mem_copy(link_frame_hdr->i_addr3, &link_addr,
2730 			     QDF_MAC_ADDR_SIZE);
2731 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2732 			     QDF_MAC_ADDR_SIZE);
2733 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2734 			     QDF_MAC_ADDR_SIZE);
2735 
2736 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_REQ_FC0;
2737 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_REQ_FC1;
2738 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2739 		qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
2740 			     QDF_MAC_ADDR_SIZE);
2741 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2742 			     QDF_MAC_ADDR_SIZE);
2743 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2744 			     QDF_MAC_ADDR_SIZE);
2745 
2746 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_PROBE_RESP_FC0;
2747 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_PROBE_RESP_FC1;
2748 	} else {
2749 		/* This is a (re)association response */
2750 
2751 		qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
2752 			     QDF_MAC_ADDR_SIZE);
2753 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2754 			     QDF_MAC_ADDR_SIZE);
2755 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2756 			     QDF_MAC_ADDR_SIZE);
2757 
2758 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_RESP_FC0;
2759 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_RESP_FC1;
2760 	}
2761 
2762 	mlo_debug("subtype:%u addr3:" QDF_MAC_ADDR_FMT " addr2:"
2763 		  QDF_MAC_ADDR_FMT " addr1:" QDF_MAC_ADDR_FMT,
2764 		  subtype,
2765 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr3),
2766 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr2),
2767 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr1));
2768 
2769 	/* Seq num not used so not populated */
2770 
2771 	qdf_mem_free(mlieseqpayload_copy);
2772 
2773 	*link_frame_len = link_frame_currlen;
2774 
2775 	return QDF_STATUS_SUCCESS;
2776 }
2777 
2778 QDF_STATUS
2779 util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2780 			struct qdf_mac_addr link_addr,
2781 			uint8_t *link_frame,
2782 			qdf_size_t link_frame_maxsize,
2783 			qdf_size_t *link_frame_len)
2784 {
2785 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2786 			(isreassoc ? WLAN_FC0_STYPE_REASSOC_REQ :
2787 				WLAN_FC0_STYPE_ASSOC_REQ),
2788 			link_addr, link_frame, link_frame_maxsize,
2789 			link_frame_len);
2790 }
2791 
2792 QDF_STATUS
2793 util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2794 			struct qdf_mac_addr link_addr,
2795 			uint8_t *link_frame,
2796 			qdf_size_t link_frame_maxsize,
2797 			qdf_size_t *link_frame_len)
2798 {
2799 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2800 			(isreassoc ?  WLAN_FC0_STYPE_REASSOC_RESP :
2801 				WLAN_FC0_STYPE_ASSOC_RESP),
2802 			link_addr, link_frame, link_frame_maxsize,
2803 			link_frame_len);
2804 }
2805 
2806 QDF_STATUS
2807 util_gen_link_probe_rsp(uint8_t *frame, qdf_size_t frame_len,
2808 			struct qdf_mac_addr link_addr,
2809 			uint8_t *link_frame,
2810 			qdf_size_t link_frame_maxsize,
2811 			qdf_size_t *link_frame_len)
2812 {
2813 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2814 			 WLAN_FC0_STYPE_PROBE_RESP,
2815 			link_addr, link_frame, link_frame_maxsize,
2816 			link_frame_len);
2817 }
2818 
2819 QDF_STATUS
2820 util_find_mlie(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq,
2821 	       qdf_size_t *mlieseqlen)
2822 {
2823 	uint8_t *bufboundary;
2824 	uint8_t *ieseq;
2825 	qdf_size_t ieseqlen;
2826 	uint8_t *currie;
2827 	uint8_t *successorfrag;
2828 
2829 	if (!buf || !buflen || !mlieseq || !mlieseqlen)
2830 		return QDF_STATUS_E_NULL_VALUE;
2831 
2832 	*mlieseq = NULL;
2833 	*mlieseqlen = 0;
2834 
2835 	/* Find Multi-Link element. In case a fragment sequence is present,
2836 	 * this element will be the leading fragment.
2837 	 */
2838 	ieseq = util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
2839 				   WLAN_EXTN_ELEMID_MULTI_LINK, buf,
2840 				   buflen);
2841 
2842 	/* Even if the element is not found, we have successfully examined the
2843 	 * buffer. The caller will be provided a NULL value for the starting of
2844 	 * the Multi-Link element. Hence, we return success.
2845 	 */
2846 	if (!ieseq)
2847 		return QDF_STATUS_SUCCESS;
2848 
2849 	bufboundary = buf + buflen;
2850 
2851 	if ((ieseq + MIN_IE_LEN) > bufboundary)
2852 		return QDF_STATUS_E_INVAL;
2853 
2854 	ieseqlen = MIN_IE_LEN + ieseq[TAG_LEN_POS];
2855 
2856 	if (ieseqlen < sizeof(struct wlan_ie_multilink))
2857 		return QDF_STATUS_E_PROTO;
2858 
2859 	if ((ieseq + ieseqlen) > bufboundary)
2860 		return QDF_STATUS_E_INVAL;
2861 
2862 	/* In the next sequence of checks, if there is no space in the buffer
2863 	 * for another element after the Multi-Link element/element fragment
2864 	 * sequence, it could indicate an issue since non-MLO EHT elements
2865 	 * would be expected to follow the Multi-Link element/element fragment
2866 	 * sequence. However, this is outside of the purview of this function,
2867 	 * hence we ignore it.
2868 	 */
2869 
2870 	currie = ieseq;
2871 	successorfrag = util_get_successorfrag(currie, buf, buflen);
2872 
2873 	/* Fragmentation definitions as of IEEE802.11be D1.0 and
2874 	 * IEEE802.11REVme D0.2 are applied. Only the case where Multi-Link
2875 	 * element is present in a buffer from the core frame is considered.
2876 	 * Future changes to fragmentation, cases where the Multi-Link element
2877 	 * is present in a subelement, etc. to be reflected here if applicable
2878 	 * as and when the rules evolve.
2879 	 */
2880 	while (successorfrag) {
2881 		/* We should not be seeing a successor fragment if the length
2882 		 * of the current IE is lesser than the max.
2883 		 */
2884 		if (currie[TAG_LEN_POS] != WLAN_MAX_IE_LEN)
2885 			return QDF_STATUS_E_PROTO;
2886 
2887 		if (successorfrag[TAG_LEN_POS] == 0)
2888 			return QDF_STATUS_E_PROTO;
2889 
2890 		ieseqlen +=  (MIN_IE_LEN + successorfrag[TAG_LEN_POS]);
2891 
2892 		currie = successorfrag;
2893 		successorfrag = util_get_successorfrag(currie, buf, buflen);
2894 	}
2895 
2896 	*mlieseq = ieseq;
2897 	*mlieseqlen = ieseqlen;
2898 	return QDF_STATUS_SUCCESS;
2899 }
2900 
2901 QDF_STATUS
2902 util_get_mlie_common_info_len(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2903 			      uint8_t *commoninfo_len)
2904 {
2905 	struct wlan_ie_multilink *mlie_fixed;
2906 	enum wlan_ml_variant variant;
2907 	uint16_t mlcontrol;
2908 
2909 	if (!mlieseq || !mlieseqlen || !commoninfo_len)
2910 		return QDF_STATUS_E_NULL_VALUE;
2911 
2912 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2913 		return QDF_STATUS_E_INVAL;
2914 
2915 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
2916 
2917 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
2918 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
2919 		return QDF_STATUS_E_INVAL;
2920 
2921 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
2922 
2923 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
2924 			       WLAN_ML_CTRL_TYPE_BITS);
2925 
2926 	if (variant != WLAN_ML_VARIANT_BASIC)
2927 		return QDF_STATUS_E_INVAL;
2928 
2929 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
2930 	 * Check if there is sufficient space in the buffer for the Common Info
2931 	 * Length and MLD MAC address.
2932 	 */
2933 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
2934 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
2935 		return QDF_STATUS_E_PROTO;
2936 
2937 	*commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
2938 
2939 	return QDF_STATUS_SUCCESS;
2940 }
2941 
2942 QDF_STATUS
2943 util_get_bvmlie_bssparamchangecnt(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2944 				  bool *bssparamchangecntfound,
2945 				  uint8_t *bssparamchangecnt)
2946 {
2947 	struct wlan_ie_multilink *mlie_fixed;
2948 	enum wlan_ml_variant variant;
2949 	uint16_t mlcontrol;
2950 	uint16_t presencebitmap;
2951 	uint8_t *commoninfo;
2952 	qdf_size_t commoninfolen;
2953 
2954 	if (!mlieseq || !mlieseqlen || !bssparamchangecntfound ||
2955 	    !bssparamchangecnt)
2956 		return QDF_STATUS_E_NULL_VALUE;
2957 
2958 	*bssparamchangecntfound = false;
2959 	*bssparamchangecnt = 0;
2960 
2961 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2962 		return QDF_STATUS_E_INVAL;
2963 
2964 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
2965 
2966 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
2967 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
2968 		return QDF_STATUS_E_INVAL;
2969 
2970 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
2971 
2972 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
2973 			       WLAN_ML_CTRL_TYPE_BITS);
2974 
2975 	if (variant != WLAN_ML_VARIANT_BASIC)
2976 		return QDF_STATUS_E_NOSUPPORT;
2977 
2978 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
2979 				      WLAN_ML_CTRL_PBM_BITS);
2980 
2981 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
2982 	commoninfolen = WLAN_ML_BV_CINFO_LENGTH_SIZE;
2983 
2984 	commoninfolen += QDF_MAC_ADDR_SIZE;
2985 
2986 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
2987 		commoninfolen += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
2988 
2989 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
2990 				mlieseqlen)
2991 			return QDF_STATUS_E_PROTO;
2992 	}
2993 
2994 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
2995 		*bssparamchangecntfound = true;
2996 		*bssparamchangecnt = *(commoninfo + commoninfolen);
2997 	}
2998 
2999 	return QDF_STATUS_SUCCESS;
3000 }
3001 
3002 QDF_STATUS
3003 util_get_mlie_variant(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3004 		      int *variant)
3005 {
3006 	struct wlan_ie_multilink *mlie_fixed;
3007 	enum wlan_ml_variant var;
3008 	uint16_t mlcontrol;
3009 
3010 	if (!mlieseq || !mlieseqlen || !variant)
3011 		return QDF_STATUS_E_NULL_VALUE;
3012 
3013 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3014 		return QDF_STATUS_E_INVAL;
3015 
3016 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3017 
3018 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3019 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3020 		return QDF_STATUS_E_INVAL;
3021 
3022 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3023 	var = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3024 			   WLAN_ML_CTRL_TYPE_BITS);
3025 
3026 	if (var >= WLAN_ML_VARIANT_INVALIDSTART)
3027 		return QDF_STATUS_E_PROTO;
3028 
3029 	*variant = var;
3030 	return QDF_STATUS_SUCCESS;
3031 }
3032 
3033 QDF_STATUS
3034 util_get_bvmlie_eml_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3035 			bool *eml_cap_found,
3036 			uint16_t *eml_cap)
3037 {
3038 	struct wlan_ie_multilink *mlie_fixed;
3039 	enum wlan_ml_variant variant;
3040 	uint16_t mlcontrol;
3041 	uint8_t eml_cap_offset;
3042 	uint8_t commoninfo_len;
3043 	uint16_t presencebitmap;
3044 
3045 	if (!mlieseq || !mlieseqlen || !eml_cap_found || !eml_cap)
3046 		return QDF_STATUS_E_NULL_VALUE;
3047 
3048 	*eml_cap = 0;
3049 	*eml_cap_found = false;
3050 
3051 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3052 		return QDF_STATUS_E_INVAL;
3053 
3054 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3055 
3056 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3057 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3058 		return QDF_STATUS_E_INVAL;
3059 
3060 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3061 
3062 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3063 			       WLAN_ML_CTRL_TYPE_BITS);
3064 
3065 	if (variant != WLAN_ML_VARIANT_BASIC)
3066 		return QDF_STATUS_E_INVAL;
3067 
3068 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3069 				      WLAN_ML_CTRL_PBM_BITS);
3070 
3071 	/* eml_cap_offset stores the offset of EML Capabilities within
3072 	 * Common Info
3073 	 */
3074 	eml_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
3075 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
3076 		eml_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3077 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
3078 		eml_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3079 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P)
3080 		eml_cap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
3081 
3082 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
3083 		/* Common Info starts at
3084 		 * mlieseq + sizeof(struct wlan_ie_multilink).
3085 		 * Check if there is sufficient space in the buffer for
3086 		 * the Common Info Length.
3087 		 */
3088 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3089 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
3090 			return QDF_STATUS_E_PROTO;
3091 
3092 		/* Check if the value indicated in the Common Info Length
3093 		 * subfield is sufficient to access the EML capabilities.
3094 		 */
3095 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3096 		if (commoninfo_len < (eml_cap_offset +
3097 				      WLAN_ML_BV_CINFO_EMLCAP_SIZE))
3098 			return QDF_STATUS_E_PROTO;
3099 
3100 		/* Common Info starts at mlieseq + sizeof(struct
3101 		 * wlan_ie_multilink). Check if there is sufficient space in
3102 		 * Common Info for the EML capability.
3103 		 */
3104 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3105 				  eml_cap_offset +
3106 				  WLAN_ML_BV_CINFO_EMLCAP_SIZE))
3107 			return QDF_STATUS_E_PROTO;
3108 
3109 		*eml_cap_found = true;
3110 		*eml_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
3111 							 sizeof(struct wlan_ie_multilink) +
3112 							 eml_cap_offset));
3113 	}
3114 	return QDF_STATUS_SUCCESS;
3115 }
3116 
3117 QDF_STATUS
3118 util_get_bvmlie_msd_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3119 			bool *msd_cap_found,
3120 			uint16_t *msd_cap)
3121 {
3122 	struct wlan_ie_multilink *mlie_fixed;
3123 	enum wlan_ml_variant variant;
3124 	uint16_t mlcontrol;
3125 	uint8_t msd_cap_offset;
3126 	uint8_t commoninfo_len;
3127 	uint16_t presencebitmap;
3128 
3129 	if (!mlieseq || !mlieseqlen || !msd_cap_found || !msd_cap)
3130 		return QDF_STATUS_E_NULL_VALUE;
3131 
3132 	*msd_cap = 0;
3133 	*msd_cap_found = false;
3134 
3135 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3136 		return QDF_STATUS_E_INVAL;
3137 
3138 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3139 
3140 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3141 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3142 		return QDF_STATUS_E_INVAL;
3143 
3144 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3145 
3146 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3147 			       WLAN_ML_CTRL_TYPE_BITS);
3148 
3149 	if (variant != WLAN_ML_VARIANT_BASIC)
3150 		return QDF_STATUS_E_INVAL;
3151 
3152 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3153 				      WLAN_ML_CTRL_PBM_BITS);
3154 
3155 	/* msd_cap_offset stores the offset of MSD capabilities within
3156 	 * Common Info
3157 	 */
3158 	msd_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
3159 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
3160 		msd_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3161 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
3162 		msd_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3163 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3164 		/* Common Info starts at
3165 		 * mlieseq + sizeof(struct wlan_ie_multilink).
3166 		 * Check if there is sufficient space in the buffer for
3167 		 * the Common Info Length.
3168 		 */
3169 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3170 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
3171 			return QDF_STATUS_E_PROTO;
3172 
3173 		/* Check if the value indicated in the Common Info Length
3174 		 * subfield is sufficient to access the MSD capabilities.
3175 		 */
3176 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3177 		if (commoninfo_len < (msd_cap_offset +
3178 				      WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3179 			return QDF_STATUS_E_PROTO;
3180 
3181 		/* Common Info starts at mlieseq + sizeof(struct
3182 		 * wlan_ie_multilink). Check if there is sufficient space in
3183 		 * Common Info for the MSD capability.
3184 		 */
3185 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3186 				  msd_cap_offset +
3187 				  WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3188 			return QDF_STATUS_E_PROTO;
3189 
3190 		*msd_cap_found = true;
3191 		*msd_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
3192 							 sizeof(struct wlan_ie_multilink) +
3193 							 msd_cap_offset));
3194 	} else {
3195 		mlo_debug("MSD caps not found in assoc rsp");
3196 	}
3197 
3198 	return QDF_STATUS_SUCCESS;
3199 }
3200 
3201 QDF_STATUS
3202 util_get_bvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3203 			   struct qdf_mac_addr *mldmacaddr)
3204 {
3205 	struct wlan_ie_multilink *mlie_fixed;
3206 	enum wlan_ml_variant variant;
3207 	uint16_t mlcontrol;
3208 	uint8_t commoninfo_len;
3209 
3210 	if (!mlieseq || !mlieseqlen || !mldmacaddr)
3211 		return QDF_STATUS_E_NULL_VALUE;
3212 
3213 	qdf_mem_zero(mldmacaddr, sizeof(*mldmacaddr));
3214 
3215 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3216 		return QDF_STATUS_E_INVAL;
3217 
3218 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3219 
3220 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3221 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3222 		return QDF_STATUS_E_INVAL;
3223 
3224 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3225 
3226 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3227 			       WLAN_ML_CTRL_TYPE_BITS);
3228 
3229 	if (variant != WLAN_ML_VARIANT_BASIC)
3230 		return QDF_STATUS_E_INVAL;
3231 
3232 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
3233 	 * Check if there is sufficient space in the buffer for the Common Info
3234 	 * Length and MLD MAC address.
3235 	 */
3236 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
3237 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
3238 		return QDF_STATUS_E_PROTO;
3239 
3240 	/* Check if the value indicated in the Common Info Length subfield is
3241 	 * sufficient to access the MLD MAC address.
3242 	 */
3243 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3244 	if (commoninfo_len < (WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE))
3245 		return QDF_STATUS_E_PROTO;
3246 
3247 	qdf_mem_copy(mldmacaddr->bytes,
3248 		     mlieseq + sizeof(struct wlan_ie_multilink) +
3249 		     WLAN_ML_BV_CINFO_LENGTH_SIZE,
3250 		     QDF_MAC_ADDR_SIZE);
3251 
3252 	return QDF_STATUS_SUCCESS;
3253 }
3254 
3255 QDF_STATUS
3256 util_get_bvmlie_primary_linkid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3257 			       bool *linkidfound, uint8_t *linkid)
3258 {
3259 	struct wlan_ie_multilink *mlie_fixed;
3260 	enum wlan_ml_variant variant;
3261 	uint16_t mlcontrol;
3262 	uint16_t presencebitmap;
3263 	uint8_t *commoninfo;
3264 	qdf_size_t commoninfolen;
3265 	uint8_t *linkidinfo;
3266 
3267 	if (!mlieseq || !mlieseqlen || !linkidfound || !linkid)
3268 		return QDF_STATUS_E_NULL_VALUE;
3269 
3270 	*linkidfound = false;
3271 	*linkid = 0;
3272 
3273 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3274 		return QDF_STATUS_E_INVAL;
3275 
3276 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3277 
3278 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3279 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3280 		return QDF_STATUS_E_INVAL;
3281 
3282 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3283 
3284 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3285 			       WLAN_ML_CTRL_TYPE_BITS);
3286 
3287 	if (variant != WLAN_ML_VARIANT_BASIC)
3288 		return QDF_STATUS_E_INVAL;
3289 
3290 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3291 				      WLAN_ML_CTRL_PBM_BITS);
3292 
3293 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3294 	commoninfolen = 0;
3295 	commoninfolen += WLAN_ML_BV_CINFO_LENGTH_SIZE;
3296 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3297 			mlieseqlen)
3298 		return QDF_STATUS_E_PROTO;
3299 
3300 	commoninfolen += QDF_MAC_ADDR_SIZE;
3301 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3302 			mlieseqlen)
3303 		return QDF_STATUS_E_PROTO;
3304 
3305 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3306 		linkidinfo = commoninfo + commoninfolen;
3307 		commoninfolen += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3308 
3309 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3310 				mlieseqlen)
3311 			return QDF_STATUS_E_PROTO;
3312 
3313 		*linkidfound = true;
3314 		*linkid = QDF_GET_BITS(linkidinfo[0],
3315 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_IDX,
3316 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_BITS);
3317 	}
3318 
3319 	return QDF_STATUS_SUCCESS;
3320 }
3321 
3322 QDF_STATUS
3323 util_get_bvmlie_mldcap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3324 		       bool *mldcapfound, uint16_t *mldcap)
3325 {
3326 	struct wlan_ie_multilink *mlie_fixed;
3327 	enum wlan_ml_variant variant;
3328 	uint16_t mlcontrol;
3329 	uint16_t presencebitmap;
3330 	uint8_t *commoninfo;
3331 	uint8_t commoninfo_len;
3332 	qdf_size_t mldcap_offset;
3333 
3334 	if (!mlieseq || !mlieseqlen || !mldcapfound || !mldcap)
3335 		return QDF_STATUS_E_NULL_VALUE;
3336 
3337 	*mldcapfound = false;
3338 	*mldcap = 0;
3339 
3340 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3341 		return QDF_STATUS_E_INVAL;
3342 
3343 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3344 
3345 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3346 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3347 		return QDF_STATUS_E_INVAL;
3348 
3349 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3350 
3351 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3352 			       WLAN_ML_CTRL_TYPE_BITS);
3353 
3354 	if (variant != WLAN_ML_VARIANT_BASIC)
3355 		return QDF_STATUS_E_NOSUPPORT;
3356 
3357 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3358 				      WLAN_ML_CTRL_PBM_BITS);
3359 
3360 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3361 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3362 	/* mldcap_offset stores the offset of MLD Capabilities within
3363 	 * Common Info
3364 	 */
3365 	mldcap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE;
3366 	mldcap_offset += QDF_MAC_ADDR_SIZE;
3367 
3368 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3369 		mldcap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3370 
3371 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3372 				mlieseqlen)
3373 			return QDF_STATUS_E_PROTO;
3374 	}
3375 
3376 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
3377 		mldcap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3378 
3379 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3380 				mlieseqlen)
3381 			return QDF_STATUS_E_PROTO;
3382 	}
3383 
3384 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3385 		mldcap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
3386 
3387 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3388 				mlieseqlen)
3389 			return QDF_STATUS_E_PROTO;
3390 	}
3391 
3392 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
3393 		mldcap_offset += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
3394 
3395 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3396 				mlieseqlen)
3397 			return QDF_STATUS_E_PROTO;
3398 	}
3399 
3400 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MLDCAPANDOP_P) {
3401 		/* Check if the value indicated in the Common Info Length
3402 		 * subfield is sufficient to access the MLD capabilities.
3403 		 */
3404 		if (commoninfo_len < (mldcap_offset +
3405 				      WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE))
3406 			return QDF_STATUS_E_PROTO;
3407 
3408 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset +
3409 					WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE) >
3410 				mlieseqlen)
3411 			return QDF_STATUS_E_PROTO;
3412 
3413 		*mldcap = qdf_le16_to_cpu(*((uint16_t *)(commoninfo + mldcap_offset)));
3414 		*mldcapfound = true;
3415 	}
3416 
3417 	return QDF_STATUS_SUCCESS;
3418 }
3419 
3420 QDF_STATUS
3421 util_get_bvmlie_persta_partner_info(uint8_t *mlieseq,
3422 				    qdf_size_t mlieseqlen,
3423 				    struct mlo_partner_info *partner_info)
3424 {
3425 	struct wlan_ie_multilink *mlie_fixed;
3426 	uint16_t mlcontrol;
3427 	enum wlan_ml_variant variant;
3428 	uint8_t *linkinfo;
3429 	qdf_size_t linkinfo_len;
3430 	struct mlo_partner_info pinfo = {0};
3431 	qdf_size_t mlieseqpayloadlen;
3432 	uint8_t *mlieseqpayload_copy;
3433 	bool is_elemfragseq;
3434 	qdf_size_t defragpayload_len;
3435 
3436 	qdf_size_t tmplen;
3437 	QDF_STATUS ret;
3438 
3439 	if (!mlieseq) {
3440 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3441 		return QDF_STATUS_E_NULL_VALUE;
3442 	}
3443 
3444 	if (!mlieseqlen) {
3445 		mlo_err("Length of Multi-Link element sequence is zero");
3446 		return QDF_STATUS_E_INVAL;
3447 	}
3448 
3449 	if (!partner_info) {
3450 		mlo_err("partner_info is NULL");
3451 		return QDF_STATUS_E_NULL_VALUE;
3452 	}
3453 
3454 	partner_info->num_partner_links = 0;
3455 
3456 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3457 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3458 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3459 		return QDF_STATUS_E_INVAL;
3460 	}
3461 
3462 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3463 
3464 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3465 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3466 		mlo_err("The element is not a Multi-Link element");
3467 		return QDF_STATUS_E_INVAL;
3468 	}
3469 
3470 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3471 
3472 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3473 			       WLAN_ML_CTRL_TYPE_BITS);
3474 
3475 	if (variant != WLAN_ML_VARIANT_BASIC) {
3476 		mlo_err("The variant value %u does not correspond to Basic Variant value %u",
3477 			variant, WLAN_ML_VARIANT_BASIC);
3478 		return QDF_STATUS_E_INVAL;
3479 	}
3480 
3481 	mlieseqpayloadlen = 0;
3482 	tmplen = 0;
3483 	is_elemfragseq = false;
3484 
3485 	ret = wlan_get_elem_fragseq_info(mlieseq,
3486 					 mlieseqlen,
3487 					 &is_elemfragseq,
3488 					 &tmplen,
3489 					 &mlieseqpayloadlen);
3490 	if (QDF_IS_STATUS_ERROR(ret))
3491 		return ret;
3492 
3493 	if (is_elemfragseq) {
3494 		if (tmplen != mlieseqlen) {
3495 			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",
3496 				   tmplen, mlieseqlen);
3497 			return QDF_STATUS_E_INVAL;
3498 		}
3499 
3500 		if (!mlieseqpayloadlen) {
3501 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3502 			return QDF_STATUS_E_FAILURE;
3503 		}
3504 
3505 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3506 			  mlieseqpayloadlen);
3507 	} else {
3508 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3509 			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",
3510 				   mlieseqlen,
3511 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3512 			return QDF_STATUS_E_FAILURE;
3513 		}
3514 
3515 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3516 	}
3517 
3518 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3519 
3520 	if (!mlieseqpayload_copy) {
3521 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3522 		return QDF_STATUS_E_NOMEM;
3523 	}
3524 
3525 	if (is_elemfragseq) {
3526 		ret = wlan_defrag_elem_fragseq(false,
3527 					       mlieseq,
3528 					       mlieseqlen,
3529 					       mlieseqpayload_copy,
3530 					       mlieseqpayloadlen,
3531 					       &defragpayload_len);
3532 		if (QDF_IS_STATUS_ERROR(ret)) {
3533 			qdf_mem_free(mlieseqpayload_copy);
3534 			return ret;
3535 		}
3536 
3537 		if (defragpayload_len != mlieseqpayloadlen) {
3538 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3539 				   defragpayload_len, mlieseqpayloadlen);
3540 			qdf_mem_free(mlieseqpayload_copy);
3541 			return QDF_STATUS_E_FAILURE;
3542 		}
3543 	} else {
3544 		qdf_mem_copy(mlieseqpayload_copy,
3545 			     mlieseq + sizeof(struct ie_header) + 1,
3546 			     mlieseqpayloadlen);
3547 	}
3548 
3549 	linkinfo = NULL;
3550 	linkinfo_len = 0;
3551 
3552 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
3553 					 mlieseqpayloadlen,
3554 					 &linkinfo,
3555 					 &linkinfo_len);
3556 	if (QDF_IS_STATUS_ERROR(ret)) {
3557 		qdf_mem_free(mlieseqpayload_copy);
3558 		return ret;
3559 	}
3560 
3561 	/* In case Link Info is absent, the number of partner links will remain
3562 	 * zero.
3563 	 */
3564 	if (!linkinfo) {
3565 		qdf_mem_free(mlieseqpayload_copy);
3566 		return QDF_STATUS_SUCCESS;
3567 	}
3568 
3569 	ret = util_parse_partner_info_from_linkinfo(linkinfo,
3570 						    linkinfo_len,
3571 						    &pinfo);
3572 
3573 	if (QDF_IS_STATUS_ERROR(ret)) {
3574 		qdf_mem_free(mlieseqpayload_copy);
3575 		return ret;
3576 	}
3577 
3578 	qdf_mem_copy(partner_info, &pinfo, sizeof(*partner_info));
3579 
3580 	qdf_mem_free(mlieseqpayload_copy);
3581 
3582 	return QDF_STATUS_SUCCESS;
3583 }
3584 
3585 QDF_STATUS
3586 util_get_prvmlie_persta_link_id(uint8_t *mlieseq,
3587 				qdf_size_t mlieseqlen,
3588 				struct mlo_probereq_info *probereq_info)
3589 {
3590 	struct wlan_ie_multilink *mlie_fixed;
3591 	uint16_t mlcontrol;
3592 	enum wlan_ml_variant variant;
3593 	uint8_t *linkinfo;
3594 	qdf_size_t linkinfo_len;
3595 	struct mlo_probereq_info pinfo = {0};
3596 	qdf_size_t mlieseqpayloadlen;
3597 	uint8_t *mlieseqpayload_copy;
3598 	bool is_elemfragseq;
3599 	qdf_size_t defragpayload_len;
3600 
3601 	qdf_size_t tmplen;
3602 	QDF_STATUS ret;
3603 
3604 	if (!mlieseq) {
3605 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3606 		return QDF_STATUS_E_NULL_VALUE;
3607 	}
3608 
3609 	if (!mlieseqlen) {
3610 		mlo_err("Length of Multi-Link element sequence is zero");
3611 		return QDF_STATUS_E_INVAL;
3612 	}
3613 
3614 	if (!probereq_info) {
3615 		mlo_err("probe request_info is NULL");
3616 		return QDF_STATUS_E_NULL_VALUE;
3617 	}
3618 
3619 	probereq_info->num_links = 0;
3620 
3621 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3622 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3623 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3624 		return QDF_STATUS_E_INVAL;
3625 	}
3626 
3627 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3628 
3629 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3630 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3631 		mlo_err("The element is not a Multi-Link element");
3632 		return QDF_STATUS_E_INVAL;
3633 	}
3634 
3635 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3636 
3637 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3638 			       WLAN_ML_CTRL_TYPE_BITS);
3639 
3640 	if (variant != WLAN_ML_VARIANT_PROBEREQ) {
3641 		mlo_err("The variant value %u does not correspond to Probe Request Variant value %u",
3642 			variant, WLAN_ML_VARIANT_PROBEREQ);
3643 		return QDF_STATUS_E_INVAL;
3644 	}
3645 
3646 	mlieseqpayloadlen = 0;
3647 	tmplen = 0;
3648 	is_elemfragseq = false;
3649 
3650 	ret = wlan_get_elem_fragseq_info(mlieseq,
3651 					 mlieseqlen,
3652 					 &is_elemfragseq,
3653 					 &tmplen,
3654 					 &mlieseqpayloadlen);
3655 	if (QDF_IS_STATUS_ERROR(ret))
3656 		return ret;
3657 
3658 	if (is_elemfragseq) {
3659 		if (tmplen != mlieseqlen) {
3660 			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",
3661 				   tmplen, mlieseqlen);
3662 			return QDF_STATUS_E_INVAL;
3663 		}
3664 
3665 		if (!mlieseqpayloadlen) {
3666 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3667 			return QDF_STATUS_E_FAILURE;
3668 		}
3669 
3670 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3671 			  mlieseqpayloadlen);
3672 	} else {
3673 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3674 			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",
3675 				   mlieseqlen,
3676 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3677 			return QDF_STATUS_E_FAILURE;
3678 		}
3679 
3680 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3681 	}
3682 
3683 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3684 
3685 	if (!mlieseqpayload_copy) {
3686 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3687 		return QDF_STATUS_E_NOMEM;
3688 	}
3689 
3690 	if (is_elemfragseq) {
3691 		ret = wlan_defrag_elem_fragseq(false,
3692 					       mlieseq,
3693 					       mlieseqlen,
3694 					       mlieseqpayload_copy,
3695 					       mlieseqpayloadlen,
3696 					       &defragpayload_len);
3697 		if (QDF_IS_STATUS_ERROR(ret)) {
3698 			qdf_mem_free(mlieseqpayload_copy);
3699 			return ret;
3700 		}
3701 
3702 		if (defragpayload_len != mlieseqpayloadlen) {
3703 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3704 				   defragpayload_len, mlieseqpayloadlen);
3705 			qdf_mem_free(mlieseqpayload_copy);
3706 			return QDF_STATUS_E_FAILURE;
3707 		}
3708 	} else {
3709 		qdf_mem_copy(mlieseqpayload_copy,
3710 			     mlieseq + sizeof(struct ie_header) + 1,
3711 			     mlieseqpayloadlen);
3712 	}
3713 
3714 	linkinfo = NULL;
3715 	linkinfo_len = 0;
3716 	ret = util_parse_prv_multi_link_ctrl(mlieseqpayload_copy,
3717 					     mlieseqpayloadlen,
3718 					     &linkinfo,
3719 					     &linkinfo_len);
3720 	if (QDF_IS_STATUS_ERROR(ret)) {
3721 		qdf_mem_free(mlieseqpayload_copy);
3722 		return ret;
3723 	}
3724 
3725 	/* In case Link Info is absent, the number of links will remain
3726 	 * zero.
3727 	 */
3728 	if (!linkinfo) {
3729 		qdf_mem_free(mlieseqpayload_copy);
3730 		return QDF_STATUS_SUCCESS;
3731 	}
3732 
3733 	ret = util_parse_probereq_info_from_linkinfo(linkinfo,
3734 						     linkinfo_len,
3735 						     &pinfo);
3736 
3737 	if (QDF_IS_STATUS_ERROR(ret)) {
3738 		qdf_mem_free(mlieseqpayload_copy);
3739 		return ret;
3740 	}
3741 
3742 	qdf_mem_copy(probereq_info, &pinfo, sizeof(*probereq_info));
3743 
3744 	qdf_mem_free(mlieseqpayload_copy);
3745 
3746 	return QDF_STATUS_SUCCESS;
3747 }
3748 
3749 QDF_STATUS
3750 util_get_prvmlie_mldid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3751 		       bool *mldidfound, uint8_t *mldid)
3752 {
3753 	struct wlan_ie_multilink *mlie_fixed;
3754 	enum wlan_ml_variant variant;
3755 	uint16_t mlcontrol;
3756 	uint16_t presencebitmap;
3757 	uint8_t *commoninfo;
3758 	qdf_size_t commoninfolen;
3759 
3760 	if (!mlieseq || !mlieseqlen || !mldidfound || !mldid)
3761 		return QDF_STATUS_E_NULL_VALUE;
3762 
3763 	*mldidfound = false;
3764 	*mldid = 0;
3765 
3766 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3767 		return QDF_STATUS_E_INVAL;
3768 
3769 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3770 
3771 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3772 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3773 		return QDF_STATUS_E_INVAL;
3774 
3775 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3776 
3777 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3778 			       WLAN_ML_CTRL_TYPE_BITS);
3779 
3780 	if (variant != WLAN_ML_VARIANT_PROBEREQ)
3781 		return QDF_STATUS_E_NOSUPPORT;
3782 
3783 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3784 				      WLAN_ML_CTRL_PBM_BITS);
3785 
3786 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3787 	commoninfolen = WLAN_ML_PRV_CINFO_LENGTH_SIZE;
3788 
3789 	if (presencebitmap & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
3790 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen +
3791 		     WLAN_ML_PRV_CINFO_MLDID_SIZE) >
3792 		    mlieseqlen)
3793 			return QDF_STATUS_E_PROTO;
3794 
3795 		*mldid = *((uint8_t *)(commoninfo + commoninfolen));
3796 		commoninfolen += WLAN_ML_PRV_CINFO_MLDID_SIZE;
3797 
3798 		*mldidfound = true;
3799 	}
3800 
3801 	return QDF_STATUS_SUCCESS;
3802 }
3803 
3804 #endif
3805