1 /*
2 * Broadcom Dongle Host Driver (DHD), RTT
3 *
4 * Copyright (C) 1999-2017, Broadcom Corporation
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 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 *
25 * <<Broadcom-WL-IPTag/Open:>>
26 *
27 * $Id$
28 */
29 #include <typedefs.h>
30 #include <osl.h>
31
32 #include <epivers.h>
33 #include <bcmutils.h>
34
35 #include <bcmendian.h>
36 #include <linuxver.h>
37 #include <linux/init.h>
38 #include <linux/kernel.h>
39 #include <linux/list.h>
40 #include <linux/sort.h>
41 #include <dngl_stats.h>
42 #include <wlioctl.h>
43
44 #include <bcmevent.h>
45 #include <dhd.h>
46 #include <dhd_rtt.h>
47 #include <dhd_dbg.h>
48 #include <wldev_common.h>
49 #ifdef WL_CFG80211
50 #include <wl_cfg80211.h>
51 #endif /* WL_CFG80211 */
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
62 #define RTT_IS_ENABLED(rtt_status) (rtt_status->status == RTT_ENABLED)
63 #define RTT_IS_STOPPED(rtt_status) (rtt_status->status == RTT_STOPPED)
64 #define TIMESPEC_TO_US(ts) (((uint64)(ts).tv_sec * USEC_PER_SEC) + \
65 (ts).tv_nsec / NSEC_PER_USEC)
66
67 #define FTM_IOC_BUFSZ 2048 /* ioc buffsize for our module (> BCM_XTLV_HDR_SIZE) */
68 #define FTM_AVAIL_MAX_SLOTS 32
69 #define FTM_MAX_CONFIGS 10
70 #define FTM_MAX_PARAMS 10
71 #define FTM_DEFAULT_SESSION 1
72 #define FTM_BURST_TIMEOUT_UNIT 250 /* 250 ns */
73 #define FTM_INVALID -1
74 #define FTM_DEFAULT_CNT_20M 12
75 #define FTM_DEFAULT_CNT_40M 10
76 #define FTM_DEFAULT_CNT_80M 5
77
78 /* convenience macros */
79 #define FTM_TU2MICRO(_tu) ((uint64)(_tu) << 10)
80 #define FTM_MICRO2TU(_tu) ((uint64)(_tu) >> 10)
81 #define FTM_TU2MILLI(_tu) ((uint32)FTM_TU2MICRO(_tu) / 1000)
82 #define FTM_MICRO2MILLI(_x) ((uint32)(_x) / 1000)
83 #define FTM_MICRO2SEC(_x) ((uint32)(_x) / 1000000)
84 #define FTM_INTVL2NSEC(_intvl) ((uint32)ftm_intvl2nsec(_intvl))
85 #define FTM_INTVL2USEC(_intvl) ((uint32)ftm_intvl2usec(_intvl))
86 #define FTM_INTVL2MSEC(_intvl) (FTM_INTVL2USEC(_intvl) / 1000)
87 #define FTM_INTVL2SEC(_intvl) (FTM_INTVL2USEC(_intvl) / 1000000)
88 #define FTM_USECIN100MILLI(_usec) ((_usec) / 100000)
89
90 /* broadcom specific set to have more accurate data */
91 #define ENABLE_VHT_ACK
92 #define CH_MIN_5G_CHANNEL 34
93 #define CH_MIN_2G_CHANNEL 1
94
95 struct rtt_noti_callback {
96 struct list_head list;
97 void *ctx;
98 dhd_rtt_compl_noti_fn noti_fn;
99 };
100
101
102 /* bitmask indicating which command groups; */
103 typedef enum {
104 FTM_SUBCMD_FLAG_METHOD = 0x01, /* FTM method command */
105 FTM_SUBCMD_FLAG_SESSION = 0x02, /* FTM session command */
106 FTM_SUBCMD_FLAG_ALL = FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION
107 } ftm_subcmd_flag_t;
108
109 /* proxd ftm config-category definition */
110 typedef enum {
111 FTM_CONFIG_CAT_GENERAL = 1, /* generial configuration */
112 FTM_CONFIG_CAT_OPTIONS = 2, /* 'config options' */
113 FTM_CONFIG_CAT_AVAIL = 3, /* 'config avail' */
114 } ftm_config_category_t;
115
116
117 typedef struct ftm_subcmd_info {
118 int16 version; /* FTM version (optional) */
119 char *name; /* cmd-name string as cmdline input */
120 wl_proxd_cmd_t cmdid; /* cmd-id */
121 bcm_xtlv_unpack_cbfn_t *handler; /* cmd response handler (optional) */
122 ftm_subcmd_flag_t cmdflag; /* CMD flag (optional) */
123 } ftm_subcmd_info_t;
124
125
126 typedef struct ftm_config_options_info {
127 uint32 flags; /* wl_proxd_flags_t/wl_proxd_session_flags_t */
128 bool enable;
129 } ftm_config_options_info_t;
130
131 typedef struct ftm_config_param_info {
132 uint16 tlvid; /* mapping TLV id for the item */
133 union {
134 uint32 chanspec;
135 struct ether_addr mac_addr;
136 wl_proxd_intvl_t data_intvl;
137 uint32 data32;
138 uint16 data16;
139 uint8 data8;
140 };
141 } ftm_config_param_info_t;
142
143 /*
144 * definition for id-string mapping.
145 * This is used to map an id (can be cmd-id, tlv-id, ....) to a text-string
146 * for debug-display or cmd-log-display
147 */
148 typedef struct ftm_strmap_entry {
149 int32 id;
150 char *text;
151 } ftm_strmap_entry_t;
152
153
154 typedef struct ftm_status_map_host_entry {
155 wl_proxd_status_t proxd_status;
156 rtt_reason_t rtt_reason;
157 } ftm_status_map_host_entry_t;
158
159 static int
160 dhd_rtt_convert_results_to_host(rtt_report_t *rtt_report, uint8 *p_data, uint16 tlvid, uint16 len);
161
162 static wifi_rate_t
163 dhd_rtt_convert_rate_to_host(uint32 ratespec);
164
165 #ifdef WL_CFG80211
166 static int
167 dhd_rtt_start(dhd_pub_t *dhd);
168 #endif /* WL_CFG80211 */
169 static const int burst_duration_idx[] = {0, 0, 1, 2, 4, 8, 16, 32, 64, 128, 0, 0};
170
171 /* ftm status mapping to host status */
172 static const ftm_status_map_host_entry_t ftm_status_map_info[] = {
173 {WL_PROXD_E_INCOMPLETE, RTT_REASON_FAILURE},
174 {WL_PROXD_E_OVERRIDDEN, RTT_REASON_FAILURE},
175 {WL_PROXD_E_ASAP_FAILED, RTT_REASON_FAILURE},
176 {WL_PROXD_E_NOTSTARTED, RTT_REASON_FAIL_NOT_SCHEDULED_YET},
177 {WL_PROXD_E_INVALIDMEAS, RTT_REASON_FAIL_INVALID_TS},
178 {WL_PROXD_E_INCAPABLE, RTT_REASON_FAIL_NO_CAPABILITY},
179 {WL_PROXD_E_MISMATCH, RTT_REASON_FAILURE},
180 {WL_PROXD_E_DUP_SESSION, RTT_REASON_FAILURE},
181 {WL_PROXD_E_REMOTE_FAIL, RTT_REASON_FAILURE},
182 {WL_PROXD_E_REMOTE_INCAPABLE, RTT_REASON_FAILURE},
183 {WL_PROXD_E_SCHED_FAIL, RTT_REASON_FAIL_SCHEDULE},
184 {WL_PROXD_E_PROTO, RTT_REASON_FAIL_PROTOCOL},
185 {WL_PROXD_E_EXPIRED, RTT_REASON_FAILURE},
186 {WL_PROXD_E_TIMEOUT, RTT_REASON_FAIL_TM_TIMEOUT},
187 {WL_PROXD_E_NOACK, RTT_REASON_FAIL_NO_RSP},
188 {WL_PROXD_E_DEFERRED, RTT_REASON_FAILURE},
189 {WL_PROXD_E_INVALID_SID, RTT_REASON_FAILURE},
190 {WL_PROXD_E_REMOTE_CANCEL, RTT_REASON_FAILURE},
191 {WL_PROXD_E_CANCELED, RTT_REASON_ABORTED},
192 {WL_PROXD_E_INVALID_SESSION, RTT_REASON_FAILURE},
193 {WL_PROXD_E_BAD_STATE, RTT_REASON_FAILURE},
194 {WL_PROXD_E_ERROR, RTT_REASON_FAILURE},
195 {WL_PROXD_E_OK, RTT_REASON_SUCCESS}
196 };
197
198 /* ftm tlv-id mapping */
199 #if 0
200 static const ftm_strmap_entry_t ftm_tlvid_loginfo[] = {
201 /* { WL_PROXD_TLV_ID_xxx, "text for WL_PROXD_TLV_ID_xxx" }, */
202 { WL_PROXD_TLV_ID_NONE, "none" },
203 { WL_PROXD_TLV_ID_METHOD, "method" },
204 { WL_PROXD_TLV_ID_FLAGS, "flags" },
205 { WL_PROXD_TLV_ID_CHANSPEC, "chanspec" },
206 { WL_PROXD_TLV_ID_TX_POWER, "tx power" },
207 { WL_PROXD_TLV_ID_RATESPEC, "ratespec" },
208 { WL_PROXD_TLV_ID_BURST_DURATION, "burst duration" },
209 { WL_PROXD_TLV_ID_BURST_PERIOD, "burst period" },
210 { WL_PROXD_TLV_ID_BURST_FTM_SEP, "burst ftm sep" },
211 { WL_PROXD_TLV_ID_BURST_NUM_FTM, "burst num ftm" },
212 { WL_PROXD_TLV_ID_NUM_BURST, "num burst" },
213 { WL_PROXD_TLV_ID_FTM_RETRIES, "ftm retries" },
214 { WL_PROXD_TLV_ID_BSS_INDEX, "BSS index" },
215 { WL_PROXD_TLV_ID_BSSID, "bssid" },
216 { WL_PROXD_TLV_ID_INIT_DELAY, "burst init delay" },
217 { WL_PROXD_TLV_ID_BURST_TIMEOUT, "burst timeout" },
218 { WL_PROXD_TLV_ID_EVENT_MASK, "event mask" },
219 { WL_PROXD_TLV_ID_FLAGS_MASK, "flags mask" },
220 { WL_PROXD_TLV_ID_PEER_MAC, "peer addr" },
221 { WL_PROXD_TLV_ID_FTM_REQ, "ftm req" },
222 { WL_PROXD_TLV_ID_LCI_REQ, "lci req" },
223 { WL_PROXD_TLV_ID_LCI, "lci" },
224 { WL_PROXD_TLV_ID_CIVIC_REQ, "civic req" },
225 { WL_PROXD_TLV_ID_CIVIC, "civic" },
226 { WL_PROXD_TLV_ID_AVAIL, "availability" },
227 { WL_PROXD_TLV_ID_SESSION_FLAGS, "session flags" },
228 { WL_PROXD_TLV_ID_SESSION_FLAGS_MASK, "session flags mask" },
229 { WL_PROXD_TLV_ID_RX_MAX_BURST, "rx max bursts" },
230 { WL_PROXD_TLV_ID_RANGING_INFO, "ranging info" },
231 { WL_PROXD_TLV_ID_RANGING_FLAGS, "ranging flags" },
232 { WL_PROXD_TLV_ID_RANGING_FLAGS_MASK, "ranging flags mask" },
233 /* output - 512 + x */
234 { WL_PROXD_TLV_ID_STATUS, "status" },
235 { WL_PROXD_TLV_ID_COUNTERS, "counters" },
236 { WL_PROXD_TLV_ID_INFO, "info" },
237 { WL_PROXD_TLV_ID_RTT_RESULT, "rtt result" },
238 { WL_PROXD_TLV_ID_AOA_RESULT, "aoa result" },
239 { WL_PROXD_TLV_ID_SESSION_INFO, "session info" },
240 { WL_PROXD_TLV_ID_SESSION_STATUS, "session status" },
241 { WL_PROXD_TLV_ID_SESSION_ID_LIST, "session ids" },
242 /* debug tlvs can be added starting 1024 */
243 { WL_PROXD_TLV_ID_DEBUG_MASK, "debug mask" },
244 { WL_PROXD_TLV_ID_COLLECT, "collect" },
245 { WL_PROXD_TLV_ID_STRBUF, "result" },
246 { WL_PROXD_TLV_ID_COLLECT_DATA, "collect-data" },
247 { WL_PROXD_TLV_ID_RI_RR, "ri_rr" },
248 { WL_PROXD_TLV_ID_COLLECT_CHAN_DATA, "chan est"}
249 };
250 #endif
251
252 static const ftm_strmap_entry_t ftm_event_type_loginfo[] = {
253 /* wl_proxd_event_type_t, text-string */
254 { WL_PROXD_EVENT_NONE, "none" },
255 { WL_PROXD_EVENT_SESSION_CREATE, "session create" },
256 { WL_PROXD_EVENT_SESSION_START, "session start" },
257 { WL_PROXD_EVENT_FTM_REQ, "FTM req" },
258 { WL_PROXD_EVENT_BURST_START, "burst start" },
259 { WL_PROXD_EVENT_BURST_END, "burst end" },
260 { WL_PROXD_EVENT_SESSION_END, "session end" },
261 { WL_PROXD_EVENT_SESSION_RESTART, "session restart" },
262 { WL_PROXD_EVENT_BURST_RESCHED, "burst rescheduled" },
263 { WL_PROXD_EVENT_SESSION_DESTROY, "session destroy" },
264 { WL_PROXD_EVENT_RANGE_REQ, "range request" },
265 { WL_PROXD_EVENT_FTM_FRAME, "FTM frame" },
266 { WL_PROXD_EVENT_DELAY, "delay" },
267 { WL_PROXD_EVENT_VS_INITIATOR_RPT, "initiator-report " }, /* rx */
268 { WL_PROXD_EVENT_RANGING, "ranging " },
269 { WL_PROXD_EVENT_COLLECT, "collect" },
270 };
271
272 /*
273 * session-state --> text string mapping
274 */
275 static const ftm_strmap_entry_t ftm_session_state_value_loginfo[] = {
276 /* wl_proxd_session_state_t, text string */
277 { WL_PROXD_SESSION_STATE_CREATED, "created" },
278 { WL_PROXD_SESSION_STATE_CONFIGURED, "configured" },
279 { WL_PROXD_SESSION_STATE_STARTED, "started" },
280 { WL_PROXD_SESSION_STATE_DELAY, "delay" },
281 { WL_PROXD_SESSION_STATE_USER_WAIT, "user-wait" },
282 { WL_PROXD_SESSION_STATE_SCHED_WAIT, "sched-wait" },
283 { WL_PROXD_SESSION_STATE_BURST, "burst" },
284 { WL_PROXD_SESSION_STATE_STOPPING, "stopping" },
285 { WL_PROXD_SESSION_STATE_ENDED, "ended" },
286 { WL_PROXD_SESSION_STATE_DESTROYING, "destroying" },
287 { WL_PROXD_SESSION_STATE_NONE, "none" }
288 };
289
290 /*
291 * ranging-state --> text string mapping
292 */
293 #if 0
294 static const ftm_strmap_entry_t ftm_ranging_state_value_loginfo [] = {
295 /* wl_proxd_ranging_state_t, text string */
296 { WL_PROXD_RANGING_STATE_NONE, "none" },
297 { WL_PROXD_RANGING_STATE_NOTSTARTED, "nonstarted" },
298 { WL_PROXD_RANGING_STATE_INPROGRESS, "inprogress" },
299 { WL_PROXD_RANGING_STATE_DONE, "done" },
300 };
301 #endif
302
303 /*
304 * status --> text string mapping
305 */
306 static const ftm_strmap_entry_t ftm_status_value_loginfo[] = {
307 /* wl_proxd_status_t, text-string */
308 { WL_PROXD_E_OVERRIDDEN, "overridden" },
309 { WL_PROXD_E_ASAP_FAILED, "ASAP failed" },
310 { WL_PROXD_E_NOTSTARTED, "not started" },
311 { WL_PROXD_E_INVALIDMEAS, "invalid measurement" },
312 { WL_PROXD_E_INCAPABLE, "incapable" },
313 { WL_PROXD_E_MISMATCH, "mismatch"},
314 { WL_PROXD_E_DUP_SESSION, "dup session" },
315 { WL_PROXD_E_REMOTE_FAIL, "remote fail" },
316 { WL_PROXD_E_REMOTE_INCAPABLE, "remote incapable" },
317 { WL_PROXD_E_SCHED_FAIL, "sched failure" },
318 { WL_PROXD_E_PROTO, "protocol error" },
319 { WL_PROXD_E_EXPIRED, "expired" },
320 { WL_PROXD_E_TIMEOUT, "timeout" },
321 { WL_PROXD_E_NOACK, "no ack" },
322 { WL_PROXD_E_DEFERRED, "deferred" },
323 { WL_PROXD_E_INVALID_SID, "invalid session id" },
324 { WL_PROXD_E_REMOTE_CANCEL, "remote cancel" },
325 { WL_PROXD_E_CANCELED, "canceled" },
326 { WL_PROXD_E_INVALID_SESSION, "invalid session" },
327 { WL_PROXD_E_BAD_STATE, "bad state" },
328 { WL_PROXD_E_ERROR, "error" },
329 { WL_PROXD_E_OK, "OK" }
330 };
331
332 /*
333 * time interval unit --> text string mapping
334 */
335 static const ftm_strmap_entry_t ftm_tmu_value_loginfo[] = {
336 /* wl_proxd_tmu_t, text-string */
337 { WL_PROXD_TMU_TU, "TU" },
338 { WL_PROXD_TMU_SEC, "sec" },
339 { WL_PROXD_TMU_MILLI_SEC, "ms" },
340 { WL_PROXD_TMU_MICRO_SEC, "us" },
341 { WL_PROXD_TMU_NANO_SEC, "ns" },
342 { WL_PROXD_TMU_PICO_SEC, "ps" }
343 };
344
345 #define RSPEC_BW(rspec) ((rspec) & WL_RSPEC_BW_MASK)
346 #define RSPEC_IS20MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_20MHZ)
347 #define RSPEC_IS40MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_40MHZ)
348 #define RSPEC_IS80MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_80MHZ)
349 #define RSPEC_IS160MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_160MHZ)
350
351 #define IS_MCS(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) != WL_RSPEC_ENCODE_RATE)
352 #define IS_STBC(rspec) (((((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) || \
353 (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT)) && \
354 (((rspec) & WL_RSPEC_STBC) == WL_RSPEC_STBC))
355 #define RSPEC_ISSGI(rspec) (((rspec) & WL_RSPEC_SGI) != 0)
356 #define RSPEC_ISLDPC(rspec) (((rspec) & WL_RSPEC_LDPC) != 0)
357 #define RSPEC_ISSTBC(rspec) (((rspec) & WL_RSPEC_STBC) != 0)
358 #define RSPEC_ISTXBF(rspec) (((rspec) & WL_RSPEC_TXBF) != 0)
359 #define RSPEC_ISVHT(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT)
360 #define RSPEC_ISHT(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT)
361 #define RSPEC_ISLEGACY(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE)
362 #define RSPEC2RATE(rspec) (RSPEC_ISLEGACY(rspec) ? \
363 ((rspec) & RSPEC_RATE_MASK) : rate_rspec2rate(rspec))
364 /* return rate in unit of 500Kbps -- for internal use in wlc_rate_sel.c */
365 #define RSPEC2KBPS(rspec) rate_rspec2rate(rspec)
366
367 struct ieee_80211_mcs_rate_info {
368 uint8 constellation_bits;
369 uint8 coding_q;
370 uint8 coding_d;
371 };
372
373 static const struct ieee_80211_mcs_rate_info wl_mcs_info[] = {
374 { 1, 1, 2 }, /* MCS 0: MOD: BPSK, CR 1/2 */
375 { 2, 1, 2 }, /* MCS 1: MOD: QPSK, CR 1/2 */
376 { 2, 3, 4 }, /* MCS 2: MOD: QPSK, CR 3/4 */
377 { 4, 1, 2 }, /* MCS 3: MOD: 16QAM, CR 1/2 */
378 { 4, 3, 4 }, /* MCS 4: MOD: 16QAM, CR 3/4 */
379 { 6, 2, 3 }, /* MCS 5: MOD: 64QAM, CR 2/3 */
380 { 6, 3, 4 }, /* MCS 6: MOD: 64QAM, CR 3/4 */
381 { 6, 5, 6 }, /* MCS 7: MOD: 64QAM, CR 5/6 */
382 { 8, 3, 4 }, /* MCS 8: MOD: 256QAM, CR 3/4 */
383 { 8, 5, 6 } /* MCS 9: MOD: 256QAM, CR 5/6 */
384 };
385
386 /**
387 * Returns the rate in [Kbps] units for a caller supplied MCS/bandwidth/Nss/Sgi combination.
388 * 'mcs' : a *single* spatial stream MCS (11n or 11ac)
389 */
390 uint
rate_mcs2rate(uint mcs,uint nss,uint bw,int sgi)391 rate_mcs2rate(uint mcs, uint nss, uint bw, int sgi)
392 {
393 const int ksps = 250; /* kilo symbols per sec, 4 us sym */
394 const int Nsd_20MHz = 52;
395 const int Nsd_40MHz = 108;
396 const int Nsd_80MHz = 234;
397 const int Nsd_160MHz = 468;
398 uint rate;
399
400 if (mcs == 32) {
401 /* just return fixed values for mcs32 instead of trying to parametrize */
402 rate = (sgi == 0) ? 6000 : 6778;
403 } else if (mcs <= 9) {
404 /* This calculation works for 11n HT and 11ac VHT if the HT mcs values
405 * are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8.
406 * That is, HT MCS 23 is a base MCS = 7, Nss = 3
407 */
408
409 /* find the number of complex numbers per symbol */
410 if (RSPEC_IS20MHZ(bw)) {
411 rate = Nsd_20MHz;
412 } else if (RSPEC_IS40MHZ(bw)) {
413 rate = Nsd_40MHz;
414 } else if (bw == WL_RSPEC_BW_80MHZ) {
415 rate = Nsd_80MHz;
416 } else if (bw == WL_RSPEC_BW_160MHZ) {
417 rate = Nsd_160MHz;
418 } else {
419 rate = 0;
420 }
421
422 /* multiply by bits per number from the constellation in use */
423 rate = rate * wl_mcs_info[mcs].constellation_bits;
424
425 /* adjust for the number of spatial streams */
426 rate = rate * nss;
427
428 /* adjust for the coding rate given as a quotient and divisor */
429 rate = (rate * wl_mcs_info[mcs].coding_q) / wl_mcs_info[mcs].coding_d;
430
431 /* multiply by Kilo symbols per sec to get Kbps */
432 rate = rate * ksps;
433
434 /* adjust the symbols per sec for SGI
435 * symbol duration is 4 us without SGI, and 3.6 us with SGI,
436 * so ratio is 10 / 9
437 */
438 if (sgi) {
439 /* add 4 for rounding of division by 9 */
440 rate = ((rate * 10) + 4) / 9;
441 }
442 } else {
443 rate = 0;
444 }
445
446 return rate;
447 } /* wlc_rate_mcs2rate */
448
449 /** take a well formed ratespec_t arg and return phy rate in [Kbps] units */
450 int
rate_rspec2rate(uint32 rspec)451 rate_rspec2rate(uint32 rspec)
452 {
453 int rate = -1;
454
455 if (RSPEC_ISLEGACY(rspec)) {
456 rate = 500 * (rspec & WL_RSPEC_RATE_MASK);
457 } else if (RSPEC_ISHT(rspec)) {
458 uint mcs = (rspec & WL_RSPEC_RATE_MASK);
459
460 if (mcs == 32) {
461 rate = rate_mcs2rate(mcs, 1, WL_RSPEC_BW_40MHZ, RSPEC_ISSGI(rspec));
462 } else {
463 uint nss = 1 + (mcs / 8);
464 mcs = mcs % 8;
465 rate = rate_mcs2rate(mcs, nss, RSPEC_BW(rspec), RSPEC_ISSGI(rspec));
466 }
467 } else if (RSPEC_ISVHT(rspec)) {
468 uint mcs = (rspec & WL_RSPEC_VHT_MCS_MASK);
469 uint nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
470
471 ASSERT(mcs <= 9);
472 ASSERT(nss <= 8);
473
474 rate = rate_mcs2rate(mcs, nss, RSPEC_BW(rspec), RSPEC_ISSGI(rspec));
475 } else {
476 ASSERT(0);
477 }
478
479 return (rate == 0) ? -1 : rate;
480 }
481
482 char resp_buf[WLC_IOCTL_SMLEN];
483
484 static uint64
ftm_intvl2nsec(const wl_proxd_intvl_t * intvl)485 ftm_intvl2nsec(const wl_proxd_intvl_t *intvl)
486 {
487 uint64 ret;
488 ret = intvl->intvl;
489 switch (intvl->tmu) {
490 case WL_PROXD_TMU_TU: ret = FTM_TU2MICRO(ret) * 1000; break;
491 case WL_PROXD_TMU_SEC: ret *= 1000000000; break;
492 case WL_PROXD_TMU_MILLI_SEC: ret *= 1000000; break;
493 case WL_PROXD_TMU_MICRO_SEC: ret *= 1000; break;
494 case WL_PROXD_TMU_PICO_SEC: ret = intvl->intvl / 1000; break;
495 case WL_PROXD_TMU_NANO_SEC: /* fall through */
496 default: break;
497 }
498 return ret;
499 }
500 uint64
ftm_intvl2usec(const wl_proxd_intvl_t * intvl)501 ftm_intvl2usec(const wl_proxd_intvl_t *intvl)
502 {
503 uint64 ret;
504 ret = intvl->intvl;
505 switch (intvl->tmu) {
506 case WL_PROXD_TMU_TU: ret = FTM_TU2MICRO(ret); break;
507 case WL_PROXD_TMU_SEC: ret *= 1000000; break;
508 case WL_PROXD_TMU_NANO_SEC: ret = intvl->intvl / 1000; break;
509 case WL_PROXD_TMU_PICO_SEC: ret = intvl->intvl / 1000000; break;
510 case WL_PROXD_TMU_MILLI_SEC: ret *= 1000; break;
511 case WL_PROXD_TMU_MICRO_SEC: /* fall through */
512 default: break;
513 }
514 return ret;
515 }
516
517 /*
518 * lookup 'id' (as a key) from a fw status to host map table
519 * if found, return the corresponding reason code
520 */
521
522 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)523 ftm_get_statusmap_info(wl_proxd_status_t id, const ftm_status_map_host_entry_t *p_table,
524 uint32 num_entries)
525 {
526 int i;
527 const ftm_status_map_host_entry_t *p_entry;
528 /* scan thru the table till end */
529 p_entry = p_table;
530 for (i = 0; i < (int) num_entries; i++)
531 {
532 if (p_entry->proxd_status == id) {
533 return p_entry->rtt_reason;
534 }
535 p_entry++; /* next entry */
536 }
537 return RTT_REASON_FAILURE; /* not found */
538 }
539 /*
540 * lookup 'id' (as a key) from a table
541 * if found, return the entry pointer, otherwise return NULL
542 */
543 static const ftm_strmap_entry_t*
ftm_get_strmap_info(int32 id,const ftm_strmap_entry_t * p_table,uint32 num_entries)544 ftm_get_strmap_info(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
545 {
546 int i;
547 const ftm_strmap_entry_t *p_entry;
548
549 /* scan thru the table till end */
550 p_entry = p_table;
551 for (i = 0; i < (int) num_entries; i++)
552 {
553 if (p_entry->id == id)
554 return p_entry;
555 p_entry++; /* next entry */
556 }
557 return NULL; /* not found */
558 }
559
560 /*
561 * map enum to a text-string for display, this function is called by the following:
562 * For debug/trace:
563 * ftm_[cmdid|tlvid]_to_str()
564 * For TLV-output log for 'get' commands
565 * ftm_[method|tmu|caps|status|state]_value_to_logstr()
566 * Input:
567 * pTable -- point to a 'enum to string' table.
568 */
569 static const char *
ftm_map_id_to_str(int32 id,const ftm_strmap_entry_t * p_table,uint32 num_entries)570 ftm_map_id_to_str(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
571 {
572 const ftm_strmap_entry_t*p_entry = ftm_get_strmap_info(id, p_table, num_entries);
573 if (p_entry)
574 return (p_entry->text);
575
576 return "invalid";
577 }
578
579
580 #ifdef RTT_DEBUG
581
582 /* define entry, e.g. { WL_PROXD_CMD_xxx, "WL_PROXD_CMD_xxx" } */
583 #define DEF_STRMAP_ENTRY(id) { (id), #id }
584
585 /* ftm cmd-id mapping */
586 static const ftm_strmap_entry_t ftm_cmdid_map[] = {
587 DEF_STRMAP_ENTRY(WL_PROXD_CMD_NONE),
588 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_VERSION),
589 DEF_STRMAP_ENTRY(WL_PROXD_CMD_ENABLE),
590 DEF_STRMAP_ENTRY(WL_PROXD_CMD_DISABLE),
591 DEF_STRMAP_ENTRY(WL_PROXD_CMD_CONFIG),
592 DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_SESSION),
593 DEF_STRMAP_ENTRY(WL_PROXD_CMD_BURST_REQUEST),
594 DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_SESSION),
595 DEF_STRMAP_ENTRY(WL_PROXD_CMD_DELETE_SESSION),
596 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RESULT),
597 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_INFO),
598 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_STATUS),
599 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_SESSIONS),
600 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_COUNTERS),
601 DEF_STRMAP_ENTRY(WL_PROXD_CMD_CLEAR_COUNTERS),
602 DEF_STRMAP_ENTRY(WL_PROXD_CMD_COLLECT),
603 DEF_STRMAP_ENTRY(WL_PROXD_CMD_TUNE),
604 DEF_STRMAP_ENTRY(WL_PROXD_CMD_DUMP),
605 DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_RANGING),
606 DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_RANGING),
607 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RANGING_INFO),
608 };
609
610 /*
611 * map a ftm cmd-id to a text-string for display
612 */
613 static const char *
ftm_cmdid_to_str(uint16 cmdid)614 ftm_cmdid_to_str(uint16 cmdid)
615 {
616 return ftm_map_id_to_str((int32) cmdid, &ftm_cmdid_map[0], ARRAYSIZE(ftm_cmdid_map));
617 }
618 #endif /* RTT_DEBUG */
619
620
621 /*
622 * convert BCME_xxx error codes into related error strings
623 * note, bcmerrorstr() defined in bcmutils is for BCMDRIVER only,
624 * this duplicate copy is for WL access and may need to clean up later
625 */
626 static const char *ftm_bcmerrorstrtable[] = BCMERRSTRINGTABLE;
627 static const char *
ftm_status_value_to_logstr(wl_proxd_status_t status)628 ftm_status_value_to_logstr(wl_proxd_status_t status)
629 {
630 static char ftm_msgbuf_status_undef[32];
631 const ftm_strmap_entry_t *p_loginfo;
632 int bcmerror;
633
634 /* check if within BCME_xxx error range */
635 bcmerror = (int) status;
636 if (VALID_BCMERROR(bcmerror))
637 return ftm_bcmerrorstrtable[-bcmerror];
638
639 /* otherwise, look for 'proxd ftm status' range */
640 p_loginfo = ftm_get_strmap_info((int32) status,
641 &ftm_status_value_loginfo[0], ARRAYSIZE(ftm_status_value_loginfo));
642 if (p_loginfo)
643 return p_loginfo->text;
644
645 /* report for 'out of range' FTM-status error code */
646 memset(ftm_msgbuf_status_undef, 0, sizeof(ftm_msgbuf_status_undef));
647 snprintf(ftm_msgbuf_status_undef, sizeof(ftm_msgbuf_status_undef),
648 "Undefined status %d", status);
649 return &ftm_msgbuf_status_undef[0];
650 }
651
652 static const char *
ftm_tmu_value_to_logstr(wl_proxd_tmu_t tmu)653 ftm_tmu_value_to_logstr(wl_proxd_tmu_t tmu)
654 {
655 return ftm_map_id_to_str((int32)tmu,
656 &ftm_tmu_value_loginfo[0], ARRAYSIZE(ftm_tmu_value_loginfo));
657 }
658
659 static const ftm_strmap_entry_t*
ftm_get_event_type_loginfo(wl_proxd_event_type_t event_type)660 ftm_get_event_type_loginfo(wl_proxd_event_type_t event_type)
661 {
662 /* look up 'event-type' from a predefined table */
663 return ftm_get_strmap_info((int32) event_type,
664 ftm_event_type_loginfo, ARRAYSIZE(ftm_event_type_loginfo));
665 }
666
667 static const char *
ftm_session_state_value_to_logstr(wl_proxd_session_state_t state)668 ftm_session_state_value_to_logstr(wl_proxd_session_state_t state)
669 {
670 return ftm_map_id_to_str((int32)state, &ftm_session_state_value_loginfo[0],
671 ARRAYSIZE(ftm_session_state_value_loginfo));
672 }
673
674
675 #ifdef WL_CFG80211
676 /*
677 * send 'proxd' iovar for all ftm get-related commands
678 */
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)679 static int rtt_do_get_ioctl(dhd_pub_t *dhd, wl_proxd_iov_t *p_proxd_iov, uint16 proxd_iovsize,
680 ftm_subcmd_info_t *p_subcmd_info)
681 {
682 wl_proxd_iov_t *p_iovresp = (wl_proxd_iov_t *)resp_buf;
683 int status;
684 int tlvs_len;
685 /* send getbuf proxd iovar */
686 status = dhd_getiovar(dhd, 0, "proxd", (char *)p_proxd_iov,
687 proxd_iovsize, (char **)&p_iovresp, WLC_IOCTL_SMLEN);
688 if (status != BCME_OK) {
689 DHD_ERROR(("%s: failed to send getbuf proxd iovar (CMD ID : %d), status=%d\n",
690 __FUNCTION__, p_subcmd_info->cmdid, status));
691 return status;
692 }
693 if (p_subcmd_info->cmdid == WL_PROXD_CMD_GET_VERSION) {
694 p_subcmd_info->version = ltoh16(p_iovresp->version);
695 DHD_RTT(("ftm version: 0x%x\n", ltoh16(p_iovresp->version)));
696 goto exit;
697 }
698
699 tlvs_len = ltoh16(p_iovresp->len) - WL_PROXD_IOV_HDR_SIZE;
700 if (tlvs_len < 0) {
701 DHD_ERROR(("%s: alert, p_iovresp->len(%d) should not be smaller than %d\n",
702 __FUNCTION__, ltoh16(p_iovresp->len), (int) WL_PROXD_IOV_HDR_SIZE));
703 tlvs_len = 0;
704 }
705
706 if (tlvs_len > 0 && p_subcmd_info->handler) {
707 /* unpack TLVs and invokes the cbfn for processing */
708 status = bcm_unpack_xtlv_buf(p_proxd_iov, (uint8 *)p_iovresp->tlvs,
709 tlvs_len, BCM_XTLV_OPTION_ALIGN32, p_subcmd_info->handler);
710 }
711 exit:
712 return status;
713 }
714
715
716 static wl_proxd_iov_t *
rtt_alloc_getset_buf(wl_proxd_method_t method,wl_proxd_session_id_t session_id,wl_proxd_cmd_t cmdid,uint16 tlvs_bufsize,uint16 * p_out_bufsize)717 rtt_alloc_getset_buf(wl_proxd_method_t method, wl_proxd_session_id_t session_id,
718 wl_proxd_cmd_t cmdid, uint16 tlvs_bufsize, uint16 *p_out_bufsize)
719 {
720 uint16 proxd_iovsize;
721 uint16 kflags;
722 wl_proxd_tlv_t *p_tlv;
723 wl_proxd_iov_t *p_proxd_iov = (wl_proxd_iov_t *) NULL;
724
725 *p_out_bufsize = 0; /* init */
726 kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
727 /* calculate the whole buffer size, including one reserve-tlv entry in the header */
728 proxd_iovsize = sizeof(wl_proxd_iov_t) + tlvs_bufsize;
729
730 p_proxd_iov = kzalloc(proxd_iovsize, kflags);
731 if (p_proxd_iov == NULL) {
732 DHD_ERROR(("error: failed to allocate %d bytes of memory\n", proxd_iovsize));
733 return NULL;
734 }
735
736 /* setup proxd-FTM-method iovar header */
737 p_proxd_iov->version = htol16(WL_PROXD_API_VERSION);
738 p_proxd_iov->len = htol16(proxd_iovsize); /* caller may adjust it based on #of TLVs */
739 p_proxd_iov->cmd = htol16(cmdid);
740 p_proxd_iov->method = htol16(method);
741 p_proxd_iov->sid = htol16(session_id);
742
743 /* initialize the reserved/dummy-TLV in iovar header */
744 p_tlv = p_proxd_iov->tlvs;
745 p_tlv->id = htol16(WL_PROXD_TLV_ID_NONE);
746 p_tlv->len = htol16(0);
747
748 *p_out_bufsize = proxd_iovsize; /* for caller's reference */
749
750 return p_proxd_iov;
751 }
752
753
754 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)755 dhd_rtt_common_get_handler(dhd_pub_t *dhd, ftm_subcmd_info_t *p_subcmd_info,
756 wl_proxd_method_t method,
757 wl_proxd_session_id_t session_id)
758 {
759 int status = BCME_OK;
760 uint16 proxd_iovsize = 0;
761 wl_proxd_iov_t *p_proxd_iov;
762 #ifdef RTT_DEBUG
763 DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
764 __FUNCTION__, method, session_id, p_subcmd_info->cmdid,
765 ftm_cmdid_to_str(p_subcmd_info->cmdid)));
766 #endif
767 /* alloc mem for ioctl headr + reserved 0 bufsize for tlvs (initialize to zero) */
768 p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
769 0, &proxd_iovsize);
770
771 if (p_proxd_iov == NULL)
772 return BCME_NOMEM;
773
774 status = rtt_do_get_ioctl(dhd, p_proxd_iov, proxd_iovsize, p_subcmd_info);
775
776 if (status != BCME_OK) {
777 DHD_RTT(("%s failed: status=%d\n", __FUNCTION__, status));
778 }
779 kfree(p_proxd_iov);
780 return status;
781 }
782
783 /*
784 * common handler for set-related proxd method commands which require no TLV as input
785 * wl proxd ftm [session-id] <set-subcmd>
786 * e.g.
787 * wl proxd ftm enable -- to enable ftm
788 * wl proxd ftm disable -- to disable ftm
789 * wl proxd ftm <session-id> start -- to start a specified session
790 * wl proxd ftm <session-id> stop -- to cancel a specified session;
791 * state is maintained till session is delete.
792 * wl proxd ftm <session-id> delete -- to delete a specified session
793 * wl proxd ftm [<session-id>] clear-counters -- to clear counters
794 * wl proxd ftm <session-id> burst-request -- on initiator: to send burst request;
795 * on target: send FTM frame
796 * wl proxd ftm <session-id> collect
797 * wl proxd ftm tune (TBD)
798 */
799 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)800 dhd_rtt_common_set_handler(dhd_pub_t *dhd, const ftm_subcmd_info_t *p_subcmd_info,
801 wl_proxd_method_t method, wl_proxd_session_id_t session_id)
802 {
803 uint16 proxd_iovsize;
804 wl_proxd_iov_t *p_proxd_iov;
805 int ret;
806
807 #ifdef RTT_DEBUG
808 DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
809 __FUNCTION__, method, session_id, p_subcmd_info->cmdid,
810 ftm_cmdid_to_str(p_subcmd_info->cmdid)));
811 #endif
812
813 /* allocate and initialize a temp buffer for 'set proxd' iovar */
814 proxd_iovsize = 0;
815 p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
816 0, &proxd_iovsize); /* no TLV */
817 if (p_proxd_iov == NULL)
818 return BCME_NOMEM;
819
820 /* no TLV to pack, simply issue a set-proxd iovar */
821 ret = dhd_iovar(dhd, 0, "proxd", (char *)p_proxd_iov, proxd_iovsize, NULL, 0, TRUE);
822 #ifdef RTT_DEBUG
823 if (ret != BCME_OK) {
824 DHD_RTT(("error: IOVAR failed, status=%d\n", ret));
825 }
826 #endif
827 /* clean up */
828 kfree(p_proxd_iov);
829
830 return ret;
831 }
832 #endif /* WL_CFG80211 */
833
834 static int
rtt_unpack_xtlv_cbfn(void * ctx,uint8 * p_data,uint16 tlvid,uint16 len)835 rtt_unpack_xtlv_cbfn(void *ctx, uint8 *p_data, uint16 tlvid, uint16 len)
836 {
837 int ret = BCME_OK;
838 int i;
839 wl_proxd_ftm_session_status_t *p_data_info = NULL;
840 wl_proxd_collect_event_data_t *p_collect_data = NULL;
841 uint32 chan_data_entry = 0;
842
843 switch (tlvid) {
844 case WL_PROXD_TLV_ID_RTT_RESULT:
845 ret = dhd_rtt_convert_results_to_host((rtt_report_t *)ctx,
846 p_data, tlvid, len);
847 break;
848 case WL_PROXD_TLV_ID_SESSION_STATUS:
849 DHD_RTT(("WL_PROXD_TLV_ID_SESSION_STATUS\n"));
850 memcpy(ctx, p_data, sizeof(wl_proxd_ftm_session_status_t));
851 p_data_info = (wl_proxd_ftm_session_status_t *)ctx;
852 p_data_info->sid = ltoh16_ua(&p_data_info->sid);
853 p_data_info->state = ltoh16_ua(&p_data_info->state);
854 p_data_info->status = ltoh32_ua(&p_data_info->status);
855 p_data_info->burst_num = ltoh16_ua(&p_data_info->burst_num);
856 DHD_RTT(("\tsid=%u, state=%d, status=%d, burst_num=%u\n",
857 p_data_info->sid, p_data_info->state,
858 p_data_info->status, p_data_info->burst_num));
859
860 break;
861 case WL_PROXD_TLV_ID_COLLECT_DATA:
862 DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_DATA\n"));
863 memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_t));
864 p_collect_data = (wl_proxd_collect_event_data_t *)ctx;
865 DHD_RTT(("\tH_RX\n"));
866 for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
867 p_collect_data->H_RX[i] = ltoh32_ua(&p_collect_data->H_RX[i]);
868 DHD_RTT(("\t%u\n", p_collect_data->H_RX[i]));
869 }
870 DHD_RTT(("\n"));
871 DHD_RTT(("\tH_LB\n"));
872 for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
873 p_collect_data->H_LB[i] = ltoh32_ua(&p_collect_data->H_LB[i]);
874 DHD_RTT(("\t%u\n", p_collect_data->H_LB[i]));
875 }
876 DHD_RTT(("\n"));
877 DHD_RTT(("\tri_rr\n"));
878 for (i = 0; i < FTM_TPK_RI_RR_LEN; i++) {
879 DHD_RTT(("\t%u\n", p_collect_data->ri_rr[i]));
880 }
881 p_collect_data->phy_err_mask = ltoh32_ua(&p_collect_data->phy_err_mask);
882 DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data->phy_err_mask));
883 break;
884 case WL_PROXD_TLV_ID_COLLECT_CHAN_DATA:
885 DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_CHAN_DATA\n"));
886 DHD_RTT(("\tchan est %u\n", (uint32) (len / sizeof(uint32))));
887 for (i = 0; i < (len/sizeof(chan_data_entry)); i++) {
888 uint32 *p = (uint32*)p_data;
889 chan_data_entry = ltoh32_ua(p + i);
890 DHD_RTT(("\t%u\n", chan_data_entry));
891 }
892 break;
893 default:
894 DHD_ERROR(("> Unsupported TLV ID %d\n", tlvid));
895 ret = BCME_ERROR;
896 break;
897 }
898
899 return ret;
900 }
901
902 #ifdef WL_CFG80211
903 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)904 rtt_handle_config_options(wl_proxd_session_id_t session_id, wl_proxd_tlv_t **p_tlv,
905 uint16 *p_buf_space_left, ftm_config_options_info_t *ftm_configs, int ftm_cfg_cnt)
906 {
907 int ret = BCME_OK;
908 int cfg_idx = 0;
909 uint32 flags = WL_PROXD_FLAG_NONE;
910 uint32 flags_mask = WL_PROXD_FLAG_NONE;
911 uint32 new_mask; /* cmdline input */
912 ftm_config_options_info_t *p_option_info;
913 uint16 type = (session_id == WL_PROXD_SESSION_ID_GLOBAL) ?
914 WL_PROXD_TLV_ID_FLAGS_MASK : WL_PROXD_TLV_ID_SESSION_FLAGS_MASK;
915 for (cfg_idx = 0; cfg_idx < ftm_cfg_cnt; cfg_idx++) {
916 p_option_info = (ftm_configs + cfg_idx);
917 if (p_option_info != NULL) {
918 new_mask = p_option_info->flags;
919 /* update flags mask */
920 flags_mask |= new_mask;
921 if (p_option_info->enable) {
922 flags |= new_mask; /* set the bit on */
923 } else {
924 flags &= ~new_mask; /* set the bit off */
925 }
926 }
927 }
928 flags = htol32(flags);
929 flags_mask = htol32(flags_mask);
930 /* setup flags_mask TLV */
931 ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
932 type, sizeof(uint32), &flags_mask, BCM_XTLV_OPTION_ALIGN32);
933 if (ret != BCME_OK) {
934 DHD_ERROR(("%s : bcm_pack_xltv_entry() for mask flags failed, status=%d\n",
935 __FUNCTION__, ret));
936 goto exit;
937 }
938
939 type = (session_id == WL_PROXD_SESSION_ID_GLOBAL)?
940 WL_PROXD_TLV_ID_FLAGS : WL_PROXD_TLV_ID_SESSION_FLAGS;
941 /* setup flags TLV */
942 ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
943 type, sizeof(uint32), &flags, BCM_XTLV_OPTION_ALIGN32);
944 if (ret != BCME_OK) {
945 #ifdef RTT_DEBUG
946 DHD_RTT(("%s: bcm_pack_xltv_entry() for flags failed, status=%d\n",
947 __FUNCTION__, ret));
948 #endif
949 }
950 exit:
951 return ret;
952 }
953
954 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)955 rtt_handle_config_general(wl_proxd_session_id_t session_id, wl_proxd_tlv_t **p_tlv,
956 uint16 *p_buf_space_left, ftm_config_param_info_t *ftm_configs, int ftm_cfg_cnt)
957 {
958 int ret = BCME_OK;
959 int cfg_idx = 0;
960 uint32 chanspec;
961 ftm_config_param_info_t *p_config_param_info;
962 void *p_src_data;
963 uint16 src_data_size; /* size of data pointed by p_src_data as 'source' */
964 for (cfg_idx = 0; cfg_idx < ftm_cfg_cnt; cfg_idx++) {
965 p_config_param_info = (ftm_configs + cfg_idx);
966 if (p_config_param_info != NULL) {
967 switch (p_config_param_info->tlvid) {
968 case WL_PROXD_TLV_ID_BSS_INDEX:
969 case WL_PROXD_TLV_ID_FTM_RETRIES:
970 case WL_PROXD_TLV_ID_FTM_REQ_RETRIES:
971 p_src_data = &p_config_param_info->data8;
972 src_data_size = sizeof(uint8);
973 break;
974 case WL_PROXD_TLV_ID_BURST_NUM_FTM: /* uint16 */
975 case WL_PROXD_TLV_ID_NUM_BURST:
976 case WL_PROXD_TLV_ID_RX_MAX_BURST:
977 p_src_data = &p_config_param_info->data16;
978 src_data_size = sizeof(uint16);
979 break;
980 case WL_PROXD_TLV_ID_TX_POWER: /* uint32 */
981 case WL_PROXD_TLV_ID_RATESPEC:
982 case WL_PROXD_TLV_ID_EVENT_MASK: /* wl_proxd_event_mask_t/uint32 */
983 case WL_PROXD_TLV_ID_DEBUG_MASK:
984 p_src_data = &p_config_param_info->data32;
985 src_data_size = sizeof(uint32);
986 break;
987 case WL_PROXD_TLV_ID_CHANSPEC: /* chanspec_t --> 32bit */
988 chanspec = p_config_param_info->chanspec;
989 p_src_data = (void *) &chanspec;
990 src_data_size = sizeof(uint32);
991 break;
992 case WL_PROXD_TLV_ID_BSSID: /* mac address */
993 case WL_PROXD_TLV_ID_PEER_MAC:
994 p_src_data = &p_config_param_info->mac_addr;
995 src_data_size = sizeof(struct ether_addr);
996 break;
997 case WL_PROXD_TLV_ID_BURST_DURATION: /* wl_proxd_intvl_t */
998 case WL_PROXD_TLV_ID_BURST_PERIOD:
999 case WL_PROXD_TLV_ID_BURST_FTM_SEP:
1000 case WL_PROXD_TLV_ID_BURST_TIMEOUT:
1001 case WL_PROXD_TLV_ID_INIT_DELAY:
1002 p_src_data = &p_config_param_info->data_intvl;
1003 src_data_size = sizeof(wl_proxd_intvl_t);
1004 break;
1005 default:
1006 ret = BCME_BADARG;
1007 break;
1008 }
1009 if (ret != BCME_OK) {
1010 DHD_ERROR(("%s bad TLV ID : %d\n",
1011 __FUNCTION__, p_config_param_info->tlvid));
1012 break;
1013 }
1014
1015 ret = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left,
1016 p_config_param_info->tlvid, src_data_size, p_src_data,
1017 BCM_XTLV_OPTION_ALIGN32);
1018 if (ret != BCME_OK) {
1019 DHD_ERROR(("%s: bcm_pack_xltv_entry() failed,"
1020 " status=%d\n", __FUNCTION__, ret));
1021 break;
1022 }
1023 }
1024 }
1025 return ret;
1026 }
1027
1028 static int
dhd_rtt_ftm_enable(dhd_pub_t * dhd,bool enable)1029 dhd_rtt_ftm_enable(dhd_pub_t *dhd, bool enable)
1030 {
1031 ftm_subcmd_info_t subcmd_info;
1032 subcmd_info.name = (enable)? "enable" : "disable";
1033 subcmd_info.cmdid = (enable)? WL_PROXD_CMD_ENABLE: WL_PROXD_CMD_DISABLE;
1034 subcmd_info.handler = NULL;
1035 return dhd_rtt_common_set_handler(dhd, &subcmd_info,
1036 WL_PROXD_METHOD_FTM, WL_PROXD_SESSION_ID_GLOBAL);
1037 }
1038
1039 static int
dhd_rtt_start_session(dhd_pub_t * dhd,wl_proxd_session_id_t session_id,bool start)1040 dhd_rtt_start_session(dhd_pub_t *dhd, wl_proxd_session_id_t session_id, bool start)
1041 {
1042 ftm_subcmd_info_t subcmd_info;
1043 subcmd_info.name = (start)? "start session" : "stop session";
1044 subcmd_info.cmdid = (start)? WL_PROXD_CMD_START_SESSION: WL_PROXD_CMD_STOP_SESSION;
1045 subcmd_info.handler = NULL;
1046 return dhd_rtt_common_set_handler(dhd, &subcmd_info,
1047 WL_PROXD_METHOD_FTM, session_id);
1048 }
1049
1050 static int
dhd_rtt_delete_session(dhd_pub_t * dhd,wl_proxd_session_id_t session_id)1051 dhd_rtt_delete_session(dhd_pub_t *dhd, wl_proxd_session_id_t session_id)
1052 {
1053 ftm_subcmd_info_t subcmd_info;
1054 subcmd_info.name = "delete session";
1055 subcmd_info.cmdid = WL_PROXD_CMD_DELETE_SESSION;
1056 subcmd_info.handler = NULL;
1057 return dhd_rtt_common_set_handler(dhd, &subcmd_info,
1058 WL_PROXD_METHOD_FTM, session_id);
1059 }
1060
1061 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)1062 dhd_rtt_ftm_config(dhd_pub_t *dhd, wl_proxd_session_id_t session_id,
1063 ftm_config_category_t catagory, void *ftm_configs, int ftm_cfg_cnt)
1064 {
1065 ftm_subcmd_info_t subcmd_info;
1066 wl_proxd_tlv_t *p_tlv;
1067 /* alloc mem for ioctl headr + reserved 0 bufsize for tlvs (initialize to zero) */
1068 wl_proxd_iov_t *p_proxd_iov;
1069 uint16 proxd_iovsize = 0;
1070 uint16 bufsize;
1071 uint16 buf_space_left;
1072 uint16 all_tlvsize;
1073 int ret = BCME_OK;
1074
1075 subcmd_info.name = "config";
1076 subcmd_info.cmdid = WL_PROXD_CMD_CONFIG;
1077
1078 p_proxd_iov = rtt_alloc_getset_buf(WL_PROXD_METHOD_FTM, session_id, subcmd_info.cmdid,
1079 FTM_IOC_BUFSZ, &proxd_iovsize);
1080
1081 if (p_proxd_iov == NULL) {
1082 DHD_ERROR(("%s : failed to allocate the iovar (size :%d)\n",
1083 __FUNCTION__, FTM_IOC_BUFSZ));
1084 return BCME_NOMEM;
1085 }
1086 /* setup TLVs */
1087 bufsize = proxd_iovsize - WL_PROXD_IOV_HDR_SIZE; /* adjust available size for TLVs */
1088 p_tlv = &p_proxd_iov->tlvs[0];
1089 /* TLV buffer starts with a full size, will decrement for each packed TLV */
1090 buf_space_left = bufsize;
1091 if (catagory == FTM_CONFIG_CAT_OPTIONS) {
1092 ret = rtt_handle_config_options(session_id, &p_tlv, &buf_space_left,
1093 (ftm_config_options_info_t *)ftm_configs, ftm_cfg_cnt);
1094 } else if (catagory == FTM_CONFIG_CAT_GENERAL) {
1095 ret = rtt_handle_config_general(session_id, &p_tlv, &buf_space_left,
1096 (ftm_config_param_info_t *)ftm_configs, ftm_cfg_cnt);
1097 }
1098 if (ret == BCME_OK) {
1099 /* update the iov header, set len to include all TLVs + header */
1100 all_tlvsize = (bufsize - buf_space_left);
1101 p_proxd_iov->len = htol16(all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
1102 ret = dhd_iovar(dhd, 0, "proxd", (char *)p_proxd_iov,
1103 all_tlvsize + WL_PROXD_IOV_HDR_SIZE, NULL, 0, TRUE);
1104 if (ret != BCME_OK) {
1105 DHD_ERROR(("%s : failed to set config\n", __FUNCTION__));
1106 }
1107 }
1108 /* clean up */
1109 kfree(p_proxd_iov);
1110 return ret;
1111 }
1112
1113 static int
dhd_rtt_get_version(dhd_pub_t * dhd,int * out_version)1114 dhd_rtt_get_version(dhd_pub_t *dhd, int *out_version)
1115 {
1116 int ret;
1117 ftm_subcmd_info_t subcmd_info;
1118 subcmd_info.name = "ver";
1119 subcmd_info.cmdid = WL_PROXD_CMD_GET_VERSION;
1120 subcmd_info.handler = NULL;
1121 ret = dhd_rtt_common_get_handler(dhd, &subcmd_info,
1122 WL_PROXD_METHOD_FTM, WL_PROXD_SESSION_ID_GLOBAL);
1123 *out_version = (ret == BCME_OK) ? subcmd_info.version : 0;
1124 return ret;
1125 }
1126 #endif /* WL_CFG80211 */
1127
1128 chanspec_t
dhd_rtt_convert_to_chspec(wifi_channel_info_t channel)1129 dhd_rtt_convert_to_chspec(wifi_channel_info_t channel)
1130 {
1131 int bw;
1132 chanspec_t chanspec = 0;
1133 uint8 center_chan;
1134 uint8 primary_chan;
1135 /* set witdh to 20MHZ for 2.4G HZ */
1136 if (channel.center_freq >= 2400 && channel.center_freq <= 2500) {
1137 channel.width = WIFI_CHAN_WIDTH_20;
1138 }
1139 switch (channel.width) {
1140 case WIFI_CHAN_WIDTH_20:
1141 bw = WL_CHANSPEC_BW_20;
1142 primary_chan = wf_mhz2channel(channel.center_freq, 0);
1143 chanspec = wf_channel2chspec(primary_chan, bw);
1144 break;
1145 case WIFI_CHAN_WIDTH_40:
1146 bw = WL_CHANSPEC_BW_40;
1147 primary_chan = wf_mhz2channel(channel.center_freq, 0);
1148 chanspec = wf_channel2chspec(primary_chan, bw);
1149 break;
1150 case WIFI_CHAN_WIDTH_80:
1151 bw = WL_CHANSPEC_BW_80;
1152 primary_chan = wf_mhz2channel(channel.center_freq, 0);
1153 center_chan = wf_mhz2channel(channel.center_freq0, 0);
1154 chanspec = wf_chspec_80(center_chan, primary_chan);
1155 break;
1156 default:
1157 DHD_ERROR(("doesn't support this bandwith : %d", channel.width));
1158 bw = -1;
1159 break;
1160 }
1161 return chanspec;
1162 }
1163
1164 int
dhd_rtt_idx_to_burst_duration(uint idx)1165 dhd_rtt_idx_to_burst_duration(uint idx)
1166 {
1167 if (idx >= ARRAY_SIZE(burst_duration_idx)) {
1168 return -1;
1169 }
1170 return burst_duration_idx[idx];
1171 }
1172
1173 int
dhd_rtt_set_cfg(dhd_pub_t * dhd,rtt_config_params_t * params)1174 dhd_rtt_set_cfg(dhd_pub_t *dhd, rtt_config_params_t *params)
1175 {
1176 int err = BCME_OK;
1177 int idx;
1178 rtt_status_info_t *rtt_status;
1179 NULL_CHECK(params, "params is NULL", err);
1180
1181 NULL_CHECK(dhd, "dhd is NULL", err);
1182 rtt_status = GET_RTTSTATE(dhd);
1183 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1184 if (!HAS_11MC_CAP(rtt_status->rtt_capa.proto)) {
1185 DHD_ERROR(("doesn't support RTT \n"));
1186 return BCME_ERROR;
1187 }
1188 if (rtt_status->status != RTT_STOPPED) {
1189 DHD_ERROR(("rtt is already started\n"));
1190 return BCME_BUSY;
1191 }
1192 DHD_RTT(("%s enter\n", __FUNCTION__));
1193
1194 memset(rtt_status->rtt_config.target_info, 0, TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
1195 rtt_status->rtt_config.rtt_target_cnt = params->rtt_target_cnt;
1196 memcpy(rtt_status->rtt_config.target_info,
1197 params->target_info, TARGET_INFO_SIZE(params->rtt_target_cnt));
1198 rtt_status->status = RTT_STARTED;
1199 /* start to measure RTT from first device */
1200 /* find next target to trigger RTT */
1201 for (idx = rtt_status->cur_idx; idx < rtt_status->rtt_config.rtt_target_cnt; idx++) {
1202 /* skip the disabled device */
1203 if (rtt_status->rtt_config.target_info[idx].disable) {
1204 continue;
1205 } else {
1206 /* set the idx to cur_idx */
1207 rtt_status->cur_idx = idx;
1208 break;
1209 }
1210 }
1211 if (idx < rtt_status->rtt_config.rtt_target_cnt) {
1212 DHD_RTT(("rtt_status->cur_idx : %d\n", rtt_status->cur_idx));
1213 schedule_work(&rtt_status->work);
1214 }
1215 return err;
1216 }
1217
1218 int
dhd_rtt_stop(dhd_pub_t * dhd,struct ether_addr * mac_list,int mac_cnt)1219 dhd_rtt_stop(dhd_pub_t *dhd, struct ether_addr *mac_list, int mac_cnt)
1220 {
1221 int err = BCME_OK;
1222 #ifdef WL_CFG8011
1223 int i = 0, j = 0;
1224 rtt_status_info_t *rtt_status;
1225 rtt_results_header_t *entry, *next;
1226 rtt_result_t *rtt_result, *next2;
1227 struct rtt_noti_callback *iter;
1228
1229 NULL_CHECK(dhd, "dhd is NULL", err);
1230 rtt_status = GET_RTTSTATE(dhd);
1231 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1232 if (rtt_status->status == RTT_STOPPED) {
1233 DHD_ERROR(("rtt is not started\n"));
1234 return BCME_OK;
1235 }
1236 DHD_RTT(("%s enter\n", __FUNCTION__));
1237 mutex_lock(&rtt_status->rtt_mutex);
1238 for (i = 0; i < mac_cnt; i++) {
1239 for (j = 0; j < rtt_status->rtt_config.rtt_target_cnt; j++) {
1240 if (!bcmp(&mac_list[i], &rtt_status->rtt_config.target_info[j].addr,
1241 ETHER_ADDR_LEN)) {
1242 rtt_status->rtt_config.target_info[j].disable = TRUE;
1243 }
1244 }
1245 }
1246 if (rtt_status->all_cancel) {
1247 /* cancel all of request */
1248 rtt_status->status = RTT_STOPPED;
1249 DHD_RTT(("current RTT process is cancelled\n"));
1250 /* remove the rtt results in cache */
1251 if (!list_empty(&rtt_status->rtt_results_cache)) {
1252 /* Iterate rtt_results_header list */
1253 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1254 #pragma GCC diagnostic push
1255 #pragma GCC diagnostic ignored "-Wcast-qual"
1256 #endif
1257 list_for_each_entry_safe(entry, next,
1258 &rtt_status->rtt_results_cache, list) {
1259 list_del(&entry->list);
1260 /* Iterate rtt_result list */
1261 list_for_each_entry_safe(rtt_result, next2,
1262 &entry->result_list, list) {
1263 list_del(&rtt_result->list);
1264 kfree(rtt_result);
1265 }
1266 kfree(entry);
1267 }
1268 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1269 #pragma GCC diagnostic pop
1270 #endif
1271 }
1272 /* send the rtt complete event to wake up the user process */
1273 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1274 #pragma GCC diagnostic push
1275 #pragma GCC diagnostic ignored "-Wcast-qual"
1276 #endif
1277 list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
1278 iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache);
1279 }
1280 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1281 #pragma GCC diagnostic pop
1282 #endif
1283 /* reinitialize the HEAD */
1284 INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
1285 /* clear information for rtt_config */
1286 rtt_status->rtt_config.rtt_target_cnt = 0;
1287 memset(rtt_status->rtt_config.target_info, 0,
1288 TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
1289 rtt_status->cur_idx = 0;
1290 dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION);
1291 dhd_rtt_ftm_enable(dhd, FALSE);
1292 }
1293 mutex_unlock(&rtt_status->rtt_mutex);
1294 #endif /* WL_CFG80211 */
1295 return err;
1296 }
1297
1298
1299 #ifdef WL_CFG80211
1300 static int
dhd_rtt_start(dhd_pub_t * dhd)1301 dhd_rtt_start(dhd_pub_t *dhd)
1302 {
1303 int err = BCME_OK;
1304 char eabuf[ETHER_ADDR_STR_LEN];
1305 char chanbuf[CHANSPEC_STR_LEN];
1306 int ftm_cfg_cnt = 0;
1307 int ftm_param_cnt = 0;
1308 uint32 rspec = 0;
1309 ftm_config_options_info_t ftm_configs[FTM_MAX_CONFIGS];
1310 ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS];
1311 rtt_target_info_t *rtt_target;
1312 rtt_status_info_t *rtt_status;
1313 int pm = PM_OFF;
1314 struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
1315 NULL_CHECK(dhd, "dhd is NULL", err);
1316
1317 rtt_status = GET_RTTSTATE(dhd);
1318 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1319
1320 DHD_RTT(("Enter %s\n", __FUNCTION__));
1321 if (rtt_status->cur_idx >= rtt_status->rtt_config.rtt_target_cnt) {
1322 err = BCME_RANGE;
1323 DHD_RTT(("%s : idx %d is out of range\n", __FUNCTION__, rtt_status->cur_idx));
1324 if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) {
1325 DHD_ERROR(("STA is set as Target/Responder \n"));
1326 return BCME_ERROR;
1327 }
1328 goto exit;
1329 }
1330 if (RTT_IS_STOPPED(rtt_status)) {
1331 DHD_RTT(("RTT is stopped\n"));
1332 goto exit;
1333 }
1334 err = wldev_ioctl_get(dev, WLC_GET_PM, &rtt_status->pm, sizeof(rtt_status->pm));
1335 if (err) {
1336 DHD_ERROR(("Failed to get the PM value\n"));
1337 } else {
1338 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
1339 if (err) {
1340 DHD_ERROR(("Failed to set the PM\n"));
1341 rtt_status->pm_restore = FALSE;
1342 } else {
1343 rtt_status->pm_restore = TRUE;
1344 }
1345 }
1346
1347 mutex_lock(&rtt_status->rtt_mutex);
1348 /* Get a target information */
1349 rtt_target = &rtt_status->rtt_config.target_info[rtt_status->cur_idx];
1350 mutex_unlock(&rtt_status->rtt_mutex);
1351 DHD_RTT(("%s enter\n", __FUNCTION__));
1352 if (!RTT_IS_ENABLED(rtt_status)) {
1353 /* enable ftm */
1354 err = dhd_rtt_ftm_enable(dhd, TRUE);
1355 if (err) {
1356 DHD_ERROR(("failed to enable FTM (%d)\n", err));
1357 goto exit;
1358 }
1359 }
1360
1361 /* delete session of index default sesession */
1362 err = dhd_rtt_delete_session(dhd, FTM_DEFAULT_SESSION);
1363 if (err < 0 && err != BCME_NOTFOUND) {
1364 DHD_ERROR(("failed to delete session of FTM (%d)\n", err));
1365 goto exit;
1366 }
1367 rtt_status->status = RTT_ENABLED;
1368 memset(ftm_configs, 0, sizeof(ftm_configs));
1369 memset(ftm_params, 0, sizeof(ftm_params));
1370
1371 /* configure the session 1 as initiator */
1372 ftm_configs[ftm_cfg_cnt].enable = TRUE;
1373 ftm_configs[ftm_cfg_cnt++].flags = WL_PROXD_SESSION_FLAG_INITIATOR;
1374 dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_OPTIONS,
1375 ftm_configs, ftm_cfg_cnt);
1376 /* target's mac address */
1377 if (!ETHER_ISNULLADDR(rtt_target->addr.octet)) {
1378 ftm_params[ftm_param_cnt].mac_addr = rtt_target->addr;
1379 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_PEER_MAC;
1380 bcm_ether_ntoa(&rtt_target->addr, eabuf);
1381 DHD_RTT((">\t target %s\n", eabuf));
1382 }
1383 /* target's chanspec */
1384 if (rtt_target->chanspec) {
1385 ftm_params[ftm_param_cnt].chanspec = htol32((uint32)rtt_target->chanspec);
1386 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_CHANSPEC;
1387 wf_chspec_ntoa(rtt_target->chanspec, chanbuf);
1388 DHD_RTT((">\t chanspec : %s\n", chanbuf));
1389 }
1390 /* num-burst */
1391 if (rtt_target->num_burst) {
1392 ftm_params[ftm_param_cnt].data16 = htol16(rtt_target->num_burst);
1393 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_NUM_BURST;
1394 DHD_RTT((">\t num of burst : %d\n", rtt_target->num_burst));
1395 }
1396 /* number of frame per burst */
1397 if (rtt_target->num_frames_per_burst == 0) {
1398 rtt_target->num_frames_per_burst =
1399 CHSPEC_IS20(rtt_target->chanspec) ? FTM_DEFAULT_CNT_20M :
1400 CHSPEC_IS40(rtt_target->chanspec) ? FTM_DEFAULT_CNT_40M :
1401 FTM_DEFAULT_CNT_80M;
1402 }
1403 ftm_params[ftm_param_cnt].data16 = htol16(rtt_target->num_frames_per_burst);
1404 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_NUM_FTM;
1405 DHD_RTT((">\t number of frame per burst : %d\n", rtt_target->num_frames_per_burst));
1406 /* FTM retry count */
1407 if (rtt_target->num_retries_per_ftm) {
1408 ftm_params[ftm_param_cnt].data8 = rtt_target->num_retries_per_ftm;
1409 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_FTM_RETRIES;
1410 DHD_RTT((">\t retry count of FTM : %d\n", rtt_target->num_retries_per_ftm));
1411 }
1412 /* FTM Request retry count */
1413 if (rtt_target->num_retries_per_ftmr) {
1414 ftm_params[ftm_param_cnt].data8 = rtt_target->num_retries_per_ftmr;
1415 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_FTM_REQ_RETRIES;
1416 DHD_RTT((">\t retry count of FTM Req : %d\n", rtt_target->num_retries_per_ftmr));
1417 }
1418 /* burst-period */
1419 if (rtt_target->burst_period) {
1420 ftm_params[ftm_param_cnt].data_intvl.intvl =
1421 htol32(rtt_target->burst_period); /* ms */
1422 ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC;
1423 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_PERIOD;
1424 DHD_RTT((">\t burst period : %d ms\n", rtt_target->burst_period));
1425 }
1426 /* burst-duration */
1427 if (rtt_target->burst_duration) {
1428 ftm_params[ftm_param_cnt].data_intvl.intvl =
1429 htol32(rtt_target->burst_duration); /* ms */
1430 ftm_params[ftm_param_cnt].data_intvl.tmu = WL_PROXD_TMU_MILLI_SEC;
1431 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_BURST_DURATION;
1432 DHD_RTT((">\t burst duration : %d ms\n",
1433 rtt_target->burst_duration));
1434 }
1435 if (rtt_target->bw && rtt_target->preamble) {
1436 bool use_default = FALSE;
1437 int nss;
1438 int mcs;
1439 switch (rtt_target->preamble) {
1440 case RTT_PREAMBLE_LEGACY:
1441 rspec |= WL_RSPEC_ENCODE_RATE; /* 11abg */
1442 rspec |= WL_RATE_6M;
1443 break;
1444 case RTT_PREAMBLE_HT:
1445 rspec |= WL_RSPEC_ENCODE_HT; /* 11n HT */
1446 mcs = 0; /* default MCS 0 */
1447 rspec |= mcs;
1448 break;
1449 case RTT_PREAMBLE_VHT:
1450 rspec |= WL_RSPEC_ENCODE_VHT; /* 11ac VHT */
1451 mcs = 0; /* default MCS 0 */
1452 nss = 1; /* default Nss = 1 */
1453 rspec |= (nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs;
1454 break;
1455 default:
1456 DHD_RTT(("doesn't support this preamble : %d\n", rtt_target->preamble));
1457 use_default = TRUE;
1458 break;
1459 }
1460 switch (rtt_target->bw) {
1461 case RTT_BW_20:
1462 rspec |= WL_RSPEC_BW_20MHZ;
1463 break;
1464 case RTT_BW_40:
1465 rspec |= WL_RSPEC_BW_40MHZ;
1466 break;
1467 case RTT_BW_80:
1468 rspec |= WL_RSPEC_BW_80MHZ;
1469 break;
1470 default:
1471 DHD_RTT(("doesn't support this BW : %d\n", rtt_target->bw));
1472 use_default = TRUE;
1473 break;
1474 }
1475 if (!use_default) {
1476 ftm_params[ftm_param_cnt].data32 = htol32(rspec);
1477 ftm_params[ftm_param_cnt++].tlvid = WL_PROXD_TLV_ID_RATESPEC;
1478 DHD_RTT((">\t ratespec : %d\n", rspec));
1479 }
1480 }
1481 dhd_set_rand_mac_oui(dhd);
1482 dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_GENERAL,
1483 ftm_params, ftm_param_cnt);
1484
1485 err = dhd_rtt_start_session(dhd, FTM_DEFAULT_SESSION, TRUE);
1486 if (err) {
1487 DHD_ERROR(("failed to start session of FTM : error %d\n", err));
1488 }
1489 exit:
1490 if (err) {
1491 DHD_ERROR(("rtt is stopped %s \n", __FUNCTION__));
1492 rtt_status->status = RTT_STOPPED;
1493 /* disable FTM */
1494 dhd_rtt_ftm_enable(dhd, FALSE);
1495 if (rtt_status->pm_restore) {
1496 DHD_ERROR(("pm_restore =%d func =%s \n",
1497 rtt_status->pm_restore, __FUNCTION__));
1498 pm = PM_FAST;
1499 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
1500 if (err) {
1501 DHD_ERROR(("Failed to set PM \n"));
1502 } else {
1503 rtt_status->pm_restore = FALSE;
1504 }
1505 }
1506 }
1507 return err;
1508 }
1509 #endif /* WL_CFG80211 */
1510
1511 int
dhd_rtt_register_noti_callback(dhd_pub_t * dhd,void * ctx,dhd_rtt_compl_noti_fn noti_fn)1512 dhd_rtt_register_noti_callback(dhd_pub_t *dhd, void *ctx, dhd_rtt_compl_noti_fn noti_fn)
1513 {
1514 int err = BCME_OK;
1515 struct rtt_noti_callback *cb = NULL, *iter;
1516 rtt_status_info_t *rtt_status;
1517 NULL_CHECK(dhd, "dhd is NULL", err);
1518 NULL_CHECK(noti_fn, "noti_fn is NULL", err);
1519
1520 rtt_status = GET_RTTSTATE(dhd);
1521 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1522 spin_lock_bh(¬i_list_lock);
1523 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1524 #pragma GCC diagnostic push
1525 #pragma GCC diagnostic ignored "-Wcast-qual"
1526 #endif
1527 list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
1528 if (iter->noti_fn == noti_fn) {
1529 goto exit;
1530 }
1531 }
1532 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1533 #pragma GCC diagnostic pop
1534 #endif
1535 cb = kmalloc(sizeof(struct rtt_noti_callback), GFP_ATOMIC);
1536 if (!cb) {
1537 err = -ENOMEM;
1538 goto exit;
1539 }
1540 cb->noti_fn = noti_fn;
1541 cb->ctx = ctx;
1542 list_add(&cb->list, &rtt_status->noti_fn_list);
1543 exit:
1544 spin_unlock_bh(¬i_list_lock);
1545 return err;
1546 }
1547
1548 int
dhd_rtt_unregister_noti_callback(dhd_pub_t * dhd,dhd_rtt_compl_noti_fn noti_fn)1549 dhd_rtt_unregister_noti_callback(dhd_pub_t *dhd, dhd_rtt_compl_noti_fn noti_fn)
1550 {
1551 int err = BCME_OK;
1552 struct rtt_noti_callback *cb = NULL, *iter;
1553 rtt_status_info_t *rtt_status;
1554 NULL_CHECK(dhd, "dhd is NULL", err);
1555 NULL_CHECK(noti_fn, "noti_fn is NULL", err);
1556 rtt_status = GET_RTTSTATE(dhd);
1557 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1558 spin_lock_bh(¬i_list_lock);
1559 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1560 #pragma GCC diagnostic push
1561 #pragma GCC diagnostic ignored "-Wcast-qual"
1562 #endif
1563 list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
1564 if (iter->noti_fn == noti_fn) {
1565 cb = iter;
1566 list_del(&cb->list);
1567 break;
1568 }
1569 }
1570 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1571 #pragma GCC diagnostic pop
1572 #endif
1573
1574 spin_unlock_bh(¬i_list_lock);
1575 if (cb) {
1576 kfree(cb);
1577 }
1578 return err;
1579 }
1580
1581 static wifi_rate_t
dhd_rtt_convert_rate_to_host(uint32 rspec)1582 dhd_rtt_convert_rate_to_host(uint32 rspec)
1583 {
1584 wifi_rate_t host_rate;
1585 memset(&host_rate, 0, sizeof(wifi_rate_t));
1586 if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE) {
1587 host_rate.preamble = 0;
1588 } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) {
1589 host_rate.preamble = 2;
1590 host_rate.rateMcsIdx = rspec & WL_RSPEC_RATE_MASK;
1591 } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT) {
1592 host_rate.preamble = 3;
1593 host_rate.rateMcsIdx = rspec & WL_RSPEC_VHT_MCS_MASK;
1594 host_rate.nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
1595 }
1596 host_rate.bw = (rspec & WL_RSPEC_BW_MASK) - 1;
1597 host_rate.bitrate = rate_rspec2rate(rspec) / 100; /* 100kbps */
1598 DHD_RTT(("bit rate : %d\n", host_rate.bitrate));
1599 return host_rate;
1600 }
1601
1602
1603 static int
dhd_rtt_convert_results_to_host(rtt_report_t * rtt_report,uint8 * p_data,uint16 tlvid,uint16 len)1604 dhd_rtt_convert_results_to_host(rtt_report_t *rtt_report, uint8 *p_data, uint16 tlvid, uint16 len)
1605 {
1606 int err = BCME_OK;
1607 char eabuf[ETHER_ADDR_STR_LEN];
1608 wl_proxd_rtt_result_t *p_data_info;
1609 wl_proxd_result_flags_t flags;
1610 wl_proxd_session_state_t session_state;
1611 wl_proxd_status_t proxd_status;
1612 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
1613 struct timespec64 ts;
1614 #endif /* LINUX_VER >= 2.6.39 */
1615 uint32 ratespec;
1616 uint32 avg_dist;
1617 wl_proxd_rtt_sample_t *p_sample;
1618 wl_proxd_intvl_t rtt;
1619 wl_proxd_intvl_t p_time;
1620
1621 NULL_CHECK(rtt_report, "rtt_report is NULL", err);
1622 NULL_CHECK(p_data, "p_data is NULL", err);
1623 DHD_RTT(("%s enter\n", __FUNCTION__));
1624 p_data_info = (wl_proxd_rtt_result_t *) p_data;
1625 /* unpack and format 'flags' for display */
1626 flags = ltoh16_ua(&p_data_info->flags);
1627
1628 /* session state and status */
1629 session_state = ltoh16_ua(&p_data_info->state);
1630 proxd_status = ltoh32_ua(&p_data_info->status);
1631 bcm_ether_ntoa((&(p_data_info->peer)), eabuf);
1632 ftm_session_state_value_to_logstr(session_state);
1633 ftm_status_value_to_logstr(proxd_status);
1634 DHD_RTT((">\tTarget(%s) session state=%d(%s), status=%d(%s)\n",
1635 eabuf,
1636 session_state,
1637 ftm_session_state_value_to_logstr(session_state),
1638 proxd_status,
1639 ftm_status_value_to_logstr(proxd_status)));
1640
1641 /* show avg_dist (1/256m units), burst_num */
1642 avg_dist = ltoh32_ua(&p_data_info->avg_dist);
1643 if (avg_dist == 0xffffffff) { /* report 'failure' case */
1644 DHD_RTT((">\tavg_dist=-1m, burst_num=%d, valid_measure_cnt=%d\n",
1645 ltoh16_ua(&p_data_info->burst_num),
1646 p_data_info->num_valid_rtt)); /* in a session */
1647 avg_dist = FTM_INVALID;
1648 }
1649 else {
1650 DHD_RTT((">\tavg_dist=%d.%04dm, burst_num=%d, valid_measure_cnt=%d num_ftm=%d\n",
1651 avg_dist >> 8, /* 1/256m units */
1652 ((avg_dist & 0xff) * 625) >> 4,
1653 ltoh16_ua(&p_data_info->burst_num),
1654 p_data_info->num_valid_rtt,
1655 p_data_info->num_ftm)); /* in a session */
1656 }
1657 /* show 'avg_rtt' sample */
1658 p_sample = &p_data_info->avg_rtt;
1659 ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu));
1660 DHD_RTT((">\tavg_rtt sample: rssi=%d rtt=%d%s std_deviation =%d.%d ratespec=0x%08x\n",
1661 (int16) ltoh16_ua(&p_sample->rssi),
1662 ltoh32_ua(&p_sample->rtt.intvl),
1663 ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)),
1664 ltoh16_ua(&p_data_info->sd_rtt)/10, ltoh16_ua(&p_data_info->sd_rtt)%10,
1665 ltoh32_ua(&p_sample->ratespec)));
1666
1667 /* set peer address */
1668 rtt_report->addr = p_data_info->peer;
1669 /* burst num */
1670 rtt_report->burst_num = ltoh16_ua(&p_data_info->burst_num);
1671 /* success num */
1672 rtt_report->success_num = p_data_info->num_valid_rtt;
1673 /* actual number of FTM supported by peer */
1674 rtt_report->num_per_burst_peer = p_data_info->num_ftm;
1675 rtt_report->negotiated_burst_num = p_data_info->num_ftm;
1676 /* status */
1677 rtt_report->status = ftm_get_statusmap_info(proxd_status,
1678 &ftm_status_map_info[0], ARRAYSIZE(ftm_status_map_info));
1679
1680 /* rssi (0.5db) */
1681 rtt_report->rssi = ABS((wl_proxd_rssi_t)ltoh16_ua(&p_data_info->avg_rtt.rssi)) * 2;
1682
1683 /* rx rate */
1684 ratespec = ltoh32_ua(&p_data_info->avg_rtt.ratespec);
1685 rtt_report->rx_rate = dhd_rtt_convert_rate_to_host(ratespec);
1686 /* tx rate */
1687 if (flags & WL_PROXD_RESULT_FLAG_VHTACK) {
1688 rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0x2010010);
1689 } else {
1690 rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0xc);
1691 }
1692 /* rtt_sd */
1693 rtt.tmu = ltoh16_ua(&p_data_info->avg_rtt.rtt.tmu);
1694 rtt.intvl = ltoh32_ua(&p_data_info->avg_rtt.rtt.intvl);
1695 rtt_report->rtt = (wifi_timespan)FTM_INTVL2NSEC(&rtt) * 1000; /* nano -> pico seconds */
1696 rtt_report->rtt_sd = ltoh16_ua(&p_data_info->sd_rtt); /* nano -> 0.1 nano */
1697 DHD_RTT(("rtt_report->rtt : %llu\n", rtt_report->rtt));
1698 DHD_RTT(("rtt_report->rssi : %d (0.5db)\n", rtt_report->rssi));
1699
1700 /* average distance */
1701 if (avg_dist != FTM_INVALID) {
1702 rtt_report->distance = (avg_dist >> 8) * 1000; /* meter -> mm */
1703 rtt_report->distance += (avg_dist & 0xff) * 1000 / 256;
1704 } else {
1705 rtt_report->distance = FTM_INVALID;
1706 }
1707 /* time stamp */
1708 /* get the time elapsed from boot time */
1709 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
1710 ts = ktime_to_timespec64(ktime_get_boottime());
1711 rtt_report->ts = (uint64)TIMESPEC_TO_US(ts);
1712 #endif /* LINUX_VER >= 2.6.39 */
1713
1714 if (proxd_status == WL_PROXD_E_REMOTE_FAIL) {
1715 /* retry time after failure */
1716 p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
1717 p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
1718 rtt_report->retry_after_duration = FTM_INTVL2SEC(&p_time); /* s -> s */
1719 DHD_RTT((">\tretry_after: %d%s\n",
1720 ltoh32_ua(&p_data_info->u.retry_after.intvl),
1721 ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.retry_after.tmu))));
1722 } else {
1723 /* burst duration */
1724 p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
1725 p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
1726 rtt_report->burst_duration = FTM_INTVL2MSEC(&p_time); /* s -> ms */
1727 DHD_RTT((">\tburst_duration: %d%s\n",
1728 ltoh32_ua(&p_data_info->u.burst_duration.intvl),
1729 ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.burst_duration.tmu))));
1730 DHD_RTT(("rtt_report->burst_duration : %d\n", rtt_report->burst_duration));
1731 }
1732 return err;
1733 }
1734
1735 int
dhd_rtt_event_handler(dhd_pub_t * dhd,wl_event_msg_t * event,void * event_data)1736 dhd_rtt_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
1737 {
1738 int ret = BCME_OK;
1739 int tlvs_len;
1740 uint16 version;
1741 wl_proxd_event_t *p_event;
1742 wl_proxd_event_type_t event_type;
1743 wl_proxd_ftm_session_status_t session_status;
1744 wl_proxd_collect_event_data_t *collect_event_data;
1745 const ftm_strmap_entry_t *p_loginfo;
1746 rtt_result_t *rtt_result;
1747 gfp_t kflags;
1748 #ifdef WL_CFG80211
1749 int idx;
1750 struct rtt_noti_callback *iter;
1751 bool is_new = TRUE;
1752 rtt_status_info_t *rtt_status;
1753 rtt_result_t *next2;
1754 rtt_results_header_t *next = NULL;
1755 rtt_target_info_t *rtt_target_info;
1756 rtt_results_header_t *entry, *rtt_results_header = NULL;
1757 #endif /* WL_CFG80211 */
1758
1759 DHD_RTT(("Enter %s \n", __FUNCTION__));
1760 NULL_CHECK(dhd, "dhd is NULL", ret);
1761
1762 #ifdef WL_CFG80211
1763 rtt_status = GET_RTTSTATE(dhd);
1764 NULL_CHECK(rtt_status, "rtt_status is NULL", ret);
1765
1766 if (RTT_IS_STOPPED(rtt_status)) {
1767 /* Ignore the Proxd event */
1768 DHD_RTT((" event handler rtt is stopped \n"));
1769 if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) {
1770 DHD_RTT(("Device is target/Responder. Recv the event. \n"));
1771 } else {
1772 return ret;
1773 }
1774 }
1775 #endif /* WL_CFG80211 */
1776 if (ntoh32_ua((void *)&event->datalen) < OFFSETOF(wl_proxd_event_t, tlvs)) {
1777 DHD_RTT(("%s: wrong datalen:%d\n", __FUNCTION__,
1778 ntoh32_ua((void *)&event->datalen)));
1779 return -EINVAL;
1780 }
1781 event_type = ntoh32_ua((void *)&event->event_type);
1782 if (event_type != WLC_E_PROXD) {
1783 DHD_ERROR((" failed event \n"));
1784 return -EINVAL;
1785 }
1786
1787 if (!event_data) {
1788 DHD_ERROR(("%s: event_data:NULL\n", __FUNCTION__));
1789 return -EINVAL;
1790 }
1791 p_event = (wl_proxd_event_t *) event_data;
1792 version = ltoh16(p_event->version);
1793 if (version < WL_PROXD_API_VERSION) {
1794 DHD_ERROR(("ignore non-ftm event version = 0x%0x < WL_PROXD_API_VERSION (0x%x)\n",
1795 version, WL_PROXD_API_VERSION));
1796 return ret;
1797 }
1798 #ifdef WL_CFG80211
1799 if (!in_atomic()) {
1800 mutex_lock(&rtt_status->rtt_mutex);
1801 }
1802 #endif /* WL_CFG80211 */
1803 event_type = (wl_proxd_event_type_t) ltoh16(p_event->type);
1804
1805 kflags = in_softirq()? GFP_ATOMIC : GFP_KERNEL;
1806
1807 DHD_RTT(("event_type=0x%x, ntoh16()=0x%x, ltoh16()=0x%x\n",
1808 p_event->type, ntoh16(p_event->type), ltoh16(p_event->type)));
1809 p_loginfo = ftm_get_event_type_loginfo(event_type);
1810 if (p_loginfo == NULL) {
1811 DHD_ERROR(("receive an invalid FTM event %d\n", event_type));
1812 ret = -EINVAL;
1813 goto exit; /* ignore this event */
1814 }
1815 /* get TLVs len, skip over event header */
1816 if (ltoh16(p_event->len) < OFFSETOF(wl_proxd_event_t, tlvs)) {
1817 DHD_ERROR(("invalid FTM event length:%d\n", ltoh16(p_event->len)));
1818 ret = -EINVAL;
1819 goto exit;
1820 }
1821 tlvs_len = ltoh16(p_event->len) - OFFSETOF(wl_proxd_event_t, tlvs);
1822 DHD_RTT(("receive '%s' event: version=0x%x len=%d method=%d sid=%d tlvs_len=%d\n",
1823 p_loginfo->text,
1824 version,
1825 ltoh16(p_event->len),
1826 ltoh16(p_event->method),
1827 ltoh16(p_event->sid),
1828 tlvs_len));
1829 #ifdef WL_CFG80211
1830 rtt_target_info = &rtt_status->rtt_config.target_info[rtt_status->cur_idx];
1831 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1832 #pragma GCC diagnostic push
1833 #pragma GCC diagnostic ignored "-Wcast-qual"
1834 #endif
1835 /* find a rtt_report_header for this mac address */
1836 list_for_each_entry(entry, &rtt_status->rtt_results_cache, list) {
1837 if (!memcmp(&entry->peer_mac, &event->addr, ETHER_ADDR_LEN)) {
1838 /* found a rtt_report_header for peer_mac in the list */
1839 is_new = FALSE;
1840 rtt_results_header = entry;
1841 break;
1842 }
1843 }
1844 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1845 #pragma GCC diagnostic pop
1846 #endif
1847 #endif /* WL_CFG80211 */
1848 switch (event_type) {
1849 case WL_PROXD_EVENT_SESSION_CREATE:
1850 DHD_RTT(("WL_PROXD_EVENT_SESSION_CREATE\n"));
1851 break;
1852 case WL_PROXD_EVENT_SESSION_START:
1853 DHD_RTT(("WL_PROXD_EVENT_SESSION_START\n"));
1854 break;
1855 case WL_PROXD_EVENT_BURST_START:
1856 DHD_RTT(("WL_PROXD_EVENT_BURST_START\n"));
1857 break;
1858 case WL_PROXD_EVENT_BURST_END:
1859 DHD_RTT(("WL_PROXD_EVENT_BURST_END\n"));
1860 #ifdef WL_CFG80211
1861 if (is_new) {
1862 /* allocate new header for rtt_results */
1863 rtt_results_header = kzalloc(sizeof(rtt_results_header_t), kflags);
1864 if (!rtt_results_header) {
1865 ret = -ENOMEM;
1866 goto exit;
1867 }
1868 /* Initialize the head of list for rtt result */
1869 INIT_LIST_HEAD(&rtt_results_header->result_list);
1870 rtt_results_header->peer_mac = event->addr;
1871 list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache);
1872 }
1873 #endif /* WL_CFG80211 */
1874 if (tlvs_len > 0) {
1875 /* allocate rtt_results for new results */
1876 rtt_result = kzalloc(sizeof(rtt_result_t), kflags);
1877 if (!rtt_result) {
1878 ret = -ENOMEM;
1879 goto exit;
1880 }
1881 /* unpack TLVs and invokes the cbfn to print the event content TLVs */
1882 ret = bcm_unpack_xtlv_buf((void *) &(rtt_result->report),
1883 (uint8 *)&p_event->tlvs[0], tlvs_len,
1884 BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn);
1885 if (ret != BCME_OK) {
1886 DHD_ERROR(("%s : Failed to unpack xtlv for an event\n",
1887 __FUNCTION__));
1888 goto exit;
1889 }
1890 #ifdef WL_CFG80211
1891 /* fill out the results from the configuration param */
1892 rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst;
1893 rtt_result->report.type = RTT_TWO_WAY;
1894 DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num));
1895 rtt_result->report_len = RTT_REPORT_SIZE;
1896
1897 list_add_tail(&rtt_result->list, &rtt_results_header->result_list);
1898 rtt_results_header->result_cnt++;
1899 rtt_results_header->result_tot_len += rtt_result->report_len;
1900 #endif /* WL_CFG80211 */
1901 }
1902 break;
1903 case WL_PROXD_EVENT_SESSION_END:
1904 DHD_RTT(("WL_PROXD_EVENT_SESSION_END\n"));
1905 #ifdef WL_CFG80211
1906 if (!RTT_IS_ENABLED(rtt_status)) {
1907 DHD_RTT(("Ignore the session end evt\n"));
1908 goto exit;
1909 }
1910 #endif /* WL_CFG80211 */
1911 if (tlvs_len > 0) {
1912 /* unpack TLVs and invokes the cbfn to print the event content TLVs */
1913 ret = bcm_unpack_xtlv_buf((void *) &session_status,
1914 (uint8 *)&p_event->tlvs[0], tlvs_len,
1915 BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn);
1916 if (ret != BCME_OK) {
1917 DHD_ERROR(("%s : Failed to unpack xtlv for an event\n",
1918 __FUNCTION__));
1919 goto exit;
1920 }
1921 }
1922 #ifdef WL_CFG80211
1923 /* In case of no result for the peer device, make fake result for error case */
1924 if (is_new) {
1925 /* allocate new header for rtt_results */
1926 rtt_results_header = kzalloc(sizeof(rtt_results_header_t), GFP_KERNEL);
1927 if (!rtt_results_header) {
1928 ret = -ENOMEM;
1929 goto exit;
1930 }
1931 /* Initialize the head of list for rtt result */
1932 INIT_LIST_HEAD(&rtt_results_header->result_list);
1933 rtt_results_header->peer_mac = event->addr;
1934 list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache);
1935
1936 /* allocate rtt_results for new results */
1937 rtt_result = kzalloc(sizeof(rtt_result_t), kflags);
1938 if (!rtt_result) {
1939 ret = -ENOMEM;
1940 kfree(rtt_results_header);
1941 goto exit;
1942 }
1943 /* fill out the results from the configuration param */
1944 rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst;
1945 rtt_result->report.type = RTT_TWO_WAY;
1946 DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num));
1947 rtt_result->report_len = RTT_REPORT_SIZE;
1948 rtt_result->report.status = RTT_REASON_FAIL_NO_RSP;
1949 rtt_result->report.addr = rtt_target_info->addr;
1950 rtt_result->report.distance = FTM_INVALID;
1951 list_add_tail(&rtt_result->list, &rtt_results_header->result_list);
1952 rtt_results_header->result_cnt++;
1953 rtt_results_header->result_tot_len += rtt_result->report_len;
1954 }
1955 /* find next target to trigger RTT */
1956 for (idx = (rtt_status->cur_idx + 1);
1957 idx < rtt_status->rtt_config.rtt_target_cnt; idx++) {
1958 /* skip the disabled device */
1959 if (rtt_status->rtt_config.target_info[idx].disable) {
1960 continue;
1961 } else {
1962 /* set the idx to cur_idx */
1963 rtt_status->cur_idx = idx;
1964 break;
1965 }
1966 }
1967 if (idx < rtt_status->rtt_config.rtt_target_cnt) {
1968 /* restart to measure RTT from next device */
1969 DHD_ERROR(("restart to measure rtt\n"));
1970 schedule_work(&rtt_status->work);
1971 } else {
1972 DHD_RTT(("RTT_STOPPED\n"));
1973 rtt_status->status = RTT_STOPPED;
1974 /* to turn on mpc mode */
1975 schedule_work(&rtt_status->work);
1976 /* notify the completed information to others */
1977 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1978 #pragma GCC diagnostic push
1979 #pragma GCC diagnostic ignored "-Wcast-qual"
1980 #endif
1981 list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
1982 iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache);
1983 }
1984 /* remove the rtt results in cache */
1985 if (!list_empty(&rtt_status->rtt_results_cache)) {
1986 /* Iterate rtt_results_header list */
1987 list_for_each_entry_safe(entry, next,
1988 &rtt_status->rtt_results_cache, list) {
1989 list_del(&entry->list);
1990 /* Iterate rtt_result list */
1991 list_for_each_entry_safe(rtt_result, next2,
1992 &entry->result_list, list) {
1993 list_del(&rtt_result->list);
1994 kfree(rtt_result);
1995 }
1996 kfree(entry);
1997 }
1998 }
1999 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2000 #pragma GCC diagnostic pop
2001 #endif
2002 /* reinitialize the HEAD */
2003 INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
2004 /* clear information for rtt_config */
2005 rtt_status->rtt_config.rtt_target_cnt = 0;
2006 memset(rtt_status->rtt_config.target_info, 0,
2007 TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
2008 rtt_status->cur_idx = 0;
2009 }
2010 #endif /* WL_CFG80211 */
2011 break;
2012 case WL_PROXD_EVENT_SESSION_RESTART:
2013 DHD_RTT(("WL_PROXD_EVENT_SESSION_RESTART\n"));
2014 break;
2015 case WL_PROXD_EVENT_BURST_RESCHED:
2016 DHD_RTT(("WL_PROXD_EVENT_BURST_RESCHED\n"));
2017 break;
2018 case WL_PROXD_EVENT_SESSION_DESTROY:
2019 DHD_RTT(("WL_PROXD_EVENT_SESSION_DESTROY\n"));
2020 break;
2021 case WL_PROXD_EVENT_FTM_FRAME:
2022 DHD_RTT(("WL_PROXD_EVENT_FTM_FRAME\n"));
2023 break;
2024 case WL_PROXD_EVENT_DELAY:
2025 DHD_RTT(("WL_PROXD_EVENT_DELAY\n"));
2026 break;
2027 case WL_PROXD_EVENT_VS_INITIATOR_RPT:
2028 DHD_RTT(("WL_PROXD_EVENT_VS_INITIATOR_RPT\n "));
2029 break;
2030 case WL_PROXD_EVENT_RANGING:
2031 DHD_RTT(("WL_PROXD_EVENT_RANGING\n"));
2032 break;
2033 case WL_PROXD_EVENT_COLLECT:
2034 DHD_RTT(("WL_PROXD_EVENT_COLLECT\n"));
2035 if (tlvs_len > 0) {
2036 collect_event_data = kzalloc(sizeof(wl_proxd_collect_event_data_t), kflags);
2037 if (!collect_event_data) {
2038 ret = -ENOMEM;
2039 goto exit;
2040 }
2041 /* unpack TLVs and invokes the cbfn to print the event content TLVs */
2042 ret = bcm_unpack_xtlv_buf((void *) collect_event_data,
2043 (uint8 *)&p_event->tlvs[0], tlvs_len,
2044 BCM_XTLV_OPTION_NONE, rtt_unpack_xtlv_cbfn);
2045 kfree(collect_event_data);
2046 if (ret != BCME_OK) {
2047 DHD_ERROR(("%s : Failed to unpack xtlv for an event\n",
2048 __FUNCTION__));
2049 goto exit;
2050 }
2051 }
2052 break;
2053
2054
2055 default:
2056 DHD_ERROR(("WLC_E_PROXD: not supported EVENT Type:%d\n", event_type));
2057 break;
2058 }
2059 exit:
2060 #ifdef WL_CFG80211
2061 if (!in_atomic()) {
2062 mutex_unlock(&rtt_status->rtt_mutex);
2063 }
2064 #endif /* WL_CFG80211 */
2065
2066 return ret;
2067 }
2068
2069 #ifdef WL_CFG80211
2070 static void
dhd_rtt_work(struct work_struct * work)2071 dhd_rtt_work(struct work_struct *work)
2072 {
2073 rtt_status_info_t *rtt_status;
2074 dhd_pub_t *dhd;
2075 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2076 #pragma GCC diagnostic push
2077 #pragma GCC diagnostic ignored "-Wcast-qual"
2078 #endif
2079 rtt_status = container_of(work, rtt_status_info_t, work);
2080 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2081 #pragma GCC diagnostic pop
2082 #endif
2083 if (rtt_status == NULL) {
2084 DHD_ERROR(("%s : rtt_status is NULL\n", __FUNCTION__));
2085 return;
2086 }
2087 dhd = rtt_status->dhd;
2088 if (dhd == NULL) {
2089 DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__));
2090 return;
2091 }
2092 (void) dhd_rtt_start(dhd);
2093 }
2094 #endif /* WL_CFG80211 */
2095
2096 int
dhd_rtt_capability(dhd_pub_t * dhd,rtt_capabilities_t * capa)2097 dhd_rtt_capability(dhd_pub_t *dhd, rtt_capabilities_t *capa)
2098 {
2099 rtt_status_info_t *rtt_status;
2100 int err = BCME_OK;
2101 NULL_CHECK(dhd, "dhd is NULL", err);
2102 rtt_status = GET_RTTSTATE(dhd);
2103 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2104 NULL_CHECK(capa, "capa is NULL", err);
2105 bzero(capa, sizeof(rtt_capabilities_t));
2106
2107 /* set rtt capabilities */
2108 if (rtt_status->rtt_capa.proto & RTT_CAP_ONE_WAY)
2109 capa->rtt_one_sided_supported = 1;
2110 if (rtt_status->rtt_capa.proto & RTT_CAP_FTM_WAY)
2111 capa->rtt_ftm_supported = 1;
2112
2113 if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCI)
2114 capa->lci_support = 1;
2115 if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCR)
2116 capa->lcr_support = 1;
2117 if (rtt_status->rtt_capa.feature & RTT_FEATURE_PREAMBLE)
2118 capa->preamble_support = 1;
2119 if (rtt_status->rtt_capa.feature & RTT_FEATURE_BW)
2120 capa->bw_support = 1;
2121
2122 /* bit mask */
2123 capa->preamble_support = rtt_status->rtt_capa.preamble;
2124 capa->bw_support = rtt_status->rtt_capa.bw;
2125
2126 return err;
2127 }
2128
2129 #ifdef WL_CFG80211
2130 int
dhd_rtt_avail_channel(dhd_pub_t * dhd,wifi_channel_info * channel_info)2131 dhd_rtt_avail_channel(dhd_pub_t *dhd, wifi_channel_info *channel_info)
2132 {
2133 u32 chanspec = 0;
2134 int err = BCME_OK;
2135 chanspec_t c = 0;
2136 u32 channel;
2137 struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
2138
2139 if ((err = wldev_iovar_getint(dev, "chanspec",
2140 (s32 *)&chanspec)) == BCME_OK) {
2141 c = (chanspec_t)dtoh32(chanspec);
2142 c = wl_chspec_driver_to_host(c);
2143 channel = wf_chspec_ctlchan(c);
2144 DHD_RTT((" control channel is %d \n", channel));
2145 if (CHSPEC_IS20(c)) {
2146 channel_info->width = WIFI_CHAN_WIDTH_20;
2147 DHD_RTT((" band is 20 \n"));
2148 } else if (CHSPEC_IS40(c)) {
2149 channel_info->width = WIFI_CHAN_WIDTH_40;
2150 DHD_RTT(("band is 40 \n"));
2151 } else {
2152 channel_info->width = WIFI_CHAN_WIDTH_80;
2153 DHD_RTT(("band is 80 \n"));
2154 }
2155 if (CHSPEC_IS2G(c) && (channel >= CH_MIN_2G_CHANNEL) &&
2156 (channel <= CH_MAX_2G_CHANNEL)) {
2157 channel_info->center_freq =
2158 ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
2159 } else if (CHSPEC_IS5G(c) && channel >= CH_MIN_5G_CHANNEL) {
2160 channel_info->center_freq =
2161 ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ);
2162 }
2163 if ((channel_info->width == WIFI_CHAN_WIDTH_80) ||
2164 (channel_info->width == WIFI_CHAN_WIDTH_40)) {
2165 channel = CHSPEC_CHANNEL(c);
2166 channel_info->center_freq0 =
2167 ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ);
2168 }
2169 } else {
2170 DHD_ERROR(("Failed to get the chanspec \n"));
2171 }
2172 return err;
2173 }
2174
2175 int
dhd_rtt_enable_responder(dhd_pub_t * dhd,wifi_channel_info * channel_info)2176 dhd_rtt_enable_responder(dhd_pub_t *dhd, wifi_channel_info *channel_info)
2177 {
2178 int err = BCME_OK;
2179 char chanbuf[CHANSPEC_STR_LEN];
2180 int pm = PM_OFF;
2181 int ftm_cfg_cnt = 0;
2182 chanspec_t chanspec;
2183 wifi_channel_info_t channel;
2184 struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
2185 ftm_config_options_info_t ftm_configs[FTM_MAX_CONFIGS];
2186 ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS];
2187 rtt_status_info_t *rtt_status;
2188
2189 memset(&channel, 0, sizeof(channel));
2190 BCM_REFERENCE(chanbuf);
2191 NULL_CHECK(dhd, "dhd is NULL", err);
2192 rtt_status = GET_RTTSTATE(dhd);
2193 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2194 if (RTT_IS_STOPPED(rtt_status)) {
2195 DHD_RTT(("STA responder/Target. \n"));
2196 }
2197 DHD_RTT(("Enter %s \n", __FUNCTION__));
2198 if (!dhd_is_associated(dhd, 0, NULL)) {
2199 if (channel_info) {
2200 channel.width = channel_info->width;
2201 channel.center_freq = channel_info->center_freq;
2202 channel.center_freq0 = channel_info->center_freq;
2203 }
2204 else {
2205 channel.width = WIFI_CHAN_WIDTH_80;
2206 channel.center_freq = DEFAULT_FTM_FREQ;
2207 channel.center_freq0 = DEFAULT_FTM_CNTR_FREQ0;
2208 }
2209 chanspec = dhd_rtt_convert_to_chspec(channel);
2210 DHD_RTT(("chanspec/channel set as %s for rtt.\n",
2211 wf_chspec_ntoa(chanspec, chanbuf)));
2212 err = wldev_iovar_setint(dev, "chanspec", chanspec);
2213 if (err) {
2214 DHD_ERROR(("Failed to set the chanspec \n"));
2215 }
2216 }
2217 err = wldev_ioctl_get(dev, WLC_GET_PM, &rtt_status->pm, sizeof(rtt_status->pm));
2218 DHD_RTT(("Current PM value read %d\n", rtt_status->pm));
2219 if (err) {
2220 DHD_ERROR(("Failed to get the PM value \n"));
2221 } else {
2222 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2223 if (err) {
2224 DHD_ERROR(("Failed to set the PM \n"));
2225 rtt_status->pm_restore = FALSE;
2226 } else {
2227 rtt_status->pm_restore = TRUE;
2228 }
2229 }
2230 if (!RTT_IS_ENABLED(rtt_status)) {
2231 err = dhd_rtt_ftm_enable(dhd, TRUE);
2232 if (err) {
2233 DHD_ERROR(("Failed to enable FTM (%d)\n", err));
2234 goto exit;
2235 }
2236 DHD_RTT(("FTM enabled \n"));
2237 }
2238 rtt_status->status = RTT_ENABLED;
2239 DHD_RTT(("Responder enabled \n"));
2240 memset(ftm_configs, 0, sizeof(ftm_configs));
2241 memset(ftm_params, 0, sizeof(ftm_params));
2242 ftm_configs[ftm_cfg_cnt].enable = TRUE;
2243 ftm_configs[ftm_cfg_cnt++].flags = WL_PROXD_SESSION_FLAG_TARGET;
2244 rtt_status->flags = WL_PROXD_SESSION_FLAG_TARGET;
2245 DHD_RTT(("Set the device as responder \n"));
2246 err = dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_OPTIONS,
2247 ftm_configs, ftm_cfg_cnt);
2248 exit:
2249 if (err) {
2250 rtt_status->status = RTT_STOPPED;
2251 DHD_ERROR(("rtt is stopped %s \n", __FUNCTION__));
2252 dhd_rtt_ftm_enable(dhd, FALSE);
2253 DHD_RTT(("restoring the PM value \n"));
2254 if (rtt_status->pm_restore) {
2255 pm = PM_FAST;
2256 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2257 if (err) {
2258 DHD_ERROR(("Failed to restore PM \n"));
2259 } else {
2260 rtt_status->pm_restore = FALSE;
2261 }
2262 }
2263 }
2264 return err;
2265 }
2266
2267 int
dhd_rtt_cancel_responder(dhd_pub_t * dhd)2268 dhd_rtt_cancel_responder(dhd_pub_t *dhd)
2269 {
2270 int err = BCME_OK;
2271 rtt_status_info_t *rtt_status;
2272 int pm = 0;
2273 struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
2274 NULL_CHECK(dhd, "dhd is NULL", err);
2275 rtt_status = GET_RTTSTATE(dhd);
2276 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2277 DHD_RTT(("Enter %s \n", __FUNCTION__));
2278 err = dhd_rtt_ftm_enable(dhd, FALSE);
2279 if (err) {
2280 DHD_ERROR(("failed to disable FTM (%d)\n", err));
2281 }
2282 rtt_status->status = RTT_STOPPED;
2283 if (rtt_status->pm_restore) {
2284 pm = PM_FAST;
2285 DHD_RTT(("pm_restore =%d \n", rtt_status->pm_restore));
2286 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2287 if (err) {
2288 DHD_ERROR(("Failed to restore PM \n"));
2289 } else {
2290 rtt_status->pm_restore = FALSE;
2291 }
2292 }
2293 return err;
2294 }
2295 #endif /* WL_CFG80211 */
2296
2297 int
dhd_rtt_init(dhd_pub_t * dhd)2298 dhd_rtt_init(dhd_pub_t *dhd)
2299 {
2300 int err = BCME_OK;
2301 #ifdef WL_CFG80211
2302 int ret;
2303 int32 drv_up = 1;
2304 int32 version;
2305 rtt_status_info_t *rtt_status;
2306 NULL_CHECK(dhd, "dhd is NULL", err);
2307 dhd->rtt_supported = FALSE;
2308 if (dhd->rtt_state) {
2309 return err;
2310 }
2311 dhd->rtt_state = kzalloc(sizeof(rtt_status_info_t), GFP_KERNEL);
2312 if (dhd->rtt_state == NULL) {
2313 err = BCME_NOMEM;
2314 DHD_ERROR(("%s : failed to create rtt_state\n", __FUNCTION__));
2315 return err;
2316 }
2317 bzero(dhd->rtt_state, sizeof(rtt_status_info_t));
2318 rtt_status = GET_RTTSTATE(dhd);
2319 rtt_status->rtt_config.target_info =
2320 kzalloc(TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT), GFP_KERNEL);
2321 if (rtt_status->rtt_config.target_info == NULL) {
2322 DHD_ERROR(("%s failed to allocate the target info for %d\n",
2323 __FUNCTION__, RTT_MAX_TARGET_CNT));
2324 err = BCME_NOMEM;
2325 goto exit;
2326 }
2327 rtt_status->dhd = dhd;
2328 /* need to do WLC_UP */
2329 dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&drv_up, sizeof(int32), TRUE, 0);
2330
2331 ret = dhd_rtt_get_version(dhd, &version);
2332 if (ret == BCME_OK && (version == WL_PROXD_API_VERSION)) {
2333 DHD_ERROR(("%s : FTM is supported\n", __FUNCTION__));
2334 dhd->rtt_supported = TRUE;
2335 /* rtt_status->rtt_capa.proto |= RTT_CAP_ONE_WAY; */
2336 rtt_status->rtt_capa.proto |= RTT_CAP_FTM_WAY;
2337
2338 /* indicate to set tx rate */
2339 rtt_status->rtt_capa.feature |= RTT_FEATURE_LCI;
2340 rtt_status->rtt_capa.feature |= RTT_FEATURE_LCR;
2341 rtt_status->rtt_capa.feature |= RTT_FEATURE_PREAMBLE;
2342 rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_VHT;
2343 rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_HT;
2344
2345 /* indicate to set bandwith */
2346 rtt_status->rtt_capa.feature |= RTT_FEATURE_BW;
2347 rtt_status->rtt_capa.bw |= RTT_BW_20;
2348 rtt_status->rtt_capa.bw |= RTT_BW_40;
2349 rtt_status->rtt_capa.bw |= RTT_BW_80;
2350 } else {
2351 if ((ret != BCME_OK) || (version == 0)) {
2352 DHD_ERROR(("%s : FTM is not supported\n", __FUNCTION__));
2353 } else {
2354 DHD_ERROR(("%s : FTM version mismatch between HOST (%d) and FW (%d)\n",
2355 __FUNCTION__, WL_PROXD_API_VERSION, version));
2356 }
2357 }
2358 /* cancel all of RTT request once we got the cancel request */
2359 rtt_status->all_cancel = TRUE;
2360 mutex_init(&rtt_status->rtt_mutex);
2361 INIT_LIST_HEAD(&rtt_status->noti_fn_list);
2362 INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
2363 INIT_WORK(&rtt_status->work, dhd_rtt_work);
2364 exit:
2365 if (err < 0) {
2366 kfree(rtt_status->rtt_config.target_info);
2367 kfree(dhd->rtt_state);
2368 }
2369 #endif /* WL_CFG80211 */
2370 return err;
2371 }
2372
2373 int
dhd_rtt_deinit(dhd_pub_t * dhd)2374 dhd_rtt_deinit(dhd_pub_t *dhd)
2375 {
2376 int err = BCME_OK;
2377 #ifdef WL_CFG80211
2378 rtt_status_info_t *rtt_status;
2379 rtt_results_header_t *rtt_header, *next;
2380 rtt_result_t *rtt_result, *next2;
2381 struct rtt_noti_callback *iter, *iter2;
2382 NULL_CHECK(dhd, "dhd is NULL", err);
2383 rtt_status = GET_RTTSTATE(dhd);
2384 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2385 rtt_status->status = RTT_STOPPED;
2386 DHD_RTT(("rtt is stopped %s \n", __FUNCTION__));
2387 /* clear evt callback list */
2388 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2389 #pragma GCC diagnostic push
2390 #pragma GCC diagnostic ignored "-Wcast-qual"
2391 #endif
2392
2393 if (!list_empty(&rtt_status->noti_fn_list)) {
2394 list_for_each_entry_safe(iter, iter2, &rtt_status->noti_fn_list, list) {
2395 list_del(&iter->list);
2396 kfree(iter);
2397 }
2398 }
2399 /* remove the rtt results */
2400 if (!list_empty(&rtt_status->rtt_results_cache)) {
2401 list_for_each_entry_safe(rtt_header, next, &rtt_status->rtt_results_cache, list) {
2402 list_del(&rtt_header->list);
2403 list_for_each_entry_safe(rtt_result, next2,
2404 &rtt_header->result_list, list) {
2405 list_del(&rtt_result->list);
2406 kfree(rtt_result);
2407 }
2408 kfree(rtt_header);
2409 }
2410 }
2411 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2412 #pragma GCC diagnostic pop
2413 #endif
2414 kfree(rtt_status->rtt_config.target_info);
2415 kfree(dhd->rtt_state);
2416 dhd->rtt_state = NULL;
2417 #endif /* WL_CFG80211 */
2418 return err;
2419 }
2420