/* * Broadcom Dongle Host Driver (DHD), RTT * * Portions of this code are copyright (c) 2017 Cypress Semiconductor Corporation * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 */