Procházet zdrojové kódy

qcacld-3.0: Handle TDLS concurrency during CSA

If CSA on SAP/P2P GO causes MCC with the current STA TDLS vdev,
then teardown the TDLS connections, disable off channel and send
disable TDLS to firmware.
If CSA causes MCC -> DBS or MCC -> SCC then allow TDLS and update
the peer off channel list to firmware
Disable TDLS off-channel before SAP/P2P GO vdev restart.
Once CSA is complete, TDLS off-channel will be re-enabled based
on the concurrency.

Add interface manager changes to notify STA channel switch.

Change-Id: I36b359a7e1cf570cfb2b2f0e6abedf148a8fd174
CRs-Fixed: 3445112
Pragaspathi Thilagaraj před 2 roky
rodič
revize
282f018e47

+ 35 - 5
components/cmn_services/interface_mgr/src/wlan_if_mgr_sap.c

@@ -47,7 +47,7 @@ QDF_STATUS if_mgr_ap_start_bss(struct wlan_objmgr_vdev *vdev,
 	if (!psoc)
 		return QDF_STATUS_E_FAILURE;
 
-	wlan_tdls_notify_start_bss(psoc);
+	wlan_tdls_notify_start_bss(psoc, vdev);
 
 	if (wlan_vdev_mlme_get_opmode(vdev) == QDF_SAP_MODE ||
 	    wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_GO_MODE)
@@ -157,10 +157,9 @@ if_mgr_ap_stop_bss_complete(struct wlan_objmgr_vdev *vdev,
 	return QDF_STATUS_SUCCESS;
 }
 
-#ifdef WLAN_FEATURE_P2P_P2P_STA
 QDF_STATUS
-if_mgr_csa_complete(struct wlan_objmgr_vdev *vdev,
-		    struct if_mgr_event_data *event_data)
+if_mgr_ap_csa_complete(struct wlan_objmgr_vdev *vdev,
+		       struct if_mgr_event_data *event_data)
 {
 	struct wlan_objmgr_psoc *psoc;
 	struct wlan_objmgr_pdev *pdev;
@@ -169,6 +168,7 @@ if_mgr_csa_complete(struct wlan_objmgr_vdev *vdev,
 	pdev = wlan_vdev_get_pdev(vdev);
 	if (!pdev)
 		return QDF_STATUS_E_FAILURE;
+
 	psoc = wlan_pdev_get_psoc(pdev);
 	if (!psoc)
 		return QDF_STATUS_E_FAILURE;
@@ -176,6 +176,36 @@ if_mgr_csa_complete(struct wlan_objmgr_vdev *vdev,
 	status = wlan_p2p_check_and_force_scc_go_plus_go(psoc, vdev);
 	if (QDF_IS_STATUS_ERROR(status))
 		ifmgr_err("force scc failure with status: %d", status);
+
+	wlan_tdls_notify_channel_switch_complete(psoc, wlan_vdev_get_id(vdev));
+
 	return status;
 }
-#endif
+
+QDF_STATUS
+if_mgr_ap_csa_start(struct wlan_objmgr_vdev *vdev,
+		    struct if_mgr_event_data *event_data)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_objmgr_pdev *pdev;
+	enum QDF_OPMODE op_mode;
+
+	op_mode = wlan_vdev_mlme_get_opmode(vdev);
+	if (op_mode != QDF_SAP_MODE && op_mode != QDF_P2P_GO_MODE)
+		return QDF_STATUS_SUCCESS;
+
+	pdev = wlan_vdev_get_pdev(vdev);
+	if (!pdev)
+		return QDF_STATUS_E_FAILURE;
+
+	psoc = wlan_pdev_get_psoc(pdev);
+	if (!psoc)
+		return QDF_STATUS_E_FAILURE;
+
+	/*
+	 * Disable TDLS off-channel before VDEV restart
+	 */
+	wlan_tdls_notify_channel_switch_start(psoc, vdev);
+
+	return QDF_STATUS_SUCCESS;
+}

+ 24 - 0
components/cmn_services/interface_mgr/src/wlan_if_mgr_sta.c

@@ -39,6 +39,7 @@
 #include <wlan_mlo_mgr_sta.h>
 #endif
 #include "wlan_vdev_mgr_utils_api.h"
+#include "wlan_tdls_api.h"
 
 QDF_STATUS if_mgr_connect_start(struct wlan_objmgr_vdev *vdev,
 				struct if_mgr_event_data *event_data)
@@ -91,6 +92,9 @@ QDF_STATUS if_mgr_connect_start(struct wlan_objmgr_vdev *vdev,
 			ucfg_nan_disable_concurrency(psoc);
 	}
 
+	if (op_mode == QDF_P2P_CLIENT_MODE)
+		wlan_tdls_handle_p2p_client_connect(psoc, vdev);
+
 	/*
 	 * STA+NDI concurrency gets preference over NDI+NDI. Disable
 	 * first NDI in case an NDI+NDI concurrency exists if FW does
@@ -232,3 +236,23 @@ QDF_STATUS if_mgr_disconnect_complete(struct wlan_objmgr_vdev *vdev,
 
 	return QDF_STATUS_SUCCESS;
 }
+
+QDF_STATUS
+if_mgr_sta_csa_complete(struct wlan_objmgr_vdev *vdev,
+			struct if_mgr_event_data *event_data)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_objmgr_pdev *pdev;
+
+	pdev = wlan_vdev_get_pdev(vdev);
+	if (!pdev)
+		return QDF_STATUS_E_FAILURE;
+
+	psoc = wlan_pdev_get_psoc(pdev);
+	if (!psoc)
+		return QDF_STATUS_E_FAILURE;
+
+	wlan_tdls_notify_channel_switch_complete(psoc, wlan_vdev_get_id(vdev));
+
+	return QDF_STATUS_SUCCESS;
+}

+ 8 - 1
components/p2p/dispatcher/inc/wlan_p2p_api.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-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
@@ -90,5 +90,12 @@ QDF_STATUS wlan_p2p_abort_scan(struct wlan_objmgr_pdev *pdev);
 QDF_STATUS
 wlan_p2p_check_and_force_scc_go_plus_go(struct wlan_objmgr_psoc *psoc,
 					struct wlan_objmgr_vdev *vdev);
+#else
+static inline QDF_STATUS
+wlan_p2p_check_and_force_scc_go_plus_go(struct wlan_objmgr_psoc *psoc,
+					struct wlan_objmgr_vdev *vdev)
+{
+	return QDF_STATUS_SUCCESS;
+}
 #endif
 #endif

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

@@ -1474,7 +1474,7 @@ void tdls_disable_offchan_and_teardown_links(
 				tdls_soc->tdls_configs.tdls_pre_off_chan_num);
 	tdls_set_tdls_secoffchanneloffset(tdls_soc,
 			TDLS_SEC_OFFCHAN_OFFSET_40PLUS);
-	tdls_set_tdls_offchannelmode(vdev, DISABLE_CHANSWITCH);
+	tdls_set_tdls_offchannelmode(vdev, DISABLE_ACTIVE_CHANSWITCH);
 
 	/* Send Msg to PE for deleting all the TDLS peers */
 	tdls_delete_all_tdls_peers(vdev, tdls_soc);

+ 75 - 26
components/tdls/core/src/wlan_tdls_main.c

@@ -24,7 +24,6 @@
  */
 
 #include "wlan_tdls_main.h"
-#include "wlan_tdls_cmds_process.h"
 #include "wlan_tdls_peer.h"
 #include "wlan_tdls_ct.h"
 #include "wlan_tdls_mgmt.h"
@@ -92,10 +91,20 @@ static char *tdls_get_cmd_type_str(enum tdls_command_type cmd_type)
 		return "TDLS_CMD_TEARDOWN_LINKS";
 	case TDLS_NOTIFY_RESET_ADAPTERS:
 		return "TDLS_NOTIFY_RESET_ADAPTERS";
+	case TDLS_CMD_GET_ALL_PEERS:
+		return "TDLS_CMD_GET_ALL_PEERS";
 	case TDLS_CMD_ANTENNA_SWITCH:
 		return "TDLS_CMD_ANTENNA_SWITCH";
 	case TDLS_CMD_START_BSS:
 		return "TDLS_CMD_START_BSS";
+	case TDLS_CMD_SET_OFFCHANMODE:
+		return "TDLS_CMD_SET_OFFCHANMODE";
+	case TDLS_CMD_SET_OFFCHANNEL:
+		return "TDLS_CMD_SET_OFFCHANNEL";
+	case TDLS_CMD_SET_SECOFFCHANOFFSET:
+		return "TDLS_CMD_SET_SECOFFCHANOFFSET";
+	case TDLS_DELETE_ALL_PEERS_INDICATION:
+		return "TDLS_DELETE_ALL_PEERS_INDICATION";
 	default:
 		return "Invalid TDLS command";
 	}
@@ -646,8 +655,7 @@ QDF_STATUS tdls_process_cmd(struct scheduler_msg *msg)
 		break;
 	case TDLS_CMD_SESSION_DECREMENT:
 		tdls_process_decrement_active_session(msg->bodyptr);
-		/* take decision on connection tracker */
-		fallthrough;
+		break;
 	case TDLS_CMD_SESSION_INCREMENT:
 		tdls_process_policy_mgr_notification(msg->bodyptr);
 		break;
@@ -1131,7 +1139,8 @@ set_state:
 	else
 		tdls_implicit_disable(tdls_vdev_obj);
 
-	tdls_debug("enable_tdls_connection_tracker %d current_mode:%d feature_flags:0x%x",
+	tdls_debug("vdev:%d enable_tdls_connection_tracker %d current_mode:%d feature_flags:0x%x",
+		   wlan_vdev_get_id(vdev),
 		   tdls_soc_obj->enable_tdls_connection_tracker,
 		   tdls_soc_obj->tdls_current_mode, tdls_feature_flags);
 }
@@ -1139,33 +1148,45 @@ set_state:
 QDF_STATUS
 tdls_process_policy_mgr_notification(struct wlan_objmgr_psoc *psoc)
 {
-	struct wlan_objmgr_vdev *tdls_obj_vdev;
+	struct wlan_objmgr_vdev *tdls_vdev;
+	struct tdls_vdev_priv_obj *tdls_priv_vdev;
+	struct tdls_soc_priv_obj *tdls_priv_soc;
+	QDF_STATUS status;
 
 	if (!psoc) {
 		tdls_err("psoc: %pK", psoc);
 		return QDF_STATUS_E_NULL_VALUE;
 	}
 
-	tdls_obj_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
-	if (!tdls_obj_vdev) {
+	tdls_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
+	if (!tdls_vdev) {
 		tdls_err("No TDLS vdev");
 		return QDF_STATUS_E_NULL_VALUE;
 	}
 
-	if (!tdls_check_is_tdls_allowed(tdls_obj_vdev)) {
-		tdls_disable_offchan_and_teardown_links(tdls_obj_vdev);
-		wlan_objmgr_vdev_release_ref(tdls_obj_vdev, WLAN_TDLS_NB_ID);
+	status = tdls_get_vdev_objects(tdls_vdev, &tdls_priv_vdev,
+				       &tdls_priv_soc);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_debug("TDLS vdev objects NULL");
+		wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_NB_ID);
 		return QDF_STATUS_E_NULL_VALUE;
 	}
 
-	tdls_set_tdls_offchannelmode(tdls_obj_vdev, ENABLE_CHANSWITCH);
+	if (!tdls_check_is_tdls_allowed(tdls_vdev)) {
+		tdls_disable_offchan_and_teardown_links(tdls_vdev);
+		tdls_debug("Disable the tdls in FW due to concurrency");
+		wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_NB_ID);
+		return QDF_STATUS_E_NULL_VALUE;
+	}
 
-	tdls_debug("enter ");
-	tdls_set_ct_mode(psoc, tdls_obj_vdev);
+	tdls_debug("vdev:%d enter", wlan_vdev_get_id(tdls_vdev));
 
-	wlan_objmgr_vdev_release_ref(tdls_obj_vdev, WLAN_TDLS_NB_ID);
+	tdls_set_tdls_offchannelmode(tdls_vdev, ENABLE_CHANSWITCH);
+	tdls_set_ct_mode(psoc, tdls_vdev);
 
+	wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_NB_ID);
 	tdls_debug("exit ");
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -1278,10 +1299,6 @@ struct wlan_objmgr_vdev *tdls_get_vdev(struct wlan_objmgr_psoc *psoc,
 {
 	uint32_t vdev_id;
 
-	if (policy_mgr_get_connection_count_with_mlo(psoc) > 1 &&
-	    !tdls_is_concurrency_allowed(psoc))
-		return NULL;
-
 	vdev_id = policy_mgr_mode_specific_vdev_id(psoc, PM_STA_MODE);
 	if (WLAN_INVALID_VDEV_ID != vdev_id)
 		return wlan_objmgr_get_vdev_by_id_from_psoc(psoc, vdev_id,
@@ -1500,6 +1517,43 @@ done:
 	return;
 }
 
+void tdls_process_enable_for_vdev(struct wlan_objmgr_vdev *vdev)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct tdls_vdev_priv_obj *tdls_vdev_obj;
+	struct tdls_soc_priv_obj *tdls_soc_obj;
+	struct tdls_sta_notify_params *ap_cap;
+	enum QDF_OPMODE opmode;
+	QDF_STATUS status;
+	uint8_t sta_count;
+
+	psoc = wlan_vdev_get_psoc(vdev);
+	if (!psoc)
+		return;
+
+	sta_count = policy_mgr_mode_specific_connection_count(psoc, PM_STA_MODE,
+							      NULL);
+	opmode = wlan_vdev_mlme_get_opmode(vdev);
+	if (opmode == QDF_P2P_CLIENT_MODE && sta_count) {
+		tdls_debug("STA + P2P concurrency. Don't allow TDLS on P2P vdev");
+		return;
+	}
+
+	status = tdls_get_vdev_objects(vdev, &tdls_vdev_obj, &tdls_soc_obj);
+	if (QDF_IS_STATUS_ERROR(status))
+		return;
+
+	ap_cap = &tdls_vdev_obj->tdls_caps;
+	if (!tdls_soc_obj->tdls_disable_in_progress)
+		tdls_send_update_to_fw(tdls_vdev_obj, tdls_soc_obj,
+				       ap_cap->tdls_prohibited,
+				       ap_cap->tdls_chan_swit_prohibited, true,
+				       ap_cap->session_id);
+
+	/* check and set the connection tracker */
+	tdls_set_ct_mode(tdls_soc_obj->soc, vdev);
+}
+
 static QDF_STATUS
 tdls_process_sta_connect(struct tdls_sta_notify_params *notify)
 {
@@ -1515,15 +1569,10 @@ tdls_process_sta_connect(struct tdls_sta_notify_params *notify)
 	if (QDF_IS_STATUS_ERROR(status))
 		return QDF_STATUS_E_INVAL;
 
-	/* Association event */
-	if (!tdls_soc_obj->tdls_disable_in_progress)
-		tdls_send_update_to_fw(tdls_vdev_obj, tdls_soc_obj,
-				       notify->tdls_prohibited,
-				       notify->tdls_chan_swit_prohibited, true,
-				       notify->session_id);
+	if (tdls_vdev_obj)
+		tdls_vdev_obj->tdls_caps = *notify;
 
-	/* check and set the connection tracker */
-	tdls_set_ct_mode(tdls_soc_obj->soc, notify->vdev);
+	tdls_process_enable_for_vdev(notify->vdev);
 
 	return QDF_STATUS_SUCCESS;
 }

+ 11 - 0
components/tdls/core/src/wlan_tdls_main.h

@@ -278,6 +278,7 @@ struct tdls_soc_priv_obj {
  * @discovery_sent_cnt: discovery sent count
  * @curr_candidate: current candidate
  * @ct_peer_table: linear mac address table for counting the packets
+ * @tdls_caps: AP TDLS capabilities
  * @valid_mac_entries: number of valid mac entry in @ct_peer_mac_table
  * @rx_mgmt: the pointer of rx mgmt info
  * @link_score: select tdls vdev per the score
@@ -297,6 +298,7 @@ struct tdls_vdev_priv_obj {
 	struct tdls_peer *curr_candidate;
 	struct tdls_conn_tracker_mac_table
 			ct_peer_table[WLAN_TDLS_CT_TABLE_SIZE];
+	struct tdls_sta_notify_params tdls_caps;
 	uint8_t valid_mac_entries;
 	struct tdls_rx_mgmt_frame *rx_mgmt;
 	uint32_t link_score;
@@ -606,6 +608,15 @@ QDF_STATUS tdls_set_operation_mode(struct tdls_set_mode_params *tdls_set_mode);
  */
 QDF_STATUS tdls_notify_sta_connect(struct tdls_sta_notify_params *notify);
 
+/**
+ * tdls_process_enable_for_vdev() - Enable TDLS in firmware and activate the
+ * connection tracker
+ * @vdev: Pointer to vdev object
+ *
+ * Return: None
+ */
+void tdls_process_enable_for_vdev(struct wlan_objmgr_vdev *vdev);
+
 /**
  * tdls_notify_sta_disconnect() - Update tdls state for every
  * disconnect event.

+ 0 - 1
components/tdls/core/src/wlan_tdls_mgmt.c

@@ -29,7 +29,6 @@
 #include "wlan_mgmt_txrx_utils_api.h"
 #include "wlan_tdls_peer.h"
 #include "wlan_tdls_ct.h"
-#include "wlan_tdls_cmds_process.h"
 #include "wlan_tdls_mgmt.h"
 #include "wlan_policy_mgr_api.h"
 #include <wlan_reg_services_api.h>

+ 2 - 0
components/tdls/core/src/wlan_tdls_mgmt.h

@@ -26,6 +26,8 @@
 #ifndef _WLAN_TDLS_MGMT_H_
 #define _WLAN_TDLS_MGMT_H_
 
+#include "wlan_tdls_cmds_process.h"
+
 #define TDLS_PUBLIC_ACTION_FRAME_OFFSET 24
 /* TDLS_PUBLIC_ACTION_FRAME_OFFSET(category[1]) + action[1] + dialog[1]
  * + cap[2]

+ 77 - 5
components/tdls/dispatcher/inc/wlan_tdls_api.h

@@ -63,12 +63,14 @@ void wlan_tdls_get_features_info(struct wlan_objmgr_psoc *psoc,
 QDF_STATUS wlan_tdls_teardown_links(struct wlan_objmgr_psoc *psoc);
 
 /**
- * wlan_tdls_teardown_links_sync() - teardown all the TDLS links
+ * wlan_tdls_check_and_teardown_links_sync() - teardown all the TDLS links
  * @psoc: psoc object
+ * @vdev: Vdev object pointer
  *
  * Return: None
  */
-void wlan_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc);
+void wlan_tdls_check_and_teardown_links_sync(struct wlan_objmgr_psoc *psoc,
+					     struct wlan_objmgr_vdev *vdev);
 
 /**
  * wlan_tdls_notify_sta_disconnect() - notify sta disconnect
@@ -127,10 +129,62 @@ void wlan_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev,
 /**
  * wlan_tdls_notify_start_bss() - Notify TDLS module on start bss
  * @psoc: Pointer to PSOC object
+ * @vdev: Vdev object pointer
  *
  * Return: None
  */
-void wlan_tdls_notify_start_bss(struct wlan_objmgr_psoc *psoc);
+void wlan_tdls_notify_start_bss(struct wlan_objmgr_psoc *psoc,
+				struct wlan_objmgr_vdev *vdev);
+
+#ifdef WLAN_FEATURE_TDLS_CONCURRENCIES
+/**
+ * wlan_tdls_notify_channel_switch_complete() - Notify TDLS module about the
+ * channel switch completion
+ * @psoc: Pointer to PSOC object
+ * @vdev_id: vdev id
+ *
+ * Return: None
+ */
+void wlan_tdls_notify_channel_switch_complete(struct wlan_objmgr_psoc *psoc,
+					      uint8_t vdev_id);
+
+/**
+ * wlan_tdls_notify_channel_switch_start() - Process channel switch start
+ * for SAP/P2P GO vdev. For STA vdev, TDLS teardown happens, so explicit
+ * disable off channel is not required.
+ * @psoc: Pointer to PSOC object
+ * @vdev: Pointer to current vdev on which CSA is triggered
+ *
+ * Return: None
+ */
+void wlan_tdls_notify_channel_switch_start(struct wlan_objmgr_psoc *psoc,
+					   struct wlan_objmgr_vdev *vdev);
+
+/**
+ * wlan_tdls_handle_p2p_client_connect() - Handle P2P Client connect start
+ * @psoc: Pointer to PSOC object
+ * @vdev: Pointer to P2P client vdev
+ *
+ * Return: None
+ */
+void wlan_tdls_handle_p2p_client_connect(struct wlan_objmgr_psoc *psoc,
+					 struct wlan_objmgr_vdev *vdev);
+#else
+static inline
+void wlan_tdls_notify_channel_switch_complete(struct wlan_objmgr_psoc *psoc,
+					      uint8_t vdev_id)
+{}
+
+static inline
+void wlan_tdls_notify_channel_switch_start(struct wlan_objmgr_psoc *psoc,
+					   struct wlan_objmgr_vdev *vdev)
+{}
+
+static inline
+void wlan_tdls_handle_p2p_client_connect(struct wlan_objmgr_psoc *psoc,
+					 struct wlan_objmgr_vdev *vdev)
+{}
+#endif /* WLAN_FEATURE_TDLS_CONCURRENCIES */
 #else
 
 #ifdef FEATURE_SET
@@ -152,7 +206,9 @@ static inline QDF_STATUS wlan_tdls_teardown_links(struct wlan_objmgr_psoc *psoc)
 	return QDF_STATUS_SUCCESS;
 }
 
-static inline void wlan_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc)
+static inline void
+wlan_tdls_check_and_teardown_links_sync(struct wlan_objmgr_psoc *psoc,
+					struct wlan_objmgr_vdev *vdev)
 {}
 
 static inline
@@ -181,7 +237,23 @@ void wlan_tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev,
 }
 
 static inline
-void wlan_tdls_notify_start_bss(struct wlan_objmgr_psoc *psoc)
+void wlan_tdls_notify_start_bss(struct wlan_objmgr_psoc *psoc,
+				struct wlan_objmgr_vdev *vdev)
+{}
+
+static inline
+void wlan_tdls_notify_channel_switch_complete(struct wlan_objmgr_psoc *psoc,
+					      uint8_t vdev_id)
+{}
+
+static inline
+void wlan_tdls_notify_channel_switch_start(struct wlan_objmgr_psoc *psoc,
+					   struct wlan_objmgr_vdev *vdev)
+{}
+
+static inline
+void wlan_tdls_handle_p2p_client_connect(struct wlan_objmgr_psoc *psoc,
+					 struct wlan_objmgr_vdev *vdev)
 {}
 #endif
 #endif

+ 5 - 2
components/tdls/dispatcher/inc/wlan_tdls_ucfg_api.h

@@ -298,10 +298,12 @@ QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_psoc *psoc);
 /**
  * ucfg_tdls_teardown_links_sync() - teardown all TDLS links.
  * @psoc: psoc object
+ * @vdev: Vdev object pointer
  *
  * Return: None
  */
-void ucfg_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc);
+void ucfg_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc,
+				   struct wlan_objmgr_vdev *vdev);
 
 /**
  * ucfg_tdls_notify_reset_adapter() - notify reset adapter
@@ -534,7 +536,8 @@ QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_psoc *psoc)
 }
 
 static inline
-void ucfg_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc)
+void ucfg_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc,
+				   struct wlan_objmgr_vdev *vdev)
 {
 }
 

+ 139 - 8
components/tdls/dispatcher/src/wlan_tdls_api.c

@@ -88,17 +88,18 @@ bool wlan_tdls_is_fw_11be_mlo_capable(struct wlan_objmgr_psoc *psoc)
 }
 #endif
 
-void  wlan_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc)
+static void  wlan_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc,
+					   struct wlan_objmgr_vdev *vdev)
 {
 	struct tdls_vdev_priv_obj *vdev_priv_obj;
 	QDF_STATUS status;
-	struct wlan_objmgr_vdev *vdev;
+	struct wlan_objmgr_vdev *tdls_vdev;
 
-	vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
+	tdls_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
 	if (!vdev)
 		return;
 
-	vdev_priv_obj = wlan_vdev_get_tdls_vdev_obj(vdev);
+	vdev_priv_obj = wlan_vdev_get_tdls_vdev_obj(tdls_vdev);
 	if (!vdev_priv_obj) {
 		tdls_err("vdev priv is NULL");
 		goto release_ref;
@@ -112,7 +113,8 @@ void  wlan_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc)
 		goto release_ref;
 	}
 
-	tdls_debug("Wait for tdls teardown completion. Timeout %u ms",
+	tdls_debug("vdev:%d Wait for tdls teardown completion. Timeout %u ms",
+		   wlan_vdev_get_id(tdls_vdev),
 		   WAIT_TIME_FOR_TDLS_TEARDOWN_LINKS);
 
 	status = qdf_wait_for_event_completion(
@@ -126,10 +128,34 @@ void  wlan_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc)
 	tdls_debug("TDLS teardown completion status %d ", status);
 
 release_ref:
-	wlan_objmgr_vdev_release_ref(vdev,
+	wlan_objmgr_vdev_release_ref(tdls_vdev,
 				     WLAN_TDLS_NB_ID);
 }
 
+void  wlan_tdls_check_and_teardown_links_sync(struct wlan_objmgr_psoc *psoc,
+					      struct wlan_objmgr_vdev *vdev)
+{
+	uint8_t sta_count;
+	enum QDF_OPMODE opmode;
+	bool tgt_tdls_concurrency_supported;
+
+	tgt_tdls_concurrency_supported =
+		wlan_psoc_nif_fw_ext2_cap_get(psoc,
+					      WLAN_TDLS_CONCURRENCIES_SUPPORT);
+	/* Don't initiate teardown in case of STA + P2P Client concurreny */
+	sta_count = policy_mgr_mode_specific_connection_count(psoc,
+							      PM_STA_MODE,
+							      NULL);
+	opmode = wlan_vdev_mlme_get_opmode(vdev);
+	if (tgt_tdls_concurrency_supported && opmode == QDF_P2P_CLIENT_MODE &&
+	    sta_count) {
+		tdls_debug("Don't teardown tdls for existing STA vdev");
+		return;
+	}
+
+	wlan_tdls_teardown_links_sync(psoc, vdev);
+}
+
 #ifdef WLAN_FEATURE_TDLS_CONCURRENCIES
 static void wlan_tdls_handle_sap_start(struct wlan_objmgr_psoc *psoc)
 {
@@ -147,20 +173,125 @@ static void wlan_tdls_handle_sap_start(struct wlan_objmgr_psoc *psoc)
 		return;
 	}
 }
+
+void wlan_tdls_notify_channel_switch_complete(struct wlan_objmgr_psoc *psoc,
+					      uint8_t vdev_id)
+{
+	struct wlan_objmgr_vdev *tdls_vdev;
+	struct tdls_vdev_priv_obj *tdls_vdev_priv;
+	struct tdls_soc_priv_obj *tdls_soc_priv;
+	QDF_STATUS status;
+
+	tdls_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
+	if (!tdls_vdev)
+		return;
+
+	status = tdls_get_vdev_objects(tdls_vdev, &tdls_vdev_priv,
+				       &tdls_soc_priv);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("Failed to get TDLS objects");
+		goto exit;
+	}
+
+	/*
+	 * Channel Switch can cause SCC -> MCC switch on
+	 * STA vdev. Disable TDLS if CSA causes STA vdev to be in MCC with
+	 * other vdev.
+	 */
+	if (!tdls_is_concurrency_allowed(psoc)) {
+		tdls_disable_offchan_and_teardown_links(tdls_vdev);
+		tdls_debug("Disable the tdls in FW after CSA");
+	} else {
+		tdls_process_enable_for_vdev(tdls_vdev);
+		tdls_set_tdls_offchannelmode(tdls_vdev, ENABLE_CHANSWITCH);
+	}
+
+exit:
+	wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_NB_ID);
+}
+
+static QDF_STATUS
+wlan_tdls_post_set_off_channel_mode(struct wlan_objmgr_psoc *psoc,
+				    uint8_t off_chan_mode)
+{
+	struct wlan_objmgr_vdev *tdls_vdev;
+	struct tdls_vdev_priv_obj *tdls_vdev_priv;
+	struct tdls_soc_priv_obj *tdls_soc_priv;
+	struct tdls_set_offchanmode *req;
+	struct scheduler_msg msg = {0};
+	QDF_STATUS status;
+
+	tdls_vdev = tdls_get_vdev(psoc, WLAN_TDLS_NB_ID);
+	if (!tdls_vdev)
+		return QDF_STATUS_E_FAILURE;
+
+	status = tdls_get_vdev_objects(tdls_vdev, &tdls_vdev_priv,
+				       &tdls_soc_priv);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_NB_ID);
+		tdls_err("Failed to get TDLS objects");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	req = qdf_mem_malloc(sizeof(*req));
+	if (!req) {
+		wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_NB_ID);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	req->offchan_mode = off_chan_mode;
+	req->vdev = tdls_vdev;
+	req->callback = wlan_tdls_offchan_parms_callback;
+
+	msg.callback = tdls_process_cmd;
+	msg.type = TDLS_CMD_SET_OFFCHANMODE;
+	msg.bodyptr = req;
+
+	status = scheduler_post_message(QDF_MODULE_ID_TDLS, QDF_MODULE_ID_TDLS,
+					QDF_MODULE_ID_TARGET_IF, &msg);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("post set offchanmode msg fail");
+		wlan_objmgr_vdev_release_ref(tdls_vdev, WLAN_TDLS_NB_ID);
+		qdf_mem_free(req);
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+void wlan_tdls_notify_channel_switch_start(struct wlan_objmgr_psoc *psoc,
+					   struct wlan_objmgr_vdev *vdev)
+{
+	wlan_tdls_post_set_off_channel_mode(psoc, DISABLE_ACTIVE_CHANSWITCH);
+}
+
+void wlan_tdls_handle_p2p_client_connect(struct wlan_objmgr_psoc *psoc,
+					 struct wlan_objmgr_vdev *vdev)
+{
+	if (policy_mgr_get_connection_count(psoc) < 2)
+		return;
+
+	/*
+	 * Disable TDLS off-channel when P2P CLI comes up as
+	 * 3rd interface. It will be re-enabled based on the
+	 * concurrency once P2P connection is complete
+	 */
+	wlan_tdls_post_set_off_channel_mode(psoc, DISABLE_ACTIVE_CHANSWITCH);
+}
 #else
 static inline void
 wlan_tdls_handle_sap_start(struct wlan_objmgr_psoc *psoc)
 {}
 #endif
 
-void wlan_tdls_notify_start_bss(struct wlan_objmgr_psoc *psoc)
+void wlan_tdls_notify_start_bss(struct wlan_objmgr_psoc *psoc,
+				struct wlan_objmgr_vdev *vdev)
 {
 	if (tdls_is_concurrency_allowed(psoc)) {
 		wlan_tdls_handle_sap_start(psoc);
 		return;
 	}
 
-	wlan_tdls_teardown_links_sync(psoc);
+	wlan_tdls_check_and_teardown_links_sync(psoc, vdev);
 }
 
 static QDF_STATUS tdls_notify_flush_cb(struct scheduler_msg *msg)

+ 3 - 2
components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c

@@ -985,9 +985,10 @@ QDF_STATUS ucfg_tdls_responder(struct tdls_set_responder_req *req)
 	return status;
 }
 
-void ucfg_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc)
+void ucfg_tdls_teardown_links_sync(struct wlan_objmgr_psoc *psoc,
+				   struct wlan_objmgr_vdev *vdev)
 {
-	return wlan_tdls_teardown_links_sync(psoc);
+	return wlan_tdls_check_and_teardown_links_sync(psoc, vdev);
 }
 
 QDF_STATUS ucfg_tdls_teardown_links(struct wlan_objmgr_psoc *psoc)

+ 15 - 0
core/hdd/src/wlan_hdd_assoc.c

@@ -2202,7 +2202,22 @@ static void hdd_roam_channel_switch_handler(struct hdd_adapter *adapter,
 		policy_mgr_check_concurrent_intf_and_restart_sap(
 		   hdd_ctx->psoc,
 		   !!adapter->deflink->session.ap.sap_config.acs_cfg.acs_mode);
+
 	wlan_twt_concurrency_update(hdd_ctx);
+	if (adapter->device_mode == QDF_STA_MODE ||
+	    adapter->device_mode == QDF_P2P_CLIENT_MODE) {
+		vdev = hdd_objmgr_get_vdev_by_user(adapter,
+						   WLAN_OSIF_ID);
+		if (!vdev)
+			return;
+
+		status = ucfg_if_mgr_deliver_event(
+				vdev, WLAN_IF_MGR_EV_STA_CSA_COMPLETE, NULL);
+		if (QDF_IS_STATUS_ERROR(status))
+			hdd_debug("Failed to deliver CSA complete evt");
+
+		hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
+	}
 }
 
 #ifdef WLAN_FEATURE_HOST_ROAM

+ 11 - 4
core/hdd/src/wlan_hdd_power.c

@@ -2635,10 +2635,17 @@ static int __wlan_hdd_cfg80211_suspend_wlan(struct wiphy *wiphy,
 								  hapd_state,
 								  &params);
 			}
-		} else if (QDF_TDLS_MODE == adapter->device_mode) {
-			if (ucfg_pmo_get_disconnect_sap_tdls_in_wow(
-								hdd_ctx->psoc))
-				ucfg_tdls_teardown_links_sync(hdd_ctx->psoc);
+		} else if (QDF_TDLS_MODE == adapter->device_mode &&
+			   ucfg_pmo_get_disconnect_sap_tdls_in_wow(
+							hdd_ctx->psoc)) {
+			vdev = hdd_objmgr_get_vdev_by_user(adapter,
+							   WLAN_TDLS_NB_ID);
+			if (vdev) {
+				ucfg_tdls_teardown_links_sync(hdd_ctx->psoc,
+							      vdev);
+				hdd_objmgr_put_vdev_by_user(vdev,
+							    WLAN_TDLS_NB_ID);
+			}
 		}
 		hdd_adapter_dev_put_debug(adapter, dbgid);
 	}

+ 1 - 1
core/sap/src/sap_api_link_cntl.c

@@ -1211,7 +1211,7 @@ QDF_STATUS wlansap_roam_callback(void *ctx,
 	case eCSR_ROAM_SET_CHANNEL_RSP:
 		sap_debug("Received set channel response");
 		ucfg_if_mgr_deliver_event(sap_ctx->vdev,
-					  WLAN_IF_MGR_EV_CSA_COMPLETE,
+					  WLAN_IF_MGR_EV_AP_CSA_COMPLETE,
 					  NULL);
 		break;
 	case eCSR_ROAM_CAC_COMPLETE_IND:

+ 13 - 2
core/sme/src/csr/csr_api_roam.c

@@ -7134,13 +7134,24 @@ QDF_STATUS csr_send_ext_change_freq(struct mac_context *mac_ctx,
 	return status;
 }
 
-QDF_STATUS csr_csa_restart(struct mac_context *mac_ctx, uint8_t session_id)
+QDF_STATUS csr_csa_restart(struct mac_context *mac_ctx, uint8_t vdev_id)
 {
 	QDF_STATUS status;
+	struct wlan_objmgr_vdev *vdev;
 	struct scheduler_msg message = {0};
 
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(mac_ctx->psoc, vdev_id,
+						    WLAN_LEGACY_MAC_ID);
+	if (!vdev) {
+		sme_err("VDEV not found for vdev id: %d", vdev_id);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if_mgr_deliver_event(vdev, WLAN_IF_MGR_EV_AP_CSA_START, NULL);
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_LEGACY_MAC_ID);
+
 	/* Serialize the req through MC thread */
-	message.bodyval = session_id;
+	message.bodyval = vdev_id;
 	message.type    = eWNI_SME_CSA_RESTART_REQ;
 	status = scheduler_post_message(QDF_MODULE_ID_SME, QDF_MODULE_ID_PE,
 					QDF_MODULE_ID_PE, &message);