Jelajahi Sumber

qcacmn: Add support for WSI link info libraries

Add support to capture ingress and egress stats
in the MLO manager

Change-Id: Ife0acab15ba802c953cfcf3a720c6e54933ded5e
CRs-Fixed: 3601749
Aditya Sathish 1 tahun lalu
induk
melakukan
25c27f83e0

+ 40 - 0
target_if/mlo_mgr/src/target_if_mlo_mgr.c

@@ -790,6 +790,43 @@ target_if_request_ml_link_state_info(struct wlan_objmgr_psoc *psoc,
 	return status;
 }
 
+#ifdef WLAN_WSI_STATS_SUPPORT
+static QDF_STATUS
+target_if_mlo_send_wsi_link_info_cmd(struct wlan_objmgr_pdev *pdev,
+				     struct mlo_wsi_link_stats *param)
+{
+	struct wmi_unified *wmi_handle;
+	struct wmi_wsi_stats_info_params params = {0};
+
+	if (!pdev) {
+		target_if_err("null pdev");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+	wmi_handle = get_wmi_unified_hdl_from_pdev(pdev);
+	if (!wmi_handle) {
+		target_if_err("null handle");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	params.pdev_id = pdev->pdev_objmgr.wlan_pdev_id;
+	params.wsi_ingress_load_info = param->ingress_cnt;
+	params.wsi_egress_load_info = param->egress_cnt;
+
+	target_if_debug("pdev id %d, ingress %d, egress %d", params.pdev_id,
+			params.wsi_ingress_load_info,
+			params.wsi_egress_load_info);
+
+	return wmi_unified_config_wsi_stats_info_cmd_send(wmi_handle, &params);
+}
+#else
+static QDF_STATUS
+target_if_mlo_send_wsi_link_info_cmd(struct wlan_objmgr_pdev *pdev,
+				     struct mlo_wsi_link_stats *param)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 QDF_STATUS target_if_mlo_send_link_removal_cmd(
 		struct wlan_objmgr_psoc *psoc,
 		const struct mlo_link_removal_cmd_params *param)
@@ -912,6 +949,9 @@ target_if_mlo_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops)
 
 	target_if_mlo_register_link_switch_cnf_handler(mlo_tx_ops);
 
+	mlo_tx_ops->send_wsi_link_info_cmd =
+		target_if_mlo_send_wsi_link_info_cmd;
+
 	target_if_mlo_register_peer_ptqm_migrate_send(mlo_tx_ops);
 	return QDF_STATUS_SUCCESS;
 }

+ 4 - 0
umac/global_umac_dispatcher/lmac_if/inc/wlan_lmac_if_def.h

@@ -1531,6 +1531,7 @@ struct wlan_lmac_if_son_rx_ops {
  * @send_vdev_pause: function to send MLO vdev pause to FW
  * @peer_ptqm_migrate_send: API to send peer ptqm migration request to FW
  * @send_mlo_link_switch_cnf_cmd: Send link switch status to FW
+ * @send_wsi_link_info_cmd: send WSI link stats to FW
  */
 struct wlan_lmac_if_mlo_tx_ops {
 	QDF_STATUS (*register_events)(struct wlan_objmgr_psoc *psoc);
@@ -1561,6 +1562,9 @@ struct wlan_lmac_if_mlo_tx_ops {
 	(*send_mlo_link_switch_cnf_cmd)(struct wlan_objmgr_psoc *psoc,
 					struct wlan_mlo_link_switch_cnf *params);
 #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
+	QDF_STATUS (*send_wsi_link_info_cmd)(
+				struct wlan_objmgr_pdev *pdev,
+				struct mlo_wsi_link_stats *param);
 };
 
 /**

+ 22 - 0
umac/mlo_mgr/inc/wlan_mlo_mgr_main.h

@@ -1000,6 +1000,28 @@ QDF_STATUS wlan_mlo_mgr_mld_vdev_attach(struct wlan_objmgr_vdev *vdev,
  * Return: QDF_STATUS
  */
 QDF_STATUS wlan_mlo_mgr_mld_vdev_detach(struct wlan_objmgr_vdev *vdev);
+
+#ifdef WLAN_MLO_MULTI_CHIP
+#ifdef WLAN_WSI_STATS_SUPPORT
+/**
+ * mlo_wsi_link_info_update_soc() - Update PSOC group in WSI stats
+ * @psoc: PSOC object
+ * @grp_id: Group ID
+ *
+ * API to update PSOC group id in WSI statas.
+ *
+ * Return: void
+ */
+void mlo_wsi_link_info_update_soc(struct wlan_objmgr_psoc *psoc,
+				  uint8_t grp_id);
+#else
+static void mlo_wsi_link_info_update_soc(struct wlan_objmgr_psoc *psoc,
+					 uint8_t grp_id)
+{
+}
+#endif
+#endif
+
 #else
 static inline QDF_STATUS wlan_mlo_mgr_init(void)
 {

+ 46 - 0
umac/mlo_mgr/inc/wlan_mlo_mgr_peer.h

@@ -835,4 +835,50 @@ uint8_t
 wlan_mld_get_best_primary_umac_w_rssi(struct wlan_mlo_peer_context *ml_peer,
 				      struct wlan_objmgr_vdev *link_vdevs[],
 				      bool allow_all_links);
+
+/**
+ * wlan_mlo_wsi_link_info_send_cmd() - Send WSI stats to FW
+ *
+ * API to send WMI commands for all radios of all PSOCs
+ *
+ * Return: SUCCESS, on sending WMI commands
+ */
+QDF_STATUS wlan_mlo_wsi_link_info_send_cmd(void);
+
+/**
+ * wlan_mlo_wsi_stats_allow_cmd() - Allow WSI stats to FW
+ *
+ * API to allows WSI stats WMI commands for all radios of all PSOCs
+ *
+ * Return: void
+ */
+void wlan_mlo_wsi_stats_allow_cmd(void);
+
+/**
+ * wlan_mlo_wsi_stats_block_cmd() - Block WSI stats to FW
+ *
+ * API to block WST stats WMI commands for all radios of all PSOCs
+ *
+ * Return: void
+ */
+void wlan_mlo_wsi_stats_block_cmd(void);
+/**
+ * wlan_mlo_peer_wsi_link_add() - Add peer to WSI info list
+ * @ml_peer: ML peer context
+ *
+ * API to add peer to WSI link stats
+ *
+ * Return: SUCCESS, if peer details added to WSI link stats
+ */
+QDF_STATUS wlan_mlo_peer_wsi_link_add(struct wlan_mlo_peer_context *ml_peer);
+
+/**
+ * wlan_mlo_peer_wsi_link_delete() - Delete peer to WSI info list
+ * @ml_peer: ML peer context
+ *
+ * API to Delete peer from WSI link stats
+ *
+ * Return: SUCCESS, if peer details deleted from WSI link stats
+ */
+QDF_STATUS wlan_mlo_peer_wsi_link_delete(struct wlan_mlo_peer_context *ml_peer);
 #endif

+ 41 - 0
umac/mlo_mgr/inc/wlan_mlo_mgr_public_structs.h

@@ -284,6 +284,45 @@ mlo_mgr_unregister_link_switch_notifier(enum wlan_umac_comp_id comp_id)
 }
 #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
 
+/*
+ * struct mlo_wsi_link_stats - MLO ingress/egress counters of PSOC
+ * @ingress_cnt:  Ingress counter
+ * @egress_cnt:  Egress counter
+ * @send_wmi_cmd: To indicate whether WMI command to be sent or not
+ */
+struct mlo_wsi_link_stats {
+	uint32_t ingress_cnt;
+	uint32_t egress_cnt;
+	bool  send_wmi_cmd;
+};
+
+/*
+ * struct mlo_wsi_psoc_grp - MLO WSI PSOC group
+ * @psoc_order:  PSOC list in WSI loop order
+ * @num_psoc: num psoc in the group
+ */
+struct mlo_wsi_psoc_grp {
+	uint32_t psoc_order[WLAN_OBJMGR_MAX_DEVICES];
+	uint32_t num_psoc;
+};
+
+#define MLO_WSI_MAX_MLO_GRPS 2
+#define MLO_WSI_PSOC_ID_MAX 0xFF
+
+/*
+ * struct mlo_wsi_info - MLO ingress/egress link context per-PSOC
+ * @mlo_psoc_grp: PSOC IDs for different MLO groups
+ * @num_psoc: Total num psoc
+ * @link_stats: Ingress and Egress counts for PSOCs
+ * @block_wmi_cmd: Blocks WMI command
+ */
+struct mlo_wsi_info {
+	struct mlo_wsi_psoc_grp mlo_psoc_grp[MLO_WSI_MAX_MLO_GRPS];
+	uint32_t num_psoc;
+	struct mlo_wsi_link_stats link_stats[WLAN_OBJMGR_MAX_DEVICES];
+	uint8_t block_wmi_cmd;
+};
+
 /**
  * struct mlo_mgr_context - MLO manager context
  * @ml_dev_list_lock: ML DEV list lock
@@ -302,6 +341,7 @@ mlo_mgr_unregister_link_switch_notifier(enum wlan_umac_comp_id comp_id)
  * @mlo_forced_primary_umac_id: Force Primary UMAC ID
  * @force_non_assoc_prim_umac: Force non-assoc link to be primary umac
  * @lswitch_notifier: Link switch notifier callbacks
+ * @wsi_info: WSI stats info
  */
 struct mlo_mgr_context {
 #ifdef WLAN_MLO_USE_SPINLOCK
@@ -332,6 +372,7 @@ struct mlo_mgr_context {
 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
 	struct wlan_mlo_link_switch_notifier lswitch_notifier[WLAN_UMAC_COMP_ID_MAX];
 #endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
+	struct mlo_wsi_info *wsi_info;
 };
 
 /**

+ 146 - 0
umac/mlo_mgr/src/wlan_mlo_mgr_main.c

@@ -34,6 +34,146 @@
 #include <wlan_mlo_mgr_public_api.h>
 #include "cdp_txrx_cmn.h"
 
+#ifdef WLAN_WSI_STATS_SUPPORT
+/*
+ * wlan_mlo_wsi_get_num_psocs() - Get the number of attached PSOCs
+ * @psoc: Pointer to psoc
+ * @arg: Pointer to variable to store count
+ * @index: Index for iteration function
+ */
+static void wlan_mlo_wsi_get_num_psocs(struct wlan_objmgr_psoc *psoc,
+				       void *arg, uint8_t index)
+{
+	/* If arg is NULL then skip increment */
+	if (!arg)
+		return;
+
+	(*(uint32_t *)arg)++;
+}
+
+static void mlo_wsi_link_info_deinit(struct mlo_mgr_context *mlo_mgr)
+{
+	if (!mlo_mgr)
+		return;
+
+	if (mlo_mgr->wsi_info) {
+		qdf_mem_free(mlo_mgr->wsi_info);
+		mlo_mgr->wsi_info = NULL;
+	}
+}
+
+#ifdef WLAN_MLO_MULTI_CHIP
+void mlo_wsi_link_info_update_soc(struct wlan_objmgr_psoc *psoc,
+				  uint8_t grp_id)
+{
+	struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx();
+	struct mlo_wsi_psoc_grp *mlo_grp_info;
+	uint8_t i, j;
+
+	if (!mlo_ctx) {
+		mlo_err("Invalid mlo_mgr_ctx");
+		return;
+	}
+
+	mlo_grp_info = &mlo_ctx->wsi_info->mlo_psoc_grp[grp_id];
+	if (!mlo_grp_info) {
+		mlo_err("mlo_grp_info is invalid for ix %d", i);
+		return;
+	}
+
+	mlo_ctx->wsi_info->num_psoc++;
+
+	/* Set the PSOC order for the MLO group */
+	for (j = 0; j < WLAN_OBJMGR_MAX_DEVICES; j++) {
+		if (mlo_grp_info->psoc_order[j] == MLO_WSI_PSOC_ID_MAX) {
+			mlo_grp_info->psoc_order[j] = wlan_psoc_get_id(psoc);
+			mlo_grp_info->num_psoc++;
+			break;
+		}
+	}
+}
+#endif
+
+static void
+mlo_wsi_link_info_setup_mlo_grps(struct mlo_mgr_context *mlo_mgr)
+{
+	struct mlo_wsi_psoc_grp *mlo_grp_info;
+	uint8_t i, j;
+
+	if (!mlo_mgr) {
+		mlo_err("Invalid mlo_mgr");
+		return;
+	}
+
+	if (!mlo_mgr->wsi_info) {
+		mlo_err("Invalid wsi_info");
+		return;
+	}
+
+	wlan_objmgr_iterate_psoc_list(wlan_mlo_wsi_get_num_psocs,
+				      &mlo_mgr->wsi_info->num_psoc,
+				      WLAN_MLO_MGR_ID);
+	if (!mlo_mgr->wsi_info->num_psoc)
+		mlo_info("Could not find active PSOCs");
+
+	for (i = 0; i < MLO_WSI_MAX_MLO_GRPS; i++) {
+		mlo_grp_info =
+			&mlo_mgr->wsi_info->mlo_psoc_grp[i];
+		if (!mlo_grp_info) {
+			mlo_err("mlo_grp_info is invalid for ix %d", i);
+			continue;
+		}
+
+		/* Set the PSOC order for the MLO group */
+		for (j = 0; j < WLAN_OBJMGR_MAX_DEVICES; j++) {
+			/*
+			 * NOTE: Inclusion of more MLO groups will require
+			 * changes to this block where rvalue will need
+			 * to be checked against the group they need to
+			 * be assigned to.
+			 */
+			if (j < mlo_mgr->wsi_info->num_psoc) {
+				mlo_grp_info->psoc_order[j] = j;
+				mlo_grp_info->num_psoc++;
+			} else {
+				mlo_grp_info->psoc_order[j] =
+							MLO_WSI_PSOC_ID_MAX;
+			}
+			mlo_err("PSOC order %d, index %d",
+				mlo_grp_info->psoc_order[j], j);
+		}
+	}
+}
+
+static void mlo_wsi_link_info_init(struct mlo_mgr_context *mlo_mgr)
+{
+	uint8_t i;
+
+	if (!mlo_mgr)
+		return;
+
+	/* Initialize the mlo_wsi_link_info structure */
+	mlo_mgr->wsi_info = qdf_mem_malloc(
+					sizeof(struct mlo_wsi_info));
+	if (!mlo_mgr->wsi_info) {
+		mlo_err("Could not allocate memory for wsi_link_info");
+		return;
+	}
+
+	/* Initialize the MLO group context in the WSI stats */
+	for (i = 0; i < MLO_WSI_MAX_MLO_GRPS; i++)
+		mlo_wsi_link_info_setup_mlo_grps(mlo_mgr);
+}
+#else
+static void mlo_wsi_link_info_init(struct mlo_mgr_context *mlo_mgr)
+{
+}
+
+static void mlo_wsi_link_info_deinit(struct mlo_mgr_context *mlo_mgr)
+{
+}
+#endif
+
 static void mlo_global_ctx_deinit(void)
 {
 	struct mlo_mgr_context *mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx();
@@ -44,6 +184,9 @@ static void mlo_global_ctx_deinit(void)
 	if (qdf_list_empty(&mlo_mgr_ctx->ml_dev_list))
 		mlo_debug("ML dev list is not empty");
 
+	/* Deallocation of the WSI link information */
+	mlo_wsi_link_info_deinit(mlo_mgr_ctx);
+
 	mlo_setup_deinit();
 	mlo_msgq_free();
 	ml_peerid_lock_destroy(mlo_mgr_ctx);
@@ -82,6 +225,9 @@ static void mlo_global_ctx_init(void)
 	mlo_mgr_ctx->mlo_is_force_primary_umac = 0;
 	mlo_mgr_ctx->force_non_assoc_prim_umac = 0;
 	mlo_msgq_init();
+
+	/* Allocation of the WSI link information */
+	mlo_wsi_link_info_init(mlo_mgr_ctx);
 }
 
 /**

+ 334 - 0
umac/mlo_mgr/src/wlan_mlo_mgr_peer.c

@@ -474,6 +474,7 @@ void wlan_mlo_partner_peer_assoc_post(struct wlan_objmgr_peer *assoc_peer)
 
 		link_peers[i] = link_peer;
 	}
+	wlan_mlo_peer_wsi_link_add(ml_peer);
 	mlo_peer_lock_release(ml_peer);
 
 	for (i = 0; i < MAX_MLO_LINK_PEERS; i++) {
@@ -739,6 +740,8 @@ void wlan_mlo_partner_peer_disconnect_notify(struct wlan_objmgr_peer *src_peer)
 		return;
 	}
 
+	wlan_mlo_peer_wsi_link_delete(ml_peer);
+
 	ml_peer->mlpeer_state = ML_PEER_DISCONN_INITIATED;
 
 	if (wlan_vdev_mlme_get_opmode(vdev) == QDF_STA_MODE) {
@@ -1885,8 +1888,15 @@ QDF_STATUS wlan_mlo_link_peer_delete(struct wlan_objmgr_peer *peer)
 	if (!ml_peer)
 		return QDF_STATUS_E_NOENT;
 
+	if (ml_peer->mlpeer_state != ML_PEER_DISCONN_INITIATED)
+		wlan_mlo_peer_wsi_link_delete(ml_peer);
+
 	mlo_reset_link_peer(ml_peer, peer);
 	mlo_peer_detach_link_peer(ml_peer, peer);
+
+	if (ml_peer->mlpeer_state != ML_PEER_DISCONN_INITIATED)
+		wlan_mlo_peer_wsi_link_add(ml_peer);
+
 	wlan_mlo_peer_release_ref(ml_peer);
 
 	return QDF_STATUS_SUCCESS;
@@ -2320,3 +2330,327 @@ QDF_STATUS wlan_mlo_validate_reassocreq(struct wlan_mlo_peer_context *ml_peer)
 
 	return status;
 }
+
+#ifdef WLAN_WSI_STATS_SUPPORT
+static uint32_t
+wlan_mlo_psoc_get_mlo_grp_idx(struct mlo_mgr_context *mlo_mgr,
+			      uint32_t psoc_id)
+{
+	struct mlo_wsi_info *wsi_info = NULL;
+	struct mlo_wsi_psoc_grp *mlo_psoc_grp;
+	uint8_t grp_id, i;
+
+	if (!mlo_mgr)
+		return MLO_WSI_MAX_MLO_GRPS;
+
+	/* Retrieve the WSI link info structure */
+	wsi_info = mlo_mgr->wsi_info;
+	if (!wsi_info)
+		return MLO_WSI_MAX_MLO_GRPS;
+
+	for (grp_id = 0; grp_id < MLO_WSI_MAX_MLO_GRPS; grp_id++) {
+		mlo_psoc_grp = &wsi_info->mlo_psoc_grp[grp_id];
+		for (i = 0; i < mlo_psoc_grp->num_psoc; i++) {
+			if (mlo_psoc_grp->psoc_order[i] == psoc_id)
+				return grp_id;
+		}
+	}
+
+	return MLO_WSI_MAX_MLO_GRPS;
+}
+
+QDF_STATUS wlan_mlo_wsi_link_info_send_cmd(void)
+{
+	struct mlo_mgr_context *mlo_mgr = NULL;
+	struct mlo_wsi_info *wsi_info = NULL;
+	struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops = NULL;
+	struct wlan_objmgr_psoc *psoc_objmgr = NULL;
+	struct wlan_objmgr_pdev *pdev = NULL;
+	QDF_STATUS error = QDF_STATUS_SUCCESS;
+	int i, j;
+
+	/* Retrieve the MLO manager context */
+	mlo_mgr = wlan_objmgr_get_mlo_ctx();
+	if (!mlo_mgr)
+		return QDF_STATUS_E_INVAL;
+
+	/* Retrieve the WSI link info structure */
+	wsi_info = mlo_mgr->wsi_info;
+	if (!wsi_info)
+		return QDF_STATUS_E_INVAL;
+
+	if (wsi_info->block_wmi_cmd) {
+		mlo_debug("WMI command is blocked");
+		return error;
+	}
+	/*
+	 * Check each PSOC if the WMI command needs to be sent
+	 * and send it for each PDEV in that PSOC
+	 */
+	for (i = 0; i < WLAN_OBJMGR_MAX_DEVICES; i++) {
+		if (!wsi_info->link_stats[i].send_wmi_cmd)
+			continue;
+
+		psoc_objmgr = wlan_objmgr_get_psoc_by_id(i, WLAN_MLO_MGR_ID);
+		if (!psoc_objmgr) {
+			mlo_err("Could not get PSOC obj for index %d", i);
+			continue;
+		}
+
+		mlo_tx_ops = &psoc_objmgr->soc_cb.tx_ops->mlo_ops;
+		if (!mlo_tx_ops || !mlo_tx_ops->send_wsi_link_info_cmd) {
+			mlo_err("mlo_tx_ops is null");
+			wlan_objmgr_psoc_release_ref(psoc_objmgr,
+						     WLAN_MLO_MGR_ID);
+			return QDF_STATUS_E_NULL_VALUE;
+		}
+
+		for (j = 0; j < psoc_objmgr->soc_objmgr.wlan_pdev_count; j++) {
+			pdev = psoc_objmgr->soc_objmgr.wlan_pdev_list[j];
+			if (!pdev)
+				continue;
+
+			if (mlo_tx_ops->send_wsi_link_info_cmd(
+					pdev, &wsi_info->link_stats[i]) !=
+						QDF_STATUS_SUCCESS) {
+				mlo_err("Could not send WMI command for PDEV %d in PSOC %d",
+					j, i);
+				error = QDF_STATUS_E_FAILURE;
+			} else {
+				wsi_info->link_stats[i].send_wmi_cmd = false;
+			}
+		}
+		wlan_objmgr_psoc_release_ref(psoc_objmgr, WLAN_MLO_MGR_ID);
+	}
+
+	return error;
+}
+
+static uint32_t wlan_mlo_psoc_get_ix_in_grp(struct mlo_mgr_context *mlo_mgr,
+					    uint32_t grp_id,
+					    uint32_t prim_psoc_id)
+{
+	uint32_t i;
+
+	if (!mlo_mgr) {
+		mlo_err("NULL mlo_mgr");
+		return 0xFF;
+	}
+
+	/* Iterate through the PSOC order and find the ID */
+	for (i = 0; i < mlo_mgr->wsi_info->mlo_psoc_grp[grp_id].num_psoc; i++) {
+		if (mlo_mgr->wsi_info->mlo_psoc_grp[grp_id].psoc_order[i] ==
+		    prim_psoc_id) {
+			return i;
+		}
+	}
+
+	return 0xFF;
+}
+
+static QDF_STATUS
+wlan_mlo_peer_wsi_link_update(struct wlan_mlo_peer_context *ml_peer, bool add)
+{
+	struct mlo_mgr_context *mlo_mgr = NULL;
+	struct mlo_wsi_info *wsi_info = NULL;
+	struct wlan_mlo_link_peer_entry *peer_entry = NULL;
+	uint32_t prim_psoc_id = 0xFF;
+	uint32_t sec_psoc_id[MAX_MLO_LINK_PEERS] = {0xFF};
+	uint32_t hops, hop_id;
+	uint32_t prim_grp_idx, sec_grp_idx;
+	uint32_t prim_psoc_ix_grp, sec_psoc_ix_grp;
+	uint32_t i, j;
+	struct mlo_wsi_psoc_grp *mlo_psoc_grp;
+
+	/* Check if ml_peer is valid */
+	if (!ml_peer) {
+		mlo_err("ML peer is null");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	/* Retrieve the MLO manager context */
+	mlo_mgr = wlan_objmgr_get_mlo_ctx();
+	if (!mlo_mgr) {
+		mlo_err("ML MGR is NULL for ML peer" QDF_MAC_ADDR_FMT,
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	/* Retrieve the WSI link info structure */
+	wsi_info = mlo_mgr->wsi_info;
+	if (!wsi_info) {
+		mlo_err("WSI Info is NULL for ML peer" QDF_MAC_ADDR_FMT,
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	prim_psoc_id = ml_peer->primary_umac_psoc_id;
+	/* Populate the primary PSOC ID of the respective group */
+	prim_grp_idx = wlan_mlo_psoc_get_mlo_grp_idx(mlo_mgr, prim_psoc_id);
+	if (prim_grp_idx == MLO_WSI_MAX_MLO_GRPS) {
+		mlo_err("Group index is invalid for ML peer" QDF_MAC_ADDR_FMT,
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	/* Populate MLO primary and secondary link */
+	for (i = 0, j = 0; i < MAX_MLO_LINK_PEERS; i++) {
+		peer_entry = &ml_peer->peer_list[i];
+		if (!peer_entry->link_peer) {
+			mlo_err("link peer is null");
+			continue;
+		}
+
+		/*
+		 * Consider all non-primary peers as secondary links.
+		 * Additionally, consider all active peers as secondary link if
+		 * the TQM is on another PSOC with bridge peer.
+		 */
+		if (!peer_entry->is_primary) {
+			sec_psoc_id[j] = wlan_vdev_get_psoc_id(
+						wlan_peer_get_vdev(
+						peer_entry->link_peer));
+
+			sec_grp_idx = wlan_mlo_psoc_get_mlo_grp_idx
+							(mlo_mgr,
+							 sec_psoc_id[j]);
+			if (sec_grp_idx != prim_grp_idx) {
+				mlo_err("Secondary link is not part of same MLO group as primary link");
+				continue;
+			}
+			j++;
+		}
+	}
+
+	/*
+	 * Logic for finding ingress and egress stats:
+	 * For a given MLO group, there is a PSOC order
+	 * (1) Iterate through each secondary link
+	 *      (1.1) Set the WMI command to true for that PSOC
+	 *      (1.2) Increment the ingress count for that PSOC
+	 *      (1.3) Calculate the number of hops from the secondary link
+	 *            to that primary link within the group.
+	 *      (1.4) Iterate through a decrement sequence from the hop_count
+	 *            (1.4.1) Increment the egress count for that PSOC index
+	 */
+	prim_psoc_ix_grp = wlan_mlo_psoc_get_ix_in_grp(mlo_mgr, prim_grp_idx,
+						       prim_psoc_id);
+
+	wsi_info->link_stats[prim_psoc_id].send_wmi_cmd = true;
+	mlo_psoc_grp = &wsi_info->mlo_psoc_grp[prim_grp_idx];
+	for (i = 0; i < j; i++) {
+		sec_psoc_ix_grp = wlan_mlo_psoc_get_ix_in_grp(mlo_mgr,
+							      prim_grp_idx,
+							      sec_psoc_id[i]);
+		hops = qdf_min(mlo_psoc_grp->num_psoc -
+			       (prim_psoc_ix_grp - sec_psoc_ix_grp),
+				(sec_psoc_ix_grp - prim_psoc_ix_grp));
+
+		wsi_info->link_stats[sec_psoc_id[i]].send_wmi_cmd = true;
+		if (add)
+			wsi_info->link_stats[sec_psoc_id[i]].ingress_cnt++;
+		else
+			wsi_info->link_stats[sec_psoc_id[i]].ingress_cnt--;
+
+		while (hops > 1) {
+			hops--;
+			hop_id = mlo_psoc_grp->psoc_order[hops];
+			wsi_info->link_stats[hop_id].send_wmi_cmd = true;
+			if (add)
+				wsi_info->link_stats[hop_id].egress_cnt++;
+			else
+				wsi_info->link_stats[hop_id].egress_cnt--;
+		}
+	}
+
+	/* Check if WMI bit is set, if yes, then send the command */
+	if (wlan_mlo_wsi_link_info_send_cmd() != QDF_STATUS_SUCCESS) {
+		mlo_err("WSI link info not sent for ML peer" QDF_MAC_ADDR_FMT,
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS wlan_mlo_peer_wsi_link_add(struct wlan_mlo_peer_context *ml_peer)
+{
+	if (!ml_peer) {
+		mlo_err("Invalid peer");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	return wlan_mlo_peer_wsi_link_update(ml_peer, 1);
+}
+
+QDF_STATUS wlan_mlo_peer_wsi_link_delete(struct wlan_mlo_peer_context *ml_peer)
+{
+	if (!ml_peer) {
+		mlo_err("Invalid peer");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	return wlan_mlo_peer_wsi_link_update(ml_peer, 0);
+}
+
+void wlan_mlo_wsi_stats_block_cmd(void)
+{
+	struct mlo_mgr_context *mlo_mgr = NULL;
+	struct mlo_wsi_info *wsi_info = NULL;
+
+	/* Retrieve the MLO manager context */
+	mlo_mgr = wlan_objmgr_get_mlo_ctx();
+	if (!mlo_mgr)
+		return;
+
+	/* Retrieve the WSI link info structure */
+	wsi_info = mlo_mgr->wsi_info;
+	if (!wsi_info)
+		return;
+
+	wsi_info->block_wmi_cmd++;
+}
+
+void wlan_mlo_wsi_stats_allow_cmd(void)
+{
+	struct mlo_mgr_context *mlo_mgr = NULL;
+	struct mlo_wsi_info *wsi_info = NULL;
+
+	/* Retrieve the MLO manager context */
+	mlo_mgr = wlan_objmgr_get_mlo_ctx();
+	if (!mlo_mgr)
+		return;
+
+	/* Retrieve the WSI link info structure */
+	wsi_info = mlo_mgr->wsi_info;
+	if (!wsi_info)
+		return;
+
+	if (wsi_info->block_wmi_cmd)
+		wsi_info->block_wmi_cmd--;
+}
+
+#else
+void wlan_mlo_wsi_stats_block_cmd(void)
+{
+}
+
+void wlan_mlo_wsi_stats_allow_cmd(void)
+{
+}
+
+QDF_STATUS wlan_mlo_wsi_link_info_send_cmd(void)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS wlan_mlo_peer_wsi_link_add(struct wlan_mlo_peer_context *ml_peer)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS wlan_mlo_peer_wsi_link_delete(struct wlan_mlo_peer_context *ml_peer)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif

+ 2 - 0
umac/mlo_mgr/src/wlan_mlo_mgr_primary_umac.c

@@ -974,6 +974,7 @@ void wlan_objmgr_mlo_update_primary_info(struct wlan_objmgr_peer *peer)
 	uint8_t i;
 
 	ml_peer = peer->mlo_peer_ctx;
+	wlan_mlo_peer_wsi_link_delete(ml_peer);
 	ml_peer->primary_umac_psoc_id = wlan_peer_get_psoc_id(peer);
 
 	for (i = 0; i < MAX_MLO_LINK_PEERS; i++) {
@@ -988,6 +989,7 @@ void wlan_objmgr_mlo_update_primary_info(struct wlan_objmgr_peer *peer)
 		if (peer_ent_iter->link_peer == peer)
 			peer_ent_iter->is_primary = true;
 	}
+	wlan_mlo_peer_wsi_link_add(ml_peer);
 }
 
 qdf_export_symbol(wlan_objmgr_mlo_update_primary_info);

+ 1 - 0
umac/mlo_mgr/src/wlan_mlo_mgr_setup.c

@@ -144,6 +144,7 @@ static void mlo_set_soc_list(uint8_t grp_id, struct wlan_objmgr_psoc *psoc)
 		if (mlo_ctx->setup_info[grp_id].soc_id_list[idx] ==
 				psoc->soc_objmgr.psoc_id) {
 			mlo_ctx->setup_info[grp_id].soc_list[idx] = psoc;
+			mlo_wsi_link_info_update_soc(psoc, grp_id);
 		}
 	}
 }

+ 8 - 0
wmi/inc/wmi_unified_priv.h

@@ -3050,6 +3050,14 @@ QDF_STATUS (*config_peer_latency_info_cmd)(
 				*param);
 #endif
 #endif
+
+#ifdef WLAN_WSI_STATS_SUPPORT
+QDF_STATUS (*send_wsi_stats_info_cmd)(
+				wmi_unified_t wmi,
+				struct wmi_wsi_stats_info_params
+				*param);
+#endif
+
 #ifdef QCA_MANUAL_TRIGGERED_ULOFDMA
 QDF_STATUS
 (*trigger_ulofdma_su_cmd)(wmi_unified_t wmi,