Prechádzať zdrojové kódy

qcacmn: Add connection tracker to TDLS UMAC

Add connection tracker support in TDLS UMAC
component

Change-Id: I725b579ab9f393d5247651bfe53df6b4a3f53649
CRs-Fixed: 2034220
Kabilan Kannan 8 rokov pred
rodič
commit
ee01ebfc20

+ 1 - 92
umac/tdls/core/src/wlan_tdls_cmds_process.c

@@ -25,6 +25,7 @@
 #include <wlan_serialization_api.h>
 #include "wlan_tdls_main.h"
 #include "wlan_tdls_peer.h"
+#include "wlan_tdls_ct.h"
 #include "wlan_tdls_cmds_process.h"
 #include "wlan_tdls_tgt_api.h"
 
@@ -1628,63 +1629,6 @@ static const char *tdls_evt_to_str(enum tdls_event_msg_type type)
 	}
 }
 
-static void
-tdls_implicit_send_discovery_request(struct tdls_vdev_priv_obj *vdev_obj)
-{
-	struct tdls_soc_priv_obj *soc_obj;
-	struct tdls_peer *curr_peer, *temp_peer;
-	struct tdls_osif_indication ind;
-
-	if (!vdev_obj) {
-		tdls_err("vdev_obj is NULL");
-		return;
-	}
-
-	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
-	if (!soc_obj) {
-		tdls_err("soc_obj is NULL");
-		return;
-	}
-
-	curr_peer = vdev_obj->curr_candidate;
-	if (!curr_peer) {
-		tdls_err("curr_peer is NULL");
-		return;
-	}
-
-	temp_peer = tdls_is_progress(vdev_obj, NULL, 0);
-	if (temp_peer) {
-		tdls_notice(QDF_MAC_ADDRESS_STR " ongoing. pre_setup ignored",
-			    QDF_MAC_ADDR_ARRAY(temp_peer->peer_mac.bytes));
-		goto done;
-	}
-
-	if (TDLS_CAP_UNKNOWN != curr_peer->tdls_support)
-		tdls_set_peer_link_status(curr_peer, TDLS_LINK_DISCOVERING,
-					  TDLS_LINK_SUCCESS);
-
-	tdls_debug("Implicit TDLS, Send Discovery request event");
-
-	qdf_mem_copy(ind.peer_mac, curr_peer->peer_mac.bytes,
-		     QDF_MAC_ADDR_SIZE);
-	ind.vdev = vdev_obj->vdev;
-
-	if (soc_obj->tdls_event_cb)
-		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
-				       TDLS_EVENT_DISCOVERY_REQ, &ind);
-
-	vdev_obj->discovery_sent_cnt++;
-	/*TODO restart peer discovery timeout*/
-
-	tdls_debug("discovery count %u, timeout %u msec",
-		   vdev_obj->discovery_sent_cnt,
-		   vdev_obj->threshold_config.tx_period_t -
-		   TDLS_DISCOVERY_TIMEOUT_BEFORE_UPDATE);
-done:
-	vdev_obj->curr_candidate = NULL;
-	vdev_obj->magic = 0;
-}
-
 QDF_STATUS tdls_process_should_discover(struct wlan_objmgr_vdev *vdev,
 					struct tdls_event_info *evt)
 {
@@ -1743,41 +1687,6 @@ QDF_STATUS tdls_process_should_discover(struct wlan_objmgr_vdev *vdev,
 	return QDF_STATUS_SUCCESS;
 }
 
-static void
-tdls_indicate_teardown(struct tdls_vdev_priv_obj *vdev_obj,
-		       struct tdls_peer *curr_peer, uint16_t reason)
-{
-	struct tdls_soc_priv_obj *soc_obj;
-	struct tdls_osif_indication ind;
-
-	soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
-	if (!soc_obj || !vdev_obj || !curr_peer) {
-		tdls_err("soc_obj: %p, vdev_obj: %p, curr_peer: %p",
-			 soc_obj, vdev_obj, curr_peer);
-		return;
-	}
-
-	if (TDLS_LINK_CONNECTED != curr_peer->link_status) {
-		tdls_debug("peer not connected");
-		return;
-	}
-
-	tdls_set_peer_link_status(curr_peer, TDLS_LINK_TEARING,
-				  TDLS_LINK_UNSPECIFIED);
-
-	tdls_debug("Teardown peer " QDF_MAC_ADDRESS_STR "reason %d",
-		   QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes), reason);
-
-	qdf_mem_copy(ind.peer_mac, curr_peer->peer_mac.bytes,
-		     QDF_MAC_ADDR_SIZE);
-	ind.reason = reason;
-	ind.vdev = vdev_obj->vdev;
-
-	if (soc_obj->tdls_event_cb)
-		soc_obj->tdls_event_cb(soc_obj->tdls_evt_cb_data,
-				       TDLS_EVENT_TEARDOWN_REQ, &ind);
-}
-
 QDF_STATUS tdls_process_should_teardown(struct wlan_objmgr_vdev *vdev,
 					struct tdls_event_info *evt)
 {

+ 894 - 0
umac/tdls/core/src/wlan_tdls_ct.c

@@ -21,3 +21,897 @@
  *
  * TDLS connection tracker function definitions
  */
+
+#include "wlan_tdls_main.h"
+#include "wlan_tdls_peer.h"
+#include "wlan_tdls_ct.h"
+
+bool tdls_is_vdev_connected(struct wlan_objmgr_vdev *vdev)
+{
+	struct wlan_objmgr_peer *peer;
+	enum wlan_peer_state peer_state;
+
+	wlan_vdev_obj_lock(vdev);
+	peer = wlan_vdev_get_bsspeer(vdev);
+	wlan_vdev_obj_unlock(vdev);
+
+	if (!peer) {
+		tdls_err("peer is null");
+		return false;
+	}
+
+	wlan_peer_obj_lock(peer);
+	peer_state = wlan_peer_mlme_get_state(peer);
+	wlan_peer_obj_unlock(peer);
+
+	if (peer_state != WLAN_ASSOC_STATE) {
+		tdls_err("peer state: %d", peer_state);
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * tdls_peer_reset_discovery_processed() - reset discovery status
+ * @tdls_vdev: TDLS vdev object
+ *
+ * This function resets discovery processing bit for all TDLS peers
+ *
+ * Caller has to take the lock before calling this function
+ *
+ * Return: 0
+ */
+static int32_t tdls_peer_reset_discovery_processed(
+					struct tdls_vdev_priv_obj *tdls_vdev)
+{
+	int i;
+	qdf_list_t *head;
+	qdf_list_node_t *p_node;
+	struct tdls_peer *peer;
+	QDF_STATUS status;
+
+	tdls_vdev->discovery_peer_cnt = 0;
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &tdls_vdev->peer_list[i];
+		status = qdf_list_peek_front(head, &p_node);
+		while (QDF_IS_STATUS_SUCCESS(status)) {
+			peer = qdf_container_of(p_node, struct tdls_peer, node);
+			peer->discovery_processed = 0;
+			status = qdf_list_peek_next(head, p_node, &p_node);
+		}
+	}
+
+	return 0;
+}
+
+void tdls_discovery_timeout_peer_cb(void *user_data)
+{
+	int i;
+	qdf_list_t *head;
+	qdf_list_node_t *p_node;
+	struct tdls_peer *peer;
+	QDF_STATUS status;
+	struct tdls_vdev_priv_obj *tdls_vdev;
+
+	if (!user_data) {
+		tdls_err("discovery time out data is null");
+		return;
+	}
+
+	tdls_vdev = (struct tdls_vdev_priv_obj *) user_data;
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &tdls_vdev->peer_list[i];
+		status = qdf_list_peek_front(head, &p_node);
+		while (QDF_IS_STATUS_SUCCESS(status)) {
+			peer = qdf_container_of(p_node, struct tdls_peer,
+						node);
+			if (TDLS_LINK_DISCOVERING != peer->link_status) {
+				status = qdf_list_peek_next(head, p_node,
+							    &p_node);
+				continue;
+			}
+			tdls_debug(QDF_MAC_ADDRESS_STR " to idle state",
+				    QDF_MAC_ADDR_ARRAY(peer->peer_mac.bytes));
+			tdls_set_peer_link_status(peer,
+						  TDLS_LINK_IDLE,
+						  TDLS_LINK_NOT_SUPPORTED);
+		}
+	}
+	tdls_vdev->discovery_sent_cnt = 0;
+
+	/* add tdls power save prohibited */
+
+	return;
+}
+
+/**
+ * tdls_reset_tx_rx() - reset tx/rx counters for all tdls peers
+ * @tdls_vdev: TDLS vdev object
+ *
+ * Caller has to take the TDLS lock before calling this function
+ *
+ * Return: Void
+ */
+static void tdls_reset_tx_rx(struct tdls_vdev_priv_obj *tdls_vdev)
+{
+	int i;
+	qdf_list_t *head;
+	qdf_list_node_t *p_node;
+	struct tdls_peer *peer;
+	QDF_STATUS status;
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &tdls_vdev->peer_list[i];
+		status = qdf_list_peek_front(head, &p_node);
+		while (QDF_IS_STATUS_SUCCESS(status)) {
+			peer = qdf_container_of(p_node, struct tdls_peer, node);
+			peer->tx_pkt = 0;
+			peer->rx_pkt = 0;
+			status = qdf_list_peek_next(head, p_node, &p_node);
+		}
+	}
+	return;
+}
+
+void tdls_implicit_disable(struct tdls_vdev_priv_obj *tdls_vdev)
+{
+	tdls_debug("Disable Implicit TDLS");
+	tdls_timers_stop(tdls_vdev);
+}
+
+/**
+ * tdls_implicit_enable() - enable implicit tdls triggering
+ * @tdls_vdev: TDLS vdev
+ *
+ * Return: Void
+ */
+void tdls_implicit_enable(struct tdls_vdev_priv_obj *tdls_vdev)
+{
+	tdls_debug("Enable Implicit TDLS");
+	if (!tdls_vdev)
+		return;
+
+	tdls_peer_reset_discovery_processed(tdls_vdev);
+	tdls_reset_tx_rx(tdls_vdev);
+	/* TODO check whether tdls power save prohibited */
+
+	/* Restart the connection tracker timer */
+	tdls_timer_restart(tdls_vdev->vdev, &tdls_vdev->peer_update_timer,
+			   tdls_vdev->threshold_config.tx_period_t);
+}
+
+/**
+ * tdls_ct_sampling_tx_rx() - collect tx/rx traffic sample
+ * @tdls_vdev_obj: tdls vdev object
+ * @tdls_soc_obj: tdls soc object
+ *
+ * Function to update data traffic information in tdls connection
+ * tracker data structure for connection tracker operation
+ *
+ * Return: None
+ */
+static void tdls_ct_sampling_tx_rx(struct tdls_vdev_priv_obj *tdls_vdev,
+				   struct tdls_soc_priv_obj *tdls_soc)
+{
+	struct tdls_peer *curr_peer;
+	uint8_t mac[QDF_MAC_ADDR_SIZE];
+	uint8_t mac_cnt;
+	uint8_t mac_entries;
+	struct tdls_conn_tracker_mac_table mac_table[WLAN_TDLS_CT_TABLE_SIZE];
+
+	qdf_spin_lock_bh(&tdls_soc->tdls_ct_spinlock);
+
+	if (0 == tdls_vdev->valid_mac_entries) {
+		qdf_spin_unlock_bh(&tdls_soc->tdls_ct_spinlock);
+		return;
+	}
+
+	mac_entries = tdls_vdev->valid_mac_entries;
+
+	qdf_mem_copy(mac_table, tdls_vdev->ct_peer_table,
+	       (sizeof(struct tdls_conn_tracker_mac_table)) * mac_entries);
+
+	qdf_mem_set(tdls_vdev->ct_peer_table, 0,
+	       (sizeof(struct tdls_conn_tracker_mac_table)) * mac_entries);
+
+	tdls_vdev->valid_mac_entries = 0;
+
+	qdf_spin_unlock_bh(&tdls_soc->tdls_ct_spinlock);
+
+	for (mac_cnt = 0; mac_cnt < mac_entries; mac_cnt++) {
+		qdf_mem_copy(mac, mac_table[mac_cnt].mac_address.bytes,
+		       QDF_MAC_ADDR_SIZE);
+		curr_peer = tdls_get_peer(tdls_vdev, mac);
+		if (NULL != curr_peer) {
+			curr_peer->tx_pkt =
+			mac_table[mac_cnt].tx_packet_cnt;
+			curr_peer->rx_pkt =
+			mac_table[mac_cnt].rx_packet_cnt;
+		}
+	}
+}
+
+void tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev,
+				 struct qdf_mac_addr *mac_addr)
+{
+	struct tdls_vdev_priv_obj *tdls_vdev_obj;
+	struct tdls_soc_priv_obj *tdls_soc_obj;
+	uint8_t mac_cnt;
+	uint8_t valid_mac_entries;
+	struct tdls_conn_tracker_mac_table *mac_table;
+
+	tdls_debug("enter ");
+	if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj,
+						   &tdls_soc_obj))
+		return;
+
+	if (!tdls_soc_obj->enable_tdls_connection_tracker)
+		return;
+
+	if (qdf_is_macaddr_group(mac_addr))
+		return;
+
+	if (qdf_mem_cmp(vdev->vdev_mlme.macaddr, mac_addr,
+		QDF_MAC_ADDR_SIZE) == 0)
+		return;
+
+	qdf_spin_lock_bh(&tdls_soc_obj->tdls_ct_spinlock);
+	valid_mac_entries = tdls_vdev_obj->valid_mac_entries;
+	mac_table = tdls_vdev_obj->ct_peer_table;
+
+	for (mac_cnt = 0; mac_cnt < valid_mac_entries; mac_cnt++) {
+		if (qdf_mem_cmp(mac_table[mac_cnt].mac_address.bytes,
+		    mac_addr, QDF_MAC_ADDR_SIZE) == 0) {
+			mac_table[mac_cnt].rx_packet_cnt++;
+			goto rx_cnt_return;
+		}
+	}
+
+	/* If we have more than 8 peers within 30 mins. we will
+	 *  stop tracking till the old entries are removed
+	 */
+	if (mac_cnt < WLAN_TDLS_CT_TABLE_SIZE) {
+		qdf_mem_copy(mac_table[mac_cnt].mac_address.bytes,
+		       mac_addr, QDF_MAC_ADDR_SIZE);
+		tdls_vdev_obj->valid_mac_entries = mac_cnt+1;
+		mac_table[mac_cnt].rx_packet_cnt = 1;
+	}
+
+rx_cnt_return:
+	qdf_spin_unlock_bh(&tdls_soc_obj->tdls_ct_spinlock);
+	tdls_debug("exit: rx pkt count %d ", mac_table[mac_cnt].rx_packet_cnt);
+	return;
+}
+
+void tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev,
+			    struct qdf_mac_addr *mac_addr)
+{
+	struct tdls_vdev_priv_obj *tdls_vdev_obj;
+	struct tdls_soc_priv_obj *tdls_soc_obj;
+	uint8_t mac_cnt;
+	uint8_t valid_mac_entries;
+	struct tdls_conn_tracker_mac_table *mac_table;
+
+	tdls_debug("enter ");
+	if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj,
+						   &tdls_soc_obj))
+		return;
+
+	if (!tdls_soc_obj->enable_tdls_connection_tracker)
+		return;
+
+	if (qdf_is_macaddr_group(mac_addr))
+		return;
+
+	if (qdf_mem_cmp(vdev->vdev_mlme.macaddr, mac_addr,
+		QDF_MAC_ADDR_SIZE) == 0)
+		return;
+
+	qdf_spin_lock_bh(&tdls_soc_obj->tdls_ct_spinlock);
+	mac_table = tdls_vdev_obj->ct_peer_table;
+
+	valid_mac_entries = tdls_vdev_obj->valid_mac_entries;
+
+	for (mac_cnt = 0; mac_cnt < valid_mac_entries; mac_cnt++) {
+		if (qdf_mem_cmp(mac_table[mac_cnt].mac_address.bytes,
+		    mac_addr, QDF_MAC_ADDR_SIZE) == 0) {
+			mac_table[mac_cnt].tx_packet_cnt++;
+			goto tx_cnt_return;
+		}
+	}
+
+	/* If we have more than 8 peers within 30 mins. we will
+	 *  stop tracking till the old entries are removed
+	 */
+	if (mac_cnt < WLAN_TDLS_CT_TABLE_SIZE) {
+		qdf_mem_copy(mac_table[mac_cnt].mac_address.bytes,
+			mac_addr, QDF_MAC_ADDR_SIZE);
+		mac_table[mac_cnt].tx_packet_cnt = 1;
+		tdls_vdev_obj->valid_mac_entries++;
+	}
+
+tx_cnt_return:
+	qdf_spin_unlock_bh(&tdls_soc_obj->tdls_ct_spinlock);
+	tdls_debug("exit: tx pkt count %d", mac_table[mac_cnt].tx_packet_cnt);
+	return;
+}
+
+void tdls_implicit_send_discovery_request(
+				struct tdls_vdev_priv_obj *tdls_vdev_obj)
+{
+	struct tdls_peer *curr_peer;
+	struct tdls_peer *temp_peer;
+	struct tdls_soc_priv_obj *tdls_psoc;
+	struct tdls_osif_indication tdls_ind;
+
+	if (NULL == tdls_vdev_obj) {
+		tdls_notice("tdls_vdev_obj is NULL");
+		return;
+	}
+
+	tdls_psoc = wlan_vdev_get_tdls_soc_obj(tdls_vdev_obj->vdev);
+
+	if (NULL == tdls_psoc) {
+		tdls_notice("tdls_psoc_obj is NULL");
+		return;
+	}
+
+	curr_peer = tdls_vdev_obj->curr_candidate;
+
+	if (NULL == curr_peer) {
+		tdls_err("curr_peer is NULL");
+		return;
+	}
+
+	/* This function is called in mutex_lock */
+	temp_peer = tdls_is_progress(tdls_vdev_obj, NULL, 0);
+	if (NULL != temp_peer) {
+		tdls_notice(QDF_MAC_ADDRESS_STR " ongoing. pre_setup ignored",
+			  QDF_MAC_ADDR_ARRAY(temp_peer->peer_mac.bytes));
+		goto done;
+	}
+
+	if (TDLS_CAP_UNKNOWN != curr_peer->tdls_support)
+		tdls_set_peer_link_status(curr_peer,
+					  TDLS_LINK_DISCOVERING,
+					  TDLS_LINK_SUCCESS);
+
+	qdf_mem_copy(tdls_ind.peer_mac, curr_peer->peer_mac.bytes,
+			QDF_MAC_ADDR_SIZE);
+
+	tdls_ind.vdev = tdls_vdev_obj->vdev;
+
+	tdls_debug("Implicit TDLS, Send Discovery request event");
+
+	tdls_psoc->tdls_event_cb(tdls_psoc->tdls_evt_cb_data,
+				 TDLS_EVENT_DISCOVERY_REQ, &tdls_ind);
+
+	tdls_vdev_obj->discovery_sent_cnt++;
+
+	tdls_timer_restart(tdls_vdev_obj->vdev,
+				&tdls_vdev_obj->peer_discovery_timer,
+				tdls_vdev_obj->threshold_config.tx_period_t -
+				TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE);
+
+	tdls_debug("discovery count %u timeout %u msec",
+		 tdls_vdev_obj->discovery_sent_cnt,
+		 tdls_vdev_obj->threshold_config.tx_period_t -
+		 TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE);
+done:
+	tdls_vdev_obj->curr_candidate = NULL;
+	tdls_vdev_obj->magic = 0;
+	return;
+}
+
+int tdls_recv_discovery_resp(struct tdls_vdev_priv_obj *tdls_vdev,
+				   const uint8_t *mac)
+{
+	struct tdls_peer *curr_peer;
+	struct tdls_soc_priv_obj *tdls_soc;
+	struct tdls_osif_indication indication;
+	struct tdls_config_params *tdls_cfg;
+	int status = 0;
+
+	if (!tdls_vdev)
+		return -EINVAL;
+
+	tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev);
+	if (NULL == tdls_soc) {
+		tdls_err("tdls soc is NULL");
+		return -EINVAL;
+	}
+
+	curr_peer = tdls_get_peer(tdls_vdev, mac);
+	if (NULL == curr_peer) {
+		tdls_err("curr_peer is NULL");
+		return -EINVAL;
+	}
+
+	if (tdls_vdev->discovery_sent_cnt)
+		tdls_vdev->discovery_sent_cnt--;
+
+	if (0 == tdls_vdev->discovery_sent_cnt)
+		qdf_mc_timer_stop(&tdls_vdev->peer_discovery_timer);
+
+	tdls_debug("Discovery(%u) Response from " QDF_MAC_ADDRESS_STR
+		   " link_status %d", tdls_vdev->discovery_sent_cnt,
+		   QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes),
+		   curr_peer->link_status);
+
+	tdls_cfg = &tdls_vdev->threshold_config;
+	if (TDLS_LINK_DISCOVERING == curr_peer->link_status) {
+		/* Since we are here, it means Throughput threshold is
+		 * already met. Make sure RSSI threshold is also met
+		 * before setting up TDLS link.
+		 */
+		if ((int32_t) curr_peer->rssi >
+		    (int32_t) tdls_cfg->rssi_trigger_threshold) {
+			tdls_set_peer_link_status(curr_peer,
+						TDLS_LINK_DISCOVERED,
+						TDLS_LINK_SUCCESS);
+			tdls_debug("Rssi Threshold met: " QDF_MAC_ADDRESS_STR
+				" rssi = %d threshold= %d",
+				QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes),
+				curr_peer->rssi,
+				tdls_cfg->rssi_trigger_threshold);
+
+			qdf_mem_copy(indication.peer_mac, mac,
+					QDF_MAC_ADDR_SIZE);
+
+			indication.vdev = tdls_vdev->vdev;
+
+			tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data,
+						TDLS_EVENT_SETUP_REQ,
+						&indication);
+		} else {
+			tdls_debug("Rssi Threshold not met: "
+				QDF_MAC_ADDRESS_STR
+				" rssi = %d threshold = %d ",
+				QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes),
+				curr_peer->rssi,
+				tdls_cfg->rssi_trigger_threshold);
+
+			tdls_set_peer_link_status(curr_peer,
+						TDLS_LINK_IDLE,
+						TDLS_LINK_UNSPECIFIED);
+
+			/* if RSSI threshold is not met then allow
+			 * further discovery attempts by decrementing
+			 * count for the last attempt
+			 */
+			if (curr_peer->discovery_attempt)
+				curr_peer->discovery_attempt--;
+		}
+	}
+
+	curr_peer->tdls_support = TDLS_CAP_SUPPORTED;
+
+	return status;
+}
+
+void tdls_indicate_teardown(struct tdls_vdev_priv_obj *tdls_vdev,
+			    struct tdls_peer *curr_peer,
+			    uint16_t reason)
+{
+	struct tdls_soc_priv_obj *tdls_soc;
+	struct tdls_osif_indication indication;
+
+	tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev);
+	if (!tdls_soc || !tdls_vdev || !curr_peer) {
+		tdls_err("tdls_soc: %p, tdls_vdev: %p, curr_peer: %p",
+			 tdls_soc, tdls_vdev, curr_peer);
+		return;
+	}
+
+	if (TDLS_LINK_CONNECTED != curr_peer->link_status)
+		return;
+
+	tdls_set_peer_link_status(curr_peer,
+				  TDLS_LINK_TEARING,
+				  TDLS_LINK_UNSPECIFIED);
+	tdls_notice("Teardown reason %d", reason);
+
+	indication.reason = reason;
+	indication.vdev = tdls_vdev->vdev;
+	qdf_mem_copy(indication.peer_mac, curr_peer->peer_mac.bytes,
+			QDF_MAC_ADDR_SIZE);
+
+	if (tdls_soc->tdls_event_cb)
+		tdls_soc->tdls_event_cb(tdls_soc->tdls_evt_cb_data,
+				     TDLS_EVENT_TEARDOWN_REQ, &indication);
+}
+
+/**
+ * tdls_get_conn_info() - get the tdls connection information.
+ * @tdls_soc: tdls soc object
+ * @idx: sta id
+ *
+ * Function to check tdls sta index
+ *
+ * Return: tdls connection information
+ */
+static struct tdls_conn_info *
+tdls_get_conn_info(struct tdls_soc_priv_obj *tdls_soc, uint8_t idx)
+{
+	uint8_t sta_idx;
+
+	/* check if there is available index for this new TDLS STA */
+	for (sta_idx = 0; sta_idx < WLAN_TDLS_STA_MAX_NUM; sta_idx++) {
+		if (idx == tdls_soc->tdls_conn_info[sta_idx].sta_id) {
+			tdls_debug("tdls peer with sta_idx %u exists", idx);
+			return &tdls_soc->tdls_conn_info[sta_idx];
+		}
+	}
+
+	tdls_err("tdls peer with staIdx %u not exists", idx);
+	return NULL;
+}
+
+static void
+tdls_ct_process_idle_handler(
+			struct tdls_ct_idle_peer_data *tdls_idle_peer_data)
+{
+	struct tdls_conn_info *tdls_info;
+	struct tdls_peer *curr_peer;
+	struct wlan_objmgr_vdev *vdev;
+	struct tdls_vdev_priv_obj *tdls_vdev_obj;
+	struct tdls_soc_priv_obj *tdls_soc_obj;
+
+	vdev = tdls_idle_peer_data->vdev;
+	tdls_info = tdls_idle_peer_data->tdls_info;
+
+	if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj,
+						   &tdls_soc_obj))
+		return;
+
+	if (!tdls_info->sta_id) {
+		tdls_err("peer (staidx %u) doesn't exists", tdls_info->sta_id);
+		return;
+	}
+
+	curr_peer = tdls_find_peer(tdls_vdev_obj,
+		(u8 *) &tdls_info->peer_mac.bytes[0]);
+
+	if (NULL == curr_peer) {
+		tdls_err("Invalid tdls idle timer expired");
+		return;
+	}
+
+	tdls_debug(QDF_MAC_ADDRESS_STR
+		" tx_pkt: %d, rx_pkt: %d, idle_packet_n: %d",
+		QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes),
+		curr_peer->tx_pkt,
+		curr_peer->rx_pkt,
+		tdls_vdev_obj->threshold_config.idle_packet_n);
+
+	/* Check tx/rx statistics on this tdls link for recent activities and
+	 * then decide whether to tear down the link or keep it.
+	 */
+	if ((curr_peer->tx_pkt >=
+	     tdls_vdev_obj->threshold_config.idle_packet_n) ||
+	    (curr_peer->rx_pkt >=
+	     tdls_vdev_obj->threshold_config.idle_packet_n)) {
+		/* this tdls link got back to normal, so keep it */
+		tdls_debug("tdls link to " QDF_MAC_ADDRESS_STR
+			 " back to normal, will stay",
+			  QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes));
+	} else {
+		/* this tdls link needs to get torn down */
+		tdls_notice("trigger tdls link to "QDF_MAC_ADDRESS_STR" down",
+			    QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes));
+		tdls_indicate_teardown(tdls_vdev_obj,
+					curr_peer,
+					TDLS_TEARDOWN_PEER_UNSPEC_REASON);
+	}
+
+	return;
+}
+
+void tdls_ct_idle_handler(void *user_data)
+{
+	struct tdls_ct_idle_peer_data *tdls_idle_peer_data;
+	struct wlan_objmgr_vdev *vdev;
+
+	tdls_idle_peer_data = (struct tdls_ct_idle_peer_data *) user_data;
+
+	if (NULL == tdls_idle_peer_data ||
+	    NULL == tdls_idle_peer_data->vdev ||
+	    NULL == tdls_idle_peer_data->tdls_info)
+		return;
+
+	vdev = tdls_idle_peer_data->vdev;
+	if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev,
+							WLAN_TDLS_NB_ID))
+		return;
+
+	tdls_ct_process_idle_handler(tdls_idle_peer_data);
+	wlan_objmgr_vdev_release_ref(vdev,
+				     WLAN_TDLS_NB_ID);
+}
+
+/**
+ * tdls_ct_process_idle_and_discovery() - process the traffic data
+ * @curr_peer: tdls peer needs to be examined
+ * @tdls_vdev_obj: tdls vdev object
+ * @tdls_soc_obj: tdls soc object
+ *
+ * Function to check the peer traffic data in idle link and  tdls
+ * discovering link
+ *
+ * Return: None
+ */
+static void
+tdls_ct_process_idle_and_discovery(struct tdls_peer *curr_peer,
+				struct tdls_vdev_priv_obj *tdls_vdev_obj,
+				struct tdls_soc_priv_obj *tdls_soc_obj)
+{
+	uint16_t valid_peers;
+
+	valid_peers = tdls_soc_obj->connected_peer_count;
+
+	if ((curr_peer->tx_pkt + curr_peer->rx_pkt) >=
+	     tdls_vdev_obj->threshold_config.tx_packet_n) {
+		if (WLAN_TDLS_STA_MAX_NUM > valid_peers) {
+			tdls_notice("Tput trigger TDLS pre-setup");
+			tdls_vdev_obj->curr_candidate = curr_peer;
+			tdls_implicit_send_discovery_request(tdls_vdev_obj);
+		} else {
+			tdls_notice("Maximum peers connected already! %d",
+				 valid_peers);
+		}
+	}
+}
+
+/**
+ * tdls_ct_process_connected_link() - process the traffic
+ * @curr_peer: tdls peer needs to be examined
+ * @tdls_vdev_obj: tdls vdev
+ * @tdls_soc_obj: tdls soc context
+ *
+ * Function to check the peer traffic data in active STA
+ * session
+ *
+ * Return: None
+ */
+static void tdls_ct_process_connected_link(
+				struct tdls_peer *curr_peer,
+				struct tdls_vdev_priv_obj *tdls_vdev,
+				struct tdls_soc_priv_obj *tdls_soc)
+{
+
+	if ((int32_t)curr_peer->rssi <
+	    (int32_t)tdls_vdev->threshold_config.rssi_teardown_threshold) {
+		tdls_warn("Tear down - low RSSI: " QDF_MAC_ADDRESS_STR "!",
+			 QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes));
+		tdls_indicate_teardown(tdls_vdev,
+					curr_peer,
+					TDLS_TEARDOWN_PEER_UNSPEC_REASON);
+		return;
+	}
+
+	/* Only teardown based on non zero idle packet threshold, to address
+	 * a use case where this threshold does not get consider for TEAR DOWN
+	 */
+	if ((0 != tdls_vdev->threshold_config.idle_packet_n) &&
+	    ((curr_peer->tx_pkt <
+	      tdls_vdev->threshold_config.idle_packet_n) &&
+	     (curr_peer->rx_pkt <
+	      tdls_vdev->threshold_config.idle_packet_n))) {
+		if (!curr_peer->is_peer_idle_timer_initialised) {
+			uint8_t sta_id = (uint8_t)curr_peer->sta_id;
+			struct tdls_conn_info *tdls_info;
+			tdls_info = tdls_get_conn_info(tdls_soc, sta_id);
+			tdls_soc->tdls_idle_peer_data.tdls_info = tdls_info;
+			tdls_soc->tdls_idle_peer_data.vdev = tdls_vdev->vdev;
+			qdf_mc_timer_init(&curr_peer->peer_idle_timer,
+					  QDF_TIMER_TYPE_SW,
+					  tdls_ct_idle_handler,
+					  &tdls_soc->tdls_idle_peer_data);
+			curr_peer->is_peer_idle_timer_initialised = true;
+		}
+		if (QDF_TIMER_STATE_RUNNING !=
+		    curr_peer->peer_idle_timer.state) {
+			tdls_warn("Tx/Rx Idle timer start: "
+				QDF_MAC_ADDRESS_STR "!",
+				QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes));
+			tdls_timer_restart(tdls_vdev->vdev,
+				&curr_peer->peer_idle_timer,
+				tdls_vdev->threshold_config.idle_timeout_t);
+		}
+	} else if (QDF_TIMER_STATE_RUNNING ==
+		   curr_peer->peer_idle_timer.state) {
+		tdls_warn("Tx/Rx Idle timer stop: " QDF_MAC_ADDRESS_STR "!",
+			 QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes));
+		qdf_mc_timer_stop(&curr_peer->peer_idle_timer);
+	}
+}
+
+/**
+ * tdls_ct_process_cap_supported() - process TDLS supported peer.
+ * @curr_peer: tdls peer needs to be examined
+ * @tdls_vdev_obj: tdls vdev context
+ * @tdls_soc_obj: tdls soc context
+ *
+ * Function to check the peer traffic data  for tdls supported peer
+ *
+ * Return: None
+ */
+static void tdls_ct_process_cap_supported(struct tdls_peer *curr_peer,
+					struct tdls_vdev_priv_obj *tdls_vdev,
+					struct tdls_soc_priv_obj *tdls_soc_obj)
+{
+	tdls_debug("tx %d rx %d thr.pkt %d/idle %d rssi %d thr.trig %d/tear %d",
+		 curr_peer->tx_pkt, curr_peer->rx_pkt,
+		 tdls_vdev->threshold_config.tx_packet_n,
+		 tdls_vdev->threshold_config.idle_packet_n,
+		 curr_peer->rssi,
+		 tdls_vdev->threshold_config.rssi_trigger_threshold,
+		 tdls_vdev->threshold_config.rssi_teardown_threshold);
+
+	switch (curr_peer->link_status) {
+	case TDLS_LINK_IDLE:
+	case TDLS_LINK_DISCOVERING:
+		if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(
+			tdls_soc_obj->tdls_configs.tdls_feature_flags) &&
+			(!curr_peer->is_forced_peer))
+			break;
+		tdls_ct_process_idle_and_discovery(curr_peer, tdls_vdev,
+						   tdls_soc_obj);
+		break;
+	case TDLS_LINK_CONNECTED:
+		tdls_ct_process_connected_link(curr_peer, tdls_vdev,
+					       tdls_soc_obj);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * tdls_ct_process_cap_unknown() - process unknown peer
+ * @curr_peer: tdls peer needs to be examined
+ * @tdls_vdev_obj: tdls vdev object
+ * @tdls_soc_obj: tdls soc object
+ *
+ * Function check the peer traffic data , when tdls capability is unknown
+ *
+ * Return: None
+ */
+static void tdls_ct_process_cap_unknown(struct tdls_peer *curr_peer,
+					struct tdls_vdev_priv_obj *tdls_vdev,
+					struct tdls_soc_priv_obj *tdlsa_soc)
+{
+	if (TDLS_IS_EXTERNAL_CONTROL_ENABLED(
+			tdlsa_soc->tdls_configs.tdls_feature_flags) &&
+			(!curr_peer->is_forced_peer))
+			return;
+
+	tdls_debug("threshold tx pkt = %d peer tx_pkt = %d & rx_pkt = %d ",
+		tdls_vdev->threshold_config.tx_packet_n, curr_peer->tx_pkt,
+		curr_peer->rx_pkt);
+
+	if (!TDLS_IS_LINK_CONNECTED(curr_peer) &&
+	    ((curr_peer->tx_pkt + curr_peer->rx_pkt) >=
+	    tdls_vdev->threshold_config.tx_packet_n)) {
+		/* Ignore discovery attempt if External Control is enabled, that
+		 * is, peer is forced. In that case, continue discovery attempt
+		 * regardless attempt count
+		 */
+		tdls_debug("TDLS UNKNOWN pre discover ");
+		if (curr_peer->is_forced_peer ||
+			curr_peer->discovery_attempt++ <
+		    tdls_vdev->threshold_config.discovery_tries_n) {
+			tdls_debug("TDLS UNKNOWN discover ");
+			tdls_vdev->curr_candidate = curr_peer;
+			tdls_implicit_send_discovery_request(tdls_vdev);
+		} else {
+			curr_peer->tdls_support = TDLS_CAP_NOT_SUPPORTED;
+			tdls_set_peer_link_status(
+				    curr_peer,
+				    TDLS_LINK_IDLE,
+				    TDLS_LINK_NOT_SUPPORTED);
+		}
+	}
+}
+
+/**
+ * tdls_ct_process_peers() - process the peer
+ * @curr_peer: tdls peer needs to be examined
+ * @tdls_vdev_obj: tdls vdev object
+ * @tdls_soc_obj: tdls soc object
+ *
+ * This function check the peer capability and process the metadata from
+ * the peer
+ *
+ * Return: None
+ */
+static void tdls_ct_process_peers(struct tdls_peer *curr_peer,
+				  struct tdls_vdev_priv_obj *tdls_vdev_obj,
+				  struct tdls_soc_priv_obj *tdls_soc_obj)
+{
+	tdls_debug(QDF_MAC_ADDRESS_STR " link_status %d tdls_support %d",
+		 QDF_MAC_ADDR_ARRAY(curr_peer->peer_mac.bytes),
+		 curr_peer->link_status, curr_peer->tdls_support);
+
+	switch (curr_peer->tdls_support) {
+	case TDLS_CAP_SUPPORTED:
+		tdls_ct_process_cap_supported(curr_peer, tdls_vdev_obj,
+						       tdls_soc_obj);
+		break;
+
+	case TDLS_CAP_UNKNOWN:
+		tdls_ct_process_cap_unknown(curr_peer, tdls_vdev_obj,
+						     tdls_soc_obj);
+		break;
+	default:
+		break;
+	}
+
+}
+
+static void tdls_ct_process_handler(struct wlan_objmgr_vdev *vdev)
+{
+	int i;
+	qdf_list_t *head;
+	qdf_list_node_t *list_node;
+	struct tdls_peer *curr_peer;
+	QDF_STATUS status;
+	struct tdls_vdev_priv_obj *tdls_vdev_obj;
+	struct tdls_soc_priv_obj *tdls_soc_obj;
+
+	if (QDF_STATUS_SUCCESS != tdls_get_vdev_objects(vdev, &tdls_vdev_obj,
+						   &tdls_soc_obj))
+		return;
+
+	/* If any concurrency is detected */
+	if (!tdls_soc_obj->enable_tdls_connection_tracker) {
+		tdls_notice("Connection tracker is disabled");
+		return;
+	}
+
+	/* Update tx rx traffic sample in tdls data structures */
+	tdls_ct_sampling_tx_rx(tdls_vdev_obj, tdls_soc_obj);
+
+	for (i = 0; i < WLAN_TDLS_PEER_LIST_SIZE; i++) {
+		head = &tdls_vdev_obj->peer_list[i];
+		status = qdf_list_peek_front(head, &list_node);
+		while (QDF_IS_STATUS_SUCCESS(status)) {
+			curr_peer = qdf_container_of(list_node,
+						struct tdls_peer, node);
+			tdls_ct_process_peers(curr_peer, tdls_vdev_obj,
+					      tdls_soc_obj);
+			curr_peer->tx_pkt = 0;
+			curr_peer->rx_pkt = 0;
+			status = qdf_list_peek_next(head,
+						    list_node, &list_node);
+		}
+	}
+
+	tdls_timer_restart(tdls_vdev_obj->vdev,
+			   &tdls_vdev_obj->peer_update_timer,
+			   tdls_vdev_obj->threshold_config.tx_period_t);
+
+}
+
+void tdls_ct_handler(void *user_data)
+{
+	struct wlan_objmgr_vdev *vdev;
+
+	if (!user_data)
+		return;
+
+	vdev = (struct wlan_objmgr_vdev *)user_data;
+
+	if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(vdev,
+							WLAN_TDLS_NB_ID))
+		return;
+
+	tdls_ct_process_handler(vdev);
+
+	wlan_objmgr_vdev_release_ref(vdev,
+				     WLAN_TDLS_NB_ID);
+}
+

+ 136 - 1
umac/tdls/core/src/wlan_tdls_ct.h

@@ -19,5 +19,140 @@
 /**
  * DOC: wlan_tdls_ct.h
  *
- * TDLS connection tracker api declaration
+ * TDLS connection tracker declarations
  */
+
+#ifndef _WLAN_TDLS_CT_H_
+#define _WLAN_TDLS_CT_H_
+
+ /*
+  * Before UpdateTimer expires, we want to timeout discovery response
+  * should not be more than 2000.
+  */
+#define TDLS_DISCOVERY_TIMEOUT_ERE_UPDATE     1000
+
+/**
+ * tdls_is_vdev_connected() - check the vdev is connected to ap
+ * @vdev: vdev object manager
+ *
+ * This function will check the vdev connection status and return
+ * true or false
+ *
+ * Return: true - Connected, false - Not connected
+ */
+bool tdls_is_vdev_connected(struct wlan_objmgr_vdev *vdev);
+
+/**
+ * tdls_implicit_enable() - enable implicit tdls triggering
+ * @tdls_vdev: TDLS vdev
+ *
+ * Return: Void
+ */
+void tdls_implicit_enable(struct tdls_vdev_priv_obj *tdls_vdev);
+
+/**
+ * tdls_update_rx_pkt_cnt() - Update rx packet count
+ * @vdev: vdev object manager
+ * @mac_addr: mac address of the data
+ *
+ * Increase the rx packet count, if the sender is not bssid and the packet is
+ * not broadcast and muticast packet
+ *
+ * This sampling information will be used in TDLS connection tracker
+ *
+ * This function expected to be called in an atomic context so blocking APIs
+ * not allowed
+ *
+ * Return: None
+ */
+void tdls_update_rx_pkt_cnt(struct wlan_objmgr_vdev *vdev,
+				     struct qdf_mac_addr *mac_addr);
+
+/**
+ * tdls_update_tx_pkt_cnt() - update tx packet
+ * @vdev: vdev object
+ * @mac_addr: mac address of the data
+ *
+ * Increase the tx packet count, if the sender is not bssid and the packet is
+ * not broadcast and muticast packet
+ *
+ * This sampling information will be used in TDLS connection tracker
+ *
+ * This function expected to be called in an atomic context so blocking APIs
+ * not allowed
+ *
+ * Return: None
+ */
+void tdls_update_tx_pkt_cnt(struct wlan_objmgr_vdev *vdev,
+				     struct qdf_mac_addr *mac_addr);
+
+/**
+ * wlan_hdd_tdls_implicit_send_discovery_request() - send discovery request
+ * @tdls_vdev_obj: tdls vdev object
+ *
+ * Return: None
+ */
+void tdls_implicit_send_discovery_request(
+				struct tdls_vdev_priv_obj *tdls_vdev_obj);
+
+/**
+ * tdls_recv_discovery_resp() - handling of tdls discovery response
+ * @soc: object manager
+ * @mac: mac address of peer from which the response was received
+ *
+ * Return: 0 for success or negative errno otherwise
+ */
+int tdls_recv_discovery_resp(struct tdls_vdev_priv_obj *tdls_vdev,
+				   const uint8_t *mac);
+
+/**
+ * tdls_indicate_teardown() - indicate teardown to upper layer
+ * @tdls_vdev: tdls vdev object
+ * @curr_peer: teardown peer
+ * @reason: teardown reason
+ *
+ * Return: Void
+ */
+void tdls_indicate_teardown(struct tdls_vdev_priv_obj *tdls_vdev,
+				struct tdls_peer *curr_peer,
+				uint16_t reason);
+
+/**
+ * tdls_ct_handler() - TDLS connection tracker handler
+ * @user_data: user data from timer
+ *
+ * tdls connection tracker timer starts, when the STA connected to AP
+ * and it's scan the traffic between two STA peers and make TDLS
+ * connection and teardown, based on the traffic threshold
+ *
+ * Return: None
+ */
+void tdls_ct_handler(void *user_data);
+
+/**
+ * tdls_ct_idle_handler() - Check tdls idle traffic
+ * @user_data: data from tdls idle timer
+ *
+ * Function to check the tdls idle traffic and make a decision about
+ * tdls teardown
+ *
+ * Return: None
+ */
+void tdls_ct_idle_handler(void *user_data);
+
+/**
+ * tdls_discovery_timeout_peer_cb() - tdls discovery timeout callback
+ * @userData: tdls vdev
+ *
+ * Return: None
+ */
+void tdls_discovery_timeout_peer_cb(void *user_data);
+
+/**
+ * tdls_implicit_disable() - disable implicit tdls triggering
+ * @pHddTdlsCtx: TDLS context
+ *
+ * Return: Void
+ */
+void tdls_implicit_disable(struct tdls_vdev_priv_obj *tdls_vdev);
+#endif

+ 33 - 3
umac/tdls/core/src/wlan_tdls_main.c

@@ -25,6 +25,7 @@
 #include "wlan_tdls_main.h"
 #include "wlan_tdls_cmds_process.h"
 #include "wlan_tdls_peer.h"
+#include "wlan_tdls_ct.h"
 
 QDF_STATUS tdls_psoc_obj_create_notification(struct wlan_objmgr_psoc *psoc,
 					     void *arg_list)
@@ -111,9 +112,10 @@ static QDF_STATUS tdls_vdev_init(struct tdls_vdev_priv_obj *vdev_obj)
 				WLAN_TDLS_PEER_SUB_LIST_SIZE);
 	}
 	qdf_mc_timer_init(&vdev_obj->peer_update_timer, QDF_TIMER_TYPE_SW,
-			  NULL, vdev_obj);
+			  tdls_ct_handler, vdev_obj->vdev);
 	qdf_mc_timer_init(&vdev_obj->peer_discovery_timer, QDF_TIMER_TYPE_SW,
-			  NULL, vdev_obj);
+			  tdls_discovery_timeout_peer_cb, vdev_obj);
+
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -247,7 +249,7 @@ QDF_STATUS tdls_process_evt(struct scheduler_msg *msg)
 	struct tdls_event_info *event;
 
 	if (!msg || !msg->bodyptr) {
-		tdls_err("msg: %p", msg);
+		tdls_err("msg is not valid: %p", msg);
 		return QDF_STATUS_E_NULL_VALUE;
 	}
 	notify = msg->bodyptr;
@@ -355,3 +357,31 @@ void tdls_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev)
 	tdls_monitor_timers_stop(tdls_vdev);
 	tdls_ct_timers_stop(tdls_vdev);
 }
+
+QDF_STATUS tdls_get_vdev_objects(struct wlan_objmgr_vdev *vdev,
+				   struct tdls_vdev_priv_obj **tdls_vdev_obj,
+				   struct tdls_soc_priv_obj **tdls_soc_obj)
+{
+	enum tQDF_ADAPTER_MODE device_mode;
+
+	if (NULL == vdev)
+		return QDF_STATUS_E_FAILURE;
+
+	*tdls_vdev_obj = wlan_vdev_get_tdls_vdev_obj(vdev);
+	if (NULL == (*tdls_vdev_obj))
+		return QDF_STATUS_E_FAILURE;
+
+	*tdls_soc_obj = wlan_vdev_get_tdls_soc_obj(vdev);
+	if (NULL == (*tdls_soc_obj))
+		return QDF_STATUS_E_FAILURE;
+
+	wlan_vdev_obj_lock(vdev);
+	device_mode = wlan_vdev_mlme_get_opmode(vdev);
+	wlan_vdev_obj_unlock(vdev);
+
+	if (device_mode != QDF_STA_MODE &&
+	    device_mode != QDF_P2P_CLIENT_MODE)
+		return QDF_STATUS_E_FAILURE;
+
+	return QDF_STATUS_SUCCESS;
+}

+ 26 - 0
umac/tdls/core/src/wlan_tdls_main.h

@@ -116,6 +116,16 @@ struct tdls_conn_tracker_mac_table {
 	uint32_t peer_timestamp_ms;
 };
 
+/**
+ * struct tdls_ct_idle_peer_data - connection tracker idle peer info
+ * @vdev: vdev object
+ * @tdls_info: tdls connection info
+ */
+struct tdls_ct_idle_peer_data {
+	struct wlan_objmgr_vdev *vdev;
+	struct tdls_conn_info *tdls_info;
+};
+
 /**
  * struct tdls_set_state_db - to record set tdls state command, we need to
  * set correct tdls state to firmware:
@@ -164,6 +174,8 @@ struct tdls_set_state_info {
  * @tdls_add_sta_req: store eWNI_SME_TDLS_ADD_STA_REQ value
  * @tdls_del_sta_req: store eWNI_SME_TDLS_DEL_STA_REQ value
  * @tdls_update_peer_state: store WMA_UPDATE_TDLS_PEER_STATE value
+ * @tdls_idle_peer_data: provide information about idle peer
+ * @tdls_ct_spinlock: connection tracker spin lock
  */
 struct tdls_soc_priv_obj {
 	struct wlan_objmgr_psoc *soc;
@@ -196,6 +208,8 @@ struct tdls_soc_priv_obj {
 	uint16_t tdls_add_sta_req;
 	uint16_t tdls_del_sta_req;
 	uint16_t tdls_update_peer_state;
+	struct tdls_ct_idle_peer_data tdls_idle_peer_data;
+	qdf_spinlock_t tdls_ct_spinlock;
 };
 
 /**
@@ -475,4 +489,16 @@ void tdls_timer_restart(struct wlan_objmgr_vdev *vdev,
  */
 void tdls_timers_stop(struct tdls_vdev_priv_obj *tdls_vdev);
 
+/**
+ * tdls_get_vdev_objects() - Get TDLS private objects
+ * @vdev: VDEV object manager
+ * @tdls_vdev_obj: tdls vdev object
+ * @tdls_soc_obj: tdls soc object
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS tdls_get_vdev_objects(struct wlan_objmgr_vdev *vdev,
+				   struct tdls_vdev_priv_obj **tdls_vdev_obj,
+				   struct tdls_soc_priv_obj **tdls_soc_obj);
+
 #endif

+ 18 - 0
umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c

@@ -142,7 +142,16 @@ static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj)
 	}
 	soc_obj->enable_tdls_connection_tracker = false;
 	soc_obj->tdls_external_peer_count = 0;
+	soc_obj->tdls_disable_in_progress = false;
 
+	qdf_spinlock_create(&soc_obj->tdls_ct_spinlock);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS tdls_global_deinit(struct tdls_soc_priv_obj *soc_obj)
+{
+	qdf_spinlock_destroy(&soc_obj->tdls_ct_spinlock);
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -259,8 +268,17 @@ QDF_STATUS ucfg_tdls_psoc_disable(struct wlan_objmgr_psoc *psoc)
 QDF_STATUS ucfg_tdls_psoc_close(struct wlan_objmgr_psoc *psoc)
 {
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct tdls_soc_priv_obj *tdls_soc;
 
 	tdls_notice("tdls psoc close");
+	tdls_soc = wlan_objmgr_psoc_get_comp_private_obj(psoc,
+							WLAN_UMAC_COMP_TDLS);
+	if (!tdls_soc) {
+		tdls_err("Failed to get tdls psoc component");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	status = tdls_global_deinit(tdls_soc);
 
 	return status;
 }