Browse Source

qcacld-3.0: Add logic to support tdls on MLO

Since for MLD device, it can only choice one link for tdls,
add logic like this:
1\ As initiator, it needs to send discovery request on each
link, before send the frame, it needs to force active the link;
2\ As responder, it sends the discovery response the link id,
before send the frame, it needs to force active the link;
3\ For other tdls management and tdls operation, it needs to
find out which link(vdev) is used as tdls link first.

Change-Id: I64e27219eb6c6b3fef62e541423aa8e5d84b1560
CRs-Fixed: 3439568
Paul Zhang 2 years ago
parent
commit
0752365a21

+ 12 - 1
components/tdls/core/src/wlan_tdls_ct.c

@@ -1008,6 +1008,7 @@ static void tdls_ct_process_handler(struct wlan_objmgr_vdev *vdev)
 void tdls_ct_handler(void *user_data)
 {
 	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_vdev *link_vdev;
 
 	if (!user_data)
 		return;
@@ -1016,7 +1017,17 @@ void tdls_ct_handler(void *user_data)
 	if (!vdev)
 		return;
 
-	tdls_ct_process_handler(vdev);
+	link_vdev = tdls_mlo_get_tdls_link_vdev(vdev);
+	if (link_vdev) {
+		if (wlan_objmgr_vdev_try_get_ref(link_vdev, WLAN_TDLS_NB_ID) ==
+		    QDF_STATUS_SUCCESS) {
+			tdls_ct_process_handler(link_vdev);
+			wlan_objmgr_vdev_release_ref(link_vdev,
+						     WLAN_TDLS_NB_ID);
+		}
+	} else {
+		tdls_ct_process_handler(vdev);
+	}
 
 	wlan_objmgr_vdev_release_ref(vdev,
 				     WLAN_TDLS_NB_ID);

+ 80 - 5
components/tdls/core/src/wlan_tdls_mgmt.c

@@ -634,14 +634,15 @@ static QDF_STATUS tdls_activate_send_mgmt_request_flush_cb(
 	return QDF_STATUS_SUCCESS;
 }
 
-static QDF_STATUS tdls_activate_send_mgmt_request(
-				struct tdls_action_frame_request *action_req)
+static QDF_STATUS
+tdls_activate_send_mgmt_request(struct tdls_action_frame_request *action_req)
 {
-	struct wlan_objmgr_peer *peer;
 	struct tdls_soc_priv_obj *tdls_soc_obj;
-	QDF_STATUS status = QDF_STATUS_SUCCESS;
-	struct scheduler_msg msg = {0};
+	QDF_STATUS status;
 	struct tdls_send_mgmt_request *tdls_mgmt_req;
+	struct wlan_objmgr_peer *peer;
+	struct scheduler_msg msg = {0};
+	struct tdls_vdev_priv_obj *tdls_vdev;
 
 	if (!action_req || !action_req->vdev)
 		return QDF_STATUS_E_NULL_VALUE;
@@ -684,6 +685,7 @@ static QDF_STATUS tdls_activate_send_mgmt_request(
 	if (!peer) {
 		tdls_err("bss peer is null");
 		qdf_mem_free(tdls_mgmt_req);
+		status = QDF_STATUS_E_NULL_VALUE;
 		goto release_cmd;
 	}
 
@@ -707,6 +709,25 @@ static QDF_STATUS tdls_activate_send_mgmt_request(
 	else
 		tdls_mgmt_req->ac = WIFI_AC_BK;
 
+	if (wlan_vdev_mlme_is_mlo_vdev(action_req->vdev) &&
+	    !tdls_mlo_get_tdls_link_vdev(action_req->vdev) &&
+	    tdls_mgmt_req->req_type == TDLS_DISCOVERY_REQUEST) {
+		tdls_vdev = wlan_vdev_get_tdls_vdev_obj(action_req->vdev);
+		if (QDF_TIMER_STATE_RUNNING !=
+		    qdf_mc_timer_get_current_state(
+					  &tdls_vdev->peer_discovery_timer)) {
+			tdls_timer_restart(tdls_vdev->vdev,
+				     &tdls_vdev->peer_discovery_timer,
+				     tdls_vdev->threshold_config.tx_period_t -
+				     TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE);
+			qdf_atomic_inc(&tdls_soc_obj->timer_cnt);
+		} else {
+			qdf_mem_free(tdls_mgmt_req);
+			status = QDF_STATUS_E_NULL_VALUE;
+			goto release_cmd;
+		}
+	}
+
 	/* Send the request to PE. */
 	qdf_mem_zero(&msg, sizeof(msg));
 
@@ -781,6 +802,52 @@ tdls_send_mgmt_serialize_callback(struct wlan_serialization_command *cmd,
 	return status;
 }
 
+#ifdef WLAN_FEATURE_11BE_MLO
+static QDF_STATUS tdls_set_link_mode(struct tdls_action_frame_request *req)
+{
+	uint8_t mlo_vdev_lst[WLAN_UMAC_MLO_MAX_VDEVS] = {-1};
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_objmgr_vdev *mlo_tdls_vdev;
+	uint8_t vdev_count = 0;
+	bool is_mlo_vdev;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	is_mlo_vdev = wlan_vdev_mlme_is_mlo_vdev(req->vdev);
+	if (!is_mlo_vdev)
+		return status;
+
+	mlo_tdls_vdev = wlan_mlo_get_tdls_link_vdev(req->vdev);
+	if (mlo_tdls_vdev)
+		return status;
+
+	psoc = wlan_vdev_get_psoc(req->vdev);
+	if (!psoc) {
+		tdls_err("psoc is NULL");
+		return QDF_STATUS_E_NULL_VALUE;
+	}
+
+	if (req->tdls_mgmt.frame_type == TDLS_DISCOVERY_RESPONSE ||
+	    req->tdls_mgmt.frame_type == TDLS_DISCOVERY_REQUEST) {
+		mlo_vdev_lst[0] = wlan_vdev_get_id(req->vdev);
+		vdev_count = 1;
+
+		status = policy_mgr_mlo_sta_set_link(psoc,
+						     MLO_LINK_FORCE_REASON_TDLS,
+						     MLO_LINK_FORCE_MODE_ACTIVE,
+						     vdev_count, mlo_vdev_lst);
+		if (status == QDF_STATUS_SUCCESS)
+			req->link_active = true;
+	}
+
+	return status;
+}
+#else
+static QDF_STATUS tdls_set_link_mode(struct tdls_action_frame_request *req)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif
+
 QDF_STATUS tdls_process_mgmt_req(
 			struct tdls_action_frame_request *tdls_mgmt_req)
 {
@@ -796,6 +863,14 @@ QDF_STATUS tdls_process_mgmt_req(
 		goto error_mgmt;
 	}
 
+	status = tdls_set_link_mode(tdls_mgmt_req);
+	if (status != QDF_STATUS_SUCCESS) {
+		tdls_err("failed to set link active");
+		status = tdls_internal_send_mgmt_tx_done(tdls_mgmt_req,
+							 status);
+		goto error_mgmt;
+	}
+
 	/* update the responder, status code information
 	 * after the  cmd validation
 	 */

+ 4 - 0
components/tdls/dispatcher/inc/wlan_tdls_public_structs.h

@@ -1176,20 +1176,24 @@ struct tdls_get_all_peers {
  * @vdev: vdev object
  * @chk_frame: This struct used to validate mgmt frame
  * @session_id: session id
+ * @link_id: link id
  * @vdev_id: vdev id
  * @cmd_buf: cmd buffer
  * @len: length of the frame
  * @use_default_ac: access category
+ * @link_active: whether link active command send successfully
  * @tdls_mgmt: tdls management
  */
 struct tdls_action_frame_request {
 	struct wlan_objmgr_vdev *vdev;
 	struct tdls_validate_action_req chk_frame;
 	uint8_t session_id;
+	uint8_t link_id;
 	uint8_t vdev_id;
 	const uint8_t *cmd_buf;
 	uint8_t len;
 	bool use_default_ac;
+	bool link_active;
 	/* Variable length, do not add anything after this */
 	struct tdls_send_mgmt tdls_mgmt;
 };

+ 48 - 0
components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h

@@ -181,6 +181,35 @@ void ucfg_tdls_update_fw_mlo_capability(struct wlan_objmgr_psoc *psoc,
 }
 #endif
 
+/**
+ * ucfg_tdls_link_vdev_is_matching() - check whether vdev is matching link vdev
+ * @vdev: vdev object
+ *
+ * Return: bool
+ */
+bool ucfg_tdls_link_vdev_is_matching(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * ucfg_tdls_get_tdls_link_vdev() - get tdls link vdev
+ * @vdev: vdev object
+ * @dbg_id: debug id
+ *
+ * Return: vdev pointer
+ */
+struct wlan_objmgr_vdev *
+ucfg_tdls_get_tdls_link_vdev(struct wlan_objmgr_vdev *vdev,
+			     wlan_objmgr_ref_dbgid dbg_id);
+
+/**
+ * ucfg_tdls_put_tdls_link_vdev() - put tdls link vdev
+ * @vdev: vdev odject
+ * @dbg_id: debug id
+ *
+ * Return: void
+ */
+void ucfg_tdls_put_tdls_link_vdev(struct wlan_objmgr_vdev *vdev,
+				  wlan_objmgr_ref_dbgid dbg_id);
+
 /**
  * ucfg_tdls_psoc_enable() - TDLS module enable API
  * @psoc: psoc object
@@ -429,6 +458,25 @@ struct wlan_objmgr_vdev *ucfg_get_tdls_vdev(struct wlan_objmgr_psoc *psoc,
 					    wlan_objmgr_ref_dbgid dbg_id);
 
 #else
+static inline
+bool ucfg_tdls_link_vdev_is_matching(struct wlan_objmgr_vdev *vdev)
+{
+	return false;
+}
+
+static inline
+struct wlan_objmgr_vdev *
+ucfg_tdls_get_tdls_link_vdev(struct wlan_objmgr_vdev *vdev,
+			     wlan_objmgr_ref_dbgid dbg_id)
+{
+	return NULL;
+}
+
+static inline
+void ucfg_tdls_put_tdls_link_vdev(struct wlan_objmgr_vdev *vdev,
+				  wlan_objmgr_ref_dbgid dbg_id)
+{
+}
 
 static inline
 QDF_STATUS ucfg_tdls_init(void)

+ 44 - 0
components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c

@@ -34,6 +34,7 @@
 #include "wlan_policy_mgr_api.h"
 #include "wlan_scan_ucfg_api.h"
 #include "cfg_tdls.h"
+#include "wlan_mlo_mgr_sta.h"
 #include "cfg_ucfg_api.h"
 #include "wlan_tdls_api.h"
 
@@ -519,6 +520,49 @@ QDF_STATUS ucfg_tdls_update_config(struct wlan_objmgr_psoc *psoc,
 	return QDF_STATUS_SUCCESS;
 }
 
+bool ucfg_tdls_link_vdev_is_matching(struct wlan_objmgr_vdev *vdev)
+{
+	struct wlan_objmgr_vdev *tdls_link_vdev;
+
+	tdls_link_vdev = tdls_mlo_get_tdls_link_vdev(vdev);
+	if (!tdls_link_vdev) {
+		wlan_vdev_mlme_feat_ext2_cap_set(vdev,
+						 WLAN_VDEV_FEXT2_MLO_STA_TDLS);
+		return true;
+	}
+
+	if (tdls_link_vdev && tdls_link_vdev != vdev) {
+		tdls_debug("tdls vdev has been created on vdev %d",
+			   wlan_vdev_get_id(tdls_link_vdev));
+		return false;
+	}
+
+	return true;
+}
+
+struct wlan_objmgr_vdev *
+ucfg_tdls_get_tdls_link_vdev(struct wlan_objmgr_vdev *vdev,
+			     wlan_objmgr_ref_dbgid dbg_id)
+{
+	struct wlan_objmgr_vdev *link_vdev;
+
+	link_vdev = tdls_mlo_get_tdls_link_vdev(vdev);
+	if (!link_vdev)
+		return NULL;
+
+	if (wlan_objmgr_vdev_try_get_ref(link_vdev, dbg_id) !=
+	    QDF_STATUS_SUCCESS)
+		return NULL;
+
+	return link_vdev;
+}
+
+void ucfg_tdls_put_tdls_link_vdev(struct wlan_objmgr_vdev *vdev,
+				  wlan_objmgr_ref_dbgid dbg_id)
+{
+	wlan_objmgr_vdev_release_ref(vdev, dbg_id);
+}
+
 QDF_STATUS ucfg_tdls_psoc_enable(struct wlan_objmgr_psoc *psoc)
 {
 	QDF_STATUS status;

+ 42 - 15
core/hdd/src/wlan_hdd_cfg80211.c

@@ -21684,12 +21684,34 @@ static int wlan_hdd_add_key_mlo_vdev(mac_handle_t mac_handle,
 		}
 	}
 
-	if (pairwise && link_id == -1)
+	link_vdev = ucfg_tdls_get_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+	if (pairwise && link_id == -1 && !link_vdev)
 		return wlan_hdd_add_key_all_mlo_vdev(mac_handle, vdev,
 						     key_index, pairwise,
 						     mac_addr, params,
 						     link_id, adapter);
 
+	if (pairwise && link_id == -1 && link_vdev) {
+		hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+		link_adapter =
+		      hdd_get_adapter_by_vdev(hdd_ctx,
+					      wlan_vdev_get_id(link_vdev));
+		link_id = wlan_vdev_get_link_id(link_vdev);
+		if (!link_adapter) {
+			ucfg_tdls_put_tdls_link_vdev(link_vdev,
+						     WLAN_OSIF_TDLS_ID);
+			hdd_err("couldn't set tdls key, link_id %d", link_id);
+			return -EINVAL;
+		}
+
+		errno = wlan_hdd_add_key_vdev(mac_handle, link_vdev, key_index,
+					      pairwise, mac_addr, params,
+					      link_id, link_adapter);
+		ucfg_tdls_put_tdls_link_vdev(link_vdev, WLAN_OSIF_TDLS_ID);
+
+		return errno;
+	}
+
 	if (wlan_vdev_get_link_id(adapter->deflink->vdev) == link_id) {
 		hdd_debug("add_key for same vdev: %d",
 			  adapter->deflink->vdev_id);
@@ -23234,6 +23256,19 @@ int wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, struct net_device *dev,
 }
 #endif
 
+#ifdef TDLS_MGMT_VERSION5
+static inline
+uint8_t wlan_hdd_get_link_id(struct station_parameters *params)
+{
+	return params->link_sta_params.link_id;
+}
+#else
+static inline
+uint8_t wlan_hdd_get_link_id(struct station_parameters *params)
+{
+	return 255;
+}
+#endif
 /**
  * __wlan_hdd_cfg80211_add_station() - add station
  * @wiphy: Pointer to wiphy
@@ -23253,6 +23288,7 @@ static int __wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
 	u32 mask, set;
+	uint8_t link_id;
 
 	hdd_enter();
 
@@ -23272,24 +23308,15 @@ static int __wlan_hdd_cfg80211_add_station(struct wiphy *wiphy,
 		return -EINVAL;
 
 	mask = params->sta_flags_mask;
-
 	set = params->sta_flags_set;
-
-	hdd_debug("mask 0x%x set 0x%x " QDF_MAC_ADDR_FMT, mask, set,
-		  QDF_MAC_ADDR_REF(mac));
+	link_id = wlan_hdd_get_link_id(params);
+	hdd_debug("mask 0x%x set 0x%x link_id %d " QDF_MAC_ADDR_FMT, mask, set,
+		  link_id, QDF_MAC_ADDR_REF(mac));
 
 	if (mask & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
 		if (set & BIT(NL80211_STA_FLAG_TDLS_PEER)) {
-			struct wlan_objmgr_vdev *vdev;
-
-			vdev = hdd_objmgr_get_vdev_by_user(adapter,
-							   WLAN_OSIF_TDLS_ID);
-			if (vdev) {
-				status = wlan_cfg80211_tdls_add_peer(vdev,
-								     mac);
-				hdd_objmgr_put_vdev_by_user(vdev,
-							    WLAN_OSIF_TDLS_ID);
-			}
+			status = wlan_cfg80211_tdls_add_peer_mlo(adapter,
+								 mac, link_id);
 		}
 	}
 #endif

+ 17 - 10
core/hdd/src/wlan_hdd_tdls.c

@@ -80,6 +80,7 @@ int wlan_hdd_tdls_get_all_peers(struct hdd_adapter *adapter,
 	int len;
 	struct hdd_context *hdd_ctx;
 	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_vdev *link_vdev;
 	int ret;
 
 	hdd_enter();
@@ -103,7 +104,15 @@ int wlan_hdd_tdls_get_all_peers(struct hdd_adapter *adapter,
 		len = scnprintf(buf, buflen, "\nVDEV is NULL\n");
 		return len;
 	}
-	ret = wlan_cfg80211_tdls_get_all_peers(vdev, buf, buflen);
+
+	link_vdev = ucfg_tdls_get_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+	if (link_vdev) {
+		ret = wlan_cfg80211_tdls_get_all_peers(link_vdev, buf, buflen);
+		ucfg_tdls_put_tdls_link_vdev(link_vdev, WLAN_OSIF_TDLS_ID);
+	} else {
+		ret = wlan_cfg80211_tdls_get_all_peers(vdev, buf, buflen);
+	}
+
 	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_TDLS_ID);
 
 	return ret;
@@ -467,6 +476,9 @@ static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
 	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
 	bool tdls_support;
+#ifndef TDLS_MGMT_VERSION5
+	int link_id = -1;
+#endif
 #if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0))
 #if !(TDLS_MGMT_VERSION2)
 	u32 peer_capability;
@@ -498,17 +510,12 @@ static int __wlan_hdd_cfg80211_tdls_mgmt(struct wiphy *wiphy,
 	}
 
 	if (hdd_ctx->tdls_umac_comp_active) {
-		struct wlan_objmgr_vdev *vdev;
 		int ret;
 
-		vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_TDLS_ID);
-		if (!vdev)
-			return -EINVAL;
-		ret = wlan_cfg80211_tdls_mgmt(vdev, peer,
-					      action_code, dialog_token,
-					      status_code, peer_capability,
-					      buf, len);
-		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_TDLS_ID);
+		ret = wlan_cfg80211_tdls_mgmt_mlo(adapter, peer,
+						  action_code, dialog_token,
+						  status_code, peer_capability,
+						  buf, len, link_id);
 		return ret;
 	}
 

+ 16 - 12
os_if/tdls/inc/wlan_cfg80211_tdls.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-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
@@ -32,6 +32,7 @@
 #include <wlan_tdls_public_structs.h>
 #include <qdf_list.h>
 #include <qdf_types.h>
+#include <wlan_hdd_main.h>
 #include <wlan_tdls_ucfg_api.h>
 
 #ifdef FEATURE_WLAN_TDLS
@@ -110,14 +111,15 @@ QDF_STATUS wlan_cfg80211_tdls_osif_priv_init(struct wlan_objmgr_vdev *vdev);
 void wlan_cfg80211_tdls_osif_priv_deinit(struct wlan_objmgr_vdev *vdev);
 
 /**
- * wlan_cfg80211_tdls_add_peer() - process cfg80211 add TDLS peer request
- * @vdev: vdev object
+ * wlan_cfg80211_tdls_add_peer_mlo() - process cfg80211 add TDLS peer request
+ * @adapter: adapter pointer
  * @mac: MAC address for TDLS peer
+ * @link_id: link id
  *
  * Return: 0 for success; negative errno otherwise
  */
-int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_vdev *vdev,
-				const uint8_t *mac);
+int wlan_cfg80211_tdls_add_peer_mlo(struct hdd_adapter *adapter,
+				    const uint8_t *mac, uint8_t link_id);
 
 /**
  * wlan_cfg80211_tdls_update_peer() - process cfg80211 update TDLS peer request
@@ -185,8 +187,9 @@ bool wlan_cfg80211_tdls_is_fw_6ghz_capable(struct wlan_objmgr_vdev *vdev);
 #endif
 
 /**
- * wlan_cfg80211_tdls_mgmt() - process TDLS management frames from the supplicant
- * @vdev: vdev object
+ * wlan_cfg80211_tdls_mgmt_mlo() - process TDLS management frames from
+ * the supplicant
+ * @adapter: adapter object
  * @peer: MAC address of the TDLS peer
  * @action_code: type of TDLS mgmt frame to be sent
  * @dialog_token: dialog token used in the frame
@@ -194,14 +197,15 @@ bool wlan_cfg80211_tdls_is_fw_6ghz_capable(struct wlan_objmgr_vdev *vdev);
  * @peer_capability: peer capability information
  * @buf: additional IEs to be included
  * @len: length of additional Ies
+ * @link_id: link id
  *
  * Return: 0 on success; negative errno otherwise
  */
-int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev,
-			    const uint8_t *peer,
-			    uint8_t action_code, uint8_t dialog_token,
-			    uint16_t status_code, uint32_t peer_capability,
-			    const uint8_t *buf, size_t len);
+int wlan_cfg80211_tdls_mgmt_mlo(struct hdd_adapter *adapter,
+				const uint8_t *peer,
+				uint8_t action_code, uint8_t dialog_token,
+				uint16_t status_code, uint32_t peer_capability,
+				const uint8_t *buf, size_t len, int link_id);
 
 /**
  * wlan_tdls_antenna_switch() - process TDLS antenna switch

+ 166 - 12
os_if/tdls/src/wlan_cfg80211_tdls.c

@@ -39,6 +39,8 @@
 #include "wlan_tdls_ucfg_api.h"
 #include "wlan_cm_roam_api.h"
 #include "wlan_mlo_mgr_sta.h"
+#include "wlan_hdd_main.h"
+#include "wlan_hdd_object_manager.h"
 
 static int wlan_cfg80211_tdls_validate_mac_addr(const uint8_t *mac)
 {
@@ -104,8 +106,8 @@ void hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev)
 	ucfg_tdls_notify_reset_adapter(vdev);
 }
 
-int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_vdev *vdev,
-				const uint8_t *mac)
+static int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_vdev *vdev,
+				       const uint8_t *mac)
 {
 	struct tdls_add_peer_params *add_peer_req;
 	int status;
@@ -163,6 +165,42 @@ error:
 	return status;
 }
 
+int wlan_cfg80211_tdls_add_peer_mlo(struct hdd_adapter *adapter,
+				    const uint8_t *mac, uint8_t link_id)
+{
+	struct wlan_objmgr_vdev *vdev;
+	bool is_mlo_vdev;
+	int status;
+
+	vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_TDLS_ID);
+	if (!vdev)
+		return -EINVAL;
+
+	is_mlo_vdev = wlan_vdev_mlme_is_mlo_vdev(vdev);
+	if (is_mlo_vdev) {
+		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_TDLS_ID);
+
+		vdev = wlan_key_get_link_vdev(adapter, WLAN_OSIF_TDLS_ID,
+					      link_id);
+		if (!vdev)
+			return -EINVAL;
+
+		if (!ucfg_tdls_link_vdev_is_matching(vdev)) {
+			wlan_key_put_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+			return -EINVAL;
+		}
+
+		osif_debug("tdls add peer for vdev %d", wlan_vdev_get_id(vdev));
+		status = wlan_cfg80211_tdls_add_peer(vdev, mac);
+		wlan_key_put_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+	} else {
+		status = wlan_cfg80211_tdls_add_peer(vdev, mac);
+		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_TDLS_ID);
+	}
+
+	return status;
+}
+
 static bool
 is_duplicate_freq(qdf_freq_t *arr, uint8_t index, qdf_freq_t freq)
 {
@@ -623,6 +661,7 @@ int wlan_cfg80211_tdls_update_peer(struct wlan_objmgr_vdev *vdev,
 	struct wlan_objmgr_psoc *psoc;
 	bool tdls_11ax_support = false;
 	bool tdls_6g_support = false;
+	bool is_mlo_vdev;
 
 	status = wlan_cfg80211_tdls_validate_mac_addr(mac);
 
@@ -632,14 +671,25 @@ int wlan_cfg80211_tdls_update_peer(struct wlan_objmgr_vdev *vdev,
 	osif_debug("Update TDLS peer " QDF_MAC_ADDR_FMT,
 		   QDF_MAC_ADDR_REF(mac));
 
-	req_info = qdf_mem_malloc(sizeof(*req_info));
-	if (!req_info)
-		return -EINVAL;
+	is_mlo_vdev = wlan_vdev_mlme_is_mlo_vdev(vdev);
+	if (is_mlo_vdev) {
+		vdev = ucfg_tdls_get_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+		if (!vdev) {
+			osif_err("no tdls link vdev");
+			return -EINVAL;
+		}
+	}
 
 	psoc = wlan_vdev_get_psoc(vdev);
 	if (!psoc) {
 		osif_err_rl("Invalid psoc");
-		return -EINVAL;
+		goto relref;
+	}
+
+	req_info = qdf_mem_malloc(sizeof(*req_info));
+	if (!req_info) {
+		status = -EINVAL;
+		goto relref;
 	}
 
 	tdls_11ax_support = ucfg_tdls_is_fw_11ax_capable(psoc);
@@ -682,6 +732,9 @@ int wlan_cfg80211_tdls_update_peer(struct wlan_objmgr_vdev *vdev,
 	}
 error:
 	qdf_mem_free(req_info);
+relref:
+	if (is_mlo_vdev)
+		ucfg_tdls_put_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
 	return status;
 }
 
@@ -759,6 +812,7 @@ int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev,
 	int status;
 	unsigned long rc;
 	enum tdls_command_type cmd;
+	bool is_mlo_vdev;
 
 	status = wlan_cfg80211_tdls_validate_mac_addr(peer);
 
@@ -771,6 +825,15 @@ int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev,
 		return -ENOTSUPP;
 	}
 
+	is_mlo_vdev = wlan_vdev_mlme_is_mlo_vdev(vdev);
+	if (is_mlo_vdev) {
+		vdev = ucfg_tdls_get_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+		if (!vdev) {
+			osif_err("no tdls link vdev");
+			return -EINVAL;
+		}
+	}
+
 	osif_debug("%s start", tdls_oper_to_str(oper));
 	cmd = tdls_oper_to_cmd(oper);
 	switch (oper) {
@@ -786,6 +849,9 @@ int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev,
 		}
 		break;
 	case NL80211_TDLS_DISABLE_LINK:
+		wlan_vdev_mlme_feat_ext2_cap_clear(vdev,
+						   WLAN_VDEV_FEXT2_MLO_STA_TDLS);
+
 		osif_priv = wlan_vdev_get_ospriv(vdev);
 
 		if (!osif_priv || !osif_priv->osif_tdls) {
@@ -816,6 +882,8 @@ int wlan_cfg80211_tdls_oper(struct wlan_objmgr_vdev *vdev,
 	}
 
 error:
+	if (is_mlo_vdev)
+		ucfg_tdls_put_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
 	return status;
 }
 
@@ -844,7 +912,8 @@ void wlan_cfg80211_tdls_rx_callback(void *user_data,
 	assoc_vdev = vdev;
 	opmode = wlan_vdev_mlme_get_opmode(vdev);
 
-	if (opmode == QDF_STA_MODE && wlan_vdev_mlme_is_mlo_vdev(vdev)) {
+	if ((opmode == QDF_STA_MODE || opmode == QDF_TDLS_MODE) &&
+	    wlan_vdev_mlme_is_mlo_vdev(vdev)) {
 		assoc_vdev = ucfg_mlo_get_assoc_link_vdev(vdev);
 		if (!assoc_vdev) {
 			osif_err("assoc vdev is null");
@@ -1000,11 +1069,12 @@ bool wlan_cfg80211_tdls_is_fw_6ghz_capable(struct wlan_objmgr_vdev *vdev)
 }
 #endif
 
-int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev,
-			    const uint8_t *peer_mac,
-			    uint8_t action_code, uint8_t dialog_token,
-			    uint16_t status_code, uint32_t peer_capability,
-			    const uint8_t *buf, size_t len)
+static int
+wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev,
+			const uint8_t *peer_mac,
+			uint8_t action_code, uint8_t dialog_token,
+			uint16_t status_code, uint32_t peer_capability,
+			const uint8_t *buf, size_t len, int link_id)
 {
 	struct tdls_action_frame_request mgmt_req;
 	struct vdev_osif_priv *osif_priv;
@@ -1060,6 +1130,8 @@ int wlan_cfg80211_tdls_mgmt(struct wlan_objmgr_vdev *vdev,
 	mgmt_req.tdls_mgmt.peer_capability = peer_capability;
 	mgmt_req.tdls_mgmt.status_code = mgmt_req.chk_frame.status_code;
 
+	mgmt_req.link_active = false;
+	mgmt_req.link_id = link_id;
 	/*populate the additional IE's */
 	mgmt_req.cmd_buf = buf;
 	mgmt_req.len = len;
@@ -1111,6 +1183,88 @@ error_mgmt_req:
 	return status;
 }
 
+int
+wlan_cfg80211_tdls_mgmt_mlo(struct hdd_adapter *adapter, const uint8_t *peer,
+			    uint8_t action_code, uint8_t dialog_token,
+			    uint16_t status_code, uint32_t peer_capability,
+			    const uint8_t *buf, size_t len, int link_id)
+{
+	struct wlan_objmgr_vdev *tdls_link_vdev = NULL;
+	struct wlan_objmgr_vdev *mlo_vdev = NULL;
+	struct wlan_objmgr_vdev *vdev;
+	bool is_mlo_vdev;
+	bool link_id_vdev = false;
+	bool dis_req_more = false;
+	uint8_t i;
+	int ret;
+
+	vdev = hdd_objmgr_get_vdev_by_user(adapter, WLAN_OSIF_TDLS_ID);
+	if (!vdev)
+		return -EINVAL;
+
+	is_mlo_vdev = wlan_vdev_mlme_is_mlo_vdev(vdev);
+	if (is_mlo_vdev) {
+		tdls_link_vdev =
+			ucfg_tdls_get_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+		if (!tdls_link_vdev) {
+			if (action_code == TDLS_DISCOVERY_RESPONSE) {
+				hdd_objmgr_put_vdev_by_user(vdev,
+							    WLAN_OSIF_TDLS_ID);
+				if (link_id < 0) {
+					osif_err("link id is invalid");
+					return -EINVAL;
+				}
+				/* Get the candidate vdev per link id */
+				link_id_vdev = true;
+				vdev = wlan_key_get_link_vdev(adapter,
+							      WLAN_OSIF_TDLS_ID,
+							      link_id);
+			} else if (action_code == TDLS_DISCOVERY_REQUEST) {
+				if (ucfg_tdls_discovery_on_going(vdev)) {
+					osif_err("discovery request is going");
+					hdd_objmgr_put_vdev_by_user(vdev,
+							     WLAN_OSIF_TDLS_ID);
+					return -EAGAIN;
+				}
+				dis_req_more = true;
+			}
+		} else {
+			hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_TDLS_ID);
+			vdev = tdls_link_vdev;
+		}
+	}
+
+	if (dis_req_more) {
+		/* it needs to send discovery request on each vdev */
+		for (i = 0 ;  i < WLAN_UMAC_MLO_MAX_VDEVS; i++) {
+			mlo_vdev = ucfg_tdls_get_mlo_vdev(vdev, i,
+							  WLAN_OSIF_TDLS_ID);
+			if (!mlo_vdev) {
+				osif_err("mlo vdev is NULL");
+				continue;
+			}
+			ret = wlan_cfg80211_tdls_mgmt(mlo_vdev, peer,
+						      action_code,
+						      dialog_token, status_code,
+						      peer_capability, buf, len,
+						      link_id);
+			ucfg_tdls_release_mlo_vdev(mlo_vdev, WLAN_OSIF_TDLS_ID);
+		}
+	} else {
+		ret = wlan_cfg80211_tdls_mgmt(vdev, peer,
+					      action_code, dialog_token,
+					      status_code, peer_capability,
+					      buf, len, link_id);
+	}
+
+	if (vdev && link_id_vdev)
+		wlan_key_put_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+	else if (!tdls_link_vdev)
+		ucfg_tdls_put_tdls_link_vdev(vdev, WLAN_OSIF_TDLS_ID);
+
+	return ret;
+}
+
 int wlan_tdls_antenna_switch(struct wlan_objmgr_vdev *vdev, uint32_t mode)
 {
 	struct vdev_osif_priv *osif_priv;