Browse Source

qcacmn: Add linked list logic to store per ppdu descriptor status tlv

Add linked list logic to hold all ppdu descriptor status in a list till
all TLVs have been processed
This design handles asynchronous arrival of tlvs from fw per ppdu

Change-Id: Ib8943d7c826c0b441f58f0eaba7ae0bdc0539d9a
Anish Nataraj 7 years ago
parent
commit
b9e7d01e52
4 changed files with 383 additions and 153 deletions
  1. 3 0
      dp/inc/cdp_txrx_cmn_struct.h
  2. 346 153
      dp/wifi3.0/dp_htt.c
  3. 7 0
      dp/wifi3.0/dp_main.c
  4. 27 0
      dp/wifi3.0/dp_types.h

+ 3 - 0
dp/inc/cdp_txrx_cmn_struct.h

@@ -903,6 +903,8 @@ struct cdp_tx_completion_ppdu_user {
  * @num_users: Number of users
  * @num_mpdu: Number of MPDUs in PPDU
  * @num_msdu: Number of MSDUs in PPDU
+ * @frame_type: frame SU or MU
+ * @frame_ctrl: frame control of 80211 header
  * @channel: Channel informartion
  * @ack_rssi: RSSI value of last ack packet (units=dB above noise floor)
  * @tx_duration: PPDU airtime
@@ -918,6 +920,7 @@ struct cdp_tx_completion_ppdu {
 	uint32_t num_mpdu:9,
 		 num_msdu:16;
 	uint16_t frame_type;
+	uint16_t frame_ctrl;
 	uint16_t channel;
 	uint16_t phy_mode;
 	uint32_t ack_rssi;

+ 346 - 153
dp/wifi3.0/dp_htt.c

@@ -47,6 +47,33 @@ do {                                                             \
 } while (0)
 
 #define HTT_MGMT_CTRL_TLV_RESERVERD_LEN 12
+/**
+ * Bitmap of HTT PPDU TLV types for Default mode
+ */
+#define HTT_PPDU_DEFAULT_TLV_BITMAP \
+	(1 << HTT_PPDU_STATS_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_RATE_TLV) | \
+	(1 << HTT_PPDU_STATS_SCH_CMD_STATUS_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMPLTN_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV)
+
+/**
+ * Bitmap of HTT PPDU TLV types for Sniffer mode
+ */
+#define HTT_PPDU_SNIFFER_AMPDU_TLV_BITMAP \
+	(1 << HTT_PPDU_STATS_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_RATE_TLV) | \
+	(1 << HTT_PPDU_STATS_SCH_CMD_STATUS_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMPLTN_COMMON_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMPLTN_BA_BITMAP_64_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_MPDU_ENQ_BITMAP_64_TLV) | \
+	(1 << HTT_PPDU_STATS_USR_COMPLTN_BA_BITMAP_256_TLV)
+
+#define HTT_FRAMECTRL_DATATYPE 0x08
+#define HTT_PPDU_DESC_MAX_DEPTH 16
 
 /*
  * dp_tx_stats_update() - Update per-peer statistics
@@ -1538,24 +1565,26 @@ void htt_t2h_stats_handler(void *context)
 }
 
 /*
- * dp_get_ppdu_info_user_index: Find place holder for the received
- * ppdu stats info
+ * dp_get_ppdu_info_user_index: Find and allocate a per-user descriptor for a PPDU,
+ * if a new peer id arrives in a PPDU
  * pdev: DP pdev handle
+ * @peer_id : peer unique identifier
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:user index to be populated
  */
 #ifdef FEATURE_PERPKT_INFO
 static uint8_t dp_get_ppdu_info_user_index(struct dp_pdev *pdev,
-						uint16_t peer_id)
+						uint16_t peer_id,
+						struct ppdu_info *ppdu_info)
 {
 	uint8_t user_index = 0;
 	struct cdp_tx_completion_ppdu *ppdu_desc;
 	struct cdp_tx_completion_ppdu_user *ppdu_user_desc;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
-	while ((user_index + 1) <= pdev->tx_ppdu_info.last_user) {
+	while ((user_index + 1) <= ppdu_info->last_user) {
 		ppdu_user_desc = &ppdu_desc->user[user_index];
 		if (ppdu_user_desc->peer_id != peer_id) {
 			user_index++;
@@ -1569,33 +1598,30 @@ static uint8_t dp_get_ppdu_info_user_index(struct dp_pdev *pdev,
 		}
 	}
 
-	pdev->tx_ppdu_info.last_user++;
+	ppdu_info->last_user++;
 	/* Max users possible is 8 so last user should not exceed 8 */
-	qdf_assert_always(pdev->tx_ppdu_info.last_user <= CDP_MU_MAX_USERS);
-	return pdev->tx_ppdu_info.last_user - 1;
+	qdf_assert_always(ppdu_info->last_user <= CDP_MU_MAX_USERS);
+	return ppdu_info->last_user - 1;
 }
 
 /*
  * dp_process_ppdu_stats_common_tlv: Process htt_ppdu_stats_common_tlv
  * pdev: DP pdev handle
  * @tag_buf: buffer containing the tlv htt_ppdu_stats_common_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_common_tlv(struct dp_pdev *pdev,
-		uint32_t *tag_buf)
+		uint32_t *tag_buf, struct ppdu_info *ppdu_info)
 {
 	uint16_t frame_type;
 	uint16_t freq;
 	struct dp_soc *soc = NULL;
-	struct cdp_tx_completion_ppdu *ppdu_desc;
-	htt_ppdu_stats_common_tlv *dp_stats_buf =
-		(htt_ppdu_stats_common_tlv *)tag_buf;
+	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
-	ppdu_desc->ppdu_id = dp_stats_buf->ppdu_id;
 	tag_buf += 2;
 	ppdu_desc->num_users =
 		HTT_PPDU_STATS_COMMON_TLV_NUM_USERS_GET(*tag_buf);
@@ -1633,11 +1659,13 @@ static void dp_process_ppdu_stats_common_tlv(struct dp_pdev *pdev,
 /*
  * dp_process_ppdu_stats_user_common_tlv: Process ppdu_stats_user_common
  * @tag_buf: buffer containing the tlv htt_ppdu_stats_user_common_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_user_common_tlv(
-		struct dp_pdev *pdev, uint32_t *tag_buf)
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	uint16_t peer_id;
 	struct dp_peer *peer;
@@ -1645,8 +1673,7 @@ static void dp_process_ppdu_stats_user_common_tlv(
 	struct cdp_tx_completion_ppdu_user *ppdu_user_desc;
 	uint8_t curr_user_index = 0;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *) qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 	peer_id = HTT_PPDU_STATS_USER_COMMON_TLV_SW_PEER_ID_GET(*tag_buf);
@@ -1655,7 +1682,7 @@ static void dp_process_ppdu_stats_user_common_tlv(
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 
 	ppdu_user_desc->peer_id = peer_id;
@@ -1677,6 +1704,7 @@ static void dp_process_ppdu_stats_user_common_tlv(
 		HTT_PPDU_STATS_USER_COMMON_TLV_QOS_CTRL_GET(*tag_buf);
 	ppdu_user_desc->frame_ctrl =
 		HTT_PPDU_STATS_USER_COMMON_TLV_FRAME_CTRL_GET(*tag_buf);
+	ppdu_desc->frame_ctrl = ppdu_user_desc->frame_ctrl;
 }
 
 
@@ -1684,11 +1712,13 @@ static void dp_process_ppdu_stats_user_common_tlv(
  * dp_process_ppdu_stats_user_rate_tlv() - Process htt_ppdu_stats_user_rate_tlv
  * @pdev: DP pdev handle
  * @tag_buf: T2H message buffer carrying the user rate TLV
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_user_rate_tlv(struct dp_pdev *pdev,
-		uint32_t *tag_buf)
+		uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	uint16_t peer_id;
 	struct dp_peer *peer;
@@ -1696,8 +1726,7 @@ static void dp_process_ppdu_stats_user_rate_tlv(struct dp_pdev *pdev,
 	struct cdp_tx_completion_ppdu_user *ppdu_user_desc;
 	uint8_t curr_user_index = 0;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *) qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 	peer_id = HTT_PPDU_STATS_USER_RATE_TLV_SW_PEER_ID_GET(*tag_buf);
@@ -1706,7 +1735,7 @@ static void dp_process_ppdu_stats_user_rate_tlv(struct dp_pdev *pdev,
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 	ppdu_user_desc->peer_id = peer_id;
@@ -1754,11 +1783,13 @@ static void dp_process_ppdu_stats_user_rate_tlv(struct dp_pdev *pdev,
  * htt_ppdu_stats_enq_mpdu_bitmap_64_tlv
  * pdev: DP PDEV handle
  * @tag_buf: buffer containing the tlv htt_ppdu_stats_enq_mpdu_bitmap_64_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_enq_mpdu_bitmap_64_tlv(
-		struct dp_pdev *pdev, uint32_t *tag_buf)
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	htt_ppdu_stats_enq_mpdu_bitmap_64_tlv *dp_stats_buf =
 		(htt_ppdu_stats_enq_mpdu_bitmap_64_tlv *)tag_buf;
@@ -1769,8 +1800,7 @@ static void dp_process_ppdu_stats_enq_mpdu_bitmap_64_tlv(
 	uint16_t peer_id;
 	struct dp_peer *peer;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 
@@ -1782,7 +1812,7 @@ static void dp_process_ppdu_stats_enq_mpdu_bitmap_64_tlv(
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 	ppdu_user_desc->peer_id = peer_id;
@@ -1797,11 +1827,13 @@ static void dp_process_ppdu_stats_enq_mpdu_bitmap_64_tlv(
  * htt_ppdu_stats_enq_mpdu_bitmap_256_tlv
  * soc: DP SOC handle
  * @tag_buf: buffer containing the tlv htt_ppdu_stats_enq_mpdu_bitmap_256_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_enq_mpdu_bitmap_256_tlv(
-		struct dp_pdev *pdev, uint32_t *tag_buf)
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	htt_ppdu_stats_enq_mpdu_bitmap_256_tlv *dp_stats_buf =
 		(htt_ppdu_stats_enq_mpdu_bitmap_256_tlv *)tag_buf;
@@ -1812,8 +1844,7 @@ static void dp_process_ppdu_stats_enq_mpdu_bitmap_256_tlv(
 	uint16_t peer_id;
 	struct dp_peer *peer;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 
@@ -1825,7 +1856,7 @@ static void dp_process_ppdu_stats_enq_mpdu_bitmap_256_tlv(
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 	ppdu_user_desc->peer_id = peer_id;
@@ -1840,11 +1871,13 @@ static void dp_process_ppdu_stats_enq_mpdu_bitmap_256_tlv(
  * htt_ppdu_stats_user_cmpltn_common_tlv
  * soc: DP SOC handle
  * @tag_buf: buffer containing the tlv htt_ppdu_stats_user_cmpltn_common_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_user_cmpltn_common_tlv(
-		struct dp_pdev *pdev, uint32_t *tag_buf)
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	uint16_t peer_id;
 	struct dp_peer *peer;
@@ -1854,8 +1887,7 @@ static void dp_process_ppdu_stats_user_cmpltn_common_tlv(
 	htt_ppdu_stats_user_cmpltn_common_tlv *dp_stats_buf =
 		(htt_ppdu_stats_user_cmpltn_common_tlv *)tag_buf;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 	peer_id =
@@ -1865,7 +1897,7 @@ static void dp_process_ppdu_stats_user_cmpltn_common_tlv(
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 	ppdu_user_desc->peer_id = peer_id;
 
@@ -1897,6 +1929,7 @@ static void dp_process_ppdu_stats_user_cmpltn_common_tlv(
 
 	ppdu_user_desc->is_ampdu =
 		HTT_PPDU_STATS_USER_CMPLTN_COMMON_TLV_IS_AMPDU_GET(*tag_buf);
+	ppdu_info->is_ampdu = ppdu_user_desc->is_ampdu;
 
 }
 
@@ -1905,11 +1938,13 @@ static void dp_process_ppdu_stats_user_cmpltn_common_tlv(
  * htt_ppdu_stats_user_compltn_ba_bitmap_64_tlv
  * pdev: DP PDEV handle
  * @tag_buf: buffer containing the htt_ppdu_stats_user_compltn_ba_bitmap_64_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_user_compltn_ba_bitmap_64_tlv(
-		struct dp_pdev *pdev, uint32_t *tag_buf)
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	htt_ppdu_stats_user_compltn_ba_bitmap_64_tlv *dp_stats_buf =
 		(htt_ppdu_stats_user_compltn_ba_bitmap_64_tlv *)tag_buf;
@@ -1919,8 +1954,7 @@ static void dp_process_ppdu_stats_user_compltn_ba_bitmap_64_tlv(
 	uint16_t peer_id;
 	struct dp_peer *peer;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 
@@ -1932,7 +1966,7 @@ static void dp_process_ppdu_stats_user_compltn_ba_bitmap_64_tlv(
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 	ppdu_user_desc->peer_id = peer_id;
@@ -1947,11 +1981,13 @@ static void dp_process_ppdu_stats_user_compltn_ba_bitmap_64_tlv(
  * htt_ppdu_stats_user_compltn_ba_bitmap_256_tlv
  * pdev: DP PDEV handle
  * @tag_buf: buffer containing the htt_ppdu_stats_user_compltn_ba_bitmap_256_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_user_compltn_ba_bitmap_256_tlv(
-		struct dp_pdev *pdev, uint32_t *tag_buf)
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	htt_ppdu_stats_user_compltn_ba_bitmap_256_tlv *dp_stats_buf =
 		(htt_ppdu_stats_user_compltn_ba_bitmap_256_tlv *)tag_buf;
@@ -1961,8 +1997,7 @@ static void dp_process_ppdu_stats_user_compltn_ba_bitmap_256_tlv(
 	uint16_t peer_id;
 	struct dp_peer *peer;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 
@@ -1974,7 +2009,7 @@ static void dp_process_ppdu_stats_user_compltn_ba_bitmap_256_tlv(
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 	ppdu_user_desc->peer_id = peer_id;
@@ -1989,11 +2024,13 @@ static void dp_process_ppdu_stats_user_compltn_ba_bitmap_256_tlv(
  * htt_ppdu_stats_user_compltn_ack_ba_status_tlv
  * pdev: DP PDE handle
  * @tag_buf: buffer containing the htt_ppdu_stats_user_compltn_ack_ba_status_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
 static void dp_process_ppdu_stats_user_compltn_ack_ba_status_tlv(
-		struct dp_pdev *pdev, uint32_t *tag_buf)
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	uint16_t peer_id;
 	struct dp_peer *peer;
@@ -2001,29 +2038,31 @@ static void dp_process_ppdu_stats_user_compltn_ack_ba_status_tlv(
 	struct cdp_tx_completion_ppdu_user *ppdu_user_desc;
 	uint8_t curr_user_index = 0;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf += 2;
 	peer_id =
 	HTT_PPDU_STATS_USER_CMPLTN_ACK_BA_STATUS_TLV_SW_PEER_ID_GET(*tag_buf);
 
+
 	peer = dp_peer_find_by_id(pdev->soc, peer_id);
 
 	if (!peer)
 		return;
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 	ppdu_user_desc->peer_id = peer_id;
 
 	tag_buf++;
+	ppdu_user_desc->tid =
+		HTT_PPDU_STATS_USER_CMPLTN_ACK_BA_STATUS_TLV_TID_NUM_GET(*tag_buf);
 	ppdu_user_desc->num_mpdu =
-	HTT_PPDU_STATS_USER_CMPLTN_ACK_BA_STATUS_TLV_NUM_MPDU_GET(*tag_buf);
+		HTT_PPDU_STATS_USER_CMPLTN_ACK_BA_STATUS_TLV_NUM_MPDU_GET(*tag_buf);
 
 	ppdu_user_desc->num_msdu =
-	HTT_PPDU_STATS_USER_CMPLTN_ACK_BA_STATUS_TLV_NUM_MSDU_GET(*tag_buf);
+		HTT_PPDU_STATS_USER_CMPLTN_ACK_BA_STATUS_TLV_NUM_MSDU_GET(*tag_buf);
 
 	ppdu_user_desc->success_msdus = ppdu_user_desc->num_msdu;
 
@@ -2037,11 +2076,13 @@ static void dp_process_ppdu_stats_user_compltn_ack_ba_status_tlv(
  * htt_ppdu_stats_user_common_array_tlv
  * pdev: DP PDEV handle
  * @tag_buf: buffer containing the htt_ppdu_stats_user_compltn_ack_ba_status_tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return:void
  */
-static void dp_process_ppdu_stats_user_common_array_tlv(struct dp_pdev *pdev,
-						uint32_t *tag_buf)
+static void dp_process_ppdu_stats_user_common_array_tlv(
+		struct dp_pdev *pdev, uint32_t *tag_buf,
+		struct ppdu_info *ppdu_info)
 {
 	uint32_t peer_id;
 	struct dp_peer *peer;
@@ -2050,8 +2091,7 @@ static void dp_process_ppdu_stats_user_common_array_tlv(struct dp_pdev *pdev,
 	uint8_t curr_user_index = 0;
 	struct htt_tx_ppdu_stats_info *dp_stats_buf;
 
-	ppdu_desc =
-	(struct cdp_tx_completion_ppdu *)qdf_nbuf_data(pdev->tx_ppdu_info.buf);
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)qdf_nbuf_data(ppdu_info->nbuf);
 
 	tag_buf++;
 	dp_stats_buf = (struct htt_tx_ppdu_stats_info *)tag_buf;
@@ -2067,7 +2107,7 @@ static void dp_process_ppdu_stats_user_common_array_tlv(struct dp_pdev *pdev,
 		return;
 	}
 
-	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id);
+	curr_user_index = dp_get_ppdu_info_user_index(pdev, peer_id, ppdu_info);
 
 	ppdu_user_desc = &ppdu_desc->user[curr_user_index];
 
@@ -2167,94 +2207,275 @@ static void dp_process_ppdu_stats_tx_mgmtctrl_payload_tlv(
 
 /**
  * dp_process_ppdu_tag(): Function to process the PPDU TLVs
- * @soc: DP Physical device (radio) handle
+ * @pdev: DP pdev handle
  * @tag_buf: TLV buffer
+ * @tlv_len: length of tlv
+ * @ppdu_info: per ppdu tlv structure
  *
  * return: void
  */
 static void dp_process_ppdu_tag(struct dp_pdev *pdev, uint32_t *tag_buf,
-		uint32_t tlv_len)
+		uint32_t tlv_len, struct ppdu_info *ppdu_info)
 {
 	uint32_t tlv_type = HTT_STATS_TLV_TAG_GET(*tag_buf);
+
 	switch (tlv_type) {
 	case HTT_PPDU_STATS_COMMON_TLV:
 		qdf_assert_always(tlv_len ==
 				sizeof(htt_ppdu_stats_common_tlv));
-		dp_process_ppdu_stats_common_tlv(pdev, tag_buf);
+		dp_process_ppdu_stats_common_tlv(pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_COMMON_TLV:
 		qdf_assert_always(tlv_len ==
 				sizeof(htt_ppdu_stats_user_common_tlv));
-		dp_process_ppdu_stats_user_common_tlv(pdev, tag_buf);
+		dp_process_ppdu_stats_user_common_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_RATE_TLV:
 		qdf_assert_always(tlv_len ==
 				sizeof(htt_ppdu_stats_user_rate_tlv));
-		dp_process_ppdu_stats_user_rate_tlv(pdev, tag_buf);
+		dp_process_ppdu_stats_user_rate_tlv(pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_MPDU_ENQ_BITMAP_64_TLV:
 		qdf_assert_always(tlv_len ==
 				sizeof(htt_ppdu_stats_enq_mpdu_bitmap_64_tlv));
-		dp_process_ppdu_stats_enq_mpdu_bitmap_64_tlv(pdev, tag_buf);
+		dp_process_ppdu_stats_enq_mpdu_bitmap_64_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_MPDU_ENQ_BITMAP_256_TLV:
 		qdf_assert_always(tlv_len ==
 				sizeof(htt_ppdu_stats_enq_mpdu_bitmap_256_tlv));
-		dp_process_ppdu_stats_enq_mpdu_bitmap_256_tlv(pdev, tag_buf);
+		dp_process_ppdu_stats_enq_mpdu_bitmap_256_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_COMPLTN_COMMON_TLV:
 		qdf_assert_always(tlv_len ==
 				sizeof(htt_ppdu_stats_user_cmpltn_common_tlv));
-		dp_process_ppdu_stats_user_cmpltn_common_tlv(pdev, tag_buf);
+		dp_process_ppdu_stats_user_cmpltn_common_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_COMPLTN_BA_BITMAP_64_TLV:
 		qdf_assert_always(tlv_len ==
 			sizeof(htt_ppdu_stats_user_compltn_ba_bitmap_64_tlv));
-		dp_process_ppdu_stats_user_compltn_ba_bitmap_64_tlv(pdev,
-								tag_buf);
+		dp_process_ppdu_stats_user_compltn_ba_bitmap_64_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_COMPLTN_BA_BITMAP_256_TLV:
 		qdf_assert_always(tlv_len ==
 			sizeof(htt_ppdu_stats_user_compltn_ba_bitmap_256_tlv));
-		dp_process_ppdu_stats_user_compltn_ba_bitmap_256_tlv(pdev,
-								tag_buf);
+		dp_process_ppdu_stats_user_compltn_ba_bitmap_256_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV:
 		qdf_assert_always(tlv_len ==
 			sizeof(htt_ppdu_stats_user_compltn_ack_ba_status_tlv));
-		dp_process_ppdu_stats_user_compltn_ack_ba_status_tlv(pdev,
-								tag_buf);
+		dp_process_ppdu_stats_user_compltn_ack_ba_status_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_COMMON_ARRAY_TLV:
 		qdf_assert_always(tlv_len ==
 			sizeof(htt_ppdu_stats_usr_common_array_tlv_v));
-		dp_process_ppdu_stats_user_common_array_tlv(pdev,
-							tag_buf);
+		dp_process_ppdu_stats_user_common_array_tlv(
+				pdev, tag_buf, ppdu_info);
 		break;
 	case HTT_PPDU_STATS_USR_COMPLTN_FLUSH_TLV:
 		qdf_assert_always(tlv_len ==
 			sizeof(htt_ppdu_stats_flush_tlv));
-		dp_process_ppdu_stats_user_compltn_flush_tlv(pdev,
-								tag_buf);
+		dp_process_ppdu_stats_user_compltn_flush_tlv(
+				pdev, tag_buf);
 		break;
 	case HTT_PPDU_STATS_TX_MGMTCTRL_PAYLOAD_TLV:
-		dp_process_ppdu_stats_tx_mgmtctrl_payload_tlv(pdev,
-							tag_buf, tlv_len);
+		dp_process_ppdu_stats_tx_mgmtctrl_payload_tlv(
+				pdev, tag_buf, tlv_len);
 		break;
 	default:
 		break;
 	}
 }
 
-static QDF_STATUS dp_htt_process_tlv(struct dp_pdev *pdev,
+/**
+ * dp_ppdu_desc_deliver(): Function to deliver Tx PPDU status descriptor
+ * to upper layer
+ * @pdev: DP pdev handle
+ * @ppdu_info: per PPDU TLV descriptor
+ *
+ * return: void
+ */
+static
+void dp_ppdu_desc_deliver(struct dp_pdev *pdev,
+			      struct ppdu_info *ppdu_info)
+{
+	struct cdp_tx_completion_ppdu *ppdu_desc = NULL;
+	struct dp_peer *peer = NULL;
+	qdf_nbuf_t nbuf;
+	uint16_t i;
+
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)
+		qdf_nbuf_data(ppdu_info->nbuf);
+
+	ppdu_desc->num_users = ppdu_info->last_user;
+	ppdu_desc->ppdu_id = ppdu_info->ppdu_id;
+
+	for (i = 0; i < ppdu_desc->num_users; i++) {
+
+
+		ppdu_desc->num_mpdu += ppdu_desc->user[i].num_mpdu;
+		ppdu_desc->num_msdu += ppdu_desc->user[i].num_msdu;
+
+		if (ppdu_desc->user[i].tid < CDP_DATA_TID_MAX) {
+			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;
+
+			dp_tx_stats_update(pdev->soc, peer,
+					&ppdu_desc->user[i],
+					ppdu_desc->ack_rssi);
+		}
+	}
+
+	/*
+	 * Remove from the list
+	 */
+	TAILQ_REMOVE(&pdev->ppdu_info_list, ppdu_info, ppdu_info_list_elem);
+	nbuf = ppdu_info->nbuf;
+	pdev->list_depth--;
+	qdf_mem_free(ppdu_info);
+
+	qdf_assert_always(nbuf);
+
+	ppdu_desc = (struct cdp_tx_completion_ppdu *)
+		qdf_nbuf_data(nbuf);
+
+	/**
+	 * Deliver PPDU stats only for valid (acked) data frames if
+	 * sniffer mode is not enabled.
+	 * If sniffer mode is enabled, PPDU stats for all frames
+	 * including mgmt/control frames should be delivered to upper layer
+	 */
+	if (pdev->tx_sniffer_enable || pdev->mcopy_mode) {
+		dp_wdi_event_handler(WDI_EVENT_TX_PPDU_DESC, pdev->soc,
+				nbuf, HTT_INVALID_PEER,
+				WDI_NO_VAL, pdev->pdev_id);
+	} else {
+		if (ppdu_desc->num_mpdu != 0 && ppdu_desc->num_users != 0 &&
+				ppdu_desc->frame_ctrl & HTT_FRAMECTRL_DATATYPE) {
+
+			dp_wdi_event_handler(WDI_EVENT_TX_PPDU_DESC,
+					pdev->soc, nbuf, HTT_INVALID_PEER,
+					WDI_NO_VAL, pdev->pdev_id);
+		} else
+			qdf_nbuf_free(nbuf);
+	}
+	return;
+}
+
+/**
+ * dp_get_ppdu_desc(): Function to allocate new PPDU status
+ * desc for new ppdu id
+ * @pdev: DP pdev handle
+ * @ppdu_id: PPDU unique identifier
+ * @tlv_type: TLV type received
+ *
+ * return: ppdu_info per ppdu tlv structure
+ */
+static
+struct ppdu_info *dp_get_ppdu_desc(struct dp_pdev *pdev, uint32_t ppdu_id,
+			uint8_t tlv_type)
+{
+	struct ppdu_info *ppdu_info = NULL;
+
+	/*
+	 * Find ppdu_id node exists or not
+	 */
+	TAILQ_FOREACH(ppdu_info, &pdev->ppdu_info_list, ppdu_info_list_elem) {
+
+		if (ppdu_info && (ppdu_info->ppdu_id == ppdu_id)) {
+			break;
+		}
+	}
+
+	if (ppdu_info) {
+		/**
+		 * if we get tlv_type that is already been processed for ppdu,
+		 * that means we got a new ppdu with same ppdu id.
+		 * Hence Flush the older ppdu
+		 */
+		if (ppdu_info->tlv_bitmap & (1 << tlv_type))
+			dp_ppdu_desc_deliver(pdev, ppdu_info);
+		else
+			return ppdu_info;
+	}
+
+	/**
+	 * Flush the head ppdu descriptor if ppdu desc list reaches max
+	 * threshold
+	 */
+	if (pdev->list_depth > HTT_PPDU_DESC_MAX_DEPTH) {
+		ppdu_info = TAILQ_FIRST(&pdev->ppdu_info_list);
+		dp_ppdu_desc_deliver(pdev, ppdu_info);
+	}
+
+	/*
+	 * Allocate new ppdu_info node
+	 */
+	ppdu_info = qdf_mem_malloc(sizeof(struct ppdu_info));
+	if (!ppdu_info)
+		return NULL;
+
+	ppdu_info->nbuf = qdf_nbuf_alloc(pdev->soc->osdev,
+			sizeof(struct cdp_tx_completion_ppdu), 0, 4,
+			TRUE);
+	if (!ppdu_info->nbuf) {
+		qdf_mem_free(ppdu_info);
+		return NULL;
+	}
+
+	qdf_mem_zero(qdf_nbuf_data(ppdu_info->nbuf),
+			sizeof(struct cdp_tx_completion_ppdu));
+
+	if (qdf_nbuf_put_tail(ppdu_info->nbuf,
+			sizeof(struct cdp_tx_completion_ppdu)) == NULL) {
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
+				"No tailroom for HTT PPDU");
+		qdf_nbuf_free(ppdu_info->nbuf);
+		ppdu_info->nbuf = NULL;
+		ppdu_info->last_user = 0;
+		qdf_mem_free(ppdu_info);
+		return NULL;
+	}
+
+	/**
+	 * No lock is needed because all PPDU TLVs are processed in
+	 * same context and this list is updated in same context
+	 */
+	TAILQ_INSERT_TAIL(&pdev->ppdu_info_list, ppdu_info,
+			ppdu_info_list_elem);
+	pdev->list_depth++;
+	return ppdu_info;
+}
+
+/**
+ * dp_htt_process_tlv(): Function to process each PPDU TLVs
+ * @pdev: DP pdev handle
+ * @htt_t2h_msg: HTT target to host message
+ *
+ * return: ppdu_info per ppdu tlv structure
+ */
+
+static struct ppdu_info *dp_htt_process_tlv(struct dp_pdev *pdev,
 		qdf_nbuf_t htt_t2h_msg)
 {
 	uint32_t length;
 	uint32_t ppdu_id;
 	uint8_t tlv_type;
-	uint32_t tlv_length;
+	uint32_t tlv_length, tlv_bitmap_expected;
 	uint8_t *tlv_buf;
-	QDF_STATUS status = QDF_STATUS_E_PENDING;
+	struct ppdu_info *ppdu_info = NULL;
 
 	uint32_t *msg_word = (uint32_t *) qdf_nbuf_data(htt_t2h_msg);
 
@@ -2263,6 +2484,7 @@ static QDF_STATUS dp_htt_process_tlv(struct dp_pdev *pdev,
 	msg_word = msg_word + 1;
 	ppdu_id = HTT_T2H_PPDU_STATS_PPDU_ID_GET(*msg_word);
 
+
 	msg_word = msg_word + 3;
 	while (length > 0) {
 		tlv_buf = (uint8_t *)msg_word;
@@ -2274,22 +2496,56 @@ static QDF_STATUS dp_htt_process_tlv(struct dp_pdev *pdev,
 		if (tlv_length == 0)
 			break;
 
-		if (tlv_type == HTT_PPDU_STATS_SCH_CMD_STATUS_TLV)
-			status = QDF_STATUS_SUCCESS;
+		tlv_length += HTT_TLV_HDR_LEN;
+
+		/**
+		 * Not allocating separate ppdu descriptor for MGMT Payload
+		 * TLV as this is sent as separate WDI indication and it
+		 * doesn't contain any ppdu information
+		 */
+		if (tlv_type == HTT_PPDU_STATS_TX_MGMTCTRL_PAYLOAD_TLV) {
+			dp_process_ppdu_tag(pdev, msg_word, tlv_length, NULL);
+			msg_word = (uint32_t *)((uint8_t *)tlv_buf + tlv_length);
+			length -= (tlv_length);
+			continue;
+		}
 
-		if ((tlv_type == HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV) &&
-				(pdev->last_ppdu_id != ppdu_id))
-			status = QDF_STATUS_SUCCESS;
+		ppdu_info = dp_get_ppdu_desc(pdev, ppdu_id, tlv_type);
+		if (!ppdu_info)
+			return NULL;
+		ppdu_info->ppdu_id = ppdu_id;
+		ppdu_info->tlv_bitmap |= (1 << tlv_type);
 
-		tlv_length += HTT_TLV_HDR_LEN;
-		dp_process_ppdu_tag(pdev, msg_word, tlv_length);
+		dp_process_ppdu_tag(pdev, msg_word, tlv_length, ppdu_info);
 
+		/**
+		 * Increment pdev level tlv count to monitor
+		 * missing TLVs
+		 */
+		pdev->tlv_count++;
+		ppdu_info->last_tlv_cnt = pdev->tlv_count;
 
 		msg_word = (uint32_t *)((uint8_t *)tlv_buf + tlv_length);
 		length -= (tlv_length);
 	}
+
 	pdev->last_ppdu_id = ppdu_id;
-	return status;
+
+	tlv_bitmap_expected = HTT_PPDU_DEFAULT_TLV_BITMAP;
+
+	if (pdev->tx_sniffer_enable || pdev->mcopy_mode) {
+		if (ppdu_info->is_ampdu)
+			tlv_bitmap_expected = HTT_PPDU_SNIFFER_AMPDU_TLV_BITMAP;
+	}
+
+	/**
+	 * Once all the TLVs for a given PPDU has been processed,
+	 * return PPDU status to be delivered to higher layer
+	 */
+	if (ppdu_info->tlv_bitmap == tlv_bitmap_expected)
+		return ppdu_info;
+
+	return NULL;
 }
 #endif /* FEATURE_PERPKT_INFO */
 
@@ -2307,78 +2563,15 @@ static void dp_txrx_ppdu_stats_handler(struct dp_soc *soc,
 		uint8_t pdev_id, qdf_nbuf_t htt_t2h_msg)
 {
 	struct dp_pdev *pdev = soc->pdev_list[pdev_id];
-	struct dp_peer *peer;
-	struct cdp_tx_completion_ppdu *ppdu_desc;
-	int status;
-	int i;
+	struct ppdu_info *ppdu_info = NULL;
 
 	if (!pdev->enhanced_stats_en && !pdev->tx_sniffer_enable &&
 			!pdev->mcopy_mode)
 		return;
 
-	if (!pdev->tx_ppdu_info.buf) {
-		/*
-		 * Todo: For MU/OFDMA, we need to account for multiple user
-		 * descriptors in a PPDU, in skb size.
-		 * The allocation has to be moved to ppdu_cmn tlv processing
-		 */
-		pdev->tx_ppdu_info.buf = qdf_nbuf_alloc(soc->osdev,
-				sizeof(struct cdp_tx_completion_ppdu), 0, 4,
-				TRUE);
-
-		if (!pdev->tx_ppdu_info.buf) {
-			QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-					"Nbuf Allocation failed for HTT PPDU");
-			return;
-		}
-
-		qdf_mem_zero(qdf_nbuf_data(pdev->tx_ppdu_info.buf),
-				sizeof(struct cdp_tx_completion_ppdu));
-
-		if (qdf_nbuf_put_tail(pdev->tx_ppdu_info.buf,
-			sizeof(struct cdp_tx_completion_ppdu)) == NULL)	{
-			QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-					"No tailroom for HTT PPDU");
-			qdf_nbuf_free(pdev->tx_ppdu_info.buf);
-			pdev->tx_ppdu_info.buf = NULL;
-			pdev->tx_ppdu_info.last_user = 0;
-			return;
-		}
-
-	}
-
-	status = dp_htt_process_tlv(pdev, htt_t2h_msg);
-
-	if (status == QDF_STATUS_SUCCESS) {
-		ppdu_desc = (struct cdp_tx_completion_ppdu *)
-			qdf_nbuf_data(pdev->tx_ppdu_info.buf);
-
-		ppdu_desc->num_users = pdev->tx_ppdu_info.last_user;
-
-		for (i = 0; i < ppdu_desc->num_users; i++) {
-			peer = dp_peer_find_by_id(soc,
-					ppdu_desc->user[i].peer_id);
-			if (!peer)
-				continue;
-
-			ppdu_desc->num_mpdu += ppdu_desc->user[i].num_mpdu;
-			ppdu_desc->num_msdu += ppdu_desc->user[i].num_msdu;
-
-			if (ppdu_desc->user[i].tid < CDP_DATA_TID_MAX) {
-				dp_tx_stats_update(soc, peer,
-						&ppdu_desc->user[i],
-						ppdu_desc->ack_rssi);
-			}
-		}
-
-		dp_wdi_event_handler(WDI_EVENT_TX_PPDU_DESC, soc,
-				pdev->tx_ppdu_info.buf, HTT_INVALID_PEER,
-				WDI_NO_VAL, pdev_id);
-
-		pdev->tx_ppdu_info.buf = NULL;
-		pdev->tx_ppdu_info.last_user = 0;
-	}
-
+	ppdu_info = dp_htt_process_tlv(pdev, htt_t2h_msg);
+	if (ppdu_info)
+		dp_ppdu_desc_deliver(pdev, ppdu_info);
 }
 #else
 static void dp_txrx_ppdu_stats_handler(struct dp_soc *soc,

+ 7 - 0
dp/wifi3.0/dp_main.c

@@ -2614,6 +2614,13 @@ static struct cdp_pdev *dp_pdev_attach_wifi3(struct cdp_soc_t *txrx_soc,
 	/* set the reo destination during initialization */
 	pdev->reo_dest = pdev->pdev_id + 1;
 
+	/*
+	 * initialize ppdu tlv list
+	 */
+	TAILQ_INIT(&pdev->ppdu_info_list);
+	pdev->tlv_count = 0;
+	pdev->list_depth = 0;
+
 	return (struct cdp_pdev *)pdev;
 
 fail1:

+ 27 - 0
dp/wifi3.0/dp_types.h

@@ -938,6 +938,29 @@ struct dp_neighbour_peer {
 	TAILQ_ENTRY(dp_neighbour_peer) neighbour_peer_list_elem;
 };
 
+/**
+ * struct ppdu_info - PPDU Status info descriptor
+ * @ppdu_id         - Unique ppduid assigned by firmware for every tx packet
+ * @max_ppdu_id     - wrap around for ppdu id
+ * @last_tlv_cnt    - Keep track for missing ppdu tlvs
+ * @last_user       - last ppdu processed for user
+ * @is_ampdu        - set if Ampdu aggregate
+ * @nbuf            - ppdu descriptor payload
+ * @ppdu_desc       - ppdu descriptor
+ * @ppdu_info_list_elem - linked list of ppdu tlvs
+ */
+struct ppdu_info {
+	uint32_t ppdu_id;
+	uint32_t max_ppdu_id;
+	uint16_t tlv_bitmap;
+	uint16_t last_tlv_cnt;
+	uint16_t last_user:8,
+		 is_ampdu:1;
+	qdf_nbuf_t nbuf;
+	struct cdp_tx_completion_ppdu *ppdu_desc;
+	TAILQ_ENTRY(ppdu_info) ppdu_info_list_elem;
+};
+
 /* PDEV level structure for data path */
 struct dp_pdev {
 	/* PDEV handle from OSIF layer TBD: see if we really need osif_pdev */
@@ -1133,6 +1156,10 @@ struct dp_pdev {
 #ifdef ATH_SUPPORT_NAC_RSSI
 	bool nac_rssi_filtering;
 #endif
+	/* list of ppdu tlvs */
+	TAILQ_HEAD(, ppdu_info) ppdu_info_list;
+	uint32_t tlv_count;
+	uint32_t list_depth;
 };
 
 struct dp_peer;