1 /*
2 * Copyright (c) 2019 The Linux Foundation. All rights reserved.
3 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4 *
5 * Permission to use, copy, modify, and/or distribute this software for
6 * any purpose with or without fee is hereby granted, provided that the
7 * above copyright notice and this permission notice appear in all
8 * copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /**
21 * DOC: wlan_hdd_fw_state.c
22 *
23 * The implementation for getting firmware state
24 */
25
26 #include "osif_sync.h"
27 #include "qca_vendor.h"
28 #include "wlan_hdd_fw_state.h"
29 #include "wlan_hdd_main.h"
30 #include "wlan_osif_request_manager.h"
31 #include "wmi_unified_param.h"
32
33 struct fw_state {
34 bool fw_active;
35 };
36
37 /**
38 * hdd_get_fw_state_cb() - Callback function to get fw state
39 * @context: opaque context originally passed to SME. HDD always passes
40 * a cookie for the request context
41 *
42 * This function receives the response/data from the lower layer and
43 * checks to see if the thread is still waiting then post the results to
44 * upper layer, if the request has timed out then ignore.
45 *
46 * Return: None
47 */
hdd_get_fw_state_cb(void * context)48 static void hdd_get_fw_state_cb(void *context)
49 {
50 struct osif_request *request;
51 struct fw_state *priv;
52
53 hdd_enter();
54
55 request = osif_request_get(context);
56 if (!request) {
57 hdd_err("Obsolete request");
58 return;
59 }
60
61 priv = osif_request_priv(request);
62 priv->fw_active = true;
63 osif_request_complete(request);
64 osif_request_put(request);
65 }
66
67 /**
68 * hdd_post_get_fw_state_rsp - send rsp to user space
69 * @hdd_ctx: pointer to hdd context
70 * @state: true for fw active, false for fw error state
71 *
72 * Return: 0 for success, non-zero for failure
73 */
hdd_post_get_fw_state_rsp(struct hdd_context * hdd_ctx,bool state)74 static int hdd_post_get_fw_state_rsp(struct hdd_context *hdd_ctx,
75 bool state)
76 {
77 struct sk_buff *skb;
78 enum qca_wlan_vendor_attr_fw_state fw_state;
79
80 skb = wlan_cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy,
81 sizeof(uint8_t) +
82 NLA_HDRLEN +
83 NLMSG_HDRLEN);
84 if (!skb) {
85 hdd_err("wlan_cfg80211_vendor_event_alloc failed");
86 return -ENOMEM;
87 }
88
89 if (state)
90 fw_state = QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE;
91 else
92 fw_state = QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR;
93
94 if (nla_put_u8(skb, QCA_WLAN_VENDOR_ATTR_FW_STATE,
95 (uint8_t)fw_state)) {
96 hdd_err("put fail");
97 goto nla_put_failure;
98 }
99
100 wlan_cfg80211_vendor_cmd_reply(skb);
101 return 0;
102
103 nla_put_failure:
104 wlan_cfg80211_vendor_free_skb(skb);
105 return -EINVAL;
106 }
107
108 /**
109 * __wlan_hdd_cfg80211_get_fw_state() - get fw state
110 * @wiphy: pointer to wireless wiphy structure.
111 * @wdev: pointer to wireless_dev structure.
112 * @data: Pointer to the data to be passed via vendor interface
113 * @data_len:Length of the data to be passed
114 *
115 * This function sends a request to fw and waits on a timer to
116 * invoke the callback. if the callback is invoked then true
117 * will be returned or otherwise fail status will be returned.
118 *
119 * Return: 0 on success; error number otherwise.
120 **/
__wlan_hdd_cfg80211_get_fw_state(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)121 static int __wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy,
122 struct wireless_dev *wdev,
123 const void *data,
124 int data_len)
125 {
126 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
127 mac_handle_t mac_handle;
128 QDF_STATUS status;
129 int retval;
130 void *cookie;
131 struct osif_request *request;
132 struct fw_state *priv;
133 static const struct osif_request_params params = {
134 .priv_size = sizeof(*priv),
135 .timeout_ms = WLAN_WAIT_TIME_STATS,
136 };
137 bool state = false;
138
139 hdd_enter_dev(wdev->netdev);
140
141 retval = wlan_hdd_validate_context(hdd_ctx);
142 if (retval)
143 return retval;
144
145 if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
146 hdd_err("Command not allowed in FTM mode");
147 return -EPERM;
148 }
149
150 request = osif_request_alloc(¶ms);
151 if (!request) {
152 hdd_err("Request allocation failure");
153 return -ENOMEM;
154 }
155 cookie = osif_request_cookie(request);
156
157 mac_handle = hdd_ctx->mac_handle;
158 status = sme_get_fw_state(mac_handle,
159 hdd_get_fw_state_cb,
160 cookie);
161 if (QDF_STATUS_SUCCESS != status) {
162 hdd_err("Unable to get fw state");
163 retval = qdf_status_to_os_return(status);
164 } else {
165 retval = osif_request_wait_for_response(request);
166 if (retval) {
167 hdd_err("Target response timed out");
168 state = false;
169 } else {
170 priv = osif_request_priv(request);
171 state = priv->fw_active;
172 }
173 }
174 retval = hdd_post_get_fw_state_rsp(hdd_ctx, state);
175 if (retval)
176 hdd_err("Failed to post fw state");
177
178 osif_request_put(request);
179
180 hdd_exit();
181 return retval;
182 }
183
184 /**
185 * wlan_hdd_cfg80211_get_fw_state() - get fw state
186 * @wiphy: wiphy pointer
187 * @wdev: pointer to struct wireless_dev
188 * @data: pointer to incoming NL vendor data
189 * @data_len: length of @data
190 *
191 * Return: 0 on success; error number otherwise.
192 */
wlan_hdd_cfg80211_get_fw_state(struct wiphy * wiphy,struct wireless_dev * wdev,const void * data,int data_len)193 int wlan_hdd_cfg80211_get_fw_state(struct wiphy *wiphy,
194 struct wireless_dev *wdev,
195 const void *data,
196 int data_len)
197 {
198 struct osif_psoc_sync *psoc_sync;
199 int errno;
200
201 errno = osif_psoc_sync_op_start(wiphy_dev(wiphy), &psoc_sync);
202 if (errno)
203 return errno;
204
205 errno = __wlan_hdd_cfg80211_get_fw_state(wiphy, wdev, data, data_len);
206
207 osif_psoc_sync_op_stop(psoc_sync);
208
209 return errno;
210 }
211