xref: /wlan-dirver/qca-wifi-host-cmn/umac/mlo_mgr/src/utils_mlo.c (revision 8cfe6b10058a04cafb17eed051f2ddf11bee8931)
1 /*
2  * Copyright (c) 2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*
19  * DOC: contains MLO manager util api's
20  */
21 #include <wlan_cmn.h>
22 #include <wlan_mlo_mgr_sta.h>
23 #include <wlan_cm_public_struct.h>
24 #include <wlan_mlo_mgr_main.h>
25 #include <wlan_cm_api.h>
26 #include "wlan_scan_api.h"
27 #include "qdf_types.h"
28 #include "utils_mlo.h"
29 #include "wlan_mlo_mgr_cmn.h"
30 #include "wlan_utility.h"
31 
32 #ifdef WLAN_FEATURE_11BE_MLO
33 
34 static uint8_t *util_find_eid(uint8_t eid, uint8_t *frame, qdf_size_t len)
35 {
36 	if (!frame)
37 		return NULL;
38 
39 	while (len >= MIN_IE_LEN && len >= frame[TAG_LEN_POS] + MIN_IE_LEN) {
40 		if (frame[ID_POS] == eid)
41 			return frame;
42 
43 		len -= frame[TAG_LEN_POS] + MIN_IE_LEN;
44 		frame += frame[TAG_LEN_POS] + MIN_IE_LEN;
45 	}
46 
47 	return NULL;
48 }
49 
50 static
51 uint8_t *util_find_extn_eid(uint8_t eid, uint8_t extn_eid,
52 			    uint8_t *frame, qdf_size_t len)
53 {
54 	if (!frame)
55 		return NULL;
56 
57 	while (len > MIN_IE_LEN && len >= frame[TAG_LEN_POS] + MIN_IE_LEN) {
58 		if ((frame[ID_POS] == eid) &&
59 		    (frame[ELEM_ID_EXTN_POS] == extn_eid))
60 			return frame;
61 
62 		len -= frame[TAG_LEN_POS] + MIN_IE_LEN;
63 		frame += frame[TAG_LEN_POS] + MIN_IE_LEN;
64 	}
65 	return NULL;
66 }
67 
68 static QDF_STATUS
69 util_parse_multi_link_ctrl(uint8_t *mlieseqpayload,
70 			   qdf_size_t mlieseqpayloadlen,
71 			   uint8_t **link_info,
72 			   qdf_size_t *link_info_len)
73 {
74 	qdf_size_t parsed_payload_len;
75 	uint16_t mlcontrol;
76 	uint16_t presence_bm;
77 	uint16_t cinfo_len = 0;
78 	uint16_t exp_cinfo_len = 0;
79 
80 	/* This helper returns the location(s) and length(s) of (sub)field(s)
81 	 * inferable after parsing the Multi Link element Control field. These
82 	 * location(s) and length(s) is/are in reference to the payload section
83 	 * of the Multi Link element (after defragmentation, if applicable).
84 	 * Here, the payload is the point after the element ID extension of the
85 	 * Multi Link element, and includes the payloads of all subsequent
86 	 * fragments (if any) but not the headers of those fragments.
87 	 *
88 	 * Currently, the helper returns the location and length of the Link
89 	 * Info field in the Multi Link element sequence. Other (sub)field(s)
90 	 * can be added later as required.
91 	 */
92 
93 	if (!mlieseqpayload) {
94 		mlo_err("ML seq payload pointer is NULL");
95 		return QDF_STATUS_E_NULL_VALUE;
96 	}
97 
98 	if (!mlieseqpayloadlen) {
99 		mlo_err("ML seq payload len is 0");
100 		return QDF_STATUS_E_INVAL;
101 	}
102 
103 	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
104 		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
105 			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
106 		return QDF_STATUS_E_PROTO;
107 	}
108 
109 	parsed_payload_len = 0;
110 
111 	qdf_mem_copy(&mlcontrol, mlieseqpayload, WLAN_ML_CTRL_SIZE);
112 	mlcontrol = qdf_le16_to_cpu(mlcontrol);
113 	parsed_payload_len += WLAN_ML_CTRL_SIZE;
114 
115 	presence_bm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
116 				   WLAN_ML_CTRL_PBM_BITS);
117 
118 	if (mlieseqpayloadlen <
119 			(parsed_payload_len + WLAN_ML_BV_CINFO_LENGTH_SIZE)) {
120 		mlo_err_rl("ML seq payload len %zu insufficient for common info length size %u after parsed payload len %zu.",
121 			   mlieseqpayloadlen,
122 			   WLAN_ML_BV_CINFO_LENGTH_SIZE,
123 			   parsed_payload_len);
124 		return QDF_STATUS_E_PROTO;
125 	}
126 
127 	cinfo_len = *(mlieseqpayload + parsed_payload_len);
128 	parsed_payload_len += WLAN_ML_BV_CINFO_LENGTH_SIZE;
129 
130 	if (mlieseqpayloadlen <
131 			(parsed_payload_len + QDF_MAC_ADDR_SIZE)) {
132 		mlo_err_rl("ML seq payload len %zu insufficient for MAC address size %u after parsed payload len %zu.",
133 			   mlieseqpayloadlen,
134 			   QDF_MAC_ADDR_SIZE,
135 			   parsed_payload_len);
136 		return QDF_STATUS_E_PROTO;
137 	}
138 
139 	parsed_payload_len += QDF_MAC_ADDR_SIZE;
140 
141 	/* Check if Link ID info is present */
142 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
143 		if (mlieseqpayloadlen <
144 				(parsed_payload_len +
145 				 WLAN_ML_BV_CINFO_LINKIDINFO_SIZE)) {
146 			mlo_err_rl("ML seq payload len %zu insufficient for Link ID info size %u after parsed payload len %zu.",
147 				   mlieseqpayloadlen,
148 				   WLAN_ML_BV_CINFO_LINKIDINFO_SIZE,
149 				   parsed_payload_len);
150 			return QDF_STATUS_E_PROTO;
151 		}
152 
153 		parsed_payload_len += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
154 	}
155 
156 	/* Check if BSS parameter change count is present */
157 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
158 		if (mlieseqpayloadlen <
159 				(parsed_payload_len +
160 				 WLAN_ML_BSSPARAMCHNGCNT_SIZE)) {
161 			mlo_err_rl("ML seq payload len %zu insufficient for BSS parameter change count size %u after parsed payload len %zu.",
162 				   mlieseqpayloadlen,
163 				   WLAN_ML_BSSPARAMCHNGCNT_SIZE,
164 				   parsed_payload_len);
165 			return QDF_STATUS_E_PROTO;
166 		}
167 
168 		parsed_payload_len += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
169 	}
170 
171 	/* Check if Medium Sync Delay Info is present */
172 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
173 		if (mlieseqpayloadlen <
174 				(parsed_payload_len +
175 				 WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE)) {
176 			mlo_err_rl("ML seq payload len %zu insufficient for Medium Sync Delay Info size %u after parsed payload len %zu.",
177 				   mlieseqpayloadlen,
178 				   WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE,
179 				   parsed_payload_len);
180 			return QDF_STATUS_E_PROTO;
181 		}
182 
183 		parsed_payload_len += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
184 	}
185 
186 	/* Check if EML cap is present */
187 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
188 		if (mlieseqpayloadlen <
189 				(parsed_payload_len +
190 				 WLAN_ML_BV_CINFO_EMLCAP_SIZE)) {
191 			mlo_err_rl("ML seq payload len %zu insufficient for EML cap size %u after parsed payload len %zu.",
192 				   mlieseqpayloadlen,
193 				   WLAN_ML_BV_CINFO_EMLCAP_SIZE,
194 				   parsed_payload_len);
195 			return QDF_STATUS_E_PROTO;
196 		}
197 
198 		parsed_payload_len += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
199 	}
200 
201 	/* Check if MLD cap is present */
202 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_MLDCAPANDOP_P) {
203 		if (mlieseqpayloadlen <
204 				(parsed_payload_len +
205 				 WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE)) {
206 			mlo_err_rl("ML seq payload len %zu insufficient for MLD cap size %u after parsed payload len %zu.",
207 				   mlieseqpayloadlen,
208 				   WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE,
209 				   parsed_payload_len);
210 			return QDF_STATUS_E_PROTO;
211 		}
212 
213 		parsed_payload_len += WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE;
214 	}
215 
216 	/* Check if MLD ID is present */
217 	if (presence_bm & WLAN_ML_BV_CTRL_PBM_MLDID_P) {
218 		if (mlieseqpayloadlen <
219 				(parsed_payload_len +
220 				 WLAN_ML_BV_CINFO_MLDID_SIZE)) {
221 			mlo_err_rl("ML seq payload len %zu insufficient for MLD ID size %u after parsed payload len %zu.",
222 				   mlieseqpayloadlen,
223 				   WLAN_ML_BV_CINFO_MLDID_SIZE,
224 				   parsed_payload_len);
225 			return QDF_STATUS_E_PROTO;
226 		}
227 
228 		parsed_payload_len += WLAN_ML_BV_CINFO_MLDID_SIZE;
229 	}
230 
231 	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 			mlo_debug("LINK ID requested is = %u", linkid);
1061 		}
1062 
1063 		linkinfo_remlen -= (sizeof(struct subelem_header) +
1064 				    subelemseqpayloadlen);
1065 		linkinfo_currpos += (sizeof(struct subelem_header) +
1066 				     subelemseqpayloadlen);
1067 	}
1068 
1069 	mlo_debug("Number of ML probe request links found=%u",
1070 		  probereq_info->num_links);
1071 
1072 	return QDF_STATUS_SUCCESS;
1073 }
1074 
1075 static
1076 QDF_STATUS util_get_noninheritlists(uint8_t *buff, qdf_size_t buff_len,
1077 				    uint8_t **ninherit_elemlist,
1078 				    qdf_size_t *ninherit_elemlist_len,
1079 				    uint8_t **ninherit_elemextlist,
1080 				    qdf_size_t *ninherit_elemextlist_len)
1081 {
1082 	uint8_t *ninherit_ie;
1083 	qdf_size_t unparsed_len;
1084 
1085 	/* Note: This functionality provided by this helper may be combined with
1086 	 * other, older non-inheritance parsing helper functionality and exposed
1087 	 * as a common API as part of future efforts once the older
1088 	 * functionality can be made generic.
1089 	 */
1090 
1091 	if (!buff) {
1092 		mlo_err("Pointer to buffer for IEs is NULL");
1093 		return QDF_STATUS_E_NULL_VALUE;
1094 	}
1095 
1096 	if (!buff_len) {
1097 		mlo_err("IE buffer length is zero");
1098 		return QDF_STATUS_E_INVAL;
1099 	}
1100 
1101 	if (!ninherit_elemlist) {
1102 		mlo_err("Pointer to Non-Inheritance element ID list array is NULL");
1103 		return QDF_STATUS_E_NULL_VALUE;
1104 	}
1105 
1106 	if (!ninherit_elemlist_len) {
1107 		mlo_err("Pointer to Non-Inheritance element ID list array length is NULL");
1108 		return QDF_STATUS_E_NULL_VALUE;
1109 	}
1110 
1111 	if (!ninherit_elemextlist) {
1112 		mlo_err("Pointer to Non-Inheritance element ID extension list array is NULL");
1113 		return QDF_STATUS_E_NULL_VALUE;
1114 	}
1115 
1116 	if (!ninherit_elemextlist_len) {
1117 		mlo_err("Pointer to Non-Inheritance element ID extension list array length is NULL");
1118 		return QDF_STATUS_E_NULL_VALUE;
1119 	}
1120 
1121 	ninherit_ie = NULL;
1122 	*ninherit_elemlist_len = 0;
1123 	*ninherit_elemlist = NULL;
1124 	*ninherit_elemextlist_len = 0;
1125 	*ninherit_elemextlist = NULL;
1126 
1127 	ninherit_ie =
1128 		(uint8_t *)util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
1129 					      WLAN_EXTN_ELEMID_NONINHERITANCE,
1130 					      buff,
1131 					      buff_len);
1132 
1133 	if (ninherit_ie) {
1134 		if ((ninherit_ie + TAG_LEN_POS) > (buff + buff_len - 1)) {
1135 			mlo_err_rl("Position of length field of Non-Inheritance element would exceed IE buffer boundary");
1136 			return QDF_STATUS_E_PROTO;
1137 		}
1138 
1139 		if ((ninherit_ie + ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) >
1140 				(buff + buff_len)) {
1141 			mlo_err_rl("Non-Inheritance element with total length %u would exceed IE buffer boundary",
1142 				   ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN);
1143 			return QDF_STATUS_E_PROTO;
1144 		}
1145 
1146 		if ((ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN) <
1147 				MIN_NONINHERITANCEELEM_LEN) {
1148 			mlo_err_rl("Non-Inheritance element size %u is smaller than the minimum required %u",
1149 				   ninherit_ie[TAG_LEN_POS] + MIN_IE_LEN,
1150 				   MIN_NONINHERITANCEELEM_LEN);
1151 			return QDF_STATUS_E_PROTO;
1152 		}
1153 
1154 		/* Track the number of unparsed octets, excluding the IE header.
1155 		 */
1156 		unparsed_len = ninherit_ie[TAG_LEN_POS];
1157 
1158 		/* Mark the element ID extension as parsed */
1159 		unparsed_len--;
1160 
1161 		*ninherit_elemlist_len = ninherit_ie[ELEM_ID_LIST_LEN_POS];
1162 		unparsed_len--;
1163 
1164 		/* While checking if the Non-Inheritance element ID list length
1165 		 * exceeds the remaining unparsed IE space, we factor in one
1166 		 * octet for the element extension ID list length and subtract
1167 		 * this from the unparsed IE space.
1168 		 */
1169 		if (*ninherit_elemlist_len > (unparsed_len - 1)) {
1170 			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",
1171 				   *ninherit_elemlist_len, unparsed_len - 1);
1172 
1173 			return QDF_STATUS_E_PROTO;
1174 		}
1175 
1176 		if (*ninherit_elemlist_len != 0) {
1177 			*ninherit_elemlist = ninherit_ie + ELEM_ID_LIST_POS;
1178 			unparsed_len -= *ninherit_elemlist_len;
1179 		}
1180 
1181 		*ninherit_elemextlist_len =
1182 			ninherit_ie[ELEM_ID_LIST_LEN_POS + *ninherit_elemlist_len + 1];
1183 		unparsed_len--;
1184 
1185 		if (*ninherit_elemextlist_len > unparsed_len) {
1186 			mlo_err_rl("Non-Inheritance element ID extension list length %zu exceeds remaining unparsed IE space %zu",
1187 				   *ninherit_elemextlist_len, unparsed_len);
1188 
1189 			return QDF_STATUS_E_PROTO;
1190 		}
1191 
1192 		if (*ninherit_elemextlist_len != 0) {
1193 			*ninherit_elemextlist = ninherit_ie +
1194 				ELEM_ID_LIST_LEN_POS + (*ninherit_elemlist_len)
1195 				+ 2;
1196 			unparsed_len -= *ninherit_elemextlist_len;
1197 		}
1198 
1199 		if (unparsed_len > 0) {
1200 			mlo_err_rl("Unparsed length is %zu, expected 0",
1201 				   unparsed_len);
1202 			return QDF_STATUS_E_PROTO;
1203 		}
1204 	}
1205 
1206 	/* If Non-Inheritance element is not found, we still return success,
1207 	 * with the list lengths kept at zero.
1208 	 */
1209 	mlo_debug("Non-Inheritance element ID list array length=%zu",
1210 		  *ninherit_elemlist_len);
1211 	mlo_debug("Non-Inheritance element ID extension list array length=%zu",
1212 		  *ninherit_elemextlist_len);
1213 
1214 	return QDF_STATUS_SUCCESS;
1215 }
1216 
1217 static
1218 QDF_STATUS util_eval_ie_in_noninheritlist(uint8_t *ie, qdf_size_t total_ie_len,
1219 					  uint8_t *ninherit_elemlist,
1220 					  qdf_size_t ninherit_elemlist_len,
1221 					  uint8_t *ninherit_elemextlist,
1222 					  qdf_size_t ninherit_elemextlist_len,
1223 					  bool *is_in_noninheritlist)
1224 {
1225 	int i;
1226 
1227 	/* Evaluate whether the given IE is in the given Non-Inheritance element
1228 	 * ID list or Non-Inheritance element ID extension list, and update the
1229 	 * result into is_in_noninheritlist. If any list is empty, then the IE
1230 	 * is considered to not be present in that list. Both lists can be
1231 	 * empty.
1232 	 *
1233 	 * If QDF_STATUS_SUCCESS is returned, it means that the evaluation is
1234 	 * successful, and that is_in_noninheritlist contains a valid value
1235 	 * (which could be true or false). If a QDF_STATUS error value is
1236 	 * returned, the value in is_in_noninheritlist is invalid and the caller
1237 	 * should ignore it.
1238 	 */
1239 
1240 	/* Note: The functionality provided by this helper may be combined with
1241 	 * other, older non-inheritance parsing helper functionality and exposed
1242 	 * as a common API as part of future efforts once the older
1243 	 * functionality can be made generic.
1244 	 */
1245 
1246 	/* Except for is_in_noninheritlist and ie, other pointer arguments are
1247 	 * permitted to be NULL if they are inapplicable. If they are
1248 	 * applicable, they will be checked to ensure they are not NULL.
1249 	 */
1250 
1251 	if (!is_in_noninheritlist) {
1252 		mlo_err("NULL pointer to flag that indicates if element is in a Non-Inheritance list");
1253 		return QDF_STATUS_E_NULL_VALUE;
1254 	}
1255 
1256 	/* If ninherit_elemlist_len and ninherit_elemextlist_len are both zero
1257 	 * as checked soon in this function, we won't be accessing the IE.
1258 	 * However, we still check right-away if the pointer to the IE is
1259 	 * non-NULL and whether the total IE length is sane enough to access the
1260 	 * element ID and if applicable, the element ID extension, since it
1261 	 * doesn't make sense to set the flag in is_in_noninheritlist for a NULL
1262 	 * IE pointer or an IE whose total length is not sane enough to
1263 	 * distinguish the identity of the IE.
1264 	 */
1265 	if (!ie) {
1266 		mlo_err("NULL pointer to IE");
1267 		return QDF_STATUS_E_NULL_VALUE;
1268 	}
1269 
1270 	if (total_ie_len < (ID_POS + 1)) {
1271 		mlo_err("Total IE length %zu is smaller than minimum required to access element ID %u",
1272 			total_ie_len, ID_POS + 1);
1273 		return QDF_STATUS_E_INVAL;
1274 	}
1275 
1276 	if ((ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1277 	    (total_ie_len < (IDEXT_POS + 1))) {
1278 		mlo_err("Total IE length %zu is smaller than minimum required to access element ID extension %u",
1279 			total_ie_len, IDEXT_POS + 1);
1280 		return QDF_STATUS_E_INVAL;
1281 	}
1282 
1283 	*is_in_noninheritlist = false;
1284 
1285 	/* If both the Non-Inheritance element list and Non-Inheritance element
1286 	 * ID extension list are empty, then return success since we can
1287 	 * conclude immediately that the given element does not occur in any
1288 	 * Non-Inheritance list. The is_in_noninheritlist remains set to false
1289 	 * as required.
1290 	 */
1291 	if (!ninherit_elemlist_len && !ninherit_elemextlist_len)
1292 		return QDF_STATUS_SUCCESS;
1293 
1294 	if (ie[ID_POS] != WLAN_ELEMID_EXTN_ELEM) {
1295 		if (!ninherit_elemlist_len)
1296 			return QDF_STATUS_SUCCESS;
1297 
1298 		if (!ninherit_elemlist) {
1299 			mlo_err("NULL pointer to Non-Inheritance element ID list though length of element ID list is %zu",
1300 				ninherit_elemlist_len);
1301 			return QDF_STATUS_E_NULL_VALUE;
1302 		}
1303 
1304 		for (i = 0; i < ninherit_elemlist_len; i++) {
1305 			if (ie[ID_POS] == ninherit_elemlist[i]) {
1306 				*is_in_noninheritlist = true;
1307 				return QDF_STATUS_SUCCESS;
1308 			}
1309 		}
1310 	} else {
1311 		if (!ninherit_elemextlist_len)
1312 			return QDF_STATUS_SUCCESS;
1313 
1314 		if (!ninherit_elemextlist) {
1315 			mlo_err("NULL pointer to Non-Inheritance element ID extension list though length of element ID extension list is %zu",
1316 				ninherit_elemextlist_len);
1317 			return QDF_STATUS_E_NULL_VALUE;
1318 		}
1319 
1320 		for (i = 0; i < ninherit_elemextlist_len; i++) {
1321 			if (ie[IDEXT_POS] == ninherit_elemextlist[i]) {
1322 				*is_in_noninheritlist = true;
1323 				return QDF_STATUS_SUCCESS;
1324 			}
1325 		}
1326 	}
1327 
1328 	return QDF_STATUS_SUCCESS;
1329 }
1330 
1331 static inline
1332 QDF_STATUS util_validate_reportingsta_ie(const uint8_t *reportingsta_ie,
1333 					 const uint8_t *frame_iesection,
1334 					 const qdf_size_t frame_iesection_len)
1335 {
1336 	qdf_size_t reportingsta_ie_size;
1337 
1338 	if (!reportingsta_ie) {
1339 		mlo_err("Pointer to reporting STA IE is NULL");
1340 		return QDF_STATUS_E_NULL_VALUE;
1341 	}
1342 
1343 	if (!frame_iesection) {
1344 		mlo_err("Pointer to start of IE section in reporting frame is NULL");
1345 		return QDF_STATUS_E_NULL_VALUE;
1346 	}
1347 
1348 	if (!frame_iesection_len) {
1349 		mlo_err("Length of IE section in reporting frame is zero");
1350 		return QDF_STATUS_E_INVAL;
1351 	}
1352 
1353 	if ((reportingsta_ie + ID_POS) > (frame_iesection +
1354 			frame_iesection_len - 1)) {
1355 		mlo_err_rl("Position of element ID field of element for reporting STA would exceed frame IE section boundary");
1356 		return QDF_STATUS_E_PROTO;
1357 	}
1358 
1359 	if ((reportingsta_ie + TAG_LEN_POS) > (frame_iesection +
1360 			frame_iesection_len - 1)) {
1361 		mlo_err_rl("Position of length field of element with element ID %u for reporting STA would exceed frame IE section boundary",
1362 			   reportingsta_ie[ID_POS]);
1363 		return QDF_STATUS_E_PROTO;
1364 	}
1365 
1366 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1367 	    ((reportingsta_ie + IDEXT_POS) > (frame_iesection +
1368 				frame_iesection_len - 1))) {
1369 		mlo_err_rl("Position of element ID extension field of element would exceed frame IE section boundary");
1370 		return QDF_STATUS_E_PROTO;
1371 	}
1372 
1373 	reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
1374 
1375 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1376 	    (reportingsta_ie_size < (IDEXT_POS + 1))) {
1377 		mlo_err_rl("Total length %zu of element for reporting STA is smaller than minimum required to access element ID extension %u",
1378 			   reportingsta_ie_size, IDEXT_POS + 1);
1379 		return QDF_STATUS_E_PROTO;
1380 	}
1381 
1382 	if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
1383 	    (reportingsta_ie_size < (PAYLOAD_START_POS + OUI_LEN))) {
1384 		mlo_err_rl("Total length %zu of element for reporting STA is smaller than minimum required to access vendor EID %u",
1385 			   reportingsta_ie_size, PAYLOAD_START_POS + OUI_LEN);
1386 		return QDF_STATUS_E_PROTO;
1387 	}
1388 
1389 	if ((reportingsta_ie + reportingsta_ie_size) >
1390 			(frame_iesection + frame_iesection_len)) {
1391 		if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
1392 			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",
1393 				   reportingsta_ie_size,
1394 				   reportingsta_ie[ID_POS],
1395 				   reportingsta_ie[IDEXT_POS]);
1396 		} else {
1397 			mlo_err_rl("Total size %zu octets of element with element ID %u for reporting STA would exceed frame IE section boundary",
1398 				   reportingsta_ie_size,
1399 				   reportingsta_ie[ID_POS]);
1400 		}
1401 
1402 		return QDF_STATUS_E_PROTO;
1403 	}
1404 
1405 	return QDF_STATUS_SUCCESS;
1406 }
1407 
1408 static inline
1409 QDF_STATUS util_validate_sta_prof_ie(const uint8_t *sta_prof_ie,
1410 				     const uint8_t *sta_prof_iesection,
1411 				     const qdf_size_t sta_prof_iesection_len)
1412 {
1413 	qdf_size_t sta_prof_ie_size;
1414 
1415 	if (!sta_prof_ie) {
1416 		mlo_err("Pointer to STA profile IE is NULL");
1417 		return QDF_STATUS_E_NULL_VALUE;
1418 	}
1419 
1420 	if (!sta_prof_iesection) {
1421 		mlo_err("Pointer to start of IE section in STA profile is NULL");
1422 		return QDF_STATUS_E_NULL_VALUE;
1423 	}
1424 
1425 	if (!sta_prof_iesection_len) {
1426 		mlo_err("Length of IE section in STA profile is zero");
1427 		return QDF_STATUS_E_INVAL;
1428 	}
1429 
1430 	if ((sta_prof_ie + ID_POS) > (sta_prof_iesection +
1431 			sta_prof_iesection_len - 1)) {
1432 		mlo_err_rl("Position of element ID field of STA profile element would exceed STA profile IE section boundary");
1433 		return QDF_STATUS_E_PROTO;
1434 	}
1435 
1436 	if ((sta_prof_ie + TAG_LEN_POS) > (sta_prof_iesection +
1437 			sta_prof_iesection_len - 1)) {
1438 		mlo_err_rl("Position of length field of element with element ID %u in STA profile would exceed STA profile IE section boundary",
1439 			   sta_prof_ie[ID_POS]);
1440 		return QDF_STATUS_E_PROTO;
1441 	}
1442 
1443 	if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1444 	    ((sta_prof_ie + IDEXT_POS) > (sta_prof_iesection +
1445 				sta_prof_iesection_len - 1))) {
1446 		mlo_err_rl("Position of element ID extension field of element would exceed STA profile IE section boundary");
1447 		return QDF_STATUS_E_PROTO;
1448 	}
1449 
1450 	sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
1451 
1452 	if ((sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
1453 	    (sta_prof_ie_size < (IDEXT_POS + 1))) {
1454 		mlo_err_rl("Total length %zu of STA profile element is smaller than minimum required to access element ID extension %u",
1455 			   sta_prof_ie_size, IDEXT_POS + 1);
1456 		return QDF_STATUS_E_PROTO;
1457 	}
1458 
1459 	if ((sta_prof_ie + sta_prof_ie_size) >
1460 			(sta_prof_iesection + sta_prof_iesection_len)) {
1461 		if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
1462 			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",
1463 				   sta_prof_ie_size,
1464 				   sta_prof_ie[ID_POS],
1465 				   sta_prof_ie[IDEXT_POS]);
1466 		} else {
1467 			mlo_err_rl("Total size %zu octets of element with element ID %u in STA profile would exceed STA profile IE section boundary",
1468 				   sta_prof_ie_size,
1469 				   sta_prof_ie[ID_POS]);
1470 		}
1471 
1472 		return QDF_STATUS_E_PROTO;
1473 	}
1474 
1475 	return QDF_STATUS_SUCCESS;
1476 }
1477 
1478 #ifdef CONN_MGR_ADV_FEATURE
1479 /**
1480  * util_add_mlie_for_prb_rsp_gen - Add the basic variant Multi-Link element
1481  * when generating link specific probe response.
1482  * @reportingsta_ie: Pointer to the reportingsta ie
1483  * @reportingsta_ie_len: Length for reporting sta ie
1484  * @plink_frame_currpos: Pointer to Link frame current pos
1485  * @plink_frame_currlen: Current length of link frame.
1486  * @link_frame_maxsize: Maximum size of the frame to be generated
1487  * @linkid: Link Id value
1488  *
1489  * Add the basic variant Multi-Link element when
1490  * generating link specific probe response.
1491  *
1492  * Return: QDF_STATUS_SUCCESS in the case of success, QDF_STATUS value giving
1493  * the reason for error in the case of failure
1494  */
1495 static QDF_STATUS
1496 util_add_mlie_for_prb_rsp_gen(const uint8_t *reportingsta_ie,
1497 			      qdf_size_t reportingsta_ie_len,
1498 			      uint8_t **plink_frame_currpos,
1499 			      qdf_size_t *plink_frame_currlen,
1500 			      qdf_size_t link_frame_maxsize,
1501 			      uint8_t linkid)
1502 {
1503 	uint8_t mlie_len = 0;
1504 	uint8_t common_info_len = 0;
1505 	struct wlan_ie_multilink ml_ie_ff;
1506 	uint16_t mlcontrol;
1507 	uint16_t presencebm;
1508 	uint8_t *mlie_frame = NULL;
1509 	uint8_t link_id_offset = sizeof(struct wlan_ie_multilink) +
1510 				QDF_MAC_ADDR_SIZE +
1511 				WLAN_ML_BV_CINFO_LENGTH_SIZE;
1512 	uint8_t *link_frame_currpos = *plink_frame_currpos;
1513 	qdf_size_t link_frame_currlen = *plink_frame_currlen;
1514 	QDF_STATUS status = QDF_STATUS_SUCCESS;
1515 
1516 	status = util_get_mlie_common_info_len((uint8_t *)reportingsta_ie,
1517 					       reportingsta_ie_len,
1518 					       &common_info_len);
1519 	if (QDF_IS_STATUS_ERROR(status)) {
1520 		mlo_err("Failed while parsing the common info length");
1521 		return status;
1522 	}
1523 
1524 	/* common info len + bvmlie fixed fields */
1525 	mlie_len = common_info_len + sizeof(struct wlan_ie_multilink);
1526 
1527 	mlo_debug_rl("mlie_len %d, common_info_len %d, link_id_offset %d",
1528 		     mlie_len,
1529 		     common_info_len,
1530 		     link_id_offset);
1531 
1532 	/*
1533 	 * Validate the buffer available before copying ML IE.
1534 	 * Incase if mlie_len is modified at later place, move this validation
1535 	 * there to make sure no buffer overflow happens.
1536 	 */
1537 	if ((link_frame_maxsize - link_frame_currlen) < mlie_len) {
1538 		mlo_err("Insufficient space in link specific frame for ML IE. Required: %u octets, available: %zu octets",
1539 			mlie_len, (link_frame_maxsize - link_frame_currlen));
1540 		return QDF_STATUS_E_NOMEM;
1541 	}
1542 
1543 	mlie_frame = qdf_mem_malloc(mlie_len);
1544 	if (!mlie_frame)
1545 		return QDF_STATUS_E_NOMEM;
1546 
1547 	/* Copy ml ie fixed fields */
1548 	qdf_mem_copy(&ml_ie_ff,
1549 		     reportingsta_ie,
1550 		     sizeof(struct wlan_ie_multilink));
1551 
1552 	ml_ie_ff.elem_len = mlie_len - sizeof(struct ie_header);
1553 
1554 	mlcontrol = qdf_le16_to_cpu(ml_ie_ff.mlcontrol);
1555 	presencebm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
1556 				  WLAN_ML_CTRL_PBM_BITS);
1557 	qdf_set_bit(WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P,
1558 		    (unsigned long *)&presencebm);
1559 
1560 	QDF_SET_BITS(ml_ie_ff.mlcontrol,
1561 		     WLAN_ML_CTRL_PBM_IDX,
1562 		     WLAN_ML_CTRL_PBM_BITS,
1563 		     presencebm);
1564 
1565 	qdf_mem_copy(mlie_frame,
1566 		     &ml_ie_ff,
1567 		     sizeof(struct wlan_ie_multilink));
1568 
1569 	qdf_mem_copy(mlie_frame + sizeof(struct wlan_ie_multilink),
1570 		     reportingsta_ie + sizeof(struct wlan_ie_multilink),
1571 		     mlie_len - sizeof(struct wlan_ie_multilink));
1572 
1573 	if (linkid == 0xFF) {
1574 		qdf_mem_free(mlie_frame);
1575 		mlo_err("Link id is invalid");
1576 		return QDF_STATUS_E_INVAL;
1577 	}
1578 	mlie_frame[link_id_offset] = (mlie_frame[link_id_offset] & ~0x0f) |
1579 				   (linkid & 0x0f);
1580 	qdf_mem_copy(link_frame_currpos,
1581 		     mlie_frame,
1582 		     mlie_len);
1583 
1584 	mlo_debug("Add mlie for link id %d", linkid);
1585 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
1586 			   mlie_frame, mlie_len);
1587 
1588 	link_frame_currpos += mlie_len;
1589 	link_frame_currlen += mlie_len;
1590 	*plink_frame_currpos = link_frame_currpos;
1591 	*plink_frame_currlen = link_frame_currlen;
1592 	qdf_mem_free(mlie_frame);
1593 
1594 	return QDF_STATUS_SUCCESS;
1595 }
1596 #else
1597 static QDF_STATUS
1598 util_add_mlie_for_prb_rsp_gen(const uint8_t *reportingsta_ie,
1599 			      qdf_size_t reportingsta_ie_len,
1600 			      uint8_t **plink_frame_currpos,
1601 			      qdf_size_t *plink_frame_currlen,
1602 			      qdf_size_t link_frame_maxsize,
1603 			      uint8_t linkid)
1604 {
1605 	return QDF_STATUS_SUCCESS;
1606 }
1607 #endif
1608 
1609 #define MLO_LINKSPECIFIC_ASSOC_REQ_FC0  0x00
1610 #define MLO_LINKSPECIFIC_ASSOC_REQ_FC1  0x00
1611 #define MLO_LINKSPECIFIC_ASSOC_RESP_FC0 0x10
1612 #define MLO_LINKSPECIFIC_ASSOC_RESP_FC1 0x00
1613 #define MLO_LINKSPECIFIC_PROBE_RESP_FC0 0x50
1614 #define MLO_LINKSPECIFIC_PROBE_RESP_FC1 0x00
1615 
1616 static
1617 QDF_STATUS util_gen_link_reqrsp_cmn(uint8_t *frame, qdf_size_t frame_len,
1618 				    uint8_t subtype,
1619 				    struct qdf_mac_addr link_addr,
1620 				    uint8_t *link_frame,
1621 				    qdf_size_t link_frame_maxsize,
1622 				    qdf_size_t *link_frame_len)
1623 {
1624 	/* Please see documentation for util_gen_link_assoc_req() and
1625 	 * util_gen_link_assoc_resp() for information on the inputs to and
1626 	 * output from this helper, since those APIs are essentially wrappers
1627 	 * over this helper.
1628 	 */
1629 
1630 	/* Pointer to Multi-Link element/Multi-Link element fragment sequence */
1631 	uint8_t *mlieseq;
1632 	/* Total length of Multi-Link element sequence (including fragments if
1633 	 * any)
1634 	 */
1635 	qdf_size_t mlieseqlen;
1636 	/* Variant (i.e. type) of the Multi-Link element */
1637 	enum wlan_ml_variant variant;
1638 
1639 	/* Length of the payload of the Multi-Link element (inclusive of
1640 	 * fragment payloads if any) without IE headers and element ID extension
1641 	 */
1642 	qdf_size_t mlieseqpayloadlen;
1643 	/* Pointer to copy of the payload of the Multi-Link element (inclusive
1644 	 * of fragment payloads if any) without IE headers and element ID
1645 	 * extension
1646 	 */
1647 	uint8_t *mlieseqpayload_copy;
1648 
1649 	/* Pointer to start of Link Info within the copy of the payload of the
1650 	 * Multi-Link element
1651 	 */
1652 	uint8_t *link_info;
1653 	/* Length of the Link Info */
1654 	qdf_size_t link_info_len;
1655 
1656 	/* Pointer to the IE section that occurs after the fixed fields in the
1657 	 * original frame for the reporting STA.
1658 	 */
1659 	uint8_t *frame_iesection;
1660 	/* Offset to the start of the IE section in the original frame for the
1661 	 * reporting STA.
1662 	 */
1663 	qdf_size_t frame_iesection_offset;
1664 	/* Total length of the IE section in the original frame for the
1665 	 * reporting STA.
1666 	 */
1667 	qdf_size_t frame_iesection_len;
1668 
1669 	/* Pointer to the IEEE802.11 frame header in the link specific frame
1670 	 * being generated for the reported STA.
1671 	 */
1672 	struct wlan_frame_hdr *link_frame_hdr;
1673 	/* Current position in the link specific frame being generated for the
1674 	 * reported STA.
1675 	 */
1676 	uint8_t *link_frame_currpos;
1677 	/* Current length of the link specific frame being generated for the
1678 	 * reported STA.
1679 	 */
1680 	qdf_size_t link_frame_currlen;
1681 
1682 	/* Pointer to IE for reporting STA */
1683 	const uint8_t *reportingsta_ie;
1684 	/* Total size of IE for reporting STA, inclusive of the element header
1685 	 */
1686 	qdf_size_t reportingsta_ie_size;
1687 
1688 	/* Pointer to current position in STA profile */
1689 	uint8_t *sta_prof_currpos;
1690 	/* Remaining length of STA profile */
1691 	qdf_size_t sta_prof_remlen;
1692 	/* Pointer to start of IE section in STA profile that occurs after fixed
1693 	 * fields.
1694 	 */
1695 	uint8_t *sta_prof_iesection;
1696 	/* Total length of IE section in STA profile */
1697 	qdf_size_t sta_prof_iesection_len;
1698 	/* Pointer to current position being processed in IE section in STA
1699 	 * profile.
1700 	 */
1701 	uint8_t *sta_prof_iesection_currpos;
1702 	/* Remaining length of IE section in STA profile */
1703 	qdf_size_t sta_prof_iesection_remlen;
1704 
1705 	/* Pointer to IE in STA profile, that occurs within IE section */
1706 	uint8_t *sta_prof_ie;
1707 	/* Total size of IE in STA profile, inclusive of the element header */
1708 	qdf_size_t sta_prof_ie_size;
1709 
1710 	/* Pointer to element ID list in Non-Inheritance IE */
1711 	uint8_t *ninherit_elemlist;
1712 	/* Length of element ID list in Non-Inheritance IE */
1713 	qdf_size_t ninherit_elemlist_len;
1714 	/* Pointer to element ID extension list in Non-Inheritance IE */
1715 	uint8_t *ninherit_elemextlist;
1716 	/* Length of element ID extension list in Non-Inheritance IE */
1717 	qdf_size_t ninherit_elemextlist_len;
1718 	/* Whether a given IE is in a non-inheritance list */
1719 	bool is_in_noninheritlist;
1720 
1721 	/* Whether MAC address of reported STA is valid */
1722 	bool is_reportedmacaddr_valid;
1723 	/* MAC address of reported STA */
1724 	struct qdf_mac_addr reportedmacaddr;
1725 
1726 	/* Pointer to per-STA profile */
1727 	uint8_t *persta_prof;
1728 	/* Length of the containing buffer which starts with the per-STA profile
1729 	 */
1730 	qdf_size_t persta_prof_bufflen;
1731 
1732 	/* Other variables for temporary purposes */
1733 
1734 	/* Variable into which API for determining fragment information will
1735 	 * indicate whether the element is the start of a fragment sequence or
1736 	 * not.
1737 	 */
1738 	bool is_elemfragseq;
1739 	/*  De-fragmented payload length returned by API for element
1740 	 *  defragmentation.
1741 	 */
1742 	qdf_size_t defragpayload_len;
1743 	/* Variable into which API for determining fragment information will
1744 	 * indicate whether the subelement is the start of a fragment sequence
1745 	 * or not.
1746 	 */
1747 	bool is_subelemfragseq;
1748 	/* Total length of the subelement fragment sequence, inclusive of
1749 	 * subelement header and the headers of fragments if any.
1750 	 */
1751 	qdf_size_t subelemseqtotallen;
1752 	/* Total length of the subelement fragment sequence payload, excluding
1753 	 * subelement header and fragment headers if any.
1754 	 */
1755 	qdf_size_t subelemseqpayloadlen;
1756 	/* Pointer to Beacon interval in STA info field */
1757 	uint16_t beaconinterval;
1758 	/* Whether Beacon interval value valid */
1759 	bool is_beaconinterval_valid;
1760 	/* TSF timer of the reporting AP */
1761 	uint64_t tsf;
1762 	/* TSF offset of the reproted AP */
1763 	uint64_t tsfoffset;
1764 	/* TSF offset value valid */
1765 	bool is_tsfoffset_valid;
1766 	/* If Complete Profile or not*/
1767 	bool is_completeprofile;
1768 	qdf_size_t tmplen;
1769 	QDF_STATUS ret;
1770 	uint8_t linkid = 0xFF;
1771 
1772 	if (!frame) {
1773 		mlo_err("Pointer to original frame is NULL");
1774 		return QDF_STATUS_E_NULL_VALUE;
1775 	}
1776 
1777 	if (!frame_len) {
1778 		mlo_err("Length of original frame is zero");
1779 		return QDF_STATUS_E_INVAL;
1780 	}
1781 
1782 	if ((subtype != WLAN_FC0_STYPE_ASSOC_REQ) &&
1783 	    (subtype != WLAN_FC0_STYPE_REASSOC_REQ) &&
1784 	    (subtype != WLAN_FC0_STYPE_ASSOC_RESP) &&
1785 	    (subtype != WLAN_FC0_STYPE_REASSOC_RESP) &&
1786 	    (subtype != WLAN_FC0_STYPE_PROBE_RESP)) {
1787 		mlo_err("802.11 frame subtype %u is invalid", subtype);
1788 		return QDF_STATUS_E_INVAL;
1789 	}
1790 
1791 	if (!link_frame) {
1792 		mlo_err("Pointer to secondary link specific frame is NULL");
1793 		return QDF_STATUS_E_NULL_VALUE;
1794 	}
1795 
1796 	if (!link_frame_maxsize) {
1797 		mlo_err("Maximum size of secondary link specific frame is zero");
1798 		return QDF_STATUS_E_INVAL;
1799 	}
1800 
1801 	if (!link_frame_len) {
1802 		mlo_err("Pointer to populated length of secondary link specific frame is NULL");
1803 		return QDF_STATUS_E_NULL_VALUE;
1804 	}
1805 
1806 	frame_iesection_offset = 0;
1807 
1808 	if (subtype == WLAN_FC0_STYPE_ASSOC_REQ) {
1809 		frame_iesection_offset = WLAN_ASSOC_REQ_IES_OFFSET;
1810 	} else if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
1811 		frame_iesection_offset = WLAN_REASSOC_REQ_IES_OFFSET;
1812 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
1813 		frame_iesection_offset = WLAN_PROBE_RESP_IES_OFFSET;
1814 		qdf_mem_copy(&tsf, frame, WLAN_TIMESTAMP_LEN);
1815 		tsf = qdf_le64_to_cpu(tsf);
1816 	} else {
1817 		/* This is a (re)association response */
1818 		frame_iesection_offset = WLAN_ASSOC_RSP_IES_OFFSET;
1819 	}
1820 
1821 	if (frame_len < frame_iesection_offset) {
1822 		/* The caller is supposed to have confirmed that this is a valid
1823 		 * frame containing a Multi-Link element. Hence we treat this as
1824 		 * a case of invalid argument being passed to us.
1825 		 */
1826 		mlo_err("Frame length %zu is smaller than the IE section offset %zu for subtype %u",
1827 			frame_len, frame_iesection_offset, subtype);
1828 		return QDF_STATUS_E_INVAL;
1829 	}
1830 
1831 	frame_iesection_len = frame_len - frame_iesection_offset;
1832 
1833 	if (frame_iesection_len == 0) {
1834 		/* The caller is supposed to have confirmed that this is a valid
1835 		 * frame containing a Multi-Link element. Hence we treat this as
1836 		 * a case of invalid argument being passed to us.
1837 		 */
1838 		mlo_err("No space left in frame for IE section");
1839 		return QDF_STATUS_E_INVAL;
1840 	}
1841 
1842 	frame_iesection = frame + frame_iesection_offset;
1843 
1844 	mlieseq = NULL;
1845 	mlieseqlen = 0;
1846 
1847 	ret = util_find_mlie(frame_iesection, frame_iesection_len, &mlieseq,
1848 			     &mlieseqlen);
1849 	if (QDF_IS_STATUS_ERROR(ret))
1850 		return ret;
1851 
1852 	if (!mlieseq) {
1853 		/* The caller is supposed to have confirmed that a Multi-Link
1854 		 * element is present in the frame. Hence we treat this as a
1855 		 * case of invalid argument being passed to us.
1856 		 */
1857 		mlo_err("Invalid original frame since no Multi-Link element found");
1858 		return QDF_STATUS_E_INVAL;
1859 	}
1860 
1861 	/* Sanity check the Multi-Link element sequence length */
1862 	if (!mlieseqlen) {
1863 		mlo_err("Length of Multi-Link element sequence is zero. Investigate.");
1864 		return QDF_STATUS_E_FAILURE;
1865 	}
1866 
1867 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
1868 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
1869 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
1870 		return QDF_STATUS_E_PROTO;
1871 	}
1872 
1873 	ret = util_get_mlie_variant(mlieseq, mlieseqlen, (int *)&variant);
1874 	if (QDF_IS_STATUS_ERROR(ret))
1875 		return ret;
1876 
1877 	if (variant != WLAN_ML_VARIANT_BASIC) {
1878 		mlo_err_rl("Unexpected variant %u of Multi-Link element.",
1879 			   variant);
1880 		return QDF_STATUS_E_PROTO;
1881 	}
1882 
1883 	mlieseqpayloadlen = 0;
1884 	tmplen = 0;
1885 	is_elemfragseq = false;
1886 
1887 	ret = wlan_get_elem_fragseq_info(mlieseq,
1888 					 mlieseqlen,
1889 					 &is_elemfragseq,
1890 					 &tmplen,
1891 					 &mlieseqpayloadlen);
1892 	if (QDF_IS_STATUS_ERROR(ret))
1893 		return ret;
1894 
1895 	if (is_elemfragseq) {
1896 		if (tmplen != mlieseqlen) {
1897 			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",
1898 				   tmplen, mlieseqlen);
1899 			return QDF_STATUS_E_FAILURE;
1900 		}
1901 
1902 		if (!mlieseqpayloadlen) {
1903 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
1904 			return QDF_STATUS_E_FAILURE;
1905 		}
1906 
1907 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
1908 			  mlieseqpayloadlen);
1909 	} else {
1910 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
1911 			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",
1912 				   mlieseqlen,
1913 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
1914 			return QDF_STATUS_E_FAILURE;
1915 		}
1916 
1917 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
1918 	}
1919 
1920 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
1921 
1922 	if (!mlieseqpayload_copy) {
1923 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
1924 		return QDF_STATUS_E_NOMEM;
1925 	}
1926 
1927 	if (is_elemfragseq) {
1928 		ret = wlan_defrag_elem_fragseq(false,
1929 					       mlieseq,
1930 					       mlieseqlen,
1931 					       mlieseqpayload_copy,
1932 					       mlieseqpayloadlen,
1933 					       &defragpayload_len);
1934 		if (QDF_IS_STATUS_ERROR(ret)) {
1935 			qdf_mem_free(mlieseqpayload_copy);
1936 			return ret;
1937 		}
1938 
1939 		if (defragpayload_len != mlieseqpayloadlen) {
1940 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
1941 				   defragpayload_len, mlieseqpayloadlen);
1942 			qdf_mem_free(mlieseqpayload_copy);
1943 			return QDF_STATUS_E_FAILURE;
1944 		}
1945 	} else {
1946 		qdf_mem_copy(mlieseqpayload_copy,
1947 			     mlieseq + sizeof(struct ie_header) + 1,
1948 			     mlieseqpayloadlen);
1949 	}
1950 
1951 	link_info = NULL;
1952 	link_info_len = 0;
1953 
1954 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
1955 					 mlieseqpayloadlen,
1956 					 &link_info,
1957 					 &link_info_len);
1958 	if (QDF_IS_STATUS_ERROR(ret)) {
1959 		qdf_mem_free(mlieseqpayload_copy);
1960 		return ret;
1961 	}
1962 
1963 	/* As per the standard, the sender must include Link Info for
1964 	 * association request/response. Throw an error if we are unable to
1965 	 * obtain this.
1966 	 */
1967 	if (!link_info) {
1968 		mlo_err_rl("Unable to successfully obtain Link Info");
1969 		qdf_mem_free(mlieseqpayload_copy);
1970 		return QDF_STATUS_E_PROTO;
1971 	}
1972 
1973 	mlo_debug("Dumping hex of link info after parsing Multi-Link element control");
1974 	QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_MLO, QDF_TRACE_LEVEL_DEBUG,
1975 			   link_info, link_info_len);
1976 
1977 	/* Note: We may have a future change to skip subelements which are not
1978 	 * Per-STA Profile, handle more than two links in MLO, handle cases
1979 	 * where we unexpectedly find more Per-STA Profiles than expected, etc.
1980 	 */
1981 
1982 	persta_prof = link_info;
1983 	persta_prof_bufflen = link_info_len;
1984 
1985 	is_subelemfragseq = false;
1986 	subelemseqtotallen = 0;
1987 	subelemseqpayloadlen = 0;
1988 
1989 	ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
1990 					    persta_prof,
1991 					    persta_prof_bufflen,
1992 					    &is_subelemfragseq,
1993 					    &subelemseqtotallen,
1994 					    &subelemseqpayloadlen);
1995 	if (QDF_IS_STATUS_ERROR(ret)) {
1996 		qdf_mem_free(mlieseqpayload_copy);
1997 		return ret;
1998 	}
1999 
2000 	if (is_subelemfragseq) {
2001 		if (!subelemseqpayloadlen) {
2002 			mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
2003 			qdf_mem_free(mlieseqpayload_copy);
2004 			return QDF_STATUS_E_FAILURE;
2005 		}
2006 
2007 		mlo_debug("Subelement fragment sequence found with payload len %zu",
2008 			  subelemseqpayloadlen);
2009 
2010 		ret = wlan_defrag_subelem_fragseq(true,
2011 						  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
2012 						  persta_prof,
2013 						  persta_prof_bufflen,
2014 						  NULL,
2015 						  0,
2016 						  &defragpayload_len);
2017 		if (QDF_IS_STATUS_ERROR(ret)) {
2018 			qdf_mem_free(mlieseqpayload_copy);
2019 			return ret;
2020 		}
2021 
2022 		if (defragpayload_len != subelemseqpayloadlen) {
2023 			mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
2024 				   defragpayload_len,
2025 				   subelemseqpayloadlen);
2026 			qdf_mem_free(mlieseqpayload_copy);
2027 			return QDF_STATUS_E_FAILURE;
2028 		}
2029 	} else {
2030 		if (persta_prof_bufflen <
2031 			(sizeof(struct subelem_header) +
2032 			 persta_prof[TAG_LEN_POS])) {
2033 			mlo_err_rl("Length of buffer containing per-STA profile %zu octets is smaller than total size of current subelement %zu octets",
2034 				   persta_prof_bufflen,
2035 				   sizeof(struct subelem_header) +
2036 						persta_prof[TAG_LEN_POS]);
2037 			return QDF_STATUS_E_PROTO;
2038 		}
2039 
2040 		subelemseqpayloadlen = persta_prof[TAG_LEN_POS];
2041 	}
2042 
2043 	sta_prof_remlen = 0;
2044 	sta_prof_currpos = NULL;
2045 	is_reportedmacaddr_valid = false;
2046 	is_beaconinterval_valid = false;
2047 	is_completeprofile = false;
2048 	is_tsfoffset_valid = false;
2049 
2050 	/* Parse per-STA profile */
2051 	ret = util_parse_bvmlie_perstaprofile_stactrl(persta_prof +
2052 						      sizeof(struct subelem_header),
2053 						      subelemseqpayloadlen,
2054 						      &linkid,
2055 						      &beaconinterval,
2056 						      &is_beaconinterval_valid,
2057 						      &tsfoffset,
2058 						      &is_tsfoffset_valid,
2059 						      &is_completeprofile,
2060 						      &is_reportedmacaddr_valid,
2061 						      &reportedmacaddr,
2062 						      true,
2063 						      &sta_prof_currpos,
2064 						      &sta_prof_remlen);
2065 	if (QDF_IS_STATUS_ERROR(ret)) {
2066 		qdf_mem_free(mlieseqpayload_copy);
2067 		return ret;
2068 	}
2069 
2070 	if (subtype == WLAN_FC0_STYPE_PROBE_RESP && !is_completeprofile) {
2071 		mlo_err("Complete profile information is not present in per-STA profile of probe response frame");
2072 		return QDF_STATUS_E_NOSUPPORT;
2073 	}
2074 
2075 	/* We double check for a NULL STA Profile, though the helper function
2076 	 * above would have taken care of this. We need to get a non-NULL STA
2077 	 * profile, because we need to get at least the expected fixed fields,
2078 	 * even if there is an (improbable) total inheritance.
2079 	 */
2080 	if (!sta_prof_currpos) {
2081 		mlo_err_rl("STA profile is NULL");
2082 		qdf_mem_free(mlieseqpayload_copy);
2083 		return QDF_STATUS_E_PROTO;
2084 	}
2085 
2086 	/* As per the standard, the sender sets the MAC address in the per-STA
2087 	 * profile in association request/response. Without this, we cannot
2088 	 * generate the link specific frame.
2089 	 */
2090 	if (!is_reportedmacaddr_valid) {
2091 		mlo_err_rl("Unable to get MAC address from per-STA profile");
2092 		qdf_mem_free(mlieseqpayload_copy);
2093 		return QDF_STATUS_E_PROTO;
2094 	}
2095 
2096 	link_frame_currpos = link_frame;
2097 	*link_frame_len = 0;
2098 	link_frame_currlen = 0;
2099 
2100 	if (link_frame_maxsize < WLAN_MAC_HDR_LEN_3A) {
2101 		mlo_err("Insufficient space in link specific frame for 802.11 header. Required: %u octets, available: %zu octets",
2102 			WLAN_MAC_HDR_LEN_3A, link_frame_maxsize);
2103 
2104 		qdf_mem_free(mlieseqpayload_copy);
2105 		return QDF_STATUS_E_NOMEM;
2106 	}
2107 
2108 	link_frame_currpos += WLAN_MAC_HDR_LEN_3A;
2109 	link_frame_currlen += WLAN_MAC_HDR_LEN_3A;
2110 
2111 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2112 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
2113 		mlo_debug("Populating fixed fields for (re)assoc req in link specific frame");
2114 
2115 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
2116 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
2117 				   sta_prof_remlen,
2118 				   WLAN_CAPABILITYINFO_LEN);
2119 
2120 			qdf_mem_free(mlieseqpayload_copy);
2121 			return QDF_STATUS_E_PROTO;
2122 		}
2123 
2124 		/* Capability information is specific to the link. Copy this
2125 		 * from the STA profile.
2126 		 */
2127 
2128 		if ((link_frame_maxsize - link_frame_currlen) <
2129 				WLAN_CAPABILITYINFO_LEN) {
2130 			mlo_err("Insufficient space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
2131 				WLAN_CAPABILITYINFO_LEN,
2132 				(link_frame_maxsize - link_frame_currlen));
2133 
2134 			qdf_mem_free(mlieseqpayload_copy);
2135 			return QDF_STATUS_E_NOMEM;
2136 		}
2137 
2138 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2139 			     WLAN_CAPABILITYINFO_LEN);
2140 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2141 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2142 		mlo_debug("Added Capability Info field (%u octets) to link specific frame",
2143 			  WLAN_CAPABILITYINFO_LEN);
2144 
2145 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2146 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2147 
2148 		/* Listen Interval is common between all links. Copy this from
2149 		 * the reporting section of the frame.
2150 		 */
2151 
2152 		if ((link_frame_maxsize - link_frame_currlen) <
2153 				WLAN_LISTENINTERVAL_LEN) {
2154 			mlo_err("Insufficient space in link specific frame for Listen Interval field. Required: %u octets, available: %zu octets",
2155 				WLAN_LISTENINTERVAL_LEN,
2156 				(link_frame_maxsize - link_frame_currlen));
2157 
2158 			qdf_mem_free(mlieseqpayload_copy);
2159 			return QDF_STATUS_E_NOMEM;
2160 		}
2161 
2162 		qdf_mem_copy(link_frame_currpos,
2163 			     frame + WLAN_CAPABILITYINFO_LEN,
2164 			     WLAN_LISTENINTERVAL_LEN);
2165 		link_frame_currpos += WLAN_LISTENINTERVAL_LEN;
2166 		link_frame_currlen += WLAN_LISTENINTERVAL_LEN;
2167 		mlo_debug("Added Listen Interval field (%u octets) to link specific frame",
2168 			  WLAN_LISTENINTERVAL_LEN);
2169 
2170 		if (subtype == WLAN_FC0_STYPE_REASSOC_REQ) {
2171 			/* Current AP address is common between all links. Copy
2172 			 * this from the reporting section of the frame.
2173 			 */
2174 			if ((link_frame_maxsize - link_frame_currlen) <
2175 				QDF_MAC_ADDR_SIZE) {
2176 				mlo_err("Insufficient space in link specific frame for current AP address. Required: %u octets, available: %zu octets",
2177 					QDF_MAC_ADDR_SIZE,
2178 					(link_frame_maxsize -
2179 						link_frame_currlen));
2180 
2181 				qdf_mem_free(mlieseqpayload_copy);
2182 				return QDF_STATUS_E_NOMEM;
2183 			}
2184 
2185 			qdf_mem_copy(link_frame_currpos,
2186 				     frame + WLAN_CAPABILITYINFO_LEN +
2187 						WLAN_LISTENINTERVAL_LEN,
2188 				     QDF_MAC_ADDR_SIZE);
2189 			link_frame_currpos += QDF_MAC_ADDR_SIZE;
2190 			link_frame_currlen += QDF_MAC_ADDR_SIZE;
2191 			mlo_debug("Reassoc req: Added Current AP address field (%u octets) to link specific frame",
2192 				  QDF_MAC_ADDR_SIZE);
2193 		}
2194 	} else if (subtype == WLAN_FC0_STYPE_ASSOC_RESP ||
2195 		   subtype == WLAN_FC0_STYPE_REASSOC_RESP) {
2196 		/* This is a (re)association response */
2197 		mlo_debug("Populating fixed fields for (re)assoc resp in link specific frame");
2198 
2199 		if (sta_prof_remlen <
2200 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2201 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info + length of Status Code %u",
2202 				   sta_prof_remlen,
2203 				   WLAN_CAPABILITYINFO_LEN +
2204 					WLAN_STATUSCODE_LEN);
2205 
2206 			qdf_mem_free(mlieseqpayload_copy);
2207 			return QDF_STATUS_E_PROTO;
2208 		}
2209 
2210 		/* Capability information and Status Code are specific to the
2211 		 * link. Copy these from the STA profile.
2212 		 */
2213 
2214 		if ((link_frame_maxsize - link_frame_currlen) <
2215 			(WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN)) {
2216 			mlo_err("Insufficient space in link specific frame for Capability Info and Status Code fields. Required: %u octets, available: %zu octets",
2217 				WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN,
2218 				(link_frame_maxsize - link_frame_currlen));
2219 
2220 			qdf_mem_free(mlieseqpayload_copy);
2221 			return QDF_STATUS_E_NOMEM;
2222 		}
2223 
2224 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2225 			     (WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN));
2226 		link_frame_currpos += (WLAN_CAPABILITYINFO_LEN +
2227 						WLAN_STATUSCODE_LEN);
2228 		link_frame_currlen += (WLAN_CAPABILITYINFO_LEN +
2229 				WLAN_STATUSCODE_LEN);
2230 		mlo_debug("Added Capability Info and Status Code fields (%u octets) to link specific frame",
2231 			  WLAN_CAPABILITYINFO_LEN + WLAN_STATUSCODE_LEN);
2232 
2233 		sta_prof_currpos += (WLAN_CAPABILITYINFO_LEN +
2234 				WLAN_STATUSCODE_LEN);
2235 		sta_prof_remlen -= (WLAN_CAPABILITYINFO_LEN +
2236 				WLAN_STATUSCODE_LEN);
2237 
2238 		/* AID is common between all links. Copy this from the original
2239 		 * frame.
2240 		 */
2241 
2242 		if ((link_frame_maxsize - link_frame_currlen) < WLAN_AID_LEN) {
2243 			mlo_err("Insufficient space in link specific frame for AID field. Required: %u octets, available: %zu octets",
2244 				WLAN_AID_LEN,
2245 				(link_frame_maxsize - link_frame_currlen));
2246 
2247 			qdf_mem_free(mlieseqpayload_copy);
2248 			return QDF_STATUS_E_NOMEM;
2249 		}
2250 
2251 		qdf_mem_copy(link_frame_currpos,
2252 			     frame + WLAN_CAPABILITYINFO_LEN +
2253 					WLAN_STATUSCODE_LEN,
2254 			     WLAN_AID_LEN);
2255 		link_frame_currpos += WLAN_AID_LEN;
2256 		link_frame_currlen += WLAN_AID_LEN;
2257 		mlo_debug("Added AID field (%u octets) to link specific frame",
2258 			  WLAN_AID_LEN);
2259 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2260 		/* This is a probe response */
2261 		mlo_debug("Populating fixed fields for probe response in link specific frame");
2262 
2263 		if ((link_frame_maxsize - link_frame_currlen) <
2264 				WLAN_TIMESTAMP_LEN) {
2265 			mlo_err("Insufficient space in link specific frame for Timestamp Info field. Required: %u octets, available: %zu octets",
2266 				WLAN_TIMESTAMP_LEN,
2267 				(link_frame_maxsize - link_frame_currlen));
2268 
2269 			qdf_mem_free(mlieseqpayload_copy);
2270 			return QDF_STATUS_E_NOMEM;
2271 		}
2272 
2273 		/* Per spec 11be_D2.1.1, the TSF Offset subfield of the STA Info
2274 		 * field indicates the offset (Toffset)between the TSF timer of
2275 		 * the reported AP (TA) and the TSF timer of the reporting
2276 		 * AP (TB) and is encoded as a 2s complement signed integer
2277 		 * with units of 2 µs. Toffset is calculated as
2278 		 * Toffset= Floor((TA – TB)/2).
2279 		 */
2280 		if (is_tsfoffset_valid)
2281 			tsf += tsfoffset * 2;
2282 
2283 		qdf_mem_copy(link_frame_currpos, &tsf, WLAN_TIMESTAMP_LEN);
2284 		link_frame_currpos += WLAN_TIMESTAMP_LEN;
2285 		link_frame_currlen += WLAN_TIMESTAMP_LEN;
2286 		mlo_debug("Added Timestamp Info field (%u octets) to link specific frame",
2287 			  WLAN_TIMESTAMP_LEN);
2288 
2289 		if (!is_beaconinterval_valid) {
2290 			mlo_err_rl("Beacon interval information not present in STA info field of per-STA profile");
2291 			qdf_mem_free(mlieseqpayload_copy);
2292 			return QDF_STATUS_E_PROTO;
2293 		}
2294 
2295 		/* Beacon Interval information copy this from
2296 		 * the STA info field.
2297 		 */
2298 		if ((link_frame_maxsize - link_frame_currlen) <
2299 				WLAN_BEACONINTERVAL_LEN) {
2300 			mlo_err("Insufficient space in link specific frame for Beacon Interval Info field. Required: %u octets, available: %zu octets",
2301 				WLAN_BEACONINTERVAL_LEN,
2302 				(link_frame_maxsize - link_frame_currlen));
2303 
2304 			qdf_mem_free(mlieseqpayload_copy);
2305 			return QDF_STATUS_E_NOMEM;
2306 		}
2307 
2308 		qdf_mem_copy(link_frame_currpos, &beaconinterval,
2309 			     WLAN_BEACONINTERVAL_LEN);
2310 		link_frame_currpos += WLAN_BEACONINTERVAL_LEN;
2311 		link_frame_currlen += WLAN_BEACONINTERVAL_LEN;
2312 		mlo_debug("Added Beacon Interval Info field (%u octets) to link specific frame",
2313 			  WLAN_BEACONINTERVAL_LEN);
2314 
2315 		if (sta_prof_remlen < WLAN_CAPABILITYINFO_LEN) {
2316 			mlo_err_rl("Remaining length of STA profile %zu octets is less than length of Capability Info %u",
2317 				   sta_prof_remlen,
2318 				   WLAN_CAPABILITYINFO_LEN);
2319 
2320 			qdf_mem_free(mlieseqpayload_copy);
2321 			return QDF_STATUS_E_PROTO;
2322 		}
2323 
2324 		/* Capability information is specific to the link. Copy this
2325 		 * from the STA profile.
2326 		 */
2327 
2328 		if ((link_frame_maxsize - link_frame_currlen) <
2329 				WLAN_CAPABILITYINFO_LEN) {
2330 			mlo_err("Insufficient space in link specific frame for Capability Info field. Required: %u octets, available: %zu octets",
2331 				WLAN_CAPABILITYINFO_LEN,
2332 				(link_frame_maxsize - link_frame_currlen));
2333 
2334 			qdf_mem_free(mlieseqpayload_copy);
2335 			return QDF_STATUS_E_NOMEM;
2336 		}
2337 
2338 		qdf_mem_copy(link_frame_currpos, sta_prof_currpos,
2339 			     WLAN_CAPABILITYINFO_LEN);
2340 		link_frame_currpos += WLAN_CAPABILITYINFO_LEN;
2341 		link_frame_currlen += WLAN_CAPABILITYINFO_LEN;
2342 		mlo_debug("Added Capability Info field (%u octets) to link specific frame",
2343 			  WLAN_CAPABILITYINFO_LEN);
2344 
2345 		sta_prof_currpos += WLAN_CAPABILITYINFO_LEN;
2346 		sta_prof_remlen -= WLAN_CAPABILITYINFO_LEN;
2347 	}
2348 
2349 	sta_prof_iesection = sta_prof_currpos;
2350 	sta_prof_iesection_len = sta_prof_remlen;
2351 
2352 	/* Populate non-inheritance lists if applicable */
2353 	ninherit_elemlist_len = 0;
2354 	ninherit_elemlist = NULL;
2355 	ninherit_elemextlist_len = 0;
2356 	ninherit_elemextlist = NULL;
2357 
2358 	ret = util_get_noninheritlists(sta_prof_iesection,
2359 				       sta_prof_iesection_len,
2360 				       &ninherit_elemlist,
2361 				       &ninherit_elemlist_len,
2362 				       &ninherit_elemextlist,
2363 				       &ninherit_elemextlist_len);
2364 	if (QDF_IS_STATUS_ERROR(ret)) {
2365 		qdf_mem_free(mlieseqpayload_copy);
2366 		return ret;
2367 	}
2368 
2369 	/* Go through IEs of the reporting STA, and those in STA profile, merge
2370 	 * them into link_frame (except for elements in the Non-Inheritance
2371 	 * list).
2372 	 *
2373 	 * Note: Currently, only 2-link MLO is supported here. We may have a
2374 	 * future change to expand to more links.
2375 	 */
2376 	reportingsta_ie = util_find_eid(WLAN_ELEMID_SSID, frame_iesection,
2377 					frame_iesection_len);
2378 
2379 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2380 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ) ||
2381 	    (subtype == WLAN_FC0_STYPE_PROBE_RESP)) {
2382 		/* Sanity check that the SSID element is present for the
2383 		 * reporting STA. There is no stipulation in the standard for
2384 		 * the STA profile in this regard, so we do not check the STA
2385 		 * profile for the SSID element.
2386 		 */
2387 		if (!reportingsta_ie) {
2388 			mlo_err_rl("SSID element not found in reporting STA of the frame.");
2389 			qdf_mem_free(mlieseqpayload_copy);
2390 			return QDF_STATUS_E_PROTO;
2391 		}
2392 	} else {
2393 		/* This is a (re)association response. Sanity check that the
2394 		 * SSID element is present neither for the reporting STA nor in
2395 		 * the STA profile.
2396 		 */
2397 		if (reportingsta_ie) {
2398 			mlo_err_rl("SSID element found for reporting STA for (re)association response. It should not be present.");
2399 			qdf_mem_free(mlieseqpayload_copy);
2400 			return QDF_STATUS_E_PROTO;
2401 		}
2402 
2403 		sta_prof_ie = util_find_eid(WLAN_ELEMID_SSID,
2404 					    sta_prof_iesection,
2405 					    sta_prof_iesection_len);
2406 
2407 		if (sta_prof_ie) {
2408 			mlo_err_rl("SSID element found in STA profile for (re)association response. It should not be present.");
2409 			qdf_mem_free(mlieseqpayload_copy);
2410 			return QDF_STATUS_E_PROTO;
2411 		}
2412 	}
2413 
2414 	reportingsta_ie = reportingsta_ie ? reportingsta_ie : frame_iesection;
2415 
2416 	ret = util_validate_reportingsta_ie(reportingsta_ie, frame_iesection,
2417 					    frame_iesection_len);
2418 	if (QDF_IS_STATUS_ERROR(ret)) {
2419 		qdf_mem_free(mlieseqpayload_copy);
2420 		return ret;
2421 	}
2422 
2423 	reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] + MIN_IE_LEN;
2424 
2425 	while (((reportingsta_ie + reportingsta_ie_size) - frame_iesection)
2426 			<= frame_iesection_len) {
2427 		/* Skip Multi-Link element */
2428 		if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) &&
2429 		    (reportingsta_ie[IDEXT_POS] ==
2430 				WLAN_EXTN_ELEMID_MULTI_LINK)) {
2431 			if (((reportingsta_ie + reportingsta_ie_size) -
2432 					frame_iesection) == frame_iesection_len)
2433 				break;
2434 
2435 			/* Add BV ML IE for link specific probe response */
2436 			if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2437 				ret = util_add_mlie_for_prb_rsp_gen(
2438 					reportingsta_ie,
2439 					reportingsta_ie[TAG_LEN_POS],
2440 					&link_frame_currpos,
2441 					&link_frame_currlen,
2442 					link_frame_maxsize,
2443 					linkid);
2444 				if (QDF_IS_STATUS_ERROR(ret)) {
2445 					qdf_mem_free(mlieseqpayload_copy);
2446 					return ret;
2447 				}
2448 			}
2449 			reportingsta_ie += reportingsta_ie_size;
2450 
2451 			ret = util_validate_reportingsta_ie(reportingsta_ie,
2452 							    frame_iesection,
2453 							    frame_iesection_len);
2454 			if (QDF_IS_STATUS_ERROR(ret)) {
2455 				qdf_mem_free(mlieseqpayload_copy);
2456 				return ret;
2457 			}
2458 
2459 			reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2460 				MIN_IE_LEN;
2461 
2462 			continue;
2463 		}
2464 
2465 		sta_prof_ie = NULL;
2466 		sta_prof_ie_size = 0;
2467 
2468 		if (sta_prof_iesection_len) {
2469 			if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2470 				sta_prof_ie = (uint8_t *)util_find_extn_eid(reportingsta_ie[ID_POS],
2471 									    reportingsta_ie[IDEXT_POS],
2472 									    sta_prof_iesection,
2473 									    sta_prof_iesection_len);
2474 			} else {
2475 				sta_prof_ie = (uint8_t *)util_find_eid(reportingsta_ie[ID_POS],
2476 								       sta_prof_iesection,
2477 								       sta_prof_iesection_len);
2478 			}
2479 		}
2480 
2481 		if (!sta_prof_ie) {
2482 			/* IE is present for reporting STA, but not in STA
2483 			 * profile.
2484 			 */
2485 
2486 			is_in_noninheritlist = false;
2487 
2488 			ret = util_eval_ie_in_noninheritlist((uint8_t *)reportingsta_ie,
2489 							     reportingsta_ie_size,
2490 							     ninherit_elemlist,
2491 							     ninherit_elemlist_len,
2492 							     ninherit_elemextlist,
2493 							     ninherit_elemextlist_len,
2494 							     &is_in_noninheritlist);
2495 
2496 			if (QDF_IS_STATUS_ERROR(ret)) {
2497 				qdf_mem_free(mlieseqpayload_copy);
2498 				return ret;
2499 			}
2500 
2501 			if (!is_in_noninheritlist) {
2502 				if ((link_frame_currpos +
2503 						reportingsta_ie_size) <=
2504 					(link_frame + link_frame_maxsize)) {
2505 					qdf_mem_copy(link_frame_currpos,
2506 						     reportingsta_ie,
2507 						     reportingsta_ie_size);
2508 
2509 					link_frame_currpos +=
2510 						reportingsta_ie_size;
2511 					link_frame_currlen +=
2512 						reportingsta_ie_size;
2513 
2514 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2515 						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",
2516 							  reportingsta_ie[ID_POS],
2517 							  reportingsta_ie[IDEXT_POS],
2518 							  reportingsta_ie_size);
2519 					} else {
2520 						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",
2521 							  reportingsta_ie[ID_POS],
2522 							  reportingsta_ie_size);
2523 					}
2524 				} else {
2525 					if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2526 						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",
2527 							   reportingsta_ie[ID_POS],
2528 							   reportingsta_ie[IDEXT_POS],
2529 							   reportingsta_ie_size,
2530 							   link_frame_maxsize -
2531 							   link_frame_currlen);
2532 					} else {
2533 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2534 							   reportingsta_ie[ID_POS],
2535 							   reportingsta_ie_size,
2536 							   link_frame_maxsize -
2537 							   link_frame_currlen);
2538 					}
2539 
2540 					qdf_mem_free(mlieseqpayload_copy);
2541 					return QDF_STATUS_E_NOMEM;
2542 				}
2543 			} else {
2544 				if (reportingsta_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2545 					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.",
2546 						  reportingsta_ie[ID_POS],
2547 						  reportingsta_ie[IDEXT_POS],
2548 						  reportingsta_ie_size);
2549 				} else {
2550 					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.",
2551 						  reportingsta_ie[ID_POS],
2552 						  reportingsta_ie_size);
2553 				}
2554 			}
2555 		} else {
2556 			/* IE is present for reporting STA and also in STA
2557 			 * profile, copy from STA profile and flag the IE in STA
2558 			 * profile as copied (by setting EID field to 0). The
2559 			 * SSID element (with EID 0) is processed first to
2560 			 * enable this. For vendor IE, compare OUI + type +
2561 			 * subType to determine if they are the same IE.
2562 			 */
2563 			/* Note: This may be revisited in a future change, to
2564 			 * adhere to provisions in the standard for multiple
2565 			 * occurrences of a given element ID/extension element
2566 			 * ID.
2567 			 */
2568 
2569 			ret = util_validate_sta_prof_ie(sta_prof_ie,
2570 							sta_prof_iesection,
2571 							sta_prof_iesection_len);
2572 			if (QDF_IS_STATUS_ERROR(ret)) {
2573 				qdf_mem_free(mlieseqpayload_copy);
2574 				return ret;
2575 			}
2576 
2577 			sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] +
2578 				MIN_IE_LEN;
2579 
2580 			sta_prof_iesection_remlen =
2581 				sta_prof_iesection_len -
2582 					(sta_prof_ie - sta_prof_iesection);
2583 
2584 			if ((reportingsta_ie[ID_POS] == WLAN_ELEMID_VENDOR) &&
2585 			    (sta_prof_iesection_remlen >= MIN_VENDOR_TAG_LEN)) {
2586 				/* If Vendor IE also presents in STA profile,
2587 				 * then ignore the Vendor IE which is for
2588 				 * reporting STA. It only needs to copy Vendor
2589 				 * IE from STA profile to link specific frame.
2590 				 * The copy happens when going through the
2591 				 * remaining IEs.
2592 				 */
2593 				;
2594 			} else {
2595 				/* Copy IE from STA profile into link specific
2596 				 * frame.
2597 				 */
2598 				if ((link_frame_currpos + sta_prof_ie_size) <=
2599 					(link_frame + link_frame_maxsize)) {
2600 					qdf_mem_copy(link_frame_currpos,
2601 						     sta_prof_ie,
2602 						     sta_prof_ie_size);
2603 
2604 					link_frame_currpos += sta_prof_ie_size;
2605 					link_frame_currlen +=
2606 						sta_prof_ie_size;
2607 
2608 					if (reportingsta_ie[ID_POS] ==
2609 							WLAN_ELEMID_EXTN_ELEM) {
2610 						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",
2611 							  sta_prof_ie[ID_POS],
2612 							  sta_prof_ie[IDEXT_POS],
2613 							  sta_prof_ie_size);
2614 					} else {
2615 						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",
2616 							  sta_prof_ie[ID_POS],
2617 							  sta_prof_ie_size);
2618 					}
2619 
2620 					sta_prof_ie[0] = 0;
2621 				} else {
2622 					if (sta_prof_ie[ID_POS] ==
2623 							WLAN_ELEMID_EXTN_ELEM) {
2624 						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",
2625 							   sta_prof_ie[ID_POS],
2626 							   sta_prof_ie[IDEXT_POS],
2627 							   sta_prof_ie_size,
2628 							   link_frame_maxsize -
2629 							   link_frame_currlen);
2630 					} else {
2631 						mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2632 							   sta_prof_ie[ID_POS],
2633 							   sta_prof_ie_size,
2634 							   link_frame_maxsize -
2635 							   link_frame_currlen);
2636 					}
2637 
2638 					qdf_mem_free(mlieseqpayload_copy);
2639 					return QDF_STATUS_E_NOMEM;
2640 				}
2641 			}
2642 		}
2643 
2644 		if (((reportingsta_ie + reportingsta_ie_size) -
2645 					frame_iesection) == frame_iesection_len)
2646 			break;
2647 
2648 		reportingsta_ie += reportingsta_ie_size;
2649 
2650 		ret = util_validate_reportingsta_ie(reportingsta_ie,
2651 						    frame_iesection,
2652 						    frame_iesection_len);
2653 		if (QDF_IS_STATUS_ERROR(ret)) {
2654 			qdf_mem_free(mlieseqpayload_copy);
2655 			return ret;
2656 		}
2657 
2658 		reportingsta_ie_size = reportingsta_ie[TAG_LEN_POS] +
2659 			MIN_IE_LEN;
2660 	}
2661 
2662 	/* Go through the remaining unprocessed IEs in STA profile and copy them
2663 	 * to the link specific frame. The processed ones are marked with 0 in
2664 	 * the first octet. The first octet corresponds to the element ID. In
2665 	 * the case of (re)association request, the element with actual ID
2666 	 * WLAN_ELEMID_SSID(0) has already been copied to the link specific
2667 	 * frame. In the case of (re)association response, it has been verified
2668 	 * that the element with actual ID WLAN_ELEMID_SSID(0) is present
2669 	 * neither for the reporting STA nor in the STA profile.
2670 	 */
2671 	sta_prof_iesection_currpos = sta_prof_iesection;
2672 	sta_prof_iesection_remlen = sta_prof_iesection_len;
2673 
2674 	while (sta_prof_iesection_remlen > 0) {
2675 		sta_prof_ie = sta_prof_iesection_currpos;
2676 		ret = util_validate_sta_prof_ie(sta_prof_ie,
2677 						sta_prof_iesection_currpos,
2678 						sta_prof_iesection_remlen);
2679 		if (QDF_IS_STATUS_ERROR(ret)) {
2680 			qdf_mem_free(mlieseqpayload_copy);
2681 			return ret;
2682 		}
2683 
2684 		sta_prof_ie_size = sta_prof_ie[TAG_LEN_POS] + MIN_IE_LEN;
2685 
2686 		if (!sta_prof_ie[0]) {
2687 			/* Skip this, since it has already been processed */
2688 			sta_prof_iesection_currpos += sta_prof_ie_size;
2689 			sta_prof_iesection_remlen -= sta_prof_ie_size;
2690 			continue;
2691 		}
2692 
2693 		/* Copy IE from STA profile into link specific frame. */
2694 		if ((link_frame_currpos + sta_prof_ie_size) <=
2695 			(link_frame + link_frame_maxsize)) {
2696 			qdf_mem_copy(link_frame_currpos,
2697 				     sta_prof_ie,
2698 				     sta_prof_ie_size);
2699 
2700 			link_frame_currpos += sta_prof_ie_size;
2701 			link_frame_currlen +=
2702 				sta_prof_ie_size;
2703 
2704 			if (reportingsta_ie[ID_POS] ==
2705 					WLAN_ELEMID_EXTN_ELEM) {
2706 				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",
2707 					  sta_prof_ie[ID_POS],
2708 					  sta_prof_ie[IDEXT_POS],
2709 					  sta_prof_ie_size);
2710 			} else {
2711 				mlo_debug("IE with element ID : %u (%zu octets) is present only in STA profile. Copied IE from STA profile to link specific frame",
2712 					  sta_prof_ie[ID_POS],
2713 					  sta_prof_ie_size);
2714 			}
2715 
2716 			sta_prof_ie[0] = 0;
2717 		} else {
2718 			if (sta_prof_ie[ID_POS] == WLAN_ELEMID_EXTN_ELEM) {
2719 				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",
2720 					   sta_prof_ie[ID_POS],
2721 					   sta_prof_ie[IDEXT_POS],
2722 					   sta_prof_ie_size,
2723 					   link_frame_maxsize -
2724 					   link_frame_currlen);
2725 			} else {
2726 				mlo_err_rl("Insufficient space in link specific frame for IE with element ID : %u. Required: %zu octets, available: %zu octets",
2727 					   sta_prof_ie[ID_POS],
2728 					   sta_prof_ie_size,
2729 					   link_frame_maxsize -
2730 					   link_frame_currlen);
2731 			}
2732 
2733 			qdf_mem_free(mlieseqpayload_copy);
2734 			return QDF_STATUS_E_NOMEM;
2735 		}
2736 
2737 		sta_prof_iesection_currpos += sta_prof_ie_size;
2738 		sta_prof_iesection_remlen -= sta_prof_ie_size;
2739 	}
2740 
2741 	/* Copy the link MAC addr */
2742 	link_frame_hdr = (struct wlan_frame_hdr *)link_frame;
2743 
2744 	if ((subtype == WLAN_FC0_STYPE_ASSOC_REQ) ||
2745 	    (subtype == WLAN_FC0_STYPE_REASSOC_REQ)) {
2746 		qdf_mem_copy(link_frame_hdr->i_addr3, &link_addr,
2747 			     QDF_MAC_ADDR_SIZE);
2748 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2749 			     QDF_MAC_ADDR_SIZE);
2750 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2751 			     QDF_MAC_ADDR_SIZE);
2752 
2753 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_REQ_FC0;
2754 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_REQ_FC1;
2755 	} else if (subtype == WLAN_FC0_STYPE_PROBE_RESP) {
2756 		qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
2757 			     QDF_MAC_ADDR_SIZE);
2758 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2759 			     QDF_MAC_ADDR_SIZE);
2760 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2761 			     QDF_MAC_ADDR_SIZE);
2762 
2763 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_PROBE_RESP_FC0;
2764 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_PROBE_RESP_FC1;
2765 	} else {
2766 		/* This is a (re)association response */
2767 
2768 		qdf_mem_copy(link_frame_hdr->i_addr3, reportedmacaddr.bytes,
2769 			     QDF_MAC_ADDR_SIZE);
2770 		qdf_mem_copy(link_frame_hdr->i_addr2, reportedmacaddr.bytes,
2771 			     QDF_MAC_ADDR_SIZE);
2772 		qdf_mem_copy(link_frame_hdr->i_addr1, &link_addr,
2773 			     QDF_MAC_ADDR_SIZE);
2774 
2775 		link_frame_hdr->i_fc[0] = MLO_LINKSPECIFIC_ASSOC_RESP_FC0;
2776 		link_frame_hdr->i_fc[1] = MLO_LINKSPECIFIC_ASSOC_RESP_FC1;
2777 	}
2778 
2779 	mlo_debug("subtype:%u addr3:" QDF_MAC_ADDR_FMT " addr2:"
2780 		  QDF_MAC_ADDR_FMT " addr1:" QDF_MAC_ADDR_FMT,
2781 		  subtype,
2782 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr3),
2783 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr2),
2784 		  QDF_MAC_ADDR_REF(link_frame_hdr->i_addr1));
2785 
2786 	/* Seq num not used so not populated */
2787 
2788 	qdf_mem_free(mlieseqpayload_copy);
2789 
2790 	*link_frame_len = link_frame_currlen;
2791 
2792 	return QDF_STATUS_SUCCESS;
2793 }
2794 
2795 QDF_STATUS
2796 util_gen_link_assoc_req(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2797 			struct qdf_mac_addr link_addr,
2798 			uint8_t *link_frame,
2799 			qdf_size_t link_frame_maxsize,
2800 			qdf_size_t *link_frame_len)
2801 {
2802 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2803 			(isreassoc ? WLAN_FC0_STYPE_REASSOC_REQ :
2804 				WLAN_FC0_STYPE_ASSOC_REQ),
2805 			link_addr, link_frame, link_frame_maxsize,
2806 			link_frame_len);
2807 }
2808 
2809 QDF_STATUS
2810 util_gen_link_assoc_rsp(uint8_t *frame, qdf_size_t frame_len, bool isreassoc,
2811 			struct qdf_mac_addr link_addr,
2812 			uint8_t *link_frame,
2813 			qdf_size_t link_frame_maxsize,
2814 			qdf_size_t *link_frame_len)
2815 {
2816 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2817 			(isreassoc ?  WLAN_FC0_STYPE_REASSOC_RESP :
2818 				WLAN_FC0_STYPE_ASSOC_RESP),
2819 			link_addr, link_frame, link_frame_maxsize,
2820 			link_frame_len);
2821 }
2822 
2823 QDF_STATUS
2824 util_gen_link_probe_rsp(uint8_t *frame, qdf_size_t frame_len,
2825 			struct qdf_mac_addr link_addr,
2826 			uint8_t *link_frame,
2827 			qdf_size_t link_frame_maxsize,
2828 			qdf_size_t *link_frame_len)
2829 {
2830 	return util_gen_link_reqrsp_cmn(frame, frame_len,
2831 			 WLAN_FC0_STYPE_PROBE_RESP,
2832 			link_addr, link_frame, link_frame_maxsize,
2833 			link_frame_len);
2834 }
2835 
2836 QDF_STATUS
2837 util_find_mlie(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq,
2838 	       qdf_size_t *mlieseqlen)
2839 {
2840 	uint8_t *bufboundary;
2841 	uint8_t *ieseq;
2842 	qdf_size_t ieseqlen;
2843 	uint8_t *currie;
2844 	uint8_t *successorfrag;
2845 
2846 	if (!buf || !buflen || !mlieseq || !mlieseqlen)
2847 		return QDF_STATUS_E_NULL_VALUE;
2848 
2849 	*mlieseq = NULL;
2850 	*mlieseqlen = 0;
2851 
2852 	/* Find Multi-Link element. In case a fragment sequence is present,
2853 	 * this element will be the leading fragment.
2854 	 */
2855 	ieseq = util_find_extn_eid(WLAN_ELEMID_EXTN_ELEM,
2856 				   WLAN_EXTN_ELEMID_MULTI_LINK, buf,
2857 				   buflen);
2858 
2859 	/* Even if the element is not found, we have successfully examined the
2860 	 * buffer. The caller will be provided a NULL value for the starting of
2861 	 * the Multi-Link element. Hence, we return success.
2862 	 */
2863 	if (!ieseq)
2864 		return QDF_STATUS_SUCCESS;
2865 
2866 	bufboundary = buf + buflen;
2867 
2868 	if ((ieseq + MIN_IE_LEN) > bufboundary)
2869 		return QDF_STATUS_E_INVAL;
2870 
2871 	ieseqlen = MIN_IE_LEN + ieseq[TAG_LEN_POS];
2872 
2873 	if (ieseqlen < sizeof(struct wlan_ie_multilink))
2874 		return QDF_STATUS_E_PROTO;
2875 
2876 	if ((ieseq + ieseqlen) > bufboundary)
2877 		return QDF_STATUS_E_INVAL;
2878 
2879 	/* In the next sequence of checks, if there is no space in the buffer
2880 	 * for another element after the Multi-Link element/element fragment
2881 	 * sequence, it could indicate an issue since non-MLO EHT elements
2882 	 * would be expected to follow the Multi-Link element/element fragment
2883 	 * sequence. However, this is outside of the purview of this function,
2884 	 * hence we ignore it.
2885 	 */
2886 
2887 	currie = ieseq;
2888 	successorfrag = util_get_successorfrag(currie, buf, buflen);
2889 
2890 	/* Fragmentation definitions as of IEEE802.11be D1.0 and
2891 	 * IEEE802.11REVme D0.2 are applied. Only the case where Multi-Link
2892 	 * element is present in a buffer from the core frame is considered.
2893 	 * Future changes to fragmentation, cases where the Multi-Link element
2894 	 * is present in a subelement, etc. to be reflected here if applicable
2895 	 * as and when the rules evolve.
2896 	 */
2897 	while (successorfrag) {
2898 		/* We should not be seeing a successor fragment if the length
2899 		 * of the current IE is lesser than the max.
2900 		 */
2901 		if (currie[TAG_LEN_POS] != WLAN_MAX_IE_LEN)
2902 			return QDF_STATUS_E_PROTO;
2903 
2904 		if (successorfrag[TAG_LEN_POS] == 0)
2905 			return QDF_STATUS_E_PROTO;
2906 
2907 		ieseqlen +=  (MIN_IE_LEN + successorfrag[TAG_LEN_POS]);
2908 
2909 		currie = successorfrag;
2910 		successorfrag = util_get_successorfrag(currie, buf, buflen);
2911 	}
2912 
2913 	*mlieseq = ieseq;
2914 	*mlieseqlen = ieseqlen;
2915 	return QDF_STATUS_SUCCESS;
2916 }
2917 
2918 QDF_STATUS
2919 util_find_mlie_by_variant(uint8_t *buf, qdf_size_t buflen, uint8_t **mlieseq,
2920 			  qdf_size_t *mlieseqlen, int variant)
2921 {
2922 	uint8_t *ieseq;
2923 	qdf_size_t ieseqlen;
2924 	QDF_STATUS status;
2925 	int ml_variant;
2926 	qdf_size_t buf_parsed_len;
2927 
2928 	if (!buf || !buflen || !mlieseq || !mlieseqlen)
2929 		return QDF_STATUS_E_NULL_VALUE;
2930 
2931 	if (variant >= WLAN_ML_VARIANT_INVALIDSTART)
2932 		return QDF_STATUS_E_PROTO;
2933 
2934 	ieseq = NULL;
2935 	ieseqlen = 0;
2936 	*mlieseq = NULL;
2937 	*mlieseqlen = 0;
2938 	buf_parsed_len = 0;
2939 
2940 	while (buflen > buf_parsed_len) {
2941 		status = util_find_mlie(buf + buf_parsed_len,
2942 					buflen - buf_parsed_len,
2943 					&ieseq, &ieseqlen);
2944 
2945 		if (QDF_IS_STATUS_ERROR(status))
2946 			return status;
2947 
2948 		/* Even if the element is not found, we have successfully
2949 		 * examined the buffer. The caller will be provided a NULL value
2950 		 * for the starting of the Multi-Link element. Hence, we return
2951 		 * success.
2952 		 */
2953 		if (!ieseq)
2954 			return QDF_STATUS_SUCCESS;
2955 
2956 		status = util_get_mlie_variant(ieseq, ieseqlen,
2957 					       &ml_variant);
2958 		if (QDF_IS_STATUS_ERROR(status)) {
2959 			mlo_err("Unable to get Multi-link element variant");
2960 			return status;
2961 		}
2962 
2963 		if (ml_variant == variant) {
2964 			*mlieseq = ieseq;
2965 			*mlieseqlen = ieseqlen;
2966 			return QDF_STATUS_SUCCESS;
2967 		}
2968 
2969 		buf_parsed_len = ieseq + ieseqlen - buf;
2970 	}
2971 
2972 	return QDF_STATUS_E_INVAL;
2973 }
2974 
2975 QDF_STATUS
2976 util_get_mlie_common_info_len(uint8_t *mlieseq, qdf_size_t mlieseqlen,
2977 			      uint8_t *commoninfo_len)
2978 {
2979 	struct wlan_ie_multilink *mlie_fixed;
2980 	enum wlan_ml_variant variant;
2981 	uint16_t mlcontrol;
2982 
2983 	if (!mlieseq || !mlieseqlen || !commoninfo_len)
2984 		return QDF_STATUS_E_NULL_VALUE;
2985 
2986 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
2987 		return QDF_STATUS_E_INVAL;
2988 
2989 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
2990 
2991 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
2992 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
2993 		return QDF_STATUS_E_INVAL;
2994 
2995 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
2996 
2997 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
2998 			       WLAN_ML_CTRL_TYPE_BITS);
2999 
3000 	if (variant != WLAN_ML_VARIANT_BASIC)
3001 		return QDF_STATUS_E_INVAL;
3002 
3003 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
3004 	 * Check if there is sufficient space in the buffer for the Common Info
3005 	 * Length and MLD MAC address.
3006 	 */
3007 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
3008 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
3009 		return QDF_STATUS_E_PROTO;
3010 
3011 	*commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3012 
3013 	return QDF_STATUS_SUCCESS;
3014 }
3015 
3016 QDF_STATUS
3017 util_get_bvmlie_bssparamchangecnt(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3018 				  bool *bssparamchangecntfound,
3019 				  uint8_t *bssparamchangecnt)
3020 {
3021 	struct wlan_ie_multilink *mlie_fixed;
3022 	enum wlan_ml_variant variant;
3023 	uint16_t mlcontrol;
3024 	uint16_t presencebitmap;
3025 	uint8_t *commoninfo;
3026 	qdf_size_t commoninfolen;
3027 
3028 	if (!mlieseq || !mlieseqlen || !bssparamchangecntfound ||
3029 	    !bssparamchangecnt)
3030 		return QDF_STATUS_E_NULL_VALUE;
3031 
3032 	*bssparamchangecntfound = false;
3033 	*bssparamchangecnt = 0;
3034 
3035 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3036 		return QDF_STATUS_E_INVAL;
3037 
3038 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3039 
3040 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3041 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3042 		return QDF_STATUS_E_INVAL;
3043 
3044 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3045 
3046 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3047 			       WLAN_ML_CTRL_TYPE_BITS);
3048 
3049 	if (variant != WLAN_ML_VARIANT_BASIC)
3050 		return QDF_STATUS_E_NOSUPPORT;
3051 
3052 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3053 				      WLAN_ML_CTRL_PBM_BITS);
3054 
3055 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3056 	commoninfolen = WLAN_ML_BV_CINFO_LENGTH_SIZE;
3057 
3058 	commoninfolen += QDF_MAC_ADDR_SIZE;
3059 
3060 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3061 		commoninfolen += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3062 
3063 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3064 				mlieseqlen)
3065 			return QDF_STATUS_E_PROTO;
3066 	}
3067 
3068 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
3069 		*bssparamchangecntfound = true;
3070 		*bssparamchangecnt = *(commoninfo + commoninfolen);
3071 	}
3072 
3073 	return QDF_STATUS_SUCCESS;
3074 }
3075 
3076 QDF_STATUS
3077 util_get_mlie_variant(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3078 		      int *variant)
3079 {
3080 	struct wlan_ie_multilink *mlie_fixed;
3081 	enum wlan_ml_variant var;
3082 	uint16_t mlcontrol;
3083 
3084 	if (!mlieseq || !mlieseqlen || !variant)
3085 		return QDF_STATUS_E_NULL_VALUE;
3086 
3087 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3088 		return QDF_STATUS_E_INVAL;
3089 
3090 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3091 
3092 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3093 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3094 		return QDF_STATUS_E_INVAL;
3095 
3096 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3097 	var = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3098 			   WLAN_ML_CTRL_TYPE_BITS);
3099 
3100 	if (var >= WLAN_ML_VARIANT_INVALIDSTART)
3101 		return QDF_STATUS_E_PROTO;
3102 
3103 	*variant = var;
3104 	return QDF_STATUS_SUCCESS;
3105 }
3106 
3107 QDF_STATUS
3108 util_get_bvmlie_eml_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3109 			bool *eml_cap_found,
3110 			uint16_t *eml_cap)
3111 {
3112 	struct wlan_ie_multilink *mlie_fixed;
3113 	enum wlan_ml_variant variant;
3114 	uint16_t mlcontrol;
3115 	uint8_t eml_cap_offset;
3116 	uint8_t commoninfo_len;
3117 	uint16_t presencebitmap;
3118 
3119 	if (!mlieseq || !mlieseqlen || !eml_cap_found || !eml_cap)
3120 		return QDF_STATUS_E_NULL_VALUE;
3121 
3122 	*eml_cap = 0;
3123 	*eml_cap_found = false;
3124 
3125 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3126 		return QDF_STATUS_E_INVAL;
3127 
3128 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3129 
3130 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3131 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3132 		return QDF_STATUS_E_INVAL;
3133 
3134 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3135 
3136 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3137 			       WLAN_ML_CTRL_TYPE_BITS);
3138 
3139 	if (variant != WLAN_ML_VARIANT_BASIC)
3140 		return QDF_STATUS_E_INVAL;
3141 
3142 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3143 				      WLAN_ML_CTRL_PBM_BITS);
3144 
3145 	/* eml_cap_offset stores the offset of EML Capabilities within
3146 	 * Common Info
3147 	 */
3148 	eml_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
3149 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
3150 		eml_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3151 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
3152 		eml_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3153 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P)
3154 		eml_cap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
3155 
3156 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
3157 		/* Common Info starts at
3158 		 * mlieseq + sizeof(struct wlan_ie_multilink).
3159 		 * Check if there is sufficient space in the buffer for
3160 		 * the Common Info Length.
3161 		 */
3162 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3163 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
3164 			return QDF_STATUS_E_PROTO;
3165 
3166 		/* Check if the value indicated in the Common Info Length
3167 		 * subfield is sufficient to access the EML capabilities.
3168 		 */
3169 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3170 		if (commoninfo_len < (eml_cap_offset +
3171 				      WLAN_ML_BV_CINFO_EMLCAP_SIZE))
3172 			return QDF_STATUS_E_PROTO;
3173 
3174 		/* Common Info starts at mlieseq + sizeof(struct
3175 		 * wlan_ie_multilink). Check if there is sufficient space in
3176 		 * Common Info for the EML capability.
3177 		 */
3178 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3179 				  eml_cap_offset +
3180 				  WLAN_ML_BV_CINFO_EMLCAP_SIZE))
3181 			return QDF_STATUS_E_PROTO;
3182 
3183 		*eml_cap_found = true;
3184 		*eml_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
3185 							 sizeof(struct wlan_ie_multilink) +
3186 							 eml_cap_offset));
3187 	}
3188 	return QDF_STATUS_SUCCESS;
3189 }
3190 
3191 QDF_STATUS
3192 util_get_bvmlie_msd_cap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3193 			bool *msd_cap_found,
3194 			uint16_t *msd_cap)
3195 {
3196 	struct wlan_ie_multilink *mlie_fixed;
3197 	enum wlan_ml_variant variant;
3198 	uint16_t mlcontrol;
3199 	uint8_t msd_cap_offset;
3200 	uint8_t commoninfo_len;
3201 	uint16_t presencebitmap;
3202 
3203 	if (!mlieseq || !mlieseqlen || !msd_cap_found || !msd_cap)
3204 		return QDF_STATUS_E_NULL_VALUE;
3205 
3206 	*msd_cap = 0;
3207 	*msd_cap_found = false;
3208 
3209 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3210 		return QDF_STATUS_E_INVAL;
3211 
3212 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3213 
3214 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3215 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3216 		return QDF_STATUS_E_INVAL;
3217 
3218 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3219 
3220 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3221 			       WLAN_ML_CTRL_TYPE_BITS);
3222 
3223 	if (variant != WLAN_ML_VARIANT_BASIC)
3224 		return QDF_STATUS_E_INVAL;
3225 
3226 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3227 				      WLAN_ML_CTRL_PBM_BITS);
3228 
3229 	/* msd_cap_offset stores the offset of MSD capabilities within
3230 	 * Common Info
3231 	 */
3232 	msd_cap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE;
3233 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P)
3234 		msd_cap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3235 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P)
3236 		msd_cap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3237 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3238 		/* Common Info starts at
3239 		 * mlieseq + sizeof(struct wlan_ie_multilink).
3240 		 * Check if there is sufficient space in the buffer for
3241 		 * the Common Info Length.
3242 		 */
3243 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3244 				  WLAN_ML_BV_CINFO_LENGTH_SIZE))
3245 			return QDF_STATUS_E_PROTO;
3246 
3247 		/* Check if the value indicated in the Common Info Length
3248 		 * subfield is sufficient to access the MSD capabilities.
3249 		 */
3250 		commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3251 		if (commoninfo_len < (msd_cap_offset +
3252 				      WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3253 			return QDF_STATUS_E_PROTO;
3254 
3255 		/* Common Info starts at mlieseq + sizeof(struct
3256 		 * wlan_ie_multilink). Check if there is sufficient space in
3257 		 * Common Info for the MSD capability.
3258 		 */
3259 		if (mlieseqlen < (sizeof(struct wlan_ie_multilink) +
3260 				  msd_cap_offset +
3261 				  WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE))
3262 			return QDF_STATUS_E_PROTO;
3263 
3264 		*msd_cap_found = true;
3265 		*msd_cap = qdf_le16_to_cpu(*(uint16_t *)(mlieseq +
3266 							 sizeof(struct wlan_ie_multilink) +
3267 							 msd_cap_offset));
3268 	} else {
3269 		mlo_debug("MSD caps not found in assoc rsp");
3270 	}
3271 
3272 	return QDF_STATUS_SUCCESS;
3273 }
3274 
3275 QDF_STATUS
3276 util_get_bvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3277 			   struct qdf_mac_addr *mldmacaddr)
3278 {
3279 	struct wlan_ie_multilink *mlie_fixed;
3280 	enum wlan_ml_variant variant;
3281 	uint16_t mlcontrol;
3282 	uint8_t commoninfo_len;
3283 
3284 	if (!mlieseq || !mlieseqlen || !mldmacaddr)
3285 		return QDF_STATUS_E_NULL_VALUE;
3286 
3287 	qdf_mem_zero(mldmacaddr, sizeof(*mldmacaddr));
3288 
3289 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3290 		return QDF_STATUS_E_INVAL;
3291 
3292 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3293 
3294 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3295 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3296 		return QDF_STATUS_E_INVAL;
3297 
3298 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3299 
3300 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3301 			       WLAN_ML_CTRL_TYPE_BITS);
3302 
3303 	if (variant != WLAN_ML_VARIANT_BASIC)
3304 		return QDF_STATUS_E_INVAL;
3305 
3306 	/* Common Info starts at mlieseq + sizeof(struct wlan_ie_multilink).
3307 	 * Check if there is sufficient space in the buffer for the Common Info
3308 	 * Length and MLD MAC address.
3309 	 */
3310 	if ((sizeof(struct wlan_ie_multilink) + WLAN_ML_BV_CINFO_LENGTH_SIZE +
3311 	    QDF_MAC_ADDR_SIZE) > mlieseqlen)
3312 		return QDF_STATUS_E_PROTO;
3313 
3314 	/* Check if the value indicated in the Common Info Length subfield is
3315 	 * sufficient to access the MLD MAC address.
3316 	 */
3317 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3318 	if (commoninfo_len < (WLAN_ML_BV_CINFO_LENGTH_SIZE + QDF_MAC_ADDR_SIZE))
3319 		return QDF_STATUS_E_PROTO;
3320 
3321 	qdf_mem_copy(mldmacaddr->bytes,
3322 		     mlieseq + sizeof(struct wlan_ie_multilink) +
3323 		     WLAN_ML_BV_CINFO_LENGTH_SIZE,
3324 		     QDF_MAC_ADDR_SIZE);
3325 
3326 	return QDF_STATUS_SUCCESS;
3327 }
3328 
3329 QDF_STATUS
3330 util_get_bvmlie_primary_linkid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3331 			       bool *linkidfound, uint8_t *linkid)
3332 {
3333 	struct wlan_ie_multilink *mlie_fixed;
3334 	enum wlan_ml_variant variant;
3335 	uint16_t mlcontrol;
3336 	uint16_t presencebitmap;
3337 	uint8_t *commoninfo;
3338 	qdf_size_t commoninfolen;
3339 	uint8_t *linkidinfo;
3340 
3341 	if (!mlieseq || !mlieseqlen || !linkidfound || !linkid)
3342 		return QDF_STATUS_E_NULL_VALUE;
3343 
3344 	*linkidfound = false;
3345 	*linkid = 0;
3346 
3347 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3348 		return QDF_STATUS_E_INVAL;
3349 
3350 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3351 
3352 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3353 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK))
3354 		return QDF_STATUS_E_INVAL;
3355 
3356 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3357 
3358 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3359 			       WLAN_ML_CTRL_TYPE_BITS);
3360 
3361 	if (variant != WLAN_ML_VARIANT_BASIC)
3362 		return QDF_STATUS_E_INVAL;
3363 
3364 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3365 				      WLAN_ML_CTRL_PBM_BITS);
3366 
3367 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3368 	commoninfolen = 0;
3369 	commoninfolen += WLAN_ML_BV_CINFO_LENGTH_SIZE;
3370 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3371 			mlieseqlen)
3372 		return QDF_STATUS_E_PROTO;
3373 
3374 	commoninfolen += QDF_MAC_ADDR_SIZE;
3375 	if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3376 			mlieseqlen)
3377 		return QDF_STATUS_E_PROTO;
3378 
3379 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3380 		linkidinfo = commoninfo + commoninfolen;
3381 		commoninfolen += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3382 
3383 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen) >
3384 				mlieseqlen)
3385 			return QDF_STATUS_E_PROTO;
3386 
3387 		*linkidfound = true;
3388 		*linkid = QDF_GET_BITS(linkidinfo[0],
3389 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_IDX,
3390 				       WLAN_ML_BV_CINFO_LINKIDINFO_LINKID_BITS);
3391 	}
3392 
3393 	return QDF_STATUS_SUCCESS;
3394 }
3395 
3396 QDF_STATUS
3397 util_get_bvmlie_mldcap(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3398 		       bool *mldcapfound, uint16_t *mldcap)
3399 {
3400 	struct wlan_ie_multilink *mlie_fixed;
3401 	enum wlan_ml_variant variant;
3402 	uint16_t mlcontrol;
3403 	uint16_t presencebitmap;
3404 	uint8_t *commoninfo;
3405 	uint8_t commoninfo_len;
3406 	qdf_size_t mldcap_offset;
3407 
3408 	if (!mlieseq || !mlieseqlen || !mldcapfound || !mldcap)
3409 		return QDF_STATUS_E_NULL_VALUE;
3410 
3411 	*mldcapfound = false;
3412 	*mldcap = 0;
3413 
3414 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3415 		return QDF_STATUS_E_INVAL;
3416 
3417 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3418 
3419 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3420 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3421 		return QDF_STATUS_E_INVAL;
3422 
3423 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3424 
3425 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3426 			       WLAN_ML_CTRL_TYPE_BITS);
3427 
3428 	if (variant != WLAN_ML_VARIANT_BASIC)
3429 		return QDF_STATUS_E_NOSUPPORT;
3430 
3431 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3432 				      WLAN_ML_CTRL_PBM_BITS);
3433 
3434 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3435 	commoninfo_len = *(mlieseq + sizeof(struct wlan_ie_multilink));
3436 	/* mldcap_offset stores the offset of MLD Capabilities within
3437 	 * Common Info
3438 	 */
3439 	mldcap_offset = WLAN_ML_BV_CINFO_LENGTH_SIZE;
3440 	mldcap_offset += QDF_MAC_ADDR_SIZE;
3441 
3442 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_LINKIDINFO_P) {
3443 		mldcap_offset += WLAN_ML_BV_CINFO_LINKIDINFO_SIZE;
3444 
3445 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3446 				mlieseqlen)
3447 			return QDF_STATUS_E_PROTO;
3448 	}
3449 
3450 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_BSSPARAMCHANGECNT_P) {
3451 		mldcap_offset += WLAN_ML_BSSPARAMCHNGCNT_SIZE;
3452 
3453 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3454 				mlieseqlen)
3455 			return QDF_STATUS_E_PROTO;
3456 	}
3457 
3458 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MEDIUMSYNCDELAYINFO_P) {
3459 		mldcap_offset += WLAN_ML_BV_CINFO_MEDMSYNCDELAYINFO_SIZE;
3460 
3461 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3462 				mlieseqlen)
3463 			return QDF_STATUS_E_PROTO;
3464 	}
3465 
3466 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_EMLCAP_P) {
3467 		mldcap_offset += WLAN_ML_BV_CINFO_EMLCAP_SIZE;
3468 
3469 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset) >
3470 				mlieseqlen)
3471 			return QDF_STATUS_E_PROTO;
3472 	}
3473 
3474 	if (presencebitmap & WLAN_ML_BV_CTRL_PBM_MLDCAPANDOP_P) {
3475 		/* Check if the value indicated in the Common Info Length
3476 		 * subfield is sufficient to access the MLD capabilities.
3477 		 */
3478 		if (commoninfo_len < (mldcap_offset +
3479 				      WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE))
3480 			return QDF_STATUS_E_PROTO;
3481 
3482 		if ((sizeof(struct wlan_ie_multilink) + mldcap_offset +
3483 					WLAN_ML_BV_CINFO_MLDCAPANDOP_SIZE) >
3484 				mlieseqlen)
3485 			return QDF_STATUS_E_PROTO;
3486 
3487 		*mldcap = qdf_le16_to_cpu(*((uint16_t *)(commoninfo + mldcap_offset)));
3488 		*mldcapfound = true;
3489 	}
3490 
3491 	return QDF_STATUS_SUCCESS;
3492 }
3493 
3494 QDF_STATUS
3495 util_get_bvmlie_persta_partner_info(uint8_t *mlieseq,
3496 				    qdf_size_t mlieseqlen,
3497 				    struct mlo_partner_info *partner_info)
3498 {
3499 	struct wlan_ie_multilink *mlie_fixed;
3500 	uint16_t mlcontrol;
3501 	enum wlan_ml_variant variant;
3502 	uint8_t *linkinfo;
3503 	qdf_size_t linkinfo_len;
3504 	struct mlo_partner_info pinfo = {0};
3505 	qdf_size_t mlieseqpayloadlen;
3506 	uint8_t *mlieseqpayload_copy;
3507 	bool is_elemfragseq;
3508 	qdf_size_t defragpayload_len;
3509 
3510 	qdf_size_t tmplen;
3511 	QDF_STATUS ret;
3512 
3513 	if (!mlieseq) {
3514 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3515 		return QDF_STATUS_E_NULL_VALUE;
3516 	}
3517 
3518 	if (!mlieseqlen) {
3519 		mlo_err("Length of Multi-Link element sequence is zero");
3520 		return QDF_STATUS_E_INVAL;
3521 	}
3522 
3523 	if (!partner_info) {
3524 		mlo_err("partner_info is NULL");
3525 		return QDF_STATUS_E_NULL_VALUE;
3526 	}
3527 
3528 	partner_info->num_partner_links = 0;
3529 
3530 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3531 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3532 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3533 		return QDF_STATUS_E_INVAL;
3534 	}
3535 
3536 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3537 
3538 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3539 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3540 		mlo_err("The element is not a Multi-Link element");
3541 		return QDF_STATUS_E_INVAL;
3542 	}
3543 
3544 	mlcontrol = le16toh(mlie_fixed->mlcontrol);
3545 
3546 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3547 			       WLAN_ML_CTRL_TYPE_BITS);
3548 
3549 	if (variant != WLAN_ML_VARIANT_BASIC) {
3550 		mlo_err("The variant value %u does not correspond to Basic Variant value %u",
3551 			variant, WLAN_ML_VARIANT_BASIC);
3552 		return QDF_STATUS_E_INVAL;
3553 	}
3554 
3555 	mlieseqpayloadlen = 0;
3556 	tmplen = 0;
3557 	is_elemfragseq = false;
3558 
3559 	ret = wlan_get_elem_fragseq_info(mlieseq,
3560 					 mlieseqlen,
3561 					 &is_elemfragseq,
3562 					 &tmplen,
3563 					 &mlieseqpayloadlen);
3564 	if (QDF_IS_STATUS_ERROR(ret))
3565 		return ret;
3566 
3567 	if (is_elemfragseq) {
3568 		if (tmplen != mlieseqlen) {
3569 			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",
3570 				   tmplen, mlieseqlen);
3571 			return QDF_STATUS_E_INVAL;
3572 		}
3573 
3574 		if (!mlieseqpayloadlen) {
3575 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3576 			return QDF_STATUS_E_FAILURE;
3577 		}
3578 
3579 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3580 			  mlieseqpayloadlen);
3581 	} else {
3582 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3583 			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",
3584 				   mlieseqlen,
3585 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3586 			return QDF_STATUS_E_FAILURE;
3587 		}
3588 
3589 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3590 	}
3591 
3592 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3593 
3594 	if (!mlieseqpayload_copy) {
3595 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3596 		return QDF_STATUS_E_NOMEM;
3597 	}
3598 
3599 	if (is_elemfragseq) {
3600 		ret = wlan_defrag_elem_fragseq(false,
3601 					       mlieseq,
3602 					       mlieseqlen,
3603 					       mlieseqpayload_copy,
3604 					       mlieseqpayloadlen,
3605 					       &defragpayload_len);
3606 		if (QDF_IS_STATUS_ERROR(ret)) {
3607 			qdf_mem_free(mlieseqpayload_copy);
3608 			return ret;
3609 		}
3610 
3611 		if (defragpayload_len != mlieseqpayloadlen) {
3612 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3613 				   defragpayload_len, mlieseqpayloadlen);
3614 			qdf_mem_free(mlieseqpayload_copy);
3615 			return QDF_STATUS_E_FAILURE;
3616 		}
3617 	} else {
3618 		qdf_mem_copy(mlieseqpayload_copy,
3619 			     mlieseq + sizeof(struct ie_header) + 1,
3620 			     mlieseqpayloadlen);
3621 	}
3622 
3623 	linkinfo = NULL;
3624 	linkinfo_len = 0;
3625 
3626 	ret = util_parse_multi_link_ctrl(mlieseqpayload_copy,
3627 					 mlieseqpayloadlen,
3628 					 &linkinfo,
3629 					 &linkinfo_len);
3630 	if (QDF_IS_STATUS_ERROR(ret)) {
3631 		qdf_mem_free(mlieseqpayload_copy);
3632 		return ret;
3633 	}
3634 
3635 	/*
3636 	 * If Probe Request variant Multi-Link element in the Multi-Link probe
3637 	 * request does not include any per-STA profile, then all APs affiliated
3638 	 * with the same AP MLD as the AP identified in the Addr 1 or Addr 3
3639 	 * field or AP MLD ID of the Multi-Link probe request are requested
3640 	 * APs return success here
3641 	 */
3642 	if (!linkinfo) {
3643 		qdf_mem_free(mlieseqpayload_copy);
3644 		return QDF_STATUS_SUCCESS;
3645 	}
3646 
3647 	ret = util_parse_partner_info_from_linkinfo(linkinfo,
3648 						    linkinfo_len,
3649 						    &pinfo);
3650 
3651 	if (QDF_IS_STATUS_ERROR(ret)) {
3652 		qdf_mem_free(mlieseqpayload_copy);
3653 		return ret;
3654 	}
3655 
3656 	qdf_mem_copy(partner_info, &pinfo, sizeof(*partner_info));
3657 
3658 	qdf_mem_free(mlieseqpayload_copy);
3659 
3660 	return QDF_STATUS_SUCCESS;
3661 }
3662 
3663 QDF_STATUS
3664 util_get_prvmlie_persta_link_id(uint8_t *mlieseq,
3665 				qdf_size_t mlieseqlen,
3666 				struct mlo_probereq_info *probereq_info)
3667 {
3668 	struct wlan_ie_multilink *mlie_fixed;
3669 	uint16_t mlcontrol;
3670 	enum wlan_ml_variant variant;
3671 	uint8_t *linkinfo;
3672 	qdf_size_t linkinfo_len;
3673 	qdf_size_t mlieseqpayloadlen;
3674 	uint8_t *mlieseqpayload_copy;
3675 	bool is_elemfragseq;
3676 	qdf_size_t defragpayload_len;
3677 
3678 	qdf_size_t tmplen;
3679 	QDF_STATUS ret;
3680 
3681 	if (!mlieseq) {
3682 		mlo_err("Pointer to Multi-Link element sequence is NULL");
3683 		return QDF_STATUS_E_NULL_VALUE;
3684 	}
3685 
3686 	if (!mlieseqlen) {
3687 		mlo_err("Length of Multi-Link element sequence is zero");
3688 		return QDF_STATUS_E_INVAL;
3689 	}
3690 
3691 	if (!probereq_info) {
3692 		mlo_err("probe request_info is NULL");
3693 		return QDF_STATUS_E_NULL_VALUE;
3694 	}
3695 
3696 	probereq_info->num_links = 0;
3697 
3698 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
3699 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
3700 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
3701 		return QDF_STATUS_E_INVAL;
3702 	}
3703 
3704 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3705 
3706 	if ((mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM) ||
3707 	    (mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)) {
3708 		mlo_err("The element is not a Multi-Link element");
3709 		return QDF_STATUS_E_INVAL;
3710 	}
3711 
3712 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3713 
3714 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3715 			       WLAN_ML_CTRL_TYPE_BITS);
3716 
3717 	if (variant != WLAN_ML_VARIANT_PROBEREQ) {
3718 		mlo_err("The variant value %u does not correspond to Probe Request Variant value %u",
3719 			variant, WLAN_ML_VARIANT_PROBEREQ);
3720 		return QDF_STATUS_E_INVAL;
3721 	}
3722 
3723 	mlieseqpayloadlen = 0;
3724 	tmplen = 0;
3725 	is_elemfragseq = false;
3726 
3727 	ret = wlan_get_elem_fragseq_info(mlieseq,
3728 					 mlieseqlen,
3729 					 &is_elemfragseq,
3730 					 &tmplen,
3731 					 &mlieseqpayloadlen);
3732 	if (QDF_IS_STATUS_ERROR(ret))
3733 		return ret;
3734 
3735 	if (is_elemfragseq) {
3736 		if (tmplen != mlieseqlen) {
3737 			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",
3738 				   tmplen, mlieseqlen);
3739 			return QDF_STATUS_E_INVAL;
3740 		}
3741 
3742 		if (!mlieseqpayloadlen) {
3743 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
3744 			return QDF_STATUS_E_FAILURE;
3745 		}
3746 
3747 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
3748 			  mlieseqpayloadlen);
3749 	} else {
3750 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
3751 			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",
3752 				   mlieseqlen,
3753 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
3754 			return QDF_STATUS_E_FAILURE;
3755 		}
3756 
3757 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
3758 	}
3759 
3760 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
3761 
3762 	if (!mlieseqpayload_copy) {
3763 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
3764 		return QDF_STATUS_E_NOMEM;
3765 	}
3766 
3767 	if (is_elemfragseq) {
3768 		ret = wlan_defrag_elem_fragseq(false,
3769 					       mlieseq,
3770 					       mlieseqlen,
3771 					       mlieseqpayload_copy,
3772 					       mlieseqpayloadlen,
3773 					       &defragpayload_len);
3774 		if (QDF_IS_STATUS_ERROR(ret)) {
3775 			qdf_mem_free(mlieseqpayload_copy);
3776 			return ret;
3777 		}
3778 
3779 		if (defragpayload_len != mlieseqpayloadlen) {
3780 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
3781 				   defragpayload_len, mlieseqpayloadlen);
3782 			qdf_mem_free(mlieseqpayload_copy);
3783 			return QDF_STATUS_E_FAILURE;
3784 		}
3785 	} else {
3786 		qdf_mem_copy(mlieseqpayload_copy,
3787 			     mlieseq + sizeof(struct ie_header) + 1,
3788 			     mlieseqpayloadlen);
3789 	}
3790 
3791 	linkinfo = NULL;
3792 	linkinfo_len = 0;
3793 	ret = util_parse_prv_multi_link_ctrl(mlieseqpayload_copy,
3794 					     mlieseqpayloadlen,
3795 					     &linkinfo,
3796 					     &linkinfo_len);
3797 	if (QDF_IS_STATUS_ERROR(ret)) {
3798 		qdf_mem_free(mlieseqpayload_copy);
3799 		return ret;
3800 	}
3801 
3802 	/* In case Link Info is absent, the number of links will remain
3803 	 * zero.
3804 	 */
3805 	if (!linkinfo) {
3806 		mlo_debug("No link info present");
3807 		qdf_mem_free(mlieseqpayload_copy);
3808 		return QDF_STATUS_SUCCESS;
3809 	}
3810 
3811 	ret = util_parse_probereq_info_from_linkinfo(linkinfo,
3812 						     linkinfo_len,
3813 						     probereq_info);
3814 
3815 	if (QDF_IS_STATUS_ERROR(ret)) {
3816 		qdf_mem_free(mlieseqpayload_copy);
3817 		return ret;
3818 	}
3819 
3820 	qdf_mem_free(mlieseqpayload_copy);
3821 
3822 	return QDF_STATUS_SUCCESS;
3823 }
3824 
3825 QDF_STATUS
3826 util_get_prvmlie_mldid(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3827 		       bool *mldidfound, uint8_t *mldid)
3828 {
3829 	struct wlan_ie_multilink *mlie_fixed;
3830 	enum wlan_ml_variant variant;
3831 	uint16_t mlcontrol;
3832 	uint16_t presencebitmap;
3833 	uint8_t *commoninfo;
3834 	qdf_size_t commoninfolen;
3835 
3836 	if (!mlieseq || !mlieseqlen || !mldidfound || !mldid)
3837 		return QDF_STATUS_E_NULL_VALUE;
3838 
3839 	*mldidfound = false;
3840 	*mldid = 0;
3841 
3842 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3843 		return QDF_STATUS_E_INVAL;
3844 
3845 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3846 
3847 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3848 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3849 		return QDF_STATUS_E_INVAL;
3850 
3851 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3852 
3853 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3854 			       WLAN_ML_CTRL_TYPE_BITS);
3855 
3856 	if (variant != WLAN_ML_VARIANT_PROBEREQ)
3857 		return QDF_STATUS_E_NOSUPPORT;
3858 
3859 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3860 				      WLAN_ML_CTRL_PBM_BITS);
3861 
3862 	commoninfo = mlieseq + sizeof(struct wlan_ie_multilink);
3863 	commoninfolen = WLAN_ML_PRV_CINFO_LENGTH_SIZE;
3864 
3865 	if (presencebitmap & WLAN_ML_PRV_CTRL_PBM_MLDID_P) {
3866 		if ((sizeof(struct wlan_ie_multilink) + commoninfolen +
3867 		     WLAN_ML_PRV_CINFO_MLDID_SIZE) >
3868 		    mlieseqlen)
3869 			return QDF_STATUS_E_PROTO;
3870 
3871 		*mldid = *((uint8_t *)(commoninfo + commoninfolen));
3872 		commoninfolen += WLAN_ML_PRV_CINFO_MLDID_SIZE;
3873 
3874 		*mldidfound = true;
3875 	}
3876 
3877 	return QDF_STATUS_SUCCESS;
3878 }
3879 
3880 QDF_STATUS util_get_rvmlie_mldmacaddr(uint8_t *mlieseq, qdf_size_t mlieseqlen,
3881 				      struct qdf_mac_addr *mldmacaddr)
3882 {
3883 	struct wlan_ie_multilink *mlie_fixed;
3884 	enum wlan_ml_variant variant;
3885 	uint16_t mlcontrol;
3886 	uint16_t presencebitmap;
3887 
3888 	if (!mlieseq || !mlieseqlen || !mldmacaddr)
3889 		return QDF_STATUS_E_NULL_VALUE;
3890 
3891 	qdf_mem_zero(mldmacaddr, sizeof(*mldmacaddr));
3892 
3893 	if (mlieseqlen < sizeof(struct wlan_ie_multilink))
3894 		return QDF_STATUS_E_INVAL;
3895 
3896 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
3897 
3898 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
3899 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK)
3900 		return QDF_STATUS_E_INVAL;
3901 
3902 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
3903 
3904 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
3905 			       WLAN_ML_CTRL_TYPE_BITS);
3906 
3907 	if (variant != WLAN_ML_VARIANT_RECONFIG)
3908 		return QDF_STATUS_E_INVAL;
3909 
3910 	presencebitmap = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3911 				      WLAN_ML_CTRL_PBM_BITS);
3912 
3913 	/* Check if MLD mac address is present */
3914 	if (presencebitmap & WLAN_ML_RV_CTRL_PBM_MLDMACADDR_P) {
3915 		if ((sizeof(struct wlan_ie_multilink) + QDF_MAC_ADDR_SIZE) >
3916 			mlieseqlen)
3917 			return QDF_STATUS_E_PROTO;
3918 
3919 		qdf_mem_copy(mldmacaddr->bytes,
3920 			     mlieseq + sizeof(struct wlan_ie_multilink),
3921 			     QDF_MAC_ADDR_SIZE);
3922 	}
3923 
3924 	return QDF_STATUS_SUCCESS;
3925 }
3926 
3927 static QDF_STATUS
3928 util_parse_rv_multi_link_ctrl(uint8_t *mlieseqpayload,
3929 			      qdf_size_t mlieseqpayloadlen,
3930 			      uint8_t **link_info,
3931 			      qdf_size_t *link_info_len)
3932 {
3933 	qdf_size_t parsed_payload_len;
3934 	uint16_t mlcontrol;
3935 	uint16_t presence_bm;
3936 
3937 	/* This helper returns the location(s) and length(s) of (sub)field(s)
3938 	 * inferable after parsing the Multi Link element Control field. These
3939 	 * location(s) and length(s) is/are in reference to the payload section
3940 	 * of the Multi Link element (after defragmentation, if applicable).
3941 	 * Here, the payload is the point after the element ID extension of the
3942 	 * Multi Link element, and includes the payloads of all subsequent
3943 	 * fragments (if any) but not the headers of those fragments.
3944 	 *
3945 	 * Currently, the helper returns the location and length of the Link
3946 	 * Info field in the Multi Link element sequence. Other (sub)field(s)
3947 	 * can be added later as required.
3948 	 */
3949 	if (!mlieseqpayload) {
3950 		mlo_err("ML seq payload pointer is NULL");
3951 		return QDF_STATUS_E_NULL_VALUE;
3952 	}
3953 
3954 	if (!mlieseqpayloadlen) {
3955 		mlo_err("ML seq payload len is 0");
3956 		return QDF_STATUS_E_INVAL;
3957 	}
3958 
3959 	if (mlieseqpayloadlen < WLAN_ML_CTRL_SIZE) {
3960 		mlo_err_rl("ML seq payload len %zu < ML Control size %u",
3961 			   mlieseqpayloadlen, WLAN_ML_CTRL_SIZE);
3962 		return QDF_STATUS_E_PROTO;
3963 	}
3964 
3965 	parsed_payload_len = 0;
3966 
3967 	qdf_mem_copy(&mlcontrol, mlieseqpayload, WLAN_ML_CTRL_SIZE);
3968 	mlcontrol = qdf_le16_to_cpu(mlcontrol);
3969 	parsed_payload_len += WLAN_ML_CTRL_SIZE;
3970 
3971 	presence_bm = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_PBM_IDX,
3972 				   WLAN_ML_CTRL_PBM_BITS);
3973 
3974 	/* Check if MLD MAC address is present */
3975 	if (presence_bm & WLAN_ML_RV_CTRL_PBM_MLDMACADDR_P) {
3976 		if (mlieseqpayloadlen <
3977 				(parsed_payload_len +
3978 				 QDF_MAC_ADDR_SIZE)) {
3979 			mlo_err_rl("ML seq payload len %zu insufficient for MLD ID size %u after parsed payload len %zu.",
3980 				   mlieseqpayloadlen,
3981 				   WLAN_ML_PRV_CINFO_MLDID_SIZE,
3982 				   parsed_payload_len);
3983 			return QDF_STATUS_E_PROTO;
3984 		}
3985 
3986 		parsed_payload_len += QDF_MAC_ADDR_SIZE;
3987 	}
3988 
3989 	if (link_info_len) {
3990 		*link_info_len = mlieseqpayloadlen - parsed_payload_len;
3991 		mlo_debug("link_info_len:%zu, parsed_payload_len:%zu",
3992 			  *link_info_len, parsed_payload_len);
3993 	}
3994 
3995 	if (mlieseqpayloadlen == parsed_payload_len) {
3996 		mlo_debug("No Link Info field present");
3997 		if (link_info)
3998 			*link_info = NULL;
3999 
4000 		return QDF_STATUS_SUCCESS;
4001 	}
4002 
4003 	if (link_info)
4004 		*link_info = mlieseqpayload + parsed_payload_len;
4005 
4006 	return QDF_STATUS_SUCCESS;
4007 }
4008 
4009 static QDF_STATUS
4010 util_parse_rvmlie_perstaprofile_stactrl(uint8_t *subelempayload,
4011 					qdf_size_t subelempayloadlen,
4012 					uint8_t *linkid,
4013 					bool *is_macaddr_valid,
4014 					struct qdf_mac_addr *macaddr,
4015 					bool *is_delete_timer_valid,
4016 					uint16_t *delete_timer)
4017 {
4018 	qdf_size_t parsed_payload_len = 0;
4019 	uint16_t stacontrol;
4020 	uint8_t completeprofile;
4021 
4022 	/* This helper returns the location(s) and where required, the length(s)
4023 	 * of (sub)field(s) inferable after parsing the STA Control field in the
4024 	 * per-STA profile subelement. These location(s) and length(s) is/are in
4025 	 * reference to the payload section of the per-STA profile subelement
4026 	 * (after defragmentation, if applicable).  Here, the payload is the
4027 	 * point after the subelement length in the subelement, and includes the
4028 	 * payloads of all subsequent fragments (if any) but not the headers of
4029 	 * those fragments.
4030 	 *
4031 	 * Currently, the helper returns the link ID, MAC address, Delete timer
4032 	 * and STA profile. More (sub)fields can be added when required.
4033 	 */
4034 	if (!subelempayload) {
4035 		mlo_err("Pointer to subelement payload is NULL");
4036 		return QDF_STATUS_E_NULL_VALUE;
4037 	}
4038 
4039 	if (!subelempayloadlen) {
4040 		mlo_err("Length of subelement payload is zero");
4041 		return QDF_STATUS_E_INVAL;
4042 	}
4043 
4044 	if (subelempayloadlen < WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE) {
4045 		mlo_err_rl("Subelement payload length %zu octets is smaller than STA control field of per-STA profile subelement %u octets",
4046 			   subelempayloadlen,
4047 			   WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE);
4048 		return QDF_STATUS_E_PROTO;
4049 	}
4050 
4051 	parsed_payload_len = 0;
4052 	qdf_mem_copy(&stacontrol,
4053 		     subelempayload,
4054 		     WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_SIZE);
4055 
4056 	stacontrol = qdf_le16_to_cpu(stacontrol);
4057 	parsed_payload_len += WLAN_ML_BV_LINFO_PERSTAPROF_STACTRL_SIZE;
4058 
4059 	if (linkid)
4060 		*linkid = QDF_GET_BITS(stacontrol,
4061 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_LINKID_IDX,
4062 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_LINKID_BITS);
4063 
4064 	/* Check if this a complete profile */
4065 	completeprofile = QDF_GET_BITS(stacontrol,
4066 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_IDX,
4067 				WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_CMPLTPROF_BITS);
4068 
4069 	if (is_macaddr_valid)
4070 		*is_macaddr_valid = false;
4071 
4072 	/* Check STA MAC address present bit */
4073 	if (QDF_GET_BITS(stacontrol,
4074 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_MACADDRP_IDX,
4075 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_MACADDRP_BITS)) {
4076 		if (subelempayloadlen <
4077 				(parsed_payload_len + QDF_MAC_ADDR_SIZE)) {
4078 			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.",
4079 				   subelempayloadlen, QDF_MAC_ADDR_SIZE,
4080 				   parsed_payload_len);
4081 			return QDF_STATUS_E_PROTO;
4082 		}
4083 
4084 		if (macaddr) {
4085 			qdf_mem_copy(macaddr->bytes,
4086 				     subelempayload + parsed_payload_len,
4087 				     QDF_MAC_ADDR_SIZE);
4088 			mlo_nofl_debug("Copied MAC address: " QDF_MAC_ADDR_FMT,
4089 				       subelempayload + parsed_payload_len);
4090 
4091 			if (is_macaddr_valid)
4092 				*is_macaddr_valid = true;
4093 		}
4094 
4095 		parsed_payload_len += QDF_MAC_ADDR_SIZE;
4096 	}
4097 
4098 	/* Check Delete timer present bit */
4099 	if (QDF_GET_BITS(stacontrol,
4100 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_DELTIMERP_IDX,
4101 			 WLAN_ML_RV_LINFO_PERSTAPROF_STACTRL_DELTIMERP_BITS)) {
4102 		if (subelempayloadlen <
4103 				(parsed_payload_len +
4104 				 WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE)) {
4105 			mlo_err_rl("Length of subelement payload %zu octets not sufficient to contain Delete timer of size %u octets after parsed payload length of %zu octets.",
4106 				   subelempayloadlen,
4107 				   WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE,
4108 				   parsed_payload_len);
4109 			return QDF_STATUS_E_PROTO;
4110 		}
4111 
4112 		if (delete_timer) {
4113 			qdf_mem_copy(delete_timer,
4114 				     subelempayload + parsed_payload_len,
4115 				     WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE);
4116 
4117 			if (is_delete_timer_valid)
4118 				*is_delete_timer_valid = true;
4119 		}
4120 
4121 		parsed_payload_len += WLAN_ML_RV_LINFO_PERSTAPROF_STAINFO_DELTIMER_SIZE;
4122 	}
4123 
4124 	return QDF_STATUS_SUCCESS;
4125 }
4126 
4127 static QDF_STATUS
4128 util_parse_rv_info_from_linkinfo(uint8_t *linkinfo,
4129 				 qdf_size_t linkinfo_len,
4130 				 struct ml_rv_info *reconfig_info)
4131 {
4132 	uint8_t linkid;
4133 	uint8_t *linkinfo_currpos;
4134 	qdf_size_t linkinfo_remlen;
4135 	bool is_subelemfragseq;
4136 	uint8_t subelemid;
4137 	qdf_size_t subelemseqtotallen;
4138 	qdf_size_t subelemseqpayloadlen;
4139 	qdf_size_t defragpayload_len;
4140 	QDF_STATUS ret;
4141 	struct qdf_mac_addr mac_addr;
4142 	bool is_macaddr_valid;
4143 	bool is_delete_timer_valid;
4144 	uint16_t delete_timer;
4145 
4146 	/* This helper function parses probe request info from the per-STA prof
4147 	 * present (if any) in the Link Info field in the payload of a Multi
4148 	 * Link element (after defragmentation if required). The caller should
4149 	 * pass a copy of the payload so that inline defragmentation of
4150 	 * subelements can be carried out if required. The subelement
4151 	 * defragmentation (if applicable) in this Control Path helper is
4152 	 * required for maintainability, accuracy and eliminating current and
4153 	 * future per-field-access multi-level fragment boundary checks and
4154 	 * adjustments, given the complex format of Multi Link elements. It is
4155 	 * also most likely to be required mainly at the client side.
4156 	 * Fragmentation is currently unlikely to be required for subelements
4157 	 * in Reconfiguration variant Multi-Link elements, but it should be
4158 	 * handled in order to be future ready.
4159 	 */
4160 	if (!linkinfo) {
4161 		mlo_err("linkinfo is NULL");
4162 		return QDF_STATUS_E_NULL_VALUE;
4163 	}
4164 
4165 	if (!linkinfo_len) {
4166 		mlo_err("linkinfo_len is zero");
4167 		return QDF_STATUS_E_NULL_VALUE;
4168 	}
4169 
4170 	if (!reconfig_info) {
4171 		mlo_err("ML reconfig info is NULL");
4172 		return QDF_STATUS_E_NULL_VALUE;
4173 	}
4174 
4175 	reconfig_info->num_links = 0;
4176 	linkinfo_currpos = linkinfo;
4177 	linkinfo_remlen = linkinfo_len;
4178 
4179 	while (linkinfo_remlen) {
4180 		if (linkinfo_remlen <  sizeof(struct subelem_header)) {
4181 			mlo_err_rl("Remaining length in link info %zu octets is smaller than subelement header length %zu octets",
4182 				   linkinfo_remlen,
4183 				   sizeof(struct subelem_header));
4184 			return QDF_STATUS_E_PROTO;
4185 		}
4186 
4187 		subelemid = linkinfo_currpos[ID_POS];
4188 		is_subelemfragseq = false;
4189 		subelemseqtotallen = 0;
4190 		subelemseqpayloadlen = 0;
4191 
4192 		ret = wlan_get_subelem_fragseq_info(WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
4193 						    linkinfo_currpos,
4194 						    linkinfo_remlen,
4195 						    &is_subelemfragseq,
4196 						    &subelemseqtotallen,
4197 						    &subelemseqpayloadlen);
4198 		if (QDF_IS_STATUS_ERROR(ret))
4199 			return ret;
4200 
4201 		if (qdf_unlikely(is_subelemfragseq)) {
4202 			if (!subelemseqpayloadlen) {
4203 				mlo_err_rl("Subelement fragment sequence payload is reported as 0, investigate");
4204 				return QDF_STATUS_E_FAILURE;
4205 			}
4206 
4207 			mlo_debug("Subelement fragment sequence found with payload len %zu",
4208 				  subelemseqpayloadlen);
4209 
4210 			ret = wlan_defrag_subelem_fragseq(true,
4211 							  WLAN_ML_LINFO_SUBELEMID_FRAGMENT,
4212 							  linkinfo_currpos,
4213 							  linkinfo_remlen,
4214 							  NULL,
4215 							  0,
4216 							  &defragpayload_len);
4217 
4218 			if (QDF_IS_STATUS_ERROR(ret))
4219 				return ret;
4220 
4221 			if (defragpayload_len != subelemseqpayloadlen) {
4222 				mlo_err_rl("Length of defragmented payload %zu octets is not equal to length of subelement fragment sequence payload %zu octets",
4223 					   defragpayload_len,
4224 					   subelemseqpayloadlen);
4225 				return QDF_STATUS_E_FAILURE;
4226 			}
4227 
4228 			/* Adjust linkinfo_remlen to reflect removal of all
4229 			 * subelement headers except the header of the lead
4230 			 * subelement.
4231 			 */
4232 			linkinfo_remlen -= (subelemseqtotallen -
4233 					    subelemseqpayloadlen -
4234 					    sizeof(struct subelem_header));
4235 		} else {
4236 			if (linkinfo_remlen <
4237 				(sizeof(struct subelem_header) +
4238 				linkinfo_currpos[TAG_LEN_POS])) {
4239 				mlo_err_rl("Remaining length in link info %zu octets is smaller than total size of current subelement %zu octets",
4240 					   linkinfo_remlen,
4241 					   sizeof(struct subelem_header) +
4242 					   linkinfo_currpos[TAG_LEN_POS]);
4243 				return QDF_STATUS_E_PROTO;
4244 			}
4245 
4246 			subelemseqpayloadlen = linkinfo_currpos[TAG_LEN_POS];
4247 		}
4248 
4249 		if (subelemid == WLAN_ML_LINFO_SUBELEMID_PERSTAPROFILE) {
4250 			is_macaddr_valid = false;
4251 			is_delete_timer_valid = false;
4252 			ret = util_parse_rvmlie_perstaprofile_stactrl(linkinfo_currpos +
4253 								      sizeof(struct subelem_header),
4254 								      subelemseqpayloadlen,
4255 								      &linkid,
4256 								      &is_macaddr_valid,
4257 								      &mac_addr,
4258 								      &is_delete_timer_valid,
4259 								      &delete_timer);
4260 			if (QDF_IS_STATUS_ERROR(ret))
4261 				return ret;
4262 
4263 			reconfig_info->link_info[reconfig_info->num_links].link_id = linkid;
4264 			reconfig_info->link_info[reconfig_info->num_links].is_delete_timer_p = is_delete_timer_valid;
4265 
4266 			if (is_delete_timer_valid)
4267 				reconfig_info->link_info[reconfig_info->num_links].delete_timer = delete_timer;
4268 			else
4269 				mlo_warn_rl("Delete timer not found in STA Info field of per-STA profile with link ID %u",
4270 					    linkid);
4271 
4272 			mlo_debug("Per-STA Profile Link ID: %u Delete timer present: %d Delete timer: %u",
4273 				  reconfig_info->link_info[reconfig_info->num_links].link_id,
4274 				  reconfig_info->link_info[reconfig_info->num_links].is_delete_timer_p,
4275 				  reconfig_info->link_info[reconfig_info->num_links].delete_timer);
4276 
4277 			reconfig_info->num_links++;
4278 		}
4279 
4280 		linkinfo_remlen -= (sizeof(struct subelem_header) +
4281 				    subelemseqpayloadlen);
4282 		linkinfo_currpos += (sizeof(struct subelem_header) +
4283 				     subelemseqpayloadlen);
4284 	}
4285 
4286 	mlo_debug("Number of ML probe request links found=%u",
4287 		  reconfig_info->num_links);
4288 
4289 	return QDF_STATUS_SUCCESS;
4290 }
4291 
4292 QDF_STATUS util_get_rvmlie_persta_link_info(uint8_t *mlieseq,
4293 					    qdf_size_t mlieseqlen,
4294 					    struct ml_rv_info *reconfig_info)
4295 {
4296 	struct wlan_ie_multilink *mlie_fixed;
4297 	uint16_t mlcontrol;
4298 	enum wlan_ml_variant variant;
4299 	uint8_t *linkinfo;
4300 	qdf_size_t linkinfo_len;
4301 	struct ml_rv_info rinfo = {0};
4302 	qdf_size_t mlieseqpayloadlen;
4303 	uint8_t *mlieseqpayload_copy;
4304 	bool is_elemfragseq;
4305 	qdf_size_t defragpayload_len;
4306 	qdf_size_t tmplen;
4307 	QDF_STATUS ret;
4308 
4309 	if (!mlieseq) {
4310 		mlo_err("Pointer to Multi-Link element sequence is NULL");
4311 		return QDF_STATUS_E_NULL_VALUE;
4312 	}
4313 
4314 	if (!mlieseqlen) {
4315 		mlo_err("Length of Multi-Link element sequence is zero");
4316 		return QDF_STATUS_E_INVAL;
4317 	}
4318 
4319 	if (!reconfig_info) {
4320 		mlo_err("reconfig_info is NULL");
4321 		return QDF_STATUS_E_NULL_VALUE;
4322 	}
4323 
4324 	reconfig_info->num_links = 0;
4325 
4326 	if (mlieseqlen < sizeof(struct wlan_ie_multilink)) {
4327 		mlo_err_rl("Multi-Link element sequence length %zu octets is smaller than required for the fixed portion of Multi-Link element (%zu octets)",
4328 			   mlieseqlen, sizeof(struct wlan_ie_multilink));
4329 		return QDF_STATUS_E_INVAL;
4330 	}
4331 
4332 	mlie_fixed = (struct wlan_ie_multilink *)mlieseq;
4333 	if (mlie_fixed->elem_id != WLAN_ELEMID_EXTN_ELEM ||
4334 	    mlie_fixed->elem_id_ext != WLAN_EXTN_ELEMID_MULTI_LINK) {
4335 		mlo_err("The element is not a Multi-Link element");
4336 		return QDF_STATUS_E_INVAL;
4337 	}
4338 
4339 	mlcontrol = qdf_le16_to_cpu(mlie_fixed->mlcontrol);
4340 
4341 	variant = QDF_GET_BITS(mlcontrol, WLAN_ML_CTRL_TYPE_IDX,
4342 			       WLAN_ML_CTRL_TYPE_BITS);
4343 
4344 	if (variant != WLAN_ML_VARIANT_RECONFIG) {
4345 		mlo_err("The variant value %u does not correspond to Reconfig Variant value %u",
4346 			variant, WLAN_ML_VARIANT_RECONFIG);
4347 		return QDF_STATUS_E_INVAL;
4348 	}
4349 
4350 	mlieseqpayloadlen = 0;
4351 	tmplen = 0;
4352 	is_elemfragseq = false;
4353 
4354 	ret = wlan_get_elem_fragseq_info(mlieseq,
4355 					 mlieseqlen,
4356 					 &is_elemfragseq,
4357 					 &tmplen,
4358 					 &mlieseqpayloadlen);
4359 
4360 	if (QDF_IS_STATUS_ERROR(ret))
4361 		return ret;
4362 
4363 	if (qdf_unlikely(is_elemfragseq)) {
4364 		if (tmplen != mlieseqlen) {
4365 			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",
4366 				   tmplen, mlieseqlen);
4367 			return QDF_STATUS_E_INVAL;
4368 		}
4369 
4370 		if (!mlieseqpayloadlen) {
4371 			mlo_err_rl("Multi-Link element fragment sequence payload is reported as 0, investigate");
4372 			return QDF_STATUS_E_FAILURE;
4373 		}
4374 
4375 		mlo_debug("Multi-Link element fragment sequence found with payload len %zu",
4376 			  mlieseqpayloadlen);
4377 	} else {
4378 		if (mlieseqlen > (sizeof(struct ie_header) + WLAN_MAX_IE_LEN)) {
4379 			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",
4380 				   mlieseqlen,
4381 				   sizeof(struct ie_header) + WLAN_MAX_IE_LEN);
4382 			return QDF_STATUS_E_FAILURE;
4383 		}
4384 
4385 		mlieseqpayloadlen = mlieseqlen - (sizeof(struct ie_header) + 1);
4386 	}
4387 
4388 	mlieseqpayload_copy = qdf_mem_malloc(mlieseqpayloadlen);
4389 
4390 	if (!mlieseqpayload_copy) {
4391 		mlo_err_rl("Could not allocate memory for Multi-Link element payload copy");
4392 		return QDF_STATUS_E_NOMEM;
4393 	}
4394 
4395 	if (qdf_unlikely(is_elemfragseq)) {
4396 		ret = wlan_defrag_elem_fragseq(false,
4397 					       mlieseq,
4398 					       mlieseqlen,
4399 					       mlieseqpayload_copy,
4400 					       mlieseqpayloadlen,
4401 					       &defragpayload_len);
4402 		if (QDF_IS_STATUS_ERROR(ret)) {
4403 			qdf_mem_free(mlieseqpayload_copy);
4404 			return ret;
4405 		}
4406 
4407 		if (defragpayload_len != mlieseqpayloadlen) {
4408 			mlo_err_rl("Length of de-fragmented payload %zu octets is not equal to length of Multi-Link element fragment sequence payload %zu octets",
4409 				   defragpayload_len, mlieseqpayloadlen);
4410 			qdf_mem_free(mlieseqpayload_copy);
4411 			return QDF_STATUS_E_FAILURE;
4412 		}
4413 	} else {
4414 		qdf_mem_copy(mlieseqpayload_copy,
4415 			     mlieseq + sizeof(struct ie_header) + 1,
4416 			     mlieseqpayloadlen);
4417 	}
4418 
4419 	linkinfo = NULL;
4420 	linkinfo_len = 0;
4421 
4422 	ret = util_parse_rv_multi_link_ctrl(mlieseqpayload_copy,
4423 					    mlieseqpayloadlen,
4424 					    &linkinfo,
4425 					    &linkinfo_len);
4426 	if (QDF_IS_STATUS_ERROR(ret)) {
4427 		qdf_mem_free(mlieseqpayload_copy);
4428 		return ret;
4429 	}
4430 
4431 	/* In case Link Info is absent, the number of links will remain
4432 	 * zero.
4433 	 */
4434 	if (!linkinfo) {
4435 		qdf_mem_free(mlieseqpayload_copy);
4436 		return QDF_STATUS_SUCCESS;
4437 	}
4438 
4439 	ret = util_parse_rv_info_from_linkinfo(linkinfo, linkinfo_len, &rinfo);
4440 	if (QDF_IS_STATUS_ERROR(ret)) {
4441 		qdf_mem_free(mlieseqpayload_copy);
4442 		return ret;
4443 	}
4444 
4445 	qdf_mem_copy(reconfig_info, &rinfo, sizeof(*reconfig_info));
4446 	qdf_mem_free(mlieseqpayload_copy);
4447 
4448 	return QDF_STATUS_SUCCESS;
4449 }
4450 
4451 #endif
4452