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