xref: /OK3568_Linux_fs/kernel/drivers/net/wireless/rockchip_wlan/cywdhd/bcmdhd/dhd_rtt.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1 /*
2  * Broadcom Dongle Host Driver (DHD), RTT
3  *
4  * Portions of this code are copyright (c) 2022 Cypress Semiconductor Corporation
5  *
6  * Copyright (C) 1999-2017, Broadcom Corporation
7  *
8  *      Unless you and Broadcom execute a separate written software license
9  * agreement governing use of this software, this software is licensed to you
10  * under the terms of the GNU General Public License version 2 (the "GPL"),
11  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
12  * following added to such license:
13  *
14  *      As a special exception, the copyright holders of this software give you
15  * permission to link this software with independent modules, and to copy and
16  * distribute the resulting executable under terms of your choice, provided that
17  * you also meet, for each linked independent module, the terms and conditions of
18  * the license of that module.  An independent module is a module which is not
19  * derived from this software.  The special exception does not apply to any
20  * modifications of the software.
21  *
22  *      Notwithstanding the above, under no circumstances may you combine this
23  * software in any way with any other Broadcom software provided under a license
24  * other than the GPL, without Broadcom's express prior written consent.
25  *
26  *
27  * <<Broadcom-WL-IPTag/Open:>>
28  *
29  * $Id$
30  */
31 #include <typedefs.h>
32 #include <osl.h>
33 
34 #include <epivers.h>
35 #include <bcmutils.h>
36 
37 #include <bcmendian.h>
38 #include <linuxver.h>
39 #include <linux/init.h>
40 #include <linux/kernel.h>
41 #include <linux/list.h>
42 #include <linux/sort.h>
43 #include <dngl_stats.h>
44 #include <wlioctl.h>
45 #include <bcmwifi_rspec.h>
46 
47 #include <bcmevent.h>
48 #include <dhd.h>
49 #include <dhd_linux.h>
50 #include <dhd_rtt.h>
51 #include <dhd_dbg.h>
52 #include <dhd_bus.h>
53 #include <wldev_common.h>
54 #ifdef WL_CFG80211
55 #include <wl_cfg80211.h>
56 #endif /* WL_CFG80211 */
57 #ifdef WL_NAN
58 #include <wl_cfgnan.h>
59 #endif /* WL_NAN */
60 
61 static DEFINE_SPINLOCK(noti_list_lock);
62 #ifndef NULL_CHECK
63 #define NULL_CHECK(p, s, err)  \
64 			do { \
65 				if (!(p)) { \
66 					printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
67 					err = BCME_ERROR; \
68 					return err; \
69 				} \
70 			} while (0)
71 #endif // endif
72 
73 #define TIMESPEC_TO_US(ts)  (((uint64)(ts).tv_sec * USEC_PER_SEC) + \
74 							(ts).tv_nsec / NSEC_PER_USEC)
75 
76 #undef DHD_RTT_MEM
77 #undef DHD_RTT_ERR
78 #define DHD_RTT_MEM DHD_LOG_MEM
79 #define DHD_RTT_ERR DHD_ERROR
80 
81 #define FTM_IOC_BUFSZ  2048	/* ioc buffsize for our module (> BCM_XTLV_HDR_SIZE) */
82 #define FTM_AVAIL_MAX_SLOTS		32
83 #define FTM_MAX_CONFIGS 10
84 #define FTM_MAX_PARAMS 10
85 #define FTM_DEFAULT_SESSION 1
86 #define FTM_BURST_TIMEOUT_UNIT 250 /* 250 ns */
87 #define FTM_INVALID -1
88 #define	FTM_DEFAULT_CNT_20M		24u
89 #define FTM_DEFAULT_CNT_40M		16u
90 #define FTM_DEFAULT_CNT_80M		11u
91 /* To handle congestion env, set max dur/timeout */
92 #define FTM_MAX_BURST_DUR_TMO_MS	128u
93 
94 /* convenience macros */
95 #define FTM_TU2MICRO(_tu) ((uint64)(_tu) << 10)
96 #define FTM_MICRO2TU(_tu) ((uint64)(_tu) >> 10)
97 #define FTM_TU2MILLI(_tu) ((uint32)FTM_TU2MICRO(_tu) / 1000)
98 #define FTM_MICRO2MILLI(_x) ((uint32)(_x) / 1000)
99 #define FTM_MICRO2SEC(_x) ((uint32)(_x) / 1000000)
100 #define FTM_INTVL2NSEC(_intvl) ((uint32)ftm_intvl2nsec(_intvl))
101 #define FTM_INTVL2USEC(_intvl) ((uint32)ftm_intvl2usec(_intvl))
102 #define FTM_INTVL2MSEC(_intvl) (FTM_INTVL2USEC(_intvl) / 1000)
103 #define FTM_INTVL2SEC(_intvl) (FTM_INTVL2USEC(_intvl) / 1000000)
104 #define FTM_USECIN100MILLI(_usec) ((_usec) / 100000)
105 
106 /* broadcom specific set to have more accurate data */
107 #define ENABLE_VHT_ACK
108 #define CH_MIN_5G_CHANNEL 34
109 
110 /* CUR ETH became obsolete with this major version onwards */
111 #define RTT_IOV_CUR_ETH_OBSOLETE 12
112 
113 /* PROXD TIMEOUT */
114 #define DHD_RTT_TIMER_INTERVAL_MS	5000u
115 #define DHD_NAN_RTT_TIMER_INTERVAL_MS	10000u
116 
117 struct rtt_noti_callback {
118 	struct list_head list;
119 	void *ctx;
120 	dhd_rtt_compl_noti_fn noti_fn;
121 };
122 
123 /* bitmask indicating which command groups; */
124 typedef enum {
125 	FTM_SUBCMD_FLAG_METHOD	= 0x01,	/* FTM method command */
126 	FTM_SUBCMD_FLAG_SESSION = 0x02,	/* FTM session command */
127 	FTM_SUBCMD_FLAG_ALL = FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION
128 } ftm_subcmd_flag_t;
129 
130 /* proxd ftm config-category definition */
131 typedef enum {
132 	FTM_CONFIG_CAT_GENERAL = 1,	/* generial configuration */
133 	FTM_CONFIG_CAT_OPTIONS = 2,	/* 'config options' */
134 	FTM_CONFIG_CAT_AVAIL = 3,	/* 'config avail' */
135 } ftm_config_category_t;
136 
137 typedef struct ftm_subcmd_info {
138 	int16				version;    /* FTM version (optional) */
139 	char				*name;		/* cmd-name string as cmdline input */
140 	wl_proxd_cmd_t		cmdid;		/* cmd-id */
141 	bcm_xtlv_unpack_cbfn_t *handler;  /* cmd response handler (optional) */
142 	ftm_subcmd_flag_t	cmdflag; /* CMD flag (optional)  */
143 } ftm_subcmd_info_t;
144 
145 typedef struct ftm_config_options_info {
146 	uint32 flags;				/* wl_proxd_flags_t/wl_proxd_session_flags_t */
147 	bool enable;
148 } ftm_config_options_info_t;
149 
150 typedef struct ftm_config_param_info {
151 	uint16		tlvid;	/* mapping TLV id for the item */
152 	union {
153 		uint32  chanspec;
154 		struct ether_addr mac_addr;
155 		wl_proxd_intvl_t data_intvl;
156 		uint32 data32;
157 		uint16 data16;
158 		uint8 data8;
159 		uint32 event_mask;
160 	};
161 } ftm_config_param_info_t;
162 
163 /*
164 * definition for id-string mapping.
165 *   This is used to map an id (can be cmd-id, tlv-id, ....) to a text-string
166 *   for debug-display or cmd-log-display
167 */
168 typedef struct ftm_strmap_entry {
169 	int32		id;
170 	char		*text;
171 } ftm_strmap_entry_t;
172 
173 typedef struct ftm_status_map_host_entry {
174 	wl_proxd_status_t proxd_status;
175 	rtt_reason_t rtt_reason;
176 } ftm_status_map_host_entry_t;
177 
178 static uint16
179 rtt_result_ver(uint16 tlvid, const uint8 *p_data);
180 
181 static int
182 dhd_rtt_convert_results_to_host_v1(rtt_result_t *rtt_result, const uint8 *p_data,
183 	uint16 tlvid, uint16 len);
184 
185 static int
186 dhd_rtt_convert_results_to_host_v2(rtt_result_t *rtt_result, const uint8 *p_data,
187 	uint16 tlvid, uint16 len);
188 
189 static wifi_rate_t
190 dhd_rtt_convert_rate_to_host(uint32 ratespec);
191 
192 #if defined(WL_CFG80211) && defined(RTT_DEBUG)
193 const char *
194 ftm_cmdid_to_str(uint16 cmdid);
195 #endif /* WL_CFG80211 && RTT_DEBUG */
196 
197 #ifdef WL_CFG80211
198 static int
199 dhd_rtt_start(dhd_pub_t *dhd);
200 static int dhd_rtt_create_failure_result(rtt_status_info_t *rtt_status,
201 	struct ether_addr *addr);
202 static void dhd_rtt_handle_rtt_session_end(dhd_pub_t *dhd);
203 static void dhd_rtt_timeout_work(struct work_struct *work);
204 #endif /* WL_CFG80211 */
205 static const int burst_duration_idx[]  = {0, 0, 1, 2, 4, 8, 16, 32, 64, 128, 0, 0};
206 
207 /* ftm status mapping to host status */
208 static const ftm_status_map_host_entry_t ftm_status_map_info[] = {
209 	{WL_PROXD_E_INCOMPLETE, RTT_STATUS_FAILURE},
210 	{WL_PROXD_E_OVERRIDDEN, RTT_STATUS_FAILURE},
211 	{WL_PROXD_E_ASAP_FAILED, RTT_STATUS_FAILURE},
212 	{WL_PROXD_E_NOTSTARTED, RTT_STATUS_FAIL_NOT_SCHEDULED_YET},
213 	{WL_PROXD_E_INVALIDMEAS, RTT_STATUS_FAIL_INVALID_TS},
214 	{WL_PROXD_E_INCAPABLE, RTT_STATUS_FAIL_NO_CAPABILITY},
215 	{WL_PROXD_E_MISMATCH, RTT_STATUS_FAILURE},
216 	{WL_PROXD_E_DUP_SESSION, RTT_STATUS_FAILURE},
217 	{WL_PROXD_E_REMOTE_FAIL, RTT_STATUS_FAILURE},
218 	{WL_PROXD_E_REMOTE_INCAPABLE, RTT_STATUS_FAILURE},
219 	{WL_PROXD_E_SCHED_FAIL, RTT_STATUS_FAIL_SCHEDULE},
220 	{WL_PROXD_E_PROTO, RTT_STATUS_FAIL_PROTOCOL},
221 	{WL_PROXD_E_EXPIRED, RTT_STATUS_FAILURE},
222 	{WL_PROXD_E_TIMEOUT, RTT_STATUS_FAIL_TM_TIMEOUT},
223 	{WL_PROXD_E_NOACK, RTT_STATUS_FAIL_NO_RSP},
224 	{WL_PROXD_E_DEFERRED, RTT_STATUS_FAILURE},
225 	{WL_PROXD_E_INVALID_SID, RTT_STATUS_FAILURE},
226 	{WL_PROXD_E_REMOTE_CANCEL, RTT_STATUS_FAILURE},
227 	{WL_PROXD_E_CANCELED, RTT_STATUS_ABORTED},
228 	{WL_PROXD_E_INVALID_SESSION, RTT_STATUS_FAILURE},
229 	{WL_PROXD_E_BAD_STATE, RTT_STATUS_FAILURE},
230 	{WL_PROXD_E_ERROR, RTT_STATUS_FAILURE},
231 	{WL_PROXD_E_OK, RTT_STATUS_SUCCESS}
232 };
233 
234 static const ftm_strmap_entry_t ftm_event_type_loginfo[] = {
235 	/* wl_proxd_event_type_t,		text-string */
236 	{ WL_PROXD_EVENT_NONE,			"none" },
237 	{ WL_PROXD_EVENT_SESSION_CREATE,	"session create" },
238 	{ WL_PROXD_EVENT_SESSION_START,		"session start" },
239 	{ WL_PROXD_EVENT_FTM_REQ,		"FTM req" },
240 	{ WL_PROXD_EVENT_BURST_START,		"burst start" },
241 	{ WL_PROXD_EVENT_BURST_END,		"burst end" },
242 	{ WL_PROXD_EVENT_SESSION_END,		"session end" },
243 	{ WL_PROXD_EVENT_SESSION_RESTART,	"session restart" },
244 	{ WL_PROXD_EVENT_BURST_RESCHED,		"burst rescheduled" },
245 	{ WL_PROXD_EVENT_SESSION_DESTROY,	"session destroy" },
246 	{ WL_PROXD_EVENT_RANGE_REQ,		"range request" },
247 	{ WL_PROXD_EVENT_FTM_FRAME,		"FTM frame" },
248 	{ WL_PROXD_EVENT_DELAY,			"delay" },
249 	{ WL_PROXD_EVENT_VS_INITIATOR_RPT,	"initiator-report " }, /* rx initiator-rpt */
250 	{ WL_PROXD_EVENT_RANGING,		"ranging " },
251 	{ WL_PROXD_EVENT_COLLECT,		"collect" },
252 	{ WL_PROXD_EVENT_MF_STATS,		"mf_stats" },
253 };
254 
255 /*
256 * session-state --> text string mapping
257 */
258 static const ftm_strmap_entry_t ftm_session_state_value_loginfo[] = {
259 	/* wl_proxd_session_state_t,		text string */
260 	{ WL_PROXD_SESSION_STATE_CREATED,	"created" },
261 	{ WL_PROXD_SESSION_STATE_CONFIGURED,	"configured" },
262 	{ WL_PROXD_SESSION_STATE_STARTED,	"started" },
263 	{ WL_PROXD_SESSION_STATE_DELAY,		"delay" },
264 	{ WL_PROXD_SESSION_STATE_USER_WAIT,	"user-wait" },
265 	{ WL_PROXD_SESSION_STATE_SCHED_WAIT,	"sched-wait" },
266 	{ WL_PROXD_SESSION_STATE_BURST,		"burst" },
267 	{ WL_PROXD_SESSION_STATE_STOPPING,	"stopping" },
268 	{ WL_PROXD_SESSION_STATE_ENDED,		"ended" },
269 	{ WL_PROXD_SESSION_STATE_DESTROYING,	"destroying" },
270 	{ WL_PROXD_SESSION_STATE_NONE,		"none" }
271 };
272 
273 /*
274 * status --> text string mapping
275 */
276 static const ftm_strmap_entry_t ftm_status_value_loginfo[] = {
277 	/* wl_proxd_status_t,			text-string */
278 	{ WL_PROXD_E_OVERRIDDEN,		"overridden" },
279 	{ WL_PROXD_E_ASAP_FAILED,		"ASAP failed" },
280 	{ WL_PROXD_E_NOTSTARTED,		"not started" },
281 	{ WL_PROXD_E_INVALIDMEAS,		"invalid measurement" },
282 	{ WL_PROXD_E_INCAPABLE,			"incapable" },
283 	{ WL_PROXD_E_MISMATCH,			"mismatch"},
284 	{ WL_PROXD_E_DUP_SESSION,		"dup session" },
285 	{ WL_PROXD_E_REMOTE_FAIL,		"remote fail" },
286 	{ WL_PROXD_E_REMOTE_INCAPABLE,		"remote incapable" },
287 	{ WL_PROXD_E_SCHED_FAIL,		"sched failure" },
288 	{ WL_PROXD_E_PROTO,			"protocol error" },
289 	{ WL_PROXD_E_EXPIRED,			"expired" },
290 	{ WL_PROXD_E_TIMEOUT,			"timeout" },
291 	{ WL_PROXD_E_NOACK,			"no ack" },
292 	{ WL_PROXD_E_DEFERRED,			"deferred" },
293 	{ WL_PROXD_E_INVALID_SID,		"invalid session id" },
294 	{ WL_PROXD_E_REMOTE_CANCEL,		"remote cancel" },
295 	{ WL_PROXD_E_CANCELED,			"canceled" },
296 	{ WL_PROXD_E_INVALID_SESSION,		"invalid session" },
297 	{ WL_PROXD_E_BAD_STATE,			"bad state" },
298 	{ WL_PROXD_E_ERROR,			"error" },
299 	{ WL_PROXD_E_OK,			"OK" }
300 };
301 
302 /*
303 * time interval unit --> text string mapping
304 */
305 static const ftm_strmap_entry_t ftm_tmu_value_loginfo[] = {
306 	/* wl_proxd_tmu_t,		text-string */
307 	{ WL_PROXD_TMU_TU,		"TU" },
308 	{ WL_PROXD_TMU_SEC,		"sec" },
309 	{ WL_PROXD_TMU_MILLI_SEC,	"ms" },
310 	{ WL_PROXD_TMU_MICRO_SEC,	"us" },
311 	{ WL_PROXD_TMU_NANO_SEC,	"ns" },
312 	{ WL_PROXD_TMU_PICO_SEC,	"ps" }
313 };
314 
315 struct ieee_80211_mcs_rate_info {
316 	uint8 constellation_bits;
317 	uint8 coding_q;
318 	uint8 coding_d;
319 };
320 
321 static const struct ieee_80211_mcs_rate_info wl_mcs_info[] = {
322 	{ 1, 1, 2 }, /* MCS  0: MOD: BPSK,   CR 1/2 */
323 	{ 2, 1, 2 }, /* MCS  1: MOD: QPSK,   CR 1/2 */
324 	{ 2, 3, 4 }, /* MCS  2: MOD: QPSK,   CR 3/4 */
325 	{ 4, 1, 2 }, /* MCS  3: MOD: 16QAM,  CR 1/2 */
326 	{ 4, 3, 4 }, /* MCS  4: MOD: 16QAM,  CR 3/4 */
327 	{ 6, 2, 3 }, /* MCS  5: MOD: 64QAM,  CR 2/3 */
328 	{ 6, 3, 4 }, /* MCS  6: MOD: 64QAM,  CR 3/4 */
329 	{ 6, 5, 6 }, /* MCS  7: MOD: 64QAM,  CR 5/6 */
330 	{ 8, 3, 4 }, /* MCS  8: MOD: 256QAM, CR 3/4 */
331 	{ 8, 5, 6 }  /* MCS  9: MOD: 256QAM, CR 5/6 */
332 };
333 
334 /**
335  * Returns the rate in [Kbps] units for a caller supplied MCS/bandwidth/Nss/Sgi combination.
336  *     'mcs' : a *single* spatial stream MCS (11n or 11ac)
337  */
338 uint
rate_mcs2rate(uint mcs,uint nss,uint bw,int sgi)339 rate_mcs2rate(uint mcs, uint nss, uint bw, int sgi)
340 {
341 	const int ksps = 250; /* kilo symbols per sec, 4 us sym */
342 	const int Nsd_20MHz = 52;
343 	const int Nsd_40MHz = 108;
344 	const int Nsd_80MHz = 234;
345 	const int Nsd_160MHz = 468;
346 	uint rate;
347 
348 	if (mcs == 32) {
349 		/* just return fixed values for mcs32 instead of trying to parametrize */
350 		rate = (sgi == 0) ? 6000 : 6778;
351 	} else if (mcs <= 9) {
352 		/* This calculation works for 11n HT and 11ac VHT if the HT mcs values
353 		 * are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8.
354 		 * That is, HT MCS 23 is a base MCS = 7, Nss = 3
355 		 */
356 
357 		/* find the number of complex numbers per symbol */
358 		if (RSPEC_IS20MHZ(bw)) {
359 			rate = Nsd_20MHz;
360 		} else if (RSPEC_IS40MHZ(bw)) {
361 			rate = Nsd_40MHz;
362 		} else if (bw == WL_RSPEC_BW_80MHZ) {
363 			rate = Nsd_80MHz;
364 		} else if (bw == WL_RSPEC_BW_160MHZ) {
365 			rate = Nsd_160MHz;
366 		} else {
367 			rate = 0;
368 		}
369 
370 		/* multiply by bits per number from the constellation in use */
371 		rate = rate * wl_mcs_info[mcs].constellation_bits;
372 
373 		/* adjust for the number of spatial streams */
374 		rate = rate * nss;
375 
376 		/* adjust for the coding rate given as a quotient and divisor */
377 		rate = (rate * wl_mcs_info[mcs].coding_q) / wl_mcs_info[mcs].coding_d;
378 
379 		/* multiply by Kilo symbols per sec to get Kbps */
380 		rate = rate * ksps;
381 
382 		/* adjust the symbols per sec for SGI
383 		 * symbol duration is 4 us without SGI, and 3.6 us with SGI,
384 		 * so ratio is 10 / 9
385 		 */
386 		if (sgi) {
387 			/* add 4 for rounding of division by 9 */
388 			rate = ((rate * 10) + 4) / 9;
389 		}
390 	} else {
391 		rate = 0;
392 	}
393 
394 	return rate;
395 } /* wlc_rate_mcs2rate */
396 
397 /** take a well formed ratespec_t arg and return phy rate in [Kbps] units */
398 static uint32
rate_rspec2rate(uint32 rspec)399 rate_rspec2rate(uint32 rspec)
400 {
401 	int rate = 0;
402 
403 	if (RSPEC_ISLEGACY(rspec)) {
404 		rate = 500 * (rspec & WL_RSPEC_RATE_MASK);
405 	} else if (RSPEC_ISHT(rspec)) {
406 		uint mcs = (rspec & WL_RSPEC_RATE_MASK);
407 
408 		if (mcs == 32) {
409 			rate = rate_mcs2rate(mcs, 1, WL_RSPEC_BW_40MHZ, RSPEC_ISSGI(rspec));
410 		} else {
411 			uint nss = 1 + (mcs / 8);
412 			mcs = mcs % 8;
413 			rate = rate_mcs2rate(mcs, nss, RSPEC_BW(rspec), RSPEC_ISSGI(rspec));
414 		}
415 	} else if (RSPEC_ISVHT(rspec)) {
416 		uint mcs = (rspec & WL_RSPEC_VHT_MCS_MASK);
417 		uint nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
418 		if (mcs > 9 || nss > 8) {
419 			DHD_RTT(("%s: Invalid mcs %d or nss %d\n", __FUNCTION__, mcs, nss));
420 			goto exit;
421 		}
422 
423 		rate = rate_mcs2rate(mcs, nss, RSPEC_BW(rspec), RSPEC_ISSGI(rspec));
424 	} else {
425 		DHD_RTT(("%s: wrong rspec:%d\n", __FUNCTION__, rspec));
426 	}
427 exit:
428 	return rate;
429 }
430 
431 char resp_buf[WLC_IOCTL_SMLEN];
432 
433 static uint64
ftm_intvl2nsec(const wl_proxd_intvl_t * intvl)434 ftm_intvl2nsec(const wl_proxd_intvl_t *intvl)
435 {
436 	uint64 ret;
437 	ret = intvl->intvl;
438 	switch (intvl->tmu) {
439 	case WL_PROXD_TMU_TU:			ret = FTM_TU2MICRO(ret) * 1000; break;
440 	case WL_PROXD_TMU_SEC:			ret *= 1000000000; break;
441 	case WL_PROXD_TMU_MILLI_SEC:	ret *= 1000000; break;
442 	case WL_PROXD_TMU_MICRO_SEC:	ret *= 1000; break;
443 	case WL_PROXD_TMU_PICO_SEC:		ret = intvl->intvl / 1000; break;
444 	case WL_PROXD_TMU_NANO_SEC:		/* fall through */
445 	default:						break;
446 	}
447 	return ret;
448 }
449 uint64
ftm_intvl2usec(const wl_proxd_intvl_t * intvl)450 ftm_intvl2usec(const wl_proxd_intvl_t *intvl)
451 {
452 	uint64 ret;
453 	ret = intvl->intvl;
454 	switch (intvl->tmu) {
455 	case WL_PROXD_TMU_TU:			ret = FTM_TU2MICRO(ret); break;
456 	case WL_PROXD_TMU_SEC:			ret *= 1000000; break;
457 	case WL_PROXD_TMU_NANO_SEC:		ret = intvl->intvl / 1000; break;
458 	case WL_PROXD_TMU_PICO_SEC:		ret = intvl->intvl / 1000000; break;
459 	case WL_PROXD_TMU_MILLI_SEC:	ret *= 1000; break;
460 	case WL_PROXD_TMU_MICRO_SEC:	/* fall through */
461 	default:						break;
462 	}
463 	return ret;
464 }
465 
466 /*
467 * lookup 'id' (as a key) from a fw status to host map table
468 * if found, return the corresponding reason code
469 */
470 
471 static rtt_reason_t
ftm_get_statusmap_info(wl_proxd_status_t id,const ftm_status_map_host_entry_t * p_table,uint32 num_entries)472 ftm_get_statusmap_info(wl_proxd_status_t id, const ftm_status_map_host_entry_t *p_table,
473 	uint32 num_entries)
474 {
475 	int i;
476 	const ftm_status_map_host_entry_t *p_entry;
477 	/* scan thru the table till end */
478 	p_entry = p_table;
479 	for (i = 0; i < (int) num_entries; i++)
480 	{
481 		if (p_entry->proxd_status == id) {
482 			return p_entry->rtt_reason;
483 		}
484 		p_entry++;		/* next entry */
485 	}
486 	return RTT_STATUS_FAILURE; /* not found */
487 }
488 /*
489 * lookup 'id' (as a key) from a table
490 * if found, return the entry pointer, otherwise return NULL
491 */
492 static const ftm_strmap_entry_t*
ftm_get_strmap_info(int32 id,const ftm_strmap_entry_t * p_table,uint32 num_entries)493 ftm_get_strmap_info(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
494 {
495 	int i;
496 	const ftm_strmap_entry_t *p_entry;
497 
498 	/* scan thru the table till end */
499 	p_entry = p_table;
500 	for (i = 0; i < (int) num_entries; i++)
501 	{
502 		if (p_entry->id == id)
503 			return p_entry;
504 		p_entry++;		/* next entry */
505 	}
506 	return NULL;			/* not found */
507 }
508 
509 /*
510 * map enum to a text-string for display, this function is called by the following:
511 * For debug/trace:
512 *     ftm_[cmdid|tlvid]_to_str()
513 * For TLV-output log for 'get' commands
514 *     ftm_[method|tmu|caps|status|state]_value_to_logstr()
515 * Input:
516 *     pTable -- point to a 'enum to string' table.
517 */
518 static const char *
ftm_map_id_to_str(int32 id,const ftm_strmap_entry_t * p_table,uint32 num_entries)519 ftm_map_id_to_str(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
520 {
521 	const ftm_strmap_entry_t*p_entry = ftm_get_strmap_info(id, p_table, num_entries);
522 	if (p_entry)
523 		return (p_entry->text);
524 
525 	return "invalid";
526 }
527 
528 #if defined(WL_CFG80211) && defined(RTT_DEBUG)
529 /* define entry, e.g. { WL_PROXD_CMD_xxx, "WL_PROXD_CMD_xxx" } */
530 #define DEF_STRMAP_ENTRY(id) { (id), #id }
531 
532 /* ftm cmd-id mapping */
533 static const ftm_strmap_entry_t ftm_cmdid_map[] = {
534 	/* {wl_proxd_cmd_t(WL_PROXD_CMD_xxx), "WL_PROXD_CMD_xxx" }, */
535 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_NONE),
536 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_VERSION),
537 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_ENABLE),
538 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_DISABLE),
539 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_CONFIG),
540 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_SESSION),
541 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_BURST_REQUEST),
542 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_SESSION),
543 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_DELETE_SESSION),
544 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RESULT),
545 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_INFO),
546 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_STATUS),
547 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_SESSIONS),
548 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_COUNTERS),
549 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_CLEAR_COUNTERS),
550 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_COLLECT),
551 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_TUNE),
552 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_DUMP),
553 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_RANGING),
554 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_RANGING),
555 	DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RANGING_INFO),
556 };
557 
558 /*
559 * map a ftm cmd-id to a text-string for display
560 */
561 const char *
ftm_cmdid_to_str(uint16 cmdid)562 ftm_cmdid_to_str(uint16 cmdid)
563 {
564 	return ftm_map_id_to_str((int32) cmdid, &ftm_cmdid_map[0], ARRAYSIZE(ftm_cmdid_map));
565 }
566 #endif /* WL_CFG80211 && RTT_DEBUG */
567 
568 /*
569 * convert BCME_xxx error codes into related error strings
570 * note, bcmerrorstr() defined in bcmutils is for BCMDRIVER only,
571 *       this duplicate copy is for WL access and may need to clean up later
572 */
573 static const char *ftm_bcmerrorstrtable[] = BCMERRSTRINGTABLE;
574 static const char *
ftm_status_value_to_logstr(wl_proxd_status_t status)575 ftm_status_value_to_logstr(wl_proxd_status_t status)
576 {
577 	static char ftm_msgbuf_status_undef[32];
578 	const ftm_strmap_entry_t *p_loginfo;
579 	int bcmerror;
580 
581 	/* check if within BCME_xxx error range */
582 	bcmerror = (int) status;
583 	if (VALID_BCMERROR(bcmerror))
584 		return ftm_bcmerrorstrtable[-bcmerror];
585 
586 	/* otherwise, look for 'proxd ftm status' range */
587 	p_loginfo = ftm_get_strmap_info((int32) status,
588 		&ftm_status_value_loginfo[0], ARRAYSIZE(ftm_status_value_loginfo));
589 	if (p_loginfo)
590 		return p_loginfo->text;
591 
592 	/* report for 'out of range' FTM-status error code */
593 	memset(ftm_msgbuf_status_undef, 0, sizeof(ftm_msgbuf_status_undef));
594 	snprintf(ftm_msgbuf_status_undef, sizeof(ftm_msgbuf_status_undef),
595 		"Undefined status %d", status);
596 	return &ftm_msgbuf_status_undef[0];
597 }
598 
599 static const char *
ftm_tmu_value_to_logstr(wl_proxd_tmu_t tmu)600 ftm_tmu_value_to_logstr(wl_proxd_tmu_t tmu)
601 {
602 	return ftm_map_id_to_str((int32)tmu,
603 		&ftm_tmu_value_loginfo[0], ARRAYSIZE(ftm_tmu_value_loginfo));
604 }
605 
606 static const ftm_strmap_entry_t*
ftm_get_event_type_loginfo(wl_proxd_event_type_t event_type)607 ftm_get_event_type_loginfo(wl_proxd_event_type_t	event_type)
608 {
609 	/* look up 'event-type' from a predefined table  */
610 	return ftm_get_strmap_info((int32) event_type,
611 		ftm_event_type_loginfo, ARRAYSIZE(ftm_event_type_loginfo));
612 }
613 
614 static const char *
ftm_session_state_value_to_logstr(wl_proxd_session_state_t state)615 ftm_session_state_value_to_logstr(wl_proxd_session_state_t state)
616 {
617 	return ftm_map_id_to_str((int32)state, &ftm_session_state_value_loginfo[0],
618 		ARRAYSIZE(ftm_session_state_value_loginfo));
619 }
620 
621 #ifdef WL_CFG80211
622 /*
623 * send 'proxd' iovar for all ftm get-related commands
624 */
625 static int
rtt_do_get_ioctl(dhd_pub_t * dhd,wl_proxd_iov_t * p_proxd_iov,uint16 proxd_iovsize,ftm_subcmd_info_t * p_subcmd_info)626 rtt_do_get_ioctl(dhd_pub_t *dhd, wl_proxd_iov_t *p_proxd_iov, uint16 proxd_iovsize,
627 		ftm_subcmd_info_t *p_subcmd_info)
628 {
629 
630 	wl_proxd_iov_t *p_iovresp = (wl_proxd_iov_t *)resp_buf;
631 	int status;
632 	int tlvs_len;
633 	/*  send getbuf proxd iovar */
634 	status = dhd_getiovar(dhd, 0, "proxd", (char *)p_proxd_iov,
635 			proxd_iovsize, (char **)&p_iovresp, WLC_IOCTL_SMLEN);
636 	if (status != BCME_OK) {
637 		DHD_RTT_ERR(("%s: failed to send getbuf proxd iovar (CMD ID : %d), status=%d\n",
638 			__FUNCTION__, p_subcmd_info->cmdid, status));
639 		return status;
640 	}
641 	if (p_subcmd_info->cmdid == WL_PROXD_CMD_GET_VERSION) {
642 		p_subcmd_info->version = ltoh16(p_iovresp->version);
643 		DHD_RTT(("ftm version: 0x%x\n", ltoh16(p_iovresp->version)));
644 		goto exit;
645 	}
646 
647 	tlvs_len = ltoh16(p_iovresp->len) - WL_PROXD_IOV_HDR_SIZE;
648 	if (tlvs_len < 0) {
649 		DHD_RTT_ERR(("%s: alert, p_iovresp->len(%d) should not be smaller than %d\n",
650 			__FUNCTION__, ltoh16(p_iovresp->len), (int) WL_PROXD_IOV_HDR_SIZE));
651 		tlvs_len = 0;
652 	}
653 
654 	if (tlvs_len > 0 && p_subcmd_info->handler) {
655 		/* unpack TLVs and invokes the cbfn for processing */
656 		status = bcm_unpack_xtlv_buf(p_proxd_iov, (uint8 *)p_iovresp->tlvs,
657 				tlvs_len, BCM_XTLV_OPTION_ALIGN32, p_subcmd_info->handler);
658 	}
659 exit:
660 	return status;
661 }
662 
663 static wl_proxd_iov_t *
rtt_alloc_getset_buf(wl_proxd_method_t method,wl_proxd_session_id_t session_id,wl_proxd_cmd_t cmdid,uint16 tlvs_bufsize,uint16 * p_out_bufsize)664 rtt_alloc_getset_buf(wl_proxd_method_t method, wl_proxd_session_id_t session_id,
665 	wl_proxd_cmd_t cmdid, uint16 tlvs_bufsize, uint16 *p_out_bufsize)
666 {
667 	uint16 proxd_iovsize;
668 	uint32 kflags;
669 	wl_proxd_tlv_t *p_tlv;
670 	wl_proxd_iov_t *p_proxd_iov = (wl_proxd_iov_t *) NULL;
671 
672 	*p_out_bufsize = 0;	/* init */
673 	kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
674 	/* calculate the whole buffer size, including one reserve-tlv entry in the header */
675 	proxd_iovsize = sizeof(wl_proxd_iov_t) + tlvs_bufsize;
676 
677 	p_proxd_iov = kzalloc(proxd_iovsize, kflags);
678 	if (p_proxd_iov == NULL) {
679 		DHD_RTT_ERR(("error: failed to allocate %d bytes of memory\n", proxd_iovsize));
680 		return NULL;
681 	}
682 
683 	/* setup proxd-FTM-method iovar header */
684 	p_proxd_iov->version = htol16(WL_PROXD_API_VERSION);
685 	p_proxd_iov->len = htol16(proxd_iovsize); /* caller may adjust it based on #of TLVs */
686 	p_proxd_iov->cmd = htol16(cmdid);
687 	p_proxd_iov->method = htol16(method);
688 	p_proxd_iov->sid = htol16(session_id);
689 
690 	/* initialize the reserved/dummy-TLV in iovar header */
691 	p_tlv = p_proxd_iov->tlvs;
692 	p_tlv->id = htol16(WL_PROXD_TLV_ID_NONE);
693 	p_tlv->len = htol16(0);
694 
695 	*p_out_bufsize = proxd_iovsize;	/* for caller's reference */
696 
697 	return p_proxd_iov;
698 }
699 
700 static int
dhd_rtt_common_get_handler(dhd_pub_t * dhd,ftm_subcmd_info_t * p_subcmd_info,wl_proxd_method_t method,wl_proxd_session_id_t session_id)701 dhd_rtt_common_get_handler(dhd_pub_t *dhd, ftm_subcmd_info_t *p_subcmd_info,
702 		wl_proxd_method_t method,
703 		wl_proxd_session_id_t session_id)
704 {
705 	int status = BCME_OK;
706 	uint16 proxd_iovsize = 0;
707 	wl_proxd_iov_t *p_proxd_iov;
708 #ifdef RTT_DEBUG
709 	DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
710 		__FUNCTION__, method, session_id, p_subcmd_info->cmdid,
711 		ftm_cmdid_to_str(p_subcmd_info->cmdid)));
712 #endif // endif
713 	/* alloc mem for ioctl headr + reserved 0 bufsize for tlvs (initialize to zero) */
714 	p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
715 		0, &proxd_iovsize);
716 
717 	if (p_proxd_iov == NULL)
718 		return BCME_NOMEM;
719 
720 	status = rtt_do_get_ioctl(dhd, p_proxd_iov, proxd_iovsize, p_subcmd_info);
721 
722 	if (status != BCME_OK) {
723 		DHD_RTT(("%s failed: status=%d\n", __FUNCTION__, status));
724 	}
725 	kfree(p_proxd_iov);
726 	return status;
727 }
728 
729 /*
730 * common handler for set-related proxd method commands which require no TLV as input
731 *   wl proxd ftm [session-id] <set-subcmd>
732 * e.g.
733 *   wl proxd ftm enable -- to enable ftm
734 *   wl proxd ftm disable -- to disable ftm
735 *   wl proxd ftm <session-id> start -- to start a specified session
736 *   wl proxd ftm <session-id> stop  -- to cancel a specified session;
737 *                                    state is maintained till session is delete.
738 *   wl proxd ftm <session-id> delete -- to delete a specified session
739 *   wl proxd ftm [<session-id>] clear-counters -- to clear counters
740 *   wl proxd ftm <session-id> burst-request -- on initiator: to send burst request;
741 *                                              on target: send FTM frame
742 *   wl proxd ftm <session-id> collect
743 *   wl proxd ftm tune     (TBD)
744 */
745 static int
dhd_rtt_common_set_handler(dhd_pub_t * dhd,const ftm_subcmd_info_t * p_subcmd_info,wl_proxd_method_t method,wl_proxd_session_id_t session_id)746 dhd_rtt_common_set_handler(dhd_pub_t *dhd, const ftm_subcmd_info_t *p_subcmd_info,
747 	wl_proxd_method_t method, wl_proxd_session_id_t session_id)
748 {
749 	uint16 proxd_iovsize;
750 	wl_proxd_iov_t *p_proxd_iov;
751 	int ret;
752 
753 #ifdef RTT_DEBUG
754 	DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
755 		__FUNCTION__, method, session_id, p_subcmd_info->cmdid,
756 		ftm_cmdid_to_str(p_subcmd_info->cmdid)));
757 #endif // endif
758 
759 	/* allocate and initialize a temp buffer for 'set proxd' iovar */
760 	proxd_iovsize = 0;
761 	p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
762 							0, &proxd_iovsize);		/* no TLV */
763 	if (p_proxd_iov == NULL)
764 		return BCME_NOMEM;
765 
766 	/* no TLV to pack, simply issue a set-proxd iovar */
767 	ret = dhd_iovar(dhd, 0, "proxd", (char *)p_proxd_iov, proxd_iovsize, NULL, 0, TRUE);
768 #ifdef RTT_DEBUG
769 	if (ret != BCME_OK) {
770 		DHD_RTT(("error: IOVAR failed, status=%d\n", ret));
771 	}
772 #endif // endif
773 	/* clean up */
774 	kfree(p_proxd_iov);
775 
776 	return ret;
777 }
778 #endif /* WL_CFG80211 */
779 
780 /* gets the length and returns the version
781  * of the wl_proxd_collect_event_t version
782  */
783 static uint
rtt_collect_data_event_ver(uint16 len)784 rtt_collect_data_event_ver(uint16 len)
785 {
786 	if (len > sizeof(wl_proxd_collect_event_data_v3_t)) {
787 		return WL_PROXD_COLLECT_EVENT_DATA_VERSION_MAX;
788 	} else if (len == sizeof(wl_proxd_collect_event_data_v3_t)) {
789 		return WL_PROXD_COLLECT_EVENT_DATA_VERSION_3;
790 	} else if (len == sizeof(wl_proxd_collect_event_data_v2_t)) {
791 		return WL_PROXD_COLLECT_EVENT_DATA_VERSION_2;
792 	} else {
793 		return WL_PROXD_COLLECT_EVENT_DATA_VERSION_1;
794 	}
795 }
796 
797 static void
rtt_collect_event_data_display(uint8 ver,void * ctx,const uint8 * p_data,uint16 len)798 rtt_collect_event_data_display(uint8 ver, void *ctx, const uint8 *p_data, uint16 len)
799 {
800 	int i;
801 	wl_proxd_collect_event_data_v1_t *p_collect_data_v1 = NULL;
802 	wl_proxd_collect_event_data_v2_t *p_collect_data_v2 = NULL;
803 	wl_proxd_collect_event_data_v3_t *p_collect_data_v3 = NULL;
804 
805 	if (!ctx || !p_data) {
806 		return;
807 	}
808 
809 	switch (ver) {
810 	case WL_PROXD_COLLECT_EVENT_DATA_VERSION_1:
811 		DHD_RTT(("\tVERSION_1\n"));
812 		memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_v1_t));
813 		p_collect_data_v1 = (wl_proxd_collect_event_data_v1_t *)ctx;
814 		DHD_RTT(("\tH_RX\n"));
815 		for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
816 			p_collect_data_v1->H_RX[i] = ltoh32_ua(&p_collect_data_v1->H_RX[i]);
817 			DHD_RTT(("\t%u\n", p_collect_data_v1->H_RX[i]));
818 		}
819 		DHD_RTT(("\n"));
820 		DHD_RTT(("\tH_LB\n"));
821 		for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
822 			p_collect_data_v1->H_LB[i] = ltoh32_ua(&p_collect_data_v1->H_LB[i]);
823 			DHD_RTT(("\t%u\n", p_collect_data_v1->H_LB[i]));
824 		}
825 		DHD_RTT(("\n"));
826 		DHD_RTT(("\tri_rr\n"));
827 		for (i = 0; i < FTM_TPK_RI_RR_LEN; i++) {
828 			DHD_RTT(("\t%u\n", p_collect_data_v1->ri_rr[i]));
829 		}
830 		p_collect_data_v1->phy_err_mask = ltoh32_ua(&p_collect_data_v1->phy_err_mask);
831 		DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data_v1->phy_err_mask));
832 		break;
833 	case WL_PROXD_COLLECT_EVENT_DATA_VERSION_2:
834 		memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_v2_t));
835 		p_collect_data_v2 = (wl_proxd_collect_event_data_v2_t *)ctx;
836 		DHD_RTT(("\tH_RX\n"));
837 		for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
838 			p_collect_data_v2->H_RX[i] = ltoh32_ua(&p_collect_data_v2->H_RX[i]);
839 			DHD_RTT(("\t%u\n", p_collect_data_v2->H_RX[i]));
840 		}
841 		DHD_RTT(("\n"));
842 		DHD_RTT(("\tH_LB\n"));
843 		for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
844 			p_collect_data_v2->H_LB[i] = ltoh32_ua(&p_collect_data_v2->H_LB[i]);
845 			DHD_RTT(("\t%u\n", p_collect_data_v2->H_LB[i]));
846 		}
847 		DHD_RTT(("\n"));
848 		DHD_RTT(("\tri_rr\n"));
849 		for (i = 0; i < FTM_TPK_RI_RR_LEN_SECURE_2_0; i++) {
850 			DHD_RTT(("\t%u\n", p_collect_data_v2->ri_rr[i]));
851 		}
852 		p_collect_data_v2->phy_err_mask = ltoh32_ua(&p_collect_data_v2->phy_err_mask);
853 		DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data_v2->phy_err_mask));
854 		break;
855 	case WL_PROXD_COLLECT_EVENT_DATA_VERSION_3:
856 		memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_v3_t));
857 		p_collect_data_v3 = (wl_proxd_collect_event_data_v3_t *)ctx;
858 		switch (p_collect_data_v3->version) {
859 		case WL_PROXD_COLLECT_EVENT_DATA_VERSION_3:
860 			if (p_collect_data_v3->length !=
861 				(len - OFFSETOF(wl_proxd_collect_event_data_v3_t, H_LB))) {
862 				DHD_RTT(("\tversion/length mismatch\n"));
863 				break;
864 			}
865 			DHD_RTT(("\tH_RX\n"));
866 			for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
867 				p_collect_data_v3->H_RX[i] =
868 					ltoh32_ua(&p_collect_data_v3->H_RX[i]);
869 				DHD_RTT(("\t%u\n", p_collect_data_v3->H_RX[i]));
870 			}
871 			DHD_RTT(("\n"));
872 			DHD_RTT(("\tH_LB\n"));
873 			for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
874 				p_collect_data_v3->H_LB[i] =
875 					ltoh32_ua(&p_collect_data_v3->H_LB[i]);
876 				DHD_RTT(("\t%u\n", p_collect_data_v3->H_LB[i]));
877 			}
878 			DHD_RTT(("\n"));
879 			DHD_RTT(("\tri_rr\n"));
880 			for (i = 0; i < FTM_TPK_RI_RR_LEN_SECURE_2_0; i++) {
881 				DHD_RTT(("\t%u\n", p_collect_data_v3->ri_rr[i]));
882 			}
883 			p_collect_data_v3->phy_err_mask =
884 				ltoh32_ua(&p_collect_data_v3->phy_err_mask);
885 			DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data_v3->phy_err_mask));
886 			break;
887 		/* future case */
888 		}
889 		break;
890 	}
891 }
892 
893 static uint16
rtt_result_ver(uint16 tlvid,const uint8 * p_data)894 rtt_result_ver(uint16 tlvid, const uint8 *p_data)
895 {
896 	uint16 ret = BCME_OK;
897 	const wl_proxd_rtt_result_v2_t *r_v2 = NULL;
898 
899 	switch (tlvid) {
900 	case WL_PROXD_TLV_ID_RTT_RESULT:
901 		BCM_REFERENCE(p_data);
902 		ret = WL_PROXD_RTT_RESULT_VERSION_1;
903 		break;
904 	case WL_PROXD_TLV_ID_RTT_RESULT_V2:
905 		if (p_data) {
906 			r_v2 = (const wl_proxd_rtt_result_v2_t *)p_data;
907 			if (r_v2->version == WL_PROXD_RTT_RESULT_VERSION_2) {
908 				ret = WL_PROXD_RTT_RESULT_VERSION_2;
909 			}
910 		}
911 		break;
912 	default:
913 		DHD_RTT_ERR(("%s: > Unsupported TLV ID %d\n",
914 			__FUNCTION__, tlvid));
915 		break;
916 	}
917 	return ret;
918 }
919 
920 /* pretty hex print a contiguous buffer */
921 static void
rtt_prhex(const char * msg,const uint8 * buf,uint nbytes)922 rtt_prhex(const char *msg, const uint8 *buf, uint nbytes)
923 {
924 	char line[128], *p;
925 	int len = sizeof(line);
926 	int nchar;
927 	uint i;
928 
929 	if (msg && (msg[0] != '\0'))
930 		DHD_RTT(("%s:\n", msg));
931 
932 	p = line;
933 	for (i = 0; i < nbytes; i++) {
934 		if (i % 16 == 0) {
935 			nchar = snprintf(p, len, "  %04d: ", i);	/* line prefix */
936 			p += nchar;
937 			len -= nchar;
938 		}
939 		if (len > 0) {
940 			nchar = snprintf(p, len, "%02x ", buf[i]);
941 			p += nchar;
942 			len -= nchar;
943 		}
944 
945 		if (i % 16 == 15) {
946 			DHD_RTT(("%s\n", line));	/* flush line */
947 			p = line;
948 			len = sizeof(line);
949 		}
950 	}
951 
952 	/* flush last partial line */
953 	if (p != line)
954 		DHD_RTT(("%s\n", line));
955 }
956 
957 static int
rtt_unpack_xtlv_cbfn(void * ctx,const uint8 * p_data,uint16 tlvid,uint16 len)958 rtt_unpack_xtlv_cbfn(void *ctx, const uint8 *p_data, uint16 tlvid, uint16 len)
959 {
960 	int ret = BCME_OK;
961 	int i;
962 	wl_proxd_ftm_session_status_t *p_data_info = NULL;
963 	uint32 chan_data_entry = 0;
964 	uint16 expected_rtt_result_ver = 0;
965 
966 	BCM_REFERENCE(p_data_info);
967 
968 	switch (tlvid) {
969 	case WL_PROXD_TLV_ID_RTT_RESULT:
970 	case WL_PROXD_TLV_ID_RTT_RESULT_V2:
971 		DHD_RTT(("WL_PROXD_TLV_ID_RTT_RESULT\n"));
972 		expected_rtt_result_ver = rtt_result_ver(tlvid, p_data);
973 		switch (expected_rtt_result_ver) {
974 		case WL_PROXD_RTT_RESULT_VERSION_1:
975 			ret = dhd_rtt_convert_results_to_host_v1((rtt_result_t *)ctx,
976 					p_data, tlvid, len);
977 			break;
978 		case WL_PROXD_RTT_RESULT_VERSION_2:
979 			ret = dhd_rtt_convert_results_to_host_v2((rtt_result_t *)ctx,
980 					p_data, tlvid, len);
981 			break;
982 		default:
983 			DHD_RTT_ERR((" > Unsupported RTT_RESULT version\n"));
984 			ret = BCME_UNSUPPORTED;
985 			break;
986 		}
987 		break;
988 	case WL_PROXD_TLV_ID_SESSION_STATUS:
989 		DHD_RTT(("WL_PROXD_TLV_ID_SESSION_STATUS\n"));
990 		memcpy(ctx, p_data, sizeof(wl_proxd_ftm_session_status_t));
991 		p_data_info = (wl_proxd_ftm_session_status_t *)ctx;
992 		p_data_info->sid = ltoh16_ua(&p_data_info->sid);
993 		p_data_info->state = ltoh16_ua(&p_data_info->state);
994 		p_data_info->status = ltoh32_ua(&p_data_info->status);
995 		p_data_info->burst_num = ltoh16_ua(&p_data_info->burst_num);
996 		DHD_RTT(("\tsid=%u, state=%d, status=%d, burst_num=%u\n",
997 			p_data_info->sid, p_data_info->state,
998 			p_data_info->status, p_data_info->burst_num));
999 
1000 		break;
1001 	case WL_PROXD_TLV_ID_COLLECT_DATA:
1002 		DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_DATA\n"));
1003 		rtt_collect_event_data_display(
1004 			rtt_collect_data_event_ver(len),
1005 			ctx, p_data, len);
1006 		break;
1007 	case WL_PROXD_TLV_ID_COLLECT_CHAN_DATA:
1008 		GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1009 		DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_CHAN_DATA\n"));
1010 		DHD_RTT(("\tchan est %u\n", (uint32) (len / sizeof(uint32))));
1011 		for (i = 0; i < (len/sizeof(chan_data_entry)); i++) {
1012 			uint32 *p = (uint32*)p_data;
1013 			chan_data_entry = ltoh32_ua(p + i);
1014 			DHD_RTT(("\t%u\n", chan_data_entry));
1015 		}
1016 		GCC_DIAGNOSTIC_POP();
1017 		break;
1018 	case WL_PROXD_TLV_ID_MF_STATS_DATA:
1019 		DHD_RTT(("WL_PROXD_TLV_ID_MF_STATS_DATA\n"));
1020 		DHD_RTT(("\tmf stats len=%u\n", len));
1021 		rtt_prhex("", p_data, len);
1022 		break;
1023 	default:
1024 		DHD_RTT_ERR(("> Unsupported TLV ID %d\n", tlvid));
1025 		ret = BCME_ERROR;
1026 		break;
1027 	}
1028 
1029 	return ret;
1030 }
1031 
1032 #ifdef WL_CFG80211
1033 static int
rtt_handle_config_options(wl_proxd_session_id_t session_id,wl_proxd_tlv_t ** p_tlv,uint16 * p_buf_space_left,ftm_config_options_info_t * ftm_configs,int ftm_cfg_cnt)1034 rtt_handle_config_options(wl_proxd_session_id_t session_id, wl_proxd_tlv_t **p_tlv,
1035 	uint16 *p_buf_space_left, ftm_config_options_info_t *ftm_configs, int ftm_cfg_cnt)
1036 {
1037 	int ret = BCME_OK;
1038 	int cfg_idx = 0;
1039 	uint32 flags = WL_PROXD_FLAG_NONE;
1040 	uint32 flags_mask = WL_PROXD_FLAG_NONE;
1041 	uint32 new_mask;		/* cmdline input */
1042 	ftm_config_options_info_t *p_option_info;
1043 	uint16 type = (session_id == WL_PROXD_SESSION_ID_GLOBAL) ?
1044 			WL_PROXD_TLV_ID_FLAGS_MASK : WL_PROXD_TLV_ID_SESSION_FLAGS_MASK;
1045 	for (cfg_idx = 0; cfg_idx < ftm_cfg_cnt; cfg_idx++) {
1046 		p_option_info = (ftm_configs + cfg_idx);
1047 		if (p_option_info != NULL) {
1048 			new_mask = p_option_info->flags;
1049 			/* update flags mask */
1050 			flags_mask |= new_mask;
1051 			if (p_option_info->enable) {
1052 				flags |= new_mask;	/* set the bit on */
1053 			} else {
1054 				flags &= ~new_mask;	/* set the bit off */
1055 			}
1056 		}
1057 	}
1058 	flags = htol32(flags);
1059 	flags_mask = htol32(flags_mask);
1060 	/* setup flags_mask TLV */
1061 	ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
1062 		type, sizeof(uint32), (uint8 *)&flags_mask, BCM_XTLV_OPTION_ALIGN32);
1063 	if (ret != BCME_OK) {
1064 		DHD_RTT_ERR(("%s : bcm_pack_xltv_entry() for mask flags failed, status=%d\n",
1065 			__FUNCTION__, ret));
1066 		goto exit;
1067 	}
1068 
1069 	type = (session_id == WL_PROXD_SESSION_ID_GLOBAL)?
1070 		WL_PROXD_TLV_ID_FLAGS : WL_PROXD_TLV_ID_SESSION_FLAGS;
1071 	/* setup flags TLV */
1072 	ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
1073 			type, sizeof(uint32), (uint8 *)&flags, BCM_XTLV_OPTION_ALIGN32);
1074 		if (ret != BCME_OK) {
1075 #ifdef RTT_DEBUG
1076 			DHD_RTT(("%s: bcm_pack_xltv_entry() for flags failed, status=%d\n",
1077 				__FUNCTION__, ret));
1078 #endif // endif
1079 		}
1080 exit:
1081 	return ret;
1082 }
1083 
1084 static int
rtt_handle_config_general(wl_proxd_session_id_t session_id,wl_proxd_tlv_t ** p_tlv,uint16 * p_buf_space_left,ftm_config_param_info_t * ftm_configs,int ftm_cfg_cnt)1085 rtt_handle_config_general(wl_proxd_session_id_t session_id, wl_proxd_tlv_t **p_tlv,
1086 	uint16 *p_buf_space_left, ftm_config_param_info_t *ftm_configs, int ftm_cfg_cnt)
1087 {
1088 	int ret = BCME_OK;
1089 	int cfg_idx = 0;
1090 	uint32 chanspec;
1091 	ftm_config_param_info_t *p_config_param_info;
1092 	void		*p_src_data;
1093 	uint16	src_data_size;	/* size of data pointed by p_src_data as 'source' */
1094 	for (cfg_idx = 0; cfg_idx < ftm_cfg_cnt; cfg_idx++) {
1095 		p_config_param_info = (ftm_configs + cfg_idx);
1096 		if (p_config_param_info != NULL) {
1097 			switch (p_config_param_info->tlvid)	{
1098 			case WL_PROXD_TLV_ID_BSS_INDEX:
1099 			case WL_PROXD_TLV_ID_FTM_RETRIES:
1100 			case WL_PROXD_TLV_ID_FTM_REQ_RETRIES:
1101 				p_src_data = &p_config_param_info->data8;
1102 				src_data_size = sizeof(uint8);
1103 				break;
1104 			case WL_PROXD_TLV_ID_BURST_NUM_FTM: /* uint16 */
1105 			case WL_PROXD_TLV_ID_NUM_BURST:
1106 			case WL_PROXD_TLV_ID_RX_MAX_BURST:
1107 				p_src_data = &p_config_param_info->data16;
1108 				src_data_size = sizeof(uint16);
1109 				break;
1110 			case WL_PROXD_TLV_ID_TX_POWER:		/* uint32 */
1111 			case WL_PROXD_TLV_ID_RATESPEC:
1112 			case WL_PROXD_TLV_ID_EVENT_MASK: /* wl_proxd_event_mask_t/uint32 */
1113 			case WL_PROXD_TLV_ID_DEBUG_MASK:
1114 				p_src_data = &p_config_param_info->data32;
1115 				src_data_size = sizeof(uint32);
1116 				break;
1117 			case WL_PROXD_TLV_ID_CHANSPEC:		/* chanspec_t --> 32bit */
1118 				chanspec = p_config_param_info->chanspec;
1119 				p_src_data = (void *) &chanspec;
1120 				src_data_size = sizeof(uint32);
1121 				break;
1122 			case WL_PROXD_TLV_ID_BSSID: /* mac address */
1123 			case WL_PROXD_TLV_ID_PEER_MAC:
1124 			case WL_PROXD_TLV_ID_CUR_ETHER_ADDR:
1125 				p_src_data = &p_config_param_info->mac_addr;
1126 				src_data_size = sizeof(struct ether_addr);
1127 				break;
1128 			case WL_PROXD_TLV_ID_BURST_DURATION:	/* wl_proxd_intvl_t */
1129 			case WL_PROXD_TLV_ID_BURST_PERIOD:
1130 			case WL_PROXD_TLV_ID_BURST_FTM_SEP:
1131 			case WL_PROXD_TLV_ID_BURST_TIMEOUT:
1132 			case WL_PROXD_TLV_ID_INIT_DELAY:
1133 				p_src_data = &p_config_param_info->data_intvl;
1134 				src_data_size = sizeof(wl_proxd_intvl_t);
1135 				break;
1136 			default:
1137 				ret = BCME_BADARG;
1138 				break;
1139 			}
1140 			if (ret != BCME_OK) {
1141 				DHD_RTT_ERR(("%s bad TLV ID : %d\n",
1142 					__FUNCTION__, p_config_param_info->tlvid));
1143 				break;
1144 			}
1145 
1146 			ret = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left,
1147 				p_config_param_info->tlvid, src_data_size, (uint8 *)p_src_data,
1148 				BCM_XTLV_OPTION_ALIGN32);
1149 			if (ret != BCME_OK) {
1150 				DHD_RTT_ERR(("%s: bcm_pack_xltv_entry() failed,"
1151 					" status=%d\n", __FUNCTION__, ret));
1152 				break;
1153 			}
1154 
1155 		}
1156 	}
1157 	return ret;
1158 }
1159 
1160 static int
dhd_rtt_ftm_enable(dhd_pub_t * dhd,bool enable)1161 dhd_rtt_ftm_enable(dhd_pub_t *dhd, bool enable)
1162 {
1163 	ftm_subcmd_info_t subcmd_info;
1164 	subcmd_info.name = (enable)? "enable" : "disable";
1165 	subcmd_info.cmdid = (enable)? WL_PROXD_CMD_ENABLE: WL_PROXD_CMD_DISABLE;
1166 	subcmd_info.handler = NULL;
1167 	return dhd_rtt_common_set_handler(dhd, &subcmd_info,
1168 			WL_PROXD_METHOD_FTM, WL_PROXD_SESSION_ID_GLOBAL);
1169 }
1170 
1171 static int
dhd_rtt_start_session(dhd_pub_t * dhd,wl_proxd_session_id_t session_id,bool start)1172 dhd_rtt_start_session(dhd_pub_t *dhd, wl_proxd_session_id_t session_id, bool start)
1173 {
1174 	ftm_subcmd_info_t subcmd_info;
1175 	subcmd_info.name = (start)? "start session" : "stop session";
1176 	subcmd_info.cmdid = (start)? WL_PROXD_CMD_START_SESSION: WL_PROXD_CMD_STOP_SESSION;
1177 	subcmd_info.handler = NULL;
1178 	return dhd_rtt_common_set_handler(dhd, &subcmd_info,
1179 			WL_PROXD_METHOD_FTM, session_id);
1180 }
1181 
1182 static int
dhd_rtt_delete_session(dhd_pub_t * dhd,wl_proxd_session_id_t session_id)1183 dhd_rtt_delete_session(dhd_pub_t *dhd, wl_proxd_session_id_t session_id)
1184 {
1185 	ftm_subcmd_info_t subcmd_info;
1186 	subcmd_info.name = "delete session";
1187 	subcmd_info.cmdid = WL_PROXD_CMD_DELETE_SESSION;
1188 	subcmd_info.handler = NULL;
1189 	return dhd_rtt_common_set_handler(dhd, &subcmd_info,
1190 			WL_PROXD_METHOD_FTM, session_id);
1191 }
1192 #ifdef WL_NAN
1193 int
dhd_rtt_delete_nan_session(dhd_pub_t * dhd)1194 dhd_rtt_delete_nan_session(dhd_pub_t *dhd)
1195 {
1196 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
1197 	struct wireless_dev *wdev = ndev_to_wdev(dev);
1198 	struct wiphy *wiphy = wdev->wiphy;
1199 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1200 	wl_cfgnan_terminate_directed_rtt_sessions(dev, cfg);
1201 	return BCME_OK;
1202 }
1203 #endif /* WL_NAN */
1204 /* API to find out if the given Peer Mac from FTM events
1205 * is nan-peer. Based on this we will handle the SESSION_END
1206 * event. For nan-peer FTM_SESSION_END event is ignored and handled in
1207 * nan-ranging-cancel or nan-ranging-end event.
1208 */
1209 static bool
dhd_rtt_is_nan_peer(dhd_pub_t * dhd,struct ether_addr * peer_mac)1210 dhd_rtt_is_nan_peer(dhd_pub_t *dhd, struct ether_addr *peer_mac)
1211 {
1212 #ifdef WL_NAN
1213 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
1214 	struct wireless_dev *wdev = ndev_to_wdev(dev);
1215 	struct wiphy *wiphy = wdev->wiphy;
1216 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1217 	nan_ranging_inst_t *ranging_inst = NULL;
1218 	bool ret = FALSE;
1219 
1220 	if (cfg->nan_enable == FALSE || ETHER_ISNULLADDR(peer_mac)) {
1221 		goto exit;
1222 	}
1223 
1224 	ranging_inst = wl_cfgnan_check_for_ranging(cfg, peer_mac);
1225 	if (ranging_inst) {
1226 		DHD_RTT((" RTT peer is of type NAN\n"));
1227 		ret = TRUE;
1228 		goto exit;
1229 	}
1230 exit:
1231 	return ret;
1232 #else
1233 	return FALSE;
1234 #endif /* WL_NAN */
1235 }
1236 
1237 #ifdef WL_NAN
1238 static int
dhd_rtt_nan_start_session(dhd_pub_t * dhd,rtt_target_info_t * rtt_target)1239 dhd_rtt_nan_start_session(dhd_pub_t *dhd, rtt_target_info_t *rtt_target)
1240 {
1241 	s32 err = BCME_OK;
1242 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
1243 	struct wireless_dev *wdev = ndev_to_wdev(dev);
1244 	struct wiphy *wiphy = wdev->wiphy;
1245 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1246 	wl_nan_ev_rng_rpt_ind_t range_res;
1247 	nan_ranging_inst_t *ranging_inst = NULL;
1248 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1249 
1250 	NAN_MUTEX_LOCK();
1251 
1252 	bzero(&range_res, sizeof(range_res));
1253 
1254 	if (!rtt_status) {
1255 		err = BCME_NOTENABLED;
1256 		goto done;
1257 	}
1258 
1259 	if (!cfg->nan_enable) { /* If nan is not enabled report error */
1260 		err = BCME_NOTENABLED;
1261 		goto done;
1262 	}
1263 
1264 	/* check if new ranging session allowed */
1265 	if (!wl_cfgnan_ranging_allowed(cfg)) {
1266 		/* responder should be in progress because initiator requests are
1267 		* queued in DHD. Since initiator has more proef cancel responder
1268 		* sessions
1269 		*/
1270 		wl_cfgnan_cancel_rng_responders(dev, cfg);
1271 	}
1272 
1273 	ranging_inst = wl_cfgnan_get_ranging_inst(cfg,
1274 			&rtt_target->addr, NAN_RANGING_ROLE_INITIATOR);
1275 	if (!ranging_inst) {
1276 		err = BCME_NORESOURCE;
1277 		goto done;
1278 	}
1279 
1280 	DHD_RTT(("Trigger nan based range request\n"));
1281 	err = wl_cfgnan_trigger_ranging(bcmcfg_to_prmry_ndev(cfg),
1282 			cfg, ranging_inst, NULL, NAN_RANGE_REQ_CMD, TRUE);
1283 	if (unlikely(err)) {
1284 		goto done;
1285 	}
1286 	ranging_inst->range_type = RTT_TYPE_NAN_DIRECTED;
1287 	ranging_inst->range_role = NAN_RANGING_ROLE_INITIATOR;
1288 	/* schedule proxd timeout */
1289 	schedule_delayed_work(&rtt_status->proxd_timeout,
1290 		msecs_to_jiffies(DHD_NAN_RTT_TIMER_INTERVAL_MS));
1291 done:
1292 	if (err) { /* notify failure RTT event to host */
1293 		DHD_RTT_ERR(("Failed to issue Nan Ranging Request err %d\n", err));
1294 		dhd_rtt_handle_nan_rtt_session_end(dhd, &rtt_target->addr);
1295 		/* try to reset geofence */
1296 		if (ranging_inst) {
1297 			wl_cfgnan_reset_geofence_ranging(cfg, ranging_inst,
1298 				RTT_SCHED_DIR_TRIGGER_FAIL);
1299 		}
1300 	}
1301 	NAN_MUTEX_UNLOCK();
1302 	return err;
1303 }
1304 #endif /* WL_NAN */
1305 
1306 static int
dhd_rtt_ftm_config(dhd_pub_t * dhd,wl_proxd_session_id_t session_id,ftm_config_category_t catagory,void * ftm_configs,int ftm_cfg_cnt)1307 dhd_rtt_ftm_config(dhd_pub_t *dhd, wl_proxd_session_id_t session_id,
1308 	ftm_config_category_t catagory, void *ftm_configs, int ftm_cfg_cnt)
1309 {
1310 	ftm_subcmd_info_t subcmd_info;
1311 	wl_proxd_tlv_t *p_tlv;
1312 	/* alloc mem for ioctl headr + reserved 0 bufsize for tlvs (initialize to zero) */
1313 	wl_proxd_iov_t *p_proxd_iov;
1314 	uint16 proxd_iovsize = 0;
1315 	uint16 bufsize;
1316 	uint16 buf_space_left;
1317 	uint16 all_tlvsize;
1318 	int ret = BCME_OK;
1319 
1320 	subcmd_info.name = "config";
1321 	subcmd_info.cmdid = WL_PROXD_CMD_CONFIG;
1322 
1323 	p_proxd_iov = rtt_alloc_getset_buf(WL_PROXD_METHOD_FTM, session_id, subcmd_info.cmdid,
1324 		FTM_IOC_BUFSZ, &proxd_iovsize);
1325 
1326 	if (p_proxd_iov == NULL) {
1327 		DHD_RTT_ERR(("%s : failed to allocate the iovar (size :%d)\n",
1328 			__FUNCTION__, FTM_IOC_BUFSZ));
1329 		return BCME_NOMEM;
1330 	}
1331 	/* setup TLVs */
1332 	bufsize = proxd_iovsize - WL_PROXD_IOV_HDR_SIZE; /* adjust available size for TLVs */
1333 	p_tlv = &p_proxd_iov->tlvs[0];
1334 	/* TLV buffer starts with a full size, will decrement for each packed TLV */
1335 	buf_space_left = bufsize;
1336 	if (catagory == FTM_CONFIG_CAT_OPTIONS) {
1337 		ret = rtt_handle_config_options(session_id, &p_tlv, &buf_space_left,
1338 				(ftm_config_options_info_t *)ftm_configs, ftm_cfg_cnt);
1339 	} else if (catagory == FTM_CONFIG_CAT_GENERAL) {
1340 		ret = rtt_handle_config_general(session_id, &p_tlv, &buf_space_left,
1341 				(ftm_config_param_info_t *)ftm_configs, ftm_cfg_cnt);
1342 	}
1343 	if (ret == BCME_OK) {
1344 		/* update the iov header, set len to include all TLVs + header */
1345 		all_tlvsize = (bufsize - buf_space_left);
1346 		p_proxd_iov->len = htol16(all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
1347 		ret = dhd_iovar(dhd, 0, "proxd", (char *)p_proxd_iov,
1348 				all_tlvsize + WL_PROXD_IOV_HDR_SIZE, NULL, 0, TRUE);
1349 		if (ret != BCME_OK) {
1350 			DHD_RTT_ERR(("%s : failed to set config\n", __FUNCTION__));
1351 		}
1352 	}
1353 	/* clean up */
1354 	kfree(p_proxd_iov);
1355 	return ret;
1356 }
1357 
1358 static int
dhd_rtt_get_version(dhd_pub_t * dhd,int * out_version)1359 dhd_rtt_get_version(dhd_pub_t *dhd, int *out_version)
1360 {
1361 	int ret;
1362 	ftm_subcmd_info_t subcmd_info;
1363 	subcmd_info.name = "ver";
1364 	subcmd_info.cmdid = WL_PROXD_CMD_GET_VERSION;
1365 	subcmd_info.handler = NULL;
1366 	ret = dhd_rtt_common_get_handler(dhd, &subcmd_info,
1367 			WL_PROXD_METHOD_FTM, WL_PROXD_SESSION_ID_GLOBAL);
1368 	*out_version = (ret == BCME_OK) ? subcmd_info.version : 0;
1369 	return ret;
1370 }
1371 #endif /* WL_CFG80211 */
1372 
1373 chanspec_t
dhd_rtt_convert_to_chspec(wifi_channel_info_t channel)1374 dhd_rtt_convert_to_chspec(wifi_channel_info_t channel)
1375 {
1376 	int bw;
1377 	chanspec_t chanspec = 0;
1378 	uint8 center_chan;
1379 	uint8 primary_chan;
1380 	/* set witdh to 20MHZ for 2.4G HZ */
1381 	if (channel.center_freq >= 2400 && channel.center_freq <= 2500) {
1382 		channel.width = WIFI_CHAN_WIDTH_20;
1383 	}
1384 	switch (channel.width) {
1385 	case WIFI_CHAN_WIDTH_20:
1386 		bw = WL_CHANSPEC_BW_20;
1387 		primary_chan = wf_mhz2channel(channel.center_freq, 0);
1388 		chanspec = wf_channel2chspec(primary_chan, bw);
1389 		break;
1390 	case WIFI_CHAN_WIDTH_40:
1391 		bw = WL_CHANSPEC_BW_40;
1392 		primary_chan = wf_mhz2channel(channel.center_freq, 0);
1393 		chanspec = wf_channel2chspec(primary_chan, bw);
1394 		break;
1395 	case WIFI_CHAN_WIDTH_80:
1396 		bw = WL_CHANSPEC_BW_80;
1397 		primary_chan = wf_mhz2channel(channel.center_freq, 0);
1398 		center_chan = wf_mhz2channel(channel.center_freq0, 0);
1399 		chanspec = wf_chspec_80(center_chan, primary_chan);
1400 		break;
1401 	default:
1402 		DHD_RTT_ERR(("doesn't support this bandwith : %d", channel.width));
1403 		bw = -1;
1404 		break;
1405 	}
1406 	return chanspec;
1407 }
1408 
1409 int
dhd_rtt_idx_to_burst_duration(uint idx)1410 dhd_rtt_idx_to_burst_duration(uint idx)
1411 {
1412 	if (idx >= ARRAY_SIZE(burst_duration_idx)) {
1413 		return -1;
1414 	}
1415 	return burst_duration_idx[idx];
1416 }
1417 
1418 int
dhd_rtt_set_cfg(dhd_pub_t * dhd,rtt_config_params_t * params)1419 dhd_rtt_set_cfg(dhd_pub_t *dhd, rtt_config_params_t *params)
1420 {
1421 	int err = BCME_OK;
1422 	int idx;
1423 	rtt_status_info_t *rtt_status = NULL;
1424 	struct net_device *dev = NULL;
1425 
1426 	NULL_CHECK(params, "params is NULL", err);
1427 	NULL_CHECK(dhd, "dhd is NULL", err);
1428 
1429 	dev = dhd_linux_get_primary_netdev(dhd);
1430 	rtt_status = GET_RTTSTATE(dhd);
1431 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1432 	NULL_CHECK(dev, "dev is NULL", err);
1433 
1434 	if (!HAS_11MC_CAP(rtt_status->rtt_capa.proto)) {
1435 		DHD_RTT_ERR(("doesn't support RTT \n"));
1436 		err = BCME_ERROR;
1437 		goto exit;
1438 	}
1439 
1440 	DHD_RTT(("%s enter\n", __FUNCTION__));
1441 
1442 	if (params->rtt_target_cnt > 0) {
1443 #ifdef WL_NAN
1444 		/* cancel ongoing geofence RTT if there */
1445 		if ((err = wl_cfgnan_suspend_geofence_rng_session(dev,
1446 			NULL, RTT_GEO_SUSPN_HOST_DIR_RTT_TRIG, 0)) != BCME_OK) {
1447 			goto exit;
1448 		}
1449 #endif /* WL_NAN */
1450 	} else {
1451 		err = BCME_BADARG;
1452 		goto exit;
1453 	}
1454 
1455 	mutex_lock(&rtt_status->rtt_mutex);
1456 	if (rtt_status->status != RTT_STOPPED) {
1457 		DHD_RTT_ERR(("rtt is already started\n"));
1458 		err = BCME_BUSY;
1459 		goto exit;
1460 	}
1461 	memset(rtt_status->rtt_config.target_info, 0, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
1462 	rtt_status->rtt_config.rtt_target_cnt = params->rtt_target_cnt;
1463 	memcpy(rtt_status->rtt_config.target_info,
1464 		params->target_info, TARGET_INFO_SIZE(params->rtt_target_cnt));
1465 	rtt_status->status = RTT_STARTED;
1466 	DHD_RTT_MEM(("dhd_rtt_set_cfg: RTT Started, target_cnt = %d\n", params->rtt_target_cnt));
1467 	/* start to measure RTT from first device */
1468 	/* find next target to trigger RTT */
1469 	for (idx = rtt_status->cur_idx; idx < rtt_status->rtt_config.rtt_target_cnt; idx++) {
1470 		/* skip the disabled device */
1471 		if (rtt_status->rtt_config.target_info[idx].disable) {
1472 			continue;
1473 		} else {
1474 			/* set the idx to cur_idx */
1475 			rtt_status->cur_idx = idx;
1476 			break;
1477 		}
1478 	}
1479 	if (idx < rtt_status->rtt_config.rtt_target_cnt) {
1480 		DHD_RTT(("rtt_status->cur_idx : %d\n", rtt_status->cur_idx));
1481 		rtt_status->rtt_sched_reason = RTT_SCHED_HOST_TRIGGER;
1482 		schedule_work(&rtt_status->work);
1483 	}
1484 exit:
1485 	mutex_unlock(&rtt_status->rtt_mutex);
1486 	return err;
1487 }
1488 
1489 #define GEOFENCE_RTT_LOCK(rtt_status) mutex_lock(&(rtt_status)->geofence_mutex)
1490 #define GEOFENCE_RTT_UNLOCK(rtt_status) mutex_unlock(&(rtt_status)->geofence_mutex)
1491 
1492 #ifdef WL_NAN
1493 /* sets geofence role concurrency state TRUE/FALSE */
1494 void
dhd_rtt_set_role_concurrency_state(dhd_pub_t * dhd,bool state)1495 dhd_rtt_set_role_concurrency_state(dhd_pub_t *dhd, bool state)
1496 {
1497 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1498 	if (!rtt_status) {
1499 		return;
1500 	}
1501 	GEOFENCE_RTT_LOCK(rtt_status);
1502 	rtt_status->geofence_cfg.role_concurr_state = state;
1503 	GEOFENCE_RTT_UNLOCK(rtt_status);
1504 }
1505 
1506 /* returns TRUE if geofence role concurrency constraint exists */
1507 bool
dhd_rtt_get_role_concurrency_state(dhd_pub_t * dhd)1508 dhd_rtt_get_role_concurrency_state(dhd_pub_t *dhd)
1509 {
1510 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1511 	if (!rtt_status) {
1512 		return FALSE;
1513 	}
1514 	return rtt_status->geofence_cfg.role_concurr_state;
1515 }
1516 
1517 int8
dhd_rtt_get_geofence_target_cnt(dhd_pub_t * dhd)1518 dhd_rtt_get_geofence_target_cnt(dhd_pub_t *dhd)
1519 {
1520 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1521 	if (!rtt_status) {
1522 		return 0;
1523 	}
1524 	return rtt_status->geofence_cfg.geofence_target_cnt;
1525 }
1526 
1527 /* sets geofence rtt state TRUE/FALSE */
1528 void
dhd_rtt_set_geofence_rtt_state(dhd_pub_t * dhd,bool state)1529 dhd_rtt_set_geofence_rtt_state(dhd_pub_t *dhd, bool state)
1530 {
1531 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1532 	if (!rtt_status) {
1533 		return;
1534 	}
1535 	GEOFENCE_RTT_LOCK(rtt_status);
1536 	rtt_status->geofence_cfg.rtt_in_progress = state;
1537 	GEOFENCE_RTT_UNLOCK(rtt_status);
1538 }
1539 
1540 /* returns TRUE if geofence rtt is in progress */
1541 bool
dhd_rtt_get_geofence_rtt_state(dhd_pub_t * dhd)1542 dhd_rtt_get_geofence_rtt_state(dhd_pub_t *dhd)
1543 {
1544 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1545 
1546 	if (!rtt_status) {
1547 		return FALSE;
1548 	}
1549 
1550 	return rtt_status->geofence_cfg.rtt_in_progress;
1551 }
1552 
1553 /* returns geofence RTT target list Head */
1554 rtt_geofence_target_info_t*
dhd_rtt_get_geofence_target_head(dhd_pub_t * dhd)1555 dhd_rtt_get_geofence_target_head(dhd_pub_t *dhd)
1556 {
1557 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1558 	rtt_geofence_target_info_t* head = NULL;
1559 
1560 	if (!rtt_status) {
1561 		return NULL;
1562 	}
1563 
1564 	if (rtt_status->geofence_cfg.geofence_target_cnt) {
1565 		head = &rtt_status->geofence_cfg.geofence_target_info[0];
1566 	}
1567 
1568 	return head;
1569 }
1570 
1571 int8
dhd_rtt_get_geofence_cur_target_idx(dhd_pub_t * dhd)1572 dhd_rtt_get_geofence_cur_target_idx(dhd_pub_t *dhd)
1573 {
1574 	int8 target_cnt = 0, cur_idx = DHD_RTT_INVALID_TARGET_INDEX;
1575 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1576 
1577 	if (!rtt_status) {
1578 		goto exit;
1579 	}
1580 
1581 	target_cnt = rtt_status->geofence_cfg.geofence_target_cnt;
1582 	if (target_cnt == 0) {
1583 		goto exit;
1584 	}
1585 
1586 	cur_idx = rtt_status->geofence_cfg.cur_target_idx;
1587 	ASSERT(cur_idx <= target_cnt);
1588 
1589 exit:
1590 	return cur_idx;
1591 }
1592 
1593 void
dhd_rtt_move_geofence_cur_target_idx_to_next(dhd_pub_t * dhd)1594 dhd_rtt_move_geofence_cur_target_idx_to_next(dhd_pub_t *dhd)
1595 {
1596 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1597 
1598 	if (!rtt_status) {
1599 		return;
1600 	}
1601 
1602 	if (rtt_status->geofence_cfg.geofence_target_cnt == 0) {
1603 		/* Invalidate current idx if no targets */
1604 		rtt_status->geofence_cfg.cur_target_idx =
1605 			DHD_RTT_INVALID_TARGET_INDEX;
1606 		/* Cancel pending retry timer if any */
1607 		if (delayed_work_pending(&rtt_status->rtt_retry_timer)) {
1608 			cancel_delayed_work(&rtt_status->rtt_retry_timer);
1609 		}
1610 		return;
1611 	}
1612 	rtt_status->geofence_cfg.cur_target_idx++;
1613 
1614 	if (rtt_status->geofence_cfg.cur_target_idx >=
1615 		rtt_status->geofence_cfg.geofence_target_cnt) {
1616 		/* Reset once all targets done */
1617 		rtt_status->geofence_cfg.cur_target_idx = 0;
1618 	}
1619 }
1620 
1621 /* returns geofence current RTT target */
1622 rtt_geofence_target_info_t*
dhd_rtt_get_geofence_current_target(dhd_pub_t * dhd)1623 dhd_rtt_get_geofence_current_target(dhd_pub_t *dhd)
1624 {
1625 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1626 	rtt_geofence_target_info_t* cur_target = NULL;
1627 	int cur_idx = 0;
1628 
1629 	if (!rtt_status) {
1630 		return NULL;
1631 	}
1632 
1633 	cur_idx = dhd_rtt_get_geofence_cur_target_idx(dhd);
1634 	if (cur_idx >= 0) {
1635 		cur_target = &rtt_status->geofence_cfg.geofence_target_info[cur_idx];
1636 	}
1637 
1638 	return cur_target;
1639 }
1640 
1641 /* returns geofence target from list for the peer */
1642 rtt_geofence_target_info_t*
dhd_rtt_get_geofence_target(dhd_pub_t * dhd,struct ether_addr * peer_addr,int8 * index)1643 dhd_rtt_get_geofence_target(dhd_pub_t *dhd, struct ether_addr* peer_addr, int8 *index)
1644 {
1645 	int8 i;
1646 	rtt_status_info_t *rtt_status;
1647 	int target_cnt;
1648 	rtt_geofence_target_info_t *geofence_target_info, *tgt = NULL;
1649 
1650 	rtt_status = GET_RTTSTATE(dhd);
1651 
1652 	if (!rtt_status) {
1653 		return NULL;
1654 	}
1655 
1656 	target_cnt = rtt_status->geofence_cfg.geofence_target_cnt;
1657 	geofence_target_info = rtt_status->geofence_cfg.geofence_target_info;
1658 
1659 	/* Loop through to find target */
1660 	for (i = 0; i < target_cnt; i++) {
1661 		if (geofence_target_info[i].valid == FALSE) {
1662 			break;
1663 		}
1664 		if (!memcmp(peer_addr, &geofence_target_info[i].peer_addr,
1665 				ETHER_ADDR_LEN)) {
1666 			*index = i;
1667 			tgt = &geofence_target_info[i];
1668 		}
1669 	}
1670 	if (!tgt) {
1671 		DHD_RTT(("dhd_rtt_get_geofence_target: Target not found in list,"
1672 			" MAC ADDR: "MACDBG" \n", MAC2STRDBG(peer_addr)));
1673 	}
1674 	return tgt;
1675 }
1676 
1677 /* add geofence target to the target list */
1678 int
dhd_rtt_add_geofence_target(dhd_pub_t * dhd,rtt_geofence_target_info_t * target)1679 dhd_rtt_add_geofence_target(dhd_pub_t *dhd, rtt_geofence_target_info_t *target)
1680 {
1681 	int err = BCME_OK;
1682 	rtt_status_info_t *rtt_status;
1683 	rtt_geofence_target_info_t  *geofence_target_info;
1684 	int8 geofence_target_cnt, index;
1685 
1686 	NULL_CHECK(dhd, "dhd is NULL", err);
1687 	rtt_status = GET_RTTSTATE(dhd);
1688 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1689 
1690 	GEOFENCE_RTT_LOCK(rtt_status);
1691 
1692 	/* Get the geofence_target via peer addr, index param is dumm here */
1693 	geofence_target_info = dhd_rtt_get_geofence_target(dhd, &target->peer_addr, &index);
1694 	if (geofence_target_info) {
1695 		DHD_RTT(("Duplicate geofencing RTT add request dropped\n"));
1696 		err = BCME_OK;
1697 		goto exit;
1698 	}
1699 
1700 	geofence_target_cnt = rtt_status->geofence_cfg.geofence_target_cnt;
1701 	if (geofence_target_cnt >= RTT_MAX_GEOFENCE_TARGET_CNT) {
1702 		DHD_RTT(("Queue full, Geofencing RTT add request dropped\n"));
1703 		err = BCME_NORESOURCE;
1704 		goto exit;
1705 	}
1706 
1707 	/* Add Geofence RTT request and increment target count */
1708 	geofence_target_info = rtt_status->geofence_cfg.geofence_target_info;
1709 	/* src and dest buffer len same, pointers of same DS statically allocated */
1710 	(void)memcpy_s(&geofence_target_info[geofence_target_cnt],
1711 		sizeof(geofence_target_info[geofence_target_cnt]), target,
1712 		sizeof(*target));
1713 	geofence_target_info[geofence_target_cnt].valid = TRUE;
1714 	rtt_status->geofence_cfg.geofence_target_cnt++;
1715 	if (rtt_status->geofence_cfg.geofence_target_cnt == 1) {
1716 		/* Adding first target */
1717 		rtt_status->geofence_cfg.cur_target_idx = 0;
1718 	}
1719 
1720 exit:
1721 	GEOFENCE_RTT_UNLOCK(rtt_status);
1722 	return err;
1723 }
1724 
1725 /* removes geofence target from the target list */
1726 int
dhd_rtt_remove_geofence_target(dhd_pub_t * dhd,struct ether_addr * peer_addr)1727 dhd_rtt_remove_geofence_target(dhd_pub_t *dhd, struct ether_addr *peer_addr)
1728 {
1729 	int err = BCME_OK;
1730 	rtt_status_info_t *rtt_status;
1731 	rtt_geofence_target_info_t  *geofence_target_info;
1732 	int8 geofence_target_cnt, j, index = 0;
1733 
1734 	NULL_CHECK(dhd, "dhd is NULL", err);
1735 	rtt_status = GET_RTTSTATE(dhd);
1736 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1737 
1738 	GEOFENCE_RTT_LOCK(rtt_status);
1739 
1740 	geofence_target_cnt = dhd_rtt_get_geofence_target_cnt(dhd);
1741 	if (geofence_target_cnt == 0) {
1742 		DHD_RTT(("Queue Empty, Geofencing RTT remove request dropped\n"));
1743 		ASSERT(0);
1744 		goto exit;
1745 	}
1746 
1747 	/* Get the geofence_target via peer addr */
1748 	geofence_target_info = dhd_rtt_get_geofence_target(dhd, peer_addr, &index);
1749 	if (geofence_target_info == NULL) {
1750 		DHD_RTT(("Geofencing RTT target not found, remove request dropped\n"));
1751 		err = BCME_NOTFOUND;
1752 		goto exit;
1753 	}
1754 
1755 	/* left shift all the valid entries, as we dont keep holes in list */
1756 	for (j = index; (j+1) < geofence_target_cnt; j++) {
1757 		if (geofence_target_info[j].valid == TRUE) {
1758 			/*
1759 			 * src and dest buffer len same, pointers of same DS
1760 			 * statically allocated
1761 			 */
1762 			(void)memcpy_s(&geofence_target_info[j], sizeof(geofence_target_info[j]),
1763 				&geofence_target_info[j + 1],
1764 				sizeof(geofence_target_info[j + 1]));
1765 		} else {
1766 			break;
1767 		}
1768 	}
1769 	rtt_status->geofence_cfg.geofence_target_cnt--;
1770 	if ((rtt_status->geofence_cfg.geofence_target_cnt == 0) ||
1771 		(index == rtt_status->geofence_cfg.cur_target_idx)) {
1772 		/* Move cur_idx to next target */
1773 		dhd_rtt_move_geofence_cur_target_idx_to_next(dhd);
1774 	} else if (index < rtt_status->geofence_cfg.cur_target_idx) {
1775 		/* Decrement cur index if cur target position changed */
1776 		rtt_status->geofence_cfg.cur_target_idx--;
1777 	}
1778 
1779 exit:
1780 	GEOFENCE_RTT_UNLOCK(rtt_status);
1781 	return err;
1782 }
1783 
1784 /* deletes/empty geofence target list */
1785 int
dhd_rtt_delete_geofence_target_list(dhd_pub_t * dhd)1786 dhd_rtt_delete_geofence_target_list(dhd_pub_t *dhd)
1787 {
1788 	rtt_status_info_t *rtt_status;
1789 
1790 	int err = BCME_OK;
1791 
1792 	NULL_CHECK(dhd, "dhd is NULL", err);
1793 	rtt_status = GET_RTTSTATE(dhd);
1794 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1795 	GEOFENCE_RTT_LOCK(rtt_status);
1796 	memset_s(&rtt_status->geofence_cfg, sizeof(rtt_geofence_cfg_t),
1797 		0, sizeof(rtt_geofence_cfg_t));
1798 	GEOFENCE_RTT_UNLOCK(rtt_status);
1799 	return err;
1800 }
1801 
1802 int
dhd_rtt_sched_geofencing_target(dhd_pub_t * dhd)1803 dhd_rtt_sched_geofencing_target(dhd_pub_t *dhd)
1804 {
1805 	rtt_geofence_target_info_t  *geofence_target_info;
1806 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
1807 	int ret = BCME_OK;
1808 	bool geofence_state;
1809 	bool role_concurrency_state;
1810 	u8 rtt_invalid_reason = RTT_STATE_VALID;
1811 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
1812 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1813 
1814 	NAN_MUTEX_LOCK();
1815 
1816 	if ((cfg->nan_init_state == FALSE) ||
1817 		(cfg->nan_enable == FALSE)) {
1818 		ret = BCME_NOTENABLED;
1819 		goto done;
1820 	}
1821 	geofence_state = dhd_rtt_get_geofence_rtt_state(dhd);
1822 	role_concurrency_state = dhd_rtt_get_role_concurrency_state(dhd);
1823 
1824 	DHD_RTT_ERR(("dhd_rtt_sched_geofencing_target: sched_reason = %d\n",
1825 		rtt_status->rtt_sched_reason));
1826 
1827 	if (geofence_state == TRUE || role_concurrency_state == TRUE) {
1828 		ret = BCME_ERROR;
1829 		DHD_RTT_ERR(("geofencing constraint , sched request dropped,"
1830 			" geofence_state = %d, role_concurrency_state = %d\n",
1831 			geofence_state, role_concurrency_state));
1832 		goto done;
1833 	}
1834 
1835 	/* Get current geofencing target */
1836 	geofence_target_info = dhd_rtt_get_geofence_current_target(dhd);
1837 
1838 	/* call cfg API for trigerring geofencing RTT */
1839 	if (geofence_target_info) {
1840 		/* check for dp/others concurrency */
1841 		rtt_invalid_reason = dhd_rtt_invalid_states(dev,
1842 				&geofence_target_info->peer_addr);
1843 		if (rtt_invalid_reason != RTT_STATE_VALID) {
1844 			ret = BCME_BUSY;
1845 			DHD_RTT_ERR(("DRV State is not valid for RTT, "
1846 				"invalid_state = %d\n", rtt_invalid_reason));
1847 			goto done;
1848 		}
1849 
1850 		ret = wl_cfgnan_trigger_geofencing_ranging(dev,
1851 				&geofence_target_info->peer_addr);
1852 		if (ret == BCME_OK) {
1853 			dhd_rtt_set_geofence_rtt_state(dhd, TRUE);
1854 		}
1855 	} else {
1856 		DHD_RTT(("No RTT target to schedule\n"));
1857 		ret = BCME_NOTFOUND;
1858 	}
1859 
1860 done:
1861 	NAN_MUTEX_UNLOCK();
1862 	return ret;
1863 }
1864 #endif /* WL_NAN */
1865 
1866 #ifdef WL_CFG80211
1867 #ifdef WL_NAN
1868 static void
dhd_rtt_retry(dhd_pub_t * dhd)1869 dhd_rtt_retry(dhd_pub_t *dhd)
1870 {
1871 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
1872 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
1873 	rtt_geofence_target_info_t  *geofence_target = NULL;
1874 	nan_ranging_inst_t *ranging_inst = NULL;
1875 
1876 	geofence_target = dhd_rtt_get_geofence_current_target(dhd);
1877 	if (!geofence_target) {
1878 		DHD_RTT(("dhd_rtt_retry: geofence target null\n"));
1879 		goto exit;
1880 	}
1881 	ranging_inst = wl_cfgnan_get_ranging_inst(cfg,
1882 		&geofence_target->peer_addr, NAN_RANGING_ROLE_INITIATOR);
1883 	if (!ranging_inst) {
1884 		DHD_RTT(("dhd_rtt_retry: ranging instance null\n"));
1885 		goto exit;
1886 	}
1887 	wl_cfgnan_reset_geofence_ranging(cfg,
1888 		ranging_inst, RTT_SCHED_RTT_RETRY_GEOFENCE);
1889 
1890 exit:
1891 	return;
1892 }
1893 
1894 static void
dhd_rtt_retry_work(struct work_struct * work)1895 dhd_rtt_retry_work(struct work_struct *work)
1896 {
1897 	rtt_status_info_t *rtt_status = NULL;
1898 	dhd_pub_t *dhd = NULL;
1899 	struct net_device *dev = NULL;
1900 	struct bcm_cfg80211 *cfg = NULL;
1901 
1902 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1903 #pragma GCC diagnostic push
1904 #pragma GCC diagnostic ignored "-Wcast-qual"
1905 #endif // endif
1906 	rtt_status = container_of(work, rtt_status_info_t, proxd_timeout.work);
1907 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1908 #pragma GCC diagnostic pop
1909 #endif // endif
1910 
1911 	dhd = rtt_status->dhd;
1912 	if (dhd == NULL) {
1913 		DHD_RTT_ERR(("%s : dhd is NULL\n", __FUNCTION__));
1914 		goto exit;
1915 	}
1916 	dev = dhd_linux_get_primary_netdev(dhd);
1917 	cfg = wl_get_cfg(dev);
1918 
1919 	NAN_MUTEX_LOCK();
1920 	mutex_lock(&rtt_status->rtt_mutex);
1921 	(void) dhd_rtt_retry(dhd);
1922 	mutex_unlock(&rtt_status->rtt_mutex);
1923 	NAN_MUTEX_UNLOCK();
1924 
1925 exit:
1926 	return;
1927 }
1928 #endif /* WL_NAN */
1929 
1930 /*
1931  * Return zero (0)
1932  * for valid RTT state
1933  * means if RTT is applicable
1934  */
1935 uint8
dhd_rtt_invalid_states(struct net_device * ndev,struct ether_addr * peer_addr)1936 dhd_rtt_invalid_states(struct net_device *ndev, struct ether_addr *peer_addr)
1937 {
1938 	uint8 invalid_reason = RTT_STATE_VALID;
1939 	struct bcm_cfg80211 *cfg = wl_get_cfg(ndev);
1940 
1941 	UNUSED_PARAMETER(cfg);
1942 	UNUSED_PARAMETER(invalid_reason);
1943 
1944 	/* Make sure peer addr is not NULL in caller */
1945 	ASSERT(peer_addr);
1946 	/*
1947 	 * Keep adding prohibited drv states here
1948 	 * Only generic conditions which block
1949 	 * All RTTs like NDP connection
1950 	 */
1951 
1952 #ifdef WL_NAN
1953 	if (wl_cfgnan_data_dp_exists_with_peer(cfg, peer_addr)) {
1954 		invalid_reason = RTT_STATE_INV_REASON_NDP_EXIST;
1955 		DHD_RTT(("NDP in progress/connected, RTT prohibited\n"));
1956 		goto exit;
1957 	}
1958 #endif /* WL_NAN */
1959 
1960 	/* Remove below #defines once more exit calls come */
1961 #ifdef WL_NAN
1962 exit:
1963 #endif /* WL_NAN */
1964 	return invalid_reason;
1965 }
1966 #endif /* WL_CFG80211 */
1967 
1968 void
dhd_rtt_schedule_rtt_work_thread(dhd_pub_t * dhd,int sched_reason)1969 dhd_rtt_schedule_rtt_work_thread(dhd_pub_t *dhd, int sched_reason)
1970 {
1971 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
1972 	if (rtt_status == NULL) {
1973 		ASSERT(0);
1974 	} else {
1975 		rtt_status->rtt_sched_reason = sched_reason;
1976 		schedule_work(&rtt_status->work);
1977 	}
1978 	return;
1979 }
1980 
1981 int
dhd_rtt_stop(dhd_pub_t * dhd,struct ether_addr * mac_list,int mac_cnt)1982 dhd_rtt_stop(dhd_pub_t *dhd, struct ether_addr *mac_list, int mac_cnt)
1983 {
1984 	int err = BCME_OK;
1985 #ifdef WL_CFG80211
1986 	int i = 0, j = 0;
1987 	rtt_status_info_t *rtt_status;
1988 	rtt_results_header_t *entry, *next;
1989 	rtt_result_t *rtt_result, *next2;
1990 	struct rtt_noti_callback *iter;
1991 
1992 	NULL_CHECK(dhd, "dhd is NULL", err);
1993 	rtt_status = GET_RTTSTATE(dhd);
1994 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1995 	if (rtt_status->status == RTT_STOPPED) {
1996 		DHD_RTT_ERR(("rtt is not started\n"));
1997 		return BCME_OK;
1998 	}
1999 	DHD_RTT(("%s enter\n", __FUNCTION__));
2000 	mutex_lock(&rtt_status->rtt_mutex);
2001 	for (i = 0; i < mac_cnt; i++) {
2002 		for (j = 0; j < rtt_status->rtt_config.rtt_target_cnt; j++) {
2003 			if (!bcmp(&mac_list[i], &rtt_status->rtt_config.target_info[j].addr,
2004 				ETHER_ADDR_LEN)) {
2005 				rtt_status->rtt_config.target_info[j].disable = TRUE;
2006 			}
2007 		}
2008 	}
2009 	if (rtt_status->all_cancel) {
2010 		/* cancel all of request */
2011 		rtt_status->status = RTT_STOPPED;
2012 		DHD_RTT(("current RTT process is cancelled\n"));
2013 		/* remove the rtt results in cache */
2014 		if (!list_empty(&rtt_status->rtt_results_cache)) {
2015 			/* Iterate rtt_results_header list */
2016 			GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2017 			list_for_each_entry_safe(entry, next,
2018 				&rtt_status->rtt_results_cache, list) {
2019 				list_del(&entry->list);
2020 				/* Iterate rtt_result list */
2021 				list_for_each_entry_safe(rtt_result, next2,
2022 					&entry->result_list, list) {
2023 					list_del(&rtt_result->list);
2024 					kfree(rtt_result);
2025 				}
2026 				kfree(entry);
2027 			}
2028 			GCC_DIAGNOSTIC_POP();
2029 		}
2030 		/* send the rtt complete event to wake up the user process */
2031 		GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2032 		list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
2033 			GCC_DIAGNOSTIC_POP();
2034 			iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache);
2035 		}
2036 		/* reinitialize the HEAD */
2037 		INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
2038 		/* clear information for rtt_config */
2039 		rtt_status->rtt_config.rtt_target_cnt = 0;
2040 		memset(rtt_status->rtt_config.target_info, 0,
2041 			TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
2042 		rtt_status->cur_idx = 0;
2043 		/* Cancel pending proxd timeout work if any */
2044 		if (delayed_work_pending(&rtt_status->proxd_timeout)) {
2045 			cancel_delayed_work(&rtt_status->proxd_timeout);
2046 		}
2047 		dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION);
2048 #ifdef WL_NAN
2049 		dhd_rtt_delete_nan_session(dhd);
2050 #endif /* WL_NAN */
2051 		dhd_rtt_ftm_enable(dhd, FALSE);
2052 	}
2053 	mutex_unlock(&rtt_status->rtt_mutex);
2054 #endif /* WL_CFG80211 */
2055 	return err;
2056 }
2057 
2058 #ifdef WL_CFG80211
2059 static void
dhd_rtt_timeout(dhd_pub_t * dhd)2060 dhd_rtt_timeout(dhd_pub_t *dhd)
2061 {
2062 	rtt_status_info_t *rtt_status;
2063 #ifndef DHD_DUMP_ON_RTT_TIMEOUT
2064 	rtt_target_info_t *rtt_target = NULL;
2065 	rtt_target_info_t *rtt_target_info = NULL;
2066 #ifdef WL_NAN
2067 	nan_ranging_inst_t *ranging_inst = NULL;
2068 	int ret = BCME_OK;
2069 	uint32 status;
2070 	struct net_device *ndev = dhd_linux_get_primary_netdev(dhd);
2071 	struct bcm_cfg80211 *cfg =  wiphy_priv(ndev->ieee80211_ptr->wiphy);
2072 #endif /* WL_NAN */
2073 #endif /* !DHD_DUMP_ON_RTT_TIMEOUT */
2074 
2075 	rtt_status = GET_RTTSTATE(dhd);
2076 	if (!rtt_status) {
2077 		DHD_RTT_ERR(("Proxd timer expired but no RTT status\n"));
2078 		goto exit;
2079 	}
2080 
2081 	if (RTT_IS_STOPPED(rtt_status)) {
2082 		DHD_RTT_ERR(("Proxd timer expired but no RTT Request\n"));
2083 		goto exit;
2084 	}
2085 
2086 #ifdef DHD_DUMP_ON_RTT_TIMEOUT
2087 	/* Dump, and Panic depending on memdump.info */
2088 	if (dhd_query_bus_erros(dhd)) {
2089 		goto exit;
2090 	}
2091 #ifdef DHD_FW_COREDUMP
2092 	if (dhd->memdump_enabled) {
2093 		/* Behave based on user memdump info */
2094 		dhd->memdump_type = DUMP_TYPE_PROXD_TIMEOUT;
2095 		dhd_bus_mem_dump(dhd);
2096 	}
2097 #endif /* DHD_FW_COREDUMP */
2098 #else /* DHD_DUMP_ON_RTT_TIMEOUT */
2099 	/* Cancel RTT for target and proceed to next target */
2100 	rtt_target_info = rtt_status->rtt_config.target_info;
2101 	if ((!rtt_target_info) ||
2102 		(rtt_status->cur_idx >= rtt_status->rtt_config.rtt_target_cnt)) {
2103 		goto exit;
2104 	}
2105 	rtt_target = &rtt_target_info[rtt_status->cur_idx];
2106 	WL_ERR(("Proxd timer expired for Target: "MACDBG" \n", MAC2STRDBG(&rtt_target->addr)));
2107 #ifdef WL_NAN
2108 	if (rtt_target->peer == RTT_PEER_NAN) {
2109 		ranging_inst = wl_cfgnan_check_for_ranging(cfg, &rtt_target->addr);
2110 		if (!ranging_inst) {
2111 			goto exit;
2112 		}
2113 		ret =  wl_cfgnan_cancel_ranging(ndev, cfg, ranging_inst->range_id,
2114 				NAN_RNG_TERM_FLAG_IMMEDIATE, &status);
2115 		if (unlikely(ret) || unlikely(status)) {
2116 			WL_ERR(("%s:nan range cancel failed ret = %d status = %d\n",
2117 				__FUNCTION__, ret, status));
2118 		}
2119 	} else
2120 #endif /* WL_NAN */
2121 	{
2122 		/* For Legacy RTT */
2123 		dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION);
2124 	}
2125 	dhd_rtt_create_failure_result(rtt_status, &rtt_target->addr);
2126 	dhd_rtt_handle_rtt_session_end(dhd);
2127 #endif /* DHD_DUMP_ON_RTT_TIMEOUT */
2128 exit:
2129 	return;
2130 }
2131 
2132 static void
dhd_rtt_timeout_work(struct work_struct * work)2133 dhd_rtt_timeout_work(struct work_struct *work)
2134 {
2135 	rtt_status_info_t *rtt_status = NULL;
2136 	dhd_pub_t *dhd = NULL;
2137 
2138 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2139 #pragma GCC diagnostic push
2140 #pragma GCC diagnostic ignored "-Wcast-qual"
2141 #endif // endif
2142 	rtt_status = container_of(work, rtt_status_info_t, proxd_timeout.work);
2143 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2144 #pragma GCC diagnostic pop
2145 #endif // endif
2146 
2147 	dhd = rtt_status->dhd;
2148 	if (dhd == NULL) {
2149 		DHD_RTT_ERR(("%s : dhd is NULL\n", __FUNCTION__));
2150 		return;
2151 	}
2152 	mutex_lock(&rtt_status->rtt_mutex);
2153 	(void) dhd_rtt_timeout(dhd);
2154 	mutex_unlock(&rtt_status->rtt_mutex);
2155 }
2156 
2157 static int
dhd_rtt_start(dhd_pub_t * dhd)2158 dhd_rtt_start(dhd_pub_t *dhd)
2159 {
2160 	int err = BCME_OK;
2161 	int err_at = 0;
2162 	char eabuf[ETHER_ADDR_STR_LEN];
2163 	char chanbuf[CHANSPEC_STR_LEN];
2164 	int pm = PM_OFF;
2165 	int ftm_cfg_cnt = 0;
2166 	int ftm_param_cnt = 0;
2167 	uint32 rspec = 0;
2168 	ftm_config_options_info_t ftm_configs[FTM_MAX_CONFIGS];
2169 	ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS];
2170 	rtt_target_info_t *rtt_target;
2171 	rtt_status_info_t *rtt_status;
2172 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
2173 	u8 ioctl_buf[WLC_IOCTL_SMLEN];
2174 	u8 rtt_invalid_reason = RTT_STATE_VALID;
2175 	int rtt_sched_type = RTT_TYPE_INVALID;
2176 
2177 	NULL_CHECK(dhd, "dhd is NULL", err);
2178 
2179 	rtt_status = GET_RTTSTATE(dhd);
2180 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2181 
2182 	DHD_RTT(("Enter %s\n", __FUNCTION__));
2183 
2184 	if (RTT_IS_STOPPED(rtt_status)) {
2185 		DHD_RTT(("No Directed RTT target to process, check for geofence\n"));
2186 		goto geofence;
2187 	}
2188 
2189 	if (rtt_status->cur_idx >= rtt_status->rtt_config.rtt_target_cnt) {
2190 		err = BCME_RANGE;
2191 		err_at = 1;
2192 		DHD_RTT(("%s : idx %d is out of range\n", __FUNCTION__, rtt_status->cur_idx));
2193 		if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) {
2194 			DHD_RTT_ERR(("STA is set as Target/Responder \n"));
2195 			err = BCME_ERROR;
2196 			err_at = 1;
2197 		}
2198 		goto exit;
2199 	}
2200 
2201 	rtt_status->pm = PM_OFF;
2202 	err = wldev_ioctl_get(dev, WLC_GET_PM, &rtt_status->pm, sizeof(rtt_status->pm));
2203 	if (err) {
2204 		DHD_RTT_ERR(("Failed to get the PM value\n"));
2205 	} else {
2206 		err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2207 		if (err) {
2208 			DHD_RTT_ERR(("Failed to set the PM\n"));
2209 			rtt_status->pm_restore = FALSE;
2210 		} else {
2211 			rtt_status->pm_restore = TRUE;
2212 		}
2213 	}
2214 
2215 	mutex_lock(&rtt_status->rtt_mutex);
2216 	/* Get a target information */
2217 	rtt_target = &rtt_status->rtt_config.target_info[rtt_status->cur_idx];
2218 	mutex_unlock(&rtt_status->rtt_mutex);
2219 	DHD_RTT(("%s enter\n", __FUNCTION__));
2220 
2221 	if (ETHER_ISNULLADDR(rtt_target->addr.octet)) {
2222 		err = BCME_BADADDR;
2223 		err_at = 2;
2224 		DHD_RTT(("RTT Target addr is NULL\n"));
2225 		goto exit;
2226 	}
2227 
2228 	/* check for dp/others concurrency */
2229 	rtt_invalid_reason = dhd_rtt_invalid_states(dev, &rtt_target->addr);
2230 	if (rtt_invalid_reason != RTT_STATE_VALID) {
2231 		err = BCME_BUSY;
2232 		err_at = 3;
2233 		DHD_RTT(("DRV State is not valid for RTT\n"));
2234 		goto exit;
2235 	}
2236 
2237 #ifdef WL_NAN
2238 	if (rtt_target->peer == RTT_PEER_NAN) {
2239 		rtt_sched_type = RTT_TYPE_NAN_DIRECTED;
2240 		rtt_status->status = RTT_ENABLED;
2241 		/* Ignore return value..failure taken care inside the API */
2242 		dhd_rtt_nan_start_session(dhd, rtt_target);
2243 		goto exit;
2244 	}
2245 #endif /* WL_NAN */
2246 	if (!RTT_IS_ENABLED(rtt_status)) {
2247 		/* enable ftm */
2248 		err = dhd_rtt_ftm_enable(dhd, TRUE);
2249 		if (err) {
2250 			DHD_RTT_ERR(("failed to enable FTM (%d)\n", err));
2251 			err_at = 5;
2252 			goto exit;
2253 		}
2254 	}
2255 
2256 	/* delete session of index default sesession  */
2257 	err = dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION);
2258 	if (err < 0 && err != BCME_NOTFOUND) {
2259 		DHD_RTT_ERR(("failed to delete session of FTM (%d)\n", err));
2260 		err_at = 6;
2261 		goto exit;
2262 	}
2263 	rtt_status->status = RTT_ENABLED;
2264 	memset(ftm_configs, 0, sizeof(ftm_configs));
2265 	memset(ftm_params, 0, sizeof(ftm_params));
2266 
2267 	/* configure the session 1 as initiator */
2268 	ftm_configs[ftm_cfg_cnt].enable = TRUE;
2269 	ftm_configs[ftm_cfg_cnt++].flags = WL_PROXD_SESSION_FLAG_INITIATOR;
2270 	dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_OPTIONS,
2271 		ftm_configs, ftm_cfg_cnt);
2272 
2273 	memset(ioctl_buf, 0, WLC_IOCTL_SMLEN);
2274 
2275 	/* Rand Mac for newer version in place of cur_eth */
2276 	if (dhd->wlc_ver_major < RTT_IOV_CUR_ETH_OBSOLETE) {
2277 		err = wldev_iovar_getbuf(dev, "cur_etheraddr", NULL, 0,
2278 				ioctl_buf, WLC_IOCTL_SMLEN, NULL);
2279 		if (err) {
2280 			DHD_RTT_ERR(("WLC_GET_CUR_ETHERADDR failed, error %d\n", err));
2281 			err_at = 7;
2282 			goto exit;
2283 		}
2284 		memcpy(rtt_target->local_addr.octet, ioctl_buf, ETHER_ADDR_LEN);
2285 
2286 		/* local mac address */
2287 		if (!ETHER_ISNULLADDR(rtt_target->local_addr.octet)) {
2288 			ftm_params[ftm_param_cnt].mac_addr = rtt_target->local_addr;
2289 			ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_CUR_ETHER_ADDR;
2290 			bcm_ether_ntoa(&rtt_target->local_addr, eabuf);
2291 			DHD_RTT((">\t local %s\n", eabuf));
2292 		}
2293 	}
2294 	/* target's mac address */
2295 	if (!ETHER_ISNULLADDR(rtt_target->addr.octet)) {
2296 		ftm_params[ftm_param_cnt].mac_addr = rtt_target->addr;
2297 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_PEER_MAC;
2298 		bcm_ether_ntoa(&rtt_target->addr, eabuf);
2299 		DHD_RTT((">\t target %s\n", eabuf));
2300 	}
2301 	/* target's chanspec */
2302 	if (rtt_target->chanspec) {
2303 		ftm_params[ftm_param_cnt].chanspec = htol32((uint32)rtt_target->chanspec);
2304 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_CHANSPEC;
2305 		wf_chspec_ntoa(rtt_target->chanspec, chanbuf);
2306 		DHD_RTT((">\t chanspec : %s\n", chanbuf));
2307 	}
2308 	/* num-burst */
2309 	if (rtt_target->num_burst) {
2310 		ftm_params[ftm_param_cnt].data16 = htol16(rtt_target->num_burst);
2311 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_NUM_BURST;
2312 		DHD_RTT((">\t num of burst : %d\n", rtt_target->num_burst));
2313 	}
2314 	/* number of frame per burst */
2315 	rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_80M;
2316 	if (CHSPEC_IS80(rtt_target->chanspec)) {
2317 		rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_80M;
2318 	} else if (CHSPEC_IS40(rtt_target->chanspec)) {
2319 		rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_40M;
2320 	} else if (CHSPEC_IS20(rtt_target->chanspec)) {
2321 		rtt_target->num_frames_per_burst = FTM_DEFAULT_CNT_20M;
2322 	}
2323 	ftm_params[ftm_param_cnt].data16 = htol16(rtt_target->num_frames_per_burst);
2324 	ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_NUM_FTM;
2325 	DHD_RTT((">\t number of frame per burst : %d\n", rtt_target->num_frames_per_burst));
2326 
2327 	/* FTM retry count */
2328 	if (rtt_target->num_retries_per_ftm) {
2329 		ftm_params[ftm_param_cnt].data8 = rtt_target->num_retries_per_ftm;
2330 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_FTM_RETRIES;
2331 		DHD_RTT((">\t retry count of FTM  : %d\n", rtt_target->num_retries_per_ftm));
2332 	}
2333 	/* FTM Request retry count */
2334 	if (rtt_target->num_retries_per_ftmr) {
2335 		ftm_params[ftm_param_cnt].data8 = rtt_target->num_retries_per_ftmr;
2336 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_FTM_REQ_RETRIES;
2337 		DHD_RTT((">\t retry count of FTM Req : %d\n", rtt_target->num_retries_per_ftmr));
2338 	}
2339 	/* burst-period */
2340 	if (rtt_target->burst_period) {
2341 		ftm_params[ftm_param_cnt].data_intvl.intvl =
2342 			htol32(rtt_target->burst_period); /* ms */
2343 		ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC;
2344 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_PERIOD;
2345 		DHD_RTT((">\t burst period : %d ms\n", rtt_target->burst_period));
2346 	}
2347 	/* Setting both duration and timeout to MAX duration
2348 	 * to handle the congestion environments.
2349 	 * Hence ignoring the user config.
2350 	 */
2351 	/* burst-duration */
2352 	rtt_target->burst_duration = FTM_MAX_BURST_DUR_TMO_MS;
2353 	if (rtt_target->burst_duration) {
2354 		ftm_params[ftm_param_cnt].data_intvl.intvl =
2355 			htol32(rtt_target->burst_duration); /* ms */
2356 		ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC;
2357 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_DURATION;
2358 		DHD_RTT((">\t burst duration : %d ms\n",
2359 			rtt_target->burst_duration));
2360 	}
2361 	/* burst-timeout */
2362 	rtt_target->burst_timeout = FTM_MAX_BURST_DUR_TMO_MS;
2363 	if (rtt_target->burst_timeout) {
2364 		ftm_params[ftm_param_cnt].data_intvl.intvl =
2365 			htol32(rtt_target->burst_timeout); /* ms */
2366 		ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC;
2367 		ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_TIMEOUT;
2368 		DHD_RTT((">\t burst timeout : %d ms\n",
2369 			rtt_target->burst_timeout));
2370 	}
2371 	/* event_mask..applicable for only Legacy RTT.
2372 	* For nan-rtt config happens from firmware
2373 	*/
2374 	ftm_params[ftm_param_cnt].event_mask = ((1 << WL_PROXD_EVENT_BURST_END) |
2375 		(1 << WL_PROXD_EVENT_SESSION_END));
2376 	ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_EVENT_MASK;
2377 
2378 	if (rtt_target->bw && rtt_target->preamble) {
2379 		bool use_default = FALSE;
2380 		int nss;
2381 		int mcs;
2382 		switch (rtt_target->preamble) {
2383 			case RTT_PREAMBLE_LEGACY:
2384 				rspec |= WL_RSPEC_ENCODE_RATE; /* 11abg */
2385 				rspec |= WL_RATE_6M;
2386 				break;
2387 			case RTT_PREAMBLE_HT:
2388 				rspec |= WL_RSPEC_ENCODE_HT; /* 11n HT */
2389 				mcs = 0; /* default MCS 0 */
2390 				rspec |= mcs;
2391 				break;
2392 			case RTT_PREAMBLE_VHT:
2393 				rspec |= WL_RSPEC_ENCODE_VHT; /* 11ac VHT */
2394 				mcs = 0; /* default MCS 0 */
2395 				nss = 1; /* default Nss = 1  */
2396 				rspec |= (nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs;
2397 				break;
2398 			default:
2399 				DHD_RTT(("doesn't support this preamble : %d\n",
2400 					rtt_target->preamble));
2401 				use_default = TRUE;
2402 				break;
2403 		}
2404 		switch (rtt_target->bw) {
2405 			case RTT_BW_20:
2406 				rspec |= WL_RSPEC_BW_20MHZ;
2407 				break;
2408 			case RTT_BW_40:
2409 				rspec |= WL_RSPEC_BW_40MHZ;
2410 				break;
2411 			case RTT_BW_80:
2412 				rspec |= WL_RSPEC_BW_80MHZ;
2413 				break;
2414 			default:
2415 				DHD_RTT(("doesn't support this BW : %d\n", rtt_target->bw));
2416 				use_default = TRUE;
2417 				break;
2418 		}
2419 		if (!use_default) {
2420 			ftm_params[ftm_param_cnt].data32 = htol32(rspec);
2421 			ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_RATESPEC;
2422 			DHD_RTT((">\t ratespec : %d\n", rspec));
2423 		}
2424 
2425 	}
2426 	dhd_set_rand_mac_oui(dhd);
2427 	dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_GENERAL,
2428 			ftm_params, ftm_param_cnt);
2429 
2430 	rtt_sched_type = RTT_TYPE_LEGACY;
2431 	err = dhd_rtt_start_session(dhd, FTM_DEFAULT_SESSION, TRUE);
2432 	if (err) {
2433 		DHD_RTT_ERR(("failed to start session of FTM : error %d\n", err));
2434 		err_at = 8;
2435 	} else {
2436 		/* schedule proxd timeout */
2437 		schedule_delayed_work(&rtt_status->proxd_timeout,
2438 			msecs_to_jiffies(DHD_NAN_RTT_TIMER_INTERVAL_MS));
2439 
2440 	}
2441 
2442 	goto exit;
2443 geofence:
2444 #ifdef WL_NAN
2445 	/* sched geofencing rtt */
2446 	rtt_sched_type = RTT_TYPE_NAN_GEOFENCE;
2447 	if ((err = dhd_rtt_sched_geofencing_target(dhd)) != BCME_OK) {
2448 		DHD_RTT_ERR(("geofencing sched failed, err = %d\n", err));
2449 		err_at = 9;
2450 	}
2451 #endif /* WL_NAN */
2452 
2453 exit:
2454 	if (err) {
2455 		/* RTT Failed */
2456 		DHD_RTT_ERR(("dhd_rtt_start: Failed & RTT_STOPPED, err = %d,"
2457 			" err_at = %d, rtt_sched_type = %d, rtt_invalid_reason = %d\n"
2458 			" sched_reason = %d",
2459 			err, err_at, rtt_sched_type, rtt_invalid_reason,
2460 			rtt_status->rtt_sched_reason));
2461 		rtt_status->status = RTT_STOPPED;
2462 		/* disable FTM */
2463 		dhd_rtt_ftm_enable(dhd, FALSE);
2464 		if (rtt_status->pm_restore) {
2465 			pm = PM_FAST;
2466 			DHD_RTT_ERR(("pm_restore =%d func =%s \n",
2467 				rtt_status->pm_restore, __FUNCTION__));
2468 			err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2469 			if (err) {
2470 				DHD_RTT_ERR(("Failed to set PM \n"));
2471 			} else {
2472 				rtt_status->pm_restore = FALSE;
2473 			}
2474 		}
2475 	}
2476 	return err;
2477 }
2478 #endif /* WL_CFG80211 */
2479 
2480 int
dhd_rtt_register_noti_callback(dhd_pub_t * dhd,void * ctx,dhd_rtt_compl_noti_fn noti_fn)2481 dhd_rtt_register_noti_callback(dhd_pub_t *dhd, void *ctx, dhd_rtt_compl_noti_fn noti_fn)
2482 {
2483 	int err = BCME_OK;
2484 	struct rtt_noti_callback *cb = NULL, *iter;
2485 	rtt_status_info_t *rtt_status;
2486 	NULL_CHECK(dhd, "dhd is NULL", err);
2487 	NULL_CHECK(noti_fn, "noti_fn is NULL", err);
2488 
2489 	rtt_status = GET_RTTSTATE(dhd);
2490 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2491 	spin_lock_bh(&noti_list_lock);
2492 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2493 	list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
2494 		GCC_DIAGNOSTIC_POP();
2495 		if (iter->noti_fn == noti_fn) {
2496 			goto exit;
2497 		}
2498 	}
2499 	cb = kmalloc(sizeof(struct rtt_noti_callback), GFP_ATOMIC);
2500 	if (!cb) {
2501 		err = -ENOMEM;
2502 		goto exit;
2503 	}
2504 	cb->noti_fn = noti_fn;
2505 	cb->ctx = ctx;
2506 	list_add(&cb->list, &rtt_status->noti_fn_list);
2507 exit:
2508 	spin_unlock_bh(&noti_list_lock);
2509 	return err;
2510 }
2511 
2512 int
dhd_rtt_unregister_noti_callback(dhd_pub_t * dhd,dhd_rtt_compl_noti_fn noti_fn)2513 dhd_rtt_unregister_noti_callback(dhd_pub_t *dhd, dhd_rtt_compl_noti_fn noti_fn)
2514 {
2515 	int err = BCME_OK;
2516 	struct rtt_noti_callback *cb = NULL, *iter;
2517 	rtt_status_info_t *rtt_status;
2518 	NULL_CHECK(dhd, "dhd is NULL", err);
2519 	NULL_CHECK(noti_fn, "noti_fn is NULL", err);
2520 	rtt_status = GET_RTTSTATE(dhd);
2521 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2522 	spin_lock_bh(&noti_list_lock);
2523 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
2524 	list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
2525 		GCC_DIAGNOSTIC_POP();
2526 		if (iter->noti_fn == noti_fn) {
2527 			cb = iter;
2528 			list_del(&cb->list);
2529 			break;
2530 		}
2531 	}
2532 
2533 	spin_unlock_bh(&noti_list_lock);
2534 	if (cb) {
2535 		kfree(cb);
2536 	}
2537 	return err;
2538 }
2539 
2540 static wifi_rate_t
dhd_rtt_convert_rate_to_host(uint32 rspec)2541 dhd_rtt_convert_rate_to_host(uint32 rspec)
2542 {
2543 	wifi_rate_t host_rate;
2544 	uint32 bandwidth;
2545 	memset(&host_rate, 0, sizeof(wifi_rate_t));
2546 	if (RSPEC_ISLEGACY(rspec)) {
2547 		host_rate.preamble = 0;
2548 	} else if (RSPEC_ISHT(rspec)) {
2549 		host_rate.preamble = 2;
2550 		host_rate.rateMcsIdx = rspec & WL_RSPEC_RATE_MASK;
2551 	} else if (RSPEC_ISVHT(rspec)) {
2552 		host_rate.preamble = 3;
2553 		host_rate.rateMcsIdx = rspec & WL_RSPEC_VHT_MCS_MASK;
2554 		host_rate.nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
2555 	}
2556 
2557 	bandwidth = RSPEC_BW(rspec);
2558 	switch (bandwidth) {
2559 	case WL_RSPEC_BW_20MHZ:
2560 		host_rate.bw = RTT_RATE_20M;
2561 		break;
2562 	case WL_RSPEC_BW_40MHZ:
2563 		host_rate.bw = RTT_RATE_40M;
2564 		break;
2565 	case WL_RSPEC_BW_80MHZ:
2566 		host_rate.bw = RTT_RATE_80M;
2567 		break;
2568 	case WL_RSPEC_BW_160MHZ:
2569 		host_rate.bw = RTT_RATE_160M;
2570 		break;
2571 	default:
2572 		host_rate.bw = RTT_RATE_20M;
2573 		break;
2574 	}
2575 
2576 	host_rate.bitrate = rate_rspec2rate(rspec) / 100; /* 100kbps */
2577 	DHD_RTT(("bit rate : %d\n", host_rate.bitrate));
2578 	return host_rate;
2579 }
2580 
2581 #define FTM_FRAME_TYPES	{"SETUP", "TRIGGER", "TIMESTAMP"}
2582 static int
dhd_rtt_convert_results_to_host_v1(rtt_result_t * rtt_result,const uint8 * p_data,uint16 tlvid,uint16 len)2583 dhd_rtt_convert_results_to_host_v1(rtt_result_t *rtt_result, const uint8 *p_data,
2584 	uint16 tlvid, uint16 len)
2585 {
2586 	int i;
2587 	int err = BCME_OK;
2588 	char eabuf[ETHER_ADDR_STR_LEN];
2589 	wl_proxd_result_flags_t flags;
2590 	wl_proxd_session_state_t session_state;
2591 	wl_proxd_status_t proxd_status;
2592 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0))
2593 	struct timespec64 ts;
2594 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
2595 	struct timespec ts;
2596 #endif /* LINUX_VER >= 2.6.39 */
2597 	uint32 ratespec;
2598 	uint32 avg_dist;
2599 	const wl_proxd_rtt_result_v1_t *p_data_info = NULL;
2600 	const wl_proxd_rtt_sample_v1_t *p_sample_avg = NULL;
2601 	const wl_proxd_rtt_sample_v1_t *p_sample = NULL;
2602 	wl_proxd_intvl_t rtt;
2603 	wl_proxd_intvl_t p_time;
2604 	uint16 num_rtt = 0, snr = 0, bitflips = 0;
2605 	wl_proxd_phy_error_t tof_phy_error = 0;
2606 	wl_proxd_phy_error_t tof_phy_tgt_error = 0;
2607 	wl_proxd_snr_t tof_target_snr = 0;
2608 	wl_proxd_bitflips_t tof_target_bitflips = 0;
2609 	int16 rssi = 0;
2610 	int32 dist = 0;
2611 	uint8 num_ftm = 0;
2612 	char *ftm_frame_types[] = FTM_FRAME_TYPES;
2613 	rtt_report_t *rtt_report = &(rtt_result->report);
2614 
2615 	BCM_REFERENCE(ftm_frame_types);
2616 	BCM_REFERENCE(dist);
2617 	BCM_REFERENCE(rssi);
2618 	BCM_REFERENCE(tof_target_bitflips);
2619 	BCM_REFERENCE(tof_target_snr);
2620 	BCM_REFERENCE(tof_phy_tgt_error);
2621 	BCM_REFERENCE(tof_phy_error);
2622 	BCM_REFERENCE(bitflips);
2623 	BCM_REFERENCE(snr);
2624 	BCM_REFERENCE(session_state);
2625 	BCM_REFERENCE(ftm_session_state_value_to_logstr);
2626 
2627 	NULL_CHECK(rtt_report, "rtt_report is NULL", err);
2628 	NULL_CHECK(p_data, "p_data is NULL", err);
2629 	DHD_RTT(("%s enter\n", __FUNCTION__));
2630 	p_data_info = (const wl_proxd_rtt_result_v1_t *) p_data;
2631 	/* unpack and format 'flags' for display */
2632 	flags = ltoh16_ua(&p_data_info->flags);
2633 
2634 	/* session state and status */
2635 	session_state = ltoh16_ua(&p_data_info->state);
2636 	proxd_status = ltoh32_ua(&p_data_info->status);
2637 	bcm_ether_ntoa((&(p_data_info->peer)), eabuf);
2638 	ftm_status_value_to_logstr(proxd_status);
2639 	DHD_RTT((">\tTarget(%s) session state=%d(%s), status=%d(%s)\n",
2640 		eabuf,
2641 		session_state,
2642 		ftm_session_state_value_to_logstr(session_state),
2643 		proxd_status,
2644 		ftm_status_value_to_logstr(proxd_status)));
2645 
2646 	/* show avg_dist (1/256m units), burst_num */
2647 	avg_dist = ltoh32_ua(&p_data_info->avg_dist);
2648 	if (avg_dist == 0xffffffff) {	/* report 'failure' case */
2649 		DHD_RTT((">\tavg_dist=-1m, burst_num=%d, valid_measure_cnt=%d\n",
2650 		ltoh16_ua(&p_data_info->burst_num),
2651 		p_data_info->num_valid_rtt)); /* in a session */
2652 		avg_dist = FTM_INVALID;
2653 	}
2654 	else {
2655 		DHD_RTT((">\tavg_dist=%d.%04dm, burst_num=%d, valid_measure_cnt=%d num_ftm=%d\n",
2656 			avg_dist >> 8, /* 1/256m units */
2657 			((avg_dist & 0xff) * 625) >> 4,
2658 			ltoh16_ua(&p_data_info->burst_num),
2659 			p_data_info->num_valid_rtt,
2660 			p_data_info->num_ftm)); /* in a session */
2661 	}
2662 	/* show 'avg_rtt' sample */
2663 	p_sample_avg = &p_data_info->avg_rtt;
2664 	ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu));
2665 	DHD_RTT((">\tavg_rtt sample: rssi=%d rtt=%d%s std_deviation =%d.%d ratespec=0x%08x\n",
2666 		(int16) ltoh16_ua(&p_sample_avg->rssi),
2667 		ltoh32_ua(&p_sample_avg->rtt.intvl),
2668 		ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu)),
2669 		ltoh16_ua(&p_data_info->sd_rtt)/10, ltoh16_ua(&p_data_info->sd_rtt)%10,
2670 		ltoh32_ua(&p_sample_avg->ratespec)));
2671 
2672 	/* set peer address */
2673 	rtt_report->addr = p_data_info->peer;
2674 	/* burst num */
2675 	rtt_report->burst_num = ltoh16_ua(&p_data_info->burst_num);
2676 	/* success num */
2677 	rtt_report->success_num = p_data_info->num_valid_rtt;
2678 	/* actual number of FTM supported by peer */
2679 	rtt_report->num_per_burst_peer = p_data_info->num_ftm;
2680 	rtt_report->negotiated_burst_num = p_data_info->num_ftm;
2681 	/* status */
2682 	rtt_report->status = ftm_get_statusmap_info(proxd_status,
2683 			&ftm_status_map_info[0], ARRAYSIZE(ftm_status_map_info));
2684 
2685 	/* rssi (0.5db) */
2686 	rtt_report->rssi = ABS((wl_proxd_rssi_t)ltoh16_ua(&p_data_info->avg_rtt.rssi)) * 2;
2687 
2688 	/* rx rate */
2689 	ratespec = ltoh32_ua(&p_data_info->avg_rtt.ratespec);
2690 	rtt_report->rx_rate = dhd_rtt_convert_rate_to_host(ratespec);
2691 	/* tx rate */
2692 	if (flags & WL_PROXD_RESULT_FLAG_VHTACK) {
2693 		rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0x2010010);
2694 	} else {
2695 		rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0xc);
2696 	}
2697 	/* rtt_sd */
2698 	rtt.tmu = ltoh16_ua(&p_data_info->avg_rtt.rtt.tmu);
2699 	rtt.intvl = ltoh32_ua(&p_data_info->avg_rtt.rtt.intvl);
2700 	rtt_report->rtt = (wifi_timespan)FTM_INTVL2NSEC(&rtt) * 1000; /* nano -> pico seconds */
2701 	rtt_report->rtt_sd = ltoh16_ua(&p_data_info->sd_rtt); /* nano -> 0.1 nano */
2702 	DHD_RTT(("rtt_report->rtt : %llu\n", rtt_report->rtt));
2703 	DHD_RTT(("rtt_report->rssi : %d (0.5db)\n", rtt_report->rssi));
2704 
2705 	/* average distance */
2706 	if (avg_dist != FTM_INVALID) {
2707 		rtt_report->distance = (avg_dist >> 8) * 1000; /* meter -> mm */
2708 		rtt_report->distance += (avg_dist & 0xff) * 1000 / 256;
2709 	} else {
2710 		rtt_report->distance = FTM_INVALID;
2711 	}
2712 	/* time stamp */
2713 	/* get the time elapsed from boot time */
2714 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
2715 	get_monotonic_boottime(&ts);
2716 	rtt_report->ts = (uint64)TIMESPEC_TO_US(ts);
2717 #endif /* LINUX_VER >= 2.6.39 */
2718 
2719 	if (proxd_status == WL_PROXD_E_REMOTE_FAIL) {
2720 		/* retry time  after failure */
2721 		p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
2722 		p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
2723 		rtt_report->retry_after_duration = FTM_INTVL2SEC(&p_time); /* s -> s */
2724 		DHD_RTT((">\tretry_after: %d%s\n",
2725 			ltoh32_ua(&p_data_info->u.retry_after.intvl),
2726 			ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.retry_after.tmu))));
2727 	} else {
2728 		/* burst duration */
2729 		p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
2730 		p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
2731 		rtt_report->burst_duration =  FTM_INTVL2MSEC(&p_time); /* s -> ms */
2732 		DHD_RTT((">\tburst_duration: %d%s\n",
2733 			ltoh32_ua(&p_data_info->u.burst_duration.intvl),
2734 			ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.burst_duration.tmu))));
2735 		DHD_RTT(("rtt_report->burst_duration : %d\n", rtt_report->burst_duration));
2736 	}
2737 
2738 	/* display detail if available */
2739 	num_rtt = ltoh16_ua(&p_data_info->num_rtt);
2740 	if (num_rtt > 0) {
2741 		DHD_RTT((">\tnum rtt: %d samples\n", num_rtt));
2742 		p_sample = &p_data_info->rtt[0];
2743 		for (i = 0; i < num_rtt; i++) {
2744 			snr = 0;
2745 			bitflips = 0;
2746 			tof_phy_error = 0;
2747 			tof_phy_tgt_error = 0;
2748 			tof_target_snr = 0;
2749 			tof_target_bitflips = 0;
2750 			rssi = 0;
2751 			dist = 0;
2752 			num_ftm = p_data_info->num_ftm;
2753 			/* FTM frames 1,4,7,11 have valid snr, rssi and bitflips */
2754 			if ((i % num_ftm) == 1) {
2755 				rssi = (wl_proxd_rssi_t) ltoh16_ua(&p_sample->rssi);
2756 				snr = (wl_proxd_snr_t) ltoh16_ua(&p_sample->snr);
2757 				bitflips = (wl_proxd_bitflips_t) ltoh16_ua(&p_sample->bitflips);
2758 				tof_phy_error =
2759 					(wl_proxd_phy_error_t)
2760 					ltoh32_ua(&p_sample->tof_phy_error);
2761 				tof_phy_tgt_error =
2762 					(wl_proxd_phy_error_t)
2763 					ltoh32_ua(&p_sample->tof_tgt_phy_error);
2764 				tof_target_snr =
2765 					(wl_proxd_snr_t)
2766 					ltoh16_ua(&p_sample->tof_tgt_snr);
2767 				tof_target_bitflips =
2768 					(wl_proxd_bitflips_t)
2769 					ltoh16_ua(&p_sample->tof_tgt_bitflips);
2770 				dist = ltoh32_ua(&p_sample->distance);
2771 			} else {
2772 				rssi = -1;
2773 				snr = 0;
2774 				bitflips = 0;
2775 				dist = 0;
2776 				tof_target_bitflips = 0;
2777 				tof_target_snr = 0;
2778 				tof_phy_tgt_error = 0;
2779 			}
2780 			DHD_RTT((">\t sample[%d]: id=%d rssi=%d snr=0x%x bitflips=%d"
2781 				" tof_phy_error %x tof_phy_tgt_error %x target_snr=0x%x"
2782 				" target_bitflips=%d dist=%d rtt=%d%s status %s"
2783 				" Type %s coreid=%d\n",
2784 				i, p_sample->id, rssi, snr,
2785 				bitflips, tof_phy_error, tof_phy_tgt_error,
2786 				tof_target_snr,
2787 				tof_target_bitflips, dist,
2788 				ltoh32_ua(&p_sample->rtt.intvl),
2789 				ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)),
2790 				ftm_status_value_to_logstr(ltoh32_ua(&p_sample->status)),
2791 				ftm_frame_types[i % num_ftm], p_sample->coreid));
2792 			p_sample++;
2793 		}
2794 	}
2795 	return err;
2796 }
2797 
2798 static int
dhd_rtt_convert_results_to_host_v2(rtt_result_t * rtt_result,const uint8 * p_data,uint16 tlvid,uint16 len)2799 dhd_rtt_convert_results_to_host_v2(rtt_result_t *rtt_result, const uint8 *p_data,
2800 	uint16 tlvid, uint16 len)
2801 {
2802 	int i;
2803 	int err = BCME_OK;
2804 	char eabuf[ETHER_ADDR_STR_LEN];
2805 	wl_proxd_result_flags_t flags;
2806 	wl_proxd_session_state_t session_state;
2807 	wl_proxd_status_t proxd_status;
2808 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0))
2809 	struct timespec64 ts;
2810 #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
2811 	struct timespec ts;
2812 #endif /* LINUX_VER >= 2.6.39 */
2813 	uint32 ratespec;
2814 	uint32 avg_dist;
2815 	const wl_proxd_rtt_result_v2_t *p_data_info = NULL;
2816 	const wl_proxd_rtt_sample_v2_t *p_sample_avg = NULL;
2817 	const wl_proxd_rtt_sample_v2_t *p_sample = NULL;
2818 	uint16 num_rtt = 0;
2819 	wl_proxd_intvl_t rtt;
2820 	wl_proxd_intvl_t p_time;
2821 	uint16 snr = 0, bitflips = 0;
2822 	wl_proxd_phy_error_t tof_phy_error = 0;
2823 	wl_proxd_phy_error_t tof_phy_tgt_error = 0;
2824 	wl_proxd_snr_t tof_target_snr = 0;
2825 	wl_proxd_bitflips_t tof_target_bitflips = 0;
2826 	int16 rssi = 0;
2827 	int32 dist = 0;
2828 	uint32 chanspec = 0;
2829 	uint8 num_ftm = 0;
2830 	char *ftm_frame_types[] =  FTM_FRAME_TYPES;
2831 	rtt_report_t *rtt_report = &(rtt_result->report);
2832 
2833 	BCM_REFERENCE(ftm_frame_types);
2834 	BCM_REFERENCE(dist);
2835 	BCM_REFERENCE(rssi);
2836 	BCM_REFERENCE(tof_target_bitflips);
2837 	BCM_REFERENCE(tof_target_snr);
2838 	BCM_REFERENCE(tof_phy_tgt_error);
2839 	BCM_REFERENCE(tof_phy_error);
2840 	BCM_REFERENCE(bitflips);
2841 	BCM_REFERENCE(snr);
2842 	BCM_REFERENCE(chanspec);
2843 	BCM_REFERENCE(session_state);
2844 	BCM_REFERENCE(ftm_session_state_value_to_logstr);
2845 
2846 	NULL_CHECK(rtt_report, "rtt_report is NULL", err);
2847 	NULL_CHECK(p_data, "p_data is NULL", err);
2848 	DHD_RTT(("%s enter\n", __FUNCTION__));
2849 	p_data_info = (const wl_proxd_rtt_result_v2_t *) p_data;
2850 	/* unpack and format 'flags' for display */
2851 	flags = ltoh16_ua(&p_data_info->flags);
2852 	/* session state and status */
2853 	session_state = ltoh16_ua(&p_data_info->state);
2854 	proxd_status = ltoh32_ua(&p_data_info->status);
2855 	bcm_ether_ntoa((&(p_data_info->peer)), eabuf);
2856 
2857 	if (proxd_status != BCME_OK) {
2858 		DHD_RTT_ERR((">\tTarget(%s) session state=%d(%s), status=%d(%s) "
2859 			"num_meas_ota %d num_valid_rtt %d result_flags %x\n",
2860 			eabuf, session_state,
2861 			ftm_session_state_value_to_logstr(session_state),
2862 			proxd_status, ftm_status_value_to_logstr(proxd_status),
2863 			p_data_info->num_meas, p_data_info->num_valid_rtt,
2864 			p_data_info->flags));
2865 	} else {
2866 		DHD_RTT((">\tTarget(%s) session state=%d(%s), status=%d(%s)\n",
2867 		eabuf, session_state,
2868 		ftm_session_state_value_to_logstr(session_state),
2869 		proxd_status, ftm_status_value_to_logstr(proxd_status)));
2870 	}
2871 	/* show avg_dist (1/256m units), burst_num */
2872 	avg_dist = ltoh32_ua(&p_data_info->avg_dist);
2873 	if (avg_dist == 0xffffffff) {	/* report 'failure' case */
2874 		DHD_RTT((">\tavg_dist=-1m, burst_num=%d, valid_measure_cnt=%d\n",
2875 		ltoh16_ua(&p_data_info->burst_num),
2876 		p_data_info->num_valid_rtt)); /* in a session */
2877 		avg_dist = FTM_INVALID;
2878 	} else {
2879 		DHD_RTT((">\tavg_dist=%d.%04dm, burst_num=%d, valid_measure_cnt=%d num_ftm=%d "
2880 			"num_meas_ota=%d, result_flags=%x\n", avg_dist >> 8, /* 1/256m units */
2881 			((avg_dist & 0xff) * 625) >> 4,
2882 			ltoh16_ua(&p_data_info->burst_num),
2883 			p_data_info->num_valid_rtt,
2884 			p_data_info->num_ftm, p_data_info->num_meas,
2885 			p_data_info->flags)); /* in a session */
2886 	}
2887 	rtt_result->rtt_detail.num_ota_meas = p_data_info->num_meas;
2888 	rtt_result->rtt_detail.result_flags = p_data_info->flags;
2889 	/* show 'avg_rtt' sample */
2890 	/* in v2, avg_rtt is the first element of the variable rtt[] */
2891 	p_sample_avg = &p_data_info->rtt[0];
2892 	ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu));
2893 	DHD_RTT((">\tavg_rtt sample: rssi=%d rtt=%d%s std_deviation =%d.%d"
2894 		"ratespec=0x%08x chanspec=0x%08x\n",
2895 		(int16) ltoh16_ua(&p_sample_avg->rssi),
2896 		ltoh32_ua(&p_sample_avg->rtt.intvl),
2897 		ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample_avg->rtt.tmu)),
2898 		ltoh16_ua(&p_data_info->sd_rtt)/10, ltoh16_ua(&p_data_info->sd_rtt)%10,
2899 		ltoh32_ua(&p_sample_avg->ratespec),
2900 		ltoh32_ua(&p_sample_avg->chanspec)));
2901 
2902 	/* set peer address */
2903 	rtt_report->addr = p_data_info->peer;
2904 
2905 	/* burst num */
2906 	rtt_report->burst_num = ltoh16_ua(&p_data_info->burst_num);
2907 
2908 	/* success num */
2909 	rtt_report->success_num = p_data_info->num_valid_rtt;
2910 
2911 	/* num-ftm configured */
2912 	rtt_report->ftm_num = p_data_info->num_ftm;
2913 
2914 	/* actual number of FTM supported by peer */
2915 	rtt_report->num_per_burst_peer = p_data_info->num_ftm;
2916 	rtt_report->negotiated_burst_num = p_data_info->num_ftm;
2917 
2918 	/* status */
2919 	rtt_report->status = ftm_get_statusmap_info(proxd_status,
2920 			&ftm_status_map_info[0], ARRAYSIZE(ftm_status_map_info));
2921 
2922 	/* Framework expects status as SUCCESS else all results will be
2923 	* set to zero even if we have partial valid result.
2924 	* So setting status as SUCCESS if we have a valid_rtt
2925 	* On burst timeout we stop burst with "timeout" reason and
2926 	* on msch end we set status as "cancel"
2927 	*/
2928 	if ((proxd_status == WL_PROXD_E_TIMEOUT ||
2929 		proxd_status == WL_PROXD_E_CANCELED) &&
2930 		rtt_report->success_num) {
2931 		rtt_report->status = RTT_STATUS_SUCCESS;
2932 	}
2933 
2934 	/* rssi (0.5db) */
2935 	rtt_report->rssi = ABS((wl_proxd_rssi_t)ltoh16_ua(&p_sample_avg->rssi)) * 2;
2936 
2937 	/* rx rate */
2938 	ratespec = ltoh32_ua(&p_sample_avg->ratespec);
2939 	rtt_report->rx_rate = dhd_rtt_convert_rate_to_host(ratespec);
2940 
2941 	/* tx rate */
2942 	if (flags & WL_PROXD_RESULT_FLAG_VHTACK) {
2943 		rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0x2010010);
2944 	} else {
2945 		rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0xc);
2946 	}
2947 
2948 	/* rtt_sd */
2949 	rtt.tmu = ltoh16_ua(&p_sample_avg->rtt.tmu);
2950 	rtt.intvl = ltoh32_ua(&p_sample_avg->rtt.intvl);
2951 	rtt_report->rtt = (wifi_timespan)FTM_INTVL2NSEC(&rtt) * 1000; /* nano -> pico seconds */
2952 	rtt_report->rtt_sd = ltoh16_ua(&p_data_info->sd_rtt); /* nano -> 0.1 nano */
2953 	DHD_RTT(("rtt_report->rtt : %llu\n", rtt_report->rtt));
2954 	DHD_RTT(("rtt_report->rssi : %d (0.5db)\n", rtt_report->rssi));
2955 
2956 	/* average distance */
2957 	if (avg_dist != FTM_INVALID) {
2958 		rtt_report->distance = (avg_dist >> 8) * 1000; /* meter -> mm */
2959 		rtt_report->distance += (avg_dist & 0xff) * 1000 / 256;
2960 		/* rtt_sd is in 0.1 ns.
2961 		* host needs distance_sd in milli mtrs
2962 		* (0.1 * rtt_sd/2 * 10^-9) * C * 1000
2963 		*/
2964 		rtt_report->distance_sd = rtt_report->rtt_sd * 15; /* mm */
2965 	} else {
2966 		rtt_report->distance = FTM_INVALID;
2967 	}
2968 	/* time stamp */
2969 	/* get the time elapsed from boot time */
2970 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
2971 	get_monotonic_boottime(&ts);
2972 	rtt_report->ts = (uint64)TIMESPEC_TO_US(ts);
2973 #endif /* LINUX_VER >= 2.6.39 */
2974 
2975 	if (proxd_status == WL_PROXD_E_REMOTE_FAIL) {
2976 		/* retry time  after failure */
2977 		p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
2978 		p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
2979 		rtt_report->retry_after_duration = FTM_INTVL2SEC(&p_time); /* s -> s */
2980 		DHD_RTT((">\tretry_after: %d%s\n",
2981 			ltoh32_ua(&p_data_info->u.retry_after.intvl),
2982 			ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.retry_after.tmu))));
2983 	} else {
2984 		/* burst duration */
2985 		p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
2986 		p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
2987 		rtt_report->burst_duration =  FTM_INTVL2MSEC(&p_time); /* s -> ms */
2988 		DHD_RTT((">\tburst_duration: %d%s\n",
2989 			ltoh32_ua(&p_data_info->u.burst_duration.intvl),
2990 			ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.burst_duration.tmu))));
2991 		DHD_RTT(("rtt_report->burst_duration : %d\n", rtt_report->burst_duration));
2992 	}
2993 	/* display detail if available */
2994 	num_rtt = ltoh16_ua(&p_data_info->num_rtt);
2995 	if (num_rtt > 0) {
2996 		DHD_RTT((">\tnum rtt: %d samples\n", num_rtt));
2997 		p_sample = &p_data_info->rtt[1];
2998 		for (i = 0; i < num_rtt; i++) {
2999 			snr = 0;
3000 			bitflips = 0;
3001 			tof_phy_error = 0;
3002 			tof_phy_tgt_error = 0;
3003 			tof_target_snr = 0;
3004 			tof_target_bitflips = 0;
3005 			rssi = 0;
3006 			dist = 0;
3007 			num_ftm = p_data_info->num_ftm;
3008 			/* FTM frames 1,4,7,11 have valid snr, rssi and bitflips */
3009 			if ((i % num_ftm) == 1) {
3010 				rssi = (wl_proxd_rssi_t) ltoh16_ua(&p_sample->rssi);
3011 				snr = (wl_proxd_snr_t) ltoh16_ua(&p_sample->snr);
3012 				bitflips = (wl_proxd_bitflips_t) ltoh16_ua(&p_sample->bitflips);
3013 				tof_phy_error =
3014 					(wl_proxd_phy_error_t)
3015 					ltoh32_ua(&p_sample->tof_phy_error);
3016 				tof_phy_tgt_error =
3017 					(wl_proxd_phy_error_t)
3018 					ltoh32_ua(&p_sample->tof_tgt_phy_error);
3019 				tof_target_snr =
3020 					(wl_proxd_snr_t)
3021 					ltoh16_ua(&p_sample->tof_tgt_snr);
3022 				tof_target_bitflips =
3023 					(wl_proxd_bitflips_t)
3024 					ltoh16_ua(&p_sample->tof_tgt_bitflips);
3025 				dist = ltoh32_ua(&p_sample->distance);
3026 				chanspec = ltoh32_ua(&p_sample->chanspec);
3027 			} else {
3028 				rssi = -1;
3029 				snr = 0;
3030 				bitflips = 0;
3031 				dist = 0;
3032 				tof_target_bitflips = 0;
3033 				tof_target_snr = 0;
3034 				tof_phy_tgt_error = 0;
3035 			}
3036 			DHD_RTT((">\t sample[%d]: id=%d rssi=%d snr=0x%x bitflips=%d"
3037 				" tof_phy_error %x tof_phy_tgt_error %x target_snr=0x%x"
3038 				" target_bitflips=%d dist=%d rtt=%d%s status %s Type %s"
3039 				" coreid=%d chanspec=0x%08x\n",
3040 				i, p_sample->id, rssi, snr,
3041 				bitflips, tof_phy_error, tof_phy_tgt_error,
3042 				tof_target_snr,
3043 				tof_target_bitflips, dist,
3044 				ltoh32_ua(&p_sample->rtt.intvl),
3045 				ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)),
3046 				ftm_status_value_to_logstr(ltoh32_ua(&p_sample->status)),
3047 				ftm_frame_types[i % num_ftm], p_sample->coreid,
3048 				chanspec));
3049 			p_sample++;
3050 		}
3051 	}
3052 	return err;
3053 }
3054 #ifdef WL_CFG80211
3055 /* Common API for handling Session End.
3056 * This API will flush out the results for a peer MAC.
3057 *
3058 * @For legacy FTM session, this API will be called
3059 * when legacy FTM_SESSION_END event is received.
3060 * @For legacy Nan-RTT , this API will be called when
3061 * we are cancelling the nan-ranging session or on
3062 * nan-ranging-end event.
3063 */
3064 static void
dhd_rtt_handle_rtt_session_end(dhd_pub_t * dhd)3065 dhd_rtt_handle_rtt_session_end(dhd_pub_t *dhd)
3066 {
3067 
3068 	int idx;
3069 	struct rtt_noti_callback *iter;
3070 	rtt_results_header_t *entry, *next;
3071 	rtt_result_t *next2;
3072 	rtt_result_t *rtt_result;
3073 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
3074 
3075 	/* Cancel pending proxd timeout work if any */
3076 	if (delayed_work_pending(&rtt_status->proxd_timeout)) {
3077 		cancel_delayed_work(&rtt_status->proxd_timeout);
3078 	}
3079 
3080 	/* find next target to trigger RTT */
3081 	for (idx = (rtt_status->cur_idx + 1);
3082 		idx < rtt_status->rtt_config.rtt_target_cnt; idx++) {
3083 		/* skip the disabled device */
3084 		if (rtt_status->rtt_config.target_info[idx].disable) {
3085 			continue;
3086 		} else {
3087 			/* set the idx to cur_idx */
3088 			rtt_status->cur_idx = idx;
3089 			break;
3090 		}
3091 	}
3092 	if (idx < rtt_status->rtt_config.rtt_target_cnt) {
3093 		/* restart to measure RTT from next device */
3094 		DHD_INFO(("restart to measure rtt\n"));
3095 		schedule_work(&rtt_status->work);
3096 	} else {
3097 		DHD_RTT(("RTT_STOPPED\n"));
3098 		rtt_status->status = RTT_STOPPED;
3099 		/* notify the completed information to others */
3100 		GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3101 		list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
3102 			iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache);
3103 		}
3104 		/* remove the rtt results in cache */
3105 		if (!list_empty(&rtt_status->rtt_results_cache)) {
3106 			/* Iterate rtt_results_header list */
3107 			list_for_each_entry_safe(entry, next,
3108 				&rtt_status->rtt_results_cache, list) {
3109 				list_del(&entry->list);
3110 				/* Iterate rtt_result list */
3111 				list_for_each_entry_safe(rtt_result, next2,
3112 					&entry->result_list, list) {
3113 					list_del(&rtt_result->list);
3114 					kfree(rtt_result);
3115 				}
3116 				kfree(entry);
3117 			}
3118 		}
3119 		GCC_DIAGNOSTIC_POP();
3120 		/* reinitialize the HEAD */
3121 		INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
3122 		/* clear information for rtt_config */
3123 		rtt_status->rtt_config.rtt_target_cnt = 0;
3124 		memset_s(rtt_status->rtt_config.target_info, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT),
3125 			0, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
3126 		rtt_status->cur_idx = 0;
3127 	}
3128 }
3129 #endif /* WL_CFG80211 */
3130 
3131 #ifdef WL_CFG80211
3132 static int
dhd_rtt_create_failure_result(rtt_status_info_t * rtt_status,struct ether_addr * addr)3133 dhd_rtt_create_failure_result(rtt_status_info_t *rtt_status,
3134 	struct ether_addr *addr)
3135 {
3136 	rtt_results_header_t *rtt_results_header = NULL;
3137 	rtt_target_info_t *rtt_target_info;
3138 	int ret = BCME_OK;
3139 	rtt_result_t *rtt_result;
3140 
3141 	/* allocate new header for rtt_results */
3142 	rtt_results_header = (rtt_results_header_t *)MALLOCZ(rtt_status->dhd->osh,
3143 		sizeof(rtt_results_header_t));
3144 	if (!rtt_results_header) {
3145 		ret = -ENOMEM;
3146 		goto exit;
3147 	}
3148 	rtt_target_info = &rtt_status->rtt_config.target_info[rtt_status->cur_idx];
3149 	/* Initialize the head of list for rtt result */
3150 	INIT_LIST_HEAD(&rtt_results_header->result_list);
3151 	/* same src and dest len */
3152 	(void)memcpy_s(&rtt_results_header->peer_mac,
3153 		ETHER_ADDR_LEN, addr, ETHER_ADDR_LEN);
3154 	list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache);
3155 
3156 	/* allocate rtt_results for new results */
3157 	rtt_result = (rtt_result_t *)MALLOCZ(rtt_status->dhd->osh,
3158 		sizeof(rtt_result_t));
3159 	if (!rtt_result) {
3160 		ret = -ENOMEM;
3161 		kfree(rtt_results_header);
3162 		goto exit;
3163 	}
3164 	/* fill out the results from the configuration param */
3165 	rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst;
3166 	rtt_result->report.type = RTT_TWO_WAY;
3167 	DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num));
3168 	rtt_result->report_len = RTT_REPORT_SIZE;
3169 	rtt_result->report.status = RTT_STATUS_FAIL_NO_RSP;
3170 	/* same src and dest len */
3171 	(void)memcpy_s(&rtt_result->report.addr, ETHER_ADDR_LEN,
3172 		&rtt_target_info->addr, ETHER_ADDR_LEN);
3173 	rtt_result->report.distance = FTM_INVALID;
3174 	list_add_tail(&rtt_result->list, &rtt_results_header->result_list);
3175 	rtt_results_header->result_cnt++;
3176 	rtt_results_header->result_tot_len += rtt_result->report_len;
3177 exit:
3178 	return ret;
3179 }
3180 
3181 static bool
dhd_rtt_get_report_header(rtt_status_info_t * rtt_status,rtt_results_header_t ** rtt_results_header,struct ether_addr * addr)3182 dhd_rtt_get_report_header(rtt_status_info_t *rtt_status,
3183 	rtt_results_header_t **rtt_results_header, struct ether_addr *addr)
3184 {
3185 	rtt_results_header_t *entry;
3186 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3187 	/* find a rtt_report_header for this mac address */
3188 	list_for_each_entry(entry, &rtt_status->rtt_results_cache, list) {
3189 		GCC_DIAGNOSTIC_POP();
3190 		if (!memcmp(&entry->peer_mac, addr, ETHER_ADDR_LEN))  {
3191 			/* found a rtt_report_header for peer_mac in the list */
3192 			if (rtt_results_header) {
3193 				*rtt_results_header = entry;
3194 			}
3195 			return TRUE;
3196 		}
3197 	}
3198 	return FALSE;
3199 }
3200 
3201 int
dhd_rtt_handle_nan_rtt_session_end(dhd_pub_t * dhd,struct ether_addr * peer)3202 dhd_rtt_handle_nan_rtt_session_end(dhd_pub_t *dhd, struct ether_addr *peer)
3203 {
3204 	bool is_new = TRUE;
3205 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
3206 	mutex_lock(&rtt_status->rtt_mutex);
3207 	is_new = !dhd_rtt_get_report_header(rtt_status, NULL, peer);
3208 
3209 	if (is_new) { /* no FTM result..create failure result */
3210 		dhd_rtt_create_failure_result(rtt_status, peer);
3211 	}
3212 	dhd_rtt_handle_rtt_session_end(dhd);
3213 	mutex_unlock(&rtt_status->rtt_mutex);
3214 	return BCME_OK;
3215 }
3216 #endif /* WL_CFG80211 */
3217 
3218 static bool
dhd_rtt_is_valid_measurement(rtt_result_t * rtt_result)3219 dhd_rtt_is_valid_measurement(rtt_result_t *rtt_result)
3220 {
3221 	bool ret = FALSE;
3222 
3223 	if (rtt_result && (rtt_result->report.success_num != 0)) {
3224 		ret = TRUE;
3225 	}
3226 	return ret;
3227 }
3228 
3229 static int
dhd_rtt_parse_result_event(wl_proxd_event_t * proxd_ev_data,int tlvs_len,rtt_result_t * rtt_result)3230 dhd_rtt_parse_result_event(wl_proxd_event_t *proxd_ev_data,
3231 	int tlvs_len, rtt_result_t *rtt_result)
3232 {
3233 	int ret = BCME_OK;
3234 
3235 	/* unpack TLVs and invokes the cbfn to print the event content TLVs */
3236 	ret = bcm_unpack_xtlv_buf((void *) rtt_result,
3237 			(uint8 *)&proxd_ev_data->tlvs[0], tlvs_len,
3238 			BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn);
3239 	if (ret != BCME_OK) {
3240 		DHD_RTT_ERR(("%s : Failed to unpack xtlv for an event\n",
3241 			__FUNCTION__));
3242 		goto exit;
3243 	}
3244 	/* fill out the results from the configuration param */
3245 	rtt_result->report.type = RTT_TWO_WAY;
3246 	DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num));
3247 	rtt_result->report_len = RTT_REPORT_SIZE;
3248 	rtt_result->detail_len = sizeof(rtt_result->rtt_detail);
3249 
3250 exit:
3251 	return ret;
3252 
3253 }
3254 
3255 static int
dhd_rtt_handle_directed_rtt_burst_end(dhd_pub_t * dhd,struct ether_addr * peer_addr,wl_proxd_event_t * proxd_ev_data,int tlvs_len,rtt_result_t * rtt_result,bool is_nan)3256 dhd_rtt_handle_directed_rtt_burst_end(dhd_pub_t *dhd, struct ether_addr *peer_addr,
3257         wl_proxd_event_t *proxd_ev_data, int tlvs_len, rtt_result_t *rtt_result, bool is_nan)
3258 {
3259 	rtt_status_info_t *rtt_status;
3260 	rtt_results_header_t *rtt_results_header = NULL;
3261 	bool is_new = TRUE;
3262 	int ret = BCME_OK;
3263 	int err_at = 0;
3264 
3265 	rtt_status = GET_RTTSTATE(dhd);
3266 	is_new = !dhd_rtt_get_report_header(rtt_status,
3267 		&rtt_results_header, peer_addr);
3268 
3269 	if (tlvs_len > 0) {
3270 		if (is_new) {
3271 			/* allocate new header for rtt_results */
3272 			rtt_results_header = (rtt_results_header_t *)MALLOCZ(rtt_status->dhd->osh,
3273 				sizeof(rtt_results_header_t));
3274 			if (!rtt_results_header) {
3275 				ret = BCME_NORESOURCE;
3276 				err_at = 1;
3277 				goto exit;
3278 			}
3279 			/* Initialize the head of list for rtt result */
3280 			INIT_LIST_HEAD(&rtt_results_header->result_list);
3281 			/* same src and header len */
3282 			(void)memcpy_s(&rtt_results_header->peer_mac, ETHER_ADDR_LEN,
3283 				peer_addr, ETHER_ADDR_LEN);
3284 			list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache);
3285 		}
3286 
3287 		ret = dhd_rtt_parse_result_event(proxd_ev_data, tlvs_len, rtt_result);
3288 		if ((ret == BCME_OK) && ((!is_nan) ||
3289 			dhd_rtt_is_valid_measurement(rtt_result))) {
3290 			/*
3291 			 * Add to list, if non-nan RTT (legacy) or
3292 			 * valid measurement in nan rtt case
3293 			 */
3294 			list_add_tail(&rtt_result->list, &rtt_results_header->result_list);
3295 			rtt_results_header->result_cnt++;
3296 			rtt_results_header->result_tot_len += rtt_result->report_len +
3297 				rtt_result->detail_len;
3298 		} else {
3299 			err_at = 2;
3300 			if (ret == BCME_OK) {
3301 				/* Case for nan rtt invalid measurement */
3302 				ret = BCME_ERROR;
3303 				err_at = 3;
3304 			}
3305 			goto exit;
3306 		}
3307 	} else {
3308 		ret = BCME_ERROR;
3309 		err_at = 4;
3310 		goto exit;
3311 	}
3312 
3313 exit:
3314 	if (ret != BCME_OK) {
3315 		DHD_RTT_ERR(("dhd_rtt_handle_directed_rtt_burst_end: failed, "
3316 			" ret = %d, err_at = %d\n", ret, err_at));
3317 		if (rtt_results_header) {
3318 			list_del(&rtt_results_header->list);
3319 			kfree(rtt_results_header);
3320 			rtt_results_header = NULL;
3321 		}
3322 	}
3323 	return ret;
3324 }
3325 
3326 #ifdef WL_NAN
3327 static	void
dhd_rtt_nan_range_report(struct bcm_cfg80211 * cfg,rtt_result_t * rtt_result)3328 dhd_rtt_nan_range_report(struct bcm_cfg80211 *cfg,
3329 		rtt_result_t *rtt_result)
3330 {
3331 	wl_nan_ev_rng_rpt_ind_t range_res;
3332 	nan_ranging_inst_t *rng_inst = NULL;
3333 	dhd_pub_t *dhd = (struct dhd_pub *)(cfg->pub);
3334 	rtt_status_info_t *rtt_status = GET_RTTSTATE(dhd);
3335 
3336 	UNUSED_PARAMETER(range_res);
3337 
3338 	if (!dhd_rtt_is_valid_measurement(rtt_result)) {
3339 		/* Drop Invalid Measurements for NAN RTT report */
3340 		DHD_RTT(("dhd_rtt_nan_range_report: Drop Invalid Measurements\n"));
3341 		return;
3342 	}
3343 	bzero(&range_res, sizeof(range_res));
3344 	range_res.indication = 0;
3345 	range_res.dist_mm = rtt_result->report.distance;
3346 	/* same src and header len, ignoring ret val here */
3347 	(void)memcpy_s(&range_res.peer_m_addr, ETHER_ADDR_LEN,
3348 		&rtt_result->report.addr, ETHER_ADDR_LEN);
3349 	wl_cfgnan_process_range_report(cfg, &range_res);
3350 	/*
3351 	 * suspend geofence ranging for this target
3352 	 * and move to next target
3353 	 * after valid measurement for the target
3354 	 */
3355 	rng_inst = wl_cfgnan_check_for_ranging(cfg, &range_res.peer_m_addr);
3356 	if (rng_inst) {
3357 		wl_cfgnan_suspend_geofence_rng_session(bcmcfg_to_prmry_ndev(cfg),
3358 			&rng_inst->peer_addr, RTT_GEO_SUSPN_RANGE_RES_REPORTED, 0);
3359 		GEOFENCE_RTT_LOCK(rtt_status);
3360 		dhd_rtt_move_geofence_cur_target_idx_to_next(dhd);
3361 		GEOFENCE_RTT_UNLOCK(rtt_status);
3362 		wl_cfgnan_reset_geofence_ranging(cfg,
3363 			rng_inst, RTT_SCHED_RNG_RPT_GEOFENCE);
3364 	}
3365 }
3366 
3367 static int
dhd_rtt_handle_nan_burst_end(dhd_pub_t * dhd,struct ether_addr * peer_addr,wl_proxd_event_t * proxd_ev_data,int tlvs_len)3368 dhd_rtt_handle_nan_burst_end(dhd_pub_t *dhd, struct ether_addr *peer_addr,
3369 	wl_proxd_event_t *proxd_ev_data, int tlvs_len)
3370 {
3371 	struct net_device *ndev = NULL;
3372 	struct bcm_cfg80211 *cfg = NULL;
3373 	nan_ranging_inst_t *rng_inst = NULL;
3374 	rtt_status_info_t *rtt_status = NULL;
3375 	rtt_result_t *rtt_result = NULL;
3376 	bool is_geofence = FALSE;
3377 	int ret = BCME_OK;
3378 
3379 	ndev = dhd_linux_get_primary_netdev(dhd);
3380 	cfg =  wiphy_priv(ndev->ieee80211_ptr->wiphy);
3381 
3382 	rtt_status = GET_RTTSTATE(dhd);
3383 	NULL_CHECK(rtt_status, "rtt_status is NULL", ret);
3384 	NAN_MUTEX_LOCK();
3385 	mutex_lock(&rtt_status->rtt_mutex);
3386 
3387 	if ((cfg->nan_enable == FALSE) ||
3388 		ETHER_ISNULLADDR(peer_addr)) {
3389 		DHD_RTT_ERR(("Received Burst End with NULL ether addr, "
3390 			"or nan disable, nan_enable = %d\n", cfg->nan_enable));
3391 		ret = BCME_UNSUPPORTED;
3392 		goto exit;
3393 	}
3394 
3395 	rng_inst = wl_cfgnan_check_for_ranging(cfg, peer_addr);
3396 	if (rng_inst) {
3397 		is_geofence = (rng_inst->range_type
3398 			== RTT_TYPE_NAN_GEOFENCE);
3399 	} else {
3400 		DHD_RTT_ERR(("Received Burst End without Ranging Instance\n"));
3401 		ret = BCME_ERROR;
3402 		goto exit;
3403 	}
3404 
3405 	/* allocate rtt_results for new results */
3406 	rtt_result = (rtt_result_t *)MALLOCZ(dhd->osh, sizeof(rtt_result_t));
3407 	if (!rtt_result) {
3408 		ret = BCME_NORESOURCE;
3409 		goto exit;
3410 	}
3411 
3412 	if (is_geofence) {
3413 		ret = dhd_rtt_parse_result_event(proxd_ev_data, tlvs_len, rtt_result);
3414 		if (ret != BCME_OK) {
3415 			DHD_RTT_ERR(("avilog: dhd_rtt_handle_nan_burst_end: "
3416 				"dhd_rtt_parse_result_event failed\n"));
3417 			goto exit;
3418 		}
3419 	} else {
3420 		if (RTT_IS_STOPPED(rtt_status)) {
3421 			/* Ignore the Proxd event */
3422 			DHD_RTT((" event handler rtt is stopped \n"));
3423 			if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) {
3424 				DHD_RTT(("Device is target/Responder. Recv the event. \n"));
3425 			} else {
3426 				ret = BCME_UNSUPPORTED;
3427 				goto exit;
3428 			}
3429 		}
3430 		ret = dhd_rtt_handle_directed_rtt_burst_end(dhd, peer_addr,
3431 			proxd_ev_data, tlvs_len, rtt_result, TRUE);
3432 		if (ret != BCME_OK) {
3433 			goto exit;
3434 		}
3435 
3436 	}
3437 
3438 exit:
3439 	mutex_unlock(&rtt_status->rtt_mutex);
3440 	if (ret == BCME_OK) {
3441 		dhd_rtt_nan_range_report(cfg, rtt_result);
3442 	}
3443 	if (rtt_result &&
3444 		((ret != BCME_OK) || is_geofence)) {
3445 		kfree(rtt_result);
3446 		rtt_result = NULL;
3447 	}
3448 	NAN_MUTEX_UNLOCK();
3449 	return ret;
3450 }
3451 #endif /* WL_NAN */
3452 
3453 int
dhd_rtt_event_handler(dhd_pub_t * dhd,wl_event_msg_t * event,void * event_data)3454 dhd_rtt_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
3455 {
3456 	int ret = BCME_OK;
3457 	int tlvs_len;
3458 	uint16 version;
3459 	wl_proxd_event_t *p_event;
3460 	wl_proxd_event_type_t event_type;
3461 	wl_proxd_ftm_session_status_t session_status;
3462 	const ftm_strmap_entry_t *p_loginfo;
3463 	rtt_result_t *rtt_result;
3464 #ifdef WL_CFG80211
3465 	rtt_status_info_t *rtt_status;
3466 	rtt_results_header_t *rtt_results_header = NULL;
3467 	bool is_new = TRUE;
3468 #endif /* WL_CFG80211 */
3469 
3470 	DHD_RTT(("Enter %s \n", __FUNCTION__));
3471 	NULL_CHECK(dhd, "dhd is NULL", ret);
3472 
3473 	if (ntoh32_ua((void *)&event->datalen) < OFFSETOF(wl_proxd_event_t, tlvs)) {
3474 		DHD_RTT(("%s: wrong datalen:%d\n", __FUNCTION__,
3475 			ntoh32_ua((void *)&event->datalen)));
3476 		return -EINVAL;
3477 	}
3478 	event_type = ntoh32_ua((void *)&event->event_type);
3479 	if (event_type != WLC_E_PROXD) {
3480 		DHD_RTT_ERR((" failed event \n"));
3481 		return -EINVAL;
3482 	}
3483 
3484 	if (!event_data) {
3485 		DHD_RTT_ERR(("%s: event_data:NULL\n", __FUNCTION__));
3486 		return -EINVAL;
3487 	}
3488 	p_event = (wl_proxd_event_t *) event_data;
3489 	version = ltoh16(p_event->version);
3490 	if (version < WL_PROXD_API_VERSION) {
3491 		DHD_RTT_ERR(("ignore non-ftm event version = 0x%0x < WL_PROXD_API_VERSION (0x%x)\n",
3492 			version, WL_PROXD_API_VERSION));
3493 		return ret;
3494 	}
3495 
3496 	event_type = (wl_proxd_event_type_t) ltoh16(p_event->type);
3497 
3498 	DHD_RTT(("event_type=0x%x, ntoh16()=0x%x, ltoh16()=0x%x\n",
3499 		p_event->type, ntoh16(p_event->type), ltoh16(p_event->type)));
3500 	p_loginfo = ftm_get_event_type_loginfo(event_type);
3501 	if (p_loginfo == NULL) {
3502 		DHD_RTT_ERR(("receive an invalid FTM event %d\n", event_type));
3503 		ret = -EINVAL;
3504 		return ret;	/* ignore this event */
3505 	}
3506 	/* get TLVs len, skip over event header */
3507 	if (ltoh16(p_event->len) < OFFSETOF(wl_proxd_event_t, tlvs)) {
3508 		DHD_RTT_ERR(("invalid FTM event length:%d\n", ltoh16(p_event->len)));
3509 		ret = -EINVAL;
3510 		return ret;
3511 	}
3512 	tlvs_len = ltoh16(p_event->len) - OFFSETOF(wl_proxd_event_t, tlvs);
3513 	DHD_RTT(("receive '%s' event: version=0x%x len=%d method=%d sid=%d tlvs_len=%d\n",
3514 		p_loginfo->text,
3515 		version,
3516 		ltoh16(p_event->len),
3517 		ltoh16(p_event->method),
3518 		ltoh16(p_event->sid),
3519 		tlvs_len));
3520 #ifdef WL_CFG80211
3521 #ifdef WL_NAN
3522 	if ((event_type == WL_PROXD_EVENT_BURST_END) &&
3523 			dhd_rtt_is_nan_peer(dhd, &event->addr)) {
3524 		DHD_RTT(("WL_PROXD_EVENT_BURST_END for NAN RTT\n"));
3525 		ret = dhd_rtt_handle_nan_burst_end(dhd, &event->addr, p_event, tlvs_len);
3526 		return ret;
3527 	}
3528 #endif /* WL_NAN */
3529 
3530 	rtt_status = GET_RTTSTATE(dhd);
3531 	NULL_CHECK(rtt_status, "rtt_status is NULL", ret);
3532 	mutex_lock(&rtt_status->rtt_mutex);
3533 
3534 	if (RTT_IS_STOPPED(rtt_status)) {
3535 		/* Ignore the Proxd event */
3536 		DHD_RTT((" event handler rtt is stopped \n"));
3537 		if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) {
3538 			DHD_RTT(("Device is target/Responder. Recv the event. \n"));
3539 		} else {
3540 			ret = BCME_NOTREADY;
3541 			goto exit;
3542 		}
3543 	}
3544 #endif /* WL_CFG80211 */
3545 
3546 #ifdef WL_CFG80211
3547 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3548 	is_new = !dhd_rtt_get_report_header(rtt_status,
3549 		&rtt_results_header, &event->addr);
3550 	GCC_DIAGNOSTIC_POP();
3551 #endif /* WL_CFG80211 */
3552 	switch (event_type) {
3553 	case WL_PROXD_EVENT_SESSION_CREATE:
3554 		DHD_RTT(("WL_PROXD_EVENT_SESSION_CREATE\n"));
3555 		break;
3556 	case WL_PROXD_EVENT_SESSION_START:
3557 		DHD_RTT(("WL_PROXD_EVENT_SESSION_START\n"));
3558 		break;
3559 	case WL_PROXD_EVENT_BURST_START:
3560 		DHD_RTT(("WL_PROXD_EVENT_BURST_START\n"));
3561 		break;
3562 	case WL_PROXD_EVENT_BURST_END:
3563 		DHD_RTT(("WL_PROXD_EVENT_BURST_END for Legacy RTT\n"));
3564 		/* allocate rtt_results for new legacy rtt results */
3565 		rtt_result = (rtt_result_t *)MALLOCZ(dhd->osh, sizeof(rtt_result_t));
3566 		if (!rtt_result) {
3567 			ret = -ENOMEM;
3568 			goto exit;
3569 		}
3570 		ret = dhd_rtt_handle_directed_rtt_burst_end(dhd, &event->addr,
3571 			p_event, tlvs_len, rtt_result, FALSE);
3572 		if (rtt_result && (ret != BCME_OK)) {
3573 			kfree(rtt_result);
3574 			rtt_result = NULL;
3575 			goto exit;
3576 		}
3577 		break;
3578 	case WL_PROXD_EVENT_SESSION_END:
3579 		DHD_RTT(("WL_PROXD_EVENT_SESSION_END\n"));
3580 		if (dhd_rtt_is_nan_peer(dhd, &event->addr)) {
3581 			/*
3582 			 * Nothing to do for session end for nan peer
3583 			 * All taken care in burst end and nan rng rep
3584 			 */
3585 			break;
3586 		}
3587 #ifdef WL_CFG80211
3588 		if (!RTT_IS_ENABLED(rtt_status)) {
3589 			DHD_RTT(("Ignore the session end evt\n"));
3590 			goto exit;
3591 		}
3592 #endif /* WL_CFG80211 */
3593 		if (tlvs_len > 0) {
3594 			/* unpack TLVs and invokes the cbfn to print the event content TLVs */
3595 			ret = bcm_unpack_xtlv_buf((void *) &session_status,
3596 				(uint8 *)&p_event->tlvs[0], tlvs_len,
3597 				BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn);
3598 			if (ret != BCME_OK) {
3599 				DHD_RTT_ERR(("%s : Failed to unpack xtlv for an event\n",
3600 					__FUNCTION__));
3601 				goto exit;
3602 			}
3603 		}
3604 #ifdef WL_CFG80211
3605 		/* In case of no result for the peer device, make fake result for error case */
3606 		if (is_new) {
3607 			dhd_rtt_create_failure_result(rtt_status, &event->addr);
3608 		}
3609 		DHD_RTT(("\n Not Nan peer..proceed to notify result and restart\n"));
3610 		dhd_rtt_handle_rtt_session_end(dhd);
3611 #endif /* WL_CFG80211 */
3612 		break;
3613 	case WL_PROXD_EVENT_SESSION_RESTART:
3614 		DHD_RTT(("WL_PROXD_EVENT_SESSION_RESTART\n"));
3615 		break;
3616 	case WL_PROXD_EVENT_BURST_RESCHED:
3617 		DHD_RTT(("WL_PROXD_EVENT_BURST_RESCHED\n"));
3618 		break;
3619 	case WL_PROXD_EVENT_SESSION_DESTROY:
3620 		DHD_RTT(("WL_PROXD_EVENT_SESSION_DESTROY\n"));
3621 		break;
3622 	case WL_PROXD_EVENT_FTM_FRAME:
3623 		DHD_RTT(("WL_PROXD_EVENT_FTM_FRAME\n"));
3624 		break;
3625 	case WL_PROXD_EVENT_DELAY:
3626 		DHD_RTT(("WL_PROXD_EVENT_DELAY\n"));
3627 		break;
3628 	case WL_PROXD_EVENT_VS_INITIATOR_RPT:
3629 		DHD_RTT(("WL_PROXD_EVENT_VS_INITIATOR_RPT\n "));
3630 		break;
3631 	case WL_PROXD_EVENT_RANGING:
3632 		DHD_RTT(("WL_PROXD_EVENT_RANGING\n"));
3633 		break;
3634 	case WL_PROXD_EVENT_COLLECT:
3635 		DHD_RTT(("WL_PROXD_EVENT_COLLECT\n"));
3636 		if (tlvs_len > 0) {
3637 			void *buffer = NULL;
3638 			if (!(buffer = (void *)MALLOCZ(dhd->osh, tlvs_len))) {
3639 				ret = -ENOMEM;
3640 				goto exit;
3641 			}
3642 			/* unpack TLVs and invokes the cbfn to print the event content TLVs */
3643 			ret = bcm_unpack_xtlv_buf(buffer,
3644 				(uint8 *)&p_event->tlvs[0], tlvs_len,
3645 				BCM_XTLV_OPTION_NONE, rtt_unpack_xtlv_cbfn);
3646 			kfree(buffer);
3647 			if (ret != BCME_OK) {
3648 				DHD_RTT_ERR(("%s : Failed to unpack xtlv for event %d\n",
3649 					__FUNCTION__, event_type));
3650 				goto exit;
3651 			}
3652 		}
3653 		break;
3654 	case WL_PROXD_EVENT_MF_STATS:
3655 		DHD_RTT(("WL_PROXD_EVENT_MF_STATS\n"));
3656 		if (tlvs_len > 0) {
3657 			void *buffer = NULL;
3658 			if (!(buffer = (void *)MALLOCZ(dhd->osh, tlvs_len))) {
3659 				ret = -ENOMEM;
3660 				goto exit;
3661 			}
3662 			/* unpack TLVs and invokes the cbfn to print the event content TLVs */
3663 			ret = bcm_unpack_xtlv_buf(buffer,
3664 				(uint8 *)&p_event->tlvs[0], tlvs_len,
3665 				BCM_XTLV_OPTION_NONE, rtt_unpack_xtlv_cbfn);
3666 			kfree(buffer);
3667 			if (ret != BCME_OK) {
3668 				DHD_RTT_ERR(("%s : Failed to unpack xtlv for event %d\n",
3669 					__FUNCTION__, event_type));
3670 				goto exit;
3671 			}
3672 		}
3673 		break;
3674 
3675 	default:
3676 		DHD_RTT_ERR(("WLC_E_PROXD: not supported EVENT Type:%d\n", event_type));
3677 		break;
3678 	}
3679 exit:
3680 #ifdef WL_CFG80211
3681 	mutex_unlock(&rtt_status->rtt_mutex);
3682 #endif /* WL_CFG80211 */
3683 
3684 	return ret;
3685 }
3686 
3687 #ifdef WL_CFG80211
3688 static void
dhd_rtt_work(struct work_struct * work)3689 dhd_rtt_work(struct work_struct *work)
3690 {
3691 	rtt_status_info_t *rtt_status;
3692 	dhd_pub_t *dhd;
3693 
3694 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
3695 	rtt_status = container_of(work, rtt_status_info_t, work);
3696 	GCC_DIAGNOSTIC_POP();
3697 
3698 	dhd = rtt_status->dhd;
3699 	if (dhd == NULL) {
3700 		DHD_RTT_ERR(("%s : dhd is NULL\n", __FUNCTION__));
3701 		return;
3702 	}
3703 	(void) dhd_rtt_start(dhd);
3704 }
3705 #endif /* WL_CFG80211 */
3706 
3707 int
dhd_rtt_capability(dhd_pub_t * dhd,rtt_capabilities_t * capa)3708 dhd_rtt_capability(dhd_pub_t *dhd, rtt_capabilities_t *capa)
3709 {
3710 	rtt_status_info_t *rtt_status;
3711 	int err = BCME_OK;
3712 	NULL_CHECK(dhd, "dhd is NULL", err);
3713 	rtt_status = GET_RTTSTATE(dhd);
3714 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
3715 	NULL_CHECK(capa, "capa is NULL", err);
3716 	bzero(capa, sizeof(rtt_capabilities_t));
3717 
3718 	/* set rtt capabilities */
3719 	if (rtt_status->rtt_capa.proto & RTT_CAP_ONE_WAY)
3720 		capa->rtt_one_sided_supported = 1;
3721 	if (rtt_status->rtt_capa.proto & RTT_CAP_FTM_WAY)
3722 		capa->rtt_ftm_supported = 1;
3723 
3724 	if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCI)
3725 		capa->lci_support = 1;
3726 	if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCR)
3727 		capa->lcr_support = 1;
3728 	if (rtt_status->rtt_capa.feature & RTT_FEATURE_PREAMBLE)
3729 		capa->preamble_support = 1;
3730 	if (rtt_status->rtt_capa.feature & RTT_FEATURE_BW)
3731 		capa->bw_support = 1;
3732 
3733 	/* bit mask */
3734 	capa->preamble_support = rtt_status->rtt_capa.preamble;
3735 	capa->bw_support = rtt_status->rtt_capa.bw;
3736 
3737 	return err;
3738 }
3739 
3740 #ifdef WL_CFG80211
3741 int
dhd_rtt_avail_channel(dhd_pub_t * dhd,wifi_channel_info * channel_info)3742 dhd_rtt_avail_channel(dhd_pub_t *dhd, wifi_channel_info *channel_info)
3743 {
3744 	u32 chanspec = 0;
3745 	int err = BCME_OK;
3746 	chanspec_t c = 0;
3747 	u32 channel;
3748 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
3749 
3750 	if ((err = wldev_iovar_getint(dev, "chanspec",
3751 		(s32 *)&chanspec)) == BCME_OK) {
3752 		c = (chanspec_t)dtoh32(chanspec);
3753 		c = wl_chspec_driver_to_host(c);
3754 		channel  = wf_chspec_ctlchan(c);
3755 		DHD_RTT((" control channel is %d \n", channel));
3756 		if (CHSPEC_IS20(c)) {
3757 			channel_info->width = WIFI_CHAN_WIDTH_20;
3758 			DHD_RTT((" band is 20 \n"));
3759 		} else if (CHSPEC_IS40(c)) {
3760 			channel_info->width = WIFI_CHAN_WIDTH_40;
3761 			DHD_RTT(("band is 40 \n"));
3762 		} else {
3763 			channel_info->width = WIFI_CHAN_WIDTH_80;
3764 			DHD_RTT(("band is 80 \n"));
3765 		}
3766 		if (CHSPEC_IS2G(c) && (channel >= CH_MIN_2G_CHANNEL) &&
3767 			(channel <= CH_MAX_2G_CHANNEL)) {
3768 			channel_info->center_freq =
3769 				ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
3770 		} else if (CHSPEC_IS5G(c) && channel >= CH_MIN_5G_CHANNEL) {
3771 			channel_info->center_freq =
3772 				ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ);
3773 		}
3774 		if ((channel_info->width == WIFI_CHAN_WIDTH_80) ||
3775 			(channel_info->width == WIFI_CHAN_WIDTH_40)) {
3776 			channel = CHSPEC_CHANNEL(c);
3777 			channel_info->center_freq0 =
3778 				ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ);
3779 		}
3780 	} else {
3781 		DHD_RTT_ERR(("Failed to get the chanspec \n"));
3782 	}
3783 	return err;
3784 }
3785 
3786 int
dhd_rtt_enable_responder(dhd_pub_t * dhd,wifi_channel_info * channel_info)3787 dhd_rtt_enable_responder(dhd_pub_t *dhd, wifi_channel_info *channel_info)
3788 {
3789 	int err = BCME_OK;
3790 	char chanbuf[CHANSPEC_STR_LEN];
3791 	int pm = PM_OFF;
3792 	int ftm_cfg_cnt = 0;
3793 	chanspec_t chanspec;
3794 	wifi_channel_info_t channel;
3795 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
3796 	ftm_config_options_info_t ftm_configs[FTM_MAX_CONFIGS];
3797 	ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS];
3798 	rtt_status_info_t *rtt_status;
3799 
3800 	memset(&channel, 0, sizeof(channel));
3801 	BCM_REFERENCE(chanbuf);
3802 	NULL_CHECK(dhd, "dhd is NULL", err);
3803 	rtt_status = GET_RTTSTATE(dhd);
3804 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
3805 	if (RTT_IS_STOPPED(rtt_status)) {
3806 		DHD_RTT(("STA responder/Target. \n"));
3807 	}
3808 	DHD_RTT(("Enter %s \n", __FUNCTION__));
3809 	if (!dhd_is_associated(dhd, 0, NULL)) {
3810 		if (channel_info) {
3811 			channel.width = channel_info->width;
3812 			channel.center_freq = channel_info->center_freq;
3813 			channel.center_freq0 = channel_info->center_freq;
3814 		}
3815 		else {
3816 			channel.width = WIFI_CHAN_WIDTH_80;
3817 			channel.center_freq = DEFAULT_FTM_FREQ;
3818 			channel.center_freq0 = DEFAULT_FTM_CNTR_FREQ0;
3819 		}
3820 		chanspec =  dhd_rtt_convert_to_chspec(channel);
3821 		DHD_RTT(("chanspec/channel set as %s for rtt.\n",
3822 			wf_chspec_ntoa(chanspec, chanbuf)));
3823 		err = wldev_iovar_setint(dev, "chanspec", chanspec);
3824 		if (err) {
3825 			DHD_RTT_ERR(("Failed to set the chanspec \n"));
3826 		}
3827 	}
3828 	rtt_status->pm = PM_OFF;
3829 	err = wldev_ioctl_get(dev, WLC_GET_PM, &rtt_status->pm, sizeof(rtt_status->pm));
3830 	DHD_RTT(("Current PM value read %d\n", rtt_status->pm));
3831 	if (err) {
3832 		DHD_RTT_ERR(("Failed to get the PM value \n"));
3833 	} else {
3834 		err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
3835 		if (err) {
3836 			DHD_RTT_ERR(("Failed to set the PM \n"));
3837 			rtt_status->pm_restore = FALSE;
3838 		} else {
3839 			rtt_status->pm_restore = TRUE;
3840 		}
3841 	}
3842 	if (!RTT_IS_ENABLED(rtt_status)) {
3843 		err = dhd_rtt_ftm_enable(dhd, TRUE);
3844 		if (err) {
3845 			DHD_RTT_ERR(("Failed to enable FTM (%d)\n", err));
3846 			goto exit;
3847 		}
3848 		DHD_RTT(("FTM enabled \n"));
3849 	}
3850 	rtt_status->status = RTT_ENABLED;
3851 	DHD_RTT(("Responder enabled \n"));
3852 	memset(ftm_configs, 0, sizeof(ftm_configs));
3853 	memset(ftm_params, 0, sizeof(ftm_params));
3854 	ftm_configs[ftm_cfg_cnt].enable = TRUE;
3855 	ftm_configs[ftm_cfg_cnt++].flags = WL_PROXD_SESSION_FLAG_TARGET;
3856 	rtt_status->flags = WL_PROXD_SESSION_FLAG_TARGET;
3857 	DHD_RTT(("Set the device as responder \n"));
3858 	err = dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_OPTIONS,
3859 		ftm_configs, ftm_cfg_cnt);
3860 exit:
3861 	if (err) {
3862 		rtt_status->status = RTT_STOPPED;
3863 		DHD_RTT_ERR(("rtt is stopped  %s \n", __FUNCTION__));
3864 		dhd_rtt_ftm_enable(dhd, FALSE);
3865 		DHD_RTT(("restoring the PM value \n"));
3866 		if (rtt_status->pm_restore) {
3867 			pm = PM_FAST;
3868 			err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
3869 			if (err) {
3870 				DHD_RTT_ERR(("Failed to restore PM \n"));
3871 			} else {
3872 				rtt_status->pm_restore = FALSE;
3873 			}
3874 		}
3875 	}
3876 	return err;
3877 }
3878 
3879 int
dhd_rtt_cancel_responder(dhd_pub_t * dhd)3880 dhd_rtt_cancel_responder(dhd_pub_t *dhd)
3881 {
3882 	int err = BCME_OK;
3883 	rtt_status_info_t *rtt_status;
3884 	int pm = 0;
3885 	struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
3886 
3887 	NULL_CHECK(dhd, "dhd is NULL", err);
3888 	rtt_status = GET_RTTSTATE(dhd);
3889 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
3890 	DHD_RTT(("Enter %s \n", __FUNCTION__));
3891 	err = dhd_rtt_ftm_enable(dhd, FALSE);
3892 	if (err) {
3893 		DHD_RTT_ERR(("failed to disable FTM (%d)\n", err));
3894 	}
3895 	rtt_status->status = RTT_STOPPED;
3896 	if (rtt_status->pm_restore) {
3897 		pm = PM_FAST;
3898 		DHD_RTT(("pm_restore =%d \n", rtt_status->pm_restore));
3899 		err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
3900 		if (err) {
3901 			DHD_RTT_ERR(("Failed to restore PM \n"));
3902 		} else {
3903 			rtt_status->pm_restore = FALSE;
3904 		}
3905 	}
3906 	return err;
3907 }
3908 #endif /* WL_CFG80211 */
3909 
3910 int
dhd_rtt_init(dhd_pub_t * dhd)3911 dhd_rtt_init(dhd_pub_t *dhd)
3912 {
3913 	int err = BCME_OK;
3914 #ifdef WL_CFG80211
3915 	int ret;
3916 	int32 drv_up = 1;
3917 	int32 version;
3918 	rtt_status_info_t *rtt_status;
3919 	ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS];
3920 	int ftm_param_cnt = 0;
3921 
3922 	NULL_CHECK(dhd, "dhd is NULL", err);
3923 	if (dhd->rtt_state) {
3924 		return err;
3925 	}
3926 	dhd->rtt_state = (rtt_status_info_t *)MALLOCZ(dhd->osh,
3927 		sizeof(rtt_status_info_t));
3928 	if (dhd->rtt_state == NULL) {
3929 		err = BCME_NOMEM;
3930 		DHD_RTT_ERR(("%s : failed to create rtt_state\n", __FUNCTION__));
3931 		return err;
3932 	}
3933 	bzero(dhd->rtt_state, sizeof(rtt_status_info_t));
3934 	rtt_status = GET_RTTSTATE(dhd);
3935 	rtt_status->rtt_config.target_info =
3936 			(rtt_target_info_t *)MALLOCZ(dhd->osh,
3937 			TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
3938 	if (rtt_status->rtt_config.target_info == NULL) {
3939 		DHD_RTT_ERR(("%s failed to allocate the target info for %d\n",
3940 			__FUNCTION__, RTT_MAX_TARGET_CNT));
3941 		err = BCME_NOMEM;
3942 		goto exit;
3943 	}
3944 	rtt_status->dhd = dhd;
3945 	/* need to do WLC_UP  */
3946 	dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&drv_up, sizeof(int32), TRUE, 0);
3947 
3948 	ret = dhd_rtt_get_version(dhd, &version);
3949 	if (ret == BCME_OK && (version == WL_PROXD_API_VERSION)) {
3950 		DHD_RTT_ERR(("%s : FTM is supported\n", __FUNCTION__));
3951 		/* rtt_status->rtt_capa.proto |= RTT_CAP_ONE_WAY; */
3952 		rtt_status->rtt_capa.proto |= RTT_CAP_FTM_WAY;
3953 
3954 		/* indicate to set tx rate */
3955 		rtt_status->rtt_capa.feature |= RTT_FEATURE_LCI;
3956 		rtt_status->rtt_capa.feature |= RTT_FEATURE_LCR;
3957 		rtt_status->rtt_capa.feature |= RTT_FEATURE_PREAMBLE;
3958 		rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_VHT;
3959 		rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_HT;
3960 
3961 		/* indicate to set bandwith */
3962 		rtt_status->rtt_capa.feature |= RTT_FEATURE_BW;
3963 		rtt_status->rtt_capa.bw |= RTT_BW_20;
3964 		rtt_status->rtt_capa.bw |= RTT_BW_40;
3965 		rtt_status->rtt_capa.bw |= RTT_BW_80;
3966 	} else {
3967 		if ((ret != BCME_OK) || (version == 0)) {
3968 			DHD_RTT_ERR(("%s : FTM is not supported\n", __FUNCTION__));
3969 		} else {
3970 			DHD_RTT_ERR(("%s : FTM version mismatch between HOST (%d) and FW (%d)\n",
3971 				__FUNCTION__, WL_PROXD_API_VERSION, version));
3972 		}
3973 	}
3974 	/* cancel all of RTT request once we got the cancel request */
3975 	rtt_status->all_cancel = TRUE;
3976 	mutex_init(&rtt_status->rtt_mutex);
3977 	mutex_init(&rtt_status->geofence_mutex);
3978 	INIT_LIST_HEAD(&rtt_status->noti_fn_list);
3979 	INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
3980 	INIT_WORK(&rtt_status->work, dhd_rtt_work);
3981 	/* initialize proxd timer */
3982 	INIT_DELAYED_WORK(&rtt_status->proxd_timeout, dhd_rtt_timeout_work);
3983 #ifdef WL_NAN
3984 	/* initialize proxd retry timer */
3985 	INIT_DELAYED_WORK(&rtt_status->rtt_retry_timer, dhd_rtt_retry_work);
3986 	/* initialize non zero params of geofenne cfg */
3987 	rtt_status->geofence_cfg.cur_target_idx = DHD_RTT_INVALID_TARGET_INDEX;
3988 #endif /* WL_NAN */
3989 	/* Global proxd config */
3990 	ftm_params[ftm_param_cnt].event_mask = ((1 << WL_PROXD_EVENT_BURST_END) |
3991 		(1 << WL_PROXD_EVENT_SESSION_END));
3992 	ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_EVENT_MASK;
3993 	dhd_rtt_ftm_config(dhd, 0, FTM_CONFIG_CAT_GENERAL,
3994 			ftm_params, ftm_param_cnt);
3995 exit:
3996 	if (err < 0) {
3997 		kfree(rtt_status->rtt_config.target_info);
3998 		kfree(dhd->rtt_state);
3999 	}
4000 #endif /* WL_CFG80211 */
4001 	return err;
4002 
4003 }
4004 
4005 int
dhd_rtt_deinit(dhd_pub_t * dhd)4006 dhd_rtt_deinit(dhd_pub_t *dhd)
4007 {
4008 	int err = BCME_OK;
4009 #ifdef WL_CFG80211
4010 	rtt_status_info_t *rtt_status;
4011 	rtt_results_header_t *rtt_header, *next;
4012 	rtt_result_t *rtt_result, *next2;
4013 	struct rtt_noti_callback *iter, *iter2;
4014 	NULL_CHECK(dhd, "dhd is NULL", err);
4015 	rtt_status = GET_RTTSTATE(dhd);
4016 	NULL_CHECK(rtt_status, "rtt_status is NULL", err);
4017 	rtt_status->status = RTT_STOPPED;
4018 	DHD_RTT(("rtt is stopped %s \n", __FUNCTION__));
4019 	/* clear evt callback list */
4020 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
4021 	if (!list_empty(&rtt_status->noti_fn_list)) {
4022 		list_for_each_entry_safe(iter, iter2, &rtt_status->noti_fn_list, list) {
4023 			list_del(&iter->list);
4024 			kfree(iter);
4025 		}
4026 	}
4027 	/* remove the rtt results */
4028 	if (!list_empty(&rtt_status->rtt_results_cache)) {
4029 		list_for_each_entry_safe(rtt_header, next, &rtt_status->rtt_results_cache, list) {
4030 			list_del(&rtt_header->list);
4031 			list_for_each_entry_safe(rtt_result, next2,
4032 				&rtt_header->result_list, list) {
4033 				list_del(&rtt_result->list);
4034 				kfree(rtt_result);
4035 			}
4036 			kfree(rtt_header);
4037 		}
4038 	}
4039 	GCC_DIAGNOSTIC_POP();
4040 
4041 	kfree(rtt_status->rtt_config.target_info);
4042 	kfree(dhd->rtt_state);
4043 	dhd->rtt_state = NULL;
4044 #endif /* WL_CFG80211 */
4045 	return err;
4046 }
4047