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
wmitlv_set_static_param_tlv_buf(void * param_tlv_buf,uint32_t max_tlvs_accommodated)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
wmitlv_get_attributes(uint32_t is_cmd_id,uint32_t cmd_event_id,uint32_t curr_tlv_order,wmitlv_attributes_struc * tlv_attr_ptr)105 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
wmitlv_check_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t is_cmd_id,uint32_t wmi_cmd_event_id)193 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
wmitlv_check_event_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id)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
wmitlv_check_command_tlv_params(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id)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
wmitlv_check_and_pad_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t is_cmd_id,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)490 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
wmitlv_check_and_pad_event_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)998 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
wmitlv_check_and_pad_command_tlvs(void * os_handle,void * param_struc_ptr,uint32_t param_buf_len,uint32_t wmi_cmd_event_id,void ** wmi_cmd_struct_ptr)1024 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 */
wmitlv_free_allocated_tlvs(uint32_t is_cmd_id,uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)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 */
wmitlv_free_allocated_command_tlvs(uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)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 */
wmitlv_free_allocated_event_tlvs(uint32_t cmd_event_id,void ** wmi_cmd_struct_ptr)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
wmi_versions_are_compatible(wmi_abi_version * vers1,wmi_abi_version * vers2)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
wmi_versions_can_downgrade(int num_allowlist,wmi_whitelist_version_info * version_whitelist_table,wmi_abi_version * my_vers,wmi_abi_version * opp_vers,wmi_abi_version * out_vers)1175 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
wmi_cmp_and_set_abi_version(int num_allowlist,wmi_whitelist_version_info * version_whitelist_table,struct _wmi_abi_version * my_vers,struct _wmi_abi_version * opp_vers,struct _wmi_abi_version * out_vers)1295 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