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