Эх сурвалжийг харах

qcacmn: Link Switch start notifier callback registration

Move the MLO MGR in UMAC to core and dispatcher directories.
Introduce function prototype of callback and provide
dispatcher API for components interested in link switch
to register callback.
Introduce API to register and unregister callback for
link switch.

Change-Id: I14a64f61ab1269f1d46b7aff03d20b820d1cd8d9
CRs-Fixed: 3556465
Vinod Kumar Pirla 1 жил өмнө
parent
commit
cbf4cdb23c

+ 42 - 2
umac/mlo_mgr/inc/wlan_mlo_mgr_link_switch.h

@@ -175,8 +175,9 @@ struct mlo_link_switch_context {
  * Update link mac addresses for the ML links
  * Return: none
  */
-void mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev *vdev,
-			       struct wlan_mlo_link_mac_update *mlo_mac_update);
+void
+mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev *vdev,
+				  struct wlan_mlo_link_mac_update *mlo_mac_update);
 
 /**
  * mlo_mgr_update_link_info_reset() - Reset link info of ml dev context
@@ -314,6 +315,20 @@ mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx);
  */
 bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev);
 
+/**
+ * mlo_mgr_link_switch_notification() - Notify MLO manager on start
+ * of link switch
+ * @vdev: VDEV object manager
+ * @lswitch_req: Link switch request params from FW
+ *
+ * The link switch notifier callback to MLO manager invoked before starting
+ * link switch disconnect
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS mlo_mgr_link_switch_notification(struct wlan_objmgr_vdev *vdev,
+					    struct wlan_mlo_link_switch_req *lswitch_req);
+
 /**
  * mlo_mgr_is_link_switch_on_assoc_vdev() - API to query whether link switch
  * is on-going on assoc VDEV.
@@ -323,6 +338,18 @@ bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev);
  */
 bool mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev);
 
+/**
+ * mlo_mgr_link_switch_get_assoc_vdev() - Get current link switch VDEV
+ * pointer if it is assoc VDEV.
+ * @vdev: VDEV object manager.
+ *
+ * If the current link switch VDEV is assoc VDEV, fetch the pointer of that VDEV
+ *
+ * Return: VDEV object manager pointer
+ */
+struct wlan_objmgr_vdev *
+mlo_mgr_link_switch_get_assoc_vdev(struct wlan_objmgr_vdev *vdev);
+
 /**
  * mlo_mgr_ser_link_switch_cmd() - The API will serialize link switch
  * command in serialization queue.
@@ -452,12 +479,25 @@ mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
 	return false;
 }
 
+static inline QDF_STATUS
+mlo_mgr_link_switch_notification(struct wlan_objmgr_vdev *vdev,
+				 struct wlan_mlo_link_switch_req *lswitch_req)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
 static inline bool
 mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev)
 {
 	return false;
 }
 
+static inline struct wlan_objmgr_vdev *
+mlo_mgr_link_switch_get_assoc_vdev(struct wlan_objmgr_vdev *vdev)
+{
+	return NULL;
+}
+
 static inline QDF_STATUS
 mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev *vdev,
 			    struct wlan_mlo_link_switch_req *req)

+ 105 - 0
umac/mlo_mgr/inc/wlan_mlo_mgr_public_api.h

@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: contains mlo mgr north bound interface api
+ */
+
+#ifndef _WLAN_MLO_MGR_PUBLIC_API_H_
+#define _WLAN_MLO_MGR_PUBLIC_API_H_
+
+#include <wlan_mlo_mgr_public_structs.h>
+
+/**
+ * wlan_mlo_mgr_register_link_switch_notifier() - Components to register
+ * notifier callback on start of link switch.
+ * @comp_id: Component ID to register callback.
+ * @cb: Callback to register.
+ *
+ * The global MLO MGR will store the list of callbacks registered via this
+ * API and call each callback on start of link switch.
+ *
+ * Return: QDF_STATUS
+ */
+static inline QDF_STATUS
+wlan_mlo_mgr_register_link_switch_notifier(enum wlan_umac_comp_id comp_id,
+					   mlo_mgr_link_switch_notifier_cb cb)
+{
+	return mlo_mgr_register_link_switch_notifier(comp_id, cb);
+}
+
+/**
+ * wlan_mlo_mgr_unregister_link_switch_notifier() - Components to unregister
+ * notifier callback on start of link switch.
+ * @comp_id: Component ID to unregister the callback.
+ *
+ * The global MLO MGR will remove the registered callback for @comp_id for
+ * notifying start of link switch.
+ *
+ * Return: QDF_STATUS
+ */
+static inline QDF_STATUS
+wlan_mlo_mgr_unregister_link_switch_notifier(enum wlan_umac_comp_id comp_id)
+{
+	return mlo_mgr_unregister_link_switch_notifier(comp_id);
+}
+
+/**
+ * wlan_mlo_mgr_is_link_switch_in_progress() - Check link switch in progress
+ * on MLO dev context level.
+ * @vdev: VDEV object manager
+ *
+ * This API will check if current state of MLO manager link switch state is
+ * in progress or not. The return value of true shall not be treated as @vdev
+ * is in link switch in progress. To know the status of VDEV in link switch or
+ * not need to use wlan_vdev_mlme_is_mlo_link_switch_in_progress() API
+ *
+ * Return: bool
+ */
+static inline bool
+wlan_mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	return mlo_mgr_is_link_switch_in_progress(vdev);
+}
+
+/**
+ * wlan_mlo_mgr_is_link_switch_on_assoc_vdev() - Is link switch in progress is
+ * on assoc VDEV.
+ * @vdev: VDEV object manager.
+ *
+ * Return true if current link switch in progress is on assoc VDEV or not.
+ *
+ * Return: void
+ */
+static inline bool
+wlan_mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev)
+{
+	return mlo_mgr_is_link_switch_on_assoc_vdev(vdev);
+}
+
+/**
+ * wlan_mlo_mgr_link_switch_get_assoc_vdev() - Return assco VDEV pointer
+ * if it is in link switch.
+ * @vdev: VDEV object manager
+ *
+ * Return: VDEV object manager pointer
+ */
+static inline struct wlan_objmgr_vdev *
+wlan_mlo_mgr_link_switch_get_assoc_vdev(struct wlan_objmgr_vdev *vdev)
+{
+	return mlo_mgr_link_switch_get_assoc_vdev(vdev);
+}
+#endif /* _WLAN_MLO_MGR_PUBLIC_API_H_ */

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

@@ -65,6 +65,7 @@ struct mlo_osif_ext_ops;
 struct vdev_mlme_obj;
 struct wlan_t2lm_context;
 struct mlo_link_switch_context;
+struct wlan_mlo_link_switch_req;
 
 /* Max LINK PEER support */
 #define MAX_MLO_LINK_PEERS WLAN_UMAC_MLO_MAX_VDEVS
@@ -207,6 +208,62 @@ struct mlo_state_params {
 
 #endif
 
+typedef QDF_STATUS
+(*mlo_mgr_link_switch_notifier_cb)(struct wlan_objmgr_vdev *vdev,
+				   struct wlan_mlo_link_switch_req *lswitch_req);
+
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+/*
+ * struct wlan_mlo_link_switch_notifier - Link switch notifier callbacks
+ * @in_use: Set to true on successful notifier callback registration
+ * @cb: Callback to notify link switch start
+ */
+struct wlan_mlo_link_switch_notifier {
+	bool in_use;
+	mlo_mgr_link_switch_notifier_cb cb;
+};
+
+/**
+ * mlo_mgr_register_link_switch_notifier() - API to register link switch
+ * start notifier callback
+ * @comp_id: Component requesting notification on link switch start
+ * @cb: Callback to register.
+ *
+ * The @cb will be triggered on start of link switch with params of the
+ * link switch.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+mlo_mgr_register_link_switch_notifier(enum wlan_umac_comp_id comp_id,
+				      mlo_mgr_link_switch_notifier_cb cb);
+
+/**
+ * mlo_mgr_unregister_link_switch_notifier() - API to unregister link switch
+ * notifier callback.
+ * @comp_id: Component to deregister.
+ *
+ * The API will cleanup the notification callback registered for link switch.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+mlo_mgr_unregister_link_switch_notifier(enum wlan_umac_comp_id comp_id);
+#else
+static inline QDF_STATUS
+mlo_mgr_register_link_switch_notifier(enum wlan_umac_comp_id comp_id,
+				      mlo_mgr_link_switch_notifier_cb cb)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline QDF_STATUS
+mlo_mgr_unregister_link_switch_notifier(enum wlan_umac_comp_id comp_id)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
+
 /*
  * struct mlo_mgr_context - MLO manager context
  * @ml_dev_list_lock: ML DEV list lock
@@ -227,6 +284,7 @@ struct mlo_state_params {
  *
  * NB: not using kernel-doc format since the kernel-doc script doesn't
  *     handle the qdf_bitmap() macro
+ * @lswitch_notifier: Holds callback functions to notify link switch start
  */
 struct mlo_mgr_context {
 #ifdef WLAN_MLO_USE_SPINLOCK
@@ -254,6 +312,9 @@ struct mlo_mgr_context {
 	bool mlo_is_force_primary_umac;
 	uint8_t mlo_forced_primary_umac_id;
 	bool force_non_assoc_prim_umac;
+#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 */
 };
 
 /*
@@ -1022,6 +1083,7 @@ struct mlo_mlme_ext_ops {
  * @mlo_mgr_osif_update_bss_info: Callback to update each link connection info.
  * @mlo_mgr_osif_update_mac_addr: Callback to notify MAC addr update complete
  *                                from old link id to new link id for the vdev.
+ * @mlo_mgr_osif_link_switch_notification: Notify OSIF on start of link switch
  */
 struct mlo_osif_ext_ops {
 	QDF_STATUS
@@ -1032,6 +1094,10 @@ struct mlo_osif_ext_ops {
 	QDF_STATUS (*mlo_mgr_osif_update_mac_addr)(int32_t ieee_old_link_id,
 						   int32_t ieee_new_link_id,
 						   uint8_t vdev_id);
+
+	QDF_STATUS
+	(*mlo_mgr_osif_link_switch_notification)(struct wlan_objmgr_vdev *vdev,
+						 uint8_t non_trans_vdev_id);
 };
 
 /* maximum size of vdev bitmap array for MLO link set active command */

+ 119 - 9
umac/mlo_mgr/src/wlan_mlo_mgr_link_switch.c

@@ -217,6 +217,32 @@ void mlo_mgr_free_link_info_wmi_chan(struct wlan_mlo_dev_context *ml_dev)
 }
 
 #ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+struct wlan_objmgr_vdev *
+mlo_mgr_link_switch_get_assoc_vdev(struct wlan_objmgr_vdev *vdev)
+{
+	uint8_t vdev_id;
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_objmgr_vdev *assoc_vdev;
+
+	if (!vdev)
+		return NULL;
+
+	if (!mlo_mgr_is_link_switch_on_assoc_vdev(vdev))
+		return NULL;
+
+	vdev_id = vdev->mlo_dev_ctx->link_ctx->last_req.vdev_id;
+	psoc = wlan_vdev_get_psoc(vdev);
+	if (!psoc) {
+		mlo_err("PSOC NULL");
+		return NULL;
+	}
+
+	assoc_vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
+							  WLAN_MLO_MGR_ID);
+
+	return assoc_vdev;
+}
+
 bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
 {
 	enum mlo_link_switch_req_state state;
@@ -287,6 +313,68 @@ mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
 	return state;
 }
 
+QDF_STATUS mlo_mgr_link_switch_notification(struct wlan_objmgr_vdev *vdev,
+					    struct wlan_mlo_link_switch_req *lswitch_req)
+{
+	uint8_t idx;
+	uint16_t vdev_count;
+	struct wlan_objmgr_vdev *assoc_vdev;
+	struct wlan_mlo_sta *sta_ctx;
+	struct wlan_objmgr_vdev *vdev_list[WLAN_UMAC_MLO_MAX_VDEVS];
+	QDF_STATUS status = QDF_STATUS_E_INVAL;
+	struct mlo_mgr_context *g_mlo_ctx = wlan_objmgr_get_mlo_ctx();
+	QDF_STATUS(*cb)(struct wlan_objmgr_vdev *vdev,
+			uint8_t non_trans_vdev_id);
+
+	if (!vdev->mlo_dev_ctx)
+		return status;
+
+	sta_ctx = vdev->mlo_dev_ctx->sta_ctx;
+	if (!sta_ctx)
+		return status;
+
+	assoc_vdev = wlan_mlo_get_assoc_link_vdev(vdev);
+	if (!assoc_vdev)
+		return status;
+
+	cb = g_mlo_ctx->osif_ops->mlo_mgr_osif_link_switch_notification;
+
+	if (lswitch_req->restore_vdev_flag) {
+		wlan_vdev_mlme_clear_mlo_link_vdev(vdev);
+		wlan_vdev_mlme_set_mlo_link_vdev(assoc_vdev);
+		lswitch_req->restore_vdev_flag = false;
+
+		status = cb(assoc_vdev, wlan_vdev_get_id(vdev));
+		return status;
+	}
+
+	if (wlan_vdev_get_id(assoc_vdev) != lswitch_req->vdev_id) {
+		mlo_debug("Not on assoc VDEV no need to swap");
+		return QDF_STATUS_SUCCESS;
+	}
+
+	mlo_sta_get_vdev_list(vdev, &vdev_count, vdev_list);
+	for (idx = 0; idx < vdev_count; idx++) {
+		if (wlan_vdev_get_id(vdev_list[idx]) != lswitch_req->vdev_id &&
+		    qdf_test_bit(idx, sta_ctx->wlan_connected_links)) {
+			wlan_vdev_mlme_clear_mlo_link_vdev(vdev_list[idx]);
+			wlan_vdev_mlme_set_mlo_link_vdev(assoc_vdev);
+			lswitch_req->restore_vdev_flag = true;
+
+			status = cb(assoc_vdev,
+				    wlan_vdev_get_id(vdev_list[idx]));
+			break;
+		}
+
+		mlo_release_vdev_ref(vdev_list[idx]);
+	}
+
+	for (; idx < vdev_count; idx++)
+		mlo_release_vdev_ref(vdev_list[idx]);
+
+	return status;
+}
+
 QDF_STATUS mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev)
 {
 	ml_dev->link_ctx =
@@ -341,10 +429,18 @@ static QDF_STATUS
 mlo_mgr_start_link_switch(struct wlan_objmgr_vdev *vdev,
 			  struct wlan_serialization_command *cmd)
 {
+	int i;
+	QDF_STATUS status = QDF_STATUS_E_INVAL;
 	uint8_t vdev_id, old_link_id, new_link_id;
+	struct mlo_mgr_context *mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx();
 	struct mlo_link_switch_context *link_ctx = vdev->mlo_dev_ctx->link_ctx;
 	struct wlan_mlo_link_switch_req *req = &link_ctx->last_req;
 
+	if (!mlo_mgr_ctx) {
+		mlo_err("Global mlo mgr NULL");
+		return status;
+	}
+
 	vdev_id = wlan_vdev_get_id(vdev);
 	old_link_id = req->curr_ieee_link_id;
 	new_link_id = req->new_ieee_link_id;
@@ -354,18 +450,28 @@ mlo_mgr_start_link_switch(struct wlan_objmgr_vdev *vdev,
 	    wlan_vdev_get_link_id(vdev) != old_link_id) {
 		mlo_err("Link switch req link id mismatch, curr link id %d",
 			wlan_vdev_get_link_id(vdev));
-		return QDF_STATUS_E_INVAL;
+		return status;
 	}
 
-	wlan_vdev_mlme_set_mlo_link_switch_in_progress(vdev);
-	if (!wlan_vdev_mlme_is_mlo_link_vdev(vdev)) {
-		wlan_vdev_mlme_set_mlo_link_vdev(vdev);
-		req->restore_vdev_flag = true;
+	status = wlan_vdev_get_bss_peer_mld_mac(vdev, &req->peer_mld_addr);
+	if (QDF_IS_STATUS_ERROR(status))
+		return status;
+
+	for (i = 0; i < WLAN_UMAC_COMP_ID_MAX; i++) {
+		if (!mlo_mgr_ctx->lswitch_notifier[i].in_use)
+			continue;
+
+		status = mlo_mgr_ctx->lswitch_notifier[i].cb(vdev, req);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			mlme_err("Link switch start rejected by %d", i);
+			return status;
+		}
 	}
 
-	mlo_mgr_remove_link_switch_cmd(vdev);
+	wlan_vdev_mlme_set_mlo_link_switch_in_progress(vdev);
 
-	return QDF_STATUS_SUCCESS;
+	mlo_mgr_remove_link_switch_cmd(vdev);
+	return status;
 }
 
 static QDF_STATUS
@@ -406,6 +512,7 @@ mlo_mgr_ser_link_switch_cb(struct wlan_serialization_command *cmd,
 
 void mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev)
 {
+	QDF_STATUS status;
 	struct wlan_serialization_queued_cmd_info cmd_info;
 	enum mlo_link_switch_req_state cur_state;
 	uint8_t vdev_id = wlan_vdev_get_id(vdev);
@@ -413,8 +520,11 @@ void mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev)
 
 	req = &vdev->mlo_dev_ctx->link_ctx->last_req;
 	if (req->restore_vdev_flag) {
-		wlan_vdev_mlme_clear_mlo_link_vdev(vdev);
-		req->restore_vdev_flag = false;
+		status = mlo_mgr_link_switch_notification(vdev, req);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			mlo_err("Failed to restore deflink in OSIF");
+			req->restore_vdev_flag = false;
+		}
 	}
 
 	cur_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);

+ 87 - 5
umac/mlo_mgr/src/wlan_mlo_mgr_main.c

@@ -18,7 +18,6 @@
 /*
  * DOC: contains MLO manager init/deinit api's
  */
-#include <wlan_mlo_mgr_link_switch.h>
 #include "wlan_cmn.h"
 #include <wlan_objmgr_cmn.h>
 #include <wlan_objmgr_global_obj.h>
@@ -32,6 +31,7 @@
 #include <target_if_mlo_mgr.h>
 #include <wlan_mlo_t2lm.h>
 #include <wlan_cm_api.h>
+#include <wlan_mlo_mgr_public_api.h>
 
 static void mlo_global_ctx_deinit(void)
 {
@@ -129,6 +129,78 @@ QDF_STATUS wlan_mlo_mgr_psoc_disable(struct wlan_objmgr_psoc *psoc)
 	return mlo_tx_ops->unregister_events(psoc);
 }
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+QDF_STATUS
+mlo_mgr_register_link_switch_notifier(enum wlan_umac_comp_id comp_id,
+				      mlo_mgr_link_switch_notifier_cb cb)
+{
+	struct mlo_mgr_context *g_mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx();
+
+	if (!g_mlo_mgr_ctx) {
+		mlo_err("global mlo mgr not initialized");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!cb || comp_id >= WLAN_UMAC_COMP_ID_MAX) {
+		mlo_err("Invalid component");
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (g_mlo_mgr_ctx->lswitch_notifier[comp_id].in_use)
+		return QDF_STATUS_E_ALREADY;
+
+	g_mlo_mgr_ctx->lswitch_notifier[comp_id].in_use = true;
+	g_mlo_mgr_ctx->lswitch_notifier[comp_id].cb = cb;
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+mlo_mgr_unregister_link_switch_notifier(enum wlan_umac_comp_id comp_id)
+{
+	struct mlo_mgr_context *g_mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx();
+
+	if (!g_mlo_mgr_ctx) {
+		mlo_err("global mlo mgr not initialized");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (comp_id >= WLAN_UMAC_COMP_ID_MAX) {
+		mlo_err("Invalid component");
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	if (!g_mlo_mgr_ctx->lswitch_notifier[comp_id].in_use)
+		return QDF_STATUS_E_INVAL;
+
+	g_mlo_mgr_ctx->lswitch_notifier[comp_id].in_use = false;
+	g_mlo_mgr_ctx->lswitch_notifier[comp_id].cb = NULL;
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS mlo_mgr_init_link_switch_notifier(void)
+{
+	int i;
+	struct mlo_mgr_context *g_mlo_mgr_ctx = wlan_objmgr_get_mlo_ctx();
+
+	if (!g_mlo_mgr_ctx)
+		return QDF_STATUS_E_INVAL;
+
+	for (i = 0; i < WLAN_UMAC_COMP_ID_MAX; i++) {
+		g_mlo_mgr_ctx->lswitch_notifier[i].in_use = false;
+		g_mlo_mgr_ctx->lswitch_notifier[i].cb = NULL;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+#else
+static inline QDF_STATUS mlo_mgr_init_link_switch_notifier(void)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
+
 QDF_STATUS wlan_mlo_mgr_init(void)
 {
 	QDF_STATUS status;
@@ -145,12 +217,21 @@ QDF_STATUS wlan_mlo_mgr_init(void)
 
 	status = wlan_objmgr_register_vdev_destroy_handler(WLAN_UMAC_COMP_MLO_MGR,
 		wlan_mlo_mgr_vdev_destroyed_notification, NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		mlo_debug("Failed to register VDEV destroy handler");
+		wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_MLO_MGR,
+					wlan_mlo_mgr_vdev_created_notification, NULL);
+		return status;
+	}
+
+	status = mlo_mgr_init_link_switch_notifier();
 	if (QDF_IS_STATUS_SUCCESS(status)) {
-		mlo_debug("MLO vdev create and delete handler registered with objmgr");
-		return QDF_STATUS_SUCCESS;
+		status = mlo_mgr_register_link_switch_notifier(WLAN_UMAC_COMP_MLO_MGR,
+							       mlo_mgr_link_switch_notification);
+		return status;
 	}
-	wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_MLO_MGR,
-				wlan_mlo_mgr_vdev_created_notification, NULL);
+	if (status == QDF_STATUS_E_NOSUPPORT)
+		status = QDF_STATUS_SUCCESS;
 
 	return status;
 }
@@ -179,6 +260,7 @@ QDF_STATUS wlan_mlo_mgr_deinit(void)
 	if (status != QDF_STATUS_SUCCESS)
 		mlo_err("Failed to unregister vdev delete handler");
 
+	wlan_mlo_mgr_unregister_link_switch_notifier(WLAN_UMAC_COMP_MLO_MGR);
 	return status;
 }