| /* |
| * Broadcom Dongle Host Driver (DHD), RTT |
| * |
| * Copyright (C) 1999-2017, Broadcom Corporation |
| * |
| * Unless you and Broadcom execute a separate written software license |
| * agreement governing use of this software, this software is licensed to you |
| * under the terms of the GNU General Public License version 2 (the "GPL"), |
| * available at http://www.broadcom.com/licenses/GPLv2.php, with the |
| * following added to such license: |
| * |
| * As a special exception, the copyright holders of this software give you |
| * permission to link this software with independent modules, and to copy and |
| * distribute the resulting executable under terms of your choice, provided that |
| * you also meet, for each linked independent module, the terms and conditions of |
| * the license of that module. An independent module is a module which is not |
| * derived from this software. The special exception does not apply to any |
| * modifications of the software. |
| * |
| * Notwithstanding the above, under no circumstances may you combine this |
| * software in any way with any other Broadcom software provided under a license |
| * other than the GPL, without Broadcom's express prior written consent. |
| * |
| * $Id: dhd_rtt.c 612549 2016-01-14 07:39:32Z $ |
| */ |
| #ifdef RTT_SUPPORT |
| #include <typedefs.h> |
| #include <osl.h> |
| |
| #include <epivers.h> |
| #include <bcmutils.h> |
| |
| #include <bcmendian.h> |
| #include <linuxver.h> |
| #include <linux/init.h> |
| #include <linux/kernel.h> |
| #include <linux/list.h> |
| #include <linux/sort.h> |
| #include <dngl_stats.h> |
| #include <wlioctl.h> |
| |
| #include <proto/bcmevent.h> |
| #include <dhd.h> |
| #include <dhd_rtt.h> |
| #include <dhd_dbg.h> |
| #define GET_RTTSTATE(dhd) ((rtt_status_info_t *)dhd->rtt_state) |
| static DEFINE_SPINLOCK(noti_list_lock); |
| #define NULL_CHECK(p, s, err) \ |
| do { \ |
| if (!(p)) { \ |
| printf("NULL POINTER (%s) : %s\n", __FUNCTION__, (s)); \ |
| err = BCME_ERROR; \ |
| return err; \ |
| } \ |
| } while (0) |
| |
| #define RTT_TWO_SIDED(capability) \ |
| do { \ |
| if ((capability & RTT_CAP_ONE_WAY) == (uint8) (RTT_CAP_ONE_WAY)) \ |
| return FALSE; \ |
| else \ |
| return TRUE; \ |
| } while (0) |
| #define TIMESPEC_TO_US(ts) (((uint64)(ts).tv_sec * USEC_PER_SEC) + \ |
| (ts).tv_nsec / NSEC_PER_USEC) |
| struct rtt_noti_callback { |
| struct list_head list; |
| void *ctx; |
| dhd_rtt_compl_noti_fn noti_fn; |
| }; |
| |
| typedef struct rtt_status_info { |
| dhd_pub_t *dhd; |
| int8 status; /* current status for the current entry */ |
| int8 cur_idx; /* current entry to do RTT */ |
| int32 capability; /* rtt capability */ |
| struct mutex rtt_mutex; |
| rtt_config_params_t rtt_config; |
| struct work_struct work; |
| struct list_head noti_fn_list; |
| struct list_head rtt_results_cache; /* store results for RTT */ |
| } rtt_status_info_t; |
| |
| static int dhd_rtt_start(dhd_pub_t *dhd); |
| |
| chanspec_t |
| dhd_rtt_convert_to_chspec(wifi_channel_info_t channel) |
| { |
| int bw; |
| /* set witdh to 20MHZ for 2.4G HZ */ |
| if (channel.center_freq >= 2400 && channel.center_freq <= 2500) { |
| channel.width = WIFI_CHAN_WIDTH_20; |
| } |
| switch (channel.width) { |
| case WIFI_CHAN_WIDTH_20: |
| bw = WL_CHANSPEC_BW_20; |
| break; |
| case WIFI_CHAN_WIDTH_40: |
| bw = WL_CHANSPEC_BW_40; |
| break; |
| case WIFI_CHAN_WIDTH_80: |
| bw = WL_CHANSPEC_BW_80; |
| break; |
| case WIFI_CHAN_WIDTH_160: |
| bw = WL_CHANSPEC_BW_160; |
| break; |
| default: |
| DHD_ERROR(("doesn't support this bandwith : %d", channel.width)); |
| bw = -1; |
| break; |
| } |
| return wf_channel2chspec(wf_mhz2channel(channel.center_freq, 0), bw); |
| } |
| |
| int |
| dhd_rtt_set_cfg(dhd_pub_t *dhd, rtt_config_params_t *params) |
| { |
| int err = BCME_OK; |
| int idx; |
| rtt_status_info_t *rtt_status; |
| NULL_CHECK(params, "params is NULL", err); |
| |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| if (rtt_status->capability == RTT_CAP_NONE) { |
| DHD_ERROR(("doesn't support RTT \n")); |
| return BCME_ERROR; |
| } |
| if (rtt_status->status == RTT_STARTED) { |
| DHD_ERROR(("rtt is already started\n")); |
| return BCME_BUSY; |
| } |
| DHD_RTT(("%s enter\n", __FUNCTION__)); |
| bcopy(params, &rtt_status->rtt_config, sizeof(rtt_config_params_t)); |
| rtt_status->status = RTT_STARTED; |
| /* start to measure RTT from 1th device */ |
| /* find next target to trigger RTT */ |
| for (idx = rtt_status->cur_idx; idx < rtt_status->rtt_config.rtt_target_cnt; idx++) { |
| /* skip the disabled device */ |
| if (rtt_status->rtt_config.target_info[idx].disable) { |
| continue; |
| } else { |
| /* set the idx to cur_idx */ |
| rtt_status->cur_idx = idx; |
| break; |
| } |
| } |
| if (idx < rtt_status->rtt_config.rtt_target_cnt) { |
| DHD_RTT(("rtt_status->cur_idx : %d\n", rtt_status->cur_idx)); |
| schedule_work(&rtt_status->work); |
| } |
| return err; |
| } |
| |
| int |
| dhd_rtt_stop(dhd_pub_t *dhd, struct ether_addr *mac_list, int mac_cnt) |
| { |
| int err = BCME_OK; |
| int i = 0, j = 0; |
| rtt_status_info_t *rtt_status; |
| |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| if (rtt_status->status == RTT_STOPPED) { |
| DHD_ERROR(("rtt is not started\n")); |
| return BCME_OK; |
| } |
| DHD_RTT(("%s enter\n", __FUNCTION__)); |
| mutex_lock(&rtt_status->rtt_mutex); |
| for (i = 0; i < mac_cnt; i++) { |
| for (j = 0; j < rtt_status->rtt_config.rtt_target_cnt; j++) { |
| if (!bcmp(&mac_list[i], &rtt_status->rtt_config.target_info[j].addr, |
| ETHER_ADDR_LEN)) { |
| rtt_status->rtt_config.target_info[j].disable = TRUE; |
| } |
| } |
| } |
| mutex_unlock(&rtt_status->rtt_mutex); |
| return err; |
| } |
| |
| static int |
| dhd_rtt_start(dhd_pub_t *dhd) |
| { |
| int err = BCME_OK; |
| int mpc = 0; |
| int nss, mcs, bw; |
| uint32 rspec = 0; |
| int8 eabuf[ETHER_ADDR_STR_LEN]; |
| int8 chanbuf[CHANSPEC_STR_LEN]; |
| bool set_mpc = FALSE; |
| wl_proxd_iovar_t proxd_iovar; |
| wl_proxd_params_iovar_t proxd_params; |
| wl_proxd_params_iovar_t proxd_tune; |
| wl_proxd_params_tof_method_t *tof_params = &proxd_params.u.tof_params; |
| rtt_status_info_t *rtt_status; |
| rtt_target_info_t *rtt_target; |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| /* turn off mpc in case of non-associted */ |
| if (!dhd_is_associated(dhd, 0, NULL)) { |
| err = dhd_iovar(dhd, 0, "mpc", (char *)&mpc, sizeof(mpc), 1); |
| if (err < 0) { |
| DHD_ERROR(("%s : failed to set proxd_tune\n", __FUNCTION__)); |
| goto exit; |
| } |
| set_mpc = TRUE; |
| } |
| |
| if (rtt_status->cur_idx >= rtt_status->rtt_config.rtt_target_cnt) { |
| err = BCME_RANGE; |
| goto exit; |
| } |
| DHD_RTT(("%s enter\n", __FUNCTION__)); |
| bzero(&proxd_tune, sizeof(proxd_tune)); |
| bzero(&proxd_params, sizeof(proxd_params)); |
| mutex_lock(&rtt_status->rtt_mutex); |
| /* Get a target information */ |
| rtt_target = &rtt_status->rtt_config.target_info[rtt_status->cur_idx]; |
| mutex_unlock(&rtt_status->rtt_mutex); |
| /* set role */ |
| proxd_iovar.method = PROXD_TOF_METHOD; |
| proxd_iovar.mode = WL_PROXD_MODE_INITIATOR; |
| |
| /* make sure that proxd is stop */ |
| /* dhd_iovar(dhd, 0, "proxd_stop", (char *)NULL, 0, 1); */ |
| |
| err = dhd_iovar(dhd, 0, "proxd", (char *)&proxd_iovar, sizeof(proxd_iovar), 1); |
| if (err < 0 && err != BCME_BUSY) { |
| DHD_ERROR(("%s : failed to set proxd %d\n", __FUNCTION__, err)); |
| goto exit; |
| } |
| if (err == BCME_BUSY) { |
| DHD_RTT(("BCME_BUSY occurred\n")); |
| } |
| /* mac address */ |
| bcopy(&rtt_target->addr, &tof_params->tgt_mac, ETHER_ADDR_LEN); |
| /* frame count */ |
| if (rtt_target->ftm_cnt > RTT_MAX_FRAME_CNT) { |
| rtt_target->ftm_cnt = RTT_MAX_FRAME_CNT; |
| } |
| |
| if (rtt_target->ftm_cnt) { |
| tof_params->ftm_cnt = htol16(rtt_target->ftm_cnt); |
| } else { |
| tof_params->ftm_cnt = htol16(DEFAULT_FTM_CNT); |
| } |
| |
| if (rtt_target->retry_cnt > RTT_MAX_RETRY_CNT) { |
| rtt_target->retry_cnt = RTT_MAX_RETRY_CNT; |
| } |
| |
| /* retry count */ |
| if (rtt_target->retry_cnt) { |
| tof_params->retry_cnt = htol16(rtt_target->retry_cnt); |
| } else { |
| tof_params->retry_cnt = htol16(DEFAULT_RETRY_CNT); |
| } |
| |
| /* chanspec */ |
| tof_params->chanspec = htol16(rtt_target->chanspec); |
| /* set parameter */ |
| DHD_RTT(("Target addr(Idx %d) %s, Channel : %s for RTT (ftm_cnt %d, rety_cnt : %d)\n", |
| rtt_status->cur_idx, |
| bcm_ether_ntoa((const struct ether_addr *)&rtt_target->addr, eabuf), |
| wf_chspec_ntoa(rtt_target->chanspec, chanbuf), rtt_target->ftm_cnt, |
| rtt_target->retry_cnt)); |
| |
| if (rtt_target->type == RTT_ONE_WAY) { |
| proxd_tune.u.tof_tune.flags = htol32(WL_PROXD_FLAG_ONEWAY); |
| /* report RTT results for initiator */ |
| proxd_tune.u.tof_tune.flags |= htol32(WL_PROXD_FLAG_INITIATOR_RPTRTT); |
| proxd_tune.u.tof_tune.vhtack = 0; |
| tof_params->tx_rate = htol16(WL_RATE_6M); |
| tof_params->vht_rate = htol16((WL_RATE_6M >> 16)); |
| } else { /* RTT TWO WAY */ |
| /* initiator will send the rtt result to the target */ |
| proxd_tune.u.tof_tune.flags = htol32(WL_PROXD_FLAG_INITIATOR_REPORT); |
| tof_params->timeout = 10; /* 10ms for timeout */ |
| rspec = WL_RSPEC_ENCODE_VHT; /* 11ac VHT */ |
| nss = 1; /* default Nss = 1 */ |
| mcs = 0; /* default MCS 0 */ |
| rspec |= (nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs; |
| bw = 0; |
| switch (CHSPEC_BW(rtt_target->chanspec)) { |
| case WL_CHANSPEC_BW_20: |
| bw = WL_RSPEC_BW_20MHZ; |
| break; |
| case WL_CHANSPEC_BW_40: |
| bw = WL_RSPEC_BW_40MHZ; |
| break; |
| case WL_CHANSPEC_BW_80: |
| bw = WL_RSPEC_BW_80MHZ; |
| break; |
| case WL_CHANSPEC_BW_160: |
| bw = WL_RSPEC_BW_160MHZ; |
| break; |
| default: |
| DHD_ERROR(("CHSPEC_BW not supported : %d", |
| CHSPEC_BW(rtt_target->chanspec))); |
| goto exit; |
| } |
| rspec |= bw; |
| tof_params->tx_rate = htol16(rspec & 0xffff); |
| tof_params->vht_rate = htol16(rspec >> 16); |
| } |
| |
| /* Set Method to TOF */ |
| proxd_tune.method = PROXD_TOF_METHOD; |
| err = dhd_iovar(dhd, 0, "proxd_tune", (char *)&proxd_tune, sizeof(proxd_tune), 1); |
| if (err < 0) { |
| DHD_ERROR(("%s : failed to set proxd_tune %d\n", __FUNCTION__, err)); |
| goto exit; |
| } |
| |
| /* Set Method to TOF */ |
| proxd_params.method = PROXD_TOF_METHOD; |
| err = dhd_iovar(dhd, 0, "proxd_params", (char *)&proxd_params, sizeof(proxd_params), 1); |
| if (err < 0) { |
| DHD_ERROR(("%s : failed to set proxd_params %d\n", __FUNCTION__, err)); |
| goto exit; |
| } |
| err = dhd_iovar(dhd, 0, "proxd_find", (char *)NULL, 0, 1); |
| if (err < 0) { |
| DHD_ERROR(("%s : failed to set proxd_find %d\n", __FUNCTION__, err)); |
| goto exit; |
| } |
| exit: |
| if (err < 0) { |
| rtt_status->status = RTT_STOPPED; |
| if (set_mpc) { |
| /* enable mpc again in case of error */ |
| mpc = 1; |
| err = dhd_iovar(dhd, 0, "mpc", (char *)&mpc, sizeof(mpc), 1); |
| } |
| } |
| return err; |
| } |
| |
| int |
| dhd_rtt_register_noti_callback(dhd_pub_t *dhd, void *ctx, dhd_rtt_compl_noti_fn noti_fn) |
| { |
| int err = BCME_OK; |
| struct rtt_noti_callback *cb = NULL, *iter; |
| rtt_status_info_t *rtt_status; |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| NULL_CHECK(noti_fn, "noti_fn is NULL", err); |
| |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| spin_lock_bh(¬i_list_lock); |
| list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { |
| if (iter->noti_fn == noti_fn) { |
| goto exit; |
| } |
| } |
| cb = kmalloc(sizeof(struct rtt_noti_callback), GFP_ATOMIC); |
| if (!cb) { |
| err = -ENOMEM; |
| goto exit; |
| } |
| cb->noti_fn = noti_fn; |
| cb->ctx = ctx; |
| list_add(&cb->list, &rtt_status->noti_fn_list); |
| exit: |
| spin_unlock_bh(¬i_list_lock); |
| return err; |
| } |
| |
| int |
| dhd_rtt_unregister_noti_callback(dhd_pub_t *dhd, dhd_rtt_compl_noti_fn noti_fn) |
| { |
| int err = BCME_OK; |
| struct rtt_noti_callback *cb = NULL, *iter; |
| rtt_status_info_t *rtt_status; |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| NULL_CHECK(noti_fn, "noti_fn is NULL", err); |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| spin_lock_bh(¬i_list_lock); |
| list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { |
| if (iter->noti_fn == noti_fn) { |
| cb = iter; |
| list_del(&cb->list); |
| break; |
| } |
| } |
| spin_unlock_bh(¬i_list_lock); |
| if (cb) { |
| kfree(cb); |
| } |
| return err; |
| } |
| |
| static int |
| dhd_rtt_convert_to_host(rtt_result_t *rtt_results, const wl_proxd_event_data_t* evp) |
| { |
| int err = BCME_OK; |
| int i; |
| char eabuf[ETHER_ADDR_STR_LEN]; |
| char diststr[40]; |
| struct timespec ts; |
| NULL_CHECK(rtt_results, "rtt_results is NULL", err); |
| NULL_CHECK(evp, "evp is NULL", err); |
| DHD_RTT(("%s enter\n", __FUNCTION__)); |
| rtt_results->distance = ntoh32(evp->distance); |
| rtt_results->sdrtt = ntoh32(evp->sdrtt); |
| rtt_results->ftm_cnt = ntoh16(evp->ftm_cnt); |
| rtt_results->avg_rssi = ntoh16(evp->avg_rssi); |
| rtt_results->validfrmcnt = ntoh16(evp->validfrmcnt); |
| rtt_results->meanrtt = ntoh32(evp->meanrtt); |
| rtt_results->modertt = ntoh32(evp->modertt); |
| rtt_results->medianrtt = ntoh32(evp->medianrtt); |
| rtt_results->err_code = evp->err_code; |
| rtt_results->tx_rate.preamble = (evp->OFDM_frame_type == TOF_FRAME_RATE_VHT)? 3 : 0; |
| rtt_results->tx_rate.nss = 0; /* 1 x 1 */ |
| rtt_results->tx_rate.bw = |
| (evp->bandwidth == TOF_BW_80MHZ)? 2 : (evp->bandwidth == TOF_BW_40MHZ)? 1 : 0; |
| rtt_results->TOF_type = evp->TOF_type; |
| if (evp->TOF_type == TOF_TYPE_ONE_WAY) { |
| /* convert to 100kbps unit */ |
| rtt_results->tx_rate.bitrate = WL_RATE_6M * 5; |
| rtt_results->tx_rate.rateMcsIdx = WL_RATE_6M; |
| } else { |
| rtt_results->tx_rate.bitrate = WL_RATE_6M * 5; |
| rtt_results->tx_rate.rateMcsIdx = 0; /* MCS 0 */ |
| } |
| memset(diststr, 0, sizeof(diststr)); |
| if (rtt_results->distance == 0xffffffff || rtt_results->distance == 0) { |
| sprintf(diststr, "distance=-1m\n"); |
| } else { |
| sprintf(diststr, "distance=%d.%d m\n", |
| rtt_results->distance >> 4, ((rtt_results->distance & 0xf) * 125) >> 1); |
| } |
| |
| if (ntoh32(evp->mode) == WL_PROXD_MODE_INITIATOR) { |
| DHD_RTT(("Target:(%s) %s;\n", bcm_ether_ntoa((&evp->peer_mac), eabuf), diststr)); |
| DHD_RTT(("RTT : mean %d mode %d median %d\n", rtt_results->meanrtt, |
| rtt_results->modertt, rtt_results->medianrtt)); |
| } else { |
| DHD_RTT(("Initiator:(%s) %s; ", bcm_ether_ntoa((&evp->peer_mac), eabuf), diststr)); |
| } |
| if (rtt_results->sdrtt > 0) { |
| DHD_RTT(("sigma:%d.%d\n", rtt_results->sdrtt/10, rtt_results->sdrtt % 10)); |
| } else { |
| DHD_RTT(("sigma:0\n")); |
| } |
| |
| DHD_RTT(("rssi:%d validfrmcnt %d, err_code : %d\n", rtt_results->avg_rssi, |
| rtt_results->validfrmcnt, evp->err_code)); |
| |
| switch (evp->err_code) { |
| case TOF_REASON_OK: |
| rtt_results->err_code = RTT_REASON_SUCCESS; |
| break; |
| case TOF_REASON_TIMEOUT: |
| rtt_results->err_code = RTT_REASON_TIMEOUT; |
| break; |
| case TOF_REASON_NOACK: |
| rtt_results->err_code = RTT_REASON_NO_RSP; |
| break; |
| case TOF_REASON_ABORT: |
| rtt_results->err_code = RTT_REASON_ABORT; |
| break; |
| default: |
| rtt_results->err_code = RTT_REASON_FAILURE; |
| break; |
| } |
| rtt_results->peer_mac = evp->peer_mac; |
| /* get the time elapsed from boot time */ |
| get_monotonic_boottime(&ts); |
| rtt_results->ts = (uint64) TIMESPEC_TO_US(ts); |
| |
| for (i = 0; i < rtt_results->ftm_cnt; i++) { |
| rtt_results->ftm_buff[i].value = ltoh32(evp->ftm_buff[i].value); |
| rtt_results->ftm_buff[i].rssi = ltoh32(evp->ftm_buff[i].rssi); |
| } |
| return err; |
| } |
| |
| int |
| dhd_rtt_event_handler(dhd_pub_t *dhd, wl_event_msg_t *event, void *event_data) |
| { |
| int err = BCME_OK; |
| int len = 0; |
| int idx; |
| uint event_type, reason, ftm_cnt; |
| rtt_status_info_t *rtt_status; |
| wl_proxd_event_data_t* evp; |
| struct rtt_noti_callback *iter; |
| rtt_result_t *rtt_result, *entry, *next; |
| gfp_t kflags; |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| event_type = ntoh32_ua((void *)&event->event_type); |
| reason = ntoh32_ua((void *)&event->reason); |
| |
| if (event_type != WLC_E_PROXD) { |
| goto exit; |
| } |
| kflags = in_softirq()? GFP_ATOMIC : GFP_KERNEL; |
| evp = (wl_proxd_event_data_t*)event_data; |
| DHD_RTT(("%s enter : mode: %s, reason :%d \n", __FUNCTION__, |
| (ntoh16(evp->mode) == WL_PROXD_MODE_INITIATOR)? |
| "initiator":"target", reason)); |
| switch (reason) { |
| case WLC_E_PROXD_STOP: |
| DHD_RTT(("WLC_E_PROXD_STOP\n")); |
| break; |
| case WLC_E_PROXD_ERROR: |
| case WLC_E_PROXD_COMPLETED: |
| if (reason == WLC_E_PROXD_ERROR) { |
| DHD_RTT(("WLC_E_PROXD_ERROR\n")); |
| } else { |
| DHD_RTT(("WLC_E_PROXD_COMPLETED\n")); |
| } |
| |
| if (!in_atomic()) { |
| mutex_lock(&rtt_status->rtt_mutex); |
| } |
| ftm_cnt = ntoh16(evp->ftm_cnt); |
| |
| if (ftm_cnt > 0) { |
| len = OFFSETOF(rtt_result_t, ftm_buff); |
| } else { |
| len = sizeof(rtt_result_t); |
| } |
| /* check whether the results is already reported or not */ |
| list_for_each_entry(entry, &rtt_status->rtt_results_cache, list) { |
| if (!memcmp(&entry->peer_mac, &evp->peer_mac, ETHER_ADDR_LEN)) { |
| if (!in_atomic()) { |
| mutex_unlock(&rtt_status->rtt_mutex); |
| } |
| goto exit; |
| } |
| } |
| rtt_result = kzalloc(len + sizeof(ftm_sample_t) * ftm_cnt, kflags); |
| if (!rtt_result) { |
| if (!in_atomic()) { |
| mutex_unlock(&rtt_status->rtt_mutex); |
| } |
| err = -ENOMEM; |
| goto exit; |
| } |
| /* point to target_info in status struct and increase pointer */ |
| rtt_result->target_info = &rtt_status->rtt_config.target_info[rtt_status->cur_idx]; |
| /* find next target to trigger RTT */ |
| for (idx = (rtt_status->cur_idx + 1); |
| idx < rtt_status->rtt_config.rtt_target_cnt; idx++) { |
| /* skip the disabled device */ |
| if (rtt_status->rtt_config.target_info[idx].disable) { |
| continue; |
| } else { |
| /* set the idx to cur_idx */ |
| rtt_status->cur_idx = idx; |
| break; |
| } |
| } |
| /* convert the event results to host format */ |
| dhd_rtt_convert_to_host(rtt_result, evp); |
| list_add_tail(&rtt_result->list, &rtt_status->rtt_results_cache); |
| if (idx < rtt_status->rtt_config.rtt_target_cnt) { |
| /* restart to measure RTT from next device */ |
| schedule_work(&rtt_status->work); |
| } else { |
| DHD_RTT(("RTT_STOPPED\n")); |
| rtt_status->status = RTT_STOPPED; |
| /* to turn on mpc mode */ |
| schedule_work(&rtt_status->work); |
| /* notify the completed information to others */ |
| list_for_each_entry(iter, &rtt_status->noti_fn_list, list) { |
| iter->noti_fn(iter->ctx, &rtt_status->rtt_results_cache); |
| } |
| /* remove the rtt results in cache */ |
| list_for_each_entry_safe(rtt_result, next, |
| &rtt_status->rtt_results_cache, list) { |
| list_del(&rtt_result->list); |
| kfree(rtt_result); |
| } |
| /* reinit the HEAD */ |
| INIT_LIST_HEAD(&rtt_status->rtt_results_cache); |
| /* clear information for rtt_config */ |
| bzero(&rtt_status->rtt_config, sizeof(rtt_status->rtt_config)); |
| rtt_status->cur_idx = 0; |
| } |
| if (!in_atomic()) { |
| mutex_unlock(&rtt_status->rtt_mutex); |
| } |
| |
| break; |
| case WLC_E_PROXD_GONE: |
| DHD_RTT(("WLC_E_PROXD_GONE\n")); |
| break; |
| case WLC_E_PROXD_START: |
| /* event for targets / accesspoints */ |
| DHD_RTT(("WLC_E_PROXD_START\n")); |
| break; |
| case WLC_E_PROXD_COLLECT_START: |
| DHD_RTT(("WLC_E_PROXD_COLLECT_START\n")); |
| break; |
| case WLC_E_PROXD_COLLECT_STOP: |
| DHD_RTT(("WLC_E_PROXD_COLLECT_STOP\n")); |
| break; |
| case WLC_E_PROXD_COLLECT_COMPLETED: |
| DHD_RTT(("WLC_E_PROXD_COLLECT_COMPLETED\n")); |
| break; |
| case WLC_E_PROXD_COLLECT_ERROR: |
| DHD_RTT(("WLC_E_PROXD_COLLECT_ERROR; ")); |
| break; |
| default: |
| DHD_ERROR(("WLC_E_PROXD: supported EVENT reason code:%d\n", reason)); |
| break; |
| } |
| |
| exit: |
| return err; |
| } |
| |
| static void |
| dhd_rtt_work(struct work_struct *work) |
| { |
| rtt_status_info_t *rtt_status; |
| dhd_pub_t *dhd; |
| rtt_status = container_of(work, rtt_status_info_t, work); |
| if (rtt_status == NULL) { |
| DHD_ERROR(("%s : rtt_status is NULL\n", __FUNCTION__)); |
| return; |
| } |
| dhd = rtt_status->dhd; |
| if (dhd == NULL) { |
| DHD_ERROR(("%s : dhd is NULL\n", __FUNCTION__)); |
| return; |
| } |
| (void) dhd_rtt_start(dhd); |
| } |
| |
| int |
| dhd_rtt_capability(dhd_pub_t *dhd, rtt_capabilities_t *capa) |
| { |
| rtt_status_info_t *rtt_status; |
| int err = BCME_OK; |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| NULL_CHECK(capa, "capa is NULL", err); |
| bzero(capa, sizeof(rtt_capabilities_t)); |
| |
| if (rtt_status->capability & RTT_CAP_ONE_WAY) { |
| capa->rtt_one_sided_supported = 1; |
| } |
| if (rtt_status->capability & RTT_CAP_11V_WAY) { |
| capa->rtt_11v_supported = 1; |
| } |
| if (rtt_status->capability & RTT_CAP_11MC_WAY) { |
| capa->rtt_ftm_supported = 1; |
| } |
| if (rtt_status->capability & RTT_CAP_VS_WAY) { |
| capa->rtt_vs_supported = 1; |
| } |
| |
| return err; |
| } |
| |
| int |
| dhd_rtt_init(dhd_pub_t *dhd) |
| { |
| int err = BCME_OK; |
| rtt_status_info_t *rtt_status; |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| if (dhd->rtt_state) { |
| goto exit; |
| } |
| dhd->rtt_state = MALLOC(dhd->osh, sizeof(rtt_status_info_t)); |
| if (dhd->rtt_state == NULL) { |
| DHD_ERROR(("failed to create rtt_state\n")); |
| goto exit; |
| } |
| bzero(dhd->rtt_state, sizeof(rtt_status_info_t)); |
| rtt_status = GET_RTTSTATE(dhd); |
| rtt_status->dhd = dhd; |
| err = dhd_iovar(dhd, 0, "proxd_params", NULL, 0, 1); |
| if (err != BCME_UNSUPPORTED) { |
| rtt_status->capability |= RTT_CAP_ONE_WAY; |
| rtt_status->capability |= RTT_CAP_VS_WAY; |
| DHD_ERROR(("%s: Support RTT Service\n", __FUNCTION__)); |
| } |
| mutex_init(&rtt_status->rtt_mutex); |
| INIT_LIST_HEAD(&rtt_status->noti_fn_list); |
| INIT_LIST_HEAD(&rtt_status->rtt_results_cache); |
| INIT_WORK(&rtt_status->work, dhd_rtt_work); |
| exit: |
| return err; |
| } |
| |
| int |
| dhd_rtt_deinit(dhd_pub_t *dhd) |
| { |
| int err = BCME_OK; |
| rtt_status_info_t *rtt_status; |
| rtt_result_t *rtt_result, *next; |
| struct rtt_noti_callback *iter, *iter2; |
| NULL_CHECK(dhd, "dhd is NULL", err); |
| rtt_status = GET_RTTSTATE(dhd); |
| NULL_CHECK(rtt_status, "rtt_status is NULL", err); |
| rtt_status->status = RTT_STOPPED; |
| /* clear evt callback list */ |
| if (!list_empty(&rtt_status->noti_fn_list)) { |
| list_for_each_entry_safe(iter, iter2, &rtt_status->noti_fn_list, list) { |
| list_del(&iter->list); |
| kfree(iter); |
| } |
| } |
| /* remove the rtt results */ |
| if (!list_empty(&rtt_status->rtt_results_cache)) { |
| list_for_each_entry_safe(rtt_result, next, &rtt_status->rtt_results_cache, list) { |
| list_del(&rtt_result->list); |
| kfree(rtt_result); |
| } |
| } |
| MFREE(dhd->osh, dhd->rtt_state, sizeof(rtt_status_info_t)); |
| dhd->rtt_state = NULL; |
| return err; |
| } |
| #endif /* RTT_SUPPORT */ |