1 /* 2 * Copyright (c) 2021, The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 /** 18 * DOC: mlo_global_h_shmem_arena.c 19 * This file contains definition of functions that parse the MLO global 20 * shared memory arena. 21 */ 22 23 #include<mlo_global_h_shmem_arena.h> 24 25 static struct wlan_host_mlo_glb_h_shmem_arena_ctx g_shmem_arena_ctx; 26 27 #define get_shmem_arena_ctx() (&g_shmem_arena_ctx) 28 29 /** 30 * get_field_value_in_tlv() - Get the value of a given field in a given TLV 31 * @ptlv: Pointer to start of the TLV 32 * @field_name: name of the field in the TLV structure 33 * @tlv_len: Length of the TLV 34 * 35 * Return: Value of the given field if the offset of the field with in the TLV 36 * structure is less than the TLV length, else 0. 37 */ 38 #define get_field_value_in_tlv(ptlv, field_name, tlv_len) \ 39 (qdf_offsetof(typeof(*(ptlv)), field_name) < (tlv_len) ? \ 40 (ptlv)->field_name : 0) 41 42 /** 43 * get_field_pointer_in_tlv() - Get the address of a given field in a given TLV 44 * @ptlv: Pointer to start of the TLV 45 * @field_name: name of the field in the TLV structure 46 * @tlv_len: Length of the TLV 47 * 48 * Return: Address of the given field if the offset of the field with in the 49 * TLV structure is less than the TLV length, else NULL. 50 */ 51 #define get_field_pointer_in_tlv(ptlv, field_name, tlv_len) \ 52 (qdf_offsetof(typeof(*(ptlv)), field_name) < (tlv_len) ? \ 53 &(ptlv)->field_name : NULL) 54 55 /** 56 * process_tlv_header() - Process a given TLV header 57 * @data: Pointer to start of the TLV 58 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 59 * @expected_tag: Expected TLV tag 60 * @tlv_len: Address of TLV length variable to be populated. This API populates 61 * the entire length(payload + header) of the TLV into @tlv_len 62 * @tlv_tag: Address of TLV Tag variable to be populated. 63 * 64 * Return: 0 on success, -1 on failure 65 */ 66 static int 67 process_tlv_header(const uint8_t *data, size_t remaining_len, 68 uint32_t expected_tag, uint32_t *tlv_len, 69 uint32_t *tlv_tag) 70 { 71 if (remaining_len < MLO_SHMEM_TLV_HDR_SIZE) { 72 target_if_err("Not enough space(%zu) to read TLV header(%u)", 73 remaining_len, (uint32_t)MLO_SHMEM_TLV_HDR_SIZE); 74 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 75 } 76 77 *tlv_len = MLO_SHMEMTLV_GET_TLVLEN(MLO_SHMEMTLV_GET_HDR(data)); 78 *tlv_len += MLO_SHMEM_TLV_HDR_SIZE; 79 if (remaining_len < *tlv_len) { 80 target_if_err("Not enough space(%zu) to read TLV payload(%u)", 81 remaining_len, *tlv_len); 82 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 83 } 84 85 *tlv_tag = MLO_SHMEMTLV_GET_TLVTAG(MLO_SHMEMTLV_GET_HDR(data)); 86 if (*tlv_tag != expected_tag) { 87 target_if_err("Unexpected TLV tag: %u is seen. Expected: %u", 88 *tlv_tag, 89 expected_tag); 90 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 91 } 92 93 return 0; 94 } 95 96 #define validate_parsed_bytes_advance_data_pointer(parsed_bytes, data, \ 97 remaining_len) \ 98 do { \ 99 if ((parsed_bytes) < 0) { \ 100 target_if_err("TLV extraction failed"); \ 101 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); \ 102 } \ 103 (data) += (parsed_bytes); \ 104 (remaining_len) -= (parsed_bytes); \ 105 } while (0) 106 107 /** 108 * extract_mgmt_rx_reo_snapshot_tlv() - extract MGMT_RX_REO_SNAPSHOT TLV 109 * @data: Pointer to start of the TLV 110 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 111 * @address_ptr: Pointer to the snapshot address. This API will populate the 112 * snapshot address into the variable pointed by @address_ptr 113 * 114 * Return: On success, the number of bytes parsed. On failure, -1. 115 */ 116 static int 117 extract_mgmt_rx_reo_snapshot_tlv(uint8_t *data, size_t remaining_len, 118 void **address_ptr) 119 { 120 mgmt_rx_reo_snapshot *ptlv; 121 uint32_t tlv_len, tlv_tag; 122 123 qdf_assert_always(data); 124 qdf_assert_always(address_ptr); 125 126 /* process MLO_SHMEM_TLV_STRUCT_MGMT_RX_REO_SNAPSHOT TLV */ 127 if (process_tlv_header(data, remaining_len, 128 MLO_SHMEM_TLV_STRUCT_MGMT_RX_REO_SNAPSHOT, 129 &tlv_len, &tlv_tag) != 0) { 130 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 131 } 132 133 ptlv = (mgmt_rx_reo_snapshot *)data; 134 *address_ptr = get_field_pointer_in_tlv(ptlv, mgmt_rx_reo_snapshot_low, 135 tlv_len); 136 137 return tlv_len; 138 } 139 140 /** 141 * extract_mlo_glb_rx_reo_per_link_info_tlv() - extract 142 * RX_REO_PER_LINK_SNAPSHOT_INFO TLV 143 * @data: Pointer to start of the TLV 144 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 145 * @link_info: Pointer to MGMT Rx REO per link info. Extracted information 146 * will be populated in this data structure. 147 * 148 * Return: On success, the number of bytes parsed. On failure, -1. 149 */ 150 static int 151 extract_mlo_glb_rx_reo_per_link_info_tlv( 152 uint8_t *data, size_t remaining_len, uint8_t link_id, 153 struct wlan_host_mlo_glb_rx_reo_per_link_info *link_info) 154 { 155 mlo_glb_rx_reo_per_link_snapshot_info *ptlv; 156 uint32_t tlv_len, tlv_tag; 157 int len; 158 uint8_t *fw_consumed; 159 160 qdf_assert_always(data); 161 qdf_assert_always(link_info); 162 163 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_PER_LINK_SNAPSHOT_INFO TLV */ 164 if (process_tlv_header(data, remaining_len, 165 MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_PER_LINK_SNAPSHOT_INFO, 166 &tlv_len, &tlv_tag) != 0) { 167 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 168 } 169 170 ptlv = (mlo_glb_rx_reo_per_link_snapshot_info *)data; 171 172 link_info->link_id = link_id; 173 174 /** 175 * Get the pointer to the fw_consumed snapshot with in the TLV. 176 * Note that snapshots are nested TLVs within link_sanpshot_info TLV. 177 */ 178 fw_consumed = (uint8_t *)get_field_pointer_in_tlv(ptlv, fw_consumed, 179 tlv_len); 180 remaining_len -= qdf_offsetof(mlo_glb_rx_reo_per_link_snapshot_info, 181 fw_consumed); 182 183 /* extract fw_consumed snapshot */ 184 len = extract_mgmt_rx_reo_snapshot_tlv(fw_consumed, remaining_len, 185 &link_info->fw_consumed); 186 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 187 188 /* extract fw_forwarded snapshot */ 189 len = extract_mgmt_rx_reo_snapshot_tlv(fw_consumed, remaining_len, 190 &link_info->fw_forwarded); 191 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 192 193 /* extract hw_forwarded snapshot */ 194 len = extract_mgmt_rx_reo_snapshot_tlv(fw_consumed, remaining_len, 195 &link_info->hw_forwarded); 196 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 197 198 /** 199 * Return the length of link_sanpshot_info TLV itself as the snapshots 200 * are nested inside link_sanpshot_info TLV and hence no need to add 201 * their lengths separately. 202 */ 203 return tlv_len; 204 } 205 206 /** 207 * get_num_links_from_valid_link_bitmap() - Get the number of valid links 208 * @valid_link_bmap: Link bit map where the valid links are set to 1 209 * 210 * Return: Number of valid links 211 */ 212 static uint8_t 213 get_num_links_from_valid_link_bitmap(uint16_t valid_link_bmap) 214 { 215 uint8_t num_links = 0; 216 217 /* Find the number of set bits */ 218 while (valid_link_bmap) { 219 num_links++; 220 valid_link_bmap &= (valid_link_bmap - 1); 221 } 222 223 return num_links; 224 } 225 226 /** 227 * extract_mlo_glb_rx_reo_snapshot_info_tlv() - extract 228 * MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV 229 * @data: Pointer to start of the TLV 230 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 231 * @snapshot_info: Pointer to MGMT Rx REO snapshot info. Extracted information 232 * will be populated in this data structure. 233 * 234 * Return: On success, the number of bytes parsed. On failure, -1. 235 */ 236 static int 237 extract_mlo_glb_rx_reo_snapshot_info_tlv( 238 uint8_t *data, size_t remaining_len, 239 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info) 240 { 241 mlo_glb_rx_reo_snapshot_info *ptlv; 242 uint32_t tlv_len, tlv_tag; 243 uint32_t link_info; 244 uint16_t valid_link_bmap; 245 246 qdf_assert_always(data); 247 qdf_assert_always(snapshot_info); 248 249 /* process MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV */ 250 if (process_tlv_header(data, remaining_len, 251 MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO, 252 &tlv_len, &tlv_tag) != 0) { 253 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 254 } 255 256 ptlv = (mlo_glb_rx_reo_snapshot_info *)data; 257 link_info = get_field_value_in_tlv(ptlv, link_info, tlv_len); 258 valid_link_bmap = 259 MLO_SHMEM_GLB_LINK_INFO_PARAM_VALID_LINK_BMAP_GET(link_info); 260 261 snapshot_info->valid_link_bmap = valid_link_bmap; 262 snapshot_info->num_links = 263 get_num_links_from_valid_link_bitmap(valid_link_bmap); 264 snapshot_info->link_info = qdf_mem_malloc( 265 sizeof(*snapshot_info->link_info) * 266 snapshot_info->num_links); 267 if (!snapshot_info->link_info) { 268 target_if_err("Couldn't allocate memory for rx_reo_per_link_info"); 269 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 270 } 271 272 return tlv_len; 273 } 274 275 /** 276 * free_mlo_glb_rx_reo_per_link_info() - Free Rx REO per-link info 277 * @snapshot_info: Pointer to MGMT Rx REO snapshot info 278 * 279 * Return: None 280 */ 281 static void free_mlo_glb_rx_reo_per_link_info( 282 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info) 283 { 284 if (snapshot_info && snapshot_info->link_info) { 285 qdf_mem_free(snapshot_info->link_info); 286 snapshot_info->link_info = NULL; 287 } 288 } 289 290 /** 291 * get_next_valid_link_id() - Get next valid link ID 292 * @valid_link_bmap: Link bit map where the valid links are set to 1 293 * @prev_link_id: Previous link ID 294 * 295 * Return: Next valid link ID if there are valid links after @prev_link_id, 296 * else -1 297 */ 298 static int 299 get_next_valid_link_id(uint16_t valid_link_bmap, int prev_link_id) 300 { 301 int cur_link_id; 302 uint16_t mask; 303 uint8_t maxbits = sizeof(valid_link_bmap) * QDF_CHAR_BIT; 304 305 cur_link_id = prev_link_id + 1; 306 mask = 1 << cur_link_id; 307 308 while (!(valid_link_bmap & mask)) { 309 cur_link_id++; 310 if (cur_link_id == maxbits) 311 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 312 mask = mask << 1; 313 } 314 315 return cur_link_id; 316 } 317 318 /** 319 * extract_mlo_glb_rx_reo_snapshot_info() - extract MGMT Rx REO snapshot info 320 * @data: Pointer to start of MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO 321 * TLV 322 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 323 * @snapshot_info: Pointer to MGMT Rx REO snapshot info. Extracted information 324 * will be populated in this data structure. 325 * 326 * Return: On success, the number of bytes parsed. On failure, -1. 327 */ 328 static int 329 extract_mlo_glb_rx_reo_snapshot_info( 330 uint8_t *data, size_t remaining_len, 331 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info) 332 { 333 int parsed_bytes, len; 334 uint8_t link; 335 int cur_link_id, prev_link_id = -1; 336 uint16_t valid_link_bmap; 337 338 qdf_assert_always(data); 339 qdf_assert_always(snapshot_info); 340 341 /* Extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_RX_REO_SNAPSHOT_INFO TLV */ 342 len = extract_mlo_glb_rx_reo_snapshot_info_tlv(data, remaining_len, 343 snapshot_info); 344 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 345 parsed_bytes = len; 346 347 valid_link_bmap = snapshot_info->valid_link_bmap; 348 /* Foreach valid link */ 349 for (link = 0; link < snapshot_info->num_links; ++link) { 350 cur_link_id = get_next_valid_link_id(valid_link_bmap, 351 prev_link_id); 352 353 qdf_assert_always(cur_link_id >= 0); 354 355 /* Extract per_link_info */ 356 len = extract_mlo_glb_rx_reo_per_link_info_tlv( 357 data, remaining_len, cur_link_id, 358 &snapshot_info->link_info[link]); 359 validate_parsed_bytes_advance_data_pointer(len, data, 360 remaining_len); 361 parsed_bytes += len; 362 prev_link_id = cur_link_id; 363 } 364 365 return parsed_bytes; 366 } 367 368 /** 369 * extract_mlo_glb_h_shmem_tlv() - extract MLO_SHMEM_TLV_STRUCT_MLO_GLB_H_SHMEM 370 * TLV 371 * @data: Pointer to start of the TLV 372 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 373 * @shmem_params: Pointer to MLO Global shared memory parameters. Extracted 374 * information will be populated in this data structure. 375 * 376 * Return: On success, the number of bytes parsed. On failure, -1. 377 */ 378 static int 379 extract_mlo_glb_h_shmem_tlv( 380 uint8_t *data, size_t remaining_len, 381 struct wlan_host_mlo_glb_h_shmem_params *shmem_params) 382 { 383 mlo_glb_h_shmem *ptlv; 384 uint32_t tlv_len, tlv_tag; 385 uint32_t major_minor_version; 386 387 qdf_assert_always(data); 388 qdf_assert_always(shmem_params); 389 if (process_tlv_header(data, remaining_len, 390 MLO_SHMEM_TLV_STRUCT_MLO_GLB_H_SHMEM, 391 &tlv_len, &tlv_tag) != 0) { 392 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 393 } 394 395 ptlv = (mlo_glb_h_shmem *)data; 396 major_minor_version = get_field_value_in_tlv(ptlv, major_minor_version, 397 tlv_len); 398 shmem_params->major_version = 399 MLO_SHMEM_GLB_H_SHMEM_PARAM_MAJOR_VERSION_GET( 400 major_minor_version); 401 shmem_params->minor_version = 402 MLO_SHMEM_GLB_H_SHMEM_PARAM_MINOR_VERSION_GET( 403 major_minor_version); 404 405 return tlv_len; 406 } 407 408 /** 409 * parse_mlo_glb_h_shmem_arena() - Parse MLO Global shared memory arena 410 * @data: Pointer to the first TLV in the arena 411 * @remaining_len: Length (in bytes) remaining in the arena from @data pointer 412 * @shmem_arena_ctx: Pointer to MLO Global shared memory arena context. 413 * Extracted information will be populated in this data structure. 414 * 415 * Return: On success, the number of bytes parsed. On failure, -1. 416 */ 417 static int parse_mlo_glb_h_shmem_arena( 418 uint8_t *data, size_t remaining_len, 419 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx) 420 { 421 int parsed_bytes, len; 422 423 qdf_assert_always(data); 424 qdf_assert_always(shmem_arena_ctx); 425 426 len = extract_mlo_glb_h_shmem_tlv(data, remaining_len, 427 &shmem_arena_ctx->shmem_params); 428 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 429 parsed_bytes += len; 430 431 len = extract_mlo_glb_rx_reo_snapshot_info( 432 data, remaining_len, &shmem_arena_ctx->rx_reo_snapshot_info); 433 validate_parsed_bytes_advance_data_pointer(len, data, remaining_len); 434 parsed_bytes += len; 435 436 return parsed_bytes; 437 } 438 439 QDF_STATUS mlo_glb_h_shmem_arena_ctx_init(uint8_t *arena_vaddr, 440 size_t arena_len) 441 { 442 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 443 444 shmem_arena_ctx = get_shmem_arena_ctx(); 445 if (!shmem_arena_ctx) { 446 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 447 return QDF_STATUS_E_NULL_VALUE; 448 } 449 450 /* We need to initialize only for the first invocation */ 451 if (qdf_atomic_read(&shmem_arena_ctx->init_count)) 452 goto success; 453 454 if (parse_mlo_glb_h_shmem_arena(arena_vaddr, arena_len, 455 shmem_arena_ctx) < 0) { 456 free_mlo_glb_rx_reo_per_link_info( 457 &shmem_arena_ctx->rx_reo_snapshot_info); 458 return QDF_STATUS_E_FAILURE; 459 } 460 461 success: 462 qdf_atomic_inc(&shmem_arena_ctx->init_count); 463 return QDF_STATUS_SUCCESS; 464 } 465 466 QDF_STATUS mlo_glb_h_shmem_arena_deinit(void) 467 { 468 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 469 470 shmem_arena_ctx = get_shmem_arena_ctx(); 471 if (!shmem_arena_ctx) { 472 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 473 return QDF_STATUS_E_NULL_VALUE; 474 } 475 476 if (!qdf_atomic_read(&shmem_arena_ctx->init_count)) { 477 target_if_fatal("shmem_arena_ctx ref cnt is 0"); 478 return QDF_STATUS_E_FAILURE; 479 } 480 481 /* We need to de-initialize only for the last invocation */ 482 if (qdf_atomic_dec_and_test(&shmem_arena_ctx->init_count)) 483 goto success; 484 485 free_mlo_glb_rx_reo_per_link_info( 486 &shmem_arena_ctx->rx_reo_snapshot_info); 487 488 success: 489 return QDF_STATUS_SUCCESS; 490 } 491 492 int mgmt_rx_reo_get_num_links(void) 493 { 494 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 495 496 shmem_arena_ctx = get_shmem_arena_ctx(); 497 if (!shmem_arena_ctx) { 498 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 499 return qdf_status_to_os_return(QDF_STATUS_E_FAILURE); 500 } 501 502 return shmem_arena_ctx->rx_reo_snapshot_info.num_links; 503 } 504 505 void *mgmt_rx_reo_get_snapshot_address( 506 uint8_t link_id, enum mgmt_rx_reo_shared_snapshot_id snapshot_id) 507 { 508 struct wlan_host_mlo_glb_h_shmem_arena_ctx *shmem_arena_ctx; 509 struct wlan_host_mlo_glb_rx_reo_snapshot_info *snapshot_info; 510 struct wlan_host_mlo_glb_rx_reo_per_link_info *snapshot_link_info; 511 uint8_t link; 512 513 if (snapshot_id >= MGMT_RX_REO_SHARED_SNAPSHOT_MAX) { 514 target_if_err("Invalid snapshot ID: %d", snapshot_id); 515 return NULL; 516 } 517 518 shmem_arena_ctx = get_shmem_arena_ctx(); 519 if (!shmem_arena_ctx) { 520 target_if_err("mlo_glb_h_shmem_arena context is NULL"); 521 return NULL; 522 } 523 524 snapshot_info = &shmem_arena_ctx->rx_reo_snapshot_info; 525 526 for (link = 0; link < snapshot_info->num_links; ++link) { 527 snapshot_link_info = &snapshot_info->link_info[link]; 528 529 if (link_id == snapshot_link_info->link_id) 530 break; 531 } 532 533 if (link == snapshot_info->num_links) { 534 target_if_err("Couldn't find the snapshot link info" 535 "corresponding to the link %d", link_id); 536 return NULL; 537 } 538 539 switch (snapshot_id) { 540 case MGMT_RX_REO_SHARED_SNAPSHOT_MAC_HW: 541 return snapshot_link_info->hw_forwarded; 542 543 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_CONSUMED: 544 return snapshot_link_info->fw_consumed; 545 546 case MGMT_RX_REO_SHARED_SNAPSHOT_FW_FORWADED: 547 return snapshot_link_info->fw_forwarded; 548 549 default: 550 qdf_assert_always(0); 551 } 552 553 return NULL; 554 } 555