qcacld-3.0: Support firmware state check through cfg80211 vendor cmd

Add the support to allow user space applications through cfg80211
vendor command to check if wlan firmware is alive or not.

Change-Id: I96bb16e01974f7689493577741a36e3832963996
CRs-Fixed: 2399508
diff --git a/Kbuild b/Kbuild
index d239ab8..5ad44b5 100644
--- a/Kbuild
+++ b/Kbuild
@@ -225,6 +225,10 @@
 HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_sysfs.o
 endif
 
+ifeq ($(CONFIG_QCACLD_FEATURE_FW_STATE), y)
+HDD_OBJS += $(HDD_SRC_DIR)/wlan_hdd_fw_state.o
+endif
+
 ###### OSIF_SYNC ########
 SYNC_DIR := os_if/sync
 SYNC_INC_DIR := $(SYNC_DIR)/inc
@@ -1650,6 +1654,9 @@
 ifeq ($(CONFIG_WLAN_FEATURE_TWT), y)
 WMA_OBJS +=	$(WMA_SRC_DIR)/wma_twt.o
 endif
+ifeq ($(CONFIG_QCACLD_FEATURE_FW_STATE), y)
+WMA_OBJS +=	$(WMA_SRC_DIR)/wma_fw_state.o
+endif
 ############## PLD ##########
 PLD_DIR := core/pld
 PLD_INC_DIR := $(PLD_DIR)/inc
@@ -2497,6 +2504,9 @@
 #Flag to enable Dynamic Voltage WDCVS (Config Voltage Mode)
 cppflags-$(CONFIG_WLAN_DYNAMIC_CVM) += -DFEATURE_WLAN_DYNAMIC_CVM
 
+#Flag to enable get firmware state feature
+cppflags-$(CONFIG_QCACLD_FEATURE_FW_STATE) += -DFEATURE_FW_STATE
+
 ifdef CONFIG_MAX_LOGS_PER_SEC
 ccflags-y += -DWLAN_MAX_LOGS_PER_SEC=$(CONFIG_MAX_LOGS_PER_SEC)
 endif
diff --git a/configs/default_defconfig b/configs/default_defconfig
index 1e856f7..fea2150 100644
--- a/configs/default_defconfig
+++ b/configs/default_defconfig
@@ -116,6 +116,9 @@
 #Flag to enable SARv1 -> SARv2 conversion
 CONFIG_WLAN_FEATURE_SARV1_TO_SARV2 := n
 
+#Flag to enable get firmware state
+CONFIG_QCACLD_FEATURE_FW_STATE := y
+
 ifeq ($(CONFIG_ARCH_MSM8998), y)
 CONFIG_QCACLD_FEATURE_METERING := y
 endif
diff --git a/core/hdd/inc/wlan_hdd_fw_state.h b/core/hdd/inc/wlan_hdd_fw_state.h
new file mode 100644
index 0000000..b28de4b
--- /dev/null
+++ b/core/hdd/inc/wlan_hdd_fw_state.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. 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
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_hdd_fw_state.h
+ *
+ * Get firmware state related API's and definitions
+ */
+
+#ifndef __WLAN_HDD_FW_STATE_H
+#define __WLAN_HDD_FW_STATE_H
+
+#ifdef FEATURE_FW_STATE
+#include <net/cfg80211.h>
+/**
+ * wlan_hdd_cfg80211_get_fw_status() - get fw state
+ * @wiphy: wiphy pointer
+ * @wdev: pointer to struct wireless_dev
+ * @data: pointer to incoming NL vendor data
+ * @data_len: length of @data
+ *
+ * Return: 0 on success; error number otherwise.
+ */
+int wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy,
+				   struct wireless_dev *wdev,
+				   const void *data,
+				   int data_len);
+
+#define FEATURE_FW_STATE_COMMANDS					\
+{									\
+	.info.vendor_id = QCA_NL80211_VENDOR_ID,			\
+	.info.subcmd = QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE,		\
+	.flags = WIPHY_VENDOR_CMD_NEED_WDEV |				\
+		WIPHY_VENDOR_CMD_NEED_NETDEV,				\
+	.doit = wlan_hdd_cfg80211_get_fw_state				\
+},
+#else /* FEATURE_FW_STATE */
+#define FEATURE_FW_STATE_COMMANDS
+#endif /* FEATURE_FW_STATE */
+#endif /* __WLAN_HDD_FW_STATE_H */
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 5989ccf..98b5392 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -94,6 +94,7 @@
 #ifdef FEATURE_WLAN_APF
 #include "wlan_hdd_apf.h"
 #endif
+#include "wlan_hdd_fw_state.h"
 
 #include <cdp_txrx_cmn.h>
 #include <cdp_txrx_misc.h>
@@ -12452,6 +12453,7 @@
 
 	FEATURE_ACTIVE_TOS_VENDOR_COMMANDS
 	FEATURE_NAN_VENDOR_COMMANDS
+	FEATURE_FW_STATE_COMMANDS
 };
 
 struct hdd_context *hdd_cfg80211_wiphy_alloc(void)
diff --git a/core/hdd/src/wlan_hdd_fw_state.c b/core/hdd/src/wlan_hdd_fw_state.c
new file mode 100644
index 0000000..1759ea7
--- /dev/null
+++ b/core/hdd/src/wlan_hdd_fw_state.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. 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
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wlan_hdd_fw_state.c
+ *
+ * The implementation for getting firmware state
+ */
+
+#include "wlan_hdd_main.h"
+#include "wmi_unified_param.h"
+#include "wlan_hdd_fw_state.h"
+#include "qca_vendor.h"
+#include "wlan_osif_request_manager.h"
+
+struct fw_state {
+	bool fw_active;
+};
+
+/**
+ * hdd_get_fw_state_cb() - Callback function to get fw state
+ * @context: opaque context originally passed to SME. HDD always passes
+ * a cookie for the request context
+ *
+ * This function receives the response/data from the lower layer and
+ * checks to see if the thread is still waiting then post the results to
+ * upper layer, if the request has timed out then ignore.
+ *
+ * Return: None
+ */
+static void hdd_get_fw_state_cb(void *context)
+{
+	struct osif_request *request;
+	struct fw_state *priv;
+
+	hdd_enter();
+
+	request = osif_request_get(context);
+	if (!request) {
+		hdd_err("Obsolete request");
+		return;
+	}
+
+	priv = osif_request_priv(request);
+	priv->fw_active = true;
+	osif_request_complete(request);
+	osif_request_put(request);
+}
+
+/**
+ * hdd_post_get_fw_state_rsp - send rsp to user space
+ * @hdd_ctx: pointer to hdd context
+ * @state: true for fw active, false for fw error state
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+static int hdd_post_get_fw_state_rsp(struct hdd_context *hdd_ctx,
+				     bool state)
+{
+	struct sk_buff *skb;
+	enum qca_wlan_vendor_attr_fw_state fw_state;
+
+	skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
+						  sizeof(uint8_t) +
+						  NLA_HDRLEN +
+						  NLMSG_HDRLEN);
+
+	if (!skb) {
+		hdd_err("cfg80211_vendor_event_alloc failed");
+		return -ENOMEM;
+	}
+
+	if (state)
+		fw_state = QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE;
+	else
+		fw_state = QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR;
+
+	if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_FW_STATE,
+		       (uint8_t)fw_state)) {
+		hdd_err("put fail");
+		goto nla_put_failure;
+	}
+
+	cfg80211_vendor_cmd_reply(skb);
+	return 0;
+
+nla_put_failure:
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+/**
+ * __wlan_hdd_cfg80211_get_fw_state() - get fw state
+ * @wiphy: pointer to wireless wiphy structure.
+ * @wdev: pointer to wireless_dev structure.
+ * @data: Pointer to the data to be passed via vendor interface
+ * @data_len:Length of the data to be passed
+ *
+ * This function sends a request to fw and waits on a timer to
+ * invoke the callback. if the callback is invoked then true
+ * will be returned or otherwise fail status will be returned.
+ *
+ * Return: 0 on success; error number otherwise.
+ **/
+static int __wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy,
+					    struct wireless_dev *wdev,
+					    const void *data,
+					    int data_len)
+{
+	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
+	mac_handle_t mac_handle;
+	QDF_STATUS status;
+	int retval;
+	void *cookie;
+	struct osif_request *request;
+	struct fw_state *priv;
+	static const struct osif_request_params params = {
+		.priv_size = sizeof(*priv),
+		.timeout_ms = WLAN_WAIT_TIME_STATS,
+	};
+	bool state = false;
+
+	hdd_enter_dev(wdev->netdev);
+
+	retval = wlan_hdd_validate_context(hdd_ctx);
+	if (retval)
+		return retval;
+
+	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
+		hdd_err("Command not allowed in FTM mode");
+		return -EPERM;
+	}
+
+	request = osif_request_alloc(&params);
+	if (!request) {
+		hdd_err("Request allocation failure");
+		return -ENOMEM;
+	}
+	cookie = osif_request_cookie(request);
+
+	mac_handle = hdd_ctx->mac_handle;
+	status = sme_get_fw_state(mac_handle,
+				  hdd_get_fw_state_cb,
+				  cookie);
+	if (QDF_STATUS_SUCCESS != status) {
+		hdd_err("Unable to get fw state");
+		retval = qdf_status_to_os_return(status);
+	} else {
+		retval = osif_request_wait_for_response(request);
+		if (retval) {
+			hdd_err("Target response timed out");
+			state = false;
+		} else {
+			priv = osif_request_priv(request);
+			state = priv->fw_active;
+		}
+	}
+	retval = hdd_post_get_fw_state_rsp(hdd_ctx, state);
+	if (retval)
+		hdd_err("Failed to post fw state");
+
+	osif_request_put(request);
+
+	hdd_exit();
+	return retval;
+}
+
+/**
+ * wlan_hdd_cfg80211_get_fw_status() - get fw state
+ * @wiphy: wiphy pointer
+ * @wdev: pointer to struct wireless_dev
+ * @data: pointer to incoming NL vendor data
+ * @data_len: length of @data
+ *
+ * Return: 0 on success; error number otherwise.
+ */
+int wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy,
+				   struct wireless_dev *wdev,
+				   const void *data,
+				   int data_len)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_cfg80211_get_fw_state(wiphy, wdev, data, data_len);
+	cds_ssr_unprotect(__func__);
+
+	return ret;
+}
diff --git a/core/mac/inc/wni_api.h b/core/mac/inc/wni_api.h
index c379682..ae02e96 100644
--- a/core/mac/inc/wni_api.h
+++ b/core/mac/inc/wni_api.h
@@ -236,7 +236,8 @@
 	WNI_SME_CFG_ACTION_FRM_HE_TB_PPDU = SIR_SME_MSG_TYPES_BEGIN + 150,
 	/* To indicate Hidden ssid start complition to upper layer */
 	eWNI_SME_HIDDEN_SSID_RESTART_RSP = SIR_SME_MSG_TYPES_BEGIN + 151,
-	eWNI_SME_MSG_TYPES_END = SIR_SME_MSG_TYPES_BEGIN + 152
+	eWNI_SME_FW_STATUS_IND = SIR_SME_MSG_TYPES_BEGIN + 152,
+	eWNI_SME_MSG_TYPES_END = SIR_SME_MSG_TYPES_BEGIN + 153
 };
 
 typedef struct sAniCfgTxRateCtrs {
diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h
index d237131..4ca06a9 100644
--- a/core/sme/inc/sme_api.h
+++ b/core/sme/inc/sme_api.h
@@ -2184,6 +2184,24 @@
 			      get_chain_rssi_callback callback,
 			      void *context);
 
+#ifdef FEATURE_FW_STATE
+/**
+ * sme_get_fw_state() - Get fw state
+ * @mac_handle: Opaque handle to the global MAC context
+ * @callback: Callback function to be called with the result
+ * @context: Opaque context to be used by the caller to associate the
+ *   request with the response
+ *
+ * This function constructs the cds message and fill in message type,
+ * post the same to WDA.
+ *
+ * Return: QDF_STATUS enumeration
+ */
+QDF_STATUS sme_get_fw_state(mac_handle_t mac_handle,
+			    fw_state_callback callback,
+			    void *context);
+#endif /* FEATURE_FW_STATE */
+
 /**
  * sme_get_valid_channels() - sme api to get valid channels for
  * current regulatory domain
diff --git a/core/sme/inc/sme_internal.h b/core/sme/inc/sme_internal.h
index 66ea60f..ea99c72 100644
--- a/core/sme/inc/sme_internal.h
+++ b/core/sme/inc/sme_internal.h
@@ -194,6 +194,15 @@
 typedef void (*get_chain_rssi_callback)(void *context,
 					struct chain_rssi_result *data);
 
+#ifdef FEATURE_FW_STATE
+/**
+ * typedef fw_state_callback - get firmware state callback
+ * @context: Opaque context that the client can use to associate the
+ *    callback with the request
+ */
+typedef void (*fw_state_callback)(void *context);
+#endif /* FEATURE_FW_STATE */
+
 typedef void (*tx_queue_cb)(hdd_handle_t hdd_handle, uint32_t vdev_id,
 			    enum netif_action_type action,
 			    enum netif_reason_type reason);
@@ -330,6 +339,10 @@
 	void (*get_arp_stats_cb)(void *, struct rsp_stats *, void *);
 	get_chain_rssi_callback get_chain_rssi_cb;
 	void *get_chain_rssi_context;
+#ifdef FEATURE_FW_STATE
+	fw_state_callback fw_state_cb;
+	void *fw_state_context;
+#endif /* FEATURE_FW_STATE */
 	tx_queue_cb tx_queue_cb;
 	twt_enable_cb twt_enable_cb;
 	twt_disable_cb twt_disable_cb;
diff --git a/core/sme/src/common/sme_api.c b/core/sme/src/common/sme_api.c
index 73b918f..09c1895 100644
--- a/core/sme/src/common/sme_api.c
+++ b/core/sme/src/common/sme_api.c
@@ -33,6 +33,7 @@
 #include "wma_if.h"
 #include "wma.h"
 #include "wma_fips_api.h"
+#include "wma_fw_state.h"
 #include "qdf_trace.h"
 #include "sme_trace.h"
 #include "qdf_types.h"
@@ -76,6 +77,8 @@
 static QDF_STATUS sme_stats_ext_event(struct mac_context *mac,
 				      struct stats_ext_event *msg);
 
+static QDF_STATUS sme_fw_state_resp(struct mac_context *mac);
+
 /* Internal SME APIs */
 QDF_STATUS sme_acquire_global_lock(struct sme_context *sme)
 {
@@ -2107,6 +2110,9 @@
 				mac->sme.pget_peer_info_ext_cb_context);
 		qdf_mem_free(pMsg->bodyptr);
 		break;
+	case eWNI_SME_FW_STATUS_IND:
+		status = sme_fw_state_resp(mac);
+		break;
 	case eWNI_SME_CSA_OFFLOAD_EVENT:
 		if (pMsg->bodyptr) {
 			csr_scan_flush_bss_entry(mac, pMsg->bodyptr);
@@ -9167,6 +9173,53 @@
 
 #endif
 
+#ifdef FEATURE_FW_STATE
+QDF_STATUS sme_get_fw_state(mac_handle_t mac_handle,
+			    fw_state_callback callback,
+			    void *context)
+{
+	QDF_STATUS status;
+	struct mac_context *mac_ctx = MAC_CONTEXT(mac_handle);
+	tp_wma_handle wma_handle;
+
+	SME_ENTER();
+
+	mac_ctx->sme.fw_state_cb = callback;
+	mac_ctx->sme.fw_state_context = context;
+	wma_handle = cds_get_context(QDF_MODULE_ID_WMA);
+	status = wma_get_fw_state(wma_handle);
+
+	SME_EXIT();
+	return status;
+}
+
+/**
+ * sme_fw_state_resp() - eWNI_SME_FW_STATUS_IND processor
+ * @mac: Global MAC context
+
+ * This callback function called when SME received eWNI_SME_FW_STATUS_IND
+ * response from WMA
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS sme_fw_state_resp(struct mac_context *mac)
+{
+	if (mac->sme.fw_state_cb)
+		mac->sme.fw_state_cb(mac->sme.fw_state_context);
+	mac->sme.fw_state_cb = NULL;
+	mac->sme.fw_state_context = NULL;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+#else /* FEATURE_FW_STATE */
+static QDF_STATUS sme_fw_state_resp(struct mac_context *mac)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+#endif /* FEATURE_FW_STATE */
+
 /*
  * sme_update_dfs_scan_mode() -
  * Update DFS roam scan mode
diff --git a/core/wma/inc/wma_fw_state.h b/core/wma/inc/wma_fw_state.h
new file mode 100644
index 0000000..d508cb0
--- /dev/null
+++ b/core/wma/inc/wma_fw_state.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. 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
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wma_fw_state.h
+ *
+ * Get firmware state related API's and definitions
+ */
+
+#ifndef __WMA_FW_STATE_H
+#define __WMA_FW_STATE_H
+
+#include "wma.h"
+
+#ifdef FEATURE_FW_STATE
+/**
+ * wma_get_fw_state() - send wmi cmd to get fw state
+ * @wma_handle: wma handler
+ *
+ * Return: Return QDF_STATUS
+ */
+QDF_STATUS wma_get_fw_state(tp_wma_handle wma_handle);
+void wma_register_fw_state_events(wmi_unified_t wmi_handle);
+#else /* FEATURE_FW_STATE */
+static inline
+void wma_register_fw_state_events(WMA_HANDLE wma_handle)
+{
+}
+#endif /* FEATURE_FW_STATE */
+#endif /* __WMA_FW_STATE_H */
diff --git a/core/wma/src/wma_fw_state.c b/core/wma/src/wma_fw_state.c
new file mode 100644
index 0000000..68e8145
--- /dev/null
+++ b/core/wma/src/wma_fw_state.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2019 The Linux Foundation. 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
+ * above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+ * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: wma_fw_state.c
+ *
+ * The implementation for getting firmware state
+ */
+
+#include "wma_fw_state.h"
+#include "wmi_unified_api.h"
+
+QDF_STATUS wma_get_fw_state(tp_wma_handle wma_handle)
+{
+	wmi_echo_cmd_fixed_param *cmd;
+	wmi_buf_t wmi_buf;
+	uint32_t len = sizeof(*cmd);
+
+	if (!wma_handle) {
+		WMA_LOGE(FL("WMA is closed, can not issue cmd"));
+		return QDF_STATUS_E_INVAL;
+	}
+
+	wmi_buf = wmi_buf_alloc(wma_handle->wmi_handle, len);
+	if (!wmi_buf)
+		return QDF_STATUS_E_NOMEM;
+
+	cmd = (wmi_echo_cmd_fixed_param *)wmi_buf_data(wmi_buf);
+	WMITLV_SET_HDR(&cmd->tlv_header,
+		       WMITLV_TAG_STRUC_wmi_echo_cmd_fixed_param,
+		       WMITLV_GET_STRUCT_TLVLEN(
+		       wmi_echo_cmd_fixed_param));
+	cmd->value = true;
+
+	if (wmi_unified_cmd_send(wma_handle->wmi_handle, wmi_buf, len,
+				 WMI_ECHO_CMDID)) {
+		wmi_buf_free(wmi_buf);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * wma_echo_event_handler() - process fw state rsp
+ * @handle: wma interface
+ * @buf: wmi event buf pointer
+ * @len: length of event buffer
+ *
+ * This function will send eWNI_SME_FW_STATUS_IND to SME
+ *
+ * Return: 0 for success or error code
+ */
+static int wma_echo_event_handler(void *handle, uint8_t *buf, uint32_t len)
+{
+	struct scheduler_msg sme_msg = {
+		.type = eWNI_SME_FW_STATUS_IND,
+	};
+	QDF_STATUS qdf_status;
+
+	WMA_LOGD("Received Echo reply from firmware!");
+
+	qdf_status = scheduler_post_message(QDF_MODULE_ID_WMA,
+					    QDF_MODULE_ID_SME,
+					    QDF_MODULE_ID_SME, &sme_msg);
+	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
+		WMA_LOGE("%s: Fail to post fw state reply msg", __func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void wma_register_fw_state_events(wmi_unified_t wmi_handle)
+{
+	wmi_unified_register_event_handler(wmi_handle,
+					   wmi_echo_event_id,
+					   wma_echo_event_handler,
+					   WMA_RX_SERIALIZER_CTX);
+}
diff --git a/core/wma/src/wma_main.c b/core/wma/src/wma_main.c
index 0d338d3..02f9ed7 100644
--- a/core/wma/src/wma_main.c
+++ b/core/wma/src/wma_main.c
@@ -67,6 +67,7 @@
 #include "cdp_txrx_misc.h"
 #include "wma_fips_api.h"
 #include "wma_nan_datapath.h"
+#include "wma_fw_state.h"
 #include "wlan_lmac_if_def.h"
 #include "wlan_lmac_if_api.h"
 #include "target_if.h"
@@ -3460,6 +3461,9 @@
 					   wma_get_arp_stats_handler,
 					   WMA_RX_SERIALIZER_CTX);
 
+	/* register for fw state response event */
+	wma_register_fw_state_events(wma_handle->wmi_handle);
+
 	/* register for peer info response event */
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 					   wmi_peer_stats_info_event_id,