qcacmn: Fix OFDMA stats counter

user info with delayed ba stats are stored and stats are updated later
on receiving BAR.

Change-Id: I574dd94d5a7db2160953b5d5d302eff18f4f8648
diff --git a/dp/inc/cdp_txrx_cmn_struct.h b/dp/inc/cdp_txrx_cmn_struct.h
index 90c17af..8dc063e 100644
--- a/dp/inc/cdp_txrx_cmn_struct.h
+++ b/dp/inc/cdp_txrx_cmn_struct.h
@@ -1152,6 +1152,57 @@
 };
 
 /**
+ * struct cdp_delayed_tx_completion_ppdu_user - Delayed Tx PPDU completion
+ * per-user information
+ * @frame_ctrl: frame control field in 802.11 header
+ * @qos_ctrl: QoS control field in 802.11 header
+ * @mpdu_tried: number of mpdus tried
+ * @ltf_size: ltf_size
+ * @stbc: stbc
+ * @he_re: he_re (range extension)
+ * @txbf: txbf
+ * @bw: Transmission bandwidth
+ *       <enum 2 transmit_bw_20_MHz>
+ *       <enum 3 transmit_bw_40_MHz>
+ *       <enum 4 transmit_bw_80_MHz>
+ *       <enum 5 transmit_bw_160_MHz>
+ * @nss: NSS 1,2, ...8
+ * @mcs: MCS index
+ * @preamble: preamble
+ * @gi: guard interval 800/400/1600/3200 ns
+ * @dcm: dcm
+ * @ldpc: ldpc
+ * @ru_start: RU start index
+ * @ru_tones: RU tones length
+ * @is_mcast: MCAST or UCAST
+ * @user_pos: user position
+ * @mu_group_id: mu group id
+ */
+struct cdp_delayed_tx_completion_ppdu_user {
+	uint32_t frame_ctrl:16,
+		 qos_ctrl:16;
+	uint32_t mpdu_tried_ucast:16,
+		mpdu_tried_mcast:16;
+	uint32_t ltf_size:2,
+		 stbc:1,
+		 he_re:1,
+		 txbf:4,
+		 bw:4,
+		 nss:4,
+		 mcs:4,
+		 preamble:4,
+		 gi:4,
+		 dcm:1,
+		 ldpc:1,
+		 delayed_ba:1;
+	uint16_t ru_start;
+	uint16_t ru_tones;
+	bool is_mcast;
+	uint32_t user_pos;
+	uint32_t mu_group_id;
+};
+
+/**
  * struct cdp_tx_completion_ppdu_user - Tx PPDU completion per-user information
  * @completion_status: completion status - OK/Filter/Abort/Timeout
  * @tid: TID number
@@ -1354,6 +1405,7 @@
  * @ppdu_id: PPDU Id
  * @ppdu_seq_id: ppdu sequence id for sojourn stats
  * @vdev_id: VAP Id
+ * @bar_num_users: BA response user count, based on completion common TLV
  * @num_users: Number of users
  * @num_mpdu: Number of MPDUs in PPDU
  * @num_msdu: Number of MSDUs in PPDU
@@ -1365,6 +1417,7 @@
  * @ppdu_start_timestamp: TSF at PPDU start
  * @ppdu_end_timestamp: TSF at PPDU end
  * @ack_timestamp: TSF at the reception of ACK
+ * @delayed_ba: Delayed ba flag
  * @user: per-User stats (array of per-user structures)
  * @mpdu_q: queue of mpdu in a ppdu
  */
@@ -1372,6 +1425,7 @@
 	uint32_t ppdu_id;
 	uint32_t ppdu_seq_id;
 	uint16_t vdev_id;
+	uint16_t bar_num_users;
 	uint32_t num_users;
 	uint8_t last_usr_index;
 	uint32_t num_mpdu:9,
@@ -1385,6 +1439,7 @@
 	uint32_t ppdu_start_timestamp;
 	uint32_t ppdu_end_timestamp;
 	uint32_t ack_timestamp;
+	bool delayed_ba;
 	struct cdp_tx_completion_ppdu_user user[CDP_MU_MAX_USERS];
 	qdf_nbuf_queue_t mpdu_q;
 };
diff --git a/dp/inc/cdp_txrx_stats_struct.h b/dp/inc/cdp_txrx_stats_struct.h
index e6eaca6..6cb31b7 100644
--- a/dp/inc/cdp_txrx_stats_struct.h
+++ b/dp/inc/cdp_txrx_stats_struct.h
@@ -127,6 +127,39 @@
 	(((nssb) & CDP_TXRX_RATECODE_NSS_MASK) << CDP_TXRX_RATECODE_NSS_LSB) | \
 	(((premb) & CDP_TXRX_RATECODE_PREM_MASK) << CDP_TXRX_RATECODE_PREM_LSB))
 
+/*
+ * cdp_tx_transmit_type: Transmit type index
+ * SU: SU Transmit type index
+ * MU_MIMO: MU_MIMO Transmit type index
+ * MU_OFDMA: MU_OFDMA Transmit type index
+ * MU_MIMO_OFDMA: MU MIMO OFDMA Transmit type index
+ */
+enum cdp_tx_transmit_type {
+	SU = 0,
+	MU_MIMO,
+	MU_OFDMA,
+	MU_MIMO_OFDMA,
+};
+
+/*
+ * cdp_ru_index: Different RU index
+ *
+ * RU_26_INDEX : 26-tone Resource Unit index
+ * RU_52_INDEX : 52-tone Resource Unit index
+ * RU_106_INDEX: 106-tone Resource Unit index
+ * RU_242_INDEX: 242-tone Resource Unit index
+ * RU_484_INDEX: 484-tone Resource Unit index
+ * RU_996_INDEX: 996-tone Resource Unit index
+ */
+enum cdp_ru_index {
+	RU_26_INDEX = 0,
+	RU_52_INDEX,
+	RU_106_INDEX,
+	RU_242_INDEX,
+	RU_484_INDEX,
+	RU_996_INDEX,
+};
+
 /* Different Packet Types */
 enum cdp_packet_type {
 	DOT11_A = 0,
@@ -383,6 +416,19 @@
 	uint32_t mcs_count[MAX_MCS];
 };
 
+/* struct cdp_tx_pkt_info - tx packet info
+ * num_msdu - successful msdu
+ * num_mpdu - successful mpdu from compltn common
+ * mpdu_tried - mpdu tried
+ *
+ * tx packet info counter field for mpdu success/tried and msdu
+ */
+struct cdp_tx_pkt_info {
+	uint32_t num_msdu;
+	uint32_t num_mpdu;
+	uint32_t mpdu_tried;
+};
+
 /* struct cdp_tx_stats - tx stats
  * @cdp_pkt_info comp_pkt: Pkt Info for which completions were received
  * @cdp_pkt_info ucast: Unicast Packet Count
@@ -452,11 +498,11 @@
  * @failed_retry_count: packets failed due to retry above 802.11 retry limit
  * @retry_count: packets successfully send after one or more retry
  * @multiple_retry_count: packets successfully sent after more than one retry
- * @transmit_type: tx transmit type
+ * @transmit_type: pkt info for tx transmit type
  * @mu_group_id: mumimo mu group id
  * @ru_start: RU start index
  * @ru_tones: RU tones size
- * @ru_loc: RU location 26/ 52/ 106/ 242/ 484 counter
+ * @ru_loc: pkt info for RU location 26/ 52/ 106/ 242/ 484 counter
  * @num_ppdu_cookie_valid : Number of comp received with valid ppdu cookie
  */
 struct cdp_tx_stats {
@@ -538,11 +584,12 @@
 	uint32_t multiple_retry_count;
 	uint32_t last_tx_rate_used;
 
-	uint32_t transmit_type[MAX_TRANSMIT_TYPES];
+	struct cdp_tx_pkt_info transmit_type[MAX_TRANSMIT_TYPES];
 	uint32_t mu_group_id[MAX_MU_GROUP_ID];
 	uint32_t ru_start;
 	uint32_t ru_tones;
-	uint32_t ru_loc[MAX_RU_LOCATIONS];
+	struct cdp_tx_pkt_info ru_loc[MAX_RU_LOCATIONS];
+
 	uint32_t num_ppdu_cookie_valid;
 };
 
@@ -1248,6 +1295,7 @@
  * @tx_comp_histogram: Number of Tx completions per interrupt
  * @rx_ind_histogram:  Number of Rx ring descriptors reaped per interrupt
  * @ppdu_stats_counter: ppdu stats counter
+ * @cdp_delayed_ba_not_recev: counter for delayed ba not received
  * @htt_tx_pdev_stats: htt pdev stats for tx
  * @htt_rx_pdev_stats: htt pdev stats for rx
  */
@@ -1293,6 +1341,7 @@
 	struct cdp_hist_tx_comp tx_comp_histogram;
 	struct cdp_hist_rx_ind rx_ind_histogram;
 	uint64_t ppdu_stats_counter[CDP_PPDU_STATS_MAX_TAG];
+	uint32_t cdp_delayed_ba_not_recev;
 
 	struct cdp_htt_tx_pdev_stats  htt_tx_pdev_stats;
 	struct cdp_htt_rx_pdev_stats  htt_rx_pdev_stats;
diff --git a/dp/wifi3.0/dp_htt.c b/dp/wifi3.0/dp_htt.c
index 349aca1..cbb4ccd 100644
--- a/dp/wifi3.0/dp_htt.c
+++ b/dp/wifi3.0/dp_htt.c
@@ -67,6 +67,108 @@
 
 #ifdef FEATURE_PERPKT_INFO
 /*
+ * dp_peer_copy_delay_stats() - copy ppdu stats to peer delayed stats.
+ * @peer: Datapath peer handle
+ * @ppdu: PPDU Descriptor
+ *
+ * Return: None
+ *
+ * on Tx data frame, we may get delayed ba set
+ * in htt_ppdu_stats_user_common_tlv. which mean we get Block Ack(BA) after we
+ * request Block Ack Request(BAR). Successful msdu is received only after Block
+ * Ack. To populate peer stats we need successful msdu(data frame).
+ * So we hold the Tx data stats on delayed_ba for stats update.
+ */
+static inline void
+dp_peer_copy_delay_stats(struct dp_peer *peer,
+			 struct cdp_tx_completion_ppdu_user *ppdu)
+{
+	struct dp_pdev *pdev;
+	struct dp_vdev *vdev;
+
+	if (peer->last_delayed_ba) {
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+			  "BA not yet recv for prev delayed ppdu[%d]\n",
+			  peer->last_delayed_ba_ppduid);
+		vdev = peer->vdev;
+		if (vdev) {
+			pdev = vdev->pdev;
+			pdev->stats.cdp_delayed_ba_not_recev++;
+		}
+	}
+
+	peer->delayed_ba_ppdu_stats.ltf_size = ppdu->ltf_size;
+	peer->delayed_ba_ppdu_stats.stbc = ppdu->stbc;
+	peer->delayed_ba_ppdu_stats.he_re = ppdu->he_re;
+	peer->delayed_ba_ppdu_stats.txbf = ppdu->txbf;
+	peer->delayed_ba_ppdu_stats.bw = ppdu->bw;
+	peer->delayed_ba_ppdu_stats.nss = ppdu->nss;
+	peer->delayed_ba_ppdu_stats.preamble = ppdu->preamble;
+	peer->delayed_ba_ppdu_stats.gi = ppdu->gi;
+	peer->delayed_ba_ppdu_stats.dcm = ppdu->dcm;
+	peer->delayed_ba_ppdu_stats.ldpc = ppdu->ldpc;
+	peer->delayed_ba_ppdu_stats.dcm = ppdu->dcm;
+	peer->delayed_ba_ppdu_stats.mpdu_tried_ucast = ppdu->mpdu_tried_ucast;
+	peer->delayed_ba_ppdu_stats.mpdu_tried_mcast = ppdu->mpdu_tried_mcast;
+	peer->delayed_ba_ppdu_stats.frame_ctrl = ppdu->frame_ctrl;
+	peer->delayed_ba_ppdu_stats.qos_ctrl = ppdu->qos_ctrl;
+	peer->delayed_ba_ppdu_stats.dcm = ppdu->dcm;
+
+	peer->delayed_ba_ppdu_stats.ru_start = ppdu->ru_start;
+	peer->delayed_ba_ppdu_stats.ru_tones = ppdu->ru_tones;
+	peer->delayed_ba_ppdu_stats.is_mcast = ppdu->is_mcast;
+
+	peer->delayed_ba_ppdu_stats.user_pos = ppdu->user_pos;
+	peer->delayed_ba_ppdu_stats.mu_group_id = ppdu->mu_group_id;
+
+	peer->last_delayed_ba = true;
+}
+
+/*
+ * dp_peer_copy_stats_to_bar() - copy delayed stats to ppdu stats.
+ * @peer: Datapath peer handle
+ * @ppdu: PPDU Descriptor
+ *
+ * Return: None
+ *
+ * For Tx BAR, PPDU stats TLV include Block Ack info. PPDU info
+ * from Tx BAR frame not required to populate peer stats.
+ * But we need successful MPDU and MSDU to update previous
+ * transmitted Tx data frame. Overwrite ppdu stats with the previous
+ * stored ppdu stats.
+ */
+static void
+dp_peer_copy_stats_to_bar(struct dp_peer *peer,
+			  struct cdp_tx_completion_ppdu_user *ppdu)
+{
+	ppdu->ltf_size = peer->delayed_ba_ppdu_stats.ltf_size;
+	ppdu->stbc = peer->delayed_ba_ppdu_stats.stbc;
+	ppdu->he_re = peer->delayed_ba_ppdu_stats.he_re;
+	ppdu->txbf = peer->delayed_ba_ppdu_stats.txbf;
+	ppdu->bw = peer->delayed_ba_ppdu_stats.bw;
+	ppdu->nss = peer->delayed_ba_ppdu_stats.nss;
+	ppdu->preamble = peer->delayed_ba_ppdu_stats.preamble;
+	ppdu->gi = peer->delayed_ba_ppdu_stats.gi;
+	ppdu->dcm = peer->delayed_ba_ppdu_stats.dcm;
+	ppdu->ldpc = peer->delayed_ba_ppdu_stats.ldpc;
+	ppdu->dcm = peer->delayed_ba_ppdu_stats.dcm;
+	ppdu->mpdu_tried_ucast = peer->delayed_ba_ppdu_stats.mpdu_tried_ucast;
+	ppdu->mpdu_tried_mcast = peer->delayed_ba_ppdu_stats.mpdu_tried_mcast;
+	ppdu->frame_ctrl = peer->delayed_ba_ppdu_stats.frame_ctrl;
+	ppdu->qos_ctrl = peer->delayed_ba_ppdu_stats.qos_ctrl;
+	ppdu->dcm = peer->delayed_ba_ppdu_stats.dcm;
+
+	ppdu->ru_start = peer->delayed_ba_ppdu_stats.ru_start;
+	ppdu->ru_tones = peer->delayed_ba_ppdu_stats.ru_tones;
+	ppdu->is_mcast = peer->delayed_ba_ppdu_stats.is_mcast;
+
+	ppdu->user_pos = peer->delayed_ba_ppdu_stats.user_pos;
+	ppdu->mu_group_id = peer->delayed_ba_ppdu_stats.mu_group_id;
+
+	peer->last_delayed_ba = false;
+}
+
+/*
  * dp_tx_rate_stats_update() - Update rate per-peer statistics
  * @peer: Datapath peer handle
  * @ppdu: PPDU Descriptor
@@ -139,10 +241,14 @@
 	struct dp_pdev *pdev = peer->vdev->pdev;
 	uint8_t preamble, mcs;
 	uint16_t num_msdu;
+	uint16_t num_mpdu;
+	uint16_t mpdu_tried;
 
 	preamble = ppdu->preamble;
 	mcs = ppdu->mcs;
 	num_msdu = ppdu->num_msdu;
+	num_mpdu = ppdu->mpdu_success;
+	mpdu_tried = ppdu->mpdu_tried_ucast + ppdu->mpdu_tried_mcast;
 
 	/* If the peer statistics are already processed as part of
 	 * per-MSDU completion handler, do not process these again in per-PPDU
@@ -176,27 +282,63 @@
 		DP_STATS_UPD(peer, tx.ru_start, ppdu->ru_start);
 		switch (ppdu->ru_tones) {
 		case RU_26:
-			DP_STATS_INC(peer, tx.ru_loc[0], num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_26_INDEX].num_msdu,
+				     num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_26_INDEX].num_mpdu,
+				     num_mpdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_26_INDEX].mpdu_tried,
+				     mpdu_tried);
 		break;
 		case RU_52:
-			DP_STATS_INC(peer, tx.ru_loc[1], num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_52_INDEX].num_msdu,
+				     num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_52_INDEX].num_mpdu,
+				     num_mpdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_52_INDEX].mpdu_tried,
+				     mpdu_tried);
 		break;
 		case RU_106:
-			DP_STATS_INC(peer, tx.ru_loc[2], num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_106_INDEX].num_msdu,
+				     num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_106_INDEX].num_mpdu,
+				     num_mpdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_106_INDEX].mpdu_tried,
+				     mpdu_tried);
 		break;
 		case RU_242:
-			DP_STATS_INC(peer, tx.ru_loc[3], num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_242_INDEX].num_msdu,
+				     num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_242_INDEX].num_mpdu,
+				     num_mpdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_242_INDEX].mpdu_tried,
+				     mpdu_tried);
 		break;
 		case RU_484:
-			DP_STATS_INC(peer, tx.ru_loc[4], num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_484_INDEX].num_msdu,
+				     num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_484_INDEX].num_mpdu,
+				     num_mpdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_484_INDEX].mpdu_tried,
+				     mpdu_tried);
 		break;
 		case RU_996:
-			DP_STATS_INC(peer, tx.ru_loc[5], num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_996_INDEX].num_msdu,
+				     num_msdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_996_INDEX].num_mpdu,
+				     num_mpdu);
+			DP_STATS_INC(peer, tx.ru_loc[RU_996_INDEX].mpdu_tried,
+				     mpdu_tried);
 		break;
 		}
 	}
 
-	DP_STATS_INC(peer, tx.transmit_type[ppdu->ppdu_type], num_msdu);
+	DP_STATS_INC(peer, tx.transmit_type[ppdu->ppdu_type].num_msdu,
+		     num_msdu);
+	DP_STATS_INC(peer, tx.transmit_type[ppdu->ppdu_type].num_mpdu,
+		     num_mpdu);
+	DP_STATS_INC(peer, tx.transmit_type[ppdu->ppdu_type].mpdu_tried,
+		     mpdu_tried);
+
 	DP_STATS_INC_PKT(peer, tx.comp_pkt,
 			num_msdu, (ppdu->success_bytes +
 				ppdu->retry_bytes + ppdu->failed_bytes));
@@ -1948,6 +2090,7 @@
 
 	if (HTT_PPDU_STATS_USER_COMMON_TLV_DELAYED_BA_GET(*tag_buf)) {
 		ppdu_user_desc->delayed_ba = 1;
+		ppdu_desc->delayed_ba = 1;
 	}
 
 	if (HTT_PPDU_STATS_USER_COMMON_TLV_MCAST_GET(*tag_buf)) {
@@ -1968,11 +2111,8 @@
 		HTT_PPDU_STATS_USER_COMMON_TLV_FRAME_CTRL_GET(*tag_buf);
 	ppdu_desc->frame_ctrl = ppdu_user_desc->frame_ctrl;
 
-	if (ppdu_user_desc->delayed_ba) {
+	if (ppdu_user_desc->delayed_ba)
 		ppdu_user_desc->mpdu_success = 0;
-		ppdu_user_desc->mpdu_tried_mcast = 0;
-		ppdu_user_desc->mpdu_tried_ucast = 0;
-	}
 
 	tag_buf += 3;
 
@@ -2244,6 +2384,19 @@
 		HTT_PPDU_STATS_USER_CMPLTN_COMMON_TLV_IS_AMPDU_GET(*tag_buf);
 	ppdu_info->is_ampdu = ppdu_user_desc->is_ampdu;
 
+	/*
+	 * increase successful mpdu counter from
+	 * htt_ppdu_stats_user_cmpltn_common_tlv
+	 */
+	ppdu_info->mpdu_compltn_common_tlv += ppdu_user_desc->mpdu_success;
+
+	/*
+	 * MU BAR may send request to n users but we may received ack only from
+	 * m users. To have count of number of users respond back, we have a
+	 * separate counter bar_num_users per PPDU that get increment for every
+	 * htt_ppdu_stats_user_cmpltn_common_tlv
+	 */
+	ppdu_desc->bar_num_users++;
 }
 
 /*
@@ -2372,6 +2525,8 @@
 	tag_buf += 2;
 	ppdu_user_desc->success_bytes = *tag_buf;
 
+	/* increase successful mpdu counter */
+	ppdu_info->mpdu_ack_ba_tlv += ppdu_user_desc->num_mpdu;
 }
 
 /*
@@ -2683,6 +2838,7 @@
 	uint32_t tlv_bitmap_expected;
 	uint32_t tlv_bitmap_default;
 	uint16_t i;
+	uint32_t num_users;
 
 	ppdu_desc = (struct cdp_tx_completion_ppdu *)
 		qdf_nbuf_data(ppdu_info->nbuf);
@@ -2699,7 +2855,15 @@
 	}
 
 	tlv_bitmap_default = tlv_bitmap_expected;
-	for (i = 0; i < ppdu_desc->num_users; i++) {
+
+	if (ppdu_desc->frame_type == CDP_PPDU_FTYPE_BAR) {
+		num_users = ppdu_desc->bar_num_users;
+		ppdu_desc->num_users = ppdu_desc->bar_num_users;
+	} else {
+		num_users = ppdu_desc->num_users;
+	}
+
+	for (i = 0; i < num_users; i++) {
 		ppdu_desc->num_mpdu += ppdu_desc->user[i].num_mpdu;
 		ppdu_desc->num_msdu += ppdu_desc->user[i].num_msdu;
 
@@ -2717,17 +2881,31 @@
 		    HTT_PPDU_STATS_USER_STATUS_OK)
 			tlv_bitmap_expected = tlv_bitmap_expected & 0xFF;
 
-		if (ppdu_info->tlv_bitmap != tlv_bitmap_expected) {
+		/*
+		 * different frame like DATA, BAR or CTRL has different
+		 * tlv bitmap expected. Apart from ACK_BA_STATUS TLV, we
+		 * receive other tlv in-order/sequential from fw.
+		 * Since ACK_BA_STATUS TLV come from Hardware it is
+		 * asynchronous So we need to depend on some tlv to confirm
+		 * all tlv is received for a ppdu.
+		 * So we depend on both HTT_PPDU_STATS_COMMON_TLV and
+		 * ACK_BA_STATUS_TLV.
+		 */
+		if (!(ppdu_info->tlv_bitmap &
+		      (1 << HTT_PPDU_STATS_COMMON_TLV)) ||
+		    !(ppdu_info->tlv_bitmap &
+		      (1 << HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV))) {
 			dp_peer_unref_del_find_by_id(peer);
 			continue;
 		}
+
 		/**
 		 * Update tx stats for data frames having Qos as well as
 		 * non-Qos data tid
 		 */
 		if ((ppdu_desc->user[i].tid < CDP_DATA_TID_MAX ||
 		     (ppdu_desc->user[i].tid == CDP_DATA_NON_QOS_TID)) &&
-		      (ppdu_desc->frame_type == CDP_PPDU_FTYPE_DATA)) {
+		      (ppdu_desc->frame_type != CDP_PPDU_FTYPE_CTRL)) {
 
 			dp_tx_stats_update(pdev->soc, peer,
 					   &ppdu_desc->user[i],
@@ -2839,6 +3017,15 @@
 			    (1 << HTT_PPDU_STATS_SCH_CMD_STATUS_TLV)))
 				return ppdu_info;
 
+			/**
+			 * apart from ACK BA STATUS TLV rest all comes in order
+			 * so if tlv type not ACK BA STATUS TLV we can deliver
+			 * ppdu_info
+			 */
+			if (tlv_type ==
+			    HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV)
+				return ppdu_info;
+
 			dp_ppdu_desc_deliver(pdev, ppdu_info);
 		} else {
 			return ppdu_info;
@@ -2913,6 +3100,8 @@
 	uint8_t *tlv_buf;
 	struct ppdu_info *ppdu_info = NULL;
 	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
+	struct dp_peer *peer;
+	uint32_t i = 0;
 
 	uint32_t *msg_word = (uint32_t *) qdf_nbuf_data(htt_t2h_msg);
 
@@ -2977,13 +3166,6 @@
 
 	tlv_bitmap_expected = HTT_PPDU_DEFAULT_TLV_BITMAP;
 
-	ppdu_desc = ppdu_info->ppdu_desc;
-	if (ppdu_desc &&
-	    ppdu_desc->user[ppdu_desc->last_usr_index].completion_status !=
-	    HTT_PPDU_STATS_USER_STATUS_OK) {
-		tlv_bitmap_expected = tlv_bitmap_expected & 0xFF;
-	}
-
 	if (pdev->tx_sniffer_enable || pdev->mcopy_mode) {
 		if (ppdu_info->is_ampdu)
 			tlv_bitmap_expected =
@@ -2991,6 +3173,85 @@
 					ppdu_info->tlv_bitmap);
 	}
 
+	ppdu_desc = ppdu_info->ppdu_desc;
+
+	if (!ppdu_desc)
+		return NULL;
+
+	if (ppdu_desc->user[ppdu_desc->last_usr_index].completion_status !=
+	    HTT_PPDU_STATS_USER_STATUS_OK) {
+		tlv_bitmap_expected = tlv_bitmap_expected & 0xFF;
+	}
+
+	if (ppdu_desc->frame_type == CDP_PPDU_FTYPE_DATA &&
+	    (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_COMMON_TLV)) &&
+	    ppdu_desc->delayed_ba) {
+		for (i = 0; i < ppdu_desc->num_users; i++) {
+			uint32_t ppdu_id;
+
+			ppdu_id = ppdu_desc->ppdu_id;
+			peer = dp_peer_find_by_id(pdev->soc,
+						  ppdu_desc->user[i].peer_id);
+			/**
+			 * This check is to make sure peer is not deleted
+			 * after processing the TLVs.
+			 */
+			if (!peer)
+				continue;
+
+			/**
+			 * save delayed ba user info
+			 */
+			if (ppdu_desc->user[i].delayed_ba) {
+				dp_peer_copy_delay_stats(peer,
+							 &ppdu_desc->user[i]);
+				peer->last_delayed_ba_ppduid = ppdu_id;
+			}
+			dp_peer_unref_del_find_by_id(peer);
+		}
+	}
+
+	/*
+	 * when frame type is BAR and STATS_COMMON_TLV is set
+	 * copy the store peer delayed info to BAR status
+	 */
+	if (ppdu_desc->frame_type == CDP_PPDU_FTYPE_BAR &&
+	    (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_COMMON_TLV))) {
+		for (i = 0; i < ppdu_desc->bar_num_users; i++) {
+			peer = dp_peer_find_by_id(pdev->soc,
+						  ppdu_desc->user[i].peer_id);
+			/**
+			 * This check is to make sure peer is not deleted
+			 * after processing the TLVs.
+			 */
+			if (!peer)
+				continue;
+
+			if (peer->last_delayed_ba) {
+				dp_peer_copy_stats_to_bar(peer,
+							  &ppdu_desc->user[i]);
+			}
+			dp_peer_unref_del_find_by_id(peer);
+		}
+	}
+
+	/*
+	 * for frame type DATA and BAR, we update stats based on MSDU,
+	 * successful msdu and mpdu are populate from ACK BA STATUS TLV
+	 * which comes out of order. successful mpdu also populated from
+	 * COMPLTN COMMON TLV which comes in order. for every ppdu_info
+	 * we store successful mpdu from both tlv and compare before delivering
+	 * to make sure we received ACK BA STATUS TLV.
+	 */
+	if (ppdu_desc->frame_type != CDP_PPDU_FTYPE_CTRL) {
+		/*
+		 * successful mpdu count should match with both tlv
+		 */
+		if (ppdu_info->mpdu_compltn_common_tlv !=
+		    ppdu_info->mpdu_ack_ba_tlv)
+			return NULL;
+	}
+
 	/**
 	 * Once all the TLVs for a given PPDU has been processed,
 	 * return PPDU status to be delivered to higher layer
diff --git a/dp/wifi3.0/dp_internal.h b/dp/wifi3.0/dp_internal.h
index b42741a..21df93e 100644
--- a/dp/wifi3.0/dp_internal.h
+++ b/dp/wifi3.0/dp_internal.h
@@ -64,6 +64,21 @@
 	(1 << HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV)
 
 /**
+ * Bitmap of HTT PPDU delayed ba TLV types for Default mode
+ */
+#define HTT_PPDU_DELAYED_BA_TLV_BITMAP \
+	(1 << HTT_PPDU_STATS_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_RATE_TLV)
+
+/**
+ * Bitmap of HTT PPDU TLV types for Delayed BA
+ */
+#define HTT_PPDU_STATUS_TLV_BITMAP \
+	(1 << HTT_PPDU_STATS_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV)
+
+/**
  * Bitmap of HTT PPDU TLV types for Sniffer mode bitmap 64
  */
 #define HTT_PPDU_SNIFFER_AMPDU_TLV_BITMAP_64 \
@@ -739,6 +754,23 @@
 extern void dp_peer_find_hash_add(struct dp_soc *soc, struct dp_peer *peer);
 extern void dp_peer_find_hash_remove(struct dp_soc *soc, struct dp_peer *peer);
 extern void dp_peer_find_hash_erase(struct dp_soc *soc);
+
+/*
+ * dp_peer_ppdu_delayed_ba_init() Initialize ppdu in peer
+ * @peer: Datapath peer
+ *
+ * return: void
+ */
+void dp_peer_ppdu_delayed_ba_init(struct dp_peer *peer);
+
+/*
+ * dp_peer_ppdu_delayed_ba_cleanup() free ppdu allocated in peer
+ * @peer: Datapath peer
+ *
+ * return: void
+ */
+void dp_peer_ppdu_delayed_ba_cleanup(struct dp_peer *peer);
+
 extern void dp_peer_rx_init(struct dp_pdev *pdev, struct dp_peer *peer);
 void dp_peer_tx_init(struct dp_pdev *pdev, struct dp_peer *peer);
 extern void dp_peer_cleanup(struct dp_vdev *vdev, struct dp_peer *peer);
diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c
index 76777c9..26ca66c 100644
--- a/dp/wifi3.0/dp_main.c
+++ b/dp/wifi3.0/dp_main.c
@@ -5392,6 +5392,8 @@
 	dp_peer_rx_init(pdev, peer);
 	dp_peer_tx_init(pdev, peer);
 
+	dp_peer_ppdu_delayed_ba_init(peer);
+
 	return;
 }
 
diff --git a/dp/wifi3.0/dp_peer.c b/dp/wifi3.0/dp_peer.c
index d58b2a3..9692f71 100644
--- a/dp/wifi3.0/dp_peer.c
+++ b/dp/wifi3.0/dp_peer.c
@@ -2299,6 +2299,32 @@
 		qdf_spinlock_destroy(&peer->rx_tid[tid].tid_lock);
 }
 
+#ifdef FEATURE_PERPKT_INFO
+/*
+ * dp_peer_ppdu_delayed_ba_init() Initialize ppdu in peer
+ * @peer: Datapath peer
+ *
+ * return: void
+ */
+void dp_peer_ppdu_delayed_ba_init(struct dp_peer *peer)
+{
+	qdf_mem_zero(&peer->delayed_ba_ppdu_stats,
+		     sizeof(struct cdp_delayed_tx_completion_ppdu_user));
+	peer->last_delayed_ba = false;
+	peer->last_delayed_ba_ppduid = 0;
+}
+#else
+/*
+ * dp_peer_ppdu_delayed_ba_init() Initialize ppdu in peer
+ * @peer: Datapath peer
+ *
+ * return: void
+ */
+void dp_peer_ppdu_delayed_ba_init(struct dp_peer *peer)
+{
+}
+#endif
+
 /*
  * dp_peer_cleanup() – Cleanup peer information
  * @vdev: Datapath vdev
diff --git a/dp/wifi3.0/dp_stats.c b/dp/wifi3.0/dp_stats.c
index 2cc9f02..56a955a 100644
--- a/dp/wifi3.0/dp_stats.c
+++ b/dp/wifi3.0/dp_stats.c
@@ -4864,10 +4864,10 @@
 
 	DP_PRINT_STATS("Transmit Type :");
 	DP_PRINT_STATS("SU %d, MU_MIMO %d, MU_OFDMA %d, MU_MIMO_OFDMA %d",
-		       peer->stats.tx.transmit_type[0],
-		       peer->stats.tx.transmit_type[1],
-		       peer->stats.tx.transmit_type[2],
-		       peer->stats.tx.transmit_type[3]);
+		       peer->stats.tx.transmit_type[SU].num_msdu,
+		       peer->stats.tx.transmit_type[MU_MIMO].num_msdu,
+		       peer->stats.tx.transmit_type[MU_OFDMA].num_msdu,
+		       peer->stats.tx.transmit_type[MU_MIMO_OFDMA].num_msdu);
 
 	for (i = 0; i < MAX_MU_GROUP_ID;) {
 		index = 0;
@@ -4887,12 +4887,18 @@
 	DP_PRINT_STATS("Last Packet RU index [%d], Size [%d]",
 		       peer->stats.tx.ru_start, peer->stats.tx.ru_tones);
 	DP_PRINT_STATS("RU Locations RU[26 52 106 242 484 996]:");
-	DP_PRINT_STATS("RU_26: %d", peer->stats.tx.ru_loc[0]);
-	DP_PRINT_STATS("RU 52: %d", peer->stats.tx.ru_loc[1]);
-	DP_PRINT_STATS("RU 106: %d", peer->stats.tx.ru_loc[2]);
-	DP_PRINT_STATS("RU 242: %d", peer->stats.tx.ru_loc[3]);
-	DP_PRINT_STATS("RU 484: %d", peer->stats.tx.ru_loc[4]);
-	DP_PRINT_STATS("RU 996: %d", peer->stats.tx.ru_loc[5]);
+	DP_PRINT_STATS("RU_26:  %d",
+		       peer->stats.tx.ru_loc[RU_26_INDEX].num_msdu);
+	DP_PRINT_STATS("RU 52:  %d",
+		       peer->stats.tx.ru_loc[RU_52_INDEX].num_msdu);
+	DP_PRINT_STATS("RU 106: %d",
+		       peer->stats.tx.ru_loc[RU_106_INDEX].num_msdu);
+	DP_PRINT_STATS("RU 242: %d",
+		       peer->stats.tx.ru_loc[RU_242_INDEX].num_msdu);
+	DP_PRINT_STATS("RU 484: %d",
+		       peer->stats.tx.ru_loc[RU_484_INDEX].num_msdu);
+	DP_PRINT_STATS("RU 996: %d",
+		       peer->stats.tx.ru_loc[RU_996_INDEX].num_msdu);
 
 	DP_PRINT_STATS("Aggregation:");
 	DP_PRINT_STATS("Number of Msdu's Part of Amsdu = %d",
@@ -5354,6 +5360,8 @@
 		DP_PRINT_STATS("	Tag[%d] = %llu", index,
 			       pdev->stats.ppdu_stats_counter[index]);
 	}
+	DP_PRINT_STATS("BA not received for delayed_ba: %d",
+		       pdev->stats.cdp_delayed_ba_not_recev);
 	DP_PRINT_STATS("tx_ppdu_proc: %llu\n",
 		       pdev->tx_ppdu_proc);
 
diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h
index 8b1eb7f..2796253 100644
--- a/dp/wifi3.0/dp_types.h
+++ b/dp/wifi3.0/dp_types.h
@@ -1211,8 +1211,10 @@
  * @is_ampdu        - set if Ampdu aggregate
  * @nbuf            - ppdu descriptor payload
  * @ppdu_desc       - ppdu descriptor
- * @ppdu_info_list_elem - linked list of ppdu tlvs
+ * @ppdu_info_list_elem  - linked list of ppdu tlvs
  * @ppdu_info_queue_elem - Singly linked list (queue) of ppdu tlvs
+ * @mpdu_compltn_common_tlv  - Successful MPDU counter from COMPLTN COMMON tlv
+ * @mpdu_ack_ba_tlv	    - Successful MPDU from ACK BA tlv
  */
 struct ppdu_info {
 	uint32_t ppdu_id;
@@ -1234,6 +1236,8 @@
 #else
 	TAILQ_ENTRY(ppdu_info) ppdu_info_list_elem;
 #endif
+	uint16_t mpdu_compltn_common_tlv;
+	uint16_t mpdu_ack_ba_tlv;
 };
 
 /**
@@ -1960,6 +1964,14 @@
 	qdf_atomic_t flush_in_progress;
 	struct dp_peer_cached_bufq bufq_info;
 #endif
+#ifdef FEATURE_PERPKT_INFO
+	/* delayed ba ppdu stats handling */
+	struct cdp_delayed_tx_completion_ppdu_user delayed_ba_ppdu_stats;
+	/* delayed ba flag */
+	bool last_delayed_ba;
+	/* delayed ba ppdu id */
+	uint32_t last_delayed_ba_ppduid;
+#endif
 };
 
 /*