/* * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. 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 * @param_buf_len: length of input buffer * @is_cmd_id: boolean for command attribute * @wmi_cmd_event_id: command event id * * * Helper Function to validate 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 * @param_buf_len: length of input buffer * @wmi_cmd_event_id: command event id * * * Helper Function to validate 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 * @param_buf_len: length of input buffer * @wmi_cmd_event_id: command event id * * * Helper Function to validate 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 * * * validate 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 Accommodated:%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, total_tlv=%d, remaining tlv=%d.\n", __func__, wmi_cmd_event_id, curr_tlv_tag, attr_struct_ptr.tag_id, attr_struct_ptr.cmd_num_tlv, remaining_expected_tlvs); 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) || (WMITLV_TAG_ARRAY_INT16 == 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: Insufficient 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 also 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 * @num_allowlist: number of entries in @version_whitelist_table * @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_allowlist, 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 allowlist 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_allowlist; 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 allowlist 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 allowlist * 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 * @num_allowlist: number of entries in @version_whitelist_table * @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 allowlist 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_allowlist, 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_allowlist, 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 */ } }