blob: ab03cb14bb56496ecb9b2f2c52827415ad08a7c9 [file] [log] [blame]
/*
* wl proxd command module
*
* Broadcom Proprietary and Confidential. Copyright (C) 2017,
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom;
* the contents of this file may not be disclosed to third parties, copied
* or duplicated in any form, in whole or in part, without the prior
* written permission of Broadcom.
*
*
* <<Broadcom-WL-IPTag/Proprietary:>>
*
* $Id: wluc_proxd.c 458728 2014-02-27 18:15:25Z $
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <wlioctl.h>
#if defined(DONGLEBUILD)
#include <typedefs.h>
#include <osl.h>
#endif
/* Because IL_BIGENDIAN was removed there are few warnings that need
* to be fixed. Windows was not compiled earlier with IL_BIGENDIAN.
* Hence these warnings were not seen earlier.
* For now ignore the following warnings
*/
#ifdef WIN32
#pragma warning(push)
#pragma warning(disable : 4244)
#pragma warning(disable : 4761)
#endif
#include <bcmutils.h>
#include <bcmendian.h>
#include "wlu_common.h"
#include "wlu.h"
#ifdef WIN32
#define bzero(b, len) memset((b), 0, (len))
#endif
#ifdef LINUX
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <net/if.h>
#include <linux/if_packet.h>
#endif /* LINUX */
#include <miniopt.h>
#include <errno.h>
static cmd_func_t wl_proxd;
static cmd_func_t wl_proxd_tune;
static cmd_func_t wl_proxd_collect;
static cmd_func_t wl_proxd_params;
static cmd_func_t wl_proxd_status;
static cmd_func_t wl_proxd_payload;
#if defined(linux)
static cmd_func_t wl_proxd_event_check;
#endif /* linux */
static cmd_func_t wl_proxd_report;
#if defined(WL_NAN)
static cmd_func_t wl_nan_ranging_config;
static cmd_func_t wl_nan_ranging_start;
static cmd_func_t wl_nan_ranging_results_host;
#endif /* WL_NAN */
#define WL_PROXD_PAYLOAD_LEN 1026
#define TOF_DEFAULT_FTMCNT_SEQ 3
#define PROXD_PARAMS_USAGE \
"\tUsage: wl proxd_params method [-c channel] [-i interval] [-d duration] [-s rssi_thresh]" \
" [-p tx_power] [-r tx_rate] [-t timeout] [-m maxconvergetime] [-g <xx:xx:xx:xx:xx:xx>]" \
" [-y retrycnt]\n\n" \
"\tMandatory args:\n" \
"\t\tmethod: == 1 (RSSI) or == 2 (TOF) methods are supported) \n\n" \
"\tOptional method specific args - for method 1: \n" \
"\t\t-c chanspec : (all methods) channel to use for Proximity Detection\n" \
"\t\t : e.g: 161/80, if [/BW] omitted, driver will assume 20Mhz by default\n" \
"\t\t-i interval : (RSSI method) interval between neighbor finding attempts (in TU)\n" \
"\t\t : (Once associated as STA mode, this value is ignored and\n" \
"\t\t : the interval follows DTIM)\n" \
"\t\t-d duration : (RSSI method) duration of neighbor finding attempts (in ms)\n" \
"\t\t : == dwelling time on home channel specificed by -c channel\n" \
"\t\t-s rssi_thresh : RSSI threshold for Proximity Detection criteria (in dBm)\n" \
"\t\t : (-99 to -1)\n" \
"\t\t-p tx_power : (RSSI method) tx power of Proximity Detection frames (in dBm)\n" \
"\t\t-r tx_rate : (all methods) tx rate of Proximity Detection frames (in Mbps)\n" \
"\t\t : (TOF) tx_rate format is {R|{hM|vM[xS]}[s][l][g]}[eT][bB]\n" \
"\t\t : R - legacy rate, hM - HT MCS index[0-23], " \
"vMxS - VHT MCS index[0-9] and Nss[1-8]\n" \
"\t\t : s - Use STBC expansion, l - Use LDPC encoding, " \
"g - SGI, Short Guard Interval\n" \
"\t\t : eT - Tx expansion, number of tx chains[0-3], " \
"bB - tx bandwidth, MHz: 20, 40, 80\n" \
"\t\t-t timeout : (all methods) state machine receive timeout of Proximity Detection " \
"frames (in ms)\n" \
"\t\t-m maxconverge : (RSSI method) device stays up for a whole interval to detect the peer\n" \
"\t\t : when no peer found after max converge time (in ms)\n\n" \
"\t\t-g tgt_mac : (TOF) proximity target mac address for a method \n" \
"\t\t-f ftm_cnt : (TOF) number of ftm frames requested by initiator from target \n" \
"\t\t-y retry_cnt : (TOF) number of retransmit attempts for FTM frames \n" \
"\tExample: wl proxd_params 1 -c 36 -i 100 -d 10 -s -40 -p 12 -r 6 -t 20 -m 1000\n" \
"\tExample: wl proxd_params 1 -s -55\n" \
"\tExample: wl proxd_params 2 -c 11 -f 10 -g 00:90:4c:a5:01:32 -r v0"
#define PROXD_TUNE_USAGE \
"\tUsage: wl proxd_tune method [operations]\n\n" \
"\tMandatory args:\n" \
"\t\tmethod: == 2 (TOF) methods are supported \n\n" \
"\tOperations:\n" \
"\t\t-k K factor : hardware dependant RTD delay adjustment factor \n" \
"\t\t-b vhtack : 0:disable VHT ACK, 1:enable VHT ACK\n" \
"\t\t-n minDT : min time difference of T1 and T4 or T2 and T3 \n" \
"\t\t-x maxDT : max time difference of T1 and T4 or T2 and T3 \n" \
"\t\t-t total_frmcnt : total count limit of measurement frames transmitted \n" \
"\t\t-N threshold_log2 : log2 number of simple threshold crossing \n" \
"\t\t-S threshold_scale: scale number of simple threshold crossing \n" \
"\t\t-F ftm_cnt : number of measurement frames requested by initiator \n" \
"\t\t-r rsv_media_value: reserve media duration value for TOF \n" \
"\t\t-f flags : TOF state machine control flags\n" \
"\t\t-A timestamp_adj : enable/disable sw/hw/seq assisted timestamp adjustment, " \
"the data format is s[0|1]h[0|1]r[0|1] \n" \
"\t\t-W window_adjust : set search window length and offset, " \
"the data format is bBlLoO, B is bandwidth \n" \
"\t\t : with value 20, 40 or 80, L is window length, O is offset"
static cmd_t wl_proxd_cmds[] = {
{ "proxd", wl_proxd, WLC_GET_VAR, WLC_SET_VAR,
"Enable/Disable Proximity Detection\n"
"\t0 : disable\n"
"\t1 [initiator|target|neutral] [u/r]: enable with the specified mode and wakeup"
" mechanism\n"
"\tftm [<session-id>] <cmd> [<param-name><param-value>...]:"
" enable FTM, type 'wl proxd -h ftm' for more information\n\n"
"\tExample: wl proxd 1 initiator"},
{ "proxd_collect", wl_proxd_collect, WLC_GET_VAR, WLC_SET_VAR,
"collect the debugging informations of Proximity Detection \n\n"
"Optional parameters is:\n"
"\tenable to enable the proxd collection.\n"
"\tdisable to disable the proxd collection.\n"
"\t-l, dump local collect data and request load remote AP collect data.\n"
"\t-r, dump remote collect data or request load remote AP collect data.\n"
"\t-f File name to dump the sample buffer (default \"proxd_collect.dat\")\n"},
{ "proxd_params", wl_proxd_params, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get operational parameters for a method of Proximity Detection\n\n"
PROXD_PARAMS_USAGE},
{ "proxd_tune", wl_proxd_tune, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get tune parameters for TOF method of Proximity Detection\n\n"
PROXD_TUNE_USAGE},
{ "proxd_bssid", wl_iov_mac, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get BSSID to be used in proximity detection frames\n\n"
"\tUsage: wl proxd_bssid <xx:xx:xx:xx:xx:xx>"},
{ "proxd_mcastaddr", wl_iov_mac, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get Multicast MAC address of Proximity Detection Frames\n\n"
"\tUsage: wl proxd_mcastaddr <xx:xx:xx:xx:xx:xx>"},
{ "proxd_find", wl_var_void, -1, WLC_SET_VAR,
"Start Proximity Detection" },
{ "proxd_stop", wl_var_void, -1, WLC_SET_VAR,
"Stop Proximity Detection" },
{ "proxd_status", wl_proxd_status, WLC_GET_VAR, -1,
"Get status of Proximity Detection" },
{ "proxd_monitor", wl_iov_mac, -1, WLC_SET_VAR,
"Monitor detected peer status in proximity\n\n"
"\tUsage: wl proxd_monitor <xx:xx:xx:xx:xx:xx>"},
{ "proxd_payload", wl_proxd_payload, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set payload content transferred between the proximity detected peers\n\n"
"\tUsage: wl proxd_payload [len hexstring]"},
{ "proxd_report", wl_proxd_report, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set report distance results list\n\n"
"\tUsage: wl proxd_report [mac address list]"},
#if defined(WL_NAN)
{"proxd_nancfg", wl_nan_ranging_config, -1, -1,
"Get/Set \"proxd_nancfg\" params .\n"
"\tUsage: wl proxd_nancfg <chanspec> <start_timeslot> <duration> [<allow_mac> [flag]]\n"},
{"proxd_nanfind", wl_nan_ranging_start, -1, -1,
"Send specified \"proxd_nanfind \" params .\n"
"\tUsage: wl proxd_nanfind <num_dws> [<chanspec> <peer addr> <bitmap> "
"<count> <retry> <flag>]\n"
"\t\t for each peer, add the [<chanspec> <peer addr> <bitmap> <count> <retry> <flag>]\n"},
{"proxd_nanstatus", wl_nan_ranging_results_host, WLC_GET_VAR, -1,
"Return host initiated ranging results.\n"},
{"proxd_nanpeer", wl_nan_ranging_results_host, WLC_GET_VAR, -1,
"Return peer initiated ranging results.\n"},
#endif /* WL_NAN */
#if defined(linux)
{ "proxd_event_check", wl_proxd_event_check, -1, -1,
"Listen and print Location Based Service events\n"
"\tproxd_event_check syntax is: proxd_event_check ifname"},
#endif /* linux */
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/*
* for FTM
*/
static int wl_proxd_cmd_method_handler(void *wl, cmd_t *cmd, char **argv);
static int ftm_handle_help(char **argv);
#if defined(linux)
static int ftm_event_check(bcm_event_t *p_bcm_event);
#endif
static void ftm_display_config_help();
static void ftm_display_config_options_help();
static void ftm_display_config_avail_help();
static void ftm_unpack_and_display_session_flags(const uint8 *p_data, uint16 tlvid);
static void ftm_unpack_and_display_config_flags(const uint8 *p_data, uint16 tlvid);
static int proxd_tune_display(wl_proxd_params_tof_tune_t *tof_tune, uint16 len);
/*
*for debug: define the flag WL_FTM_DEBUG to enable debug log for FTM
*/
/* #define WL_FTM_DEBUG */
#ifdef WL_FTM_DEBUG
static void ftm_format_event_mask(wl_proxd_event_type_t event, char *p_strbuf, int bufsize);
#endif /* WL_FTM_DEBUG */
/* module initialization */
void
wluc_proxd_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register proxd commands */
wl_module_cmds_register(wl_proxd_cmds);
}
static int
wl_proxd(void *wl, cmd_t *cmd, char **argv)
{
uint16 var[2], *reply;
uint16 method = 0, role = 0;
void *ptr;
int ret;
/* skip the command name and check if NULL */
if (!*++argv) {
/* Get */
ret = wlu_var_getbuf(wl, cmd->name, &var, sizeof(var), &ptr);
if (ret != BCME_OK) {
return ret;
}
reply = (uint16 *)ptr;
method = dtoh16(reply[0]);
printf("%d\n", method);
if (method > 0) {
char c = 'u';
role = dtoh16(reply[1]);
if (role & WL_PROXD_RANDOM_WAKEUP) {
c = 'r';
role &= ~WL_PROXD_RANDOM_WAKEUP;
}
if (role == WL_PROXD_MODE_INITIATOR)
printf("%s %c\n", "initiator", c);
else if (role == WL_PROXD_MODE_TARGET)
printf("%s %c\n", "target", c);
else if (role == WL_PROXD_MODE_NEUTRAL)
printf("%s %c\n", "neutral", c);
}
} else {
/* Set */
if (!isdigit((int)*argv[0]))
return wl_proxd_cmd_method_handler(wl, cmd, argv);
/* parse method and role */
method = (uint16)atoi(argv[0]);
if (method > 0) {
if (!argv[1]) {
/* Default when it is not specified */
role = WL_PROXD_MODE_NEUTRAL | WL_PROXD_RANDOM_WAKEUP;
}
else {
if (stricmp(argv[1], "initiator") == 0)
role = WL_PROXD_MODE_INITIATOR;
else if (stricmp(argv[1], "target") == 0)
role = WL_PROXD_MODE_TARGET;
else if (stricmp(argv[1], "neutral") == 0) {
role = WL_PROXD_MODE_NEUTRAL;
role |= WL_PROXD_RANDOM_WAKEUP;
}
else
return BCME_USAGE_ERROR;
if (argv[2]) {
if (*argv[2] == 'R' || *argv[2] == 'r')
role |= WL_PROXD_RANDOM_WAKEUP;
else if (*argv[2] == 'u' || *argv[2] == 'U')
role &= ~WL_PROXD_RANDOM_WAKEUP;
}
}
}
var[0] = htod16(method);
var[1] = htod16(role);
ret = wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
}
return ret;
}
static int
wl_proxd_get_debug_data(void *wl, cmd_t *cmd, int index)
{
int ret;
void *buff;
wl_proxd_collect_query_t query;
wl_proxd_debug_data_t *replay;
bzero(&query, sizeof(query));
query.method = htol32(PROXD_TOF_METHOD);
query.request = PROXD_COLLECT_QUERY_DEBUG;
query.index = htol16(index);
ret = wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
if (ret != BCME_OK)
return ret;
replay = (wl_proxd_debug_data_t *)buff;
if (index == 0)
printf("\n/* Debug Informations */\n");
printf("%s[%u,%u]: type %u action(%u,%u) token(%u, %u)\n",
replay->received? "RX" : "TX",
replay->count, replay->stage,
replay->paket_type,
replay->category, replay->action,
replay->token, replay->follow_token);
if (replay->tof_cmd == 0 && replay->tof_rsp == 0) {
printf("\n");
return BCME_OK;
}
printf("Index=%d\n", ltoh16(replay->index));
printf("M_TOF_CMD=0x%04x\tM_TOF_RSP=0x%04x\tM_TOF_ID=0x%04x\n",
ltoh16(replay->tof_cmd), ltoh16(replay->tof_rsp), ltoh16(replay->tof_id));
printf("M_TOF_AVB_RX_L=0x%04x\tM_TOF_AVB_RX_H=0x%04x\t",
ltoh16(replay->tof_avb_rxl), ltoh16(replay->tof_avb_rxh));
printf("M_TOF_AVB_TX_L=0x%04x\tM_TOF_AVB_TX_H=0x%04x\n",
ltoh16(replay->tof_avb_txl), ltoh16(replay->tof_avb_txh));
printf("M_TOF_STATUS0=0x%04x\tM_TOF_STATUS2=0x%04x\t",
ltoh16(replay->tof_status0), ltoh16(replay->tof_status2));
printf("M_TOF_CHNSM_0=0x%04x\tM_TOF_CHNSM_1=0x%04x\n",
ltoh16(replay->tof_chsm0), 0);
printf("M_TOF_PHYCTL0=0x%04x\tM_TOF_PHYCTL1=0x%04x\tM_TOF_PHYCTL2=0x%04x\n",
ltoh16(replay->tof_phyctl0), ltoh16(replay->tof_phyctl1),
ltoh16(replay->tof_phyctl2));
printf("M_TOF_LSIG=0x%04x\tM_TOF_VHTA0=0x%04x\tM_TOF_VHTA1=0x%04x\n",
ltoh16(replay->tof_lsig), ltoh16(replay->tof_vhta0), ltoh16(replay->tof_vhta1));
printf("M_TOF_VHTA2=0x%04x\tM_TOF_VHTB0=0x%04x\tM_TOF_VHTB1=0x%04x\n",
ltoh16(replay->tof_vhta2), ltoh16(replay->tof_vhtb0), ltoh16(replay->tof_vhtb1));
printf("M_TOF_AMPDU_CTL=0x%04x\tM_TOF_AMPDU_DLIM=0x%04x\tM_TOF_AMPDU_LEN=0x%04x\n\n",
ltoh16(replay->tof_apmductl), ltoh16(replay->tof_apmdudlim),
ltoh16(replay->tof_apmdulen));
return BCME_OK;
}
static void
wlc_proxd_collec_header_dump(wl_proxd_collect_header_t *pHdr)
{
int i;
printf("total_frames %lu\n", (unsigned long)ltoh16(pHdr->total_frames));
printf("nfft %lu\n", (unsigned long)ltoh16(pHdr->nfft));
printf("bandwidth %lu\n", (unsigned long)ltoh16(pHdr->bandwidth));
printf("channel %lu\n", (unsigned long)ltoh16(pHdr->channel));
printf("chanspec %lu\n", (unsigned long)ltoh32(pHdr->chanspec));
printf("fpfactor %lu\n", (unsigned long)ltoh32(pHdr->fpfactor));
printf("fpfactor_shift %lu\n", (unsigned long)ltoh16(pHdr->fpfactor_shift));
printf("distance %li\n", (long)ltoh32(pHdr->distance));
printf("meanrtt %lu\n", (unsigned long)ltoh32(pHdr->meanrtt));
printf("modertt %lu\n", (unsigned long)ltoh32(pHdr->modertt));
printf("medianrtt %lu\n", (unsigned long)ltoh32(pHdr->medianrtt));
printf("sdrtt %lu\n", (unsigned long)ltoh32(pHdr->sdrtt));
printf("clkdivisor %lu\n", (unsigned long)ltoh32(pHdr->clkdivisor));
printf("chipnum %lu\n", (unsigned long)ltoh16(pHdr->chipnum));
printf("chiprev %lu\n", (unsigned long)pHdr->chiprev);
printf("phyver %lu\n", (unsigned long)pHdr->phyver);
printf("localMacAddr %s\n", wl_ether_etoa(&(pHdr->localMacAddr)));
printf("remoteMacAddr %s\n", wl_ether_etoa(&(pHdr->remoteMacAddr)));
printf("params_Ki %lu\n", (unsigned long)ltoh32(pHdr->params.Ki));
printf("params_Kt %lu\n", (unsigned long)ltoh32(pHdr->params.Kt));
printf("params_vhtack %li\n", (long)ltoh16(pHdr->params.vhtack));
printf("params_N_log2 %d\n", TOF_BW_NUM);
for (i = 0; i < TOF_BW_NUM; i++) {
printf("%li\n", (long)ltoh16(pHdr->params.N_log2[i]));
}
printf("params_N_scale %d\n", TOF_BW_NUM);
for (i = 0; i < TOF_BW_NUM; i++) {
printf("%li\n", (long)ltoh16(pHdr->params.N_scale[i]));
}
printf("params_sw_adj %lu\n", (unsigned long)pHdr->params.sw_adj);
printf("params_hw_adj %lu\n", (unsigned long)pHdr->params.hw_adj);
printf("params_seq_en %lu\n", (unsigned long)pHdr->params.seq_en);
printf("params_core %lu\n", (unsigned long)pHdr->params.core);
printf("params_N_log2_seq 2\n");
for (i = 0; i < 2; i++) {
printf("%li\n", (long)ltoh16(pHdr->params.N_log2[i + TOF_BW_NUM]));
}
printf("params_N_scale_seq 2\n");
for (i = 0; i < 2; i++) {
printf("%li\n", (long)ltoh16(pHdr->params.N_scale[i + TOF_BW_NUM]));
}
printf("params_w_offset %d\n", TOF_BW_NUM);
for (i = 0; i < TOF_BW_NUM; i++) {
printf("%li\n", (long)ltoh16(pHdr->params.w_offset[i]));
};
printf("params_w_len %d\n", TOF_BW_NUM);
for (i = 0; i < TOF_BW_NUM; i++) {
printf("%li\n", (long)ltoh16(pHdr->params.w_len[i]));
};
printf("params_maxDT %li\n", (long)ltoh32(pHdr->params.maxDT));
printf("params_minDT %li\n", (long)ltoh32(pHdr->params.minDT));
printf("params_totalfrmcnt %lu\n", (unsigned long)pHdr->params.totalfrmcnt);
printf("params_rsv_media %lu\n", (unsigned long)ltoh16(pHdr->params.rsv_media));
}
static int wlc_proxd_collect_data_check_ver_len(uint16 expected_ver,
uint16 ver, uint16 len)
{
int ret = BCME_OK;
switch (expected_ver) {
case WL_PROXD_COLLECT_DATA_VERSION_1:
/* v1 does not haver version and length fields */
ret = BCME_OK;
break;
case WL_PROXD_COLLECT_DATA_VERSION_2:
if ((ver != expected_ver) ||
(len != sizeof(wl_proxd_collect_data_t_v2) -
OFFSETOF(wl_proxd_collect_data_t_v2, len))) {
ret = BCME_VERSION;
}
break;
default:
ret = BCME_UNSUPPORTED;
}
return ret;
}
static int wlc_proxd_collec_data_dump(void *replay, FILE *fp,
wl_proxd_rssi_bias_avg_t *rssi_bias_avg, uint16 version)
{
int i, n, nbytes;
int ret = BCME_OK;
wl_proxd_collect_data_t_v1 *replay_v1 = NULL;
wl_proxd_collect_data_t_v2 *replay_v2 = NULL;
wl_proxd_collect_info_t *info = NULL;
uint32 *H = NULL;
uint32 *chan = NULL;
uint8 *ri_rr = NULL;
int nfft = 0;
#ifdef RSSI_REFINE
int rssi_dec = 0;
#else
/* rssi_bias_avg is unused under !RSSI_REFINE. */
/* Macro to prevent compiler warning under some platforms */
UNUSED_PARAMETER(rssi_bias_avg);
#endif
BCM_REFERENCE(H);
BCM_REFERENCE(chan);
BCM_REFERENCE(ri_rr);
/* Depending on version add the corresponding case and pointers to point
* to the correct member for use in the function
*/
switch (version) {
case WL_PROXD_COLLECT_DATA_VERSION_1:
replay_v1 = (wl_proxd_collect_data_t_v1 *)replay;
info = &replay_v1->info;
H = replay_v1->H;
ri_rr = replay_v1->ri_rr;
nbytes = sizeof(wl_proxd_collect_data_t_v1)
- (K_TOF_COLLECT_H_SIZE_20MHZ - nfft) * sizeof(uint32);
break;
case WL_PROXD_COLLECT_DATA_VERSION_2:
replay_v2 = (wl_proxd_collect_data_t_v2 *)replay;
ret = wlc_proxd_collect_data_check_ver_len(version, replay_v2->version,
replay_v2->len);
if (ret != BCME_OK) {
break;
}
info = &replay_v2->info;
H = replay_v2->H;
ri_rr = replay_v2->ri_rr;
chan = replay_v2->chan;
nbytes = sizeof(wl_proxd_collect_data_t_v2)
- (K_TOF_COLLECT_H_SIZE_20MHZ - nfft) * sizeof(uint32);
break;
default:
ret = BCME_VERSION;
break;
}
if (ret != BCME_OK) {
goto done;
}
if (info) {
nfft = (int)ltoh16(info->nfft);
#ifdef RSSI_REFINE
if (nfft > 0) {
printf("}\nImpulse Response = {\n");
for (i = 0; i < nfft; i++) {
printf("%010d ", ltoh32(info->rssi_bias.imp_resp[i]));
if ((i & 7) == 7)
printf("\n");
}
printf("RSSI_VERSION = %d\n", ltoh32(info->rssi_bias.version));
printf("PEAK_OFFSET = %d\n", ltoh32(info->rssi_bias.peak_offset));
rssi_bias_avg->avg_peak_offset += ltoh32(info->rssi_bias.peak_offset);
printf("PEAK_TO_AVG = %d", ltoh32(info->rssi_bias.bias));
rssi_bias_avg->avg_bias += ltoh32(info->rssi_bias.bias);
printf("\n");
for (i = 0; i < 10; i++) {
printf("THRESHOLD_%d = %u", i,
ltoh32(info->rssi_bias.threshold[i]));
if ((i+1) % 5)
printf(", ");
else
printf("\n");
rssi_bias_avg->avg_threshold[i] +=
ltoh32(info->rssi_bias.threshold[i]);
}
printf("SCALAR = %d", info->rssi_bias.threshold[10]);
}
/* convert tof_status2 from hex to dec */
rssi_dec = info->tof_rssi;
printf("\nRSSI10 = %d", rssi_dec);
rssi_bias_avg->avg_rssi += rssi_dec;
printf("\n\n");
#endif /* RSSI_REFINE */
/* printing the info portion */
printf("info_type %lu\n", (unsigned long)ltoh16(info->type));
printf("info_index %lu\n", (unsigned long)ltoh16(info->index));
printf("info_tof_cmd %lu\n", (unsigned long)ltoh16(info->tof_cmd));
printf("info_tof_rsp %lu\n", (unsigned long)ltoh16(info->tof_rsp));
printf("info_tof_avb_rxl %lu\n", (unsigned long)ltoh16(info->tof_avb_rxl));
printf("info_tof_avb_rxh %lu\n", (unsigned long)ltoh16(info->tof_avb_rxh));
printf("info_tof_avb_txl %lu\n", (unsigned long)ltoh16(info->tof_avb_txl));
printf("info_tof_avb_txh %lu\n", (unsigned long)ltoh16(info->tof_avb_txh));
printf("info_tof_id %lu\n", (unsigned long)ltoh16(info->tof_id));
printf("info_tof_frame_type %lu\n", (unsigned long)info->tof_frame_type);
printf("info_tof_frame_bw %lu\n", (unsigned long)info->tof_frame_bw);
printf("info_tof_rssi %li\n", (long)info->tof_rssi);
printf("info_tof_cfo %li\n", (long)ltoh32(info->tof_cfo));
printf("info_gd_adj_ns %li\n", (long)ltoh32(info->gd_adj_ns));
printf("info_gd_h_adj_ns %li\n", (long)ltoh32(info->gd_h_adj_ns));
printf("info_nfft %li\n", (long)ltoh16(info->nfft));
if (H) {
/* printing the H portion */
printf("H %d\n", nfft);
for (i = 0; i < nfft; i++) {
printf("%lu\n", (unsigned long)ltoh32(H[i]));
}
}
/* printing the chan and ri_rr */
if (info->index == 1) {
if (chan && version >= WL_PROXD_COLLECT_DATA_VERSION_2) {
n = (int)ltoh16((info->num_max_cores + 1)*
(K_TOF_COLLECT_CHAN_SIZE >> (2 - info->tof_frame_bw)));
printf("chan_est %d\n", n);
for (i = 0; i < n; i++) {
printf("%u\n", chan[i]);
}
}
if (ri_rr) {
printf("ri_rr %d\n", FTM_TPK_RI_RR_LEN);
for (i = 0; i < FTM_TPK_RI_RR_LEN; i++) {
printf("%u\n", ri_rr[i]);
}
}
}
ret = fwrite(replay, 1, nbytes, fp);
if (ret != nbytes) {
fprintf(stderr, "Error writing %d bytes to file, rc %d!\n",
nbytes, ret);
return BCME_ERROR;
}
}
done:
return ret;
}
static int
wl_proxd_get_collect_data(void *wl, cmd_t *cmd, FILE *fp, int index,
wl_proxd_rssi_bias_avg_t *rssi_bias_avg)
{
int ret;
void *buff;
wl_proxd_collect_query_t query;
uint16 expected_collect_data_ver = 0;
/* check firmware version */
/* Select collect data structure version based on FW version.
* Extend here for future versions
*/
if (wlc_ver_major(wl) <= 5) {
/* use v1 */
expected_collect_data_ver = WL_PROXD_COLLECT_DATA_VERSION_1;
} else {
/* use v2 */
expected_collect_data_ver = WL_PROXD_COLLECT_DATA_VERSION_2;
}
bzero(&query, sizeof(query));
query.method = htol32(PROXD_TOF_METHOD);
query.request = PROXD_COLLECT_QUERY_DATA;
query.index = htol16(index);
ret = wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
if (ret != BCME_OK)
return ret;
ret = wlc_proxd_collec_data_dump(buff, fp, rssi_bias_avg,
expected_collect_data_ver);
if (ret != BCME_OK) {
return ret;
}
return BCME_OK;
}
static int
wl_proxd_collect(void *wl, cmd_t *cmd, char **argv)
{
int ret;
void *buff;
wl_proxd_collect_query_t query, *pStatus;
wl_proxd_collect_header_t *pHdr;
const char *fname = "proxd_collect.dat";
FILE *fp = NULL;
int i, collect_method, total_frames, load_request = 0, remote_request = 0;
char chspec_str[CHANSPEC_STR_LEN];
chanspec_t chanspec;
float d_ref = -1;
#ifdef RSSI_REFINE
wl_proxd_rssi_bias_avg_t rssi_bias_avg;
bzero(&rssi_bias_avg, sizeof(rssi_bias_avg));
#endif
bzero(&query, sizeof(query));
query.method = htol32(PROXD_TOF_METHOD);
/* Skip the command name */
argv++;
while (*argv) {
if (strcmp(argv[0], "disable") == 0 || *argv[0] == '0') {
query.request = PROXD_COLLECT_SET_STATUS;
query.status = 0;
return wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
}
collect_method = (uint16)atoi(argv[0]);
if (strcmp(argv[0], "enable") == 0) {
query.request = PROXD_COLLECT_SET_STATUS;
query.status = 1;
return wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
} else if (isdigit((int)*argv[0]) &&
((collect_method >= WL_PROXD_COLLECT_METHOD_TYPE_DISABLE) &&
(collect_method <= WL_PROXD_COLLECT_METHOD_TYPE_EVENT_LOG))) {
query.request = PROXD_COLLECT_SET_STATUS;
query.status = collect_method;
return wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
}
if (!strcmp(argv[0], "-l")) {
load_request = 1;
argv++;
}
else if (!strcmp(argv[0], "-r")) {
remote_request = 1;
argv++;
}
else if (!strcmp(argv[0], "-f")) {
if (argv[1] == NULL)
return -1;
fname = argv[1];
argv += 2;
}
else if (!strcmp(argv[0], "-d")) {
if (argv[1] == NULL)
return -1;
sscanf((const char*)argv[1], "%f", &d_ref);
argv += 2;
} else
return -1;
}
query.request = PROXD_COLLECT_GET_STATUS;
ret = wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
if (ret != BCME_OK)
return ret;
pStatus = (wl_proxd_collect_query_t *)buff;
if (!pStatus->status) {
printf("Disable\n");
return BCME_OK;
}
if (pStatus->busy) {
printf("Busy\n");
return BCME_OK;
}
if ((pStatus->mode == WL_PROXD_MODE_TARGET) &&
(remote_request || load_request)) {
printf("Unsupport\n");
return BCME_OK;
}
if (remote_request && !pStatus->remote) {
printf("Remote data have not ready, please run this command again\n");
load_request = 1;
goto exit;
}
if (load_request && pStatus->remote) {
printf("Local data have not ready, please run command 'proxd_find' to get it\n");
goto exit;
}
query.request = PROXD_COLLECT_QUERY_HEADER;
ret = wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
if (ret != BCME_OK)
return ret;
pHdr = (wl_proxd_collect_header_t *)buff;
total_frames = (int)ltoh32(pHdr->total_frames);
if (!total_frames) {
printf("Enable\n");
goto exit;
}
chanspec = wl_chspec_from_driver(pHdr->chanspec);
wf_chspec_ntoa(chanspec, chspec_str);
printf("d_ref %5.1f\n", d_ref);
wlc_proxd_collec_header_dump(pHdr);
if ((fp = fopen(fname, "wb")) == NULL) {
fprintf(stderr, "Problem opening file %s\n", fname);
return 0;
}
ret = fwrite(buff, 1, sizeof(wl_proxd_collect_header_t), fp);
if (ret != sizeof(wl_proxd_collect_header_t)) {
fprintf(stderr, "Error writing to file rc %d\n", ret);
ret = -1;
goto exit;
}
for (i = 0; i < total_frames; i++) {
#ifdef RSSI_REFINE
ret = wl_proxd_get_collect_data(wl, cmd, fp, i, &rssi_bias_avg);
#else
ret = wl_proxd_get_collect_data(wl, cmd, fp, i, NULL);
#endif
if (ret != BCME_OK)
goto exit;
}
#ifdef RSSI_REFINE
if (total_frames > 0) {
printf("avg_rssi = %d avg_peak_offset = %d\n",
rssi_bias_avg.avg_rssi/total_frames,
rssi_bias_avg.avg_peak_offset/total_frames);
for (i = 0; i < 10; i++) {
printf("avg_threshold_%d = %d",
i, rssi_bias_avg.avg_threshold[i]/total_frames);
if ((i+1) % 5)
printf(", ");
else
printf("\n");
}
printf("avg_bias = %d\n", rssi_bias_avg.avg_bias/total_frames);
}
#endif
for (i = 0; i < 256; i++) {
ret = wl_proxd_get_debug_data(wl, cmd, i);
if (ret != BCME_OK)
break;
}
if (!load_request) {
/* FTM remote collect done */
query.request = PROXD_COLLECT_DONE;
(void)wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
}
ret = BCME_OK;
exit:
if (ret == BCME_OK && load_request) {
query.request = PROXD_COLLECT_REMOTE_REQUEST;
ret = wlu_var_getbuf(wl, cmd->name, &query, sizeof(query), &buff);
}
if (fp) fclose(fp);
return ret;
}
/* set measurement packet transmit rate */
static int proxd_method_set_vht_rate(miniopt_t *mopt)
{
char *startp, *endp;
char c;
bool legacy_set = FALSE, ht_set = FALSE, vht_set = FALSE;
int rate, mcs, Nss, tx_exp, bw, val;
bool stbc, ldpc, sgi;
uint32 rspec = 0;
/* set default values */
rate = 0;
mcs = 0;
Nss = 0;
tx_exp = 0;
stbc = FALSE;
ldpc = FALSE;
sgi = FALSE;
bw = 0;
startp = mopt->valstr;
endp = NULL;
if (*startp != 'h' && *startp != 'v') {
if ((rate = (int)strtol(startp, &endp, 10)) == 0)
return -1;
rate *= 2;
if (endp[0] == '.' && endp[1] == '5') {
rate += 1;
endp += 2;
}
startp = endp;
legacy_set = TRUE;
}
while (startp && ((c = *startp++) != '\0')) {
if (c == 'h') {
ht_set = TRUE;
mcs = (int)strtol(startp, &endp, 10);
if (mcs < 0 || mcs > 23) {
printf("HT MCS index %d out of range [0-23].\n", mcs);
return -1;
}
startp = endp;
}
else if (c == 'v') {
vht_set = TRUE;
mcs = (int)strtol(startp, &endp, 10);
if (mcs < 0 || mcs > 9) {
printf("HT MCS index %d out of range [0-9].\n", mcs);
return -1;
}
startp = endp;
}
else if (c == 'x') {
Nss = (int)strtol(startp, &endp, 10);
if (Nss < 1 || Nss > 8) {
printf("Nss %d out of range [1-8].\n", Nss);
return -1;
}
startp = endp;
}
else if (c == 'e') {
tx_exp = (int)strtol(startp, &endp, 10);
if (tx_exp < 0 || tx_exp > 3) {
printf("tx expansion %d out of range [0-3].\n", tx_exp);
return -1;
}
startp = endp;
}
else if (c == 's') {
stbc = TRUE;
continue;
}
else if (c == 'l') {
ldpc = TRUE;
continue;
}
else if (c == 'g') {
sgi = TRUE;
continue;
}
else if (c == 'b') {
val = (int)strtol(startp, &endp, 10);
if (val == 20) {
bw = WL_RSPEC_BW_20MHZ;
} else if (val == 40) {
bw = WL_RSPEC_BW_40MHZ;
} else if (val == 80) {
bw = WL_RSPEC_BW_80MHZ;
} else if (val == 160) {
bw = WL_RSPEC_BW_160MHZ;
} else {
printf("unexpected bandwidth specified \"%d\", "
"expected 20, 40, 80, or 160\n", val);
return -1;
}
startp = endp;
}
}
if (!legacy_set && !ht_set && !vht_set) {
printf("must specify one of legacy rate, HT (11n) rate hM, "
"or VHT (11ac) rate vM[xS]\n");
return -1;
}
if (legacy_set && (ht_set || vht_set)) {
printf("cannot use legacy rate and HT rate or VHT rate at the same time\n");
return -1;
}
if (ht_set && vht_set) {
printf("cannot use HT rate hM and HT rate vM[xS] at the same time\n");
return -1;
}
if (!vht_set && Nss != 0) {
printf("cannot use xS option with non VHT rate\n");
return -1;
}
if ((stbc || ldpc || sgi) && !(ht_set || vht_set)) {
printf("cannot use STBC/LDPC/SGI options with non HT/VHT rates\n");
return -1;
}
if (legacy_set) {
rspec = WL_RSPEC_ENCODE_RATE; /* 11abg */
rspec |= rate;
} else if (ht_set) {
rspec = WL_RSPEC_ENCODE_HT; /* 11n HT */
rspec |= mcs;
} else {
rspec = WL_RSPEC_ENCODE_VHT; /* 11ac VHT */
if (Nss == 0) {
Nss = 1; /* default Nss = 1 if --ss option not given */
}
rspec |= (Nss << WL_RSPEC_VHT_NSS_SHIFT) | mcs;
}
/* set the other rspec fields */
rspec |= (tx_exp << WL_RSPEC_TXEXP_SHIFT);
rspec |= bw;
rspec |= (stbc ? WL_RSPEC_STBC : 0);
rspec |= (ldpc ? WL_RSPEC_LDPC : 0);
rspec |= (sgi ? WL_RSPEC_SGI : 0);
mopt->uval = rspec;
return 0;
}
/* proxd set params common cmdl opts */
int proxd_method_set_common_param_from_opt(cmd_t *cmd,
miniopt_t *mopt, wl_proxd_params_common_t *proxd_params)
{
chanspec_t chanspec;
if (mopt->opt == 'c') {
/* chanspec iovar uses wl_chspec32_to_driver(chanspec), why ? */
if ((chanspec = wf_chspec_aton(mopt->valstr)) == 0) {
fprintf(stderr, "%s: could not parse \"%s\" as a channel\n",
cmd->name, mopt->valstr);
return BCME_BADARG;
}
proxd_params->chanspec
= wl_chspec_to_driver(chanspec);
if (proxd_params->chanspec == INVCHANSPEC) {
fprintf(stderr,
"%s: wl_chspec_to_driver() error \"%s\" \n",
cmd->name, mopt->valstr);
return BCME_BADARG;
}
} else if (mopt->opt == 't') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a timeout\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->timeout = htod16(mopt->val);
}
else
return BCME_USAGE_ERROR;
return BCME_OK;
}
/* proxd TOF cmdl ops */
int proxd_method_tof_set_param_from_opt(cmd_t *cmd,
miniopt_t *mopt, wl_proxd_params_tof_method_t *proxd_params)
{
if (mopt->opt == 'g') {
/* this param is only valid for TOF method only */
struct ether_addr ea;
if (!wl_ether_atoe(mopt->valstr, &ea)) {
fprintf(stderr,
"%s: could not parse \"%s\" as MAC address\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
memcpy(&proxd_params->tgt_mac, &ea, 6);
} else if (mopt->opt == 'f') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as FTM frame count\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->ftm_cnt = htod16(mopt->val);
} else if (mopt->opt == 'y') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as Retry Count\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->retry_cnt = htod16(mopt->val);
} else if (mopt->opt == 'r') {
if (!mopt->good_int) {
/* special case check for "-r 5.5" */
if (!strcmp(mopt->valstr, "5.5")) {
mopt->uval = 11;
} else if (proxd_method_set_vht_rate(mopt)) {
fprintf(stderr,
"%s: could not parse \"%s\" as a rate\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
} else
mopt->uval = mopt->uval*2;
proxd_params->tx_rate = htod16((mopt->uval & 0xffff));
proxd_params->vht_rate = htod16((mopt->uval >> 16));
} else
return BCME_USAGE_ERROR;
return BCME_OK;
}
/* RSSI method specific mdl opts */
int proxd_method_rssi_set_param_from_opt(cmd_t *cmd,
miniopt_t *mopt, wl_proxd_params_rssi_method_t *proxd_params)
{
if (mopt->opt == 'i') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an interval\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->interval = htod16(mopt->val);
} else if (mopt->opt == 'd') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a duration\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->duration = htod16(mopt->val);
} else if (mopt->opt == 'p') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a power\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->tx_power = htod16(mopt->val);
} else if (mopt->opt == 's') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a RSSI\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->rssi_thresh = htod16(mopt->val);
} else if (mopt->opt == 'm') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as a maxconvergetime\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_params->maxconvergtmo = htod16(mopt->val);
} else if (mopt->opt == 'r') {
if (!mopt->good_int) {
/* special case check for "-r 5.5" */
if (!strcmp(mopt->valstr, "5.5")) {
mopt->val = 11;
} else {
fprintf(stderr,
"%s: could not parse \"%s\" as a rate\n",
cmd->name, mopt->valstr);
return BCME_USAGE_ERROR;
}
} else
mopt->val = mopt->val*2;
proxd_params->tx_rate = htod16(mopt->val);
}
else
return BCME_USAGE_ERROR;
return BCME_OK;
}
static int
wl_proxd_params(void *wl, cmd_t *cmd, char **argv)
{
wl_proxd_params_iovar_t proxd_params, *reply;
uint16 method;
void *ptr = NULL;
int ret, opt_err;
miniopt_t to;
char chspec_str[CHANSPEC_STR_LEN];
char rate_str[64];
chanspec_t chanspec;
uint16 interval, duration;
uint32 tx_rate;
/* skip the command name and check if mandatory exists */
if (!*++argv) {
fprintf(stderr, "missing mandatory parameter \'method\'\n");
return BCME_USAGE_ERROR;
}
/* parse method */
method = (uint16)atoi(argv[0]);
if (method == 0) {
fprintf(stderr, "invalid parameter \'method\'\n");
return BCME_USAGE_ERROR;
}
bzero(&proxd_params, sizeof(proxd_params));
/* set method to get/set */
proxd_params.method = htod16(method);
ret = wlu_var_getbuf_sm(wl, cmd->name, &proxd_params, sizeof(proxd_params), &ptr);
if (ret != BCME_OK) {
return ret;
}
if (!*++argv) {
/* get */
/* display proxd_params got */
reply = (wl_proxd_params_iovar_t *)ptr;
printf("bf proxd_params.method:%d\n", proxd_params.method);
switch (proxd_params.method) {
case PROXD_RSSI_METHOD:
chanspec = wl_chspec_from_driver(reply->u.rssi_params.chanspec);
tx_rate = dtoh16(reply->u.rssi_params.tx_rate);
wf_chspec_ntoa(chanspec, chspec_str);
printf("channel=%s\n", chspec_str);
printf("interval=%d TU\n", dtoh16(reply->u.rssi_params.interval));
printf("duration=%d ms\n", dtoh16(reply->u.rssi_params.duration));
printf("rssi_thresh=%d dBm\n",
(int16)dtoh16(reply->u.rssi_params.rssi_thresh));
printf("maxconvergetime=%d ms\n\n",
dtoh16(reply->u.rssi_params.maxconvergtmo));
printf("tx_power=%d dBm\n",
(int16)dtoh16(reply->u.rssi_params.tx_power));
printf("tx_rate=%d%s Mbps\n",
(tx_rate / 2), (tx_rate & 1) ? ".5" : "");
printf("timeout=%d ms\n", dtoh16(reply->u.rssi_params.timeout));
break;
case PROXD_TOF_METHOD:
chanspec = wl_chspec_from_driver(reply->u.tof_params.chanspec);
tx_rate = dtoh16(reply->u.tof_params.tx_rate) |
(dtoh16(reply->u.tof_params.vht_rate) << 16);
wf_chspec_ntoa(chanspec, chspec_str);
wl_rate_print(rate_str, tx_rate);
printf("tgt_mac=%s \n",
wl_ether_etoa(&reply->u.tof_params.tgt_mac));
printf("ftm_cnt= %d\n", dtoh16(reply->u.tof_params.ftm_cnt));
printf("channel=%s (0x%04x)\n", chspec_str, chanspec);
printf("tx_rate=%s (0x%08x)\n", rate_str, tx_rate);
printf("timeout=%d ms\n",
dtoh16(reply->u.tof_params.timeout));
printf("retry_cnt=%d \n", dtoh16(reply->u.tof_params.retry_cnt));
break;
default:
fprintf(stderr,
"%s: ERROR undefined method \n", cmd->name);
return BCME_BADARG;
}
} else { /* set */
memcpy((void *)&proxd_params, (void *)ptr, sizeof(proxd_params));
proxd_params.method = method;
/* set */
miniopt_init(&to, cmd->name, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
int com_res, meth_res;
if (opt_err == 1) {
return BCME_USAGE_ERROR;
}
argv += to.consumed;
/* process cmd opts common for all methods */
com_res = proxd_method_set_common_param_from_opt(cmd,
&to, &proxd_params.u.cmn_params);
/* method specific opts */
switch (method) {
case PROXD_RSSI_METHOD:
meth_res = proxd_method_rssi_set_param_from_opt(cmd,
&to, &proxd_params.u.rssi_params);
break;
case PROXD_TOF_METHOD:
meth_res = proxd_method_tof_set_param_from_opt(cmd,
&to, &proxd_params.u.tof_params);
if (meth_res == BCME_BADARG)
return meth_res;
break;
default:
printf("ERROR: unsupported method\n");
return BCME_USAGE_ERROR;
}
/* if option is unknown to both common and meth specific */
if ((com_res != BCME_OK) && (meth_res != BCME_OK)) {
printf(">>>> Method:%d doesn't support cmd option:'%c'\n",
method, to.opt);
return BCME_USAGE_ERROR;
}
}
/* Sanity check of parameters against each other */
interval = dtoh16(proxd_params.u.rssi_params.interval);
duration = dtoh16(proxd_params.u.rssi_params.duration);
if (interval < duration) {
fprintf(stderr,
"%s: \'interval\' cannot be shorter than \'duration\'\n",
cmd->name);
return BCME_BADARG;
}
ret = wlu_var_setbuf(wl, cmd->name, &proxd_params, sizeof(proxd_params));
}
return ret;
}
/* proxd parse mixed param: <str0><val0><str1><val1>... */
static void
proxd_method_tof_parse_mixed_param(char* str, const char** p_name, int* p_val, int* p_map)
{
char* p;
/* parse stuff of format <str0><val0><str1><val1>... */
while (*p_name) {
p = strstr((const char*)str, (const char*)*p_name);
if (p) {
p += strlen((const char*)*p_name);
*p_map = 1;
*p_val = strtol(p, NULL, 10);
} else {
*p_map = 0;
}
p_name++;
p_map++;
p_val++;
}
}
/* proxd TOF tune ops */
static int
proxd_tune_set_param_from_opt(const char* cmd_name,
miniopt_t *mopt, wl_proxd_params_tof_tune_t *proxd_tune)
{
if (mopt->opt == 'k') {
if (!mopt->good_int) {
char *p = strstr(mopt->valstr, ",");
if (p) {
proxd_tune->Ki = htod32(atoi(mopt->valstr));
proxd_tune->Kt = htod32(atoi(p+1));
}
else {
fprintf(stderr,
"%s: could not parse \"%s\" as K\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
}
else {
proxd_tune->Ki = htod32(mopt->val);
proxd_tune->Kt = htod32(mopt->val);
}
proxd_tune->setflags |= WL_PROXD_SETFLAG_K;
} else if (mopt->opt == 'b') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as vhtack\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->vhtack = htod16(mopt->val);
} else if (mopt->opt == 'c') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as core\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->core = htod16(mopt->val);
} else if (mopt->opt == 'A') {
const char* n[4] = {"s", "h", "r", NULL};
int v[3] = {0, 0, 0};
int m[3] = {0, 0, 0};
proxd_method_tof_parse_mixed_param(mopt->valstr, n, v, m);
if (m[TOF_ADJ_SOFTWARE]) {
/* sw adj */
proxd_tune->sw_adj = (int16)v[TOF_ADJ_SOFTWARE];
}
if (m[TOF_ADJ_HARDWARE]) {
/* hw adj */
proxd_tune->hw_adj = (int16)v[TOF_ADJ_HARDWARE];
}
if (m[TOF_ADJ_SEQ]) {
/* ranging sequence */
proxd_tune->seq_en = (int16)v[TOF_ADJ_SEQ];
}
if ((m[TOF_ADJ_SOFTWARE] | m[TOF_ADJ_HARDWARE] | m[TOF_ADJ_SEQ]) == 0) {
fprintf(stderr,
"%s: could not parse \"%s\" as hw/sw adjustment enable params\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
} else if (mopt->opt == 'n') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as min time difference limitation\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->minDT = htod32(mopt->val);
} else if (mopt->opt == 'x') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as max time difference limitation\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->maxDT = htod32(mopt->val);
} else if (mopt->opt == 'N') {
int i = 0;
if (!mopt->good_int) {
char *p = mopt->valstr;
while (p && i < TOF_BW_SEQ_NUM) {
if (htod16(atoi(p)))
proxd_tune->N_log2[i] = htod16(atoi(p));
i++;
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->N_log2_2g = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_5g20.N_tx_log2 = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_5g20.N_rx_log2 = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_2g20.N_tx_log2 = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_2g20.N_rx_log2 = htod16(atoi(p));
}
} else {
for (; i < TOF_BW_SEQ_NUM; i++) {
proxd_tune->N_log2[i] = htod16(mopt->val);
}
proxd_tune->N_log2_2g = htod16(mopt->val);
proxd_tune->seq_5g20.N_tx_log2 = htod16(mopt->val);
proxd_tune->seq_5g20.N_rx_log2 = htod16(mopt->val);
proxd_tune->seq_2g20.N_tx_log2 = htod16(mopt->val);
proxd_tune->seq_2g20.N_rx_log2 = htod16(mopt->val);
}
proxd_tune->setflags |= WL_PROXD_SETFLAG_N;
} else if (mopt->opt == 'S') {
int i = 0;
if (!mopt->good_int) {
char *p = mopt->valstr;
while (p && i < TOF_BW_SEQ_NUM) {
if (htod16(atoi(p)))
proxd_tune->N_scale[i] = htod16(atoi(p));
i++;
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->N_scale_2g = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_5g20.N_tx_scale = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_5g20.N_rx_scale = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_2g20.N_tx_scale = htod16(atoi(p));
p = strstr(p, ",");
if (p)
p++;
}
if (p) {
proxd_tune->seq_2g20.N_rx_scale = htod16(atoi(p));
}
} else {
for (; i < TOF_BW_SEQ_NUM; i++) {
proxd_tune->N_scale[i] = htod16(mopt->val);
}
proxd_tune->N_scale_2g = htod16(mopt->val);
proxd_tune->seq_5g20.N_tx_scale = htod16(mopt->val);
proxd_tune->seq_5g20.N_rx_scale = htod16(mopt->val);
proxd_tune->seq_2g20.N_rx_scale = htod16(mopt->val);
proxd_tune->seq_2g20.N_rx_scale = htod16(mopt->val);
}
proxd_tune->setflags |= WL_PROXD_SETFLAG_S;
} else if (mopt->opt == 'F') {
int i = 0;
if (!mopt->good_int) {
char *p = mopt->valstr;
while (p && i < TOF_BW_SEQ_NUM) {
if (htod16(atoi(p)))
proxd_tune->ftm_cnt[i] = htod16(atoi(p));
i++;
p = strstr(p, ",");
if (p)
p++;
}
} else {
for (; i < TOF_BW_SEQ_NUM; i++) {
proxd_tune->ftm_cnt[i] = htod16(mopt->val);
}
}
} else if (mopt->opt == 't') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as total frmcnt\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->totalfrmcnt = (mopt->val);
} else if (mopt->opt == 'r') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as media reserve value\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->rsv_media = (mopt->val);
} else if (mopt->opt == 'f') {
if (!mopt->good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as flags\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->flags = htod16(mopt->val);
} else if (mopt->opt == 'W') {
const char* n[4] = {"b", "l", "o", NULL};
const char* s[4] = {"s", "l", "o", NULL};
const char **p;
int v[TOF_BW_NUM] = {0, 0, 0};
int m[TOF_BW_NUM] = {0, 0, 0};
int i;
if (*mopt->valstr == 's')
p = s;
else
p = n;
proxd_method_tof_parse_mixed_param(mopt->valstr, p, v, m);
if (m[0]) {
/* Got bw */
if (v[0] == TOF_BW_80MHZ)
i = TOF_BW_80MHZ_INDEX;
else if (v[0] == TOF_BW_40MHZ)
i = TOF_BW_40MHZ_INDEX;
else if (v[0] == TOF_BW_20MHZ)
i = TOF_BW_20MHZ_INDEX;
else {
fprintf(stderr,
"%s: could not parse \"%s\" as window params\n",
cmd_name, mopt->valstr);
return BCME_USAGE_ERROR;
}
if (p == n) {
/* Normal */
if (m[1]) {
/* Got length */
proxd_tune->w_len[i] = (int16)v[1];
}
if (m[2]) {
/* Got offset */
proxd_tune->w_offset[i] = (int16)v[2];
}
} else {
/* Seq */
if (m[1] && i == TOF_BW_20MHZ_INDEX) {
/* Got length */
proxd_tune->seq_5g20.w_len = (int16)v[1];
proxd_tune->seq_2g20.w_len = (int16)v[1];
}
if (m[2]&& i == TOF_BW_20MHZ_INDEX) {
/* Got offset */
proxd_tune->seq_5g20.w_offset = (int16)v[2];
proxd_tune->seq_2g20.w_offset = (int16)v[2];
}
}
}
} else if (mopt->opt == 'B') {
if (!mopt->good_int) {
fprintf(stderr,
"proxd ftm tune: could not parse \"%s\" as bitflip_threshold\n",
mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->bitflip_thresh = htod16(mopt->val);
} else if (mopt->opt == 'R') {
if (!mopt->good_int) {
fprintf(stderr,
"proxd ftm tune: could not parse \"%s\" as snr_threshold\n",
mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->snr_thresh = htod16(mopt->val);
} else if (mopt->opt == 'T') {
if (!mopt->good_int) {
fprintf(stderr,
"proxd ftm tune: could not parse \"%s\" as recv_2g_threshold\n",
mopt->valstr);
return BCME_USAGE_ERROR;
}
if (mopt->val >= 0)
return BCME_RANGE;
proxd_tune->recv_2g_thresh = mopt->val;
} else if (mopt->opt == 'V') {
if (!mopt->good_int) {
fprintf(stderr,
"proxd ftm tune: could not parse \"%s\" as Auto Core Select "
"Group Delay Variance threshold\n",
mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->acs_gdv_thresh = htod32(mopt->val);
} else if (mopt->opt == 'I') {
if (!mopt->good_int) {
fprintf(stderr,
"proxd ftm tune: could not parse \"%s\" as Auto Core Select "
"RSSI threshold\n",
mopt->valstr);
return BCME_USAGE_ERROR;
}
if (mopt->val >= 0)
return BCME_RANGE;
proxd_tune->acs_rssi_thresh = mopt->val;
} else if (mopt->opt == 's') {
if (!mopt->good_int) {
fprintf(stderr,
"proxd ftm tune: could not parse \"%s\" as smoothing window "
"enable\n",
mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->smooth_win_en = mopt->val;
} else if (mopt->opt == 'M') {
if (!mopt->good_int) {
fprintf(stderr,
"proxd ftm tune: could not parse \"%s\" as auto core select "
"group delay max - min threshold\n",
mopt->valstr);
return BCME_USAGE_ERROR;
}
proxd_tune->acs_gdmm_thresh = htod32(mopt->val);
} else
return BCME_USAGE_ERROR;
return BCME_OK;
}
static int
wl_proxd_tune(void *wl, cmd_t *cmd, char **argv)
{
wl_proxd_params_iovar_t proxd_tune, *reply;
uint16 method;
void *ptr = NULL;
int ret, opt_err;
miniopt_t to;
/* skip the command name and check if mandatory exists */
if (!*++argv) {
fprintf(stderr, "missing mandatory parameter \'method\'\n");
return BCME_USAGE_ERROR;
}
/* parse method */
method = (uint16)atoi(argv[0]);
if (method == 0) {
fprintf(stderr, "invalid parameter \'method\'\n");
return BCME_USAGE_ERROR;
}
bzero(&proxd_tune, sizeof(proxd_tune));
proxd_tune.method = htod16(method);
ret = wlu_var_getbuf_sm(wl, cmd->name, &proxd_tune, sizeof(proxd_tune), &ptr);
if (ret != BCME_OK) {
return ret;
}
if (!*++argv) {
/* get */
/* display proxd_params got */
reply = (wl_proxd_params_iovar_t *)ptr;
printf("bf proxd_params.method:%d\n", proxd_tune.method);
switch (proxd_tune.method) {
case PROXD_RSSI_METHOD:
break;
case PROXD_TOF_METHOD:
ret = proxd_tune_display(&reply->u.tof_tune, sizeof(reply->u.tof_tune));
break;
default:
fprintf(stderr,
"%s: ERROR undefined method \n", cmd->name);
return BCME_BADARG;
}
} else {
/* set */
memcpy((void *)&proxd_tune, (void *)ptr, sizeof(proxd_tune));
proxd_tune.method = method;
miniopt_init(&to, cmd->name, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
int meth_res = BCME_USAGE_ERROR;
if (opt_err == 1) {
return BCME_USAGE_ERROR;
}
argv += to.consumed;
/* method specific opts */
switch (method) {
case PROXD_RSSI_METHOD:
break;
case PROXD_TOF_METHOD:
meth_res = proxd_tune_set_param_from_opt(cmd->name,
&to, &proxd_tune.u.tof_tune);
break;
default:
printf("ERROR: unsupported method\n");
return BCME_USAGE_ERROR;
}
/* if option is unknown to tune specific */
if (meth_res != BCME_OK) {
printf(">>>> Method:%d doesn't support cmd option:'%c'\n",
method, to.opt);
return meth_res;
}
}
ret = wlu_var_setbuf(wl, cmd->name, &proxd_tune, sizeof(proxd_tune));
}
return ret;
}
static const char *wl_proxd_mode_str(uint8 mode)
{
static const char *proxd_mode[] = {"Undetected", "Neutral", "Initiator", "Target",
"UNKNOWN"};
if (mode > WL_PROXD_MODE_TARGET)
mode = WL_PROXD_MODE_TARGET+1;
return proxd_mode[mode];
}
static const char *wl_proxd_state_str(uint8 state)
{
static const char *proxd_state[] = {"Poll", "Pairing", "Handshake", "Detected",
"Pipeline", "NegMode", "Monitor", "Unknown"};
if (state == RSSI_STATE_POLL)
return proxd_state[0];
if (state == RSSI_STATE_TPAIRING || state == RSSI_STATE_IPAIRING)
return proxd_state[1];
if (state == RSSI_STATE_THANDSHAKE || state == RSSI_STATE_IHANDSHAKE)
return proxd_state[2];
if (state == RSSI_STATE_CONFIRMED)
return proxd_state[3];
if (state == RSSI_STATE_PIPELINE)
return proxd_state[4];
if (state == RSSI_STATE_NEGMODE)
return proxd_state[5];
if (state == RSSI_STATE_MONITOR)
return proxd_state[6];
return proxd_state[7];
}
static const char *wl_proxd_tof_state_str(uint8 state)
{
static const char *tof_proxd_state[] =
{"Idle", "Wait", "LegacyWait", "Confirmed", "Unknown", "Report"};
if (state == TOF_STATE_IDLE)
return tof_proxd_state[0];
if (state == TOF_STATE_IWAITM || state == TOF_STATE_TWAITM ||
state == TOF_STATE_IWAITCL || state == TOF_STATE_TWAITCL)
return tof_proxd_state[1];
if (state == TOF_STATE_ILEGACY)
return tof_proxd_state[2];
if (state == TOF_STATE_ICONFIRM)
return tof_proxd_state[3];
if (state == TOF_STATE_IREPORT)
return tof_proxd_state[5];
return tof_proxd_state[4];
}
static const char *wl_proxd_tof_reason_str(uint8 reason)
{
static const char *tof_proxd_reason[] = {"OK", "RxedReqEnd", "Timeout",
"LostACK", "InvalidAVB"};
if (reason > TOF_REASON_INVALIDAVB)
reason = 0;
return tof_proxd_reason[reason];
}
static const char *wl_proxd_reason_str(uint8 reason)
{
static const char *proxd_reason[] = {"Unknown", "Low rssi", "State machine out of SYNC",
"Timeout"};
if (reason > RSSI_REASON_TIMEOUT)
reason = 0;
return proxd_reason[reason];
}
static int
wl_proxd_status(void *wl, cmd_t *cmd, char **argv)
{
wl_proxd_status_iovar_t status, *statusp;
int ret = BCME_BADARG;
if (!*++argv) {
/* Get */
bzero(&status, sizeof(wl_proxd_status_iovar_t));
if ((ret = wlu_iovar_get(wl, cmd->name, (void *) &status,
(sizeof(wl_proxd_status_iovar_t)))) < 0)
return (ret);
statusp = &status;
switch (statusp->method)
{
case PROXD_RSSI_METHOD:
printf("mode=%s\n", wl_proxd_mode_str(statusp->mode));
printf("state=%s\n", wl_proxd_state_str(statusp->state));
printf("peer mode=%s\n", wl_proxd_mode_str(statusp->peermode));
printf("peer=%s\n", wl_ether_etoa(&statusp->peer));
printf("lowest rssi=%d\n", statusp->low_rssi);
printf("highest rssi=%d\n", statusp->hi_rssi);
printf("tx pkts=%d\n", statusp->txcnt);
printf("rx pkts=%d\n", statusp->rxcnt);
printf("reason=%s\n\n", wl_proxd_reason_str(statusp->reason));
break;
case PROXD_TOF_METHOD:
printf("mode=%s\n", wl_proxd_mode_str(statusp->mode));
printf("state=%s\n", wl_proxd_tof_state_str(statusp->state));
if (statusp->distance == 0xffffffff)
printf("distance=-1\n");
else
printf("distance=%d.%04d\n", statusp->distance >> 4,
((statusp->distance & 0xf)*625));
printf("peer=%s\n", wl_ether_etoa(&statusp->peer));
printf("avg rssi=%d\n", statusp->avg_rssi);
printf("tx pkts=%d\n", statusp->txcnt);
printf("rx pkts=%d\n", statusp->rxcnt);
printf("frame types = CCK %d OFDM %d 11N %d 11AC %d\n",
statusp->frame_type_cnt[FRAME_TYPE_CCK],
statusp->frame_type_cnt[FRAME_TYPE_OFDM],
statusp->frame_type_cnt[FRAME_TYPE_11N],
statusp->frame_type_cnt[FRAME_TYPE_11AC]);
printf("adj types = SW %d HW %d SEQ %d NONE %d\n",
statusp->adj_type_cnt[TOF_ADJ_SOFTWARE],
statusp->adj_type_cnt[TOF_ADJ_HARDWARE],
statusp->adj_type_cnt[TOF_ADJ_SEQ],
statusp->adj_type_cnt[TOF_ADJ_NONE]);
printf("report status= %d\n", statusp->dbgstatus);
printf("reason=%s\n", wl_proxd_tof_reason_str(statusp->reason));
printf("frmcnt=%d\n", statusp->low_rssi);
if (statusp->hi_rssi == TOF_LEGACY_AP)
printf("measure=OneSide\n\n");
else
printf("measure=TwoSide\n\n");
break;
default:
printf("ERROR: unsupported method\n");
return BCME_USAGE_ERROR;
}
}
else
printf("Cannot set proxd_status\n");
return ret;
}
static int
wl_proxd_payload(void *wl, cmd_t *cmd, char **argv)
{
char *buf;
uint16 len, *reply;
int ret;
buf = malloc(WL_PROXD_PAYLOAD_LEN);
if (buf == NULL) {
fprintf(stderr, "Failed to allocate buffer of %d bytes\n", WL_PROXD_PAYLOAD_LEN);
return -1;
}
bzero(buf, WL_PROXD_PAYLOAD_LEN);
/* skip the command name and check if NULL */
if (!*++argv) {
/* Get */
if ((ret = wlu_iovar_get(wl, cmd->name, (void *)buf, WL_PROXD_PAYLOAD_LEN)) < 0) {
free(buf);
return (ret);
}
reply = (uint16 *)buf;
len = dtoh16(reply[0]);
if (len > 0) {
char *ptr = buf+sizeof(uint16);
char *endptr = ptr+len;
int num = 0;
uint8 val;
printf("Payload Length %d\n", len);
while (ptr < endptr)
{
val = *(uint8 *)ptr++;
printf("%02X", val);
if (++num == 40)
{
printf("\n");
num = 0;
}
}
if (num) printf("\n");
}
} else {
/* Set */
len = (uint16)atoi(argv[0]);
if (len > 0) {
if (!argv[1]) {
printf("Payload content is missing\n");
free(buf);
return -1;
}
else {
char *ptr = argv[1];
char *bufp = buf;
char hex[] = "XX";
if ((uint16)strlen(ptr) != len*2)
{
printf("Payload length mismatch %d %d\n", len,
((int)strlen(ptr))/2);
free(buf);
return -1;
}
while (*ptr) {
strncpy(hex, ptr, 2);
*bufp++ = (char) strtoul(hex, NULL, 16);
ptr += 2;
}
}
}
ret = wlu_var_setbuf(wl, cmd->name, buf, len);
}
free(buf);
return ret;
}
static int wl_proxd_report(void *wl, cmd_t *cmd, char **argv)
{
uint16 len;
int i, ret;
struct ether_addr addrlist[WL_PROXD_MAXREPORT];
/* skip the command name and check if NULL */
if (!*++argv) {
/* Get */
len = sizeof(struct ether_addr) * WL_PROXD_MAXREPORT;
bzero(&addrlist[0], len);
if ((ret = wlu_iovar_get(wl, cmd->name, (void *)addrlist, len)) < 0) {
return (ret);
}
for (i = 0; i < WL_PROXD_MAXREPORT; i++)
{
if (ETHER_ISNULLADDR(&addrlist[i]))
break;
printf("%s ", wl_ether_etoa(&addrlist[i]));
}
printf("\n");
} else {
/* Set */
i = 0;
while (argv[0]) {
if (i >= WL_PROXD_MAXREPORT) {
printf("Over the maxium report number %d\n", WL_PROXD_MAXREPORT);
return BCME_BUFTOOLONG;
}
if (!wl_ether_atoe(argv[0], &addrlist[i])) {
if (i == 0)
return BCME_BADADDR;
break;
}
i++;
argv++;
}
ret = wlu_var_setbuf(wl, cmd->name, addrlist, ETHER_ADDR_LEN * i);
}
return ret;
}
/* print or calculate & print location info */
void wl_proxd_tof_host_calc(wl_proxd_event_data_t* evp)
{
uint32 distance;
uint32 meanrtt, modertt, medianrtt, dst_sigma; /* standard deviation */
int ftm_cnt;
int16 avg_rssi, validfrmcnt;
int32 var1, var2, var3;
char diststr[40];
distance = ntoh32(evp->distance);
dst_sigma = ntoh32(evp->sdrtt);
ftm_cnt = ntoh16(evp->ftm_cnt);
avg_rssi = ntoh16(evp->avg_rssi);
validfrmcnt = ntoh16(evp->validfrmcnt);
meanrtt = ntoh32(evp->meanrtt);
modertt = ntoh32(evp->modertt);
medianrtt = ntoh32(evp->medianrtt);
var1 = ntoh32(evp->var1);
var2 = ntoh32(evp->var2);
var3 = ntoh32(evp->var3);
bzero(diststr, sizeof(diststr));
if (distance == 0xffffffff)
sprintf(diststr, "distance=-1m\n");
else
sprintf(diststr, "distance=%d.%04dm\n", distance>>4, (distance & 0xf) * 625);
if (ntoh16(evp->mode) == WL_PROXD_MODE_TARGET) {
if (evp->TOF_type == TOF_TYPE_REPORT) {
printf("Report:(%s) and ", wl_ether_etoa(&evp->peer_mac));
printf("(%s); %s", wl_ether_etoa((struct ether_addr *)
&evp->peer_router_info), diststr);
}
else
printf("Target:(%s); %s; ", wl_ether_etoa(&evp->peer_mac), diststr);
printf("mean %d mode %d median %d\n", meanrtt, modertt, medianrtt);
}
else
printf("Initiator:(%s); %s; ", wl_ether_etoa(&evp->peer_mac), diststr);
printf("sigma:%d.%d;", dst_sigma/10, dst_sigma % 10);
printf("rssi:%d validfrmcnt %d\n", avg_rssi, validfrmcnt);
if (evp->TOF_type == TOF_TYPE_REPORT)
printf("var3: %d\n", var3);
else
printf("var: %d %d %d\n", var1, var2, var3);
if (ftm_cnt > 1) {
int i;
printf("event contains %d rtd samples for host side calculation:\n",
ftm_cnt);
for (i = 0; i < ftm_cnt; i++) {
printf("ftm[%d] --> value:%d rssi:%d\n", i,
ntoh32(evp->ftm_buff[i].value), evp->ftm_buff[i].rssi);
}
printf("host side calculation result: TBD\n");
/* TODO: process raw samples */
/* e.g:
1) mean Mn = (S1+S2+... Sn)/N
2) variatin: Xn = (Sn - M)^2;
3) sqrt((X1 + X2 + .. Xn)/N)
*/
}
}
#ifdef WL_NAN
static const char *wl_proxd_nan_status_str(uint8 status)
{
static const char *proxd_nan_status[] = {"Unknown", "Success", "Fail", "Timeout", "Abort"};
if (status > WL_NAN_RANGING_STATUS_ABORT)
status = 0;
return proxd_nan_status[status];
}
/* print or calculate & print nan event */
void wl_proxd_nan_host_calc(wl_nan_ranging_event_data_t* evp)
{
wl_nan_ranging_result_t *rp;
int i;
uint32 distance;
uint32 dst_sigma; /* standard deviation */
uint8 validfrmcnt;
char dist[40];
rp = evp->rr;
for (i = 0; i < evp->count; i++, rp++) {
distance = rp->distance;
dst_sigma = rp->rtt_var;
validfrmcnt = rp->sounding_count;
bzero(dist, sizeof(dist));
if (distance == 0xffffffff)
sprintf(dist, "distance=-1m\n");
else
sprintf(dist, "distance=%d.%dm\n", distance>>4, (distance & 0xf)*625);
if (evp->mode == WL_PROXD_MODE_TARGET) {
if (!ETHER_ISNULLADDR(&rp->tgtea)) {
printf("Report:(%s) and ", wl_ether_etoa(&rp->ea));
printf("(%s); %s", wl_ether_etoa(&rp->tgtea), dist);
}
else
printf("Target:(%s); %s; ", wl_ether_etoa(&rp->ea), dist);
}
else
printf("Initiator:(%s); %s", wl_ether_etoa(&rp->ea), dist);
printf("%s sigma:%d.%d validfrmcnt %d\n", wl_proxd_nan_status_str(rp->status),
dst_sigma/10, dst_sigma % 10, validfrmcnt);
}
}
#endif /* WL_NAN */
#if defined(linux)
/* print timestamp info */
static void wl_proxd_tof_ts_results(wl_proxd_event_ts_results_t* tsp)
{
int16 ver;
int tscnt;
int i;
ver = ntoh16(tsp->ver);
tscnt = ntoh16(tsp->ts_cnt);
if (ver == 2) {
printf("Frame Count %d\n Timestamps:\n", tscnt);
if (tscnt > 0) {
for (i = 0; i < tscnt; i++) {
printf("t1[%d]=%u : t2[%d]=%u : t3[%d]=%u : t4[%d]=%u\n",
i, ntoh32(tsp->ts_buff[i].t1),
i, ntoh32(tsp->ts_buff[i].t2),
i, ntoh32(tsp->ts_buff[i].t3),
i, ntoh32(tsp->ts_buff[i].t4));
}
}
}
}
#define PROXD_EVENTS_BUFFER_SIZE 2048
static int
wl_proxd_event_check(void *wl, cmd_t *cmd, char **argv)
{
bool exit_on1stresult = FALSE;
int fd, err, octets;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth0"};
bcm_event_t *event;
uint32 reason;
uint16 mode; /* target or initiator */
char* data;
int event_type;
uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* 128-bit mask */
wl_proxd_event_data_t* evp = NULL;
#ifdef WL_NAN
wl_nan_ranging_event_data_t *nanevp = NULL;
#endif
wl_proxd_event_ts_results_t* tsp = NULL;
UNUSED_PARAMETER(wl);
UNUSED_PARAMETER(cmd);
if (argv[1] == NULL) {
printf("<ifname> param is missing\n");
return -1;
}
if (*++argv) {
strncpy(ifnames, *argv, (IFNAMSIZ - 1));
}
bzero(&ifr, sizeof(ifr));
strncpy(ifr.ifr_name, ifnames, (IFNAMSIZ - 1));
/* read current mask state */
if ((err = wlu_iovar_get(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN))) {
printf("couldn't read event_msgs\n");
return (err);
}
event_inds_mask[WLC_E_PROXD / 8] |= 1 << (WLC_E_PROXD % 8);
if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
return (err);
if (*++argv) {
if (strcmp(*argv, "osh") == 0) {
/* exit after processing 1st proximity result */;
exit_on1stresult = TRUE;
}
}
fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM));
if (fd < 0) {
printf("Cannot create socket %d\n", fd);
return -1;
}
err = ioctl(fd, SIOCGIFINDEX, &ifr);
if (err < 0) {
printf("Cannot get iface:%s index \n", ifr.ifr_name);
goto exit1;
}
bzero(&sll, sizeof(sll));
sll.sll_family = AF_PACKET;
sll.sll_protocol = hton16(ETHER_TYPE_BRCM);
sll.sll_ifindex = ifr.ifr_ifindex;
err = bind(fd, (struct sockaddr *)&sll, sizeof(sll));
if (err < 0) {
printf("Cannot bind %d\n", err);
goto exit1;
}
data = (char*)malloc(PROXD_EVENTS_BUFFER_SIZE);
if (data == NULL) {
printf("Cannot not allocate %d bytes for events receive buffer\n",
PROXD_EVENTS_BUFFER_SIZE);
goto exit1;
}
printf("waiting for LBS events :%s\n", ifr.ifr_name);
while (1) {
fflush(stdout);
octets = recv(fd, data, PROXD_EVENTS_BUFFER_SIZE, 0);
if (octets <= 0) {
/* sigterm */
err = -1;
break;
}
event = (bcm_event_t *)data;
event_type = ntoh32(event->event.event_type);
reason = ntoh32(event->event.reason);
#ifdef WL_FTM_DEBUG
printf("%s: event_type 0x%x, ntoh32()=0x%x, ltoh32()=0x%x\n",
__FUNCTION__, event->event.event_type, event_type,
ltoh32(event->event.event_type));
#endif /* WL_FTM_DEBUG */
if ((event_type != WLC_E_PROXD)) {
/* may be other BCM events we are not interested in */
#ifdef WL_FTM_DEBUG
printf("WARNING: not a proxd BCM_EVENT:%d\n", event_type);
#endif /* WL_FTM_DEBUG */
continue;
}
/* check 'version' for FTM events */
if (ftm_event_check((bcm_event_t *) data) == 0)
continue; /* continue to rx event */
/* continue to handle non-FTM events */
#ifdef WL_NAN
if (reason == WLC_E_PROXD_NAN_EVENT) {
nanevp = (wl_nan_ranging_event_data_t *)&data[sizeof(bcm_event_t)];
mode = nanevp->mode;
}
else
#endif
if (reason == WLC_E_PROXD_TS_RESULTS) {
tsp = (wl_proxd_event_ts_results_t*)&data[sizeof(bcm_event_t)];
mode = ntoh16(tsp->mode);
} else {
/* move to bcm event payload, which is proxd event structure */
evp = (wl_proxd_event_data_t*)&data[sizeof(bcm_event_t)];
mode = ntoh16(evp->mode);
}
printf("mode:%s; event:",
(mode == WL_PROXD_MODE_INITIATOR)?"initiator":"target");
switch (reason) {
case WLC_E_PROXD_FOUND:
printf("WLC_E_PROXD_FOUND; ");
wl_proxd_tof_host_calc(evp); /* backward compatibility with RSSI method */
break;
case WLC_E_PROXD_GONE:
printf("WLC_E_PROXD_GONE; ");
break;
case WLC_E_PROXD_START:
/* event for targets / accesspoints */
printf("WLC_E_PROXD_START; ");
break;
case WLC_E_PROXD_STOP:
printf("WLC_E_PROXD_STOP; ");
break;
case WLC_E_PROXD_COMPLETED:
printf("WLC_E_PROXD_COMPLETED; ");
/* all new method results should land here */
wl_proxd_tof_host_calc(evp);
if (exit_on1stresult)
goto exit0;
break;
case WLC_E_PROXD_TS_RESULTS:
printf("WLC_E_PROXD_TS_RESULTS; ");
wl_proxd_tof_ts_results(tsp);
if (exit_on1stresult)
goto exit0;
break;
case WLC_E_PROXD_ERROR:
printf("WLC_E_PROXD_ERROR:%d;", evp->err_code);
/* all new method results should land here */
wl_proxd_tof_host_calc(evp);
if (exit_on1stresult)
goto exit0;
break;
case WLC_E_PROXD_COLLECT_START:
printf("WLC_E_PROXD_COLLECT_START; ");
break;
case WLC_E_PROXD_COLLECT_STOP:
printf("WLC_E_PROXD_COLLECT_STOP; ");
break;
case WLC_E_PROXD_COLLECT_COMPLETED:
printf("WLC_E_PROXD_COLLECT_COMPLETED; ");
break;
case WLC_E_PROXD_COLLECT_ERROR:
printf("WLC_E_PROXD_COLLECT_ERROR; ");
break;
#ifdef WL_NAN
case WLC_E_PROXD_NAN_EVENT:
printf("WLC_E_NAN_EVENT; ");
wl_proxd_nan_host_calc(nanevp);
break;
#endif
default:
printf("ERROR: unsupported EVENT reason code:%d; ",
reason);
err = -1;
break;
}
printf("\n");
}
exit0:
/* if we ever reach here */
free(data);
exit1:
close(fd);
/* Read the event mask from driver and mask the event WLC_E_PROXD */
if (!(err = wlu_iovar_get(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN))) {
event_inds_mask[WLC_E_PROXD / 8] &= (~(1 << (WLC_E_PROXD % 8)));
err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN);
}
fflush(stdout);
return (err);
}
#endif /* linux */
#if defined(WL_NAN)
static int wl_nan_ranging_config(void *wl, cmd_t *cmd, char **argv)
{
wl_nan_ranging_config_t nanconfig, *pnan_config;
char chspec_str[CHANSPEC_STR_LEN];
int rc;
void *ptr = NULL;
int count;
chanspec_t chanspec;
count = ARGCNT(argv);
/* GET operation */
if (*++argv == NULL) {
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
pnan_config = (wl_nan_ranging_config_t *)ptr;
wf_chspec_ntoa(pnan_config->chanspec, chspec_str);
printf("chanspec: %s(0x%04x)\n", chspec_str, pnan_config->chanspec);
printf("timeslot: %d\n", pnan_config->timeslot);
printf("duration: %d\n", pnan_config->duration);
printf("allowed mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
pnan_config->allow_mac.octet[0],
pnan_config->allow_mac.octet[1],
pnan_config->allow_mac.octet[2],
pnan_config->allow_mac.octet[3],
pnan_config->allow_mac.octet[4],
pnan_config->allow_mac.octet[5]);
printf("flags: 0x%04x\n", pnan_config->flags);
return rc;
}
/*
** Set the attributes.
*/
if (count < 4) {
return BCME_USAGE_ERROR;
}
pnan_config = &nanconfig;
if ((chanspec = wf_chspec_aton(*argv)) == 0) {
fprintf(stderr, "%s: could not parse \"%s\" as a channel\n",
cmd->name, *argv);
return BCME_BADARG;
}
pnan_config->chanspec = wl_chspec_to_driver(chanspec);
if (pnan_config->chanspec == INVCHANSPEC) {
fprintf(stderr, "%s: wl_chspec_to_driver() error \"%s\" \n",
cmd->name, *argv);
return BCME_BADARG;
}
pnan_config->timeslot = strtoul(*++argv, NULL, 0);
if (pnan_config->timeslot == 0 || pnan_config->timeslot >= 512) {
fprintf(stderr, "Invalid timeslot \"%s\" \n", *argv);
return BCME_BADARG;
}
pnan_config->duration = strtoul(*++argv, NULL, 0);
if (pnan_config->duration == 0 || pnan_config->duration >= 512) {
fprintf(stderr, "Invalid duration \"%s\" \n", *argv);
return BCME_BADARG;
}
if (*++argv) {
/* MAC address */
if (!wl_ether_atoe(*argv, &pnan_config->allow_mac)) {
fprintf(stderr, "nan ranging config mac addr err\n");
return BCME_BADARG;
}
if (*++argv) {
pnan_config->flags = strtoul(*argv, NULL, 0);
} else
pnan_config->flags = 0;
} else {
memcpy(&pnan_config->allow_mac, &ether_bcast, ETHER_ADDR_LEN);
pnan_config->flags = 0;
}
rc = wlu_var_setbuf(wl, cmd->name, pnan_config, sizeof(wl_nan_ranging_config_t));
return (rc);
}
static int wl_nan_ranging_start(void *wl, cmd_t *cmd, char **argv)
{
const char *str;
wl_nan_ranging_list_t *pnan_list;
int buf_len;
int str_len;
int rc, i;
void *ptr = NULL;
int count;
chanspec_t chanspec;
count = ARGCNT(argv);
/* GET operation */
if (*++argv == NULL) {
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
pnan_list = (wl_nan_ranging_list_t *)ptr;
printf("num peers: %d \n", pnan_list->count);
printf("num peers done: %d \n", pnan_list->num_peers_done);
printf("num dws: %d \n", pnan_list->num_dws);
printf("----------------------------------------------------------------------\n");
printf("Address \t\tchanspec\tcount\tretry\tabitmap\tflags\n");
for (i = 0; i < pnan_list->count; i++) {
printf("%02x:%02x:%02x:%02x:%02x:%02x\t0x%04x\t\t%d\t%d\t0x%x\t0x%x\n",
pnan_list->rp[i].ea.octet[0],
pnan_list->rp[i].ea.octet[1],
pnan_list->rp[i].ea.octet[2],
pnan_list->rp[i].ea.octet[3],
pnan_list->rp[i].ea.octet[4],
pnan_list->rp[i].ea.octet[5],
pnan_list->rp[i].chanspec,
pnan_list->rp[i].frmcnt,
pnan_list->rp[i].retrycnt,
pnan_list->rp[i].abitmap,
pnan_list->rp[i].flags);
}
return rc;
}
/*
** Set the attributes.
*/
if ((count % 6) != 2) {
return BCME_USAGE_ERROR;
}
str = cmd->name;
str_len = strlen(str);
strncpy(buf, str, str_len);
buf[ str_len ] = '\0';
pnan_list = (wl_nan_ranging_list_t *) (buf + str_len + 1);
pnan_list->count = count/6;
pnan_list->num_peers_done = 0;
pnan_list->num_dws = strtoul(*argv++, NULL, 0);
for (i = 0; i < pnan_list->count; i++, argv++) {
if ((chanspec = wf_chspec_aton(*argv)) == 0) {
fprintf(stderr, "%s: could not parse \"%s\" as a channel\n",
cmd->name, *argv);
return BCME_BADARG;
}
pnan_list->rp[i].chanspec = wl_chspec_to_driver(chanspec);
if (pnan_list->rp[i].chanspec == INVCHANSPEC) {
fprintf(stderr, "%s: wl_chspec_to_driver() error \"%s\" \n",
cmd->name, *argv);
return BCME_BADARG;
}
if (!wl_ether_atoe(*++argv, &pnan_list->rp[i].ea)) {
fprintf(stderr, "ranging mac addr err\n");
return BCME_BADADDR;
}
pnan_list->rp[i].abitmap = strtoul(*++argv, NULL, 0);
pnan_list->rp[i].frmcnt = strtoul(*++argv, NULL, 0);
if (pnan_list->rp[i].frmcnt >= 255) {
fprintf(stderr, "Invalid frame count \"%s\" \n", *argv);
return BCME_BADARG;
}
pnan_list->rp[i].retrycnt = strtoul(*++argv, NULL, 0);
pnan_list->rp[i].flags = strtoul(*++argv, NULL, 0);
}
fprintf(stderr, "count %d\n", pnan_list->count);
buf_len = str_len + 1 + sizeof(wl_nan_ranging_list_t) +
sizeof(wl_nan_ranging_peer_t) * (pnan_list->count - 1);
rc = wlu_set(wl, WLC_SET_VAR, buf, buf_len);
return (rc);
}
static int wl_nan_ranging_results_host(void *wl, cmd_t *cmd, char **argv)
{
wl_nan_ranging_event_data_t *pnan_event;
int rc, i;
void *ptr = NULL;
UNUSED_PARAMETER(argv);
if ((rc = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return rc;
pnan_event = (wl_nan_ranging_event_data_t *) ptr;
printf("num results: %d \n", pnan_event->count);
printf("num good results: %d \n", pnan_event->success_count);
printf("------------------------------------------------------------------------------\n");
printf("Address\t\t\tchanspec\tvalidcnt\tts\t\tdist\tstatus\n");
for (i = 0; i < pnan_event->count; i++) {
printf("%02x:%02x:%02x:%02x:%02x:%02x\t",
pnan_event->rr[i].ea.octet[0],
pnan_event->rr[i].ea.octet[1],
pnan_event->rr[i].ea.octet[2],
pnan_event->rr[i].ea.octet[3],
pnan_event->rr[i].ea.octet[4],
pnan_event->rr[i].ea.octet[5]);
printf("%04x\t\t%d\t\t%u\t",
pnan_event->rr[i].chanspec,
pnan_event->rr[i].sounding_count,
pnan_event->rr[i].timestamp);
if (pnan_event->rr[i].distance == 0xffffffff)
printf("-1\t");
else
printf("%d.%04d\t", pnan_event->rr[i].distance >> 4,
(pnan_event->rr[i].distance & 0x0f) * 625);
printf("%s\n", wl_proxd_nan_status_str(pnan_event->rr[i].status));
}
return (rc);
}
#endif /* WL_NAN */
/*
* proxd ftm sub-commands info and handlers
*/
typedef struct ftm_subcmd_info ftm_subcmd_info_t;
typedef int (ftm_cmd_handler_t)(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv);
/* bitmask indicating which command groups; */
typedef enum {
FTM_SUBCMD_FLAG_METHOD = 0x01, /* FTM method command */
FTM_SUBCMD_FLAG_SESSION = 0x02, /* FTM session command */
FTM_SUBCMD_FLAG_ALL = FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION
} ftm_subcmd_flag_t;
struct ftm_subcmd_info {
char *name; /* cmd-name string as cmdline input */
wl_proxd_cmd_t cmdid; /* cmd-id */
ftm_cmd_handler_t *handler; /* cmd-handler */
ftm_subcmd_flag_t cmdflag;
char *helpmsg; /* messages for 'wl proxd -h ftm' */
};
#define FTM_IOC_BUFSZ 2048 /* ioc buffsize for our module (> BCM_XTLV_HDR_SIZE) */
/* declare proxd ftm-method command handlers ftm_subcmd_xxxx() */
/* e.g. static ftm_cmd_handler_t ftm_subcmd_get_version; */
#define FTM_SUBCMD_FUNC(suffix) ftm_subcmd_ ##suffix
#define DECL_CMDHANDLER(X) static ftm_cmd_handler_t ftm_subcmd_##X
/* get */
DECL_CMDHANDLER(get_version); /* method-only */
DECL_CMDHANDLER(get_result); /* session-only */
DECL_CMDHANDLER(get_info); /* method + session */
DECL_CMDHANDLER(get_status); /* method + session */
DECL_CMDHANDLER(get_sessions); /* method-only */
DECL_CMDHANDLER(get_counters); /* method + session */
DECL_CMDHANDLER(dump); /* method + session */
DECL_CMDHANDLER(get_ranging_info); /* method-only */
/* set */
DECL_CMDHANDLER(enable); /* method-only */
DECL_CMDHANDLER(disable); /* method-only */
DECL_CMDHANDLER(config); /* method + session */
DECL_CMDHANDLER(start_session); /* session-only */
DECL_CMDHANDLER(burst_request); /* session-only */
DECL_CMDHANDLER(stop_session); /* session-only */
DECL_CMDHANDLER(delete_session); /* session-only */
DECL_CMDHANDLER(clear_counters); /* method + session */
DECL_CMDHANDLER(start_ranging); /* method-only */
DECL_CMDHANDLER(stop_ranging); /* method-only */
DECL_CMDHANDLER(tune); /* method-only both set/get */
static const ftm_subcmd_info_t ftm_cmdlist[] = {
/* (get) wl proxd ftm ver */
{"ver", WL_PROXD_CMD_GET_VERSION, FTM_SUBCMD_FUNC(get_version),
FTM_SUBCMD_FLAG_METHOD, "Get the proxd FTM method API version information" },
/* (get) wl proxd ftm <session-id> result */
{"result", WL_PROXD_CMD_GET_RESULT, FTM_SUBCMD_FUNC(get_result),
FTM_SUBCMD_FLAG_SESSION, "Get a session result" },
/* (get) wl proxd ftm [<session-id>] info */
{"info", WL_PROXD_CMD_GET_INFO, FTM_SUBCMD_FUNC(get_info),
FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION,
"Get the detail information for the FTM method or a specific session" },
/* (get) wl proxd ftm [<session-id>] status */
{"status", WL_PROXD_CMD_GET_STATUS, FTM_SUBCMD_FUNC(get_status),
FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION,
"Get the status for the FTM method or a specific session" },
/* (get) wl proxd ftm sessions */
{"sessions", WL_PROXD_CMD_GET_SESSIONS, FTM_SUBCMD_FUNC(get_sessions),
FTM_SUBCMD_FLAG_METHOD, "List all sessions" },
/* (get) wl proxd ftm [<session-id>] counters */
{"counters", WL_PROXD_CMD_GET_COUNTERS, FTM_SUBCMD_FUNC(get_counters),
FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION,
"Get the counters for the FTM method or a specific session" },
/* (get) wl proxd ftm ranging-info */
{ "ranging-info", WL_PROXD_CMD_GET_RANGING_INFO, FTM_SUBCMD_FUNC(get_ranging_info),
FTM_SUBCMD_FLAG_METHOD,
"Get the ranging information for the FTM method" },
/* (get) wl proxd ftm [<session-id>] dump */
{"dump", WL_PROXD_CMD_DUMP, FTM_SUBCMD_FUNC(dump),
FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION,
"Dump all information for the FTM method or a specific session" },
/* (set) wl proxd ftm enable */
{"enable", WL_PROXD_CMD_ENABLE, FTM_SUBCMD_FUNC(enable),
FTM_SUBCMD_FLAG_METHOD, "Enable proximity detection using FTM method" },
/* (set) wl proxd ftm disable */
{"disable", WL_PROXD_CMD_DISABLE, FTM_SUBCMD_FUNC(disable),
FTM_SUBCMD_FLAG_METHOD, "Disable proximity detection using FTM method" },
/* wl proxd ftm [<session-id>] config .... */
{"config", WL_PROXD_CMD_CONFIG, FTM_SUBCMD_FUNC(config),
FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION,
"Set the configuration for the FTM method or a specific session" },
/* (set) wl proxd ftm <session-id> start */
{"start", WL_PROXD_CMD_START_SESSION, FTM_SUBCMD_FUNC(start_session),
FTM_SUBCMD_FLAG_SESSION, "Start scheduling the burst(s) for a session" },
/* (set) wl proxd ftm <session-id> burst-request */
{"burst-request", WL_PROXD_CMD_BURST_REQUEST, FTM_SUBCMD_FUNC(burst_request),
FTM_SUBCMD_FLAG_SESSION,
"On initiator, send burst requests and process FTM frames. On target, send FTM frames" },
/* (set) wl proxd ftm <session-id> stop */
{"stop", WL_PROXD_CMD_STOP_SESSION, FTM_SUBCMD_FUNC(stop_session),
FTM_SUBCMD_FLAG_SESSION, "Stop a session" },
/* (set) wl proxd ftm <session-id> delete */
{"delete", WL_PROXD_CMD_DELETE_SESSION, FTM_SUBCMD_FUNC(delete_session),
FTM_SUBCMD_FLAG_SESSION, "Delete a session" },
/* (set) wl proxd ftm [<session-id>] clear_counters */
{"clear-counters", WL_PROXD_CMD_CLEAR_COUNTERS, FTM_SUBCMD_FUNC(clear_counters),
FTM_SUBCMD_FLAG_METHOD | FTM_SUBCMD_FLAG_SESSION,
"Clear the counters for the FTM method or a specific session" },
{"start-ranging", WL_PROXD_CMD_START_RANGING, FTM_SUBCMD_FUNC(start_ranging),
FTM_SUBCMD_FLAG_METHOD, "Start ranging for sessions" },
{ "stop-ranging", WL_PROXD_CMD_STOP_RANGING, FTM_SUBCMD_FUNC(stop_ranging),
FTM_SUBCMD_FLAG_METHOD, "Stop ranging" },
{ "tune", WL_PROXD_CMD_TUNE, FTM_SUBCMD_FUNC(tune),
FTM_SUBCMD_FLAG_METHOD, "proxd_tune" },
};
/*
* get subcmd info for a specific FTM 'cmdname'
* return NULL if specified <cmdname> is not supported.
*/
const ftm_subcmd_info_t *
ftm_get_subcmd_info(char *cmdname)
{
int i;
const ftm_subcmd_info_t *p_subcmd_info;
/* search for the specified cmdname from the pre-defined supported cmd list */
p_subcmd_info = &ftm_cmdlist[0];
for (i = 0; i < (int) ARRAYSIZE(ftm_cmdlist); i++) {
if (stricmp(p_subcmd_info->name, cmdname) == 0)
return p_subcmd_info;
p_subcmd_info++;
}
return NULL; /* cmd not supported */
}
/*
* 'wl proxd ascii-method' handler
* handle 'wl proxd ftm [<session-id>] <cmdname> [<param-name><param-value>]*'
*/
static int
wl_proxd_cmd_method_handler(void *wl, cmd_t *cmd, char **argv)
{
wl_proxd_method_t method;
wl_proxd_session_id_t session_id;
const ftm_subcmd_info_t *p_subcmd_info;
/* ignore the command name 'proxd' */
UNUSED_PARAMETER(cmd);
/* check for "wl proxd -h ftm" or "wl proxd help ftm" --> display the usage */
if (!stricmp(*argv, "-h") || !stricmp(*argv, "help")) {
return ftm_handle_help(++argv); /* return BCME_USAGE_ERROR; */
}
/* parse proxd-method, currently only support 'ftm' */
if (stricmp(*argv, "ftm") != 0) { /* un-supported proxd-method */
printf("error: proxd-method '%s' is not supported\n", *argv);
return BCME_USAGE_ERROR;
}
method = WL_PROXD_METHOD_FTM;
/* skip 'ftm-method' and parse session-id if specified */
session_id = WL_PROXD_SESSION_ID_GLOBAL;
if (*++argv) {
if (isdigit((int)*argv[0])) {
session_id = (wl_proxd_session_id_t) atoi(argv[0]);
argv++; /* skip to 'cmdname' */
}
}
/* parse 'cmd' in "wl proxd ftm [<session-id>] <cmd> [<param-name><param-value>]*" */
if (!*argv) {
printf("error: proxd ftm-method cmdname is not specified\n");
return BCME_USAGE_ERROR;
}
/* search for the specified <cmdname> from the pre-defined supported cmd list */
p_subcmd_info = ftm_get_subcmd_info(*argv);
if (p_subcmd_info == NULL) {
/* cannot find the cmd in the ftm_cmdlist */
printf("error: invalid proxd ftm command '%s'\n", *argv);
return BCME_USAGE_ERROR;
}
/* dispacth cmd to appropriate handler */
if (p_subcmd_info->handler) {
/* For 'method-only' commands, 'session-id' can be omitted */
if (((p_subcmd_info->cmdflag & FTM_SUBCMD_FLAG_METHOD) != 0) &&
((p_subcmd_info->cmdflag & FTM_SUBCMD_FLAG_SESSION) == 0)) {
if (session_id != WL_PROXD_SESSION_ID_GLOBAL) {
printf("proxd ftm %s: error, does not accept non-zero session-id\n",
p_subcmd_info->name);
return BCME_USAGE_ERROR;
}
}
/* For 'session-only' commands, 'session-id' must be provided */
if (((p_subcmd_info->cmdflag & FTM_SUBCMD_FLAG_METHOD) == 0) &&
((p_subcmd_info->cmdflag & FTM_SUBCMD_FLAG_SESSION) != 0)) {
if (session_id == WL_PROXD_SESSION_ID_GLOBAL) {
printf("proxd ftm %s: error, please specify a valid session-id\n",
p_subcmd_info->name);
return BCME_USAGE_ERROR;
}
}
return p_subcmd_info->handler(wl, p_subcmd_info, method, session_id, ++argv);
}
return BCME_OK;
}
/*
* definition for id-string mapping.
* This is used to map an id (can be cmd-id, tlv-id, ....) to a text-string
* for debug-display or cmd-log-display
*/
typedef struct ftm_strmap_entry {
int32 id;
char *text;
} ftm_strmap_entry_t;
/*
* lookup 'id' (as a key) from a table
* if found, return the entry pointer, otherwise return NULL
*/
static const ftm_strmap_entry_t*
ftm_get_strmap_info(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
{
int i;
const ftm_strmap_entry_t *p_entry;
/* scan thru the table till end */
p_entry = p_table;
for (i = 0; i < (int) num_entries; i++)
{
if (p_entry->id == id)
return p_entry;
p_entry++; /* next entry */
}
return NULL; /* not found */
}
/*
* lookup 'str' (as a key) from a table
* if found, return the entry pointer, otherwise return NULL
*/
static const ftm_strmap_entry_t*
ftm_get_strmap_info_strkey(char *strkey, const ftm_strmap_entry_t *p_table, uint32 num_entries)
{
int i;
const ftm_strmap_entry_t *p_entry;
/* scan thru the table till end */
p_entry = p_table;
for (i = 0; i < (int) num_entries; i++)
{
if (stricmp(p_entry->text, strkey) == 0)
return p_entry;
p_entry++; /* next entry */
}
return NULL; /* not found */
}
/*
* map enum to a text-string for display, this function is called by the following:
* For debug/trace:
* ftm_[cmdid|tlvid]_to_str()
* For TLV-output log for 'get' commands
* ftm_[method|tmu|caps|status|state]_value_to_logstr()
* Input:
* pTable -- point to a 'enum to string' table.
*/
static const char *
ftm_map_id_to_str(int32 id, const ftm_strmap_entry_t *p_table, uint32 num_entries)
{
const ftm_strmap_entry_t*p_entry = ftm_get_strmap_info(id, p_table, num_entries);
if (p_entry)
return (p_entry->text);
return "invalid";
}
/* define entry, e.g. { WL_PROXD_CMD_xxx, "WL_PROXD_CMD_xxx" } */
#define DEF_STRMAP_ENTRY(id) { (id), #id }
#ifdef WL_FTM_DEBUG
/* ftm cmd-id mapping */
static const ftm_strmap_entry_t ftm_cmdid_map[] = {
/* {wl_proxd_cmd_t(WL_PROXD_CMD_xxx), "WL_PROXD_CMD_xxx" }, */
DEF_STRMAP_ENTRY(WL_PROXD_CMD_NONE),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_VERSION),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_ENABLE),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_DISABLE),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_CONFIG),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_SESSION),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_BURST_REQUEST),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_SESSION),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_DELETE_SESSION),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RESULT),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_INFO),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_STATUS),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_SESSIONS),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_COUNTERS),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_CLEAR_COUNTERS),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_DUMP),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_START_RANGING),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_STOP_RANGING),
DEF_STRMAP_ENTRY(WL_PROXD_CMD_GET_RANGING_INFO),
};
/*
* map a ftm cmd-id to a text-string for display
*/
static const char *
ftm_cmdid_to_str(uint16 cmdid)
{
return ftm_map_id_to_str((int32) cmdid, &ftm_cmdid_map[0], ARRAYSIZE(ftm_cmdid_map));
}
#endif /* WL_FTM_DEBUG */
/* ftm tlv-id mapping */
static const ftm_strmap_entry_t ftm_tlvid_loginfo[] = {
/* { WL_PROXD_TLV_ID_xxx, "text for WL_PROXD_TLV_ID_xxx" }, */
{ WL_PROXD_TLV_ID_NONE, "none" },
{ WL_PROXD_TLV_ID_METHOD, "method" },
{ WL_PROXD_TLV_ID_FLAGS, "flags" },
{ WL_PROXD_TLV_ID_CHANSPEC, "chanspec" },
{ WL_PROXD_TLV_ID_TX_POWER, "tx power" },
{ WL_PROXD_TLV_ID_RATESPEC, "ratespec" },
{ WL_PROXD_TLV_ID_BURST_DURATION, "burst duration" },
{ WL_PROXD_TLV_ID_BURST_PERIOD, "burst period" },
{ WL_PROXD_TLV_ID_BURST_FTM_SEP, "burst ftm sep" },
{ WL_PROXD_TLV_ID_BURST_NUM_FTM, "burst num ftm" },
{ WL_PROXD_TLV_ID_NUM_BURST, "num burst" },
{ WL_PROXD_TLV_ID_FTM_RETRIES, "ftm retries" },
{ WL_PROXD_TLV_ID_BSS_INDEX, "BSS index" },
{ WL_PROXD_TLV_ID_BSSID, "bssid" },
{ WL_PROXD_TLV_ID_INIT_DELAY, "burst init delay" },
{ WL_PROXD_TLV_ID_BURST_TIMEOUT, "burst timeout" },
{ WL_PROXD_TLV_ID_EVENT_MASK, "event mask" },
{ WL_PROXD_TLV_ID_FLAGS_MASK, "flags mask" },
{ WL_PROXD_TLV_ID_PEER_MAC, "peer addr" },
{ WL_PROXD_TLV_ID_FTM_REQ, "ftm req" },
{ WL_PROXD_TLV_ID_LCI_REQ, "lci req" },
{ WL_PROXD_TLV_ID_LCI, "lci" },
{ WL_PROXD_TLV_ID_CIVIC_REQ, "civic req" },
{ WL_PROXD_TLV_ID_CIVIC, "civic" },
{ WL_PROXD_TLV_ID_AVAIL24, "availability" },
{ WL_PROXD_TLV_ID_SESSION_FLAGS, "session flags" },
{ WL_PROXD_TLV_ID_SESSION_FLAGS_MASK, "session flags mask" },
{ WL_PROXD_TLV_ID_RX_MAX_BURST, "rx max bursts" },
{ WL_PROXD_TLV_ID_RANGING_INFO, "ranging info" },
{ WL_PROXD_TLV_ID_RANGING_FLAGS, "ranging flags" },
{ WL_PROXD_TLV_ID_RANGING_FLAGS_MASK, "ranging flags mask" },
{ WL_PROXD_TLV_ID_NAN_MAP_ID, "nan map id" },
{ WL_PROXD_TLV_ID_DEV_ADDR, "dev addr" },
{ WL_PROXD_TLV_ID_AVAIL, "availability" },
{ WL_PROXD_TLV_ID_FTM_REQ_RETRIES, "ftm request retries" },
{ WL_PROXD_TLV_ID_TPK, "FTM TPK" },
/* output - 512 + x */
{ WL_PROXD_TLV_ID_STATUS, "status" },
{ WL_PROXD_TLV_ID_COUNTERS, "counters" },
{ WL_PROXD_TLV_ID_INFO, "info" },
{ WL_PROXD_TLV_ID_RTT_RESULT, "rtt result" },
{ WL_PROXD_TLV_ID_AOA_RESULT, "aoa result" },
{ WL_PROXD_TLV_ID_SESSION_INFO, "session info" },
{ WL_PROXD_TLV_ID_SESSION_STATUS, "session status" },
{ WL_PROXD_TLV_ID_SESSION_ID_LIST, "session ids" },
/* debug tlvs can be added starting 1024 */
{ WL_PROXD_TLV_ID_DEBUG_MASK, "debug mask" },
{ WL_PROXD_TLV_ID_COLLECT, "collect" },
{ WL_PROXD_TLV_ID_STRBUF, "result" }
};
/*
* map a ftm TLV-id to a text-string for display as a 'heading'
*/
static const char *
ftm_tlvid_to_logstr(uint16 tlvid)
{
return ftm_map_id_to_str((int32) tlvid,
&ftm_tlvid_loginfo[0], ARRAYSIZE(ftm_tlvid_loginfo));
}
/*
* The following string mapping tables are used for TLVs display received from 'get' commands
*/
/*
* proximity detection method --> text string mapping
*/
static const ftm_strmap_entry_t ftm_method_value_loginfo[] = {
/* wl_proxd_method_t, text-string */
{ WL_PROXD_METHOD_RSVD1, "RSSI not supported" },
{ WL_PROXD_METHOD_TOF, "11v+BCM proprietary" },
{ WL_PROXD_METHOD_RSVD2, "11v only" },
{ WL_PROXD_METHOD_FTM, "FTM" }, /* IEEE rev mc/2014 */
{ WL_PROXD_METHOD_NONE, "none" }
};
static const char *
ftm_method_value_to_logstr(wl_proxd_method_t method)
{
return ftm_map_id_to_str((int32)method,
&ftm_method_value_loginfo[0], ARRAYSIZE(ftm_method_value_loginfo));
}
/*
* time interval unit --> text string mapping
*/
static const ftm_strmap_entry_t ftm_tmu_value_loginfo[] = {
/* wl_proxd_tmu_t, text-string */
{ WL_PROXD_TMU_TU, "TU" },
{ WL_PROXD_TMU_SEC, "sec" },
{ WL_PROXD_TMU_MILLI_SEC, "ms" },
{ WL_PROXD_TMU_MICRO_SEC, "us" },
{ WL_PROXD_TMU_NANO_SEC, "ns" },
{ WL_PROXD_TMU_PICO_SEC, "ps" }
};
static const char *
ftm_tmu_value_to_logstr(wl_proxd_tmu_t tmu)
{
return ftm_map_id_to_str((int32)tmu,
&ftm_tmu_value_loginfo[0], ARRAYSIZE(ftm_tmu_value_loginfo));
}
/*
* proxd FTM method capabilities --> text string mapping
*/
static const ftm_strmap_entry_t ftm_caps_value_loginfo[] = {
/* wl_proxd_ftm_capts_t, text-string */
{ WL_PROXD_FTM_CAP_FTM1, "FTM" },
{ WL_PROXD_FTM_CAP_NONE, "none" }
};
static const char *
ftm_caps_value_to_logstr(wl_proxd_ftm_caps_t caps)
{
return ftm_map_id_to_str((int32)caps,
&ftm_caps_value_loginfo[0], ARRAYSIZE(ftm_caps_value_loginfo));
}
/*
* status --> text string mapping
*/
static const ftm_strmap_entry_t ftm_status_value_loginfo[] = {
/* wl_proxd_status_t, text-string */
{ WL_PROXD_E_NOT_BCM, "different vendor" },
{ WL_PROXD_E_FRAME_TYPE, "invalid frame type" },
{ WL_PROXD_E_VERNOSUPPORT, "unsupported version" },
{ WL_PROXD_E_SEC_NOKEY, "no key" },
{ WL_PROXD_E_SEC_POLICY, "security policy violation" },
{ WL_PROXD_E_SCAN_INPROCESS, "scan in process" },
{ WL_PROXD_E_BAD_PARTIAL_TSF, "bad partial TSF" },
{ WL_PROXD_E_SCANFAIL, "scan failed" },
{ WL_PROXD_E_NOTSF, "no TSF" },
{ WL_PROXD_E_POLICY, "policy failed" },
{ WL_PROXD_E_INCOMPLETE, "incomplete" },
{ WL_PROXD_E_OVERRIDDEN, "overridden" },
{ WL_PROXD_E_ASAP_FAILED, "ASAP failed" },
{ WL_PROXD_E_NOTSTARTED, "not started" },
{ WL_PROXD_E_INVALIDMEAS, "invalid measurement" },
{ WL_PROXD_E_INCAPABLE, "incapable" },
{ WL_PROXD_E_MISMATCH, "mismatch"},
{ WL_PROXD_E_DUP_SESSION, "dup session" },
{ WL_PROXD_E_REMOTE_FAIL, "remote fail" },
{ WL_PROXD_E_REMOTE_INCAPABLE, "remote incapable" },
{ WL_PROXD_E_SCHED_FAIL, "sched failure" },
{ WL_PROXD_E_PROTO, "protocol error" },
{ WL_PROXD_E_EXPIRED, "expired" },
{ WL_PROXD_E_TIMEOUT, "timeout" },
{ WL_PROXD_E_NOACK, "no ack" },
{ WL_PROXD_E_DEFERRED, "deferred" },
{ WL_PROXD_E_INVALID_SID, "invalid session id" },
{ WL_PROXD_E_REMOTE_CANCEL, "remote cancel" },
{ WL_PROXD_E_CANCELED, "canceled" },
{ WL_PROXD_E_INVALID_SESSION, "invalid session" },
{ WL_PROXD_E_BAD_STATE, "bad state" },
{ WL_PROXD_E_ERROR, "error" },
{ WL_PROXD_E_OK, "OK" }
};
/*
* convert BCME_xxx error codes into related error strings
* note, bcmerrorstr() defined in bcmutils is for BCMDRIVER only,
* this duplicate copy is for WL access and may need to clean up later
*/
static const char *ftm_bcmerrorstrtable[] = BCMERRSTRINGTABLE;
static const char *
ftm_status_value_to_logstr(wl_proxd_status_t status)
{
static char ftm_msgbuf_status_undef[32];
const ftm_strmap_entry_t *p_loginfo;
int bcmerror;
/* check if within BCME_xxx error range */
bcmerror = (int) status;
if (VALID_BCMERROR(bcmerror))
return ftm_bcmerrorstrtable[-bcmerror];
/* otherwise, look for 'proxd ftm status' range */
p_loginfo = ftm_get_strmap_info((int32) status,
&ftm_status_value_loginfo[0], ARRAYSIZE(ftm_status_value_loginfo));
if (p_loginfo)
return p_loginfo->text;
/* report for 'out of range' FTM-status error code */
memset(ftm_msgbuf_status_undef, 0, sizeof(ftm_msgbuf_status_undef));
snprintf(ftm_msgbuf_status_undef, sizeof(ftm_msgbuf_status_undef),
"Undefined status %d", status);
return &ftm_msgbuf_status_undef[0];
}
/*
* session-state --> text string mapping
*/
static const ftm_strmap_entry_t ftm_session_state_value_loginfo[] = {
/* wl_proxd_session_state_t, text string */
{ WL_PROXD_SESSION_STATE_CREATED, "created" },
{ WL_PROXD_SESSION_STATE_CONFIGURED, "configured" },
{ WL_PROXD_SESSION_STATE_STARTED, "started" },
{ WL_PROXD_SESSION_STATE_DELAY, "delay" },
{ WL_PROXD_SESSION_STATE_USER_WAIT, "user-wait" },
{ WL_PROXD_SESSION_STATE_SCHED_WAIT, "sched-wait" },
{ WL_PROXD_SESSION_STATE_BURST, "burst" },
{ WL_PROXD_SESSION_STATE_STOPPING, "stopping" },
{ WL_PROXD_SESSION_STATE_ENDED, "ended" },
{ WL_PROXD_SESSION_STATE_START_WAIT, "start-wait" },
{ WL_PROXD_SESSION_STATE_DESTROYING, "destroying" },
{ WL_PROXD_SESSION_STATE_NONE, "none" }
};
static const char *
ftm_session_state_value_to_logstr(wl_proxd_session_state_t state)
{
return ftm_map_id_to_str((int32)state, &ftm_session_state_value_loginfo[0],
ARRAYSIZE(ftm_session_state_value_loginfo));
}
/*
* ranging-state --> text string mapping
*/
static const ftm_strmap_entry_t ftm_ranging_state_value_loginfo [] = {
/* wl_proxd_ranging_state_t, text string */
{ WL_PROXD_RANGING_STATE_NONE, "none" },
{ WL_PROXD_RANGING_STATE_NOTSTARTED, "nonstarted" },
{ WL_PROXD_RANGING_STATE_INPROGRESS, "inprogress" },
{ WL_PROXD_RANGING_STATE_DONE, "done" },
};
static const char *
ftm_ranging_state_value_to_logstr(wl_proxd_ranging_state_t state)
{
return ftm_map_id_to_str((int32) state, &ftm_ranging_state_value_loginfo[0],
ARRAYSIZE(ftm_ranging_state_value_loginfo));
}
/*
* ranging-flags --> text string mapping
*/
static const ftm_strmap_entry_t ftm_ranging_flags_value_loginfo [] = {
/* wl_proxd_ranging_flags_t, text string */
{ WL_PROXD_RANGING_FLAG_NONE, "none" }, /* no flags */
{ WL_PROXD_RANGING_FLAG_DEL_SESSIONS_ON_STOP, "del sessions on stop" },
{ WL_PROXD_RANGING_FLAG_ALL, "all" },
};
static const char *
ftm_ranging_flags_value_to_logstr(wl_proxd_ranging_flags_t flags)
{
return ftm_map_id_to_str((int32) flags, &ftm_ranging_flags_value_loginfo[0],
ARRAYSIZE(ftm_ranging_flags_value_loginfo));
}
/*
* availability flag --> text string mapping
*/
static const ftm_strmap_entry_t ftm_avail_flags_value_loginfo [] = {
/* wl_proxd_avail_flags_t, text string */
{ WL_PROXD_AVAIL_NONE, "none" },
{ WL_PROXD_AVAIL_NAN_PUBLISHED, "NAN published" },
{ WL_PROXD_AVAIL_SCHEDULED, "scheduled" } /* scheduled by proxd */
};
static const char *
ftm_avail_flags_value_to_logstr(wl_proxd_avail_flags_t flags)
{
return ftm_map_id_to_str((int32) flags, &ftm_avail_flags_value_loginfo[0],
ARRAYSIZE(ftm_avail_flags_value_loginfo));
}
/*
* availability time-ref --> text string mapping
* (use for logging and cmd-line params parsing)
*/
static const ftm_strmap_entry_t ftm_avail_timeref_value_loginfo [] = {
/* wl_proxd_time_ref_t, text string */
{ WL_PROXD_TREF_NONE, "none" },
{ WL_PROXD_TREF_DEV_TSF, "dev-tsf" },
{ WL_PROXD_TREF_NAN_DW, "nan-dw" },
{ WL_PROXD_TREF_TBTT, "tbtt" }
};
static const char *
ftm_avail_timeref_value_to_logstr(wl_proxd_time_ref_t timeref)
{
return ftm_map_id_to_str((int32) timeref, &ftm_avail_timeref_value_loginfo[0],
ARRAYSIZE(ftm_avail_timeref_value_loginfo));
}
/*
* allocate a buffer for get/set 'proxd ftm' iovar
*
* Input:
* tlvs_bufsize: specify the max-size of all TLVs reserved for this buffer
* The following fields will be used for setting the proxd-method iovar header:
* method, session_id, cmdid
*
* if succeeds, this function allocates a buffer, setup proxd-method iovar header
* then returns the pointer to allocated buffer and the bufsize(in bytes)
* to caller. Note, the 'len' and a dummy-TLV will be set in the iovar header,
* caller may adjust the 'len' based on #of valid TLVs later.
* if failed, return 'NULL' to indicate error (e.g. no memory).
*/
static wl_proxd_iov_t *
ftm_alloc_getset_buf(wl_proxd_method_t method, wl_proxd_session_id_t session_id,
wl_proxd_cmd_t cmdid, uint16 tlvs_bufsize, uint16 *p_out_bufsize)
{
uint16 proxd_iovsize;
wl_proxd_tlv_t *p_tlv;
wl_proxd_iov_t *p_proxd_iov = (wl_proxd_iov_t *) NULL;
*p_out_bufsize = 0; /* init */
/* calculate the whole buffer size, including one reserve-tlv entry in the header */
proxd_iovsize = sizeof(wl_proxd_iov_t) + tlvs_bufsize;
p_proxd_iov = calloc(1, proxd_iovsize);
if (p_proxd_iov == NULL) {
printf("error: failed to allocate %d bytes of memory\n", proxd_iovsize);
return NULL;
}
/* setup proxd-FTM-method iovar header */
p_proxd_iov->version = htol16(WL_PROXD_API_VERSION);
p_proxd_iov->len = htol16(proxd_iovsize); /* caller may adjust it based on #of TLVs */
p_proxd_iov->cmd = htol16(cmdid);
p_proxd_iov->method = htol16(method);
p_proxd_iov->sid = htol16(session_id);
/* initialize the reserved/dummy-TLV in iovar header */
p_tlv = p_proxd_iov->tlvs;
p_tlv->id = htol16(WL_PROXD_TLV_ID_NONE);
p_tlv->len = htol16(0);
*p_out_bufsize = proxd_iovsize; /* for caller's reference */
return p_proxd_iov;
}
/*
* unpack and display rtt_result TLV
*/
#define PDFTM_BURST_STATE_NAMES \
{"INVALID", \
"FTM2/M1/RESPONSE", \
"FTM3/M2/LTFTRIGGER", \
"FTM4/M3/DONE", \
}
#define FTM_FRAME_TYPES \
{"SETUP", "TRIGGER", "TIMESTAMP"}
static void
ftm_unpack_and_display_rtt_result(const uint8 *p_data, uint16 tlvid, uint16 len)
{
int i;
char dispbuf[256];
char *p_dest;
wl_proxd_rtt_result_t *p_data_info;
wl_proxd_result_flags_t flags;
wl_proxd_session_state_t session_state;
int32 avg_dist;
wl_proxd_rtt_sample_t *p_sample;
uint16 num_rtt;
char* pstatestr[] = PDFTM_BURST_STATE_NAMES;
char *ftm_frame_types[] = FTM_FRAME_TYPES;
UNUSED_PARAMETER(len);
p_data_info = (wl_proxd_rtt_result_t *) p_data;
/* unpack and format 'flags' for display */
flags = ltoh16_ua(&p_data_info->flags);
memset(dispbuf, 0, sizeof(dispbuf));
p_dest = &dispbuf[0];
if (flags & WL_PROXD_RESULT_FLAG_NLOS) {
strncpy(p_dest, "NLOS", sizeof(dispbuf) - strlen(dispbuf) - 1);
p_dest = &dispbuf[strlen(dispbuf)];
}
if (flags & WL_PROXD_RESULT_FLAG_LOS)
strncpy(p_dest, " LOS", sizeof(dispbuf) - strlen(dispbuf) - 1);
if (flags & WL_PROXD_RESULT_FLAG_FATAL)
strncpy(p_dest, " Fatal error", sizeof(dispbuf) - strlen(dispbuf) - 1);
printf("> %s:\n>\tsessionId=%d, flags=0x%04x('%s'), peer=%s\n",
ftm_tlvid_to_logstr(tlvid),
ltoh16_ua(&p_data_info->sid),
flags,
dispbuf, /* flags */
wl_ether_etoa(&p_data_info->peer));
/* session state and status */
session_state = ltoh16_ua(&p_data_info->state);
printf(">\tsession state=%d(%s)",
session_state, ftm_session_state_value_to_logstr(session_state));
printf(">\tburst_duration: %d%s\n",
ltoh32_ua(&p_data_info->u.burst_duration.intvl),
ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_info->u.burst_duration.tmu)));
/* show avg_dist (1/256m units), burst_num */
avg_dist = ltoh32_ua(&p_data_info->avg_dist);
if ((uint32)avg_dist == 0xffffffff) { /* report 'failure' case */
printf(">\tavg_dist=-1m, burst_num=%d, valid_measure_cnt=%d num_ftm=%d\n",
ltoh16_ua(&p_data_info->burst_num),
p_data_info->num_valid_rtt, p_data_info->num_ftm); /* in a session */
}
else {
printf(">\tavg_dist=%d.%04dm, burst_num=%d, valid_measure_cnt=%d num_ftm=%d\n",
avg_dist >> 8, /* 1/256m units */
((avg_dist & 0xff) * 625) >> 4,
ltoh16_ua(&p_data_info->burst_num),
p_data_info->num_valid_rtt, p_data_info->num_ftm); /* in a session */
}
/* show 'avg_rtt' sample */
p_sample = &p_data_info->avg_rtt;
printf(">\tavg_rtt sample: rssi=%d snr=%d bitflips=%d rtt=%d%s "
"std_deviation =%d.%d ratespec=0x%08x\n",
(wl_proxd_rssi_t) ltoh16_ua(&p_sample->rssi),
(wl_proxd_snr_t) ltoh16_ua(&p_sample->snr),
(wl_proxd_bitflips_t) ltoh16_ua(&p_sample->bitflips),
ltoh32_ua(&p_sample->rtt.intvl),
ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)),
ltoh16_ua(&p_data_info->sd_rtt)/10, ltoh16_ua(&p_data_info->sd_rtt)%10,
ltoh32_ua(&p_sample->ratespec));
printf(">\tnum_measurements: %d ", p_data_info->num_meas);
printf(" Flags:");
if (p_data_info->flags & WL_PROXD_REQUEST_SENT) {
if (p_data_info->flags & WL_PROXD_REQUEST_ACKED) {
if (p_data_info->num_meas)
printf("(%s)", pstatestr[p_data_info->num_meas]);
else
printf("(FTM1/REQSENT/ACKED)");
} else {
printf("(FTM1/REQSENT/NOACK)");
}
} else {
printf("(NO_REQ_SENT)");
}
printf("(LTFSEQ %sSTARTED)\n", (p_data_info->flags & WL_PROXD_LTFSEQ_STARTED)? "":"not ");
/* display detail if available */
num_rtt = ltoh16_ua(&p_data_info->num_rtt);
if (num_rtt > 0)
{
printf(">\tnum rtt: %d samples\n", num_rtt);
p_sample = &p_data_info->rtt[0];
for (i = 0; i < num_rtt; i++)
{
uint16 snr = 0, bitflips = 0;
int16 rssi = 0;
int32 dist = 0;
/* FTM frames 1,4,7,11 have valid snr, rssi and bitflips */
if ((i%TOF_DEFAULT_FTMCNT_SEQ) == 1) {
rssi = (wl_proxd_rssi_t) ltoh16_ua(&p_sample->rssi);
snr = (wl_proxd_snr_t) ltoh16_ua(&p_sample->snr);
bitflips = (wl_proxd_bitflips_t) ltoh16_ua(&p_sample->bitflips);
dist = ltoh32_ua(&p_sample->distance);
} else {
rssi = -1;
snr = 0;
bitflips = 0;
dist = 0;
}
printf(">\t sample[%d]: id=%d rssi=%d snr=%d bitflips=%d "
"dist=%d rtt=%d%s status %s Type %s coreid=%d\n",
i, p_sample->id, rssi, snr, bitflips, dist,
ltoh32_ua(&p_sample->rtt.intvl),
ftm_tmu_value_to_logstr(ltoh16_ua(&p_sample->rtt.tmu)),
ftm_status_value_to_logstr(ltoh32_ua(&p_sample->status)),
ftm_frame_types[i % TOF_DEFAULT_FTMCNT_SEQ], p_sample->coreid);
p_sample++;
}
}
return;
}
/*
* unpack and display session_info TLV (WL_PROXD_TLV_ID_SESSION_INFO)
*/
static void
ftm_unpack_and_display_session_info(const uint8 *p_data, uint16 tlvid, uint16 len)
{
wl_proxd_ftm_session_info_t *p_data_info;
wl_proxd_session_state_t session_state;
wl_proxd_status_t proxd_status;
UNUSED_PARAMETER(len);
p_data_info = (wl_proxd_ftm_session_info_t *) p_data;
printf("> %s: bssidx=%d, bssid=%s\n",
ftm_tlvid_to_logstr(tlvid),
p_data_info->bss_index,
wl_ether_etoa(&p_data_info->bssid));
session_state = ltoh16_ua(&p_data_info->state);
proxd_status = ltoh32_ua(&p_data_info->status);
printf("\tsessionId=%d, state=%d(%s), status=%d(%s), burst_num=%d, "
"meas_start %u.%u\n",
ltoh16_ua(&p_data_info->sid),
session_state,
ftm_session_state_value_to_logstr(session_state),
proxd_status,
ftm_status_value_to_logstr(proxd_status),
ltoh16_ua(&p_data_info->burst_num),
ltoh32_ua(&p_data_info->meas_start_hi), ltoh32_ua(&p_data_info->meas_start_lo));
return;
}
/*
* unpack and display session status TLV (WL_PROXD_TLV_ID_SESSION_STATUS)
*/
static void
ftm_unpack_and_display_session_status(const uint8 *p_data, uint16 tlvid, uint16 len)
{
wl_proxd_ftm_session_status_t *p_data_info;
wl_proxd_session_state_t session_state;
wl_proxd_status_t proxd_status;
UNUSED_PARAMETER(len);
p_data_info = (wl_proxd_ftm_session_status_t *) p_data;
session_state = ltoh16_ua(&p_data_info->state);
proxd_status = ltoh32_ua(&p_data_info->status);
printf("> %s: sessionId=%d, state=%d(%s), status=%d(%s), burst_num=%d\n",
ftm_tlvid_to_logstr(tlvid),
ltoh16_ua(&p_data_info->sid),
session_state,
ftm_session_state_value_to_logstr(session_state),
proxd_status,
ftm_status_value_to_logstr(proxd_status),
ltoh16_ua(&p_data_info->burst_num));
return;
}
/*
* unpack and display session_id lists TLV (WL_PROXD_TLV_ID_COUNTERS)
*/
static void
ftm_unpack_and_display_counters(const uint8 *p_data, uint16 tlvid, uint16 len)
{
wl_proxd_counters_t *p_data_info;
UNUSED_PARAMETER(len);
p_data_info = (wl_proxd_counters_t *) p_data;
printf("> %s:\n\ttx-frame-count=%d, rx-frame_count=%d\n",
ftm_tlvid_to_logstr(tlvid),
ltoh32_ua(&p_data_info->tx), /* tx frame count */
ltoh32_ua(&p_data_info->rx)); /* rx frame count */
printf("\tnoack=%d, txfail=%d num_meas=%d\n",
ltoh32_ua(&p_data_info->noack), /* tx w/o ack */
ltoh32_ua(&p_data_info->txfail), /* tx failures */
ltoh32_ua(&p_data_info->num_meas)); /* tx failures */
printf("\tburst=%d, sessions=%d, max_sessions=%d\n",
ltoh32_ua(&p_data_info->burst), /* # of burst */
ltoh32_ua(&p_data_info->sessions), /* # of sessions */
ltoh32_ua(&p_data_info->max_sessions)); /* max concurrency */
printf("\tsched_fail=%d, timeouts=%d, protocol errors=%d\n",
ltoh32_ua(&p_data_info->sched_fail), /* scheduling failures */
ltoh32_ua(&p_data_info->timeouts), /* timeouts */
ltoh32_ua(&p_data_info->protoerr)); /* protocol err */
printf("\tLCI: tx request=%d, rx request=%d, tx report=%d, rx report=%d\n",
ltoh32_ua(&p_data_info->lci_req_tx), /* tx LCI requests */
ltoh32_ua(&p_data_info->lci_req_rx), /* rx LCI requests */
ltoh32_ua(&p_data_info->lci_rep_tx), /* tx LCI reports */
ltoh32_ua(&p_data_info->lci_rep_rx)); /* rx LCI reports */
printf("\tcivic: tx request=%d, rx request=%d, tx report=%d, "
"rx report=%d\n",
ltoh32_ua(&p_data_info->civic_req_tx), /* tx civic requests */
ltoh32_ua(&p_data_info->civic_req_rx), /* rx civic requests */
ltoh32_ua(&p_data_info->civic_rep_tx), /* tx civic reports */
ltoh32_ua(&p_data_info->civic_rep_rx)); /* rx civic reports */
printf("\tranging: created=%d, done=%d\n",
ltoh32_ua(&p_data_info->rctx), ltoh32_ua(&p_data_info->rctx_done));
printf("\tpublish errors=%d\n", ltoh32_ua(&p_data_info->publish_err));
printf("\tsched on_chan=%d off_chan=%d\n", ltoh32_ua(&p_data_info->on_chan),
ltoh32_ua(&p_data_info->off_chan));
printf("\ttsf %u.%u\n",
ltoh32_ua(&p_data_info->tsf_hi),
ltoh32_ua(&p_data_info->tsf_lo));
return;
}
/*
* unpack and display session_id lists TLV (WL_PROXD_TLV_ID_SESSION_ID_LIST)
*/
static void
ftm_unpack_and_display_session_idlist(const uint8 *p_data, uint16 tlvid, uint16 len)
{
wl_proxd_session_id_list_t *p_data_info;
int i;
uint16 num_ids;
UNUSED_PARAMETER(len);
p_data_info = (wl_proxd_session_id_list_t *) p_data;
num_ids = ltoh16_ua(&p_data_info->num_ids);
printf("> %s: total %d id(s)\n", ftm_tlvid_to_logstr(tlvid), num_ids);
for (i = 0; i < num_ids; i++) {
printf(">\tsession[%d]: %d\n", i, ltoh16_ua(&p_data_info->ids[i]));
}
return;
}
/*
* unpack and display availability TLV
*/
static void
ftm_unpack_and_display_avail(const uint8 *p_data, uint16 tlvid, uint16 len)
{
wl_proxd_avail_t *avail;
wl_proxd_avail24_t *avail24;
wl_proxd_avail_t l_avail;
wl_proxd_avail_flags_t flags;
wl_proxd_time_ref_t time_ref;
uint16 num_slots;
wl_proxd_time_slot_t *avail_slot;
chanspec_t chanspec;
int i;
char dispbuf[256];
UNUSED_PARAMETER(len);
/* unpack and format the avail-header for display */
if (tlvid == WL_PROXD_TLV_ID_AVAIL24) {
avail24 = (wl_proxd_avail24_t *) p_data;
/* convert the old version */
l_avail.flags = avail24->flags;
l_avail.time_ref = avail24->time_ref;
l_avail.num_slots = avail24->num_slots;
l_avail.max_slots = avail24->max_slots;
l_avail.repeat = avail24->repeat;
avail = &l_avail;
avail_slot = &avail24->ts0[0];
}
else { /* WL_PROXD_TLV_ID_AVAIL */
avail = (wl_proxd_avail_t *) p_data;
avail_slot = WL_PROXD_AVAIL_TIMESLOTS(avail);
}
flags = ltoh16_ua(&avail->flags);
time_ref = ltoh16_ua(&avail->time_ref);
num_slots = ltoh16_ua(&avail->num_slots);
printf("> %s:\n>\tflags=0x%04x('%s'), time-ref=0x%04x('%s'), "
"period=%d%s, num_slots=%d, max_slots=%d\n",
ftm_tlvid_to_logstr(tlvid),
flags, ftm_avail_flags_value_to_logstr(flags),
time_ref, ftm_avail_timeref_value_to_logstr(time_ref),
ltoh32_ua(&avail->repeat.intvl),
ftm_tmu_value_to_logstr(ltoh16_ua(&avail->repeat.tmu)),
num_slots, ltoh16_ua(&avail->max_slots));
/* unpack and format the time slots for display */
for (i = 0; i < num_slots; i++) {
printf(">\tslots[%d]: start: %d%s duration: %d%s",
i, ltoh32_ua(&avail_slot->start.intvl),
ftm_tmu_value_to_logstr(ltoh16_ua(&avail_slot->start.tmu)),
ltoh32_ua(&avail_slot->duration.intvl),
ftm_tmu_value_to_logstr(ltoh16_ua(&avail_slot->duration.tmu)));
chanspec = (chanspec_t) ltoh32_ua(&avail_slot->chanspec);
if (wf_chspec_valid(chanspec)) {
memset(dispbuf, 0, sizeof(dispbuf));
wf_chspec_ntoa(chanspec, dispbuf);
printf(" chanspec: 0x%04x(%s)\n", chanspec, dispbuf);
}
else {
printf(" chanspec: 0x%04x(invalid)\n", chanspec);
}
avail_slot++;
}
}
/*
* unpack and display session_info TLV (WL_PROXD_TLV_ID_RANGING_INFO)
*/
static void
ftm_unpack_and_display_ranging_info(const uint8 *p_data, uint16 tlvid, uint16 len)
{
wl_proxd_ranging_info_t *p_data_info;
wl_proxd_status_t proxd_status;
wl_proxd_ranging_state_t state;
wl_proxd_ranging_flags_t flags;
UNUSED_PARAMETER(len);
p_data_info = (wl_proxd_ranging_info_t *) p_data;
printf("> %s:\n", ftm_tlvid_to_logstr(tlvid));
proxd_status = ltoh32_ua(&p_data_info->status);
state = ltoh16_ua(&p_data_info->state);
flags = ltoh16_ua(&p_data_info->flags);
printf("\tstatus=%d(%s), state=%d(%s), flags=0x%04x(%s), num_sids=%d, num_done=%d\n",
proxd_status,
ftm_status_value_to_logstr(proxd_status),
state,
ftm_ranging_state_value_to_logstr(state),
flags,
ftm_ranging_flags_value_to_logstr(flags),
ltoh16_ua(&p_data_info->num_sids),
ltoh16_ua(&p_data_info->num_done));
return;
}
/*
* a callback function, displays (wl_proxd_tlv_t) bcm_xtlv variables rcvd in
* get ioctl's xtlv buffer.
* -- This function processes GET result for all 'proxd ftm' commands, provided
* that XTLV types (AKA the explicit xtlv types) packed into the ioctl buff
* are unique across all 'proxd ftm' ioctl commands
* -- This function is also used to display rx FTM events (content in XTLVs),
* see ftm_event_check().
*/
static int
ftm_unpack_xtlv_cbfn(void *ctx, const uint8 *p_data, uint16 tlvid, uint16 len)
{
int res = BCME_OK;
char dispbuf[256];
wl_proxd_event_type_t event_data;
char event_dispbuf[256];
chanspec_t chanspec;
wl_proxd_intvl_t *p_data_intvl;
wl_proxd_ftm_info_t *p_data_info;
wl_proxd_tpk_t *tpk_info;
#ifdef WL_FTM_DEBUG
/* for 'get' commands :
wl_proxd_iov_t *p_proxd_iov = (wl_proxd_iov_t *) ctx;
for event_check:
ctx is NULL
*/
printf("enter %s(ctx=%p, data=%p, tlvid=%d, len=%d)...\n",
__FUNCTION__, ctx, p_data, tlvid, len);
#else
UNUSED_PARAMETER(ctx);
#endif /* WL_FTM_DEBUG */
memset(dispbuf, 0, sizeof(dispbuf)); /* clear the buffer for display */
switch (tlvid) {
/* data=uint8 as a number, display in decimal */
case WL_PROXD_TLV_ID_BSS_INDEX:
case WL_PROXD_TLV_ID_FTM_RETRIES: /* at FTM level */
case WL_PROXD_TLV_ID_FTM_REQ_RETRIES:
printf("> %s: %d\n", ftm_tlvid_to_logstr(tlvid), *p_data);
break;
/* data=uint16 as a number, display in decimal */
case WL_PROXD_TLV_ID_BURST_NUM_FTM: /* per burst */
case WL_PROXD_TLV_ID_NUM_BURST:
case WL_PROXD_TLV_ID_RX_MAX_BURST:
printf("> %s: %d\n", ftm_tlvid_to_logstr(tlvid), ltoh16_ua(p_data));
break;
/* data=uin32 as a number, display in decimal */
case WL_PROXD_TLV_ID_NONE:
case WL_PROXD_TLV_ID_TX_POWER:
case WL_PROXD_TLV_ID_STATUS: /* not used ? SJL_FIXME */
case WL_PROXD_TLV_ID_NAN_MAP_ID:
printf("> %s: %d\n", ftm_tlvid_to_logstr(tlvid), ltoh32_ua(p_data));
break;
/* data=uint32 as a number, display in hex */
case WL_PROXD_TLV_ID_RATESPEC:
case WL_PROXD_TLV_ID_DEBUG_MASK:
case WL_PROXD_TLV_ID_RANGING_FLAGS:
case WL_PROXD_TLV_ID_RANGING_FLAGS_MASK:
printf("> %s: 0x%x\n", ftm_tlvid_to_logstr(tlvid), ltoh32_ua(p_data));
break;
/* data=uint32 as a number, display in hex */
case WL_PROXD_TLV_ID_FLAGS:
case WL_PROXD_TLV_ID_FLAGS_MASK:
ftm_unpack_and_display_config_flags(p_data, tlvid);
break;
/* data=uint32 as a number, display in hex */
case WL_PROXD_TLV_ID_SESSION_FLAGS:
case WL_PROXD_TLV_ID_SESSION_FLAGS_MASK:
ftm_unpack_and_display_session_flags(p_data, tlvid);
break;
/* data=uint32 as a number, display in hex */
case WL_PROXD_TLV_ID_EVENT_MASK:
{
event_data = ltoh32_ua(p_data);
event_dispbuf[0] = '\0';
#ifdef WL_FTM_DEBUG
/* convert to a readable text-string */
ftm_format_event_mask(event_data, event_dispbuf, sizeof(event_dispbuf));
#endif /* WL_FTM_DEBUG */
printf("> %s: 0x%x %s\n",
ftm_tlvid_to_logstr(tlvid), event_data, event_dispbuf);
}
break;
/* data=uin32 as a number, convert and display a text-string */
case WL_PROXD_TLV_ID_METHOD:
printf("> %s: %s\n", ftm_tlvid_to_logstr(tlvid),
ftm_method_value_to_logstr((wl_proxd_method_t) ltoh32_ua(p_data)));
break;
/* data=uint32 as a chanspec */
case WL_PROXD_TLV_ID_CHANSPEC:
{
chanspec = (chanspec_t) ltoh32_ua(p_data);
if (wf_chspec_valid(chanspec)) {
wf_chspec_ntoa(chanspec, dispbuf);
printf("> %s: %s 0x%04x\n",
ftm_tlvid_to_logstr(tlvid), dispbuf, chanspec);
} else {
printf("> %s: invalid 0x%04x\n",
ftm_tlvid_to_logstr(tlvid), chanspec);
}
}
break;
/* data=intvl */
case WL_PROXD_TLV_ID_BURST_DURATION:
case WL_PROXD_TLV_ID_BURST_PERIOD:
case WL_PROXD_TLV_ID_BURST_FTM_SEP:
case WL_PROXD_TLV_ID_INIT_DELAY:
case WL_PROXD_TLV_ID_BURST_TIMEOUT:
{
p_data_intvl = (wl_proxd_intvl_t *) p_data;
printf("> %s: %d%s\n", ftm_tlvid_to_logstr(tlvid),
ltoh32_ua(&p_data_intvl->intvl),
ftm_tmu_value_to_logstr(ltoh16_ua(&p_data_intvl->tmu)));
}
break;
case WL_PROXD_TLV_ID_BSSID:
case WL_PROXD_TLV_ID_PEER_MAC:
case WL_PROXD_TLV_ID_DEV_ADDR:
printf("> %s: %s\n", ftm_tlvid_to_logstr(tlvid),
wl_ether_etoa((struct ether_addr *)p_data));
break;
case WL_PROXD_TLV_ID_TPK:
{
tpk_info = (wl_proxd_tpk_t *)p_data;
printf("> %s: mac addr%s \n", ftm_tlvid_to_logstr(tlvid),
wl_ether_etoa((struct ether_addr *)&tpk_info->peer));
}
break;
case WL_PROXD_TLV_ID_INFO: /* data=wl_proxd_ftm_info_t */
{
p_data_info = (wl_proxd_ftm_info_t *) p_data;
printf("> %s: capabilities=%s, max sessions=%d, num sessions=%d,"
"rx_max_burst=%d\n",
ftm_tlvid_to_logstr(tlvid),
ftm_caps_value_to_logstr(ltoh16_ua(&p_data_info->caps)),
ltoh16_ua(&p_data_info->max_sessions),
ltoh16_ua(&p_data_info->num_sessions),
ltoh16_ua(&p_data_info->rx_max_burst));
}
break;
case WL_PROXD_TLV_ID_FTM_REQ: /* data=dot11_ftm_req_t, var-len */
printf("> %s: data-len=%d byte(s)\n",
ftm_tlvid_to_logstr(tlvid), len);
if (len > 0)
prhex(NULL, (uint8 *)p_data, len);
break;
case WL_PROXD_TLV_ID_RTT_RESULT: /* data=wl_proxd_rtt_result_t */
ftm_unpack_and_display_rtt_result(p_data, tlvid, len);
break;
case WL_PROXD_TLV_ID_SESSION_INFO: /* data=wl_proxd_ftm_session_info_t */
ftm_unpack_and_display_session_info(p_data, tlvid, len);
break;
case WL_PROXD_TLV_ID_SESSION_STATUS: /* data=wl_proxd_ftm_session_status_t */
ftm_unpack_and_display_session_status(p_data, tlvid, len);
break;
case WL_PROXD_TLV_ID_COUNTERS: /* data=wl_proxd_counters_t */
ftm_unpack_and_display_counters(p_data, tlvid, len);
break;
case WL_PROXD_TLV_ID_SESSION_ID_LIST: /* data=wl_proxd_session_id_list */
ftm_unpack_and_display_session_idlist(p_data, tlvid, len);
break;
case WL_PROXD_TLV_ID_RANGING_INFO: /* data=wl_proxd_ranging_info_t */
ftm_unpack_and_display_ranging_info(p_data, tlvid, len);
break;
case WL_PROXD_TLV_ID_STRBUF:
if (len > 0)
printf("> %s:\n%s\n", ftm_tlvid_to_logstr(tlvid), p_data);
break;
case WL_PROXD_TLV_ID_AVAIL24:
case WL_PROXD_TLV_ID_AVAIL:
ftm_unpack_and_display_avail(p_data, tlvid, len);
break;
case WL_PROXD_TLV_ID_LCI:
case WL_PROXD_TLV_ID_CIVIC:
printf("> %s: data-len=%d byte(s)\n",
ftm_tlvid_to_logstr(tlvid), len);
if (len > 0)
prhex(NULL, (uint8*)p_data, len);
break;
case WL_PROXD_TLV_ID_TUNE:
res = proxd_tune_display((wl_proxd_params_tof_tune_t *)p_data, len);
break;
case WL_PROXD_TLV_ID_AOA_RESULT: /* SJL_FIXME */
case WL_PROXD_TLV_ID_COLLECT: /* SJL_FIXME */
case WL_PROXD_TLV_ID_LCI_REQ: /* SJL_FIXME */
case WL_PROXD_TLV_ID_CIVIC_REQ: /* SJL_FIXME */
/* fall thru (not used/supported now) */
default:
printf("> Unsupported %s: %d\n", ftm_tlvid_to_logstr(tlvid), tlvid);
res = BCME_ERROR;
break;
}
return res;
}
/* pack TLVs for checking if a specific tlv-id is supported or not
* for WL_PROXD_CMD_IS_TLV_SUPPORTED
*/
static int
ftm_pack_tlv_id_support(uint8 *buf, uint16 bufsize, void *ctx, uint16 *all_tlvsize)
{
int err = BCME_OK;
uint16 buf_space_left;
uint16 tlv_id;
wl_proxd_tlv_t *p_tlv = (wl_proxd_tlv_t *) buf;
*all_tlvsize = 0;
if (!ctx)
return BCME_BADARG;
/* TLV buffer starts with a full size, will decrement for each packed TLV */
buf_space_left = bufsize;
tlv_id = htol16(*((uint16 *) ctx));
err = bcm_pack_xtlv_entry((uint8 **) &p_tlv, &buf_space_left,
WL_PROXD_TLV_ID_TLV_ID, sizeof(uint16), (void *) &tlv_id,
BCM_XTLV_OPTION_ALIGN32);
if (err != BCME_OK) {
printf("%s: failed to pack TLVs, err=%d\n",
__FUNCTION__, err);
goto done;
}
/* return the len to include all TLVs(including TLV header) */
*all_tlvsize = (bufsize - buf_space_left);
done:
return err;
}
/*
* check if a tlv-id is supported:
* return BCME_OK if tlv-id is supported
*/
static int
ftm_is_tlv_id_supported(void *wl, uint16 tlv_id)
{
int err;
wl_proxd_iov_t *p_proxd_iov;
uint16 proxd_iovsize;
uint16 all_tlvsize = 0;
wl_proxd_iov_t *p_iovresp = NULL;
/* alloc mem for ioctl header + reserved bufsize for tlvs (initialize to zero) */
p_proxd_iov = ftm_alloc_getset_buf(WL_PROXD_METHOD_FTM,
WL_PROXD_SESSION_ID_GLOBAL, WL_PROXD_CMD_IS_TLV_SUPPORTED,
FTM_IOC_BUFSZ, &proxd_iovsize);
if (p_proxd_iov == NULL) {
err = BCME_NOMEM;
goto done;
}
/* Setup TLVs -- pack a TLV_ID for support checking */
err = ftm_pack_tlv_id_support((uint8 *) &p_proxd_iov->tlvs[0],
proxd_iovsize - WL_PROXD_IOV_HDR_SIZE,
(void *) &tlv_id, &all_tlvsize);
if (err != BCME_OK)
goto done;
/* update the iov header, set len to include all TLVs + header */
proxd_iovsize = all_tlvsize + WL_PROXD_IOV_HDR_SIZE;
p_proxd_iov->len = htol16(proxd_iovsize);
/* submit a 'get' request to see if the tlv-id is supported or not */
err = wlu_var_getbuf(wl, "proxd", p_proxd_iov, proxd_iovsize, (void *) &p_iovresp);
done:
/* clean up */
free(p_proxd_iov);
return err;
}
/*
* send 'proxd' iovar for all ftm get-related commands
*/
static int
ftm_do_get_ioctl(void *wl, wl_proxd_iov_t *p_proxd_iov, uint16 proxd_iovsize,
const ftm_subcmd_info_t *p_subcmd_info)
{
/* for gets we only need to pass ioc header */
wl_proxd_iov_t *p_iovresp = NULL;
int status;
/* send getbuf proxd iovar */
status = wlu_var_getbuf(wl, "proxd", p_proxd_iov, proxd_iovsize, (void *)&p_iovresp);
if (status != BCME_OK) {
#ifdef WL_FTM_DEBUG
printf("%s: failed to send getbuf proxd iovar, status=%d\n",
__FUNCTION__, status);
#endif /* WL_FTM_DEBUG */
return status;
}
if (p_iovresp != NULL) {
int tlvs_len = ltoh16(p_iovresp->len) - WL_PROXD_IOV_HDR_SIZE;
if (tlvs_len < 0)
{
#ifdef WL_FTM_DEBUG
printf("%s: alert, p_iovresp->len(%d) should not be smaller than %d\n",
__FUNCTION__, ltoh16(p_iovresp->len), (int) WL_PROXD_IOV_HDR_SIZE);
#endif /* WL_FTM_DEBUG */
tlvs_len = 0;
}
if (p_subcmd_info->cmdid == WL_PROXD_CMD_GET_VERSION) /* 'wl proxd ftm version' */
printf("> version: 0x%x\n", ltoh16(p_iovresp->version));
#ifdef WL_FTM_DEBUG
printf("%s: p_iovresp->(tlvs=%p,len=%d), tlvs_len=%d, cmdid=%d(%s) ver=0x%x\n",
__FUNCTION__, p_iovresp->tlvs, ltoh16(p_iovresp->len), tlvs_len,
ltoh16(p_iovresp->cmd), ftm_cmdid_to_str(ltoh16(p_iovresp->cmd)),
ltoh16(p_iovresp->version));
#endif /* WL_FTM_DEBUG */
if (tlvs_len > 0)
{
/* unpack TLVs and invokes the cbfn for processing */
status = bcm_unpack_xtlv_buf(p_proxd_iov, (uint8 *)p_iovresp->tlvs,
tlvs_len, BCM_XTLV_OPTION_ALIGN32, ftm_unpack_xtlv_cbfn);
printf("\n");
}
}
return status;
}
/*
* common handler for all get-related proxd method commands (no TLVs params for these commands)
* wl proxd ftm [session-id] <get-subcmd>
* where <get-subcmd> can be "ver", "result", "info", "status",
* "sessions", "counters", "dump"
* Note, this <get-subcmd> does not accept any parameters (i.e. no <param-name><param-value>)
*/
static int
ftm_common_getcmd_handler(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
int status = BCME_OK;
uint16 proxd_iovsize = 0;
#ifdef WL_FTM_DEBUG
printf("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
__FUNCTION__, method, session_id, p_subcmd_info->cmdid,
ftm_cmdid_to_str(p_subcmd_info->cmdid));
#endif /* WL_FTM_DEBUG */
/* alloc mem for ioctl headr + reserved 0 bufsize for tlvs (initialize to zero) */
wl_proxd_iov_t *p_proxd_iov;
p_proxd_iov = ftm_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
0, &proxd_iovsize);
if (p_proxd_iov == NULL)
return BCME_NOMEM;
if (*argv == NULL) { /* get */
status = ftm_do_get_ioctl(wl, p_proxd_iov, proxd_iovsize, p_subcmd_info);
} else {
printf("error: proxd ftm %s cmd doesn't accept any parameters\n",
p_subcmd_info->name);
status = BCME_ERROR;
}
/* clean up */
free(p_proxd_iov);
#ifdef WL_FTM_DEBUG
if (status != BCME_OK)
printf("%s failed: status=%d\n", __FUNCTION__, status);
#endif /* WL_FTM_DEBUG */
return status;
}
/*
* get proxd ftm-method version (API version)
* Usage: wl proxd ftm ver
*
* Note, 'session-id' is ignore
*/
static int
ftm_subcmd_get_version(void *wl, const ftm_subcmd_info_t *p_subcmd_info, wl_proxd_method_t method,
wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'version' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/*
* get session result - may be in progress. Both rtt and aoa results returned (as available).
* Also lci and civic location if available.
* Usage: wl proxd ftm <session-id> result
* Note, caller should have verified the session-id before this call.
*/
static int
ftm_subcmd_get_result(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'session result' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/*
* get the global ftm-method info or session-info if session-id is specified.
* Usage:
* wl proxd ftm [<session-id>] info
*/
static int
ftm_subcmd_get_info(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'method/session info' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/*
* get the ftm-method status or session status if session-id is specified.
* Usage: wl proxd ftm [<session-id>] status
*/
static int
ftm_subcmd_get_status(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'method/session status' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/*
* list all sessions for the ftm method
* Usage: wl proxd ftm sessions
*/
static int
ftm_subcmd_get_sessions(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'sessions' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/*
* get the ftm-method counters or sessions counters if session-id is specified.
* Usage:wl proxd ftm [<session-id>] counters
*/
static int
ftm_subcmd_get_counters(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'method/session counters' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/*
* get the global ftm-method ranging-info
* Usage:
* wl proxd ftm ranging-info
*/
static int
ftm_subcmd_get_ranging_info(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'ranging info' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/*
* dump the ftm-method or a session if session-id is specified.
* Usage: wl proxd ftm [<session-id>] dump
*/
static int
ftm_subcmd_dump(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar and display the 'dump result' */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
}
/* ***************************** set commands ********************* */
/*
* common handler for set-related proxd method commands which require no TLV as input
* wl proxd ftm [session-id] <set-subcmd>
* e.g.
* wl proxd ftm enable -- to enable ftm
* wl proxd ftm disable -- to disable ftm
* wl proxd ftm <session-id> start -- to start a specified session
* wl proxd ftm <session-id> stop -- to cancel a specified session;
* state is maintained till session is delete.
* wl proxd ftm <session-id> delete -- to delete a specified session
* wl proxd ftm [<session-id>] clear-counters -- to clear counters
* wl proxd ftm <session-id> burst-request -- on initiator: to send burst request;
* on target: send FTM frame
* wl proxd ftm <session-id> collect
* wl proxd ftm tune (TBD)
*/
static int
ftm_subcmd_setiov_no_tlv(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
uint16 proxd_iovsize;
wl_proxd_iov_t *p_proxd_iov;
int res;
UNUSED_PARAMETER(wl);
#ifdef WL_FTM_DEBUG
printf("enter %s: method=%d, session_id=%d, cmdid=%d(%s)\n",
__FUNCTION__, method, session_id,
p_subcmd_info->cmdid, ftm_cmdid_to_str(p_subcmd_info->cmdid));
#endif /* WL_FTM_DEBUG */
/* do not accept any parameters */
if (*argv != NULL)
{
printf("error: proxd ftm %s cmd doesn't accept any parameters\n",
p_subcmd_info->name);
return BCME_USAGE_ERROR;
}
/* allocate and initialize a temp buffer for 'set proxd' iovar */
proxd_iovsize = 0;
p_proxd_iov = ftm_alloc_getset_buf(method, session_id, p_subcmd_info->cmdid,
0, &proxd_iovsize); /* no TLV */
if (p_proxd_iov == NULL)
return BCME_NOMEM;
/* no TLV to pack, simply issue a set-proxd iovar */
res = wlu_var_setbuf(wl, "proxd", (void *) p_proxd_iov, proxd_iovsize);
if (res != BCME_OK) {
printf("error: IOVAR failed, status=%d\n", res);
}
/* clean up */
free(p_proxd_iov);
return res;
}
/*
* enable FTM method
* Usage: wl proxd ftm enable
*/
static int
ftm_subcmd_enable(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* disable FTM method
* Usage: wl proxd ftm disable
*/
static int
ftm_subcmd_disable(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* start scheduling the burst(s) for the session. Initiates FTM request or await requests on target.
* Usage: wl proxd ftm <session-id> start
*
* Note, caller should have verified the session-id before this call.
*/
static int
ftm_subcmd_start_session(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* cancel the session. State maintained until session is deleted.
* Usage: wl proxd ftm <session-id> stop
*
* Note, caller should have verify the session-id before this call.
*/
static int ftm_subcmd_stop_session(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* burst request: on initiator send burst request and process FTM frames.
* On target, send FTM frames
* Usage: wl proxd ftm <session-id> burst-request
*
* Note, caller should have verified the session-id before this call.
*/
static int
ftm_subcmd_burst_request(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* delete the session.
* Usage: wl proxd ftm <session-id> delete
*
* Note, caller should have verify the session-id before this call.
*/
static int
ftm_subcmd_delete_session(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* clear counters
* Usage: wl proxd ftm [<session-id>] clear_counters
*
* Note, caller should have verify the session-id before this call.
*/
static int
ftm_subcmd_clear_counters(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* define param-info for global/session 'non-options' configuration for
* 'wl proxd ftm [<session-id>] config [<param-name> <param-value>]*'
*/
typedef struct ftm_config_param_info {
char *name; /* <param-name> string to identify a configurable item */
uint16 tlvid; /* mapping TLV id for the item */
char *name_helpmsg; /* help message for the <param-name> config-item */
ftm_subcmd_flag_t cmdflag; /* supported group(s) */
} ftm_config_param_info_t;
static const ftm_config_param_info_t ftm_config_param_info[] = {
/* "name", tlv_id param-name helpmsg */
{ "bssidx", WL_PROXD_TLV_ID_BSS_INDEX, "BSS index to use for the session",
FTM_SUBCMD_FLAG_ALL },
{ "chanspec", WL_PROXD_TLV_ID_CHANSPEC, "channel spec to use",
FTM_SUBCMD_FLAG_ALL },
{ "tx-power", WL_PROXD_TLV_ID_TX_POWER, "tx power to use in dbm",
FTM_SUBCMD_FLAG_ALL },
{ "ratespec", WL_PROXD_TLV_ID_RATESPEC, "rate to use",
FTM_SUBCMD_FLAG_ALL },
{ "num-ftm", WL_PROXD_TLV_ID_BURST_NUM_FTM, "number of FTM frames in a burst",
FTM_SUBCMD_FLAG_ALL },
{ "num-burst", WL_PROXD_TLV_ID_NUM_BURST, "number of bursts in the session",
FTM_SUBCMD_FLAG_ALL },
{ "retries", WL_PROXD_TLV_ID_FTM_RETRIES, "number of retries; not 802.11 mac level retries",
FTM_SUBCMD_FLAG_ALL },
{ "event-mask", WL_PROXD_TLV_ID_EVENT_MASK, "bitmask of subscribed events",
FTM_SUBCMD_FLAG_ALL },
{ "debug-mask", WL_PROXD_TLV_ID_DEBUG_MASK, "bitmask for logging FTM messages",
FTM_SUBCMD_FLAG_ALL },
{ "burst-duration", WL_PROXD_TLV_ID_BURST_DURATION, "duration for a single burst",
FTM_SUBCMD_FLAG_ALL },
{ "burst-period", WL_PROXD_TLV_ID_BURST_PERIOD, "time between bursts",
FTM_SUBCMD_FLAG_ALL },
{ "ftm-sep", WL_PROXD_TLV_ID_BURST_FTM_SEP, "time between FTM frames in a burst",
FTM_SUBCMD_FLAG_ALL },
{ "burst-timeout", WL_PROXD_TLV_ID_BURST_TIMEOUT, "timeout",
FTM_SUBCMD_FLAG_ALL },
{ "init-delay", WL_PROXD_TLV_ID_INIT_DELAY, "delay after start",
FTM_SUBCMD_FLAG_ALL },
{ "bssid", WL_PROXD_TLV_ID_BSSID, "BSSID used for the session",
FTM_SUBCMD_FLAG_ALL },
{ "peer", WL_PROXD_TLV_ID_PEER_MAC, "peer mac address",
FTM_SUBCMD_FLAG_ALL },
{ "rx-max-burst", WL_PROXD_TLV_ID_RX_MAX_BURST, "limit bursts for rx(method only)",
FTM_SUBCMD_FLAG_METHOD },
/* special 'config options', no matching TLV */
{ "options", WL_PROXD_TLV_ID_NONE,
"type 'wl proxd -h ftm config options' for more information",
FTM_SUBCMD_FLAG_ALL },
{ "session-options", WL_PROXD_TLV_ID_NONE,
"type 'wl proxd -h ftm config options' for more information",
FTM_SUBCMD_FLAG_ALL },
{ "avail", WL_PROXD_TLV_ID_NONE,
"type 'wl proxd -h ftm config avail' for more information",
FTM_SUBCMD_FLAG_ALL },
{ "nan-map-id", WL_PROXD_TLV_ID_NAN_MAP_ID, "NAN map id(method only)",
FTM_SUBCMD_FLAG_METHOD },
{ "dev-addr", WL_PROXD_TLV_ID_DEV_ADDR, "deivce address(method only)",
FTM_SUBCMD_FLAG_METHOD },
{ "req-retries", WL_PROXD_TLV_ID_FTM_REQ_RETRIES, "number of FTM request retries",
FTM_SUBCMD_FLAG_ALL },
{ "tpk", WL_PROXD_TLV_ID_TPK, "tpk to be configured", FTM_SUBCMD_FLAG_ALL }
};
/* map a specified text-string to a proxd time unit
*
* return true if succeeds, otherwise return false.
*/
static bool
ftm_get_tmu_from_str(char *str, wl_proxd_tmu_t *p_tmu)
{
if (stricmp(str, "tu") == 0)
*p_tmu = WL_PROXD_TMU_TU;
else if (stricmp(str, "s") == 0)
*p_tmu = WL_PROXD_TMU_SEC;
else if (stricmp(str, "ms") == 0)
*p_tmu = WL_PROXD_TMU_MILLI_SEC;
else if (stricmp(str, "us") == 0)
*p_tmu = WL_PROXD_TMU_MICRO_SEC;
else if (stricmp(str, "ns") == 0)
*p_tmu = WL_PROXD_TMU_NANO_SEC;
else if (stricmp(str, "ps") == 0)
*p_tmu = WL_PROXD_TMU_PICO_SEC;
else
return FALSE;
return TRUE;
}
/*
* get the config_param_info from the table based on 'param-name' for 'wl proxd ftm config' command
*
* return NULL if 'param-name' is not supported
*/
static const ftm_config_param_info_t *
ftm_get_config_param_info(char *p_param_name, wl_proxd_session_id_t session_id)
{
int i;
ftm_subcmd_flag_t search_cmdflag;
const ftm_config_param_info_t *p_config_param_info;
/* determine if this is for 'session' or 'method' config */
search_cmdflag = (session_id == WL_PROXD_SESSION_ID_GLOBAL) ? FTM_SUBCMD_FLAG_METHOD
: FTM_SUBCMD_FLAG_SESSION;
p_config_param_info = &ftm_config_param_info[0];
for (i = 0; i < (int) ARRAYSIZE(ftm_config_param_info); i++) {
if (stricmp(p_param_name, p_config_param_info->name) == 0) {
/* check if this config-param is supported for method/session */
if ((p_config_param_info->cmdflag & search_cmdflag) == 0) {
/* not supported for this method/session command */
return (ftm_config_param_info_t *) NULL;
}
return p_config_param_info;
}
p_config_param_info++; /* next */
}
return (ftm_config_param_info_t *) NULL; /* 'invalid param name' */
}
/*
* define param-value info for global/session 'options' configuration for
* 'wl proxd ftm [<session-id>] config options {[+|-]<param-value>}*'
*/
typedef struct ftm_config_options_info {
char *param_value_str; /* <param-value> str to identify an options-flag */
uint32 flags; /* wl_proxd_flags_t/wl_proxd_session_flags_t */
char *helpmsg; /* help message for the param-value */
} ftm_config_options_info_t;
/* global/method config options/flags */
static const ftm_config_options_info_t ftm_method_options_info[] = {
/* param-value-string wl_proxd_flags_t helpmsg */
{ "rx-enable", WL_PROXD_FLAG_RX_ENABLED,
"If enabled, process requests; If disabled, requests will be ignored"},
{ "rx-range-req", WL_PROXD_FLAG_RX_RANGE_REQ,
"If enabled, process 11mc range requests; If disabled, requests will be ignored" },
{ "tx-lci", WL_PROXD_FLAG_TX_LCI, "transmit LCI, if available" },
{ "tx-civic", WL_PROXD_FLAG_TX_CIVIC, "tx civic location, if available" },
{ "rx-auto-burst", WL_PROXD_FLAG_RX_AUTO_BURST,
"If enabled, respond to requests without host action.\n"
"\t\t "
"If disabled, the request is forwarded to host via an event.\n"
"\t\t "
"If the event is masked, the request is dropped" },
{ "tx-auto-burst", WL_PROXD_FLAG_TX_AUTO_BURST,
"If enabled, continue an initiated session with a new burst without host action\n"
"\t\t "
"until the number of bursts for the session is completed" },
{ "avail-publish", WL_PROXD_FLAG_AVAIL_PUBLISH, "publish local availability" },
{ "avail-schedule", WL_PROXD_FLAG_AVAIL_SCHEDULE, "schedule local availability" },
{ "asap-capable", WL_PROXD_FLAG_ASAP_CAPABLE, "capable of ASAP scheduling"},
{ "mburst-followup", WL_PROXD_FLAG_MBURST_FOLLOWUP,
"enable multi-burst followup algorithm " },
{ "secure", WL_PROXD_FLAG_SECURE,
"Enable security for ftm - per BSS" },
{ "all", WL_PROXD_FLAG_ALL, "all of the above" }
};
/* config session options/flags */
static const ftm_config_options_info_t ftm_session_options_info[] = {
/* param-value-string wl_proxd_session_flags_t helpmsg */
{ "initiator", WL_PROXD_SESSION_FLAG_INITIATOR, "local device is an initiator" },
{ "target", WL_PROXD_SESSION_FLAG_TARGET, "local device is a target" },
{ "one-way", WL_PROXD_SESSION_FLAG_ONE_WAY, "(initiated) 1-way rtt " },
{ "auto-burst", WL_PROXD_SESSION_FLAG_AUTO_BURST, "created with rx_auto_burst" },
{ "immediate", WL_PROXD_SESSION_FLAG_MBURST_NODELAY, "immediate next burst" },
{ "rtt-detail", WL_PROXD_SESSION_FLAG_RTT_DETAIL, "provide rtt detail in results" },
#ifdef SJL_FIXME
{ "aoa", WL_PROXD_SESSION_FLAG_AOA, "AOA along with RTT" },
#endif /* SJL_FIXME */
{ "rx-auto-burst", WL_PROXD_SESSION_FLAG_RX_AUTO_BURST,
"Same as proxd flags above, applies to the session" },
{ "tx-auto-burst", WL_PROXD_SESSION_FLAG_TX_AUTO_BURST,
"Same as proxd flags above, applies to the session" },
{ "nan-bss", WL_PROXD_SESSION_FLAG_NAN_BSS, "Use NAN BSS, if applicable" },
{ "ts1", WL_PROXD_SESSION_FLAG_TS1, "FTM1 - ASAP-capable" }, /* readonly */
#ifdef SJL_FIXME
{ "rpt-failure", WL_PROXD_SESSION_FLAG_REPORT_FAILURE,
"(target) report not rx distance to host" },
#endif /* SJL_FIXME */
{ "initiator-rpt", WL_PROXD_SESSION_FLAG_INITIATOR_RPT,
"tx initiator-report to target" },
{ "neutral", WL_PROXD_SESSION_FLAG_NETRUAL, "neutral mode" },
{ "seq-en", WL_PROXD_SESSION_FLAG_SEQ_EN, "sequence sampling capture" },
{"no-param-override", WL_PROXD_SESSION_FLAG_NO_PARAM_OVRD,
"disallow override from target"},
{"asap", WL_PROXD_SESSION_FLAG_ASAP, "use ASAP scheduling"},
{ "tx-lci-req", WL_PROXD_SESSION_FLAG_REQ_LCI, "transmit LCI request" },
{ "tx-civic-req", WL_PROXD_SESSION_FLAG_REQ_CIV, "tx civic location request" },
{ "pre-scan", WL_PROXD_SESSION_FLAG_PRE_SCAN, "enable asap pre-scan on initiator" },
{ "auto-vhtack", WL_PROXD_SESSION_FLAG_AUTO_VHTACK, "use vhtack based on brcm ie" },
{ "vhtack", WL_PROXD_SESSION_FLAG_VHTACK, "vht ack is in use - output only" },
{ "burst-duration-nopref", WL_PROXD_SESSION_FLAG_BDUR_NOPREF,
"duration for a single burst - no preference" },
{ "num-ftm-nopref", WL_PROXD_SESSION_FLAG_NUM_FTM_NOPREF,
"number of FTM frames in a burst - no preference" },
{ "ftm-sep-nopref", WL_PROXD_SESSION_FLAG_FTM_SEP_NOPREF,
"time between FTM frames in a burst - no preference " },
{ "num-burst-nopref", WL_PROXD_SESSION_FLAG_NUM_BURST_NOPREF,
"number of bursts in a session - no preference " },
{ "burst-period-nopref", WL_PROXD_SESSION_FLAG_BURST_PERIOD_NOPREF,
"time between bursts - no preference " },
{ "all", WL_PROXD_SESSION_FLAG_ALL, "all of the above" }
};
static void
ftm_cmn_display_config_options_value(uint32 flags,
const ftm_config_options_info_t *p_table, uint32 num_entries)
{
const ftm_config_options_info_t *p_entry;
uint32 i;
if (flags) {
printf(" (");
/* walk thru the table to find a match indicating
the specified param_value_str is valid
*/
p_entry = p_table;
for (i = 0; i < num_entries; i++) {
if ((p_entry->flags & flags) == p_entry->flags) {
printf(" %s", p_entry->param_value_str);
}
p_entry++; /* next */
}
printf(" )");
}
printf("\n");
}
static void
ftm_unpack_and_display_session_flags(const uint8 *p_data, uint16 tlvid)
{
wl_proxd_session_flags_t flags = ltoh32_ua(p_data);
printf("> %s: 0x%x", ftm_tlvid_to_logstr(tlvid), flags);
/* display the value in a readable format */
ftm_cmn_display_config_options_value((uint32) flags,
&ftm_session_options_info[0], ARRAYSIZE(ftm_session_options_info));
}
static void
ftm_unpack_and_display_config_flags(const uint8 *p_data, uint16 tlvid)
{
wl_proxd_flags_t flags = ltoh32_ua(p_data);
printf("> %s: 0x%x", ftm_tlvid_to_logstr(tlvid), flags);
/* display the value in a readable format */
return ftm_cmn_display_config_options_value((uint32) flags,
&ftm_method_options_info[0], ARRAYSIZE(ftm_method_options_info));
}
/* get the method/session config-options info from the table based on 'options param_value_str'
* Input:
* param_value_str -- a 'config options' param-value string to identify a config-options flag
* useMethod -- set to true to look up 'config options' table for FTM method
* set to false to look up 'config options' table for a session
* if succeeds, return a pointer to the config_options_info, otherwise return NULL
*/
static const ftm_config_options_info_t *
ftm_get_config_options_info(char *param_value_str, bool useMethod)
{
int i;
const ftm_config_options_info_t *p_entry;
int num_entries;
if (useMethod) { /* choose an options table for method command */
p_entry = &ftm_method_options_info[0];
num_entries = ARRAYSIZE(ftm_method_options_info);
}
else { /* choose an options table for sessions command */
p_entry = &ftm_session_options_info[0];
num_entries = ARRAYSIZE(ftm_session_options_info);
}
/* walk thru the table to find a match indicating the specified param_value_str is valid */
for (i = 0; i < num_entries; i++) {
if (stricmp(param_value_str, p_entry->param_value_str) == 0)
return p_entry;
p_entry++; /* next */
}
return (ftm_config_options_info_t *) NULL; /* 'invalid param value' */
}
/*
* handle 'wl proxd ftm [<session-id>] config options [+|-]param-value'
* parse cmd-line, setup method/sessions options/flags TLVs in caller
* provided buffer and adjusted-buffer-space if applies.
* Note, input '<session-id>' is used to determine if config-options is
* for 'global/method' or 'sessions'.
*
* This function is invoked from ftm_subcmd_config().
*/
static int
ftm_handle_config_options(wl_proxd_session_id_t session_id, char **argv,
wl_proxd_tlv_t **p_tlv, uint16 *p_buf_space_left, bool useMethod)
{
bool bEnable;
int status;
char *p_param_value;
uint32 flags = WL_PROXD_FLAG_NONE;
uint32 flags_mask = WL_PROXD_FLAG_NONE;
uint32 new_mask; /* cmdline input */
const ftm_config_options_info_t *p_options_info;
UNUSED_PARAMETER(session_id);
/* check if user provides param-values for 'config options' command */
if (*argv == NULL)
return BCME_USAGE_ERROR;
/* <param-value> followed 'wl proxd ftm [session-id] config options' */
while (*argv != NULL) {
bEnable = TRUE; /* default: set the flag if '+/-' is omitted */
if ((*argv)[0] == '-' || (*argv)[0] == '+') {
bEnable = ((*argv)[0] == '-') ? FALSE : TRUE;
p_param_value = *argv + 1; /* skip the prefix */
if (*p_param_value == '\0')
return BCME_USAGE_ERROR;
}
else
p_param_value = *argv;
/* check if 'param-value' (specified in ascii-string) is valid */
p_options_info = ftm_get_config_options_info(p_param_value, useMethod);
if (p_options_info != (ftm_config_options_info_t *) NULL) {
new_mask = p_options_info->flags;
}
else {
/* check if specifed as an intergral number string */
new_mask = strtoul(p_param_value, NULL, 0);
if (new_mask == 0) { /* conversion error, abort */
printf("error: invalid param-value(%s)\n", p_param_value);
return BCME_USAGE_ERROR; /* param-value is invalid */
}
}
/* update flags mask */
flags_mask = flags_mask | new_mask;
if (bEnable)
flags |= new_mask; /* set the bit on */
else
flags &= ~new_mask; /* set the bit off */
argv++; /* continue on next <param-value> */
}
#ifdef WL_FTM_DEBUG
printf("%s: about to set TLVs for %s-options flags_mask=0x%x flags=0x%x\n",
__FUNCTION__, useMethod ? "method" : "sessions", flags_mask, flags);
#endif /* WL_FTM_DEBUG */
flags = htol32(flags);
flags_mask = htol32(flags_mask);
/* setup flags_mask TLV */
status = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
useMethod ? WL_PROXD_TLV_ID_FLAGS_MASK : WL_PROXD_TLV_ID_SESSION_FLAGS_MASK,
sizeof(uint32), (uint8 *)&flags_mask, BCM_XTLV_OPTION_ALIGN32);
if (status != BCME_OK) {
#ifdef WL_FTM_DEBUG
printf("%s: bcm_pack_xltv_entry() for flags_mask failed, status=%d\n",
__FUNCTION__, status);
#endif /* WL_FTM_DEBUG */
return status; /* abort */
}
/* setup flags TLV */
status = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
useMethod ? WL_PROXD_TLV_ID_FLAGS : WL_PROXD_TLV_ID_SESSION_FLAGS,
sizeof(uint32), (uint8 *)&flags, BCM_XTLV_OPTION_ALIGN32);
if (status != BCME_OK) {
#ifdef WL_FTM_DEBUG
printf("%s: bcm_pack_xltv_entry() for flags failed, status=%d\n",
__FUNCTION__, status);
#endif /* WL_FTM_DEBUG */
return status; /* abort */
}
return BCME_OK;
}
/*
num_burst must be power of 2 (max exponent is 14)
*/
static int
ftm_validate_num_burst(uint16 num_burst, const ftm_config_param_info_t *p_config_param_info)
{
#define WL_FTM_MAX_NUM_BURST_EXP 14
uint16 exponent;
uint64 x_num_burst;
int i;
/* get the exponent value */
exponent = 0;
x_num_burst = (uint64) num_burst;
for (i = 0; i < (int) (sizeof(num_burst) * NBBY); ++i, x_num_burst >>= 1) {
if (x_num_burst & 0x1)
exponent = i;
}
if (exponent > WL_FTM_MAX_NUM_BURST_EXP) {
printf("error:' %s' value is out of range (should not exceed 0x%x)\n",
p_config_param_info->name, 1 << WL_FTM_MAX_NUM_BURST_EXP);
return BCME_RANGE;
}
if (num_burst != (1 << exponent)) {
printf("error: '%s' value must be power of 2\n",
p_config_param_info->name);
return BCME_RANGE; /* not power of 2 */
}
return BCME_OK;
}
/*
parse 'param-value' for 'ftm config' command
*/
static int
ftm_config_parse_ul(char **argv, const ftm_config_param_info_t *p_config_param_info,
unsigned long int min_value, unsigned long int max_value,
unsigned long int *out_data)
{
int status = BCME_OK;
unsigned long int tmp_data_ul;
tmp_data_ul = strtoul(*argv, NULL, 0);
if (tmp_data_ul < min_value || tmp_data_ul > max_value) {
printf("error: '%s' value is out of range\n",
p_config_param_info->name);
return BCME_RANGE;
}
/* validate the arguments */
if (p_config_param_info->tlvid == WL_PROXD_TLV_ID_NUM_BURST ||
p_config_param_info->tlvid == WL_PROXD_TLV_ID_RX_MAX_BURST) {
status = ftm_validate_num_burst((uint16) tmp_data_ul, p_config_param_info);
if (status != BCME_OK)
return status;
}
/* param-value parsing: success */
*out_data = tmp_data_ul;
return BCME_OK;
}
/*
* parse and pack 'chanspec' from a command-line argument
* Input:
* arg_channel: a channel-string
* out_channel: buffer to store the result
*/
static int
ftm_config_parse_channel(char *arg_channel, uint32 *out_channel)
{
int status = BCME_OK;
uint32 src_data_channel = 0; /* default: invalid */
/* note, chanespec_t is currently defined as 16-bit, however */
/* wl-interface use 'uint32' to allow future change for 32-bit */
if (arg_channel != (char *) NULL)
src_data_channel = (uint32) wf_chspec_aton(arg_channel);
if (src_data_channel == 0) {
printf("error: invalid chanspec\n");
status = BCME_BADARG;
}
*out_channel = htol32(src_data_channel);
return status;
}
/*
* parse and pack 'intvl' param-value from a command-line argument
* Input:
* arg_intvl: a time-intvl string
* out_intvl: buffer to store the result
*/
static int
ftm_config_parse_intvl(char *arg_intvl, wl_proxd_intvl_t *out_intvl)
{
wl_proxd_intvl_t src_data_intvl;
char *p_end;
/* initialize */
memset(out_intvl, 0, sizeof(*out_intvl));
if (arg_intvl == (char *) NULL) {
printf("error: time-interval value is not specified\n");
return BCME_BADARG;
}
errno = 0;
memset(&src_data_intvl, 0, sizeof(src_data_intvl));
/* time interval e.g. 10ns */
/* get the number */
p_end = NULL;
src_data_intvl.intvl = htol32(strtoul(arg_intvl, &p_end, 10));
if (errno) {
printf("error: invalid time interval (errno=%d)\n", errno);
return BCME_BADARG;
}
/* get time-unit */
src_data_intvl.tmu = WL_PROXD_TMU_TU; /* default */
if (*p_end != '\0') {
if (!ftm_get_tmu_from_str(p_end, &src_data_intvl.tmu)) {
printf("error: invalid time-unit %s\n", p_end);
return BCME_BADARG;
}
}
src_data_intvl.tmu = htol16(src_data_intvl.tmu);
/* return to caller */
memcpy(out_intvl, &src_data_intvl, sizeof(*out_intvl));
return BCME_OK;
}
/*
* parse and pack 'intvl' param-value from a command-line argument
* Input:
* arg_tpk: tpk param-value argument string
* out_tpk: buffer to store tpk and mac address
*/
static int
ftm_config_parse_tpk_peer(char *arg_tpk, wl_proxd_tpk_t *out_tpk)
{
wl_proxd_tpk_t src_data_tpk;
/* initialize */
memset(out_tpk, 0, sizeof(*out_tpk));
if (arg_tpk == (char *) NULL) {
printf("error: tpk value is not specified\n");
return BCME_BADARG;
}
errno = 0;
memset(&src_data_tpk, 0, sizeof(src_data_tpk));
/* get link mac address */
if (!wl_ether_atoe(arg_tpk, &src_data_tpk.peer))
return BCME_USAGE_ERROR;
/* get TPK */
if (*arg_tpk) {
memcpy(&src_data_tpk.peer, arg_tpk, ETHER_ADDR_LEN);
}
/* return to caller */
memcpy(out_tpk, &src_data_tpk, sizeof(*out_tpk));
return BCME_OK;
}
/*
* parse and pack one 'config avail slot' param-value from a command-line
* Input:
* arg_slot: 'slot' param-value argument string
* in "channel:start-tmu:duration-tmu" format
* out_avail_slot: buffer to store the result
*/
static int
ftm_config_avail_parse_slot(char *arg_slot, wl_proxd_time_slot_t *out_avail_slot)
{
int arg_idx;
const char *tmp_start, *tmp_end;
char tmpbuf[128];
int len;
int status = BCME_OK;
if (arg_slot == (char *) NULL) {
printf("error: slot value is not specified\n");
return BCME_BADARG;
}
/* parse channel:start-tmu:duration-tmu */
tmp_start = arg_slot;
for (arg_idx = 0; arg_idx < 3; arg_idx++) {
tmp_end = strchr(tmp_start, ':');
if (tmp_end == NULL) {
if (arg_idx != 2 || *tmp_start == '\0') {
status = BCME_BADARG;
goto done;
}
/* for last 'duration intvl' */
tmp_end = tmp_start + strlen(tmp_start);
}
/* create a temp null-terminated substring */
if ((len = tmp_end - tmp_start) >= (int) sizeof(tmpbuf)) {
status = BCME_BADARG;
goto done;
}
memcpy(tmpbuf, tmp_start, len);
tmpbuf[len] = '\0'; /* null-terminate */
if (arg_idx == 0)
status = ftm_config_parse_channel(tmpbuf, &out_avail_slot->chanspec);
else if (arg_idx == 1)
status = ftm_config_parse_intvl(tmpbuf, &out_avail_slot->start);
else /* arg_idx == 2 */
status = ftm_config_parse_intvl(tmpbuf, &out_avail_slot->duration);
if (status != BCME_OK)
goto done;
/* continue on next element */
tmp_start = tmp_end + 1;
}
/* make sure no string beyond 'channel:start-tmu:duration-tmu' */
if (*tmp_end != '\0') {
printf("error: invalid 'slot' value '%s'\n", tmp_end);
status = BCME_BADARG;
}
done:
if (status == BCME_BADARG)
printf("error: invalid value for slot\n");
return status;
}
/*
* parse and pack 'config avail time-ref' param-value from a command-line
* Input:
* arg_tref: 'time-ref' param-value argument string
* in "none|dev-tsf|nan-dw|tbtt" format
* out_tref: buffer to store the result
*/
static int
ftm_config_avail_parse_tref(char *arg_tref, wl_proxd_time_ref_t *out_tref)
{
wl_proxd_time_ref_t src_data_tref;
const ftm_strmap_entry_t *p_entry;
if (arg_tref == (char *) NULL) {
printf("error: time-ref value is not specified\n");
return BCME_BADARG;
}
/* loop up */
p_entry = ftm_get_strmap_info_strkey(arg_tref, &ftm_avail_timeref_value_loginfo[0],
ARRAYSIZE(ftm_avail_timeref_value_loginfo));
if (p_entry)
src_data_tref = p_entry->id;
else {
printf("error: invalid time-ref value\n");
return BCME_BADARG;
}
*out_tref = htol16((uint16) src_data_tref);
return BCME_OK;
}
#define FTM_AVAIL_MAX_SLOTS 32 /* also in pdftmpvt.h */
/*
* parse and pack a list of 'slot-value' for availability
* Input:
* argv -- point to something like "ch1:start-tmu1:duration-tmu1
* ch2:start-tmu2:duration-tmu2 ..."
*/
static int
ftm_config_avail_parse_all_slots(char **argv, wl_proxd_avail_t *avail, uint16 *out_num_slots)
{
int err = BCME_OK;
uint16 num_slots;
wl_proxd_time_slot_t *avail_slot;
*out_num_slots = 0;
num_slots = 0;
avail_slot = WL_PROXD_AVAIL_TIMESLOTS(avail);
while (*argv != NULL) { /* parse each slot-value */
if (num_slots >= FTM_AVAIL_MAX_SLOTS) {
printf("error: number of slots exceed the limit (%d)\n",
FTM_AVAIL_MAX_SLOTS);
err = BCME_BADARG; /* too many time-slots */
goto done;
}
/* parse channel:start-tmu:duration-tmu */
err = ftm_config_avail_parse_slot(*argv, avail_slot);
if (err != BCME_OK)
goto done;
num_slots++;
avail_slot++;
++argv; /* continue on next slot-value */
}
if (num_slots == 0) {
printf("error: slot-value is not specified\n");
err = BCME_BADARG;
goto done;
}
*out_num_slots = num_slots;
done:
return err;
}
/*
* allocate the availability info
* if succeeds, an avail-buffer with 'max_slots' will be allocated
*/
static int
ftm_config_avail_alloc(wl_proxd_avail_t **out_avail)
{
uint16 bufsize;
wl_proxd_avail_t *avail;
/* init */
*out_avail = (wl_proxd_avail_t *) NULL;
bufsize = WL_PROXD_AVAIL_SIZE(avail, FTM_AVAIL_MAX_SLOTS);
avail = calloc(1, bufsize);
if (avail == NULL) {
printf("error: failed to allocate %d bytes of memory for avail\n",
bufsize);
return BCME_NOMEM;
}
/* initialize */
avail->flags = htol16(WL_PROXD_AVAIL_NONE); /* don't care, for query only */
avail->time_ref = htol16(WL_PROXD_TREF_NONE);
avail->max_slots = htol16(FTM_AVAIL_MAX_SLOTS);
avail->num_slots = htol16(0);
#define WLU_PROXD_AVAIL_REPEAT_TU 512 /* default interval */
avail->repeat.intvl = htol32(WLU_PROXD_AVAIL_REPEAT_TU);
avail->repeat.tmu = htol16(WL_PROXD_TMU_TU);
*out_avail = avail;
return BCME_OK;
}
/* Support AVAIL24 */
/*
* allocate and convert the availability info to AVAIL24 format
* if succeeds, an avail-buffer with 'in_avail_num_slots' time slot in AVAIL24
* format will be allocated.
*/
static int
ftm_config_avail_to_avail24(wl_proxd_avail_t *in_avail,
uint16 in_avail_num_slots, wl_proxd_avail24_t **out_avail)
{
uint16 bufsize;
wl_proxd_avail24_t *avail24 = NULL;
wl_proxd_time_slot_t *avail_slot, *avail24_slot;
/* init */
*out_avail = (wl_proxd_avail24_t *) NULL;
bufsize = WL_PROXD_AVAIL24_SIZE(avail24, in_avail_num_slots);
avail24 = calloc(1, bufsize);
if (avail24 == NULL) {
printf("error: failed to allocate %d bytes of memory for avail\n",
bufsize);
return BCME_NOMEM;
}
/* convert the header */
avail24->flags = in_avail->flags;
avail24->time_ref = in_avail->time_ref;
avail24->max_slots = htol16(in_avail_num_slots); /* don't care, for query only */
avail24->num_slots = avail24->max_slots;
avail24->repeat.intvl = in_avail->repeat.intvl;
avail24->repeat.tmu = in_avail->repeat.tmu;
/* convert time-slot */
avail_slot = WL_PROXD_AVAIL_TIMESLOTS(in_avail);
avail24_slot = WL_PROXD_AVAIL24_TIMESLOTS(avail24); /* &avail24->ts0[0]; */
memcpy(avail24_slot, avail_slot, in_avail_num_slots * sizeof(avail24->ts0[0]));
*out_avail = avail24;
return BCME_OK;
}
/*
* parse cmd-line for 'wl proxd ftm [<session-id>] config avail' command
* { [ftm-ref] none |
* time-ref dev-tsf|nan-adv|tbtt repeat repeat-tmu slot {channel:start-tmu:duration-tmu}+ }
* if succeeds, an avail info is allocated (data is packed) and returned.
* Also, number of time-slots associated with this buffer will be returned
* in 'out_num_slots'.
*/
static int
ftm_pack_config_avail_from_cmdarg(char **argv,
wl_proxd_avail_t **out_avail, uint16 *out_num_slots)
{
wl_proxd_avail_t *avail = NULL;
wl_proxd_time_ref_t time_ref;
uint16 num_slots = 0;
int err = BCME_OK;
/* init */
*out_avail = NULL;
*out_num_slots = 0;
/* check if user provides <param-name><param-value> for 'config avail' command */
if (*argv == NULL) {
err = BCME_USAGE_ERROR;
goto done;
}
/* allocate a buffer for parsing cmd-args */
err = ftm_config_avail_alloc(&avail);
if (err != BCME_OK)
goto done;
/* parse input arguments */
num_slots = 0;
time_ref = htol16(WL_PROXD_TREF_NONE); /* default */
while (*argv != NULL) {
if (stricmp(*argv, "none") == 0) {
time_ref = htol16(WL_PROXD_TREF_NONE);
}
else if (stricmp(*argv, "time-ref") == 0) {
++argv; /* advance to 'param-value' */
err = ftm_config_avail_parse_tref(*argv, &time_ref);
if (err != BCME_OK)
goto done;
}
else if (stricmp(*argv, "repeat") == 0) {
++argv; /* advance to 'param-value' */
err = ftm_config_parse_intvl(*argv, &avail->repeat);
if (err != BCME_OK)
goto done;
}
else if (stricmp(*argv, "slot") == 0) {
if (time_ref == htol16(WL_PROXD_TREF_NONE)) {
printf("error: time-ref is missing or invalid for slot\n");
err = BCME_BADARG;
goto done;
}
++argv; /* advance to 'param-value' */
err = ftm_config_avail_parse_all_slots(argv, avail, &num_slots);
if (err != BCME_OK)
goto done;
break; /* done parsing */
}
else {
printf("error: invalid param-name (%s)\n", *argv);
err = BCME_USAGE_ERROR; /* param-name is not specified */
goto done;
}
++argv; /* continue on next 'param-name' */
}
avail->time_ref = time_ref;
avail->num_slots = htol16(num_slots);
*out_avail = avail;
*out_num_slots = num_slots;
avail = (wl_proxd_avail_t *) NULL;
done:
if (err != BCME_OK) {
if (avail) /* cleanup */
free(avail);
}
return err;
}
/*
* handle 'wl proxd ftm [<session-id>] config avail
* { [ftm-ref] none |
* time-ref dev-tsf|nan-adv|tbtt repeat repeat-tmu slot {channel:start-tmu:duration-tmu}+ }
* parse cmd-line, setup local/peer(method/sessions) availability TLVs in caller
* provided buffer and adjusted-buffer-space if applies.
* Note, input '<session-id>' is used to determine if config-avail is
* for 'local' or 'peer'.
*
* This function is invoked from ftm_subcmd_config().
*/
static int
ftm_do_config_avail_iovar(void *wl, wl_proxd_iov_t *p_proxd_iov, uint16 proxd_iovsize,
uint16 tlvid, void *avail, int avail_size)
{
int err = BCME_OK;
uint16 bufsize;
wl_proxd_tlv_t *p_tlv;
uint16 buf_space_left;
uint16 all_tlvsize;
/* setup TLVs */
bufsize = proxd_iovsize - WL_PROXD_IOV_HDR_SIZE; /* adjust available size for TLVs */
p_tlv = &p_proxd_iov->tlvs[0];
/* TLV buffer starts with a full size, will decrement for each packed TLV */
buf_space_left = bufsize;
err = bcm_pack_xtlv_entry((uint8 **) &p_tlv, &buf_space_left,
tlvid, avail_size, (void *) avail,
BCM_XTLV_OPTION_ALIGN32);
if (err != BCME_OK) {
printf("%s: failed to pack TLVs for availability, status=%d\n",
__FUNCTION__, err);
goto done;
}
/* update the iov header, set len to include all TLVs + header */
all_tlvsize = (bufsize - buf_space_left);
p_proxd_iov->len = htol16(all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
err = wlu_var_setbuf(wl, "proxd", p_proxd_iov,
all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
done:
return err;
}
static int
ftm_handle_config_avail(void *wl, wl_proxd_iov_t *p_proxd_iov, uint16 proxd_iovsize,
wl_proxd_session_id_t session_id, char **argv)
{
int err = BCME_OK;
wl_proxd_avail_t *avail = NULL;
wl_proxd_avail24_t *avail24 = NULL;
uint16 num_slots;
int avail_size;
UNUSED_PARAMETER(session_id);
/* parse cmd line and alloc/pack the avail info */
err = ftm_pack_config_avail_from_cmdarg(argv,
&avail, &num_slots);
if (err != BCME_OK)
goto done;
/* pack and request to config the avail using the latest AVAIL if supported */
err = ftm_is_tlv_id_supported(wl, WL_PROXD_TLV_ID_AVAIL);
if (err == BCME_OK) {
/* adjust size based on num-slots available */
avail_size = WL_PROXD_AVAIL_SIZE(avail, num_slots);
err = ftm_do_config_avail_iovar(wl, p_proxd_iov, proxd_iovsize,
WL_PROXD_TLV_ID_AVAIL, (void *) avail, avail_size);
goto done;
}
/* try AVAIL24 */
/* convert to AVAIL24 format */
err = ftm_config_avail_to_avail24(avail,
num_slots, &avail24);
if (err != BCME_OK)
goto done;
/* adjust size based on num-slots available */
avail_size = WL_PROXD_AVAIL24_SIZE(avail24, num_slots);
err = ftm_do_config_avail_iovar(wl, p_proxd_iov, proxd_iovsize,
WL_PROXD_TLV_ID_AVAIL24, (void *) avail24, avail_size);
done:
if (avail) /* clean up */
free(avail);
if (avail24)
free(avail24);
return err;
}
/* proxd ftm config-category definition */
typedef enum {
FTM_CONFIG_CAT_GENERAL = 1, /* generial configuration */
FTM_CONFIG_CAT_OPTIONS = 2, /* 'config options' */
FTM_CONFIG_CAT_AVAIL = 3, /* 'config avail' */
FTM_CONFIG_CAT_SESSION_OPTIONS = 4
} ftm_config_category_t;
/*
* parse the config-category from a command-line
* Input:
* arg_type: a config-category string after 'wl proxd ftm config' from the command-line
*/
static ftm_config_category_t
ftm_parse_config_category(char *arg_category)
{
if (stricmp(arg_category, "options") == 0)
return FTM_CONFIG_CAT_OPTIONS;
if (stricmp(arg_category, "avail") == 0)
return FTM_CONFIG_CAT_AVAIL;
if (stricmp(arg_category, "session-options") == 0)
return FTM_CONFIG_CAT_SESSION_OPTIONS;
return FTM_CONFIG_CAT_GENERAL;
}
/*
* 'wl proxd ftm config' handler for general-configuration:
* wl proxd ftm [<session-id>] config [<param-name> <param-value>]+
*/
static int
ftm_handle_config_general(wl_proxd_session_id_t session_id, char **argv,
wl_proxd_tlv_t **p_tlv, uint16 *p_buf_space_left)
{
int status = BCME_OK;
/* data from command line as 'src' */
unsigned long int tmp_data_ul;
uint8 src_data_uint8;
uint16 src_data_uint16;
uint32 src_data_uint32;
struct ether_addr src_data_mac = ether_null;
wl_proxd_intvl_t src_data_intvl;
wl_proxd_tpk_t src_data_tpk;
void *p_src_data;
uint16 src_data_size; /* size of data pointed by p_src_data as 'source' */
const ftm_config_param_info_t *p_config_param_info;
/* parse the cmd-line, scan thru all <param-name> <param-value> */
while (*argv != NULL) {
/* look up 'config-param-info' based on 'param-name' */
p_config_param_info = ftm_get_config_param_info(*argv, session_id);
if (p_config_param_info == (ftm_config_param_info_t *) NULL) {
printf("error: invalid param-name (%s)\n", *argv);
status = BCME_USAGE_ERROR; /* param-name is not specified */
break;
}
/* parse 'param-value' */
if (*(argv + 1) == NULL) {
printf("error: invalid param-value\n");
status = BCME_USAGE_ERROR; /* param-value is not specified */
break;
}
++argv;
/* parse param-value, setup tlv-data */
p_src_data = (void *) NULL;
src_data_size = 0;
switch (p_config_param_info->tlvid) {
case WL_PROXD_TLV_ID_BSS_INDEX: /* uint8 */
case WL_PROXD_TLV_ID_NAN_MAP_ID:
case WL_PROXD_TLV_ID_FTM_REQ_RETRIES:
case WL_PROXD_TLV_ID_FTM_RETRIES:
src_data_uint8 = atoi(*argv);
p_src_data = (void *) &src_data_uint8;
src_data_size = sizeof(uint8);
break;
case WL_PROXD_TLV_ID_BURST_NUM_FTM: /* uint16 */
case WL_PROXD_TLV_ID_NUM_BURST:
case WL_PROXD_TLV_ID_RX_MAX_BURST:
if ((status = ftm_config_parse_ul(argv, p_config_param_info,
(unsigned long int) 0, (unsigned long int) 0xffff,
&tmp_data_ul)) != BCME_OK)
break;
src_data_uint16 = htol16((uint16) tmp_data_ul);
p_src_data = (void *) &src_data_uint16;
src_data_size = sizeof(uint16);
break;
case WL_PROXD_TLV_ID_TX_POWER: /* uint32 */
case WL_PROXD_TLV_ID_RATESPEC:
case WL_PROXD_TLV_ID_EVENT_MASK: /* wl_proxd_event_mask_t/uint32 */
case WL_PROXD_TLV_ID_DEBUG_MASK: /* allow in 0x######## format */
if ((status = ftm_config_parse_ul(argv, p_config_param_info,
(unsigned long int) 0, (unsigned long int) 0xffffffff,
&tmp_data_ul)) != BCME_OK)
break;
/* get the bitmask */
src_data_uint32 = htol32((uint32) tmp_data_ul);
p_src_data = &src_data_uint32;
src_data_size = sizeof(uint32);
break;
case WL_PROXD_TLV_ID_CHANSPEC: /* chanspec_t --> 32bit */
status = ftm_config_parse_channel(*argv, &src_data_uint32);
if (status == BCME_OK) {
p_src_data = (void *) &src_data_uint32;
src_data_size = sizeof(uint32);
}
break;
case WL_PROXD_TLV_ID_BSSID: /* mac address */
case WL_PROXD_TLV_ID_PEER_MAC:
case WL_PROXD_TLV_ID_DEV_ADDR:
src_data_mac = ether_null;
if (!wl_ether_atoe(*argv, &src_data_mac)) {
printf("error: invalid MAC address parameter\n");
status = BCME_USAGE_ERROR;
break;
}
p_src_data = &src_data_mac;
src_data_size = sizeof(src_data_mac);
break;
case WL_PROXD_TLV_ID_BURST_DURATION: /* wl_proxd_intvl_t */
case WL_PROXD_TLV_ID_BURST_PERIOD:
case WL_PROXD_TLV_ID_BURST_FTM_SEP:
case WL_PROXD_TLV_ID_BURST_TIMEOUT:
case WL_PROXD_TLV_ID_INIT_DELAY:
status = ftm_config_parse_intvl(*argv, &src_data_intvl);
if (status == BCME_OK) {
p_src_data = (void *) &src_data_intvl;
src_data_size = sizeof(src_data_intvl);
}
break;
case WL_PROXD_TLV_ID_TPK:
status = ftm_config_parse_tpk_peer(*argv, &src_data_tpk);
if (status == BCME_OK) {
p_src_data = (void *) &src_data_tpk;
src_data_size = sizeof(src_data_tpk);
}
break;
default:
/* not supported now */
status = BCME_USAGE_ERROR;
break;
}
if (status != BCME_OK)
break; /* abort */
status = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left,
p_config_param_info->tlvid, src_data_size, p_src_data,
BCM_XTLV_OPTION_ALIGN32);
if (status != BCME_OK)
{
#ifdef WL_FTM_DEBUG
printf("%s: bcm_pack_xltv_entry() failed, status=%d\n",
__FUNCTION__, status);
#endif /* WL_FTM_DEBUG */
break; /* abort */
}
argv++; /* continue on next <param-name><param-value> */
}
return status;
}
/*
* 'wl proxd ftm config' handler, there are two formats:
* For options/flags config, use
* wl proxd ftm [<session-id>] config options { [+|-]<param-value> }+
* for non-options/flags config, use
* wl proxd ftm [<session-id>] config [<param-name> <param-value>]+
*/
static int
ftm_subcmd_config(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
ftm_config_category_t category;
uint16 proxd_iovsize;
wl_proxd_iov_t *p_proxd_iov;
int status = BCME_OK;
uint16 bufsize;
wl_proxd_tlv_t *p_tlv;
uint16 buf_space_left;
uint16 all_tlvsize;
if (*argv == NULL) {
printf("error: config command requires parameters\n");
/* display 'proxd -h ftm config' helpmsg */
ftm_display_config_help();
return BCME_OK; /* no need to show 'proxd'-level help */
}
/* allocate a buffer for proxd-ftm config via 'set' iovar */
p_proxd_iov = ftm_alloc_getset_buf(method, session_id,
p_subcmd_info->cmdid, FTM_IOC_BUFSZ, &proxd_iovsize);
if (p_proxd_iov == (wl_proxd_iov_t *) NULL)
return BCME_NOMEM;
/* setup TLVs */
bufsize = proxd_iovsize - WL_PROXD_IOV_HDR_SIZE; /* adjust available size for TLVs */
p_tlv = &p_proxd_iov->tlvs[0];
/* TLV buffer starts with a full size, will decrement for each packed TLV */
buf_space_left = bufsize;
/* parse the cmd-line, get the config-category and dispatch to the handler */
/* to parse the parameters based on the 'config-category' */
category = ftm_parse_config_category(*argv);
if (category == FTM_CONFIG_CAT_OPTIONS) {
/* for 'wl proxd ftm [session-id] config options' */
/* dispatch to setup TLVs for method/session options/flags */
status = ftm_handle_config_options(session_id, ++argv, &p_tlv,
&buf_space_left, session_id != WL_PROXD_SESSION_ID_GLOBAL ? FALSE : TRUE);
} else if (category == FTM_CONFIG_CAT_SESSION_OPTIONS) {
status = ftm_handle_config_options(session_id, ++argv, &p_tlv,
&buf_space_left, FALSE);
} else if (category == FTM_CONFIG_CAT_AVAIL) {
status = ftm_handle_config_avail(wl, p_proxd_iov, proxd_iovsize,
session_id, ++argv);
}
else
status = ftm_handle_config_general(session_id, argv, &p_tlv,
&buf_space_left);
if (status == BCME_OK && category != FTM_CONFIG_CAT_AVAIL) {
/* update the iov header, set len to include all TLVs + header */
all_tlvsize = (bufsize - buf_space_left);
p_proxd_iov->len = htol16(all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
status = wlu_var_setbuf(wl, "proxd", p_proxd_iov,
all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
}
if (status == BCME_USAGE_ERROR) {
if (category == FTM_CONFIG_CAT_OPTIONS)
ftm_display_config_options_help();
else if (category == FTM_CONFIG_CAT_AVAIL)
ftm_display_config_avail_help();
else
ftm_display_config_help();
status = BCME_OK; /* reset to avoid showing 'proxd'-level help */
}
/* clean up */
free(p_proxd_iov);
#ifdef WL_FTM_DEBUG
if (status != BCME_OK)
printf("error: exit %s, status = %d\n", __FUNCTION__, status);
#endif /* WL_FTM_DEBUG */
return status;
}
static void
ftm_display_cmd_help(const ftm_subcmd_info_t *p_subcmd_info,
const char *cmd_params)
{
if (p_subcmd_info == (ftm_subcmd_info_t *) NULL)
return;
if (p_subcmd_info->helpmsg == (char *) NULL)
return;
/* print help messages for a specific FTM sub-command */
printf("\n\t%s\n", p_subcmd_info->helpmsg);
/* display cmd usage (these commands require no parameters) */
printf("\tUsage: wl proxd ftm ");
if (p_subcmd_info->cmdflag & FTM_SUBCMD_FLAG_SESSION) {
if (p_subcmd_info->cmdflag & FTM_SUBCMD_FLAG_METHOD)
printf("[<session-id>] ");
else
printf("<session-id> ");
}
printf("%s%s\n\n",
p_subcmd_info->name, cmd_params ? cmd_params : "");
}
/*
* pack tlvs for 'wl proxd ftm start-ranging [-d] <sid1> <sid2> ...'
* parse cmd-line, setup TLVs in caller provided buffer,
* adjusted buffer-space if applies.
* This function is invoked from ftm_subcmd_start_ranging().
*/
static int
ftm_pack_sids_from_cmdarg(char **argv,
wl_proxd_tlv_t **p_tlv, uint16 *p_buf_space_left)
{
char **tmp_argv;
uint16 num_sids;
int err = BCME_OK;
uint16 ranging_sids_size;
wl_proxd_session_id_list_t *ranging_sids = NULL;
wl_proxd_session_id_t sid;
uint16 count;
/* figout out number of session-id from the input */
tmp_argv = argv;
num_sids = 0;
while (*tmp_argv != NULL) {
tmp_argv++;
num_sids++;
}
if (num_sids == 0) {
err = BCME_USAGE_ERROR;
goto done;
}
/* allocate a temp buffer for parsing cmd-args */
ranging_sids_size = OFFSETOF(wl_proxd_session_id_list_t, ids) +
num_sids * sizeof(wl_proxd_session_id_t);
ranging_sids = calloc(1, ranging_sids_size);
if (ranging_sids == (wl_proxd_session_id_list_t *) NULL) {
printf("error: failed to allocate %d bytes of memory for ranging\n",
ranging_sids_size);
err = BCME_NOMEM;
goto done;
}
/* get session-ids followed 'wl proxd ftm start-ranging [-d]' */
count = 0;
while (*argv != NULL) {
sid = (uint16) atoi(*argv);
ranging_sids->ids[count++] = htol16(sid);
argv++; /* continue on next session-id */
}
ranging_sids->num_ids = htol16(num_sids);
err = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left,
WL_PROXD_TLV_ID_SESSION_ID_LIST, ranging_sids_size,
(uint8 *)ranging_sids, BCM_XTLV_OPTION_ALIGN32);
if (err != BCME_OK) {
printf("%s: failed to pack ranging-ids in xtlv, err=%d\n",
__FUNCTION__, err);
goto done;
}
done:
/* clean up */
if (ranging_sids)
free(ranging_sids);
return err;
}
/*
* pack tlvs for 'wl proxd ftm start-ranging [-d] <sid1> <sid2> ...'
* parse cmd-line, setup TLVs in caller provided buffer,
* adjusted buffer-space if applies.
* This function is invoked from ftm_subcmd_start_ranging().
*/
static int
ftm_pack_ranging_config_from_cmdarg(char **argv,
wl_proxd_tlv_t **p_tlv, uint16 *p_buf_space_left)
{
int err = BCME_OK;
wl_proxd_ranging_flags_t flags;
wl_proxd_ranging_flags_t flags_mask;
/* parse the ranging-flags '-d' followed 'wl proxd ftm start-ranging' if available */
flags = WL_PROXD_RANGING_FLAG_NONE;
while (*argv != NULL) {
if (stricmp(*argv, "-d") != 0)
break; /* skip to handle 'sids' arguments */
flags = WL_PROXD_RANGING_FLAG_DEL_SESSIONS_ON_STOP;
argv++; /* continue on next session-id */
}
/* pack ranging-flags/mask in xTLVs if provided */
if (flags != WL_PROXD_RANGING_FLAG_NONE) {
/* setup ranging flags TLV */
err = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left,
WL_PROXD_TLV_ID_RANGING_FLAGS,
sizeof(uint16), (uint8 *)&flags, BCM_XTLV_OPTION_ALIGN32);
if (err != BCME_OK) {
printf("%s: failed to pack ranging-flags in xtlv, err=%d\n",
__FUNCTION__, err);
goto done; /* abort */
}
/* setup ranging flags_mask TLV */
flags_mask = WL_PROXD_RANGING_FLAG_ALL;
err = bcm_pack_xtlv_entry((uint8 **) p_tlv, p_buf_space_left,
WL_PROXD_TLV_ID_RANGING_FLAGS_MASK,
sizeof(uint16), (uint8 *)&flags_mask, BCM_XTLV_OPTION_ALIGN32);
if (err != BCME_OK) {
printf("%s: failed to pack ranging flags_mask in xtlv, err=%d\n",
__FUNCTION__, err);
goto done; /* abort */
}
}
/* parse the 'sids' followed 'wl proxd ftm start-ranging [-d]' and
* pack the parameters in xTLVs if available
*/
err = ftm_pack_sids_from_cmdarg(argv, p_tlv, p_buf_space_left);
done:
if (err != BCME_OK)
printf("%s failed, err = %d\n",
__FUNCTION__, err);
return err;
}
/*
* 'wl proxd ftm start-ranging' handler
* wl proxd ftm start-ranging [-d] <sid1> <sid2> ...
*/
static int
ftm_subcmd_start_ranging(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
wl_proxd_iov_t *p_proxd_iov = NULL;
uint16 proxd_iovsize;
uint16 bufsize;
wl_proxd_tlv_t *p_tlv;
uint16 buf_space_left;
int err = BCME_OK;
uint16 all_tlvsize;
UNUSED_PARAMETER(wl);
/* this should apply to a method command */
if (*argv == NULL || session_id != WL_PROXD_SESSION_ID_GLOBAL) {
printf("error: start-ranging command requires parameters\n");
err = BCME_USAGE_ERROR;
goto done;
}
/* allocate a buffer for proxd-ftm config via 'set' iovar */
p_proxd_iov = ftm_alloc_getset_buf(method, session_id,
p_subcmd_info->cmdid, FTM_IOC_BUFSZ, &proxd_iovsize);
if (p_proxd_iov == (wl_proxd_iov_t *) NULL) {
err = BCME_NOMEM;
goto done;
}
/* setup TLVs */
bufsize = proxd_iovsize - WL_PROXD_IOV_HDR_SIZE; /* adjust available size for TLVs */
p_tlv = &p_proxd_iov->tlvs[0];
/* TLV buffer starts with a full size, will decrement for each packed TLV */
buf_space_left = bufsize;
/* parse the cmd-line, pack the parameters in TLVs */
err = ftm_pack_ranging_config_from_cmdarg(argv, &p_tlv,
&buf_space_left);
if (err != BCME_OK)
goto done;
/* update the iov header, set len to include all TLVs + header */
all_tlvsize = (bufsize - buf_space_left);
p_proxd_iov->len = htol16(all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
err = wlu_var_setbuf(wl, "proxd", p_proxd_iov,
all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
done:
/* clean up */
if (p_proxd_iov)
free(p_proxd_iov);
if (err == BCME_USAGE_ERROR) {
/* display 'proxd -h ftm' helpmsg */
ftm_display_cmd_help(p_subcmd_info, "[-d] <sid1> <sid2> ...");
err = BCME_OK; /* reset to avoid showing 'proxd'-level help */
}
return err;
}
/*
* 'wl proxd ftm stop-ranging' handler,
* wl proxd ftm stop-ranging
*/
static int
ftm_subcmd_stop_ranging(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
/* this should apply to a method command */
if (session_id != WL_PROXD_SESSION_ID_GLOBAL) {
printf("error: no session-id is allowed for stop-ranging\n");
/* display 'proxd -h ftm stop-ranging ' helpmsg */
ftm_display_cmd_help(p_subcmd_info, "");
return BCME_OK; /* no need to show 'proxd'-level help */
}
/* issue an iovar call */
return ftm_subcmd_setiov_no_tlv(wl, p_subcmd_info, method, session_id, argv);
}
/*
* display help-messages for 'wl proxd -h ftm'
*/
static void
ftm_display_method_help()
{
int i;
int max_cmdname_len = 20;
const ftm_subcmd_info_t *p_subcmd_info;
/* a header */
printf("\n\tProximity Detection using FTM\n");
printf("\tUsage: wl proxd ftm [<session-id>] <cmd> [<param-name> <param-value>]*\n");
printf("\t\t<session-id>: specify a session; If omitted, <cmd> applies to FTM method\n");
printf("\t\t<cmd>: specify a command from the following list,\n");
/* show the command list and the help messages for each FTM command */
p_subcmd_info = &ftm_cmdlist[0];
for (i = 0; i < (int) ARRAYSIZE(ftm_cmdlist); i++) {
printf("\t\t %*s: %s\n", max_cmdname_len, p_subcmd_info->name,
p_subcmd_info->helpmsg);
p_subcmd_info++;
}
printf("\t\t<param-name> <param-value>: <cmd>-specific parameters\n\n");
printf("\t\ttype 'wl proxd -h ftm config' for configuration command usage.\n");
printf("\t\ttype 'wl proxd -h ftm config options' "
"for options-configuration command usage.\n");
printf("\t\ttype 'wl proxd -h ftm config avail' "
"for availability-configuration command usage.\n");
printf("\t\ttype 'wl proxd -h ftm <cmd>' for others command usage.\n\n");
/* show examples */
printf("\tExample: wl proxd ftm enable\n\n");
return;
}
/*
* display help-messages for 'wl proxd -h ftm config'
*/
static void
ftm_display_config_help()
{
int i;
int max_param_name_len = 20;
const ftm_config_param_info_t *p_config_param_info;
/* a header */
printf("\n\tConfigure Proximity Detection using FTM\n");
printf("\tUsage: wl proxd ftm [<session-id>] config { <param-name> <param-value> }+ \n");
printf("\t\t<session-id>: specify a session id; If omitted, configure global items\n");
/* print helpmsg for each config-item */
printf("\t\t<param-name> <param-value>: configuration parameters from the following\n");
p_config_param_info = &ftm_config_param_info[0];
for (i = 0; i < (int) ARRAYSIZE(ftm_config_param_info); i++) {
printf("\t\t %*s: %s\n", max_param_name_len, p_config_param_info->name,
p_config_param_info->name_helpmsg);
p_config_param_info++;
}
/* show examples */
printf("\n\tExample: wl proxd ftm config retries 10\n");
printf("\t wl proxd ftm 1 config retries 10\n");
}
/*
* display help-messages for 'wl proxd -h ftm config options'
*/
static void
ftm_display_config_options_help()
{
int i;
int max_option_name_len = 20;
const ftm_config_options_info_t *p_config_options_info;
/* a header */
printf("\n\tConfigure-Options Proximity Detection using FTM\n");
printf("\tUsage: wl proxd ftm [<session-id>] config options { [+|-]<param-value> }+ \n");
printf("\t\t<session-id>: specify a session id; If omitted, set the global options\n");
printf("\t\t+|- prefix: to add or remove an option\n");
printf("\t\t<param-value>: specify an option\n");
/* print helpmsg for each global option flag */
printf("\t\t\tConfigurable options for a FTM method:\n");
p_config_options_info = &ftm_method_options_info[0];
for (i = 0; i < (int) ARRAYSIZE(ftm_method_options_info); i++) {
printf("\t\t %*s (0x%08x): %s\n", max_option_name_len,
p_config_options_info->param_value_str,
p_config_options_info->flags, p_config_options_info->helpmsg);
p_config_options_info++;
}
/* print helpmsg for each session option flag */
printf("\n\t\t\tConfigurable options for a specific session:\n");
p_config_options_info = &ftm_session_options_info[0];
for (i = 0; i < (int) ARRAYSIZE(ftm_session_options_info); i++) {
printf("\t\t %*s (0x%08x): %s\n", max_option_name_len,
p_config_options_info->param_value_str,
p_config_options_info->flags, p_config_options_info->helpmsg);
p_config_options_info++;
}
/* show examples */
printf("\n\tExample: wl proxd ftm config options +rx-enable\n");
printf("\t wl proxd ftm config options +0x00000001\n");
printf("\t wl proxd ftm 1 config options +rx-auto-burst -rtt-detail\n\n");
return;
}
/*
* display help-messages for 'wl proxd -h ftm config avail'
*/
static void
ftm_display_config_avail_help()
{
/* a header */
printf("\n\tConfigure-availability Proximity Detection using FTM\n");
printf("\t\tUsage: wl proxd ftm [<session-id>] config avail\n");
printf("\t\t { [time-ref] none |\n");
printf("\t\t time-ref <timeref-value> repeat <repeat-tmu> slot {<slot-value>}+ }\n");
printf("\t\t<session-id>: a session id; If omitted, set the local availability\n");
printf("\t\t otherwise, set the peer availability\n");
printf("\t\tnone or time-ref none: to clear availability\n");
printf("\t\t<timeref-value>: a timer reference. Possible values are:\n");
printf("\t\t dev-tsf | nan-dw | tbtt\n");
printf("\t\t<repeat-tmu>: repeat period in time-interval format\n");
printf("\t\t<slot-value>: a time slot in channel:start-tmu:duration-tmu\n");
printf("\t\t channel -- a channel (see 'wl chanspec')\n");
printf("\t\t start-tmu -- start time in time-interval format\n");
printf("\t\t (a number followed by a time unit)\n");
printf("\t\t duration-tmu -- duration in time-interval format\n");
printf("\t\t Time uints are:\n");
printf("\t\t s (seconds) | ms (milliseconds) | us (microseconds)\n");
printf("\t\t ns (nanoseconds) | ps (picoseconds) | tu (for TUs,default)\n");
printf("\t\tMore than one <slot-value>' can be specified for setting\n");
printf("\t\tmultiple time slots\n");
/* show examples */
printf("\n\tExample: wl proxd ftm config avail none\n");
printf("\t wl proxd ftm config avail time-ref dev-tsf slot 11:100s:100tu\n");
printf("\t wl proxd ftm 1 config avail time-ref dev-tsf slot 11:200s:300tu");
printf(" 11:600s:400tu\n");
return;
}
/*
* handle
* wl proxd -h
* wl proxd -h ftm
* wl proxd -h ftm <cmd>
* wl proxd -h ftm config options
* Output:
* BCME_USAGE_ERROR -- caller should bring up 'proxd' level help messages
* BCME_OK -- this func displays context-help for FTM method,
* caller does not need to show any help messages.
*/
static int
ftm_handle_help(char **argv)
{
const ftm_subcmd_info_t *p_subcmd_info;
if (!*argv)
return BCME_USAGE_ERROR;
if (stricmp(*argv, "ftm") != 0) /* check 'wl proxd -h ftm' */
return BCME_USAGE_ERROR;
++argv;
if (*argv == NULL) {
/* show help messages for 'wl proxd -h ftm' */
ftm_display_method_help();
return BCME_OK;
}
/* parse 'wl proxd -h ftm <cmd>' */
p_subcmd_info = ftm_get_subcmd_info(*argv);
if (p_subcmd_info != NULL) {
if (p_subcmd_info->cmdid == WL_PROXD_CMD_CONFIG) {
/* help for 'wl proxd -h ftm config' */
++argv;
if (*argv != NULL) { /* parse 'wl proxd -h ftm config options' */
if (stricmp(*argv, "options") == 0) {
/* display help for 'wl proxd -h ftm config options' */
ftm_display_config_options_help();
return BCME_OK;
}
if (stricmp(*argv, "avail") == 0) {
/* display help for 'wl proxd -h ftm config avail' */
ftm_display_config_avail_help();
return BCME_OK;
}
}
/* display help for 'wl proxd -h ftm config' */
ftm_display_config_help();
return BCME_OK;
}
else {
/* print help messages for a specific FTM sub-command */
ftm_display_cmd_help(p_subcmd_info, "");
return BCME_OK;
}
}
/* invalid <cmd>, display help messages for FTM method */
ftm_display_method_help();
return BCME_OK;
}
static const ftm_strmap_entry_t ftm_event_type_loginfo[] = {
/* wl_proxd_event_type_t, text-string */
{ WL_PROXD_EVENT_NONE, "none" },
{ WL_PROXD_EVENT_SESSION_CREATE, "session create" },
{ WL_PROXD_EVENT_SESSION_START, "session start" },
{ WL_PROXD_EVENT_FTM_REQ, "FTM req" },
{ WL_PROXD_EVENT_BURST_START, "burst start" },
{ WL_PROXD_EVENT_BURST_END, "burst end" },
{ WL_PROXD_EVENT_SESSION_END, "session end" },
{ WL_PROXD_EVENT_SESSION_RESTART, "session restart" },
{ WL_PROXD_EVENT_BURST_RESCHED, "burst rescheduled" },
{ WL_PROXD_EVENT_SESSION_DESTROY, "session destroy" },
{ WL_PROXD_EVENT_RANGE_REQ, "range request" },
{ WL_PROXD_EVENT_FTM_FRAME, "FTM frame" },
{ WL_PROXD_EVENT_DELAY, "delay" },
{ WL_PROXD_EVENT_VS_INITIATOR_RPT, "initiator-report " }, /* rx initiator-rpt */
{ WL_PROXD_EVENT_RANGING, "ranging " },
{ WL_PROXD_EVENT_LCI_MEAS_REP, "LCI measurement report" },
{ WL_PROXD_EVENT_CIVIC_MEAS_REP, "civic measurement report" },
{ WL_PROXD_EVENT_START_WAIT, "start wait" },
};
#if defined(linux)
static const ftm_strmap_entry_t*
ftm_get_event_type_loginfo(wl_proxd_event_type_t event_type)
{
/* look up 'event-type' from a predefined table */
return ftm_get_strmap_info((int32) event_type,
ftm_event_type_loginfo, ARRAYSIZE(ftm_event_type_loginfo));
}
/*
* check FTM event
* return -1: if event's version does not match. In this case,
* caller should continue the event checking.
* return 0: this func reports receving a new-version FTM-event and
* caller should skip the checking for this event.
*/
static int
ftm_event_check(bcm_event_t *p_bcm_event)
{
int status;
wl_proxd_event_t *p_event;
uint16 version;
wl_proxd_event_type_t event_type;
const ftm_strmap_entry_t *p_loginfo;
int tlvs_len;
/* move to bcm event payload, which is proxd event structure */
p_event = (wl_proxd_event_t *) (p_bcm_event + 1);
version = ltoh16(p_event->version);
if (version < WL_PROXD_API_VERSION) {
#ifdef WL_FTM_DEBUG
printf("ignore non-ftm event version = 0x%x < WL_PROXD_API_VERSION (0x%x)\n",
version, WL_PROXD_API_VERSION);
#endif /* WL_FTM_DEBUG */
return -1; /* let caller handle the old version */
}
event_type = (wl_proxd_event_type_t) ltoh16(p_event->type);
#ifdef WL_FTM_DEBUG
printf("event_type=0x%x, ntoh16()=0x%x, ltoh16()=0x%x",
p_event->type, ntoh16(p_event->type), ltoh16(p_event->type));
#endif /* WL_FTM_DEBUG */
p_loginfo = ftm_get_event_type_loginfo(event_type);
if (p_loginfo == NULL) {
printf("receive an invalid FTM event %d\n", event_type);
return 0; /* ignore this event */
}
/* get TLVs len, skip over event header */
tlvs_len = ltoh16(p_event->len) - OFFSETOF(wl_proxd_event_t, tlvs);
printf("receive '%s' event: version=0x%x len=%d method=%s sid=%d tlvs_len=%d\n",
p_loginfo->text,
version,
ltoh16(p_event->len),
ftm_method_value_to_logstr(ltoh16(p_event->method)),
ltoh16(p_event->sid),
tlvs_len);
/* print event contents TLVs */
if (tlvs_len > 0) {
/* unpack TLVs and invokes the cbfn to print the event content TLVs */
status = bcm_unpack_xtlv_buf((void *) NULL, (uint8 *)&p_event->tlvs[0],
tlvs_len, BCM_XTLV_OPTION_ALIGN32, ftm_unpack_xtlv_cbfn);
if (status != BCME_OK)
printf("Failed to unpack xtlv for an event\n");
}
return 0; /* indicate we have processed this FTM event */
}
#endif /* linux */
#ifdef WL_FTM_DEBUG
/*
* Format an 'event' bitmask to a text-string to caller-provided buffer
*/
static void
ftm_format_event_mask(wl_proxd_event_type_t event, char *p_strbuf, int bufsize)
{
int i, nChars;
int event_enabled_count = 0;
memset(p_strbuf, 0, bufsize);
char *p_tmpbuf = p_strbuf;
const ftm_strmap_entry_t *p_loginfo = &ftm_event_type_loginfo[0];
for (i = 0; i < (int) ARRAYSIZE(ftm_event_type_loginfo); i++) {
if (WL_PROXD_EVENT_ENABLED(event, (wl_proxd_event_type_t) p_loginfo->id)) {
if (event_enabled_count == 0) {
if ((nChars = snprintf(p_tmpbuf, bufsize, "(")) < 0)
return; /* error, ignore */
bufsize -= nChars;
p_tmpbuf += nChars;
}
nChars = snprintf(p_tmpbuf, bufsize, "'%s' ", p_loginfo->text);
if (nChars < 0)
return; /* abort if error */
bufsize -= nChars;
p_tmpbuf += nChars;
event_enabled_count++;
}
p_loginfo++;
}
if (event_enabled_count > 0)
snprintf(p_tmpbuf, bufsize, ")");
}
#endif /* WL_FTM_DEBUG */
/*
* 'wl proxd ftm tune' handler,
* wl proxd ftm tune
*/
static int
ftm_handle_tune_options(char **argv, wl_proxd_tlv_t **p_tlv, uint16 *p_buf_space_left,
wl_proxd_params_tof_tune_t *tof_tune)
{
miniopt_t to;
int opt_err;
int ret = BCME_USAGE_ERROR;
if (*argv == NULL)
return BCME_USAGE_ERROR;
miniopt_init(&to, "tune", NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
return BCME_USAGE_ERROR;
}
argv += to.consumed;
ret = proxd_tune_set_param_from_opt("proxd ftm tune", &to, tof_tune);
}
#ifdef WL_FTM_DEBUG
prhex("tune_opt", (uint8*)tof_tune, sizeof(wl_proxd_params_tof_tune_t));
#endif /* WL_FTM_DEBUG */
ret = bcm_pack_xtlv_entry((uint8 **)p_tlv, p_buf_space_left,
WL_PROXD_TLV_ID_TUNE, sizeof(wl_proxd_params_tof_tune_t), (uint8*)tof_tune,
BCM_XTLV_OPTION_ALIGN32);
if (ret != BCME_OK) {
printf("%s: failed to pack proxd ftm tune in xtlv, err=%d\n",
__FUNCTION__, ret);
return ret;
}
return BCME_OK;
}
/* Used for getting the current options before setting the new ones only */
static int
ftm_tune_cbfn(void *ctx, const uint8 *p_data, uint16 tlvid, uint16 len)
{
wl_proxd_params_tof_tune_t *tunep = (wl_proxd_params_tof_tune_t *)ctx;
if (tlvid == WL_PROXD_TLV_ID_TUNE) {
#ifdef WL_FTM_DEBUG
prhex("ftm_tune_cbbn", p_data, len);
#endif /* WL_FTM_DEBUG */
if (len < sizeof(wl_proxd_params_tof_tune_t)) {
/* FW has older version */
memcpy(&tunep->Ki, p_data, len);
} else {
memcpy(tunep, p_data, sizeof(wl_proxd_params_tof_tune_t));
}
}
return BCME_OK;
}
static int
ftm_subcmd_tune(void *wl, const ftm_subcmd_info_t *p_subcmd_info,
wl_proxd_method_t method, wl_proxd_session_id_t session_id, char **argv)
{
uint16 proxd_tune_method;
uint16 proxd_iovsize;
wl_proxd_iov_t *p_proxd_iov, *p_iovresp;
uint16 bufsize;
wl_proxd_tlv_t *p_tlv;
uint16 buf_space_left;
uint16 all_tlvsize = 0;
int ret = BCME_OK;
wl_proxd_params_tof_tune_t tof_tune; /* TOF tune parameters */
memset(&tof_tune, 0, sizeof(tof_tune));
/* this should apply to a method command */
if (session_id != WL_PROXD_SESSION_ID_GLOBAL) {
printf("error: no session-id is allowed for tune\n");
return BCME_OK;
}
/* skip the command name and check if mandatory exists */
if (!*argv) {
fprintf(stderr, "missing mandatory parameter \'method\'\n");
return BCME_USAGE_ERROR;
}
/* parse method */
proxd_tune_method = (uint16)atoi(argv[0]);
if (proxd_tune_method == 0) {
fprintf(stderr, "invalid parameter \'method\'\n");
return BCME_USAGE_ERROR;
}
/* only supports PROXD_TOF_METHOD */
if (proxd_tune_method == PROXD_RSSI_METHOD)
return BCME_OK;
if (!*++argv) {
/* get */
return ftm_common_getcmd_handler(wl, p_subcmd_info, method, session_id, argv);
} else {
/* allocate iovar getset buffer */
p_proxd_iov = ftm_alloc_getset_buf(method, session_id,
p_subcmd_info->cmdid, sizeof(wl_proxd_params_tof_tune_t),
&proxd_iovsize);
if (p_proxd_iov == (wl_proxd_iov_t *) NULL)
return BCME_NOMEM;
bufsize = proxd_iovsize - WL_PROXD_IOV_HDR_SIZE;
p_tlv = &p_proxd_iov->tlvs[0];
buf_space_left = bufsize;
/* get current tune params */
ret = wlu_var_getbuf(wl, "proxd", p_proxd_iov, proxd_iovsize,
(void *)&p_iovresp);
if (ret != BCME_OK) {
#ifdef WL_FTM_DEBUG
printf("%s: failed to send getbuf proxd iovar, status=%d\n",
__FUNCTION__, ret);
#endif /* WL_FTM_DEBUG */
goto done;
}
if (p_iovresp != NULL) {
int tlvs_len = ltoh16(p_iovresp->len) - WL_PROXD_IOV_HDR_SIZE;
if (tlvs_len > 0) {
ret = bcm_unpack_xtlv_buf(&tof_tune, (uint8 *)p_iovresp->tlvs,
tlvs_len, BCM_XTLV_OPTION_ALIGN32, ftm_tune_cbfn);
}
}
/* handle options */
ret = ftm_handle_tune_options(argv, &p_tlv, &buf_space_left, &tof_tune);
if (ret == BCME_OK) {
/* prep to transport xtlv */
all_tlvsize = bufsize - buf_space_left;
p_proxd_iov->len = htol16(all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
ret = wlu_var_setbuf(wl, "proxd", p_proxd_iov,
all_tlvsize + WL_PROXD_IOV_HDR_SIZE);
}
}
done:
/* clean up */
free(p_proxd_iov);
#ifdef wL_FTM_DEBUG
if (ret != BCME_OK)
printf("error: exit %s, status = %d\n", __FUNCTION__, ret);
#endif /* WL_FTM_DEBUG */
return ret;
}
static int
proxd_tune_display(wl_proxd_params_tof_tune_t *tof_tune, uint16 len)
{
int err = BCME_OK;
int i, version = 0;
if (!tof_tune)
return err;
printf("Ki=%d \n", dtoh32(tof_tune->Ki));
printf("Kt=%d \n", dtoh32(tof_tune->Kt));
printf("vhtack=%d \n", dtoh16(tof_tune->vhtack));
printf("seq_en=%d\n", dtoh16(tof_tune->seq_en));
printf("core=%d\n", tof_tune->core);
printf("sw_adj=%d\n", dtoh16(tof_tune->sw_adj));
printf("hw_adj=%d\n", dtoh16(tof_tune->hw_adj));
printf("minDT = %d\n", tof_tune->minDT);
printf("maxDT = %d\n", tof_tune->maxDT);
printf("threshold_log2=%d %d %d seqtx %d seqrx %d 2g %d seqtx5g20 %d"
" seqrx5g20 %d seqtx2g20 %d seqrx2g20 %d\n",
dtoh16(tof_tune->N_log2[TOF_BW_20MHZ_INDEX]),
dtoh16(tof_tune->N_log2[TOF_BW_40MHZ_INDEX]),
dtoh16(tof_tune->N_log2[TOF_BW_80MHZ_INDEX]),
dtoh16(tof_tune->N_log2[TOF_BW_SEQTX_INDEX]),
dtoh16(tof_tune->N_log2[TOF_BW_SEQRX_INDEX]),
dtoh16(tof_tune->N_log2_2g),
dtoh16(tof_tune->seq_5g20.N_tx_log2),
dtoh16(tof_tune->seq_5g20.N_rx_log2),
dtoh16(tof_tune->seq_2g20.N_tx_log2),
dtoh16(tof_tune->seq_2g20.N_rx_log2));
printf("threshold_scale=%d %d %d seqtx %d seqrx %d 2g %d seqtx5g20 %d"
" seqrx5g20 %d seqtx2g20 %d seqrx2g20 %d\n",
dtoh16(tof_tune->N_scale[TOF_BW_20MHZ_INDEX]),
dtoh16(tof_tune->N_scale[TOF_BW_40MHZ_INDEX]),
dtoh16(tof_tune->N_scale[TOF_BW_80MHZ_INDEX]),
dtoh16(tof_tune->N_scale[TOF_BW_SEQTX_INDEX]),
dtoh16(tof_tune->N_scale[TOF_BW_SEQRX_INDEX]),
dtoh16(tof_tune->N_scale_2g),
dtoh16(tof_tune->seq_5g20.N_tx_scale),
dtoh16(tof_tune->seq_5g20.N_rx_scale),
dtoh16(tof_tune->seq_2g20.N_tx_scale),
dtoh16(tof_tune->seq_2g20.N_rx_scale));
printf("total_frmcnt=%d \n", tof_tune->totalfrmcnt);
printf("reserve_media=%d \n", tof_tune->rsv_media);
printf("flags=0x%x \n", dtoh16(tof_tune->flags));
for (i = 0; i < TOF_BW_NUM; i++) {
printf("window length %dMHz = %d\n",
(20 << i), tof_tune->w_len[i]);
printf("window offset %dMHz = %d\n",
(20 << i), tof_tune->w_offset[i]);
}
printf("seq window length 5G-20MHz = %2d 2G-20MHz = %2d\n",
tof_tune->seq_5g20.w_len,
tof_tune->seq_2g20.w_len);
printf("seq window offset 5G-20MHz = %2d 2G-20MHz = %2d\n",
tof_tune->seq_5g20.w_offset,
tof_tune->seq_2g20.w_offset);
printf("frame count=%d %d %d seq %d\n",
tof_tune->ftm_cnt[TOF_BW_20MHZ_INDEX],
tof_tune->ftm_cnt[TOF_BW_40MHZ_INDEX],
tof_tune->ftm_cnt[TOF_BW_80MHZ_INDEX],
tof_tune->ftm_cnt[TOF_BW_SEQTX_INDEX]);
if (len == sizeof(wl_proxd_params_tof_tune_t)) {
version = dtoh32(tof_tune->version);
switch (version) {
case WL_PROXD_TUNE_VERSION_1:
printf("bitflip threshold=%u\n", dtoh16(tof_tune->bitflip_thresh));
printf("SNR threshold=%u\n", dtoh16(tof_tune->snr_thresh));
printf("2G receive sensitivity threshold=%d\n", tof_tune->recv_2g_thresh);
printf("ACS GDV threshold=%u\n", dtoh32(tof_tune->acs_gdv_thresh));
printf("ACS RSSI threshold=%d\n", tof_tune->acs_rssi_thresh);
printf("Smoothing window enable=%u\n", tof_tune->smooth_win_en);
break;
default:
break;
}
}
return err;
}