1 /* 2 * Copyright (c) 2021, The Linux Foundation. All rights reserved. 3 * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for 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 #include "hal_be_hw_headers.h" 19 #include "dp_types.h" 20 #include "hal_be_tx.h" 21 #include "hal_api.h" 22 #include "qdf_trace.h" 23 #include "hal_be_api_mon.h" 24 #include "dp_internal.h" 25 #include "qdf_mem.h" /* qdf_mem_malloc,free */ 26 #include "dp_mon.h" 27 #include <dp_mon_2.0.h> 28 #include <dp_tx_mon_2.0.h> 29 #include <dp_be.h> 30 #include <hal_be_api_mon.h> 31 #include <dp_mon_filter_2.0.h> 32 #ifdef FEATURE_PERPKT_INFO 33 #include "dp_ratetable.h" 34 #endif 35 36 #define MAX_TX_MONITOR_STUCK 50 37 38 #ifdef TXMON_DEBUG 39 /* 40 * dp_tx_mon_debug_statu() - API to display tx monitor status 41 * @tx_mon_be - pointer to dp_pdev_tx_monitor_be 42 * @work_done - tx monitor work done 43 * 44 * Return: void 45 */ 46 static inline void 47 dp_tx_mon_debug_status(struct dp_pdev_tx_monitor_be *tx_mon_be, 48 uint32_t work_done) 49 { 50 if (tx_mon_be->mode && !work_done) 51 tx_mon_be->stats.tx_mon_stuck++; 52 else if (tx_mon_be->mode && work_done) 53 tx_mon_be->stats.tx_mon_stuck = 0; 54 55 if (tx_mon_be->stats.tx_mon_stuck > MAX_TX_MONITOR_STUCK) { 56 dp_mon_warn("Tx monitor block got stuck!!!!!"); 57 tx_mon_be->stats.tx_mon_stuck = 0; 58 tx_mon_be->stats.total_tx_mon_stuck++; 59 } 60 61 dp_mon_debug_rl("tx_ppdu_info[%u :D %u] STATUS[R %llu: F %llu] PKT_BUF[R %llu: F %llu : P %llu : S %llu]", 62 tx_mon_be->tx_ppdu_info_list_depth, 63 tx_mon_be->defer_ppdu_info_list_depth, 64 tx_mon_be->stats.status_buf_recv, 65 tx_mon_be->stats.status_buf_free, 66 tx_mon_be->stats.pkt_buf_recv, 67 tx_mon_be->stats.pkt_buf_free, 68 tx_mon_be->stats.pkt_buf_processed, 69 tx_mon_be->stats.pkt_buf_to_stack); 70 } 71 72 #else 73 /* 74 * dp_tx_mon_debug_statu() - API to display tx monitor status 75 * @tx_mon_be - pointer to dp_pdev_tx_monitor_be 76 * @work_done - tx monitor work done 77 * 78 * Return: void 79 */ 80 static inline void 81 dp_tx_mon_debug_status(struct dp_pdev_tx_monitor_be *tx_mon_be, 82 uint32_t work_done) 83 { 84 if (tx_mon_be->mode && !work_done) 85 tx_mon_be->stats.tx_mon_stuck++; 86 else if (tx_mon_be->mode && work_done) 87 tx_mon_be->stats.tx_mon_stuck = 0; 88 89 if (tx_mon_be->stats.tx_mon_stuck > MAX_TX_MONITOR_STUCK) { 90 dp_mon_warn("Tx monitor block got stuck!!!!!"); 91 tx_mon_be->stats.tx_mon_stuck = 0; 92 tx_mon_be->stats.total_tx_mon_stuck++; 93 } 94 } 95 #endif 96 97 static inline uint32_t 98 dp_tx_mon_srng_process_2_0(struct dp_soc *soc, struct dp_intr *int_ctx, 99 uint32_t mac_id, uint32_t quota) 100 { 101 struct dp_pdev *pdev = dp_get_pdev_for_lmac_id(soc, mac_id); 102 void *tx_mon_dst_ring_desc; 103 hal_soc_handle_t hal_soc; 104 void *mon_dst_srng; 105 struct dp_mon_pdev *mon_pdev; 106 struct dp_mon_pdev_be *mon_pdev_be; 107 uint32_t work_done = 0; 108 struct dp_mon_soc *mon_soc = soc->monitor_soc; 109 struct dp_mon_soc_be *mon_soc_be = dp_get_be_mon_soc_from_dp_mon_soc(mon_soc); 110 struct dp_pdev_tx_monitor_be *tx_mon_be = NULL; 111 struct dp_mon_desc_pool *tx_mon_desc_pool = &mon_soc_be->tx_desc_mon; 112 struct dp_tx_mon_desc_list mon_desc_list; 113 uint32_t replenish_cnt = 0; 114 115 if (!pdev) { 116 dp_mon_err("%pK: pdev is null for mac_id = %d", soc, mac_id); 117 return work_done; 118 } 119 120 mon_pdev = pdev->monitor_pdev; 121 mon_dst_srng = mon_soc_be->tx_mon_dst_ring[mac_id].hal_srng; 122 123 if (!mon_dst_srng || !hal_srng_initialized(mon_dst_srng)) { 124 dp_mon_err("%pK: : HAL Monitor Destination Ring Init Failed -- %pK", 125 soc, mon_dst_srng); 126 return work_done; 127 } 128 129 mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 130 if (qdf_unlikely(!mon_pdev_be)) 131 return work_done; 132 133 tx_mon_be = &mon_pdev_be->tx_monitor_be; 134 hal_soc = soc->hal_soc; 135 136 qdf_assert((hal_soc && pdev)); 137 138 qdf_spin_lock_bh(&mon_pdev->mon_lock); 139 mon_desc_list.desc_list = NULL; 140 mon_desc_list.tail = NULL; 141 mon_desc_list.tx_mon_reap_cnt = 0; 142 143 if (qdf_unlikely(dp_srng_access_start(int_ctx, soc, mon_dst_srng))) { 144 dp_mon_err("%s %d : HAL Mon Dest Ring access Failed -- %pK", 145 __func__, __LINE__, mon_dst_srng); 146 qdf_spin_unlock_bh(&mon_pdev->mon_lock); 147 return work_done; 148 } 149 150 while (qdf_likely((tx_mon_dst_ring_desc = 151 (void *)hal_srng_dst_peek(hal_soc, mon_dst_srng)) 152 && quota--)) { 153 struct hal_mon_desc hal_mon_tx_desc = {0}; 154 struct dp_mon_desc *mon_desc = NULL; 155 qdf_frag_t status_frag = NULL; 156 uint32_t end_offset = 0; 157 158 hal_be_get_mon_dest_status(soc->hal_soc, 159 tx_mon_dst_ring_desc, 160 &hal_mon_tx_desc); 161 162 if (hal_mon_tx_desc.empty_descriptor) { 163 /* update stats counter */ 164 dp_mon_debug("P_ID:%d INIT:%d E_DESC:%d R_ID:%d L_CNT:%d DROP[PPDU:%d MPDU:%d TLV:%d] E_O_PPDU:%d", 165 hal_mon_tx_desc.ppdu_id, 166 hal_mon_tx_desc.initiator, 167 hal_mon_tx_desc.empty_descriptor, 168 hal_mon_tx_desc.ring_id, 169 hal_mon_tx_desc.looping_count, 170 hal_mon_tx_desc.ppdu_drop_count, 171 hal_mon_tx_desc.mpdu_drop_count, 172 hal_mon_tx_desc.tlv_drop_count, 173 hal_mon_tx_desc.end_of_ppdu_dropped); 174 175 tx_mon_be->stats.ppdu_drop_cnt += 176 hal_mon_tx_desc.ppdu_drop_count; 177 tx_mon_be->stats.mpdu_drop_cnt += 178 hal_mon_tx_desc.mpdu_drop_count; 179 tx_mon_be->stats.tlv_drop_cnt += 180 hal_mon_tx_desc.tlv_drop_count; 181 work_done++; 182 hal_srng_dst_get_next(hal_soc, mon_dst_srng); 183 continue; 184 } 185 186 dp_mon_debug("P_ID:%d INIT:%d E_DESC:%d R_ID:%d L_CNT:%d BUF_ADDR: 0x%llx E_OFF: %d E_REA: %d", 187 hal_mon_tx_desc.ppdu_id, 188 hal_mon_tx_desc.initiator, 189 hal_mon_tx_desc.empty_descriptor, 190 hal_mon_tx_desc.ring_id, 191 hal_mon_tx_desc.looping_count, 192 hal_mon_tx_desc.buf_addr, 193 hal_mon_tx_desc.end_offset, 194 hal_mon_tx_desc.end_reason); 195 196 mon_desc = (struct dp_mon_desc *)(uintptr_t)(hal_mon_tx_desc.buf_addr); 197 qdf_assert_always(mon_desc); 198 199 if (!mon_desc->unmapped) { 200 qdf_mem_unmap_page(soc->osdev, mon_desc->paddr, 201 DP_MON_DATA_BUFFER_SIZE, 202 QDF_DMA_FROM_DEVICE); 203 mon_desc->unmapped = 1; 204 } 205 206 if (mon_desc->magic != DP_MON_DESC_MAGIC) { 207 dp_mon_err("Invalid monitor descriptor"); 208 qdf_assert_always(0); 209 } 210 211 end_offset = hal_mon_tx_desc.end_offset; 212 213 status_frag = (qdf_frag_t)(mon_desc->buf_addr); 214 mon_desc->buf_addr = NULL; 215 /* increment reap count */ 216 ++mon_desc_list.tx_mon_reap_cnt; 217 218 /* add the mon_desc to free list */ 219 dp_mon_add_to_free_desc_list(&mon_desc_list.desc_list, 220 &mon_desc_list.tail, mon_desc); 221 222 223 if (qdf_unlikely(!status_frag)) { 224 dp_mon_debug("P_ID:%d INIT:%d E_DESC:%d R_ID:%d L_CNT:%d BUF_ADDR: 0x%llx E_OFF: %d E_REA: %d", 225 hal_mon_tx_desc.ppdu_id, 226 hal_mon_tx_desc.initiator, 227 hal_mon_tx_desc.empty_descriptor, 228 hal_mon_tx_desc.ring_id, 229 hal_mon_tx_desc.looping_count, 230 hal_mon_tx_desc.buf_addr, 231 hal_mon_tx_desc.end_offset, 232 hal_mon_tx_desc.end_reason); 233 234 work_done++; 235 hal_srng_dst_get_next(hal_soc, mon_dst_srng); 236 continue; 237 } 238 239 tx_mon_be->stats.status_buf_recv++; 240 241 if ((hal_mon_tx_desc.end_reason == HAL_MON_FLUSH_DETECTED) || 242 (hal_mon_tx_desc.end_reason == HAL_MON_PPDU_TRUNCATED)) { 243 tx_mon_be->be_ppdu_id = hal_mon_tx_desc.ppdu_id; 244 245 dp_tx_mon_update_end_reason(mon_pdev, 246 hal_mon_tx_desc.ppdu_id, 247 hal_mon_tx_desc.end_reason); 248 /* check and free packet buffer from status buffer */ 249 dp_tx_mon_status_free_packet_buf(pdev, status_frag, 250 end_offset, 251 &mon_desc_list); 252 253 tx_mon_be->stats.status_buf_free++; 254 qdf_frag_free(status_frag); 255 256 work_done++; 257 hal_srng_dst_get_next(hal_soc, mon_dst_srng); 258 continue; 259 } 260 261 dp_tx_mon_process_status_tlv(soc, pdev, 262 &hal_mon_tx_desc, 263 status_frag, 264 end_offset, 265 &mon_desc_list); 266 267 work_done++; 268 hal_srng_dst_get_next(hal_soc, mon_dst_srng); 269 } 270 dp_srng_access_end(int_ctx, soc, mon_dst_srng); 271 272 if (mon_desc_list.tx_mon_reap_cnt) { 273 dp_mon_buffers_replenish(soc, &mon_soc_be->tx_mon_buf_ring, 274 tx_mon_desc_pool, 275 mon_desc_list.tx_mon_reap_cnt, 276 &mon_desc_list.desc_list, 277 &mon_desc_list.tail, 278 &replenish_cnt); 279 } 280 qdf_spin_unlock_bh(&mon_pdev->mon_lock); 281 dp_mon_debug("mac_id: %d, work_done:%d tx_monitor_reap_cnt:%d", 282 mac_id, work_done, mon_desc_list.tx_mon_reap_cnt); 283 284 tx_mon_be->stats.total_tx_mon_reap_cnt += mon_desc_list.tx_mon_reap_cnt; 285 tx_mon_be->stats.totat_tx_mon_replenish_cnt += replenish_cnt; 286 dp_tx_mon_debug_status(tx_mon_be, work_done); 287 288 return work_done; 289 } 290 291 uint32_t 292 dp_tx_mon_process_2_0(struct dp_soc *soc, struct dp_intr *int_ctx, 293 uint32_t mac_id, uint32_t quota) 294 { 295 uint32_t work_done; 296 297 work_done = dp_tx_mon_srng_process_2_0(soc, int_ctx, mac_id, quota); 298 299 return work_done; 300 } 301 302 void 303 dp_tx_mon_buf_desc_pool_deinit(struct dp_soc *soc) 304 { 305 struct dp_mon_soc *mon_soc = soc->monitor_soc; 306 struct dp_mon_soc_be *mon_soc_be = 307 dp_get_be_mon_soc_from_dp_mon_soc(mon_soc); 308 309 dp_mon_desc_pool_deinit(&mon_soc_be->tx_desc_mon); 310 } 311 312 QDF_STATUS 313 dp_tx_mon_buf_desc_pool_init(struct dp_soc *soc) 314 { 315 struct dp_mon_soc *mon_soc = soc->monitor_soc; 316 struct dp_mon_soc_be *mon_soc_be = 317 dp_get_be_mon_soc_from_dp_mon_soc(mon_soc); 318 uint32_t num_entries; 319 320 num_entries = 321 wlan_cfg_get_dp_soc_tx_mon_buf_ring_size(soc->wlan_cfg_ctx); 322 323 return dp_mon_desc_pool_init(&mon_soc_be->tx_desc_mon, num_entries); 324 } 325 326 void dp_tx_mon_buf_desc_pool_free(struct dp_soc *soc) 327 { 328 struct dp_mon_soc *mon_soc = soc->monitor_soc; 329 struct dp_mon_soc_be *mon_soc_be = 330 dp_get_be_mon_soc_from_dp_mon_soc(mon_soc); 331 332 if (mon_soc_be) 333 dp_mon_desc_pool_free(&mon_soc_be->tx_desc_mon); 334 } 335 336 QDF_STATUS 337 dp_tx_mon_buf_desc_pool_alloc(struct dp_soc *soc) 338 { 339 struct dp_srng *mon_buf_ring; 340 struct dp_mon_desc_pool *tx_mon_desc_pool; 341 int entries; 342 struct wlan_cfg_dp_soc_ctxt *soc_cfg_ctx; 343 struct dp_mon_soc *mon_soc = soc->monitor_soc; 344 struct dp_mon_soc_be *mon_soc_be = 345 dp_get_be_mon_soc_from_dp_mon_soc(mon_soc); 346 347 soc_cfg_ctx = soc->wlan_cfg_ctx; 348 349 entries = wlan_cfg_get_dp_soc_tx_mon_buf_ring_size(soc_cfg_ctx); 350 351 mon_buf_ring = &mon_soc_be->tx_mon_buf_ring; 352 353 tx_mon_desc_pool = &mon_soc_be->tx_desc_mon; 354 355 qdf_print("%s:%d tx mon buf desc pool entries: %d", __func__, __LINE__, entries); 356 return dp_mon_desc_pool_alloc(entries, tx_mon_desc_pool); 357 } 358 359 void 360 dp_tx_mon_buffers_free(struct dp_soc *soc) 361 { 362 struct dp_mon_desc_pool *tx_mon_desc_pool; 363 struct dp_mon_soc *mon_soc = soc->monitor_soc; 364 struct dp_mon_soc_be *mon_soc_be = 365 dp_get_be_mon_soc_from_dp_mon_soc(mon_soc); 366 367 tx_mon_desc_pool = &mon_soc_be->tx_desc_mon; 368 369 dp_mon_pool_frag_unmap_and_free(soc, tx_mon_desc_pool); 370 } 371 372 QDF_STATUS 373 dp_tx_mon_buffers_alloc(struct dp_soc *soc, uint32_t size) 374 { 375 struct dp_srng *mon_buf_ring; 376 struct dp_mon_desc_pool *tx_mon_desc_pool; 377 union dp_mon_desc_list_elem_t *desc_list = NULL; 378 union dp_mon_desc_list_elem_t *tail = NULL; 379 struct dp_mon_soc *mon_soc = soc->monitor_soc; 380 struct dp_mon_soc_be *mon_soc_be = 381 dp_get_be_mon_soc_from_dp_mon_soc(mon_soc); 382 383 mon_buf_ring = &mon_soc_be->tx_mon_buf_ring; 384 385 tx_mon_desc_pool = &mon_soc_be->tx_desc_mon; 386 387 return dp_mon_buffers_replenish(soc, mon_buf_ring, 388 tx_mon_desc_pool, 389 size, 390 &desc_list, &tail, NULL); 391 } 392 393 #ifdef WLAN_TX_PKT_CAPTURE_ENH_BE 394 395 /* 396 * dp_tx_mon_nbuf_get_num_frag() - get total number of fragments 397 * @buf: Network buf instance 398 * 399 * Return: number of fragments 400 */ 401 static inline 402 uint32_t dp_tx_mon_nbuf_get_num_frag(qdf_nbuf_t nbuf) 403 { 404 uint32_t num_frag = 0; 405 406 if (qdf_unlikely(!nbuf)) 407 return num_frag; 408 409 num_frag = qdf_nbuf_get_nr_frags_in_fraglist(nbuf); 410 411 return num_frag; 412 } 413 414 /* 415 * dp_tx_mon_free_usr_mpduq() - API to free user mpduq 416 * @tx_ppdu_info - pointer to tx_ppdu_info 417 * @usr_idx - user index 418 * @tx_mon_be - pointer to tx capture be 419 * 420 * Return: void 421 */ 422 void dp_tx_mon_free_usr_mpduq(struct dp_tx_ppdu_info *tx_ppdu_info, 423 uint8_t usr_idx, 424 struct dp_pdev_tx_monitor_be *tx_mon_be) 425 { 426 qdf_nbuf_queue_t *mpdu_q; 427 uint32_t num_frag = 0; 428 qdf_nbuf_t buf = NULL; 429 430 if (qdf_unlikely(!tx_ppdu_info)) 431 return; 432 433 mpdu_q = &TXMON_PPDU_USR(tx_ppdu_info, usr_idx, mpdu_q); 434 435 while ((buf = qdf_nbuf_queue_remove(mpdu_q)) != NULL) { 436 num_frag += dp_tx_mon_nbuf_get_num_frag(buf); 437 qdf_nbuf_free(buf); 438 } 439 tx_mon_be->stats.pkt_buf_free += num_frag; 440 } 441 442 /* 443 * dp_tx_mon_free_ppdu_info() - API to free dp_tx_ppdu_info 444 * @tx_ppdu_info - pointer to tx_ppdu_info 445 * @tx_mon_be - pointer to tx capture be 446 * 447 * Return: void 448 */ 449 void dp_tx_mon_free_ppdu_info(struct dp_tx_ppdu_info *tx_ppdu_info, 450 struct dp_pdev_tx_monitor_be *tx_mon_be) 451 { 452 uint32_t user = 0; 453 454 for (; user < TXMON_PPDU_HAL(tx_ppdu_info, num_users); user++) { 455 qdf_nbuf_queue_t *mpdu_q; 456 uint32_t num_frag = 0; 457 qdf_nbuf_t buf = NULL; 458 459 mpdu_q = &TXMON_PPDU_USR(tx_ppdu_info, user, mpdu_q); 460 461 while ((buf = qdf_nbuf_queue_remove(mpdu_q)) != NULL) { 462 num_frag += dp_tx_mon_nbuf_get_num_frag(buf); 463 qdf_nbuf_free(buf); 464 } 465 tx_mon_be->stats.pkt_buf_free += num_frag; 466 } 467 468 TXMON_PPDU_HAL(tx_ppdu_info, is_used) = 0; 469 qdf_mem_free(tx_ppdu_info); 470 } 471 472 /* 473 * dp_tx_mon_get_ppdu_info() - API to allocate dp_tx_ppdu_info 474 * @pdev - pdev handle 475 * @type - type of ppdu_info data or protection 476 * @num_user - number user in a ppdu_info 477 * @ppdu_id - ppdu_id number 478 * 479 * Return: pointer to dp_tx_ppdu_info 480 */ 481 struct dp_tx_ppdu_info *dp_tx_mon_get_ppdu_info(struct dp_pdev *pdev, 482 enum tx_ppdu_info_type type, 483 uint8_t num_user, 484 uint32_t ppdu_id) 485 { 486 struct dp_mon_pdev *mon_pdev = pdev->monitor_pdev; 487 struct dp_mon_pdev_be *mon_pdev_be = 488 dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 489 struct dp_pdev_tx_monitor_be *tx_mon_be = 490 &mon_pdev_be->tx_monitor_be; 491 struct dp_tx_ppdu_info *tx_ppdu_info; 492 size_t sz_ppdu_info = 0; 493 uint8_t i; 494 495 /* allocate new tx_ppdu_info */ 496 sz_ppdu_info = (sizeof(struct dp_tx_ppdu_info) + 497 (sizeof(struct mon_rx_user_status) * num_user)); 498 499 tx_ppdu_info = (struct dp_tx_ppdu_info *)qdf_mem_malloc(sz_ppdu_info); 500 if (!tx_ppdu_info) { 501 dp_mon_err("allocation of tx_ppdu_info type[%d] failed!!!", 502 type); 503 return NULL; 504 } 505 506 TXMON_PPDU_HAL(tx_ppdu_info, is_used) = 0; 507 TXMON_PPDU_HAL(tx_ppdu_info, num_users) = num_user; 508 TXMON_PPDU_HAL(tx_ppdu_info, ppdu_id) = ppdu_id; 509 tx_ppdu_info->ppdu_id = ppdu_id; 510 511 for (i = 0; i < num_user; i++) { 512 qdf_nbuf_queue_t *mpdu_q; 513 514 mpdu_q = &TXMON_PPDU_USR(tx_ppdu_info, i, mpdu_q); 515 qdf_nbuf_queue_init(mpdu_q); 516 } 517 518 /* assign tx_ppdu_info to monitor pdev for reference */ 519 if (type == TX_PROT_PPDU_INFO) { 520 tx_mon_be->tx_prot_ppdu_info = tx_ppdu_info; 521 TXMON_PPDU_HAL(tx_ppdu_info, is_data) = 0; 522 } else { 523 tx_mon_be->tx_data_ppdu_info = tx_ppdu_info; 524 TXMON_PPDU_HAL(tx_ppdu_info, is_data) = 1; 525 } 526 527 return tx_ppdu_info; 528 } 529 530 /* 531 * dp_print_pdev_tx_monitor_stats_2_0: print tx capture stats 532 * @pdev: DP PDEV handle 533 * 534 * return: void 535 */ 536 void dp_print_pdev_tx_monitor_stats_2_0(struct dp_pdev *pdev) 537 { 538 struct dp_mon_pdev *mon_pdev = pdev->monitor_pdev; 539 struct dp_mon_pdev_be *mon_pdev_be = 540 dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 541 struct dp_pdev_tx_monitor_be *tx_mon_be = 542 &mon_pdev_be->tx_monitor_be; 543 struct dp_tx_monitor_drop_stats stats = {0}; 544 545 qdf_mem_copy(&stats, &tx_mon_be->stats, 546 sizeof(struct dp_tx_monitor_drop_stats)); 547 548 /* TX monitor stats needed for beryllium */ 549 DP_PRINT_STATS("\n\tTX Capture BE stats mode[%d]:", tx_mon_be->mode); 550 DP_PRINT_STATS("\tbuffer pending : %u", tx_mon_be->last_frag_q_idx); 551 DP_PRINT_STATS("\treplenish count: %llu", 552 stats.totat_tx_mon_replenish_cnt); 553 DP_PRINT_STATS("\treap count : %llu", stats.total_tx_mon_reap_cnt); 554 DP_PRINT_STATS("\tmonitor stuck : %u", stats.total_tx_mon_stuck); 555 DP_PRINT_STATS("\tStatus buffer"); 556 DP_PRINT_STATS("\t\treceived : %llu", stats.status_buf_recv); 557 DP_PRINT_STATS("\t\tfree : %llu", stats.status_buf_free); 558 DP_PRINT_STATS("\tPacket buffer"); 559 DP_PRINT_STATS("\t\treceived : %llu", stats.pkt_buf_recv); 560 DP_PRINT_STATS("\t\tfree : %llu", stats.pkt_buf_free); 561 DP_PRINT_STATS("\t\tprocessed : %llu", stats.pkt_buf_processed); 562 DP_PRINT_STATS("\t\tto stack : %llu", stats.pkt_buf_to_stack); 563 DP_PRINT_STATS("\tppdu info"); 564 DP_PRINT_STATS("\t\tthreshold : %llu", stats.ppdu_info_drop_th); 565 DP_PRINT_STATS("\t\tflush : %llu", stats.ppdu_info_drop_flush); 566 DP_PRINT_STATS("\t\ttruncated : %llu", stats.ppdu_info_drop_trunc); 567 DP_PRINT_STATS("\tDrop stats"); 568 DP_PRINT_STATS("\t\tppdu drop : %llu", stats.ppdu_drop_cnt); 569 DP_PRINT_STATS("\t\tmpdu drop : %llu", stats.mpdu_drop_cnt); 570 DP_PRINT_STATS("\t\ttlv drop : %llu", stats.tlv_drop_cnt); 571 } 572 573 /* 574 * dp_config_enh_tx_monitor_2_0()- API to enable/disable enhanced tx capture 575 * @pdev_handle: DP_PDEV handle 576 * @val: user provided value 577 * 578 * Return: QDF_STATUS 579 */ 580 QDF_STATUS 581 dp_config_enh_tx_monitor_2_0(struct dp_pdev *pdev, uint8_t val) 582 { 583 struct dp_mon_pdev *mon_pdev = pdev->monitor_pdev; 584 struct dp_mon_pdev_be *mon_pdev_be = 585 dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 586 struct dp_pdev_tx_monitor_be *tx_mon_be = 587 &mon_pdev_be->tx_monitor_be; 588 589 switch (val) { 590 case TX_MON_BE_DISABLE: 591 { 592 /* TODO: send HTT msg to configure TLV based on mode */ 593 tx_mon_be->mode = TX_MON_BE_DISABLE; 594 mon_pdev_be->tx_mon_mode = 0; 595 mon_pdev_be->tx_mon_filter_length = DMA_LENGTH_64B; 596 break; 597 } 598 case TX_MON_BE_FULL_CAPTURE: 599 { 600 /* TODO: send HTT msg to configure TLV based on mode */ 601 qdf_mem_zero(&tx_mon_be->stats, 602 sizeof(struct dp_tx_monitor_drop_stats)); 603 tx_mon_be->mode = TX_MON_BE_FULL_CAPTURE; 604 mon_pdev_be->tx_mon_mode = 1; 605 mon_pdev_be->tx_mon_filter_length = DEFAULT_DMA_LENGTH; 606 break; 607 } 608 case TX_MON_BE_PEER_FILTER: 609 { 610 /* TODO: send HTT msg to configure TLV based on mode */ 611 tx_mon_be->mode = TX_MON_BE_PEER_FILTER; 612 mon_pdev_be->tx_mon_mode = 2; 613 mon_pdev_be->tx_mon_filter_length = DMA_LENGTH_256B; 614 break; 615 } 616 default: 617 { 618 /* TODO: do we need to set to disable ? */ 619 return QDF_STATUS_E_INVAL; 620 } 621 } 622 623 dp_mon_info("Tx monitor mode:%d mon_mode_flag:%d config_length:%d", 624 tx_mon_be->mode, mon_pdev_be->tx_mon_mode, 625 mon_pdev_be->tx_mon_filter_length); 626 627 dp_mon_filter_setup_tx_mon_mode(pdev); 628 dp_tx_mon_filter_update(pdev); 629 630 return QDF_STATUS_SUCCESS; 631 } 632 633 /* 634 * dp_peer_set_tx_capture_enabled_2_0() - add tx monitor peer filter 635 * @pdev: Datapath PDEV handle 636 * @peer: Datapath PEER handle 637 * @is_tx_pkt_cap_enable: flag for tx capture enable/disable 638 * @peer_mac: peer mac address 639 * 640 * Return: status 641 */ 642 QDF_STATUS dp_peer_set_tx_capture_enabled_2_0(struct dp_pdev *pdev_handle, 643 struct dp_peer *peer_handle, 644 uint8_t is_tx_pkt_cap_enable, 645 uint8_t *peer_mac) 646 { 647 return QDF_STATUS_SUCCESS; 648 } 649 650 #ifdef QCA_SUPPORT_LITE_MONITOR 651 static void dp_fill_lite_mon_vdev(struct cdp_tx_indication_info *tx_cap_info, 652 struct dp_mon_pdev_be *mon_pdev_be) 653 { 654 struct dp_lite_mon_config *config; 655 struct dp_vdev *lite_mon_vdev; 656 657 config = &mon_pdev_be->lite_mon_tx_config->tx_config; 658 lite_mon_vdev = config->lite_mon_vdev; 659 660 if (lite_mon_vdev) 661 tx_cap_info->osif_vdev = lite_mon_vdev->osif_vdev; 662 } 663 #else 664 static void dp_fill_lite_mon_vdev(struct cdp_tx_indication_info *tx_cap_info, 665 struct dp_mon_pdev_be *mon_pdev_be) 666 { 667 } 668 #endif 669 670 /** 671 * dp_tx_mon_send_to_stack() - API to send to stack 672 * @pdev: pdev Handle 673 * @mpdu: pointer to mpdu 674 * @num_frag: number of frag in mpdu 675 * 676 * Return: void 677 */ 678 static void 679 dp_tx_mon_send_to_stack(struct dp_pdev *pdev, qdf_nbuf_t mpdu, 680 uint32_t num_frag) 681 { 682 struct dp_mon_pdev *mon_pdev = pdev->monitor_pdev; 683 struct dp_mon_pdev_be *mon_pdev_be = 684 dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 685 struct dp_pdev_tx_monitor_be *tx_mon_be = 686 &mon_pdev_be->tx_monitor_be; 687 struct cdp_tx_indication_info tx_capture_info = {0}; 688 689 tx_mon_be->stats.pkt_buf_to_stack += num_frag; 690 691 tx_capture_info.radiotap_done = 1; 692 tx_capture_info.mpdu_nbuf = mpdu; 693 if (!dp_lite_mon_is_tx_enabled(mon_pdev)) { 694 dp_wdi_event_handler(WDI_EVENT_TX_PKT_CAPTURE, 695 pdev->soc, 696 &tx_capture_info, 697 HTT_INVALID_PEER, 698 WDI_NO_VAL, 699 pdev->pdev_id); 700 } else { 701 dp_fill_lite_mon_vdev(&tx_capture_info, mon_pdev_be); 702 dp_wdi_event_handler(WDI_EVENT_LITE_MON_TX, 703 pdev->soc, 704 &tx_capture_info, 705 HTT_INVALID_PEER, 706 WDI_NO_VAL, 707 pdev->pdev_id); 708 } 709 if (tx_capture_info.mpdu_nbuf) 710 qdf_nbuf_free(tx_capture_info.mpdu_nbuf); 711 } 712 713 /** 714 * dp_tx_mon_send_per_usr_mpdu() - API to send per usr mpdu to stack 715 * @pdev: pdev Handle 716 * @ppdu_info: pointer to dp_tx_ppdu_info 717 * @user_id: current user index 718 * 719 * Return: void 720 */ 721 static void 722 dp_tx_mon_send_per_usr_mpdu(struct dp_pdev *pdev, 723 struct dp_tx_ppdu_info *ppdu_info, 724 uint8_t user_idx) 725 { 726 qdf_nbuf_queue_t *usr_mpdu_q = NULL; 727 qdf_nbuf_t buf = NULL; 728 729 usr_mpdu_q = &TXMON_PPDU_USR(ppdu_info, user_idx, mpdu_q); 730 731 while ((buf = qdf_nbuf_queue_remove(usr_mpdu_q)) != NULL) { 732 uint32_t num_frag = dp_tx_mon_nbuf_get_num_frag(buf); 733 734 ppdu_info->hal_txmon.rx_status.rx_user_status = 735 &ppdu_info->hal_txmon.rx_user_status[user_idx]; 736 737 qdf_nbuf_update_radiotap(&ppdu_info->hal_txmon.rx_status, 738 buf, qdf_nbuf_headroom(buf)); 739 740 dp_tx_mon_send_to_stack(pdev, buf, num_frag); 741 } 742 } 743 744 /** 745 * dp_tx_mon_update_radiotap() - API to update radiotap information 746 * @pdev: pdev Handle 747 * @ppdu_info: pointer to dp_tx_ppdu_info 748 * 749 * Return: void 750 */ 751 static void 752 dp_tx_mon_update_radiotap(struct dp_pdev *pdev, 753 struct dp_tx_ppdu_info *ppdu_info) 754 { 755 uint32_t usr_idx = 0; 756 uint32_t num_users = 0; 757 758 num_users = TXMON_PPDU_HAL(ppdu_info, num_users); 759 760 if (qdf_unlikely(TXMON_PPDU_COM(ppdu_info, chan_num) == 0)) 761 TXMON_PPDU_COM(ppdu_info, chan_num) = 762 pdev->operating_channel.num; 763 764 if (qdf_unlikely(TXMON_PPDU_COM(ppdu_info, chan_freq) == 0)) 765 TXMON_PPDU_COM(ppdu_info, chan_freq) = 766 pdev->operating_channel.freq; 767 768 for (usr_idx = 0; usr_idx < num_users; usr_idx++) { 769 qdf_nbuf_queue_t *mpdu_q = NULL; 770 771 /* set AMPDU flag if number mpdu is more than 1 */ 772 mpdu_q = &TXMON_PPDU_USR(ppdu_info, usr_idx, mpdu_q); 773 if (mpdu_q && (qdf_nbuf_queue_len(mpdu_q) > 1)) { 774 TXMON_PPDU_COM(ppdu_info, 775 rs_flags) |= IEEE80211_AMPDU_FLAG; 776 TXMON_PPDU_USR(ppdu_info, usr_idx, is_ampdu) = 1; 777 } 778 779 if (qdf_unlikely(!TXMON_PPDU_COM(ppdu_info, rate))) { 780 uint32_t rate = 0; 781 uint32_t rix = 0; 782 uint16_t ratecode = 0; 783 784 rate = dp_getrateindex(TXMON_PPDU_COM(ppdu_info, sgi), 785 TXMON_PPDU_USR(ppdu_info, 786 usr_idx, mcs), 787 TXMON_PPDU_COM(ppdu_info, nss), 788 TXMON_PPDU_COM(ppdu_info, 789 preamble_type), 790 TXMON_PPDU_COM(ppdu_info, bw), 791 0, 792 &rix, &ratecode); 793 794 /* update rate */ 795 TXMON_PPDU_COM(ppdu_info, rate) = rate; 796 } 797 798 dp_tx_mon_send_per_usr_mpdu(pdev, ppdu_info, usr_idx); 799 } 800 } 801 802 /** 803 * dp_tx_mon_ppdu_process - Deferred PPDU stats handler 804 * @context: Opaque work context (PDEV) 805 * 806 * Return: none 807 */ 808 void dp_tx_mon_ppdu_process(void *context) 809 { 810 struct dp_pdev *pdev = (struct dp_pdev *)context; 811 struct dp_mon_pdev *mon_pdev; 812 struct dp_mon_pdev_be *mon_pdev_be; 813 struct dp_tx_ppdu_info *defer_ppdu_info = NULL; 814 struct dp_tx_ppdu_info *defer_ppdu_info_next = NULL; 815 struct dp_pdev_tx_monitor_be *tx_mon_be; 816 817 /* sanity check */ 818 if (qdf_unlikely(!pdev)) 819 return; 820 821 mon_pdev = pdev->monitor_pdev; 822 823 if (qdf_unlikely(!mon_pdev)) 824 return; 825 826 mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 827 if (qdf_unlikely(!mon_pdev_be)) 828 return; 829 830 tx_mon_be = &mon_pdev_be->tx_monitor_be; 831 if (qdf_unlikely(TX_MON_BE_DISABLE == tx_mon_be->mode && 832 !dp_lite_mon_is_tx_enabled(mon_pdev))) 833 return; 834 835 /* take lock here */ 836 qdf_spin_lock_bh(&tx_mon_be->tx_mon_list_lock); 837 STAILQ_CONCAT(&tx_mon_be->defer_tx_ppdu_info_queue, 838 &tx_mon_be->tx_ppdu_info_queue); 839 tx_mon_be->defer_ppdu_info_list_depth += 840 tx_mon_be->tx_ppdu_info_list_depth; 841 tx_mon_be->tx_ppdu_info_list_depth = 0; 842 qdf_spin_unlock_bh(&tx_mon_be->tx_mon_list_lock); 843 844 STAILQ_FOREACH_SAFE(defer_ppdu_info, 845 &tx_mon_be->defer_tx_ppdu_info_queue, 846 tx_ppdu_info_queue_elem, defer_ppdu_info_next) { 847 /* remove dp_tx_ppdu_info from the list */ 848 STAILQ_REMOVE(&tx_mon_be->defer_tx_ppdu_info_queue, 849 defer_ppdu_info, 850 dp_tx_ppdu_info, 851 tx_ppdu_info_queue_elem); 852 tx_mon_be->defer_ppdu_info_list_depth--; 853 854 dp_tx_mon_update_radiotap(pdev, defer_ppdu_info); 855 856 /* free the ppdu_info */ 857 dp_tx_mon_free_ppdu_info(defer_ppdu_info, tx_mon_be); 858 defer_ppdu_info = NULL; 859 } 860 } 861 862 /** 863 * dp_tx_ppdu_stats_attach_2_0 - Initialize Tx PPDU stats and enhanced capture 864 * @pdev: DP PDEV 865 * 866 * Return: none 867 */ 868 void dp_tx_ppdu_stats_attach_2_0(struct dp_pdev *pdev) 869 { 870 struct dp_mon_pdev *mon_pdev; 871 struct dp_mon_pdev_be *mon_pdev_be; 872 struct dp_pdev_tx_monitor_be *tx_mon_be; 873 874 if (qdf_unlikely(!pdev)) 875 return; 876 877 mon_pdev = pdev->monitor_pdev; 878 879 if (qdf_unlikely(!mon_pdev)) 880 return; 881 882 mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 883 if (qdf_unlikely(!mon_pdev_be)) 884 return; 885 886 tx_mon_be = &mon_pdev_be->tx_monitor_be; 887 888 STAILQ_INIT(&tx_mon_be->tx_ppdu_info_queue); 889 tx_mon_be->tx_ppdu_info_list_depth = 0; 890 891 STAILQ_INIT(&tx_mon_be->defer_tx_ppdu_info_queue); 892 tx_mon_be->defer_ppdu_info_list_depth = 0; 893 894 qdf_spinlock_create(&tx_mon_be->tx_mon_list_lock); 895 /* Work queue setup for TX MONITOR post handling */ 896 qdf_create_work(0, &tx_mon_be->post_ppdu_work, 897 dp_tx_mon_ppdu_process, pdev); 898 899 tx_mon_be->post_ppdu_workqueue = 900 qdf_alloc_unbound_workqueue("tx_mon_ppdu_work_queue"); 901 } 902 903 /** 904 * dp_tx_ppdu_stats_detach_be - Cleanup Tx PPDU stats and enhanced capture 905 * @pdev: DP PDEV 906 * 907 * Return: none 908 */ 909 void dp_tx_ppdu_stats_detach_2_0(struct dp_pdev *pdev) 910 { 911 struct dp_mon_pdev *mon_pdev; 912 struct dp_mon_pdev_be *mon_pdev_be; 913 struct dp_pdev_tx_monitor_be *tx_mon_be; 914 struct dp_tx_ppdu_info *tx_ppdu_info = NULL; 915 struct dp_tx_ppdu_info *tx_ppdu_info_next = NULL; 916 917 if (qdf_unlikely(!pdev)) 918 return; 919 920 mon_pdev = pdev->monitor_pdev; 921 922 if (qdf_unlikely(!mon_pdev)) 923 return; 924 925 mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 926 if (qdf_unlikely(!mon_pdev_be)) 927 return; 928 929 tx_mon_be = &mon_pdev_be->tx_monitor_be; 930 /* TODO: disable tx_monitor, to avoid further packet from HW */ 931 dp_monitor_config_enh_tx_capture(pdev, TX_MON_BE_DISABLE); 932 933 /* flush workqueue */ 934 qdf_flush_workqueue(0, tx_mon_be->post_ppdu_workqueue); 935 qdf_destroy_workqueue(0, tx_mon_be->post_ppdu_workqueue); 936 937 /* 938 * TODO: iterate both tx_ppdu_info and defer_ppdu_info_list 939 * free the tx_ppdu_info and decrement depth 940 */ 941 qdf_spin_lock_bh(&tx_mon_be->tx_mon_list_lock); 942 STAILQ_FOREACH_SAFE(tx_ppdu_info, 943 &tx_mon_be->tx_ppdu_info_queue, 944 tx_ppdu_info_queue_elem, tx_ppdu_info_next) { 945 /* remove dp_tx_ppdu_info from the list */ 946 STAILQ_REMOVE(&tx_mon_be->tx_ppdu_info_queue, tx_ppdu_info, 947 dp_tx_ppdu_info, tx_ppdu_info_queue_elem); 948 /* decrement list length */ 949 tx_mon_be->tx_ppdu_info_list_depth--; 950 /* free tx_ppdu_info */ 951 dp_tx_mon_free_ppdu_info(tx_ppdu_info, tx_mon_be); 952 } 953 qdf_spin_unlock_bh(&tx_mon_be->tx_mon_list_lock); 954 955 qdf_spin_lock_bh(&tx_mon_be->tx_mon_list_lock); 956 STAILQ_FOREACH_SAFE(tx_ppdu_info, 957 &tx_mon_be->defer_tx_ppdu_info_queue, 958 tx_ppdu_info_queue_elem, tx_ppdu_info_next) { 959 /* remove dp_tx_ppdu_info from the list */ 960 STAILQ_REMOVE(&tx_mon_be->defer_tx_ppdu_info_queue, 961 tx_ppdu_info, 962 dp_tx_ppdu_info, tx_ppdu_info_queue_elem); 963 /* decrement list length */ 964 tx_mon_be->defer_ppdu_info_list_depth--; 965 /* free tx_ppdu_info */ 966 dp_tx_mon_free_ppdu_info(tx_ppdu_info, tx_mon_be); 967 } 968 qdf_spin_unlock_bh(&tx_mon_be->tx_mon_list_lock); 969 970 qdf_spinlock_destroy(&tx_mon_be->tx_mon_list_lock); 971 } 972 #endif /* WLAN_TX_PKT_CAPTURE_ENH_BE */ 973 974 #if (defined(WIFI_MONITOR_SUPPORT) && !defined(WLAN_TX_PKT_CAPTURE_ENH_BE)) 975 /* 976 * dp_config_enh_tx_core_monitor_2_0()- API to validate core framework 977 * @pdev_handle: DP_PDEV handle 978 * @val: user provided value 979 * 980 * Return: QDF_STATUS 981 */ 982 QDF_STATUS 983 dp_config_enh_tx_core_monitor_2_0(struct dp_pdev *pdev, uint8_t val) 984 { 985 struct dp_mon_pdev *mon_pdev = pdev->monitor_pdev; 986 struct dp_mon_pdev_be *mon_pdev_be = 987 dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev); 988 struct dp_pdev_tx_monitor_be *tx_mon_be = 989 &mon_pdev_be->tx_monitor_be; 990 991 switch (val) { 992 case TX_MON_BE_FRM_WRK_DISABLE: 993 { 994 tx_mon_be->mode = val; 995 mon_pdev_be->tx_mon_mode = 0; 996 mon_pdev_be->tx_mon_filter_length = DMA_LENGTH_64B; 997 break; 998 } 999 case TX_MON_BE_FRM_WRK_FULL_CAPTURE: 1000 { 1001 tx_mon_be->mode = val; 1002 qdf_mem_zero(&tx_mon_be->stats, 1003 sizeof(struct dp_tx_monitor_drop_stats)); 1004 tx_mon_be->mode = val; 1005 mon_pdev_be->tx_mon_mode = 1; 1006 mon_pdev_be->tx_mon_filter_length = DEFAULT_DMA_LENGTH; 1007 break; 1008 } 1009 case TX_MON_BE_FRM_WRK_128B_CAPTURE: 1010 { 1011 tx_mon_be->mode = val; 1012 mon_pdev_be->tx_mon_mode = 1; 1013 mon_pdev_be->tx_mon_filter_length = DMA_LENGTH_128B; 1014 break; 1015 } 1016 default: 1017 { 1018 return QDF_STATUS_E_INVAL; 1019 } 1020 } 1021 1022 dp_mon_debug("Tx monitor mode:%d mon_mode_flag:%d config_length:%d", 1023 tx_mon_be->mode, mon_pdev_be->tx_mon_mode, 1024 mon_pdev_be->tx_mon_filter_length); 1025 1026 /* send HTT msg to configure TLV based on mode */ 1027 dp_mon_filter_setup_tx_mon_mode(pdev); 1028 dp_tx_mon_filter_update(pdev); 1029 1030 return QDF_STATUS_SUCCESS; 1031 } 1032 #endif 1033