1 /* 2 * Copyright (c) 2021, The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 /** 19 * DOC: mlo_global_h_shmem_arena.c 20 * This file contains definition of functions that parse the MLO global 21 * shared memory arena. 22 */ 23 24 #include<mlo_global_h_shmem_arena.h> 25 26 static struct wlan_host_mlo_glb_h_shmem_arena_ctx g_shmem_arena_ctx; 27 28 #define get_shmem_arena_ctx() (&g_shmem_arena_ctx) 29 30 /** 31 * is_field_present_in_tlv() - Check whether a given field is present 32 * in a given TLV 33 * @ptlv: Pointer to start of the TLV 34 * @field_name: name of the field in the TLV structure 35 * @tlv_len: Length of the TLV 36 * 37 * Return: true if the field is present within the TLV, 38 * else false 39 */ 40 #define is_field_present_in_tlv(ptlv, field_name, tlv_len) \ 41 (qdf_offsetof(typeof(*(ptlv)), field_name) < (tlv_len) ? \ 42 true : false) 43 44 /** 45 * get_field_value_in_tlv() - Get the value of a given field in a given TLV 46 * @ptlv: Pointer to start of the TLV 47 * @field_name: name of the field in the TLV structure 48 * @tlv_len: Length of the TLV 49 * 50 * Return: Value of the given field if the offset of the field with in the TLV 51 * structure is less than the TLV length, else 0. 52 */ 53 #define get_field_value_in_tlv(ptlv, field_name, tlv_len) \ 54 (qdf_offsetof(typeof(*(ptlv)), field_name) < (tlv_len) ? \ 55 (ptlv)->field_name : 0) 56 57 /** 58 * get_field_pointer_in_tlv() - Get the address of a given field in a given TLV 59 * @ptlv: Pointer to start of the TLV 60 * @field_name: name of the field in the TLV structure 61 * @tlv_len: Length of the TLV 62 * 63 * Return: Address of the given field if the offset of the field with in the 64 * TLV structure is less than the TLV length, else NULL. 65 */ 66 #define get_field_pointer_in_tlv(ptlv, field_name, tlv_len) \ 67 (qdf_offsetof(typeof(*(ptlv)), field_name) < (tlv_len) ? \ 68 &(ptlv)->field_name : NULL) 69 70 /** 71 * process_tlv_header() - Process a given TLV header 72 * @data: Pointer to start of the TLV 73 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 74 * @expected_tag: Expected TLV tag 75 * @tlv_len: Address of TLV length variable to be populated. This API populates 76 * the entire length(payload + header) of the TLV into @tlv_len 77 * @tlv_tag: Address of TLV Tag variable to be populated. 78 * 79 * Return: 0 on success, -1 on failure 80 */ 81 static int 82 process_tlv_header(const uint8_t *data, size_t remaining_len, 83 uint32_t expected_tag, uint32_t *tlv_len, 84 uint32_t *tlv_tag) 85 { 86 if (remaining_len < MLO_SHMEM_TLV_HDR_SIZE) { 87 target_if_err("Not enough space(%zu) to read TLV header(%u)", 88 remaining_len, (uint32_t)MLO_SHMEM_TLV_HDR_SIZE); 89 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 90 } 91 92 *tlv_len = MLO_SHMEMTLV_GET_TLVLEN(MLO_SHMEMTLV_GET_HDR(data)); 93 *tlv_len += MLO_SHMEM_TLV_HDR_SIZE; 94 if (remaining_len < *tlv_len) { 95 target_if_err("Not enough space(%zu) to read TLV payload(%u)", 96 remaining_len, *tlv_len); 97 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 98 } 99 100 *tlv_tag = MLO_SHMEMTLV_GET_TLVTAG(MLO_SHMEMTLV_GET_HDR(data)); 101 if (*tlv_tag != expected_tag) { 102 target_if_err("Unexpected TLV tag: %u is seen. Expected: %u", 103 *tlv_tag, 104 expected_tag); 105 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 106 } 107 108 return 0; 109 } 110 111 #define validate_parsed_bytes_advance_data_pointer(parsed_bytes, data, \ 112 remaining_len) \ 113 do { \ 114 if ((parsed_bytes) < 0) { \ 115 target_if_err("TLV extraction failed"); \ 116 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); \ 117 } \ 118 (data) += (parsed_bytes); \ 119 (remaining_len) -= (parsed_bytes); \ 120 } while (0) 121 122 /** 123 * extract_mgmt_rx_reo_snapshot_tlv() - extract MGMT_RX_REO_SNAPSHOT TLV 124 * @data: Pointer to start of the TLV 125 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 126 * @address_ptr: Pointer to the snapshot address. This API will populate the 127 * snapshot address into the variable pointed by @address_ptr 128 * 129 * Return: On success, the number of bytes parsed. On failure, -1. 130 */ 131 static int 132 extract_mgmt_rx_reo_snapshot_tlv(uint8_t *data, size_t remaining_len, 133 void **address_ptr) 134 { 135 mgmt_rx_reo_snapshot *ptlv; 136 uint32_t tlv_len, tlv_tag; 137 138 qdf_assert_always(data); 139 qdf_assert_always(address_ptr); 140 141 /* process MLO_SHMEM_TLV_STRUCT_MGMT_RX_REO_SNAPSHOT TLV */ 142 if (process_tlv_header(data, remaining_len, 143 MLO_SHMEM_TLV_STRUCT_MGMT_RX_REO_SNAPSHOT, 144 &tlv_len, &tlv_tag) != 0) { 145 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 146 } 147 148 ptlv = (mgmt_rx_reo_snapshot *)data; 149 *address_ptr = get_field_pointer_in_tlv(ptlv, mgmt_rx_reo_snapshot_low, 150 tlv_len); 151 152 return tlv_len; 153 } 154 155 /** 156 * extract_mlo_glb_rx_reo_per_link_info_tlv() - extract 157 * RX_REO_PER_LINK_SNAPSHOT_INFO TLV 158 * @data: Pointer to start of the TLV 159 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 160 * @link_info: Pointer to MGMT Rx REO per link info. Extracted information 161 * will be populated in this data structure. 162 * 163 * Return: On success, the number of bytes parsed. On failure, -1. 164 */ 165 static int 166 extract_mlo_glb_rx_reo_per_link_info_tlv( 167 uint8_t *data, size_t remaining_len, uint8_t link_id, 168 struct wlan_host_mlo_glb_rx_reo_per_link_info *link_info) 169 { 170 mlo_glb_rx_reo_per_link_snapshot_info *ptlv; 171 uint32_t tlv_len, tlv_tag; 172 int len; 173 uint8_t *fw_consumed; 174 int parsed_bytes; 175 176 qdf_assert_always(data); 177 qdf_assert_always(link_info); 178 179 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_PER_LINK_SNAPSHOT_INFO TLV */ 180 if (process_tlv_header(data, remaining_len, 181 MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_PER_LINK_SNAPSHOT_INFO, 182 &tlv_len, &tlv_tag) != 0) { 183 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 184 } 185 186 ptlv = (mlo_glb_rx_reo_per_link_snapshot_info *)data; 187 188 link_info->link_id = link_id; 189 190 /** 191 * Get the pointer to the fw_consumed snapshot with in the TLV. 192 * Note that snapshots are nested TLVs within link_sanpshot_info TLV. 193 */ 194 data += qdf_offsetof(mlo_glb_rx_reo_per_link_snapshot_info, 195 fw_consumed); 196 fw_consumed = (uint8_t *)get_field_pointer_in_tlv(ptlv, fw_consumed, 197 tlv_len); 198 remaining_len -= qdf_offsetof(mlo_glb_rx_reo_per_link_snapshot_info, 199 fw_consumed); 200 parsed_bytes = qdf_offsetof(mlo_glb_rx_reo_per_link_snapshot_info, 201 fw_consumed); 202 203 /* extract fw_consumed snapshot */ 204 len = extract_mgmt_rx_reo_snapshot_tlv(data, remaining_len, 205 &link_info->fw_consumed); 206 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 207 parsed_bytes += len; 208 209 /* extract fw_forwarded snapshot */ 210 len = extract_mgmt_rx_reo_snapshot_tlv(data, remaining_len, 211 &link_info->fw_forwarded); 212 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 213 parsed_bytes += len; 214 215 /* extract hw_forwarded snapshot */ 216 len = extract_mgmt_rx_reo_snapshot_tlv(data, remaining_len, 217 &link_info->hw_forwarded); 218 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 219 parsed_bytes += len; 220 221 /** 222 * Return the length of link_sanpshot_info TLV itself as the snapshots 223 * are nested inside link_sanpshot_info TLV and hence no need to add 224 * their lengths separately. 225 */ 226 return tlv_len; 227 } 228 229 /** 230 * get_num_links_from_valid_link_bitmap() - Get the number of valid links 231 * @valid_link_bmap: Link bit map where the valid links are set to 1 232 * 233 * Return: Number of valid links 234 */ 235 static uint8_t 236 get_num_links_from_valid_link_bitmap(uint16_t valid_link_bmap) 237 { 238 uint8_t num_links = 0; 239 240 /* Find the number of set bits */ 241 while (valid_link_bmap) { 242 num_links++; 243 valid_link_bmap &= (valid_link_bmap - 1); 244 } 245 246 return num_links; 247 } 248 249 /** 250 * extract_mlo_glb_rx_reo_snapshot_info_tlv() - extract 251 * MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV 252 * @data: Pointer to start of the TLV 253 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 254 * @snapshot_info: Pointer to MGMT Rx REO snapshot info. Extracted information 255 * will be populated in this data structure. 256 * 257 * Return: On success, the number of bytes parsed. On failure, -1. 258 */ 259 static int 260 extract_mlo_glb_rx_reo_snapshot_info_tlv( 261 uint8_t *data, size_t remaining_len, 262 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info) 263 { 264 mlo_glb_rx_reo_snapshot_info *ptlv; 265 uint32_t tlv_len, tlv_tag; 266 uint32_t link_info; 267 uint16_t valid_link_bmap; 268 269 qdf_assert_always(data); 270 qdf_assert_always(snapshot_info); 271 272 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV */ 273 if (process_tlv_header(data, remaining_len, 274 MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO, 275 &tlv_len, &tlv_tag) != 0) { 276 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 277 } 278 279 ptlv = (mlo_glb_rx_reo_snapshot_info *)data; 280 link_info = get_field_value_in_tlv(ptlv, link_info, tlv_len); 281 valid_link_bmap = 282 MLO_SHMEM_GLB_LINK_INFO_PARAM_VALID_LINK_BMAP_GET(link_info); 283 284 snapshot_info->valid_link_bmap = valid_link_bmap; 285 286 if (is_field_present_in_tlv(ptlv, snapshot_ver_info, tlv_len)) { 287 uint32_t snapshot_ver_info; 288 289 snapshot_ver_info = get_field_value_in_tlv 290 (ptlv, snapshot_ver_info, tlv_len); 291 snapshot_info->hw_forwaded_snapshot_ver = 292 MLO_SHMEM_GLB_RX_REO_SNAPSHOT_PARAM_HW_FWD_SNAPSHOT_VER_GET(snapshot_ver_info); 293 snapshot_info->fw_forwaded_snapshot_ver = 294 MLO_SHMEM_GLB_RX_REO_SNAPSHOT_PARAM_FW_FWD_SNAPSHOT_VER_GET(snapshot_ver_info); 295 snapshot_info->fw_consumed_snapshot_ver = 296 MLO_SHMEM_GLB_RX_REO_SNAPSHOT_PARAM_FW_CONSUMED_SNAPSHOT_VER_GET(snapshot_ver_info); 297 } 298 299 snapshot_info->num_links = 300 get_num_links_from_valid_link_bitmap(valid_link_bmap); 301 snapshot_info->link_info = qdf_mem_malloc( 302 sizeof(*snapshot_info->link_info) * 303 snapshot_info->num_links); 304 if (!snapshot_info->link_info) { 305 target_if_err("Couldn't allocate memory for rx_reo_per_link_info"); 306 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 307 } 308 309 return tlv_len; 310 } 311 312 /** 313 * extract_mlo_glb_link_info_tlv() - extract lobal link info from shmem 314 * @data: Pointer to the first TLV in the arena 315 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 316 * @link_info: Pointer to which link info needs to be copied 317 * 318 * Return: On success, the number of bytes parsed. On failure, -1. 319 */ 320 static int 321 extract_mlo_glb_link_info_tlv(uint8_t *data, 322 size_t remaining_len, 323 uint32_t *link_info) 324 { 325 mlo_glb_link_info *ptlv; 326 uint32_t tlv_len, tlv_tag; 327 328 qdf_assert_always(data); 329 qdf_assert_always(link_info); 330 331 if (process_tlv_header(data, remaining_len, 332 MLO_SHMEM_TLV_STRUCT_MLO_GLB_LINK_INFO, 333 &tlv_len, &tlv_tag) != 0) { 334 return -EPERM; 335 } 336 337 ptlv = (mlo_glb_link_info *)data; 338 339 *link_info = get_field_value_in_tlv(ptlv, link_info, tlv_len); 340 341 return tlv_len; 342 } 343 344 /** 345 * process_mlo_glb_per_link_status_tlv() - process per link info 346 * @data: Pointer to the first TLV in the arena 347 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 348 * 349 * Return: On success, the number of bytes parsed. On failure, -1. 350 */ 351 static int 352 process_mlo_glb_per_link_status_tlv(uint8_t *data, size_t remaining_len) 353 { 354 uint32_t tlv_len, tlv_tag; 355 356 qdf_assert_always(data); 357 358 if (process_tlv_header(data, remaining_len, 359 MLO_SHMEM_TLV_STRUCT_MLO_GLB_LINK, 360 &tlv_len, &tlv_tag) != 0) { 361 return -EPERM; 362 } 363 364 return tlv_len; 365 } 366 367 /** 368 * parse_global_link_info() - parse lobal link info 369 * @data: Pointer to the first TLV in the arena 370 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 371 * 372 * Return: On success, the number of bytes parsed. On failure, -1. 373 */ 374 static int 375 parse_global_link_info(uint8_t *data, size_t remaining_len) 376 { 377 int parsed_bytes, len; 378 uint8_t link; 379 uint32_t link_info; 380 uint8_t num_links; 381 382 qdf_assert_always(data); 383 384 /* Extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_LINK_INFO_TLV */ 385 len = extract_mlo_glb_link_info_tlv(data, remaining_len, &link_info); 386 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 387 parsed_bytes = len; 388 389 num_links = MLO_SHMEM_GLB_LINK_INFO_PARAM_NO_OF_LINKS_GET(link_info); 390 391 for (link = 0; link < num_links; link++) { 392 len = process_mlo_glb_per_link_status_tlv(data, remaining_len); 393 validate_parsed_bytes_advance_data_pointer(len, data, 394 remaining_len); 395 parsed_bytes += len; 396 } 397 398 return parsed_bytes; 399 } 400 401 /** 402 * free_mlo_glb_rx_reo_per_link_info() - Free Rx REO per-link info 403 * @snapshot_info: Pointer to MGMT Rx REO snapshot info 404 * 405 * Return: None 406 */ 407 static void free_mlo_glb_rx_reo_per_link_info( 408 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info) 409 { 410 if (snapshot_info && snapshot_info->link_info) { 411 qdf_mem_free(snapshot_info->link_info); 412 snapshot_info->link_info = NULL; 413 } 414 } 415 416 /** 417 * get_next_valid_link_id() - Get next valid link ID 418 * @valid_link_bmap: Link bit map where the valid links are set to 1 419 * @prev_link_id: Previous link ID 420 * 421 * Return: Next valid link ID if there are valid links after @prev_link_id, 422 * else -1 423 */ 424 static int 425 get_next_valid_link_id(uint16_t valid_link_bmap, int prev_link_id) 426 { 427 int cur_link_id; 428 uint16_t mask; 429 uint8_t maxbits = sizeof(valid_link_bmap) * QDF_CHAR_BIT; 430 431 cur_link_id = prev_link_id + 1; 432 mask = 1 << cur_link_id; 433 434 while (!(valid_link_bmap & mask)) { 435 cur_link_id++; 436 if (cur_link_id == maxbits) 437 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 438 mask = mask << 1; 439 } 440 441 return cur_link_id; 442 } 443 444 /** 445 * extract_mlo_glb_rx_reo_snapshot_info() - extract MGMT Rx REO snapshot info 446 * @data: Pointer to start of MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO 447 * TLV 448 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 449 * @snapshot_info: Pointer to MGMT Rx REO snapshot info. Extracted information 450 * will be populated in this data structure. 451 * 452 * Return: On success, the number of bytes parsed. On failure, -1. 453 */ 454 static int 455 extract_mlo_glb_rx_reo_snapshot_info( 456 uint8_t *data, size_t remaining_len, 457 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info) 458 { 459 int parsed_bytes, len; 460 uint8_t link; 461 int cur_link_id, prev_link_id = -1; 462 uint16_t valid_link_bmap; 463 464 qdf_assert_always(data); 465 qdf_assert_always(snapshot_info); 466 467 /* Extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV */ 468 len = extract_mlo_glb_rx_reo_snapshot_info_tlv(data, remaining_len, 469 snapshot_info); 470 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 471 parsed_bytes = len; 472 473 valid_link_bmap = snapshot_info->valid_link_bmap; 474 /* Foreach valid link */ 475 for (link = 0; link < snapshot_info->num_links; ++link) { 476 cur_link_id = get_next_valid_link_id(valid_link_bmap, 477 prev_link_id); 478 479 qdf_assert_always(cur_link_id >= 0); 480 481 /* Extract per_link_info */ 482 len = extract_mlo_glb_rx_reo_per_link_info_tlv( 483 data, remaining_len, cur_link_id, 484 &snapshot_info->link_info[link]); 485 validate_parsed_bytes_advance_data_pointer(len, data, 486 remaining_len); 487 parsed_bytes += len; 488 prev_link_id = cur_link_id; 489 } 490 491 return parsed_bytes; 492 } 493 494 /** 495 * mlo_glb_h_shmem_arena_get_no_of_chips_from_crash_info() - get the number of 496 * chips from crash info 497 * 498 * Return: number of chips participating in MLO from crash info shared by target 499 * in case of success, else returns 0 500 */ 501 uint8_t mlo_glb_h_shmem_arena_get_no_of_chips_from_crash_info(void) 502 { 503 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 504 505 shmem_arena_ctx = get_shmem_arena_ctx(); 506 507 if (!shmem_arena_ctx) { 508 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 509 return 0; 510 } 511 512 return shmem_arena_ctx->chip_crash_info.no_of_chips; 513 } 514 515 /** 516 * mlo_glb_h_shmem_arena_get_crash_reason_address() - get the address of crash 517 * reason associated with chip_id 518 * 519 * Return: Address of crash_reason field fron global shmem arena in case of 520 * success, else returns NULL 521 */ 522 void *mlo_glb_h_shmem_arena_get_crash_reason_address(uint8_t chip_id) 523 { 524 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 525 struct wlan_host_mlo_glb_chip_crash_info *crash_info; 526 struct wlan_host_mlo_glb_per_chip_crash_info *per_chip_crash_info; 527 uint8_t chip; 528 529 shmem_arena_ctx = get_shmem_arena_ctx(); 530 if (!shmem_arena_ctx) { 531 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 532 return NULL; 533 } 534 535 crash_info = &shmem_arena_ctx->chip_crash_info; 536 537 for (chip = 0; chip < crash_info->no_of_chips; chip++) { 538 per_chip_crash_info = &crash_info->per_chip_crash_info[chip]; 539 540 if (chip_id == per_chip_crash_info->chip_id) 541 break; 542 } 543 544 if (chip == crash_info->no_of_chips) { 545 target_if_err("No crash info corressponding to chip %u", 546 chip_id); 547 return NULL; 548 } 549 550 return per_chip_crash_info->crash_reason; 551 } 552 553 /** 554 * free_mlo_glb_per_chip_crash_info() - free per chip crash info 555 * @crash_info: Pointer to crash info 556 * 557 * Return: None 558 */ 559 static void free_mlo_glb_per_chip_crash_info( 560 struct wlan_host_mlo_glb_chip_crash_info *crash_info) 561 { 562 if (crash_info) { 563 qdf_mem_free(crash_info->per_chip_crash_info); 564 crash_info->per_chip_crash_info = NULL; 565 } 566 } 567 568 /** 569 * extract_mlo_glb_per_chip_crash_info_tlv() - extract PER_CHIP_CRASH_INFO TLV 570 * @data: Pointer to start of the TLV 571 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 572 * @chip_id: Chip id to which the crash info tlv being extracted. 573 * @chip_crash_info: Pointer to the per chip crash info. This API will populate 574 * the crash_reason address & chip_id into chip_crash_info 575 */ 576 static int extract_mlo_glb_per_chip_crash_info_tlv( 577 uint8_t *data, size_t remaining_len, uint8_t chip_id, 578 struct wlan_host_mlo_glb_per_chip_crash_info *chip_crash_info) 579 { 580 mlo_glb_per_chip_crash_info *ptlv; 581 uint32_t tlv_len, tlv_tag; 582 uint8_t *crash_reason; 583 584 qdf_assert_always(data); 585 qdf_assert_always(chip_crash_info); 586 587 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_PER_CHIP_CRASH_INFO TLV */ 588 if (process_tlv_header(data, remaining_len, 589 MLO_SHMEM_TLV_STRUCT_MLO_GLB_PER_CHIP_CRASH_INFO, 590 &tlv_len, &tlv_tag) != 0) { 591 return -EPERM; 592 } 593 594 ptlv = (mlo_glb_per_chip_crash_info *)data; 595 596 chip_crash_info->chip_id = chip_id; 597 crash_reason = (uint8_t *)get_field_pointer_in_tlv( 598 ptlv, crash_reason, tlv_len); 599 chip_crash_info->crash_reason = (void *)crash_reason; 600 return tlv_len; 601 } 602 603 /** 604 * extract_mlo_glb_chip_crash_info_tlv() - extract CHIP_CRASH_INFO TLV 605 * @data: Pointer to start of the TLV 606 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 607 * @crash_info: Pointer to the crash_info structure to which crash info fields 608 * are populated. 609 * 610 * Return: On success, the number of bytes parsed. On failure, -1. 611 */ 612 static int extract_mlo_glb_chip_crash_info_tlv( 613 uint8_t *data, size_t remaining_len, 614 struct wlan_host_mlo_glb_chip_crash_info *crash_info) 615 { 616 mlo_glb_chip_crash_info *ptlv; 617 uint32_t tlv_len, tlv_tag; 618 uint32_t chip_info; 619 uint8_t chip_map; 620 621 qdf_assert_always(data); 622 qdf_assert_always(crash_info); 623 624 if (process_tlv_header(data, remaining_len, 625 MLO_SHMEM_TLV_STRUCT_MLO_GLB_CHIP_CRASH_INFO, 626 &tlv_len, &tlv_tag) != 0) { 627 return -EPERM; 628 } 629 630 ptlv = (mlo_glb_chip_crash_info *)data; 631 chip_info = get_field_value_in_tlv(ptlv, chip_info, tlv_len); 632 crash_info->no_of_chips = 633 MLO_SHMEM_CHIP_CRASH_INFO_PARAM_NO_OF_CHIPS_GET(chip_info); 634 chip_map = 635 MLO_SHMEM_CHIP_CRASH_INFO_PARAM_VALID_CHIP_BMAP_GET(chip_info); 636 qdf_mem_copy(crash_info->valid_chip_bmap, 637 &chip_map, 638 qdf_min(sizeof(crash_info->valid_chip_bmap), 639 sizeof(chip_map))); 640 641 crash_info->per_chip_crash_info = 642 qdf_mem_malloc(sizeof(*crash_info->per_chip_crash_info) * 643 crash_info->no_of_chips); 644 645 if (!crash_info->per_chip_crash_info) { 646 target_if_err("Couldn't allocate memory for crash info"); 647 return -EPERM; 648 } 649 650 return tlv_len; 651 } 652 653 /** 654 * extract_mlo_glb_chip_crash_info() - extract the crash information from global 655 * shmem arena 656 * @data: Pointer to start of the TLV 657 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 658 * @crash_info: Pointer to the crash_info structure to which crash info fields 659 * are populated. 660 * 661 * Return: On success, the number of bytes parsed. On failure, -1. 662 */ 663 static int extract_mlo_glb_chip_crash_info( 664 uint8_t *data, size_t remaining_len, 665 struct wlan_host_mlo_glb_chip_crash_info *crash_info) 666 { 667 int parsed_bytes, len; 668 int cur_chip_id; 669 qdf_bitmap(valid_chip_bmap, QDF_CHAR_BIT); 670 uint8_t chip; 671 672 qdf_assert_always(data); 673 qdf_assert_always(crash_info); 674 675 /* Extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_CHIP_CRASH_INFO_TLV */ 676 len = extract_mlo_glb_chip_crash_info_tlv( 677 data, remaining_len, crash_info); 678 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 679 680 parsed_bytes = len; 681 qdf_mem_copy(valid_chip_bmap, 682 crash_info->valid_chip_bmap, 683 qdf_min(sizeof(valid_chip_bmap), 684 sizeof(crash_info->valid_chip_bmap))); 685 /* Foreach valid chip_id */ 686 for (chip = 0; chip < crash_info->no_of_chips; chip++) { 687 cur_chip_id = qdf_find_first_bit(valid_chip_bmap, QDF_CHAR_BIT); 688 qdf_clear_bit(cur_chip_id, valid_chip_bmap); 689 qdf_assert_always(cur_chip_id >= 0); 690 /* Extract per_chip_crash_info */ 691 len = extract_mlo_glb_per_chip_crash_info_tlv( 692 data, remaining_len, cur_chip_id, 693 &crash_info->per_chip_crash_info[chip]); 694 validate_parsed_bytes_advance_data_pointer( 695 len, data, remaining_len); 696 parsed_bytes += len; 697 } 698 return parsed_bytes; 699 } 700 701 /** 702 * extract_mlo_glb_h_shmem_tlv() - extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_H_SHMEM 703 * TLV 704 * @data: Pointer to start of the TLV 705 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 706 * @shmem_params: Pointer to MLO Global shared memory parameters. Extracted 707 * information will be populated in this data structure. 708 * 709 * Return: On success, the number of bytes parsed. On failure, -1. 710 */ 711 static int 712 extract_mlo_glb_h_shmem_tlv( 713 uint8_t *data, size_t remaining_len, 714 struct wlan_host_mlo_glb_h_shmem_params *shmem_params) 715 { 716 mlo_glb_h_shmem *ptlv; 717 uint32_t tlv_len, tlv_tag; 718 uint32_t major_minor_version; 719 720 qdf_assert_always(data); 721 qdf_assert_always(shmem_params); 722 if (process_tlv_header(data, remaining_len, 723 MLO_SHMEM_TLV_STRUCT_MLO_GLB_H_SHMEM, 724 &tlv_len, &tlv_tag) != 0) { 725 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 726 } 727 728 ptlv = (mlo_glb_h_shmem *)data; 729 major_minor_version = get_field_value_in_tlv(ptlv, major_minor_version, 730 tlv_len); 731 shmem_params->major_version = 732 MLO_SHMEM_GLB_H_SHMEM_PARAM_MAJOR_VERSION_GET( 733 major_minor_version); 734 shmem_params->minor_version = 735 MLO_SHMEM_GLB_H_SHMEM_PARAM_MINOR_VERSION_GET( 736 major_minor_version); 737 738 return tlv_len; 739 } 740 741 /** 742 * parse_mlo_glb_h_shmem_arena() - Parse MLO Global shared memory arena 743 * @data: Pointer to the first TLV in the arena 744 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 745 * @shmem_arena_ctx: Pointer to MLO Global shared memory arena context. 746 * Extracted information will be populated in this data structure. 747 * 748 * Return: On success, the number of bytes parsed. On failure, -1. 749 */ 750 static int parse_mlo_glb_h_shmem_arena( 751 uint8_t *data, size_t remaining_len, 752 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx) 753 { 754 int parsed_bytes; 755 int len; 756 757 qdf_assert_always(data); 758 qdf_assert_always(shmem_arena_ctx); 759 760 len = extract_mlo_glb_h_shmem_tlv(data, remaining_len, 761 &shmem_arena_ctx->shmem_params); 762 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 763 parsed_bytes = len; 764 765 len = extract_mlo_glb_rx_reo_snapshot_info( 766 data, remaining_len, &shmem_arena_ctx->rx_reo_snapshot_info); 767 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 768 parsed_bytes += len; 769 770 len = parse_global_link_info(data, remaining_len); 771 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 772 parsed_bytes += len; 773 774 len = extract_mlo_glb_chip_crash_info( 775 data, remaining_len, &shmem_arena_ctx->chip_crash_info); 776 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 777 parsed_bytes += len; 778 779 return parsed_bytes; 780 } 781 782 QDF_STATUS mlo_glb_h_shmem_arena_ctx_init(void *arena_vaddr, 783 size_t arena_len) 784 { 785 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 786 787 shmem_arena_ctx = get_shmem_arena_ctx(); 788 if (!shmem_arena_ctx) { 789 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 790 return QDF_STATUS_E_NULL_VALUE; 791 } 792 793 /* We need to initialize only for the first invocation */ 794 if (qdf_atomic_read(&shmem_arena_ctx->init_count)) 795 goto success; 796 797 if (parse_mlo_glb_h_shmem_arena(arena_vaddr, arena_len, 798 shmem_arena_ctx) < 0) { 799 free_mlo_glb_rx_reo_per_link_info( 800 &shmem_arena_ctx->rx_reo_snapshot_info); 801 free_mlo_glb_per_chip_crash_info( 802 &shmem_arena_ctx->chip_crash_info); 803 return QDF_STATUS_E_FAILURE; 804 } 805 806 success: 807 qdf_atomic_inc(&shmem_arena_ctx->init_count); 808 return QDF_STATUS_SUCCESS; 809 } 810 811 qdf_export_symbol(mlo_glb_h_shmem_arena_ctx_init); 812 813 QDF_STATUS mlo_glb_h_shmem_arena_ctx_deinit(void) 814 { 815 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 816 817 shmem_arena_ctx = get_shmem_arena_ctx(); 818 if (!shmem_arena_ctx) { 819 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 820 return QDF_STATUS_E_NULL_VALUE; 821 } 822 823 if (!qdf_atomic_read(&shmem_arena_ctx->init_count)) { 824 target_if_fatal("shmem_arena_ctx ref cnt is 0"); 825 return QDF_STATUS_E_FAILURE; 826 } 827 828 /* We need to de-initialize only for the last invocation */ 829 if (qdf_atomic_dec_and_test(&shmem_arena_ctx->init_count)) 830 goto success; 831 832 free_mlo_glb_rx_reo_per_link_info( 833 &shmem_arena_ctx->rx_reo_snapshot_info); 834 free_mlo_glb_per_chip_crash_info( 835 &shmem_arena_ctx->chip_crash_info); 836 837 success: 838 return QDF_STATUS_SUCCESS; 839 } 840 841 qdf_export_symbol(mlo_glb_h_shmem_arena_ctx_deinit); 842 843 #ifdef WLAN_MGMT_RX_REO_SUPPORT 844 uint16_t mgmt_rx_reo_get_valid_link_bitmap(void) 845 { 846 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 847 848 shmem_arena_ctx = get_shmem_arena_ctx(); 849 if (!shmem_arena_ctx) { 850 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 851 return 0; 852 } 853 854 return shmem_arena_ctx->rx_reo_snapshot_info.valid_link_bmap; 855 } 856 857 int mgmt_rx_reo_get_num_links(void) 858 { 859 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 860 861 shmem_arena_ctx = get_shmem_arena_ctx(); 862 if (!shmem_arena_ctx) { 863 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 864 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 865 } 866 867 return shmem_arena_ctx->rx_reo_snapshot_info.num_links; 868 } 869 870 void *mgmt_rx_reo_get_snapshot_address( 871 uint8_t link_id, enum mgmt_rx_reo_shared_snapshot_id snapshot_id) 872 { 873 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 874 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info; 875 struct wlan_host_mlo_glb_rx_reo_per_link_info *snapshot_link_info; 876 uint8_t link; 877 878 if (snapshot_id >= MGMT_RX_REO_SHARED_SNAPSHOT_MAX) { 879 target_if_err("Invalid snapshot ID: %d", snapshot_id); 880 return NULL; 881 } 882 883 shmem_arena_ctx = get_shmem_arena_ctx(); 884 if (!shmem_arena_ctx) { 885 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 886 return NULL; 887 } 888 889 snapshot_info = &shmem_arena_ctx->rx_reo_snapshot_info; 890 891 for (link = 0; link < snapshot_info->num_links; ++link) { 892 snapshot_link_info = &snapshot_info->link_info[link]; 893 894 if (link_id == snapshot_link_info->link_id) 895 break; 896 } 897 898 if (link == snapshot_info->num_links) { 899 target_if_err("Couldn't find the snapshot link info" 900 "corresponding to the link %d", link_id); 901 return NULL; 902 } 903 904 switch (snapshot_id) { 905 case MGMT_RX_REO_SHARED_SNAPSHOT_MAC_HW: 906 return snapshot_link_info->hw_forwarded; 907 908 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_CONSUMED: 909 return snapshot_link_info->fw_consumed; 910 911 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_FORWADED: 912 return snapshot_link_info->fw_forwarded; 913 914 default: 915 qdf_assert_always(0); 916 } 917 918 return NULL; 919 } 920 921 int8_t mgmt_rx_reo_get_snapshot_version(enum mgmt_rx_reo_shared_snapshot_id id) 922 { 923 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 924 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info; 925 int8_t snapshot_version; 926 927 if (id >= MGMT_RX_REO_SHARED_SNAPSHOT_MAX) { 928 target_if_err("Invalid snapshot ID: %d", id); 929 return MGMT_RX_REO_INVALID_SNAPSHOT_VERSION; 930 } 931 932 shmem_arena_ctx = get_shmem_arena_ctx(); 933 if (!shmem_arena_ctx) { 934 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 935 return MGMT_RX_REO_INVALID_SNAPSHOT_VERSION; 936 } 937 938 snapshot_info = &shmem_arena_ctx->rx_reo_snapshot_info; 939 940 switch (id) { 941 case MGMT_RX_REO_SHARED_SNAPSHOT_MAC_HW: 942 snapshot_version = snapshot_info->hw_forwaded_snapshot_ver; 943 break; 944 945 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_CONSUMED: 946 snapshot_version = snapshot_info->fw_consumed_snapshot_ver; 947 break; 948 949 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_FORWADED: 950 snapshot_version = snapshot_info->fw_forwaded_snapshot_ver; 951 break; 952 953 default: 954 snapshot_version = MGMT_RX_REO_INVALID_SNAPSHOT_VERSION; 955 break; 956 } 957 958 return snapshot_version; 959 } 960 #endif /* WLAN_MGMT_RX_REO_SUPPORT */ 961