blob: 7710527d9a65877e8e603f7ab7d8eb9676a6e82d [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"
Jeff Johnsondde34492016-10-05 16:28:04 -070033#include "wlan_hdd_tsf.h"
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070034#include "wma_api.h"
35
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070036static struct completion tsf_sync_get_completion_evt;
37#define WLAN_TSF_SYNC_GET_TIMEOUT 2000
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070038/**
39 * hdd_capture_tsf() - capture tsf
40 * @adapter: pointer to adapter
41 * @buf: pointer to uplayer buf
42 * @len : the length of buf
43 *
44 * This function returns tsf value to uplayer.
45 *
46 * Return: 0 for success or non-zero negative failure code
47 */
48int hdd_capture_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len)
49{
50 int ret = 0;
51 hdd_station_ctx_t *hdd_sta_ctx;
52
53 if (adapter == NULL || buf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070054 hdd_err("invalid pointer");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070055 return -EINVAL;
56 }
57 if (len != 1)
58 return -EINVAL;
59
60 /* Reset TSF value for new capture */
61 adapter->tsf_high = 0;
62 adapter->tsf_low = 0;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070063 adapter->tsf_sync_soc_timer = 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070064
65 if (adapter->device_mode == QDF_STA_MODE ||
66 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
67 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
68 if (hdd_sta_ctx->conn_info.connState !=
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070069 eConnectionState_Associated) {
70 hdd_err("failed to cap tsf, not connect with ap");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070071 buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
72 return ret;
73 }
74 }
75 if ((adapter->device_mode == QDF_SAP_MODE ||
76 adapter->device_mode == QDF_P2P_GO_MODE) &&
77 !(test_bit(SOFTAP_BSS_STARTED, &adapter->event_flags))) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070078 hdd_err("Soft AP / P2p GO not beaconing");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070079 buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
80 return ret;
81 }
82 if (adapter->tsf_state == TSF_CAP_STATE) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070083 hdd_err("current in capture state, pls reset");
84 buf[0] = TSF_CURRENT_IN_CAP_STATE;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070085 } else {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070086 hdd_info("Send TSF capture to FW");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070087 buf[0] = TSF_RETURN;
88 adapter->tsf_state = TSF_CAP_STATE;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070089 init_completion(&tsf_sync_get_completion_evt);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070090 ret = wma_cli_set_command((int)adapter->sessionId,
91 (int)GEN_PARAM_CAPTURE_TSF,
92 adapter->sessionId,
93 GEN_CMD);
94
95 if (ret != QDF_STATUS_SUCCESS) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070096 hdd_err("capture fail");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070097 buf[0] = TSF_CAPTURE_FAIL;
98 adapter->tsf_state = TSF_IDLE;
99 }
100 }
101 return ret;
102}
103
104/**
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700105 * hdd_tsf_reset_gpio() - Reset TSF GPIO used for host timer sync
106 * @adapter: pointer to adapter
107 *
108 * This function send WMI command to reset GPIO configured in FW after
109 * TSF get operation.
110 *
111 * Return: TSF_RETURN on Success, TSF_RESET_GPIO_FAIL on failure
112 */
113#ifdef QCA_WIFI_3_0
Jeff Johnsondde34492016-10-05 16:28:04 -0700114static int hdd_tsf_reset_gpio(struct hdd_adapter_s *adapter)
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700115{
116 /* No GPIO Host timer sync for integrated WIFI Device */
117 return TSF_RETURN;
118}
119#else
Jeff Johnsondde34492016-10-05 16:28:04 -0700120static int hdd_tsf_reset_gpio(struct hdd_adapter_s *adapter)
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700121{
122 int ret;
123 ret = wma_cli_set_command((int)adapter->sessionId,
124 (int)GEN_PARAM_RESET_TSF_GPIO, adapter->sessionId,
125 GEN_CMD);
126
127 if (ret != 0) {
128 hdd_err("tsf reset GPIO fail ");
129 ret = TSF_RESET_GPIO_FAIL;
130 } else {
131 ret = TSF_RETURN;
132 }
133 return ret;
134}
135#endif
136
137/**
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700138 * hdd_indicate_tsf() - return tsf to uplayer
139 * @adapter: pointer to adapter
140 * @buf: pointer to uplayer buf
141 * @len : the length of buf
142 *
143 * This function returns tsf value to upper layer.
144 *
145 * Return: 0 for success or non-zero negative failure code
146 */
147int hdd_indicate_tsf(struct hdd_adapter_s *adapter, uint32_t *buf, int len)
148{
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700149 hdd_station_ctx_t *hdd_sta_ctx;
150
151 if (adapter == NULL || buf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700152 hdd_err("invalid pointer");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700153 return -EINVAL;
154 }
155
156 if (len != 3)
157 return -EINVAL;
158
159 buf[1] = 0;
160 buf[2] = 0;
161 if (adapter->device_mode == QDF_STA_MODE ||
162 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
163 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
164 if (hdd_sta_ctx->conn_info.connState !=
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700165 eConnectionState_Associated) {
166 hdd_info("fail to get tsf, sta in disconnected");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700167 buf[0] = TSF_STA_NOT_CONNECTED_NO_TSF;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700168 return 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700169 }
170 }
171 if ((adapter->device_mode == QDF_SAP_MODE ||
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700172 adapter->device_mode == QDF_P2P_GO_MODE) &&
173 !(test_bit(SOFTAP_BSS_STARTED,
174 &adapter->event_flags))) {
175 hdd_err("Soft AP / P2p GO not beaconing");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700176 buf[0] = TSF_SAP_NOT_STARTED_NO_TSF;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700177 return 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700178 }
179 if (adapter->tsf_high == 0 && adapter->tsf_low == 0) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700180 hdd_info("TSF value not received");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700181 buf[0] = TSF_NOT_RETURNED_BY_FW;
182 } else {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700183 buf[0] = hdd_tsf_reset_gpio(adapter);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700184 buf[1] = adapter->tsf_low;
185 buf[2] = adapter->tsf_high;
186 adapter->tsf_state = TSF_IDLE;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700187 hdd_info("get tsf cmd,status=%u, tsf_low=%u, tsf_high=%u",
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700188 buf[0], buf[1], buf[2]);
189 }
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700190 return 0;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700191}
192
193/**
194 * hdd_get_tsf_cb() - handle tsf callback
195 * @pcb_cxt: pointer to the hdd_contex
196 * @ptsf: pointer to struct stsf
197 *
198 * This function handle the event that reported by firmware at first.
199 * The event contains the vdev_id, current tsf value of this vdev,
200 * tsf value is 64bits, discripted in two varaible tsf_low and tsf_high.
201 * These two values each is uint32.
202 *
203 * Return: 0 for success or non-zero negative failure code
204 */
Arun Khandavalli4b55da72016-07-19 19:55:01 +0530205int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf)
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700206{
207 struct hdd_context_s *hddctx;
208 struct hdd_adapter_s *adapter;
209 int status;
210
211 if (pcb_cxt == NULL || ptsf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700212 hdd_err("HDD context is not valid");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700213 return -EINVAL;
214 }
215
216 hddctx = (struct hdd_context_s *)pcb_cxt;
217 status = wlan_hdd_validate_context(hddctx);
218 if (0 != status) {
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700219 return -EINVAL;
220 }
221
222 adapter = hdd_get_adapter_by_vdev(hddctx, ptsf->vdev_id);
223
224 if (NULL == adapter) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700225 hdd_err("failed to find adapter");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700226 return -EINVAL;
227 }
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700228 hdd_info("tsf cb handle event, device_mode is %d",
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700229 adapter->device_mode);
230
231 adapter->tsf_low = ptsf->tsf_low;
232 adapter->tsf_high = ptsf->tsf_high;
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700233 adapter->tsf_sync_soc_timer = ((uint64_t) ptsf->soc_timer_high << 32 |
234 ptsf->soc_timer_low);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700235
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700236 complete(&tsf_sync_get_completion_evt);
237 hdd_info("Vdev=%u, tsf_low=%u, tsf_high=%u soc_timer=%llu",
238 ptsf->vdev_id, ptsf->tsf_low, ptsf->tsf_high,
239 adapter->tsf_sync_soc_timer);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700240 return 0;
241}
242
243/**
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700244 * __wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
245 * @wiphy: Pointer to wireless phy
246 * @wdev: Pointer to wireless device
247 * @data: Pointer to data
248 * @data_len: Data length
249 *
250 * Handle TSF SET / GET operation from userspace
251 *
252 * Return: 0 on success, negative errno on failure
253 */
254static int __wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
255 struct wireless_dev *wdev,
256 const void *data,
257 int data_len)
258{
259 struct net_device *dev = wdev->netdev;
260 hdd_adapter_t *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
261 hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
262 struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1];
263 int status, ret;
264 struct sk_buff *reply_skb;
265 uint32_t tsf_op_resp[3], tsf_cmd;
266
267 ENTER_DEV(wdev->netdev);
268
269 status = wlan_hdd_validate_context(hdd_ctx);
270 if (0 != status)
271 return -EINVAL;
272
273 if (nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TSF_MAX, data,
274 data_len, NULL)) {
275 hdd_err("Invalid TSF cmd");
276 return -EINVAL;
277 }
278
279 if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]) {
280 hdd_err("Invalid TSF cmd");
281 return -EINVAL;
282 }
283 tsf_cmd = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]);
284
285 if (tsf_cmd == QCA_TSF_CAPTURE || tsf_cmd == QCA_TSF_SYNC_GET) {
286 hdd_capture_tsf(adapter, tsf_op_resp, 1);
287 switch (tsf_op_resp[0]) {
288 case TSF_RETURN:
289 status = 0;
290 break;
291 case TSF_CURRENT_IN_CAP_STATE:
292 status = -EALREADY;
293 break;
294 case TSF_STA_NOT_CONNECTED_NO_TSF:
295 case TSF_SAP_NOT_STARTED_NO_TSF:
296 status = -EPERM;
297 break;
298 default:
299 case TSF_CAPTURE_FAIL:
300 status = -EINVAL;
301 break;
302 }
303 }
304 if (status < 0)
305 goto end;
306
307 if (tsf_cmd == QCA_TSF_SYNC_GET) {
308 ret = wait_for_completion_timeout(&tsf_sync_get_completion_evt,
309 msecs_to_jiffies(WLAN_TSF_SYNC_GET_TIMEOUT));
310 if (ret == 0) {
311 status = -ETIMEDOUT;
312 goto end;
313 }
314 }
315
316 if (tsf_cmd == QCA_TSF_GET || tsf_cmd == QCA_TSF_SYNC_GET) {
317 hdd_indicate_tsf(adapter, tsf_op_resp, 3);
318 switch (tsf_op_resp[0]) {
319 case TSF_RETURN:
320 status = 0;
321 break;
322 case TSF_NOT_RETURNED_BY_FW:
323 status = -EINPROGRESS;
324 break;
325 case TSF_STA_NOT_CONNECTED_NO_TSF:
326 case TSF_SAP_NOT_STARTED_NO_TSF:
327 status = -EPERM;
328 break;
329 default:
330 status = -EINVAL;
331 break;
332 }
333 if (status != 0)
334 goto end;
335
336 reply_skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
337 sizeof(uint64_t) * 2 + NLMSG_HDRLEN,
338 QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX,
339 GFP_KERNEL);
340 if (!reply_skb) {
341 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
342 status = -ENOMEM;
343 goto end;
344 }
Dustin Brownbb7e2f52016-10-17 12:16:35 -0700345 if (hdd_wlan_nla_put_u64(reply_skb,
346 QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700347 ((uint64_t) adapter->tsf_high << 32 |
348 adapter->tsf_low)) ||
Dustin Brownbb7e2f52016-10-17 12:16:35 -0700349 hdd_wlan_nla_put_u64(reply_skb,
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700350 QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
Dustin Brownbb7e2f52016-10-17 12:16:35 -0700351 adapter->tsf_sync_soc_timer)) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700352 hdd_err("nla put fail");
353 kfree_skb(reply_skb);
354 status = -EINVAL;
355 goto end;
356 }
357 status = cfg80211_vendor_cmd_reply(reply_skb);
358 }
359
360end:
361 hdd_info("TSF operation %d Status: %d", tsf_cmd, status);
362 return status;
363}
364
365/**
366 * wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
367 * @wiphy: Pointer to wireless phy
368 * @wdev: Pointer to wireless device
369 * @data: Pointer to data
370 * @data_len: Data length
371 *
372 * Handle TSF SET / GET operation from userspace
373 *
374 * Return: 0 on success, negative errno on failure
375 */
376int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
377 struct wireless_dev *wdev,
378 const void *data,
379 int data_len)
380{
381 int ret;
382
383 cds_ssr_protect(__func__);
384 ret = __wlan_hdd_cfg80211_handle_tsf_cmd(wiphy, wdev, data, data_len);
385 cds_ssr_unprotect(__func__);
386
387 return ret;
388}
389
390/**
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700391 * wlan_hdd_tsf_init() - set callback to handle tsf value.
392 * @hdd_ctx: pointer to the struct hdd_context_s
393 *
394 * This function set the callback to sme module, the callback will be
395 * called when a tsf event is reported by firmware
396 *
397 * Return: none
398 */
399void wlan_hdd_tsf_init(struct hdd_context_s *hdd_ctx)
400{
401 sme_set_tsfcb(hdd_ctx->hHal, hdd_get_tsf_cb, hdd_ctx);
402}