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