
Per the Linux Kernel coding style, as enforced by the kernel checkpatch script, pointers should not be explicitly compared to NULL. Therefore within wmi replace any such comparisons with logical operations performed on the pointer itself. Change-Id: Ida6b1b32330d97392bd879f84929782a64f4fc85 CRs-Fixed: 2418250
1351 lines
42 KiB
C
1351 lines
42 KiB
C
/*
|
|
* Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for
|
|
* any purpose with or without fee is hereby granted, provided that the
|
|
* above copyright notice and this permission notice appear in all
|
|
* copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
|
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
|
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
|
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "wmi_tlv_platform.c"
|
|
#include "wmi_tlv_defs.h"
|
|
#include "wmi_version.h"
|
|
#include "qdf_module.h"
|
|
|
|
#define WMITLV_GET_ATTRIB_NUM_TLVS 0xFFFFFFFF
|
|
|
|
#define WMITLV_GET_CMDID(val) (val & 0x00FFFFFF)
|
|
#define WMITLV_GET_NUM_TLVS(val) ((val >> 24) & 0xFF)
|
|
|
|
#define WMITLV_GET_TAGID(val) (val & 0x00000FFF)
|
|
#define WMITLV_GET_TAG_STRUCT_SIZE(val) ((val >> 12) & 0x000001FF)
|
|
#define WMITLV_GET_TAG_ARRAY_SIZE(val) ((val >> 21) & 0x000001FF)
|
|
#define WMITLV_GET_TAG_VARIED(val) ((val >> 30) & 0x00000001)
|
|
|
|
#define WMITLV_SET_ATTRB0(id) ((WMITLV_GET_TAG_NUM_TLV_ATTRIB(id) << 24) | \
|
|
(id & 0x00FFFFFF))
|
|
#define WMITLV_SET_ATTRB1(tagID, tagStructSize, tagArraySize, tagVaried) \
|
|
(((tagVaried&0x1)<<30) | ((tagArraySize&0x1FF)<<21) | \
|
|
((tagStructSize&0x1FF)<<12) | (tagID&0xFFF))
|
|
|
|
#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) \
|
|
WMITLV_SET_ATTRB1(elem_tlv_tag, sizeof(elem_struc_type), arr_size, var_len),
|
|
|
|
#define WMITLV_GET_CMD_EVT_ATTRB_LIST(id) \
|
|
WMITLV_SET_ATTRB0(id), \
|
|
WMITLV_TABLE(id,SET_TLV_ATTRIB, NULL, 0)
|
|
|
|
uint32_t cmd_attr_list[] = {
|
|
WMITLV_ALL_CMD_LIST(WMITLV_GET_CMD_EVT_ATTRB_LIST)
|
|
};
|
|
|
|
uint32_t evt_attr_list[] = {
|
|
WMITLV_ALL_EVT_LIST(WMITLV_GET_CMD_EVT_ATTRB_LIST)
|
|
};
|
|
|
|
#ifdef NO_DYNAMIC_MEM_ALLOC
|
|
static wmitlv_cmd_param_info *g_wmi_static_cmd_param_info_buf;
|
|
uint32_t g_wmi_static_max_cmd_param_tlvs;
|
|
#endif
|
|
|
|
|
|
/**
|
|
* wmitlv_set_static_param_tlv_buf() - tlv helper function
|
|
* @param_tlv_buf: tlv buffer parameter
|
|
* @max_tlvs_accommodated: max no of tlv entries
|
|
*
|
|
*
|
|
* WMI TLV Helper function to set the static cmd_param_tlv structure
|
|
* and number of TLVs that can be accommodated in the structure.
|
|
* This function should be used when dynamic memory allocation is not
|
|
* supported. When dynamic memory allocation is not supported by any
|
|
* component then NO_DYNAMIC_MEMALLOC macro has to be defined in respective
|
|
* tlv_platform.c file. And respective component has to allocate
|
|
* cmd_param_tlv structure buffer to accommodate whatever number of TLV's.
|
|
* Both the buffer address and number of TLV's that can be accommodated in
|
|
* the buffer should be sent as arguments to this function.
|
|
*
|
|
* Return None
|
|
*/
|
|
void
|
|
wmitlv_set_static_param_tlv_buf(void *param_tlv_buf,
|
|
uint32_t max_tlvs_accommodated)
|
|
{
|
|
#ifdef NO_DYNAMIC_MEM_ALLOC
|
|
g_wmi_static_cmd_param_info_buf = param_tlv_buf;
|
|
g_wmi_static_max_cmd_param_tlvs = max_tlvs_accommodated;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* wmitlv_get_attributes() - tlv helper function
|
|
* @is_cmd_id: boolean for command attribute
|
|
* @cmd_event_id: command event id
|
|
* @curr_tlv_order: tlv order
|
|
* @tlv_attr_ptr: pointer to tlv attribute
|
|
*
|
|
*
|
|
* WMI TLV Helper functions to find the attributes of the
|
|
* Command/Event TLVs.
|
|
*
|
|
* Return: 0 if success. Return >=1 if failure.
|
|
*/
|
|
static
|
|
uint32_t wmitlv_get_attributes(uint32_t is_cmd_id, uint32_t cmd_event_id,
|
|
uint32_t curr_tlv_order,
|
|
wmitlv_attributes_struc *tlv_attr_ptr)
|
|
{
|
|
uint32_t i, base_index, num_tlvs, num_entries;
|
|
uint32_t *pAttrArrayList;
|
|
|
|
if (is_cmd_id) {
|
|
pAttrArrayList = &cmd_attr_list[0];
|
|
num_entries = QDF_ARRAY_SIZE(cmd_attr_list);
|
|
} else {
|
|
pAttrArrayList = &evt_attr_list[0];
|
|
num_entries = QDF_ARRAY_SIZE(evt_attr_list);
|
|
}
|
|
|
|
for (i = 0; i < num_entries; i++) {
|
|
num_tlvs = WMITLV_GET_NUM_TLVS(pAttrArrayList[i]);
|
|
if (WMITLV_GET_CMDID(cmd_event_id) ==
|
|
WMITLV_GET_CMDID(pAttrArrayList[i])) {
|
|
tlv_attr_ptr->cmd_num_tlv = num_tlvs;
|
|
/* Return success from here when only number of TLVS for
|
|
* this command/event is required */
|
|
if (curr_tlv_order == WMITLV_GET_ATTRIB_NUM_TLVS) {
|
|
wmi_tlv_print_verbose
|
|
("%s: WMI TLV attribute definitions for %s:0x%x found; num_of_tlvs:%d\n",
|
|
__func__, (is_cmd_id ? "Cmd" : "Evt"),
|
|
cmd_event_id, num_tlvs);
|
|
return 0;
|
|
}
|
|
|
|
/* Return failure if tlv_order is more than the expected
|
|
* number of TLVs */
|
|
if (curr_tlv_order >= num_tlvs) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV order %d greater than num_of_tlvs:%d for %s:0x%x\n",
|
|
__func__, curr_tlv_order, num_tlvs,
|
|
(is_cmd_id ? "Cmd" : "Evt"), cmd_event_id);
|
|
return 1;
|
|
}
|
|
|
|
base_index = i + 1; /* index to first TLV attributes */
|
|
wmi_tlv_print_verbose
|
|
("%s: WMI TLV attributes for %s:0x%x tlv[%d]:0x%x\n",
|
|
__func__, (is_cmd_id ? "Cmd" : "Evt"),
|
|
cmd_event_id, curr_tlv_order,
|
|
pAttrArrayList[(base_index + curr_tlv_order)]);
|
|
tlv_attr_ptr->tag_order = curr_tlv_order;
|
|
tlv_attr_ptr->tag_id =
|
|
WMITLV_GET_TAGID(pAttrArrayList
|
|
[(base_index + curr_tlv_order)]);
|
|
tlv_attr_ptr->tag_struct_size =
|
|
WMITLV_GET_TAG_STRUCT_SIZE(pAttrArrayList
|
|
[(base_index +
|
|
curr_tlv_order)]);
|
|
tlv_attr_ptr->tag_varied_size =
|
|
WMITLV_GET_TAG_VARIED(pAttrArrayList
|
|
[(base_index +
|
|
curr_tlv_order)]);
|
|
tlv_attr_ptr->tag_array_size =
|
|
WMITLV_GET_TAG_ARRAY_SIZE(pAttrArrayList
|
|
[(base_index +
|
|
curr_tlv_order)]);
|
|
return 0;
|
|
}
|
|
i += num_tlvs;
|
|
}
|
|
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: Didn't found WMI TLV attribute definitions for %s:0x%x\n",
|
|
__func__, (is_cmd_id ? "Cmd" : "Evt"), cmd_event_id);
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* wmitlv_check_tlv_params() - tlv helper function
|
|
* @os_handle: os context handle
|
|
* @param_struc_ptr: pointer to tlv structure
|
|
* @is_cmd_id: boolean for command attribute
|
|
* @wmi_cmd_event_id: command event id
|
|
*
|
|
*
|
|
* Helper Function to vaidate the prepared TLV's for
|
|
* an WMI event/command to be sent.
|
|
*
|
|
* Return: 0 if success. Return < 0 if failure.
|
|
*/
|
|
static int
|
|
wmitlv_check_tlv_params(void *os_handle, void *param_struc_ptr,
|
|
uint32_t param_buf_len, uint32_t is_cmd_id,
|
|
uint32_t wmi_cmd_event_id)
|
|
{
|
|
wmitlv_attributes_struc attr_struct_ptr;
|
|
uint32_t buf_idx = 0;
|
|
uint32_t tlv_index = 0;
|
|
uint8_t *buf_ptr = (unsigned char *)param_struc_ptr;
|
|
uint32_t expected_num_tlvs, expected_tlv_len;
|
|
int32_t error = -1;
|
|
|
|
/* Get the number of TLVs for this command/event */
|
|
if (wmitlv_get_attributes
|
|
(is_cmd_id, wmi_cmd_event_id, WMITLV_GET_ATTRIB_NUM_TLVS,
|
|
&attr_struct_ptr) != 0) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: Couldn't get expected number of TLVs for Cmd=%d\n",
|
|
__func__, wmi_cmd_event_id);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
/* NOTE: the returned number of TLVs is in "attr_struct_ptr.cmd_num_tlv" */
|
|
|
|
expected_num_tlvs = attr_struct_ptr.cmd_num_tlv;
|
|
|
|
while ((buf_idx + WMI_TLV_HDR_SIZE) <= param_buf_len) {
|
|
uint32_t curr_tlv_tag =
|
|
WMITLV_GET_TLVTAG(WMITLV_GET_HDR(buf_ptr));
|
|
uint32_t curr_tlv_len =
|
|
WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
|
|
|
|
if ((buf_idx + WMI_TLV_HDR_SIZE + curr_tlv_len) > param_buf_len) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: Invalid TLV length for Cmd=%d Tag_order=%d buf_idx=%d Tag:%d Len:%d TotalLen:%d\n",
|
|
__func__, wmi_cmd_event_id, tlv_index, buf_idx,
|
|
curr_tlv_tag, curr_tlv_len, param_buf_len);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
/* Get the attributes of the TLV with the given order in "tlv_index" */
|
|
wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
|
|
sizeof(wmitlv_attributes_struc));
|
|
if (wmitlv_get_attributes
|
|
(is_cmd_id, wmi_cmd_event_id, tlv_index,
|
|
&attr_struct_ptr) != 0) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: No TLV attributes found for Cmd=%d Tag_order=%d\n",
|
|
__func__, wmi_cmd_event_id, tlv_index);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
/* Found the TLV that we wanted */
|
|
wmi_tlv_print_verbose("%s: [tlv %d]: tag=%d, len=%d\n",
|
|
__func__, tlv_index, curr_tlv_tag,
|
|
curr_tlv_len);
|
|
|
|
/* Validating Tag ID order */
|
|
if (curr_tlv_tag != attr_struct_ptr.tag_id) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV has wrong tag in order for Cmd=0x%x. Given=%d, Expected=%d.\n",
|
|
__func__, wmi_cmd_event_id, curr_tlv_tag,
|
|
attr_struct_ptr.tag_id);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
/* Validate Tag length */
|
|
/* Array TLVs length checking needs special handling */
|
|
if ((curr_tlv_tag >= WMITLV_TAG_FIRST_ARRAY_ENUM)
|
|
&& (curr_tlv_tag <= WMITLV_TAG_LAST_ARRAY_ENUM)) {
|
|
if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
|
|
/* Array size can't be invalid for fixed size Array TLV */
|
|
if (WMITLV_ARR_SIZE_INVALID ==
|
|
attr_struct_ptr.tag_array_size) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: array_size can't be invalid for Array TLV Cmd=0x%x Tag=%d\n",
|
|
__func__, wmi_cmd_event_id,
|
|
curr_tlv_tag);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
expected_tlv_len =
|
|
attr_struct_ptr.tag_array_size *
|
|
attr_struct_ptr.tag_struct_size;
|
|
/* Paddding is only required for Byte array Tlvs all other
|
|
* array tlv's should be aligned to 4 bytes during their
|
|
* definition */
|
|
if (WMITLV_TAG_ARRAY_BYTE ==
|
|
attr_struct_ptr.tag_id) {
|
|
expected_tlv_len =
|
|
roundup(expected_tlv_len,
|
|
sizeof(uint32_t));
|
|
}
|
|
|
|
if (curr_tlv_len != expected_tlv_len) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV has wrong length for Cmd=0x%x. Tag_order=%d Tag=%d, Given_Len:%d Expected_Len=%d.\n",
|
|
__func__, wmi_cmd_event_id,
|
|
tlv_index, curr_tlv_tag,
|
|
curr_tlv_len, expected_tlv_len);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
} else {
|
|
/* Array size should be invalid for variable size Array TLV */
|
|
if (WMITLV_ARR_SIZE_INVALID !=
|
|
attr_struct_ptr.tag_array_size) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: array_size should be invalid for Array TLV Cmd=0x%x Tag=%d\n",
|
|
__func__, wmi_cmd_event_id,
|
|
curr_tlv_tag);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
/* Incase of variable length TLV's, there is no expectation
|
|
* on the length field so do whatever checking you can
|
|
* depending on the TLV tag if TLV length is non-zero */
|
|
if (curr_tlv_len != 0) {
|
|
/* Verify TLV length is aligned to the size of structure */
|
|
if ((curr_tlv_len %
|
|
attr_struct_ptr.tag_struct_size) !=
|
|
0) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV length %d for Cmd=0x%x is not aligned to size of structure(%d bytes)\n",
|
|
__func__, curr_tlv_len,
|
|
wmi_cmd_event_id,
|
|
attr_struct_ptr.
|
|
tag_struct_size);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
if (curr_tlv_tag ==
|
|
WMITLV_TAG_ARRAY_STRUC) {
|
|
uint8_t *tlv_buf_ptr = NULL;
|
|
uint32_t in_tlv_len;
|
|
uint32_t idx;
|
|
uint32_t num_of_elems;
|
|
|
|
/* Verify length of inner TLVs */
|
|
|
|
num_of_elems =
|
|
curr_tlv_len /
|
|
attr_struct_ptr.
|
|
tag_struct_size;
|
|
/* Set tlv_buf_ptr to the first inner TLV address */
|
|
tlv_buf_ptr =
|
|
buf_ptr + WMI_TLV_HDR_SIZE;
|
|
for (idx = 0;
|
|
idx < num_of_elems;
|
|
idx++) {
|
|
in_tlv_len =
|
|
WMITLV_GET_TLVLEN
|
|
(WMITLV_GET_HDR
|
|
(tlv_buf_ptr));
|
|
if ((in_tlv_len +
|
|
WMI_TLV_HDR_SIZE)
|
|
!=
|
|
attr_struct_ptr.
|
|
tag_struct_size) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV has wrong length for Cmd=0x%x. Tag_order=%d Tag=%d, Given_Len:%zu Expected_Len=%d.\n",
|
|
__func__,
|
|
wmi_cmd_event_id,
|
|
tlv_index,
|
|
curr_tlv_tag,
|
|
(in_tlv_len
|
|
+
|
|
WMI_TLV_HDR_SIZE),
|
|
attr_struct_ptr.
|
|
tag_struct_size);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
tlv_buf_ptr +=
|
|
in_tlv_len +
|
|
WMI_TLV_HDR_SIZE;
|
|
}
|
|
} else
|
|
if ((curr_tlv_tag ==
|
|
WMITLV_TAG_ARRAY_UINT32)
|
|
|| (curr_tlv_tag ==
|
|
WMITLV_TAG_ARRAY_BYTE)
|
|
|| (curr_tlv_tag ==
|
|
WMITLV_TAG_ARRAY_FIXED_STRUC)) {
|
|
/* Nothing to verify here */
|
|
} else {
|
|
wmi_tlv_print_error
|
|
("%s ERROR Need to handle the Array tlv %d for variable length for Cmd=0x%x\n",
|
|
__func__,
|
|
attr_struct_ptr.tag_id,
|
|
wmi_cmd_event_id);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
/* Non-array TLV. */
|
|
|
|
if ((curr_tlv_len + WMI_TLV_HDR_SIZE) !=
|
|
attr_struct_ptr.tag_struct_size) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV has wrong length for Cmd=0x%x. Given=%zu, Expected=%d.\n",
|
|
__func__, wmi_cmd_event_id,
|
|
(curr_tlv_len + WMI_TLV_HDR_SIZE),
|
|
attr_struct_ptr.tag_struct_size);
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
}
|
|
|
|
/* Check TLV length is aligned to 4 bytes or not */
|
|
if ((curr_tlv_len % sizeof(uint32_t)) != 0) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV length %d for Cmd=0x%x is not aligned to %zu bytes\n",
|
|
__func__, curr_tlv_len, wmi_cmd_event_id,
|
|
sizeof(uint32_t));
|
|
goto Error_wmitlv_check_tlv_params;
|
|
}
|
|
|
|
tlv_index++;
|
|
buf_ptr += curr_tlv_len + WMI_TLV_HDR_SIZE;
|
|
buf_idx += curr_tlv_len + WMI_TLV_HDR_SIZE;
|
|
}
|
|
|
|
if (tlv_index != expected_num_tlvs) {
|
|
wmi_tlv_print_verbose
|
|
("%s: INFO: Less number of TLVs filled for Cmd=0x%x Filled %d Expected=%d\n",
|
|
__func__, wmi_cmd_event_id, tlv_index, expected_num_tlvs);
|
|
}
|
|
|
|
return 0;
|
|
Error_wmitlv_check_tlv_params:
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* wmitlv_check_event_tlv_params() - tlv helper function
|
|
* @os_handle: os context handle
|
|
* @param_struc_ptr: pointer to tlv structure
|
|
* @is_cmd_id: boolean for command attribute
|
|
* @wmi_cmd_event_id: command event id
|
|
*
|
|
*
|
|
* Helper Function to vaidate the prepared TLV's for
|
|
* an WMI event/command to be sent.
|
|
*
|
|
* Return: 0 if success. Return < 0 if failure.
|
|
*/
|
|
int
|
|
wmitlv_check_event_tlv_params(void *os_handle, void *param_struc_ptr,
|
|
uint32_t param_buf_len, uint32_t wmi_cmd_event_id)
|
|
{
|
|
uint32_t is_cmd_id = 0;
|
|
|
|
return wmitlv_check_tlv_params
|
|
(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
|
|
wmi_cmd_event_id);
|
|
}
|
|
|
|
/**
|
|
* wmitlv_check_command_tlv_params() - tlv helper function
|
|
* @os_handle: os context handle
|
|
* @param_struc_ptr: pointer to tlv structure
|
|
* @is_cmd_id: boolean for command attribute
|
|
* @wmi_cmd_event_id: command event id
|
|
*
|
|
*
|
|
* Helper Function to vaidate the prepared TLV's for
|
|
* an WMI event/command to be sent.
|
|
*
|
|
* Return: 0 if success. Return < 0 if failure.
|
|
*/
|
|
int
|
|
wmitlv_check_command_tlv_params(void *os_handle, void *param_struc_ptr,
|
|
uint32_t param_buf_len,
|
|
uint32_t wmi_cmd_event_id)
|
|
{
|
|
uint32_t is_cmd_id = 1;
|
|
|
|
return wmitlv_check_tlv_params
|
|
(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
|
|
wmi_cmd_event_id);
|
|
}
|
|
qdf_export_symbol(wmitlv_check_command_tlv_params);
|
|
|
|
/**
|
|
* wmitlv_check_and_pad_tlvs() - tlv helper function
|
|
* @os_handle: os context handle
|
|
* @param_buf_len: length of tlv parameter
|
|
* @param_struc_ptr: pointer to tlv structure
|
|
* @is_cmd_id: boolean for command attribute
|
|
* @wmi_cmd_event_id: command event id
|
|
* @wmi_cmd_struct_ptr: wmi command structure
|
|
*
|
|
*
|
|
* vaidate the TLV's coming for an event/command and
|
|
* also pads data to TLV's if necessary
|
|
*
|
|
* Return: 0 if success. Return < 0 if failure.
|
|
*/
|
|
static int
|
|
wmitlv_check_and_pad_tlvs(void *os_handle, void *param_struc_ptr,
|
|
uint32_t param_buf_len, uint32_t is_cmd_id,
|
|
uint32_t wmi_cmd_event_id, void **wmi_cmd_struct_ptr)
|
|
{
|
|
wmitlv_attributes_struc attr_struct_ptr;
|
|
uint32_t buf_idx = 0;
|
|
uint32_t tlv_index = 0;
|
|
uint32_t num_of_elems = 0;
|
|
int tlv_size_diff = 0;
|
|
uint8_t *buf_ptr = (unsigned char *)param_struc_ptr;
|
|
wmitlv_cmd_param_info *cmd_param_tlvs_ptr = NULL;
|
|
uint32_t remaining_expected_tlvs = 0xFFFFFFFF;
|
|
uint32_t len_wmi_cmd_struct_buf;
|
|
uint32_t free_buf_len;
|
|
int32_t error = -1;
|
|
|
|
/* Get the number of TLVs for this command/event */
|
|
if (wmitlv_get_attributes
|
|
(is_cmd_id, wmi_cmd_event_id, WMITLV_GET_ATTRIB_NUM_TLVS,
|
|
&attr_struct_ptr) != 0) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: Couldn't get expected number of TLVs for Cmd=%d\n",
|
|
__func__, wmi_cmd_event_id);
|
|
return error;
|
|
}
|
|
/* NOTE: the returned number of TLVs is in "attr_struct_ptr.cmd_num_tlv" */
|
|
|
|
if (param_buf_len < WMI_TLV_HDR_SIZE) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: Incorrect param buf length passed\n",
|
|
__func__);
|
|
return error;
|
|
}
|
|
|
|
/* Create base structure of format wmi_cmd_event_id##_param_tlvs */
|
|
len_wmi_cmd_struct_buf =
|
|
attr_struct_ptr.cmd_num_tlv * sizeof(wmitlv_cmd_param_info);
|
|
#ifndef NO_DYNAMIC_MEM_ALLOC
|
|
/* Dynamic memory allocation supported */
|
|
wmi_tlv_os_mem_alloc(os_handle, *wmi_cmd_struct_ptr,
|
|
len_wmi_cmd_struct_buf);
|
|
#else
|
|
/* 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(),
|
|
* for base structure of format wmi_cmd_event_id##_param_tlvs */
|
|
*wmi_cmd_struct_ptr = g_wmi_static_cmd_param_info_buf;
|
|
if (attr_struct_ptr.cmd_num_tlv > g_wmi_static_max_cmd_param_tlvs) {
|
|
/* Error: Expecting more TLVs that accommodated for static structure */
|
|
wmi_tlv_print_error
|
|
("%s: Error: Expecting more TLVs that accommodated for static structure. Expected:%d Accomodated:%d\n",
|
|
__func__, attr_struct_ptr.cmd_num_tlv,
|
|
g_wmi_static_max_cmd_param_tlvs);
|
|
return error;
|
|
}
|
|
#endif
|
|
if (!*wmi_cmd_struct_ptr) {
|
|
/* Error: unable to alloc memory */
|
|
wmi_tlv_print_error
|
|
("%s: Error: unable to alloc memory (size=%d) for TLV\n",
|
|
__func__, len_wmi_cmd_struct_buf);
|
|
return error;
|
|
}
|
|
|
|
cmd_param_tlvs_ptr = (wmitlv_cmd_param_info *) *wmi_cmd_struct_ptr;
|
|
wmi_tlv_OS_MEMZERO(cmd_param_tlvs_ptr, len_wmi_cmd_struct_buf);
|
|
remaining_expected_tlvs = attr_struct_ptr.cmd_num_tlv;
|
|
|
|
while (((buf_idx + WMI_TLV_HDR_SIZE) <= param_buf_len)
|
|
&& (remaining_expected_tlvs)) {
|
|
uint32_t curr_tlv_tag =
|
|
WMITLV_GET_TLVTAG(WMITLV_GET_HDR(buf_ptr));
|
|
uint32_t curr_tlv_len =
|
|
WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr));
|
|
int num_padding_bytes = 0;
|
|
|
|
free_buf_len = param_buf_len - (buf_idx + WMI_TLV_HDR_SIZE);
|
|
if (curr_tlv_len > free_buf_len) {
|
|
wmi_tlv_print_error("%s: TLV length overflow",
|
|
__func__);
|
|
goto Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
|
|
/* Get the attributes of the TLV with the given order in "tlv_index" */
|
|
wmi_tlv_OS_MEMZERO(&attr_struct_ptr,
|
|
sizeof(wmitlv_attributes_struc));
|
|
if (wmitlv_get_attributes
|
|
(is_cmd_id, wmi_cmd_event_id, tlv_index,
|
|
&attr_struct_ptr) != 0) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: No TLV attributes found for Cmd=%d Tag_order=%d\n",
|
|
__func__, wmi_cmd_event_id, tlv_index);
|
|
goto Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
|
|
/* Found the TLV that we wanted */
|
|
wmi_tlv_print_verbose("%s: [tlv %d]: tag=%d, len=%d\n",
|
|
__func__, tlv_index, curr_tlv_tag,
|
|
curr_tlv_len);
|
|
|
|
/* Validating Tag order */
|
|
if (curr_tlv_tag != attr_struct_ptr.tag_id) {
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV has wrong tag in order for Cmd=0x%x. Given=%d, Expected=%d.\n",
|
|
__func__, wmi_cmd_event_id, curr_tlv_tag,
|
|
attr_struct_ptr.tag_id);
|
|
goto Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
|
|
if ((curr_tlv_tag >= WMITLV_TAG_FIRST_ARRAY_ENUM)
|
|
&& (curr_tlv_tag <= WMITLV_TAG_LAST_ARRAY_ENUM)) {
|
|
/* Current Tag is an array of some kind. */
|
|
/* Skip the TLV header of this array */
|
|
buf_ptr += WMI_TLV_HDR_SIZE;
|
|
buf_idx += WMI_TLV_HDR_SIZE;
|
|
} else {
|
|
/* Non-array TLV. */
|
|
curr_tlv_len += WMI_TLV_HDR_SIZE;
|
|
}
|
|
|
|
if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
|
|
/* This TLV is fixed length */
|
|
if (WMITLV_ARR_SIZE_INVALID ==
|
|
attr_struct_ptr.tag_array_size) {
|
|
tlv_size_diff =
|
|
curr_tlv_len -
|
|
attr_struct_ptr.tag_struct_size;
|
|
num_of_elems =
|
|
(curr_tlv_len > WMI_TLV_HDR_SIZE) ? 1 : 0;
|
|
} else {
|
|
tlv_size_diff =
|
|
curr_tlv_len -
|
|
(attr_struct_ptr.tag_struct_size *
|
|
attr_struct_ptr.tag_array_size);
|
|
num_of_elems = attr_struct_ptr.tag_array_size;
|
|
}
|
|
} else {
|
|
/* This TLV has a variable number of elements */
|
|
if (WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) {
|
|
uint32_t in_tlv_len = 0;
|
|
|
|
if (curr_tlv_len != 0) {
|
|
in_tlv_len =
|
|
WMITLV_GET_TLVLEN(WMITLV_GET_HDR
|
|
(buf_ptr));
|
|
in_tlv_len += WMI_TLV_HDR_SIZE;
|
|
if (in_tlv_len > curr_tlv_len) {
|
|
wmi_tlv_print_error("%s: Invalid in_tlv_len=%d",
|
|
__func__,
|
|
in_tlv_len);
|
|
goto
|
|
Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
tlv_size_diff =
|
|
in_tlv_len -
|
|
attr_struct_ptr.tag_struct_size;
|
|
num_of_elems =
|
|
curr_tlv_len / in_tlv_len;
|
|
wmi_tlv_print_verbose
|
|
("%s: WARN: TLV array of structures in_tlv_len=%d struct_size:%d diff:%d num_of_elems=%d \n",
|
|
__func__, in_tlv_len,
|
|
attr_struct_ptr.tag_struct_size,
|
|
tlv_size_diff, num_of_elems);
|
|
} else {
|
|
tlv_size_diff = 0;
|
|
num_of_elems = 0;
|
|
}
|
|
} else
|
|
if ((WMITLV_TAG_ARRAY_UINT32 ==
|
|
attr_struct_ptr.tag_id)
|
|
|| (WMITLV_TAG_ARRAY_BYTE ==
|
|
attr_struct_ptr.tag_id)
|
|
|| (WMITLV_TAG_ARRAY_FIXED_STRUC ==
|
|
attr_struct_ptr.tag_id)) {
|
|
tlv_size_diff = 0;
|
|
num_of_elems =
|
|
curr_tlv_len /
|
|
attr_struct_ptr.tag_struct_size;
|
|
} else {
|
|
wmi_tlv_print_error
|
|
("%s ERROR Need to handle this tag ID for variable length %d\n",
|
|
__func__, attr_struct_ptr.tag_id);
|
|
goto Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
}
|
|
|
|
if ((WMITLV_TAG_ARRAY_STRUC == attr_struct_ptr.tag_id) &&
|
|
(tlv_size_diff != 0)) {
|
|
void *new_tlv_buf = NULL;
|
|
uint8_t *tlv_buf_ptr = NULL;
|
|
uint32_t in_tlv_len;
|
|
uint32_t i;
|
|
|
|
if (attr_struct_ptr.tag_varied_size == WMITLV_SIZE_FIX) {
|
|
/* This is not allowed. The tag WMITLV_TAG_ARRAY_STRUC can
|
|
* only be used with variable-length structure array
|
|
* should not have a fixed number of elements (contradicting).
|
|
* Use WMITLV_TAG_ARRAY_FIXED_STRUC tag for fixed size
|
|
* structure array(where structure never change without
|
|
* breaking compatibility) */
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: TLV (tag=%d) should be variable-length and not fixed length\n",
|
|
__func__, curr_tlv_tag);
|
|
goto Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
|
|
/* Warning: Needs to allocate a larger structure and pad with zeros */
|
|
wmi_tlv_print_verbose
|
|
("%s: WARN: TLV array of structures needs padding. tlv_size_diff=%d\n",
|
|
__func__, tlv_size_diff);
|
|
|
|
/* incoming structure length */
|
|
in_tlv_len =
|
|
WMITLV_GET_TLVLEN(WMITLV_GET_HDR(buf_ptr)) +
|
|
WMI_TLV_HDR_SIZE;
|
|
#ifndef NO_DYNAMIC_MEM_ALLOC
|
|
wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
|
|
(num_of_elems *
|
|
attr_struct_ptr.tag_struct_size));
|
|
if (!new_tlv_buf) {
|
|
/* Error: unable to alloc memory */
|
|
wmi_tlv_print_error
|
|
("%s: Error: unable to alloc memory (size=%d) for padding the TLV array %d\n",
|
|
__func__,
|
|
(num_of_elems *
|
|
attr_struct_ptr.tag_struct_size),
|
|
curr_tlv_tag);
|
|
goto Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
|
|
wmi_tlv_OS_MEMZERO(new_tlv_buf,
|
|
(num_of_elems *
|
|
attr_struct_ptr.tag_struct_size));
|
|
tlv_buf_ptr = (uint8_t *) new_tlv_buf;
|
|
for (i = 0; i < num_of_elems; i++) {
|
|
if (tlv_size_diff > 0) {
|
|
/* Incoming structure size is greater than expected
|
|
* structure size. so copy the number of bytes equal
|
|
* to expected structure size */
|
|
wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
|
|
(void *)(buf_ptr +
|
|
i *
|
|
in_tlv_len),
|
|
attr_struct_ptr.
|
|
tag_struct_size);
|
|
} else {
|
|
/* Incoming structure size is smaller than expected
|
|
* structure size. so copy the number of bytes equal
|
|
* to incoming structure size */
|
|
wmi_tlv_OS_MEMCPY(tlv_buf_ptr,
|
|
(void *)(buf_ptr +
|
|
i *
|
|
in_tlv_len),
|
|
in_tlv_len);
|
|
}
|
|
tlv_buf_ptr += attr_struct_ptr.tag_struct_size;
|
|
}
|
|
#else
|
|
{
|
|
uint8_t *src_addr;
|
|
uint8_t *dst_addr;
|
|
uint32_t buf_mov_len;
|
|
|
|
if (tlv_size_diff < 0) {
|
|
/* Incoming structure size is smaller than expected size
|
|
* then this needs padding for each element in the array */
|
|
|
|
/* Find amount of bytes to be padded for one element */
|
|
num_padding_bytes = tlv_size_diff * -1;
|
|
|
|
/* Move subsequent TLVs by number of bytes to be padded
|
|
* for all elements */
|
|
if ((free_buf_len <
|
|
attr_struct_ptr.tag_struct_size *
|
|
num_of_elems) ||
|
|
(param_buf_len <
|
|
buf_idx + curr_tlv_len +
|
|
num_padding_bytes * num_of_elems)) {
|
|
wmi_tlv_print_error("%s: Insufficent buffer\n",
|
|
__func__);
|
|
goto
|
|
Error_wmitlv_check_and_pad_tlvs;
|
|
} else {
|
|
src_addr =
|
|
buf_ptr + curr_tlv_len;
|
|
dst_addr =
|
|
buf_ptr + curr_tlv_len +
|
|
(num_padding_bytes *
|
|
num_of_elems);
|
|
buf_mov_len =
|
|
param_buf_len - (buf_idx +
|
|
curr_tlv_len);
|
|
|
|
wmi_tlv_OS_MEMMOVE(dst_addr,
|
|
src_addr,
|
|
buf_mov_len);
|
|
}
|
|
|
|
/* Move subsequent elements of array down by number of
|
|
* bytes to be padded for one element and alse set
|
|
* padding bytes to zero */
|
|
tlv_buf_ptr = buf_ptr;
|
|
for (i = 0; i < num_of_elems - 1; i++) {
|
|
src_addr =
|
|
tlv_buf_ptr + in_tlv_len;
|
|
if (i != (num_of_elems - 1)) {
|
|
dst_addr =
|
|
tlv_buf_ptr +
|
|
in_tlv_len +
|
|
num_padding_bytes;
|
|
buf_mov_len =
|
|
curr_tlv_len -
|
|
((i +
|
|
1) * in_tlv_len);
|
|
|
|
wmi_tlv_OS_MEMMOVE
|
|
(dst_addr, src_addr,
|
|
buf_mov_len);
|
|
}
|
|
|
|
/* Set the padding bytes to zeroes */
|
|
wmi_tlv_OS_MEMZERO(src_addr,
|
|
num_padding_bytes);
|
|
|
|
tlv_buf_ptr +=
|
|
attr_struct_ptr.
|
|
tag_struct_size;
|
|
}
|
|
src_addr = tlv_buf_ptr + in_tlv_len;
|
|
wmi_tlv_OS_MEMZERO(src_addr,
|
|
num_padding_bytes);
|
|
|
|
/* Update the number of padding bytes to total number
|
|
* of bytes padded for all elements in the array */
|
|
num_padding_bytes =
|
|
num_padding_bytes * num_of_elems;
|
|
|
|
new_tlv_buf = buf_ptr;
|
|
} else {
|
|
/* Incoming structure size is greater than expected size
|
|
* then this needs shrinking for each element in the array */
|
|
|
|
/* Find amount of bytes to be shrunk for one element */
|
|
num_padding_bytes = tlv_size_diff * -1;
|
|
|
|
/* Move subsequent elements of array up by number of bytes
|
|
* to be shrunk for one element */
|
|
tlv_buf_ptr = buf_ptr;
|
|
for (i = 0; i < (num_of_elems - 1); i++) {
|
|
src_addr =
|
|
tlv_buf_ptr + in_tlv_len;
|
|
dst_addr =
|
|
tlv_buf_ptr + in_tlv_len +
|
|
num_padding_bytes;
|
|
buf_mov_len =
|
|
curr_tlv_len -
|
|
((i + 1) * in_tlv_len);
|
|
|
|
wmi_tlv_OS_MEMMOVE(dst_addr,
|
|
src_addr,
|
|
buf_mov_len);
|
|
|
|
tlv_buf_ptr +=
|
|
attr_struct_ptr.
|
|
tag_struct_size;
|
|
}
|
|
|
|
/* Move subsequent TLVs by number of bytes to be shrunk
|
|
* for all elements */
|
|
if (param_buf_len >
|
|
(buf_idx + curr_tlv_len)) {
|
|
src_addr =
|
|
buf_ptr + curr_tlv_len;
|
|
dst_addr =
|
|
buf_ptr + curr_tlv_len +
|
|
(num_padding_bytes *
|
|
num_of_elems);
|
|
buf_mov_len =
|
|
param_buf_len - (buf_idx +
|
|
curr_tlv_len);
|
|
|
|
wmi_tlv_OS_MEMMOVE(dst_addr,
|
|
src_addr,
|
|
buf_mov_len);
|
|
}
|
|
|
|
/* Update the number of padding bytes to total number of
|
|
* bytes shrunk for all elements in the array */
|
|
num_padding_bytes =
|
|
num_padding_bytes * num_of_elems;
|
|
|
|
new_tlv_buf = buf_ptr;
|
|
}
|
|
}
|
|
#endif
|
|
cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
|
|
cmd_param_tlvs_ptr[tlv_index].num_elements =
|
|
num_of_elems;
|
|
cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1; /* Indicates that buffer is allocated */
|
|
|
|
} else if (tlv_size_diff >= 0) {
|
|
/* Warning: some parameter truncation */
|
|
if (tlv_size_diff > 0) {
|
|
wmi_tlv_print_verbose
|
|
("%s: WARN: TLV truncated. tlv_size_diff=%d, curr_tlv_len=%d\n",
|
|
__func__, tlv_size_diff, curr_tlv_len);
|
|
}
|
|
/* TODO: this next line needs more comments and explanation */
|
|
cmd_param_tlvs_ptr[tlv_index].tlv_ptr =
|
|
(attr_struct_ptr.tag_varied_size
|
|
&& !curr_tlv_len) ? NULL : (void *)buf_ptr;
|
|
cmd_param_tlvs_ptr[tlv_index].num_elements =
|
|
num_of_elems;
|
|
cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 0; /* Indicates that buffer is not allocated */
|
|
} else {
|
|
void *new_tlv_buf = NULL;
|
|
|
|
/* Warning: Needs to allocate a larger structure and pad with zeros */
|
|
wmi_tlv_print_verbose
|
|
("%s: WARN: TLV needs padding. tlv_size_diff=%d\n",
|
|
__func__, tlv_size_diff);
|
|
#ifndef NO_DYNAMIC_MEM_ALLOC
|
|
/* Dynamic memory allocation is supported */
|
|
wmi_tlv_os_mem_alloc(os_handle, new_tlv_buf,
|
|
(curr_tlv_len - tlv_size_diff));
|
|
if (!new_tlv_buf) {
|
|
/* Error: unable to alloc memory */
|
|
wmi_tlv_print_error
|
|
("%s: Error: unable to alloc memory (size=%d) for padding the TLV %d\n",
|
|
__func__, (curr_tlv_len - tlv_size_diff),
|
|
curr_tlv_tag);
|
|
goto Error_wmitlv_check_and_pad_tlvs;
|
|
}
|
|
|
|
wmi_tlv_OS_MEMZERO(new_tlv_buf,
|
|
(curr_tlv_len - tlv_size_diff));
|
|
wmi_tlv_OS_MEMCPY(new_tlv_buf, (void *)buf_ptr,
|
|
curr_tlv_len);
|
|
#else
|
|
/* Dynamic memory allocation is not supported. Padding has
|
|
* to be done with in the existing buffer assuming we have
|
|
* enough space to grow */
|
|
{
|
|
/* Note: tlv_size_diff is a value less than zero */
|
|
/* Move the Subsequent TLVs by amount of bytes needs to be padded */
|
|
uint8_t *src_addr;
|
|
uint8_t *dst_addr;
|
|
uint32_t src_len;
|
|
|
|
num_padding_bytes = (tlv_size_diff * -1);
|
|
|
|
src_addr = buf_ptr + curr_tlv_len;
|
|
dst_addr =
|
|
buf_ptr + curr_tlv_len + num_padding_bytes;
|
|
src_len =
|
|
param_buf_len - (buf_idx + curr_tlv_len);
|
|
|
|
wmi_tlv_OS_MEMMOVE(dst_addr, src_addr, src_len);
|
|
|
|
/* Set the padding bytes to zeroes */
|
|
wmi_tlv_OS_MEMZERO(src_addr, num_padding_bytes);
|
|
|
|
new_tlv_buf = buf_ptr;
|
|
}
|
|
#endif
|
|
cmd_param_tlvs_ptr[tlv_index].tlv_ptr = new_tlv_buf;
|
|
cmd_param_tlvs_ptr[tlv_index].num_elements =
|
|
num_of_elems;
|
|
cmd_param_tlvs_ptr[tlv_index].buf_is_allocated = 1; /* Indicates that buffer is allocated */
|
|
}
|
|
|
|
tlv_index++;
|
|
remaining_expected_tlvs--;
|
|
buf_ptr += curr_tlv_len + num_padding_bytes;
|
|
buf_idx += curr_tlv_len + num_padding_bytes;
|
|
}
|
|
|
|
return 0;
|
|
Error_wmitlv_check_and_pad_tlvs:
|
|
if (is_cmd_id) {
|
|
wmitlv_free_allocated_command_tlvs(wmi_cmd_event_id,
|
|
wmi_cmd_struct_ptr);
|
|
} else {
|
|
wmitlv_free_allocated_event_tlvs(wmi_cmd_event_id,
|
|
wmi_cmd_struct_ptr);
|
|
}
|
|
*wmi_cmd_struct_ptr = NULL;
|
|
return error;
|
|
}
|
|
|
|
/**
|
|
* wmitlv_check_and_pad_event_tlvs() - tlv helper function
|
|
* @os_handle: os context handle
|
|
* @param_struc_ptr: pointer to tlv structure
|
|
* @param_buf_len: length of tlv parameter
|
|
* @wmi_cmd_event_id: command event id
|
|
* @wmi_cmd_struct_ptr: wmi command structure
|
|
*
|
|
*
|
|
* validate and pad(if necessary) for incoming WMI Event TLVs
|
|
*
|
|
* Return: 0 if success. Return < 0 if failure.
|
|
*/
|
|
int
|
|
wmitlv_check_and_pad_event_tlvs(void *os_handle, void *param_struc_ptr,
|
|
uint32_t param_buf_len,
|
|
uint32_t wmi_cmd_event_id,
|
|
void **wmi_cmd_struct_ptr)
|
|
{
|
|
uint32_t is_cmd_id = 0;
|
|
return wmitlv_check_and_pad_tlvs
|
|
(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
|
|
wmi_cmd_event_id, wmi_cmd_struct_ptr);
|
|
}
|
|
qdf_export_symbol(wmitlv_check_and_pad_event_tlvs);
|
|
|
|
/**
|
|
* wmitlv_check_and_pad_command_tlvs() - tlv helper function
|
|
* @os_handle: os context handle
|
|
* @param_struc_ptr: pointer to tlv structure
|
|
* @param_buf_len: length of tlv parameter
|
|
* @wmi_cmd_event_id: command event id
|
|
* @wmi_cmd_struct_ptr: wmi command structure
|
|
*
|
|
*
|
|
* validate and pad(if necessary) for incoming WMI Command TLVs
|
|
*
|
|
* Return: 0 if success. Return < 0 if failure.
|
|
*/
|
|
int
|
|
wmitlv_check_and_pad_command_tlvs(void *os_handle, void *param_struc_ptr,
|
|
uint32_t param_buf_len,
|
|
uint32_t wmi_cmd_event_id,
|
|
void **wmi_cmd_struct_ptr)
|
|
{
|
|
uint32_t is_cmd_id = 1;
|
|
return wmitlv_check_and_pad_tlvs
|
|
(os_handle, param_struc_ptr, param_buf_len, is_cmd_id,
|
|
wmi_cmd_event_id, wmi_cmd_struct_ptr);
|
|
}
|
|
|
|
/**
|
|
* wmitlv_free_allocated_tlvs() - tlv helper function
|
|
* @is_cmd_id: bollean to check if cmd or event tlv
|
|
* @cmd_event_id: command or event id
|
|
* @wmi_cmd_struct_ptr: wmi command structure
|
|
*
|
|
*
|
|
* free any allocated buffers for WMI Event/Command TLV processing
|
|
*
|
|
* Return: none
|
|
*/
|
|
static void wmitlv_free_allocated_tlvs(uint32_t is_cmd_id,
|
|
uint32_t cmd_event_id,
|
|
void **wmi_cmd_struct_ptr)
|
|
{
|
|
void *ptr = *wmi_cmd_struct_ptr;
|
|
|
|
if (!ptr) {
|
|
wmi_tlv_print_error("%s: Nothing to free for CMD/Event 0x%x\n",
|
|
__func__, cmd_event_id);
|
|
return;
|
|
}
|
|
#ifndef NO_DYNAMIC_MEM_ALLOC
|
|
|
|
/* macro to free that previously allocated memory for this TLV. When (op==FREE_TLV_ELEM). */
|
|
#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) \
|
|
if ((((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->WMITLV_FIELD_BUF_IS_ALLOCATED(elem_name)) && \
|
|
(((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name)) \
|
|
{ \
|
|
wmi_tlv_os_mem_free(((WMITLV_TYPEDEF_STRUCT_PARAMS_TLVS(wmi_cmd_event_id) *)ptr)->elem_name); \
|
|
}
|
|
|
|
#define WMITLV_FREE_TLV_ELEMS(id) \
|
|
case id: \
|
|
{ \
|
|
WMITLV_TABLE(id, FREE_TLV_ELEM, NULL, 0) \
|
|
} \
|
|
break;
|
|
|
|
if (is_cmd_id) {
|
|
switch (cmd_event_id) {
|
|
WMITLV_ALL_CMD_LIST(WMITLV_FREE_TLV_ELEMS);
|
|
default:
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
|
|
__func__, cmd_event_id, cmd_event_id);
|
|
}
|
|
} else {
|
|
switch (cmd_event_id) {
|
|
WMITLV_ALL_EVT_LIST(WMITLV_FREE_TLV_ELEMS);
|
|
default:
|
|
wmi_tlv_print_error
|
|
("%s: ERROR: Cannot find the TLVs attributes for Cmd=0x%x, %d\n",
|
|
__func__, cmd_event_id, cmd_event_id);
|
|
}
|
|
}
|
|
|
|
wmi_tlv_os_mem_free(*wmi_cmd_struct_ptr);
|
|
*wmi_cmd_struct_ptr = NULL;
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* wmitlv_free_allocated_command_tlvs() - tlv helper function
|
|
* @cmd_event_id: command or event id
|
|
* @wmi_cmd_struct_ptr: wmi command structure
|
|
*
|
|
*
|
|
* free any allocated buffers for WMI Event/Command TLV processing
|
|
*
|
|
* Return: none
|
|
*/
|
|
void wmitlv_free_allocated_command_tlvs(uint32_t cmd_event_id,
|
|
void **wmi_cmd_struct_ptr)
|
|
{
|
|
wmitlv_free_allocated_tlvs(1, cmd_event_id, wmi_cmd_struct_ptr);
|
|
}
|
|
|
|
/**
|
|
* wmitlv_free_allocated_event_tlvs() - tlv helper function
|
|
* @cmd_event_id: command or event id
|
|
* @wmi_cmd_struct_ptr: wmi command structure
|
|
*
|
|
*
|
|
* free any allocated buffers for WMI Event/Command TLV processing
|
|
*
|
|
* Return: none
|
|
*/
|
|
void wmitlv_free_allocated_event_tlvs(uint32_t cmd_event_id,
|
|
void **wmi_cmd_struct_ptr)
|
|
{
|
|
wmitlv_free_allocated_tlvs(0, cmd_event_id, wmi_cmd_struct_ptr);
|
|
}
|
|
qdf_export_symbol(wmitlv_free_allocated_event_tlvs);
|
|
|
|
/**
|
|
* wmi_versions_are_compatible() - tlv helper function
|
|
* @vers1: host wmi version
|
|
* @vers2: target wmi version
|
|
*
|
|
*
|
|
* check if two given wmi versions are compatible
|
|
*
|
|
* Return: none
|
|
*/
|
|
int
|
|
wmi_versions_are_compatible(wmi_abi_version *vers1, wmi_abi_version *vers2)
|
|
{
|
|
if ((vers1->abi_version_ns_0 != vers2->abi_version_ns_0) ||
|
|
(vers1->abi_version_ns_1 != vers2->abi_version_ns_1) ||
|
|
(vers1->abi_version_ns_2 != vers2->abi_version_ns_2) ||
|
|
(vers1->abi_version_ns_3 != vers2->abi_version_ns_3)) {
|
|
/* The namespaces are different. Incompatible. */
|
|
return 0;
|
|
}
|
|
|
|
if (vers1->abi_version_0 != vers2->abi_version_0) {
|
|
/* The major or minor versions are different. Incompatible */
|
|
return 0;
|
|
}
|
|
/* We ignore the build version */
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* wmi_versions_can_downgrade() - tlv helper function
|
|
* @version_whitelist_table: version table
|
|
* @my_vers: host version
|
|
* @opp_vers: target version
|
|
* @out_vers: downgraded version
|
|
*
|
|
*
|
|
* check if target wmi version can be downgraded
|
|
*
|
|
* Return: 0 if success. Return < 0 if failure.
|
|
*/
|
|
static int
|
|
wmi_versions_can_downgrade(int num_whitelist,
|
|
wmi_whitelist_version_info *version_whitelist_table,
|
|
wmi_abi_version *my_vers,
|
|
wmi_abi_version *opp_vers,
|
|
wmi_abi_version *out_vers)
|
|
{
|
|
uint8_t can_try_to_downgrade;
|
|
uint32_t my_major_vers = WMI_VER_GET_MAJOR(my_vers->abi_version_0);
|
|
uint32_t my_minor_vers = WMI_VER_GET_MINOR(my_vers->abi_version_0);
|
|
uint32_t opp_major_vers = WMI_VER_GET_MAJOR(opp_vers->abi_version_0);
|
|
uint32_t opp_minor_vers = WMI_VER_GET_MINOR(opp_vers->abi_version_0);
|
|
uint32_t downgraded_minor_vers;
|
|
|
|
if ((my_vers->abi_version_ns_0 != opp_vers->abi_version_ns_0) ||
|
|
(my_vers->abi_version_ns_1 != opp_vers->abi_version_ns_1) ||
|
|
(my_vers->abi_version_ns_2 != opp_vers->abi_version_ns_2) ||
|
|
(my_vers->abi_version_ns_3 != opp_vers->abi_version_ns_3)) {
|
|
/* The namespaces are different. Incompatible. */
|
|
can_try_to_downgrade = false;
|
|
} else if (my_major_vers != opp_major_vers) {
|
|
/* Major version is different. Incompatible and cannot downgrade. */
|
|
can_try_to_downgrade = false;
|
|
} else {
|
|
/* Same major version. */
|
|
|
|
if (my_minor_vers < opp_minor_vers) {
|
|
/* Opposite party is newer. Incompatible and cannot downgrade. */
|
|
can_try_to_downgrade = false;
|
|
} else if (my_minor_vers > opp_minor_vers) {
|
|
/* Opposite party is older. Check whitelist if we can downgrade */
|
|
can_try_to_downgrade = true;
|
|
} else {
|
|
/* Same version */
|
|
wmi_tlv_OS_MEMCPY(out_vers, my_vers,
|
|
sizeof(wmi_abi_version));
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (!can_try_to_downgrade) {
|
|
wmi_tlv_print_error("%s: Warning: incompatible WMI version.\n",
|
|
__func__);
|
|
wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
|
|
return 0;
|
|
}
|
|
/* Try to see we can downgrade the supported version */
|
|
downgraded_minor_vers = my_minor_vers;
|
|
while (downgraded_minor_vers > opp_minor_vers) {
|
|
uint8_t downgraded = false;
|
|
int i;
|
|
|
|
for (i = 0; i < num_whitelist; i++) {
|
|
if (version_whitelist_table[i].major != my_major_vers) {
|
|
continue; /* skip */
|
|
}
|
|
if ((version_whitelist_table[i].namespace_0 !=
|
|
my_vers->abi_version_ns_0)
|
|
|| (version_whitelist_table[i].namespace_1 !=
|
|
my_vers->abi_version_ns_1)
|
|
|| (version_whitelist_table[i].namespace_2 !=
|
|
my_vers->abi_version_ns_2)
|
|
|| (version_whitelist_table[i].namespace_3 !=
|
|
my_vers->abi_version_ns_3)) {
|
|
continue; /* skip */
|
|
}
|
|
if (version_whitelist_table[i].minor ==
|
|
downgraded_minor_vers) {
|
|
/* Found the next version that I can downgrade */
|
|
wmi_tlv_print_error
|
|
("%s: Note: found a whitelist entry to downgrade. wh. list ver: %d,%d,0x%x 0x%x 0x%x 0x%x\n",
|
|
__func__, version_whitelist_table[i].major,
|
|
version_whitelist_table[i].minor,
|
|
version_whitelist_table[i].namespace_0,
|
|
version_whitelist_table[i].namespace_1,
|
|
version_whitelist_table[i].namespace_2,
|
|
version_whitelist_table[i].namespace_3);
|
|
downgraded_minor_vers--;
|
|
downgraded = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!downgraded) {
|
|
break; /* Done since we did not find any whitelist to downgrade version */
|
|
}
|
|
}
|
|
wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
|
|
out_vers->abi_version_0 =
|
|
WMI_VER_GET_VERSION_0(my_major_vers, downgraded_minor_vers);
|
|
if (downgraded_minor_vers != opp_minor_vers) {
|
|
wmi_tlv_print_error
|
|
("%s: Warning: incompatible WMI version and cannot downgrade.\n",
|
|
__func__);
|
|
return 0; /* Incompatible */
|
|
} else {
|
|
return 1; /* Compatible */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* wmi_cmp_and_set_abi_version() - tlv helper function
|
|
* @version_whitelist_table: version table
|
|
* @my_vers: host version
|
|
* @opp_vers: target version
|
|
* @out_vers: downgraded version
|
|
*
|
|
* This routine will compare and set the WMI ABI version.
|
|
* First, compare my version with the opposite side's version.
|
|
* If incompatible, then check the whitelist to see if our side can downgrade.
|
|
* Finally, fill in the final ABI version into the output, out_vers.
|
|
* Return 0 if the output version is compatible
|
|
* Else return 1 if the output version is incompatible
|
|
*
|
|
* Return: 0 if the output version is compatible else < 0.
|
|
*/
|
|
int
|
|
wmi_cmp_and_set_abi_version(int num_whitelist,
|
|
wmi_whitelist_version_info *
|
|
version_whitelist_table,
|
|
struct _wmi_abi_version *my_vers,
|
|
struct _wmi_abi_version *opp_vers,
|
|
struct _wmi_abi_version *out_vers)
|
|
{
|
|
wmi_tlv_print_verbose
|
|
("%s: Our WMI Version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
|
|
__func__, WMI_VER_GET_MAJOR(my_vers->abi_version_0),
|
|
WMI_VER_GET_MINOR(my_vers->abi_version_0), my_vers->abi_version_1,
|
|
my_vers->abi_version_ns_0, my_vers->abi_version_ns_1,
|
|
my_vers->abi_version_ns_2, my_vers->abi_version_ns_3);
|
|
|
|
wmi_tlv_print_verbose
|
|
("%s: Opposite side WMI Version: Mj=%d, Mn=%d, bd=%d, ns0=0x%x ns1:0x%x ns2:0x%x ns3:0x%x\n",
|
|
__func__, WMI_VER_GET_MAJOR(opp_vers->abi_version_0),
|
|
WMI_VER_GET_MINOR(opp_vers->abi_version_0),
|
|
opp_vers->abi_version_1, opp_vers->abi_version_ns_0,
|
|
opp_vers->abi_version_ns_1, opp_vers->abi_version_ns_2,
|
|
opp_vers->abi_version_ns_3);
|
|
|
|
/* By default, the output version is our version. */
|
|
wmi_tlv_OS_MEMCPY(out_vers, my_vers, sizeof(wmi_abi_version));
|
|
if (!wmi_versions_are_compatible(my_vers, opp_vers)) {
|
|
/* Our host version and the given firmware version are incompatible. */
|
|
if (wmi_versions_can_downgrade
|
|
(num_whitelist, version_whitelist_table, my_vers, opp_vers,
|
|
out_vers)) {
|
|
/* We can downgrade our host versions to match firmware. */
|
|
wmi_tlv_print_error
|
|
("%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",
|
|
__func__,
|
|
WMI_VER_GET_MAJOR(out_vers->abi_version_0),
|
|
WMI_VER_GET_MINOR(out_vers->abi_version_0),
|
|
out_vers->abi_version_1,
|
|
out_vers->abi_version_ns_0,
|
|
out_vers->abi_version_ns_1,
|
|
out_vers->abi_version_ns_2,
|
|
out_vers->abi_version_ns_3);
|
|
return 0; /* Compatible */
|
|
} else {
|
|
/* Warn: We cannot downgrade our host versions to match firmware. */
|
|
wmi_tlv_print_error
|
|
("%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",
|
|
__func__,
|
|
WMI_VER_GET_MAJOR(out_vers->abi_version_0),
|
|
WMI_VER_GET_MINOR(out_vers->abi_version_0),
|
|
out_vers->abi_version_1,
|
|
out_vers->abi_version_ns_0,
|
|
out_vers->abi_version_ns_1,
|
|
out_vers->abi_version_ns_2,
|
|
out_vers->abi_version_ns_3);
|
|
|
|
return 1; /* Incompatible */
|
|
}
|
|
} else {
|
|
/* We are compatible. Our host version is the output version */
|
|
wmi_tlv_print_verbose
|
|
("%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",
|
|
__func__, WMI_VER_GET_MAJOR(out_vers->abi_version_0),
|
|
WMI_VER_GET_MINOR(out_vers->abi_version_0),
|
|
out_vers->abi_version_1, out_vers->abi_version_ns_0,
|
|
out_vers->abi_version_ns_1, out_vers->abi_version_ns_2,
|
|
out_vers->abi_version_ns_3);
|
|
return 0; /* Compatible */
|
|
}
|
|
}
|