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