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