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