qcacld-3.0: Add wow support for TDLS

Prevent wow if TDLS is started, allow wow if TDLS link disabled. This
feature is enabled only if lithium plaform isn't drv supported.

Change-Id: Ie66d62cb139fe9a1d292925a78f2dd861f16ef69
CRs-Fixed: 2716922
diff --git a/Kbuild b/Kbuild
index f991e56..a24f2fc 100644
--- a/Kbuild
+++ b/Kbuild
@@ -2434,6 +2434,10 @@
 
 #normally, TDLS negative behavior is not needed
 cppflags-$(CONFIG_QCOM_TDLS) += -DFEATURE_WLAN_TDLS
+ifeq ($(CONFIG_LITHIUM), y)
+cppflags-$(CONFIG_QCOM_TDLS) += -DTDLS_WOW_ENABLED
+endif
+
 
 cppflags-$(CONFIG_QCACLD_WLAN_LFR3) += -DWLAN_FEATURE_ROAM_OFFLOAD
 
diff --git a/components/tdls/core/src/wlan_tdls_main.h b/components/tdls/core/src/wlan_tdls_main.h
index 722eedf..f9df6d9 100644
--- a/components/tdls/core/src/wlan_tdls_main.h
+++ b/components/tdls/core/src/wlan_tdls_main.h
@@ -165,6 +165,7 @@
  * @tdls_external_peer_count: external tdls peer count
  * @tdls_nss_switch_in_progress: tdls antenna switch in progress
  * @tdls_nss_teardown_complete: tdls tear down complete
+ * @tdls_disable_in_progress: tdls is disable in progress
  * @tdls_nss_transition_mode: tdls nss transition mode
  * @tdls_teardown_peers_cnt: tdls tear down peer count
  * @set_state_info: set tdls state info
@@ -182,6 +183,11 @@
  * @tdls_update_dp_vdev_flags store CDP_UPDATE_TDLS_FLAGS
  * @tdls_idle_peer_data: provide information about idle peer
  * @tdls_ct_spinlock: connection tracker spin lock
+ * @is_prevent_suspend: prevent suspend or not
+ * @is_drv_supported: platform supports drv or not, enable/disable tdls wow
+ * based on this flag.
+ * @wake_lock: wake lock
+ * @runtime_lock: runtime lock
  * @tdls_osif_init_cb: Callback to initialize the tdls private
  * @tdls_osif_deinit_cb: Callback to deinitialize the tdls private
  */
@@ -224,6 +230,12 @@
 	uint16_t tdls_del_all_peers;
 	uint32_t tdls_update_dp_vdev_flags;
 	qdf_spinlock_t tdls_ct_spinlock;
+#ifdef TDLS_WOW_ENABLED
+	bool is_prevent_suspend;
+	bool is_drv_supported;
+	qdf_wake_lock_t wake_lock;
+	qdf_runtime_lock_t runtime_lock;
+#endif
 	tdls_vdev_init_cb tdls_osif_init_cb;
 	tdls_vdev_deinit_cb tdls_osif_deinit_cb;
 };
diff --git a/components/tdls/core/src/wlan_tdls_peer.c b/components/tdls/core/src/wlan_tdls_peer.c
index 241924a..43ce1e8 100644
--- a/components/tdls/core/src/wlan_tdls_peer.c
+++ b/components/tdls/core/src/wlan_tdls_peer.c
@@ -27,6 +27,7 @@
 #include <wlan_utility.h>
 #include <wlan_policy_mgr_api.h>
 #include "wlan_reg_ucfg_api.h"
+#include <host_diag_core_event.h>
 
 static uint8_t calculate_hash_key(const uint8_t *macaddr)
 {
@@ -552,6 +553,84 @@
 			peer->supported_oper_classes[i];
 }
 
+#ifdef TDLS_WOW_ENABLED
+/**
+ * tdls_prevent_suspend(): Prevent suspend for TDLS
+ *
+ * Acquire wake lock and prevent suspend for TDLS
+ *
+ * Return None
+ */
+static void tdls_prevent_suspend(struct tdls_soc_priv_obj *tdls_soc)
+{
+	if (tdls_soc->is_prevent_suspend)
+		return;
+
+	qdf_wake_lock_acquire(&tdls_soc->wake_lock,
+			      WIFI_POWER_EVENT_WAKELOCK_TDLS);
+	qdf_runtime_pm_prevent_suspend(&tdls_soc->runtime_lock);
+	tdls_soc->is_prevent_suspend = true;
+}
+
+/**
+ * tdls_allow_suspend(): Allow suspend for TDLS
+ *
+ * Release wake lock and allow suspend for TDLS
+ *
+ * Return None
+ */
+static void tdls_allow_suspend(struct tdls_soc_priv_obj *tdls_soc)
+{
+	if (!tdls_soc->is_prevent_suspend)
+		return;
+
+	qdf_wake_lock_release(&tdls_soc->wake_lock,
+			      WIFI_POWER_EVENT_WAKELOCK_TDLS);
+	qdf_runtime_pm_allow_suspend(&tdls_soc->runtime_lock);
+	tdls_soc->is_prevent_suspend = false;
+}
+
+/**
+ * tdls_update_pmo_status() - Update PMO status by TDLS status
+ * @tdls_vdev: TDLS vdev object
+ * @old_status: old link status
+ * @new_status: new link status
+ *
+ * Return: None.
+ */
+static void tdls_update_pmo_status(struct tdls_vdev_priv_obj *tdls_vdev,
+				   enum tdls_link_state old_status,
+				   enum tdls_link_state new_status)
+{
+	struct tdls_soc_priv_obj *tdls_soc;
+
+	tdls_soc = wlan_vdev_get_tdls_soc_obj(tdls_vdev->vdev);
+	if (!tdls_soc) {
+		tdls_err("NULL psoc object");
+		return;
+	}
+
+	if (tdls_soc->is_drv_supported)
+		return;
+
+	if ((old_status < TDLS_LINK_CONNECTING) &&
+	    (new_status == TDLS_LINK_CONNECTING))
+		tdls_prevent_suspend(tdls_soc);
+
+	if ((old_status > TDLS_LINK_IDLE) &&
+	    (new_status == TDLS_LINK_IDLE) &&
+	    (!tdls_soc->connected_peer_count) &&
+	    (!tdls_is_progress(tdls_vdev, NULL, 0)))
+		tdls_allow_suspend(tdls_soc);
+}
+#else
+static void tdls_update_pmo_status(struct tdls_vdev_priv_obj *tdls_vdev,
+				   enum tdls_link_state old_status,
+				   enum tdls_link_state new_status)
+{
+}
+#endif
+
 /**
  * tdls_set_link_status() - set link statue for TDLS peer
  * @vdev_obj: TDLS vdev object
@@ -572,6 +651,7 @@
 	uint32_t channel = 0;
 	struct tdls_peer *peer;
 	struct tdls_soc_priv_obj *soc_obj;
+	enum tdls_link_state old_status;
 
 	peer = tdls_find_peer(vdev_obj, mac);
 	if (!peer) {
@@ -580,7 +660,9 @@
 		return;
 	}
 
+	old_status = peer->link_status;
 	peer->link_status = link_status;
+	tdls_update_pmo_status(vdev_obj, old_status, link_status);
 
 	if (link_status >= TDLS_LINK_DISCOVERED)
 		peer->discovery_attempt = 0;
@@ -612,11 +694,16 @@
 	uint32_t channel = 0;
 	struct tdls_soc_priv_obj *soc_obj;
 	struct tdls_vdev_priv_obj *vdev_obj;
+	enum tdls_link_state old_status;
 
 	tdls_debug("state %d reason %d peer:" QDF_MAC_ADDR_STR,
 		   link_status, link_reason,
 		   QDF_MAC_ADDR_ARRAY(peer->peer_mac.bytes));
+
+	vdev_obj = peer->vdev_priv;
+	old_status = peer->link_status;
 	peer->link_status = link_status;
+	tdls_update_pmo_status(vdev_obj, old_status, link_status);
 
 	if (link_status >= TDLS_LINK_DISCOVERED)
 		peer->discovery_attempt = 0;
@@ -624,7 +711,6 @@
 	if (peer->is_forced_peer && peer->state_change_notification) {
 		peer->reason = link_reason;
 
-		vdev_obj = peer->vdev_priv;
 		soc_obj = wlan_vdev_get_tdls_soc_obj(vdev_obj->vdev);
 		if (!soc_obj) {
 			tdls_err("NULL psoc object");
diff --git a/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c b/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c
index 4c31074..3176a20 100644
--- a/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c
+++ b/components/tdls/dispatcher/src/wlan_tdls_ucfg_api.c
@@ -226,9 +226,50 @@
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef TDLS_WOW_ENABLED
+/**
+ * tdls_wow_init(): Create/init wake lock for TDLS
+ *
+ * Create/init wake lock for TDLS if DVR isn't supported
+ *
+ * Return None
+ */
+static void tdls_wow_init(struct tdls_soc_priv_obj *soc_obj)
+{
+	soc_obj->is_prevent_suspend = false;
+	soc_obj->is_drv_supported = qdf_is_drv_supported();
+	if (!soc_obj->is_drv_supported) {
+		qdf_wake_lock_create(&soc_obj->wake_lock, "wlan_tdls");
+		qdf_runtime_lock_init(&soc_obj->runtime_lock);
+	}
+}
+
+/**
+ * tdls_wow_deinit(): Destroy/deinit wake lock for TDLS
+ *
+ * Destroy/deinit wake lock for TDLS if DVR isn't supported
+ *
+ * Return None
+ */
+static void tdls_wow_deinit(struct tdls_soc_priv_obj *soc_obj)
+{
+	if (!soc_obj->is_drv_supported) {
+		qdf_runtime_lock_deinit(&soc_obj->runtime_lock);
+		qdf_wake_lock_destroy(&soc_obj->wake_lock);
+	}
+}
+#else
+static void tdls_wow_init(struct tdls_soc_priv_obj *soc_obj)
+{
+}
+
+static void tdls_wow_deinit(struct tdls_soc_priv_obj *soc_obj)
+{
+}
+#endif
+
 static QDF_STATUS tdls_global_init(struct tdls_soc_priv_obj *soc_obj)
 {
-
 	tdls_object_init_params(soc_obj);
 	soc_obj->connected_peer_count = 0;
 	soc_obj->tdls_nss_switch_in_progress = false;
@@ -240,13 +281,16 @@
 	soc_obj->tdls_disable_in_progress = false;
 
 	qdf_spinlock_create(&soc_obj->tdls_ct_spinlock);
+	tdls_wow_init(soc_obj);
 
 	return QDF_STATUS_SUCCESS;
 }
 
 static QDF_STATUS tdls_global_deinit(struct tdls_soc_priv_obj *soc_obj)
 {
+	tdls_wow_deinit(soc_obj);
 	qdf_spinlock_destroy(&soc_obj->tdls_ct_spinlock);
+
 	return QDF_STATUS_SUCCESS;
 }