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(¶ms);
+ 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,