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