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