浏览代码

qcacmn: Add support for ptqm migration

Add support for ptqm migration

Change-Id: I3f3d6e410bee554477e8e058b1da51f2af5abb23
CRs-Fixed: 3529926
Himanshu Batra 2 年之前
父节点
当前提交
be34bbe6fc

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

@@ -650,6 +650,52 @@ QDF_STATUS target_if_mlo_send_vdev_pause(struct wlan_objmgr_psoc *psoc,
 	return wmi_send_mlo_vdev_pause(wmi_handle, info);
 }
 
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+static QDF_STATUS target_if_mlo_send_peer_ptqm_migrate_cmd(
+					struct wlan_objmgr_vdev *vdev,
+					struct peer_ptqm_migrate_params *param)
+{
+	struct wlan_objmgr_pdev *pdev = NULL;
+	struct wmi_unified *wmi_handle;
+	QDF_STATUS status;
+
+	if (!vdev || !param) {
+		target_if_err("Invalid input");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	pdev = wlan_vdev_get_pdev(vdev);
+	if (!pdev) {
+		target_if_err("null pdev");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	wmi_handle = lmac_get_pdev_wmi_handle(pdev);
+	if (!wmi_handle) {
+		target_if_err("Failed to get WMI handle!");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	status = wmi_unified_peer_ptqm_migrate_send(wmi_handle, param);
+	if (QDF_IS_STATUS_ERROR(status))
+		target_if_err("Failed to send peer ptqm migration WMI");
+
+	return status;
+}
+
+static void target_if_mlo_register_peer_ptqm_migrate_send(
+		struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops)
+{
+	mlo_tx_ops->peer_ptqm_migrate_send =
+				target_if_mlo_send_peer_ptqm_migrate_cmd;
+}
+#else
+static void target_if_mlo_register_peer_ptqm_migrate_send(
+		struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops)
+{
+}
+#endif
+
 /**
  * target_if_mlo_register_tx_ops() - lmac handler to register mlo tx ops
  *  callback functions
@@ -685,6 +731,7 @@ target_if_mlo_register_tx_ops(struct wlan_lmac_if_tx_ops *tx_ops)
 		target_if_request_ml_link_state_info;
 	mlo_tx_ops->send_vdev_pause = target_if_mlo_send_vdev_pause;
 
+	target_if_mlo_register_peer_ptqm_migrate_send(mlo_tx_ops);
 	return QDF_STATUS_SUCCESS;
 }
 

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

@@ -1512,6 +1512,7 @@ struct wlan_lmac_if_son_rx_ops {
  * @send_tid_to_link_mapping: function to send T2LM command to FW
  * @send_link_removal_cmd: function to send MLO link removal command to FW
  * @send_vdev_pause: function to send MLO vdev pause to FW
+ * @peer_ptqm_migrate_send: API to send peer ptqm migration request to FW
  */
 struct wlan_lmac_if_mlo_tx_ops {
 	QDF_STATUS (*register_events)(struct wlan_objmgr_psoc *psoc);
@@ -1532,6 +1533,11 @@ struct wlan_lmac_if_mlo_tx_ops {
 		const struct mlo_link_removal_cmd_params *param);
 	QDF_STATUS (*send_vdev_pause)(struct wlan_objmgr_psoc *psoc,
 				      struct mlo_vdev_pause *info);
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+	QDF_STATUS (*peer_ptqm_migrate_send)(
+					struct wlan_objmgr_vdev *vdev,
+					struct peer_ptqm_migrate_params *param);
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
 };
 
 /**

+ 17 - 0
umac/mlo_mgr/inc/wlan_mlo_mgr_ap.h

@@ -579,4 +579,21 @@ QDF_STATUS mlo_peer_create_get_frm_buf(
  */
 uint16_t wlan_mlo_ap_get_active_links(struct wlan_objmgr_vdev *vdev);
 
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+/**
+ * mlo_ap_ml_ptqm_peerid_free() - API to clear ml peer id bmap set for
+ * ptqm migration
+ * @ml_dev: ML dev pointer
+ * @mlo_peer_id: MLO peer id
+ *
+ * Return: none
+ */
+void mlo_ap_ml_ptqm_peerid_free(struct wlan_mlo_dev_context *ml_dev,
+				uint16_t mlo_peer_id);
+#else
+static inline
+void mlo_ap_ml_ptqm_peerid_free(struct wlan_mlo_dev_context *ml_dev,
+				uint16_t mlo_peer_id)
+{ }
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
 #endif

+ 25 - 0
umac/mlo_mgr/inc/wlan_mlo_mgr_cmn.h

@@ -625,3 +625,28 @@ util_parse_bw_ind(struct wlan_ie_bw_ind *bw_ind, uint8_t *ccfs0,
 		  uint8_t *ccfs1, enum phy_ch_width *ch_width,
 		  uint16_t *puncture_bitmap);
 #endif
+
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+/**
+ * mlo_mlme_ptqm_migrate_timer_cb() - Timer callback for ptqm migration
+ * @arg: timer function argument
+ *
+ * Return: None
+ */
+void mlo_mlme_ptqm_migrate_timer_cb(void *arg);
+
+/*
+ * wlan_mlo_set_ptqm_migration() - API to trigger ptqm migration.
+ * @vdev: vdev object
+ * @ml_peer: ml peer object
+ * @link_migration: flag to indicate if all peers of vdev need migration
+ * or individual peer migration
+ * @link_id: link id for new ptqm
+ *
+ * Return: Success if migration is triggered, else failure
+ */
+QDF_STATUS wlan_mlo_set_ptqm_migration(struct wlan_objmgr_vdev *vdev,
+				       struct wlan_mlo_peer_context *ml_peer,
+				       bool link_migration,
+				       uint32_t link_id);
+#endif

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

@@ -468,6 +468,11 @@ struct wlan_mlo_peer_list {
  * @ap_ctx: AP related information
  * @t2lm_ctx: T2LM related information
  * @epcs_ctx: EPCS related information
+ * @ptqm_migrate_timer: timer for ptqm migration
+ * @mlo_peer_id_bmap: mlo_peer_id bitmap for ptqm migration
+ *
+ * NB: Not using kernel-doc format since the kernel-doc script doesn't
+ *     handle the qdf_bitmap() macro
  */
 struct wlan_mlo_dev_context {
 	qdf_list_node_t node;
@@ -490,6 +495,10 @@ struct wlan_mlo_dev_context {
 	struct wlan_mlo_ap *ap_ctx;
 	struct wlan_t2lm_context t2lm_ctx;
 	struct wlan_epcs_context epcs_ctx;
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+	qdf_timer_t ptqm_migrate_timer;
+	qdf_bitmap(mlo_peer_id_bmap, MAX_MLO_PEER_ID);
+#endif
 };
 
 /**
@@ -1152,4 +1161,90 @@ struct mlo_link_disable_request_evt_params {
 	struct qdf_mac_addr mld_addr;
 	uint32_t link_id_bitmap;
 };
+
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+/**
+ * struct peer_ptqm_migrate_entry - peer ptqm migrate entry
+ * @ml_peer_id: ML peer id
+ * @hw_link_id: HW link id
+ */
+struct peer_ptqm_migrate_entry {
+	uint16_t ml_peer_id;
+	uint16_t hw_link_id;
+};
+
+/**
+ * struct peer_ptqm_migrate_params - peer ptqm migrate request parameter
+ * @vdev_id: vdev id
+ * @num_peers: peer count
+ * @peer_list: list of peers to be migrated
+ */
+struct peer_ptqm_migrate_params {
+	uint8_t vdev_id;
+	uint16_t num_peers;
+	struct peer_ptqm_migrate_entry *peer_list;
+};
+
+/**
+ * struct peer_ptqm_migrate_list_entry - peer ptqm migrate list
+ * @peer: objmgr peer object
+ * @mlo_peer_id: mlo peer id
+ * @new_hw_link_id: hw link id of new primary
+ * @peer_list_elem: peer ptqm migrate entry list
+ */
+struct peer_ptqm_migrate_list_entry {
+	struct wlan_objmgr_peer *peer;
+	uint32_t mlo_peer_id;
+	uint8_t new_hw_link_id;
+
+	TAILQ_ENTRY(peer_ptqm_migrate_list_entry) peer_list_elem;
+};
+
+/**
+ * struct peer_migrate_ptqm_multi_entries - multi ptqm migrate peer entry params
+ * @num_entries: Number of entries in the peer_list list
+ * @peer_list: List to hold the peer entries to be migrated
+ *
+ * NB: Not using kernel-doc format since the kernel-doc script doesn't
+ *     handle the TAILQ_HEAD() macro
+ */
+struct peer_migrate_ptqm_multi_entries {
+	uint16_t num_entries;
+
+	TAILQ_HEAD(, peer_ptqm_migrate_list_entry) peer_list;
+};
+
+enum primary_link_peer_migration_evenr_status {
+	PRIMARY_LINK_PEER_MIGRATION_SUCCESS,
+	PRIMARY_LINK_PEER_MIGRATION_IN_PROGRESS,
+	PRIMARY_LINK_PEER_MIGRATION_DELETE_IN_PROGRESS,
+	PRIMARY_LINK_PEER_MIGRATION_DELETED,
+	PRIMARY_LINK_PEER_MIGRATION_TX_PIPES_FAILED,
+	PRIMARY_LINK_PEER_MIGRATION_RX_PIPES_FAILED,
+
+	/* Add any new status above this line */
+	PRIMARY_LINK_PEER_MIGRATION_FAIL = 255,
+};
+
+/**
+ * struct peer_ptqm_migrate_event_params - peer ptqm migrate event parameter
+ * @vdev_id: vdev id
+ * @num_peers: peer count
+ */
+struct peer_ptqm_migrate_event_params {
+	uint8_t vdev_id;
+	uint16_t num_peers;
+};
+
+/**
+ * struct peer_entry_ptqm_migrate_event_params - peer entry ptqm migrate
+ * event parameter
+ * @ml_peer_id: ML peer id
+ * @status: migration status
+ */
+struct peer_entry_ptqm_migrate_event_params {
+	uint16_t ml_peer_id;
+	enum primary_link_peer_migration_evenr_status status;
+};
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
 #endif

+ 10 - 0
umac/mlo_mgr/src/wlan_mlo_mgr_ap.c

@@ -478,6 +478,16 @@ uint16_t mlo_ap_ml_peerid_alloc(void)
 	return mlo_peer_id;
 }
 
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+void mlo_ap_ml_ptqm_peerid_free(struct wlan_mlo_dev_context *ml_dev,
+				uint16_t mlo_peer_id)
+{
+	/* Free the bitmap for ptqm migration */
+	if (qdf_test_bit(mlo_peer_id, ml_dev->mlo_peer_id_bmap))
+		qdf_clear_bit(mlo_peer_id, ml_dev->mlo_peer_id_bmap);
+}
+#endif
+
 void mlo_ap_ml_peerid_free(uint16_t mlo_peer_id)
 {
 	struct mlo_mgr_context *mlo_ctx = wlan_objmgr_get_mlo_ctx();

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

@@ -686,6 +686,24 @@ static inline void mlo_epcs_ctx_init(struct wlan_mlo_dev_context *ml_dev)
 	qdf_mem_zero(epcs_ctx, sizeof(struct wlan_epcs_context));
 }
 
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+/**
+ * mlo_ptqm_migration_init() - API to initialize ptqm migration timer
+ * @ml_dev: Pointer to ML Dev context
+ *
+ * Return: None
+ */
+static inline void mlo_ptqm_migration_init(struct wlan_mlo_dev_context *ml_dev)
+{
+	qdf_timer_init(NULL, &ml_dev->ptqm_migrate_timer,
+		       mlo_mlme_ptqm_migrate_timer_cb, (void *)(ml_dev),
+		       QDF_TIMER_TYPE_WAKE_APPS);
+}
+#else
+static inline void mlo_ptqm_migration_init(struct wlan_mlo_dev_context *ml_dev)
+{ }
+#endif
+
 static QDF_STATUS mlo_dev_ctx_init(struct wlan_objmgr_vdev *vdev)
 {
 	struct wlan_mlo_dev_context *ml_dev;
@@ -763,10 +781,29 @@ static QDF_STATUS mlo_dev_ctx_init(struct wlan_objmgr_vdev *vdev)
 
 	mlo_t2lm_ctx_init(ml_dev, vdev);
 	mlo_epcs_ctx_init(ml_dev);
+	mlo_ptqm_migration_init(ml_dev);
 
 	return status;
 }
 
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+/**
+ * mlo_ptqm_migration_deinit() - API to deinitialize ptqm migration timer
+ * @ml_dev: Pointer to ML Dev context
+ *
+ * Return: None
+ */
+static inline void mlo_ptqm_migration_deinit(
+			struct wlan_mlo_dev_context *ml_dev)
+{
+	qdf_timer_free(&ml_dev->ptqm_migrate_timer);
+}
+#else
+static inline void mlo_ptqm_migration_deinit(
+			struct wlan_mlo_dev_context *ml_dev)
+{ }
+#endif
+
 /**
  * mlo_t2lm_ctx_deinit() - API to deinitialize the t2lm context with the default
  * values.
@@ -857,6 +894,7 @@ static QDF_STATUS mlo_dev_ctx_deinit(struct wlan_objmgr_vdev *vdev)
 		else if (wlan_vdev_mlme_get_opmode(vdev) == QDF_SAP_MODE)
 			qdf_mem_free(ml_dev->ap_ctx);
 
+		mlo_ptqm_migration_deinit(ml_dev);
 		mlo_t2lm_ctx_deinit(vdev);
 		tsf_recalculation_lock_destroy(ml_dev);
 		mlo_dev_lock_destroy(ml_dev);

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

@@ -714,6 +714,7 @@ static void mlo_peer_free(struct wlan_mlo_peer_context *ml_peer)
 	mlo_debug("ML Peer " QDF_MAC_ADDR_FMT " is freed",
 		  QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
 	mlo_peer_lock_destroy(ml_peer);
+	mlo_ap_ml_ptqm_peerid_free(ml_dev, ml_peer->mlo_peer_id);
 	mlo_ap_ml_peerid_free(ml_peer->mlo_peer_id);
 	mlo_peer_free_aid(ml_dev, ml_peer);
 	mlo_peer_free_primary_umac(ml_dev, ml_peer);

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

@@ -350,6 +350,8 @@ struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer_by_ml_peerid(
 	return NULL;
 }
 
+qdf_export_symbol(wlan_mlo_get_mlpeer_by_ml_peerid);
+
 struct wlan_mlo_peer_context *wlan_mlo_get_mlpeer(
 				struct wlan_mlo_dev_context *ml_dev,
 				struct qdf_mac_addr *ml_addr)

+ 504 - 1
umac/mlo_mgr/src/wlan_mlo_mgr_primary_umac.c

@@ -55,6 +55,8 @@ struct mlo_all_link_rssi {
 #define ML_INVALID_PRIMARY_TQM   0xff
 /* Congestion value */
 #define ML_PRIMARY_TQM_CONGESTION 30
+/* PTQM migration timeout value in ms */
+#define ML_PRIMARY_TQM_MIGRATRION_TIMEOUT 4000
 
 static void wlan_mlo_peer_get_rssi(struct wlan_objmgr_psoc *psoc,
 				   void *obj, void *args)
@@ -728,4 +730,505 @@ void wlan_objmgr_mlo_update_primary_info(struct wlan_objmgr_peer *peer)
 }
 
 qdf_export_symbol(wlan_objmgr_mlo_update_primary_info);
-#endif
+
+void mlo_mlme_ptqm_migrate_timer_cb(void *arg)
+{
+	struct wlan_mlo_dev_context *ml_dev = (struct wlan_mlo_dev_context *)arg;
+	struct wlan_mlo_peer_context *ml_peer = NULL;
+	uint16_t i = 0;
+
+	if (!ml_dev)
+		return;
+
+	/* Check for pending bitmaps and issue disconnect */
+	for (i = 0; i < MAX_MLO_PEER_ID; i++) {
+		if (qdf_test_bit(i, ml_dev->mlo_peer_id_bmap)) {
+			ml_peer = wlan_mlo_get_mlpeer_by_ml_peerid(ml_dev, i);
+			if (ml_peer && ml_peer->primary_umac_migration_in_progress) {
+				ml_peer->primary_umac_migration_in_progress = false;
+				mlo_err("Issue disconnect for ml peer with ml peer id:%d", i);
+				wlan_mlo_peer_deauth_init(ml_peer,
+							  NULL, 0);
+			}
+			qdf_clear_bit(i, ml_dev->mlo_peer_id_bmap);
+		}
+	}
+}
+
+/**
+ * wlan_mlo_send_ptqm_migrate_cmd() - API to send WMI to trigger ptqm migration
+ * @vdev: objmgr vdev object
+ * @list: peer list to be migrated
+ *
+ * API to send WMI to trigger ptqm migration
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+wlan_mlo_send_ptqm_migrate_cmd(struct wlan_objmgr_vdev *vdev,
+			       struct peer_migrate_ptqm_multi_entries *list)
+{
+	struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops;
+	struct wlan_objmgr_psoc *psoc;
+	QDF_STATUS status;
+	struct peer_ptqm_migrate_params param = {0};
+	struct peer_ptqm_migrate_entry *peer_list = NULL;
+	struct peer_ptqm_migrate_list_entry *peer_entry, *tmp_entry;
+	struct wlan_mlo_dev_context *ml_dev = NULL;
+	uint16_t i = 0;
+
+	ml_dev = vdev->mlo_dev_ctx;
+	if (!ml_dev)
+		return QDF_STATUS_E_FAILURE;
+
+	psoc = wlan_vdev_get_psoc(vdev);
+	if (!psoc) {
+		mlo_err("null psoc");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops;
+	if (!mlo_tx_ops || !mlo_tx_ops->peer_ptqm_migrate_send) {
+		mlo_err("mlo_tx_ops is null!");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	param.vdev_id = wlan_vdev_get_id(vdev);
+	param.num_peers = list->num_entries;
+
+	param.peer_list = qdf_mem_malloc(sizeof(struct peer_ptqm_migrate_entry) *
+					 list->num_entries);
+	if (!param.peer_list) {
+		mlo_err("Failed to allocate memory for ptqm migration command");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	peer_list = param.peer_list;
+
+	TAILQ_FOREACH_SAFE(peer_entry, &list->peer_list,
+			   peer_list_elem, tmp_entry) {
+		peer_list[i].ml_peer_id = peer_entry->mlo_peer_id;
+		peer_list[i].hw_link_id = peer_entry->new_hw_link_id;
+
+		qdf_set_bit(peer_entry->mlo_peer_id,
+			    ml_dev->mlo_peer_id_bmap);
+
+		mlo_debug("idx:%d, ml_peer_id:%d, hw_link_id:%d",
+			  i, peer_list[i].ml_peer_id,
+			  peer_list[i].hw_link_id);
+		i++;
+	}
+
+	status = mlo_tx_ops->peer_ptqm_migrate_send(vdev, &param);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlo_err("Failed to send WMI for ptqm migration");
+		qdf_mem_free(param.peer_list);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	/* Set timeout equal to peer delete timeout as requested by FW.
+	 * Timeout value to be optimized later. Timeout value will be
+	 * updated later based on stress testings.
+	 */
+	qdf_timer_mod(&ml_dev->ptqm_migrate_timer,
+		      ML_PRIMARY_TQM_MIGRATRION_TIMEOUT);
+
+	qdf_mem_free(param.peer_list);
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * wlan_mlo_get_new_ptqm_id() - API to get new ptqm ID
+ * @curr_vdev: objmgr vdev object for current primary link
+ * @ml_peer: ml peer object
+ * @new_primary_link_id: new primary link id
+ * @new_hw_link_id: hw link id for new primary TQM
+ *
+ * API to get new ptqm ID
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+wlan_mlo_get_new_ptqm_id(struct wlan_objmgr_vdev *curr_vdev,
+			 struct wlan_mlo_peer_context *ml_peer,
+			 uint8_t new_primary_link_id,
+			 uint16_t *new_hw_link_id)
+{
+	uint8_t current_primary_link_id = WLAN_LINK_ID_INVALID;
+	struct wlan_objmgr_vdev *tmp_vdev = NULL;
+	struct wlan_objmgr_vdev *wlan_vdev_list[WLAN_UMAC_MLO_MAX_VDEVS] = { NULL };
+	struct wlan_mlo_link_peer_entry *peer_entry;
+	QDF_STATUS status;
+	uint8_t i = 0, idx = 0;
+
+	if (wlan_vdev_mlme_get_opmode(curr_vdev) == QDF_SAP_MODE &&
+	    QDF_IS_STATUS_ERROR(wlan_mlo_peer_is_assoc_done(ml_peer))) {
+		mlo_err("ML peer " QDF_MAC_ADDR_FMT " is not associated",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	*new_hw_link_id = INVALID_HW_LINK_ID;
+	current_primary_link_id =
+		wlan_mlo_peer_get_primary_peer_link_id_by_ml_peer(ml_peer);
+	if (current_primary_link_id == WLAN_LINK_ID_INVALID) {
+		mlo_err("ML peer " QDF_MAC_ADDR_FMT "current primary link id is invalid",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (current_primary_link_id == new_primary_link_id) {
+		mlo_err("current and requested link_id are same");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	for (i = 0; i < MAX_MLO_LINK_PEERS; i++) {
+		peer_entry = &ml_peer->peer_list[i];
+		if (!peer_entry || !peer_entry->link_peer)
+			continue;
+
+		if (wlan_peer_get_peer_type(peer_entry->link_peer) ==
+					WLAN_PEER_MLO_BRIDGE)
+			goto exit;
+	}
+
+	for (i = 0; i < MAX_MLO_LINK_PEERS; i++) {
+		peer_entry = &ml_peer->peer_list[i];
+		if (!peer_entry || !peer_entry->link_peer)
+			continue;
+
+		tmp_vdev = wlan_peer_get_vdev(peer_entry->link_peer);
+		if (!tmp_vdev || tmp_vdev == curr_vdev)
+			continue;
+
+		status = wlan_objmgr_vdev_try_get_ref(tmp_vdev,
+						      WLAN_MLME_SB_ID);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			mlo_err("failed to get vdev ref");
+			continue;
+		}
+		wlan_vdev_list[idx++] = tmp_vdev;
+	}
+
+	if (new_primary_link_id == WLAN_LINK_ID_INVALID) {
+		mlo_debug("Invalid link id provided, select new link id");
+		ml_peer->migrate_primary_umac_psoc_id =
+			wlan_mld_get_best_primary_umac_w_rssi(ml_peer, wlan_vdev_list);
+		if (ml_peer->migrate_primary_umac_psoc_id ==
+					ML_PRIMARY_UMAC_ID_INVAL) {
+			mlo_err("Unable to fetch new primary link id for ml peer " QDF_MAC_ADDR_FMT,
+				QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+			goto exit;
+		}
+		for (i = 0; i < idx; i++) {
+			if (ml_peer->migrate_primary_umac_psoc_id ==
+				wlan_vdev_get_psoc_id(wlan_vdev_list[i])) {
+				*new_hw_link_id = wlan_mlo_get_pdev_hw_link_id(
+							wlan_vdev_get_pdev(wlan_vdev_list[i]));
+				break;
+			}
+		}
+	} else {
+		/* check if provided link id is part of current ml peer links */
+		for (i = 0; i < idx; i++) {
+			if (new_primary_link_id == wlan_vdev_get_link_id(wlan_vdev_list[i])) {
+				/* Check if the soc is enabled to be pumac or not */
+				if (wlan_vdev_skip_pumac(wlan_vdev_list[i])) {
+					mlo_err("Given link %d cannot be selected as primary",
+						new_primary_link_id);
+					goto exit;
+				}
+				*new_hw_link_id = wlan_mlo_get_pdev_hw_link_id(
+							wlan_vdev_get_pdev(wlan_vdev_list[i]));
+				break;
+			}
+		}
+	}
+
+	if (*new_hw_link_id == INVALID_HW_LINK_ID) {
+		mlo_err("New primary link id not found for ml peer " QDF_MAC_ADDR_FMT,
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		goto exit;
+	}
+
+	if (i < idx) {
+		if (wlan_vdev_mlme_op_flags_get(wlan_vdev_list[i],
+						WLAN_VDEV_OP_MLO_LINK_REMOVAL_IN_PROGRESS)) {
+			mlo_err("New selected primary link is going for removal, skip migration");
+			goto exit;
+		}
+	}
+
+	for (i = 0; i < idx; i++)
+		wlan_objmgr_vdev_release_ref(wlan_vdev_list[i],
+					     WLAN_MLME_SB_ID);
+
+	return QDF_STATUS_SUCCESS;
+
+exit:
+	ml_peer->migrate_primary_umac_psoc_id = ML_PRIMARY_UMAC_ID_INVAL;
+
+	for (i = 0; i < idx; i++)
+		wlan_objmgr_vdev_release_ref(wlan_vdev_list[i],
+					     WLAN_MLME_SB_ID);
+
+	return QDF_STATUS_E_FAILURE;
+}
+
+/**
+ * wlan_mlo_free_ptqm_migrate_list() - API to free peer ptqm migration list
+ * @list: peer ptqm migration list
+ *
+ * API to free peer ptqm migration list
+ *
+ * Return: void
+ */
+static void wlan_mlo_free_ptqm_migrate_list(
+			struct peer_migrate_ptqm_multi_entries *list)
+{
+	struct peer_ptqm_migrate_list_entry *peer_entry, *tmp_entry;
+
+	TAILQ_FOREACH_SAFE(peer_entry, &list->peer_list,
+			   peer_list_elem, tmp_entry) {
+		TAILQ_REMOVE(&list->peer_list, peer_entry, peer_list_elem);
+		list->num_entries--;
+		if (peer_entry->peer)
+			wlan_objmgr_peer_release_ref(peer_entry->peer,
+						     WLAN_MLME_SB_ID);
+		qdf_mem_free(peer_entry);
+	}
+}
+
+/**
+ * wlan_mlo_reset_ptqm_migrate_list() - API to reset peer ptqm migration list
+ * @ml_dev: MLO dev context
+ * @list: peer ptqm migration list
+ *
+ * API to reset peer ptqm migration list
+ *
+ * Return: void
+ */
+static void wlan_mlo_reset_ptqm_migrate_list(
+			struct wlan_mlo_dev_context *ml_dev,
+			struct peer_migrate_ptqm_multi_entries *list)
+{
+	struct peer_ptqm_migrate_list_entry *peer_entry, *tmp_entry;
+
+	if (!ml_dev)
+		return;
+
+	TAILQ_FOREACH_SAFE(peer_entry, &list->peer_list,
+			   peer_list_elem, tmp_entry) {
+		if (peer_entry->peer) {
+			qdf_clear_bit(peer_entry->mlo_peer_id, ml_dev->mlo_peer_id_bmap);
+			peer_entry->peer->mlo_peer_ctx->primary_umac_migration_in_progress = false;
+			peer_entry->peer->mlo_peer_ctx->migrate_primary_umac_psoc_id =
+							ML_PRIMARY_UMAC_ID_INVAL;
+		}
+	}
+}
+
+/**
+ * wlan_mlo_build_ptqm_migrate_list() - API to build peer ptqm migration list
+ * @vdev: objmgr vdev list
+ * @object: peer object
+ * @arg: list pointer
+ *
+ * API to build peer ptqm migration list
+ *
+ * Return: void
+ */
+static void wlan_mlo_build_ptqm_migrate_list(struct wlan_objmgr_vdev *vdev,
+					     void *object, void *arg)
+{
+	struct wlan_objmgr_peer *peer = (struct wlan_objmgr_peer *)object;
+	struct peer_migrate_ptqm_multi_entries *list =
+				(struct peer_migrate_ptqm_multi_entries *)arg;
+	struct peer_ptqm_migrate_list_entry *peer_entry;
+	struct wlan_mlo_peer_context *ml_peer;
+	uint16_t new_hw_link_id = INVALID_HW_LINK_ID;
+	uint8_t current_primary_link_id = WLAN_LINK_ID_INVALID;
+	QDF_STATUS status;
+
+	if (!wlan_peer_is_mlo(peer) || !peer->mlo_peer_ctx)
+		return;
+
+	ml_peer = peer->mlo_peer_ctx;
+
+	if (ml_peer->link_peer_cnt == 1)
+		return;
+
+	if (ml_peer->primary_umac_migration_in_progress) {
+		mlo_err("peer " QDF_MAC_ADDR_FMT " primary umac migration already in progress",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return;
+	}
+
+	current_primary_link_id = wlan_mlo_peer_get_primary_peer_link_id_by_ml_peer(ml_peer);
+	if (current_primary_link_id == WLAN_LINK_ID_INVALID ||
+	    current_primary_link_id != wlan_vdev_get_link_id(vdev)) {
+		mlo_debug("peer " QDF_MAC_ADDR_FMT " not having primary on current vdev",
+			  QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return;
+	}
+
+	status = wlan_mlo_get_new_ptqm_id(vdev, ml_peer,
+					  WLAN_LINK_ID_INVALID,
+					  &new_hw_link_id);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlo_err("peer " QDF_MAC_ADDR_FMT " unable to get new ptqm id",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return;
+	}
+	ml_peer->primary_umac_migration_in_progress = true;
+
+	peer_entry = (struct peer_ptqm_migrate_list_entry *)
+			qdf_mem_malloc(sizeof(struct peer_ptqm_migrate_list_entry));
+	if (!peer_entry) {
+		mlo_err("peer " QDF_MAC_ADDR_FMT " unable to allocate peer entry",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return;
+	}
+
+	status = wlan_objmgr_peer_try_get_ref(peer, WLAN_MLME_SB_ID);
+	peer_entry->peer = peer;
+	peer_entry->new_hw_link_id = new_hw_link_id;
+	peer_entry->mlo_peer_id = ml_peer->mlo_peer_id;
+	TAILQ_INSERT_TAIL(&list->peer_list, peer_entry, peer_list_elem);
+	list->num_entries++;
+}
+
+/**
+ * wlan_mlo_trigger_link_ptqm_migration() - API to trigger ptqm migration
+ * for a link
+ * @vdev: objmgr vdev object
+ *
+ * API to trigger ptqm migration of all peers having primary on given link
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS wlan_mlo_trigger_link_ptqm_migration(
+				struct wlan_objmgr_vdev *vdev)
+{
+	struct peer_migrate_ptqm_multi_entries migrate_list = {0};
+	QDF_STATUS status;
+
+	TAILQ_INIT(&migrate_list.peer_list);
+	wlan_objmgr_iterate_peerobj_list(vdev,
+					 wlan_mlo_build_ptqm_migrate_list,
+					 &migrate_list, WLAN_MLME_NB_ID);
+
+	/* trigger WMI */
+	if (migrate_list.num_entries == 0) {
+		mlo_err("No peer found");
+		return QDF_STATUS_SUCCESS;
+	}
+
+	status = wlan_mlo_send_ptqm_migrate_cmd(vdev, &migrate_list);
+	if (QDF_IS_STATUS_ERROR(status))
+		wlan_mlo_reset_ptqm_migrate_list(vdev->mlo_dev_ctx,
+						 &migrate_list);
+	wlan_mlo_free_ptqm_migrate_list(&migrate_list);
+	return status;
+}
+
+QDF_STATUS wlan_mlo_set_ptqm_migration(struct wlan_objmgr_vdev *vdev,
+				       struct wlan_mlo_peer_context *ml_peer,
+				       bool link_migration,
+				       uint32_t link_id)
+{
+	uint16_t new_hw_link_id = INVALID_HW_LINK_ID;
+	struct peer_migrate_ptqm_multi_entries migrate_list = {0};
+	struct peer_ptqm_migrate_list_entry *peer_entry;
+	struct wlan_objmgr_vdev *curr_vdev = NULL;
+	uint8_t current_primary_link_id = WLAN_LINK_ID_INVALID;
+	QDF_STATUS status;
+
+	if (!vdev) {
+		mlo_err("Vdev is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (link_migration == false && !ml_peer) {
+		mlo_err("ML peer is NULL");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (link_migration) {
+		if (wlan_vdev_mlme_op_flags_get(vdev, WLAN_VDEV_OP_MLO_LINK_REMOVAL_IN_PROGRESS)) {
+			mlo_err("Link removal in progress, skip umac migration for vdev:%d",
+				wlan_vdev_get_id(vdev));
+			return QDF_STATUS_E_INVAL;
+		}
+		mlo_err("Trigger migration for full link");
+		// trigger full link migration
+		status = wlan_mlo_trigger_link_ptqm_migration(vdev);
+		if (QDF_IS_STATUS_ERROR(status))
+			mlo_err("Failed to trigger link migration");
+		return status;
+	}
+
+	if (ml_peer->link_peer_cnt == 1) {
+		mlo_err("peer " QDF_MAC_ADDR_FMT " is SLO",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (ml_peer->primary_umac_migration_in_progress) {
+		mlo_err("peer " QDF_MAC_ADDR_FMT " primary umac migration already in progress",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	current_primary_link_id = wlan_mlo_peer_get_primary_peer_link_id_by_ml_peer(ml_peer);
+	if (current_primary_link_id == WLAN_LINK_ID_INVALID) {
+		mlo_err("Current primary link id is invalid");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	curr_vdev = mlo_get_vdev_by_link_id(vdev, current_primary_link_id);
+	if (!curr_vdev) {
+		mlo_err("Unable to get current primary vdev");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	status = wlan_mlo_get_new_ptqm_id(curr_vdev, ml_peer,
+					  link_id, &new_hw_link_id);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlo_err("peer " QDF_MAC_ADDR_FMT " unable to get new ptqm id",
+			QDF_MAC_ADDR_REF(ml_peer->peer_mld_addr.bytes));
+		goto exit;
+	}
+	ml_peer->primary_umac_migration_in_progress = true;
+
+	peer_entry = (struct peer_ptqm_migrate_list_entry *)
+				qdf_mem_malloc(sizeof(struct peer_ptqm_migrate_list_entry));
+	if (!peer_entry) {
+		mlo_err("Failed to allocate peer entry");
+		goto exit;
+	}
+
+	peer_entry->new_hw_link_id = new_hw_link_id;
+	peer_entry->mlo_peer_id = ml_peer->mlo_peer_id;
+	TAILQ_INIT(&migrate_list.peer_list);
+	TAILQ_INSERT_TAIL(&migrate_list.peer_list, peer_entry, peer_list_elem);
+	migrate_list.num_entries = 1;
+
+	//trigger WMI
+	status = wlan_mlo_send_ptqm_migrate_cmd(curr_vdev, &migrate_list);
+	if (QDF_IS_STATUS_ERROR(status))
+		wlan_mlo_reset_ptqm_migrate_list(curr_vdev->mlo_dev_ctx,
+						 &migrate_list);
+	wlan_mlo_free_ptqm_migrate_list(&migrate_list);
+
+	mlo_release_vdev_ref(curr_vdev);
+
+	return status;
+
+exit:
+	if (curr_vdev)
+		mlo_release_vdev_ref(curr_vdev);
+
+	return QDF_STATUS_E_FAILURE;
+}
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */

+ 42 - 0
wmi/inc/wmi_unified_11be_api.h

@@ -219,4 +219,46 @@ QDF_STATUS wmi_extract_mlo_link_disable_request_evt(
 		struct mlo_link_disable_request_evt_params *params);
 #endif /* WLAN_FEATURE_11BE */
 
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+/**
+ * wmi_unified_peer_ptqm_migrate_send() - send PEER ptqm migrate command to fw
+ * @wmi_hdl: wmi handle
+ * @param: pointer to hold peer ptqm migrate parameters
+ *
+ * Return: QDF_STATUS_SUCCESS on success and QDF_STATUS_E_FAILURE for failure
+ */
+QDF_STATUS wmi_unified_peer_ptqm_migrate_send(
+					wmi_unified_t wmi_hdl,
+					struct peer_ptqm_migrate_params *param);
+
+/**
+ * wmi_extract_peer_ptqm_migrate_event() - extract peer ptqm migrate event params
+ * @wmi: wmi handle
+ * @evt_buf: pointer to event buffer
+ * @resp: Pointer to host structure to get the event params
+ *
+ * This function gets called to extract peer ptqm migrate event params
+ *
+ * Return: QDF_STATUS_SUCCESS on success and QDF_STATUS_E_FAILURE for failure
+ */
+QDF_STATUS
+wmi_extract_peer_ptqm_migrate_event(
+		wmi_unified_t wmi, void *evt_buf,
+		struct peer_ptqm_migrate_event_params *resp);
+
+/**
+ * wmi_extract_peer_ptqm_entry_param() - extract peer entry ptqm migrate param
+ * @wmi_handle: wmi handle
+ * @evt_buf: pointer to event buffer
+ * @index: Index into pdev stats
+ * @entry: Pointer to peer entry params
+ *
+ * Return: QDF_STATUS_SUCCESS on success and QDF_STATUS_E_FAILURE for failure
+ */
+QDF_STATUS
+wmi_extract_peer_ptqm_entry_param(
+		wmi_unified_t wmi_handle, void *evt_buf,
+		uint32_t index,
+		struct peer_entry_ptqm_migrate_event_params *entry);
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
 #endif /*_WMI_UNIFIED_11BE_API_H_*/

+ 3 - 0
wmi/inc/wmi_unified_param.h

@@ -5309,6 +5309,9 @@ typedef enum {
 	wmi_csa_ie_received_event_id,
 #ifdef WLAN_FEATURE_11BE_MLO
 	wmi_mlo_link_state_info_eventid,
+#endif
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+	wmi_peer_ptqm_migration_response_eventid,
 #endif
 	wmi_events_max,
 } wmi_conv_event_id;

+ 16 - 0
wmi/inc/wmi_unified_priv.h

@@ -3311,6 +3311,22 @@ QDF_STATUS
 (*extract_csa_ie_received_ev_params)(wmi_unified_t wmi_handle,
 				     void *evt_buf, uint8_t *vdev_id,
 				     struct csa_offload_params *csa_event);
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+QDF_STATUS (*send_peer_ptqm_migrate_cmd)(
+				wmi_unified_t wmi,
+				struct peer_ptqm_migrate_params *param);
+
+QDF_STATUS (*extract_peer_ptqm_migrate_event)(
+		struct wmi_unified *wmi_handle,
+		uint8_t *buf,
+		struct peer_ptqm_migrate_event_params *params);
+
+QDF_STATUS (*extract_peer_entry_ptqm_migrate_event)(
+		struct wmi_unified *wmi_handle,
+		uint8_t *buf,
+		uint32_t index,
+		struct peer_entry_ptqm_migrate_event_params *entry);
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
 };
 
 /* Forward declaration for psoc*/

+ 39 - 0
wmi/src/wmi_unified_11be_api.c

@@ -211,3 +211,42 @@ QDF_STATUS wmi_extract_mgmt_rx_mlo_link_removal_info(
 
 	return QDF_STATUS_E_FAILURE;
 }
+
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+QDF_STATUS wmi_unified_peer_ptqm_migrate_send(
+					wmi_unified_t wmi_hdl,
+					struct peer_ptqm_migrate_params *param)
+{
+	if (wmi_hdl->ops->send_peer_ptqm_migrate_cmd)
+		return wmi_hdl->ops->send_peer_ptqm_migrate_cmd(wmi_hdl, param);
+
+	return QDF_STATUS_E_FAILURE;
+}
+
+QDF_STATUS
+wmi_extract_peer_ptqm_migrate_event(
+		wmi_unified_t wmi, void *evt_buf,
+		struct peer_ptqm_migrate_event_params *resp)
+{
+	if (wmi->ops->extract_peer_ptqm_migrate_event) {
+		return wmi->ops->extract_peer_ptqm_migrate_event(wmi,
+								 evt_buf,
+								 resp);
+	}
+	return QDF_STATUS_E_FAILURE;
+}
+
+QDF_STATUS
+wmi_extract_peer_ptqm_entry_param(
+		wmi_unified_t wmi_handle, void *evt_buf,
+		uint32_t index,
+		struct peer_entry_ptqm_migrate_event_params *entry)
+{
+	if (wmi_handle->ops->extract_peer_entry_ptqm_migrate_event)
+		return wmi_handle->ops->extract_peer_entry_ptqm_migrate_event(
+			wmi_handle, evt_buf,
+			index, entry);
+
+	return QDF_STATUS_E_FAILURE;
+}
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */

+ 170 - 0
wmi/src/wmi_unified_11be_tlv.c

@@ -1876,6 +1876,171 @@ QDF_STATUS extract_mgmt_rx_ml_cu_params_tlv(wmi_unified_t wmi_handle,
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+/**
+ * send_peer_ptqm_migrate_cmd_tlv() - send PEER ptqm migrate command to fw
+ * @wmi_handle: wmi handle
+ * @param: pointer to hold peer ptqm migrate parameter
+ *
+ * Return: QDF_STATUS_SUCCESS for success else error code
+ */
+static QDF_STATUS send_peer_ptqm_migrate_cmd_tlv(
+				wmi_unified_t wmi_handle,
+				struct peer_ptqm_migrate_params *param)
+{
+	/* Todo: copy send_peer_delete_all_cmd_tlv */
+	uint16_t i = 0;
+	wmi_buf_t buf;
+	uint8_t *buf_ptr;
+	wmi_mlo_primary_link_peer_migration_fixed_param *cmd;
+	uint32_t len = sizeof(*cmd);
+	uint16_t num_entry = 0;
+	uint16_t max_entry_per_cmd = 0, max_entry_cnt = 0;
+	struct peer_ptqm_migrate_entry *param_list = param->peer_list;
+	wmi_mlo_new_primary_link_peer_info *entry;
+	uint32_t pending_cnt = param->num_peers;
+
+	/* Get max entries which can be send in a single WMI command.
+	 * If no. of entries is more than max entries supported, multiple
+	 * WMI commands will be send.
+	 */
+	max_entry_per_cmd = (wmi_get_max_msg_len(wmi_handle) -
+			     sizeof(*cmd) - WMI_TLV_HDR_SIZE) /
+			     (sizeof(wmi_mlo_new_primary_link_peer_info));
+
+	if (param->num_peers > max_entry_per_cmd)
+		max_entry_cnt = max_entry_per_cmd;
+	else
+		max_entry_cnt = param->num_peers;
+
+	wmi_debug("Setting max entry limit as %u", max_entry_cnt);
+	while (pending_cnt > 0) {
+		len = sizeof(*cmd) + WMI_TLV_HDR_SIZE;
+		if (pending_cnt >= max_entry_cnt)
+			num_entry = max_entry_cnt;
+		else
+			num_entry = pending_cnt;
+
+		len += num_entry * sizeof(wmi_mlo_new_primary_link_peer_info);
+		buf = wmi_buf_alloc(wmi_handle, len);
+		if (!buf)
+			return QDF_STATUS_E_NOMEM;
+
+		buf_ptr = (uint8_t *)wmi_buf_data(buf);
+
+		cmd = (wmi_mlo_primary_link_peer_migration_fixed_param *)
+						wmi_buf_data(buf);
+		WMITLV_SET_HDR(
+			&cmd->tlv_header,
+			WMITLV_TAG_STRUC_wmi_mlo_primary_link_peer_migration_fixed_param,
+			WMITLV_GET_STRUCT_TLVLEN
+			(wmi_mlo_primary_link_peer_migration_fixed_param));
+		buf_ptr += sizeof(*cmd);
+		cmd->vdev_id = param->vdev_id;
+		WMITLV_SET_HDR(buf_ptr, WMITLV_TAG_ARRAY_STRUC,
+			       num_entry * sizeof(wmi_mlo_new_primary_link_peer_info));
+		buf_ptr += WMI_TLV_HDR_SIZE;
+		entry = (wmi_mlo_new_primary_link_peer_info *)buf_ptr;
+		for (i = 0; i < num_entry; i++) {
+			WMITLV_SET_HDR(&entry[i].tlv_header,
+				       WMITLV_TAG_STRUC_wmi_mlo_new_primary_link_peer_info,
+				       WMITLV_GET_STRUCT_TLVLEN(wmi_mlo_new_primary_link_peer_info));
+			WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_ML_PEER_ID_SET(
+					entry[i].new_link_info,
+					param_list[i].ml_peer_id);
+			WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_HW_LINK_ID_SET(
+					entry[i].new_link_info,
+					param_list[i].hw_link_id);
+			wmi_debug("i:%d, ml_peer_id:%d, hw_link_id:%d",
+				  i, entry[i].ml_peer_id, entry[i].hw_link_id);
+		}
+		pending_cnt -= num_entry;
+		param_list += num_entry;
+
+		wmi_mtrace(WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_CMDID,
+			   cmd->vdev_id, 0);
+
+		if (wmi_unified_cmd_send(wmi_handle, buf, len,
+					 WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_CMDID)) {
+			wmi_err("num_entries:%d failed!",
+				param->num_peers);
+			wmi_buf_free(buf);
+			return QDF_STATUS_E_FAILURE;
+		}
+		wmi_debug("num_entries:%d done!",
+			  num_entry);
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+extract_peer_ptqm_migrate_evt_param_tlv(
+		struct wmi_unified *wmi_handle,
+		uint8_t *buf,
+		struct peer_ptqm_migrate_event_params *params)
+{
+	WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_EVENTID_param_tlvs *param_buf;
+	wmi_mlo_primary_link_peer_migration_compl_fixed_param *ev;
+
+	param_buf =
+		(WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_EVENTID_param_tlvs *)buf;
+	if (!param_buf) {
+		wmi_err_rl("Param_buf is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!param_buf->primary_link_peer_migration_status) {
+		wmi_err_rl("primary_link_peer_migration_status not present in event");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	ev = (wmi_mlo_primary_link_peer_migration_compl_fixed_param *)
+		param_buf->fixed_param;
+
+	params->vdev_id = ev->vdev_id;
+	params->num_peers = param_buf->num_primary_link_peer_migration_status;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+extract_peer_entry_ptqm_migrate_evt_param_tlv(
+		struct wmi_unified *wmi_handle,
+		uint8_t *buf,
+		uint32_t index,
+		struct peer_entry_ptqm_migrate_event_params *params)
+{
+	WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_EVENTID_param_tlvs *param_buf;
+
+	param_buf =
+		(WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_EVENTID_param_tlvs *)buf;
+	if (!param_buf) {
+		wmi_err_rl("Param_buf is NULL");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (index > param_buf->num_primary_link_peer_migration_status) {
+		wmi_err_rl("Index greater than total peer entries");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!param_buf->primary_link_peer_migration_status) {
+		wmi_err_rl("primary_link_peer_migration_status not present in event");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	params->ml_peer_id =
+		WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_STATUS_ML_PEER_ID_GET(
+			param_buf->primary_link_peer_migration_status[index].status_info);
+
+	params->status =
+		WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_STATUS_STATUS_GET(
+			param_buf->primary_link_peer_migration_status[index].status_info);
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
+
 void wmi_11be_attach_tlv(wmi_unified_t wmi_handle)
 {
 	struct wmi_ops *ops = wmi_handle->ops;
@@ -1912,4 +2077,9 @@ void wmi_11be_attach_tlv(wmi_unified_t wmi_handle)
 			extract_mlo_link_disable_request_evt_param_tlv;
 	ops->send_mlo_vdev_pause =
 			send_mlo_vdev_pause_cmd_tlv;
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+	ops->send_peer_ptqm_migrate_cmd = send_peer_ptqm_migrate_cmd_tlv;
+	ops->extract_peer_ptqm_migrate_event = extract_peer_ptqm_migrate_evt_param_tlv;
+	ops->extract_peer_entry_ptqm_migrate_event = extract_peer_entry_ptqm_migrate_evt_param_tlv;
+#endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
 }

+ 5 - 0
wmi/src/wmi_unified_tlv.c

@@ -21774,6 +21774,11 @@ static void populate_tlv_events_id(WMI_EVT_ID *event_ids)
 #endif
 	event_ids[wmi_csa_ie_received_event_id] =
 		WMI_CSA_IE_RECEIVED_EVENTID;
+
+#ifdef QCA_SUPPORT_PRIMARY_LINK_MIGRATE
+	event_ids[wmi_peer_ptqm_migration_response_eventid] =
+			WMI_MLO_PRIMARY_LINK_PEER_MIGRATION_EVENTID;
+#endif
 }
 
 #ifdef WLAN_FEATURE_LINK_LAYER_STATS