qca-wifi: Support MU in tx capture mode

Support tx capture for OFDMA and MIMO packet

Change-Id: I24ed22168f5aec30c6c2281028257037f860f3c6
CRs-Fixed: 2621791
diff --git a/dp/wifi3.0/dp_tx_capture.c b/dp/wifi3.0/dp_tx_capture.c
index 2324945..1be6848 100644
--- a/dp/wifi3.0/dp_tx_capture.c
+++ b/dp/wifi3.0/dp_tx_capture.c
@@ -110,12 +110,120 @@
 
 #ifdef WLAN_TX_PKT_CAPTURE_ENH
 
+/* stats counter */
+#ifdef WLAN_TX_PKT_CAPTURE_ENH_DEBUG
+/**
+ * dp_tx_cap_stats_msdu_update() - update msdu level stats counter per peer
+ * @peer: DP PEER object
+ * @msdu_desc: msdu desc index
+ * @count: count to update
+ *
+ * Return: void
+ */
+static inline
+void dp_tx_cap_stats_msdu_update(struct dp_peer *peer,
+				 uint8_t msdu_desc, uint32_t count)
+{
+	struct dp_peer_tx_capture_stats *stats;
+
+	stats = &peer->tx_capture.stats;
+
+	stats->msdu[msdu_desc] += count;
+}
+
+/**
+ * dp_tx_cap_stats_mpdu_update() - update mpdu level stats counter per peer
+ * @peer: DP PEER object
+ * @mpdu_desc: mpdu desc index
+ * @count: count to update
+ *
+ * Return: void
+ */
+static inline
+void dp_tx_cap_stats_mpdu_update(struct dp_peer *peer,
+				 uint8_t mpdu_desc, uint32_t count)
+{
+	struct dp_peer_tx_capture_stats *stats;
+
+	stats = &peer->tx_capture.stats;
+
+	stats->mpdu[mpdu_desc] += count;
+}
+
+/**
+ * dp_tx_capture_print_stats() - print stats counter per peer
+ * @peer: DP PEER object
+ *
+ * Return: void
+ */
+static inline
+void dp_tx_capture_print_stats(struct dp_peer *peer)
+{
+	struct dp_peer_tx_capture_stats *stats;
+
+	stats = &peer->tx_capture.stats;
+	DP_PRINT_STATS(" peer_id[%d] MSDU[S:%u E:%u D:%u F:%u DP:%u X:%u] MPDU[T:%u S:%u R:%u A:%u C:%u ST:%u]",
+			peer->peer_ids[0],
+			stats->msdu[PEER_MSDU_SUCC],
+			stats->msdu[PEER_MSDU_ENQ],
+			stats->msdu[PEER_MSDU_DEQ],
+			stats->msdu[PEER_MSDU_FLUSH],
+			stats->msdu[PEER_MSDU_DROP],
+			stats->msdu[PEER_MSDU_XRETRY],
+			stats->mpdu[PEER_MPDU_TRI],
+			stats->mpdu[PEER_MPDU_SUCC],
+			stats->mpdu[PEER_MPDU_RESTITCH],
+			stats->mpdu[PEER_MPDU_ARR],
+			stats->mpdu[PEER_MPDU_CLONE],
+			stats->mpdu[PEER_MPDU_TO_STACK]);
+}
+#else
+/**
+ * dp_tx_cap_stats_msdu_update() - update msdu level stats counter per peer
+ * @peer: DP PEER object
+ * @msdu_desc: msdu desc index
+ * @count: count to update
+ *
+ * Return: void
+ */
+static inline
+void dp_tx_cap_stats_msdu_update(struct dp_peer *peer,
+				 uint8_t msdu_desc, uint32_t count)
+{
+}
+
+/**
+ * dp_tx_cap_stats_mpdu_update() - update mpdu level stats counter per peer
+ * @peer: DP PEER object
+ * @mpdu_desc: mpdu desc index
+ * @count: count to update
+ *
+ * Return: void
+ */
+static inline
+void dp_tx_cap_stats_mpdu_update(struct dp_peer *peer,
+				 uint8_t mpdu_desc, uint32_t count)
+{
+}
+
+/**
+ * dp_tx_capture_print_stats() - print stats counter per peer
+ * @peer: DP PEER object
+ *
+ * Return: void
+ */
+static inline
+void dp_tx_capture_print_stats(struct dp_peer *peer)
+{
+}
+#endif
+
 /**
  * dp_tx_cap_peer_find_by_id() - Returns peer object given the peer id
  * if delete_in_progress in not set for peer
  *
- * @soc		: core DP soc context
- * @peer_id	: peer id from peer object can be retrieved
+ * @soc: core DP soc context
+ * @peer_id: peer id from peer object can be retrieved
  *
  * Return: struct dp_peer*: Pointer to DP peer object
  */
@@ -166,15 +274,18 @@
 	pdev->tx_capture.htt_frame_type[htt_frame_type]++;
 }
 
+/*
+ * dp_iterate_print_tid_qlen_per_peer()- API to print peer tid msdu queue
+ * @pdev_handle: DP_PDEV handle
+ *
+ * Return: void
+ */
 void dp_print_tid_qlen_per_peer(void *pdev_hdl)
 {
 	struct dp_pdev *pdev = (struct dp_pdev *)pdev_hdl;
 	struct dp_soc *soc = pdev->soc;
 	struct dp_vdev *vdev = NULL;
 	struct dp_peer *peer = NULL;
-	struct dp_pdev_tx_capture *ptr_tx_cap;
-
-	ptr_tx_cap = &(pdev->tx_capture);
 
 	DP_PRINT_STATS("pending peer msdu and ppdu:");
 	qdf_spin_lock_bh(&soc->peer_ref_mutex);
@@ -185,28 +296,71 @@
 			int tid;
 			struct dp_tx_tid *tx_tid;
 			uint32_t msdu_len;
+			uint32_t tasklet_msdu_len;
 			uint32_t ppdu_len;
-
 			for (tid = 0; tid < DP_MAX_TIDS; tid++) {
 				tx_tid = &peer->tx_capture.tx_tid[tid];
 				msdu_len =
+				qdf_nbuf_queue_len(&tx_tid->defer_msdu_q);
+				tasklet_msdu_len =
 				qdf_nbuf_queue_len(&tx_tid->msdu_comp_q);
 				ppdu_len =
 				qdf_nbuf_queue_len(&tx_tid->pending_ppdu_q);
-
-				if (!msdu_len && !ppdu_len)
+				if (!msdu_len && !ppdu_len && !tasklet_msdu_len)
 					continue;
-
-				DP_PRINT_STATS(" peer_id[%d] tid[%d] msdu_comp_q[%d] pending_ppdu_q[%d]",
+				DP_PRINT_STATS(" peer_id[%d] tid[%d] msdu_comp_q[%d] defer_msdu_q[%d] pending_ppdu_q[%d]",
 					       peer->peer_ids[0], tid,
+					       tasklet_msdu_len,
 					       msdu_len, ppdu_len);
 			}
+			dp_tx_capture_print_stats(peer);
 		}
 	}
+
 	qdf_spin_unlock_bh(&pdev->vdev_list_lock);
 	qdf_spin_unlock_bh(&soc->peer_ref_mutex);
 }
 
+static void
+dp_ppdu_queue_free(qdf_nbuf_t ppdu_nbuf, uint8_t usr_idx)
+{
+	int i;
+	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
+	struct cdp_tx_completion_ppdu_user *user;
+	qdf_nbuf_t mpdu_nbuf = NULL;
+
+	if (!ppdu_nbuf)
+		return;
+
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_nbuf);
+	if (!ppdu_desc)
+		return;
+
+	user = &ppdu_desc->user[usr_idx];
+
+	if (!user->mpdus)
+		goto free_ppdu_desc_mpdu_q;
+
+	for (i = 0; i < user->ba_size &&
+	     i < CDP_BA_256_BIT_MAP_SIZE_DWORDS; i++) {
+		mpdu_nbuf = user->mpdus[i];
+		if (mpdu_nbuf) {
+			qdf_nbuf_free(mpdu_nbuf);
+			user->mpdus[i] = NULL;
+		}
+	}
+
+free_ppdu_desc_mpdu_q:
+
+	if (!qdf_nbuf_is_queue_empty(&user->mpdu_q))
+		qdf_nbuf_queue_free(&user->mpdu_q);
+
+	if (user->mpdus)
+		qdf_mem_free(user->mpdus);
+
+	user->mpdus = NULL;
+}
+
 /*
  * dp_tx_cature_stats: print tx capture stats
  * @pdev: DP PDEV handle
@@ -223,6 +377,10 @@
 	DP_PRINT_STATS("tx capture stats:");
 	DP_PRINT_STATS(" pending ppdu dropped: %u",
 		       ptr_tx_cap->pend_ppdu_dropped);
+	DP_PRINT_STATS(" ppdu stats queue depth: %u",
+		       ptr_tx_cap->ppdu_stats_queue_depth);
+	DP_PRINT_STATS(" ppdu stats defer queue depth: %u",
+		       ptr_tx_cap->ppdu_stats_defer_queue_depth);
 	DP_PRINT_STATS(" mgmt control enqueue stats:");
 	for (i = 0; i < TXCAP_MAX_TYPE; i++) {
 		for (j = 0; j < TXCAP_MAX_SUBTYPE; j++) {
@@ -241,14 +399,14 @@
 		}
 	}
 
-	dp_print_tid_qlen_per_peer(pdev);
-
 	for (i = 0; i < TX_CAP_HTT_MAX_FTYPE; i++) {
 		if (!ptr_tx_cap->htt_frame_type[i])
 			continue;
 		DP_PRINT_STATS(" sgen htt frame type[%d] = %d",
 			       i, ptr_tx_cap->htt_frame_type[i]);
 	}
+
+	dp_print_tid_qlen_per_peer(pdev);
 }
 
 /**
@@ -281,6 +439,56 @@
 }
 
 /*
+ * dp_tx_find_usr_idx_from_peer_id()- find user index based on peer_id
+ * @ppdu_desc: pointer to ppdu_desc structure
+ * @peer_id: peer id
+ *
+ * Return: user index
+ */
+static uint8_t
+dp_tx_find_usr_idx_from_peer_id(struct cdp_tx_completion_ppdu *ppdu_desc,
+				uint16_t peer_id)
+{
+	uint8_t usr_idx = 0;
+	bool found = false;
+
+	for (usr_idx = 0; usr_idx < ppdu_desc->num_users; usr_idx++) {
+		if (ppdu_desc->user[usr_idx].peer_id == peer_id) {
+			found = true;
+			break;
+		}
+	}
+
+	if (!found) {
+		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+			  QDF_TRACE_LEVEL_FATAL,
+			  "%s: %d peer_id: %d, ppdu_desc[%p][num_users: %d]\n",
+			  __func__, __LINE__, peer_id, ppdu_desc,
+			  ppdu_desc->num_users);
+		qdf_assert_always(0);
+	}
+
+	return usr_idx;
+}
+
+/*
+ * dp_peer_tid_peer_id_update() – update peer_id to tid structure
+ * @peer: Datapath peer
+ * @peer_id: peer_id
+ *
+ */
+void dp_peer_tid_peer_id_update(struct dp_peer *peer, uint16_t peer_id)
+{
+	int tid;
+	struct dp_tx_tid *tx_tid;
+
+	for (tid = 0; tid < DP_MAX_TIDS; tid++) {
+		tx_tid = &peer->tx_capture.tx_tid[tid];
+		tx_tid->peer_id = peer_id;
+	}
+}
+
+/*
  * dp_peer_tid_queue_init() – Initialize ppdu stats queue per TID
  * @peer: Datapath peer
  *
@@ -293,15 +501,23 @@
 	for (tid = 0; tid < DP_MAX_TIDS; tid++) {
 		tx_tid = &peer->tx_capture.tx_tid[tid];
 		tx_tid->tid = tid;
+		qdf_nbuf_queue_init(&tx_tid->defer_msdu_q);
 		qdf_nbuf_queue_init(&tx_tid->msdu_comp_q);
 		qdf_nbuf_queue_init(&tx_tid->pending_ppdu_q);
 		tx_tid->max_ppdu_id = 0;
 
 		/* spinlock create */
 		qdf_spinlock_create(&tx_tid->tid_lock);
+		qdf_spinlock_create(&tx_tid->tasklet_tid_lock);
 	}
 }
 
+/*
+ * dp_peer_tx_cap_tid_queue_flush() – flush peer tx cap per TID
+ * @peer: Datapath peer
+ *
+ * return: void
+ */
 static
 void dp_peer_tx_cap_tid_queue_flush(struct dp_peer *peer)
 {
@@ -312,9 +528,13 @@
 		tx_tid = &peer->tx_capture.tx_tid[tid];
 
 		qdf_spin_lock_bh(&tx_tid->tid_lock);
-		qdf_nbuf_queue_free(&tx_tid->msdu_comp_q);
+		qdf_nbuf_queue_free(&tx_tid->defer_msdu_q);
 		qdf_spin_unlock_bh(&tx_tid->tid_lock);
 
+		qdf_spin_lock_bh(&tx_tid->tasklet_tid_lock);
+		qdf_nbuf_queue_free(&tx_tid->msdu_comp_q);
+		qdf_spin_unlock_bh(&tx_tid->tasklet_tid_lock);
+
 		tx_tid->max_ppdu_id = 0;
 	}
 }
@@ -326,18 +546,56 @@
  */
 void dp_peer_tid_queue_cleanup(struct dp_peer *peer)
 {
-	int tid;
 	struct dp_tx_tid *tx_tid;
+	struct cdp_tx_completion_ppdu *xretry_ppdu;
+	struct cdp_tx_completion_ppdu_user *xretry_user;
+	struct cdp_tx_completion_ppdu *ppdu_desc;
+	struct cdp_tx_completion_ppdu_user *user;
+	qdf_nbuf_t ppdu_nbuf = NULL;
+	int tid;
+	uint16_t peer_id;
 
 	for (tid = 0; tid < DP_MAX_TIDS; tid++) {
 		tx_tid = &peer->tx_capture.tx_tid[tid];
+		xretry_ppdu = &tx_tid->xretry_ppdu;
+		xretry_user = &xretry_ppdu->user[0];
 
 		qdf_spin_lock_bh(&tx_tid->tid_lock);
-		qdf_nbuf_queue_free(&tx_tid->msdu_comp_q);
+		qdf_nbuf_queue_free(&tx_tid->defer_msdu_q);
 		qdf_spin_unlock_bh(&tx_tid->tid_lock);
 
+		qdf_spin_lock_bh(&tx_tid->tasklet_tid_lock);
+		qdf_nbuf_queue_free(&tx_tid->msdu_comp_q);
+		qdf_spin_unlock_bh(&tx_tid->tasklet_tid_lock);
+
 		/* spinlock destroy */
 		qdf_spinlock_destroy(&tx_tid->tid_lock);
+		qdf_spinlock_destroy(&tx_tid->tasklet_tid_lock);
+
+		peer_id = tx_tid->peer_id;
+
+		/* free pending ppdu_q and xretry mpdu_q */
+		while ((ppdu_nbuf = qdf_nbuf_queue_remove(
+						&tx_tid->pending_ppdu_q))) {
+			uint8_t usr_idx;
+
+			ppdu_desc = (struct cdp_tx_completion_ppdu *)
+					qdf_nbuf_data(ppdu_nbuf);
+
+			/*
+			 * check if peer id is matching
+			 * the user peer_id
+			 */
+			usr_idx = dp_tx_find_usr_idx_from_peer_id(ppdu_desc,
+								  peer_id);
+			user = &ppdu_desc->user[usr_idx];
+
+			/* free all the mpdu_q and mpdus for usr_idx */
+			dp_ppdu_queue_free(ppdu_nbuf, usr_idx);
+			qdf_nbuf_free(ppdu_nbuf);
+		}
+		qdf_nbuf_queue_free(&xretry_user->mpdu_q);
+
 		tx_tid->max_ppdu_id = 0;
 	}
 }
@@ -418,16 +676,23 @@
 			return;
 		}
 
+		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_DEBUG,
+			  "dlvr mgmt frm(%d 0x%08x): fc 0x%x %x, dur 0x%x%x tsf:%u",
+			  ptr_mgmt_hdr->ppdu_id,
+			  ptr_mgmt_hdr->ppdu_id,
+			  wh->i_fc[1], wh->i_fc[0],
+			  wh->i_dur[1], wh->i_dur[0], ptr_mgmt_hdr->tx_tsf);
+
+		QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_TX_CAPTURE,
+				   QDF_TRACE_LEVEL_DEBUG,
+				   qdf_nbuf_data(nbuf), 64);
+
 		qdf_spin_lock_bh(
 			&pdev->tx_capture.ctl_mgmt_lock[type][subtype]);
 		qdf_nbuf_queue_add(&pdev->tx_capture.ctl_mgmt_q[type][subtype],
 			nbuf);
 		qdf_spin_unlock_bh(
 			&pdev->tx_capture.ctl_mgmt_lock[type][subtype]);
-		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_DEBUG,
-			"dlvr mgmt frm(0x%08x): fc 0x%x %x, dur 0x%x%x\n",
-			ptr_mgmt_hdr->ppdu_id, wh->i_fc[1], wh->i_fc[0],
-			wh->i_dur[1], wh->i_dur[0]);
 	} else {
 		if (!pdev->bpr_enable)
 			qdf_nbuf_free(nbuf);
@@ -629,7 +894,7 @@
 	bool found = false;
 
 	found = dp_peer_tx_cap_search(pdev, peer_id, mac_addr);
-	QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_INFO_HIGH,
+	QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_INFO_LOW,
 		  "%s: %d peer_id[%d] mac_addr[%pM] found[%d]!",
 		  __func__, __LINE__, peer_id, mac_addr, found);
 
@@ -648,8 +913,8 @@
 	struct dp_pdev_tx_capture *tx_capture;
 	int i, j;
 
+	pdev->tx_capture.tx_cap_mode_flag = true;
 	tx_capture = &pdev->tx_capture;
-	tx_capture->tx_cap_mode_flag = true;
 	/* Work queue setup for HTT stats and tx capture handling */
 	qdf_create_work(0, &pdev->tx_capture.ppdu_stats_work,
 			dp_tx_ppdu_stats_process,
@@ -723,6 +988,8 @@
 	}
 	for (i = 0; i < TXCAP_MAX_TYPE; i++) {
 		for (j = 0; j < TXCAP_MAX_SUBTYPE; j++) {
+			qdf_nbuf_queue_t *retries_q;
+
 			qdf_spin_lock_bh(
 				&pdev->tx_capture.ctl_mgmt_lock[i][j]);
 			qdf_nbuf_queue_free(
@@ -731,6 +998,11 @@
 				&pdev->tx_capture.ctl_mgmt_lock[i][j]);
 			qdf_spinlock_destroy(
 				&pdev->tx_capture.ctl_mgmt_lock[i][j]);
+
+			retries_q = &pdev->tx_capture.retries_ctl_mgmt_q[i][j];
+
+			if (!qdf_nbuf_is_queue_empty(retries_q))
+				qdf_nbuf_queue_free(retries_q);
 		}
 	}
 
@@ -739,6 +1011,68 @@
 
 #define MAX_MSDU_THRESHOLD_TSF 100000
 #define MAX_MSDU_ENQUEUE_THRESHOLD 10000
+
+/**
+ * dp_drop_enq_msdu_on_thresh(): Function to drop msdu when exceed
+ * storing threshold limit
+ * @peer: dp_peer
+ * @ptr_msdu_comp_q: pointer to skb queue, it can be either tasklet or WQ msdu q
+ * @tsf: current timestamp
+ *
+ * this function must be called inside lock of corresponding msdu_q
+ * return: status
+ */
+QDF_STATUS
+dp_drop_enq_msdu_on_thresh(struct dp_peer *peer,
+			   qdf_nbuf_queue_t *ptr_msdu_comp_q,
+			   uint32_t tsf)
+{
+	struct msdu_completion_info *ptr_msdu_info = NULL;
+	qdf_nbuf_t nbuf;
+	qdf_nbuf_t head_msdu;
+	uint32_t tsf_delta;
+	uint32_t qlen;
+
+	while ((head_msdu = qdf_nbuf_queue_first(ptr_msdu_comp_q))) {
+		ptr_msdu_info =
+		(struct msdu_completion_info *)qdf_nbuf_data(head_msdu);
+
+		if (tsf > ptr_msdu_info->tsf)
+			tsf_delta = tsf - ptr_msdu_info->tsf;
+		else
+			tsf_delta = LOWER_32_MASK - ptr_msdu_info->tsf + tsf;
+
+		if (tsf_delta < MAX_MSDU_THRESHOLD_TSF)
+			break;
+
+		/* free head */
+		nbuf = qdf_nbuf_queue_remove(ptr_msdu_comp_q);
+		if (qdf_unlikely(!nbuf)) {
+			qdf_assert_always(0);
+			break;
+		}
+
+		qdf_nbuf_free(nbuf);
+		dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_DROP, 1);
+	}
+
+	/* get queue length */
+	qlen = qdf_nbuf_queue_len(ptr_msdu_comp_q);
+	if (qlen > MAX_MSDU_ENQUEUE_THRESHOLD) {
+		/* free head */
+		nbuf = qdf_nbuf_queue_remove(ptr_msdu_comp_q);
+		if (qdf_unlikely(!nbuf)) {
+			qdf_assert_always(0);
+			return QDF_STATUS_E_ABORTED;
+		}
+
+		qdf_nbuf_free(nbuf);
+		dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_DROP, 1);
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
 /**
  * dp_update_msdu_to_list(): Function to queue msdu from wbm
  * @pdev: dp_pdev
@@ -757,11 +1091,6 @@
 {
 	struct dp_tx_tid *tx_tid;
 	struct msdu_completion_info *msdu_comp_info;
-	struct msdu_completion_info *ptr_msdu_info = NULL;
-	qdf_nbuf_t nbuf;
-	qdf_nbuf_t head_msdu;
-	uint32_t tsf_delta;
-	uint32_t qlen;
 
 	if (!peer) {
 		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_ERROR,
@@ -785,14 +1114,14 @@
 		return QDF_STATUS_E_FAILURE;
 	}
 
-	qdf_nbuf_unmap(soc->osdev, netbuf, QDF_DMA_TO_DEVICE);
-
 	if (!qdf_nbuf_push_head(netbuf, sizeof(struct msdu_completion_info))) {
 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
 			  FL("No headroom"));
 		return QDF_STATUS_E_NOMEM;
 	}
 
+	qdf_nbuf_unmap(soc->osdev, netbuf, QDF_DMA_TO_DEVICE);
+
 	msdu_comp_info = (struct msdu_completion_info *)qdf_nbuf_data(netbuf);
 
 	/* copy msdu_completion_info to control buffer */
@@ -806,9 +1135,22 @@
 	msdu_comp_info->tsf = ts->tsf;
 	msdu_comp_info->status = ts->status;
 
+	/* lock here */
+	qdf_spin_lock_bh(&tx_tid->tasklet_tid_lock);
+	if (tx_tid->max_ppdu_id != ts->ppdu_id)
+		dp_drop_enq_msdu_on_thresh(peer, &tx_tid->msdu_comp_q,
+					   ts->tsf);
+
+	/* add nbuf to tail queue per peer tid */
+	qdf_nbuf_queue_add(&tx_tid->msdu_comp_q, netbuf);
+	dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_ENQ, 1);
+	/* unlock here */
+	qdf_spin_unlock_bh(&tx_tid->tasklet_tid_lock);
+
 	/* update max ppdu_id */
 	tx_tid->max_ppdu_id = ts->ppdu_id;
 	pdev->tx_capture.last_msdu_id = ts->ppdu_id;
+	pdev->tx_capture.last_peer_id = ts->peer_id;
 
 	QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_INFO,
 		  "msdu_completion: ppdu_id[%d] peer_id[%d] tid[%d] rel_src[%d] status[%d] tsf[%u] A[%d] CNT[%d]",
@@ -816,38 +1158,6 @@
 		  ts->status, ts->tsf, ts->msdu_part_of_amsdu,
 		  ts->transmit_cnt);
 
-	/* lock here */
-	qdf_spin_lock_bh(&tx_tid->tid_lock);
-	while ((head_msdu = qdf_nbuf_queue_first(&tx_tid->msdu_comp_q))) {
-		ptr_msdu_info =
-		(struct msdu_completion_info *)qdf_nbuf_data(head_msdu);
-
-		if (ts->tsf > ptr_msdu_info->tsf)
-			tsf_delta = ts->tsf - ptr_msdu_info->tsf;
-		else
-			tsf_delta = LOWER_32_MASK - ptr_msdu_info->tsf + ts->tsf;
-
-		if (tsf_delta < MAX_MSDU_THRESHOLD_TSF)
-			break;
-
-		/* free head */
-		nbuf = qdf_nbuf_queue_remove(&tx_tid->msdu_comp_q);
-		qdf_nbuf_free(nbuf);
-	}
-
-	/* get queue length */
-	qlen = qdf_nbuf_queue_len(&tx_tid->msdu_comp_q);
-	if (qlen > MAX_MSDU_ENQUEUE_THRESHOLD) {
-		/* free head */
-		nbuf = qdf_nbuf_queue_remove(&tx_tid->msdu_comp_q);
-		qdf_nbuf_free(nbuf);
-	}
-
-	/* add nbuf to tail queue per peer tid */
-	qdf_nbuf_queue_add(&tx_tid->msdu_comp_q, netbuf);
-
-	qdf_spin_unlock_bh(&tx_tid->tid_lock);
-
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -855,7 +1165,7 @@
  * dp_tx_add_to_comp_queue() - add completion msdu to queue
  * @soc: DP Soc handle
  * @tx_desc: software Tx descriptor
- * @ts : Tx completion status from HAL/HTT descriptor
+ * @ts: Tx completion status from HAL/HTT descriptor
  * @peer: DP peer
  *
  * Return: none
@@ -873,10 +1183,6 @@
 	    ((ts->status == HAL_TX_TQM_RR_FRAME_ACKED) ||
 	    (ts->status == HAL_TX_TQM_RR_REM_CMD_TX) ||
 	    ((ts->status == HAL_TX_TQM_RR_REM_CMD_AGED) && ts->transmit_cnt))) {
-		/* skip enqueuing OFDMA frames */
-		if (ts->ofdma)
-			return ret;
-
 		if (qdf_unlikely(desc->pkt_offset != 0) &&
 		    (qdf_nbuf_pull_head(
 				desc->nbuf, desc->pkt_offset) == NULL)) {
@@ -1047,44 +1353,9 @@
 	qdf_spin_lock_bh(&pdev->vdev_list_lock);
 	DP_PDEV_ITERATE_VDEV_LIST(pdev, vdev) {
 		DP_VDEV_ITERATE_PEER_LIST(vdev, peer) {
-			int tid;
-			struct dp_tx_tid *tx_tid;
-
 			/* set peer tx cap enabled to 0, when feature disable */
 			peer->tx_cap_enabled = 0;
-			for (tid = 0; tid < DP_MAX_TIDS; tid++) {
-				qdf_nbuf_t ppdu_nbuf = NULL;
-				struct cdp_tx_completion_ppdu *ppdu_desc =
-					NULL;
-				int i;
-				tx_tid = &peer->tx_capture.tx_tid[tid];
-
-				/* spinlock hold */
-				qdf_spin_lock_bh(&tx_tid->tid_lock);
-				qdf_nbuf_queue_free(&tx_tid->msdu_comp_q);
-				qdf_spin_unlock_bh(&tx_tid->tid_lock);
-				while ((ppdu_nbuf = qdf_nbuf_queue_remove(
-				       &tx_tid->pending_ppdu_q))) {
-					ppdu_desc =
-					(struct cdp_tx_completion_ppdu *)
-					qdf_nbuf_data(ppdu_nbuf);
-					if (!ppdu_desc->mpdus) {
-						qdf_nbuf_free(ppdu_nbuf);
-						continue;
-					}
-					for (i = 0; i <
-					   ppdu_desc->user[0].ba_size; i++) {
-						if (!ppdu_desc->mpdus[i])
-							continue;
-						qdf_nbuf_free(
-							ppdu_desc->mpdus[i]);
-						ppdu_desc->mpdus[i] = NULL;
-					}
-					qdf_mem_free(ppdu_desc->mpdus);
-					ppdu_desc->mpdus = NULL;
-					qdf_nbuf_free(ppdu_nbuf);
-				}
-			}
+			dp_peer_tid_queue_cleanup(peer);
 		}
 	}
 	qdf_spin_unlock_bh(&pdev->vdev_list_lock);
@@ -1106,9 +1377,9 @@
 
 	for (i = 0; i < MAX_PDEV_CNT; i++) {
 		pdev = soc->pdev_list[i];
-		if (!pdev) {
+		if (!pdev)
 			continue;
-		}
+
 		if (!pdev->tx_capture_enabled)
 			continue;
 
@@ -1301,6 +1572,48 @@
 	return QDF_STATUS_SUCCESS;
 }
 
+/**
+ * dp_ppdu_desc_debug_print(): Function to print ppdu_desc
+ * @ppdu_desc: ppdu desc pointer
+ * @usr_idx: user index
+ * @func: caller function name
+ * @line: caller function line number
+ *
+ * return: void
+ */
+void dp_ppdu_desc_debug_print(struct cdp_tx_completion_ppdu *ppdu_desc,
+			      uint8_t usr_idx, const char *func, uint32_t line)
+{
+	struct cdp_tx_completion_ppdu_user *user;
+	uint8_t num_users;
+
+	num_users = ppdu_desc->num_users;
+
+	QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_INFO_MED,
+		  "%s: %d PID: %d, BPID: %d SCHED: %d usr_idx: %d TLV_BITMAP[0x%x] num_users:%d",
+		  func, line,
+		  ppdu_desc->ppdu_id, ppdu_desc->bar_ppdu_id,
+		  ppdu_desc->sched_cmdid,
+		  usr_idx, ppdu_desc->tlv_bitmap, ppdu_desc->num_users);
+
+	user = &ppdu_desc->user[usr_idx];
+	QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_INFO_MED,
+		  "%s: %d P[%d] CS:%d S_SEQ: %d L_ENQ_SEQ:%d BA_SEQ:%d BA_SZ:%d M[TRI: %d, SUC: %d] ENQ[%x:%x:%x:%x] BA[%x:%x:%x:%x] F[%x:%x:%x:%x] tlv[0x%x]",
+		  func, line, user->peer_id,
+		  user->completion_status,
+		  user->start_seq, user->last_enq_seq,
+		  user->ba_seq_no, user->ba_size,
+		  user->mpdu_tried_ucast + user->mpdu_tried_mcast,
+		  user->mpdu_success,
+		  user->enq_bitmap[0], user->enq_bitmap[1],
+		  user->enq_bitmap[2], user->enq_bitmap[3],
+		  user->ba_bitmap[0], user->ba_bitmap[1],
+		  user->ba_bitmap[2], user->ba_bitmap[3],
+		  user->failed_bitmap[0], user->failed_bitmap[1],
+		  user->failed_bitmap[2], user->failed_bitmap[3],
+		  user->tlv_bitmap);
+}
+
 /*
  * dp_peer_tx_wds_addr_add() – Update WDS peer to include 4th address
  * @peer: Datapath peer
@@ -1330,6 +1643,7 @@
  * @nbuf: 802.11 frame
  * @ether_type: ethernet type
  * @src_addr: ether shost address
+ * @usr_idx: user index
  *
  */
 static uint32_t dp_tx_update_80211_wds_hdr(struct dp_pdev *pdev,
@@ -1337,15 +1651,18 @@
 					   void *data,
 					   qdf_nbuf_t nbuf,
 					   uint16_t ether_type,
-					   uint8_t *src_addr)
+					   uint8_t *src_addr,
+					   uint8_t usr_idx)
 {
 	struct cdp_tx_completion_ppdu *ppdu_desc;
+	struct cdp_tx_completion_ppdu_user *user;
 	uint32_t mpdu_buf_len, frame_size;
 	uint8_t *ptr_hdr;
 	uint16_t eth_type = qdf_htons(ether_type);
 	struct ieee80211_qosframe_addr4 *ptr_wh;
 
 	ppdu_desc = (struct cdp_tx_completion_ppdu *)data;
+	user = &ppdu_desc->user[usr_idx];
 
 	ptr_wh = &peer->tx_capture.tx_wifi_addr4_qos_hdr;
 
@@ -1361,15 +1678,15 @@
 		ptr_wh->i_dur[1] = (ppdu_desc->tx_duration & 0xFF00) >> 8;
 		ptr_wh->i_dur[0] = (ppdu_desc->tx_duration & 0xFF);
 
-		ptr_wh->i_qos[1] = (ppdu_desc->user[0].qos_ctrl & 0xFF00) >> 8;
-		ptr_wh->i_qos[0] = (ppdu_desc->user[0].qos_ctrl & 0xFF);
+		ptr_wh->i_qos[1] = (user->qos_ctrl & 0xFF00) >> 8;
+		ptr_wh->i_qos[0] = (user->qos_ctrl & 0xFF);
 		/* Update Addr 3 (SA) with SA derived from ether packet */
 		qdf_mem_copy(ptr_wh->i_addr3, src_addr, QDF_MAC_ADDR_SIZE);
 
 		peer->tx_capture.tx_wifi_ppdu_id = ppdu_desc->ppdu_id;
 	}
 
-	frame_size = (ppdu_desc->user[0].tid != DP_NON_QOS_TID) ?
+	frame_size = (user->tid != DP_NON_QOS_TID) ?
 		      sizeof(struct ieee80211_qosframe_addr4) :
 		      sizeof(struct ieee80211_frame_addr4);
 
@@ -1403,8 +1720,8 @@
 	return 0;
 }
 
-/*
- * dp_peer_tx_update_80211_hdr() – Update 80211 frame header to set QoS
+/**
+ * dp_tx_update_80211_hdr() – Update 80211 frame header to set QoS
  * related information if necessary
  * @pdev: Physical device reference
  * @peer: Datapath peer
@@ -1414,15 +1731,16 @@
  * @src_addr: ether shost address
  *
  */
-
 static uint32_t dp_tx_update_80211_hdr(struct dp_pdev *pdev,
 				       struct dp_peer *peer,
 				       void *data,
 				       qdf_nbuf_t nbuf,
 				       uint16_t ether_type,
-				       uint8_t *src_addr)
+				       uint8_t *src_addr,
+				       uint8_t usr_idx)
 {
 	struct cdp_tx_completion_ppdu *ppdu_desc;
+	struct cdp_tx_completion_ppdu_user *user;
 	uint32_t mpdu_buf_len, frame_size;
 	uint8_t *ptr_hdr;
 	uint16_t eth_type = qdf_htons(ether_type);
@@ -1430,6 +1748,7 @@
 	struct ieee80211_qosframe *ptr_wh;
 
 	ppdu_desc = (struct cdp_tx_completion_ppdu *)data;
+	user = &ppdu_desc->user[usr_idx];
 
 	ptr_wh = &peer->tx_capture.tx_wifi_qos_hdr;
 
@@ -1439,21 +1758,21 @@
 	 * mac address and duration
 	 */
 	if (ppdu_desc->ppdu_id != peer->tx_capture.tx_wifi_ppdu_id) {
-		ptr_wh->i_fc[1] = (ppdu_desc->frame_ctrl & 0xFF00) >> 8;
-		ptr_wh->i_fc[0] = (ppdu_desc->frame_ctrl & 0xFF);
+		ptr_wh->i_fc[1] = (user->frame_ctrl & 0xFF00) >> 8;
+		ptr_wh->i_fc[0] = (user->frame_ctrl & 0xFF);
 
 		ptr_wh->i_dur[1] = (ppdu_desc->tx_duration & 0xFF00) >> 8;
 		ptr_wh->i_dur[0] = (ppdu_desc->tx_duration & 0xFF);
 
-		ptr_wh->i_qos[1] = (ppdu_desc->user[0].qos_ctrl & 0xFF00) >> 8;
-		ptr_wh->i_qos[0] = (ppdu_desc->user[0].qos_ctrl & 0xFF);
+		ptr_wh->i_qos[1] = (user->qos_ctrl & 0xFF00) >> 8;
+		ptr_wh->i_qos[0] = (user->qos_ctrl & 0xFF);
 		/* Update Addr 3 (SA) with SA derived from ether packet */
 		qdf_mem_copy(ptr_wh->i_addr3, src_addr, QDF_MAC_ADDR_SIZE);
 
 		peer->tx_capture.tx_wifi_ppdu_id = ppdu_desc->ppdu_id;
 	}
 
-	frame_size = (ppdu_desc->user[0].tid != DP_NON_QOS_TID) ?
+	frame_size = (user->tid != DP_NON_QOS_TID) ?
 		      sizeof(struct ieee80211_qosframe) :
 		      sizeof(struct ieee80211_frame);
 
@@ -1500,7 +1819,7 @@
 dp_tx_mon_restitch_mpdu(struct dp_pdev *pdev, struct dp_peer *peer,
 			struct cdp_tx_completion_ppdu *ppdu_desc,
 			qdf_nbuf_queue_t *head_msdu,
-			qdf_nbuf_queue_t *mpdu_q)
+			qdf_nbuf_queue_t *mpdu_q, uint8_t usr_idx)
 {
 	qdf_nbuf_t curr_nbuf = NULL;
 	qdf_nbuf_t first_nbuf = NULL;
@@ -1515,6 +1834,11 @@
 	uint8_t first_msdu_not_seen = 1;
 	uint16_t ether_type = 0;
 	qdf_ether_header_t *eh = NULL;
+	size_t msdu_comp_info_sz;
+	size_t ether_hdr_sz;
+
+	if (qdf_nbuf_is_queue_empty(head_msdu))
+		return 0;
 
 	curr_nbuf = qdf_nbuf_queue_remove(head_msdu);
 
@@ -1529,9 +1853,14 @@
 					   sizeof(struct msdu_completion_info));
 		ether_type = eh->ether_type;
 
+		msdu_comp_info_sz = sizeof(struct msdu_completion_info);
 		/* pull msdu_completion_info added in pre header */
-		qdf_nbuf_pull_head(curr_nbuf,
-			sizeof(struct msdu_completion_info));
+		if (NULL == qdf_nbuf_pull_head(curr_nbuf, msdu_comp_info_sz)) {
+			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+				  QDF_TRACE_LEVEL_FATAL,
+				  " No Head space to pull !!\n");
+			qdf_assert_always(0);
+		}
 
 		if ((qdf_likely((peer->vdev->tx_encap_type !=
 				 htt_cmn_pkt_type_raw))) &&
@@ -1544,16 +1873,22 @@
 			frag_list_sum_len = 0;
 			first_msdu_not_seen = 0;
 
+			ether_hdr_sz = sizeof(qdf_ether_header_t);
 			/* pull ethernet header from first MSDU alone */
-			qdf_nbuf_pull_head(curr_nbuf,
-					   sizeof(qdf_ether_header_t));
+			if (NULL == qdf_nbuf_pull_head(curr_nbuf,
+						       ether_hdr_sz)) {
+				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+					  QDF_TRACE_LEVEL_FATAL,
+					  " No Head space to pull !!\n");
+				qdf_assert_always(0);
+			}
 
 			/* update first buffer to previous buffer */
 			prev_nbuf = curr_nbuf;
 
 		} else if (first_msdu && !first_msdu_not_seen) {
 			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-				  QDF_TRACE_LEVEL_FATAL,
+				  QDF_TRACE_LEVEL_ERROR,
 				  "!!!!! NO LAST MSDU\n");
 			/*
 			 * no last msdu in a mpdu
@@ -1567,7 +1902,7 @@
 			goto free_ppdu_desc_mpdu_q;
 		} else if (!first_msdu && first_msdu_not_seen) {
 			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-				  QDF_TRACE_LEVEL_FATAL,
+				  QDF_TRACE_LEVEL_ERROR,
 				  "!!!!! NO FIRST MSDU\n");
 			/*
 			 * no first msdu in a mpdu
@@ -1598,22 +1933,25 @@
 
 			if (!mpdu_nbuf) {
 				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-					  QDF_TRACE_LEVEL_FATAL,
+					  QDF_TRACE_LEVEL_ERROR,
 					  "MPDU head allocation failed !!!");
 				goto free_ppdu_desc_mpdu_q;
 			}
+
 			if (((ppdu_desc->frame_ctrl & IEEE80211_FC1_DIR_MASK) &&
 			     (IEEE80211_FC1_DIR_TODS |
 			      IEEE80211_FC1_DIR_FROMDS))) {
 				dp_tx_update_80211_wds_hdr(pdev, peer,
 							   ppdu_desc, mpdu_nbuf,
 							   ether_type,
-							   eh->ether_shost);
+							   eh->ether_shost,
+							   usr_idx);
 			} else {
 				dp_tx_update_80211_hdr(pdev, peer,
 						       ppdu_desc, mpdu_nbuf,
 						       ether_type,
-						       eh->ether_shost);
+						       eh->ether_shost,
+						       usr_idx);
 			}
 
 			/*
@@ -1640,7 +1978,7 @@
 		if (!curr_nbuf) {
 			/* msdu missed in list */
 			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-				  QDF_TRACE_LEVEL_FATAL,
+				  QDF_TRACE_LEVEL_ERROR,
 				  "!!!! WAITING for msdu but list empty !!!!");
 
 			/* for incomplete list, free up the queue */
@@ -1697,7 +2035,6 @@
 	struct dp_tx_tid *tx_tid  = NULL;
 	uint32_t msdu_ppdu_id;
 	qdf_nbuf_t curr_msdu = NULL;
-	qdf_nbuf_t prev_msdu = NULL;
 	struct msdu_completion_info *ptr_msdu_info = NULL;
 	uint32_t wbm_tsf;
 	uint32_t matched = 0;
@@ -1718,14 +2055,21 @@
 		return 0;
 
 	/* lock here */
-	qdf_spin_lock_bh(&tx_tid->tid_lock);
+	qdf_spin_lock_bh(&tx_tid->tasklet_tid_lock);
+	qdf_nbuf_queue_append(&tx_tid->defer_msdu_q, &tx_tid->msdu_comp_q);
+	qdf_nbuf_queue_init(&tx_tid->msdu_comp_q);
+	/* unlock here */
+	qdf_spin_unlock_bh(&tx_tid->tasklet_tid_lock);
 
-	if (qdf_nbuf_is_queue_empty(&tx_tid->msdu_comp_q)) {
-		qdf_spin_unlock_bh(&tx_tid->tid_lock);
+	/* lock here */
+	qdf_spin_lock(&tx_tid->tid_lock);
+
+	if (qdf_nbuf_is_queue_empty(&tx_tid->defer_msdu_q)) {
+		qdf_spin_unlock(&tx_tid->tid_lock);
 		return 0;
 	}
 
-	curr_msdu = qdf_nbuf_queue_first(&tx_tid->msdu_comp_q);
+	curr_msdu = qdf_nbuf_queue_first(&tx_tid->defer_msdu_q);
 
 	while (curr_msdu) {
 		if (qdf_nbuf_queue_len(head) == num_msdu) {
@@ -1743,11 +2087,11 @@
 		if ((ptr_msdu_info->status == HAL_TX_TQM_RR_REM_CMD_TX) ||
 		    (ptr_msdu_info->status == HAL_TX_TQM_RR_REM_CMD_AGED)) {
 			/* Frames removed due to excessive retries */
-			qdf_nbuf_queue_remove(&tx_tid->msdu_comp_q);
+			qdf_nbuf_queue_remove(&tx_tid->defer_msdu_q);
 			qdf_nbuf_queue_add(head_xretries, curr_msdu);
+			dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_XRETRY, 1);
 			curr_msdu = qdf_nbuf_queue_first(
-				&tx_tid->msdu_comp_q);
-			prev_msdu = NULL;
+				&tx_tid->defer_msdu_q);
 			continue;
 		}
 
@@ -1761,55 +2105,97 @@
 
 		if (wbm_tsf && (wbm_tsf < start_tsf)) {
 			/* remove the aged packet */
-			qdf_nbuf_queue_remove(&tx_tid->msdu_comp_q);
+			qdf_nbuf_queue_remove(&tx_tid->defer_msdu_q);
 			qdf_nbuf_free(curr_msdu);
+			dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_DROP, 1);
 
 			curr_msdu = qdf_nbuf_queue_first(
-					&tx_tid->msdu_comp_q);
-			prev_msdu = NULL;
+					&tx_tid->defer_msdu_q);
 			continue;
 		}
+
 		if (msdu_ppdu_id == ppdu_id) {
-			if (qdf_likely(!prev_msdu)) {
-				/* remove head */
-				qdf_nbuf_queue_remove(&tx_tid->msdu_comp_q);
+			/* remove head */
+			qdf_nbuf_queue_remove(&tx_tid->defer_msdu_q);
 
-				/* add msdu to head queue */
-				qdf_nbuf_queue_add(head, curr_msdu);
-				/* get next msdu from msdu_comp_q */
-				curr_msdu = qdf_nbuf_queue_first(
-						&tx_tid->msdu_comp_q);
-				continue;
-			} else {
-				/* update prev_msdu next to current msdu next */
-				prev_msdu->next = curr_msdu->next;
-				/* set current msdu next as NULL */
-				curr_msdu->next = NULL;
-				/* decrement length */
-				((qdf_nbuf_queue_t *)(
-					&tx_tid->msdu_comp_q))->qlen--;
-
-				/* add msdu to head queue */
-				qdf_nbuf_queue_add(head, curr_msdu);
-				/* set previous msdu to current msdu */
-				curr_msdu = prev_msdu->next;
-				continue;
-			}
+			/* add msdu to head queue */
+			qdf_nbuf_queue_add(head, curr_msdu);
+			dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_DEQ,
+						    1);
+			/* get next msdu from defer_msdu_q */
+			curr_msdu = qdf_nbuf_queue_first(&tx_tid->defer_msdu_q);
+			continue;
+		} else {
+			/*
+			 * at this point wbm_tsf is inbetween start_tsf and
+			 * end tsf but there is a mismatch in ppdu_id
+			 */
+			break;
 		}
 
-		prev_msdu = curr_msdu;
-		curr_msdu = prev_msdu->next;
 	}
 
-	qdf_spin_unlock_bh(&tx_tid->tid_lock);
+	qdf_spin_unlock(&tx_tid->tid_lock);
 
 	return matched;
 }
 
 /**
+ * dp_tx_cap_nbuf_list_get_ref() - get nbuf_list reference
+ * @ptr_nbuf_list: dp_tx_cap_nbuf_list list
+ *
+ * Return: reference count
+ */
+static inline uint8_t
+dp_tx_cap_nbuf_list_get_ref(struct dp_tx_cap_nbuf_list *ptr_nbuf_list)
+{
+	return ptr_nbuf_list->ref_cnt;
+}
+
+/**
+ * dp_tx_cap_nbuf_list_dec_ref() - dec nbuf_list reference
+ * @ptr_nbuf_list: dp_tx_cap_nbuf_list list
+ *
+ * Return: none
+ */
+static inline
+void dp_tx_cap_nbuf_list_dec_ref(struct dp_tx_cap_nbuf_list *ptr_nbuf_list)
+{
+	ptr_nbuf_list->ref_cnt--;
+	if (!ptr_nbuf_list->ref_cnt)
+		ptr_nbuf_list->nbuf_ppdu = NULL;
+}
+
+/**
+ * dp_tx_cap_nbuf_list_inc_ref() - inc nbuf_list reference
+ * @ptr_nbuf_list: dp_tx_cap_nbuf_list list
+ *
+ * Return: none
+ */
+static inline
+void dp_tx_cap_nbuf_list_inc_ref(struct dp_tx_cap_nbuf_list *ptr_nbuf_list)
+{
+	ptr_nbuf_list->ref_cnt++;
+}
+
+/**
+ * dp_tx_cap_nbuf_list_update_ref() - update nbuf_list reference
+ * @ptr_nbuf_list: dp_tx_cap_nbuf_list list
+ * @ref_cnt: reference count
+ *
+ * Return: none
+ */
+static inline void
+dp_tx_cap_nbuf_list_update_ref(struct dp_tx_cap_nbuf_list *ptr_nbuf_list,
+			       uint8_t ref_cnt)
+{
+	ptr_nbuf_list->ref_cnt = ref_cnt;
+}
+
+/**
  * get_mpdu_clone_from_next_ppdu(): Function to clone missing mpdu from
  * next ppdu
- * @nbuf_ppdu_desc_list: nbuf list
+ * @nbuf_ppdu_list: nbuf list
  * @ppdu_desc_cnt: ppdu_desc_cnt
  * @missed_seq_no:
  * @ppdu_id: ppdu_id
@@ -1817,29 +2203,40 @@
  *
  * return: void
  */
-static
-qdf_nbuf_t get_mpdu_clone_from_next_ppdu(qdf_nbuf_t nbuf_ppdu_desc_list[],
-					 uint32_t ppdu_desc_cnt,
-					 uint16_t missed_seq_no,
-					 uint16_t peer_id, uint32_t ppdu_id)
+static qdf_nbuf_t
+get_mpdu_clone_from_next_ppdu(struct dp_tx_cap_nbuf_list nbuf_list[],
+			      uint32_t ppdu_desc_cnt,
+			      uint16_t missed_seq_no,
+			      uint16_t peer_id, uint32_t ppdu_id,
+			      uint8_t usr_idx)
 {
+	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
+	struct cdp_tx_completion_ppdu_user *user;
+	qdf_nbuf_t mpdu = NULL;
+	struct dp_tx_cap_nbuf_list *ptr_nbuf_list;
+	qdf_nbuf_t nbuf_ppdu;
 	uint32_t i = 0;
 	uint32_t found = 0;
 	uint32_t seq_no = 0;
-	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
-	qdf_nbuf_t mpdu = NULL;
+	uint32_t mpdu_q_len;
 
 	for (i = 1; i < ppdu_desc_cnt; i++) {
+		ptr_nbuf_list = &nbuf_list[i];
+		nbuf_ppdu = ptr_nbuf_list->nbuf_ppdu;
 		ppdu_desc = (struct cdp_tx_completion_ppdu *)
-				qdf_nbuf_data(nbuf_ppdu_desc_list[i]);
+				qdf_nbuf_data(nbuf_ppdu);
+		user = &ppdu_desc->user[usr_idx];
+
+		if (user->skip == 1)
+			continue;
 
 		/* check if seq number is between the range */
-		if ((peer_id == ppdu_desc->user[0].peer_id) &&
-		    ((missed_seq_no >= ppdu_desc->user[0].start_seq) &&
-		    (missed_seq_no <= ppdu_desc->user[0].last_enq_seq))) {
-			seq_no = ppdu_desc->user[0].start_seq;
-			if (SEQ_BIT(ppdu_desc->user[0].failed_bitmap,
-			    (missed_seq_no - seq_no))) {
+		if ((peer_id == user->peer_id) &&
+		    ((missed_seq_no >= user->start_seq) &&
+		    (missed_seq_no <= user->last_enq_seq))) {
+			seq_no = user->start_seq;
+			if (SEQ_BIT(user->failed_bitmap,
+				    (missed_seq_no - seq_no))) {
 				found = 1;
 				break;
 			}
@@ -1849,18 +2246,20 @@
 	if (found == 0) {
 		/* mpdu not found in sched cmd id */
 		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_DEBUG,
-			  "%s: missed seq_no[%d] ppdu_id[%d] [%d] not found!!!",
-			  __func__, missed_seq_no, ppdu_id, ppdu_desc_cnt);
+			  "%s: peer_id[%d] missed seq_no[%d] ppdu_id[%d] [%d] not found!!!",
+			  __func__, peer_id,
+			  missed_seq_no, ppdu_id, ppdu_desc_cnt);
 		return NULL;
 	}
 
 	QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_DEBUG,
-		  "%s: seq_no[%d] missed ppdu_id[%d] m[%d] found in ppdu_id[%d]!!",
-		  __func__,
+		  "%s: peer_id[%d] seq_no[%d] missed ppdu_id[%d] m[%d] found in ppdu_id[%d]!!",
+		  __func__, peer_id,
 		  missed_seq_no, ppdu_id,
 		  (missed_seq_no - seq_no), ppdu_desc->ppdu_id);
 
-	mpdu = qdf_nbuf_queue_first(&ppdu_desc->mpdu_q);
+	mpdu = qdf_nbuf_queue_first(&ppdu_desc->user[usr_idx].mpdu_q);
+	mpdu_q_len = qdf_nbuf_queue_len(&ppdu_desc->user[usr_idx].mpdu_q);
 	if (!mpdu) {
 		/* bitmap shows it found  sequence number, but
 		 * MPDU not found in PPDU
@@ -1868,6 +2267,9 @@
 		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, QDF_TRACE_LEVEL_ERROR,
 			  "%s: missed seq_no[%d] ppdu_id[%d] [%d] found but queue empty!!!",
 			  __func__, missed_seq_no, ppdu_id, ppdu_desc_cnt);
+		if (mpdu_q_len)
+			qdf_assert_always(0);
+
 		return NULL;
 	}
 
@@ -1884,6 +2286,9 @@
 		}
 	}
 
+	if (!mpdu)
+		return NULL;
+
 	return qdf_nbuf_copy_expand_fraglist(mpdu, MAX_MONITOR_HEADER, 0);
 }
 
@@ -1981,11 +2386,11 @@
 					 IEEE80211_FC0_TYPE_CTL);
 	break;
 	case HTT_STATS_FTYPE_SGEN_MU_TRIG:
+	case HTT_STATS_FTYPE_SGEN_MU_BAR:
 		ppdu_desc->frame_ctrl = (IEEE80211_FC0_SUBTYPE_TRIGGER |
 					 IEEE80211_FC0_TYPE_CTL);
 	break;
 	case HTT_STATS_FTYPE_SGEN_BAR:
-	case HTT_STATS_FTYPE_SGEN_MU_BAR:
 		ppdu_desc->frame_ctrl = (IEEE80211_FC0_SUBTYPE_BAR |
 					  IEEE80211_FC0_TYPE_CTL);
 	break;
@@ -1997,17 +2402,18 @@
  * to upper layer if complete
  * @pdev: DP pdev handle
  * @desc: cdp tx completion ppdu desc
+ * @usr_idx: user index
  *
  * return: status
  */
 static inline
 QDF_STATUS dp_send_dummy_mpdu_info_to_stack(struct dp_pdev *pdev,
-					  void *desc)
+					    void *desc, uint8_t usr_idx)
 {
 	struct dp_peer *peer;
 	struct dp_vdev *vdev = NULL;
 	struct cdp_tx_completion_ppdu *ppdu_desc = desc;
-	struct cdp_tx_completion_ppdu_user *user = &ppdu_desc->user[0];
+	struct cdp_tx_completion_ppdu_user *user = &ppdu_desc->user[usr_idx];
 	struct ieee80211_ctlframe_addr2 *wh_min;
 	uint16_t frame_ctrl_le, duration_le;
 	struct cdp_tx_indication_info tx_capture_info;
@@ -2036,7 +2442,7 @@
 	/* update cdp_tx_indication_mpdu_info */
 	dp_tx_update_user_mpdu_info(ppdu_desc->bar_ppdu_id,
 				    &tx_capture_info.mpdu_info,
-				    &ppdu_desc->user[0]);
+				    user);
 	tx_capture_info.ppdu_desc = ppdu_desc;
 
 	mpdu_info->ppdu_id = ppdu_desc->ppdu_id;
@@ -2136,7 +2542,8 @@
  */
 static
 void dp_send_dummy_rts_cts_frame(struct dp_pdev *pdev,
-				 struct cdp_tx_completion_ppdu *cur_ppdu_desc)
+				 struct cdp_tx_completion_ppdu *cur_ppdu_desc,
+				 uint8_t usr_idx)
 {
 	struct cdp_tx_completion_ppdu *ppdu_desc;
 	struct dp_pdev_tx_capture *ptr_tx_cap;
@@ -2177,7 +2584,7 @@
 			     &cur_ppdu_desc->user[0].mac_addr,
 			     QDF_MAC_ADDR_SIZE);
 
-		dp_send_dummy_mpdu_info_to_stack(pdev, ppdu_desc);
+		dp_send_dummy_mpdu_info_to_stack(pdev, ppdu_desc, usr_idx);
 	}
 
 	if ((rts_send && cur_ppdu_desc->rts_success) ||
@@ -2213,7 +2620,7 @@
 			qdf_mem_copy(&ppdu_desc->user[0].mac_addr,
 				     vdev->mac_addr.raw, QDF_MAC_ADDR_SIZE);
 
-		dp_send_dummy_mpdu_info_to_stack(pdev, ppdu_desc);
+		dp_send_dummy_mpdu_info_to_stack(pdev, ppdu_desc, usr_idx);
 	}
 }
 
@@ -2255,27 +2662,43 @@
 		dp_peer_unref_del_find_by_id(peer);
 	}
 
-	dp_send_dummy_mpdu_info_to_stack(pdev, ppdu_desc);
+	dp_send_dummy_mpdu_info_to_stack(pdev, ppdu_desc, 0);
 }
 
 /**
  * dp_send_data_to_stack(): Function to deliver mpdu info to stack
  * to upper layer
  * @pdev: DP pdev handle
- * @nbuf_ppdu_desc_list: ppdu_desc_list per sched cmd id
+ * @nbuf_ppdu_list: ppdu_desc_list per sched cmd id
  * @ppdu_desc_cnt: number of ppdu_desc_cnt
  *
  * return: status
  */
 static
 void dp_send_data_to_stack(struct dp_pdev *pdev,
-			   struct cdp_tx_completion_ppdu *ppdu_desc)
+			   struct cdp_tx_completion_ppdu *ppdu_desc,
+			   uint8_t usr_idx)
 {
+	struct cdp_tx_completion_ppdu_user *user = NULL;
 	struct cdp_tx_indication_info tx_capture_info;
 	struct cdp_tx_indication_mpdu_info *mpdu_info;
 	int i;
 	uint32_t seq_no, start_seq;
-	uint32_t ppdu_id = ppdu_desc->ppdu_id;
+	uint32_t ppdu_id;
+	uint32_t mpdu_tried;
+	uint32_t mpdu_enq = 0;
+	struct dp_peer *peer;
+
+	if (!ppdu_desc)
+		return;
+
+	ppdu_id = ppdu_desc->ppdu_id;
+	user = &ppdu_desc->user[usr_idx];
+
+	peer = dp_tx_cap_peer_find_by_id(pdev->soc, user->peer_id);
+	if (!peer) {
+		return;
+	}
 
 	qdf_mem_set(&tx_capture_info,
 		    sizeof(struct cdp_tx_indication_info),
@@ -2283,6 +2706,7 @@
 
 	mpdu_info = &tx_capture_info.mpdu_info;
 
+	mpdu_info->usr_idx = usr_idx;
 	mpdu_info->channel = ppdu_desc->channel;
 	mpdu_info->frame_type = ppdu_desc->frame_type;
 	mpdu_info->ppdu_start_timestamp =
@@ -2300,26 +2724,42 @@
 	/* update cdp_tx_indication_mpdu_info */
 	dp_tx_update_user_mpdu_info(ppdu_id,
 				    &tx_capture_info.mpdu_info,
-				    &ppdu_desc->user[0]);
+				    user);
 	tx_capture_info.ppdu_desc = ppdu_desc;
 	tx_capture_info.mpdu_info.channel_num = pdev->operating_channel.num;
 
 	if (ppdu_desc->mprot_type)
-		dp_send_dummy_rts_cts_frame(pdev, ppdu_desc);
+		dp_send_dummy_rts_cts_frame(pdev, ppdu_desc, usr_idx);
 
-	start_seq = ppdu_desc->user[0].start_seq;
-	for (i = 0; i < ppdu_desc->user[0].ba_size; i++) {
-		if (qdf_likely(ppdu_desc->user[0].tid !=
-		    DP_NON_QOS_TID) &&
-		    !(SEQ_BIT(ppdu_desc->user[0].enq_bitmap, i))) {
+	start_seq = user->start_seq;
+
+	if (!user->mpdus)
+		goto return_send_to_stack;
+
+	mpdu_tried = user->mpdu_tried_ucast + user->mpdu_tried_mcast;
+
+	for (i = 0; i < CDP_BA_256_BIT_MAP_SIZE_DWORDS; i++)
+		mpdu_enq += get_number_of_1s(user->enq_bitmap[i]);
+
+	if (mpdu_tried > mpdu_enq)
+		dp_ppdu_desc_debug_print(ppdu_desc, usr_idx,
+					 __func__, __LINE__);
+
+	for (i = 0; i < user->ba_size && mpdu_tried; i++) {
+		if (qdf_likely(user->tid != DP_NON_QOS_TID) &&
+		    !(SEQ_BIT(user->enq_bitmap, i))) {
 			continue;
 		}
+
+		mpdu_tried--;
+
 		seq_no = start_seq + i;
-		if (!ppdu_desc->mpdus[i])
+		if (!user->mpdus[i])
 			continue;
 
-		tx_capture_info.mpdu_nbuf = ppdu_desc->mpdus[i];
-		ppdu_desc->mpdus[i] = NULL;
+		tx_capture_info.mpdu_nbuf = user->mpdus[i];
+		dp_tx_cap_stats_mpdu_update(peer, PEER_MPDU_TO_STACK, 1);
+		user->mpdus[i] = NULL;
 		mpdu_info->seq_no = seq_no;
 		dp_tx_update_sequence_number(tx_capture_info.mpdu_nbuf, seq_no);
 		/*
@@ -2337,50 +2777,111 @@
 	}
 
 	if (ppdu_desc->resp_type == HTT_PPDU_STATS_ACK_EXPECTED_E &&
-	    ppdu_desc->user[0].completion_status ==
+	    ppdu_desc->user[usr_idx].completion_status ==
 	    HTT_PPDU_STATS_USER_STATUS_OK)
 		dp_gen_ack_rx_frame(pdev, &tx_capture_info);
+
+return_send_to_stack:
+	dp_tx_cap_peer_unref_del(peer);
+	return;
 }
 
-static qdf_nbuf_t dp_tx_mon_get_next_mpdu(
-	struct cdp_tx_completion_ppdu *xretry_ppdu,
-	qdf_nbuf_t mpdu_nbuf)
+/**
+ * dp_ppdu_desc_free(): Function to free ppdu_desc and stored queue
+ * @ptr_nbuf_list: pointer to ptr_nbuf_list
+ * @usr_idx: user index
+ *
+ * return: void
+ */
+static void dp_ppdu_desc_free(struct dp_tx_cap_nbuf_list *ptr_nbuf_list,
+			      uint8_t usr_idx)
+{
+	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
+	struct cdp_tx_completion_ppdu_user *user = NULL;
+	qdf_nbuf_t tmp_nbuf;
+
+	if (!ptr_nbuf_list->nbuf_ppdu ||
+	    !dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list))
+		return;
+
+	tmp_nbuf = ptr_nbuf_list->nbuf_ppdu;
+
+	if (tmp_nbuf) {
+		ppdu_desc = (struct cdp_tx_completion_ppdu *)
+				qdf_nbuf_data(tmp_nbuf);
+		user = &ppdu_desc->user[usr_idx];
+
+		dp_ppdu_queue_free(tmp_nbuf, usr_idx);
+		dp_tx_cap_nbuf_list_dec_ref(ptr_nbuf_list);
+		qdf_nbuf_free(tmp_nbuf);
+	}
+}
+
+/**
+ * dp_ppdu_desc_free_all(): Function to free all user in a ppdu_desc and
+ * its stored queue
+ * @ptr_nbuf_list: pointer to ptr_nbuf_list
+ * @max_users: maximum number of users
+ *
+ * return: void
+ */
+static void dp_ppdu_desc_free_all(struct dp_tx_cap_nbuf_list *ptr_nbuf_list,
+				  uint8_t max_users)
+{
+	uint8_t i = 0;
+
+	for (i = 0; i < max_users; i++)
+		dp_ppdu_desc_free(ptr_nbuf_list, i);
+}
+
+/**
+ * dp_tx_mon_get_next_mpdu(): get next mpdu from retry queue.
+ * @xretry_user: pointer to ppdu_desc user.
+ * @mpdu_nbuf: mpdu nbuf
+ *
+ * return: qdf_nbuf_t
+ */
+static qdf_nbuf_t
+dp_tx_mon_get_next_mpdu(struct cdp_tx_completion_ppdu_user *xretry_user,
+			qdf_nbuf_t mpdu_nbuf)
 {
 	qdf_nbuf_t next_nbuf = NULL;
 	qdf_nbuf_queue_t temp_xretries;
 
-	if (mpdu_nbuf != qdf_nbuf_queue_first(&xretry_ppdu->mpdu_q)) {
+	if (mpdu_nbuf != qdf_nbuf_queue_first(&xretry_user->mpdu_q)) {
+		qdf_err(" mpdu_nbuf is not the head");
 		next_nbuf = qdf_nbuf_queue_next(mpdu_nbuf);
 		/* Initialize temp list */
 		qdf_nbuf_queue_init(&temp_xretries);
 		/* Move entries into temp list till the mpdu_nbuf is found */
-		while ((qdf_nbuf_queue_first(&xretry_ppdu->mpdu_q)) &&
+		while ((qdf_nbuf_queue_first(&xretry_user->mpdu_q)) &&
 		       (mpdu_nbuf !=
-				qdf_nbuf_queue_first(&xretry_ppdu->mpdu_q))) {
+				qdf_nbuf_queue_first(&xretry_user->mpdu_q))) {
 			qdf_nbuf_queue_add(&temp_xretries,
-				qdf_nbuf_queue_remove(&xretry_ppdu->mpdu_q));
+				qdf_nbuf_queue_remove(&xretry_user->mpdu_q));
 		}
-		if ((qdf_nbuf_queue_first(&xretry_ppdu->mpdu_q)) &&
-		    (mpdu_nbuf == qdf_nbuf_queue_first(&xretry_ppdu->mpdu_q))) {
+		if ((qdf_nbuf_queue_first(&xretry_user->mpdu_q)) &&
+		    (mpdu_nbuf == qdf_nbuf_queue_first(&xretry_user->mpdu_q))) {
 			/* Remove mpdu_nbuf from queue */
-			qdf_nbuf_queue_remove(&xretry_ppdu->mpdu_q);
+			qdf_nbuf_queue_remove(&xretry_user->mpdu_q);
 			/* Add remaining nbufs into temp queue */
 			qdf_nbuf_queue_append(&temp_xretries,
-					      &xretry_ppdu->mpdu_q);
-			/* Reinit xretry_ppdu->mpdu_q */
-			qdf_nbuf_queue_init(&xretry_ppdu->mpdu_q);
+					      &xretry_user->mpdu_q);
+			/* Reinit xretry_user->mpdu_q */
+			qdf_nbuf_queue_init(&xretry_user->mpdu_q);
 			/* append all the entries into original queue */
-			qdf_nbuf_queue_append(&xretry_ppdu->mpdu_q,
+			qdf_nbuf_queue_append(&xretry_user->mpdu_q,
 					      &temp_xretries);
 		} else {
 			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
 				  QDF_TRACE_LEVEL_FATAL,
 				  "%s: This is buggy scenario, did not find nbuf in queue ",
 				  __func__);
+			qdf_assert_always(0);
 		}
 	} else {
-		qdf_nbuf_queue_remove(&xretry_ppdu->mpdu_q);
-		next_nbuf = qdf_nbuf_queue_first(&xretry_ppdu->mpdu_q);
+		qdf_nbuf_queue_remove(&xretry_user->mpdu_q);
+		next_nbuf = qdf_nbuf_queue_first(&xretry_user->mpdu_q);
 	}
 
 	return next_nbuf;
@@ -2393,44 +2894,57 @@
 	struct dp_tx_tid *tx_tid = &peer->tx_capture.tx_tid[tid];
 	struct cdp_tx_completion_ppdu *ppdu_desc;
 	struct cdp_tx_completion_ppdu *xretry_ppdu;
+	struct cdp_tx_completion_ppdu_user *user = NULL;
+	struct cdp_tx_completion_ppdu_user *xretry_user = NULL;
 	qdf_nbuf_t ppdu_nbuf;
 	qdf_nbuf_t mpdu_nbuf;
 	uint32_t mpdu_tried = 0;
 	int i;
 	uint32_t seq_no;
+	uint8_t usr_idx = 0;
 
 	xretry_ppdu = &tx_tid->xretry_ppdu;
+	xretry_user = &xretry_ppdu->user[0];
 
 	if (qdf_nbuf_is_queue_empty(&tx_tid->pending_ppdu_q)) {
-		qdf_nbuf_queue_free(&xretry_ppdu->mpdu_q);
+		qdf_nbuf_queue_free(&xretry_user->mpdu_q);
 		return;
 	}
 
-	if (qdf_nbuf_is_queue_empty(&xretry_ppdu->mpdu_q))
+	if (qdf_nbuf_is_queue_empty(&xretry_user->mpdu_q))
 		return;
 
 	ppdu_nbuf = qdf_nbuf_queue_first(&tx_tid->pending_ppdu_q);
+
 	while (ppdu_nbuf) {
 		struct msdu_completion_info *ptr_msdu_info = NULL;
 
 		ppdu_desc = (struct cdp_tx_completion_ppdu *)
 			qdf_nbuf_data(ppdu_nbuf);
-		if (ppdu_desc->pending_retries) {
-			uint32_t start_seq = ppdu_desc->user[0].start_seq;
 
-			mpdu_tried = ppdu_desc->user[0].mpdu_tried_ucast +
-			ppdu_desc->user[0].mpdu_tried_mcast;
-			mpdu_nbuf = qdf_nbuf_queue_first(&xretry_ppdu->mpdu_q);
-			for (i = 0; (i < ppdu_desc->user[0].ba_size) &&
-				(mpdu_tried > 0) && (mpdu_nbuf); i++) {
-				if (!(SEQ_BIT(ppdu_desc->user[0].enq_bitmap,
-				    i)))
+		usr_idx = dp_tx_find_usr_idx_from_peer_id(ppdu_desc,
+							  peer->peer_ids[0]);
+
+		user = &ppdu_desc->user[usr_idx];
+
+		if (user->pending_retries) {
+			uint32_t start_seq = user->start_seq;
+
+			mpdu_tried = user->mpdu_tried_ucast +
+				     user->mpdu_tried_mcast;
+			mpdu_nbuf = qdf_nbuf_queue_first(&xretry_user->mpdu_q);
+
+			for (i = 0;
+			     (i < user->ba_size) &&
+			     (mpdu_tried > 0) && mpdu_nbuf;
+			     i++) {
+				if (!(SEQ_BIT(user->enq_bitmap, i)))
 					continue;
 				mpdu_tried--;
 				/* missed seq number */
 				seq_no = start_seq + i;
 
-				if (SEQ_BIT(ppdu_desc->user[0].failed_bitmap, i))
+				if (SEQ_BIT(user->failed_bitmap, i))
 					continue;
 				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
 					  QDF_TRACE_LEVEL_INFO,
@@ -2443,38 +2957,40 @@
 					(sizeof(struct msdu_completion_info) +
 					sizeof(qdf_ether_header_t)));
 				ptr_msdu_info->transmit_cnt--;
-				SEQ_SEG(ppdu_desc->user[0].failed_bitmap, i) |=
-				SEQ_SEG_MSK(ppdu_desc->user[0].failed_bitmap[0],
-					    i);
-				ppdu_desc->pending_retries--;
+				SEQ_SEG(user->failed_bitmap, i) |=
+				SEQ_SEG_MSK(user->failed_bitmap[0], i);
+				user->pending_retries--;
 				if (ptr_msdu_info->transmit_cnt == 0) {
-					ppdu_desc->mpdus[seq_no - start_seq] =
-							mpdu_nbuf;
+					user->mpdus[seq_no - start_seq] =
+						mpdu_nbuf;
+					dp_tx_cap_stats_mpdu_update(peer,
+							PEER_MPDU_ARR, 1);
 					/*
-					 * This API removes mpdu_nbuf from q and
-					 * returns next mpdu from the queue
+					 * This API removes mpdu_nbuf from q
+					 * and returns next mpdu from the queue
 					 */
 					mpdu_nbuf = dp_tx_mon_get_next_mpdu(
-							xretry_ppdu, mpdu_nbuf);
+							xretry_user, mpdu_nbuf);
 				} else {
-					ppdu_desc->mpdus[seq_no - start_seq] =
+					user->mpdus[seq_no - start_seq] =
 					qdf_nbuf_copy_expand_fraglist(
 						mpdu_nbuf,
 						MAX_MONITOR_HEADER, 0);
+					dp_tx_cap_stats_mpdu_update(peer,
+							PEER_MPDU_CLONE, 1);
 					mpdu_nbuf =
 						qdf_nbuf_queue_next(mpdu_nbuf);
 				}
 			}
 		}
 
-		if ((ppdu_desc->pending_retries == 0) && (ppdu_nbuf ==
-			qdf_nbuf_queue_first(&tx_tid->pending_ppdu_q))) {
+		if ((user->pending_retries == 0) &&
+		    (ppdu_nbuf ==
+		     qdf_nbuf_queue_first(&tx_tid->pending_ppdu_q))) {
 			qdf_nbuf_queue_remove(&tx_tid->pending_ppdu_q);
 			/* Deliver PPDU */
-			dp_send_data_to_stack(pdev, ppdu_desc);
-			qdf_nbuf_queue_free(&ppdu_desc->mpdu_q);
-			qdf_mem_free(ppdu_desc->mpdus);
-			ppdu_desc->mpdus = NULL;
+			dp_send_data_to_stack(pdev, ppdu_desc, usr_idx);
+			dp_ppdu_queue_free(ppdu_nbuf, usr_idx);
 			qdf_nbuf_free(ppdu_nbuf);
 			ppdu_nbuf = qdf_nbuf_queue_first(
 				&tx_tid->pending_ppdu_q);
@@ -2482,205 +2998,360 @@
 			ppdu_nbuf = qdf_nbuf_queue_next(ppdu_nbuf);
 		}
 	}
-	qdf_nbuf_queue_free(&xretry_ppdu->mpdu_q);
+	qdf_nbuf_queue_free(&xretry_user->mpdu_q);
+}
+
+static
+struct cdp_tx_completion_ppdu *
+check_subseq_ppdu_to_pending_q(struct dp_tx_cap_nbuf_list nbuf_ppdu_list[],
+			       uint32_t ppdu_desc_cnt,
+			       uint32_t *ppdu_cnt,
+			       qdf_nbuf_queue_t *head_ppdu,
+			       uint32_t peer_id, uint32_t cur_last_seq,
+			       bool last_pend_ppdu)
+{
+	struct cdp_tx_completion_ppdu *next_ppdu = NULL;
+	struct cdp_tx_completion_ppdu_user *next_user;
+	struct dp_tx_cap_nbuf_list *ptr_nbuf_list = NULL;
+	uint8_t cur_usr_idx;
+
+	while (*ppdu_cnt < (ppdu_desc_cnt - 1)) {
+		(*ppdu_cnt)++;
+		ptr_nbuf_list = &nbuf_ppdu_list[*ppdu_cnt];
+
+		if (!ptr_nbuf_list->nbuf_ppdu ||
+		    !dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list))
+			continue;
+
+		next_ppdu = (struct cdp_tx_completion_ppdu *)
+				qdf_nbuf_data(ptr_nbuf_list->nbuf_ppdu);
+
+		if (!next_ppdu)
+			continue;
+
+		cur_usr_idx = dp_tx_find_usr_idx_from_peer_id(next_ppdu,
+							      peer_id);
+		next_user = &next_ppdu->user[cur_usr_idx];
+
+		if ((next_user->skip == 1) || (peer_id != next_user->peer_id))
+			continue;
+
+		if (last_pend_ppdu) {
+			qdf_nbuf_t tmp_pend_nbuf;
+			uint32_t ppdu_ref_cnt;
+
+			/*
+			 * get reference count if it
+			 * more than one do clone and
+			 * add that to head_ppdu
+			 */
+			ppdu_ref_cnt =
+				dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list);
+
+			if (ppdu_ref_cnt == 1) {
+				tmp_pend_nbuf = ptr_nbuf_list->nbuf_ppdu;
+			} else {
+				tmp_pend_nbuf = qdf_nbuf_clone(
+						ptr_nbuf_list->nbuf_ppdu);
+				if (qdf_unlikely(!tmp_pend_nbuf)) {
+					qdf_assert_always(0);
+					continue;
+				}
+				qdf_nbuf_free(ptr_nbuf_list->nbuf_ppdu);
+			}
+
+			qdf_nbuf_queue_add(head_ppdu, tmp_pend_nbuf);
+
+			/* decrement reference */
+			dp_tx_cap_nbuf_list_dec_ref(ptr_nbuf_list);
+		}
+
+		if (next_user->last_enq_seq > cur_last_seq)
+			return next_ppdu;
+	}
+	return NULL;
 }
 
 #define MAX_PENDING_PPDUS 32
 static void
 dp_tx_mon_proc_pending_ppdus(struct dp_pdev *pdev, struct dp_tx_tid *tx_tid,
-			     qdf_nbuf_t nbuf_ppdu_desc_list[], uint32_t
-			     ppdu_desc_cnt, qdf_nbuf_queue_t *head_ppdu,
-			     uint32_t peer_id)
+		struct dp_tx_cap_nbuf_list nbuf_ppdu_list[],
+		uint32_t ppdu_desc_cnt, qdf_nbuf_queue_t *head_ppdu,
+		uint32_t peer_id, uint8_t cur_usr_idx)
 {
 	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
 	struct cdp_tx_completion_ppdu *cur_ppdu_desc = NULL;
+	struct cdp_tx_completion_ppdu_user *user = NULL;
+	struct cdp_tx_completion_ppdu_user *cur_user = NULL;
+	struct dp_tx_cap_nbuf_list *ptr_nbuf_list = NULL;
 	qdf_nbuf_t pend_ppdu;
 	uint32_t ppdu_cnt;
 	uint32_t failed_seq;
 	uint32_t cur_index, cur_start_seq, cur_last_seq;
 	int i, k;
 	bool last_pend_ppdu = false;
-	qdf_nbuf_t tmp_nbuf;
+	uint8_t usr_idx;
 
 	pend_ppdu = qdf_nbuf_queue_first(&tx_tid->pending_ppdu_q);
 	if (!pend_ppdu) {
 		for (ppdu_cnt = 0; ppdu_cnt < ppdu_desc_cnt; ppdu_cnt++) {
-			if (!nbuf_ppdu_desc_list[ppdu_cnt])
+			ptr_nbuf_list = &nbuf_ppdu_list[ppdu_cnt];
+
+			if (!dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list)) {
+				if (ptr_nbuf_list->nbuf_ppdu)
+					qdf_assert_always(0);
 				continue;
+			}
+
 			ppdu_desc = (struct cdp_tx_completion_ppdu *)
-				qdf_nbuf_data(
-				nbuf_ppdu_desc_list[ppdu_cnt]);
-			if (!ppdu_desc || (peer_id !=
-			    ppdu_desc->user[0].peer_id) || (tx_tid->tid !=
-			    ppdu_desc->user[0].tid))
+				qdf_nbuf_data(ptr_nbuf_list->nbuf_ppdu);
+
+			if (!ppdu_desc)
 				continue;
-			if ((ppdu_desc->pending_retries == 0) &&
+
+			user = &ppdu_desc->user[cur_usr_idx];
+
+			if ((user->skip == 1) || (peer_id != user->peer_id) ||
+			    (tx_tid->tid != user->tid))
+				continue;
+			if ((user->pending_retries == 0) &&
 			    qdf_nbuf_is_queue_empty(&tx_tid->pending_ppdu_q) &&
 			    qdf_nbuf_is_queue_empty(head_ppdu)) {
-				dp_send_data_to_stack(pdev, ppdu_desc);
-				qdf_nbuf_queue_free(&ppdu_desc->mpdu_q);
-				qdf_mem_free(ppdu_desc->mpdus);
-				ppdu_desc->mpdus = NULL;
-				tmp_nbuf = nbuf_ppdu_desc_list[ppdu_cnt];
-				nbuf_ppdu_desc_list[ppdu_cnt] = NULL;
-				qdf_nbuf_free(tmp_nbuf);
+				dp_send_data_to_stack(pdev, ppdu_desc,
+						      cur_usr_idx);
+				/* free ppd_desc from list */
+				dp_ppdu_desc_free(ptr_nbuf_list, cur_usr_idx);
 			} else {
-				qdf_nbuf_queue_add(head_ppdu,
-						   nbuf_ppdu_desc_list[ppdu_cnt]);
-				nbuf_ppdu_desc_list[ppdu_cnt] = NULL;
+				qdf_nbuf_t tmp_pend_nbuf;
+				uint32_t ppdu_ref_cnt;
+
+				/*
+				 * get reference count if it more than one
+				 * do clone and add that to head_ppdu
+				 */
+				ppdu_ref_cnt =
+				dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list);
+				if (ppdu_ref_cnt == 1) {
+					tmp_pend_nbuf =
+						ptr_nbuf_list->nbuf_ppdu;
+				} else {
+					tmp_pend_nbuf =
+						qdf_nbuf_clone(
+						ptr_nbuf_list->nbuf_ppdu);
+					if (qdf_unlikely(!tmp_pend_nbuf)) {
+						qdf_assert_always(0);
+						continue;
+					}
+					/*
+					 * free ppdu_desc to
+					 * decrease reference
+					 */
+					qdf_nbuf_free(ptr_nbuf_list->nbuf_ppdu);
+				}
+
+				qdf_nbuf_queue_add(head_ppdu, tmp_pend_nbuf);
+				/* decrement reference */
+				dp_tx_cap_nbuf_list_dec_ref(ptr_nbuf_list);
 			}
 		}
 		return;
 	}
 	while (pend_ppdu) {
 		qdf_nbuf_t mpdu_nbuf;
+		uint32_t mpdu_tried = 0;
 
 		/* Find missing mpdus from current schedule list */
 		ppdu_cnt = 0;
-
-		while (!nbuf_ppdu_desc_list[ppdu_cnt]) {
+		while (ppdu_cnt < ppdu_desc_cnt) {
+			ptr_nbuf_list = &nbuf_ppdu_list[ppdu_cnt];
 			ppdu_cnt++;
-			if (ppdu_cnt < ppdu_desc_cnt)
+			if (!dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list))
 				continue;
+
+			cur_ppdu_desc = (struct cdp_tx_completion_ppdu *)
+					qdf_nbuf_data(ptr_nbuf_list->nbuf_ppdu);
+			if (!cur_ppdu_desc)
+				continue;
+
+			cur_usr_idx = dp_tx_find_usr_idx_from_peer_id(
+						cur_ppdu_desc, peer_id);
+
+			cur_user = &cur_ppdu_desc->user[cur_usr_idx];
+
+			if (cur_user->skip == 1)
+				continue;
+			/* to handle last ppdu case we need to decrement */
+			ppdu_cnt--;
 			break;
 		}
 
 		if (ppdu_cnt == ppdu_desc_cnt)
 			break;
 
+		if (qdf_unlikely(!cur_user))
+			break;
+
 		ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(
 			pend_ppdu);
 
-		cur_ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(
-			nbuf_ppdu_desc_list[ppdu_cnt]);
+		usr_idx = dp_tx_find_usr_idx_from_peer_id(ppdu_desc,
+							  peer_id);
+		user = &ppdu_desc->user[usr_idx];
+
 		if (pend_ppdu == qdf_nbuf_queue_last(
 		    &tx_tid->pending_ppdu_q)) {
+			qdf_nbuf_t tmp_pend_nbuf;
+			uint32_t ppdu_ref_cnt;
+
 			last_pend_ppdu = true;
-			qdf_nbuf_queue_add(head_ppdu,
-					   nbuf_ppdu_desc_list[ppdu_cnt]);
-			nbuf_ppdu_desc_list[ppdu_cnt] = NULL;
+			/*
+			 * get reference count if it more than one
+			 * do clone and add that to head_ppdu
+			 */
+			ppdu_ref_cnt =
+				dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list);
+			if (ppdu_ref_cnt == 1) {
+				tmp_pend_nbuf =
+					ptr_nbuf_list->nbuf_ppdu;
+			} else {
+				tmp_pend_nbuf = qdf_nbuf_clone(
+						ptr_nbuf_list->nbuf_ppdu);
+				if (qdf_unlikely(!tmp_pend_nbuf)) {
+					qdf_assert_always(0);
+					break;
+				}
+				qdf_nbuf_free(ptr_nbuf_list->nbuf_ppdu);
+			}
+
+			qdf_nbuf_queue_add(head_ppdu, tmp_pend_nbuf);
+			/* decrement reference */
+			dp_tx_cap_nbuf_list_dec_ref(ptr_nbuf_list);
 		}
 		cur_index = 0;
-		cur_start_seq = cur_ppdu_desc->user[0].start_seq;
-		cur_last_seq = cur_ppdu_desc->user[0].last_enq_seq;
-		if (qdf_unlikely(ppdu_desc->user[0].ba_size >
+		cur_start_seq = cur_user->start_seq;
+		cur_last_seq = cur_user->last_enq_seq;
+		if (qdf_unlikely(user->ba_size >
 		    CDP_BA_256_BIT_MAP_SIZE_DWORDS *
-		    SEQ_SEG_SZ_BITS(ppdu_desc->user[0].failed_bitmap))) {
+		    SEQ_SEG_SZ_BITS(user->failed_bitmap))) {
+			dp_ppdu_desc_debug_print(ppdu_desc, usr_idx,
+						 __func__, __LINE__);
 			qdf_assert_always(0);
 			return;
 		}
 
-		for (i = 0; (i < ppdu_desc->user[0].ba_size) && cur_ppdu_desc;
-		     i++) {
-			if (!(i & (SEQ_SEG_SZ_BITS(
-			    ppdu_desc->user[0].failed_bitmap) - 1))) {
-				k =
-				SEQ_SEG_INDEX(ppdu_desc->user[0].failed_bitmap,
-					      i);
-				failed_seq =
-					ppdu_desc->user[0].failed_bitmap[k] ^
-					ppdu_desc->user[0].enq_bitmap[k];
+		/* mpdu tried */
+		mpdu_tried = user->mpdu_tried_mcast + user->mpdu_tried_ucast;
+
+		for (i = 0; (i < user->ba_size) && cur_ppdu_desc &&
+		     mpdu_tried && cur_index < cur_user->ba_size; i++) {
+			if (!(i & (SEQ_SEG_SZ_BITS(user->failed_bitmap) - 1))) {
+				k = SEQ_SEG_INDEX(user->failed_bitmap, i);
+				failed_seq = user->failed_bitmap[k] ^
+					     user->enq_bitmap[k];
 			}
 
+			if (SEQ_BIT(user->enq_bitmap, i))
+				mpdu_tried--;
+
 			/* Skip to next bitmap segment if there are no
 			 * more holes in current segment
 			 */
 			if (!failed_seq) {
 				i = ((k + 1) *
-				SEQ_SEG_SZ_BITS(ppdu_desc->user[0].failed_bitmap))
-						- 1;
+				SEQ_SEG_SZ_BITS(user->failed_bitmap)) - 1;
 				continue;
 			}
 			if (!(SEQ_SEG_BIT(failed_seq, i)))
 				continue;
 			failed_seq ^= SEQ_SEG_MSK(failed_seq, i);
 
-			mpdu_nbuf = cur_ppdu_desc->mpdus[cur_index];
+			if (!cur_user->mpdus) {
+				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+					  QDF_TRACE_LEVEL_INFO,
+					  "%s: %d peer_id:%d usr_idx:%d cur_usr_idx:%d cur_usr_peer_id:%d\n",
+					  __func__, __LINE__,
+					  peer_id, usr_idx,
+					  cur_usr_idx, cur_user->peer_id);
+				continue;
+			}
+			mpdu_nbuf = cur_user->mpdus[cur_index];
 			if (mpdu_nbuf) {
+				struct dp_peer *peer;
 				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
 					  QDF_TRACE_LEVEL_INFO,
 					"%s: fill seqno %d (%d) from swretries",
 					__func__,
-					ppdu_desc->user[0].start_seq + i,
+					user->start_seq + i,
 					ppdu_desc->ppdu_id);
-				ppdu_desc->mpdus[i] =
+				user->mpdus[i] =
 				qdf_nbuf_copy_expand_fraglist(
 					mpdu_nbuf, MAX_MONITOR_HEADER, 0);
-				ppdu_desc->user[0].failed_bitmap[k] |=
-				SEQ_SEG_MSK(ppdu_desc->user[0].failed_bitmap[k],
-					    i);
-				ppdu_desc->pending_retries--;
+
+				peer = dp_tx_cap_peer_find_by_id(pdev->soc,
+								 user->peer_id);
+				if (peer) {
+					dp_tx_cap_stats_mpdu_update(peer,
+							PEER_MPDU_CLONE, 1);
+					dp_tx_cap_peer_unref_del(peer);
+				}
+				user->failed_bitmap[k] |=
+				SEQ_SEG_MSK(user->failed_bitmap[k], i);
+				user->pending_retries--;
 			}
 
 			cur_index++;
-			if (cur_index >= cur_ppdu_desc->user[0].ba_size) {
+			if (cur_index >= cur_user->ba_size) {
 				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
 					  QDF_TRACE_LEVEL_INFO,
 					  "%s: ba_size[%d] cur_index[%d]\n",
 					__func__,
-					cur_ppdu_desc->user[0].ba_size,
-					cur_index);
+					cur_user->ba_size, cur_index);
 				break;
 			}
-			/* Skip through empty slots in current PPDU */
-			while (!(SEQ_BIT(cur_ppdu_desc->user[0].enq_bitmap,
-			       cur_index))) {
-				struct cdp_tx_completion_ppdu *next_ppdu = NULL;
 
+			/* Skip through empty slots in current PPDU */
+			while (!(SEQ_BIT(cur_user->enq_bitmap, cur_index))) {
 				cur_index++;
-				if (cur_index <= (cur_last_seq -
-				    cur_start_seq))
+				if (cur_index <= (cur_last_seq - cur_start_seq))
 					continue;
 				cur_ppdu_desc = NULL;
-				/* Check if subsequent PPDUs in this schedule
+
+				/*
+				 * Check if subsequent PPDUs in this schedule
 				 * has higher sequence numbers enqueued
 				 */
-				while (ppdu_cnt < (ppdu_desc_cnt - 1)) {
-					ppdu_cnt++;
-					if (!nbuf_ppdu_desc_list[ppdu_cnt])
-						continue;
-					next_ppdu =
-					(struct cdp_tx_completion_ppdu *)
-					qdf_nbuf_data(
-						      nbuf_ppdu_desc_list[
-							ppdu_cnt]);
-					if (!next_ppdu || (peer_id !=
-					    next_ppdu->user[0].peer_id))
-						continue;
-					if (last_pend_ppdu) {
-						qdf_nbuf_queue_add(head_ppdu,
-							nbuf_ppdu_desc_list[ppdu_cnt]);
-						nbuf_ppdu_desc_list[ppdu_cnt] =
-							NULL;
-					}
-					if (next_ppdu->user[0].last_enq_seq >
-					    cur_last_seq) {
-						cur_ppdu_desc = next_ppdu;
-						break;
-					}
-				}
+				cur_ppdu_desc = check_subseq_ppdu_to_pending_q(
+								nbuf_ppdu_list,
+								ppdu_desc_cnt,
+								&ppdu_cnt,
+								head_ppdu,
+								peer_id,
+								cur_last_seq,
+								last_pend_ppdu);
 				if (!cur_ppdu_desc)
 					break;
+
+				cur_usr_idx = dp_tx_find_usr_idx_from_peer_id(
+						cur_ppdu_desc, peer_id);
+
+				cur_user = &cur_ppdu_desc->user[cur_usr_idx];
+
 				/* Start from seq. no following cur_last_seq
 				 * since everything before is already populated
 				 * from previous PPDU
 				 */
-				cur_start_seq =
-					cur_ppdu_desc->user[0].start_seq;
+				cur_start_seq = cur_user->start_seq;
 				cur_index = (cur_last_seq >= cur_start_seq) ?
 					cur_last_seq - cur_start_seq + 1 : 0;
-				cur_last_seq =
-					cur_ppdu_desc->user[0].last_enq_seq;
+				cur_last_seq = cur_user->last_enq_seq;
 			}
 		}
 		if ((pend_ppdu ==
 		    qdf_nbuf_queue_first(&tx_tid->pending_ppdu_q)) &&
-		    (ppdu_desc->pending_retries == 0)) {
+		    (user->pending_retries == 0)) {
 			qdf_nbuf_queue_remove(&tx_tid->pending_ppdu_q);
-			dp_send_data_to_stack(pdev, ppdu_desc);
-			qdf_nbuf_queue_free(&ppdu_desc->mpdu_q);
-			qdf_mem_free(ppdu_desc->mpdus);
-			ppdu_desc->mpdus = NULL;
+			dp_send_data_to_stack(pdev, ppdu_desc, usr_idx);
+			dp_ppdu_queue_free(pend_ppdu, usr_idx);
 			qdf_nbuf_free(pend_ppdu);
 			pend_ppdu = qdf_nbuf_queue_first(
 				&tx_tid->pending_ppdu_q);
@@ -2698,6 +3369,7 @@
 			   bool is_payload)
 {
 	struct cdp_tx_completion_ppdu *ppdu_desc;
+	struct cdp_tx_completion_ppdu_user *user;
 	struct cdp_tx_indication_mpdu_info *mpdu_info;
 	struct ieee80211_frame *wh;
 	uint16_t duration_le, seq_le;
@@ -2708,9 +3380,10 @@
 	mpdu_info = &ptr_tx_cap_info->mpdu_info;
 	ppdu_desc = (struct cdp_tx_completion_ppdu *)
 			qdf_nbuf_data(nbuf_ppdu_desc);
+	user = &ppdu_desc->user[0];
 
 	if (ppdu_desc->mprot_type)
-		dp_send_dummy_rts_cts_frame(pdev, ppdu_desc);
+		dp_send_dummy_rts_cts_frame(pdev, ppdu_desc, 0);
 
 	type = (ppdu_desc->frame_ctrl &
 		IEEE80211_FC0_TYPE_MASK) >>
@@ -2726,7 +3399,7 @@
 			duration_le = qdf_cpu_to_le16(ppdu_desc->tx_duration);
 			wh->i_dur[1] = (duration_le & 0xFF00) >> 8;
 			wh->i_dur[0] = duration_le & 0xFF;
-			seq_le = qdf_cpu_to_le16(ppdu_desc->user[0].start_seq <<
+			seq_le = qdf_cpu_to_le16(user->start_seq <<
 						 IEEE80211_SEQ_SEQ_SHIFT);
 			wh->i_seq[1] = (seq_le & 0xFF00) >> 8;
 			wh->i_seq[0] = seq_le & 0xFF;
@@ -2779,15 +3452,18 @@
 static uint32_t
 dp_update_tx_cap_info(struct dp_pdev *pdev,
 		      qdf_nbuf_t nbuf_ppdu_desc,
-		      void *tx_info, bool is_payload)
+		      void *tx_info, bool is_payload,
+		      bool bar_frm_with_data)
 {
 	struct cdp_tx_completion_ppdu *ppdu_desc;
+	struct cdp_tx_completion_ppdu_user *user;
 	struct cdp_tx_indication_info *tx_capture_info =
 		(struct cdp_tx_indication_info *)tx_info;
 	struct cdp_tx_indication_mpdu_info *mpdu_info;
 
 	ppdu_desc = (struct cdp_tx_completion_ppdu *)
 			qdf_nbuf_data(nbuf_ppdu_desc);
+	user = &ppdu_desc->user[0];
 
 	qdf_mem_set(tx_capture_info, sizeof(struct cdp_tx_indication_info), 0);
 	mpdu_info = &tx_capture_info->mpdu_info;
@@ -2797,13 +3473,20 @@
 	mpdu_info->ppdu_start_timestamp = ppdu_desc->ppdu_start_timestamp;
 	mpdu_info->ppdu_end_timestamp = ppdu_desc->ppdu_end_timestamp;
 	mpdu_info->tx_duration = ppdu_desc->tx_duration;
-	mpdu_info->seq_no = ppdu_desc->user[0].start_seq;
+	if (bar_frm_with_data) {
+		mpdu_info->ppdu_start_timestamp =
+			ppdu_desc->bar_ppdu_start_timestamp;
+		mpdu_info->ppdu_end_timestamp =
+			ppdu_desc->bar_ppdu_end_timestamp;
+		mpdu_info->tx_duration = ppdu_desc->bar_tx_duration;
+	}
+	mpdu_info->seq_no = user->start_seq;
 	mpdu_info->num_msdu = ppdu_desc->num_msdu;
 
 	/* update cdp_tx_indication_mpdu_info */
 	dp_tx_update_user_mpdu_info(ppdu_desc->ppdu_id,
 				    &tx_capture_info->mpdu_info,
-				    &ppdu_desc->user[0]);
+				    user);
 	tx_capture_info->ppdu_desc = ppdu_desc;
 	tx_capture_info->mpdu_info.channel_num = pdev->operating_channel.num;
 
@@ -2824,7 +3507,7 @@
 
 static uint32_t
 dp_check_mgmt_ctrl_ppdu(struct dp_pdev *pdev,
-			qdf_nbuf_t nbuf_ppdu_desc)
+			qdf_nbuf_t nbuf_ppdu_desc, bool bar_frm_with_data)
 {
 	struct cdp_tx_indication_info tx_capture_info;
 	qdf_nbuf_t mgmt_ctl_nbuf;
@@ -2834,28 +3517,51 @@
 	struct cdp_tx_mgmt_comp_info *ptr_comp_info;
 	qdf_nbuf_queue_t *retries_q;
 	struct cdp_tx_completion_ppdu *ppdu_desc;
+	struct cdp_tx_completion_ppdu_user *user;
 	uint32_t ppdu_id;
+	uint32_t desc_ppdu_id;
 	size_t head_size;
 	uint32_t status = 1;
 	uint32_t tsf_delta;
+	uint64_t start_tsf;
+	uint64_t end_tsf;
+	uint16_t ppdu_desc_frame_ctrl;
 
 	ppdu_desc = (struct cdp_tx_completion_ppdu *)
 		qdf_nbuf_data(nbuf_ppdu_desc);
+
+	user = &ppdu_desc->user[0];
+
+	ppdu_desc_frame_ctrl = ppdu_desc->frame_ctrl;
+	if (ppdu_desc->htt_frame_type == HTT_STATS_FTYPE_SGEN_MU_BAR)
+		ppdu_desc_frame_ctrl = (IEEE80211_FC0_SUBTYPE_TRIGGER |
+					IEEE80211_FC0_TYPE_CTL);
+
+	if (bar_frm_with_data) {
+		desc_ppdu_id = ppdu_desc->bar_ppdu_id;
+		start_tsf = ppdu_desc->bar_ppdu_start_timestamp;
+		end_tsf = ppdu_desc->bar_ppdu_end_timestamp;
+	} else {
+		desc_ppdu_id = ppdu_desc->ppdu_id;
+		start_tsf = ppdu_desc->ppdu_start_timestamp;
+		end_tsf = ppdu_desc->ppdu_end_timestamp;
+	}
+
 	/*
 	 * only for host generated frame we do have
 	 * timestamp and retries count.
 	 */
 	head_size = sizeof(struct cdp_tx_mgmt_comp_info);
 
-	fc_type = (ppdu_desc->frame_ctrl &
+	fc_type = (ppdu_desc_frame_ctrl &
 		  IEEE80211_FC0_TYPE_MASK);
-	fc_subtype = (ppdu_desc->frame_ctrl &
+	fc_subtype = (ppdu_desc_frame_ctrl &
 		     IEEE80211_FC0_SUBTYPE_MASK);
 
-	type = (ppdu_desc->frame_ctrl &
+	type = (ppdu_desc_frame_ctrl &
 		IEEE80211_FC0_TYPE_MASK) >>
 		IEEE80211_FC0_TYPE_SHIFT;
-	subtype = (ppdu_desc->frame_ctrl &
+	subtype = (ppdu_desc_frame_ctrl &
 		IEEE80211_FC0_SUBTYPE_MASK) >>
 		IEEE80211_FC0_SUBTYPE_SHIFT;
 
@@ -2895,15 +3601,13 @@
 
 	if (mgmt_ctl_nbuf) {
 		qdf_nbuf_t tmp_mgmt_ctl_nbuf;
-		uint32_t start_tsf;
 
 		ptr_comp_info = (struct cdp_tx_mgmt_comp_info *)
 				qdf_nbuf_data(mgmt_ctl_nbuf);
 		is_sgen_pkt = ptr_comp_info->is_sgen_pkt;
 		ppdu_id = ptr_comp_info->ppdu_id;
 
-		if (!is_sgen_pkt && ptr_comp_info->tx_tsf <
-		    ppdu_desc->ppdu_start_timestamp) {
+		if (!is_sgen_pkt && ptr_comp_info->tx_tsf < start_tsf) {
 			/*
 			 * free the older mgmt buffer from
 			 * the queue and get new mgmt buffer
@@ -2912,29 +3616,27 @@
 			goto get_mgmt_pkt_from_queue;
 		}
 
-		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-			  QDF_TRACE_LEVEL_INFO_HIGH,
-			  "ppdu_id [%d 0x%x] type_subtype[%d %d] is_sgen[%d] h_sz[%d]",
-			  ppdu_id, ppdu_id, type, subtype,
-			  is_sgen_pkt, head_size);
-
-		QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_TX_CAPTURE,
-				   QDF_TRACE_LEVEL_INFO_HIGH,
-				   qdf_nbuf_data(mgmt_ctl_nbuf), 32);
-
 		/*
 		 * for sgen frame we won't have, retries count
 		 * and 64 bits tsf in the head.
 		 */
-		if (ppdu_id != ppdu_desc->ppdu_id) {
+		if (ppdu_id != desc_ppdu_id) {
 			if (is_sgen_pkt) {
-				start_tsf = (ppdu_desc->ppdu_start_timestamp &
-					     LOWER_32_MASK);
-
+				start_tsf = (start_tsf & LOWER_32_MASK);
 				if (start_tsf > ptr_comp_info->tx_tsf)
-					tsf_delta = start_tsf - ptr_comp_info->tx_tsf;
+					tsf_delta = start_tsf -
+						ptr_comp_info->tx_tsf;
 				else
-					tsf_delta = LOWER_32_MASK - ptr_comp_info->tx_tsf + start_tsf;
+					tsf_delta = LOWER_32_MASK -
+						ptr_comp_info->tx_tsf +
+						start_tsf;
+
+				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+					  QDF_TRACE_LEVEL_INFO,
+					  "%s: ppdu_id[m:%d desc:%d] start_tsf: %u mgmt_tsf:%u tsf_delta:%u bar_frm_with_data:%d",
+					  __func__, ppdu_id, desc_ppdu_id,
+					  start_tsf, ptr_comp_info->tx_tsf,
+					  tsf_delta, bar_frm_with_data);
 
 				if (tsf_delta > MAX_MGMT_ENQ_DELAY) {
 					/*
@@ -2943,6 +3645,11 @@
 					 */
 					qdf_nbuf_free(mgmt_ctl_nbuf);
 					goto get_mgmt_pkt_from_queue;
+				} else {
+					/* drop the ppdu_desc */
+					qdf_nbuf_free(nbuf_ppdu_desc);
+					status = 0;
+					goto insert_mgmt_buf_to_queue;
 				}
 			}
 
@@ -2951,16 +3658,13 @@
 			 * packets drop by firmware is not handled in this
 			 * feature
 			 */
-			if (ppdu_desc->user[0].completion_status ==
+			if (user->completion_status ==
 			    HTT_PPDU_STATS_USER_STATUS_FILTERED) {
 				qdf_nbuf_free(nbuf_ppdu_desc);
 				status = 0;
 				goto insert_mgmt_buf_to_queue;
 			}
 
-			/*
-			 * add the ppdu_desc into retry queue
-			 */
 			qdf_nbuf_queue_add(retries_q, nbuf_ppdu_desc);
 			status = 0;
 
@@ -2981,13 +3685,21 @@
 			struct cdp_tx_completion_ppdu *tmp_ppdu_desc;
 			uint16_t frame_ctrl_le;
 			struct ieee80211_frame *wh;
+			uint32_t retry_len = 0;
+
+			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+				  QDF_TRACE_LEVEL_INFO,
+				  "%s: ppdu_id[m:%d desc:%d] start_tsf: %u mgmt_tsf:%u bar_frm_with_data:%d is_sgen:%d",
+				  __func__, ppdu_id, desc_ppdu_id,
+				  start_tsf, ptr_comp_info->tx_tsf,
+				  bar_frm_with_data, is_sgen_pkt);
 
 			/*
 			 * only for the packets send over the air are handled
 			 * packets drop by firmware is not handled in this
 			 * feature
 			 */
-			if (ppdu_desc->user[0].completion_status ==
+			if (user->completion_status ==
 			    HTT_PPDU_STATS_USER_STATUS_FILTERED) {
 				qdf_nbuf_free(nbuf_ppdu_desc);
 				qdf_nbuf_free(mgmt_ctl_nbuf);
@@ -2995,22 +3707,41 @@
 				goto free_ppdu_desc;
 			}
 
-			while (!!qdf_nbuf_queue_len(retries_q)) {
+			while (qdf_nbuf_queue_len(retries_q)) {
 				/*
 				 * send retried packet stored
 				 * in queue
 				 */
 				nbuf_retry_ppdu =
 					qdf_nbuf_queue_remove(retries_q);
+
+				retry_len = qdf_nbuf_queue_len(retries_q);
+				if (!nbuf_retry_ppdu) {
+					QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+						  QDF_TRACE_LEVEL_FATAL,
+						  "%s: %d retry q type[%d][%d] retry q len = %d\n",
+						  __func__, __LINE__,
+						  type, subtype, retry_len);
+					qdf_assert_always(0);
+					break;
+				}
+
 				tmp_ppdu_desc =
 					(struct cdp_tx_completion_ppdu *)
 						qdf_nbuf_data(nbuf_retry_ppdu);
 				tmp_mgmt_ctl_nbuf =
 					qdf_nbuf_copy_expand(mgmt_ctl_nbuf,
 							0, 0);
+				if (qdf_unlikely(!tmp_mgmt_ctl_nbuf)) {
+					QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+						  QDF_TRACE_LEVEL_FATAL,
+						  "No memory to do copy!!");
+					qdf_assert_always(0);
+				}
 
 				dp_update_tx_cap_info(pdev, nbuf_retry_ppdu,
-						      &tx_capture_info, true);
+						      &tx_capture_info, true,
+						      bar_frm_with_data);
 				if (!tx_capture_info.mpdu_nbuf) {
 					qdf_nbuf_free(nbuf_retry_ppdu);
 					qdf_nbuf_free(tmp_mgmt_ctl_nbuf);
@@ -3018,8 +3749,14 @@
 				}
 
 				/* pull head based on sgen pkt or mgmt pkt */
-				qdf_nbuf_pull_head(tmp_mgmt_ctl_nbuf,
-						   head_size);
+				if (NULL ==
+				    qdf_nbuf_pull_head(tmp_mgmt_ctl_nbuf,
+						       head_size)) {
+					QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+						  QDF_TRACE_LEVEL_FATAL,
+						  " No Head space to pull !!\n");
+					qdf_assert_always(0);
+				}
 
 				/*
 				 * frame control from ppdu_desc has
@@ -3047,19 +3784,28 @@
 			}
 
 			dp_update_tx_cap_info(pdev, nbuf_ppdu_desc,
-					      &tx_capture_info, true);
+					      &tx_capture_info, true,
+					      bar_frm_with_data);
 			if (!tx_capture_info.mpdu_nbuf) {
 				qdf_nbuf_free(mgmt_ctl_nbuf);
+				qdf_nbuf_free(nbuf_ppdu_desc);
+				status = 0;
 				goto free_ppdu_desc;
 			}
 
 			tx_capture_info.mpdu_info.ppdu_id =
 				*(uint32_t *)qdf_nbuf_data(mgmt_ctl_nbuf);
 			/* pull head based on sgen pkt or mgmt pkt */
-			qdf_nbuf_pull_head(mgmt_ctl_nbuf, head_size);
+			if (NULL == qdf_nbuf_pull_head(mgmt_ctl_nbuf,
+						       head_size)) {
+				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+					  QDF_TRACE_LEVEL_FATAL,
+					  " No Head space to pull !!\n");
+				qdf_assert_always(0);
+			}
 
 			/* frame control from ppdu_desc has retry flag set */
-			frame_ctrl_le = qdf_cpu_to_le16(ppdu_desc->frame_ctrl);
+			frame_ctrl_le = qdf_cpu_to_le16(ppdu_desc_frame_ctrl);
 			wh = (struct ieee80211_frame *)
 				(qdf_nbuf_data(mgmt_ctl_nbuf));
 			wh->i_fc[1] = (frame_ctrl_le & 0xFF00) >> 8;
@@ -3079,7 +3825,7 @@
 		 * packets drop by firmware is not handled in this
 		 * feature
 		 */
-		if (ppdu_desc->user[0].completion_status ==
+		if (user->completion_status ==
 		    HTT_PPDU_STATS_USER_STATUS_FILTERED) {
 			qdf_nbuf_free(nbuf_ppdu_desc);
 			status = 0;
@@ -3090,7 +3836,7 @@
 		 */
 		qdf_nbuf_queue_add(retries_q, nbuf_ppdu_desc);
 		status = 0;
-	} else if ((ppdu_desc->frame_ctrl &
+	} else if ((ppdu_desc_frame_ctrl &
 		   IEEE80211_FC0_TYPE_MASK) ==
 		   IEEE80211_FC0_TYPE_CTL) {
 
@@ -3099,7 +3845,7 @@
 		 * packets drop by firmware is not handled in this
 		 * feature
 		 */
-		if (ppdu_desc->user[0].completion_status ==
+		if (user->completion_status ==
 		    HTT_PPDU_STATS_USER_STATUS_FILTERED) {
 			qdf_nbuf_free(nbuf_ppdu_desc);
 			status = 0;
@@ -3107,9 +3853,13 @@
 		}
 
 		dp_update_tx_cap_info(pdev, nbuf_ppdu_desc,
-				      &tx_capture_info, false);
-		if (!tx_capture_info.mpdu_nbuf)
+				      &tx_capture_info, false,
+				      bar_frm_with_data);
+		if (!tx_capture_info.mpdu_nbuf) {
+			qdf_nbuf_free(nbuf_ppdu_desc);
+			status = 0;
 			goto free_ppdu_desc;
+		}
 		/*
 		 * send MPDU to osif layer
 		 */
@@ -3132,7 +3882,8 @@
 static void
 dp_peer_tx_cap_tid_queue_flush_tlv(struct dp_pdev *pdev,
 				   struct dp_peer *peer,
-				   struct cdp_tx_completion_ppdu *ppdu_desc)
+				   struct cdp_tx_completion_ppdu *ppdu_desc,
+				   uint8_t usr_idx)
 {
 	int tid;
 	struct dp_tx_tid *tx_tid;
@@ -3140,14 +3891,16 @@
 	qdf_nbuf_queue_t head_msdu;
 	uint32_t qlen = 0;
 	uint32_t qlen_curr = 0;
+	struct cdp_tx_completion_ppdu_user *xretry_user;
 
-	tid = ppdu_desc->user[0].tid;
+	xretry_user = &ppdu_desc->user[usr_idx];
+	tid = xretry_user->tid;
 	tx_tid = &peer->tx_capture.tx_tid[tid];
 
 	qdf_nbuf_queue_init(&head_msdu);
 	qdf_nbuf_queue_init(&head_xretries);
 
-	qlen = qdf_nbuf_queue_len(&tx_tid->msdu_comp_q);
+	qlen = qdf_nbuf_queue_len(&tx_tid->defer_msdu_q);
 
 	dp_tx_msdu_dequeue(peer, INVALID_PPDU_ID,
 			   tid, ppdu_desc->num_msdu,
@@ -3155,9 +3908,14 @@
 			   &head_xretries,
 			   0, MAX_END_TSF);
 
+	dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_FLUSH,
+				    qdf_nbuf_queue_len(&head_msdu));
+	dp_tx_cap_stats_msdu_update(peer, PEER_MSDU_FLUSH,
+				    qdf_nbuf_queue_len(&head_xretries));
 	if (!qdf_nbuf_is_queue_empty(&head_xretries)) {
 		struct cdp_tx_completion_ppdu *xretry_ppdu =
 						&tx_tid->xretry_ppdu;
+		uint32_t xretry_qlen;
 
 		xretry_ppdu->ppdu_id = peer->tx_capture.tx_wifi_ppdu_id;
 
@@ -3165,18 +3923,23 @@
 		dp_tx_mon_restitch_mpdu(pdev, peer,
 					xretry_ppdu,
 					&head_xretries,
-					&xretry_ppdu->mpdu_q);
+					&xretry_user->mpdu_q,
+					0);
+
+		xretry_qlen = qdf_nbuf_queue_len(&xretry_user->mpdu_q);
+		dp_tx_cap_stats_mpdu_update(peer, PEER_MPDU_RESTITCH,
+					    xretry_qlen);
 	}
 	qdf_nbuf_queue_free(&head_msdu);
 	qdf_nbuf_queue_free(&head_xretries);
-	qlen_curr = qdf_nbuf_queue_len(&tx_tid->msdu_comp_q);
+	qlen_curr = qdf_nbuf_queue_len(&tx_tid->defer_msdu_q);
 
 	dp_tx_mon_proc_xretries(pdev, peer, tid);
 
 	QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
 		  QDF_TRACE_LEVEL_INFO_MED,
 		  "peer_id [%d 0x%x] tid[%d] qlen[%d -> %d]",
-		  ppdu_desc->user[0].peer_id, peer, tid, qlen, qlen_curr);
+		  ppdu_desc->user[usr_idx].peer_id, peer, tid, qlen, qlen_curr);
 
 }
 
@@ -3189,312 +3952,705 @@
  */
 static void
 dp_tx_ppdu_stats_flush(struct dp_pdev *pdev,
-		       struct cdp_tx_completion_ppdu *ppdu_desc)
+		       struct cdp_tx_completion_ppdu *ppdu_desc,
+			   uint8_t usr_idx)
 {
 	struct dp_peer *peer;
+	struct cdp_tx_completion_ppdu_user *user;
 
-	peer = dp_tx_cap_peer_find_by_id(pdev->soc,
-					 ppdu_desc->user[0].peer_id);
+	user = &ppdu_desc->user[usr_idx];
+	peer = dp_tx_cap_peer_find_by_id(pdev->soc, user->peer_id);
 
 	if (!peer)
 		return;
 
-	dp_peer_tx_cap_tid_queue_flush_tlv(pdev, peer, ppdu_desc);
+	dp_peer_tx_cap_tid_queue_flush_tlv(pdev, peer, ppdu_desc, usr_idx);
 
 	dp_tx_cap_peer_unref_del(peer);
 	return;
 }
 
-static void dp_ppdu_queue_free(struct cdp_tx_completion_ppdu *ppdu_desc)
-{
-	int i;
-	qdf_nbuf_t mpdu_nbuf = NULL;
-
-	for (i = 0; i < ppdu_desc->user[0].ba_size; i++) {
-		mpdu_nbuf = ppdu_desc->mpdus[i];
-		if (mpdu_nbuf)
-			qdf_nbuf_free(mpdu_nbuf);
-	}
-
-	qdf_nbuf_queue_free(&ppdu_desc->mpdu_q);
-	qdf_mem_free(ppdu_desc->mpdus);
-	ppdu_desc->mpdus = NULL;
-
-	return;
-}
-
 /**
  * dp_check_ppdu_and_deliver(): Check PPDUs for any holes and deliver
  * to upper layer if complete
  * @pdev: DP pdev handle
- * @nbuf_ppdu_desc_list: ppdu_desc_list per sched cmd id
+ * @nbuf_ppdu_list: ppdu_desc_list per sched cmd id
  * @ppdu_desc_cnt: number of ppdu_desc_cnt
  *
  * return: status
  */
 static void
 dp_check_ppdu_and_deliver(struct dp_pdev *pdev,
-			  qdf_nbuf_t nbuf_ppdu_desc_list[],
+			  struct dp_tx_cap_nbuf_list nbuf_ppdu_list[],
 			  uint32_t ppdu_desc_cnt)
 {
 	uint32_t ppdu_id;
 	uint32_t desc_cnt;
 	qdf_nbuf_t tmp_nbuf;
-	struct cdp_tx_completion_ppdu *tmp_ppdu_desc;
 	struct dp_tx_tid *tx_tid  = NULL;
 	int i;
+	uint8_t max_num_users = 0;
+	uint8_t usr_idx;
+	struct dp_tx_cap_nbuf_list *ptr_nbuf_list;
 
 	for (desc_cnt = 0; desc_cnt < ppdu_desc_cnt; desc_cnt++) {
 		struct cdp_tx_completion_ppdu *ppdu_desc;
+		struct cdp_tx_completion_ppdu_user *user;
 		uint32_t num_mpdu;
 		uint16_t start_seq, seq_no = 0;
 		int i;
 		qdf_nbuf_t mpdu_nbuf;
 		struct dp_peer *peer;
 		uint8_t type;
+		uint8_t subtype;
+		uint8_t usr_type;
 		uint32_t mpdus_tried;
+		uint8_t num_users;
+		qdf_nbuf_t nbuf_ppdu;
+		bool is_bar_frm_with_data = false;
 
-		if (!nbuf_ppdu_desc_list[desc_cnt])
+		ptr_nbuf_list = &nbuf_ppdu_list[desc_cnt];
+
+		if (!dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list)) {
+			if (ptr_nbuf_list->nbuf_ppdu)
+				qdf_assert_always(0);
 			continue;
+		}
 
+		nbuf_ppdu = ptr_nbuf_list->nbuf_ppdu;
 		ppdu_desc = (struct cdp_tx_completion_ppdu *)
-			qdf_nbuf_data(nbuf_ppdu_desc_list[desc_cnt]);
+			qdf_nbuf_data(nbuf_ppdu);
 
 		ppdu_id = ppdu_desc->ppdu_id;
-		type = (ppdu_desc->frame_ctrl & IEEE80211_FC0_TYPE_MASK) >>
-			IEEE80211_FC0_TYPE_SHIFT;
+		num_users = ppdu_desc->num_users;
 
 		if (ppdu_desc->is_flush) {
-			dp_tx_ppdu_stats_flush(pdev, ppdu_desc);
-			tmp_nbuf = nbuf_ppdu_desc_list[desc_cnt];
-			nbuf_ppdu_desc_list[desc_cnt] = NULL;
-			qdf_nbuf_free(tmp_nbuf);
+			dp_tx_ppdu_stats_flush(pdev, ppdu_desc, 0);
+			dp_ppdu_desc_free_all(ptr_nbuf_list, num_users);
 			continue;
 		}
 
-		if ((ppdu_desc->frame_type == CDP_PPDU_FTYPE_CTRL) ||
+		if (max_num_users < ppdu_desc->num_users)
+			max_num_users = ppdu_desc->num_users;
+
+		type = (ppdu_desc->frame_ctrl & IEEE80211_FC0_TYPE_MASK);
+		subtype = (ppdu_desc->frame_ctrl &
+			   IEEE80211_FC0_SUBTYPE_MASK);
+		usr_type = (ppdu_desc->user[0].frame_ctrl &
+			    IEEE80211_FC0_TYPE_MASK);
+
+		/* handle management frame */
+		if ((type != IEEE80211_FC0_TYPE_DATA) ||
 		    (ppdu_desc->htt_frame_type ==
-		     HTT_STATS_FTYPE_SGEN_QOS_NULL) ||
-		    (type != FRAME_CTRL_TYPE_DATA)) {
-			qdf_nbuf_t nbuf_ppdu = nbuf_ppdu_desc_list[desc_cnt];
+		     HTT_STATS_FTYPE_SGEN_MU_BAR) ||
+		    (ppdu_desc->htt_frame_type ==
+		     HTT_STATS_FTYPE_SGEN_QOS_NULL)) {
+			qdf_nbuf_t tmp_nbuf_ppdu;
 
-			if (dp_check_mgmt_ctrl_ppdu(pdev, nbuf_ppdu)) {
-				tmp_nbuf = nbuf_ppdu_desc_list[desc_cnt];
-				nbuf_ppdu_desc_list[desc_cnt] = NULL;
-				qdf_nbuf_free(tmp_nbuf);
-				continue;
-			}
-			nbuf_ppdu_desc_list[desc_cnt] = NULL;
-			continue;
-		}
-
-		peer = dp_tx_cap_peer_find_by_id(pdev->soc,
-						 ppdu_desc->user[0].peer_id);
-		if (!peer) {
-			tmp_nbuf = nbuf_ppdu_desc_list[desc_cnt];
-			nbuf_ppdu_desc_list[desc_cnt] = NULL;
-			qdf_nbuf_queue_free(&ppdu_desc->mpdu_q);
-			qdf_nbuf_free(tmp_nbuf);
-			continue;
-		}
-
-		tx_tid = &peer->tx_capture.tx_tid[ppdu_desc->user[0].tid];
-
-		ppdu_id = ppdu_desc->ppdu_id;
-
-		/* find mpdu tried is same as success mpdu */
-
-		num_mpdu = ppdu_desc->user[0].mpdu_success;
-
-		/* ba_size is updated in BA bitmap TLVs, which are not received
-		 * in case of non-QoS TID.
-		 */
-		if (qdf_unlikely(ppdu_desc->user[0].tid == DP_NON_QOS_TID)) {
-			ppdu_desc->user[0].ba_size = 1;
-			ppdu_desc->user[0].last_enq_seq =
-				ppdu_desc->user[0].start_seq;
-		}
-
-		if (ppdu_desc->user[0].ba_size == 0)
-			ppdu_desc->user[0].ba_size = 1;
-
-		/* find list of missing sequence */
-		ppdu_desc->mpdus = qdf_mem_malloc(sizeof(qdf_nbuf_t) *
-			ppdu_desc->user[0].ba_size);
-
-		if (ppdu_desc->frame_type == CDP_PPDU_FTYPE_BAR)
-			dp_send_dummy_mpdu_info_to_stack(pdev,
-							 ppdu_desc);
-
-		if (qdf_unlikely(!ppdu_desc->mpdus)) {
-			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-				  QDF_TRACE_LEVEL_FATAL,
-				  "%s: ppdu_desc->mpdus allocation failed",
-				  __func__);
-			tmp_nbuf = nbuf_ppdu_desc_list[desc_cnt];
-			nbuf_ppdu_desc_list[desc_cnt] = NULL;
-			qdf_nbuf_queue_free(&ppdu_desc->mpdu_q);
-			qdf_nbuf_free(tmp_nbuf);
-			dp_tx_cap_peer_unref_del(peer);
-			continue;
-		}
-
-		if (qdf_unlikely(ppdu_desc->user[0].ba_size >
-		    CDP_BA_256_BIT_MAP_SIZE_DWORDS *
-		    SEQ_SEG_SZ_BITS(ppdu_desc->user[0].failed_bitmap))) {
-			dp_tx_cap_peer_unref_del(peer);
-			qdf_assert_always(0);
-			return;
-		}
-		/* Fill seq holes within current schedule list */
-		start_seq = ppdu_desc->user[0].start_seq;
-		mpdus_tried = ppdu_desc->user[0].mpdu_tried_mcast +
-			ppdu_desc->user[0].mpdu_tried_ucast;
-		for (i = 0; (i < ppdu_desc->user[0].ba_size) && mpdus_tried;
-		     i++) {
-			if (qdf_likely(ppdu_desc->user[0].tid !=
-			    DP_NON_QOS_TID) &&
-			    !(SEQ_BIT(ppdu_desc->user[0].enq_bitmap, i)))
-				continue;
-			mpdus_tried--;
-			/* missed seq number */
-			seq_no = start_seq + i;
-
-			/* Fill failed MPDUs in AMPDU if they're available in
-			 * subsequent PPDUs in current burst schedule. This
-			 * is not applicable for non-QoS TIDs (no AMPDUs)
+			tmp_nbuf_ppdu = nbuf_ppdu;
+			/*
+			 * take reference of ppdu_desc if the htt_frame_type is
+			 * HTT_STATS_FTYPE_SGEN_MU_BAR, as there will be
+			 * corresponding data frame
 			 */
-			if (qdf_likely(ppdu_desc->user[0].tid !=
-			    DP_NON_QOS_TID) &&
-			    !(SEQ_BIT(ppdu_desc->user[0].failed_bitmap, i))) {
-				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-					  QDF_TRACE_LEVEL_DEBUG,
-					  "%s:find seq %d in next ppdu %d",
-					  __func__, seq_no,
-					  ppdu_desc_cnt);
-
-				mpdu_nbuf = get_mpdu_clone_from_next_ppdu(
-						nbuf_ppdu_desc_list +
-						desc_cnt,
-						ppdu_desc_cnt -
-						desc_cnt, seq_no,
-						ppdu_desc->user[0].peer_id,
-						ppdu_id);
-
-				/* check mpdu_nbuf NULL */
-				if (!mpdu_nbuf) {
-					ppdu_desc->pending_retries++;
+			if (((type == IEEE80211_FC0_TYPE_CTL) &&
+			     (subtype == IEEE80211_FC0_SUBTYPE_BAR) &&
+			     (usr_type == IEEE80211_FC0_TYPE_DATA)) ||
+			    (ppdu_desc->htt_frame_type ==
+			     HTT_STATS_FTYPE_SGEN_MU_BAR)) {
+				/*
+				 * clonning ppdu_desc additional reference as
+				 * handling data frame
+				 */
+				tmp_nbuf_ppdu = qdf_nbuf_clone(nbuf_ppdu);
+				if (qdf_unlikely(!tmp_nbuf_ppdu)) {
+					qdf_assert_always(0);
 					continue;
 				}
-				ppdu_desc->mpdus[seq_no - start_seq] =
-					mpdu_nbuf;
-				SEQ_SEG(ppdu_desc->user[0].failed_bitmap, i) |=
-				SEQ_SEG_MSK(ppdu_desc->user[0].failed_bitmap[0],
-					    i);
+
+				dp_tx_cap_nbuf_list_inc_ref(ptr_nbuf_list);
+				is_bar_frm_with_data = true;
+			}
+
+			if (dp_check_mgmt_ctrl_ppdu(pdev, tmp_nbuf_ppdu,
+						    is_bar_frm_with_data)) {
+				dp_tx_cap_nbuf_list_dec_ref(ptr_nbuf_list);
+				qdf_nbuf_free(tmp_nbuf_ppdu);
 			} else {
-				/* any error case we need to handle */
-				mpdu_nbuf = qdf_nbuf_queue_remove(
-					&ppdu_desc->mpdu_q);
-				/* check mpdu_nbuf NULL */
-				if (!mpdu_nbuf)
-					continue;
-
-				ppdu_desc->mpdus[seq_no - start_seq] =
-					mpdu_nbuf;
+				dp_tx_cap_nbuf_list_dec_ref(ptr_nbuf_list);
 			}
+
+			if (!is_bar_frm_with_data)
+				continue;
 		}
 
-		/* It is possible that enq_bitmap received has more bits than
-		 * actual mpdus tried if HW was unable to send all MPDUs, and
-		 * last_enq_seq and ba_size should be adjusted in that case
+		/*
+		 * process only data frame and other
 		 */
-		if (i < ppdu_desc->user[0].ba_size) {
-			ppdu_desc->user[0].last_enq_seq = seq_no;
-			ppdu_desc->user[0].ba_size = seq_no - start_seq + 1;
-		}
+		for (usr_idx = 0; usr_idx < num_users; usr_idx++) {
+			uint32_t mpdu_enq = 0;
+			uint32_t mpdu_tried = 0;
 
-		dp_tx_cap_peer_unref_del(peer);
+			if (!ptr_nbuf_list->nbuf_ppdu ||
+			    !dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list))
+				continue;
 
-		if ((ppdu_desc->pending_retries == 0) &&
-		    qdf_nbuf_is_queue_empty(&tx_tid->pending_ppdu_q)) {
-			dp_send_data_to_stack(pdev, ppdu_desc);
-			dp_ppdu_queue_free(ppdu_desc);
-			tmp_nbuf = nbuf_ppdu_desc_list[desc_cnt];
-			nbuf_ppdu_desc_list[desc_cnt] = NULL;
-			qdf_nbuf_free(tmp_nbuf);
-		}
-	}
+			nbuf_ppdu = ptr_nbuf_list->nbuf_ppdu;
 
-	for (i = 0; i < ppdu_desc_cnt; i++) {
-		uint32_t pending_ppdus;
-		struct cdp_tx_completion_ppdu *cur_ppdu_desc;
-		struct dp_peer *peer;
-		qdf_nbuf_queue_t head_ppdu;
-		uint16_t peer_id;
+			ppdu_desc = (struct cdp_tx_completion_ppdu *)
+					qdf_nbuf_data(nbuf_ppdu);
 
-		if (!nbuf_ppdu_desc_list[i])
-			continue;
-		cur_ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(
-			nbuf_ppdu_desc_list[i]);
-		if (!cur_ppdu_desc)
-			continue;
+			user = &ppdu_desc->user[usr_idx];
 
-		peer_id = cur_ppdu_desc->user[0].peer_id;
-		peer = dp_tx_cap_peer_find_by_id(pdev->soc, peer_id);
-		if (!peer) {
-			tmp_nbuf = nbuf_ppdu_desc_list[i];
-			nbuf_ppdu_desc_list[i] = NULL;
-			dp_ppdu_queue_free(cur_ppdu_desc);
-			qdf_nbuf_free(tmp_nbuf);
-			continue;
-		}
-		tx_tid = &peer->tx_capture.tx_tid[cur_ppdu_desc->user[0].tid];
-		qdf_nbuf_queue_init(&head_ppdu);
-		dp_tx_mon_proc_pending_ppdus(pdev, tx_tid,
-					     nbuf_ppdu_desc_list + i,
-					     ppdu_desc_cnt - i, &head_ppdu,
-					     peer_id);
+			if (user->delayed_ba || user->skip == 1)
+				continue;
 
-		if (qdf_nbuf_is_queue_empty(&tx_tid->pending_ppdu_q)) {
-			while ((tmp_nbuf = qdf_nbuf_queue_first(&head_ppdu))) {
-				cur_ppdu_desc =
-				(struct cdp_tx_completion_ppdu *)
-					qdf_nbuf_data(tmp_nbuf);
-				if (cur_ppdu_desc->pending_retries)
-					break;
-				dp_send_data_to_stack(pdev, cur_ppdu_desc);
-				dp_ppdu_queue_free(cur_ppdu_desc);
-				qdf_nbuf_queue_remove(&head_ppdu);
-				qdf_nbuf_free(tmp_nbuf);
+			peer = dp_tx_cap_peer_find_by_id(pdev->soc,
+							 user->peer_id);
+			if (!peer) {
+				dp_ppdu_desc_free(ptr_nbuf_list, usr_idx);
+				continue;
 			}
-		}
-		qdf_nbuf_queue_append(&tx_tid->pending_ppdu_q, &head_ppdu);
 
-		dp_tx_mon_proc_xretries(pdev, peer, tx_tid->tid);
+			tx_tid = &peer->tx_capture.tx_tid[user->tid];
+			ppdu_id = ppdu_desc->ppdu_id;
 
-		dp_tx_cap_peer_unref_del(peer);
-		pending_ppdus = qdf_nbuf_queue_len(&tx_tid->pending_ppdu_q);
-		if (pending_ppdus > MAX_PENDING_PPDUS) {
-			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-				  QDF_TRACE_LEVEL_FATAL,
-				  "pending ppdus (%d, %d) : %d\n",
-				  peer_id,
-				  tx_tid->tid, pending_ppdus);
-			tmp_nbuf =
-				qdf_nbuf_queue_remove(&tx_tid->pending_ppdu_q);
-			if (qdf_unlikely(!tmp_nbuf)) {
+			/* find mpdu tried is same as success mpdu */
+			num_mpdu = user->mpdu_success;
+
+			/*
+			 * ba_size is updated in BA bitmap TLVs,
+			 * which are not received
+			 * in case of non-QoS TID.
+			 */
+			if (qdf_unlikely(user->tid == DP_NON_QOS_TID)) {
+				user->ba_size = 1;
+				user->last_enq_seq = user->start_seq;
+			}
+
+			if (user->ba_size == 0)
+				user->ba_size = 1;
+
+			/* find list of missing sequence */
+			user->mpdus = qdf_mem_malloc(sizeof(qdf_nbuf_t) *
+						     user->ba_size);
+
+			if (qdf_unlikely(!user->mpdus)) {
+				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+					  QDF_TRACE_LEVEL_FATAL,
+					  "%s: ppdu_desc->mpdus allocation failed",
+					  __func__);
+				dp_ppdu_desc_free_all(ptr_nbuf_list, num_users);
+				dp_tx_cap_peer_unref_del(peer);
+				dp_print_pdev_tx_capture_stats(pdev);
 				qdf_assert_always(0);
 				return;
 			}
 
-			tmp_ppdu_desc = (struct cdp_tx_completion_ppdu *)
-				qdf_nbuf_data(tmp_nbuf);
-			dp_send_data_to_stack(pdev, tmp_ppdu_desc);
-			dp_ppdu_queue_free(tmp_ppdu_desc);
-			qdf_nbuf_free(tmp_nbuf);
-			pdev->tx_capture.pend_ppdu_dropped++;
+			if (qdf_unlikely(user->ba_size >
+			    CDP_BA_256_BIT_MAP_SIZE_DWORDS *
+				SEQ_SEG_SZ_BITS(user->failed_bitmap))) {
+				dp_tx_cap_peer_unref_del(peer);
+				qdf_assert_always(0);
+				return;
+			}
+			/* Fill seq holes within current schedule list */
+			start_seq = user->start_seq;
+			seq_no = 0;
+			mpdus_tried = user->mpdu_tried_mcast +
+				user->mpdu_tried_ucast;
+
+			for (i = 0; (i < user->ba_size) && mpdus_tried; i++) {
+				if (qdf_likely(user->tid != DP_NON_QOS_TID) &&
+				    !(SEQ_BIT(user->enq_bitmap, i)))
+					continue;
+				mpdus_tried--;
+				/* missed seq number */
+				seq_no = start_seq + i;
+
+				/*
+				 * Fill failed MPDUs in AMPDU if they're
+				 * available in subsequent PPDUs in current
+				 * burst schedule. This is not applicable
+				 * for non-QoS TIDs (no AMPDUs)
+				 */
+				if (qdf_likely(user->tid != DP_NON_QOS_TID) &&
+				    !(SEQ_BIT(user->failed_bitmap, i))) {
+					uint8_t seq_idx;
+
+					QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+						  QDF_TRACE_LEVEL_DEBUG,
+						  "%s:find seq %d in next ppdu %d",
+						  __func__, seq_no,
+						  ppdu_desc_cnt);
+
+					mpdu_nbuf =
+						get_mpdu_clone_from_next_ppdu(
+							nbuf_ppdu_list +
+							desc_cnt,
+							ppdu_desc_cnt -
+							desc_cnt, seq_no,
+							user->peer_id,
+							ppdu_id, usr_idx);
+
+					seq_idx = seq_no - start_seq;
+					/* check mpdu_nbuf NULL */
+					if (!mpdu_nbuf) {
+						user->mpdus[seq_idx] = NULL;
+						user->pending_retries++;
+						continue;
+					}
+
+					dp_tx_cap_stats_mpdu_update(peer,
+							PEER_MPDU_CLONE, 1);
+					user->mpdus[seq_idx] = mpdu_nbuf;
+
+					SEQ_SEG(user->failed_bitmap, i) |=
+					SEQ_SEG_MSK(user->failed_bitmap[0], i);
+				} else {
+					qdf_nbuf_queue_t *tmp_q;
+
+					tmp_q = &user->mpdu_q;
+					/* any error case we need to handle */
+					mpdu_nbuf =
+						qdf_nbuf_queue_remove(tmp_q);
+					/* check mpdu_nbuf NULL */
+					if (!mpdu_nbuf)
+						continue;
+
+					user->mpdus[seq_no - start_seq] =
+						mpdu_nbuf;
+					dp_tx_cap_stats_mpdu_update(peer,
+							PEER_MPDU_ARR, 1);
+				}
+			}
+
+			mpdu_tried = user->mpdu_tried_ucast +
+					user->mpdu_tried_mcast;
+			for (i = 0; i < CDP_BA_256_BIT_MAP_SIZE_DWORDS; i++)
+				mpdu_enq +=
+					get_number_of_1s(user->enq_bitmap[i]);
+
+			if (mpdu_tried > mpdu_enq)
+				dp_ppdu_desc_debug_print(ppdu_desc, usr_idx,
+							 __func__, __LINE__);
+
+			/*
+			 * It is possible that enq_bitmap received has
+			 * more bits than actual mpdus tried if HW was
+			 * unable to send all MPDUs, and last_enq_seq
+			 * and ba_size should be adjusted in that case
+			 */
+			if (i < user->ba_size) {
+				user->last_enq_seq = seq_no;
+				user->ba_size = seq_no - start_seq + 1;
+			}
+
+			dp_tx_cap_peer_unref_del(peer);
 		}
 	}
+
+	for (usr_idx = 0; usr_idx < max_num_users; usr_idx++) {
+		for (i = 0; i < ppdu_desc_cnt; i++) {
+			uint32_t pending_ppdus;
+			struct cdp_tx_completion_ppdu *cur_ppdu_desc;
+			struct cdp_tx_completion_ppdu_user *cur_user;
+			struct dp_peer *peer;
+			qdf_nbuf_queue_t head_ppdu;
+			uint16_t peer_id;
+
+			ptr_nbuf_list = &nbuf_ppdu_list[i];
+
+			if (!ptr_nbuf_list->nbuf_ppdu ||
+			    !dp_tx_cap_nbuf_list_get_ref(ptr_nbuf_list))
+				continue;
+
+			cur_ppdu_desc = (struct cdp_tx_completion_ppdu *)
+					qdf_nbuf_data(ptr_nbuf_list->nbuf_ppdu);
+
+			if (!cur_ppdu_desc)
+				continue;
+
+			if (usr_idx >= cur_ppdu_desc->num_users)
+				continue;
+
+			cur_user = &cur_ppdu_desc->user[usr_idx];
+			if (cur_user->delayed_ba == 1 || cur_user->skip == 1)
+				continue;
+
+			peer_id = cur_ppdu_desc->user[usr_idx].peer_id;
+			peer = dp_tx_cap_peer_find_by_id(pdev->soc, peer_id);
+			if (!peer) {
+				dp_ppdu_desc_free(ptr_nbuf_list, usr_idx);
+				continue;
+			}
+			tx_tid = &peer->tx_capture.tx_tid[cur_user->tid];
+			qdf_nbuf_queue_init(&head_ppdu);
+			dp_tx_mon_proc_pending_ppdus(pdev, tx_tid,
+						     nbuf_ppdu_list + i,
+						     ppdu_desc_cnt - i,
+						     &head_ppdu,
+						     cur_user->peer_id,
+						     usr_idx);
+
+			if (qdf_nbuf_is_queue_empty(&tx_tid->pending_ppdu_q)) {
+				while ((tmp_nbuf =
+					qdf_nbuf_queue_first(&head_ppdu))) {
+					cur_ppdu_desc =
+					(struct cdp_tx_completion_ppdu *)
+						qdf_nbuf_data(tmp_nbuf);
+					cur_user =
+						&cur_ppdu_desc->user[usr_idx];
+
+					if (cur_user->pending_retries)
+						break;
+					dp_send_data_to_stack(pdev,
+							      cur_ppdu_desc,
+							      usr_idx);
+					dp_ppdu_queue_free(tmp_nbuf, usr_idx);
+					qdf_nbuf_queue_remove(&head_ppdu);
+					qdf_nbuf_free(tmp_nbuf);
+				}
+			}
+			qdf_nbuf_queue_append(&tx_tid->pending_ppdu_q,
+					      &head_ppdu);
+
+			dp_tx_mon_proc_xretries(pdev, peer, tx_tid->tid);
+
+			pending_ppdus =
+				qdf_nbuf_queue_len(&tx_tid->pending_ppdu_q);
+			if (pending_ppdus > MAX_PENDING_PPDUS) {
+				struct cdp_tx_completion_ppdu *tmp_ppdu_desc;
+				uint8_t tmp_usr_idx;
+				qdf_nbuf_queue_t *tmp_ppdu_q;
+
+				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+					  QDF_TRACE_LEVEL_ERROR,
+					  "pending ppdus (%d, %d) : %d\n",
+					  peer_id,
+					  tx_tid->tid, pending_ppdus);
+				tmp_ppdu_q = &tx_tid->pending_ppdu_q;
+				tmp_nbuf = qdf_nbuf_queue_remove(tmp_ppdu_q);
+				if (qdf_unlikely(!tmp_nbuf)) {
+					qdf_assert_always(0);
+					return;
+				}
+
+				tmp_ppdu_desc =
+					(struct cdp_tx_completion_ppdu *)
+					qdf_nbuf_data(tmp_nbuf);
+				tmp_usr_idx = dp_tx_find_usr_idx_from_peer_id(
+						tmp_ppdu_desc, peer_id);
+				dp_send_data_to_stack(pdev, tmp_ppdu_desc,
+						      tmp_usr_idx);
+				dp_ppdu_queue_free(tmp_nbuf, tmp_usr_idx);
+				qdf_nbuf_free(tmp_nbuf);
+				pdev->tx_capture.pend_ppdu_dropped++;
+			}
+			dp_tx_cap_peer_unref_del(peer);
+		}
+	}
+}
+
+static uint32_t
+dp_tx_cap_proc_per_ppdu_info(struct dp_pdev *pdev, qdf_nbuf_t nbuf_ppdu,
+			     struct dp_tx_cap_nbuf_list nbuf_ppdu_list[],
+			     uint32_t ppdu_desc_cnt)
+{
+	struct dp_tx_cap_nbuf_list *ptr_nbuf_list;
+	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
+	struct dp_peer *peer = NULL;
+	qdf_nbuf_queue_t head_msdu;
+	qdf_nbuf_queue_t head_xretries;
+	uint32_t retries = 0;
+	uint32_t ret = 0;
+	uint32_t start_tsf = 0;
+	uint32_t end_tsf = 0;
+	uint32_t bar_start_tsf = 0;
+	uint32_t bar_end_tsf = 0;
+	uint16_t tid = 0;
+	uint32_t num_msdu = 0;
+	uint32_t qlen = 0;
+	uint16_t peer_id;
+	uint8_t type, subtype;
+	uint8_t usr_idx;
+	bool is_bar_frm_with_data = false;
+	uint8_t usr_type;
+	uint8_t usr_subtype;
+
+	qdf_nbuf_queue_init(&head_msdu);
+	qdf_nbuf_queue_init(&head_xretries);
+
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(nbuf_ppdu);
+	type = (ppdu_desc->frame_ctrl &
+		IEEE80211_FC0_TYPE_MASK);
+	subtype = (ppdu_desc->frame_ctrl &
+		   IEEE80211_FC0_SUBTYPE_MASK);
+
+	if ((type == IEEE80211_FC0_TYPE_DATA) &&
+	    (subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) &&
+	    (ppdu_desc->htt_frame_type ==
+	     HTT_STATS_FTYPE_TIDQ_DATA_SU)) {
+		ppdu_desc->htt_frame_type =
+			HTT_STATS_FTYPE_SGEN_QOS_NULL;
+	}
+
+	usr_type = (ppdu_desc->user[0].frame_ctrl &
+		    IEEE80211_FC0_TYPE_MASK);
+	usr_subtype = (ppdu_desc->user[0].frame_ctrl &
+		       IEEE80211_FC0_SUBTYPE_MASK);
+
+	if (((type == IEEE80211_FC0_TYPE_CTL) &&
+	     (subtype == IEEE80211_FC0_SUBTYPE_BAR) &&
+	     (usr_type == IEEE80211_FC0_TYPE_DATA)) ||
+	    ppdu_desc->htt_frame_type == HTT_STATS_FTYPE_SGEN_MU_BAR)
+		is_bar_frm_with_data = true;
+
+	ptr_nbuf_list = &nbuf_ppdu_list[ppdu_desc_cnt];
+
+	/* ppdu start timestamp */
+	start_tsf = ppdu_desc->ppdu_start_timestamp;
+	end_tsf = ppdu_desc->ppdu_end_timestamp;
+	bar_start_tsf = ppdu_desc->bar_ppdu_start_timestamp;
+	bar_end_tsf = ppdu_desc->bar_ppdu_end_timestamp;
+
+	if (((ppdu_desc->frame_type == CDP_PPDU_FTYPE_DATA) &&
+	     (ppdu_desc->htt_frame_type !=
+	      HTT_STATS_FTYPE_SGEN_QOS_NULL)) ||
+		is_bar_frm_with_data) {
+		uint32_t mpdu_suc;
+		uint32_t mpdu_tri;
+		uint8_t ref_cnt = 0;
+		uint8_t num_users = ppdu_desc->num_users;
+		struct dp_tx_tid *tx_tid;
+		struct cdp_tx_completion_ppdu *xretry_ppdu;
+		struct cdp_tx_completion_ppdu_user *xretry_user;
+		struct cdp_tx_completion_ppdu_user *user;
+		qdf_nbuf_queue_t *mpdu_q;
+		qdf_nbuf_queue_t *x_mpdu_q;
+
+		for (usr_idx = 0; usr_idx < num_users;
+		     usr_idx++) {
+			uint32_t ppdu_id;
+
+			peer = NULL;
+			user = &ppdu_desc->user[usr_idx];
+			if (usr_idx + 1 != num_users)
+				qdf_nbuf_ref(nbuf_ppdu);
+
+			if (user->delayed_ba == 1) {
+				user->skip = 1;
+				goto free_nbuf_dec_ref;
+			}
+
+			peer_id = user->peer_id;
+			peer = dp_tx_cap_peer_find_by_id(pdev->soc,
+							 peer_id);
+			/**
+			 * peer can be NULL
+			 */
+			if (!peer) {
+				user->skip = 1;
+				goto free_nbuf_dec_ref;
+			}
+
+			/**
+			 * check whether it is bss peer,
+			 * if bss_peer no need to process
+			 * further check whether tx_capture
+			 * feature is enabled for this peer
+			 * or globally for all peers
+			 */
+			if (peer->bss_peer ||
+			    !dp_peer_or_pdev_tx_cap_enabled(pdev,
+				NULL, peer->mac_addr.raw)) {
+				user->skip = 1;
+				goto free_nbuf_dec_ref;
+			}
+
+			/* update failed bitmap */
+			dp_process_ppdu_stats_update_failed_bitmap(
+				pdev, user, ppdu_desc->ppdu_id,
+				CDP_BA_256_BIT_MAP_SIZE_DWORDS);
+			/* print the bit map */
+			dp_tx_print_bitmap(pdev, ppdu_desc,
+					   usr_idx,
+					   ppdu_desc->ppdu_id);
+			if (user->tid > DP_MAX_TIDS) {
+				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+					  QDF_TRACE_LEVEL_ERROR,
+					  "%s: ppdu[%d] peer_id[%d] TID[%d] > NON_QOS_TID!",
+					  __func__,
+					  ppdu_desc->ppdu_id,
+					  user->peer_id,
+					  user->tid);
+
+				user->skip = 1;
+				goto free_nbuf_dec_ref;
+			}
+
+			if (is_bar_frm_with_data)
+				ppdu_id = ppdu_desc->bar_ppdu_id;
+			else
+				ppdu_id = ppdu_desc->ppdu_id;
+
+			tid = user->tid;
+			num_msdu = user->num_msdu;
+
+dequeue_msdu_again:
+			/*
+			 * retrieve msdu buffer based on ppdu_id & tid
+			 * based msdu queue and store it in local queue
+			 * sometimes, wbm comes later than per ppdu
+			 * stats. Assumption: all packets are SU,
+			 * and packets comes in order
+			 */
+			ret = dp_tx_msdu_dequeue(peer,
+						 ppdu_id,
+						 tid,
+						 num_msdu,
+						 &head_msdu,
+						 &head_xretries,
+						 start_tsf,
+						 end_tsf);
+
+			if (!ret && (++retries < 2)) {
+				/* wait for wbm to complete */
+				qdf_mdelay(2);
+				goto dequeue_msdu_again;
+			}
+
+			/*
+			 * restitch mpdu from xretry msdu
+			 * xretry msdu queue empty check is
+			 * done inside restitch function
+			 */
+			tx_tid = &peer->tx_capture.tx_tid[tid];
+			xretry_ppdu = &tx_tid->xretry_ppdu;
+			xretry_user = &xretry_ppdu->user[0];
+			xretry_ppdu->ppdu_id =
+			peer->tx_capture.tx_wifi_ppdu_id;
+			x_mpdu_q = &xretry_user->mpdu_q;
+
+			/* Restitch MPDUs from xretry MSDUs */
+			dp_tx_mon_restitch_mpdu(pdev, peer,
+						xretry_ppdu,
+						&head_xretries,
+						x_mpdu_q, 0);
+
+			qlen = qdf_nbuf_queue_len(x_mpdu_q);
+			dp_tx_cap_stats_mpdu_update(peer, PEER_MPDU_RESTITCH,
+						    qlen);
+
+			if (qdf_nbuf_is_queue_empty(
+						&head_msdu)) {
+				user->skip = 1;
+				goto free_nbuf_dec_ref;
+			}
+
+			mpdu_q = &user->mpdu_q;
+			/*
+			 * now head_msdu hold - msdu list for
+			 * that particular ppdu_id, restitch
+			 * mpdu from msdu and create a mpdu
+			 * queue
+			 */
+			dp_tx_mon_restitch_mpdu(pdev,
+						peer,
+						ppdu_desc,
+						&head_msdu,
+						mpdu_q,
+						usr_idx);
+			/*
+			 * sanity: free local head msdu queue
+			 * do we need this ?
+			 */
+			qdf_nbuf_queue_free(&head_msdu);
+			qlen = qdf_nbuf_queue_len(mpdu_q);
+			dp_tx_cap_stats_mpdu_update(peer, PEER_MPDU_RESTITCH,
+						    qlen);
+
+			if (!qlen) {
+				dp_ppdu_queue_free(nbuf_ppdu,
+						   usr_idx);
+				user->skip = 1;
+				goto free_nbuf_dec_ref;
+			}
+
+			mpdu_suc = user->mpdu_success;
+			mpdu_tri = user->mpdu_tried_ucast +
+				   user->mpdu_tried_mcast;
+
+			/* print ppdu_desc info for debugging purpose */
+			QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+				  QDF_TRACE_LEVEL_INFO,
+				  "%s: ppdu[%d] b_ppdu_id[%d] p_id[%d], tid[%d], n_mpdu[%d %d] n_msdu[%d] retr[%d] qlen[%d] tsf[%u - %u] b_tsf[%u - %u] dur[%u] seq[%d] ppdu_desc_cnt[%d]",
+				  __func__,
+				  ppdu_desc->ppdu_id,
+				  ppdu_desc->bar_ppdu_id,
+				  user->peer_id,
+				  user->tid,
+				  ppdu_desc->num_mpdu,
+				  mpdu_suc,
+				  ppdu_desc->num_msdu, retries,
+				  qlen,
+				  start_tsf, end_tsf,
+				  bar_start_tsf, bar_end_tsf,
+				  ppdu_desc->tx_duration,
+				  user->start_seq,
+				  ppdu_desc_cnt);
+
+			dp_tx_cap_stats_mpdu_update(peer, PEER_MPDU_SUCC,
+						    mpdu_suc);
+			dp_tx_cap_stats_mpdu_update(peer, PEER_MPDU_TRI,
+						    mpdu_tri);
+			dp_tx_cap_peer_unref_del(peer);
+			/* get reference count */
+			ref_cnt = qdf_nbuf_get_users(nbuf_ppdu);
+			continue;
+free_nbuf_dec_ref:
+			/* get reference before free */
+			ref_cnt = qdf_nbuf_get_users(nbuf_ppdu);
+			qdf_nbuf_free(nbuf_ppdu);
+			ref_cnt--;
+			if (peer)
+				dp_tx_cap_peer_unref_del(peer);
+			continue;
+		}
+
+		if (ref_cnt == 0)
+			return ppdu_desc_cnt;
+		ptr_nbuf_list->nbuf_ppdu = nbuf_ppdu;
+		dp_tx_cap_nbuf_list_update_ref(ptr_nbuf_list, ref_cnt);
+		ppdu_desc_cnt++;
+	} else {
+		/*
+		 * other packet frame also added to
+		 * descriptor list
+		 */
+		/* print ppdu_desc info for debugging purpose */
+		QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+			  QDF_TRACE_LEVEL_INFO_HIGH,
+			  "%s: ppdu[%d], p_id[%d], tid[%d], fctrl[0x%x 0x%x] ftype[%d] h_frm_t[%d] seq[%d] tsf[%u b %u] dur[%u]",
+			  __func__, ppdu_desc->ppdu_id,
+			  ppdu_desc->user[0].peer_id,
+			  ppdu_desc->user[0].tid,
+			  ppdu_desc->frame_ctrl,
+			  ppdu_desc->user[0].frame_ctrl,
+			  ppdu_desc->frame_type,
+			  ppdu_desc->htt_frame_type,
+			  ppdu_desc->user[0].start_seq,
+			  ppdu_desc->ppdu_start_timestamp,
+			  ppdu_desc->bar_ppdu_start_timestamp,
+			  ppdu_desc->tx_duration);
+
+		ptr_nbuf_list->nbuf_ppdu = nbuf_ppdu;
+		dp_tx_cap_nbuf_list_update_ref(ptr_nbuf_list,
+					       1);
+		ppdu_desc_cnt++;
+	}
+
+	return ppdu_desc_cnt;
 }
 
 /**
@@ -3517,10 +4673,12 @@
 	STAILQ_HEAD(, ppdu_info) sched_ppdu_queue;
 
 	struct ppdu_info *sched_ppdu_list_last_ptr;
-	qdf_nbuf_t *nbuf_ppdu_desc_list;
+	struct dp_tx_cap_nbuf_list *nbuf_ppdu_list;
+	struct dp_tx_cap_nbuf_list *ptr_nbuf_list;
 	qdf_nbuf_t tmp_nbuf;
+	qdf_nbuf_t nbuf_ppdu;
 	struct dp_pdev_tx_capture *ptr_tx_cap = &pdev->tx_capture;
-	qdf_nbuf_queue_t head_xretries;
+	size_t nbuf_list_sz;
 
 	STAILQ_INIT(&sched_ppdu_queue);
 	/* Move the PPDU entries to defer list */
@@ -3562,14 +4720,15 @@
 
 		ptr_tx_cap->ppdu_stats_defer_queue_depth -= ppdu_cnt;
 
-		nbuf_ppdu_desc_list =
-			(qdf_nbuf_t *) qdf_mem_malloc(sizeof(qdf_nbuf_t) *
-						      ppdu_cnt);
+		nbuf_list_sz = sizeof(struct dp_tx_cap_nbuf_list);
+		nbuf_ppdu_list = (struct dp_tx_cap_nbuf_list *)
+					qdf_mem_malloc(nbuf_list_sz * ppdu_cnt);
+
 		/*
 		 * if there is no memory allocated we need to free sched ppdu
 		 * list, no ppdu stats will be updated.
 		 */
-		if (!nbuf_ppdu_desc_list) {
+		if (!nbuf_ppdu_list) {
 			STAILQ_FOREACH_SAFE(sched_ppdu_info,
 					    &sched_ppdu_queue,
 					    ppdu_info_queue_elem,
@@ -3587,60 +4746,31 @@
 		STAILQ_FOREACH_SAFE(sched_ppdu_info,
 				    &sched_ppdu_queue,
 				    ppdu_info_queue_elem, tmp_ppdu_info) {
-			struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
-			struct dp_peer *peer = NULL;
-			qdf_nbuf_t nbuf;
-			uint32_t retries = 0;
-			uint32_t ret = 0;
-			qdf_nbuf_queue_t head_msdu;
-			uint32_t start_tsf = 0;
-			uint32_t end_tsf = 0;
-			uint16_t tid = 0;
-			uint32_t num_msdu = 0;
-			uint32_t qlen = 0;
-			uint16_t peer_id;
-			uint8_t type, subtype;
-
-			qdf_nbuf_queue_init(&head_msdu);
-			qdf_nbuf_queue_init(&head_xretries);
-
 			ppdu_info = sched_ppdu_info;
-			ppdu_desc = (struct cdp_tx_completion_ppdu *)
-				qdf_nbuf_data(ppdu_info->nbuf);
-			pdev->tx_ppdu_proc++;
+			pdev->stats.tx_ppdu_proc++;
 
+			/* update ppdu desc user stats */
 			dp_ppdu_desc_user_stats_update(pdev, ppdu_info);
 			/*
 			 * While processing/corelating Tx buffers, we should
 			 * hold the entire PPDU list for the give sched_cmdid
 			 * instead of freeing below.
 			 */
-			nbuf = ppdu_info->nbuf;
+			nbuf_ppdu = ppdu_info->nbuf;
 			qdf_mem_free(ppdu_info);
 
-			qdf_assert_always(nbuf);
+			qdf_assert_always(nbuf_ppdu);
 
-			ppdu_desc = (struct cdp_tx_completion_ppdu *)
-				qdf_nbuf_data(nbuf);
-			type = (ppdu_desc->frame_ctrl &
-				IEEE80211_FC0_TYPE_MASK);
-			subtype = (ppdu_desc->frame_ctrl &
-				   IEEE80211_FC0_SUBTYPE_MASK);
-
-			if ((type == IEEE80211_FC0_TYPE_DATA) &&
-			    (subtype == IEEE80211_FC0_SUBTYPE_QOS_NULL) &&
-			    (ppdu_desc->htt_frame_type ==
-			     HTT_STATS_FTYPE_TIDQ_DATA_SU)) {
-				ppdu_desc->htt_frame_type =
-					HTT_STATS_FTYPE_SGEN_QOS_NULL;
-			}
-
-			/* send WDI event */
+			/* check tx capture disable */
 			if (pdev->tx_capture_enabled ==
 			    CDP_TX_ENH_CAPTURE_DISABLED) {
+				struct cdp_tx_completion_ppdu *ppdu_desc;
+
 				if (!pdev->tx_capture.tx_cap_mode_flag)
 					dp_enh_tx_capture_disable(pdev);
 
+				ppdu_desc = (struct cdp_tx_completion_ppdu *)
+					qdf_nbuf_data(nbuf_ppdu);
 				/**
 				 * Deliver PPDU stats only for valid (acked)
 				 * data frames if sniffer mode is not enabled.
@@ -3654,7 +4784,7 @@
 					dp_wdi_event_handler(
 							WDI_EVENT_TX_PPDU_DESC,
 							pdev->soc,
-							nbuf,
+							nbuf_ppdu,
 							HTT_INVALID_PEER,
 							WDI_NO_VAL,
 							pdev->pdev_id);
@@ -3666,174 +4796,24 @@
 						dp_wdi_event_handler(
 							WDI_EVENT_TX_PPDU_DESC,
 							pdev->soc,
-							nbuf,
+							nbuf_ppdu,
 							HTT_INVALID_PEER,
 							WDI_NO_VAL,
 							pdev->pdev_id);
 					} else {
-						qdf_nbuf_free(nbuf);
+						qdf_nbuf_free(nbuf_ppdu);
 					}
 				}
 
 				continue;
-			}
-
-			/* Drop all type of MU frame */
-			if ((ppdu_desc->htt_frame_type ==
-			     HTT_STATS_FTYPE_TIDQ_DATA_MU) ||
-			    ((ppdu_desc->htt_frame_type >=
-			      HTT_STATS_FTYPE_SGEN_MU_BAR) &&
-			     (ppdu_desc->htt_frame_type <=
-			      HTT_STATS_FTYPE_SGEN_MU_BSR))) {
-				qdf_nbuf_free(nbuf);
-				continue;
-			}
-
-			if (((ppdu_desc->frame_type == CDP_PPDU_FTYPE_DATA) &&
-			     (ppdu_desc->htt_frame_type !=
-			      HTT_STATS_FTYPE_SGEN_QOS_NULL)) ||
-			    (ppdu_desc->num_mpdu &&
-			     ppdu_desc->frame_type == CDP_PPDU_FTYPE_BAR)) {
-				peer_id = ppdu_desc->user[0].peer_id;
-				peer = dp_tx_cap_peer_find_by_id(pdev->soc,
-								 peer_id);
-				/**
-				 * peer can be NULL
-				 */
-				if (!peer) {
-					qdf_nbuf_free(nbuf);
-					continue;
-				}
-
-				/**
-				 * check whether it is bss peer,
-				 * if bss_peer no need to process further
-				 * check whether tx_capture feature is enabled
-				 * for this peer or globally for all peers
-				 */
-				if (peer->bss_peer ||
-				    !dp_peer_or_pdev_tx_cap_enabled(pdev,
-				    peer, peer->mac_addr.raw)) {
-					dp_tx_cap_peer_unref_del(peer);
-					qdf_nbuf_free(nbuf);
-					continue;
-				}
-
-				/* print the bit map */
-				dp_tx_print_bitmap(pdev, ppdu_desc,
-						   0, ppdu_desc->ppdu_id);
-				if (ppdu_desc->user[0].tid > DP_MAX_TIDS) {
-					QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-						  QDF_TRACE_LEVEL_ERROR,
-						  "%s: ppdu[%d] peer_id[%d] TID[%d] > NON_QOS_TID!",
-						  __func__,
-						  ppdu_desc->ppdu_id,
-						  ppdu_desc->user[0].peer_id,
-						  ppdu_desc->user[0].tid);
-
-					dp_tx_cap_peer_unref_del(peer);
-					qdf_nbuf_free(nbuf);
-					continue;
-				}
-
-				tid = ppdu_desc->user[0].tid;
-dequeue_msdu_again:
-				num_msdu = ppdu_desc->user[0].num_msdu;
-				start_tsf = ppdu_desc->ppdu_start_timestamp;
-				end_tsf = ppdu_desc->ppdu_end_timestamp;
-				/*
-				 * retrieve msdu buffer based on ppdu_id & tid
-				 * based msdu queue and store it in local queue
-				 * sometimes, wbm comes later than per ppdu
-				 * stats. Assumption: all packets are SU,
-				 * and packets comes in order
-				 */
-				ret = dp_tx_msdu_dequeue(peer,
-							 ppdu_desc->ppdu_id,
-							 ppdu_desc->user[0].tid,
-							 num_msdu,
-							 &head_msdu,
-							 &head_xretries,
-							 start_tsf, end_tsf);
-
-				if (!ret && (++retries < 2)) {
-					/* wait for wbm to complete */
-					qdf_mdelay(2);
-					goto dequeue_msdu_again;
-				}
-
-				if (!qdf_nbuf_is_queue_empty(&head_xretries)) {
-					struct dp_tx_tid *tx_tid =
-						&peer->tx_capture.tx_tid[tid];
-					struct cdp_tx_completion_ppdu
-						*xretry_ppdu =
-						&tx_tid->xretry_ppdu;
-
-					xretry_ppdu->ppdu_id =
-					peer->tx_capture.tx_wifi_ppdu_id;
-					/* Restitch MPDUs from xretry MSDUs */
-					dp_tx_mon_restitch_mpdu(pdev, peer,
-								xretry_ppdu,
-								&head_xretries,
-								&xretry_ppdu->mpdu_q);
-				}
-				if (!qdf_nbuf_is_queue_empty(&head_msdu)) {
-					/*
-					 * now head_msdu hold - msdu list for
-					 * that particular ppdu_id, restitch
-					 * mpdu from msdu and create a mpdu
-					 * queue
-					 */
-					dp_tx_mon_restitch_mpdu(pdev, peer,
-								ppdu_desc,
-								&head_msdu,
-								&ppdu_desc->mpdu_q);
-					/*
-					 * sanity: free local head msdu queue
-					 * do we need this ?
-					 */
-					qdf_nbuf_queue_free(&head_msdu);
-					qlen =
-					qdf_nbuf_queue_len(&ppdu_desc->mpdu_q);
-					if (!qlen) {
-						qdf_nbuf_free(nbuf);
-						dp_tx_cap_peer_unref_del(peer);
-						continue;
-					}
-				} else {
-					qdf_nbuf_free(nbuf);
-					dp_tx_cap_peer_unref_del(peer);
-					continue;
-				}
-
-				nbuf_ppdu_desc_list[ppdu_desc_cnt++] = nbuf;
-
-				/* print ppdu_desc info for debugging purpose */
-				QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
-					  QDF_TRACE_LEVEL_INFO,
-					  "%s: ppdu[%d], p_id[%d], tid[%d], n_mpdu[%d %d] n_msdu[%d] retr[%d] qlen[%d] s_tsf[%u] dur[%u] seq[%d] [%d %d]",
-					  __func__, ppdu_desc->ppdu_id,
-					  ppdu_desc->user[0].peer_id,
-					  ppdu_desc->user[0].tid,
-					  ppdu_desc->num_mpdu,
-					  ppdu_desc->user[0].mpdu_success,
-					  ppdu_desc->num_msdu, retries,
-					  qlen,
-					  ppdu_desc->ppdu_start_timestamp,
-					  ppdu_desc->tx_duration,
-					  ppdu_desc->user[0].start_seq,
-					  ppdu_cnt,
-					  ppdu_desc_cnt);
-
-				dp_tx_cap_peer_unref_del(peer);
 			} else {
-				/*
-				 * other packet frame also added to
-				 * descriptor list
-				 */
-				nbuf_ppdu_desc_list[ppdu_desc_cnt++] = nbuf;
+				/* process ppdu_info on tx capture turned on */
+				ppdu_desc_cnt = dp_tx_cap_proc_per_ppdu_info(
+							pdev,
+							nbuf_ppdu,
+							nbuf_ppdu_list,
+							ppdu_desc_cnt);
 			}
-
 		}
 
 		/*
@@ -3841,12 +4821,27 @@
 		 * based on packet capture flags send mpdu info to upper stack
 		 */
 		if (ppdu_desc_cnt) {
-			dp_check_ppdu_and_deliver(pdev, nbuf_ppdu_desc_list,
+			uint32_t i;
+
+			dp_check_ppdu_and_deliver(pdev, nbuf_ppdu_list,
 						  ppdu_desc_cnt);
+
+			for (i = 0; i < ppdu_desc_cnt; i++) {
+				ptr_nbuf_list = &nbuf_ppdu_list[i];
+
+				if (dp_tx_cap_nbuf_list_get_ref(
+							ptr_nbuf_list)) {
+					QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE,
+						  QDF_TRACE_LEVEL_FATAL,
+						  "%s: %d missing handling of ppdu_desc ref_cnt:%d ,i : %d!!!\n",
+						  __func__, __LINE__,
+						  ptr_nbuf_list->ref_cnt, i);
+				}
+			}
 		}
 
 		qdf_spin_unlock(&ptr_tx_cap->config_lock);
-		qdf_mem_free(nbuf_ppdu_desc_list);
+		qdf_mem_free(nbuf_ppdu_list);
 
 		qdf_spin_lock(&pdev->tx_capture.config_lock);
 		if (!pdev->tx_capture.tx_cap_mode_flag)
@@ -3874,6 +4869,7 @@
 
 	ppdu_desc = (struct cdp_tx_completion_ppdu *)
 			qdf_nbuf_data(ppdu_info->nbuf);
+	ppdu_desc->tlv_bitmap = ppdu_info->tlv_bitmap;
 
 	qdf_spin_lock_bh(&pdev->tx_capture.ppdu_stats_lock);
 
@@ -4218,6 +5214,9 @@
 		dp_wdi_event_handler(WDI_EVENT_TX_DATA, pdev->soc,
 				     &tx_capture_info, HTT_INVALID_PEER,
 				     WDI_NO_VAL, pdev->pdev_id);
+
+		if (tx_capture_info.mpdu_nbuf)
+			qdf_nbuf_free(tx_capture_info.mpdu_nbuf);
 		return;
 	}
 
@@ -4280,6 +5279,8 @@
 			     &tx_capture_info, HTT_INVALID_PEER,
 			     WDI_NO_VAL, pdev->pdev_id);
 
+	if (tx_capture_info.mpdu_nbuf)
+		qdf_nbuf_free(tx_capture_info.mpdu_nbuf);
 }
 /**
  * dp_send_ack_frame_to_stack(): Function to generate BA or ACK frame and
diff --git a/dp/wifi3.0/dp_tx_capture.h b/dp/wifi3.0/dp_tx_capture.h
index 43edaf3..dc298e4 100644
--- a/dp/wifi3.0/dp_tx_capture.h
+++ b/dp/wifi3.0/dp_tx_capture.h
@@ -40,6 +40,37 @@
 #define RTS_INTERVAL 40
 
 #define MAX_MGMT_PEER_FILTER 16
+
+/* stats */
+enum CDP_PEER_MSDU_DESC {
+	PEER_MSDU_SUCC,
+	PEER_MSDU_ENQ,
+	PEER_MSDU_DEQ,
+	PEER_MSDU_FLUSH,
+	PEER_MSDU_DROP,
+	PEER_MSDU_XRETRY,
+	PEER_MSDU_DESC_MAX,
+};
+
+enum CDP_PEER_MPDU_DESC {
+	PEER_MPDU_TRI,
+	PEER_MPDU_SUCC,
+	PEER_MPDU_RESTITCH,
+	PEER_MPDU_ARR,
+	PEER_MPDU_CLONE,
+	PEER_MPDU_TO_STACK,
+	PEER_MPDU_DESC_MAX,
+};
+
+#ifdef WLAN_TX_PKT_CAPTURE_ENH_DEBUG
+struct dp_peer_tx_capture_stats {
+	/* mpdu success and restich count */
+	uint32_t mpdu[PEER_MPDU_DESC_MAX];
+	/*msdu success and restich count */
+	uint32_t msdu[PEER_MSDU_DESC_MAX];
+};
+#endif
+
 struct dp_peer_mgmt_list {
 	uint8_t mac_addr[QDF_MAC_ADDR_SIZE];
 	uint32_t mgmt_pkt_counter;
@@ -47,6 +78,11 @@
 	bool avail;
 };
 
+struct dp_tx_cap_nbuf_list {
+	qdf_nbuf_t nbuf_ppdu;
+	uint8_t ref_cnt;
+};
+
 struct dp_pdev_tx_capture {
 	/* For deferred PPDU status processing */
 	qdf_spinlock_t ppdu_stats_lock;
@@ -64,6 +100,7 @@
 	qdf_spinlock_t msdu_comp_q_list_lock;
 	uint32_t missed_ppdu_id;
 	uint32_t last_msdu_id;
+	uint16_t last_peer_id;
 	qdf_event_t miss_ppdu_event;
 	uint32_t ppdu_dropped;
 	uint32_t pend_ppdu_dropped;
@@ -81,10 +118,14 @@
 struct dp_tx_tid {
 	/* TID */
 	uint8_t tid;
+	/* peer id */
+	uint16_t peer_id;
 	/* max ppdu_id in a tid */
 	uint32_t max_ppdu_id;
 	/* tx_tid lock */
 	qdf_spinlock_t tid_lock;
+	qdf_spinlock_t tasklet_tid_lock;
+	qdf_nbuf_queue_t defer_msdu_q;
 	qdf_nbuf_queue_t msdu_comp_q;
 	qdf_nbuf_queue_t pending_ppdu_q;
 	struct cdp_tx_completion_ppdu xretry_ppdu;
@@ -109,9 +150,20 @@
 		struct ieee80211_frame_addr4 tx_wifi_addr4_hdr;
 		struct ieee80211_qosframe_addr4 tx_wifi_addr4_qos_hdr;
 	};
+#ifdef WLAN_TX_PKT_CAPTURE_ENH_DEBUG
+	struct dp_peer_tx_capture_stats stats;
+#endif
 };
 
 /*
+ * dp_peer_tid_peer_id_update() – update peer_id to tid structure
+ * @peer: Datapath peer
+ * @peer_id: peer_id
+ *
+ */
+void dp_peer_tid_peer_id_update(struct dp_peer *peer, uint16_t peer_id);
+
+/*
  * dp_peer_tid_queue_init() – Initialize ppdu stats queue per TID
  * @peer: Datapath peer
  *