qcacld-3.0: enhance the robustness of TSF capturing

1. Failed to get TSF if issuing cap_tsf more
   than 1 time continuously, due to the TSF
   value will be reset by the following 'cap_tsf'
   command, however, these 'cap_tsf' won't be
   excuted actually, because 'get_tsf' was not
   received after last successful 'cap_tsf'.
   To fix it, do TSF reset only if 'cap_tsf'
   will be actually excuted.

2. Add flag 'cap_tsf_flag' to make sure cap_tsf
   being executed serially.

3. Add flag 'tsf_ready_flag' to indicate whether
   TSF feature has been initialized successfully.
   Add 'TSF_NOT_READY' in 'hdd_tsf_get_state' to
   indicate this state.

Propagated from qcacld-2.0

Change-Id: Ib768e3d7f0b33a75e8bd838d4ed1c0b84e3aa13d
CRs-Fixed: 2057693
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index d265901..c83346e 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -1051,10 +1051,7 @@
 
 #ifdef WLAN_FEATURE_TSF
 	/* tsf value received from firmware */
-	uint32_t tsf_low;
-	uint32_t tsf_high;
-	/* TSF capture state */
-	enum hdd_tsf_capture_state tsf_state;
+	uint64_t cur_target_time;
 	uint64_t tsf_sync_soc_timer;
 #endif
 
@@ -1620,6 +1617,14 @@
 	/* Lock to control access to dnbs and coex avoid freq list */
 	struct mutex avoid_freq_lock;
 #endif
+#ifdef WLAN_FEATURE_TSF
+	/* indicate whether tsf has been initialized */
+	qdf_atomic_t tsf_ready_flag;
+	/* indicate whether it's now capturing tsf(updating tstamp-pair) */
+	qdf_atomic_t cap_tsf_flag;
+	/* the context that is capturing tsf */
+	hdd_adapter_t *cap_tsf_context;
+#endif
 };
 
 /**
diff --git a/core/hdd/inc/wlan_hdd_tsf.h b/core/hdd/inc/wlan_hdd_tsf.h
index cdb3890..645fcb5 100644
--- a/core/hdd/inc/wlan_hdd_tsf.h
+++ b/core/hdd/inc/wlan_hdd_tsf.h
@@ -38,6 +38,7 @@
  * @TSF_GET_FAIL:                 get fail
  * @TSF_RESET_GPIO_FAIL:          GPIO reset fail
  * @TSF_SAP_NOT_STARTED_NO_TSF    SAP not started
+ * @TSF_NOT_READY: TSF module is not initialized or init failed
  */
 enum hdd_tsf_get_state {
 	TSF_RETURN = 0,
@@ -47,7 +48,8 @@
 	TSF_CAPTURE_FAIL,
 	TSF_GET_FAIL,
 	TSF_RESET_GPIO_FAIL,
-	TSF_SAP_NOT_STARTED_NO_TSF
+	TSF_SAP_NOT_STARTED_NO_TSF,
+	TSF_NOT_READY
 };
 
 /**
@@ -61,19 +63,81 @@
 };
 
 #ifdef WLAN_FEATURE_TSF
+/**
+ * wlan_hdd_tsf_init() - set gpio and callbacks for
+ *     capturing tsf and init tsf_plus
+ * @hdd_ctx: pointer to the hdd_context_t
+ *
+ * This function set the callback to sme module, the callback will be
+ * called when a tsf event is reported by firmware; set gpio number
+ * to FW, FW will toggle this gpio when received a CAP_TSF command;
+ * do tsf_plus init
+ *
+ * Return: nothing
+ */
 void wlan_hdd_tsf_init(struct hdd_context_s *hdd_ctx);
+
+/**
+ * wlan_hdd_tsf_deinit() - reset callbacks for capturing tsf, deinit tsf_plus
+ * @hdd_ctx: pointer to the hdd_context_t
+ *
+ * This function reset the callback to sme module, and deinit tsf_plus
+ *
+ * Return: nothing
+ */
+void wlan_hdd_tsf_deinit(hdd_context_t *hdd_ctx);
+
+/**
+ * hdd_capture_tsf() - capture tsf
+ * @adapter: pointer to adapter
+ * @buf: pointer to uplayer buf
+ * @len : the length of buf
+ *
+ * This function returns tsf value to uplayer.
+ *
+ * Return: 0 for success or non-zero negative failure code
+ */
 int hdd_capture_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len);
+
+/**
+ * hdd_indicate_tsf() - return tsf to uplayer
+ *
+ * @adapter: pointer to adapter
+ * @buf: pointer to uplayer buf
+ * @len : the length of buf
+ *
+ * This function returns tsf value to uplayer.
+ *
+ * Return: Describe the execute result of this routine
+ */
 int hdd_indicate_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len);
+
+/**
+ * wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Data length
+ *
+ * Handle TSF SET / GET operation from userspace
+ *
+ * Return: 0 on success, negative errno on failure
+ */
 int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
 					struct wireless_dev *wdev,
 					const void *data,
 					int data_len);
+
 int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf);
 #else
 static inline void wlan_hdd_tsf_init(struct hdd_context_s *hdd_ctx)
 {
 }
 
+static inline void wlan_hdd_tsf_deinit(hdd_context_t *hdd_ctx)
+{
+}
+
 static inline int hdd_indicate_tsf(struct hdd_adapter_s *adapter, uint32_t *buf,
 				int len)
 {
diff --git a/core/hdd/src/wlan_hdd_main.c b/core/hdd/src/wlan_hdd_main.c
index 18354be..d4ef349 100644
--- a/core/hdd/src/wlan_hdd_main.c
+++ b/core/hdd/src/wlan_hdd_main.c
@@ -8483,35 +8483,6 @@
 	}
 }
 
-/**
- * hdd_tsf_init() - Initialize the TSF synchronization interface
- * @hdd_ctx: HDD global context
- *
- * When TSF synchronization via GPIO is supported by the driver and
- * has been enabled in the configuration file, this function plumbs
- * the GPIO value down to firmware via SME.
- *
- * Return: None
- */
-#ifdef WLAN_FEATURE_TSF
-static void hdd_tsf_init(hdd_context_t *hdd_ctx)
-{
-	QDF_STATUS status;
-
-	if (hdd_ctx->config->tsf_gpio_pin == TSF_GPIO_PIN_INVALID)
-		return;
-
-	status = sme_set_tsf_gpio(hdd_ctx->hHal,
-				      hdd_ctx->config->tsf_gpio_pin);
-	if (!QDF_IS_STATUS_SUCCESS(status))
-		hdd_err("Set tsf GPIO failed, status: %d", status);
-}
-#else
-static void hdd_tsf_init(hdd_context_t *hdd_ctx)
-{
-}
-#endif
-
 static int hdd_set_smart_chainmask_enabled(hdd_context_t *hdd_ctx)
 {
 	int vdev_id = 0;
@@ -8899,7 +8870,7 @@
 	if (!QDF_IS_STATUS_SUCCESS(status))
 		hdd_err("Error setting txlimit in sme: %d", status);
 
-	hdd_tsf_init(hdd_ctx);
+	wlan_hdd_tsf_init(hdd_ctx);
 
 	ret = hdd_register_cb(hdd_ctx);
 	if (ret) {
@@ -8945,9 +8916,21 @@
 
 }
 
+/**
+ * hdd_features_deinit() - Deinit features
+ * @hdd_ctx:	HDD context
+ *
+ * De-Initialize features and their feature context.
+ *
+ * Return: none.
+ */
+static void hdd_features_deinit(hdd_context_t *hdd_ctx)
+{
+	wlan_hdd_tsf_deinit(hdd_ctx);
+}
+
 #ifdef NAPIER_SCAN
 /**
- *
  * hdd_post_cds_enable_config() - HDD post cds start config helper
  * @adapter - Pointer to the HDD
  *
@@ -9055,6 +9038,10 @@
 	int ret = 0;
 
 	ENTER();
+
+	/* De-init features */
+	hdd_features_deinit(hdd_ctx);
+
 	/* De-register the SME callbacks */
 	hdd_deregister_cb(hdd_ctx);
 
diff --git a/core/hdd/src/wlan_hdd_tsf.c b/core/hdd/src/wlan_hdd_tsf.c
index aadd775..92fb505 100644
--- a/core/hdd/src/wlan_hdd_tsf.c
+++ b/core/hdd/src/wlan_hdd_tsf.c
@@ -36,72 +36,65 @@
 
 static struct completion tsf_sync_get_completion_evt;
 #define WLAN_TSF_SYNC_GET_TIMEOUT 2000
+
 /**
- * hdd_capture_tsf() - capture tsf
- * @adapter: pointer to adapter
- * @buf: pointer to uplayer buf
- * @len : the length of buf
+ * enum hdd_tsf_op_result - result of tsf operation
  *
- * This function returns tsf value to uplayer.
- *
- * Return: 0 for success or non-zero negative failure code
+ * HDD_TSF_OP_SUCC:  succeed
+ * HDD_TSF_OP_FAIL:  fail
  */
-int hdd_capture_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len)
+enum hdd_tsf_op_result {
+	HDD_TSF_OP_SUCC,
+	HDD_TSF_OP_FAIL
+};
+
+static
+enum hdd_tsf_get_state hdd_tsf_check_conn_state(hdd_adapter_t *adapter)
 {
-	int ret = 0;
+	enum hdd_tsf_get_state ret = TSF_RETURN;
 	hdd_station_ctx_t *hdd_sta_ctx;
 
-	if (adapter == NULL || buf == NULL) {
-		hdd_err("invalid pointer");
-		return -EINVAL;
-	}
-	if (len != 1)
-		return -EINVAL;
-
-	/* Reset TSF value for new capture */
-	adapter->tsf_high = 0;
-	adapter->tsf_low = 0;
-	adapter->tsf_sync_soc_timer = 0;
-
 	if (adapter->device_mode == QDF_STA_MODE ||
-		adapter->device_mode == QDF_P2P_CLIENT_MODE) {
+			adapter->device_mode == QDF_P2P_CLIENT_MODE) {
 		hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
 		if (hdd_sta_ctx->conn_info.connState !=
 				eConnectionState_Associated) {
 			hdd_err("failed to cap tsf, not connect with ap");
-			buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
-			return ret;
+			ret = TSF_STA_NOT_CONNECTED_NO_TSF;
 		}
-	}
-	if ((adapter->device_mode == QDF_SAP_MODE ||
-		adapter->device_mode == QDF_P2P_GO_MODE) &&
-		!(test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags))) {
+	} else if ((adapter->device_mode == QDF_SAP_MODE ||
+				adapter->device_mode == QDF_P2P_GO_MODE) &&
+			!(test_bit(SOFTAP_BSS_STARTED,
+					&adapter->event_flags))) {
 		hdd_err("Soft AP / P2p GO not beaconing");
-		buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
-		return ret;
-	}
-	if (adapter->tsf_state == TSF_CAP_STATE) {
-		hdd_err("current in capture state, pls reset");
-		buf[0] = TSF_CURRENT_IN_CAP_STATE;
-	} else {
-		hdd_info("Send TSF capture to FW");
-		buf[0] = TSF_RETURN;
-		adapter->tsf_state = TSF_CAP_STATE;
-		init_completion(&tsf_sync_get_completion_evt);
-		ret = wma_cli_set_command((int)adapter->sessionId,
-				(int)GEN_PARAM_CAPTURE_TSF,
-				adapter->sessionId,
-				GEN_CMD);
-
-		if (ret != QDF_STATUS_SUCCESS) {
-			hdd_err("capture fail");
-			buf[0] = TSF_CAPTURE_FAIL;
-			adapter->tsf_state = TSF_IDLE;
-		}
+		ret = TSF_SAP_NOT_STARTED_NO_TSF;
 	}
 	return ret;
 }
 
+static bool hdd_tsf_is_initialized(hdd_adapter_t *adapter)
+{
+	hdd_context_t *hddctx;
+
+	if (!adapter) {
+		hdd_err("invalid adapter");
+		return false;
+	}
+
+	hddctx = WLAN_HDD_GET_CTX(adapter);
+	if (!hddctx) {
+		hdd_err("invalid hdd context");
+		return false;
+	}
+
+	if (!qdf_atomic_read(&hddctx->tsf_ready_flag)) {
+		hdd_err("TSF is not initialized");
+		return false;
+	}
+
+	return true;
+}
+
 /**
  * hdd_tsf_reset_gpio() - Reset TSF GPIO used for host timer sync
  * @adapter: pointer to adapter
@@ -136,60 +129,137 @@
 }
 #endif
 
-/**
- * hdd_indicate_tsf() - return tsf to uplayer
- * @adapter: pointer to adapter
- * @buf: pointer to uplayer buf
- * @len : the length of buf
- *
- * This function returns tsf value to upper layer.
- *
- * Return: 0 for success or non-zero negative failure code
- */
-int hdd_indicate_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len)
+static enum hdd_tsf_op_result hdd_capture_tsf_internal(
+	hdd_adapter_t *adapter, uint32_t *buf, int len)
 {
-	hdd_station_ctx_t *hdd_sta_ctx;
+	int ret;
+	hdd_context_t *hddctx;
 
 	if (adapter == NULL || buf == NULL) {
 		hdd_err("invalid pointer");
-		return -EINVAL;
+		return HDD_TSF_OP_FAIL;
+	}
+
+	if (len != 1)
+		return HDD_TSF_OP_FAIL;
+
+	hddctx = WLAN_HDD_GET_CTX(adapter);
+	if (!hddctx) {
+		hdd_err("invalid hdd context");
+		return HDD_TSF_OP_FAIL;
+	}
+
+	if (!hdd_tsf_is_initialized(adapter)) {
+		buf[0] = TSF_NOT_READY;
+		return HDD_TSF_OP_SUCC;
+	}
+
+	buf[0] = hdd_tsf_check_conn_state(adapter);
+	if (buf[0] != TSF_RETURN)
+		return HDD_TSF_OP_SUCC;
+
+	if (qdf_atomic_inc_return(&hddctx->cap_tsf_flag) > 1) {
+		hdd_err("current in capture state");
+		buf[0] = TSF_CURRENT_IN_CAP_STATE;
+		return HDD_TSF_OP_SUCC;
+	}
+
+	/* record adapter for cap_tsf_irq_handler  */
+	hddctx->cap_tsf_context = adapter;
+
+	hdd_err("+ioctl issue cap tsf cmd");
+
+	/* Reset TSF value for new capture */
+	adapter->cur_target_time = 0;
+
+	buf[0] = TSF_RETURN;
+	init_completion(&tsf_sync_get_completion_evt);
+	ret = wma_cli_set_command((int)adapter->sessionId,
+				  (int)GEN_PARAM_CAPTURE_TSF,
+				  adapter->sessionId, GEN_CMD);
+	if (QDF_STATUS_SUCCESS != ret) {
+		hdd_err("cap tsf fail");
+		buf[0] = TSF_CAPTURE_FAIL;
+		hddctx->cap_tsf_context = NULL;
+		qdf_atomic_set(&hddctx->cap_tsf_flag, 0);
+		return HDD_TSF_OP_SUCC;
+	}
+	hdd_err("-ioctl return cap tsf cmd");
+	return HDD_TSF_OP_SUCC;
+}
+
+static enum hdd_tsf_op_result hdd_indicate_tsf_internal(
+	hdd_adapter_t *adapter, uint32_t *buf, int len)
+{
+	int ret;
+	hdd_context_t *hddctx;
+
+	if (!adapter || !buf) {
+		hdd_err("invalid pointer");
+		return HDD_TSF_OP_FAIL;
 	}
 
 	if (len != 3)
-		return -EINVAL;
+		return HDD_TSF_OP_FAIL;
+
+	hddctx = WLAN_HDD_GET_CTX(adapter);
+	if (!hddctx) {
+		hdd_err("invalid hdd context");
+		return HDD_TSF_OP_FAIL;
+	}
 
 	buf[1] = 0;
 	buf[2] = 0;
-	if (adapter->device_mode == QDF_STA_MODE ||
-		adapter->device_mode == QDF_P2P_CLIENT_MODE) {
-		hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
-		if (hdd_sta_ctx->conn_info.connState !=
-				eConnectionState_Associated) {
-			hdd_info("fail to get tsf, sta in disconnected");
-			buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
-			return 0;
-		}
+
+	if (!hdd_tsf_is_initialized(adapter)) {
+		buf[0] = TSF_NOT_READY;
+		return HDD_TSF_OP_SUCC;
 	}
-	if ((adapter->device_mode == QDF_SAP_MODE ||
-			adapter->device_mode == QDF_P2P_GO_MODE) &&
-			!(test_bit(SOFTAP_BSS_STARTED,
-			&adapter->event_flags))) {
-		hdd_err("Soft AP / P2p GO not beaconing");
-		buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
-		return 0;
-	}
-	if (adapter->tsf_high == 0 && adapter->tsf_low == 0) {
+
+	buf[0] = hdd_tsf_check_conn_state(adapter);
+	if (buf[0] != TSF_RETURN)
+		return HDD_TSF_OP_SUCC;
+
+	if (adapter->cur_target_time == 0) {
 		hdd_info("TSF value not received");
 		buf[0] = TSF_NOT_RETURNED_BY_FW;
+		return HDD_TSF_OP_SUCC;
 	} else {
-		buf[0] = hdd_tsf_reset_gpio(adapter);
-		buf[1] = adapter->tsf_low;
-		buf[2] = adapter->tsf_high;
-		adapter->tsf_state = TSF_IDLE;
+		buf[0] = TSF_RETURN;
+		buf[1] = (uint32_t)(adapter->cur_target_time & 0xffffffff);
+		buf[2] = (uint32_t)((adapter->cur_target_time >> 32) &
+				0xffffffff);
+
+		if (!qdf_atomic_read(&hddctx->cap_tsf_flag)) {
+			hdd_info("old: status=%u, tsf_low=%u, tsf_high=%u",
+				 buf[0], buf[1], buf[2]);
+			return HDD_TSF_OP_SUCC;
+		}
+
+		ret = hdd_tsf_reset_gpio(adapter);
+		if (0 != ret) {
+			hdd_err("reset tsf gpio fail");
+			buf[0] = TSF_RESET_GPIO_FAIL;
+			return HDD_TSF_OP_SUCC;
+		}
+		hddctx->cap_tsf_context = NULL;
+		qdf_atomic_set(&hddctx->cap_tsf_flag, 0);
 		hdd_info("get tsf cmd,status=%u, tsf_low=%u, tsf_high=%u",
 			buf[0], buf[1], buf[2]);
+		return HDD_TSF_OP_SUCC;
 	}
-	return 0;
+}
+
+int hdd_capture_tsf(hdd_adapter_t *adapter, uint32_t *buf, int len)
+{
+	return (hdd_capture_tsf_internal(adapter, buf, len) ==
+		HDD_TSF_OP_SUCC) ? 0 : -EINVAL;
+}
+
+int hdd_indicate_tsf(hdd_adapter_t *adapter, uint32_t *buf, int len)
+{
+	return (hdd_indicate_tsf_internal(adapter, buf, len) ==
+		HDD_TSF_OP_SUCC) ? 0 : -EINVAL;
 }
 
 /**
@@ -226,11 +296,17 @@
 		hdd_err("failed to find adapter");
 		return -EINVAL;
 	}
+
+	if (!hdd_tsf_is_initialized(adapter)) {
+		hdd_err("tsf is not init, ignore tsf event");
+		return -EINVAL;
+	}
+
 	hdd_info("tsf cb handle event, device_mode is %d",
 		adapter->device_mode);
 
-	adapter->tsf_low = ptsf->tsf_low;
-	adapter->tsf_high = ptsf->tsf_high;
+	adapter->cur_target_time = ((uint64_t)ptsf->tsf_high << 32 |
+			 ptsf->tsf_low);
 	adapter->tsf_sync_soc_timer = ((uint64_t) ptsf->soc_timer_high << 32 |
 						  ptsf->soc_timer_low);
 
@@ -349,8 +425,7 @@
 		}
 		if (hdd_wlan_nla_put_u64(reply_skb,
 				QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
-				((uint64_t) adapter->tsf_high << 32 |
-				adapter->tsf_low)) ||
+				adapter->cur_target_time) ||
 		    hdd_wlan_nla_put_u64(reply_skb,
 				QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
 				adapter->tsf_sync_soc_timer)) {
@@ -367,17 +442,6 @@
 	return status;
 }
 
-/**
- * wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
- * @wiphy: Pointer to wireless phy
- * @wdev: Pointer to wireless device
- * @data: Pointer to data
- * @data_len: Data length
- *
- * Handle TSF SET / GET operation from userspace
- *
- * Return: 0 on success, negative errno on failure
- */
 int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
 					struct wireless_dev *wdev,
 					const void *data,
@@ -403,5 +467,40 @@
  */
 void wlan_hdd_tsf_init(struct hdd_context_s *hdd_ctx)
 {
-	sme_set_tsfcb(hdd_ctx->hHal, hdd_get_tsf_cb, hdd_ctx);
+	QDF_STATUS hal_status;
+
+	if (!hdd_ctx)
+		return;
+
+	if (qdf_atomic_inc_return(&hdd_ctx->tsf_ready_flag) > 1)
+		return;
+
+	qdf_atomic_init(&hdd_ctx->cap_tsf_flag);
+
+	if (hdd_ctx->config->tsf_gpio_pin == TSF_GPIO_PIN_INVALID)
+		goto fail;
+
+	hal_status = sme_set_tsf_gpio(hdd_ctx->hHal,
+				      hdd_ctx->config->tsf_gpio_pin);
+	if (QDF_STATUS_SUCCESS != hal_status) {
+		hdd_err("set tsf GPIO failed, status: %d", hal_status);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	qdf_atomic_set(&hdd_ctx->tsf_ready_flag, 0);
+}
+
+void wlan_hdd_tsf_deinit(hdd_context_t *hdd_ctx)
+{
+	if (!hdd_ctx)
+		return;
+
+	if (!qdf_atomic_read(&hdd_ctx->tsf_ready_flag))
+		return;
+
+	qdf_atomic_set(&hdd_ctx->tsf_ready_flag, 0);
+	qdf_atomic_set(&hdd_ctx->cap_tsf_flag, 0);
 }