dhd: import wifi and bluetooth firmware
[GitHub/LineageOS/G12/android_hardware_amlogic_kernel-modules_dhd-driver.git] / bcmdhd.1.579.77.41.1.cn / dhd_rtt.c
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 static const ftm_strmap_entry_t ftm_tlvid_loginfo[] = {
200 /* { WL_PROXD_TLV_ID_xxx, "text for WL_PROXD_TLV_ID_xxx" }, */
201 { WL_PROXD_TLV_ID_NONE, "none" },
202 { WL_PROXD_TLV_ID_METHOD, "method" },
203 { WL_PROXD_TLV_ID_FLAGS, "flags" },
204 { WL_PROXD_TLV_ID_CHANSPEC, "chanspec" },
205 { WL_PROXD_TLV_ID_TX_POWER, "tx power" },
206 { WL_PROXD_TLV_ID_RATESPEC, "ratespec" },
207 { WL_PROXD_TLV_ID_BURST_DURATION, "burst duration" },
208 { WL_PROXD_TLV_ID_BURST_PERIOD, "burst period" },
209 { WL_PROXD_TLV_ID_BURST_FTM_SEP, "burst ftm sep" },
210 { WL_PROXD_TLV_ID_BURST_NUM_FTM, "burst num ftm" },
211 { WL_PROXD_TLV_ID_NUM_BURST, "num burst" },
212 { WL_PROXD_TLV_ID_FTM_RETRIES, "ftm retries" },
213 { WL_PROXD_TLV_ID_BSS_INDEX, "BSS index" },
214 { WL_PROXD_TLV_ID_BSSID, "bssid" },
215 { WL_PROXD_TLV_ID_INIT_DELAY, "burst init delay" },
216 { WL_PROXD_TLV_ID_BURST_TIMEOUT, "burst timeout" },
217 { WL_PROXD_TLV_ID_EVENT_MASK, "event mask" },
218 { WL_PROXD_TLV_ID_FLAGS_MASK, "flags mask" },
219 { WL_PROXD_TLV_ID_PEER_MAC, "peer addr" },
220 { WL_PROXD_TLV_ID_FTM_REQ, "ftm req" },
221 { WL_PROXD_TLV_ID_LCI_REQ, "lci req" },
222 { WL_PROXD_TLV_ID_LCI, "lci" },
223 { WL_PROXD_TLV_ID_CIVIC_REQ, "civic req" },
224 { WL_PROXD_TLV_ID_CIVIC, "civic" },
225 { WL_PROXD_TLV_ID_AVAIL, "availability" },
226 { WL_PROXD_TLV_ID_SESSION_FLAGS, "session flags" },
227 { WL_PROXD_TLV_ID_SESSION_FLAGS_MASK, "session flags mask" },
228 { WL_PROXD_TLV_ID_RX_MAX_BURST, "rx max bursts" },
229 { WL_PROXD_TLV_ID_RANGING_INFO, "ranging info" },
230 { WL_PROXD_TLV_ID_RANGING_FLAGS, "ranging flags" },
231 { WL_PROXD_TLV_ID_RANGING_FLAGS_MASK, "ranging flags mask" },
232 /* output - 512 + x */
233 { WL_PROXD_TLV_ID_STATUS, "status" },
234 { WL_PROXD_TLV_ID_COUNTERS, "counters" },
235 { WL_PROXD_TLV_ID_INFO, "info" },
236 { WL_PROXD_TLV_ID_RTT_RESULT, "rtt result" },
237 { WL_PROXD_TLV_ID_AOA_RESULT, "aoa result" },
238 { WL_PROXD_TLV_ID_SESSION_INFO, "session info" },
239 { WL_PROXD_TLV_ID_SESSION_STATUS, "session status" },
240 { WL_PROXD_TLV_ID_SESSION_ID_LIST, "session ids" },
241 /* debug tlvs can be added starting 1024 */
242 { WL_PROXD_TLV_ID_DEBUG_MASK, "debug mask" },
243 { WL_PROXD_TLV_ID_COLLECT, "collect" },
244 { WL_PROXD_TLV_ID_STRBUF, "result" },
245 { WL_PROXD_TLV_ID_COLLECT_DATA, "collect-data" },
246 { WL_PROXD_TLV_ID_RI_RR, "ri_rr" },
247 { WL_PROXD_TLV_ID_COLLECT_CHAN_DATA, "chan est"}
248 };
249
250 static const ftm_strmap_entry_t ftm_event_type_loginfo[] = {
251 /* wl_proxd_event_type_t, text-string */
252 { WL_PROXD_EVENT_NONE, "none" },
253 { WL_PROXD_EVENT_SESSION_CREATE, "session create" },
254 { WL_PROXD_EVENT_SESSION_START, "session start" },
255 { WL_PROXD_EVENT_FTM_REQ, "FTM req" },
256 { WL_PROXD_EVENT_BURST_START, "burst start" },
257 { WL_PROXD_EVENT_BURST_END, "burst end" },
258 { WL_PROXD_EVENT_SESSION_END, "session end" },
259 { WL_PROXD_EVENT_SESSION_RESTART, "session restart" },
260 { WL_PROXD_EVENT_BURST_RESCHED, "burst rescheduled" },
261 { WL_PROXD_EVENT_SESSION_DESTROY, "session destroy" },
262 { WL_PROXD_EVENT_RANGE_REQ, "range request" },
263 { WL_PROXD_EVENT_FTM_FRAME, "FTM frame" },
264 { WL_PROXD_EVENT_DELAY, "delay" },
265 { WL_PROXD_EVENT_VS_INITIATOR_RPT, "initiator-report " }, /* rx */
266 { WL_PROXD_EVENT_RANGING, "ranging " },
267 { WL_PROXD_EVENT_COLLECT, "collect" },
268 };
269
270 /*
271 * session-state --> text string mapping
272 */
273 static const ftm_strmap_entry_t ftm_session_state_value_loginfo[] = {
274 /* wl_proxd_session_state_t, text string */
275 { WL_PROXD_SESSION_STATE_CREATED, "created" },
276 { WL_PROXD_SESSION_STATE_CONFIGURED, "configured" },
277 { WL_PROXD_SESSION_STATE_STARTED, "started" },
278 { WL_PROXD_SESSION_STATE_DELAY, "delay" },
279 { WL_PROXD_SESSION_STATE_USER_WAIT, "user-wait" },
280 { WL_PROXD_SESSION_STATE_SCHED_WAIT, "sched-wait" },
281 { WL_PROXD_SESSION_STATE_BURST, "burst" },
282 { WL_PROXD_SESSION_STATE_STOPPING, "stopping" },
283 { WL_PROXD_SESSION_STATE_ENDED, "ended" },
284 { WL_PROXD_SESSION_STATE_DESTROYING, "destroying" },
285 { WL_PROXD_SESSION_STATE_NONE, "none" }
286 };
287
288 /*
289 * ranging-state --> text string mapping
290 */
291 static const ftm_strmap_entry_t ftm_ranging_state_value_loginfo [] = {
292 /* wl_proxd_ranging_state_t, text string */
293 { WL_PROXD_RANGING_STATE_NONE, "none" },
294 { WL_PROXD_RANGING_STATE_NOTSTARTED, "nonstarted" },
295 { WL_PROXD_RANGING_STATE_INPROGRESS, "inprogress" },
296 { WL_PROXD_RANGING_STATE_DONE, "done" },
297 };
298
299 /*
300 * status --> text string mapping
301 */
302 static const ftm_strmap_entry_t ftm_status_value_loginfo[] = {
303 /* wl_proxd_status_t, text-string */
304 { WL_PROXD_E_OVERRIDDEN, "overridden" },
305 { WL_PROXD_E_ASAP_FAILED, "ASAP failed" },
306 { WL_PROXD_E_NOTSTARTED, "not started" },
307 { WL_PROXD_E_INVALIDMEAS, "invalid measurement" },
308 { WL_PROXD_E_INCAPABLE, "incapable" },
309 { WL_PROXD_E_MISMATCH, "mismatch"},
310 { WL_PROXD_E_DUP_SESSION, "dup session" },
311 { WL_PROXD_E_REMOTE_FAIL, "remote fail" },
312 { WL_PROXD_E_REMOTE_INCAPABLE, "remote incapable" },
313 { WL_PROXD_E_SCHED_FAIL, "sched failure" },
314 { WL_PROXD_E_PROTO, "protocol error" },
315 { WL_PROXD_E_EXPIRED, "expired" },
316 { WL_PROXD_E_TIMEOUT, "timeout" },
317 { WL_PROXD_E_NOACK, "no ack" },
318 { WL_PROXD_E_DEFERRED, "deferred" },
319 { WL_PROXD_E_INVALID_SID, "invalid session id" },
320 { WL_PROXD_E_REMOTE_CANCEL, "remote cancel" },
321 { WL_PROXD_E_CANCELED, "canceled" },
322 { WL_PROXD_E_INVALID_SESSION, "invalid session" },
323 { WL_PROXD_E_BAD_STATE, "bad state" },
324 { WL_PROXD_E_ERROR, "error" },
325 { WL_PROXD_E_OK, "OK" }
326 };
327
328 /*
329 * time interval unit --> text string mapping
330 */
331 static const ftm_strmap_entry_t ftm_tmu_value_loginfo[] = {
332 /* wl_proxd_tmu_t, text-string */
333 { WL_PROXD_TMU_TU, "TU" },
334 { WL_PROXD_TMU_SEC, "sec" },
335 { WL_PROXD_TMU_MILLI_SEC, "ms" },
336 { WL_PROXD_TMU_MICRO_SEC, "us" },
337 { WL_PROXD_TMU_NANO_SEC, "ns" },
338 { WL_PROXD_TMU_PICO_SEC, "ps" }
339 };
340
341 #define RSPEC_BW(rspec) ((rspec) & WL_RSPEC_BW_MASK)
342 #define RSPEC_IS20MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_20MHZ)
343 #define RSPEC_IS40MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_40MHZ)
344 #define RSPEC_IS80MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_80MHZ)
345 #define RSPEC_IS160MHZ(rspec) (RSPEC_BW(rspec) == WL_RSPEC_BW_160MHZ)
346
347 #define IS_MCS(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) != WL_RSPEC_ENCODE_RATE)
348 #define IS_STBC(rspec) (((((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) || \
349 (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT)) && \
350 (((rspec) & WL_RSPEC_STBC) == WL_RSPEC_STBC))
351 #define RSPEC_ISSGI(rspec) (((rspec) & WL_RSPEC_SGI) != 0)
352 #define RSPEC_ISLDPC(rspec) (((rspec) & WL_RSPEC_LDPC) != 0)
353 #define RSPEC_ISSTBC(rspec) (((rspec) & WL_RSPEC_STBC) != 0)
354 #define RSPEC_ISTXBF(rspec) (((rspec) & WL_RSPEC_TXBF) != 0)
355 #define RSPEC_ISVHT(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT)
356 #define RSPEC_ISHT(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT)
357 #define RSPEC_ISLEGACY(rspec) (((rspec) & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE)
358 #define RSPEC2RATE(rspec) (RSPEC_ISLEGACY(rspec) ? \
359 ((rspec) & RSPEC_RATE_MASK) : rate_rspec2rate(rspec))
360 /* return rate in unit of 500Kbps -- for internal use in wlc_rate_sel.c */
361 #define RSPEC2KBPS(rspec) rate_rspec2rate(rspec)
362
363 struct ieee_80211_mcs_rate_info {
364 uint8 constellation_bits;
365 uint8 coding_q;
366 uint8 coding_d;
367 };
368
369 static const struct ieee_80211_mcs_rate_info wl_mcs_info[] = {
370 { 1, 1, 2 }, /* MCS 0: MOD: BPSK, CR 1/2 */
371 { 2, 1, 2 }, /* MCS 1: MOD: QPSK, CR 1/2 */
372 { 2, 3, 4 }, /* MCS 2: MOD: QPSK, CR 3/4 */
373 { 4, 1, 2 }, /* MCS 3: MOD: 16QAM, CR 1/2 */
374 { 4, 3, 4 }, /* MCS 4: MOD: 16QAM, CR 3/4 */
375 { 6, 2, 3 }, /* MCS 5: MOD: 64QAM, CR 2/3 */
376 { 6, 3, 4 }, /* MCS 6: MOD: 64QAM, CR 3/4 */
377 { 6, 5, 6 }, /* MCS 7: MOD: 64QAM, CR 5/6 */
378 { 8, 3, 4 }, /* MCS 8: MOD: 256QAM, CR 3/4 */
379 { 8, 5, 6 } /* MCS 9: MOD: 256QAM, CR 5/6 */
380 };
381
382 /**
383 * Returns the rate in [Kbps] units for a caller supplied MCS/bandwidth/Nss/Sgi combination.
384 * 'mcs' : a *single* spatial stream MCS (11n or 11ac)
385 */
386 uint
387 rate_mcs2rate(uint mcs, uint nss, uint bw, int sgi)
388 {
389 const int ksps = 250; /* kilo symbols per sec, 4 us sym */
390 const int Nsd_20MHz = 52;
391 const int Nsd_40MHz = 108;
392 const int Nsd_80MHz = 234;
393 const int Nsd_160MHz = 468;
394 uint rate;
395
396 if (mcs == 32) {
397 /* just return fixed values for mcs32 instead of trying to parametrize */
398 rate = (sgi == 0) ? 6000 : 6778;
399 } else if (mcs <= 9) {
400 /* This calculation works for 11n HT and 11ac VHT if the HT mcs values
401 * are decomposed into a base MCS = MCS % 8, and Nss = 1 + MCS / 8.
402 * That is, HT MCS 23 is a base MCS = 7, Nss = 3
403 */
404
405 /* find the number of complex numbers per symbol */
406 if (RSPEC_IS20MHZ(bw)) {
407 rate = Nsd_20MHz;
408 } else if (RSPEC_IS40MHZ(bw)) {
409 rate = Nsd_40MHz;
410 } else if (bw == WL_RSPEC_BW_80MHZ) {
411 rate = Nsd_80MHz;
412 } else if (bw == WL_RSPEC_BW_160MHZ) {
413 rate = Nsd_160MHz;
414 } else {
415 rate = 0;
416 }
417
418 /* multiply by bits per number from the constellation in use */
419 rate = rate * wl_mcs_info[mcs].constellation_bits;
420
421 /* adjust for the number of spatial streams */
422 rate = rate * nss;
423
424 /* adjust for the coding rate given as a quotient and divisor */
425 rate = (rate * wl_mcs_info[mcs].coding_q) / wl_mcs_info[mcs].coding_d;
426
427 /* multiply by Kilo symbols per sec to get Kbps */
428 rate = rate * ksps;
429
430 /* adjust the symbols per sec for SGI
431 * symbol duration is 4 us without SGI, and 3.6 us with SGI,
432 * so ratio is 10 / 9
433 */
434 if (sgi) {
435 /* add 4 for rounding of division by 9 */
436 rate = ((rate * 10) + 4) / 9;
437 }
438 } else {
439 rate = 0;
440 }
441
442 return rate;
443 } /* wlc_rate_mcs2rate */
444
445 /** take a well formed ratespec_t arg and return phy rate in [Kbps] units */
446 int
447 rate_rspec2rate(uint32 rspec)
448 {
449 int rate = -1;
450
451 if (RSPEC_ISLEGACY(rspec)) {
452 rate = 500 * (rspec & WL_RSPEC_RATE_MASK);
453 } else if (RSPEC_ISHT(rspec)) {
454 uint mcs = (rspec & WL_RSPEC_RATE_MASK);
455
456 if (mcs == 32) {
457 rate = rate_mcs2rate(mcs, 1, WL_RSPEC_BW_40MHZ, RSPEC_ISSGI(rspec));
458 } else {
459 uint nss = 1 + (mcs / 8);
460 mcs = mcs % 8;
461 rate = rate_mcs2rate(mcs, nss, RSPEC_BW(rspec), RSPEC_ISSGI(rspec));
462 }
463 } else if (RSPEC_ISVHT(rspec)) {
464 uint mcs = (rspec & WL_RSPEC_VHT_MCS_MASK);
465 uint nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
466
467 ASSERT(mcs <= 9);
468 ASSERT(nss <= 8);
469
470 rate = rate_mcs2rate(mcs, nss, RSPEC_BW(rspec), RSPEC_ISSGI(rspec));
471 } else {
472 ASSERT(0);
473 }
474
475 return (rate == 0) ? -1 : rate;
476 }
477
478 char resp_buf[WLC_IOCTL_SMLEN];
479
480 static uint64
481 ftm_intvl2nsec(const wl_proxd_intvl_t *intvl)
482 {
483 uint64 ret;
484 ret = intvl->intvl;
485 switch (intvl->tmu) {
486 case WL_PROXD_TMU_TU: ret = FTM_TU2MICRO(ret) * 1000; break;
487 case WL_PROXD_TMU_SEC: ret *= 1000000000; break;
488 case WL_PROXD_TMU_MILLI_SEC: ret *= 1000000; break;
489 case WL_PROXD_TMU_MICRO_SEC: ret *= 1000; break;
490 case WL_PROXD_TMU_PICO_SEC: ret = intvl->intvl / 1000; break;
491 case WL_PROXD_TMU_NANO_SEC: /* fall through */
492 default: break;
493 }
494 return ret;
495 }
496 uint64
497 ftm_intvl2usec(const wl_proxd_intvl_t *intvl)
498 {
499 uint64 ret;
500 ret = intvl->intvl;
501 switch (intvl->tmu) {
502 case WL_PROXD_TMU_TU: ret = FTM_TU2MICRO(ret); break;
503 case WL_PROXD_TMU_SEC: ret *= 1000000; break;
504 case WL_PROXD_TMU_NANO_SEC: ret = intvl->intvl / 1000; break;
505 case WL_PROXD_TMU_PICO_SEC: ret = intvl->intvl / 1000000; break;
506 case WL_PROXD_TMU_MILLI_SEC: ret *= 1000; break;
507 case WL_PROXD_TMU_MICRO_SEC: /* fall through */
508 default: break;
509 }
510 return ret;
511 }
512
513 /*
514 * lookup 'id' (as a key) from a fw status to host map table
515 * if found, return the corresponding reason code
516 */
517
518 static rtt_reason_t
519 ftm_get_statusmap_info(wl_proxd_status_t id, const ftm_status_map_host_entry_t *p_table,
520 uint32 num_entries)
521 {
522 int i;
523 const ftm_status_map_host_entry_t *p_entry;
524 /* scan thru the table till end */
525 p_entry = p_table;
526 for (i = 0; i < (int) num_entries; i++)
527 {
528 if (p_entry->proxd_status == id) {
529 return p_entry->rtt_reason;
530 }
531 p_entry++; /* next entry */
532 }
533 return RTT_REASON_FAILURE; /* not found */
534 }
535 /*
536 * lookup 'id' (as a key) from a table
537 * if found, return the entry pointer, otherwise return NULL
538 */
539 static const ftm_strmap_entry_t*
540 ftm_get_strmap_info(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
541 {
542 int i;
543 const ftm_strmap_entry_t *p_entry;
544
545 /* scan thru the table till end */
546 p_entry = p_table;
547 for (i = 0; i < (int) num_entries; i++)
548 {
549 if (p_entry->id == id)
550 return p_entry;
551 p_entry++; /* next entry */
552 }
553 return NULL; /* not found */
554 }
555
556 /*
557 * map enum to a text-string for display, this function is called by the following:
558 * For debug/trace:
559 * ftm_[cmdid|tlvid]_to_str()
560 * For TLV-output log for 'get' commands
561 * ftm_[method|tmu|caps|status|state]_value_to_logstr()
562 * Input:
563 * pTable -- point to a 'enum to string' table.
564 */
565 static const char *
566 ftm_map_id_to_str(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
567 {
568 const ftm_strmap_entry_t*p_entry = ftm_get_strmap_info(id, p_table, num_entries);
569 if (p_entry)
570 return (p_entry->text);
571
572 return "invalid";
573 }
574
575
576 #ifdef RTT_DEBUG
577
578 /* define entry, e.g. { WL_PROXD_CMD_xxx, "WL_PROXD_CMD_xxx" } */
579 #define DEF_STRMAP_ENTRY(id) { (id), #id }
580
581 /* ftm cmd-id mapping */
582 static const ftm_strmap_entry_t ftm_cmdid_map[] = {
583 /* {wl_proxd_cmd_t(WL_PROXD_CMD_xxx), "WL_PROXD_CMD_xxx" }, */
584 DEF_STRMAP_ENTRY(WL_PROXD_CMD_NONE),
585 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_VERSION),
586 DEF_STRMAP_ENTRY(WL_PROXD_CMD_ENABLE),
587 DEF_STRMAP_ENTRY(WL_PROXD_CMD_DISABLE),
588 DEF_STRMAP_ENTRY(WL_PROXD_CMD_CONFIG),
589 DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_SESSION),
590 DEF_STRMAP_ENTRY(WL_PROXD_CMD_BURST_REQUEST),
591 DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_SESSION),
592 DEF_STRMAP_ENTRY(WL_PROXD_CMD_DELETE_SESSION),
593 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RESULT),
594 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_INFO),
595 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_STATUS),
596 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_SESSIONS),
597 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_COUNTERS),
598 DEF_STRMAP_ENTRY(WL_PROXD_CMD_CLEAR_COUNTERS),
599 DEF_STRMAP_ENTRY(WL_PROXD_CMD_COLLECT),
600 DEF_STRMAP_ENTRY(WL_PROXD_CMD_TUNE),
601 DEF_STRMAP_ENTRY(WL_PROXD_CMD_DUMP),
602 DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_RANGING),
603 DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_RANGING),
604 DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RANGING_INFO),
605 };
606
607 /*
608 * map a ftm cmd-id to a text-string for display
609 */
610 static const char *
611 ftm_cmdid_to_str(uint16 cmdid)
612 {
613 return ftm_map_id_to_str((int32) cmdid, &ftm_cmdid_map[0], ARRAYSIZE(ftm_cmdid_map));
614 }
615 #endif /* RTT_DEBUG */
616
617
618 /*
619 * convert BCME_xxx error codes into related error strings
620 * note, bcmerrorstr() defined in bcmutils is for BCMDRIVER only,
621 * this duplicate copy is for WL access and may need to clean up later
622 */
623 static const char *ftm_bcmerrorstrtable[] = BCMERRSTRINGTABLE;
624 static const char *
625 ftm_status_value_to_logstr(wl_proxd_status_t status)
626 {
627 static char ftm_msgbuf_status_undef[32];
628 const ftm_strmap_entry_t *p_loginfo;
629 int bcmerror;
630
631 /* check if within BCME_xxx error range */
632 bcmerror = (int) status;
633 if (VALID_BCMERROR(bcmerror))
634 return ftm_bcmerrorstrtable[-bcmerror];
635
636 /* otherwise, look for 'proxd ftm status' range */
637 p_loginfo = ftm_get_strmap_info((int32) status,
638 &ftm_status_value_loginfo[0], ARRAYSIZE(ftm_status_value_loginfo));
639 if (p_loginfo)
640 return p_loginfo->text;
641
642 /* report for 'out of range' FTM-status error code */
643 memset(ftm_msgbuf_status_undef, 0, sizeof(ftm_msgbuf_status_undef));
644 snprintf(ftm_msgbuf_status_undef, sizeof(ftm_msgbuf_status_undef),
645 "Undefined status %d", status);
646 return &ftm_msgbuf_status_undef[0];
647 }
648
649 static const char *
650 ftm_tmu_value_to_logstr(wl_proxd_tmu_t tmu)
651 {
652 return ftm_map_id_to_str((int32)tmu,
653 &ftm_tmu_value_loginfo[0], ARRAYSIZE(ftm_tmu_value_loginfo));
654 }
655
656 static const ftm_strmap_entry_t*
657 ftm_get_event_type_loginfo(wl_proxd_event_type_t event_type)
658 {
659 /* look up 'event-type' from a predefined table */
660 return ftm_get_strmap_info((int32) event_type,
661 ftm_event_type_loginfo, ARRAYSIZE(ftm_event_type_loginfo));
662 }
663
664 static const char *
665 ftm_session_state_value_to_logstr(wl_proxd_session_state_t state)
666 {
667 return ftm_map_id_to_str((int32)state, &ftm_session_state_value_loginfo[0],
668 ARRAYSIZE(ftm_session_state_value_loginfo));
669 }
670
671
672 #ifdef WL_CFG80211
673 /*
674 * send 'proxd' iovar for all ftm get-related commands
675 */
676 static int
677 rtt_do_get_ioctl(dhd_pub_t *dhd, wl_proxd_iov_t *p_proxd_iov, uint16 proxd_iovsize,
678 ftm_subcmd_info_t *p_subcmd_info)
679 {
680
681 wl_proxd_iov_t *p_iovresp = (wl_proxd_iov_t *)resp_buf;
682 int status;
683 int tlvs_len;
684 /* send getbuf proxd iovar */
685 status = dhd_getiovar(dhd, 0, "proxd", (char *)p_proxd_iov,
686 proxd_iovsize, (char **)&p_iovresp, WLC_IOCTL_SMLEN);
687 if (status != BCME_OK) {
688 DHD_ERROR(("%s: failed to send getbuf proxd iovar (CMD ID : %d), status=%d\n",
689 __FUNCTION__, p_subcmd_info->cmdid, status));
690 return status;
691 }
692 if (p_subcmd_info->cmdid == WL_PROXD_CMD_GET_VERSION) {
693 p_subcmd_info->version = ltoh16(p_iovresp->version);
694 DHD_RTT(("ftm version: 0x%x\n", ltoh16(p_iovresp->version)));
695 goto exit;
696 }
697
698 tlvs_len = ltoh16(p_iovresp->len) - WL_PROXD_IOV_HDR_SIZE;
699 if (tlvs_len < 0) {
700 DHD_ERROR(("%s: alert, p_iovresp->len(%d) should not be smaller than %d\n",
701 __FUNCTION__, ltoh16(p_iovresp->len), (int) WL_PROXD_IOV_HDR_SIZE));
702 tlvs_len = 0;
703 }
704
705 if (tlvs_len > 0 && p_subcmd_info->handler) {
706 /* unpack TLVs and invokes the cbfn for processing */
707 status = bcm_unpack_xtlv_buf(p_proxd_iov, (uint8 *)p_iovresp->tlvs,
708 tlvs_len, BCM_XTLV_OPTION_ALIGN32, p_subcmd_info->handler);
709 }
710 exit:
711 return status;
712 }
713
714
715 static wl_proxd_iov_t *
716 rtt_alloc_getset_buf(wl_proxd_method_t method, wl_proxd_session_id_t session_id,
717 wl_proxd_cmd_t cmdid, uint16 tlvs_bufsize, uint16 *p_out_bufsize)
718 {
719 uint16 proxd_iovsize;
720 uint16 kflags;
721 wl_proxd_tlv_t *p_tlv;
722 wl_proxd_iov_t *p_proxd_iov = (wl_proxd_iov_t *) NULL;
723
724 *p_out_bufsize = 0; /* init */
725 kflags = in_atomic() ? GFP_ATOMIC : GFP_KERNEL;
726 /* calculate the whole buffer size, including one reserve-tlv entry in the header */
727 proxd_iovsize = sizeof(wl_proxd_iov_t) + tlvs_bufsize;
728
729 p_proxd_iov = kzalloc(proxd_iovsize, kflags);
730 if (p_proxd_iov == NULL) {
731 DHD_ERROR(("error: failed to allocate %d bytes of memory\n", proxd_iovsize));
732 return NULL;
733 }
734
735 /* setup proxd-FTM-method iovar header */
736 p_proxd_iov->version = htol16(WL_PROXD_API_VERSION);
737 p_proxd_iov->len = htol16(proxd_iovsize); /* caller may adjust it based on #of TLVs */
738 p_proxd_iov->cmd = htol16(cmdid);
739 p_proxd_iov->method = htol16(method);
740 p_proxd_iov->sid = htol16(session_id);
741
742 /* initialize the reserved/dummy-TLV in iovar header */
743 p_tlv = p_proxd_iov->tlvs;
744 p_tlv->id = htol16(WL_PROXD_TLV_ID_NONE);
745 p_tlv->len = htol16(0);
746
747 *p_out_bufsize = proxd_iovsize; /* for caller's reference */
748
749 return p_proxd_iov;
750 }
751
752
753 static int
754 dhd_rtt_common_get_handler(dhd_pub_t *dhd, ftm_subcmd_info_t *p_subcmd_info,
755 wl_proxd_method_t method,
756 wl_proxd_session_id_t session_id)
757 {
758 int status = BCME_OK;
759 uint16 proxd_iovsize = 0;
760 wl_proxd_iov_t *p_proxd_iov;
761 #ifdef RTT_DEBUG
762 DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
763 __FUNCTION__, method, session_id, p_subcmd_info->cmdid,
764 ftm_cmdid_to_str(p_subcmd_info->cmdid)));
765 #endif
766 /* alloc mem for ioctl headr + reserved 0 bufsize for tlvs (initialize to zero) */
767 p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
768 0, &proxd_iovsize);
769
770 if (p_proxd_iov == NULL)
771 return BCME_NOMEM;
772
773 status = rtt_do_get_ioctl(dhd, p_proxd_iov, proxd_iovsize, p_subcmd_info);
774
775 if (status != BCME_OK) {
776 DHD_RTT(("%s failed: status=%d\n", __FUNCTION__, status));
777 }
778 kfree(p_proxd_iov);
779 return status;
780 }
781
782 /*
783 * common handler for set-related proxd method commands which require no TLV as input
784 * wl proxd ftm [session-id] <set-subcmd>
785 * e.g.
786 * wl proxd ftm enable -- to enable ftm
787 * wl proxd ftm disable -- to disable ftm
788 * wl proxd ftm <session-id> start -- to start a specified session
789 * wl proxd ftm <session-id> stop -- to cancel a specified session;
790 * state is maintained till session is delete.
791 * wl proxd ftm <session-id> delete -- to delete a specified session
792 * wl proxd ftm [<session-id>] clear-counters -- to clear counters
793 * wl proxd ftm <session-id> burst-request -- on initiator: to send burst request;
794 * on target: send FTM frame
795 * wl proxd ftm <session-id> collect
796 * wl proxd ftm tune (TBD)
797 */
798 static int
799 dhd_rtt_common_set_handler(dhd_pub_t *dhd, const ftm_subcmd_info_t *p_subcmd_info,
800 wl_proxd_method_t method, wl_proxd_session_id_t session_id)
801 {
802 uint16 proxd_iovsize;
803 wl_proxd_iov_t *p_proxd_iov;
804 int ret;
805
806 #ifdef RTT_DEBUG
807 DHD_RTT(("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
808 __FUNCTION__, method, session_id, p_subcmd_info->cmdid,
809 ftm_cmdid_to_str(p_subcmd_info->cmdid)));
810 #endif
811
812 /* allocate and initialize a temp buffer for 'set proxd' iovar */
813 proxd_iovsize = 0;
814 p_proxd_iov = rtt_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
815 0, &proxd_iovsize); /* no TLV */
816 if (p_proxd_iov == NULL)
817 return BCME_NOMEM;
818
819 /* no TLV to pack, simply issue a set-proxd iovar */
820 ret = dhd_iovar(dhd, 0, "proxd", (char *)p_proxd_iov, proxd_iovsize, NULL, 0, TRUE);
821 #ifdef RTT_DEBUG
822 if (ret != BCME_OK) {
823 DHD_RTT(("error: IOVAR failed, status=%d\n", ret));
824 }
825 #endif
826 /* clean up */
827 kfree(p_proxd_iov);
828
829 return ret;
830 }
831 #endif /* WL_CFG80211 */
832
833 static int
834 rtt_unpack_xtlv_cbfn(void *ctx, uint8 *p_data, uint16 tlvid, uint16 len)
835 {
836 int ret = BCME_OK;
837 int i;
838 wl_proxd_ftm_session_status_t *p_data_info = NULL;
839 wl_proxd_collect_event_data_t *p_collect_data = NULL;
840 uint32 chan_data_entry = 0;
841
842 switch (tlvid) {
843 case WL_PROXD_TLV_ID_RTT_RESULT:
844 ret = dhd_rtt_convert_results_to_host((rtt_report_t *)ctx,
845 p_data, tlvid, len);
846 break;
847 case WL_PROXD_TLV_ID_SESSION_STATUS:
848 DHD_RTT(("WL_PROXD_TLV_ID_SESSION_STATUS\n"));
849 memcpy(ctx, p_data, sizeof(wl_proxd_ftm_session_status_t));
850 p_data_info = (wl_proxd_ftm_session_status_t *)ctx;
851 p_data_info->sid = ltoh16_ua(&p_data_info->sid);
852 p_data_info->state = ltoh16_ua(&p_data_info->state);
853 p_data_info->status = ltoh32_ua(&p_data_info->status);
854 p_data_info->burst_num = ltoh16_ua(&p_data_info->burst_num);
855 DHD_RTT(("\tsid=%u, state=%d, status=%d, burst_num=%u\n",
856 p_data_info->sid, p_data_info->state,
857 p_data_info->status, p_data_info->burst_num));
858
859 break;
860 case WL_PROXD_TLV_ID_COLLECT_DATA:
861 DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_DATA\n"));
862 memcpy(ctx, p_data, sizeof(wl_proxd_collect_event_data_t));
863 p_collect_data = (wl_proxd_collect_event_data_t *)ctx;
864 DHD_RTT(("\tH_RX\n"));
865 for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
866 p_collect_data->H_RX[i] = ltoh32_ua(&p_collect_data->H_RX[i]);
867 DHD_RTT(("\t%u\n", p_collect_data->H_RX[i]));
868 }
869 DHD_RTT(("\n"));
870 DHD_RTT(("\tH_LB\n"));
871 for (i = 0; i < K_TOF_COLLECT_H_SIZE_20MHZ; i++) {
872 p_collect_data->H_LB[i] = ltoh32_ua(&p_collect_data->H_LB[i]);
873 DHD_RTT(("\t%u\n", p_collect_data->H_LB[i]));
874 }
875 DHD_RTT(("\n"));
876 DHD_RTT(("\tri_rr\n"));
877 for (i = 0; i < FTM_TPK_RI_RR_LEN; i++) {
878 DHD_RTT(("\t%u\n", p_collect_data->ri_rr[i]));
879 }
880 p_collect_data->phy_err_mask = ltoh32_ua(&p_collect_data->phy_err_mask);
881 DHD_RTT(("\tphy_err_mask=0x%x\n", p_collect_data->phy_err_mask));
882 break;
883 case WL_PROXD_TLV_ID_COLLECT_CHAN_DATA:
884 DHD_RTT(("WL_PROXD_TLV_ID_COLLECT_CHAN_DATA\n"));
885 DHD_RTT(("\tchan est %u\n", (uint32) (len / sizeof(uint32))));
886 for (i = 0; i < (len/sizeof(chan_data_entry)); i++) {
887 uint32 *p = (uint32*)p_data;
888 chan_data_entry = ltoh32_ua(p + i);
889 DHD_RTT(("\t%u\n", chan_data_entry));
890 }
891 break;
892 default:
893 DHD_ERROR(("> Unsupported TLV ID %d\n", tlvid));
894 ret = BCME_ERROR;
895 break;
896 }
897
898 return ret;
899 }
900
901 #ifdef WL_CFG80211
902 static int
903 rtt_handle_config_options(wl_proxd_session_id_t session_id, wl_proxd_tlv_t **p_tlv,
904 uint16 *p_buf_space_left, ftm_config_options_info_t *ftm_configs, int ftm_cfg_cnt)
905 {
906 int ret = BCME_OK;
907 int cfg_idx = 0;
908 uint32 flags = WL_PROXD_FLAG_NONE;
909 uint32 flags_mask = WL_PROXD_FLAG_NONE;
910 uint32 new_mask; /* cmdline input */
911 ftm_config_options_info_t *p_option_info;
912 uint16 type = (session_id == WL_PROXD_SESSION_ID_GLOBAL) ?
913 WL_PROXD_TLV_ID_FLAGS_MASK : WL_PROXD_TLV_ID_SESSION_FLAGS_MASK;
914 for (cfg_idx = 0; cfg_idx < ftm_cfg_cnt; cfg_idx++) {
915 p_option_info = (ftm_configs + cfg_idx);
916 if (p_option_info != NULL) {
917 new_mask = p_option_info->flags;
918 /* update flags mask */
919 flags_mask |= new_mask;
920 if (p_option_info->enable) {
921 flags |= new_mask; /* set the bit on */
922 } else {
923 flags &= ~new_mask; /* set the bit off */
924 }
925 }
926 }
927 flags = htol32(flags);
928 flags_mask = htol32(flags_mask);
929 /* setup flags_mask TLV */
930 ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
931 type, sizeof(uint32), &flags_mask, BCM_XTLV_OPTION_ALIGN32);
932 if (ret != BCME_OK) {
933 DHD_ERROR(("%s : bcm_pack_xltv_entry() for mask flags failed, status=%d\n",
934 __FUNCTION__, ret));
935 goto exit;
936 }
937
938 type = (session_id == WL_PROXD_SESSION_ID_GLOBAL)?
939 WL_PROXD_TLV_ID_FLAGS : WL_PROXD_TLV_ID_SESSION_FLAGS;
940 /* setup flags TLV */
941 ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
942 type, sizeof(uint32), &flags, BCM_XTLV_OPTION_ALIGN32);
943 if (ret != BCME_OK) {
944 #ifdef RTT_DEBUG
945 DHD_RTT(("%s: bcm_pack_xltv_entry() for flags failed, status=%d\n",
946 __FUNCTION__, ret));
947 #endif
948 }
949 exit:
950 return ret;
951 }
952
953 static int
954 rtt_handle_config_general(wl_proxd_session_id_t session_id, wl_proxd_tlv_t **p_tlv,
955 uint16 *p_buf_space_left, ftm_config_param_info_t *ftm_configs, int ftm_cfg_cnt)
956 {
957 int ret = BCME_OK;
958 int cfg_idx = 0;
959 uint32 chanspec;
960 ftm_config_param_info_t *p_config_param_info;
961 void *p_src_data;
962 uint16 src_data_size; /* size of data pointed by p_src_data as 'source' */
963 for (cfg_idx = 0; cfg_idx < ftm_cfg_cnt; cfg_idx++) {
964 p_config_param_info = (ftm_configs + cfg_idx);
965 if (p_config_param_info != NULL) {
966 switch (p_config_param_info->tlvid) {
967 case WL_PROXD_TLV_ID_BSS_INDEX:
968 case WL_PROXD_TLV_ID_FTM_RETRIES:
969 case WL_PROXD_TLV_ID_FTM_REQ_RETRIES:
970 p_src_data = &p_config_param_info->data8;
971 src_data_size = sizeof(uint8);
972 break;
973 case WL_PROXD_TLV_ID_BURST_NUM_FTM: /* uint16 */
974 case WL_PROXD_TLV_ID_NUM_BURST:
975 case WL_PROXD_TLV_ID_RX_MAX_BURST:
976 p_src_data = &p_config_param_info->data16;
977 src_data_size = sizeof(uint16);
978 break;
979 case WL_PROXD_TLV_ID_TX_POWER: /* uint32 */
980 case WL_PROXD_TLV_ID_RATESPEC:
981 case WL_PROXD_TLV_ID_EVENT_MASK: /* wl_proxd_event_mask_t/uint32 */
982 case WL_PROXD_TLV_ID_DEBUG_MASK:
983 p_src_data = &p_config_param_info->data32;
984 src_data_size = sizeof(uint32);
985 break;
986 case WL_PROXD_TLV_ID_CHANSPEC: /* chanspec_t --> 32bit */
987 chanspec = p_config_param_info->chanspec;
988 p_src_data = (void *) &chanspec;
989 src_data_size = sizeof(uint32);
990 break;
991 case WL_PROXD_TLV_ID_BSSID: /* mac address */
992 case WL_PROXD_TLV_ID_PEER_MAC:
993 p_src_data = &p_config_param_info->mac_addr;
994 src_data_size = sizeof(struct ether_addr);
995 break;
996 case WL_PROXD_TLV_ID_BURST_DURATION: /* wl_proxd_intvl_t */
997 case WL_PROXD_TLV_ID_BURST_PERIOD:
998 case WL_PROXD_TLV_ID_BURST_FTM_SEP:
999 case WL_PROXD_TLV_ID_BURST_TIMEOUT:
1000 case WL_PROXD_TLV_ID_INIT_DELAY:
1001 p_src_data = &p_config_param_info->data_intvl;
1002 src_data_size = sizeof(wl_proxd_intvl_t);
1003 break;
1004 default:
1005 ret = BCME_BADARG;
1006 break;
1007 }
1008 if (ret != BCME_OK) {
1009 DHD_ERROR(("%s bad TLV ID : %d\n",
1010 __FUNCTION__, p_config_param_info->tlvid));
1011 break;
1012 }
1013
1014 ret = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left,
1015 p_config_param_info->tlvid, src_data_size, p_src_data,
1016 BCM_XTLV_OPTION_ALIGN32);
1017 if (ret != BCME_OK) {
1018 DHD_ERROR(("%s: bcm_pack_xltv_entry() failed,"
1019 " status=%d\n", __FUNCTION__, ret));
1020 break;
1021 }
1022
1023 }
1024 }
1025 return ret;
1026 }
1027
1028 static int
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
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
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
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
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
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
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
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
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
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 }
1482 dhd_set_rand_mac_oui(dhd);
1483 dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_GENERAL,
1484 ftm_params, ftm_param_cnt);
1485
1486 err = dhd_rtt_start_session(dhd, FTM_DEFAULT_SESSION, TRUE);
1487 if (err) {
1488 DHD_ERROR(("failed to start session of FTM : error %d\n", err));
1489 }
1490 exit:
1491 if (err) {
1492 DHD_ERROR(("rtt is stopped %s \n", __FUNCTION__));
1493 rtt_status->status = RTT_STOPPED;
1494 /* disable FTM */
1495 dhd_rtt_ftm_enable(dhd, FALSE);
1496 if (rtt_status->pm_restore) {
1497 DHD_ERROR(("pm_restore =%d func =%s \n",
1498 rtt_status->pm_restore, __FUNCTION__));
1499 pm = PM_FAST;
1500 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
1501 if (err) {
1502 DHD_ERROR(("Failed to set PM \n"));
1503 } else {
1504 rtt_status->pm_restore = FALSE;
1505 }
1506 }
1507 }
1508 return err;
1509 }
1510 #endif /* WL_CFG80211 */
1511
1512 int
1513 dhd_rtt_register_noti_callback(dhd_pub_t *dhd, void *ctx, dhd_rtt_compl_noti_fn noti_fn)
1514 {
1515 int err = BCME_OK;
1516 struct rtt_noti_callback *cb = NULL, *iter;
1517 rtt_status_info_t *rtt_status;
1518 NULL_CHECK(dhd, "dhd is NULL", err);
1519 NULL_CHECK(noti_fn, "noti_fn is NULL", err);
1520
1521 rtt_status = GET_RTTSTATE(dhd);
1522 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1523 spin_lock_bh(&noti_list_lock);
1524 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1525 #pragma GCC diagnostic push
1526 #pragma GCC diagnostic ignored "-Wcast-qual"
1527 #endif
1528 list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
1529 if (iter->noti_fn == noti_fn) {
1530 goto exit;
1531 }
1532 }
1533 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1534 #pragma GCC diagnostic pop
1535 #endif
1536 cb = kmalloc(sizeof(struct rtt_noti_callback), GFP_ATOMIC);
1537 if (!cb) {
1538 err = -ENOMEM;
1539 goto exit;
1540 }
1541 cb->noti_fn = noti_fn;
1542 cb->ctx = ctx;
1543 list_add(&cb->list, &rtt_status->noti_fn_list);
1544 exit:
1545 spin_unlock_bh(&noti_list_lock);
1546 return err;
1547 }
1548
1549 int
1550 dhd_rtt_unregister_noti_callback(dhd_pub_t *dhd, dhd_rtt_compl_noti_fn noti_fn)
1551 {
1552 int err = BCME_OK;
1553 struct rtt_noti_callback *cb = NULL, *iter;
1554 rtt_status_info_t *rtt_status;
1555 NULL_CHECK(dhd, "dhd is NULL", err);
1556 NULL_CHECK(noti_fn, "noti_fn is NULL", err);
1557 rtt_status = GET_RTTSTATE(dhd);
1558 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
1559 spin_lock_bh(&noti_list_lock);
1560 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1561 #pragma GCC diagnostic push
1562 #pragma GCC diagnostic ignored "-Wcast-qual"
1563 #endif
1564 list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
1565 if (iter->noti_fn == noti_fn) {
1566 cb = iter;
1567 list_del(&cb->list);
1568 break;
1569 }
1570 }
1571 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1572 #pragma GCC diagnostic pop
1573 #endif
1574
1575 spin_unlock_bh(&noti_list_lock);
1576 if (cb) {
1577 kfree(cb);
1578 }
1579 return err;
1580 }
1581
1582 static wifi_rate_t
1583 dhd_rtt_convert_rate_to_host(uint32 rspec)
1584 {
1585 wifi_rate_t host_rate;
1586 memset(&host_rate, 0, sizeof(wifi_rate_t));
1587 if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_RATE) {
1588 host_rate.preamble = 0;
1589 } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_HT) {
1590 host_rate.preamble = 2;
1591 host_rate.rateMcsIdx = rspec & WL_RSPEC_RATE_MASK;
1592 } else if ((rspec & WL_RSPEC_ENCODING_MASK) == WL_RSPEC_ENCODE_VHT) {
1593 host_rate.preamble = 3;
1594 host_rate.rateMcsIdx = rspec & WL_RSPEC_VHT_MCS_MASK;
1595 host_rate.nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT;
1596 }
1597 host_rate.bw = (rspec & WL_RSPEC_BW_MASK) - 1;
1598 host_rate.bitrate = rate_rspec2rate(rspec) / 100; /* 100kbps */
1599 DHD_RTT(("bit rate : %d\n", host_rate.bitrate));
1600 return host_rate;
1601 }
1602
1603
1604 static int
1605 dhd_rtt_convert_results_to_host(rtt_report_t *rtt_report, uint8 *p_data, uint16 tlvid, uint16 len)
1606 {
1607 int err = BCME_OK;
1608 char eabuf[ETHER_ADDR_STR_LEN];
1609 wl_proxd_rtt_result_t *p_data_info;
1610 wl_proxd_result_flags_t flags;
1611 wl_proxd_session_state_t session_state;
1612 wl_proxd_status_t proxd_status;
1613 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
1614 struct timespec ts;
1615 #endif /* LINUX_VER >= 2.6.39 */
1616 uint32 ratespec;
1617 uint32 avg_dist;
1618 wl_proxd_rtt_sample_t *p_sample;
1619 wl_proxd_intvl_t rtt;
1620 wl_proxd_intvl_t p_time;
1621
1622 NULL_CHECK(rtt_report, "rtt_report is NULL", err);
1623 NULL_CHECK(p_data, "p_data is NULL", err);
1624 DHD_RTT(("%s enter\n", __FUNCTION__));
1625 p_data_info = (wl_proxd_rtt_result_t *) p_data;
1626 /* unpack and format 'flags' for display */
1627 flags = ltoh16_ua(&p_data_info->flags);
1628
1629 /* session state and status */
1630 session_state = ltoh16_ua(&p_data_info->state);
1631 proxd_status = ltoh32_ua(&p_data_info->status);
1632 bcm_ether_ntoa((&(p_data_info->peer)), eabuf);
1633 ftm_session_state_value_to_logstr(session_state);
1634 ftm_status_value_to_logstr(proxd_status);
1635 DHD_RTT((">\tTarget(%s) session state=%d(%s), status=%d(%s)\n",
1636 eabuf,
1637 session_state,
1638 ftm_session_state_value_to_logstr(session_state),
1639 proxd_status,
1640 ftm_status_value_to_logstr(proxd_status)));
1641
1642 /* show avg_dist (1/256m units), burst_num */
1643 avg_dist = ltoh32_ua(&p_data_info->avg_dist);
1644 if (avg_dist == 0xffffffff) { /* report 'failure' case */
1645 DHD_RTT((">\tavg_dist=-1m, burst_num=%d, valid_measure_cnt=%d\n",
1646 ltoh16_ua(&p_data_info->burst_num),
1647 p_data_info->num_valid_rtt)); /* in a session */
1648 avg_dist = FTM_INVALID;
1649 }
1650 else {
1651 DHD_RTT((">\tavg_dist=%d.%04dm, burst_num=%d, valid_measure_cnt=%d num_ftm=%d\n",
1652 avg_dist >> 8, /* 1/256m units */
1653 ((avg_dist & 0xff) * 625) >> 4,
1654 ltoh16_ua(&p_data_info->burst_num),
1655 p_data_info->num_valid_rtt,
1656 p_data_info->num_ftm)); /* in a session */
1657 }
1658 /* show 'avg_rtt' sample */
1659 p_sample = &p_data_info->avg_rtt;
1660 ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu));
1661 DHD_RTT((">\tavg_rtt sample: rssi=%d rtt=%d%s std_deviation =%d.%d ratespec=0x%08x\n",
1662 (int16) ltoh16_ua(&p_sample->rssi),
1663 ltoh32_ua(&p_sample->rtt.intvl),
1664 ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)),
1665 ltoh16_ua(&p_data_info->sd_rtt)/10, ltoh16_ua(&p_data_info->sd_rtt)%10,
1666 ltoh32_ua(&p_sample->ratespec)));
1667
1668 /* set peer address */
1669 rtt_report->addr = p_data_info->peer;
1670 /* burst num */
1671 rtt_report->burst_num = ltoh16_ua(&p_data_info->burst_num);
1672 /* success num */
1673 rtt_report->success_num = p_data_info->num_valid_rtt;
1674 /* actual number of FTM supported by peer */
1675 rtt_report->num_per_burst_peer = p_data_info->num_ftm;
1676 rtt_report->negotiated_burst_num = p_data_info->num_ftm;
1677 /* status */
1678 rtt_report->status = ftm_get_statusmap_info(proxd_status,
1679 &ftm_status_map_info[0], ARRAYSIZE(ftm_status_map_info));
1680
1681 /* rssi (0.5db) */
1682 rtt_report->rssi = ABS((wl_proxd_rssi_t)ltoh16_ua(&p_data_info->avg_rtt.rssi)) * 2;
1683
1684 /* rx rate */
1685 ratespec = ltoh32_ua(&p_data_info->avg_rtt.ratespec);
1686 rtt_report->rx_rate = dhd_rtt_convert_rate_to_host(ratespec);
1687 /* tx rate */
1688 if (flags & WL_PROXD_RESULT_FLAG_VHTACK) {
1689 rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0x2010010);
1690 } else {
1691 rtt_report->tx_rate = dhd_rtt_convert_rate_to_host(0xc);
1692 }
1693 /* rtt_sd */
1694 rtt.tmu = ltoh16_ua(&p_data_info->avg_rtt.rtt.tmu);
1695 rtt.intvl = ltoh32_ua(&p_data_info->avg_rtt.rtt.intvl);
1696 rtt_report->rtt = (wifi_timespan)FTM_INTVL2NSEC(&rtt) * 1000; /* nano -> pico seconds */
1697 rtt_report->rtt_sd = ltoh16_ua(&p_data_info->sd_rtt); /* nano -> 0.1 nano */
1698 DHD_RTT(("rtt_report->rtt : %llu\n", rtt_report->rtt));
1699 DHD_RTT(("rtt_report->rssi : %d (0.5db)\n", rtt_report->rssi));
1700
1701 /* average distance */
1702 if (avg_dist != FTM_INVALID) {
1703 rtt_report->distance = (avg_dist >> 8) * 1000; /* meter -> mm */
1704 rtt_report->distance += (avg_dist & 0xff) * 1000 / 256;
1705 } else {
1706 rtt_report->distance = FTM_INVALID;
1707 }
1708 /* time stamp */
1709 /* get the time elapsed from boot time */
1710 #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39))
1711 get_monotonic_boottime(&ts);
1712 rtt_report->ts = (uint64)TIMESPEC_TO_US(ts);
1713 #endif /* LINUX_VER >= 2.6.39 */
1714
1715 if (proxd_status == WL_PROXD_E_REMOTE_FAIL) {
1716 /* retry time after failure */
1717 p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
1718 p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
1719 rtt_report->retry_after_duration = FTM_INTVL2SEC(&p_time); /* s -> s */
1720 DHD_RTT((">\tretry_after: %d%s\n",
1721 ltoh32_ua(&p_data_info->u.retry_after.intvl),
1722 ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.retry_after.tmu))));
1723 } else {
1724 /* burst duration */
1725 p_time.intvl = ltoh32_ua(&p_data_info->u.retry_after.intvl);
1726 p_time.tmu = ltoh16_ua(&p_data_info->u.retry_after.tmu);
1727 rtt_report->burst_duration = FTM_INTVL2MSEC(&p_time); /* s -> ms */
1728 DHD_RTT((">\tburst_duration: %d%s\n",
1729 ltoh32_ua(&p_data_info->u.burst_duration.intvl),
1730 ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.burst_duration.tmu))));
1731 DHD_RTT(("rtt_report->burst_duration : %d\n", rtt_report->burst_duration));
1732 }
1733 return err;
1734 }
1735
1736 int
1737 dhd_rtt_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
1738 {
1739 int ret = BCME_OK;
1740 int tlvs_len;
1741 uint16 version;
1742 wl_proxd_event_t *p_event;
1743 wl_proxd_event_type_t event_type;
1744 wl_proxd_ftm_session_status_t session_status;
1745 wl_proxd_collect_event_data_t *collect_event_data;
1746 const ftm_strmap_entry_t *p_loginfo;
1747 rtt_result_t *rtt_result;
1748 gfp_t kflags;
1749 #ifdef WL_CFG80211
1750 int idx;
1751 struct rtt_noti_callback *iter;
1752 bool is_new = TRUE;
1753 rtt_status_info_t *rtt_status;
1754 rtt_result_t *next2;
1755 rtt_results_header_t *next = NULL;
1756 rtt_target_info_t *rtt_target_info;
1757 rtt_results_header_t *entry, *rtt_results_header = NULL;
1758 #endif /* WL_CFG80211 */
1759
1760 DHD_RTT(("Enter %s \n", __FUNCTION__));
1761 NULL_CHECK(dhd, "dhd is NULL", ret);
1762
1763 #ifdef WL_CFG80211
1764 rtt_status = GET_RTTSTATE(dhd);
1765 NULL_CHECK(rtt_status, "rtt_status is NULL", ret);
1766
1767 if (RTT_IS_STOPPED(rtt_status)) {
1768 /* Ignore the Proxd event */
1769 DHD_RTT((" event handler rtt is stopped \n"));
1770 if (rtt_status->flags == WL_PROXD_SESSION_FLAG_TARGET) {
1771 DHD_RTT(("Device is target/Responder. Recv the event. \n"));
1772 } else {
1773 return ret;
1774 }
1775 }
1776 #endif /* WL_CFG80211 */
1777 if (ntoh32_ua((void *)&event->datalen) < OFFSETOF(wl_proxd_event_t, tlvs)) {
1778 DHD_RTT(("%s: wrong datalen:%d\n", __FUNCTION__,
1779 ntoh32_ua((void *)&event->datalen)));
1780 return -EINVAL;
1781 }
1782 event_type = ntoh32_ua((void *)&event->event_type);
1783 if (event_type != WLC_E_PROXD) {
1784 DHD_ERROR((" failed event \n"));
1785 return -EINVAL;
1786 }
1787
1788 if (!event_data) {
1789 DHD_ERROR(("%s: event_data:NULL\n", __FUNCTION__));
1790 return -EINVAL;
1791 }
1792 p_event = (wl_proxd_event_t *) event_data;
1793 version = ltoh16(p_event->version);
1794 if (version < WL_PROXD_API_VERSION) {
1795 DHD_ERROR(("ignore non-ftm event version = 0x%0x < WL_PROXD_API_VERSION (0x%x)\n",
1796 version, WL_PROXD_API_VERSION));
1797 return ret;
1798 }
1799 #ifdef WL_CFG80211
1800 if (!in_atomic()) {
1801 mutex_lock(&rtt_status->rtt_mutex);
1802 }
1803 #endif /* WL_CFG80211 */
1804 event_type = (wl_proxd_event_type_t) ltoh16(p_event->type);
1805
1806 kflags = in_softirq()? GFP_ATOMIC : GFP_KERNEL;
1807
1808 DHD_RTT(("event_type=0x%x, ntoh16()=0x%x, ltoh16()=0x%x\n",
1809 p_event->type, ntoh16(p_event->type), ltoh16(p_event->type)));
1810 p_loginfo = ftm_get_event_type_loginfo(event_type);
1811 if (p_loginfo == NULL) {
1812 DHD_ERROR(("receive an invalid FTM event %d\n", event_type));
1813 ret = -EINVAL;
1814 goto exit; /* ignore this event */
1815 }
1816 /* get TLVs len, skip over event header */
1817 if (ltoh16(p_event->len) < OFFSETOF(wl_proxd_event_t, tlvs)) {
1818 DHD_ERROR(("invalid FTM event length:%d\n", ltoh16(p_event->len)));
1819 ret = -EINVAL;
1820 goto exit;
1821 }
1822 tlvs_len = ltoh16(p_event->len) - OFFSETOF(wl_proxd_event_t, tlvs);
1823 DHD_RTT(("receive '%s' event: version=0x%x len=%d method=%d sid=%d tlvs_len=%d\n",
1824 p_loginfo->text,
1825 version,
1826 ltoh16(p_event->len),
1827 ltoh16(p_event->method),
1828 ltoh16(p_event->sid),
1829 tlvs_len));
1830 #ifdef WL_CFG80211
1831 rtt_target_info = &rtt_status->rtt_config.target_info[rtt_status->cur_idx];
1832 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1833 #pragma GCC diagnostic push
1834 #pragma GCC diagnostic ignored "-Wcast-qual"
1835 #endif
1836 /* find a rtt_report_header for this mac address */
1837 list_for_each_entry(entry, &rtt_status->rtt_results_cache, list) {
1838 if (!memcmp(&entry->peer_mac, &event->addr, ETHER_ADDR_LEN)) {
1839 /* found a rtt_report_header for peer_mac in the list */
1840 is_new = FALSE;
1841 rtt_results_header = entry;
1842 break;
1843 }
1844 }
1845 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1846 #pragma GCC diagnostic pop
1847 #endif
1848 #endif /* WL_CFG80211 */
1849 switch (event_type) {
1850 case WL_PROXD_EVENT_SESSION_CREATE:
1851 DHD_RTT(("WL_PROXD_EVENT_SESSION_CREATE\n"));
1852 break;
1853 case WL_PROXD_EVENT_SESSION_START:
1854 DHD_RTT(("WL_PROXD_EVENT_SESSION_START\n"));
1855 break;
1856 case WL_PROXD_EVENT_BURST_START:
1857 DHD_RTT(("WL_PROXD_EVENT_BURST_START\n"));
1858 break;
1859 case WL_PROXD_EVENT_BURST_END:
1860 DHD_RTT(("WL_PROXD_EVENT_BURST_END\n"));
1861 #ifdef WL_CFG80211
1862 if (is_new) {
1863 /* allocate new header for rtt_results */
1864 rtt_results_header = kzalloc(sizeof(rtt_results_header_t), kflags);
1865 if (!rtt_results_header) {
1866 ret = -ENOMEM;
1867 goto exit;
1868 }
1869 /* Initialize the head of list for rtt result */
1870 INIT_LIST_HEAD(&rtt_results_header->result_list);
1871 rtt_results_header->peer_mac = event->addr;
1872 list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache);
1873 }
1874 #endif /* WL_CFG80211 */
1875 if (tlvs_len > 0) {
1876 /* allocate rtt_results for new results */
1877 rtt_result = kzalloc(sizeof(rtt_result_t), kflags);
1878 if (!rtt_result) {
1879 ret = -ENOMEM;
1880 goto exit;
1881 }
1882 /* unpack TLVs and invokes the cbfn to print the event content TLVs */
1883 ret = bcm_unpack_xtlv_buf((void *) &(rtt_result->report),
1884 (uint8 *)&p_event->tlvs[0], tlvs_len,
1885 BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn);
1886 if (ret != BCME_OK) {
1887 DHD_ERROR(("%s : Failed to unpack xtlv for an event\n",
1888 __FUNCTION__));
1889 goto exit;
1890 }
1891 #ifdef WL_CFG80211
1892 /* fill out the results from the configuration param */
1893 rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst;
1894 rtt_result->report.type = RTT_TWO_WAY;
1895 DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num));
1896 rtt_result->report_len = RTT_REPORT_SIZE;
1897
1898 list_add_tail(&rtt_result->list, &rtt_results_header->result_list);
1899 rtt_results_header->result_cnt++;
1900 rtt_results_header->result_tot_len += rtt_result->report_len;
1901 #endif /* WL_CFG80211 */
1902 }
1903 break;
1904 case WL_PROXD_EVENT_SESSION_END:
1905 DHD_RTT(("WL_PROXD_EVENT_SESSION_END\n"));
1906 #ifdef WL_CFG80211
1907 if (!RTT_IS_ENABLED(rtt_status)) {
1908 DHD_RTT(("Ignore the session end evt\n"));
1909 goto exit;
1910 }
1911 #endif /* WL_CFG80211 */
1912 if (tlvs_len > 0) {
1913 /* unpack TLVs and invokes the cbfn to print the event content TLVs */
1914 ret = bcm_unpack_xtlv_buf((void *) &session_status,
1915 (uint8 *)&p_event->tlvs[0], tlvs_len,
1916 BCM_XTLV_OPTION_ALIGN32, rtt_unpack_xtlv_cbfn);
1917 if (ret != BCME_OK) {
1918 DHD_ERROR(("%s : Failed to unpack xtlv for an event\n",
1919 __FUNCTION__));
1920 goto exit;
1921 }
1922 }
1923 #ifdef WL_CFG80211
1924 /* In case of no result for the peer device, make fake result for error case */
1925 if (is_new) {
1926 /* allocate new header for rtt_results */
1927 rtt_results_header = kzalloc(sizeof(rtt_results_header_t), GFP_KERNEL);
1928 if (!rtt_results_header) {
1929 ret = -ENOMEM;
1930 goto exit;
1931 }
1932 /* Initialize the head of list for rtt result */
1933 INIT_LIST_HEAD(&rtt_results_header->result_list);
1934 rtt_results_header->peer_mac = event->addr;
1935 list_add_tail(&rtt_results_header->list, &rtt_status->rtt_results_cache);
1936
1937 /* allocate rtt_results for new results */
1938 rtt_result = kzalloc(sizeof(rtt_result_t), kflags);
1939 if (!rtt_result) {
1940 ret = -ENOMEM;
1941 kfree(rtt_results_header);
1942 goto exit;
1943 }
1944 /* fill out the results from the configuration param */
1945 rtt_result->report.ftm_num = rtt_target_info->num_frames_per_burst;
1946 rtt_result->report.type = RTT_TWO_WAY;
1947 DHD_RTT(("report->ftm_num : %d\n", rtt_result->report.ftm_num));
1948 rtt_result->report_len = RTT_REPORT_SIZE;
1949 rtt_result->report.status = RTT_REASON_FAIL_NO_RSP;
1950 rtt_result->report.addr = rtt_target_info->addr;
1951 rtt_result->report.distance = FTM_INVALID;
1952 list_add_tail(&rtt_result->list, &rtt_results_header->result_list);
1953 rtt_results_header->result_cnt++;
1954 rtt_results_header->result_tot_len += rtt_result->report_len;
1955 }
1956 /* find next target to trigger RTT */
1957 for (idx = (rtt_status->cur_idx + 1);
1958 idx < rtt_status->rtt_config.rtt_target_cnt; idx++) {
1959 /* skip the disabled device */
1960 if (rtt_status->rtt_config.target_info[idx].disable) {
1961 continue;
1962 } else {
1963 /* set the idx to cur_idx */
1964 rtt_status->cur_idx = idx;
1965 break;
1966 }
1967 }
1968 if (idx < rtt_status->rtt_config.rtt_target_cnt) {
1969 /* restart to measure RTT from next device */
1970 DHD_ERROR(("restart to measure rtt\n"));
1971 schedule_work(&rtt_status->work);
1972 } else {
1973 DHD_RTT(("RTT_STOPPED\n"));
1974 rtt_status->status = RTT_STOPPED;
1975 /* to turn on mpc mode */
1976 schedule_work(&rtt_status->work);
1977 /* notify the completed information to others */
1978 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
1979 #pragma GCC diagnostic push
1980 #pragma GCC diagnostic ignored "-Wcast-qual"
1981 #endif
1982 list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
1983 iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache);
1984 }
1985 /* remove the rtt results in cache */
1986 if (!list_empty(&rtt_status->rtt_results_cache)) {
1987 /* Iterate rtt_results_header list */
1988 list_for_each_entry_safe(entry, next,
1989 &rtt_status->rtt_results_cache, list) {
1990 list_del(&entry->list);
1991 /* Iterate rtt_result list */
1992 list_for_each_entry_safe(rtt_result, next2,
1993 &entry->result_list, list) {
1994 list_del(&rtt_result->list);
1995 kfree(rtt_result);
1996 }
1997 kfree(entry);
1998 }
1999 }
2000 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2001 #pragma GCC diagnostic pop
2002 #endif
2003 /* reinitialize the HEAD */
2004 INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
2005 /* clear information for rtt_config */
2006 rtt_status->rtt_config.rtt_target_cnt = 0;
2007 memset(rtt_status->rtt_config.target_info, 0,
2008 TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT));
2009 rtt_status->cur_idx = 0;
2010 }
2011 #endif /* WL_CFG80211 */
2012 break;
2013 case WL_PROXD_EVENT_SESSION_RESTART:
2014 DHD_RTT(("WL_PROXD_EVENT_SESSION_RESTART\n"));
2015 break;
2016 case WL_PROXD_EVENT_BURST_RESCHED:
2017 DHD_RTT(("WL_PROXD_EVENT_BURST_RESCHED\n"));
2018 break;
2019 case WL_PROXD_EVENT_SESSION_DESTROY:
2020 DHD_RTT(("WL_PROXD_EVENT_SESSION_DESTROY\n"));
2021 break;
2022 case WL_PROXD_EVENT_FTM_FRAME:
2023 DHD_RTT(("WL_PROXD_EVENT_FTM_FRAME\n"));
2024 break;
2025 case WL_PROXD_EVENT_DELAY:
2026 DHD_RTT(("WL_PROXD_EVENT_DELAY\n"));
2027 break;
2028 case WL_PROXD_EVENT_VS_INITIATOR_RPT:
2029 DHD_RTT(("WL_PROXD_EVENT_VS_INITIATOR_RPT\n "));
2030 break;
2031 case WL_PROXD_EVENT_RANGING:
2032 DHD_RTT(("WL_PROXD_EVENT_RANGING\n"));
2033 break;
2034 case WL_PROXD_EVENT_COLLECT:
2035 DHD_RTT(("WL_PROXD_EVENT_COLLECT\n"));
2036 if (tlvs_len > 0) {
2037 collect_event_data = kzalloc(sizeof(wl_proxd_collect_event_data_t), kflags);
2038 if (!collect_event_data) {
2039 ret = -ENOMEM;
2040 goto exit;
2041 }
2042 /* unpack TLVs and invokes the cbfn to print the event content TLVs */
2043 ret = bcm_unpack_xtlv_buf((void *) collect_event_data,
2044 (uint8 *)&p_event->tlvs[0], tlvs_len,
2045 BCM_XTLV_OPTION_NONE, rtt_unpack_xtlv_cbfn);
2046 kfree(collect_event_data);
2047 if (ret != BCME_OK) {
2048 DHD_ERROR(("%s : Failed to unpack xtlv for an event\n",
2049 __FUNCTION__));
2050 goto exit;
2051 }
2052 }
2053 break;
2054
2055
2056 default:
2057 DHD_ERROR(("WLC_E_PROXD: not supported EVENT Type:%d\n", event_type));
2058 break;
2059 }
2060 exit:
2061 #ifdef WL_CFG80211
2062 if (!in_atomic()) {
2063 mutex_unlock(&rtt_status->rtt_mutex);
2064 }
2065 #endif /* WL_CFG80211 */
2066
2067 return ret;
2068 }
2069
2070 #ifdef WL_CFG80211
2071 static void
2072 dhd_rtt_work(struct work_struct *work)
2073 {
2074 rtt_status_info_t *rtt_status;
2075 dhd_pub_t *dhd;
2076 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2077 #pragma GCC diagnostic push
2078 #pragma GCC diagnostic ignored "-Wcast-qual"
2079 #endif
2080 rtt_status = container_of(work, rtt_status_info_t, work);
2081 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2082 #pragma GCC diagnostic pop
2083 #endif
2084 if (rtt_status == NULL) {
2085 DHD_ERROR(("%s : rtt_status is NULL\n", __FUNCTION__));
2086 return;
2087 }
2088 dhd = rtt_status->dhd;
2089 if (dhd == NULL) {
2090 DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__));
2091 return;
2092 }
2093 (void) dhd_rtt_start(dhd);
2094 }
2095 #endif /* WL_CFG80211 */
2096
2097 int
2098 dhd_rtt_capability(dhd_pub_t *dhd, rtt_capabilities_t *capa)
2099 {
2100 rtt_status_info_t *rtt_status;
2101 int err = BCME_OK;
2102 NULL_CHECK(dhd, "dhd is NULL", err);
2103 rtt_status = GET_RTTSTATE(dhd);
2104 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2105 NULL_CHECK(capa, "capa is NULL", err);
2106 bzero(capa, sizeof(rtt_capabilities_t));
2107
2108 /* set rtt capabilities */
2109 if (rtt_status->rtt_capa.proto & RTT_CAP_ONE_WAY)
2110 capa->rtt_one_sided_supported = 1;
2111 if (rtt_status->rtt_capa.proto & RTT_CAP_FTM_WAY)
2112 capa->rtt_ftm_supported = 1;
2113
2114 if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCI)
2115 capa->lci_support = 1;
2116 if (rtt_status->rtt_capa.feature & RTT_FEATURE_LCR)
2117 capa->lcr_support = 1;
2118 if (rtt_status->rtt_capa.feature & RTT_FEATURE_PREAMBLE)
2119 capa->preamble_support = 1;
2120 if (rtt_status->rtt_capa.feature & RTT_FEATURE_BW)
2121 capa->bw_support = 1;
2122
2123 /* bit mask */
2124 capa->preamble_support = rtt_status->rtt_capa.preamble;
2125 capa->bw_support = rtt_status->rtt_capa.bw;
2126
2127 return err;
2128 }
2129
2130 #ifdef WL_CFG80211
2131 int
2132 dhd_rtt_avail_channel(dhd_pub_t *dhd, wifi_channel_info *channel_info)
2133 {
2134 u32 chanspec = 0;
2135 int err = BCME_OK;
2136 chanspec_t c = 0;
2137 u32 channel;
2138 struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
2139
2140 if ((err = wldev_iovar_getint(dev, "chanspec",
2141 (s32 *)&chanspec)) == BCME_OK) {
2142 c = (chanspec_t)dtoh32(chanspec);
2143 c = wl_chspec_driver_to_host(c);
2144 channel = wf_chspec_ctlchan(c);
2145 DHD_RTT((" control channel is %d \n", channel));
2146 if (CHSPEC_IS20(c)) {
2147 channel_info->width = WIFI_CHAN_WIDTH_20;
2148 DHD_RTT((" band is 20 \n"));
2149 } else if (CHSPEC_IS40(c)) {
2150 channel_info->width = WIFI_CHAN_WIDTH_40;
2151 DHD_RTT(("band is 40 \n"));
2152 } else {
2153 channel_info->width = WIFI_CHAN_WIDTH_80;
2154 DHD_RTT(("band is 80 \n"));
2155 }
2156 if (CHSPEC_IS2G(c) && (channel >= CH_MIN_2G_CHANNEL) &&
2157 (channel <= CH_MAX_2G_CHANNEL)) {
2158 channel_info->center_freq =
2159 ieee80211_channel_to_frequency(channel, IEEE80211_BAND_2GHZ);
2160 } else if (CHSPEC_IS5G(c) && channel >= CH_MIN_5G_CHANNEL) {
2161 channel_info->center_freq =
2162 ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ);
2163 }
2164 if ((channel_info->width == WIFI_CHAN_WIDTH_80) ||
2165 (channel_info->width == WIFI_CHAN_WIDTH_40)) {
2166 channel = CHSPEC_CHANNEL(c);
2167 channel_info->center_freq0 =
2168 ieee80211_channel_to_frequency(channel, IEEE80211_BAND_5GHZ);
2169 }
2170 } else {
2171 DHD_ERROR(("Failed to get the chanspec \n"));
2172 }
2173 return err;
2174 }
2175
2176 int
2177 dhd_rtt_enable_responder(dhd_pub_t *dhd, wifi_channel_info *channel_info)
2178 {
2179 int err = BCME_OK;
2180 char chanbuf[CHANSPEC_STR_LEN];
2181 int pm = PM_OFF;
2182 int ftm_cfg_cnt = 0;
2183 chanspec_t chanspec;
2184 wifi_channel_info_t channel;
2185 struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
2186 ftm_config_options_info_t ftm_configs[FTM_MAX_CONFIGS];
2187 ftm_config_param_info_t ftm_params[FTM_MAX_PARAMS];
2188 rtt_status_info_t *rtt_status;
2189
2190 memset(&channel, 0, sizeof(channel));
2191 BCM_REFERENCE(chanbuf);
2192 NULL_CHECK(dhd, "dhd is NULL", err);
2193 rtt_status = GET_RTTSTATE(dhd);
2194 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2195 if (RTT_IS_STOPPED(rtt_status)) {
2196 DHD_RTT(("STA responder/Target. \n"));
2197 }
2198 DHD_RTT(("Enter %s \n", __FUNCTION__));
2199 if (!dhd_is_associated(dhd, 0, NULL)) {
2200 if (channel_info) {
2201 channel.width = channel_info->width;
2202 channel.center_freq = channel_info->center_freq;
2203 channel.center_freq0 = channel_info->center_freq;
2204 }
2205 else {
2206 channel.width = WIFI_CHAN_WIDTH_80;
2207 channel.center_freq = DEFAULT_FTM_FREQ;
2208 channel.center_freq0 = DEFAULT_FTM_CNTR_FREQ0;
2209 }
2210 chanspec = dhd_rtt_convert_to_chspec(channel);
2211 DHD_RTT(("chanspec/channel set as %s for rtt.\n",
2212 wf_chspec_ntoa(chanspec, chanbuf)));
2213 err = wldev_iovar_setint(dev, "chanspec", chanspec);
2214 if (err) {
2215 DHD_ERROR(("Failed to set the chanspec \n"));
2216 }
2217 }
2218 err = wldev_ioctl_get(dev, WLC_GET_PM, &rtt_status->pm, sizeof(rtt_status->pm));
2219 DHD_RTT(("Current PM value read %d\n", rtt_status->pm));
2220 if (err) {
2221 DHD_ERROR(("Failed to get the PM value \n"));
2222 } else {
2223 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2224 if (err) {
2225 DHD_ERROR(("Failed to set the PM \n"));
2226 rtt_status->pm_restore = FALSE;
2227 } else {
2228 rtt_status->pm_restore = TRUE;
2229 }
2230 }
2231 if (!RTT_IS_ENABLED(rtt_status)) {
2232 err = dhd_rtt_ftm_enable(dhd, TRUE);
2233 if (err) {
2234 DHD_ERROR(("Failed to enable FTM (%d)\n", err));
2235 goto exit;
2236 }
2237 DHD_RTT(("FTM enabled \n"));
2238 }
2239 rtt_status->status = RTT_ENABLED;
2240 DHD_RTT(("Responder enabled \n"));
2241 memset(ftm_configs, 0, sizeof(ftm_configs));
2242 memset(ftm_params, 0, sizeof(ftm_params));
2243 ftm_configs[ftm_cfg_cnt].enable = TRUE;
2244 ftm_configs[ftm_cfg_cnt++].flags = WL_PROXD_SESSION_FLAG_TARGET;
2245 rtt_status->flags = WL_PROXD_SESSION_FLAG_TARGET;
2246 DHD_RTT(("Set the device as responder \n"));
2247 err = dhd_rtt_ftm_config(dhd, FTM_DEFAULT_SESSION, FTM_CONFIG_CAT_OPTIONS,
2248 ftm_configs, ftm_cfg_cnt);
2249 exit:
2250 if (err) {
2251 rtt_status->status = RTT_STOPPED;
2252 DHD_ERROR(("rtt is stopped %s \n", __FUNCTION__));
2253 dhd_rtt_ftm_enable(dhd, FALSE);
2254 DHD_RTT(("restoring the PM value \n"));
2255 if (rtt_status->pm_restore) {
2256 pm = PM_FAST;
2257 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2258 if (err) {
2259 DHD_ERROR(("Failed to restore PM \n"));
2260 } else {
2261 rtt_status->pm_restore = FALSE;
2262 }
2263 }
2264 }
2265 return err;
2266 }
2267
2268 int
2269 dhd_rtt_cancel_responder(dhd_pub_t *dhd)
2270 {
2271 int err = BCME_OK;
2272 rtt_status_info_t *rtt_status;
2273 int pm = 0;
2274 struct net_device *dev = dhd_linux_get_primary_netdev(dhd);
2275 NULL_CHECK(dhd, "dhd is NULL", err);
2276 rtt_status = GET_RTTSTATE(dhd);
2277 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2278 DHD_RTT(("Enter %s \n", __FUNCTION__));
2279 err = dhd_rtt_ftm_enable(dhd, FALSE);
2280 if (err) {
2281 DHD_ERROR(("failed to disable FTM (%d)\n", err));
2282 }
2283 rtt_status->status = RTT_STOPPED;
2284 if (rtt_status->pm_restore) {
2285 pm = PM_FAST;
2286 DHD_RTT(("pm_restore =%d \n", rtt_status->pm_restore));
2287 err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm));
2288 if (err) {
2289 DHD_ERROR(("Failed to restore PM \n"));
2290 } else {
2291 rtt_status->pm_restore = FALSE;
2292 }
2293 }
2294 return err;
2295 }
2296 #endif /* WL_CFG80211 */
2297
2298 int
2299 dhd_rtt_init(dhd_pub_t *dhd)
2300 {
2301 int err = BCME_OK;
2302 #ifdef WL_CFG80211
2303 int ret;
2304 int32 drv_up = 1;
2305 int32 version;
2306 rtt_status_info_t *rtt_status;
2307 NULL_CHECK(dhd, "dhd is NULL", err);
2308 dhd->rtt_supported = FALSE;
2309 if (dhd->rtt_state) {
2310 return err;
2311 }
2312 dhd->rtt_state = kzalloc(sizeof(rtt_status_info_t), GFP_KERNEL);
2313 if (dhd->rtt_state == NULL) {
2314 err = BCME_NOMEM;
2315 DHD_ERROR(("%s : failed to create rtt_state\n", __FUNCTION__));
2316 return err;
2317 }
2318 bzero(dhd->rtt_state, sizeof(rtt_status_info_t));
2319 rtt_status = GET_RTTSTATE(dhd);
2320 rtt_status->rtt_config.target_info =
2321 kzalloc(TARGET_INFO_SIZE(RTT_MAX_TARGET_CNT), GFP_KERNEL);
2322 if (rtt_status->rtt_config.target_info == NULL) {
2323 DHD_ERROR(("%s failed to allocate the target info for %d\n",
2324 __FUNCTION__, RTT_MAX_TARGET_CNT));
2325 err = BCME_NOMEM;
2326 goto exit;
2327 }
2328 rtt_status->dhd = dhd;
2329 /* need to do WLC_UP */
2330 dhd_wl_ioctl_cmd(dhd, WLC_UP, (char *)&drv_up, sizeof(int32), TRUE, 0);
2331
2332 ret = dhd_rtt_get_version(dhd, &version);
2333 if (ret == BCME_OK && (version == WL_PROXD_API_VERSION)) {
2334 DHD_ERROR(("%s : FTM is supported\n", __FUNCTION__));
2335 dhd->rtt_supported = TRUE;
2336 /* rtt_status->rtt_capa.proto |= RTT_CAP_ONE_WAY; */
2337 rtt_status->rtt_capa.proto |= RTT_CAP_FTM_WAY;
2338
2339 /* indicate to set tx rate */
2340 rtt_status->rtt_capa.feature |= RTT_FEATURE_LCI;
2341 rtt_status->rtt_capa.feature |= RTT_FEATURE_LCR;
2342 rtt_status->rtt_capa.feature |= RTT_FEATURE_PREAMBLE;
2343 rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_VHT;
2344 rtt_status->rtt_capa.preamble |= RTT_PREAMBLE_HT;
2345
2346 /* indicate to set bandwith */
2347 rtt_status->rtt_capa.feature |= RTT_FEATURE_BW;
2348 rtt_status->rtt_capa.bw |= RTT_BW_20;
2349 rtt_status->rtt_capa.bw |= RTT_BW_40;
2350 rtt_status->rtt_capa.bw |= RTT_BW_80;
2351 } else {
2352 if ((ret != BCME_OK) || (version == 0)) {
2353 DHD_ERROR(("%s : FTM is not supported\n", __FUNCTION__));
2354 } else {
2355 DHD_ERROR(("%s : FTM version mismatch between HOST (%d) and FW (%d)\n",
2356 __FUNCTION__, WL_PROXD_API_VERSION, version));
2357 }
2358 }
2359 /* cancel all of RTT request once we got the cancel request */
2360 rtt_status->all_cancel = TRUE;
2361 mutex_init(&rtt_status->rtt_mutex);
2362 INIT_LIST_HEAD(&rtt_status->noti_fn_list);
2363 INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
2364 INIT_WORK(&rtt_status->work, dhd_rtt_work);
2365 exit:
2366 if (err < 0) {
2367 kfree(rtt_status->rtt_config.target_info);
2368 kfree(dhd->rtt_state);
2369 }
2370 #endif /* WL_CFG80211 */
2371 return err;
2372
2373 }
2374
2375 int
2376 dhd_rtt_deinit(dhd_pub_t *dhd)
2377 {
2378 int err = BCME_OK;
2379 #ifdef WL_CFG80211
2380 rtt_status_info_t *rtt_status;
2381 rtt_results_header_t *rtt_header, *next;
2382 rtt_result_t *rtt_result, *next2;
2383 struct rtt_noti_callback *iter, *iter2;
2384 NULL_CHECK(dhd, "dhd is NULL", err);
2385 rtt_status = GET_RTTSTATE(dhd);
2386 NULL_CHECK(rtt_status, "rtt_status is NULL", err);
2387 rtt_status->status = RTT_STOPPED;
2388 DHD_RTT(("rtt is stopped %s \n", __FUNCTION__));
2389 /* clear evt callback list */
2390 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2391 #pragma GCC diagnostic push
2392 #pragma GCC diagnostic ignored "-Wcast-qual"
2393 #endif
2394
2395 if (!list_empty(&rtt_status->noti_fn_list)) {
2396 list_for_each_entry_safe(iter, iter2, &rtt_status->noti_fn_list, list) {
2397 list_del(&iter->list);
2398 kfree(iter);
2399 }
2400 }
2401 /* remove the rtt results */
2402 if (!list_empty(&rtt_status->rtt_results_cache)) {
2403 list_for_each_entry_safe(rtt_header, next, &rtt_status->rtt_results_cache, list) {
2404 list_del(&rtt_header->list);
2405 list_for_each_entry_safe(rtt_result, next2,
2406 &rtt_header->result_list, list) {
2407 list_del(&rtt_result->list);
2408 kfree(rtt_result);
2409 }
2410 kfree(rtt_header);
2411 }
2412 }
2413 #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__)
2414 #pragma GCC diagnostic pop
2415 #endif
2416 kfree(rtt_status->rtt_config.target_info);
2417 kfree(dhd->rtt_state);
2418 dhd->rtt_state = NULL;
2419 #endif /* WL_CFG80211 */
2420 return err;
2421 }