blob: e230925a7cd73882502640acc011ae9ffc8beedb [file] [log] [blame]
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001/*
2 * Copyright (c) 2016 The Linux Foundation. All rights reserved.
3 *
4 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
5 *
6 *
7 * Permission to use, copy, modify, and/or distribute this software for
8 * any purpose with or without fee is hereby granted, provided that the
9 * above copyright notice and this permission notice appear in all
10 * copies.
11 *
12 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
13 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
14 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
15 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
16 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
17 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
18 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19 * PERFORMANCE OF THIS SOFTWARE.
20 */
21
22/*
23 * This file was originally distributed by Qualcomm Atheros, Inc.
24 * under proprietary terms before Copyright ownership was assigned
25 * to the Linux Foundation.
26 */
27
28/**
29 * wlan_hdd_tsf.c - WLAN Host Device Driver tsf related implementation
30 */
31
32#include "wlan_hdd_main.h"
33#include "wma_api.h"
34
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070035static struct completion tsf_sync_get_completion_evt;
36#define WLAN_TSF_SYNC_GET_TIMEOUT 2000
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070037/**
38 * hdd_capture_tsf() - capture tsf
39 * @adapter: pointer to adapter
40 * @buf: pointer to uplayer buf
41 * @len : the length of buf
42 *
43 * This function returns tsf value to uplayer.
44 *
45 * Return: 0 for success or non-zero negative failure code
46 */
47int hdd_capture_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len)
48{
49 int ret = 0;
50 hdd_station_ctx_t *hdd_sta_ctx;
51
52 if (adapter == NULL || buf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070053 hdd_err("invalid pointer");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070054 return -EINVAL;
55 }
56 if (len != 1)
57 return -EINVAL;
58
59 /* Reset TSF value for new capture */
60 adapter->tsf_high = 0;
61 adapter->tsf_low = 0;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070062 adapter->tsf_sync_soc_timer = 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070063
64 if (adapter->device_mode == QDF_STA_MODE ||
65 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
66 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
67 if (hdd_sta_ctx->conn_info.connState !=
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070068 eConnectionState_Associated) {
69 hdd_err("failed to cap tsf, not connect with ap");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070070 buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
71 return ret;
72 }
73 }
74 if ((adapter->device_mode == QDF_SAP_MODE ||
75 adapter->device_mode == QDF_P2P_GO_MODE) &&
76 !(test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags))) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070077 hdd_err("Soft AP / P2p GO not beaconing");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070078 buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
79 return ret;
80 }
81 if (adapter->tsf_state == TSF_CAP_STATE) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070082 hdd_err("current in capture state, pls reset");
83 buf[0] = TSF_CURRENT_IN_CAP_STATE;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070084 } else {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070085 hdd_info("Send TSF capture to FW");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070086 buf[0] = TSF_RETURN;
87 adapter->tsf_state = TSF_CAP_STATE;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070088 init_completion(&tsf_sync_get_completion_evt);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070089 ret = wma_cli_set_command((int)adapter->sessionId,
90 (int)GEN_PARAM_CAPTURE_TSF,
91 adapter->sessionId,
92 GEN_CMD);
93
94 if (ret != QDF_STATUS_SUCCESS) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070095 hdd_err("capture fail");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070096 buf[0] = TSF_CAPTURE_FAIL;
97 adapter->tsf_state = TSF_IDLE;
98 }
99 }
100 return ret;
101}
102
103/**
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700104 * hdd_tsf_reset_gpio() - Reset TSF GPIO used for host timer sync
105 * @adapter: pointer to adapter
106 *
107 * This function send WMI command to reset GPIO configured in FW after
108 * TSF get operation.
109 *
110 * Return: TSF_RETURN on Success, TSF_RESET_GPIO_FAIL on failure
111 */
112#ifdef QCA_WIFI_3_0
113int hdd_tsf_reset_gpio(struct hdd_adapter_s *adapter)
114{
115 /* No GPIO Host timer sync for integrated WIFI Device */
116 return TSF_RETURN;
117}
118#else
119int hdd_tsf_reset_gpio(struct hdd_adapter_s *adapter)
120{
121 int ret;
122 ret = wma_cli_set_command((int)adapter->sessionId,
123 (int)GEN_PARAM_RESET_TSF_GPIO, adapter->sessionId,
124 GEN_CMD);
125
126 if (ret != 0) {
127 hdd_err("tsf reset GPIO fail ");
128 ret = TSF_RESET_GPIO_FAIL;
129 } else {
130 ret = TSF_RETURN;
131 }
132 return ret;
133}
134#endif
135
136/**
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700137 * hdd_indicate_tsf() - return tsf to uplayer
138 * @adapter: pointer to adapter
139 * @buf: pointer to uplayer buf
140 * @len : the length of buf
141 *
142 * This function returns tsf value to upper layer.
143 *
144 * Return: 0 for success or non-zero negative failure code
145 */
146int hdd_indicate_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len)
147{
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700148 hdd_station_ctx_t *hdd_sta_ctx;
149
150 if (adapter == NULL || buf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700151 hdd_err("invalid pointer");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700152 return -EINVAL;
153 }
154
155 if (len != 3)
156 return -EINVAL;
157
158 buf[1] = 0;
159 buf[2] = 0;
160 if (adapter->device_mode == QDF_STA_MODE ||
161 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
162 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
163 if (hdd_sta_ctx->conn_info.connState !=
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700164 eConnectionState_Associated) {
165 hdd_info("fail to get tsf, sta in disconnected");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700166 buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700167 return 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700168 }
169 }
170 if ((adapter->device_mode == QDF_SAP_MODE ||
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700171 adapter->device_mode == QDF_P2P_GO_MODE) &&
172 !(test_bit(SOFTAP_BSS_STARTED,
173 &adapter->event_flags))) {
174 hdd_err("Soft AP / P2p GO not beaconing");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700175 buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700176 return 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700177 }
178 if (adapter->tsf_high == 0 && adapter->tsf_low == 0) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700179 hdd_info("TSF value not received");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700180 buf[0] = TSF_NOT_RETURNED_BY_FW;
181 } else {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700182 buf[0] = hdd_tsf_reset_gpio(adapter);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700183 buf[1] = adapter->tsf_low;
184 buf[2] = adapter->tsf_high;
185 adapter->tsf_state = TSF_IDLE;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700186 hdd_info("get tsf cmd,status=%u, tsf_low=%u, tsf_high=%u",
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700187 buf[0], buf[1], buf[2]);
188 }
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700189 return 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700190}
191
192/**
193 * hdd_get_tsf_cb() - handle tsf callback
194 * @pcb_cxt: pointer to the hdd_contex
195 * @ptsf: pointer to struct stsf
196 *
197 * This function handle the event that reported by firmware at first.
198 * The event contains the vdev_id, current tsf value of this vdev,
199 * tsf value is 64bits, discripted in two varaible tsf_low and tsf_high.
200 * These two values each is uint32.
201 *
202 * Return: 0 for success or non-zero negative failure code
203 */
Arun Khandavalli4b55da72016-07-19 19:55:01 +0530204int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf)
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700205{
206 struct hdd_context_s *hddctx;
207 struct hdd_adapter_s *adapter;
208 int status;
209
210 if (pcb_cxt == NULL || ptsf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700211 hdd_err("HDD context is not valid");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700212 return -EINVAL;
213 }
214
215 hddctx = (struct hdd_context_s *)pcb_cxt;
216 status = wlan_hdd_validate_context(hddctx);
217 if (0 != status) {
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700218 return -EINVAL;
219 }
220
221 adapter = hdd_get_adapter_by_vdev(hddctx, ptsf->vdev_id);
222
223 if (NULL == adapter) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700224 hdd_err("failed to find adapter");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700225 return -EINVAL;
226 }
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700227 hdd_info("tsf cb handle event, device_mode is %d",
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700228 adapter->device_mode);
229
230 adapter->tsf_low = ptsf->tsf_low;
231 adapter->tsf_high = ptsf->tsf_high;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700232 adapter->tsf_sync_soc_timer = ((uint64_t) ptsf->soc_timer_high << 32 |
233 ptsf->soc_timer_low);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700234
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700235 complete(&tsf_sync_get_completion_evt);
236 hdd_info("Vdev=%u, tsf_low=%u, tsf_high=%u soc_timer=%llu",
237 ptsf->vdev_id, ptsf->tsf_low, ptsf->tsf_high,
238 adapter->tsf_sync_soc_timer);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700239 return 0;
240}
241
242/**
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700243 * __wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
244 * @wiphy: Pointer to wireless phy
245 * @wdev: Pointer to wireless device
246 * @data: Pointer to data
247 * @data_len: Data length
248 *
249 * Handle TSF SET / GET operation from userspace
250 *
251 * Return: 0 on success, negative errno on failure
252 */
253static int __wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
254 struct wireless_dev *wdev,
255 const void *data,
256 int data_len)
257{
258 struct net_device *dev = wdev->netdev;
259 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
260 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
261 struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1];
262 int status, ret;
263 struct sk_buff *reply_skb;
264 uint32_t tsf_op_resp[3], tsf_cmd;
265
266 ENTER_DEV(wdev->netdev);
267
268 status = wlan_hdd_validate_context(hdd_ctx);
269 if (0 != status)
270 return -EINVAL;
271
272 if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TSF_MAX, data,
273 data_len, NULL)) {
274 hdd_err("Invalid TSF cmd");
275 return -EINVAL;
276 }
277
278 if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]) {
279 hdd_err("Invalid TSF cmd");
280 return -EINVAL;
281 }
282 tsf_cmd = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]);
283
284 if (tsf_cmd == QCA_TSF_CAPTURE || tsf_cmd == QCA_TSF_SYNC_GET) {
285 hdd_capture_tsf(adapter, tsf_op_resp, 1);
286 switch (tsf_op_resp[0]) {
287 case TSF_RETURN:
288 status = 0;
289 break;
290 case TSF_CURRENT_IN_CAP_STATE:
291 status = -EALREADY;
292 break;
293 case TSF_STA_NOT_CONNECTED_NO_TSF:
294 case TSF_SAP_NOT_STARTED_NO_TSF:
295 status = -EPERM;
296 break;
297 default:
298 case TSF_CAPTURE_FAIL:
299 status = -EINVAL;
300 break;
301 }
302 }
303 if (status < 0)
304 goto end;
305
306 if (tsf_cmd == QCA_TSF_SYNC_GET) {
307 ret = wait_for_completion_timeout(&tsf_sync_get_completion_evt,
308 msecs_to_jiffies(WLAN_TSF_SYNC_GET_TIMEOUT));
309 if (ret == 0) {
310 status = -ETIMEDOUT;
311 goto end;
312 }
313 }
314
315 if (tsf_cmd == QCA_TSF_GET || tsf_cmd == QCA_TSF_SYNC_GET) {
316 hdd_indicate_tsf(adapter, tsf_op_resp, 3);
317 switch (tsf_op_resp[0]) {
318 case TSF_RETURN:
319 status = 0;
320 break;
321 case TSF_NOT_RETURNED_BY_FW:
322 status = -EINPROGRESS;
323 break;
324 case TSF_STA_NOT_CONNECTED_NO_TSF:
325 case TSF_SAP_NOT_STARTED_NO_TSF:
326 status = -EPERM;
327 break;
328 default:
329 status = -EINVAL;
330 break;
331 }
332 if (status != 0)
333 goto end;
334
335 reply_skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
336 sizeof(uint64_t) * 2 + NLMSG_HDRLEN,
337 QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX,
338 GFP_KERNEL);
339 if (!reply_skb) {
340 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
341 status = -ENOMEM;
342 goto end;
343 }
344 if (nla_put_u64(reply_skb, QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
345 ((uint64_t) adapter->tsf_high << 32 |
346 adapter->tsf_low)) ||
347 nla_put_u64(reply_skb,
348 QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
349 adapter->tsf_sync_soc_timer)){
350 hdd_err("nla put fail");
351 kfree_skb(reply_skb);
352 status = -EINVAL;
353 goto end;
354 }
355 status = cfg80211_vendor_cmd_reply(reply_skb);
356 }
357
358end:
359 hdd_info("TSF operation %d Status: %d", tsf_cmd, status);
360 return status;
361}
362
363/**
364 * wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
365 * @wiphy: Pointer to wireless phy
366 * @wdev: Pointer to wireless device
367 * @data: Pointer to data
368 * @data_len: Data length
369 *
370 * Handle TSF SET / GET operation from userspace
371 *
372 * Return: 0 on success, negative errno on failure
373 */
374int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
375 struct wireless_dev *wdev,
376 const void *data,
377 int data_len)
378{
379 int ret;
380
381 cds_ssr_protect(__func__);
382 ret = __wlan_hdd_cfg80211_handle_tsf_cmd(wiphy, wdev, data, data_len);
383 cds_ssr_unprotect(__func__);
384
385 return ret;
386}
387
388/**
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700389 * wlan_hdd_tsf_init() - set callback to handle tsf value.
390 * @hdd_ctx: pointer to the struct hdd_context_s
391 *
392 * This function set the callback to sme module, the callback will be
393 * called when a tsf event is reported by firmware
394 *
395 * Return: none
396 */
397void wlan_hdd_tsf_init(struct hdd_context_s *hdd_ctx)
398{
399 sme_set_tsfcb(hdd_ctx->hHal, hdd_get_tsf_cb, hdd_ctx);
400}