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