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