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