net: wifi: rockchip: update broadcom drivers for kernel4.4
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / rockchip_wlan / rkwifi / bcmdhd / dhd_rtt.c
1 /*
2  * Broadcom Dongle Host Driver (DHD), RTT
3  *
4  * Copyright (C) 1999-2016, 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  * $Id: dhd_rtt.c 606280 2015-12-15 05:28:25Z $
25  */
26 #ifdef RTT_SUPPORT
27 #include <typedefs.h>
28 #include <osl.h>
29
30 #include <epivers.h>
31 #include <bcmutils.h>
32
33 #include <bcmendian.h>
34 #include <linuxver.h>
35 #include <linux/init.h>
36 #include <linux/kernel.h>
37 #include <linux/list.h>
38 #include <linux/sort.h>
39 #include <dngl_stats.h>
40 #include <wlioctl.h>
41
42 #include <proto/bcmevent.h>
43 #include <dhd.h>
44 #include <dhd_rtt.h>
45 #include <dhd_dbg.h>
46 #define GET_RTTSTATE(dhd) ((rtt_status_info_t *)dhd->rtt_state)
47 static DEFINE_SPINLOCK(noti_list_lock);
48 #define NULL_CHECK(p, s, err)  \
49                         do { \
50                                 if (!(p)) { \
51                                         printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \
52                                         err = BCME_ERROR; \
53                                         return err; \
54                                 } \
55                         } while (0)
56
57 #define RTT_TWO_SIDED(capability) \
58                         do { \
59                                 if ((capability & RTT_CAP_ONE_WAY) == (uint8) (RTT_CAP_ONE_WAY)) \
60                                         return FALSE; \
61                                 else \
62                                         return TRUE; \
63                         } while (0)
64 #define TIMESPEC_TO_US(ts)  (((uint64)(ts).tv_sec * USEC_PER_SEC) + \
65                                                         (ts).tv_nsec / NSEC_PER_USEC)
66 struct rtt_noti_callback {
67         struct list_head list;
68         void *ctx;
69         dhd_rtt_compl_noti_fn noti_fn;
70 };
71
72 typedef struct rtt_status_info {
73         dhd_pub_t *dhd;
74         int8 status;   /* current status for the current entry */
75         int8 cur_idx; /* current entry to do RTT */
76         int32 capability; /* rtt capability */
77         struct mutex rtt_mutex;
78         rtt_config_params_t rtt_config;
79         struct work_struct work;
80         struct list_head noti_fn_list;
81         struct list_head rtt_results_cache; /* store results for RTT */
82 } rtt_status_info_t;
83
84 static int dhd_rtt_start(dhd_pub_t *dhd);
85
86 chanspec_t
87 dhd_rtt_convert_to_chspec(wifi_channel_info_t channel)
88 {
89         int bw;
90         /* set witdh to 20MHZ for 2.4G HZ */
91         if (channel.center_freq >= 2400 && channel.center_freq <= 2500) {
92                 channel.width = WIFI_CHAN_WIDTH_20;
93         }
94         switch (channel.width) {
95         case WIFI_CHAN_WIDTH_20:
96                 bw = WL_CHANSPEC_BW_20;
97                 break;
98         case WIFI_CHAN_WIDTH_40:
99                 bw = WL_CHANSPEC_BW_40;
100                 break;
101         case WIFI_CHAN_WIDTH_80:
102                 bw = WL_CHANSPEC_BW_80;
103                 break;
104         case WIFI_CHAN_WIDTH_160:
105                 bw = WL_CHANSPEC_BW_160;
106                 break;
107         default:
108                 DHD_ERROR(("doesn't support this bandwith : %d", channel.width));
109                 bw = -1;
110                 break;
111         }
112         return wf_channel2chspec(wf_mhz2channel(channel.center_freq, 0), bw);
113 }
114
115 int
116 dhd_rtt_set_cfg(dhd_pub_t *dhd, rtt_config_params_t *params)
117 {
118         int err = BCME_OK;
119         int idx;
120         rtt_status_info_t *rtt_status;
121         NULL_CHECK(params, "params is NULL", err);
122
123         NULL_CHECK(dhd, "dhd is NULL", err);
124         rtt_status = GET_RTTSTATE(dhd);
125         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
126         if (rtt_status->capability == RTT_CAP_NONE) {
127                 DHD_ERROR(("doesn't support RTT \n"));
128                 return BCME_ERROR;
129         }
130         if (rtt_status->status == RTT_STARTED) {
131                 DHD_ERROR(("rtt is already started\n"));
132                 return BCME_BUSY;
133         }
134         DHD_RTT(("%s enter\n", __FUNCTION__));
135         bcopy(params, &rtt_status->rtt_config, sizeof(rtt_config_params_t));
136         rtt_status->status = RTT_STARTED;
137         /* start to measure RTT from 1th device */
138         /* find next target to trigger RTT */
139         for (idx = rtt_status->cur_idx; idx < rtt_status->rtt_config.rtt_target_cnt; idx++) {
140                 /* skip the disabled device */
141                 if (rtt_status->rtt_config.target_info[idx].disable) {
142                         continue;
143                 } else {
144                         /* set the idx to cur_idx */
145                         rtt_status->cur_idx = idx;
146                         break;
147                 }
148         }
149         if (idx < rtt_status->rtt_config.rtt_target_cnt) {
150                 DHD_RTT(("rtt_status->cur_idx : %d\n", rtt_status->cur_idx));
151                 schedule_work(&rtt_status->work);
152         }
153         return err;
154 }
155
156 int
157 dhd_rtt_stop(dhd_pub_t *dhd, struct ether_addr *mac_list, int mac_cnt)
158 {
159         int err = BCME_OK;
160         int i = 0, j = 0;
161         rtt_status_info_t *rtt_status;
162
163         NULL_CHECK(dhd, "dhd is NULL", err);
164         rtt_status = GET_RTTSTATE(dhd);
165         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
166         if (rtt_status->status == RTT_STOPPED) {
167                 DHD_ERROR(("rtt is not started\n"));
168                 return BCME_OK;
169         }
170         DHD_RTT(("%s enter\n", __FUNCTION__));
171         mutex_lock(&rtt_status->rtt_mutex);
172         for (i = 0; i < mac_cnt; i++) {
173                 for (j = 0; j < rtt_status->rtt_config.rtt_target_cnt; j++) {
174                         if (!bcmp(&mac_list[i], &rtt_status->rtt_config.target_info[j].addr,
175                                 ETHER_ADDR_LEN)) {
176                                 rtt_status->rtt_config.target_info[j].disable = TRUE;
177                         }
178                 }
179         }
180         mutex_unlock(&rtt_status->rtt_mutex);
181         return err;
182 }
183
184 static int
185 dhd_rtt_start(dhd_pub_t *dhd)
186 {
187         int err = BCME_OK;
188         int mpc = 0;
189         int nss, mcs, bw;
190         uint32 rspec = 0;
191         int8 eabuf[ETHER_ADDR_STR_LEN];
192         int8 chanbuf[CHANSPEC_STR_LEN];
193         bool set_mpc = FALSE;
194         wl_proxd_iovar_t proxd_iovar;
195         wl_proxd_params_iovar_t proxd_params;
196         wl_proxd_params_iovar_t proxd_tune;
197         wl_proxd_params_tof_method_t *tof_params = &proxd_params.u.tof_params;
198         rtt_status_info_t *rtt_status;
199         rtt_target_info_t *rtt_target;
200         NULL_CHECK(dhd, "dhd is NULL", err);
201
202         rtt_status = GET_RTTSTATE(dhd);
203         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
204         /* turn off mpc in case of non-associted */
205         if (!dhd_is_associated(dhd, 0, NULL)) {
206                 err = dhd_iovar(dhd, 0, "mpc", (char *)&mpc, sizeof(mpc), 1);
207                 if (err < 0) {
208                         DHD_ERROR(("%s : failed to set proxd_tune\n", __FUNCTION__));
209                         goto exit;
210                 }
211                 set_mpc = TRUE;
212         }
213
214         if (rtt_status->cur_idx >= rtt_status->rtt_config.rtt_target_cnt) {
215                 err = BCME_RANGE;
216                 goto exit;
217         }
218         DHD_RTT(("%s enter\n", __FUNCTION__));
219         bzero(&proxd_tune, sizeof(proxd_tune));
220         bzero(&proxd_params, sizeof(proxd_params));
221         mutex_lock(&rtt_status->rtt_mutex);
222         /* Get a target information */
223         rtt_target = &rtt_status->rtt_config.target_info[rtt_status->cur_idx];
224         mutex_unlock(&rtt_status->rtt_mutex);
225         /* set role */
226         proxd_iovar.method = PROXD_TOF_METHOD;
227         proxd_iovar.mode = WL_PROXD_MODE_INITIATOR;
228
229         /* make sure that proxd is stop */
230         /* dhd_iovar(dhd, 0, "proxd_stop", (char *)NULL, 0, 1); */
231
232         err = dhd_iovar(dhd, 0, "proxd", (char *)&proxd_iovar, sizeof(proxd_iovar), 1);
233         if (err < 0 && err != BCME_BUSY) {
234                 DHD_ERROR(("%s : failed to set proxd %d\n", __FUNCTION__, err));
235                 goto exit;
236         }
237         if (err == BCME_BUSY) {
238                 DHD_RTT(("BCME_BUSY occurred\n"));
239         }
240         /* mac address */
241         bcopy(&rtt_target->addr, &tof_params->tgt_mac, ETHER_ADDR_LEN);
242         /* frame count */
243         if (rtt_target->ftm_cnt > RTT_MAX_FRAME_CNT) {
244                 rtt_target->ftm_cnt = RTT_MAX_FRAME_CNT;
245         }
246
247         if (rtt_target->ftm_cnt) {
248                 tof_params->ftm_cnt = htol16(rtt_target->ftm_cnt);
249         } else {
250                 tof_params->ftm_cnt = htol16(DEFAULT_FTM_CNT);
251         }
252
253         if (rtt_target->retry_cnt > RTT_MAX_RETRY_CNT) {
254                 rtt_target->retry_cnt = RTT_MAX_RETRY_CNT;
255         }
256
257         /* retry count */
258         if (rtt_target->retry_cnt) {
259                 tof_params->retry_cnt = htol16(rtt_target->retry_cnt);
260         } else {
261                 tof_params->retry_cnt = htol16(DEFAULT_RETRY_CNT);
262         }
263
264         /* chanspec */
265         tof_params->chanspec = htol16(rtt_target->chanspec);
266         /* set parameter */
267         DHD_RTT(("Target addr(Idx %d) %s, Channel : %s for RTT (ftm_cnt %d, rety_cnt : %d)\n",
268                 rtt_status->cur_idx,
269                 bcm_ether_ntoa((const struct ether_addr *)&rtt_target->addr, eabuf),
270                 wf_chspec_ntoa(rtt_target->chanspec, chanbuf), rtt_target->ftm_cnt,
271                 rtt_target->retry_cnt));
272
273         if (rtt_target->type == RTT_ONE_WAY) {
274                 proxd_tune.u.tof_tune.flags = htol32(WL_PROXD_FLAG_ONEWAY);
275                 /* report RTT results for initiator */
276                 proxd_tune.u.tof_tune.flags |= htol32(WL_PROXD_FLAG_INITIATOR_RPTRTT);
277                 proxd_tune.u.tof_tune.vhtack = 0;
278                 tof_params->tx_rate = htol16(WL_RATE_6M);
279                 tof_params->vht_rate = htol16((WL_RATE_6M >> 16));
280         } else { /* RTT TWO WAY */
281                 /* initiator will send the rtt result to the target  */
282                 proxd_tune.u.tof_tune.flags = htol32(WL_PROXD_FLAG_INITIATOR_REPORT);
283                 tof_params->timeout = 10; /* 10ms for timeout */
284                 rspec = WL_RSPEC_ENCODE_VHT;    /* 11ac VHT */
285                 nss = 1; /* default Nss = 1 */
286                 mcs = 0; /* default MCS 0 */
287                 rspec |= (nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs;
288                 bw = 0;
289                 switch (CHSPEC_BW(rtt_target->chanspec)) {
290                 case WL_CHANSPEC_BW_20:
291                         bw = WL_RSPEC_BW_20MHZ;
292                         break;
293                 case WL_CHANSPEC_BW_40:
294                         bw = WL_RSPEC_BW_40MHZ;
295                         break;
296                 case WL_CHANSPEC_BW_80:
297                         bw = WL_RSPEC_BW_80MHZ;
298                         break;
299                 case WL_CHANSPEC_BW_160:
300                         bw = WL_RSPEC_BW_160MHZ;
301                         break;
302                 default:
303                         DHD_ERROR(("CHSPEC_BW not supported : %d",
304                                 CHSPEC_BW(rtt_target->chanspec)));
305                         goto exit;
306                 }
307                 rspec |= bw;
308                 tof_params->tx_rate = htol16(rspec & 0xffff);
309                 tof_params->vht_rate = htol16(rspec >> 16);
310         }
311
312         /* Set Method to TOF */
313         proxd_tune.method = PROXD_TOF_METHOD;
314         err = dhd_iovar(dhd, 0, "proxd_tune", (char *)&proxd_tune, sizeof(proxd_tune), 1);
315         if (err < 0) {
316                 DHD_ERROR(("%s : failed to set proxd_tune %d\n", __FUNCTION__, err));
317                 goto exit;
318         }
319
320         /* Set Method to TOF */
321         proxd_params.method = PROXD_TOF_METHOD;
322         err = dhd_iovar(dhd, 0, "proxd_params", (char *)&proxd_params, sizeof(proxd_params), 1);
323         if (err < 0) {
324                 DHD_ERROR(("%s : failed to set proxd_params %d\n", __FUNCTION__, err));
325                 goto exit;
326         }
327         err = dhd_iovar(dhd, 0, "proxd_find", (char *)NULL, 0, 1);
328         if (err < 0) {
329                 DHD_ERROR(("%s : failed to set proxd_find %d\n", __FUNCTION__, err));
330                 goto exit;
331         }
332 exit:
333         if (err < 0) {
334                 rtt_status->status = RTT_STOPPED;
335                 if (set_mpc) {
336                         /* enable mpc again in case of error */
337                         mpc = 1;
338                         err = dhd_iovar(dhd, 0, "mpc", (char *)&mpc, sizeof(mpc), 1);
339                 }
340         }
341         return err;
342 }
343
344 int
345 dhd_rtt_register_noti_callback(dhd_pub_t *dhd, void *ctx, dhd_rtt_compl_noti_fn noti_fn)
346 {
347         int err = BCME_OK;
348         struct rtt_noti_callback *cb = NULL, *iter;
349         rtt_status_info_t *rtt_status;
350         NULL_CHECK(dhd, "dhd is NULL", err);
351         NULL_CHECK(noti_fn, "noti_fn is NULL", err);
352
353         rtt_status = GET_RTTSTATE(dhd);
354         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
355         spin_lock_bh(&noti_list_lock);
356         list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
357                 if (iter->noti_fn == noti_fn) {
358                         goto exit;
359                 }
360         }
361         cb = kmalloc(sizeof(struct rtt_noti_callback), GFP_ATOMIC);
362         if (!cb) {
363                 err = -ENOMEM;
364                 goto exit;
365         }
366         cb->noti_fn = noti_fn;
367         cb->ctx = ctx;
368         list_add(&cb->list, &rtt_status->noti_fn_list);
369 exit:
370         spin_unlock_bh(&noti_list_lock);
371         return err;
372 }
373
374 int
375 dhd_rtt_unregister_noti_callback(dhd_pub_t *dhd, dhd_rtt_compl_noti_fn noti_fn)
376 {
377         int err = BCME_OK;
378         struct rtt_noti_callback *cb = NULL, *iter;
379         rtt_status_info_t *rtt_status;
380         NULL_CHECK(dhd, "dhd is NULL", err);
381         NULL_CHECK(noti_fn, "noti_fn is NULL", err);
382         rtt_status = GET_RTTSTATE(dhd);
383         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
384         spin_lock_bh(&noti_list_lock);
385         list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
386                 if (iter->noti_fn == noti_fn) {
387                         cb = iter;
388                         list_del(&cb->list);
389                         break;
390                 }
391         }
392         spin_unlock_bh(&noti_list_lock);
393         if (cb) {
394                 kfree(cb);
395         }
396         return err;
397 }
398
399 static int
400 dhd_rtt_convert_to_host(rtt_result_t *rtt_results, const wl_proxd_event_data_t* evp)
401 {
402         int err = BCME_OK;
403         int i;
404         char eabuf[ETHER_ADDR_STR_LEN];
405         char diststr[40];
406         struct timespec ts;
407         NULL_CHECK(rtt_results, "rtt_results is NULL", err);
408         NULL_CHECK(evp, "evp is NULL", err);
409         DHD_RTT(("%s enter\n", __FUNCTION__));
410         rtt_results->distance = ntoh32(evp->distance);
411         rtt_results->sdrtt = ntoh32(evp->sdrtt);
412         rtt_results->ftm_cnt = ntoh16(evp->ftm_cnt);
413         rtt_results->avg_rssi = ntoh16(evp->avg_rssi);
414         rtt_results->validfrmcnt = ntoh16(evp->validfrmcnt);
415         rtt_results->meanrtt = ntoh32(evp->meanrtt);
416         rtt_results->modertt = ntoh32(evp->modertt);
417         rtt_results->medianrtt = ntoh32(evp->medianrtt);
418         rtt_results->err_code = evp->err_code;
419         rtt_results->tx_rate.preamble = (evp->OFDM_frame_type == TOF_FRAME_RATE_VHT)? 3 : 0;
420         rtt_results->tx_rate.nss = 0; /* 1 x 1 */
421         rtt_results->tx_rate.bw =
422                 (evp->bandwidth == TOF_BW_80MHZ)? 2 : (evp->bandwidth == TOF_BW_40MHZ)? 1 : 0;
423         rtt_results->TOF_type = evp->TOF_type;
424         if (evp->TOF_type == TOF_TYPE_ONE_WAY) {
425                 /* convert to 100kbps unit */
426                 rtt_results->tx_rate.bitrate = WL_RATE_6M * 5;
427                 rtt_results->tx_rate.rateMcsIdx = WL_RATE_6M;
428         } else {
429                 rtt_results->tx_rate.bitrate = WL_RATE_6M * 5;
430                 rtt_results->tx_rate.rateMcsIdx = 0; /* MCS 0 */
431         }
432         memset(diststr, 0, sizeof(diststr));
433         if (rtt_results->distance == 0xffffffff || rtt_results->distance == 0) {
434                 sprintf(diststr, "distance=-1m\n");
435         } else {
436                 sprintf(diststr, "distance=%d.%d m\n",
437                         rtt_results->distance >> 4, ((rtt_results->distance & 0xf) * 125) >> 1);
438         }
439
440         if (ntoh32(evp->mode) == WL_PROXD_MODE_INITIATOR) {
441                 DHD_RTT(("Target:(%s) %s;\n", bcm_ether_ntoa((&evp->peer_mac), eabuf), diststr));
442                 DHD_RTT(("RTT : mean %d mode %d median %d\n", rtt_results->meanrtt,
443                         rtt_results->modertt, rtt_results->medianrtt));
444         } else {
445                 DHD_RTT(("Initiator:(%s) %s; ", bcm_ether_ntoa((&evp->peer_mac), eabuf), diststr));
446         }
447         if (rtt_results->sdrtt > 0) {
448                 DHD_RTT(("sigma:%d.%d\n", rtt_results->sdrtt/10, rtt_results->sdrtt % 10));
449         } else {
450                 DHD_RTT(("sigma:0\n"));
451         }
452
453         DHD_RTT(("rssi:%d validfrmcnt %d, err_code : %d\n", rtt_results->avg_rssi,
454                 rtt_results->validfrmcnt, evp->err_code));
455
456         switch (evp->err_code) {
457         case TOF_REASON_OK:
458                 rtt_results->err_code = RTT_REASON_SUCCESS;
459                 break;
460         case TOF_REASON_TIMEOUT:
461                 rtt_results->err_code = RTT_REASON_TIMEOUT;
462                 break;
463         case TOF_REASON_NOACK:
464                 rtt_results->err_code = RTT_REASON_NO_RSP;
465                 break;
466         case TOF_REASON_ABORT:
467                 rtt_results->err_code = RTT_REASON_ABORT;
468                 break;
469         default:
470                 rtt_results->err_code = RTT_REASON_FAILURE;
471                 break;
472         }
473         rtt_results->peer_mac = evp->peer_mac;
474         /* get the time elapsed from boot time */
475         get_monotonic_boottime(&ts);
476         rtt_results->ts = (uint64) TIMESPEC_TO_US(ts);
477
478         for (i = 0; i < rtt_results->ftm_cnt; i++) {
479                 rtt_results->ftm_buff[i].value = ltoh32(evp->ftm_buff[i].value);
480                 rtt_results->ftm_buff[i].rssi = ltoh32(evp->ftm_buff[i].rssi);
481         }
482         return err;
483 }
484
485 int
486 dhd_rtt_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data)
487 {
488         int err = BCME_OK;
489         int len = 0;
490         int idx;
491         uint status, event_type, flags, reason, ftm_cnt;
492         rtt_status_info_t *rtt_status;
493         wl_proxd_event_data_t* evp;
494         struct rtt_noti_callback *iter;
495         rtt_result_t *rtt_result, *entry, *next;
496         gfp_t kflags;
497         NULL_CHECK(dhd, "dhd is NULL", err);
498         rtt_status = GET_RTTSTATE(dhd);
499         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
500         event_type = ntoh32_ua((void *)&event->event_type);
501         flags = ntoh16_ua((void *)&event->flags);
502         status = ntoh32_ua((void *)&event->status);
503         reason = ntoh32_ua((void *)&event->reason);
504
505         if (event_type != WLC_E_PROXD) {
506                 goto exit;
507         }
508         kflags = in_softirq()? GFP_ATOMIC : GFP_KERNEL;
509         evp = (wl_proxd_event_data_t*)event_data;
510         DHD_RTT(("%s enter : mode: %s, reason :%d \n", __FUNCTION__,
511                 (ntoh16(evp->mode) == WL_PROXD_MODE_INITIATOR)?
512                 "initiator":"target", reason));
513         switch (reason) {
514         case WLC_E_PROXD_STOP:
515                 DHD_RTT(("WLC_E_PROXD_STOP\n"));
516                 break;
517         case WLC_E_PROXD_ERROR:
518         case WLC_E_PROXD_COMPLETED:
519                 if (reason == WLC_E_PROXD_ERROR) {
520                         DHD_RTT(("WLC_E_PROXD_ERROR\n"));
521                 } else {
522                         DHD_RTT(("WLC_E_PROXD_COMPLETED\n"));
523                 }
524
525                 if (!in_atomic()) {
526                         mutex_lock(&rtt_status->rtt_mutex);
527                 }
528                 ftm_cnt = ntoh16(evp->ftm_cnt);
529
530                 if (ftm_cnt > 0) {
531                         len = OFFSETOF(rtt_result_t, ftm_buff);
532                 } else {
533                         len = sizeof(rtt_result_t);
534                 }
535                 /* check whether the results is already reported or not */
536                 list_for_each_entry(entry, &rtt_status->rtt_results_cache, list) {
537                         if (!memcmp(&entry->peer_mac, &evp->peer_mac, ETHER_ADDR_LEN))  {
538                                 if (!in_atomic()) {
539                                         mutex_unlock(&rtt_status->rtt_mutex);
540                                 }
541                                 goto exit;
542                         }
543                 }
544                 rtt_result = kzalloc(len + sizeof(ftm_sample_t) * ftm_cnt, kflags);
545                 if (!rtt_result) {
546                         if (!in_atomic()) {
547                                 mutex_unlock(&rtt_status->rtt_mutex);
548                         }
549                         err = -ENOMEM;
550                         goto exit;
551                 }
552                 /* point to target_info in status struct and increase pointer */
553                 rtt_result->target_info = &rtt_status->rtt_config.target_info[rtt_status->cur_idx];
554                 /* find next target to trigger RTT */
555                 for (idx = (rtt_status->cur_idx + 1);
556                         idx < rtt_status->rtt_config.rtt_target_cnt; idx++) {
557                         /* skip the disabled device */
558                         if (rtt_status->rtt_config.target_info[idx].disable) {
559                                 continue;
560                         } else {
561                                 /* set the idx to cur_idx */
562                                 rtt_status->cur_idx = idx;
563                                 break;
564                         }
565                 }
566                 /* convert the event results to host format */
567                 dhd_rtt_convert_to_host(rtt_result, evp);
568                 list_add_tail(&rtt_result->list, &rtt_status->rtt_results_cache);
569                 if (idx < rtt_status->rtt_config.rtt_target_cnt) {
570                         /* restart to measure RTT from next device */
571                         schedule_work(&rtt_status->work);
572                 } else {
573                         DHD_RTT(("RTT_STOPPED\n"));
574                         rtt_status->status = RTT_STOPPED;
575                         /* to turn on mpc mode */
576                         schedule_work(&rtt_status->work);
577                         /* notify the completed information to others */
578                         list_for_each_entry(iter, &rtt_status->noti_fn_list, list) {
579                                 iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache);
580                         }
581                         /* remove the rtt results in cache */
582                         list_for_each_entry_safe(rtt_result, next,
583                                 &rtt_status->rtt_results_cache, list) {
584                                 list_del(&rtt_result->list);
585                                 kfree(rtt_result);
586                         }
587                         /* reinit the HEAD */
588                         INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
589                         /* clear information for rtt_config */
590                         bzero(&rtt_status->rtt_config, sizeof(rtt_status->rtt_config));
591                         rtt_status->cur_idx = 0;
592                 }
593                 if (!in_atomic()) {
594                         mutex_unlock(&rtt_status->rtt_mutex);
595                 }
596
597                 break;
598         case WLC_E_PROXD_GONE:
599                 DHD_RTT(("WLC_E_PROXD_GONE\n"));
600                 break;
601         case WLC_E_PROXD_START:
602                 /* event for targets / accesspoints  */
603                 DHD_RTT(("WLC_E_PROXD_START\n"));
604                 break;
605         case WLC_E_PROXD_COLLECT_START:
606                 DHD_RTT(("WLC_E_PROXD_COLLECT_START\n"));
607                 break;
608         case WLC_E_PROXD_COLLECT_STOP:
609                 DHD_RTT(("WLC_E_PROXD_COLLECT_STOP\n"));
610                 break;
611         case WLC_E_PROXD_COLLECT_COMPLETED:
612                 DHD_RTT(("WLC_E_PROXD_COLLECT_COMPLETED\n"));
613                 break;
614         case WLC_E_PROXD_COLLECT_ERROR:
615                 DHD_RTT(("WLC_E_PROXD_COLLECT_ERROR; "));
616                 break;
617         default:
618                 DHD_ERROR(("WLC_E_PROXD: supported EVENT reason code:%d\n", reason));
619                 break;
620         }
621
622 exit:
623         return err;
624 }
625
626 static void
627 dhd_rtt_work(struct work_struct *work)
628 {
629         rtt_status_info_t *rtt_status;
630         dhd_pub_t *dhd;
631         rtt_status = container_of(work, rtt_status_info_t, work);
632         if (rtt_status == NULL) {
633                 DHD_ERROR(("%s : rtt_status is NULL\n", __FUNCTION__));
634                 return;
635         }
636         dhd = rtt_status->dhd;
637         if (dhd == NULL) {
638                 DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__));
639                 return;
640         }
641         (void) dhd_rtt_start(dhd);
642 }
643
644 int
645 dhd_rtt_capability(dhd_pub_t *dhd, rtt_capabilities_t *capa)
646 {
647         rtt_status_info_t *rtt_status;
648         int err = BCME_OK;
649         NULL_CHECK(dhd, "dhd is NULL", err);
650         rtt_status = GET_RTTSTATE(dhd);
651         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
652         NULL_CHECK(capa, "capa is NULL", err);
653         bzero(capa, sizeof(rtt_capabilities_t));
654
655         if (rtt_status->capability & RTT_CAP_ONE_WAY) {
656                 capa->rtt_one_sided_supported = 1;
657         }
658         if (rtt_status->capability & RTT_CAP_11V_WAY) {
659                 capa->rtt_11v_supported = 1;
660         }
661         if (rtt_status->capability & RTT_CAP_11MC_WAY) {
662                 capa->rtt_ftm_supported = 1;
663         }
664         if (rtt_status->capability & RTT_CAP_VS_WAY) {
665                 capa->rtt_vs_supported = 1;
666         }
667
668         return err;
669 }
670
671 int
672 dhd_rtt_init(dhd_pub_t *dhd)
673 {
674         int err = BCME_OK;
675         rtt_status_info_t *rtt_status;
676         NULL_CHECK(dhd, "dhd is NULL", err);
677         if (dhd->rtt_state) {
678                 goto exit;
679         }
680         dhd->rtt_state = MALLOC(dhd->osh, sizeof(rtt_status_info_t));
681         if (dhd->rtt_state == NULL) {
682                 DHD_ERROR(("failed to create rtt_state\n"));
683                 goto exit;
684         }
685         bzero(dhd->rtt_state, sizeof(rtt_status_info_t));
686         rtt_status = GET_RTTSTATE(dhd);
687         rtt_status->dhd = dhd;
688         err = dhd_iovar(dhd, 0, "proxd_params", NULL, 0, 1);
689         if (err != BCME_UNSUPPORTED) {
690                 rtt_status->capability |= RTT_CAP_ONE_WAY;
691                 rtt_status->capability |= RTT_CAP_VS_WAY;
692                 DHD_ERROR(("%s: Support RTT Service\n", __FUNCTION__));
693         }
694         mutex_init(&rtt_status->rtt_mutex);
695         INIT_LIST_HEAD(&rtt_status->noti_fn_list);
696         INIT_LIST_HEAD(&rtt_status->rtt_results_cache);
697         INIT_WORK(&rtt_status->work, dhd_rtt_work);
698 exit:
699         return err;
700 }
701
702 int
703 dhd_rtt_deinit(dhd_pub_t *dhd)
704 {
705         int err = BCME_OK;
706         rtt_status_info_t *rtt_status;
707         rtt_result_t *rtt_result, *next;
708         struct rtt_noti_callback *iter, *iter2;
709         NULL_CHECK(dhd, "dhd is NULL", err);
710         rtt_status = GET_RTTSTATE(dhd);
711         NULL_CHECK(rtt_status, "rtt_status is NULL", err);
712         rtt_status->status = RTT_STOPPED;
713         /* clear evt callback list */
714         if (!list_empty(&rtt_status->noti_fn_list)) {
715                 list_for_each_entry_safe(iter, iter2, &rtt_status->noti_fn_list, list) {
716                         list_del(&iter->list);
717                         kfree(iter);
718                 }
719         }
720         /* remove the rtt results */
721         if (!list_empty(&rtt_status->rtt_results_cache)) {
722                 list_for_each_entry_safe(rtt_result, next, &rtt_status->rtt_results_cache, list) {
723                         list_del(&rtt_result->list);
724                         kfree(rtt_result);
725                 }
726         }
727         MFREE(dhd->osh, dhd->rtt_state, sizeof(rtt_status_info_t));
728         dhd->rtt_state = NULL;
729         return err;
730 }
731 #endif /* RTT_SUPPORT */