|
@@ -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;
|
|
|
+}
|