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