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