blob: 1246152010630ef6f81c9e3a19a81b26b4d46fd1 [file] [log] [blame]
Govind Singh6b411b52016-03-06 19:55:02 +05301/*
2 * Copyright (c) 2013-2016 The Linux Foundation. All rights reserved.
3 *
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
521 /* Create base structure of format wmi_cmd_event_id##_param_tlvs */
522 len_wmi_cmd_struct_buf =
523 attr_struct_ptr.cmd_num_tlv * sizeof(wmitlv_cmd_param_info);
524#ifndef NO_DYNAMIC_MEM_ALLOC
525 /* Dynamic memory allocation supported */
526 wmi_tlv_os_mem_alloc(os_handle, *wmi_cmd_struct_ptr,
527 len_wmi_cmd_struct_buf);
528#else
529 /* Dynamic memory allocation is not supported. Use the buffer
530 * g_wmi_static_cmd_param_info_buf, which should be set using
531 * wmi_tlv_set_static_param_tlv_buf(),
532 * for base structure of format wmi_cmd_event_id##_param_tlvs */
533 *wmi_cmd_struct_ptr = g_wmi_static_cmd_param_info_buf;
534 if (attr_struct_ptr.cmd_num_tlv > g_wmi_static_max_cmd_param_tlvs) {
535 /* Error: Expecting more TLVs that accomodated for static structure */
536 wmi_tlv_print_error
537 ("%s: Error: Expecting more TLVs that accomodated for static structure. Expected:%d Accomodated:%d\n",
538 __func__, attr_struct_ptr.cmd_num_tlv,
539 g_wmi_static_max_cmd_param_tlvs);
540 return error;
541 }
542#endif
543 if (*wmi_cmd_struct_ptr == NULL) {
544 /* Error: unable to alloc memory */
545 wmi_tlv_print_error
546 ("%s: Error: unable to alloc memory (size=%d) for TLV\n",
547 __func__, len_wmi_cmd_struct_buf);
548 return error;
549 }
550
551 cmd_param_tlvs_ptr = (wmitlv_cmd_param_info *) *wmi_cmd_struct_ptr;
552 wmi_tlv_OS_MEMZERO(cmd_param_tlvs_ptr, len_wmi_cmd_struct_buf);
553 remaining_expected_tlvs = attr_struct_ptr.cmd_num_tlv;
554
555 while (((buf_idx + WMI_TLV_HDR_SIZE) <= param_buf_len)
556 && (remaining_expected_tlvs)) {
557 A_UINT32 curr_tlv_tag =
558 WMITLV_GET_TLVTAG(WMITLV_GET_HDR(buf_ptr));
559 A_UINT32 curr_tlv_len =
560 WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
561 int num_padding_bytes = 0;
562
563 /* Get the attributes of the TLV with the given order in "tlv_index" */
564 wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
565 sizeof(wmitlv_attributes_struc));
566 if (wmitlv_get_attributes
567 (is_cmd_id, wmi_cmd_event_id, tlv_index,
568 &attr_struct_ptr) != 0) {
569 wmi_tlv_print_error
570 ("%s: ERROR: No TLV attributes found for Cmd=%d Tag_order=%d\n",
571 __func__, wmi_cmd_event_id, tlv_index);
572 goto Error_wmitlv_check_and_pad_tlvs;
573 }
574
575 /* Found the TLV that we wanted */
576 wmi_tlv_print_verbose("%s: [tlv %d]: tag=%d, len=%d\n",
577 __func__, tlv_index, curr_tlv_tag,
578 curr_tlv_len);
579
580 /* Validating Tag order */
581 if (curr_tlv_tag != attr_struct_ptr.tag_id) {
582 wmi_tlv_print_error
583 ("%s: ERROR: TLV has wrong tag in order for Cmd=0x%x. Given=%d, Expected=%d.\n",
584 __func__, wmi_cmd_event_id, curr_tlv_tag,
585 attr_struct_ptr.tag_id);
586 goto Error_wmitlv_check_and_pad_tlvs;
587 }
588
589 if ((curr_tlv_tag >= WMITLV_TAG_FIRST_ARRAY_ENUM)
590 && (curr_tlv_tag <= WMITLV_TAG_LAST_ARRAY_ENUM)) {
591 /* Current Tag is an array of some kind. */
592 /* Skip the TLV header of this array */
593 buf_ptr += WMI_TLV_HDR_SIZE;
594 buf_idx += WMI_TLV_HDR_SIZE;
595 } else {
596 /* Non-array TLV. */
597 curr_tlv_len += WMI_TLV_HDR_SIZE;
598 }
599
600 if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
601 /* This TLV is fixed length */
602 if (WMITLV_ARR_SIZE_INVALID ==
603 attr_struct_ptr.tag_array_size) {
604 tlv_size_diff =
605 curr_tlv_len -
606 attr_struct_ptr.tag_struct_size;
607 num_of_elems =
608 (curr_tlv_len > WMI_TLV_HDR_SIZE) ? 1 : 0;
609 } else {
610 tlv_size_diff =
611 curr_tlv_len -
612 (attr_struct_ptr.tag_struct_size *
613 attr_struct_ptr.tag_array_size);
614 num_of_elems = attr_struct_ptr.tag_array_size;
615 }
616 } else {
617 /* This TLV has a variable number of elements */
618 if (WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) {
619 A_UINT32 in_tlv_len = 0;
620
621 if (curr_tlv_len != 0) {
622 in_tlv_len =
623 WMITLV_GET_TLVLEN(WMITLV_GET_HDR
624 (buf_ptr));
625 in_tlv_len += WMI_TLV_HDR_SIZE;
626 tlv_size_diff =
627 in_tlv_len -
628 attr_struct_ptr.tag_struct_size;
629 num_of_elems =
630 curr_tlv_len / in_tlv_len;
631 wmi_tlv_print_verbose
632 ("%s: WARN: TLV array of structures in_tlv_len=%d struct_size:%d diff:%d num_of_elems=%d \n",
633 __func__, in_tlv_len,
634 attr_struct_ptr.tag_struct_size,
635 tlv_size_diff, num_of_elems);
636 } else {
637 tlv_size_diff = 0;
638 num_of_elems = 0;
639 }
640 } else
641 if ((WMITLV_TAG_ARRAY_UINT32 ==
642 attr_struct_ptr.tag_id)
643 || (WMITLV_TAG_ARRAY_BYTE ==
644 attr_struct_ptr.tag_id)
645 || (WMITLV_TAG_ARRAY_FIXED_STRUC ==
646 attr_struct_ptr.tag_id)) {
647 tlv_size_diff = 0;
648 num_of_elems =
649 curr_tlv_len /
650 attr_struct_ptr.tag_struct_size;
651 } else {
652 wmi_tlv_print_error
653 ("%s ERROR Need to handle this tag ID for variable length %d\n",
654 __func__, attr_struct_ptr.tag_id);
655 goto Error_wmitlv_check_and_pad_tlvs;
656 }
657 }
658
659 if ((WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) &&
660 (tlv_size_diff != 0)) {
661 void *new_tlv_buf = NULL;
662 A_UINT8 *tlv_buf_ptr = NULL;
663 A_UINT32 in_tlv_len;
664 A_UINT32 i;
665
666 if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
667 /* This is not allowed. The tag WMITLV_TAG_ARRAY_STRUC can
668 * only be used with variable-length structure array
669 * should not have a fixed number of elements (contradicting).
670 * Use WMITLV_TAG_ARRAY_FIXED_STRUC tag for fixed size
671 * structure array(where structure never change without
672 * breaking compatibility) */
673 wmi_tlv_print_error
674 ("%s: ERROR: TLV (tag=%d) should be variable-length and not fixed length\n",
675 __func__, curr_tlv_tag);
676 goto Error_wmitlv_check_and_pad_tlvs;
677 }
678
679 /* Warning: Needs to allocate a larger structure and pad with zeros */
Nitesh Shah7a252892016-09-29 18:42:53 +0530680 wmi_tlv_print_verbose
Govind Singh6b411b52016-03-06 19:55:02 +0530681 ("%s: WARN: TLV array of structures needs padding. tlv_size_diff=%d\n",
682 __func__, tlv_size_diff);
683
684 /* incoming structure length */
685 in_tlv_len =
686 WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr)) +
687 WMI_TLV_HDR_SIZE;
688#ifndef NO_DYNAMIC_MEM_ALLOC
689 wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
690 (num_of_elems *
691 attr_struct_ptr.tag_struct_size));
692 if (new_tlv_buf == NULL) {
693 /* Error: unable to alloc memory */
694 wmi_tlv_print_error
695 ("%s: Error: unable to alloc memory (size=%d) for padding the TLV array %d\n",
696 __func__,
697 (num_of_elems *
698 attr_struct_ptr.tag_struct_size),
699 curr_tlv_tag);
700 goto Error_wmitlv_check_and_pad_tlvs;
701 }
702
703 wmi_tlv_OS_MEMZERO(new_tlv_buf,
704 (num_of_elems *
705 attr_struct_ptr.tag_struct_size));
706 tlv_buf_ptr = (A_UINT8 *) new_tlv_buf;
707 for (i = 0; i < num_of_elems; i++) {
708 if (tlv_size_diff > 0) {
709 /* Incoming structure size is greater than expected
710 * structure size. so copy the number of bytes equal
711 * to expected structure size */
712 wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
713 (void *)(buf_ptr +
714 i *
715 in_tlv_len),
716 attr_struct_ptr.
717 tag_struct_size);
718 } else {
719 /* Incoming structure size is smaller than expected
720 * structure size. so copy the number of bytes equal
721 * to incoming structure size */
722 wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
723 (void *)(buf_ptr +
724 i *
725 in_tlv_len),
726 in_tlv_len);
727 }
728 tlv_buf_ptr += attr_struct_ptr.tag_struct_size;
729 }
730#else
731 {
732 A_UINT8 *src_addr;
733 A_UINT8 *dst_addr;
734 A_UINT32 buf_mov_len;
735
736 if (tlv_size_diff < 0) {
737 /* Incoming structure size is smaller than expected size
738 * then this needs padding for each element in the array */
739
740 /* Find amount of bytes to be padded for one element */
741 num_padding_bytes = tlv_size_diff * -1;
742
743 /* Move subsequent TLVs by number of bytes to be padded
744 * for all elements */
745 if (param_buf_len >
746 (buf_idx + curr_tlv_len)) {
747 src_addr =
748 buf_ptr + curr_tlv_len;
749 dst_addr =
750 buf_ptr + curr_tlv_len +
751 (num_padding_bytes *
752 num_of_elems);
753 buf_mov_len =
754 param_buf_len - (buf_idx +
755 curr_tlv_len);
756
757 wmi_tlv_OS_MEMMOVE(dst_addr,
758 src_addr,
759 buf_mov_len);
760 }
761
762 /* Move subsequent elements of array down by number of
763 * bytes to be padded for one element and alse set
764 * padding bytes to zero */
765 tlv_buf_ptr = buf_ptr;
766 for (i = 0; i < num_of_elems; i++) {
767 src_addr =
768 tlv_buf_ptr + in_tlv_len;
769 if (i != (num_of_elems - 1)) {
770 /* Need not move anything for last element
771 * in the array */
772 dst_addr =
773 tlv_buf_ptr +
774 in_tlv_len +
775 num_padding_bytes;
776 buf_mov_len =
777 curr_tlv_len -
778 ((i +
779 1) * in_tlv_len);
780
781 wmi_tlv_OS_MEMMOVE
782 (dst_addr, src_addr,
783 buf_mov_len);
784 }
785
786 /* Set the padding bytes to zeroes */
787 wmi_tlv_OS_MEMZERO(src_addr,
788 num_padding_bytes);
789
790 tlv_buf_ptr +=
791 attr_struct_ptr.
792 tag_struct_size;
793 }
794
795 /* Update the number of padding bytes to total number
796 * of bytes padded for all elements in the array */
797 num_padding_bytes =
798 num_padding_bytes * num_of_elems;
799
800 new_tlv_buf = buf_ptr;
801 } else {
802 /* Incoming structure size is greater than expected size
803 * then this needs shrinking for each element in the array */
804
805 /* Find amount of bytes to be shrinked for one element */
806 num_padding_bytes = tlv_size_diff * -1;
807
808 /* Move subsequent elements of array up by number of bytes
809 * to be shrinked for one element */
810 tlv_buf_ptr = buf_ptr;
811 for (i = 0; i < (num_of_elems - 1); i++) {
812 src_addr =
813 tlv_buf_ptr + in_tlv_len;
814 dst_addr =
815 tlv_buf_ptr + in_tlv_len +
816 num_padding_bytes;
817 buf_mov_len =
818 curr_tlv_len -
819 ((i + 1) * in_tlv_len);
820
821 wmi_tlv_OS_MEMMOVE(dst_addr,
822 src_addr,
823 buf_mov_len);
824
825 tlv_buf_ptr +=
826 attr_struct_ptr.
827 tag_struct_size;
828 }
829
830 /* Move subsequent TLVs by number of bytes to be shrinked
831 * for all elements */
832 if (param_buf_len >
833 (buf_idx + curr_tlv_len)) {
834 src_addr =
835 buf_ptr + curr_tlv_len;
836 dst_addr =
837 buf_ptr + curr_tlv_len +
838 (num_padding_bytes *
839 num_of_elems);
840 buf_mov_len =
841 param_buf_len - (buf_idx +
842 curr_tlv_len);
843
844 wmi_tlv_OS_MEMMOVE(dst_addr,
845 src_addr,
846 buf_mov_len);
847 }
848
849 /* Update the number of padding bytes to total number of
850 * bytes shrinked for all elements in the array */
851 num_padding_bytes =
852 num_padding_bytes * num_of_elems;
853
854 new_tlv_buf = buf_ptr;
855 }
856 }
857#endif
858 cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
859 cmd_param_tlvs_ptr[tlv_index].num_elements =
860 num_of_elems;
861 cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1; /* Indicates that buffer is allocated */
862
863 } else if (tlv_size_diff >= 0) {
864 /* Warning: some parameter truncation */
865 if (tlv_size_diff > 0) {
866 wmi_tlv_print_verbose
867 ("%s: WARN: TLV truncated. tlv_size_diff=%d, curr_tlv_len=%d\n",
868 __func__, tlv_size_diff, curr_tlv_len);
869 }
870 /* TODO: this next line needs more comments and explanation */
871 cmd_param_tlvs_ptr[tlv_index].tlv_ptr =
872 (attr_struct_ptr.tag_varied_size
873 && !curr_tlv_len) ? NULL : (void *)buf_ptr;
874 cmd_param_tlvs_ptr[tlv_index].num_elements =
875 num_of_elems;
876 cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 0; /* Indicates that buffer is not allocated */
877 } else {
878 void *new_tlv_buf = NULL;
879
880 /* Warning: Needs to allocate a larger structure and pad with zeros */
881 wmi_tlv_print_verbose
882 ("%s: WARN: TLV needs padding. tlv_size_diff=%d\n",
883 __func__, tlv_size_diff);
884#ifndef NO_DYNAMIC_MEM_ALLOC
885 /* Dynamic memory allocation is supported */
886 wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
887 (curr_tlv_len - tlv_size_diff));
888 if (new_tlv_buf == NULL) {
889 /* Error: unable to alloc memory */
890 wmi_tlv_print_error
891 ("%s: Error: unable to alloc memory (size=%d) for padding the TLV %d\n",
892 __func__, (curr_tlv_len - tlv_size_diff),
893 curr_tlv_tag);
894 goto Error_wmitlv_check_and_pad_tlvs;
895 }
896
897 wmi_tlv_OS_MEMZERO(new_tlv_buf,
898 (curr_tlv_len - tlv_size_diff));
899 wmi_tlv_OS_MEMCPY(new_tlv_buf, (void *)buf_ptr,
900 curr_tlv_len);
901#else
902 /* Dynamic memory allocation is not supported. Padding has
903 * to be done with in the existing buffer assuming we have
904 * enough space to grow */
905 {
906 /* Note: tlv_size_diff is a value less than zero */
907 /* Move the Subsequent TLVs by amount of bytes needs to be padded */
908 A_UINT8 *src_addr;
909 A_UINT8 *dst_addr;
910 A_UINT32 src_len;
911
912 num_padding_bytes = (tlv_size_diff * -1);
913
914 src_addr = buf_ptr + curr_tlv_len;
915 dst_addr =
916 buf_ptr + curr_tlv_len + num_padding_bytes;
917 src_len =
918 param_buf_len - (buf_idx + curr_tlv_len);
919
920 wmi_tlv_OS_MEMMOVE(dst_addr, src_addr, src_len);
921
922 /* Set the padding bytes to zeroes */
923 wmi_tlv_OS_MEMZERO(src_addr, num_padding_bytes);
924
925 new_tlv_buf = buf_ptr;
926 }
927#endif
928 cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
929 cmd_param_tlvs_ptr[tlv_index].num_elements =
930 num_of_elems;
931 cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1; /* Indicates that buffer is allocated */
932 }
933
934 tlv_index++;
935 remaining_expected_tlvs--;
936 buf_ptr += curr_tlv_len + num_padding_bytes;
937 buf_idx += curr_tlv_len + num_padding_bytes;
938 }
939
940 return 0;
941Error_wmitlv_check_and_pad_tlvs:
942 if (is_cmd_id) {
943 wmitlv_free_allocated_command_tlvs(wmi_cmd_event_id,
944 wmi_cmd_struct_ptr);
945 } else {
946 wmitlv_free_allocated_event_tlvs(wmi_cmd_event_id,
947 wmi_cmd_struct_ptr);
948 }
949 *wmi_cmd_struct_ptr = NULL;
950 return error;
951}
952
953/**
954 * wmitlv_check_and_pad_event_tlvs() - tlv helper function
955 * @os_handle: os context handle
956 * @param_struc_ptr: pointer to tlv structure
957 * @param_buf_len: length of tlv parameter
958 * @wmi_cmd_event_id: command event id
959 * @wmi_cmd_struct_ptr: wmi command structure
960 *
961 *
962 * validate and pad(if necessary) for incoming WMI Event TLVs
963 *
964 * Return: 0 if success. Return < 0 if failure.
965 */
966int
967wmitlv_check_and_pad_event_tlvs(void *os_handle, void *param_struc_ptr,
968 A_UINT32 param_buf_len,
969 A_UINT32 wmi_cmd_event_id,
970 void **wmi_cmd_struct_ptr)
971{
972 A_UINT32 is_cmd_id = 0;
973 return wmitlv_check_and_pad_tlvs
974 (os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
975 wmi_cmd_event_id, wmi_cmd_struct_ptr);
976}
977
978/**
979 * wmitlv_check_and_pad_command_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 Command TLVs
988 *
989 * Return: 0 if success. Return < 0 if failure.
990 */
991int
992wmitlv_check_and_pad_command_tlvs(void *os_handle, void *param_struc_ptr,
993 A_UINT32 param_buf_len,
994 A_UINT32 wmi_cmd_event_id,
995 void **wmi_cmd_struct_ptr)
996{
997 A_UINT32 is_cmd_id = 1;
998 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}
1002
1003/**
1004 * wmitlv_free_allocated_tlvs() - tlv helper function
1005 * @is_cmd_id: bollean to check if cmd or event tlv
1006 * @cmd_event_id: command or event id
1007 * @wmi_cmd_struct_ptr: wmi command structure
1008 *
1009 *
1010 * free any allocated buffers for WMI Event/Command TLV processing
1011 *
1012 * Return: none
1013 */
1014static void wmitlv_free_allocated_tlvs(A_UINT32 is_cmd_id,
1015 A_UINT32 cmd_event_id,
1016 void **wmi_cmd_struct_ptr)
1017{
1018 void *ptr = *wmi_cmd_struct_ptr;
1019
1020 if (!ptr) {
1021 wmi_tlv_print_error("%s: Nothing to free for CMD/Event 0x%x\n",
1022 __func__, cmd_event_id);
1023 return;
1024 }
1025#ifndef NO_DYNAMIC_MEM_ALLOC
1026
1027/* macro to free that previously allocated memory for this TLV. When (op==FREE_TLV_ELEM). */
1028#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) \
1029 if ((((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->WMITLV_FIELD_BUF_IS_ALLOCATED(elem_name)) && \
1030 (((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name != NULL)) \
1031 { \
1032 wmi_tlv_os_mem_free(((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name); \
1033 }
1034
1035#define WMITLV_FREE_TLV_ELEMS(id) \
1036case id: \
1037{ \
1038 WMITLV_TABLE(id, FREE_TLV_ELEM, NULL, 0) \
1039} \
1040break;
1041
1042 if (is_cmd_id) {
1043 switch (cmd_event_id) {
1044 WMITLV_ALL_CMD_LIST(WMITLV_FREE_TLV_ELEMS);
1045 default:
1046 wmi_tlv_print_error
1047 ("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
1048 __func__, cmd_event_id, cmd_event_id);
1049 }
1050 } else {
1051 switch (cmd_event_id) {
1052 WMITLV_ALL_EVT_LIST(WMITLV_FREE_TLV_ELEMS);
1053 default:
1054 wmi_tlv_print_error
1055 ("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
1056 __func__, cmd_event_id, cmd_event_id);
1057 }
1058 }
1059
1060 wmi_tlv_os_mem_free(*wmi_cmd_struct_ptr);
1061 *wmi_cmd_struct_ptr = NULL;
1062#endif
1063
1064 return;
1065}
1066
1067/**
1068 * wmitlv_free_allocated_command_tlvs() - tlv helper function
1069 * @cmd_event_id: command or event id
1070 * @wmi_cmd_struct_ptr: wmi command structure
1071 *
1072 *
1073 * free any allocated buffers for WMI Event/Command TLV processing
1074 *
1075 * Return: none
1076 */
1077void wmitlv_free_allocated_command_tlvs(A_UINT32 cmd_event_id,
1078 void **wmi_cmd_struct_ptr)
1079{
1080 wmitlv_free_allocated_tlvs(1, cmd_event_id, wmi_cmd_struct_ptr);
1081}
1082
1083/**
1084 * wmitlv_free_allocated_event_tlvs() - tlv helper function
1085 * @cmd_event_id: command or event id
1086 * @wmi_cmd_struct_ptr: wmi command structure
1087 *
1088 *
1089 * free any allocated buffers for WMI Event/Command TLV processing
1090 *
1091 * Return: none
1092 */
1093void wmitlv_free_allocated_event_tlvs(A_UINT32 cmd_event_id,
1094 void **wmi_cmd_struct_ptr)
1095{
1096 wmitlv_free_allocated_tlvs(0, cmd_event_id, wmi_cmd_struct_ptr);
1097}
1098
1099/**
1100 * wmi_versions_are_compatible() - tlv helper function
1101 * @vers1: host wmi version
1102 * @vers2: target wmi version
1103 *
1104 *
1105 * check if two given wmi versions are compatible
1106 *
1107 * Return: none
1108 */
1109int
1110wmi_versions_are_compatible(wmi_abi_version *vers1, wmi_abi_version *vers2)
1111{
1112 if ((vers1->abi_version_ns_0 != vers2->abi_version_ns_0) ||
1113 (vers1->abi_version_ns_1 != vers2->abi_version_ns_1) ||
1114 (vers1->abi_version_ns_2 != vers2->abi_version_ns_2) ||
1115 (vers1->abi_version_ns_3 != vers2->abi_version_ns_3)) {
1116 /* The namespaces are different. Incompatible. */
1117 return 0;
1118 }
1119
1120 if (vers1->abi_version_0 != vers2->abi_version_0) {
1121 /* The major or minor versions are different. Incompatible */
1122 return 0;
1123 }
1124 /* We ignore the build version */
1125 return 1;
1126}
1127
1128/**
1129 * wmi_versions_can_downgrade() - tlv helper function
1130 * @version_whitelist_table: version table
1131 * @my_vers: host version
1132 * @opp_vers: target version
1133 * @out_vers: downgraded version
1134 *
1135 *
1136 * check if target wmi version can be downgraded
1137 *
1138 * Return: 0 if success. Return < 0 if failure.
1139 */
Jeff Johnson9366d7a2016-10-07 13:03:02 -07001140static int
Govind Singh6b411b52016-03-06 19:55:02 +05301141wmi_versions_can_downgrade(int num_whitelist,
1142 wmi_whitelist_version_info *version_whitelist_table,
1143 wmi_abi_version *my_vers,
1144 wmi_abi_version *opp_vers,
1145 wmi_abi_version *out_vers)
1146{
1147 A_UINT8 can_try_to_downgrade;
1148 A_UINT32 my_major_vers = WMI_VER_GET_MAJOR(my_vers->abi_version_0);
1149 A_UINT32 my_minor_vers = WMI_VER_GET_MINOR(my_vers->abi_version_0);
1150 A_UINT32 opp_major_vers = WMI_VER_GET_MAJOR(opp_vers->abi_version_0);
1151 A_UINT32 opp_minor_vers = WMI_VER_GET_MINOR(opp_vers->abi_version_0);
1152 A_UINT32 downgraded_minor_vers;
1153
1154 if ((my_vers->abi_version_ns_0 != opp_vers->abi_version_ns_0) ||
1155 (my_vers->abi_version_ns_1 != opp_vers->abi_version_ns_1) ||
1156 (my_vers->abi_version_ns_2 != opp_vers->abi_version_ns_2) ||
1157 (my_vers->abi_version_ns_3 != opp_vers->abi_version_ns_3)) {
1158 /* The namespaces are different. Incompatible. */
1159 can_try_to_downgrade = false;
1160 } else if (my_major_vers != opp_major_vers) {
1161 /* Major version is different. Incompatible and cannot downgrade. */
1162 can_try_to_downgrade = false;
1163 } else {
1164 /* Same major version. */
1165
1166 if (my_minor_vers < opp_minor_vers) {
1167 /* Opposite party is newer. Incompatible and cannot downgrade. */
1168 can_try_to_downgrade = false;
1169 } else if (my_minor_vers > opp_minor_vers) {
1170 /* Opposite party is older. Check whitelist if we can downgrade */
1171 can_try_to_downgrade = true;
1172 } else {
1173 /* Same version */
1174 wmi_tlv_OS_MEMCPY(out_vers, my_vers,
1175 sizeof(wmi_abi_version));
1176 return 1;
1177 }
1178 }
1179
1180 if (!can_try_to_downgrade) {
1181 wmi_tlv_print_error("%s: Warning: incompatible WMI version.\n",
1182 __func__);
1183 wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1184 return 0;
1185 }
1186 /* Try to see we can downgrade the supported version */
1187 downgraded_minor_vers = my_minor_vers;
1188 while (downgraded_minor_vers > opp_minor_vers) {
1189 A_UINT8 downgraded = false;
1190 int i;
1191
1192 for (i = 0; i < num_whitelist; i++) {
1193 if (version_whitelist_table[i].major != my_major_vers) {
1194 continue; /* skip */
1195 }
1196 if ((version_whitelist_table[i].namespace_0 !=
1197 my_vers->abi_version_ns_0)
1198 || (version_whitelist_table[i].namespace_1 !=
1199 my_vers->abi_version_ns_1)
1200 || (version_whitelist_table[i].namespace_2 !=
1201 my_vers->abi_version_ns_2)
1202 || (version_whitelist_table[i].namespace_3 !=
1203 my_vers->abi_version_ns_3)) {
1204 continue; /* skip */
1205 }
1206 if (version_whitelist_table[i].minor ==
1207 downgraded_minor_vers) {
1208 /* Found the next version that I can downgrade */
1209 wmi_tlv_print_error
1210 ("%s: Note: found a whitelist entry to downgrade. wh. list ver: %d,%d,0x%x 0x%x 0x%x 0x%x\n",
1211 __func__, version_whitelist_table[i].major,
1212 version_whitelist_table[i].minor,
1213 version_whitelist_table[i].namespace_0,
1214 version_whitelist_table[i].namespace_1,
1215 version_whitelist_table[i].namespace_2,
1216 version_whitelist_table[i].namespace_3);
1217 downgraded_minor_vers--;
1218 downgraded = true;
1219 break;
1220 }
1221 }
1222 if (!downgraded) {
1223 break; /* Done since we did not find any whitelist to downgrade version */
1224 }
1225 }
1226 wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1227 out_vers->abi_version_0 =
1228 WMI_VER_GET_VERSION_0(my_major_vers, downgraded_minor_vers);
1229 if (downgraded_minor_vers != opp_minor_vers) {
1230 wmi_tlv_print_error
1231 ("%s: Warning: incompatible WMI version and cannot downgrade.\n",
1232 __func__);
1233 return 0; /* Incompatible */
1234 } else {
1235 return 1; /* Compatible */
1236 }
1237}
1238
1239/**
1240 * wmi_cmp_and_set_abi_version() - tlv helper function
1241 * @version_whitelist_table: version table
1242 * @my_vers: host version
1243 * @opp_vers: target version
1244 * @out_vers: downgraded version
1245 *
1246 * This routine will compare and set the WMI ABI version.
1247 * First, compare my version with the opposite side's version.
1248 * If incompatible, then check the whitelist to see if our side can downgrade.
1249 * Finally, fill in the final ABI version into the output, out_vers.
1250 * Return 0 if the output version is compatible
1251 * Else return 1 if the output version is incompatible
1252 *
1253 * Return: 0 if the output version is compatible else < 0.
1254 */
1255int
1256wmi_cmp_and_set_abi_version(int num_whitelist,
1257 wmi_whitelist_version_info *
1258 version_whitelist_table,
1259 struct _wmi_abi_version *my_vers,
1260 struct _wmi_abi_version *opp_vers,
1261 struct _wmi_abi_version *out_vers)
1262{
1263 wmi_tlv_print_verbose
1264 ("%s: Our WMI Version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
1265 __func__, WMI_VER_GET_MAJOR(my_vers->abi_version_0),
1266 WMI_VER_GET_MINOR(my_vers->abi_version_0), my_vers->abi_version_1,
1267 my_vers->abi_version_ns_0, my_vers->abi_version_ns_1,
1268 my_vers->abi_version_ns_2, my_vers->abi_version_ns_3);
1269
1270 wmi_tlv_print_verbose
1271 ("%s: Opposite side 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(opp_vers->abi_version_0),
1273 WMI_VER_GET_MINOR(opp_vers->abi_version_0),
1274 opp_vers->abi_version_1, opp_vers->abi_version_ns_0,
1275 opp_vers->abi_version_ns_1, opp_vers->abi_version_ns_2,
1276 opp_vers->abi_version_ns_3);
1277
1278 /* By default, the output version is our version. */
1279 wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
1280 if (!wmi_versions_are_compatible(my_vers, opp_vers)) {
1281 /* Our host version and the given firmware version are incompatible. */
1282 if (wmi_versions_can_downgrade
1283 (num_whitelist, version_whitelist_table, my_vers, opp_vers,
1284 out_vers)) {
1285 /* We can downgrade our host versions to match firmware. */
1286 wmi_tlv_print_error
1287 ("%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",
1288 __func__,
1289 WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1290 WMI_VER_GET_MINOR(out_vers->abi_version_0),
1291 out_vers->abi_version_1,
1292 out_vers->abi_version_ns_0,
1293 out_vers->abi_version_ns_1,
1294 out_vers->abi_version_ns_2,
1295 out_vers->abi_version_ns_3);
1296 return 0; /* Compatible */
1297 } else {
1298 /* Warn: We cannot downgrade our host versions to match firmware. */
1299 wmi_tlv_print_error
1300 ("%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",
1301 __func__,
1302 WMI_VER_GET_MAJOR(out_vers->abi_version_0),
1303 WMI_VER_GET_MINOR(out_vers->abi_version_0),
1304 out_vers->abi_version_1,
1305 out_vers->abi_version_ns_0,
1306 out_vers->abi_version_ns_1,
1307 out_vers->abi_version_ns_2,
1308 out_vers->abi_version_ns_3);
1309
1310 return 1; /* Incompatible */
1311 }
1312 } else {
1313 /* We are compatible. Our host version is the output version */
1314 wmi_tlv_print_verbose
1315 ("%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",
1316 __func__, 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, out_vers->abi_version_ns_0,
1319 out_vers->abi_version_ns_1, out_vers->abi_version_ns_2,
1320 out_vers->abi_version_ns_3);
1321 return 0; /* Compatible */
1322 }
1323}