xref: /wlan-driver/qca-wifi-host-cmn/wmi/src/wmi_tlv_helper.c (revision 5113495b16420b49004c444715d2daae2066e7dc)
1*5113495bSYour Name /*
2*5113495bSYour Name  * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
3*5113495bSYour Name  * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4*5113495bSYour Name  *
5*5113495bSYour Name  * Permission to use, copy, modify, and/or distribute this software for
6*5113495bSYour Name  * any purpose with or without fee is hereby granted, provided that the
7*5113495bSYour Name  * above copyright notice and this permission notice appear in all
8*5113495bSYour Name  * copies.
9*5113495bSYour Name  *
10*5113495bSYour Name  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11*5113495bSYour Name  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12*5113495bSYour Name  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13*5113495bSYour Name  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14*5113495bSYour Name  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15*5113495bSYour Name  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16*5113495bSYour Name  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17*5113495bSYour Name  * PERFORMANCE OF THIS SOFTWARE.
18*5113495bSYour Name  */
19*5113495bSYour Name 
20*5113495bSYour Name #include "wmi_tlv_platform.c"
21*5113495bSYour Name #include "wmi_tlv_defs.h"
22*5113495bSYour Name #include "wmi_version.h"
23*5113495bSYour Name #include "qdf_module.h"
24*5113495bSYour Name 
25*5113495bSYour Name #define WMITLV_GET_ATTRIB_NUM_TLVS  0xFFFFFFFF
26*5113495bSYour Name 
27*5113495bSYour Name #define WMITLV_GET_CMDID(val) (val & 0x00FFFFFF)
28*5113495bSYour Name #define WMITLV_GET_NUM_TLVS(val) ((val >> 24) & 0xFF)
29*5113495bSYour Name 
30*5113495bSYour Name #define WMITLV_GET_TAGID(val) (val & 0x00000FFF)
31*5113495bSYour Name #define WMITLV_GET_TAG_STRUCT_SIZE(val) ((val >> 12) & 0x000001FF)
32*5113495bSYour Name #define WMITLV_GET_TAG_ARRAY_SIZE(val) ((val >> 21) & 0x000001FF)
33*5113495bSYour Name #define WMITLV_GET_TAG_VARIED(val) ((val >> 30) & 0x00000001)
34*5113495bSYour Name 
35*5113495bSYour Name #define WMITLV_SET_ATTRB0(id) ((WMITLV_GET_TAG_NUM_TLV_ATTRIB(id) << 24) | \
36*5113495bSYour Name 				(id & 0x00FFFFFF))
37*5113495bSYour Name #define WMITLV_SET_ATTRB1(tagID, tagStructSize, tagArraySize, tagVaried) \
38*5113495bSYour Name 	(((tagVaried&0x1)<<30) | ((tagArraySize&0x1FF)<<21) | \
39*5113495bSYour Name 	((tagStructSize&0x1FF)<<12) | (tagID&0xFFF))
40*5113495bSYour Name 
41*5113495bSYour Name #define WMITLV_OP_SET_TLV_ATTRIB_macro(param_ptr, param_len, wmi_cmd_event_id, \
42*5113495bSYour Name 	elem_tlv_tag, elem_struc_type, elem_name, var_len, arr_size)  \
43*5113495bSYour Name 	WMITLV_SET_ATTRB1(elem_tlv_tag, sizeof(elem_struc_type), arr_size, var_len),
44*5113495bSYour Name 
45*5113495bSYour Name #define WMITLV_GET_CMD_EVT_ATTRB_LIST(id) \
46*5113495bSYour Name 	WMITLV_SET_ATTRB0(id), \
47*5113495bSYour Name 	WMITLV_TABLE(id,SET_TLV_ATTRIB, NULL, 0)
48*5113495bSYour Name 
49*5113495bSYour Name uint32_t cmd_attr_list[] = {
50*5113495bSYour Name 	WMITLV_ALL_CMD_LIST(WMITLV_GET_CMD_EVT_ATTRB_LIST)
51*5113495bSYour Name };
52*5113495bSYour Name 
53*5113495bSYour Name uint32_t evt_attr_list[] = {
54*5113495bSYour Name 	WMITLV_ALL_EVT_LIST(WMITLV_GET_CMD_EVT_ATTRB_LIST)
55*5113495bSYour Name };
56*5113495bSYour Name 
57*5113495bSYour Name #ifdef NO_DYNAMIC_MEM_ALLOC
58*5113495bSYour Name static wmitlv_cmd_param_info *g_wmi_static_cmd_param_info_buf;
59*5113495bSYour Name uint32_t g_wmi_static_max_cmd_param_tlvs;
60*5113495bSYour Name #endif
61*5113495bSYour Name 
62*5113495bSYour Name 
63*5113495bSYour Name /**
64*5113495bSYour Name  * wmitlv_set_static_param_tlv_buf() - tlv helper function
65*5113495bSYour Name  * @param_tlv_buf: tlv buffer parameter
66*5113495bSYour Name  * @max_tlvs_accommodated: max no of tlv entries
67*5113495bSYour Name  *
68*5113495bSYour Name  *
69*5113495bSYour Name  * WMI TLV Helper function to set the static cmd_param_tlv structure
70*5113495bSYour Name  * and number of TLVs that can be accommodated in the structure.
71*5113495bSYour Name  * This function should be used when dynamic memory allocation is not
72*5113495bSYour Name  * supported. When dynamic memory allocation is not supported by any
73*5113495bSYour Name  * component then NO_DYNAMIC_MEMALLOC macro has to be defined in respective
74*5113495bSYour Name  * tlv_platform.c file. And respective component has to allocate
75*5113495bSYour Name  * cmd_param_tlv structure buffer to accommodate whatever number of TLV's.
76*5113495bSYour Name  * Both the buffer address and number of TLV's that can be accommodated in
77*5113495bSYour Name  * the buffer should be sent as arguments to this function.
78*5113495bSYour Name  *
79*5113495bSYour Name  * Return None
80*5113495bSYour Name  */
81*5113495bSYour Name void
wmitlv_set_static_param_tlv_buf(void * param_tlv_buf,uint32_t max_tlvs_accommodated)82*5113495bSYour Name wmitlv_set_static_param_tlv_buf(void *param_tlv_buf,
83*5113495bSYour Name 				uint32_t max_tlvs_accommodated)
84*5113495bSYour Name {
85*5113495bSYour Name #ifdef NO_DYNAMIC_MEM_ALLOC
86*5113495bSYour Name 	g_wmi_static_cmd_param_info_buf = param_tlv_buf;
87*5113495bSYour Name 	g_wmi_static_max_cmd_param_tlvs = max_tlvs_accommodated;
88*5113495bSYour Name #endif
89*5113495bSYour Name }
90*5113495bSYour Name 
91*5113495bSYour Name /**
92*5113495bSYour Name  * wmitlv_get_attributes() - tlv helper function
93*5113495bSYour Name  * @is_cmd_id: boolean for command attribute
94*5113495bSYour Name  * @cmd_event_id: command event id
95*5113495bSYour Name  * @curr_tlv_order: tlv order
96*5113495bSYour Name  * @tlv_attr_ptr: pointer to tlv attribute
97*5113495bSYour Name  *
98*5113495bSYour Name  *
99*5113495bSYour Name  * WMI TLV Helper functions to find the attributes of the
100*5113495bSYour Name  * Command/Event TLVs.
101*5113495bSYour Name  *
102*5113495bSYour Name  * Return: 0 if success. Return >=1 if failure.
103*5113495bSYour Name  */
104*5113495bSYour Name static
wmitlv_get_attributes(uint32_t is_cmd_id,uint32_t cmd_event_id,uint32_t curr_tlv_order,wmitlv_attributes_struc * tlv_attr_ptr)105*5113495bSYour Name uint32_t wmitlv_get_attributes(uint32_t is_cmd_id, uint32_t cmd_event_id,
106*5113495bSYour Name 			       uint32_t curr_tlv_order,
107*5113495bSYour Name 			       wmitlv_attributes_struc *tlv_attr_ptr)
108*5113495bSYour Name {
109*5113495bSYour Name 	uint32_t i, base_index, num_tlvs, num_entries;
110*5113495bSYour Name 	uint32_t *pAttrArrayList;
111*5113495bSYour Name 
112*5113495bSYour Name 	if (is_cmd_id) {
113*5113495bSYour Name 		pAttrArrayList = &cmd_attr_list[0];
114*5113495bSYour Name 		num_entries = QDF_ARRAY_SIZE(cmd_attr_list);
115*5113495bSYour Name 	} else {
116*5113495bSYour Name 		pAttrArrayList = &evt_attr_list[0];
117*5113495bSYour Name 		num_entries = QDF_ARRAY_SIZE(evt_attr_list);
118*5113495bSYour Name 	}
119*5113495bSYour Name 
120*5113495bSYour Name 	for (i = 0; i < num_entries; i++) {
121*5113495bSYour Name 		num_tlvs = WMITLV_GET_NUM_TLVS(pAttrArrayList[i]);
122*5113495bSYour Name 		if (WMITLV_GET_CMDID(cmd_event_id) ==
123*5113495bSYour Name 		    WMITLV_GET_CMDID(pAttrArrayList[i])) {
124*5113495bSYour Name 			tlv_attr_ptr->cmd_num_tlv = num_tlvs;
125*5113495bSYour Name 			/* Return success from here when only number of TLVS for
126*5113495bSYour Name 			 * this command/event is required */
127*5113495bSYour Name 			if (curr_tlv_order == WMITLV_GET_ATTRIB_NUM_TLVS) {
128*5113495bSYour Name 				wmi_tlv_print_verbose
129*5113495bSYour Name 					("%s: WMI TLV attribute definitions for %s:0x%x found; num_of_tlvs:%d\n",
130*5113495bSYour Name 					__func__, (is_cmd_id ? "Cmd" : "Evt"),
131*5113495bSYour Name 					cmd_event_id, num_tlvs);
132*5113495bSYour Name 				return 0;
133*5113495bSYour Name 			}
134*5113495bSYour Name 
135*5113495bSYour Name 			/* Return failure if tlv_order is more than the expected
136*5113495bSYour Name 			 * number of TLVs */
137*5113495bSYour Name 			if (curr_tlv_order >= num_tlvs) {
138*5113495bSYour Name 				wmi_tlv_print_error
139*5113495bSYour Name 					("%s: ERROR: TLV order %d greater than num_of_tlvs:%d for %s:0x%x\n",
140*5113495bSYour Name 					__func__, curr_tlv_order, num_tlvs,
141*5113495bSYour Name 					(is_cmd_id ? "Cmd" : "Evt"), cmd_event_id);
142*5113495bSYour Name 				return 1;
143*5113495bSYour Name 			}
144*5113495bSYour Name 
145*5113495bSYour Name 			base_index = i + 1;     /* index to first TLV attributes */
146*5113495bSYour Name 			wmi_tlv_print_verbose
147*5113495bSYour Name 				("%s: WMI TLV attributes for %s:0x%x tlv[%d]:0x%x\n",
148*5113495bSYour Name 				__func__, (is_cmd_id ? "Cmd" : "Evt"),
149*5113495bSYour Name 				cmd_event_id, curr_tlv_order,
150*5113495bSYour Name 				pAttrArrayList[(base_index + curr_tlv_order)]);
151*5113495bSYour Name 			tlv_attr_ptr->tag_order = curr_tlv_order;
152*5113495bSYour Name 			tlv_attr_ptr->tag_id =
153*5113495bSYour Name 				WMITLV_GET_TAGID(pAttrArrayList
154*5113495bSYour Name 						 [(base_index + curr_tlv_order)]);
155*5113495bSYour Name 			tlv_attr_ptr->tag_struct_size =
156*5113495bSYour Name 				WMITLV_GET_TAG_STRUCT_SIZE(pAttrArrayList
157*5113495bSYour Name 							   [(base_index +
158*5113495bSYour Name 							     curr_tlv_order)]);
159*5113495bSYour Name 			tlv_attr_ptr->tag_varied_size =
160*5113495bSYour Name 				WMITLV_GET_TAG_VARIED(pAttrArrayList
161*5113495bSYour Name 						      [(base_index +
162*5113495bSYour Name 							curr_tlv_order)]);
163*5113495bSYour Name 			tlv_attr_ptr->tag_array_size =
164*5113495bSYour Name 				WMITLV_GET_TAG_ARRAY_SIZE(pAttrArrayList
165*5113495bSYour Name 							  [(base_index +
166*5113495bSYour Name 							    curr_tlv_order)]);
167*5113495bSYour Name 			return 0;
168*5113495bSYour Name 		}
169*5113495bSYour Name 		i += num_tlvs;
170*5113495bSYour Name 	}
171*5113495bSYour Name 
172*5113495bSYour Name 	wmi_tlv_print_error
173*5113495bSYour Name 		("%s: ERROR: Didn't found WMI TLV attribute definitions for %s:0x%x\n",
174*5113495bSYour Name 		__func__, (is_cmd_id ? "Cmd" : "Evt"), cmd_event_id);
175*5113495bSYour Name 	return 1;
176*5113495bSYour Name }
177*5113495bSYour Name 
178*5113495bSYour Name /**
179*5113495bSYour Name  * wmitlv_check_tlv_params() - tlv helper function
180*5113495bSYour Name  * @os_handle: os context handle
181*5113495bSYour Name  * @param_struc_ptr: pointer to tlv structure
182*5113495bSYour Name  * @param_buf_len: length of input buffer
183*5113495bSYour Name  * @is_cmd_id: boolean for command attribute
184*5113495bSYour Name  * @wmi_cmd_event_id: command event id
185*5113495bSYour Name  *
186*5113495bSYour Name  *
187*5113495bSYour Name  * Helper Function to validate the prepared TLV's for
188*5113495bSYour Name  * an WMI event/command to be sent.
189*5113495bSYour Name  *
190*5113495bSYour Name  * Return: 0 if success. Return < 0 if failure.
191*5113495bSYour Name  */
192*5113495bSYour Name static int
wmitlv_check_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t is_cmd_id,uint32_t wmi_cmd_event_id)193*5113495bSYour Name wmitlv_check_tlv_params(void *os_handle, void *param_struc_ptr,
194*5113495bSYour Name 			uint32_t param_buf_len, uint32_t is_cmd_id,
195*5113495bSYour Name 			uint32_t wmi_cmd_event_id)
196*5113495bSYour Name {
197*5113495bSYour Name 	wmitlv_attributes_struc attr_struct_ptr;
198*5113495bSYour Name 	uint32_t buf_idx = 0;
199*5113495bSYour Name 	uint32_t tlv_index = 0;
200*5113495bSYour Name 	uint8_t *buf_ptr = (unsigned char *)param_struc_ptr;
201*5113495bSYour Name 	uint32_t expected_num_tlvs, expected_tlv_len;
202*5113495bSYour Name 	int32_t error = -1;
203*5113495bSYour Name 
204*5113495bSYour Name 	/* Get the number of TLVs for this command/event */
205*5113495bSYour Name 	if (wmitlv_get_attributes
206*5113495bSYour Name 		    (is_cmd_id, wmi_cmd_event_id, WMITLV_GET_ATTRIB_NUM_TLVS,
207*5113495bSYour Name 		    &attr_struct_ptr) != 0) {
208*5113495bSYour Name 		wmi_tlv_print_error
209*5113495bSYour Name 			("%s: ERROR: Couldn't get expected number of TLVs for Cmd=%d\n",
210*5113495bSYour Name 			__func__, wmi_cmd_event_id);
211*5113495bSYour Name 		goto Error_wmitlv_check_tlv_params;
212*5113495bSYour Name 	}
213*5113495bSYour Name 
214*5113495bSYour Name 	/* NOTE: the returned number of TLVs is in "attr_struct_ptr.cmd_num_tlv" */
215*5113495bSYour Name 
216*5113495bSYour Name 	expected_num_tlvs = attr_struct_ptr.cmd_num_tlv;
217*5113495bSYour Name 
218*5113495bSYour Name 	while ((buf_idx + WMI_TLV_HDR_SIZE) <= param_buf_len) {
219*5113495bSYour Name 		uint32_t curr_tlv_tag =
220*5113495bSYour Name 			WMITLV_GET_TLVTAG(WMITLV_GET_HDR(buf_ptr));
221*5113495bSYour Name 		uint32_t curr_tlv_len =
222*5113495bSYour Name 			WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
223*5113495bSYour Name 
224*5113495bSYour Name 		if ((buf_idx + WMI_TLV_HDR_SIZE + curr_tlv_len) > param_buf_len) {
225*5113495bSYour Name 			wmi_tlv_print_error
226*5113495bSYour Name 				("%s: ERROR: Invalid TLV length for Cmd=%d Tag_order=%d buf_idx=%d Tag:%d Len:%d TotalLen:%d\n",
227*5113495bSYour Name 				__func__, wmi_cmd_event_id, tlv_index, buf_idx,
228*5113495bSYour Name 				curr_tlv_tag, curr_tlv_len, param_buf_len);
229*5113495bSYour Name 			goto Error_wmitlv_check_tlv_params;
230*5113495bSYour Name 		}
231*5113495bSYour Name 
232*5113495bSYour Name 		/* Get the attributes of the TLV with the given order in "tlv_index" */
233*5113495bSYour Name 		wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
234*5113495bSYour Name 				   sizeof(wmitlv_attributes_struc));
235*5113495bSYour Name 		if (wmitlv_get_attributes
236*5113495bSYour Name 			    (is_cmd_id, wmi_cmd_event_id, tlv_index,
237*5113495bSYour Name 			    &attr_struct_ptr) != 0) {
238*5113495bSYour Name 			wmi_tlv_print_error
239*5113495bSYour Name 				("%s: ERROR: No TLV attributes found for Cmd=%d Tag_order=%d\n",
240*5113495bSYour Name 				__func__, wmi_cmd_event_id, tlv_index);
241*5113495bSYour Name 			goto Error_wmitlv_check_tlv_params;
242*5113495bSYour Name 		}
243*5113495bSYour Name 
244*5113495bSYour Name 		/* Found the TLV that we wanted */
245*5113495bSYour Name 		wmi_tlv_print_verbose("%s: [tlv %d]: tag=%d, len=%d\n",
246*5113495bSYour Name 				      __func__, tlv_index, curr_tlv_tag,
247*5113495bSYour Name 				      curr_tlv_len);
248*5113495bSYour Name 
249*5113495bSYour Name 		/* Validating Tag ID order */
250*5113495bSYour Name 		if (curr_tlv_tag != attr_struct_ptr.tag_id) {
251*5113495bSYour Name 			wmi_tlv_print_error
252*5113495bSYour Name 				("%s: ERROR: TLV has wrong tag in order for Cmd=0x%x. Given=%d, Expected=%d.\n",
253*5113495bSYour Name 				__func__, wmi_cmd_event_id, curr_tlv_tag,
254*5113495bSYour Name 				attr_struct_ptr.tag_id);
255*5113495bSYour Name 			goto Error_wmitlv_check_tlv_params;
256*5113495bSYour Name 		}
257*5113495bSYour Name 
258*5113495bSYour Name 		/* Validate Tag length */
259*5113495bSYour Name 		/* Array TLVs length checking needs special handling */
260*5113495bSYour Name 		if ((curr_tlv_tag >= WMITLV_TAG_FIRST_ARRAY_ENUM)
261*5113495bSYour Name 		    && (curr_tlv_tag <= WMITLV_TAG_LAST_ARRAY_ENUM)) {
262*5113495bSYour Name 			if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
263*5113495bSYour Name 				/* Array size can't be invalid for fixed size Array TLV */
264*5113495bSYour Name 				if (WMITLV_ARR_SIZE_INVALID ==
265*5113495bSYour Name 				    attr_struct_ptr.tag_array_size) {
266*5113495bSYour Name 					wmi_tlv_print_error
267*5113495bSYour Name 						("%s: ERROR: array_size can't be invalid for Array TLV Cmd=0x%x Tag=%d\n",
268*5113495bSYour Name 						__func__, wmi_cmd_event_id,
269*5113495bSYour Name 						curr_tlv_tag);
270*5113495bSYour Name 					goto Error_wmitlv_check_tlv_params;
271*5113495bSYour Name 				}
272*5113495bSYour Name 
273*5113495bSYour Name 				expected_tlv_len =
274*5113495bSYour Name 					attr_struct_ptr.tag_array_size *
275*5113495bSYour Name 					attr_struct_ptr.tag_struct_size;
276*5113495bSYour Name 				/* Paddding is only required for Byte array Tlvs all other
277*5113495bSYour Name 				 * array tlv's should be aligned to 4 bytes during their
278*5113495bSYour Name 				 * definition */
279*5113495bSYour Name 				if (WMITLV_TAG_ARRAY_BYTE ==
280*5113495bSYour Name 				    attr_struct_ptr.tag_id) {
281*5113495bSYour Name 					expected_tlv_len =
282*5113495bSYour Name 						roundup(expected_tlv_len,
283*5113495bSYour Name 							sizeof(uint32_t));
284*5113495bSYour Name 				}
285*5113495bSYour Name 
286*5113495bSYour Name 				if (curr_tlv_len != expected_tlv_len) {
287*5113495bSYour Name 					wmi_tlv_print_error
288*5113495bSYour Name 						("%s: ERROR: TLV has wrong length for Cmd=0x%x. Tag_order=%d  Tag=%d, Given_Len:%d Expected_Len=%d.\n",
289*5113495bSYour Name 						__func__, wmi_cmd_event_id,
290*5113495bSYour Name 						tlv_index, curr_tlv_tag,
291*5113495bSYour Name 						curr_tlv_len, expected_tlv_len);
292*5113495bSYour Name 					goto Error_wmitlv_check_tlv_params;
293*5113495bSYour Name 				}
294*5113495bSYour Name 			} else {
295*5113495bSYour Name 				/* Array size should be invalid for variable size Array TLV */
296*5113495bSYour Name 				if (WMITLV_ARR_SIZE_INVALID !=
297*5113495bSYour Name 				    attr_struct_ptr.tag_array_size) {
298*5113495bSYour Name 					wmi_tlv_print_error
299*5113495bSYour Name 						("%s: ERROR: array_size should be invalid for Array TLV Cmd=0x%x Tag=%d\n",
300*5113495bSYour Name 						__func__, wmi_cmd_event_id,
301*5113495bSYour Name 						curr_tlv_tag);
302*5113495bSYour Name 					goto Error_wmitlv_check_tlv_params;
303*5113495bSYour Name 				}
304*5113495bSYour Name 
305*5113495bSYour Name 				/* Incase of variable length TLV's, there is no expectation
306*5113495bSYour Name 				 * on the length field so do whatever checking you can
307*5113495bSYour Name 				 * depending on the TLV tag if TLV length is non-zero */
308*5113495bSYour Name 				if (curr_tlv_len != 0) {
309*5113495bSYour Name 					/* Verify TLV length is aligned to the size of structure */
310*5113495bSYour Name 					if ((curr_tlv_len %
311*5113495bSYour Name 					     attr_struct_ptr.tag_struct_size) !=
312*5113495bSYour Name 					    0) {
313*5113495bSYour Name 						wmi_tlv_print_error
314*5113495bSYour Name 							("%s: ERROR: TLV length %d for Cmd=0x%x is not aligned to size of structure(%d bytes)\n",
315*5113495bSYour Name 							__func__, curr_tlv_len,
316*5113495bSYour Name 							wmi_cmd_event_id,
317*5113495bSYour Name 							attr_struct_ptr.
318*5113495bSYour Name 							tag_struct_size);
319*5113495bSYour Name 						goto Error_wmitlv_check_tlv_params;
320*5113495bSYour Name 					}
321*5113495bSYour Name 
322*5113495bSYour Name 					if (curr_tlv_tag ==
323*5113495bSYour Name 					    WMITLV_TAG_ARRAY_STRUC) {
324*5113495bSYour Name 						uint8_t *tlv_buf_ptr = NULL;
325*5113495bSYour Name 						uint32_t in_tlv_len;
326*5113495bSYour Name 						uint32_t idx;
327*5113495bSYour Name 						uint32_t num_of_elems;
328*5113495bSYour Name 
329*5113495bSYour Name 						/* Verify length of inner TLVs */
330*5113495bSYour Name 
331*5113495bSYour Name 						num_of_elems =
332*5113495bSYour Name 							curr_tlv_len /
333*5113495bSYour Name 							attr_struct_ptr.
334*5113495bSYour Name 							tag_struct_size;
335*5113495bSYour Name 						/* Set tlv_buf_ptr to the first inner TLV address */
336*5113495bSYour Name 						tlv_buf_ptr =
337*5113495bSYour Name 							buf_ptr + WMI_TLV_HDR_SIZE;
338*5113495bSYour Name 						for (idx = 0;
339*5113495bSYour Name 						     idx < num_of_elems;
340*5113495bSYour Name 						     idx++) {
341*5113495bSYour Name 							in_tlv_len =
342*5113495bSYour Name 								WMITLV_GET_TLVLEN
343*5113495bSYour Name 									(WMITLV_GET_HDR
344*5113495bSYour Name 										(tlv_buf_ptr));
345*5113495bSYour Name 							if ((in_tlv_len +
346*5113495bSYour Name 							     WMI_TLV_HDR_SIZE)
347*5113495bSYour Name 							    !=
348*5113495bSYour Name 							    attr_struct_ptr.
349*5113495bSYour Name 							    tag_struct_size) {
350*5113495bSYour Name 								wmi_tlv_print_error
351*5113495bSYour Name 									("%s: ERROR: TLV has wrong length for Cmd=0x%x. Tag_order=%d  Tag=%d, Given_Len:%zu Expected_Len=%d.\n",
352*5113495bSYour Name 									__func__,
353*5113495bSYour Name 									wmi_cmd_event_id,
354*5113495bSYour Name 									tlv_index,
355*5113495bSYour Name 									curr_tlv_tag,
356*5113495bSYour Name 									(in_tlv_len
357*5113495bSYour Name 									 +
358*5113495bSYour Name 									 WMI_TLV_HDR_SIZE),
359*5113495bSYour Name 									attr_struct_ptr.
360*5113495bSYour Name 									tag_struct_size);
361*5113495bSYour Name 								goto Error_wmitlv_check_tlv_params;
362*5113495bSYour Name 							}
363*5113495bSYour Name 							tlv_buf_ptr +=
364*5113495bSYour Name 								in_tlv_len +
365*5113495bSYour Name 								WMI_TLV_HDR_SIZE;
366*5113495bSYour Name 						}
367*5113495bSYour Name 					} else
368*5113495bSYour Name 					if ((curr_tlv_tag ==
369*5113495bSYour Name 					     WMITLV_TAG_ARRAY_UINT32)
370*5113495bSYour Name 					    || (curr_tlv_tag ==
371*5113495bSYour Name 						WMITLV_TAG_ARRAY_BYTE)
372*5113495bSYour Name 					    || (curr_tlv_tag ==
373*5113495bSYour Name 						WMITLV_TAG_ARRAY_FIXED_STRUC)) {
374*5113495bSYour Name 						/* Nothing to verify here */
375*5113495bSYour Name 					} else {
376*5113495bSYour Name 						wmi_tlv_print_error
377*5113495bSYour Name 							("%s ERROR Need to handle the Array tlv %d for variable length for Cmd=0x%x\n",
378*5113495bSYour Name 							__func__,
379*5113495bSYour Name 							attr_struct_ptr.tag_id,
380*5113495bSYour Name 							wmi_cmd_event_id);
381*5113495bSYour Name 						goto Error_wmitlv_check_tlv_params;
382*5113495bSYour Name 					}
383*5113495bSYour Name 				}
384*5113495bSYour Name 			}
385*5113495bSYour Name 		} else {
386*5113495bSYour Name 			/* Non-array TLV. */
387*5113495bSYour Name 
388*5113495bSYour Name 			if ((curr_tlv_len + WMI_TLV_HDR_SIZE) !=
389*5113495bSYour Name 			    attr_struct_ptr.tag_struct_size) {
390*5113495bSYour Name 				wmi_tlv_print_error
391*5113495bSYour Name 					("%s: ERROR: TLV has wrong length for Cmd=0x%x. Given=%zu, Expected=%d.\n",
392*5113495bSYour Name 					__func__, wmi_cmd_event_id,
393*5113495bSYour Name 					(curr_tlv_len + WMI_TLV_HDR_SIZE),
394*5113495bSYour Name 					attr_struct_ptr.tag_struct_size);
395*5113495bSYour Name 				goto Error_wmitlv_check_tlv_params;
396*5113495bSYour Name 			}
397*5113495bSYour Name 		}
398*5113495bSYour Name 
399*5113495bSYour Name 		/* Check TLV length is aligned to 4 bytes or not */
400*5113495bSYour Name 		if ((curr_tlv_len % sizeof(uint32_t)) != 0) {
401*5113495bSYour Name 			wmi_tlv_print_error
402*5113495bSYour Name 				("%s: ERROR: TLV length %d for Cmd=0x%x is not aligned to %zu bytes\n",
403*5113495bSYour Name 				__func__, curr_tlv_len, wmi_cmd_event_id,
404*5113495bSYour Name 				sizeof(uint32_t));
405*5113495bSYour Name 			goto Error_wmitlv_check_tlv_params;
406*5113495bSYour Name 		}
407*5113495bSYour Name 
408*5113495bSYour Name 		tlv_index++;
409*5113495bSYour Name 		buf_ptr += curr_tlv_len + WMI_TLV_HDR_SIZE;
410*5113495bSYour Name 		buf_idx += curr_tlv_len + WMI_TLV_HDR_SIZE;
411*5113495bSYour Name 	}
412*5113495bSYour Name 
413*5113495bSYour Name 	if (tlv_index != expected_num_tlvs) {
414*5113495bSYour Name 		wmi_tlv_print_verbose
415*5113495bSYour Name 			("%s: INFO: Less number of TLVs filled for Cmd=0x%x Filled %d Expected=%d\n",
416*5113495bSYour Name 			__func__, wmi_cmd_event_id, tlv_index, expected_num_tlvs);
417*5113495bSYour Name 	}
418*5113495bSYour Name 
419*5113495bSYour Name 	return 0;
420*5113495bSYour Name Error_wmitlv_check_tlv_params:
421*5113495bSYour Name 	return error;
422*5113495bSYour Name }
423*5113495bSYour Name 
424*5113495bSYour Name /**
425*5113495bSYour Name  * wmitlv_check_event_tlv_params() - tlv helper function
426*5113495bSYour Name  * @os_handle: os context handle
427*5113495bSYour Name  * @param_struc_ptr: pointer to tlv structure
428*5113495bSYour Name  * @param_buf_len: length of input buffer
429*5113495bSYour Name  * @wmi_cmd_event_id: command event id
430*5113495bSYour Name  *
431*5113495bSYour Name  *
432*5113495bSYour Name  * Helper Function to validate the prepared TLV's for
433*5113495bSYour Name  * an WMI event/command to be sent.
434*5113495bSYour Name  *
435*5113495bSYour Name  * Return: 0 if success. Return < 0 if failure.
436*5113495bSYour Name  */
437*5113495bSYour Name int
wmitlv_check_event_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id)438*5113495bSYour Name wmitlv_check_event_tlv_params(void *os_handle, void *param_struc_ptr,
439*5113495bSYour Name 			      uint32_t param_buf_len, uint32_t wmi_cmd_event_id)
440*5113495bSYour Name {
441*5113495bSYour Name 	uint32_t is_cmd_id = 0;
442*5113495bSYour Name 
443*5113495bSYour Name 	return wmitlv_check_tlv_params
444*5113495bSYour Name 			(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
445*5113495bSYour Name 			wmi_cmd_event_id);
446*5113495bSYour Name }
447*5113495bSYour Name 
448*5113495bSYour Name /**
449*5113495bSYour Name  * wmitlv_check_command_tlv_params() - tlv helper function
450*5113495bSYour Name  * @os_handle: os context handle
451*5113495bSYour Name  * @param_struc_ptr: pointer to tlv structure
452*5113495bSYour Name  * @param_buf_len: length of input buffer
453*5113495bSYour Name  * @wmi_cmd_event_id: command event id
454*5113495bSYour Name  *
455*5113495bSYour Name  *
456*5113495bSYour Name  * Helper Function to validate the prepared TLV's for
457*5113495bSYour Name  * an WMI event/command to be sent.
458*5113495bSYour Name  *
459*5113495bSYour Name  * Return: 0 if success. Return < 0 if failure.
460*5113495bSYour Name  */
461*5113495bSYour Name int
wmitlv_check_command_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id)462*5113495bSYour Name wmitlv_check_command_tlv_params(void *os_handle, void *param_struc_ptr,
463*5113495bSYour Name 				uint32_t param_buf_len,
464*5113495bSYour Name 				uint32_t wmi_cmd_event_id)
465*5113495bSYour Name {
466*5113495bSYour Name 	uint32_t is_cmd_id = 1;
467*5113495bSYour Name 
468*5113495bSYour Name 	return wmitlv_check_tlv_params
469*5113495bSYour Name 			(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
470*5113495bSYour Name 			wmi_cmd_event_id);
471*5113495bSYour Name }
472*5113495bSYour Name qdf_export_symbol(wmitlv_check_command_tlv_params);
473*5113495bSYour Name 
474*5113495bSYour Name /**
475*5113495bSYour Name  * wmitlv_check_and_pad_tlvs() - tlv helper function
476*5113495bSYour Name  * @os_handle: os context handle
477*5113495bSYour Name  * @param_buf_len: length of tlv parameter
478*5113495bSYour Name  * @param_struc_ptr: pointer to tlv structure
479*5113495bSYour Name  * @is_cmd_id: boolean for command attribute
480*5113495bSYour Name  * @wmi_cmd_event_id: command event id
481*5113495bSYour Name  * @wmi_cmd_struct_ptr: wmi command structure
482*5113495bSYour Name  *
483*5113495bSYour Name  *
484*5113495bSYour Name  * validate the TLV's coming for an event/command and
485*5113495bSYour Name  * also pads data to TLV's if necessary
486*5113495bSYour Name  *
487*5113495bSYour Name  * Return: 0 if success. Return < 0 if failure.
488*5113495bSYour Name  */
489*5113495bSYour Name static int
wmitlv_check_and_pad_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t is_cmd_id,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)490*5113495bSYour Name wmitlv_check_and_pad_tlvs(void *os_handle, void *param_struc_ptr,
491*5113495bSYour Name 			  uint32_t param_buf_len, uint32_t is_cmd_id,
492*5113495bSYour Name 			  uint32_t wmi_cmd_event_id, void **wmi_cmd_struct_ptr)
493*5113495bSYour Name {
494*5113495bSYour Name 	wmitlv_attributes_struc attr_struct_ptr;
495*5113495bSYour Name 	uint32_t buf_idx = 0;
496*5113495bSYour Name 	uint32_t tlv_index = 0;
497*5113495bSYour Name 	uint32_t num_of_elems = 0;
498*5113495bSYour Name 	int tlv_size_diff = 0;
499*5113495bSYour Name 	uint8_t *buf_ptr = (unsigned char *)param_struc_ptr;
500*5113495bSYour Name 	wmitlv_cmd_param_info *cmd_param_tlvs_ptr = NULL;
501*5113495bSYour Name 	uint32_t remaining_expected_tlvs = 0xFFFFFFFF;
502*5113495bSYour Name 	uint32_t len_wmi_cmd_struct_buf;
503*5113495bSYour Name 	uint32_t free_buf_len;
504*5113495bSYour Name 	int32_t error = -1;
505*5113495bSYour Name 
506*5113495bSYour Name 	/* Get the number of TLVs for this command/event */
507*5113495bSYour Name 	if (wmitlv_get_attributes
508*5113495bSYour Name 		    (is_cmd_id, wmi_cmd_event_id, WMITLV_GET_ATTRIB_NUM_TLVS,
509*5113495bSYour Name 		    &attr_struct_ptr) != 0) {
510*5113495bSYour Name 		wmi_tlv_print_error
511*5113495bSYour Name 			("%s: ERROR: Couldn't get expected number of TLVs for Cmd=%d\n",
512*5113495bSYour Name 			__func__, wmi_cmd_event_id);
513*5113495bSYour Name 		return error;
514*5113495bSYour Name 	}
515*5113495bSYour Name 	/* NOTE: the returned number of TLVs is in "attr_struct_ptr.cmd_num_tlv" */
516*5113495bSYour Name 
517*5113495bSYour Name 	if (param_buf_len < WMI_TLV_HDR_SIZE) {
518*5113495bSYour Name 		wmi_tlv_print_error
519*5113495bSYour Name 			("%s: ERROR: Incorrect param buf length passed\n",
520*5113495bSYour Name 			__func__);
521*5113495bSYour Name 		return error;
522*5113495bSYour Name 	}
523*5113495bSYour Name 
524*5113495bSYour Name 	/* Create base structure of format wmi_cmd_event_id##_param_tlvs */
525*5113495bSYour Name 	len_wmi_cmd_struct_buf =
526*5113495bSYour Name 		attr_struct_ptr.cmd_num_tlv * sizeof(wmitlv_cmd_param_info);
527*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
528*5113495bSYour Name 	/* Dynamic memory allocation supported */
529*5113495bSYour Name 	wmi_tlv_os_mem_alloc(os_handle, *wmi_cmd_struct_ptr,
530*5113495bSYour Name 			     len_wmi_cmd_struct_buf);
531*5113495bSYour Name #else
532*5113495bSYour Name 	/* Dynamic memory allocation is not supported. Use the buffer
533*5113495bSYour Name 	 * g_wmi_static_cmd_param_info_buf, which should be set using
534*5113495bSYour Name 	 * wmi_tlv_set_static_param_tlv_buf(),
535*5113495bSYour Name 	 * for base structure of format wmi_cmd_event_id##_param_tlvs */
536*5113495bSYour Name 	*wmi_cmd_struct_ptr = g_wmi_static_cmd_param_info_buf;
537*5113495bSYour Name 	if (attr_struct_ptr.cmd_num_tlv > g_wmi_static_max_cmd_param_tlvs) {
538*5113495bSYour Name 		/* Error: Expecting more TLVs that accommodated for static structure  */
539*5113495bSYour Name 		wmi_tlv_print_error
540*5113495bSYour Name 			("%s: Error: Expecting more TLVs that accommodated for static structure. Expected:%d Accommodated:%d\n",
541*5113495bSYour Name 			__func__, attr_struct_ptr.cmd_num_tlv,
542*5113495bSYour Name 			g_wmi_static_max_cmd_param_tlvs);
543*5113495bSYour Name 		return error;
544*5113495bSYour Name 	}
545*5113495bSYour Name #endif
546*5113495bSYour Name 	if (!*wmi_cmd_struct_ptr) {
547*5113495bSYour Name 		/* Error: unable to alloc memory */
548*5113495bSYour Name 		wmi_tlv_print_error
549*5113495bSYour Name 			("%s: Error: unable to alloc memory (size=%d) for TLV\n",
550*5113495bSYour Name 			__func__, len_wmi_cmd_struct_buf);
551*5113495bSYour Name 		return error;
552*5113495bSYour Name 	}
553*5113495bSYour Name 
554*5113495bSYour Name 	cmd_param_tlvs_ptr = (wmitlv_cmd_param_info *) *wmi_cmd_struct_ptr;
555*5113495bSYour Name 	wmi_tlv_OS_MEMZERO(cmd_param_tlvs_ptr, len_wmi_cmd_struct_buf);
556*5113495bSYour Name 	remaining_expected_tlvs = attr_struct_ptr.cmd_num_tlv;
557*5113495bSYour Name 
558*5113495bSYour Name 	while (((buf_idx + WMI_TLV_HDR_SIZE) <= param_buf_len)
559*5113495bSYour Name 	       && (remaining_expected_tlvs)) {
560*5113495bSYour Name 		uint32_t curr_tlv_tag =
561*5113495bSYour Name 			WMITLV_GET_TLVTAG(WMITLV_GET_HDR(buf_ptr));
562*5113495bSYour Name 		uint32_t curr_tlv_len =
563*5113495bSYour Name 			WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
564*5113495bSYour Name 		int num_padding_bytes = 0;
565*5113495bSYour Name 
566*5113495bSYour Name 		free_buf_len = param_buf_len - (buf_idx + WMI_TLV_HDR_SIZE);
567*5113495bSYour Name 		if (curr_tlv_len > free_buf_len) {
568*5113495bSYour Name 			wmi_tlv_print_error("%s: TLV length overflow",
569*5113495bSYour Name 					    __func__);
570*5113495bSYour Name 			goto Error_wmitlv_check_and_pad_tlvs;
571*5113495bSYour Name 		}
572*5113495bSYour Name 
573*5113495bSYour Name 		/* Get the attributes of the TLV with the given order in "tlv_index" */
574*5113495bSYour Name 		wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
575*5113495bSYour Name 				   sizeof(wmitlv_attributes_struc));
576*5113495bSYour Name 		if (wmitlv_get_attributes
577*5113495bSYour Name 			    (is_cmd_id, wmi_cmd_event_id, tlv_index,
578*5113495bSYour Name 			    &attr_struct_ptr) != 0) {
579*5113495bSYour Name 			wmi_tlv_print_error
580*5113495bSYour Name 				("%s: ERROR: No TLV attributes found for Cmd=%d Tag_order=%d\n",
581*5113495bSYour Name 				__func__, wmi_cmd_event_id, tlv_index);
582*5113495bSYour Name 			goto Error_wmitlv_check_and_pad_tlvs;
583*5113495bSYour Name 		}
584*5113495bSYour Name 
585*5113495bSYour Name 		/* Found the TLV that we wanted */
586*5113495bSYour Name 		wmi_tlv_print_verbose("%s: [tlv %d]: tag=%d, len=%d\n",
587*5113495bSYour Name 				      __func__, tlv_index, curr_tlv_tag,
588*5113495bSYour Name 				      curr_tlv_len);
589*5113495bSYour Name 
590*5113495bSYour Name 		/* Validating Tag order */
591*5113495bSYour Name 		if (curr_tlv_tag != attr_struct_ptr.tag_id) {
592*5113495bSYour Name 			wmi_tlv_print_error
593*5113495bSYour Name 				("%s: ERROR: TLV has wrong tag in order for Cmd=0x%x. Given=%d, Expected=%d, total_tlv=%d, remaining tlv=%d.\n",
594*5113495bSYour Name 				__func__, wmi_cmd_event_id, curr_tlv_tag,
595*5113495bSYour Name 				attr_struct_ptr.tag_id,
596*5113495bSYour Name 				attr_struct_ptr.cmd_num_tlv,
597*5113495bSYour Name 				remaining_expected_tlvs);
598*5113495bSYour Name 			goto Error_wmitlv_check_and_pad_tlvs;
599*5113495bSYour Name 		}
600*5113495bSYour Name 
601*5113495bSYour Name 		if ((curr_tlv_tag >= WMITLV_TAG_FIRST_ARRAY_ENUM)
602*5113495bSYour Name 		    && (curr_tlv_tag <= WMITLV_TAG_LAST_ARRAY_ENUM)) {
603*5113495bSYour Name 			/* Current Tag is an array of some kind. */
604*5113495bSYour Name 			/* Skip the TLV header of this array */
605*5113495bSYour Name 			buf_ptr += WMI_TLV_HDR_SIZE;
606*5113495bSYour Name 			buf_idx += WMI_TLV_HDR_SIZE;
607*5113495bSYour Name 		} else {
608*5113495bSYour Name 			/* Non-array TLV. */
609*5113495bSYour Name 			curr_tlv_len += WMI_TLV_HDR_SIZE;
610*5113495bSYour Name 		}
611*5113495bSYour Name 
612*5113495bSYour Name 		if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
613*5113495bSYour Name 			/* This TLV is fixed length */
614*5113495bSYour Name 			if (WMITLV_ARR_SIZE_INVALID ==
615*5113495bSYour Name 			    attr_struct_ptr.tag_array_size) {
616*5113495bSYour Name 				tlv_size_diff =
617*5113495bSYour Name 					curr_tlv_len -
618*5113495bSYour Name 					attr_struct_ptr.tag_struct_size;
619*5113495bSYour Name 				num_of_elems =
620*5113495bSYour Name 					(curr_tlv_len > WMI_TLV_HDR_SIZE) ? 1 : 0;
621*5113495bSYour Name 			} else {
622*5113495bSYour Name 				tlv_size_diff =
623*5113495bSYour Name 					curr_tlv_len -
624*5113495bSYour Name 					(attr_struct_ptr.tag_struct_size *
625*5113495bSYour Name 					 attr_struct_ptr.tag_array_size);
626*5113495bSYour Name 				num_of_elems = attr_struct_ptr.tag_array_size;
627*5113495bSYour Name 			}
628*5113495bSYour Name 		} else {
629*5113495bSYour Name 			/* This TLV has a variable number of elements */
630*5113495bSYour Name 			if (WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) {
631*5113495bSYour Name 				uint32_t in_tlv_len = 0;
632*5113495bSYour Name 
633*5113495bSYour Name 				if (curr_tlv_len != 0) {
634*5113495bSYour Name 					in_tlv_len =
635*5113495bSYour Name 						WMITLV_GET_TLVLEN(WMITLV_GET_HDR
636*5113495bSYour Name 									  (buf_ptr));
637*5113495bSYour Name 					in_tlv_len += WMI_TLV_HDR_SIZE;
638*5113495bSYour Name 					if (in_tlv_len > curr_tlv_len) {
639*5113495bSYour Name 						wmi_tlv_print_error("%s: Invalid in_tlv_len=%d",
640*5113495bSYour Name 								    __func__,
641*5113495bSYour Name 								    in_tlv_len);
642*5113495bSYour Name 						goto
643*5113495bSYour Name 						Error_wmitlv_check_and_pad_tlvs;
644*5113495bSYour Name 					}
645*5113495bSYour Name 					tlv_size_diff =
646*5113495bSYour Name 						in_tlv_len -
647*5113495bSYour Name 						attr_struct_ptr.tag_struct_size;
648*5113495bSYour Name 					num_of_elems =
649*5113495bSYour Name 						curr_tlv_len / in_tlv_len;
650*5113495bSYour Name 					wmi_tlv_print_verbose
651*5113495bSYour Name 						("%s: WARN: TLV array of structures in_tlv_len=%d struct_size:%d diff:%d num_of_elems=%d \n",
652*5113495bSYour Name 						__func__, in_tlv_len,
653*5113495bSYour Name 						attr_struct_ptr.tag_struct_size,
654*5113495bSYour Name 						tlv_size_diff, num_of_elems);
655*5113495bSYour Name 				} else {
656*5113495bSYour Name 					tlv_size_diff = 0;
657*5113495bSYour Name 					num_of_elems = 0;
658*5113495bSYour Name 				}
659*5113495bSYour Name 			} else
660*5113495bSYour Name 			if ((WMITLV_TAG_ARRAY_UINT32 ==
661*5113495bSYour Name 			     attr_struct_ptr.tag_id)
662*5113495bSYour Name 			    || (WMITLV_TAG_ARRAY_BYTE ==
663*5113495bSYour Name 				attr_struct_ptr.tag_id)
664*5113495bSYour Name 			    || (WMITLV_TAG_ARRAY_FIXED_STRUC ==
665*5113495bSYour Name 				attr_struct_ptr.tag_id) ||
666*5113495bSYour Name 				(WMITLV_TAG_ARRAY_INT16 ==
667*5113495bSYour Name 					attr_struct_ptr.tag_id)) {
668*5113495bSYour Name 				tlv_size_diff = 0;
669*5113495bSYour Name 				num_of_elems =
670*5113495bSYour Name 					curr_tlv_len /
671*5113495bSYour Name 					attr_struct_ptr.tag_struct_size;
672*5113495bSYour Name 			} else {
673*5113495bSYour Name 				wmi_tlv_print_error
674*5113495bSYour Name 					("%s ERROR Need to handle this tag ID for variable length %d\n",
675*5113495bSYour Name 					__func__, attr_struct_ptr.tag_id);
676*5113495bSYour Name 				goto Error_wmitlv_check_and_pad_tlvs;
677*5113495bSYour Name 			}
678*5113495bSYour Name 		}
679*5113495bSYour Name 
680*5113495bSYour Name 		if ((WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) &&
681*5113495bSYour Name 		    (tlv_size_diff != 0)) {
682*5113495bSYour Name 			void *new_tlv_buf = NULL;
683*5113495bSYour Name 			uint8_t *tlv_buf_ptr = NULL;
684*5113495bSYour Name 			uint32_t in_tlv_len;
685*5113495bSYour Name 			uint32_t i;
686*5113495bSYour Name 
687*5113495bSYour Name 			if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
688*5113495bSYour Name 				/* This is not allowed. The tag WMITLV_TAG_ARRAY_STRUC can
689*5113495bSYour Name 				 * only be used with variable-length structure array
690*5113495bSYour Name 				 * should not have a fixed number of elements (contradicting).
691*5113495bSYour Name 				 * Use WMITLV_TAG_ARRAY_FIXED_STRUC tag for fixed size
692*5113495bSYour Name 				 * structure array(where structure never change without
693*5113495bSYour Name 				 * breaking compatibility) */
694*5113495bSYour Name 				wmi_tlv_print_error
695*5113495bSYour Name 					("%s: ERROR: TLV (tag=%d) should be variable-length and not fixed length\n",
696*5113495bSYour Name 					__func__, curr_tlv_tag);
697*5113495bSYour Name 				goto Error_wmitlv_check_and_pad_tlvs;
698*5113495bSYour Name 			}
699*5113495bSYour Name 
700*5113495bSYour Name 			/* Warning: Needs to allocate a larger structure and pad with zeros */
701*5113495bSYour Name 			wmi_tlv_print_verbose
702*5113495bSYour Name 				("%s: WARN: TLV array of structures needs padding. tlv_size_diff=%d\n",
703*5113495bSYour Name 				__func__, tlv_size_diff);
704*5113495bSYour Name 
705*5113495bSYour Name 			/* incoming structure length */
706*5113495bSYour Name 			in_tlv_len =
707*5113495bSYour Name 				WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr)) +
708*5113495bSYour Name 				WMI_TLV_HDR_SIZE;
709*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
710*5113495bSYour Name 			wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
711*5113495bSYour Name 					     (num_of_elems *
712*5113495bSYour Name 					      attr_struct_ptr.tag_struct_size));
713*5113495bSYour Name 			if (!new_tlv_buf) {
714*5113495bSYour Name 				/* Error: unable to alloc memory */
715*5113495bSYour Name 				wmi_tlv_print_error
716*5113495bSYour Name 					("%s: Error: unable to alloc memory (size=%d) for padding the TLV array %d\n",
717*5113495bSYour Name 					__func__,
718*5113495bSYour Name 					(num_of_elems *
719*5113495bSYour Name 					 attr_struct_ptr.tag_struct_size),
720*5113495bSYour Name 					curr_tlv_tag);
721*5113495bSYour Name 				goto Error_wmitlv_check_and_pad_tlvs;
722*5113495bSYour Name 			}
723*5113495bSYour Name 
724*5113495bSYour Name 			wmi_tlv_OS_MEMZERO(new_tlv_buf,
725*5113495bSYour Name 					   (num_of_elems *
726*5113495bSYour Name 					    attr_struct_ptr.tag_struct_size));
727*5113495bSYour Name 			tlv_buf_ptr = (uint8_t *) new_tlv_buf;
728*5113495bSYour Name 			for (i = 0; i < num_of_elems; i++) {
729*5113495bSYour Name 				if (tlv_size_diff > 0) {
730*5113495bSYour Name 					/* Incoming structure size is greater than expected
731*5113495bSYour Name 					 * structure size. so copy the number of bytes equal
732*5113495bSYour Name 					 * to expected structure size */
733*5113495bSYour Name 					wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
734*5113495bSYour Name 							  (void *)(buf_ptr +
735*5113495bSYour Name 								   i *
736*5113495bSYour Name 								   in_tlv_len),
737*5113495bSYour Name 							  attr_struct_ptr.
738*5113495bSYour Name 							  tag_struct_size);
739*5113495bSYour Name 				} else {
740*5113495bSYour Name 					/* Incoming structure size is smaller than expected
741*5113495bSYour Name 					 * structure size. so copy the number of bytes equal
742*5113495bSYour Name 					 * to incoming structure size */
743*5113495bSYour Name 					wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
744*5113495bSYour Name 							  (void *)(buf_ptr +
745*5113495bSYour Name 								   i *
746*5113495bSYour Name 								   in_tlv_len),
747*5113495bSYour Name 							  in_tlv_len);
748*5113495bSYour Name 				}
749*5113495bSYour Name 				tlv_buf_ptr += attr_struct_ptr.tag_struct_size;
750*5113495bSYour Name 			}
751*5113495bSYour Name #else
752*5113495bSYour Name 			{
753*5113495bSYour Name 				uint8_t *src_addr;
754*5113495bSYour Name 				uint8_t *dst_addr;
755*5113495bSYour Name 				uint32_t buf_mov_len;
756*5113495bSYour Name 
757*5113495bSYour Name 				if (tlv_size_diff < 0) {
758*5113495bSYour Name 					/* Incoming structure size is smaller than expected size
759*5113495bSYour Name 					 * then this needs padding for each element in the array */
760*5113495bSYour Name 
761*5113495bSYour Name 					/* Find amount of bytes to be padded for one element */
762*5113495bSYour Name 					num_padding_bytes = tlv_size_diff * -1;
763*5113495bSYour Name 
764*5113495bSYour Name 					/* Move subsequent TLVs by number of bytes to be padded
765*5113495bSYour Name 					 * for all elements */
766*5113495bSYour Name 					if ((free_buf_len <
767*5113495bSYour Name 					    attr_struct_ptr.tag_struct_size *
768*5113495bSYour Name 					    num_of_elems) ||
769*5113495bSYour Name 					    (param_buf_len <
770*5113495bSYour Name 					    buf_idx + curr_tlv_len +
771*5113495bSYour Name 					    num_padding_bytes * num_of_elems)) {
772*5113495bSYour Name 						wmi_tlv_print_error("%s: Insufficient buffer\n",
773*5113495bSYour Name 								    __func__);
774*5113495bSYour Name 						goto
775*5113495bSYour Name 						Error_wmitlv_check_and_pad_tlvs;
776*5113495bSYour Name 					} else {
777*5113495bSYour Name 						src_addr =
778*5113495bSYour Name 							buf_ptr + curr_tlv_len;
779*5113495bSYour Name 						dst_addr =
780*5113495bSYour Name 							buf_ptr + curr_tlv_len +
781*5113495bSYour Name 							(num_padding_bytes *
782*5113495bSYour Name 							 num_of_elems);
783*5113495bSYour Name 						buf_mov_len =
784*5113495bSYour Name 							param_buf_len - (buf_idx +
785*5113495bSYour Name 									 curr_tlv_len);
786*5113495bSYour Name 
787*5113495bSYour Name 						wmi_tlv_OS_MEMMOVE(dst_addr,
788*5113495bSYour Name 								   src_addr,
789*5113495bSYour Name 								   buf_mov_len);
790*5113495bSYour Name 					}
791*5113495bSYour Name 
792*5113495bSYour Name 					/* Move subsequent elements of array down by number of
793*5113495bSYour Name 					 * bytes to be padded for one element and also set
794*5113495bSYour Name 					 * padding bytes to zero */
795*5113495bSYour Name 					tlv_buf_ptr = buf_ptr;
796*5113495bSYour Name 					for (i = 0; i < num_of_elems - 1; i++) {
797*5113495bSYour Name 						src_addr =
798*5113495bSYour Name 							tlv_buf_ptr + in_tlv_len;
799*5113495bSYour Name 						if (i != (num_of_elems - 1)) {
800*5113495bSYour Name 							dst_addr =
801*5113495bSYour Name 								tlv_buf_ptr +
802*5113495bSYour Name 								in_tlv_len +
803*5113495bSYour Name 								num_padding_bytes;
804*5113495bSYour Name 							buf_mov_len =
805*5113495bSYour Name 								curr_tlv_len -
806*5113495bSYour Name 								((i +
807*5113495bSYour Name 								  1) * in_tlv_len);
808*5113495bSYour Name 
809*5113495bSYour Name 							wmi_tlv_OS_MEMMOVE
810*5113495bSYour Name 								(dst_addr, src_addr,
811*5113495bSYour Name 								buf_mov_len);
812*5113495bSYour Name 						}
813*5113495bSYour Name 
814*5113495bSYour Name 						/* Set the padding bytes to zeroes */
815*5113495bSYour Name 						wmi_tlv_OS_MEMZERO(src_addr,
816*5113495bSYour Name 								   num_padding_bytes);
817*5113495bSYour Name 
818*5113495bSYour Name 						tlv_buf_ptr +=
819*5113495bSYour Name 							attr_struct_ptr.
820*5113495bSYour Name 							tag_struct_size;
821*5113495bSYour Name 					}
822*5113495bSYour Name 					src_addr = tlv_buf_ptr + in_tlv_len;
823*5113495bSYour Name 					wmi_tlv_OS_MEMZERO(src_addr,
824*5113495bSYour Name 							   num_padding_bytes);
825*5113495bSYour Name 
826*5113495bSYour Name 					/* Update the number of padding bytes to total number
827*5113495bSYour Name 					 * of bytes padded for all elements in the array */
828*5113495bSYour Name 					num_padding_bytes =
829*5113495bSYour Name 						num_padding_bytes * num_of_elems;
830*5113495bSYour Name 
831*5113495bSYour Name 					new_tlv_buf = buf_ptr;
832*5113495bSYour Name 				} else {
833*5113495bSYour Name 					/* Incoming structure size is greater than expected size
834*5113495bSYour Name 					 * then this needs shrinking for each element in the array */
835*5113495bSYour Name 
836*5113495bSYour Name 					/* Find amount of bytes to be shrunk for one element */
837*5113495bSYour Name 					num_padding_bytes = tlv_size_diff * -1;
838*5113495bSYour Name 
839*5113495bSYour Name 					/* Move subsequent elements of array up by number of bytes
840*5113495bSYour Name 					 * to be shrunk for one element */
841*5113495bSYour Name 					tlv_buf_ptr = buf_ptr;
842*5113495bSYour Name 					for (i = 0; i < (num_of_elems - 1); i++) {
843*5113495bSYour Name 						src_addr =
844*5113495bSYour Name 							tlv_buf_ptr + in_tlv_len;
845*5113495bSYour Name 						dst_addr =
846*5113495bSYour Name 							tlv_buf_ptr + in_tlv_len +
847*5113495bSYour Name 							num_padding_bytes;
848*5113495bSYour Name 						buf_mov_len =
849*5113495bSYour Name 							curr_tlv_len -
850*5113495bSYour Name 							((i + 1) * in_tlv_len);
851*5113495bSYour Name 
852*5113495bSYour Name 						wmi_tlv_OS_MEMMOVE(dst_addr,
853*5113495bSYour Name 								   src_addr,
854*5113495bSYour Name 								   buf_mov_len);
855*5113495bSYour Name 
856*5113495bSYour Name 						tlv_buf_ptr +=
857*5113495bSYour Name 							attr_struct_ptr.
858*5113495bSYour Name 							tag_struct_size;
859*5113495bSYour Name 					}
860*5113495bSYour Name 
861*5113495bSYour Name 					/* Move subsequent TLVs by number of bytes to be shrunk
862*5113495bSYour Name 					 * for all elements */
863*5113495bSYour Name 					if (param_buf_len >
864*5113495bSYour Name 					    (buf_idx + curr_tlv_len)) {
865*5113495bSYour Name 						src_addr =
866*5113495bSYour Name 							buf_ptr + curr_tlv_len;
867*5113495bSYour Name 						dst_addr =
868*5113495bSYour Name 							buf_ptr + curr_tlv_len +
869*5113495bSYour Name 							(num_padding_bytes *
870*5113495bSYour Name 							 num_of_elems);
871*5113495bSYour Name 						buf_mov_len =
872*5113495bSYour Name 							param_buf_len - (buf_idx +
873*5113495bSYour Name 									 curr_tlv_len);
874*5113495bSYour Name 
875*5113495bSYour Name 						wmi_tlv_OS_MEMMOVE(dst_addr,
876*5113495bSYour Name 								   src_addr,
877*5113495bSYour Name 								   buf_mov_len);
878*5113495bSYour Name 					}
879*5113495bSYour Name 
880*5113495bSYour Name 					/* Update the number of padding bytes to total number of
881*5113495bSYour Name 					 * bytes shrunk for all elements in the array */
882*5113495bSYour Name 					num_padding_bytes =
883*5113495bSYour Name 						num_padding_bytes * num_of_elems;
884*5113495bSYour Name 
885*5113495bSYour Name 					new_tlv_buf = buf_ptr;
886*5113495bSYour Name 				}
887*5113495bSYour Name 			}
888*5113495bSYour Name #endif
889*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
890*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].num_elements =
891*5113495bSYour Name 				num_of_elems;
892*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1;     /* Indicates that buffer is allocated */
893*5113495bSYour Name 
894*5113495bSYour Name 		} else if (tlv_size_diff >= 0) {
895*5113495bSYour Name 			/* Warning: some parameter truncation */
896*5113495bSYour Name 			if (tlv_size_diff > 0) {
897*5113495bSYour Name 				wmi_tlv_print_verbose
898*5113495bSYour Name 					("%s: WARN: TLV truncated. tlv_size_diff=%d, curr_tlv_len=%d\n",
899*5113495bSYour Name 					__func__, tlv_size_diff, curr_tlv_len);
900*5113495bSYour Name 			}
901*5113495bSYour Name 			/* TODO: this next line needs more comments and explanation */
902*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].tlv_ptr =
903*5113495bSYour Name 				(attr_struct_ptr.tag_varied_size
904*5113495bSYour Name 				 && !curr_tlv_len) ? NULL : (void *)buf_ptr;
905*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].num_elements =
906*5113495bSYour Name 				num_of_elems;
907*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 0;     /* Indicates that buffer is not allocated */
908*5113495bSYour Name 		} else {
909*5113495bSYour Name 			void *new_tlv_buf = NULL;
910*5113495bSYour Name 
911*5113495bSYour Name 			/* Warning: Needs to allocate a larger structure and pad with zeros */
912*5113495bSYour Name 			wmi_tlv_print_verbose
913*5113495bSYour Name 				("%s: WARN: TLV needs padding. tlv_size_diff=%d\n",
914*5113495bSYour Name 				__func__, tlv_size_diff);
915*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
916*5113495bSYour Name 			/* Dynamic memory allocation is supported */
917*5113495bSYour Name 			wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
918*5113495bSYour Name 					     (curr_tlv_len - tlv_size_diff));
919*5113495bSYour Name 			if (!new_tlv_buf) {
920*5113495bSYour Name 				/* Error: unable to alloc memory */
921*5113495bSYour Name 				wmi_tlv_print_error
922*5113495bSYour Name 					("%s: Error: unable to alloc memory (size=%d) for padding the TLV %d\n",
923*5113495bSYour Name 					__func__, (curr_tlv_len - tlv_size_diff),
924*5113495bSYour Name 					curr_tlv_tag);
925*5113495bSYour Name 				goto Error_wmitlv_check_and_pad_tlvs;
926*5113495bSYour Name 			}
927*5113495bSYour Name 
928*5113495bSYour Name 			wmi_tlv_OS_MEMZERO(new_tlv_buf,
929*5113495bSYour Name 					   (curr_tlv_len - tlv_size_diff));
930*5113495bSYour Name 			wmi_tlv_OS_MEMCPY(new_tlv_buf, (void *)buf_ptr,
931*5113495bSYour Name 					  curr_tlv_len);
932*5113495bSYour Name #else
933*5113495bSYour Name 			/* Dynamic memory allocation is not supported. Padding has
934*5113495bSYour Name 			 * to be done with in the existing buffer assuming we have
935*5113495bSYour Name 			 * enough space to grow */
936*5113495bSYour Name 			{
937*5113495bSYour Name 				/* Note: tlv_size_diff is a value less than zero */
938*5113495bSYour Name 				/* Move the Subsequent TLVs by amount of bytes needs to be padded */
939*5113495bSYour Name 				uint8_t *src_addr;
940*5113495bSYour Name 				uint8_t *dst_addr;
941*5113495bSYour Name 				uint32_t src_len;
942*5113495bSYour Name 
943*5113495bSYour Name 				num_padding_bytes = (tlv_size_diff * -1);
944*5113495bSYour Name 
945*5113495bSYour Name 				src_addr = buf_ptr + curr_tlv_len;
946*5113495bSYour Name 				dst_addr =
947*5113495bSYour Name 					buf_ptr + curr_tlv_len + num_padding_bytes;
948*5113495bSYour Name 				src_len =
949*5113495bSYour Name 					param_buf_len - (buf_idx + curr_tlv_len);
950*5113495bSYour Name 
951*5113495bSYour Name 				wmi_tlv_OS_MEMMOVE(dst_addr, src_addr, src_len);
952*5113495bSYour Name 
953*5113495bSYour Name 				/* Set the padding bytes to zeroes */
954*5113495bSYour Name 				wmi_tlv_OS_MEMZERO(src_addr, num_padding_bytes);
955*5113495bSYour Name 
956*5113495bSYour Name 				new_tlv_buf = buf_ptr;
957*5113495bSYour Name 			}
958*5113495bSYour Name #endif
959*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
960*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].num_elements =
961*5113495bSYour Name 				num_of_elems;
962*5113495bSYour Name 			cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1;     /* Indicates that buffer is allocated */
963*5113495bSYour Name 		}
964*5113495bSYour Name 
965*5113495bSYour Name 		tlv_index++;
966*5113495bSYour Name 		remaining_expected_tlvs--;
967*5113495bSYour Name 		buf_ptr += curr_tlv_len + num_padding_bytes;
968*5113495bSYour Name 		buf_idx += curr_tlv_len + num_padding_bytes;
969*5113495bSYour Name 	}
970*5113495bSYour Name 
971*5113495bSYour Name 	return 0;
972*5113495bSYour Name Error_wmitlv_check_and_pad_tlvs:
973*5113495bSYour Name 	if (is_cmd_id) {
974*5113495bSYour Name 		wmitlv_free_allocated_command_tlvs(wmi_cmd_event_id,
975*5113495bSYour Name 						   wmi_cmd_struct_ptr);
976*5113495bSYour Name 	} else {
977*5113495bSYour Name 		wmitlv_free_allocated_event_tlvs(wmi_cmd_event_id,
978*5113495bSYour Name 						 wmi_cmd_struct_ptr);
979*5113495bSYour Name 	}
980*5113495bSYour Name 	*wmi_cmd_struct_ptr = NULL;
981*5113495bSYour Name 	return error;
982*5113495bSYour Name }
983*5113495bSYour Name 
984*5113495bSYour Name /**
985*5113495bSYour Name  * wmitlv_check_and_pad_event_tlvs() - tlv helper function
986*5113495bSYour Name  * @os_handle: os context handle
987*5113495bSYour Name  * @param_struc_ptr: pointer to tlv structure
988*5113495bSYour Name  * @param_buf_len: length of tlv parameter
989*5113495bSYour Name  * @wmi_cmd_event_id: command event id
990*5113495bSYour Name  * @wmi_cmd_struct_ptr: wmi command structure
991*5113495bSYour Name  *
992*5113495bSYour Name  *
993*5113495bSYour Name  * validate and pad(if necessary) for incoming WMI Event TLVs
994*5113495bSYour Name  *
995*5113495bSYour Name  * Return: 0 if success. Return < 0 if failure.
996*5113495bSYour Name  */
997*5113495bSYour Name int
wmitlv_check_and_pad_event_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)998*5113495bSYour Name wmitlv_check_and_pad_event_tlvs(void *os_handle, void *param_struc_ptr,
999*5113495bSYour Name 				uint32_t param_buf_len,
1000*5113495bSYour Name 				uint32_t wmi_cmd_event_id,
1001*5113495bSYour Name 				void **wmi_cmd_struct_ptr)
1002*5113495bSYour Name {
1003*5113495bSYour Name 	uint32_t is_cmd_id = 0;
1004*5113495bSYour Name 	return wmitlv_check_and_pad_tlvs
1005*5113495bSYour Name 			(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
1006*5113495bSYour Name 			wmi_cmd_event_id, wmi_cmd_struct_ptr);
1007*5113495bSYour Name }
1008*5113495bSYour Name qdf_export_symbol(wmitlv_check_and_pad_event_tlvs);
1009*5113495bSYour Name 
1010*5113495bSYour Name /**
1011*5113495bSYour Name  * wmitlv_check_and_pad_command_tlvs() - tlv helper function
1012*5113495bSYour Name  * @os_handle: os context handle
1013*5113495bSYour Name  * @param_struc_ptr: pointer to tlv structure
1014*5113495bSYour Name  * @param_buf_len: length of tlv parameter
1015*5113495bSYour Name  * @wmi_cmd_event_id: command event id
1016*5113495bSYour Name  * @wmi_cmd_struct_ptr: wmi command structure
1017*5113495bSYour Name  *
1018*5113495bSYour Name  *
1019*5113495bSYour Name  * validate and pad(if necessary) for incoming WMI Command TLVs
1020*5113495bSYour Name  *
1021*5113495bSYour Name  * Return: 0 if success. Return < 0 if failure.
1022*5113495bSYour Name  */
1023*5113495bSYour Name int
wmitlv_check_and_pad_command_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)1024*5113495bSYour Name wmitlv_check_and_pad_command_tlvs(void *os_handle, void *param_struc_ptr,
1025*5113495bSYour Name 				  uint32_t param_buf_len,
1026*5113495bSYour Name 				  uint32_t wmi_cmd_event_id,
1027*5113495bSYour Name 				  void **wmi_cmd_struct_ptr)
1028*5113495bSYour Name {
1029*5113495bSYour Name 	uint32_t is_cmd_id = 1;
1030*5113495bSYour Name 	return wmitlv_check_and_pad_tlvs
1031*5113495bSYour Name 			(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
1032*5113495bSYour Name 			wmi_cmd_event_id, wmi_cmd_struct_ptr);
1033*5113495bSYour Name }
1034*5113495bSYour Name 
1035*5113495bSYour Name /**
1036*5113495bSYour Name  * wmitlv_free_allocated_tlvs() - tlv helper function
1037*5113495bSYour Name  * @is_cmd_id: bollean to check if cmd or event tlv
1038*5113495bSYour Name  * @cmd_event_id: command or event id
1039*5113495bSYour Name  * @wmi_cmd_struct_ptr: wmi command structure
1040*5113495bSYour Name  *
1041*5113495bSYour Name  *
1042*5113495bSYour Name  * free any allocated buffers for WMI Event/Command TLV processing
1043*5113495bSYour Name  *
1044*5113495bSYour Name  * Return: none
1045*5113495bSYour Name  */
wmitlv_free_allocated_tlvs(uint32_t is_cmd_id,uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)1046*5113495bSYour Name static void wmitlv_free_allocated_tlvs(uint32_t is_cmd_id,
1047*5113495bSYour Name 				       uint32_t cmd_event_id,
1048*5113495bSYour Name 				       void **wmi_cmd_struct_ptr)
1049*5113495bSYour Name {
1050*5113495bSYour Name 	void *ptr = *wmi_cmd_struct_ptr;
1051*5113495bSYour Name 
1052*5113495bSYour Name 	if (!ptr) {
1053*5113495bSYour Name 		wmi_tlv_print_error("%s: Nothing to free for CMD/Event 0x%x\n",
1054*5113495bSYour Name 				    __func__, cmd_event_id);
1055*5113495bSYour Name 		return;
1056*5113495bSYour Name 	}
1057*5113495bSYour Name #ifndef NO_DYNAMIC_MEM_ALLOC
1058*5113495bSYour Name 
1059*5113495bSYour Name /* macro to free that previously allocated memory for this TLV. When (op==FREE_TLV_ELEM). */
1060*5113495bSYour Name #define WMITLV_OP_FREE_TLV_ELEM_macro(param_ptr, param_len, wmi_cmd_event_id, elem_tlv_tag, elem_struc_type, elem_name, var_len, arr_size)  \
1061*5113495bSYour Name 	if ((((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->WMITLV_FIELD_BUF_IS_ALLOCATED(elem_name)) &&	\
1062*5113495bSYour Name 	    (((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name)) \
1063*5113495bSYour Name 	{ \
1064*5113495bSYour Name 		wmi_tlv_os_mem_free(((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name); \
1065*5113495bSYour Name 	}
1066*5113495bSYour Name 
1067*5113495bSYour Name #define WMITLV_FREE_TLV_ELEMS(id)	     \
1068*5113495bSYour Name case id: \
1069*5113495bSYour Name { \
1070*5113495bSYour Name 	WMITLV_TABLE(id, FREE_TLV_ELEM, NULL, 0)     \
1071*5113495bSYour Name } \
1072*5113495bSYour Name break;
1073*5113495bSYour Name 
1074*5113495bSYour Name 	if (is_cmd_id) {
1075*5113495bSYour Name 		switch (cmd_event_id) {
1076*5113495bSYour Name 			WMITLV_ALL_CMD_LIST(WMITLV_FREE_TLV_ELEMS);
1077*5113495bSYour Name 		default:
1078*5113495bSYour Name 			wmi_tlv_print_error
1079*5113495bSYour Name 				("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
1080*5113495bSYour Name 				__func__, cmd_event_id, cmd_event_id);
1081*5113495bSYour Name 		}
1082*5113495bSYour Name 	} else {
1083*5113495bSYour Name 		switch (cmd_event_id) {
1084*5113495bSYour Name 			WMITLV_ALL_EVT_LIST(WMITLV_FREE_TLV_ELEMS);
1085*5113495bSYour Name 		default:
1086*5113495bSYour Name 			wmi_tlv_print_error
1087*5113495bSYour Name 				("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
1088*5113495bSYour Name 				__func__, cmd_event_id, cmd_event_id);
1089*5113495bSYour Name 		}
1090*5113495bSYour Name 	}
1091*5113495bSYour Name 
1092*5113495bSYour Name 	wmi_tlv_os_mem_free(*wmi_cmd_struct_ptr);
1093*5113495bSYour Name 	*wmi_cmd_struct_ptr = NULL;
1094*5113495bSYour Name #endif
1095*5113495bSYour Name 
1096*5113495bSYour Name 	return;
1097*5113495bSYour Name }
1098*5113495bSYour Name 
1099*5113495bSYour Name /**
1100*5113495bSYour Name  * wmitlv_free_allocated_command_tlvs() - tlv helper function
1101*5113495bSYour Name  * @cmd_event_id: command or event id
1102*5113495bSYour Name  * @wmi_cmd_struct_ptr: wmi command structure
1103*5113495bSYour Name  *
1104*5113495bSYour Name  *
1105*5113495bSYour Name  * free any allocated buffers for WMI Event/Command TLV processing
1106*5113495bSYour Name  *
1107*5113495bSYour Name  * Return: none
1108*5113495bSYour Name  */
wmitlv_free_allocated_command_tlvs(uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)1109*5113495bSYour Name void wmitlv_free_allocated_command_tlvs(uint32_t cmd_event_id,
1110*5113495bSYour Name 					void **wmi_cmd_struct_ptr)
1111*5113495bSYour Name {
1112*5113495bSYour Name 	wmitlv_free_allocated_tlvs(1, cmd_event_id, wmi_cmd_struct_ptr);
1113*5113495bSYour Name }
1114*5113495bSYour Name 
1115*5113495bSYour Name /**
1116*5113495bSYour Name  * wmitlv_free_allocated_event_tlvs() - tlv helper function
1117*5113495bSYour Name  * @cmd_event_id: command or event id
1118*5113495bSYour Name  * @wmi_cmd_struct_ptr: wmi command structure
1119*5113495bSYour Name  *
1120*5113495bSYour Name  *
1121*5113495bSYour Name  * free any allocated buffers for WMI Event/Command TLV processing
1122*5113495bSYour Name  *
1123*5113495bSYour Name  * Return: none
1124*5113495bSYour Name  */
wmitlv_free_allocated_event_tlvs(uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)1125*5113495bSYour Name void wmitlv_free_allocated_event_tlvs(uint32_t cmd_event_id,
1126*5113495bSYour Name 				      void **wmi_cmd_struct_ptr)
1127*5113495bSYour Name {
1128*5113495bSYour Name 	wmitlv_free_allocated_tlvs(0, cmd_event_id, wmi_cmd_struct_ptr);
1129*5113495bSYour Name }
1130*5113495bSYour Name qdf_export_symbol(wmitlv_free_allocated_event_tlvs);
1131*5113495bSYour Name 
1132*5113495bSYour Name /**
1133*5113495bSYour Name  * wmi_versions_are_compatible() - tlv helper function
1134*5113495bSYour Name  * @vers1: host wmi version
1135*5113495bSYour Name  * @vers2: target wmi version
1136*5113495bSYour Name  *
1137*5113495bSYour Name  *
1138*5113495bSYour Name  * check if two given wmi versions are compatible
1139*5113495bSYour Name  *
1140*5113495bSYour Name  * Return: none
1141*5113495bSYour Name  */
1142*5113495bSYour Name int
wmi_versions_are_compatible(wmi_abi_version * vers1,wmi_abi_version * vers2)1143*5113495bSYour Name wmi_versions_are_compatible(wmi_abi_version *vers1, wmi_abi_version *vers2)
1144*5113495bSYour Name {
1145*5113495bSYour Name 	if ((vers1->abi_version_ns_0 != vers2->abi_version_ns_0) ||
1146*5113495bSYour Name 	    (vers1->abi_version_ns_1 != vers2->abi_version_ns_1) ||
1147*5113495bSYour Name 	    (vers1->abi_version_ns_2 != vers2->abi_version_ns_2) ||
1148*5113495bSYour Name 	    (vers1->abi_version_ns_3 != vers2->abi_version_ns_3)) {
1149*5113495bSYour Name 		/* The namespaces are different. Incompatible. */
1150*5113495bSYour Name 		return 0;
1151*5113495bSYour Name 	}
1152*5113495bSYour Name 
1153*5113495bSYour Name 	if (vers1->abi_version_0 != vers2->abi_version_0) {
1154*5113495bSYour Name 		/* The major or minor versions are different. Incompatible */
1155*5113495bSYour Name 		return 0;
1156*5113495bSYour Name 	}
1157*5113495bSYour Name 	/* We ignore the build version */
1158*5113495bSYour Name 	return 1;
1159*5113495bSYour Name }
1160*5113495bSYour Name 
1161*5113495bSYour Name /**
1162*5113495bSYour Name  * wmi_versions_can_downgrade() - tlv helper function
1163*5113495bSYour Name  * @num_allowlist: number of entries in @version_whitelist_table
1164*5113495bSYour Name  * @version_whitelist_table: version table
1165*5113495bSYour Name  * @my_vers: host version
1166*5113495bSYour Name  * @opp_vers: target version
1167*5113495bSYour Name  * @out_vers: downgraded version
1168*5113495bSYour Name  *
1169*5113495bSYour Name  *
1170*5113495bSYour Name  * check if target wmi version can be downgraded
1171*5113495bSYour Name  *
1172*5113495bSYour Name  * Return: 0 if success. Return < 0 if failure.
1173*5113495bSYour Name  */
1174*5113495bSYour Name static int
wmi_versions_can_downgrade(int num_allowlist,wmi_whitelist_version_info * version_whitelist_table,wmi_abi_version * my_vers,wmi_abi_version * opp_vers,wmi_abi_version * out_vers)1175*5113495bSYour Name wmi_versions_can_downgrade(int num_allowlist,
1176*5113495bSYour Name 			   wmi_whitelist_version_info *version_whitelist_table,
1177*5113495bSYour Name 			   wmi_abi_version *my_vers,
1178*5113495bSYour Name 			   wmi_abi_version *opp_vers,
1179*5113495bSYour Name 			   wmi_abi_version *out_vers)
1180*5113495bSYour Name {
1181*5113495bSYour Name 	uint8_t can_try_to_downgrade;
1182*5113495bSYour Name 	uint32_t my_major_vers = WMI_VER_GET_MAJOR(my_vers->abi_version_0);
1183*5113495bSYour Name 	uint32_t my_minor_vers = WMI_VER_GET_MINOR(my_vers->abi_version_0);
1184*5113495bSYour Name 	uint32_t opp_major_vers = WMI_VER_GET_MAJOR(opp_vers->abi_version_0);
1185*5113495bSYour Name 	uint32_t opp_minor_vers = WMI_VER_GET_MINOR(opp_vers->abi_version_0);
1186*5113495bSYour Name 	uint32_t downgraded_minor_vers;
1187*5113495bSYour Name 
1188*5113495bSYour Name 	if ((my_vers->abi_version_ns_0 != opp_vers->abi_version_ns_0) ||
1189*5113495bSYour Name 	    (my_vers->abi_version_ns_1 != opp_vers->abi_version_ns_1) ||
1190*5113495bSYour Name 	    (my_vers->abi_version_ns_2 != opp_vers->abi_version_ns_2) ||
1191*5113495bSYour Name 	    (my_vers->abi_version_ns_3 != opp_vers->abi_version_ns_3)) {
1192*5113495bSYour Name 		/* The namespaces are different. Incompatible. */
1193*5113495bSYour Name 		can_try_to_downgrade = false;
1194*5113495bSYour Name 	} else if (my_major_vers != opp_major_vers) {
1195*5113495bSYour Name 		/* Major version is different. Incompatible and cannot downgrade. */
1196*5113495bSYour Name 		can_try_to_downgrade = false;
1197*5113495bSYour Name 	} else {
1198*5113495bSYour Name 		/* Same major version. */
1199*5113495bSYour Name 
1200*5113495bSYour Name 		if (my_minor_vers < opp_minor_vers) {
1201*5113495bSYour Name 			/* Opposite party is newer. Incompatible and cannot downgrade. */
1202*5113495bSYour Name 			can_try_to_downgrade = false;
1203*5113495bSYour Name 		} else if (my_minor_vers > opp_minor_vers) {
1204*5113495bSYour Name 			/* Opposite party is older. Check allowlist if
1205*5113495bSYour Name 			 * we can downgrade
1206*5113495bSYour Name 			 */
1207*5113495bSYour Name 			can_try_to_downgrade = true;
1208*5113495bSYour Name 		} else {
1209*5113495bSYour Name 			/* Same version */
1210*5113495bSYour Name 			wmi_tlv_OS_MEMCPY(out_vers, my_vers,
1211*5113495bSYour Name 					  sizeof(wmi_abi_version));
1212*5113495bSYour Name 			return 1;
1213*5113495bSYour Name 		}
1214*5113495bSYour Name 	}
1215*5113495bSYour Name 
1216*5113495bSYour Name 	if (!can_try_to_downgrade) {
1217*5113495bSYour Name 		wmi_tlv_print_error("%s: Warning: incompatible WMI version.\n",
1218*5113495bSYour Name 				    __func__);
1219*5113495bSYour Name 		wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1220*5113495bSYour Name 		return 0;
1221*5113495bSYour Name 	}
1222*5113495bSYour Name 	/* Try to see we can downgrade the supported version */
1223*5113495bSYour Name 	downgraded_minor_vers = my_minor_vers;
1224*5113495bSYour Name 	while (downgraded_minor_vers > opp_minor_vers) {
1225*5113495bSYour Name 		uint8_t downgraded = false;
1226*5113495bSYour Name 		int i;
1227*5113495bSYour Name 
1228*5113495bSYour Name 		for (i = 0; i < num_allowlist; i++) {
1229*5113495bSYour Name 			if (version_whitelist_table[i].major != my_major_vers)
1230*5113495bSYour Name 				continue;       /* skip */
1231*5113495bSYour Name 			if (version_whitelist_table[i].namespace_0 !=
1232*5113495bSYour Name 				my_vers->abi_version_ns_0 ||
1233*5113495bSYour Name 			    version_whitelist_table[i].namespace_1 !=
1234*5113495bSYour Name 				my_vers->abi_version_ns_1 ||
1235*5113495bSYour Name 			    version_whitelist_table[i].namespace_2 !=
1236*5113495bSYour Name 				my_vers->abi_version_ns_2 ||
1237*5113495bSYour Name 			    version_whitelist_table[i].namespace_3 !=
1238*5113495bSYour Name 				my_vers->abi_version_ns_3) {
1239*5113495bSYour Name 				continue;       /* skip */
1240*5113495bSYour Name 			}
1241*5113495bSYour Name 			if (version_whitelist_table[i].minor ==
1242*5113495bSYour Name 			    downgraded_minor_vers) {
1243*5113495bSYour Name 				/* Found the next version that I can downgrade */
1244*5113495bSYour Name 				wmi_tlv_print_error
1245*5113495bSYour Name 					("%s: Note: found a allowlist entry to downgrade. wh. list ver: %d,%d,0x%x 0x%x 0x%x 0x%x\n",
1246*5113495bSYour Name 					__func__,
1247*5113495bSYour Name 					version_whitelist_table[i].major,
1248*5113495bSYour Name 					version_whitelist_table[i].minor,
1249*5113495bSYour Name 					version_whitelist_table[i].namespace_0,
1250*5113495bSYour Name 					version_whitelist_table[i].namespace_1,
1251*5113495bSYour Name 					version_whitelist_table[i].namespace_2,
1252*5113495bSYour Name 					version_whitelist_table[i].namespace_3);
1253*5113495bSYour Name 				downgraded_minor_vers--;
1254*5113495bSYour Name 				downgraded = true;
1255*5113495bSYour Name 				break;
1256*5113495bSYour Name 			}
1257*5113495bSYour Name 		}
1258*5113495bSYour Name 		if (!downgraded) {
1259*5113495bSYour Name 			break;  /* Done since we did not find any allowlist
1260*5113495bSYour Name 				 * to downgrade version
1261*5113495bSYour Name 				 */
1262*5113495bSYour Name 		}
1263*5113495bSYour Name 	}
1264*5113495bSYour Name 	wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1265*5113495bSYour Name 	out_vers->abi_version_0 =
1266*5113495bSYour Name 		WMI_VER_GET_VERSION_0(my_major_vers, downgraded_minor_vers);
1267*5113495bSYour Name 	if (downgraded_minor_vers != opp_minor_vers) {
1268*5113495bSYour Name 		wmi_tlv_print_error
1269*5113495bSYour Name 			("%s: Warning: incompatible WMI version and cannot downgrade.\n",
1270*5113495bSYour Name 			__func__);
1271*5113495bSYour Name 		return 0;       /* Incompatible */
1272*5113495bSYour Name 	} else {
1273*5113495bSYour Name 		return 1;       /* Compatible */
1274*5113495bSYour Name 	}
1275*5113495bSYour Name }
1276*5113495bSYour Name 
1277*5113495bSYour Name /**
1278*5113495bSYour Name  * wmi_cmp_and_set_abi_version() - tlv helper function
1279*5113495bSYour Name  * @num_allowlist: number of entries in @version_whitelist_table
1280*5113495bSYour Name  * @version_whitelist_table: version table
1281*5113495bSYour Name  * @my_vers: host version
1282*5113495bSYour Name  * @opp_vers: target version
1283*5113495bSYour Name  * @out_vers: downgraded version
1284*5113495bSYour Name  *
1285*5113495bSYour Name  * This routine will compare and set the WMI ABI version.
1286*5113495bSYour Name  * First, compare my version with the opposite side's version.
1287*5113495bSYour Name  * If incompatible, then check the allowlist to see if our side can downgrade.
1288*5113495bSYour Name  * Finally, fill in the final ABI version into the output, out_vers.
1289*5113495bSYour Name  * Return 0 if the output version is compatible
1290*5113495bSYour Name  * Else return 1 if the output version is incompatible
1291*5113495bSYour Name  *
1292*5113495bSYour Name  * Return: 0 if the output version is compatible else < 0.
1293*5113495bSYour Name  */
1294*5113495bSYour Name int
wmi_cmp_and_set_abi_version(int num_allowlist,wmi_whitelist_version_info * version_whitelist_table,struct _wmi_abi_version * my_vers,struct _wmi_abi_version * opp_vers,struct _wmi_abi_version * out_vers)1295*5113495bSYour Name wmi_cmp_and_set_abi_version(int num_allowlist,
1296*5113495bSYour Name 			    wmi_whitelist_version_info *
1297*5113495bSYour Name 			    version_whitelist_table,
1298*5113495bSYour Name 			    struct _wmi_abi_version *my_vers,
1299*5113495bSYour Name 			    struct _wmi_abi_version *opp_vers,
1300*5113495bSYour Name 			    struct _wmi_abi_version *out_vers)
1301*5113495bSYour Name {
1302*5113495bSYour Name 	wmi_tlv_print_verbose
1303*5113495bSYour Name 		("%s: Our WMI Version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1304*5113495bSYour Name 		__func__, WMI_VER_GET_MAJOR(my_vers->abi_version_0),
1305*5113495bSYour Name 		WMI_VER_GET_MINOR(my_vers->abi_version_0), my_vers->abi_version_1,
1306*5113495bSYour Name 		my_vers->abi_version_ns_0, my_vers->abi_version_ns_1,
1307*5113495bSYour Name 		my_vers->abi_version_ns_2, my_vers->abi_version_ns_3);
1308*5113495bSYour Name 
1309*5113495bSYour Name 	wmi_tlv_print_verbose
1310*5113495bSYour Name 		("%s: Opposite side WMI Version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1311*5113495bSYour Name 		__func__, WMI_VER_GET_MAJOR(opp_vers->abi_version_0),
1312*5113495bSYour Name 		WMI_VER_GET_MINOR(opp_vers->abi_version_0),
1313*5113495bSYour Name 		opp_vers->abi_version_1, opp_vers->abi_version_ns_0,
1314*5113495bSYour Name 		opp_vers->abi_version_ns_1, opp_vers->abi_version_ns_2,
1315*5113495bSYour Name 		opp_vers->abi_version_ns_3);
1316*5113495bSYour Name 
1317*5113495bSYour Name 	/* By default, the output version is our version. */
1318*5113495bSYour Name 	wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1319*5113495bSYour Name 	if (!wmi_versions_are_compatible(my_vers, opp_vers)) {
1320*5113495bSYour Name 		/* Our host version and the given firmware version are incompatible. */
1321*5113495bSYour Name 		if (wmi_versions_can_downgrade(num_allowlist,
1322*5113495bSYour Name 					       version_whitelist_table,
1323*5113495bSYour Name 					       my_vers,
1324*5113495bSYour Name 					       opp_vers,
1325*5113495bSYour Name 					       out_vers)) {
1326*5113495bSYour Name 			/* We can downgrade our host versions to match firmware. */
1327*5113495bSYour Name 			wmi_tlv_print_error
1328*5113495bSYour Name 				("%s: Host downgraded WMI Versions to match fw. Ret version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1329*5113495bSYour Name 				__func__,
1330*5113495bSYour Name 				WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1331*5113495bSYour Name 				WMI_VER_GET_MINOR(out_vers->abi_version_0),
1332*5113495bSYour Name 				out_vers->abi_version_1,
1333*5113495bSYour Name 				out_vers->abi_version_ns_0,
1334*5113495bSYour Name 				out_vers->abi_version_ns_1,
1335*5113495bSYour Name 				out_vers->abi_version_ns_2,
1336*5113495bSYour Name 				out_vers->abi_version_ns_3);
1337*5113495bSYour Name 			return 0;       /* Compatible */
1338*5113495bSYour Name 		} else {
1339*5113495bSYour Name 			/* Warn: We cannot downgrade our host versions to match firmware. */
1340*5113495bSYour Name 			wmi_tlv_print_error
1341*5113495bSYour Name 				("%s: WARN: Host WMI Versions mismatch with fw. Ret version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1342*5113495bSYour Name 				__func__,
1343*5113495bSYour Name 				WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1344*5113495bSYour Name 				WMI_VER_GET_MINOR(out_vers->abi_version_0),
1345*5113495bSYour Name 				out_vers->abi_version_1,
1346*5113495bSYour Name 				out_vers->abi_version_ns_0,
1347*5113495bSYour Name 				out_vers->abi_version_ns_1,
1348*5113495bSYour Name 				out_vers->abi_version_ns_2,
1349*5113495bSYour Name 				out_vers->abi_version_ns_3);
1350*5113495bSYour Name 
1351*5113495bSYour Name 			return 1;       /* Incompatible */
1352*5113495bSYour Name 		}
1353*5113495bSYour Name 	} else {
1354*5113495bSYour Name 		/* We are compatible. Our host version is the output version */
1355*5113495bSYour Name 		wmi_tlv_print_verbose
1356*5113495bSYour Name 			("%s: Host and FW Compatible WMI Versions. Ret version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1357*5113495bSYour Name 			__func__, WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1358*5113495bSYour Name 			WMI_VER_GET_MINOR(out_vers->abi_version_0),
1359*5113495bSYour Name 			out_vers->abi_version_1, out_vers->abi_version_ns_0,
1360*5113495bSYour Name 			out_vers->abi_version_ns_1, out_vers->abi_version_ns_2,
1361*5113495bSYour Name 			out_vers->abi_version_ns_3);
1362*5113495bSYour Name 		return 0;       /* Compatible */
1363*5113495bSYour Name 	}
1364*5113495bSYour Name }
1365