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