blob: 2ed913682adea0c52d8d37ba1febf732d22866ad [file] [log] [blame]
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001/*
Dustin Brown4ea21db2018-01-05 14:13:17 -08002 * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07003 *
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07004 * Permission to use, copy, modify, and/or distribute this software for
5 * any purpose with or without fee is hereby granted, provided that the
6 * above copyright notice and this permission notice appear in all
7 * copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
17 */
18
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070019/**
20 * wlan_hdd_tsf.c - WLAN Host Device Driver tsf related implementation
21 */
22
23#include "wlan_hdd_main.h"
Jeff Johnsondde34492016-10-05 16:28:04 -070024#include "wlan_hdd_tsf.h"
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070025#include "wma_api.h"
Sourav Mohapatra0f3b8572018-09-12 10:03:51 +053026#include "wlan_fwol_ucfg_api.h"
Sandeep Puligillafdd201e2017-02-02 18:43:46 -080027#include <qca_vendor.h>
yuanl2746f072018-09-21 19:19:16 +080028#include <linux/errqueue.h>
29#include "ol_txrx_api.h"
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070030static struct completion tsf_sync_get_completion_evt;
31#define WLAN_TSF_SYNC_GET_TIMEOUT 2000
yuanl2746f072018-09-21 19:19:16 +080032#define WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS 500
33#define WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS 100
34
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070035/**
Yu Wangf5d5b5f2017-05-25 22:38:32 +080036 * enum hdd_tsf_op_result - result of tsf operation
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070037 *
Yu Wangf5d5b5f2017-05-25 22:38:32 +080038 * HDD_TSF_OP_SUCC: succeed
39 * HDD_TSF_OP_FAIL: fail
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070040 */
Yu Wangf5d5b5f2017-05-25 22:38:32 +080041enum hdd_tsf_op_result {
42 HDD_TSF_OP_SUCC,
43 HDD_TSF_OP_FAIL
44};
45
Yu Wang000dc2f2017-05-26 17:38:48 +080046#ifdef WLAN_FEATURE_TSF_PLUS
yuanl2746f072018-09-21 19:19:16 +080047#define WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL 9
48
Jeff Johnson8f389862017-08-29 14:19:23 -070049static inline void hdd_set_th_sync_status(struct hdd_adapter *adapter,
Yu Wang000dc2f2017-05-26 17:38:48 +080050 bool initialized)
51{
52 qdf_atomic_set(&adapter->tsf_sync_ready_flag,
53 (initialized ? 1 : 0));
54}
55
Jeff Johnson8f389862017-08-29 14:19:23 -070056static inline bool hdd_get_th_sync_status(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +080057{
58 return qdf_atomic_read(&adapter->tsf_sync_ready_flag) != 0;
59}
60
61#else
Jeff Johnson8f389862017-08-29 14:19:23 -070062static inline bool hdd_get_th_sync_status(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +080063{
64 return true;
65}
66#endif
67
Yu Wangf5d5b5f2017-05-25 22:38:32 +080068static
Jeff Johnson8f389862017-08-29 14:19:23 -070069enum hdd_tsf_get_state hdd_tsf_check_conn_state(struct hdd_adapter *adapter)
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070070{
Yu Wangf5d5b5f2017-05-25 22:38:32 +080071 enum hdd_tsf_get_state ret = TSF_RETURN;
Jeff Johnson40dae4e2017-08-29 14:00:25 -070072 struct hdd_station_ctx *hdd_sta_ctx;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070073
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070074 if (adapter->device_mode == QDF_STA_MODE ||
Yu Wangf5d5b5f2017-05-25 22:38:32 +080075 adapter->device_mode == QDF_P2P_CLIENT_MODE) {
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070076 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
77 if (hdd_sta_ctx->conn_info.connState !=
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070078 eConnectionState_Associated) {
79 hdd_err("failed to cap tsf, not connect with ap");
Yu Wangf5d5b5f2017-05-25 22:38:32 +080080 ret = TSF_STA_NOT_CONNECTED_NO_TSF;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070081 }
Yu Wangf5d5b5f2017-05-25 22:38:32 +080082 } else if ((adapter->device_mode == QDF_SAP_MODE ||
83 adapter->device_mode == QDF_P2P_GO_MODE) &&
84 !(test_bit(SOFTAP_BSS_STARTED,
85 &adapter->event_flags))) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -070086 hdd_err("Soft AP / P2p GO not beaconing");
Yu Wangf5d5b5f2017-05-25 22:38:32 +080087 ret = TSF_SAP_NOT_STARTED_NO_TSF;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -070088 }
89 return ret;
90}
91
Jeff Johnson8f389862017-08-29 14:19:23 -070092static bool hdd_tsf_is_initialized(struct hdd_adapter *adapter)
Yu Wangf5d5b5f2017-05-25 22:38:32 +080093{
Jeff Johnson854cccd2017-08-28 11:39:24 -070094 struct hdd_context *hddctx;
Yu Wangf5d5b5f2017-05-25 22:38:32 +080095
96 if (!adapter) {
97 hdd_err("invalid adapter");
98 return false;
99 }
100
101 hddctx = WLAN_HDD_GET_CTX(adapter);
102 if (!hddctx) {
103 hdd_err("invalid hdd context");
104 return false;
105 }
106
Yu Wang000dc2f2017-05-26 17:38:48 +0800107 if (!qdf_atomic_read(&hddctx->tsf_ready_flag) ||
108 !hdd_get_th_sync_status(adapter)) {
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800109 hdd_err("TSF is not initialized");
110 return false;
111 }
112
113 return true;
114}
115
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700116/**
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700117 * hdd_tsf_reset_gpio() - Reset TSF GPIO used for host timer sync
118 * @adapter: pointer to adapter
119 *
120 * This function send WMI command to reset GPIO configured in FW after
121 * TSF get operation.
122 *
123 * Return: TSF_RETURN on Success, TSF_RESET_GPIO_FAIL on failure
124 */
yuanl2746f072018-09-21 19:19:16 +0800125#if defined(WLAN_FEATURE_TSF_PLUS_NOIRQ) && defined(WLAN_FEATURE_TSF_PLUS)
Jeff Johnson85b5c112017-08-11 15:15:23 -0700126static int hdd_tsf_reset_gpio(struct hdd_adapter *adapter)
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700127{
128 /* No GPIO Host timer sync for integrated WIFI Device */
129 return TSF_RETURN;
130}
yuanl2746f072018-09-21 19:19:16 +0800131
132/**
133 * hdd_tsf_set_gpio() - Set TSF GPIO used for host timer sync
134 * @hdd_ctx: pointer to hdd context
135 *
136 * This function is a dummy function for adrastea arch
137 *
138 * Return: QDF_STATUS_SUCCESS on Success
139 */
140
141static QDF_STATUS hdd_tsf_set_gpio(struct hdd_context *hdd_ctx)
142{
143 return QDF_STATUS_SUCCESS;
144}
145
146bool hdd_tsf_is_ptp_enabled(struct hdd_context *hdd)
147{
148 return (hdd && (hdd->config) &&
149 (hdd->config->tsf_ptp_options));
150}
151
152bool hdd_tsf_is_tx_set(struct hdd_context *hdd)
153{
154 return (hdd && (hdd->config) &&
155 ((hdd->config->tsf_ptp_options)
156 & CFG_SET_TSF_PTP_OPT_TX));
157}
158
159bool hdd_tsf_is_rx_set(struct hdd_context *hdd)
160{
161 return (hdd && (hdd->config) &&
162 ((hdd->config->tsf_ptp_options)
163 & CFG_SET_TSF_PTP_OPT_RX));
164}
165
166bool hdd_tsf_is_raw_set(struct hdd_context *hdd)
167{
168 return (hdd && (hdd->config) &&
169 ((hdd->config->tsf_ptp_options)
170 & CFG_SET_TSF_PTP_OPT_RAW));
171}
172
173bool hdd_tsf_is_dbg_fs_set(struct hdd_context *hdd)
174{
175 return (hdd && (hdd->config) &&
176 ((hdd->config->tsf_ptp_options)
177 & CFG_SET_TSF_DBG_FS));
178}
179
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700180#else
Jeff Johnson85b5c112017-08-11 15:15:23 -0700181static int hdd_tsf_reset_gpio(struct hdd_adapter *adapter)
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700182{
183 int ret;
Srinivas Girigowda388dea82017-03-25 11:49:56 -0700184
Jeff Johnson1b780e42017-10-31 14:11:45 -0700185 ret = wma_cli_set_command((int)adapter->session_id,
186 (int)GEN_PARAM_RESET_TSF_GPIO, adapter->session_id,
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700187 GEN_CMD);
188
189 if (ret != 0) {
190 hdd_err("tsf reset GPIO fail ");
191 ret = TSF_RESET_GPIO_FAIL;
192 } else {
193 ret = TSF_RETURN;
194 }
195 return ret;
196}
yuanl2746f072018-09-21 19:19:16 +0800197
198/**
199 * hdd_tsf_set_gpio() - Set TSF GPIO used for host timer sync
200 * @hdd_ctx: pointer to hdd context
201 *
202 * This function check GPIO and set GPIO as IRQ to FW side on
203 * none Adrastea arch
204 *
205 * Return: QDF_STATUS_SUCCESS on Success, others on Failure.
206 */
207static QDF_STATUS hdd_tsf_set_gpio(struct hdd_context *hdd_ctx)
208{
209 QDF_STATUS status;
210 uint32_t tsf_gpio_pin = TSF_GPIO_PIN_INVALID;
211
212 status = ucfg_fwol_get_tsf_gpio_pin(hdd_ctx->psoc, &tsf_gpio_pin);
213 if (QDF_IS_STATUS_ERROR(status))
214 return QDF_STATUS_E_INVAL;
215
216 if (tsf_gpio_pin == TSF_GPIO_PIN_INVALID)
217 return QDF_STATUS_E_INVAL;
218
219 status = sme_set_tsf_gpio(hdd_ctx->mac_handle,
220 tsf_gpio_pin);
221
222 return status;
223}
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700224#endif
225
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800226static enum hdd_tsf_op_result hdd_capture_tsf_internal(
Jeff Johnson8f389862017-08-29 14:19:23 -0700227 struct hdd_adapter *adapter, uint32_t *buf, int len)
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700228{
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800229 int ret;
Jeff Johnson854cccd2017-08-28 11:39:24 -0700230 struct hdd_context *hddctx;
yuanl2746f072018-09-21 19:19:16 +0800231 qdf_mc_timer_t *cap_timer;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700232
233 if (adapter == NULL || buf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700234 hdd_err("invalid pointer");
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800235 return HDD_TSF_OP_FAIL;
236 }
237
238 if (len != 1)
239 return HDD_TSF_OP_FAIL;
240
241 hddctx = WLAN_HDD_GET_CTX(adapter);
242 if (!hddctx) {
243 hdd_err("invalid hdd context");
244 return HDD_TSF_OP_FAIL;
245 }
246
247 if (!hdd_tsf_is_initialized(adapter)) {
248 buf[0] = TSF_NOT_READY;
249 return HDD_TSF_OP_SUCC;
250 }
251
252 buf[0] = hdd_tsf_check_conn_state(adapter);
253 if (buf[0] != TSF_RETURN)
254 return HDD_TSF_OP_SUCC;
255
256 if (qdf_atomic_inc_return(&hddctx->cap_tsf_flag) > 1) {
257 hdd_err("current in capture state");
258 buf[0] = TSF_CURRENT_IN_CAP_STATE;
259 return HDD_TSF_OP_SUCC;
260 }
261
262 /* record adapter for cap_tsf_irq_handler */
263 hddctx->cap_tsf_context = adapter;
264
yuanl2746f072018-09-21 19:19:16 +0800265 hdd_info("+ioctl issue cap tsf cmd");
266 cap_timer = &adapter->host_capture_req_timer;
267 qdf_mc_timer_init(cap_timer, QDF_TIMER_TYPE_SW,
268 hdd_capture_req_timer_expired_handler,
269 (void *)adapter);
270 qdf_mc_timer_start(cap_timer, WLAN_HDD_CAPTURE_TSF_REQ_TIMEOUT_MS);
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800271
272 /* Reset TSF value for new capture */
273 adapter->cur_target_time = 0;
274
275 buf[0] = TSF_RETURN;
276 init_completion(&tsf_sync_get_completion_evt);
Jeff Johnson1b780e42017-10-31 14:11:45 -0700277 ret = wma_cli_set_command((int)adapter->session_id,
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800278 (int)GEN_PARAM_CAPTURE_TSF,
Jeff Johnson1b780e42017-10-31 14:11:45 -0700279 adapter->session_id, GEN_CMD);
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800280 if (QDF_STATUS_SUCCESS != ret) {
281 hdd_err("cap tsf fail");
282 buf[0] = TSF_CAPTURE_FAIL;
283 hddctx->cap_tsf_context = NULL;
284 qdf_atomic_set(&hddctx->cap_tsf_flag, 0);
yuanl2746f072018-09-21 19:19:16 +0800285 qdf_mc_timer_stop(&adapter->host_capture_req_timer);
286 qdf_mc_timer_destroy(&adapter->host_capture_req_timer);
287
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800288 return HDD_TSF_OP_SUCC;
289 }
yuanl2746f072018-09-21 19:19:16 +0800290 hdd_info("-ioctl return cap tsf cmd");
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800291 return HDD_TSF_OP_SUCC;
292}
293
294static enum hdd_tsf_op_result hdd_indicate_tsf_internal(
Jeff Johnson8f389862017-08-29 14:19:23 -0700295 struct hdd_adapter *adapter, uint32_t *buf, int len)
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800296{
297 int ret;
Jeff Johnson854cccd2017-08-28 11:39:24 -0700298 struct hdd_context *hddctx;
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800299
300 if (!adapter || !buf) {
301 hdd_err("invalid pointer");
302 return HDD_TSF_OP_FAIL;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700303 }
304
305 if (len != 3)
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800306 return HDD_TSF_OP_FAIL;
307
308 hddctx = WLAN_HDD_GET_CTX(adapter);
309 if (!hddctx) {
310 hdd_err("invalid hdd context");
311 return HDD_TSF_OP_FAIL;
312 }
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700313
314 buf[1] = 0;
315 buf[2] = 0;
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800316
317 if (!hdd_tsf_is_initialized(adapter)) {
318 buf[0] = TSF_NOT_READY;
319 return HDD_TSF_OP_SUCC;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700320 }
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800321
322 buf[0] = hdd_tsf_check_conn_state(adapter);
323 if (buf[0] != TSF_RETURN)
324 return HDD_TSF_OP_SUCC;
325
326 if (adapter->cur_target_time == 0) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -0700327 hdd_info("TSF value not received");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700328 buf[0] = TSF_NOT_RETURNED_BY_FW;
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800329 return HDD_TSF_OP_SUCC;
Jeff Johnsona5fb2182017-10-06 20:27:38 -0700330 }
331
332 buf[0] = TSF_RETURN;
333 buf[1] = (uint32_t)(adapter->cur_target_time & 0xffffffff);
334 buf[2] = (uint32_t)((adapter->cur_target_time >> 32) &
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800335 0xffffffff);
336
Jeff Johnsona5fb2182017-10-06 20:27:38 -0700337 if (!qdf_atomic_read(&hddctx->cap_tsf_flag)) {
338 hdd_info("old: status=%u, tsf_low=%u, tsf_high=%u",
339 buf[0], buf[1], buf[2]);
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800340 return HDD_TSF_OP_SUCC;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -0700341 }
Jeff Johnsona5fb2182017-10-06 20:27:38 -0700342
343 ret = hdd_tsf_reset_gpio(adapter);
344 if (0 != ret) {
345 hdd_err("reset tsf gpio fail");
346 buf[0] = TSF_RESET_GPIO_FAIL;
347 return HDD_TSF_OP_SUCC;
348 }
349 hddctx->cap_tsf_context = NULL;
350 qdf_atomic_set(&hddctx->cap_tsf_flag, 0);
351 hdd_info("get tsf cmd,status=%u, tsf_low=%u, tsf_high=%u",
352 buf[0], buf[1], buf[2]);
353
354 return HDD_TSF_OP_SUCC;
Yu Wangf5d5b5f2017-05-25 22:38:32 +0800355}
356
Yu Wang000dc2f2017-05-26 17:38:48 +0800357#ifdef WLAN_FEATURE_TSF_PLUS
358/* unit for target time: us; host time: ns */
359#define HOST_TO_TARGET_TIME_RATIO NSEC_PER_USEC
gaolezc2b72082017-10-30 15:29:59 +0800360#define MAX_ALLOWED_DEVIATION_NS (100 * NSEC_PER_USEC)
Yu Wang000dc2f2017-05-26 17:38:48 +0800361#define MAX_CONTINUOUS_ERROR_CNT 3
Yu Wang04ccd762017-05-31 19:29:43 +0800362
363/* to distinguish 32-bit overflow case, this inverval should:
364 * equal or less than (1/2 * OVERFLOW_INDICATOR32 us)
365 */
gaolezc2b72082017-10-30 15:29:59 +0800366#define WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC 10
Yu Wang04ccd762017-05-31 19:29:43 +0800367#define OVERFLOW_INDICATOR32 (((int64_t)0x1) << 32)
gaolezc2b72082017-10-30 15:29:59 +0800368#define CAP_TSF_TIMER_FIX_SEC 1
Yu Wang000dc2f2017-05-26 17:38:48 +0800369
370/**
371 * TS_STATUS - timestamp status
372 *
373 * HDD_TS_STATUS_WAITING: one of the stamp-pair
374 * is not updated
375 * HDD_TS_STATUS_READY: valid tstamp-pair
376 * HDD_TS_STATUS_INVALID: invalid tstamp-pair
377 */
378enum hdd_ts_status {
379 HDD_TS_STATUS_WAITING,
380 HDD_TS_STATUS_READY,
381 HDD_TS_STATUS_INVALID
382};
383
384static
Jeff Johnson8f389862017-08-29 14:19:23 -0700385enum hdd_tsf_op_result __hdd_start_tsf_sync(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +0800386{
387 QDF_STATUS ret;
388
389 if (!hdd_get_th_sync_status(adapter)) {
390 hdd_err("Host Target sync has not initialized");
391 return HDD_TSF_OP_FAIL;
392 }
393
394 ret = qdf_mc_timer_start(&adapter->host_target_sync_timer,
395 WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS);
396 if (ret != QDF_STATUS_SUCCESS && ret != QDF_STATUS_E_ALREADY) {
397 hdd_err("Failed to start timer, ret: %d", ret);
398 return HDD_TSF_OP_FAIL;
399 }
400 return HDD_TSF_OP_SUCC;
401}
402
403static
Jeff Johnson8f389862017-08-29 14:19:23 -0700404enum hdd_tsf_op_result __hdd_stop_tsf_sync(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +0800405{
406 QDF_STATUS ret;
407
408 if (!hdd_get_th_sync_status(adapter)) {
409 hdd_err("Host Target sync has not initialized");
410 return HDD_TSF_OP_SUCC;
411 }
412
413 ret = qdf_mc_timer_stop(&adapter->host_target_sync_timer);
414 if (ret != QDF_STATUS_SUCCESS) {
415 hdd_err("Failed to stop timer, ret: %d", ret);
416 return HDD_TSF_OP_FAIL;
417 }
418 return HDD_TSF_OP_SUCC;
419}
420
Jeff Johnson8f389862017-08-29 14:19:23 -0700421static inline void hdd_reset_timestamps(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +0800422{
423 qdf_spin_lock_bh(&adapter->host_target_sync_lock);
424 adapter->cur_host_time = 0;
425 adapter->cur_target_time = 0;
426 adapter->last_host_time = 0;
427 adapter->last_target_time = 0;
428 qdf_spin_unlock_bh(&adapter->host_target_sync_lock);
429}
430
431/**
432 * hdd_check_timestamp_status() - return the tstamp status
433 *
434 * @last_target_time: the last saved target time
435 * @last_host_time: the last saved host time
436 * @cur_target_time : new target time
437 * @cur_host_time : new host time
438 *
439 * This function check the new timstamp-pair(cur_host_time/cur_target_time)
440 *
441 * Return:
442 * HDD_TS_STATUS_WAITING: cur_host_time or cur_host_time is 0
443 * HDD_TS_STATUS_READY: cur_target_time/cur_host_time is a valid pair,
444 * and can be saved
445 * HDD_TS_STATUS_INVALID: cur_target_time/cur_host_time is a invalid pair,
446 * should be discard
447 */
448static
449enum hdd_ts_status hdd_check_timestamp_status(
450 uint64_t last_target_time,
451 uint64_t last_host_time,
452 uint64_t cur_target_time,
453 uint64_t cur_host_time)
454{
455 uint64_t delta_ns, delta_target_time, delta_host_time;
456
457 /* one or more are not updated, need to wait */
458 if (cur_target_time == 0 || cur_host_time == 0)
459 return HDD_TS_STATUS_WAITING;
460
461 /* init value, it's the first time to update the pair */
462 if (last_target_time == 0 && last_host_time == 0)
463 return HDD_TS_STATUS_READY;
464
465 /* the new values should be greater than the saved values */
466 if ((cur_target_time <= last_target_time) ||
467 (cur_host_time <= last_host_time)) {
468 hdd_err("Invalid timestamps!last_target_time: %llu;"
469 "last_host_time: %llu; cur_target_time: %llu;"
470 "cur_host_time: %llu",
471 last_target_time, last_host_time,
472 cur_target_time, cur_host_time);
473 return HDD_TS_STATUS_INVALID;
474 }
475
476 delta_target_time = (cur_target_time - last_target_time) *
477 HOST_TO_TARGET_TIME_RATIO;
478 delta_host_time = cur_host_time - last_host_time;
479
480 /*
481 * DO NOT use abs64() , a big uint64 value might be turned to
482 * a small int64 value
483 */
484 delta_ns = ((delta_target_time > delta_host_time) ?
485 (delta_target_time - delta_host_time) :
486 (delta_host_time - delta_target_time));
487
488 /* the deviation should be smaller than a threshold */
489 if (delta_ns > MAX_ALLOWED_DEVIATION_NS) {
yuanl2746f072018-09-21 19:19:16 +0800490 hdd_warn("Invalid timestamps - delta: %llu ns", delta_ns);
Yu Wang000dc2f2017-05-26 17:38:48 +0800491 return HDD_TS_STATUS_INVALID;
492 }
493 return HDD_TS_STATUS_READY;
494}
495
Jeff Johnson8f389862017-08-29 14:19:23 -0700496static void hdd_update_timestamp(struct hdd_adapter *adapter,
Yu Wang000dc2f2017-05-26 17:38:48 +0800497 uint64_t target_time, uint64_t host_time)
498{
499 int interval = 0;
500 enum hdd_ts_status sync_status;
501
502 if (!adapter)
503 return;
504
Yu Wang70757d52017-06-08 12:15:42 +0800505 /* host time is updated in IRQ context, it's always before target time,
506 * and so no need to try update last_host_time at present;
507 * since the interval of capturing TSF
508 * (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC) is long enough, host and target
509 * time are updated in pairs, and one by one, we can return here to
510 * avoid requiring spin lock, and to speed up the IRQ processing.
511 */
512 if (host_time > 0) {
yuanl2746f072018-09-21 19:19:16 +0800513 /* on ADREASTEA ach, Qtime is used to sync host and tsf time as a
514 * intermedia there is no IRQ to sync up TSF-HOST, so host time in ns
515 * and target in us will be updated at the same time in WMI command
516 * callback
517 */
Yu Wang70757d52017-06-08 12:15:42 +0800518 adapter->cur_host_time = host_time;
yuanl2746f072018-09-21 19:19:16 +0800519
520 if (0 == target_time)
521 return;
Yu Wang70757d52017-06-08 12:15:42 +0800522 }
523
Yu Wang000dc2f2017-05-26 17:38:48 +0800524 qdf_spin_lock_bh(&adapter->host_target_sync_lock);
525 if (target_time > 0)
526 adapter->cur_target_time = target_time;
Yu Wang000dc2f2017-05-26 17:38:48 +0800527
528 sync_status = hdd_check_timestamp_status(adapter->last_target_time,
529 adapter->last_host_time,
530 adapter->cur_target_time,
531 adapter->cur_host_time);
yuanl2746f072018-09-21 19:19:16 +0800532 hdd_info("sync_status %d", sync_status);
Yu Wang000dc2f2017-05-26 17:38:48 +0800533 switch (sync_status) {
534 case HDD_TS_STATUS_INVALID:
535 if (++adapter->continuous_error_count <
536 MAX_CONTINUOUS_ERROR_CNT) {
537 interval =
538 WLAN_HDD_CAPTURE_TSF_INIT_INTERVAL_MS;
yuanl2746f072018-09-21 19:19:16 +0800539 adapter->last_target_time = adapter->cur_target_time;
540 adapter->last_host_time = adapter->cur_host_time;
Yu Wang000dc2f2017-05-26 17:38:48 +0800541 adapter->cur_target_time = 0;
542 adapter->cur_host_time = 0;
543 break;
544 }
yuanl2746f072018-09-21 19:19:16 +0800545 hdd_warn("Reach the max continuous error count");
Yu Wang000dc2f2017-05-26 17:38:48 +0800546 /*
547 * fall through:
548 * If reach MAX_CONTINUOUS_ERROR_CNT, treat it as a
549 * valid pair
550 */
551 case HDD_TS_STATUS_READY:
552 adapter->last_target_time = adapter->cur_target_time;
553 adapter->last_host_time = adapter->cur_host_time;
554 adapter->cur_target_time = 0;
555 adapter->cur_host_time = 0;
556 hdd_info("ts-pair updated: target: %llu; host: %llu",
557 adapter->last_target_time,
558 adapter->last_host_time);
gaolezc2b72082017-10-30 15:29:59 +0800559
560 /*
561 * TSF-HOST need to be updated in at most
562 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, it couldn't be achieved
563 * if the timer interval is also
564 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC, due to processing or
565 * schedule delay. So deduct several seconds from
566 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC.
567 * Without this change, hdd_get_hosttime_from_targettime() will
568 * get wrong host time when it's longer than
569 * WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC from last
570 * TSF-HOST update.
571 */
572 interval = (WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC -
573 CAP_TSF_TIMER_FIX_SEC) * MSEC_PER_SEC;
Yu Wang000dc2f2017-05-26 17:38:48 +0800574 adapter->continuous_error_count = 0;
yuanl2746f072018-09-21 19:19:16 +0800575 hdd_debug("ts-pair updated: interval: %d",
576 interval);
Yu Wang000dc2f2017-05-26 17:38:48 +0800577 break;
578 case HDD_TS_STATUS_WAITING:
579 interval = 0;
yuanl2746f072018-09-21 19:19:16 +0800580 hdd_warn("TS status is waiting due to one or more pair not updated");
Yu Wang000dc2f2017-05-26 17:38:48 +0800581 break;
582 }
583 qdf_spin_unlock_bh(&adapter->host_target_sync_lock);
584
585 if (interval > 0)
586 qdf_mc_timer_start(&adapter->host_target_sync_timer, interval);
587}
588
Jeff Johnson8f389862017-08-29 14:19:23 -0700589static inline bool hdd_tsf_is_in_cap(struct hdd_adapter *adapter)
Yu Wang04ccd762017-05-31 19:29:43 +0800590{
Jeff Johnson854cccd2017-08-28 11:39:24 -0700591 struct hdd_context *hddctx;
Yu Wang04ccd762017-05-31 19:29:43 +0800592
593 hddctx = WLAN_HDD_GET_CTX(adapter);
594 if (!hddctx)
595 return false;
596
597 return qdf_atomic_read(&hddctx->cap_tsf_flag) > 0;
598}
599
600/* define 64bit plus/minus to deal with overflow */
601static inline int hdd_64bit_plus(uint64_t x, int64_t y, uint64_t *ret)
602{
603 if ((y < 0 && (-y) > x) ||
604 (y > 0 && (y > U64_MAX - x))) {
605 *ret = 0;
606 return -EINVAL;
607 }
608
609 *ret = x + y;
610 return 0;
611}
612
Yu Wang0ea10172017-06-02 13:40:52 +0800613static inline int hdd_uint64_plus(uint64_t x, uint64_t y, uint64_t *ret)
614{
615 if (!ret)
616 return -EINVAL;
617
618 if (x > (U64_MAX - y)) {
619 *ret = 0;
620 return -EINVAL;
621 }
622
623 *ret = x + y;
624 return 0;
625}
626
627static inline int hdd_uint64_minus(uint64_t x, uint64_t y, uint64_t *ret)
628{
629 if (!ret)
630 return -EINVAL;
631
632 if (x < y) {
633 *ret = 0;
634 return -EINVAL;
635 }
636
637 *ret = x - y;
638 return 0;
639}
640
Yu Wang04ccd762017-05-31 19:29:43 +0800641static inline int32_t hdd_get_hosttime_from_targettime(
Jeff Johnson8f389862017-08-29 14:19:23 -0700642 struct hdd_adapter *adapter, uint64_t target_time,
Yu Wang04ccd762017-05-31 19:29:43 +0800643 uint64_t *host_time)
644{
645 int32_t ret = -EINVAL;
646 int64_t delta32_target;
647 bool in_cap_state;
gaolezd11c7c52018-03-28 15:04:13 +0800648 int64_t normal_interval_target;
Yu Wang04ccd762017-05-31 19:29:43 +0800649
650 in_cap_state = hdd_tsf_is_in_cap(adapter);
651
652 /*
653 * To avoid check the lock when it's not capturing tsf
654 * (the tstamp-pair won't be changed)
655 */
656 if (in_cap_state)
657 qdf_spin_lock_bh(&adapter->host_target_sync_lock);
658
659 /* at present, target_time is only 32bit in fact */
660 delta32_target = (int64_t)((target_time & U32_MAX) -
661 (adapter->last_target_time & U32_MAX));
662
gaolezd11c7c52018-03-28 15:04:13 +0800663 normal_interval_target =
664 qdf_do_div(WLAN_HDD_CAPTURE_TSF_INTERVAL_SEC *
665 NSEC_PER_SEC, HOST_TO_TARGET_TIME_RATIO);
666
Yu Wang04ccd762017-05-31 19:29:43 +0800667 if (delta32_target <
gaolezd11c7c52018-03-28 15:04:13 +0800668 (normal_interval_target - OVERFLOW_INDICATOR32))
Yu Wang04ccd762017-05-31 19:29:43 +0800669 delta32_target += OVERFLOW_INDICATOR32;
670 else if (delta32_target >
gaolezd11c7c52018-03-28 15:04:13 +0800671 (OVERFLOW_INDICATOR32 - normal_interval_target))
Yu Wang04ccd762017-05-31 19:29:43 +0800672 delta32_target -= OVERFLOW_INDICATOR32;
673
674 ret = hdd_64bit_plus(adapter->last_host_time,
675 HOST_TO_TARGET_TIME_RATIO * delta32_target,
676 host_time);
677
678 if (in_cap_state)
679 qdf_spin_unlock_bh(&adapter->host_target_sync_lock);
680
681 return ret;
682}
683
Yu Wang0ea10172017-06-02 13:40:52 +0800684static inline int32_t hdd_get_targettime_from_hosttime(
Jeff Johnson8f389862017-08-29 14:19:23 -0700685 struct hdd_adapter *adapter, uint64_t host_time,
Yu Wang0ea10172017-06-02 13:40:52 +0800686 uint64_t *target_time)
687{
688 int32_t ret = -EINVAL;
689 bool in_cap_state;
690
691 if (!adapter || host_time == 0)
692 return ret;
693
694 in_cap_state = hdd_tsf_is_in_cap(adapter);
695 if (in_cap_state)
696 qdf_spin_lock_bh(&adapter->host_target_sync_lock);
697
698 if (host_time < adapter->last_host_time)
699 ret = hdd_uint64_minus(adapter->last_target_time,
Yu Wangc9ef24f2017-09-15 18:37:07 +0800700 qdf_do_div(adapter->last_host_time -
701 host_time,
702 HOST_TO_TARGET_TIME_RATIO),
Yu Wang0ea10172017-06-02 13:40:52 +0800703 target_time);
704 else
705 ret = hdd_uint64_plus(adapter->last_target_time,
Yu Wangc9ef24f2017-09-15 18:37:07 +0800706 qdf_do_div(host_time -
707 adapter->last_host_time,
708 HOST_TO_TARGET_TIME_RATIO),
Yu Wang0ea10172017-06-02 13:40:52 +0800709 target_time);
710
711 if (in_cap_state)
712 qdf_spin_unlock_bh(&adapter->host_target_sync_lock);
713
714 return ret;
715}
716
Yu Wang66a250b2017-07-19 11:46:40 +0800717static inline
718uint64_t hdd_get_monotonic_host_time(struct hdd_context *hdd_ctx)
Yu Wang000dc2f2017-05-26 17:38:48 +0800719{
yuanl2746f072018-09-21 19:19:16 +0800720 return hdd_tsf_is_raw_set(hdd_ctx) ?
Yu Wang66a250b2017-07-19 11:46:40 +0800721 ktime_get_ns() : ktime_get_real_ns();
Yu Wang000dc2f2017-05-26 17:38:48 +0800722}
723
Yu Wang0ea10172017-06-02 13:40:52 +0800724static ssize_t __hdd_wlan_tsf_show(struct device *dev,
725 struct device_attribute *attr, char *buf)
726{
Jeff Johnson40dae4e2017-08-29 14:00:25 -0700727 struct hdd_station_ctx *hdd_sta_ctx;
Jeff Johnson8f389862017-08-29 14:19:23 -0700728 struct hdd_adapter *adapter;
Yu Wang66a250b2017-07-19 11:46:40 +0800729 struct hdd_context *hdd_ctx;
Yu Wang0ea10172017-06-02 13:40:52 +0800730 ssize_t size;
731 uint64_t host_time, target_time;
732
733 struct net_device *net_dev = container_of(dev, struct net_device, dev);
734
Jeff Johnson8f389862017-08-29 14:19:23 -0700735 adapter = (struct hdd_adapter *)(netdev_priv(net_dev));
Yu Wang0ea10172017-06-02 13:40:52 +0800736 if (adapter->magic != WLAN_HDD_ADAPTER_MAGIC)
737 return scnprintf(buf, PAGE_SIZE, "Invalid device\n");
738
739 if (!hdd_get_th_sync_status(adapter))
740 return scnprintf(buf, PAGE_SIZE,
741 "TSF sync is not initialized\n");
742
743 hdd_sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
744 if (eConnectionState_Associated != hdd_sta_ctx->conn_info.connState)
745 return scnprintf(buf, PAGE_SIZE, "NOT connected\n");
746
Yu Wang66a250b2017-07-19 11:46:40 +0800747 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
748 if (!hdd_ctx)
749 return scnprintf(buf, PAGE_SIZE, "Invalid HDD context\n");
750
751 host_time = hdd_get_monotonic_host_time(hdd_ctx);
yuanl2746f072018-09-21 19:19:16 +0800752
Yu Wang0ea10172017-06-02 13:40:52 +0800753 if (hdd_get_targettime_from_hosttime(adapter, host_time,
754 &target_time))
755 size = scnprintf(buf, PAGE_SIZE, "Invalid timestamp\n");
756 else
757 size = scnprintf(buf, PAGE_SIZE, "%s%llu %llu %pM\n",
758 buf, target_time, host_time,
759 hdd_sta_ctx->conn_info.bssId.bytes);
yuanl2746f072018-09-21 19:19:16 +0800760
Yu Wang0ea10172017-06-02 13:40:52 +0800761 return size;
762}
763
764static ssize_t hdd_wlan_tsf_show(struct device *dev,
765 struct device_attribute *attr, char *buf)
766{
767 ssize_t ret;
768
769 cds_ssr_protect(__func__);
770 ret = __hdd_wlan_tsf_show(dev, attr, buf);
771 cds_ssr_unprotect(__func__);
772
773 return ret;
774}
775
776static DEVICE_ATTR(tsf, 0400, hdd_wlan_tsf_show, NULL);
777
Yu Wang000dc2f2017-05-26 17:38:48 +0800778static void hdd_capture_tsf_timer_expired_handler(void *arg)
779{
780 uint32_t tsf_op_resp;
Jeff Johnson8f389862017-08-29 14:19:23 -0700781 struct hdd_adapter *adapter;
Yu Wang000dc2f2017-05-26 17:38:48 +0800782
783 if (!arg)
784 return;
785
Jeff Johnson8f389862017-08-29 14:19:23 -0700786 adapter = (struct hdd_adapter *)arg;
Yu Wang000dc2f2017-05-26 17:38:48 +0800787 hdd_capture_tsf_internal(adapter, &tsf_op_resp, 1);
788}
789
yuanl2746f072018-09-21 19:19:16 +0800790#ifndef WLAN_FEATURE_TSF_PLUS_NOIRQ
Yu Wang000dc2f2017-05-26 17:38:48 +0800791static irqreturn_t hdd_tsf_captured_irq_handler(int irq, void *arg)
792{
Jeff Johnson8f389862017-08-29 14:19:23 -0700793 struct hdd_adapter *adapter;
Jeff Johnson854cccd2017-08-28 11:39:24 -0700794 struct hdd_context *hdd_ctx;
Yu Wang000dc2f2017-05-26 17:38:48 +0800795 uint64_t host_time;
796 char *name = NULL;
797
798 if (!arg)
799 return IRQ_NONE;
800
Jeff Johnson854cccd2017-08-28 11:39:24 -0700801 hdd_ctx = (struct hdd_context *)arg;
Yu Wang66a250b2017-07-19 11:46:40 +0800802 host_time = hdd_get_monotonic_host_time(hdd_ctx);
Yu Wang000dc2f2017-05-26 17:38:48 +0800803
804 adapter = hdd_ctx->cap_tsf_context;
805 if (!adapter)
806 return IRQ_HANDLED;
807
808 if (!hdd_tsf_is_initialized(adapter)) {
809 hdd_err("tsf is not init, ignore irq");
810 return IRQ_HANDLED;
811 }
812
813 hdd_update_timestamp(adapter, 0, host_time);
814 if (adapter->dev)
815 name = adapter->dev->name;
816
817 hdd_info("irq: %d - iface: %s - host_time: %llu",
818 irq, (!name ? "none" : name), host_time);
819
820 return IRQ_HANDLED;
821}
yuanl2746f072018-09-21 19:19:16 +0800822#endif
823
824void hdd_capture_req_timer_expired_handler(void *arg)
825{
826 struct hdd_adapter *adapter;
827 struct hdd_context *hdd_ctx;
828 QDF_TIMER_STATE capture_req_timer_status;
829 qdf_mc_timer_t *sync_timer;
830 int interval;
831 int ret;
832
833 if (!arg)
834 return;
835 adapter = (struct hdd_adapter *)arg;
836
837 hdd_ctx = WLAN_HDD_GET_CTX(adapter);
838 if (!hdd_ctx) {
839 hdd_warn("invalid hdd context");
840 return;
841 }
842
843 if (!hdd_tsf_is_initialized(adapter)) {
844 qdf_mc_timer_destroy(&adapter->host_capture_req_timer);
845 hdd_warn("tsf not init");
846 return;
847 }
848
849 qdf_spin_lock_bh(&adapter->host_target_sync_lock);
850 adapter->cur_host_time = 0;
851 adapter->cur_target_time = 0;
852 qdf_spin_unlock_bh(&adapter->host_target_sync_lock);
853
854 ret = hdd_tsf_reset_gpio(adapter);
855 if (0 != ret)
856 hdd_info("reset tsf gpio fail");
857
858 hdd_ctx->cap_tsf_context = NULL;
859 qdf_atomic_set(&hdd_ctx->cap_tsf_flag, 0);
860 qdf_mc_timer_destroy(&adapter->host_capture_req_timer);
861
862 sync_timer = &adapter->host_target_sync_timer;
863 capture_req_timer_status =
864 qdf_mc_timer_get_current_state(sync_timer);
865
866 if (capture_req_timer_status == QDF_TIMER_STATE_UNUSED) {
867 hdd_warn("invalid timer status");
868 return;
869 }
870
871 interval = WLAN_HDD_CAPTURE_TSF_RESYNC_INTERVAL * MSEC_PER_SEC;
872 qdf_mc_timer_start(sync_timer, interval);
873}
Yu Wang000dc2f2017-05-26 17:38:48 +0800874
Jeff Johnson8f389862017-08-29 14:19:23 -0700875static enum hdd_tsf_op_result hdd_tsf_sync_init(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +0800876{
877 QDF_STATUS ret;
Jeff Johnson854cccd2017-08-28 11:39:24 -0700878 struct hdd_context *hddctx;
Yu Wang0ea10172017-06-02 13:40:52 +0800879 struct net_device *net_dev;
Yu Wang000dc2f2017-05-26 17:38:48 +0800880
881 if (!adapter)
882 return HDD_TSF_OP_FAIL;
883
884 hddctx = WLAN_HDD_GET_CTX(adapter);
885 if (!hddctx) {
886 hdd_err("invalid hdd context");
887 return HDD_TSF_OP_FAIL;
888 }
889
890 if (!qdf_atomic_read(&hddctx->tsf_ready_flag)) {
891 hdd_err("TSF feature has NOT been initialized");
892 return HDD_TSF_OP_FAIL;
893 }
894
895 if (hdd_get_th_sync_status(adapter)) {
896 hdd_err("Host Target sync has been initialized!!");
897 return HDD_TSF_OP_SUCC;
898 }
899
900 qdf_spinlock_create(&adapter->host_target_sync_lock);
901
902 hdd_reset_timestamps(adapter);
903
904 ret = qdf_mc_timer_init(&adapter->host_target_sync_timer,
905 QDF_TIMER_TYPE_SW,
906 hdd_capture_tsf_timer_expired_handler,
907 (void *)adapter);
908 if (ret != QDF_STATUS_SUCCESS) {
909 hdd_err("Failed to init timer, ret: %d", ret);
910 goto fail;
911 }
912
Yu Wang0ea10172017-06-02 13:40:52 +0800913 net_dev = adapter->dev;
yuanl2746f072018-09-21 19:19:16 +0800914 if (net_dev && hdd_tsf_is_dbg_fs_set(hddctx))
Yu Wang66a250b2017-07-19 11:46:40 +0800915 device_create_file(&net_dev->dev, &dev_attr_tsf);
Yu Wang000dc2f2017-05-26 17:38:48 +0800916 hdd_set_th_sync_status(adapter, true);
917
918 return HDD_TSF_OP_SUCC;
919fail:
920 hdd_set_th_sync_status(adapter, false);
921 return HDD_TSF_OP_FAIL;
922}
923
Jeff Johnson8f389862017-08-29 14:19:23 -0700924static enum hdd_tsf_op_result hdd_tsf_sync_deinit(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +0800925{
926 QDF_STATUS ret;
Jeff Johnson854cccd2017-08-28 11:39:24 -0700927 struct hdd_context *hddctx;
Yu Wang0ea10172017-06-02 13:40:52 +0800928 struct net_device *net_dev;
Yu Wang000dc2f2017-05-26 17:38:48 +0800929
930 if (!adapter)
931 return HDD_TSF_OP_FAIL;
932
933 if (!hdd_get_th_sync_status(adapter)) {
934 hdd_err("Host Target sync has not been initialized!!");
935 return HDD_TSF_OP_SUCC;
936 }
937
938 hdd_set_th_sync_status(adapter, false);
Yu Wang000dc2f2017-05-26 17:38:48 +0800939 ret = qdf_mc_timer_destroy(&adapter->host_target_sync_timer);
940 if (ret != QDF_STATUS_SUCCESS)
941 hdd_err("Failed to destroy timer, ret: %d", ret);
942
943 hddctx = WLAN_HDD_GET_CTX(adapter);
944
945 /* reset the cap_tsf flag and gpio if needed */
946 if (hddctx && qdf_atomic_read(&hddctx->cap_tsf_flag) &&
947 hddctx->cap_tsf_context == adapter) {
948 int reset_ret = hdd_tsf_reset_gpio(adapter);
949
950 if (reset_ret)
951 hdd_err("Failed to reset tsf gpio, ret:%d",
952 reset_ret);
953 hddctx->cap_tsf_context = NULL;
954 qdf_atomic_set(&hddctx->cap_tsf_flag, 0);
955 }
956
957 hdd_reset_timestamps(adapter);
Yu Wang66a250b2017-07-19 11:46:40 +0800958
959 net_dev = adapter->dev;
yuanl2746f072018-09-21 19:19:16 +0800960 if (net_dev && hdd_tsf_is_dbg_fs_set(hddctx)) {
Yu Wang66a250b2017-07-19 11:46:40 +0800961 struct device *dev = &net_dev->dev;
962
963 device_remove_file(dev, &dev_attr_tsf);
964 }
Yu Wang000dc2f2017-05-26 17:38:48 +0800965 return HDD_TSF_OP_SUCC;
966}
967
yuanl2746f072018-09-21 19:19:16 +0800968#ifdef WLAN_FEATURE_TSF_PLUS_NOIRQ
969static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf)
970{
971 uint32_t tsf_op_resp[3];
972 struct hdd_context *hddctx;
973 uint64_t host_time;
974
975 hddctx = WLAN_HDD_GET_CTX(adapter);
976 host_time = hdd_get_monotonic_host_time(hddctx);
977 hdd_indicate_tsf_internal(adapter, tsf_op_resp, 3);
978 host_time -= qdf_log_timestamp_to_usecs(qdf_get_log_timestamp()
979 - adapter->tsf_sync_soc_timer) * NSEC_PER_USEC;
980 adapter->cur_host_time = host_time;
981 hdd_update_timestamp(adapter, tsf, host_time);
982}
983#else
Jeff Johnson8f389862017-08-29 14:19:23 -0700984static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf)
Yu Wang000dc2f2017-05-26 17:38:48 +0800985{
986 uint32_t tsf_op_resp[3];
987
988 hdd_indicate_tsf_internal(adapter, tsf_op_resp, 3);
989 hdd_update_timestamp(adapter, tsf, 0);
990}
yuanl2746f072018-09-21 19:19:16 +0800991#endif
Yu Wang000dc2f2017-05-26 17:38:48 +0800992
Yu Wang04ccd762017-05-31 19:29:43 +0800993static inline
994enum hdd_tsf_op_result hdd_netbuf_timestamp(qdf_nbuf_t netbuf,
995 uint64_t target_time)
996{
Jeff Johnson8f389862017-08-29 14:19:23 -0700997 struct hdd_adapter *adapter;
Yu Wang04ccd762017-05-31 19:29:43 +0800998 struct net_device *net_dev = netbuf->dev;
999
1000 if (!net_dev)
1001 return HDD_TSF_OP_FAIL;
1002
Jeff Johnson8f389862017-08-29 14:19:23 -07001003 adapter = (struct hdd_adapter *)(netdev_priv(net_dev));
Yu Wang04ccd762017-05-31 19:29:43 +08001004 if (adapter && adapter->magic == WLAN_HDD_ADAPTER_MAGIC &&
1005 hdd_get_th_sync_status(adapter)) {
1006 uint64_t host_time;
1007 int32_t ret = hdd_get_hosttime_from_targettime(adapter,
1008 target_time, &host_time);
1009 if (!ret) {
1010 netbuf->tstamp = ns_to_ktime(host_time);
1011 return HDD_TSF_OP_SUCC;
1012 }
1013 }
1014
1015 return HDD_TSF_OP_FAIL;
1016}
1017
Jeff Johnson8f389862017-08-29 14:19:23 -07001018int hdd_start_tsf_sync(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +08001019{
1020 enum hdd_tsf_op_result ret;
1021
1022 if (!adapter)
1023 return -EINVAL;
1024
1025 ret = hdd_tsf_sync_init(adapter);
1026 if (ret != HDD_TSF_OP_SUCC) {
1027 hdd_err("Failed to init tsf sync, ret: %d", ret);
1028 return -EINVAL;
1029 }
1030
1031 return (__hdd_start_tsf_sync(adapter) ==
1032 HDD_TSF_OP_SUCC) ? 0 : -EINVAL;
1033}
1034
Jeff Johnson8f389862017-08-29 14:19:23 -07001035int hdd_stop_tsf_sync(struct hdd_adapter *adapter)
Yu Wang000dc2f2017-05-26 17:38:48 +08001036{
1037 enum hdd_tsf_op_result ret;
1038
1039 if (!adapter)
1040 return -EINVAL;
1041
1042 ret = __hdd_stop_tsf_sync(adapter);
1043 if (ret != HDD_TSF_OP_SUCC)
1044 return -EINVAL;
1045
1046 ret = hdd_tsf_sync_deinit(adapter);
1047 if (ret != HDD_TSF_OP_SUCC) {
1048 hdd_err("Failed to deinit tsf sync, ret: %d", ret);
1049 return -EINVAL;
1050 }
1051 return 0;
1052}
1053
Yu Wang04ccd762017-05-31 19:29:43 +08001054int hdd_tx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time)
1055{
1056 struct sock *sk = netbuf->sk;
1057
1058 if (!sk)
1059 return -EINVAL;
1060
1061 if ((skb_shinfo(netbuf)->tx_flags & SKBTX_SW_TSTAMP) &&
1062 !(skb_shinfo(netbuf)->tx_flags & SKBTX_IN_PROGRESS)) {
1063 struct sock_exterr_skb *serr;
1064 qdf_nbuf_t new_netbuf;
1065 int err;
1066
1067 if (hdd_netbuf_timestamp(netbuf, target_time) !=
1068 HDD_TSF_OP_SUCC)
1069 return -EINVAL;
1070
1071 new_netbuf = qdf_nbuf_clone(netbuf);
1072 if (!new_netbuf)
1073 return -ENOMEM;
1074
1075 serr = SKB_EXT_ERR(new_netbuf);
1076 memset(serr, 0, sizeof(*serr));
1077 serr->ee.ee_errno = ENOMSG;
1078 serr->ee.ee_origin = SO_EE_ORIGIN_TIMESTAMPING;
1079
1080 err = sock_queue_err_skb(sk, new_netbuf);
1081 if (err) {
1082 qdf_nbuf_free(new_netbuf);
1083 return err;
1084 }
1085
1086 return 0;
1087 }
1088 return -EINVAL;
1089}
1090
1091int hdd_rx_timestamp(qdf_nbuf_t netbuf, uint64_t target_time)
1092{
1093 if (hdd_netbuf_timestamp(netbuf, target_time) ==
1094 HDD_TSF_OP_SUCC)
1095 return 0;
1096
1097 /* reset tstamp when failed */
1098 netbuf->tstamp = ns_to_ktime(0);
1099 return -EINVAL;
1100}
1101
Jeff Johnson8f389862017-08-29 14:19:23 -07001102static inline int __hdd_capture_tsf(struct hdd_adapter *adapter,
Yu Wang000dc2f2017-05-26 17:38:48 +08001103 uint32_t *buf, int len)
1104{
1105 if (!adapter || !buf) {
1106 hdd_err("invalid pointer");
1107 return -EINVAL;
1108 }
1109
1110 if (len != 1)
1111 return -EINVAL;
1112
1113 buf[0] = TSF_DISABLED_BY_TSFPLUS;
1114
1115 return 0;
1116}
1117
Jeff Johnson8f389862017-08-29 14:19:23 -07001118static inline int __hdd_indicate_tsf(struct hdd_adapter *adapter,
Yu Wang000dc2f2017-05-26 17:38:48 +08001119 uint32_t *buf, int len)
1120{
1121 if (!adapter || !buf) {
1122 hdd_err("invalid pointer");
1123 return -EINVAL;
1124 }
1125
1126 if (len != 3)
1127 return -EINVAL;
1128
1129 buf[0] = TSF_DISABLED_BY_TSFPLUS;
1130 buf[1] = 0;
1131 buf[2] = 0;
1132
1133 return 0;
1134}
1135
yuanl2746f072018-09-21 19:19:16 +08001136#ifdef WLAN_FEATURE_TSF_PLUS_NOIRQ
1137static inline
1138enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
1139{
1140 if (!hdd_tsf_is_ptp_enabled(hdd_ctx)) {
1141 hdd_info("To enable TSF_PLUS, set gtsf_ptp_options in ini");
1142 return HDD_TSF_OP_FAIL;
1143 }
1144
1145 if (hdd_tsf_is_tx_set(hdd_ctx))
1146 ol_register_timestamp_callback(hdd_tx_timestamp);
1147 return HDD_TSF_OP_SUCC;
1148}
1149
1150static inline
1151enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
1152{
1153 QDF_STATUS status;
1154 QDF_TIMER_STATE capture_req_timer_status;
1155 qdf_mc_timer_t *cap_timer;
1156 struct hdd_adapter *adapter, *adapternode_ptr, *next_ptr;
1157
1158 if (!hdd_tsf_is_ptp_enabled(hdd_ctx))
1159 return HDD_TSF_OP_SUCC;
1160
1161 if (hdd_tsf_is_tx_set(hdd_ctx))
1162 ol_deregister_timestamp_callback();
1163
1164 status = hdd_get_front_adapter(hdd_ctx, &adapternode_ptr);
1165
1166 while (NULL != adapternode_ptr && QDF_STATUS_SUCCESS == status) {
1167 adapter = adapternode_ptr;
1168 status =
1169 hdd_get_next_adapter(hdd_ctx, adapternode_ptr, &next_ptr);
1170 adapternode_ptr = next_ptr;
1171 if (adapter->host_capture_req_timer.state == 0)
1172 continue;
1173 cap_timer = &adapter->host_capture_req_timer;
1174 capture_req_timer_status =
1175 qdf_mc_timer_get_current_state(cap_timer);
1176
1177 if (capture_req_timer_status != QDF_TIMER_STATE_UNUSED) {
1178 qdf_mc_timer_stop(cap_timer);
1179 status =
1180 qdf_mc_timer_destroy(cap_timer);
1181 if (status != QDF_STATUS_SUCCESS)
1182 hdd_err("remove timer failed: %d", status);
1183 }
1184 }
1185
1186 return HDD_TSF_OP_SUCC;
1187}
1188#else
Yu Wang000dc2f2017-05-26 17:38:48 +08001189static inline
Jeff Johnson854cccd2017-08-28 11:39:24 -07001190enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
Yu Wang000dc2f2017-05-26 17:38:48 +08001191{
1192 int ret;
1193
yuanl2746f072018-09-21 19:19:16 +08001194 if (!hdd_tsf_is_ptp_enabled(hdd_ctx)) {
Yu Wang66a250b2017-07-19 11:46:40 +08001195 hdd_info("To enable TSF_PLUS, set gtsf_ptp_options in ini");
1196 return HDD_TSF_OP_FAIL;
1197 }
1198
Yu Wang000dc2f2017-05-26 17:38:48 +08001199 ret = cnss_common_register_tsf_captured_handler(
1200 hdd_ctx->parent_dev,
1201 hdd_tsf_captured_irq_handler,
1202 (void *)hdd_ctx);
1203 if (ret != 0) {
1204 hdd_err("Failed to register irq handler: %d", ret);
1205 return HDD_TSF_OP_FAIL;
1206 }
Yu Wangceb357b2017-06-01 12:04:18 +08001207
yuanl2746f072018-09-21 19:19:16 +08001208 if (hdd_tsf_is_tx_set(hdd_ctx))
Yu Wang66a250b2017-07-19 11:46:40 +08001209 ol_register_timestamp_callback(hdd_tx_timestamp);
Yu Wang000dc2f2017-05-26 17:38:48 +08001210 return HDD_TSF_OP_SUCC;
1211}
1212
1213static inline
Jeff Johnson854cccd2017-08-28 11:39:24 -07001214enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
Yu Wang000dc2f2017-05-26 17:38:48 +08001215{
1216 int ret;
1217
yuanl2746f072018-09-21 19:19:16 +08001218 if (!hdd_tsf_is_ptp_enabled(hdd_ctx))
Yu Wang66a250b2017-07-19 11:46:40 +08001219 return HDD_TSF_OP_SUCC;
1220
yuanl2746f072018-09-21 19:19:16 +08001221 if (hdd_tsf_is_tx_set(hdd_ctx))
Yu Wang66a250b2017-07-19 11:46:40 +08001222 ol_deregister_timestamp_callback();
1223
Yu Wang000dc2f2017-05-26 17:38:48 +08001224 ret = cnss_common_unregister_tsf_captured_handler(
1225 hdd_ctx->parent_dev,
1226 (void *)hdd_ctx);
1227 if (ret != 0) {
1228 hdd_err("Failed to unregister irq handler, ret:%d",
1229 ret);
1230 ret = HDD_TSF_OP_FAIL;
1231 }
1232
1233 return HDD_TSF_OP_SUCC;
1234}
yuanl2746f072018-09-21 19:19:16 +08001235#endif
Yu Wang000dc2f2017-05-26 17:38:48 +08001236
Jeff Johnson8f389862017-08-29 14:19:23 -07001237void hdd_tsf_notify_wlan_state_change(struct hdd_adapter *adapter,
Yu Wang000dc2f2017-05-26 17:38:48 +08001238 eConnectionState old_state,
1239 eConnectionState new_state)
1240{
1241 if (!adapter)
1242 return;
1243
1244 if (old_state != eConnectionState_Associated &&
1245 new_state == eConnectionState_Associated)
1246 hdd_start_tsf_sync(adapter);
1247 else if (old_state == eConnectionState_Associated &&
1248 new_state != eConnectionState_Associated)
1249 hdd_stop_tsf_sync(adapter);
1250}
1251#else
Jeff Johnson8f389862017-08-29 14:19:23 -07001252static inline void hdd_update_tsf(struct hdd_adapter *adapter, uint64_t tsf)
Yu Wang000dc2f2017-05-26 17:38:48 +08001253{
1254}
1255
Jeff Johnson8f389862017-08-29 14:19:23 -07001256static inline int __hdd_indicate_tsf(struct hdd_adapter *adapter,
Yu Wang000dc2f2017-05-26 17:38:48 +08001257 uint32_t *buf, int len)
1258{
1259 return (hdd_indicate_tsf_internal(adapter, buf, len) ==
1260 HDD_TSF_OP_SUCC) ? 0 : -EINVAL;
1261}
1262
Jeff Johnson8f389862017-08-29 14:19:23 -07001263static inline int __hdd_capture_tsf(struct hdd_adapter *adapter,
Yu Wang000dc2f2017-05-26 17:38:48 +08001264 uint32_t *buf, int len)
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001265{
1266 return (hdd_capture_tsf_internal(adapter, buf, len) ==
1267 HDD_TSF_OP_SUCC) ? 0 : -EINVAL;
1268}
1269
Yu Wang000dc2f2017-05-26 17:38:48 +08001270static inline
Jeff Johnson854cccd2017-08-28 11:39:24 -07001271enum hdd_tsf_op_result wlan_hdd_tsf_plus_init(struct hdd_context *hdd_ctx)
Yu Wang000dc2f2017-05-26 17:38:48 +08001272{
1273 return HDD_TSF_OP_SUCC;
1274}
1275
1276static inline
Jeff Johnson854cccd2017-08-28 11:39:24 -07001277enum hdd_tsf_op_result wlan_hdd_tsf_plus_deinit(struct hdd_context *hdd_ctx)
Yu Wang000dc2f2017-05-26 17:38:48 +08001278{
1279 return HDD_TSF_OP_SUCC;
1280}
1281#endif /* WLAN_FEATURE_TSF_PLUS */
1282
Jeff Johnson8f389862017-08-29 14:19:23 -07001283int hdd_capture_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len)
Yu Wang000dc2f2017-05-26 17:38:48 +08001284{
1285 return __hdd_capture_tsf(adapter, buf, len);
1286}
1287
Jeff Johnson8f389862017-08-29 14:19:23 -07001288int hdd_indicate_tsf(struct hdd_adapter *adapter, uint32_t *buf, int len)
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001289{
Yu Wang000dc2f2017-05-26 17:38:48 +08001290 return __hdd_indicate_tsf(adapter, buf, len);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001291}
1292
1293/**
1294 * hdd_get_tsf_cb() - handle tsf callback
1295 * @pcb_cxt: pointer to the hdd_contex
1296 * @ptsf: pointer to struct stsf
1297 *
1298 * This function handle the event that reported by firmware at first.
1299 * The event contains the vdev_id, current tsf value of this vdev,
1300 * tsf value is 64bits, discripted in two varaible tsf_low and tsf_high.
1301 * These two values each is uint32.
1302 *
1303 * Return: 0 for success or non-zero negative failure code
1304 */
Arun Khandavalli4b55da72016-07-19 19:55:01 +05301305int hdd_get_tsf_cb(void *pcb_cxt, struct stsf *ptsf)
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001306{
Jeff Johnson82797b62017-08-11 15:31:27 -07001307 struct hdd_context *hddctx;
Jeff Johnson85b5c112017-08-11 15:15:23 -07001308 struct hdd_adapter *adapter;
yuanl2746f072018-09-21 19:19:16 +08001309 int ret;
1310 QDF_STATUS status;
1311 QDF_TIMER_STATE capture_req_timer_status;
1312 qdf_mc_timer_t *capture_timer;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001313
1314 if (pcb_cxt == NULL || ptsf == NULL) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001315 hdd_err("HDD context is not valid");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001316 return -EINVAL;
1317 }
1318
Jeff Johnson82797b62017-08-11 15:31:27 -07001319 hddctx = (struct hdd_context *)pcb_cxt;
yuanl2746f072018-09-21 19:19:16 +08001320 ret = wlan_hdd_validate_context(hddctx);
1321 if (0 != ret)
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001322 return -EINVAL;
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001323
1324 adapter = hdd_get_adapter_by_vdev(hddctx, ptsf->vdev_id);
1325
1326 if (NULL == adapter) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001327 hdd_err("failed to find adapter");
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001328 return -EINVAL;
1329 }
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001330
1331 if (!hdd_tsf_is_initialized(adapter)) {
1332 hdd_err("tsf is not init, ignore tsf event");
1333 return -EINVAL;
1334 }
1335
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001336 hdd_info("tsf cb handle event, device_mode is %d",
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001337 adapter->device_mode);
1338
yuanl2746f072018-09-21 19:19:16 +08001339 capture_timer = &adapter->host_capture_req_timer;
1340 capture_req_timer_status =
1341 qdf_mc_timer_get_current_state(capture_timer);
1342 if (capture_req_timer_status == QDF_TIMER_STATE_UNUSED) {
1343 hdd_warn("invalid timer status");
1344 return -EINVAL;
1345 }
1346
1347 qdf_mc_timer_stop(capture_timer);
1348 status = qdf_mc_timer_destroy(capture_timer);
1349 if (status != QDF_STATUS_SUCCESS)
1350 hdd_warn("destroy cap req timer fail, ret: %d", status);
1351
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001352 adapter->cur_target_time = ((uint64_t)ptsf->tsf_high << 32 |
1353 ptsf->tsf_low);
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001354 adapter->tsf_sync_soc_timer = ((uint64_t) ptsf->soc_timer_high << 32 |
1355 ptsf->soc_timer_low);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001356
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001357 complete(&tsf_sync_get_completion_evt);
Yu Wang000dc2f2017-05-26 17:38:48 +08001358 hdd_update_tsf(adapter, adapter->cur_target_time);
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001359 hdd_info("Vdev=%u, tsf_low=%u, tsf_high=%u soc_timer=%llu",
1360 ptsf->vdev_id, ptsf->tsf_low, ptsf->tsf_high,
1361 adapter->tsf_sync_soc_timer);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001362 return 0;
1363}
1364
Jeff Johnson363764f2017-06-02 13:43:36 -07001365static const struct nla_policy tsf_policy[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1] = {
1366 [QCA_WLAN_VENDOR_ATTR_TSF_CMD] = {.type = NLA_U32},
1367};
1368
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001369/**
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001370 * __wlan_hdd_cfg80211_handle_tsf_cmd(): Setup TSF operations
1371 * @wiphy: Pointer to wireless phy
1372 * @wdev: Pointer to wireless device
1373 * @data: Pointer to data
1374 * @data_len: Data length
1375 *
1376 * Handle TSF SET / GET operation from userspace
1377 *
1378 * Return: 0 on success, negative errno on failure
1379 */
1380static int __wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
1381 struct wireless_dev *wdev,
1382 const void *data,
1383 int data_len)
1384{
1385 struct net_device *dev = wdev->netdev;
Jeff Johnson8f389862017-08-29 14:19:23 -07001386 struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
Jeff Johnson854cccd2017-08-28 11:39:24 -07001387 struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001388 struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_MAX + 1];
1389 int status, ret;
1390 struct sk_buff *reply_skb;
1391 uint32_t tsf_op_resp[3], tsf_cmd;
1392
Dustin Brownfdf17c12018-03-14 12:55:34 -07001393 hdd_enter_dev(wdev->netdev);
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001394
1395 status = wlan_hdd_validate_context(hdd_ctx);
1396 if (0 != status)
1397 return -EINVAL;
1398
Dustin Brown4ea21db2018-01-05 14:13:17 -08001399 if (wlan_cfg80211_nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_TSF_MAX,
1400 data, data_len, tsf_policy)) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001401 hdd_err("Invalid TSF cmd");
1402 return -EINVAL;
1403 }
1404
1405 if (!tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]) {
1406 hdd_err("Invalid TSF cmd");
1407 return -EINVAL;
1408 }
1409 tsf_cmd = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_TSF_CMD]);
1410
1411 if (tsf_cmd == QCA_TSF_CAPTURE || tsf_cmd == QCA_TSF_SYNC_GET) {
1412 hdd_capture_tsf(adapter, tsf_op_resp, 1);
1413 switch (tsf_op_resp[0]) {
1414 case TSF_RETURN:
1415 status = 0;
1416 break;
1417 case TSF_CURRENT_IN_CAP_STATE:
1418 status = -EALREADY;
1419 break;
1420 case TSF_STA_NOT_CONNECTED_NO_TSF:
1421 case TSF_SAP_NOT_STARTED_NO_TSF:
1422 status = -EPERM;
1423 break;
1424 default:
1425 case TSF_CAPTURE_FAIL:
1426 status = -EINVAL;
1427 break;
1428 }
1429 }
1430 if (status < 0)
1431 goto end;
1432
1433 if (tsf_cmd == QCA_TSF_SYNC_GET) {
1434 ret = wait_for_completion_timeout(&tsf_sync_get_completion_evt,
1435 msecs_to_jiffies(WLAN_TSF_SYNC_GET_TIMEOUT));
1436 if (ret == 0) {
1437 status = -ETIMEDOUT;
1438 goto end;
1439 }
1440 }
1441
1442 if (tsf_cmd == QCA_TSF_GET || tsf_cmd == QCA_TSF_SYNC_GET) {
1443 hdd_indicate_tsf(adapter, tsf_op_resp, 3);
1444 switch (tsf_op_resp[0]) {
1445 case TSF_RETURN:
1446 status = 0;
1447 break;
1448 case TSF_NOT_RETURNED_BY_FW:
1449 status = -EINPROGRESS;
1450 break;
1451 case TSF_STA_NOT_CONNECTED_NO_TSF:
1452 case TSF_SAP_NOT_STARTED_NO_TSF:
1453 status = -EPERM;
1454 break;
1455 default:
1456 status = -EINVAL;
1457 break;
1458 }
1459 if (status != 0)
1460 goto end;
1461
1462 reply_skb = cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
1463 sizeof(uint64_t) * 2 + NLMSG_HDRLEN,
1464 QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX,
1465 GFP_KERNEL);
1466 if (!reply_skb) {
1467 hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed");
1468 status = -ENOMEM;
1469 goto end;
1470 }
Dustin Brownbb7e2f52016-10-17 12:16:35 -07001471 if (hdd_wlan_nla_put_u64(reply_skb,
1472 QCA_WLAN_VENDOR_ATTR_TSF_TIMER_VALUE,
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001473 adapter->cur_target_time) ||
Dustin Brownbb7e2f52016-10-17 12:16:35 -07001474 hdd_wlan_nla_put_u64(reply_skb,
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001475 QCA_WLAN_VENDOR_ATTR_TSF_SOC_TIMER_VALUE,
Dustin Brownbb7e2f52016-10-17 12:16:35 -07001476 adapter->tsf_sync_soc_timer)) {
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001477 hdd_err("nla put fail");
1478 kfree_skb(reply_skb);
1479 status = -EINVAL;
1480 goto end;
1481 }
1482 status = cfg80211_vendor_cmd_reply(reply_skb);
1483 }
1484
1485end:
yuanl2746f072018-09-21 19:19:16 +08001486 hdd_info("TSF operation %d status: %d", tsf_cmd, status);
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001487 return status;
1488}
1489
Manikandan Mohan5356c2b2016-04-03 15:51:35 -07001490int wlan_hdd_cfg80211_handle_tsf_cmd(struct wiphy *wiphy,
1491 struct wireless_dev *wdev,
1492 const void *data,
1493 int data_len)
1494{
1495 int ret;
1496
1497 cds_ssr_protect(__func__);
1498 ret = __wlan_hdd_cfg80211_handle_tsf_cmd(wiphy, wdev, data, data_len);
1499 cds_ssr_unprotect(__func__);
1500
1501 return ret;
1502}
1503
1504/**
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001505 * wlan_hdd_tsf_init() - set callback to handle tsf value.
Jeff Johnson82797b62017-08-11 15:31:27 -07001506 * @hdd_ctx: pointer to the struct hdd_context
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001507 *
1508 * This function set the callback to sme module, the callback will be
1509 * called when a tsf event is reported by firmware
1510 *
1511 * Return: none
1512 */
Jeff Johnson82797b62017-08-11 15:31:27 -07001513void wlan_hdd_tsf_init(struct hdd_context *hdd_ctx)
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001514{
Jeff Johnsonad278e62018-06-14 11:11:24 -07001515 QDF_STATUS status;
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001516
1517 if (!hdd_ctx)
1518 return;
1519
1520 if (qdf_atomic_inc_return(&hdd_ctx->tsf_ready_flag) > 1)
1521 return;
1522
1523 qdf_atomic_init(&hdd_ctx->cap_tsf_flag);
1524
yuanl2746f072018-09-21 19:19:16 +08001525 status = hdd_tsf_set_gpio(hdd_ctx);
Sourav Mohapatra0f3b8572018-09-12 10:03:51 +05301526
Jeff Johnsonad278e62018-06-14 11:11:24 -07001527 if (QDF_STATUS_SUCCESS != status) {
1528 hdd_err("set tsf GPIO failed, status: %d", status);
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001529 goto fail;
1530 }
1531
Yu Wang000dc2f2017-05-26 17:38:48 +08001532 if (wlan_hdd_tsf_plus_init(hdd_ctx) != HDD_TSF_OP_SUCC)
1533 goto fail;
1534
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001535 return;
1536
1537fail:
1538 qdf_atomic_set(&hdd_ctx->tsf_ready_flag, 0);
1539}
1540
Jeff Johnson854cccd2017-08-28 11:39:24 -07001541void wlan_hdd_tsf_deinit(struct hdd_context *hdd_ctx)
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001542{
1543 if (!hdd_ctx)
1544 return;
1545
1546 if (!qdf_atomic_read(&hdd_ctx->tsf_ready_flag))
1547 return;
1548
Yu Wang000dc2f2017-05-26 17:38:48 +08001549 wlan_hdd_tsf_plus_deinit(hdd_ctx);
Yu Wangf5d5b5f2017-05-25 22:38:32 +08001550 qdf_atomic_set(&hdd_ctx->tsf_ready_flag, 0);
1551 qdf_atomic_set(&hdd_ctx->cap_tsf_flag, 0);
Manikandan Mohandcc21ba2016-03-15 14:31:56 -07001552}