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