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