Răsfoiți Sursa

qcacmn: Add support to send tx monitor packet to stack

Add support,
1. Deferred tx monitor processing
2. update radiotap headers and
3. skeleton flow for tx monitor to send each mpdu per user to
stack.

Change-Id: I0809a2fe06a9dc001f6da20a5c4a689244bf9a85
CRs-Fixed: 3115608
nobelj 4 ani în urmă
părinte
comite
dffdb7c215

+ 0 - 36
dp/wifi3.0/monitor/2.0/dp_mon_2.0.c

@@ -937,31 +937,6 @@ QDF_STATUS dp_mon_pdev_alloc_2_0(struct dp_pdev *pdev)
 	return QDF_STATUS_SUCCESS;
 }
 
-void dp_tx_ppdu_stats_attach_2_0(struct dp_pdev *pdev)
-{
-}
-
-void dp_tx_ppdu_stats_detach_2_0(struct dp_pdev *pdev)
-{
-}
-
-void dp_print_pdev_tx_capture_stats_2_0(struct dp_pdev *pdev)
-{
-}
-
-QDF_STATUS dp_config_enh_tx_capture_2_0(struct dp_pdev *pdev, uint8_t val)
-{
-	return QDF_STATUS_SUCCESS;
-}
-
-QDF_STATUS dp_peer_set_tx_capture_enabled_2_0(struct dp_pdev *pdev_handle,
-					      struct dp_peer *peer_handle,
-					      uint8_t is_tx_pkt_cap_enable,
-					      uint8_t *peer_mac)
-{
-	return QDF_STATUS_SUCCESS;
-}
-
 #else
 static inline
 QDF_STATUS dp_mon_htt_srng_setup_2_0(struct dp_soc *soc,
@@ -1035,17 +1010,6 @@ QDF_STATUS dp_vdev_set_monitor_mode_rings_2_0(struct dp_pdev *pdev,
 {
 	return QDF_STATUS_SUCCESS;
 }
-
-static inline
-void dp_mon_pdev_free_2_0(struct dp_pdev *pdev)
-{
-}
-
-static inline
-QDF_STATUS dp_mon_pdev_alloc_2_0(struct dp_pdev *pdev)
-{
-	return QDF_STATUS_SUCCESS;
-}
 #endif
 
 static void dp_mon_register_intr_ops_2_0(struct dp_soc *soc)

+ 509 - 0
dp/wifi3.0/monitor/2.0/dp_tx_mon_2.0.c

@@ -225,3 +225,512 @@ dp_tx_mon_buffers_alloc(struct dp_soc *soc, uint32_t size)
 					size,
 					&desc_list, &tail);
 }
+
+#ifdef WLAN_TX_PKT_CAPTURE_ENH_BE
+/*
+ * dp_tx_mon_free_usr_mpduq() - API to free user mpduq
+ * @tx_ppdu_info - pointer to tx_ppdu_info
+ * @usr_idx - user index
+ *
+ * Return: void
+ */
+void dp_tx_mon_free_usr_mpduq(struct dp_tx_ppdu_info *tx_ppdu_info,
+			      uint8_t usr_idx)
+{
+	qdf_nbuf_queue_t *mpdu_q;
+
+	if (qdf_unlikely(!tx_ppdu_info))
+		return;
+
+	mpdu_q = &TXMON_PPDU_USR(tx_ppdu_info, usr_idx, mpdu_q);
+	qdf_nbuf_queue_remove(mpdu_q);
+}
+
+/*
+ * dp_tx_mon_free_ppdu_info() - API to free dp_tx_ppdu_info
+ * @tx_ppdu_info - pointer to tx_ppdu_info
+ *
+ * Return: void
+ */
+void dp_tx_mon_free_ppdu_info(struct dp_tx_ppdu_info *tx_ppdu_info)
+{
+	uint32_t user = 0;
+
+	for (; user < TXMON_PPDU_HAL(tx_ppdu_info, num_users); user++) {
+		qdf_nbuf_queue_t *mpdu_q;
+
+		mpdu_q = &TXMON_PPDU_USR(tx_ppdu_info, user, mpdu_q);
+		qdf_nbuf_queue_remove(mpdu_q);
+	}
+
+	TXMON_PPDU_HAL(tx_ppdu_info, is_used) = 0;
+	qdf_mem_free(tx_ppdu_info);
+}
+
+/*
+ * dp_tx_mon_get_ppdu_info() - API to allocate dp_tx_ppdu_info
+ * @pdev - pdev handle
+ * @type - type of ppdu_info data or protection
+ * @num_user - number user in a ppdu_info
+ * @ppdu_id - ppdu_id number
+ *
+ * Return: pointer to dp_tx_ppdu_info
+ */
+struct dp_tx_ppdu_info *dp_tx_mon_get_ppdu_info(struct dp_pdev *pdev,
+						enum tx_ppdu_info_type type,
+						uint8_t num_user,
+						uint32_t ppdu_id)
+{
+	struct dp_mon_pdev *mon_pdev = pdev->monitor_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be =
+			dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	struct dp_pdev_tx_capture_be *tx_cap_be =
+			&mon_pdev_be->tx_capture_be;
+	struct dp_tx_ppdu_info *tx_ppdu_info;
+	size_t sz_ppdu_info = 0;
+	uint8_t i;
+
+	if (type == TX_PROT_PPDU_INFO) {
+		tx_ppdu_info = tx_cap_be->tx_prot_ppdu_info;
+		if (tx_ppdu_info &&
+		    TXMON_PPDU_HAL(tx_ppdu_info, is_used) != 1) {
+			return tx_ppdu_info;
+		} else if (tx_ppdu_info) {
+			dp_tx_mon_free_ppdu_info(tx_ppdu_info);
+			tx_ppdu_info = NULL;
+		}
+
+		/* for protection frame num_user is always 1 */
+		num_user = 1;
+	} else {
+		tx_ppdu_info = tx_cap_be->tx_data_ppdu_info;
+		if (tx_ppdu_info &&
+		    TXMON_PPDU_HAL(tx_ppdu_info, is_used) != 1 &&
+		    num_user == TXMON_PPDU_HAL(tx_ppdu_info, num_users)) {
+			/* number of user matched */
+			return tx_ppdu_info;
+		} else if (tx_ppdu_info) {
+			dp_tx_mon_free_ppdu_info(tx_ppdu_info);
+			tx_ppdu_info = NULL;
+		}
+	}
+
+	/* allocate new tx_ppdu_info */
+	sz_ppdu_info = (sizeof(struct dp_tx_ppdu_info) +
+			(sizeof(struct mon_rx_user_status) * num_user));
+
+	tx_ppdu_info = (struct dp_tx_ppdu_info *)qdf_mem_malloc(sz_ppdu_info);
+	if (!tx_ppdu_info) {
+		dp_mon_err("allocation of tx_ppdu_info type[%d] failed!!!",
+			   type);
+		return NULL;
+	}
+
+	TXMON_PPDU_HAL(tx_ppdu_info, is_used) = 0;
+	TXMON_PPDU_HAL(tx_ppdu_info, num_users) = num_user;
+	TXMON_PPDU_HAL(tx_ppdu_info, ppdu_id) = ppdu_id;
+
+	for (i = 0; i < num_user; i++) {
+		qdf_nbuf_queue_t *mpdu_q;
+
+		mpdu_q = &TXMON_PPDU_USR(tx_ppdu_info, i, mpdu_q);
+		if (qdf_unlikely(qdf_nbuf_queue_len(mpdu_q)))
+			qdf_nbuf_queue_free(mpdu_q);
+
+		qdf_nbuf_queue_init(mpdu_q);
+	}
+
+	/* assign tx_ppdu_info to monitor pdev for reference */
+	if (type == TX_PROT_PPDU_INFO) {
+		tx_cap_be->tx_prot_ppdu_info = tx_ppdu_info;
+		TXMON_PPDU_HAL(tx_ppdu_info, is_data) = 0;
+	} else {
+		tx_cap_be->tx_data_ppdu_info = tx_ppdu_info;
+		TXMON_PPDU_HAL(tx_ppdu_info, is_data) = 1;
+	}
+
+	return tx_ppdu_info;
+}
+
+/*
+ * dp_print_pdev_tx_capture_stats_2_0: print tx capture stats
+ * @pdev: DP PDEV handle
+ *
+ * return: void
+ */
+void dp_print_pdev_tx_capture_stats_2_0(struct dp_pdev *pdev)
+{
+	/* TX monitor stats needed for beryllium */
+}
+
+/*
+ * dp_config_enh_tx_capture_be()- API to enable/disable enhanced tx capture
+ * @pdev_handle: DP_PDEV handle
+ * @val: user provided value
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+dp_config_enh_tx_capture_2_0(struct dp_pdev *pdev, uint8_t val)
+{
+	struct dp_mon_pdev *mon_pdev = pdev->monitor_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be =
+			dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	struct dp_pdev_tx_capture_be *tx_cap_be =
+			&mon_pdev_be->tx_capture_be;
+
+	switch (val) {
+	case TX_MON_BE_DISABLE:
+	{
+		/* TODO: send HTT msg to configure TLV based on mode */
+		tx_cap_be->mode = TX_MON_BE_DISABLE;
+		break;
+	}
+	case TX_MON_BE_FULL_CAPTURE:
+	{
+		/* TODO: send HTT msg to configure TLV based on mode */
+		tx_cap_be->mode = TX_MON_BE_FULL_CAPTURE;
+		break;
+	}
+	case TX_MON_BE_PEER_FILTER:
+	{
+		/* TODO: send HTT msg to configure TLV based on mode */
+		tx_cap_be->mode = TX_MON_BE_PEER_FILTER;
+		break;
+	}
+	default:
+	{
+		/* TODO: do we need to set to disable ? */
+		return QDF_STATUS_E_INVAL;
+	}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/*
+ * dp_peer_set_tx_capture_enabled_2_0() -  add tx monitor peer filter
+ * @pdev: Datapath PDEV handle
+ * @peer: Datapath PEER handle
+ * @is_tx_pkt_cap_enable: flag for tx capture enable/disable
+ * @peer_mac: peer mac address
+ *
+ * Return: status
+ */
+QDF_STATUS dp_peer_set_tx_capture_enabled_2_0(struct dp_pdev *pdev_handle,
+					      struct dp_peer *peer_handle,
+					      uint8_t is_tx_pkt_cap_enable,
+					      uint8_t *peer_mac)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_tx_mon_send_to_stack() - API to send to stack
+ * @pdev: pdev Handle
+ * @mpdu: pointer to mpdu
+ *
+ * Return: void
+ */
+static void
+dp_tx_mon_send_to_stack(struct dp_pdev *pdev, qdf_nbuf_t mpdu)
+{
+	struct cdp_tx_indication_info tx_capture_info;
+
+	qdf_mem_set(&tx_capture_info,
+		    sizeof(struct cdp_tx_indication_info),
+		    0);
+	tx_capture_info.radiotap_done = 1;
+	tx_capture_info.mpdu_nbuf = mpdu;
+	dp_wdi_event_handler(WDI_EVENT_TX_PKT_CAPTURE,
+			     pdev->soc,
+			     &tx_capture_info,
+			     HTT_INVALID_PEER,
+			     WDI_NO_VAL,
+			     pdev->pdev_id);
+}
+
+/**
+ * dp_tx_mon_send_per_usr_mpdu() - API to send per usr mpdu to stack
+ * @pdev: pdev Handle
+ * @ppdu_info: pointer to dp_tx_ppdu_info
+ * @user_id: current user index
+ * @radiota_hdr_ref: pointer to radiotap ref
+ *
+ * Return: void
+ */
+static void
+dp_tx_mon_send_per_usr_mpdu(struct dp_pdev *pdev,
+			    struct dp_tx_ppdu_info *ppdu_info,
+			    uint8_t user_id,
+			    qdf_nbuf_t *radiotap_hdr_ref)
+{
+	uint32_t mpdu_queue_len = 0;
+	qdf_nbuf_queue_t *usr_mpdu_q = NULL;
+	qdf_nbuf_t buf = NULL;
+	qdf_nbuf_t radiotap_copy = NULL;
+
+	usr_mpdu_q = &TXMON_PPDU_USR(ppdu_info, user_id, mpdu_q);
+
+	if (!usr_mpdu_q) {
+		qdf_nbuf_free(*radiotap_hdr_ref);
+		*radiotap_hdr_ref = NULL;
+		/* free radiotap hdr */
+		return;
+	}
+
+	while ((buf = qdf_nbuf_queue_remove(usr_mpdu_q)) != NULL) {
+		mpdu_queue_len = qdf_nbuf_queue_len(usr_mpdu_q);
+		/*
+		 * In an aggregated frame, each mpdu is send individualy to
+		 * stack. caller of the function already allocate
+		 * and update radiotap header information. We copy the
+		 * same radiotap header until we reach the end of mpdu.
+		 * Once we reached end of mpdu we use the buffer allocated
+		 * for radiotap hdr.
+		 */
+		if (mpdu_queue_len) {
+			radiotap_copy = qdf_nbuf_copy_expand(*radiotap_hdr_ref,
+							     0, 0);
+		} else {
+			radiotap_copy = *radiotap_hdr_ref;
+			*radiotap_hdr_ref = NULL;
+		}
+
+		/* add msdu as a fraglist of mpdu */
+		qdf_nbuf_append_ext_list(radiotap_copy,
+					 buf, qdf_nbuf_len(buf));
+		dp_tx_mon_send_to_stack(pdev, radiotap_copy);
+	}
+}
+
+/**
+ * dp_tx_mon_alloc_radiotap_hdr() - API to allocate radiotap header
+ * @pdev: pdev Handle
+ * @nbuf_ref: reference to nbuf
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+dp_tx_mon_alloc_radiotap_hdr(struct dp_pdev *pdev, qdf_nbuf_t *nbuf_ref)
+{
+	*nbuf_ref = qdf_nbuf_alloc(pdev->soc->osdev,
+				   MAX_MONITOR_HEADER,
+				   MAX_MONITOR_HEADER,
+				   4, FALSE);
+	if (*nbuf_ref)
+		return QDF_STATUS_E_NOMEM;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * dp_tx_mon_update_radiotap() - API to update radiotap information
+ * @pdev: pdev Handle
+ * @ppdu_info: pointer to dp_tx_ppdu_info
+ *
+ * Return: void
+ */
+static void
+dp_tx_mon_update_radiotap(struct dp_pdev *pdev,
+			  struct dp_tx_ppdu_info *ppdu_info)
+{
+	uint32_t i = 0;
+	uint32_t num_users = 0;
+	qdf_nbuf_t radiotap_hdr = NULL;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	num_users = TXMON_PPDU_HAL(ppdu_info, num_users);
+
+	for (i = 0; i < num_users; i++) {
+		/* allocate radiotap_hdr */
+		status = dp_tx_mon_alloc_radiotap_hdr(pdev, &radiotap_hdr);
+		if (status != QDF_STATUS_SUCCESS) {
+			/* free ppdu_info per user */
+			dp_tx_mon_free_usr_mpduq(ppdu_info, i);
+			continue;
+		}
+
+		/* copy rx_status to rx_status and invoke update radiotap */
+		ppdu_info->hal_txmon.rx_status.rx_user_status =
+					&ppdu_info->hal_txmon.rx_user_status[i];
+		qdf_nbuf_update_radiotap(&ppdu_info->hal_txmon.rx_status,
+					 radiotap_hdr, MAX_MONITOR_HEADER);
+
+		/* radiotap header will be free in below function */
+		dp_tx_mon_send_per_usr_mpdu(pdev, ppdu_info, i, &radiotap_hdr);
+
+		/*
+		 * radiotap_hdr will be free in above function
+		 * if not free here
+		 */
+		if (!!radiotap_hdr)
+			qdf_nbuf_free(radiotap_hdr);
+	}
+}
+
+/**
+ * dp_tx_mon_ppdu_process - Deferred PPDU stats handler
+ * @context: Opaque work context (PDEV)
+ *
+ * Return: none
+ */
+void dp_tx_mon_ppdu_process(void *context)
+{
+	struct dp_pdev *pdev = (struct dp_pdev *)context;
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_tx_ppdu_info *defer_ppdu_info = NULL;
+	struct dp_tx_ppdu_info *defer_ppdu_info_next = NULL;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
+
+	/* sanity check */
+	if (qdf_unlikely(!pdev))
+		return;
+
+	mon_pdev = pdev->monitor_pdev;
+
+	if (qdf_unlikely(!mon_pdev))
+		return;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
+	if (qdf_unlikely(TX_MON_BE_DISABLE == tx_cap_be->mode))
+		return;
+
+	/* take lock here */
+	qdf_spin_lock_bh(&tx_cap_be->tx_mon_list_lock);
+	TAILQ_CONCAT(&tx_cap_be->defer_ppdu_info_list,
+		     &tx_cap_be->tx_ppdu_info_list,
+		     tx_ppdu_info_list_elem);
+	tx_cap_be->defer_ppdu_info_list_depth =
+		tx_cap_be->tx_ppdu_info_list_depth;
+	tx_cap_be->tx_ppdu_info_list_depth = 0;
+	qdf_spin_unlock_bh(&tx_cap_be->tx_mon_list_lock);
+
+	TAILQ_FOREACH_SAFE(defer_ppdu_info,
+			   &tx_cap_be->defer_ppdu_info_list,
+			   tx_ppdu_info_list_elem, defer_ppdu_info_next) {
+		/* remove dp_tx_ppdu_info from the list */
+		TAILQ_REMOVE(&tx_cap_be->defer_ppdu_info_list,
+			     defer_ppdu_info,
+			     tx_ppdu_info_list_elem);
+		tx_cap_be->defer_ppdu_info_list_depth--;
+
+		dp_tx_mon_update_radiotap(pdev, defer_ppdu_info);
+
+		/* free the ppdu_info */
+		dp_tx_mon_free_ppdu_info(defer_ppdu_info);
+		defer_ppdu_info = NULL;
+	}
+}
+
+/**
+ * dp_tx_ppdu_stats_attach_2_0 - Initialize Tx PPDU stats and enhanced capture
+ * @pdev: DP PDEV
+ *
+ * Return: none
+ */
+void dp_tx_ppdu_stats_attach_2_0(struct dp_pdev *pdev)
+{
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
+
+	if (qdf_unlikely(!pdev))
+		return;
+
+	mon_pdev = pdev->monitor_pdev;
+
+	if (qdf_unlikely(!mon_pdev))
+		return;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
+
+	TAILQ_INIT(&tx_cap_be->tx_ppdu_info_list);
+	tx_cap_be->tx_ppdu_info_list_depth = 0;
+
+	qdf_spinlock_create(&tx_cap_be->tx_mon_list_lock);
+	/* Work queue setup for TX MONITOR post handling */
+	qdf_create_work(0, &tx_cap_be->post_ppdu_work,
+			dp_tx_mon_ppdu_process, pdev);
+
+	tx_cap_be->post_ppdu_workqueue =
+			qdf_alloc_unbound_workqueue("tx_mon_ppdu_work_queue");
+}
+
+/**
+ * dp_tx_ppdu_stats_detach_be - Cleanup Tx PPDU stats and enhanced capture
+ * @pdev: DP PDEV
+ *
+ * Return: none
+ */
+void dp_tx_ppdu_stats_detach_2_0(struct dp_pdev *pdev)
+{
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
+	struct dp_tx_ppdu_info *tx_ppdu_info = NULL;
+	struct dp_tx_ppdu_info *tx_ppdu_info_next = NULL;
+
+	if (qdf_unlikely(!pdev))
+		return;
+
+	mon_pdev = pdev->monitor_pdev;
+
+	if (qdf_unlikely(!mon_pdev))
+		return;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
+	/* TODO: disable tx_monitor, to avoid further packet from HW */
+	dp_monitor_config_enh_tx_capture(pdev, TX_MON_BE_DISABLE);
+
+	/* flush workqueue */
+	qdf_flush_workqueue(0, tx_cap_be->post_ppdu_workqueue);
+	qdf_destroy_workqueue(0, tx_cap_be->post_ppdu_workqueue);
+
+	/*
+	 * TODO: iterate both tx_ppdu_info and defer_ppdu_info_list
+	 * free the tx_ppdu_info and decrement depth
+	 */
+	qdf_spin_lock_bh(&tx_cap_be->tx_mon_list_lock);
+	TAILQ_FOREACH_SAFE(tx_ppdu_info,
+			   &tx_cap_be->tx_ppdu_info_list,
+			   tx_ppdu_info_list_elem, tx_ppdu_info_next) {
+		/* remove dp_tx_ppdu_info from the list */
+		TAILQ_REMOVE(&tx_cap_be->tx_ppdu_info_list, tx_ppdu_info,
+			     tx_ppdu_info_list_elem);
+		/* decrement list length */
+		tx_cap_be->tx_ppdu_info_list_depth--;
+		/* free tx_ppdu_info */
+		dp_tx_mon_free_ppdu_info(tx_ppdu_info);
+	}
+	qdf_spin_unlock_bh(&tx_cap_be->tx_mon_list_lock);
+
+	qdf_spin_lock_bh(&tx_cap_be->tx_mon_list_lock);
+	TAILQ_FOREACH_SAFE(tx_ppdu_info, &tx_cap_be->defer_ppdu_info_list,
+			   tx_ppdu_info_list_elem, tx_ppdu_info_next) {
+		/* remove dp_tx_ppdu_info from the list */
+		TAILQ_REMOVE(&tx_cap_be->defer_ppdu_info_list, tx_ppdu_info,
+			     tx_ppdu_info_list_elem);
+		/* decrement list length */
+		tx_cap_be->defer_ppdu_info_list_depth--;
+		/* free tx_ppdu_info */
+		dp_tx_mon_free_ppdu_info(tx_ppdu_info);
+	}
+	qdf_spin_unlock_bh(&tx_cap_be->tx_mon_list_lock);
+
+	qdf_spinlock_destroy(&tx_cap_be->tx_mon_list_lock);
+}
+#endif

+ 90 - 0
dp/wifi3.0/monitor/2.0/dp_tx_mon_2.0.h

@@ -103,6 +103,7 @@ dp_tx_mon_process_2_0(struct dp_soc *soc, struct dp_intr *int_ctx,
 #define MAX_MONITOR_HEADER (512)
 #define MAX_DUMMY_FRM_BODY (128)
 
+#define MAX_STATUS_BUFFER_IN_PPDU (64)
 #define TXMON_NO_BUFFER_SZ (64)
 
 #define TXMON_PPDU(ppdu_info, field) ppdu_info->field
@@ -321,4 +322,93 @@ struct dp_pdev_tx_capture_be {
 struct dp_peer_tx_capture_be {
 };
 #endif /* WLAN_TX_PKT_CAPTURE_ENH_BE */
+
+/*
+ * dp_tx_mon_ppdu_info_free() - API to free dp_tx_ppdu_info
+ * @tx_ppdu_info - pointer to tx_ppdu_info
+ *
+ * Return: void
+ */
+void dp_tx_mon_ppdu_info_free(struct dp_tx_ppdu_info *tx_ppdu_info);
+
+/*
+ * dp_tx_mon_free_usr_mpduq() - API to free user mpduq
+ * @tx_ppdu_info - pointer to tx_ppdu_info
+ * @usr_idx - user index
+ *
+ * Return: void
+ */
+void dp_tx_mon_free_usr_mpduq(struct dp_tx_ppdu_info *tx_ppdu_info,
+			      uint8_t usr_idx);
+
+/*
+ * dp_tx_mon_free_ppdu_info() - API to free dp_tx_ppdu_info
+ * @tx_ppdu_info - pointer to tx_ppdu_info
+ *
+ * Return: void
+ */
+void dp_tx_mon_free_ppdu_info(struct dp_tx_ppdu_info *tx_ppdu_info);
+
+/*
+ * dp_tx_mon_get_ppdu_info() - API to allocate dp_tx_ppdu_info
+ * @pdev - pdev handle
+ * @type - type of ppdu_info data or protection
+ * @num_user - number user in a ppdu_info
+ * @ppdu_id - ppdu_id number
+ *
+ * Return: pointer to dp_tx_ppdu_info
+ */
+struct dp_tx_ppdu_info *dp_tx_mon_get_ppdu_info(struct dp_pdev *pdev,
+						enum tx_ppdu_info_type type,
+						uint8_t num_user,
+						uint32_t ppdu_id);
+
+#ifdef WLAN_TX_PKT_CAPTURE_ENH_BE
+/**
+ * dp_tx_ppdu_stats_attach_2_0 - Initialize Tx PPDU stats and enhanced capture
+ * @pdev: DP PDEV
+ *
+ * Return: none
+ */
+void dp_tx_ppdu_stats_attach_2_0(struct dp_pdev *pdev);
+
+/**
+ * dp_tx_ppdu_stats_detach_2_0 - Cleanup Tx PPDU stats and enhanced capture
+ * @pdev: DP PDEV
+ *
+ * Return: none
+ */
+void dp_tx_ppdu_stats_detach_2_0(struct dp_pdev *pdev);
+
+/*
+ * dp_print_pdev_tx_capture_stats_2_0: print tx capture stats
+ * @pdev: DP PDEV handle
+ *
+ * return: void
+ */
+void dp_print_pdev_tx_capture_stats_2_0(struct dp_pdev *pdev);
+
+/*
+ * dp_config_enh_tx_capture_be()- API to enable/disable enhanced tx capture
+ * @pdev_handle: DP_PDEV handle
+ * @val: user provided value
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS dp_config_enh_tx_capture_2_0(struct dp_pdev *pdev, uint8_t val);
+
+/*
+ * dp_peer_set_tx_capture_enabled_2_0() -  add tx monitor peer filter
+ * @pdev: Datapath PDEV handle
+ * @peer: Datapath PEER handle
+ * @is_tx_pkt_cap_enable: flag for tx capture enable/disable
+ * @peer_mac: peer mac address
+ *
+ * Return: status
+ */
+QDF_STATUS dp_peer_set_tx_capture_enabled_2_0(struct dp_pdev *pdev_handle,
+					      struct dp_peer *peer_handle,
+					      uint8_t is_tx_pkt_cap_enable,
+					      uint8_t *peer_mac);
+#endif /* WLAN_TX_PKT_CAPTURE_ENH_BE */
 #endif /* _DP_TX_MON_2_0_H_ */

+ 97 - 18
dp/wifi3.0/monitor/2.0/dp_tx_mon_status_2.0.c

@@ -360,14 +360,28 @@ static void
 dp_tx_mon_generate_data_frm(struct dp_pdev *pdev,
 			    struct dp_tx_ppdu_info *tx_ppdu_info)
 {
-	struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
-	struct dp_mon_pdev_be *mpdev = be_pdev->monitor_pdev_be;
-	struct dp_pdev_tx_capture_be *tx_cap_be = &mpdev->tx_capture_be;
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
 	struct hal_tx_status_info *tx_status_info;
 	qdf_nbuf_t mpdu_nbuf = NULL;
 	qdf_nbuf_queue_t *usr_mpdu_q = NULL;
 	uint32_t usr_idx = 0;
 
+	/* sanity check */
+	if (qdf_unlikely(!pdev))
+		return;
+
+	mon_pdev = pdev->monitor_pdev;
+	if (qdf_unlikely(!mon_pdev))
+		return;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
+
 	tx_status_info = &tx_cap_be->data_status_info;
 	usr_idx = TXMON_PPDU(tx_ppdu_info, cur_usr_idx);
 	usr_mpdu_q = &TXMON_PPDU_USR(tx_ppdu_info, usr_idx, mpdu_q);
@@ -396,11 +410,24 @@ static void
 dp_tx_mon_generate_prot_frm(struct dp_pdev *pdev,
 			    struct dp_tx_ppdu_info *tx_ppdu_info)
 {
-	struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
-	struct dp_mon_pdev_be *mpdev = be_pdev->monitor_pdev_be;
-	struct dp_pdev_tx_capture_be *tx_cap_be = &mpdev->tx_capture_be;
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
 	struct hal_tx_status_info *tx_status_info;
 
+	/* sanity check */
+	if (qdf_unlikely(!pdev))
+		return;
+
+	mon_pdev = pdev->monitor_pdev;
+	if (qdf_unlikely(!mon_pdev))
+		return;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
 	tx_status_info = &tx_cap_be->prot_status_info;
 
 	switch (TXMON_STATUS_INFO(tx_status_info, medium_prot_type)) {
@@ -455,13 +482,26 @@ dp_tx_mon_update_ppdu_info_status(struct dp_pdev *pdev,
 				  qdf_frag_t status_frag,
 				  uint32_t tlv_status)
 {
-	struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
-	struct dp_mon_pdev_be *mpdev = be_pdev->monitor_pdev_be;
-	struct dp_pdev_tx_capture_be *tx_cap_be = &mpdev->tx_capture_be;
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
 	struct hal_tx_status_info *tx_status_info;
-
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
 
+	/* sanity check */
+	if (qdf_unlikely(!pdev))
+		return QDF_STATUS_E_NOMEM;
+
+	mon_pdev = pdev->monitor_pdev;
+	if (qdf_unlikely(!mon_pdev))
+		return QDF_STATUS_E_NOMEM;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return QDF_STATUS_E_NOMEM;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
+
 	switch (tlv_status) {
 	case HAL_MON_TX_FES_SETUP:
 	{
@@ -555,9 +595,9 @@ dp_tx_mon_update_ppdu_info_status(struct dp_pdev *pdev,
  */
 QDF_STATUS dp_tx_mon_process_tlv_2_0(struct dp_pdev *pdev)
 {
-	struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
-	struct dp_mon_pdev_be *mpdev = be_pdev->monitor_pdev_be;
-	struct dp_pdev_tx_capture_be *tx_cap_be = &mpdev->tx_capture_be;
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
 	struct dp_tx_ppdu_info *tx_prot_ppdu_info = NULL;
 	struct dp_tx_ppdu_info *tx_data_ppdu_info = NULL;
 	struct hal_tx_status_info *tx_status_prot;
@@ -568,7 +608,22 @@ QDF_STATUS dp_tx_mon_process_tlv_2_0(struct dp_pdev *pdev)
 	uint32_t tlv_status;
 	uint32_t status;
 	uint8_t num_users = 0;
-	uint8_t cur_frag_q_idx = tx_cap_be->cur_frag_q_idx;
+	uint8_t cur_frag_q_idx;
+
+	/* sanity check */
+	if (qdf_unlikely(!pdev))
+		return QDF_STATUS_E_NOMEM;
+
+	mon_pdev = pdev->monitor_pdev;
+	if (qdf_unlikely(!mon_pdev))
+		return QDF_STATUS_E_NOMEM;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return QDF_STATUS_E_NOMEM;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
+	cur_frag_q_idx = tx_cap_be->cur_frag_q_idx;
 
 	tx_status_prot = &tx_cap_be->prot_status_info;
 	tx_status_data = &tx_cap_be->data_status_info;
@@ -725,10 +780,34 @@ QDF_STATUS dp_tx_mon_process_status_tlv(struct dp_soc *soc,
 					struct hal_mon_desc *mon_ring_desc,
 					qdf_dma_addr_t addr)
 {
-	struct dp_pdev_be *be_pdev = dp_get_be_pdev_from_dp_pdev(pdev);
-	struct dp_mon_pdev_be *mpdev = be_pdev->monitor_pdev_be;
-	struct dp_pdev_tx_capture_be *tx_cap_be = &mpdev->tx_capture_be;
-	uint8_t last_frag_q_idx = tx_cap_be->last_frag_q_idx;
+	struct dp_mon_pdev *mon_pdev;
+	struct dp_mon_pdev_be *mon_pdev_be;
+	struct dp_pdev_tx_capture_be *tx_cap_be;
+	uint8_t last_frag_q_idx;
+
+	/* sanity check */
+	if (qdf_unlikely(!pdev))
+		return QDF_STATUS_E_NOMEM;
+
+	mon_pdev = pdev->monitor_pdev;
+	if (qdf_unlikely(!mon_pdev))
+		return QDF_STATUS_E_NOMEM;
+
+	mon_pdev_be = dp_get_be_mon_pdev_from_dp_mon_pdev(mon_pdev);
+	if (qdf_unlikely(!mon_pdev_be))
+		return QDF_STATUS_E_NOMEM;
+
+	tx_cap_be = &mon_pdev_be->tx_capture_be;
+
+	last_frag_q_idx = tx_cap_be->last_frag_q_idx;
+
+	if (qdf_unlikely(last_frag_q_idx > MAX_STATUS_BUFFER_IN_PPDU)) {
+		dp_mon_err("status frag queue for a ppdu[%d] exceed %d\n",
+			   mon_pdev_be->be_ppdu_id,
+			   MAX_STATUS_BUFFER_IN_PPDU);
+		dp_tx_mon_status_queue_free(pdev, tx_cap_be);
+		return QDF_STATUS_E_NOMEM;
+	}
 
 	if (tx_cap_be->mode == TX_MON_BE_DISABLE)
 		return QDF_STATUS_E_INVAL;