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