Parcourir la source

qcacmn: FW Link switch request event handler and cnf resp

Once the FW sends the link switch request to host handle
the request from scheduler thread and send confirmation
back on completion of link switch process with status of
link switch (success/failure).

Add new serialization command type for link switch.

Introduce flags to get the current state of link switch
request, set the state to idle when no link switch in
progress or once the current link switch is completed.
Access to state is protected with MLO dev context lock.
Implement various helper API to:
    a) Transition link switch to next state.
    b) Get current state of link switch.
    c) To check whether any link switch is in progress.
    c) To check whether link switch is happening
       on assoc VDEV or not.

Introduce a new VDEV flag to suggest the VDEV is in
link switch process and also implement helper APIs to
set/get/clear this VDEV flag.
   a) The flag is set at start of link switch, once
      the FW request params are validated and before
      proceeding for link switch disconnect on VDEV.
   b) Clear the flag once the Link switch confirmation
      is sent to FW.

Validate the link switch request params:
     a) IEEE link ID's received.
     b) Check if new connection is part of MLO connection.
     c) Check if VDEV is MLO STA VDEV or not.
     d) Is VDEV in connected state or not, that means
        VDEV is not in transitioning state due to disconnect.
     e) Check if any link switch in progress on this MLD
     f) Current link ID of VDEV equals the FW params.

If validation is successful, serialize the link switch
command and in the serialization activation start the
actual link switch process.

Change-Id: Ie582650541054c8cf39aaa8316e86a7a40256a15
CRs-Fixed: 3556422
Vinod Kumar Pirla il y a 2 ans
Parent
commit
af6cf93a07

+ 147 - 2
target_if/mlo_mgr/src/target_if_mlo_mgr.c

@@ -191,6 +191,139 @@ target_if_extract_mlo_link_removal_info_mgmt_rx(
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+static QDF_STATUS
+target_if_send_mlo_link_switch_cnf_cmd(struct wlan_objmgr_psoc *psoc,
+				       struct wlan_mlo_link_switch_cnf *params)
+{
+	struct wmi_unified *wmi_handle = NULL;
+
+	if (!psoc) {
+		target_if_err("null pdev");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!params) {
+		target_if_err("params is null");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	wmi_handle = get_wmi_unified_hdl_from_psoc(psoc);
+	if (!wmi_handle) {
+		target_if_err("null wmi handle");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	return wmi_send_mlo_link_switch_req_cnf_cmd(wmi_handle, params);
+}
+
+static int
+target_if_mlo_link_switch_request_event_handler(ol_scn_t scn, uint8_t *data,
+						uint32_t datalen)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wmi_unified *wmi_handle;
+	struct wlan_lmac_if_mlo_rx_ops *mlo_rx_ops;
+	QDF_STATUS status;
+	struct wlan_mlo_link_switch_req req = {0};
+
+	if (!scn || !data) {
+		target_if_err("scn: 0x%pK, data: 0x%pK", scn, data);
+		return -EINVAL;
+	}
+
+	psoc = target_if_get_psoc_from_scn_hdl(scn);
+	if (!psoc) {
+		target_if_err("null psoc");
+		return -EINVAL;
+	}
+
+	mlo_rx_ops = target_if_mlo_get_rx_ops(psoc);
+	if (!mlo_rx_ops || !mlo_rx_ops->mlo_link_switch_request_handler) {
+		target_if_err("callback not registered");
+		return -EINVAL;
+	}
+
+	wmi_handle = get_wmi_unified_hdl_from_psoc(psoc);
+	if (!wmi_handle) {
+		target_if_err("wmi_handle is null");
+		return -EINVAL;
+	}
+
+	status = wmi_extract_mlo_link_switch_request_evt(wmi_handle, data,
+							 &req);
+
+	if (QDF_IS_STATUS_ERROR(status)) {
+		target_if_err("Unable to extract fixed param, ret = %d",
+			      status);
+		goto exit;
+	}
+
+	status = mlo_rx_ops->mlo_link_switch_request_handler(psoc, &req);
+
+exit:
+	return status;
+}
+
+static inline void
+target_if_mlo_register_link_switch_cnf_handler(struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops)
+{
+	mlo_tx_ops->send_mlo_link_switch_cnf_cmd =
+			target_if_send_mlo_link_switch_cnf_cmd;
+}
+
+static QDF_STATUS
+target_if_mlo_register_link_switch_event_handler(struct wmi_unified *wmi_handle)
+{
+	QDF_STATUS status;
+
+	status = wmi_unified_register_event_handler(
+			wmi_handle,
+			wmi_mlo_link_switch_request_eventid,
+			target_if_mlo_link_switch_request_event_handler,
+			WMI_RX_SERIALIZER_CTX);
+
+	return status;
+}
+
+static inline void
+target_if_mlo_unregister_link_switch_event_handler(struct wmi_unified *wmi_handle)
+{
+	wmi_unified_unregister_event(wmi_handle,
+				     wmi_mlo_link_switch_request_eventid);
+}
+#else
+static inline QDF_STATUS
+target_if_mlo_register_link_switch_event_handler(struct wmi_unified *wmi_handle)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline void
+target_if_mlo_unregister_link_switch_event_handler(struct wmi_unified *wmi_handle)
+{
+}
+
+static inline QDF_STATUS
+target_if_send_mlo_link_switch_cnf_cmd(struct wlan_objmgr_psoc *psoc,
+				       struct wlan_mlo_link_switch_cnf *params)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline void
+target_if_mlo_register_link_switch_cnf_handler(struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops)
+{
+}
+
+static inline int
+target_if_mlo_link_switch_request_event_handler(ol_scn_t scn, uint8_t *data,
+						uint32_t datalen)
+{
+	return 0;
+}
+#endif
+
 /**
  * target_if_mlo_link_disable_request_event_handler() - Handler for MLO
  * link disable request event sent by the FW
@@ -287,7 +420,7 @@ target_if_mlo_register_event_handler(struct wlan_objmgr_psoc *psoc)
 	if (QDF_IS_STATUS_ERROR(status)) {
 		target_if_err("Register mlo link set active resp cb errcode %d",
 			      status);
-		if (status ==  QDF_STATUS_E_NOSUPPORT)
+		if (status == QDF_STATUS_E_NOSUPPORT)
 			status = QDF_STATUS_SUCCESS;
 	}
 
@@ -301,7 +434,15 @@ target_if_mlo_register_event_handler(struct wlan_objmgr_psoc *psoc)
 	if (QDF_IS_STATUS_ERROR(status)) {
 		target_if_err("Couldn't register handler for link disable request WMI event %d",
 			      status);
-		if (status ==  QDF_STATUS_E_NOSUPPORT)
+		if (status == QDF_STATUS_E_NOSUPPORT)
+			status = QDF_STATUS_SUCCESS;
+	}
+
+	status = target_if_mlo_register_link_switch_event_handler(wmi_handle);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		target_if_err("Couldn't register handler for link switch WMI event %d",
+			      status);
+		if (status == QDF_STATUS_E_NOSUPPORT)
 			status = QDF_STATUS_SUCCESS;
 	}
 
@@ -343,6 +484,8 @@ target_if_mlo_unregister_event_handler(struct wlan_objmgr_psoc *psoc)
 	wmi_unified_unregister_event(wmi_handle,
 				     wmi_mlo_link_disable_request_eventid);
 
+	target_if_mlo_unregister_link_switch_event_handler(wmi_handle);
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -734,6 +877,8 @@ 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_link_switch_cnf_handler(mlo_tx_ops);
+
 	target_if_mlo_register_peer_ptqm_migrate_send(mlo_tx_ops);
 	return QDF_STATUS_SUCCESS;
 }

+ 82 - 0
umac/cmn_services/obj_mgr/inc/wlan_objmgr_vdev_obj.h

@@ -238,6 +238,8 @@
 
 /* MLO link removal is in progress on this VDEV */
 #define WLAN_VDEV_OP_MLO_LINK_REMOVAL_IN_PROGRESS 0x01000000
+/* MLO link switch is in progress on this VDEV */
+#define WLAN_VDEV_OP_MLO_LINK_SWITCH_IN_PROGRESS 0x02000000
 
  /* flag to indicate disconnect only legacy peers due to moving to DFS channel
   * from non-DFS channel
@@ -1760,6 +1762,70 @@ void wlan_vdev_mlme_set_mlo_link_vdev(struct wlan_objmgr_vdev *vdev);
  * Return: void
  */
 void wlan_vdev_mlme_clear_mlo_link_vdev(struct wlan_objmgr_vdev *vdev);
+
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+/**
+ * wlan_vdev_mlme_set_mlo_link_switch_in_progress() - Set link switch in
+ * progress flag for VDEV.
+ * @vdev: VDEV object manager.
+ *
+ * Return: void
+ */
+static inline void
+wlan_vdev_mlme_set_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	unsigned long flag = WLAN_VDEV_OP_MLO_LINK_SWITCH_IN_PROGRESS;
+
+	wlan_vdev_mlme_op_flags_set(vdev, flag);
+}
+
+/**
+ * wlan_vdev_mlme_clear_mlo_link_switch_in_progress() - Clear link switch in
+ * progress flag for VDEV.
+ * @vdev: VDEV object manager
+ *
+ * Return: void
+ */
+static inline void
+wlan_vdev_mlme_clear_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	unsigned long flag = WLAN_VDEV_OP_MLO_LINK_SWITCH_IN_PROGRESS;
+
+	wlan_vdev_mlme_op_flags_clear(vdev, flag);
+}
+
+/**
+ * wlan_vdev_mlme_is_mlo_link_switch_in_progress() - Return true if VDEV is
+ * in link transitioning state.
+ * @vdev: VDEV object manager.
+ *
+ * Return: bool
+ */
+static inline bool
+wlan_vdev_mlme_is_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	unsigned long flag = WLAN_VDEV_OP_MLO_LINK_SWITCH_IN_PROGRESS;
+
+	return wlan_vdev_mlme_op_flags_get(vdev, flag);
+}
+#else
+static inline void
+wlan_vdev_mlme_set_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+}
+
+static inline void
+wlan_vdev_mlme_clear_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+}
+
+static inline bool
+wlan_vdev_mlme_is_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	return false;
+}
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE  */
+
 #ifdef WLAN_MCAST_MLO
 /**
  * wlan_vdev_mlme_is_mlo_mcast_vdev() - whether it is mlo mcast vdev or not
@@ -1875,6 +1941,22 @@ void wlan_vdev_mlme_clear_mlo_link_vdev(struct wlan_objmgr_vdev *vdev)
 {
 }
 
+static inline void
+wlan_vdev_mlme_set_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+}
+
+static inline void
+wlan_vdev_mlme_clear_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+}
+
+static inline bool
+wlan_vdev_mlme_is_mlo_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	return false;
+}
+
 static inline
 bool wlan_vdev_mlme_is_mlo_link_vdev(struct wlan_objmgr_vdev *vdev)
 {

+ 2 - 0
umac/cmn_services/serialization/inc/wlan_serialization_api.h

@@ -199,6 +199,7 @@ typedef QDF_STATUS (*wlan_ser_umac_cmd_cb)(void *umac_cmd);
  * @WLAN_SER_CMD_PDEV_CSA_RESTART: Cmd to CSA restart all AP VDEVs of a PDEV
  * @WLAN_SER_CMD_VDEV_ROAM: Cmd to roam a STA VDEV
  * @WLAN_SER_CMD_SET_MLO_LINK: Cmd to force mlo link active/inactive
+ * @WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH: Cmd to serialize link switch operation
  * @WLAN_SER_CMD_MAX: Max enumeration
  */
 enum wlan_serialization_cmd_type {
@@ -233,6 +234,7 @@ enum wlan_serialization_cmd_type {
 	WLAN_SER_CMD_PDEV_CSA_RESTART,
 	WLAN_SER_CMD_VDEV_ROAM,
 	WLAN_SER_CMD_SET_MLO_LINK,
+	WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH,
 	WLAN_SER_CMD_MAX
 };
 

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

@@ -59,6 +59,10 @@
 #include <wlan_ipa_public_struct.h>
 #endif
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+#include <wlan_mlo_mgr_link_switch.h>
+#endif
+
 /* Number of dev type: Direct attach and Offload */
 #define MAX_DEV_TYPE 2
 
@@ -1526,6 +1530,7 @@ struct wlan_lmac_if_son_rx_ops {
  * @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
+ * @send_mlo_link_switch_cnf_cmd: Send link switch status to FW
  */
 struct wlan_lmac_if_mlo_tx_ops {
 	QDF_STATUS (*register_events)(struct wlan_objmgr_psoc *psoc);
@@ -1551,6 +1556,11 @@ struct wlan_lmac_if_mlo_tx_ops {
 					struct wlan_objmgr_vdev *vdev,
 					struct peer_ptqm_migrate_params *param);
 #endif /* QCA_SUPPORT_PRIMARY_LINK_MIGRATE */
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+	QDF_STATUS
+	(*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 */
 };
 
 /**
@@ -1560,6 +1570,8 @@ struct wlan_lmac_if_mlo_tx_ops {
  * @mlo_link_removal_handler: function pointer for MLO link removal handler
  * @process_mlo_link_state_info_event: function pointer for mlo link state
  * @mlo_link_disable_request_handler: function ptr for mlo link disable request
+ * @mlo_link_switch_request_handler: Handler function pointer to deliver link
+ * switch request params from FW to host.
  */
 struct wlan_lmac_if_mlo_rx_ops {
 	QDF_STATUS
@@ -1577,6 +1589,11 @@ struct wlan_lmac_if_mlo_rx_ops {
 	QDF_STATUS (*mlo_link_disable_request_handler)(
 			struct wlan_objmgr_psoc *psoc,
 			void *evt_params);
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+	QDF_STATUS
+	(*mlo_link_switch_request_handler)(struct wlan_objmgr_psoc *psoc,
+					   void *evt_params);
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
 };
 #endif
 

+ 16 - 0
umac/global_umac_dispatcher/lmac_if/src/wlan_lmac_if.c

@@ -975,6 +975,20 @@ wlan_lmac_if_umac_ftm_rx_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
 #endif
 
 #ifdef WLAN_FEATURE_11BE_MLO
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+static inline void
+wlan_lmac_if_mlo_rx_link_switch_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
+{
+	rx_ops->mlo_rx_ops.mlo_link_switch_request_handler =
+					mlo_mgr_link_switch_request_params;
+}
+#else
+static inline void
+wlan_lmac_if_mlo_rx_link_switch_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
+{
+}
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
+
 /**
  * wlan_lmac_if_mlo_mgr_rx_ops_register() - API to register mlo mgr Rx Ops
  * @rx_ops: pointer to lmac rx ops
@@ -995,6 +1009,8 @@ wlan_lmac_if_mlo_mgr_rx_ops_register(struct wlan_lmac_if_rx_ops *rx_ops)
 		wlan_handle_ml_link_state_info_event;
 	rx_ops->mlo_rx_ops.mlo_link_disable_request_handler =
 		wlan_mlo_link_disable_request_handler;
+
+	wlan_lmac_if_mlo_rx_link_switch_ops_register(rx_ops);
 }
 #else
 static void

+ 187 - 9
umac/mlo_mgr/inc/wlan_mlo_mgr_link_switch.h

@@ -92,10 +92,27 @@ enum wlan_mlo_link_switch_reason {
 	MLO_LINK_SWITCH_REASON_MAX,
 };
 
+/*
+ * enum mlo_link_switch_req_state - Enum to maintain the current state of
+ * link switch request.
+ * @MLO_LINK_SWITCH_STATE_IDLE: The last link switch request is inactive
+ * @MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK: Current link disconnect
+ *                                              in progress.
+ * @MLO_LINK_SWITCH_STATE_SET_MAC_ADDR: MAC address update in progress
+ * @MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK: New link connect in progress.
+ * @MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS: Link switch completed successfully
+ */
+enum mlo_link_switch_req_state {
+	MLO_LINK_SWITCH_STATE_IDLE,
+	MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK,
+	MLO_LINK_SWITCH_STATE_SET_MAC_ADDR,
+	MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK,
+	MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS,
+};
+
 /**
  * struct wlan_mlo_link_switch_req - Data Structure because of link switch
  * request
- * @is_in_progress: is link switch in progress
  * @restore_vdev_flag: VDEV Flag to be restored post link switch.
  * @vdev_id: VDEV Id of the link which is under link switch
  * @curr_ieee_link_id: Current link id of the ML link
@@ -103,11 +120,11 @@ enum wlan_mlo_link_switch_reason {
  * @peer_mld_addr: Peer MLD address
  * @new_primary_freq: primary frequency of link switch link
  * @new_phymode: Phy mode of link switch link
+ * @state: Current state of link switch
  * @reason: Link switch reason
  * @link_switch_ts: Link switch timestamp
  */
 struct wlan_mlo_link_switch_req {
-	bool is_in_progress;
 	bool restore_vdev_flag;
 	uint8_t vdev_id;
 	uint8_t curr_ieee_link_id;
@@ -115,6 +132,7 @@ struct wlan_mlo_link_switch_req {
 	struct qdf_mac_addr peer_mld_addr;
 	uint32_t new_primary_freq;
 	uint32_t new_phymode;
+	enum mlo_link_switch_req_state state;
 	enum wlan_mlo_link_switch_reason reason;
 	qdf_time_t link_switch_ts;
 };
@@ -246,12 +264,108 @@ struct mlo_link_info *mlo_mgr_get_ap_link(struct wlan_objmgr_vdev *vdev);
  */
 void mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev,
 				      int32_t link_id);
-#else
-static inline void
-mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
-{
-}
-#endif
+
+/**
+ * mlo_mgr_link_switch_init_state() - Set the current state of link switch
+ * to init state.
+ * @mlo_dev_ctx: MLO dev context
+ *
+ * Sets the current state of link switch to MLO_LINK_SWITCH_STATE_IDLE with
+ * MLO dev context lock held.
+ *
+ * Return: void
+ */
+void mlo_mgr_link_switch_init_state(struct wlan_mlo_dev_context *mlo_dev_ctx);
+
+/**
+ * mlo_mgr_link_switch_trans_next_state() - Transition to next state based
+ * on current state.
+ * @mlo_dev_ctx: MLO dev context
+ *
+ * Move to next state in link switch process based on current state with
+ * MLO dev context lock held.
+ *
+ * Return: void
+ */
+void
+mlo_mgr_link_switch_trans_next_state(struct wlan_mlo_dev_context *mlo_dev_ctx);
+
+/**
+ * mlo_mgr_link_switch_get_curr_state() - Get the current state of link switch.
+ * @mlo_dev_ctx: MLO dev context.
+ *
+ * Get the current state of link switch with MLO dev context lock held.
+ *
+ * Return: void
+ */
+enum mlo_link_switch_req_state
+mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx);
+
+/**
+ * mlo_mgr_is_link_switch_in_progress() - Check in link ctx in MLO dev context
+ * if the last received link switch is in progress.
+ * @vdev: VDEV object manager
+ *
+ * The API is to be called for VDEV which has MLO dev context and link context
+ * initialized. Returns the value of 'is_in_progress' flag in last received
+ * link switch request.
+ *
+ * Return: bool
+ */
+bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * mlo_mgr_is_link_switch_on_assoc_vdev() - API to query whether link switch
+ * is on-going on assoc VDEV.
+ * @vdev: VDEV object manager
+ *
+ * Return: bool
+ */
+bool mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * mlo_mgr_ser_link_switch_cmd() - The API will serialize link switch
+ * command in serialization queue.
+ * @vdev: VDEV objmgr pointer
+ * @req: Link switch request parameters
+ *
+ * On receiving link switch request with valid parameters from FW, this
+ * API will serialize the link switch command to procced for link switch
+ * on @vdev once the command comes to active queue.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev *vdev,
+				       struct wlan_mlo_link_switch_req *req);
+
+/**
+ * mlo_mgr_remove_link_switch_cmd() - The API will remove the link switch
+ * command from active serialization queue.
+ * @vdev: VDEV object manager
+ *
+ * Once link switch process on @vdev is completed either in success of failure
+ * case, the API removes the link switch command from serialization queue.
+ *
+ * Return: void
+ */
+void mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * mlo_mgr_link_switch_validate_request() - Validate link switch request
+ * received from FW.
+ * @vdev: VDEV object manager
+ * @req: Request params from FW
+ *
+ * The API performs initial validation of link switch params received from FW
+ * before serializing the link switch cmd. If any of the params is invalid or
+ * the current status of MLO manager can't allow link switch, the API returns
+ * failure and link switch has to be terminated.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+mlo_mgr_link_switch_validate_request(struct wlan_objmgr_vdev *vdev,
+				     struct wlan_mlo_link_switch_req *req);
 
 /**
  * mlo_mgr_link_switch_request_params() - Link switch request params from FW.
@@ -277,7 +391,6 @@ QDF_STATUS mlo_mgr_link_switch_request_params(struct wlan_objmgr_psoc *psoc,
  */
 QDF_STATUS mlo_mgr_link_switch_complete(struct wlan_objmgr_vdev *vdev);
 
-#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
 /**
  * mlo_mgr_link_switch_init() - API to initialize link switch
  * @ml_dev: MLO dev context
@@ -300,6 +413,11 @@ QDF_STATUS mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev);
  */
 QDF_STATUS mlo_mgr_link_switch_deinit(struct wlan_mlo_dev_context *ml_dev);
 #else
+static inline void
+mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
+{
+}
+
 static inline QDF_STATUS
 mlo_mgr_link_switch_deinit(struct wlan_mlo_dev_context *ml_dev)
 {
@@ -311,5 +429,65 @@ mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev)
 {
 	return QDF_STATUS_SUCCESS;
 }
+
+static inline void
+mlo_mgr_link_switch_init_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
+{
+}
+
+static inline void
+mlo_mgr_link_switch_trans_next_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
+{
+}
+
+static inline enum mlo_link_switch_req_state
+mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
+{
+	return MLO_LINK_SWITCH_STATE_IDLE;
+}
+
+static inline bool
+mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	return false;
+}
+
+static inline bool
+mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev)
+{
+	return false;
+}
+
+static inline QDF_STATUS
+mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev *vdev,
+			    struct wlan_mlo_link_switch_req *req)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline void
+mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev)
+{
+}
+
+static inline QDF_STATUS
+mlo_mgr_link_switch_validate_request(struct wlan_objmgr_vdev *vdev,
+				     struct wlan_mlo_link_switch_req *req)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline QDF_STATUS
+mlo_mgr_link_switch_request_params(struct wlan_objmgr_psoc *psoc,
+				   void *evt_params)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+
+static inline QDF_STATUS
+mlo_mgr_link_switch_complete(struct wlan_objmgr_vdev *vdev)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
 #endif
 #endif

+ 342 - 3
umac/mlo_mgr/src/wlan_mlo_mgr_link_switch.c

@@ -18,8 +18,10 @@
  * DOC: contains MLO manager Link Switch related functionality
  */
 #include <wlan_mlo_mgr_link_switch.h>
-#include <wlan_objmgr_vdev_obj.h>
-#include <wlan_mlo_mgr_cmn.h>
+#include <wlan_mlo_mgr_main.h>
+#include <wlan_mlo_mgr_sta.h>
+#include <wlan_serialization_api.h>
+#include <wlan_cm_api.h>
 
 void mlo_mgr_update_link_info_mac_addr(struct wlan_objmgr_vdev *vdev,
 				       struct wlan_mlo_link_mac_update *ml_mac_update)
@@ -214,6 +216,77 @@ void mlo_mgr_free_link_info_wmi_chan(struct wlan_mlo_dev_context *ml_dev)
 	}
 }
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+bool mlo_mgr_is_link_switch_in_progress(struct wlan_objmgr_vdev *vdev)
+{
+	enum mlo_link_switch_req_state state;
+	struct wlan_mlo_dev_context *mlo_dev_ctx = vdev->mlo_dev_ctx;
+
+	if (!mlo_dev_ctx)
+		return false;
+
+	state = mlo_mgr_link_switch_get_curr_state(mlo_dev_ctx);
+	return (state != MLO_LINK_SWITCH_STATE_IDLE);
+}
+
+bool mlo_mgr_is_link_switch_on_assoc_vdev(struct wlan_objmgr_vdev *vdev)
+{
+	if (!mlo_mgr_is_link_switch_in_progress(vdev))
+		return false;
+
+	return vdev->mlo_dev_ctx->link_ctx->last_req.restore_vdev_flag;
+}
+
+void mlo_mgr_link_switch_init_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
+{
+	mlo_dev_lock_acquire(mlo_dev_ctx);
+	mlo_dev_ctx->link_ctx->last_req.state = MLO_LINK_SWITCH_STATE_IDLE;
+	mlo_dev_lock_release(mlo_dev_ctx);
+}
+
+void
+mlo_mgr_link_switch_trans_next_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
+{
+	enum mlo_link_switch_req_state cur_state, next_state;
+
+	mlo_dev_lock_acquire(mlo_dev_ctx);
+	cur_state = mlo_dev_ctx->link_ctx->last_req.state;
+	switch (cur_state) {
+	case MLO_LINK_SWITCH_STATE_IDLE:
+		next_state = MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK;
+		break;
+	case MLO_LINK_SWITCH_STATE_DISCONNECT_CURR_LINK:
+		next_state = MLO_LINK_SWITCH_STATE_SET_MAC_ADDR;
+		break;
+	case MLO_LINK_SWITCH_STATE_SET_MAC_ADDR:
+		next_state = MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK;
+		break;
+	case MLO_LINK_SWITCH_STATE_CONNECT_NEW_LINK:
+		next_state = MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS;
+		break;
+	case MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS:
+		next_state = MLO_LINK_SWITCH_STATE_IDLE;
+		break;
+	default:
+		QDF_ASSERT(0);
+		break;
+	}
+	mlo_dev_ctx->link_ctx->last_req.state = next_state;
+	mlo_dev_lock_release(mlo_dev_ctx);
+}
+
+enum mlo_link_switch_req_state
+mlo_mgr_link_switch_get_curr_state(struct wlan_mlo_dev_context *mlo_dev_ctx)
+{
+	enum mlo_link_switch_req_state state;
+
+	mlo_dev_lock_acquire(mlo_dev_ctx);
+	state = mlo_dev_ctx->link_ctx->last_req.state;
+	mlo_dev_lock_release(mlo_dev_ctx);
+
+	return state;
+}
+
 QDF_STATUS mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev)
 {
 	ml_dev->link_ctx =
@@ -222,6 +295,7 @@ QDF_STATUS mlo_mgr_link_switch_init(struct wlan_mlo_dev_context *ml_dev)
 	if (!ml_dev->link_ctx)
 		return QDF_STATUS_E_NOMEM;
 
+	mlo_mgr_link_switch_init_state(ml_dev);
 	mlo_mgr_alloc_link_info_wmi_chan(ml_dev);
 	mlo_mgr_update_link_info_reset(ml_dev);
 
@@ -236,7 +310,6 @@ QDF_STATUS mlo_mgr_link_switch_deinit(struct wlan_mlo_dev_context *ml_dev)
 	return QDF_STATUS_SUCCESS;
 }
 
-#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
 void
 mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
 {
@@ -263,4 +336,270 @@ mlo_mgr_osif_update_connect_info(struct wlan_objmgr_vdev *vdev, int32_t link_id)
 	osif_bss_update_cb(&link_info->link_addr, &link_info->ap_link_addr,
 			   link_id);
 }
+
+static QDF_STATUS
+mlo_mgr_start_link_switch(struct wlan_objmgr_vdev *vdev,
+			  struct wlan_serialization_command *cmd)
+{
+	uint8_t vdev_id, old_link_id, new_link_id;
+	struct mlo_link_switch_context *link_ctx = vdev->mlo_dev_ctx->link_ctx;
+	struct wlan_mlo_link_switch_req *req = &link_ctx->last_req;
+
+	vdev_id = wlan_vdev_get_id(vdev);
+	old_link_id = req->curr_ieee_link_id;
+	new_link_id = req->new_ieee_link_id;
+
+	mlo_debug("VDEV %d start link switch", vdev_id);
+	if (!wlan_cm_is_vdev_connected(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;
+	}
+
+	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;
+	}
+
+	mlo_mgr_remove_link_switch_cmd(vdev);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+mlo_mgr_ser_link_switch_cb(struct wlan_serialization_command *cmd,
+			   enum wlan_serialization_cb_reason reason)
+{
+	struct wlan_objmgr_vdev *vdev;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (!cmd) {
+		mlo_err("cmd is NULL, reason: %d", reason);
+		QDF_ASSERT(0);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	vdev = cmd->vdev;
+	switch (reason) {
+	case WLAN_SER_CB_ACTIVATE_CMD:
+		status = mlo_mgr_start_link_switch(vdev, cmd);
+		break;
+	case WLAN_SER_CB_RELEASE_MEM_CMD:
+		mlo_mgr_link_switch_complete(vdev);
+		break;
+	case WLAN_SER_CB_CANCEL_CMD:
+		mlo_err("Link switch cmd cancelled");
+		break;
+	case WLAN_SER_CB_ACTIVE_CMD_TIMEOUT:
+		mlo_err("Link switch active cmd timeout");
+		break;
+	default:
+		QDF_ASSERT(0);
+		mlo_mgr_link_switch_complete(vdev);
+		break;
+	}
+
+	return status;
+}
+
+void mlo_mgr_remove_link_switch_cmd(struct wlan_objmgr_vdev *vdev)
+{
+	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);
+	struct wlan_mlo_link_switch_req *req;
+
+	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;
+	}
+
+	cur_state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
+	if (cur_state == MLO_LINK_SWITCH_STATE_IDLE)
+		return;
+
+	cmd_info.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
+			  (req->curr_ieee_link_id);
+	cmd_info.req_type = WLAN_SER_CANCEL_NON_SCAN_CMD;
+	cmd_info.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
+	cmd_info.vdev = vdev;
+	cmd_info.queue_type = WLAN_SERIALIZATION_ACTIVE_QUEUE;
+
+	wlan_serialization_remove_cmd(&cmd_info);
+}
+
+#define MLO_MGR_MAX_LSWITCH_TIMEOUT	35000
+
+QDF_STATUS mlo_mgr_ser_link_switch_cmd(struct wlan_objmgr_vdev *vdev,
+				       struct wlan_mlo_link_switch_req *req)
+{
+	enum wlan_serialization_status ser_cmd_status;
+	struct wlan_serialization_command cmd = {0};
+	uint8_t vdev_id = wlan_vdev_get_id(vdev);
+	struct mlo_link_switch_context *link_ctx;
+
+	if (!vdev->mlo_dev_ctx) {
+		mlo_err("ML dev ctx NULL, reject link switch");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	link_ctx = vdev->mlo_dev_ctx->link_ctx;
+	link_ctx->last_req = *req;
+
+	cmd.cmd_type = WLAN_SER_CMD_MLO_VDEV_LINK_SWITCH;
+	cmd.cmd_id = (vdev_id << 16) + (req->new_ieee_link_id << 8) +
+		     (req->curr_ieee_link_id);
+	cmd.cmd_cb = mlo_mgr_ser_link_switch_cb;
+	cmd.source = WLAN_UMAC_COMP_MLO_MGR;
+	cmd.is_high_priority = false;
+	cmd.cmd_timeout_duration = MLO_MGR_MAX_LSWITCH_TIMEOUT;
+	cmd.vdev = vdev;
+	cmd.is_blocking = true;
+
+	ser_cmd_status = wlan_serialization_request(&cmd);
+	switch (ser_cmd_status) {
+	case WLAN_SER_CMD_PENDING:
+		mlo_debug("Link switch cmd in pending queue");
+		break;
+	case WLAN_SER_CMD_ACTIVE:
+		mlo_debug("Link switch cmd in active queue");
+		break;
+	default:
+		return QDF_STATUS_E_INVAL;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+mlo_mgr_link_switch_validate_request(struct wlan_objmgr_vdev *vdev,
+				     struct wlan_mlo_link_switch_req *req)
+{
+	QDF_STATUS status = QDF_STATUS_E_INVAL;
+	uint8_t vdev_id = wlan_vdev_get_id(vdev);
+
+	if (req->curr_ieee_link_id >= WLAN_INVALID_LINK_ID ||
+	    req->new_ieee_link_id >= WLAN_INVALID_LINK_ID) {
+		mlo_err("Invalid link params, curr link id %d, new link id %d",
+			req->curr_ieee_link_id, req->new_ieee_link_id);
+		return status;
+	}
+
+	if (!mlo_mgr_get_ap_link_by_link_id(vdev, req->new_ieee_link_id)) {
+		mlo_err("New link id %d not part of association",
+			req->new_ieee_link_id);
+		return status;
+	}
+
+	if (!mlo_is_mld_sta(vdev)) {
+		mlo_err("Link switch req not valid for VDEV %d", vdev_id);
+		return status;
+	}
+
+	if (!wlan_cm_is_vdev_connected(vdev)) {
+		mlo_err("VDEV %d not in connected state", vdev_id);
+		return status;
+	}
+
+	if (mlo_mgr_is_link_switch_in_progress(vdev)) {
+		mlo_err("Link switch already in progress");
+		return status;
+	}
+
+	if (wlan_vdev_get_link_id(vdev) != req->curr_ieee_link_id) {
+		mlo_err("VDEV %d link id wrong, curr link id %d",
+			vdev_id, wlan_vdev_get_link_id(vdev));
+		return status;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS mlo_mgr_link_switch_request_params(struct wlan_objmgr_psoc *psoc,
+					      void *evt_params)
+{
+	QDF_STATUS status;
+	struct wlan_mlo_link_switch_req *req;
+	struct wlan_objmgr_vdev *vdev;
+
+	if (!evt_params) {
+		mlo_err("Invalid params");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	req = (struct wlan_mlo_link_switch_req *)evt_params;
+
+	/* The reference is released on Link Switch status confirm to FW */
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, req->vdev_id,
+						    WLAN_MLO_MGR_ID);
+	if (!vdev) {
+		mlo_err("Invalid VDEV for link switch");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	status = mlo_mgr_link_switch_validate_request(vdev, req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
+		mlo_debug("Link switch params/request invalid");
+		return QDF_STATUS_E_INVAL;
+	}
+
+	mlo_debug("VDEV %d, curr_link_id %d, new_link_id %d, new_freq %d, new_phymode: %d, reason %d",
+		  req->vdev_id, req->curr_ieee_link_id, req->new_ieee_link_id,
+		  req->new_primary_freq, req->new_phymode, req->reason);
+
+	status = mlo_mgr_ser_link_switch_cmd(vdev, req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		/* Release ref as link switch is not serialized */
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
+		mlo_err("Failed to serialize link switch command");
+	}
+
+	return status;
+}
+
+QDF_STATUS mlo_mgr_link_switch_complete(struct wlan_objmgr_vdev *vdev)
+{
+	QDF_STATUS status;
+	enum mlo_link_switch_req_state state;
+	struct wlan_lmac_if_mlo_tx_ops *mlo_tx_ops;
+	struct wlan_mlo_link_switch_cnf params = {0};
+	struct mlo_link_switch_context *link_ctx;
+	struct wlan_mlo_link_switch_req *req;
+	struct wlan_objmgr_psoc *psoc;
+
+	/* Not checking NULL value as reference is already taken for vdev */
+	psoc = wlan_vdev_get_psoc(vdev);
+
+	link_ctx = vdev->mlo_dev_ctx->link_ctx;
+	req = &link_ctx->last_req;
+
+	mlo_tx_ops = &psoc->soc_cb.tx_ops->mlo_ops;
+	if (!mlo_tx_ops || !mlo_tx_ops->send_mlo_link_switch_cnf_cmd) {
+		mlo_err("handler is not registered");
+		status = QDF_STATUS_E_NULL_VALUE;
+		goto release_ref;
+	}
+
+	state = mlo_mgr_link_switch_get_curr_state(vdev->mlo_dev_ctx);
+	if (state != MLO_LINK_SWITCH_STATE_COMPLETE_SUCCESS)
+		params.status = MLO_LINK_SWITCH_CNF_STATUS_REJECT;
+	else
+		params.status = MLO_LINK_SWITCH_CNF_STATUS_ACCEPT;
+
+	params.vdev_id = wlan_vdev_get_id(vdev);
+	params.reason = MLO_LINK_SWITCH_CNF_REASON_BSS_PARAMS_CHANGED;
+
+	status = mlo_tx_ops->send_mlo_link_switch_cnf_cmd(psoc, &params);
+	mlo_debug("VDEV %d link switch completed", params.vdev_id);
+
+release_ref:
+	mlo_mgr_link_switch_init_state(vdev->mlo_dev_ctx);
+	wlan_vdev_mlme_clear_mlo_link_switch_in_progress(vdev);
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_MLO_MGR_ID);
+	return status;
+}
 #endif

+ 41 - 0
wmi/inc/wmi_unified_11be_api.h

@@ -204,6 +204,47 @@ QDF_STATUS wmi_extract_mlo_link_state_info_event(
 			void *evt_buf,
 			struct ml_link_state_info_event *params);
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+/**
+ * wmi_send_mlo_link_switch_req_cnf_cmd() - Send WMI command to FW on
+ * status of Link switch request received.
+ * @wmi: wmi handle
+ * @params: Params to send to FW.
+ *
+ * Return: QDF_STATUS.
+ */
+QDF_STATUS
+wmi_send_mlo_link_switch_req_cnf_cmd(wmi_unified_t wmi,
+				     struct wlan_mlo_link_switch_cnf *params);
+
+/**
+ * wmi_extract_mlo_link_switch_request_evt() - Extract fixed params TLV
+ * from the MLO link switch request WMI event.
+ * @wmi: wmi handle
+ * @buf: pointer to event buffer
+ * @req: MLO link switch request event params.
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+wmi_extract_mlo_link_switch_request_evt(struct wmi_unified *wmi, void *buf,
+					struct wlan_mlo_link_switch_req *req);
+#else
+static inline QDF_STATUS
+wmi_send_mlo_link_switch_req_cnf_cmd(wmi_unified_t wmi,
+				     struct wlan_mlo_link_switch_cnf *params)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline QDF_STATUS
+wmi_extract_mlo_link_switch_request_evt(struct wmi_unified *wmi, void *buf,
+					struct wlan_mlo_link_switch_req *req)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 /**
  * wmi_extract_mlo_link_disable_request_evt() - Extract fixed parameters TLV
  * from the MLO link disable request WMI event

+ 7 - 1
wmi/inc/wmi_unified_param.h

@@ -1224,6 +1224,8 @@ struct wmi_host_link_state_params {
  * @nstr_bitmap_size: Indicates size of NSTR bitmap,
  *                    as per the 802.11be specification
  * @mlo_bridge_peer: indicate if it is bridge peer
+ * @link_switch_in_progress: Flag to indicate FW MLO peer assoc params are sent
+ *                           for the peer due to link switch
  * @unused: spare bits
  * @mld_mac: MLD mac address
  * @logical_link_index: Unique index for links of the mlo. Starts with Zero
@@ -1257,7 +1259,8 @@ struct peer_assoc_mlo_params {
 		 nstr_bitmap_present:1,
 		 nstr_bitmap_size:1,
 		 mlo_bridge_peer:1,
-		 unused:20;
+		 link_switch_in_progress:1,
+		 unused:19;
 	uint8_t mld_mac[QDF_MAC_ADDR_SIZE];
 	uint32_t logical_link_index;
 	uint32_t ml_peer_id;
@@ -5282,6 +5285,9 @@ typedef enum {
 	wmi_mlo_link_set_active_resp_eventid,
 	wmi_mlo_link_removal_eventid,
 	wmi_mlo_link_disable_request_eventid,
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+	wmi_mlo_link_switch_request_eventid,
+#endif
 #endif
 	wmi_pdev_fips_extend_event_id,
 	wmi_roam_frame_event_id,

+ 11 - 0
wmi/inc/wmi_unified_priv.h

@@ -97,6 +97,7 @@
 #ifdef WLAN_FEATURE_11BE_MLO
 #include <wmi_unified_11be_param.h>
 #include "wlan_mlo_mgr_public_structs.h"
+#include <wlan_mlo_mgr_link_switch.h>
 #endif
 
 #if defined(WLAN_SUPPORT_TWT) && defined(WLAN_TWT_CONV_SUPPORTED)
@@ -3146,6 +3147,16 @@ QDF_STATUS (*extract_mlo_link_disable_request_evt_param)(
 		struct wmi_unified *wmi_handle,
 		void *buf,
 		struct mlo_link_disable_request_evt_params *params);
+
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+QDF_STATUS
+(*extract_mlo_link_switch_request_event)(struct wmi_unified *wmi_handle,
+					 void *buf,
+					 struct wlan_mlo_link_switch_req *req);
+QDF_STATUS
+(*send_mlo_link_switch_req_cnf_cmd)(wmi_unified_t wmi_handle,
+				    struct wlan_mlo_link_switch_cnf *params);
+#endif
 #endif
 
 #ifdef WLAN_FEATURE_SON

+ 25 - 1
wmi/src/wmi_unified_11be_api.c

@@ -81,6 +81,31 @@ QDF_STATUS wmi_send_mlo_vdev_tid_to_link_map_cmd(
 	return QDF_STATUS_E_FAILURE;
 }
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+QDF_STATUS
+wmi_send_mlo_link_switch_req_cnf_cmd(wmi_unified_t wmi,
+				     struct wlan_mlo_link_switch_cnf *params)
+{
+	if (wmi->ops->send_mlo_link_switch_req_cnf_cmd)
+		return wmi->ops->send_mlo_link_switch_req_cnf_cmd(wmi, params);
+
+	return QDF_STATUS_E_FAILURE;
+}
+
+QDF_STATUS
+wmi_extract_mlo_link_switch_request_evt(struct wmi_unified *wmi,
+					void *buf,
+					struct wlan_mlo_link_switch_req *req)
+{
+	if (wmi->ops->extract_mlo_link_switch_request_event)
+		return wmi->ops->extract_mlo_link_switch_request_event(wmi,
+								       buf,
+								       req);
+
+	return QDF_STATUS_E_FAILURE;
+}
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
+
 QDF_STATUS wmi_send_mlo_link_state_request_cmd(
 		wmi_unified_t wmi,
 		struct wmi_host_link_state_params *params)
@@ -140,7 +165,6 @@ QDF_STATUS wmi_extract_mlo_link_disable_request_evt(
 
 	return QDF_STATUS_E_FAILURE;
 }
-
 #endif /* WLAN_FEATURE_11BE */
 
 QDF_STATUS

+ 89 - 0
wmi/src/wmi_unified_11be_tlv.c

@@ -1413,6 +1413,89 @@ static uint8_t *populate_link_control_tlv(
 }
 #endif
 
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+/**
+ * extract_mlo_link_switch_request_event_tlv() - Extract fixed
+ * params TLV from MLO link switch request WMI event.
+ * @wmi_handle: wmi handle
+ * @buf: Pointer to event buffer.
+ * @req: MLO Link switch event parameters.
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+extract_mlo_link_switch_request_event_tlv(struct wmi_unified *wmi_handle,
+					  void *buf,
+					  struct wlan_mlo_link_switch_req *req)
+{
+	WMI_MLO_LINK_SWITCH_REQUEST_EVENTID_param_tlvs *param_buf = buf;
+	wmi_mlo_link_switch_req_evt_fixed_param *ev;
+
+	if (!param_buf) {
+		wmi_err_rl("buf is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (!req) {
+		wmi_err_rl("req is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	ev = param_buf->fixed_param;
+	req->vdev_id = ev->vdev_id;
+	req->curr_ieee_link_id = ev->curr_ieee_link_id;
+	req->new_ieee_link_id = ev->new_ieee_link_id;
+	req->new_primary_freq = ev->new_primary_freq;
+	req->new_phymode = ev->new_phymode;
+	req->reason = ev->reason;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS
+send_link_switch_request_cnf_cmd_tlv(wmi_unified_t wmi_handle,
+				     struct wlan_mlo_link_switch_cnf *params)
+{
+	wmi_mlo_link_switch_cnf_fixed_param *cmd;
+	wmi_buf_t buf;
+	uint8_t *buf_ptr;
+	QDF_STATUS ret = QDF_STATUS_SUCCESS;
+	uint32_t buf_len;
+
+	buf_len = sizeof(wmi_mlo_link_switch_cnf_fixed_param);
+
+	buf = wmi_buf_alloc(wmi_handle, buf_len);
+	if (!buf) {
+		wmi_err("wmi buf alloc failed for vdev id %d while link state cmd send: ",
+			params->vdev_id);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	buf_ptr = (uint8_t *)wmi_buf_data(buf);
+	cmd = (wmi_mlo_link_switch_cnf_fixed_param *)buf_ptr;
+
+	WMITLV_SET_HDR(
+		&cmd->tlv_header,
+		WMITLV_TAG_STRUC_wmi_mlo_link_switch_cnf_fixed_param,
+		WMITLV_GET_STRUCT_TLVLEN(
+			wmi_mlo_link_switch_cnf_fixed_param));
+
+	cmd->vdev_id = params->vdev_id;
+	cmd->status = params->status;
+	cmd->reason = params->reason;
+	buf_ptr += sizeof(wmi_mlo_link_switch_cnf_fixed_param);
+	wmi_mtrace(WMI_MLO_LINK_SWITCH_CONF_CMDID, cmd->vdev_id, 0);
+	ret = wmi_unified_cmd_send(wmi_handle, buf, buf_len,
+				   WMI_MLO_LINK_SWITCH_CONF_CMDID);
+	if (ret) {
+		wmi_err("Failed to send ml link switch cnf command to FW: %d vdev id %d",
+			ret, cmd->vdev_id);
+		wmi_buf_free(buf);
+	}
+	return ret;
+}
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
+
 static QDF_STATUS
 send_link_state_request_cmd_tlv(wmi_unified_t wmi_handle,
 				struct wmi_host_link_state_params *params)
@@ -2365,4 +2448,10 @@ void wmi_11be_attach_tlv(wmi_unified_t wmi_handle)
 	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 */
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+	ops->extract_mlo_link_switch_request_event =
+			extract_mlo_link_switch_request_event_tlv;
+	ops->send_mlo_link_switch_req_cnf_cmd =
+			send_link_switch_request_cnf_cmd_tlv;
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
 }

+ 4 - 0
wmi/src/wmi_unified_tlv.c

@@ -21452,6 +21452,10 @@ static void populate_tlv_events_id_mlo(WMI_EVT_ID *event_ids)
 			WMI_MLO_VDEV_LINK_INFO_EVENTID;
 	event_ids[wmi_mlo_link_disable_request_eventid] =
 			WMI_MLO_LINK_DISABLE_REQUEST_EVENTID;
+#ifdef WLAN_FEATURE_11BE_MLO_ADV_FEATURE
+	event_ids[wmi_mlo_link_switch_request_eventid] =
+			WMI_MLO_LINK_SWITCH_REQUEST_EVENTID;
+#endif /* WLAN_FEATURE_11BE_MLO_ADV_FEATURE */
 }
 #else /* WLAN_FEATURE_11BE_MLO */
 static inline void populate_tlv_events_id_mlo(WMI_EVT_ID *event_ids)