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