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