blob: a04353e0c0e6af19786bdff70ecd2024399d1f95 [file] [log] [blame]
/*
* wl stf 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_stf.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"
#include "wlu_rates_matrix.h"
#define WL_MIMO_PS_MAX_PARAMS 4
#define WL_MIMO_PS_LEARNING_MAX_PARAMS 3
static void wl_txppr_print(ppr_t *ppr, int cck, uint flags);
static void wl_txppr_print_bw(ppr_t *ppr, int cck, uint flags, wl_tx_bw_t bw);
static cmd_func_t wl_get_current_txppr;
static cmd_func_t wl_txcore;
static cmd_func_t wl_txcore_pwr_offset;
static int wl_mimo_stf(void *wl, cmd_t *cmd, char **argv);
static cmd_func_t wl_spatial_policy, wl_ratetbl_ppr;
static cmd_func_t wl_mimo_ps_cfg;
static cmd_func_t wl_mimo_ps_learning_cfg;
static int wl_mimo_ps_learning_cfg_get_status(void *wl, const char *iovar);
static cmd_func_t wl_mimo_ps_status;
static int wl_mimo_ps_status_dump(wl_mimo_ps_status_t *status);
static int wl_mimo_ps_status_assoc_status_dump(uint8 assoc_status);
static void wl_mimo_ps_status_hw_state_dump(uint16 hw_state);
static cmd_func_t wl_temp_throttle_control;
static cmd_func_t wl_ocl_status;
static void wl_ocl_status_fw_dump(uint16 fw_state);
static void wl_ocl_status_hw_dump(uint8 hw_state);
static cmd_t wl_stf_cmds[] = {
{ "curppr", wl_get_current_txppr, WLC_GET_VAR, -1,
"Return current tx power per rate offset."},
{ "txcore", wl_txcore, WLC_GET_VAR, WLC_SET_VAR,
"Usage: wl txcore -k <CCK core mask> -o <OFDM core mask> -s <1..4> -c <core bitmap>\n"
"\t-k CCK core mask\n"
"\t-o OFDM core mask\n"
"\t-s # of space-time-streams\n"
"\t-c active core (bitmask) to be used when transmitting frames"
},
{ "txcore_override", wl_txcore, WLC_GET_VAR, -1,
"Usage: wl txcore_override\n"
"\tget the user override of txcore"
},
{ "txchain_pwr_offset", wl_txcore_pwr_offset, WLC_GET_VAR, WLC_SET_VAR,
"Usage: wl txchain_pwr_offset [qdBm offsets]\n"
"\tGet/Set the current offsets for each core in qdBm (quarter dBm)"
},
{ "mimo_ss_stf", wl_mimo_stf, WLC_GET_VAR, WLC_SET_VAR,
"get/set SS STF mode.\n"
"\tUsage: wl mimo_ss_stf <value> <-b a | b>\n"
"\tvalue: 0 - SISO; 1 - CDD\n"
"\t-b(band): a - 5G; b - 2.4G"},
{ "spatial_policy", wl_spatial_policy, WLC_GET_VAR, WLC_SET_VAR,
"set/get spatial_policy\n"
"\tUsage: wl spatial_policy <-1: auto / 0: turn off / 1: turn on>\n"
"\t to control individual band/sub-band use\n"
"\t wl spatial_policy a b c d e\n"
"\t where a is 2.4G band setting\n"
"\t where b is 5G lower band setting\n"
"\t where c is 5G middle band setting\n"
"\t where d is 5G high band setting\n"
"\t where e is 5G upper band setting"},
{ "ratetbl_ppr", wl_ratetbl_ppr, WLC_GET_VAR, WLC_SET_VAR,
"Usage: For get: wl ratetbl_ppr\n"
"\t For set: wl ratetbl_ppr <rate> <ppr>" },
{ "mrc_rssi_threshold", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"get/set mrc_rssi_threshold for mimo power save,\n "
"\twhen RSSI is below RSSI level specified by mrc_rssi_threshold then \n"
"\tall rx chains will be made active for all rx frames to benefit "
"from Maximal ratio combining (MRC) "
},
{ "mimo_ps_cfg_change_wait_time", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"get/set mimo_ps_guard_interval for mimo power save,\n "
"\tspecifies the time STA will wait before receiving packets"
"when moving from higher configuration to lower configuration\n"
},
{ "mimo_ps_cfg", wl_mimo_ps_cfg, WLC_GET_VAR, WLC_SET_VAR,
"Description: Configure MIMO Power save configuration.\n"
"\twhere active_chains: 0 for all, 1 for 1 chain. Only 0 and 1 are supported for now.\n"
"\tIn future more than 1 can be supported to enable more than 1 but less than max chains.\n"
"\tmode = static (0) or dynamic (1) or disabled(3)."
"Mode applies only when active_chains is 0.\n"
"\tbandwidth = Full (0), 20M (1), 40M (2), 80M (3).\n"
"\tApply changes after learning donot appy(0) apply(1).\n"
},
{ "mimo_ps_status", wl_mimo_ps_status, WLC_GET_VAR, -1,
"usage: mimo_ps_status\n"
"Displays MIMO PS related state/information"
},
{ "mimo_ps_learning_config", wl_mimo_ps_learning_cfg, WLC_GET_VAR, WLC_SET_VAR,
"Description: Configure mimo ps learning related parameters\n"
"\tusage: wl mimo_ps_learning_config <flag> <no_of_packets> \n"
"\tFlag :\n\t0 - Get status of current learning\n"
"\t1 - ABORT current learning. \n"
"\t2 - used to configure only no of packets\n"
},
{ "temp_throttle_control", wl_temp_throttle_control, WLC_GET_VAR, WLC_SET_VAR,
"Usage: temp_throttle_control <enable/disable> <value>\n"
},
{ "ocl_status", wl_ocl_status, WLC_GET_VAR, -1,
"usage: ocl_status\n"
"Displays ocl fw/hw state/information"
},
{ "ocl_rssi_threshold", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"get/set ocl_rssi_threshold such that,\n "
"\twhen RSSI is below the specified ocl_rssi_threshold then \n"
"\tocl is disabled.\n"
},
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/* module initialization */
void
wluc_stf_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register stf commands */
wl_module_cmds_register(wl_stf_cmds);
}
static void
wl_txppr_print(ppr_t *ppr, int cck, uint flags)
{
switch (ppr_get_ch_bw(ppr)) {
case WL_TX_BW_20:
printf("\n20MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20);
break;
case WL_TX_BW_40:
printf("\n20 in 40MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20IN40);
printf("\n40MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_40);
break;
case WL_TX_BW_80:
printf("\n20 in 80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20IN80);
printf("\n40 in 80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_40IN80);
printf("\n80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_80);
break;
case WL_TX_BW_160:
printf("\n20 in 160MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20IN160);
printf("\n40 in 160MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_40IN160);
printf("\n80 in 160MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_80IN160);
printf("\n160MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_160);
break;
case WL_TX_BW_8080:
printf("\n20 in 80+80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_20IN8080);
printf("\n40 in 80+80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_40IN8080);
printf("\n80 in 80+80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_80IN8080);
printf("\nchan1 80+80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_8080);
printf("\nchan2 80+80MHz:\n");
wl_txppr_print_bw(ppr, cck, flags, WL_TX_BW_8080CHAN2);
break;
default:
break;
}
/* MCS32 value is obsoleted */
/* printf("MCS32 %2d\n", ppr->mcs32); */
printf("\n");
}
#define PRINT_PPR_RATE_LOOP(idx, len, rates) \
for (idx = 0; idx < len; idx++) { \
if (rates[idx] == WL_RATE_DISABLED) \
printf(" -"); \
else \
printf(" %2d", rates[idx]); \
}
/* print power offset for for a given bandwidth */
static void
wl_txppr_print_bw(ppr_t *ppr, int cck, uint flags, wl_tx_bw_t bw)
{
uint i, j, rlen;
uint n = WL_NUM_2x2_ELEMENTS;
uint offset = 0;
int8 *ptr, *vhtptr;
const char *str = "";
bool siso = ((flags & WL_TX_POWER_F_MIMO) == 0);
bool vht = ((flags & WL_TX_POWER_F_VHT) != 0);
ppr_ofdm_rateset_t ofdm_rate;
ppr_vht_mcs_rateset_t vhtrate;
if (!siso) {
offset = WL_NUM_3x3_ELEMENTS;
n = WL_NUM_4x4_ELEMENTS;
}
if (cck) {
ppr_dsss_rateset_t rate;
ppr_get_dsss(ppr, bw, WL_TX_CHAINS_1, &rate);
printf("CCK ");
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_DSSS, rate.pwr);
if (!siso) {
for (i = WL_TX_CHAINS_2; i <= WL_TX_CHAINS_4; i++) {
ppr_get_dsss(ppr, bw, i, &rate);
printf("\nCCK CDD 1x%d ", i);
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_DSSS, rate.pwr);
}
}
}
ppr_get_ofdm(ppr, bw, WL_TX_MODE_NONE, WL_TX_CHAINS_1, &ofdm_rate);
printf("\nOFDM ");
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_OFDM, ofdm_rate.pwr);
for (i = WL_TX_CHAINS_2; i <= WL_TX_CHAINS_4; i++) {
ppr_get_ofdm(ppr, bw, WL_TX_MODE_CDD, i, &ofdm_rate);
printf("\nOFDM-CDD 1x%d", i);
PRINT_PPR_RATE_LOOP(j, WL_RATESET_SZ_OFDM, ofdm_rate.pwr);
}
printf("\n");
for (i = 0; i < n; i++) {
wl_tx_nss_t nss;
wl_tx_mode_t mode;
wl_tx_chains_t chains;
switch (i + offset) {
case 0:
str = "MCS-SISO ";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_1;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 1:
str = "MCS-CDD ";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_CDD;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 2:
str = "MCS STBC ";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_STBC;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 3:
str = "MCS 8~15 ";
nss = WL_TX_NSS_2;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = NULL;
break;
case 4:
case 5:
ptr = NULL;
vhtptr = NULL;
break;
case 6:
str = "1 Nsts 1 Tx";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_1;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 7:
str = "1 Nsts 2 Tx";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_CDD;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 8:
str = "1 Nsts 3 Tx";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_CDD;
chains = WL_TX_CHAINS_3;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 9:
str = "1 Nsts 4 Tx";
nss = WL_TX_NSS_1;
mode = WL_TX_MODE_CDD;
chains = WL_TX_CHAINS_4;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 10:
str = "2 Nsts 2 Tx";
nss = WL_TX_NSS_2;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_2;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 11:
str = "2 Nsts 3 Tx";
nss = WL_TX_NSS_2;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_3;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 12:
str = "2 Nsts 4 Tx";
nss = WL_TX_NSS_2;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_4;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 13:
str = "3 Nsts 3 Tx";
nss = WL_TX_NSS_3;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_3;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 14:
str = "3 Nsts 4 Tx";
nss = WL_TX_NSS_3;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_4;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
case 15:
str = "4 Nsts 4 Tx";
nss = WL_TX_NSS_4;
mode = WL_TX_MODE_NONE;
chains = WL_TX_CHAINS_4;
ptr = vhtrate.pwr;
vhtptr = &vhtrate.pwr[8];
break;
default:
ptr = NULL;
vhtptr = NULL;
break;
}
if (ptr == NULL)
continue;
ppr_get_vht_mcs(ppr, bw, nss, mode, chains, &vhtrate);
printf("%s ", str);
if (vht && vhtptr)
rlen = WL_RATESET_SZ_VHT_MCS_P;
else
rlen = WL_RATESET_SZ_HT_MCS;
PRINT_PPR_RATE_LOOP(j, rlen, ptr);
printf("\n");
}
}
static int
wl_get_current_txppr(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint flags;
chanspec_t chanspec;
char chanspec_str[CHANSPEC_STR_LEN];
uint pprsize = ppr_ser_size_by_bw(ppr_get_max_bw());
wl_txppr_t *wl_txppr;
ppr_t *pprptr = NULL;
wl_txppr = (wl_txppr_t *)malloc(sizeof(*wl_txppr) + pprsize);
if (wl_txppr == NULL) {
fprintf(stderr, "Error allocating memory failed for curppr");
return BCME_NOMEM;
}
memset(wl_txppr, 0, sizeof(*wl_txppr));
wl_txppr->buflen = pprsize;
if ((err = ppr_init_ser_mem_by_bw(wl_txppr->pprbuf, ppr_get_max_bw(), pprsize))
!= BCME_OK) {
free(wl_txppr);
return err;
}
if (WLC_IOCTL_MAXLEN < sizeof(wl_txppr_t) + pprsize) {
free(wl_txppr);
return BCME_ERROR;
}
argv++;
if (*argv)
fprintf(stderr, "Ignoring arguments for %s\n", cmd->name);
wl_txppr->ver = WL_TXPPR_VERSION;
wl_txppr->len = WL_TXPPR_LENGTH;
if ((err = wlu_iovar_getbuf(wl, "curppr", wl_txppr, sizeof(*wl_txppr) + pprsize,
buf, WLC_IOCTL_MAXLEN)) < 0) {
free(wl_txppr);
return err;
}
/* the input buffer is no longer needed, output results are in buf */
free(wl_txppr);
wl_txppr = (wl_txppr_t *)buf;
/* parse */
wl_txppr->flags = dtoh32(wl_txppr->flags);
wl_txppr->chanspec = wl_chspec_from_driver(wl_txppr->chanspec);
wl_txppr->local_chanspec = wl_chspec_from_driver(wl_txppr->local_chanspec);
chanspec = wl_txppr->chanspec;
flags = (wl_txppr->flags & WL_TX_POWER_F_VHT) |
(wl_txppr->flags & WL_TX_POWER_F_HT) |
(wl_txppr->flags & WL_TX_POWER_F_MIMO) |
(wl_txppr->flags & WL_TX_POWER_F_SISO);
/* dump */
printf("Current channel:\t %s\n",
wf_chspec_ntoa(wl_txppr->chanspec, chanspec_str));
printf("BSS channel:\t\t %s\n",
wf_chspec_ntoa(wl_txppr->local_chanspec, chanspec_str));
printf("Power/Rate Dump (in %s): Channel %d\n", (wl_txppr->flags & WL_TX_POWER_F_UNIT_QDBM)?
"1/4dB":"1/2dB", CHSPEC_CHANNEL(chanspec));
if ((err = ppr_deserialize_create(NULL, wl_txppr->pprbuf, pprsize, &pprptr)) == BCME_OK) {
wl_txppr_print(pprptr, CHSPEC_IS2G(chanspec), flags);
ppr_delete(NULL, pprptr);
}
return err;
}
static int
wl_txcore_pwr_offset(void *wl, cmd_t *cmd, char **argv)
{
wl_txchain_pwr_offsets_t offsets;
char *endptr;
int i;
long val;
int err;
/* toss the command name */
argv++;
if (!*argv) {
err = wlu_iovar_get(wl, cmd->name, &offsets, sizeof(wl_txchain_pwr_offsets_t));
if (err < 0)
return err;
printf("txcore offsets qdBm: %d %d %d %d\n",
offsets.offset[0], offsets.offset[1],
offsets.offset[2], offsets.offset[3]);
return 0;
}
memset(&offsets, 0, sizeof(wl_txchain_pwr_offsets_t));
for (i = 0; i < WL_NUM_TXCHAIN_MAX; i++, argv++) {
if (!*argv)
break;
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if (val > 0)
return BCME_BADARG;
offsets.offset[i] = (int8)val;
}
err = wlu_iovar_set(wl, cmd->name, &offsets, sizeof(wl_txchain_pwr_offsets_t));
return err;
}
static int
wl_txcore(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_txcore";
int err = 0, opt_err, val;
uint8 streams = 0;
bool streams_set = FALSE;
uint8 core = 0;
bool core_set = FALSE;
uint8 cck_mask = 0;
bool cck_set = FALSE;
uint8 ofdm_mask = 0;
bool ofdm_set = FALSE;
uint8 mcs_mask[4] = {0, 0, 0, 0}; /* pre-initialize # of streams {core:4 | stream:4} */
bool mcs_set = FALSE;
uint8 idx;
uint32 coremask[2] = {0, 0};
/* toss the command name */
argv++;
if (!*argv) {
if (cmd->get < 0)
return -1;
if ((err = wlu_iovar_get(wl, cmd->name, &coremask, sizeof(uint32)*2)) < 0)
return err;
printf("txcore enabled bitmap (Nsts {4..1}) 0x%02x 0x%02x 0x%02x 0x%02x\n",
(coremask[0] >> 24) & 0xff, (coremask[0] >> 16) & 0xff,
(coremask[0] >> 8) & 0xff, coremask[0] & 0xff);
printf("txcore mask OFDM 0x%02x CCK 0x%02x\n",
(coremask[1] >> 8) & 0xff, coremask[1] & 0xff);
return 0;
}
val = atoi(*argv);
if (val == -1)
goto next;
miniopt_init(&to, fn_name, "w", FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 's') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for streams\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
streams_set = TRUE;
streams = (to.val & 0x0f);
if (streams > 4)
fprintf(stderr, "%s: Nsts > %d\n", fn_name, to.val);
}
if (to.opt == 'c') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for stf core\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
core_set = TRUE;
core = (to.val & 0x0f) << 4;
if (core == 0) {
fprintf(stderr, "%s: %1d-stream core cannot be zero\n",
fn_name, streams);
err = BCME_BADARG;
goto exit;
}
}
if (to.opt == 'o') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for streams\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
ofdm_set = TRUE;
ofdm_mask = (to.val & 0x0f);
if (ofdm_mask == 0) {
fprintf(stderr, "%s: OFDM core cannot be zero\n", fn_name);
err = BCME_BADARG;
goto exit;
}
}
if (to.opt == 'k') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for streams\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
cck_set = TRUE;
cck_mask = (to.val & 0x0f);
if (cck_mask == 0) {
fprintf(stderr, "%s: CCK core cannot be zero\n", fn_name);
err = BCME_BADARG;
goto exit;
}
}
if (streams_set && core_set) {
streams_set = core_set = FALSE;
mcs_set = TRUE;
idx = streams - 1;
mcs_mask[idx] = (uint8)(core|streams);
}
}
if (streams_set != core_set) {
fprintf(stderr, "%s: require to set both -s x -c y\n", fn_name);
err = BCME_BADARG;
goto exit;
}
if (mcs_set) {
coremask[0] |= mcs_mask[0] << 0;
coremask[0] |= mcs_mask[1] << 8;
coremask[0] |= mcs_mask[2] << 16;
coremask[0] |= mcs_mask[3] << 24;
}
if (cck_set)
coremask[1] |= cck_mask;
if (ofdm_set)
coremask[1] |= ofdm_mask << 8;
next:
err = wlu_var_setbuf(wl, cmd->name, coremask, sizeof(uint32)*2);
exit:
return err;
}
static int
wl_mimo_stf(void *wl, cmd_t *cmd, char **argv)
{
char var[256];
int32 int_val;
bool get = TRUE;
uint32 len = 0;
void *ptr = NULL;
char *endptr;
int i = 0, j = 0;
int ret = 0;
while (argv[i])
i++;
if (i > 4)
return BCME_USAGE_ERROR;
/* toss the command name */
argv++;
j = 1;
if (i == 1) {
int_val = -1;
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
else {
if (isdigit((unsigned char)(**argv))) {
get = FALSE;
int_val = htod32(strtoul(*argv, &endptr, 0));
if ((int_val != 0) && (int_val != 1)) {
fprintf(stderr, "wl mimo_ss_stf: bad stf mode.\n");
return BCME_BADARG;
}
memcpy(var, (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
argv++;
j++;
}
if (j == i) {
int_val = -1;
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
else if (!strncmp(*argv, "-b", 2)) {
argv++;
j++;
if (j == i)
return BCME_BADARG;
if (!strcmp(*argv, "a"))
int_val = 1;
else if (!strcmp(*argv, "b"))
int_val = 0;
else {
fprintf(stderr,
"wl mimo_ss_stf: wrong -b option, \"-b a\" or \"-b b\"\n");
return BCME_USAGE_ERROR;
}
j++;
if (j < i)
return BCME_BADARG;
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
}
if (get) {
if ((ret = wlu_var_getbuf(wl, cmd->name, var, sizeof(var), &ptr)) < 0)
return ret;
printf("0x%x\n", dtoh32(*(int *)ptr));
}
else
ret = wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
return ret;
}
static int
wl_spatial_policy(void *wl, cmd_t *cmd, char **argv)
{
void *ptr = NULL;
int err, i, *reply;
int mode[SPATIAL_MODE_MAX_IDX] = {-1, -1, -1, -1, -1};
/* Order is 2G, 5G-LOW, 5G-MID, 5G-HIGH, 5G-UPPER
* if only one argument given, than all band or sub-band take the
* same value
*/
if (!*++argv) {
bool all_same = TRUE;
if ((err = wlu_var_getbuf(wl, cmd->name, &mode, sizeof(mode), &ptr)) < 0)
return err;
reply = (int *)ptr;
for (i = 1; i < SPATIAL_MODE_MAX_IDX; i++) {
/* check if return values for each band/sub-band is same or not */
if (reply[i-1] != reply[i])
all_same = FALSE;
}
if (all_same)
printf("%2d\n", reply[0]);
else {
printf("2.4GHz : %2d\n", reply[SPATIAL_MODE_2G_IDX]);
printf("5GHz (lower) : %2d\n", reply[SPATIAL_MODE_5G_LOW_IDX]);
printf("5GHz (middle): %2d\n", reply[SPATIAL_MODE_5G_MID_IDX]);
printf("5GHz (high) : %2d\n", reply[SPATIAL_MODE_5G_HIGH_IDX]);
printf("5GHz (upper) : %2d\n", reply[SPATIAL_MODE_5G_UPPER_IDX]);
}
return 0;
}
mode[0] = atoi(*argv);
if (!*++argv) {
for (i = 1; i < SPATIAL_MODE_MAX_IDX; i++)
mode[i] = mode[0];
} else {
for (i = 1; i < SPATIAL_MODE_MAX_IDX; i++) {
mode[i] = atoi(*argv);
if (!*++argv && i < (SPATIAL_MODE_MAX_IDX - 1)) {
printf("error: missing arguments\n");
return BCME_USAGE_ERROR;
}
}
}
err = wlu_var_setbuf(wl, cmd->name, &mode, sizeof(mode));
return err;
}
static int
wl_ratetbl_ppr(void *wl, cmd_t *cmd, char **argv)
{
void *ptr = NULL;
int err, i, *reply;
int val[12];
/* Order is 2G, 5G-LOW, 5G-MID, 5G-HIGH, 5G-UPPER
* if only one argument given, than all band or sub-band take the
* same value
*/
memset(&val, 0, sizeof(val));
if (!*++argv) {
if ((err = wlu_var_getbuf(wl, cmd->name, &val, sizeof(val), &ptr)) < 0)
return err;
reply = (int *)ptr;
for (i = 0; i < 12; i++)
printf("%s: %2d\n", (reply[i] & 0x80) ? "OFDM" : "CCK ", (reply[i] & 0x7f));
return 0;
}
val[0] = atoi(*argv++);
val[1] = atoi(*argv++);
err = wlu_var_setbuf(wl, cmd->name, &val, sizeof(val));
return err;
}
static int
wl_mimo_ps_learning_cfg(void *wl, cmd_t *cmd, char **argv)
{
wl_mimops_learning_cfg_t mimo_ps_learning_cfg;
char *endptr;
int i;
long val;
int err;
/* toss the command name */
argv++;
if (!*argv) {
err = wl_mimo_ps_learning_cfg_get_status(wl, cmd->name);
return err;
}
memset(&mimo_ps_learning_cfg, 0, sizeof(wl_mimops_learning_cfg_t));
for (i = 0; i < WL_MIMO_PS_LEARNING_MAX_PARAMS; i++, argv++) {
if (!*argv) {
break;
}
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
return BCME_BADARG;
}
switch (i) {
case 0:
if (val == 0)
mimo_ps_learning_cfg.flag =
(uint8)WL_MIMO_PS_PS_LEARNING_CFG_STATUS;
else if (val == 1)
mimo_ps_learning_cfg.flag =
(uint8)WL_MIMO_PS_PS_LEARNING_CFG_ABORT;
else if (val == 2)
mimo_ps_learning_cfg.flag =
(uint8)WL_MIMO_PS_PS_LEARNING_CFG_CONFIG;
else
return BCME_BADARG;
break;
case 1:
mimo_ps_learning_cfg.no_of_packets_for_learning = (uint32)val;
mimo_ps_learning_cfg.learning_rssi_threshold = 0;
break;
case 2:
mimo_ps_learning_cfg.learning_rssi_threshold = (int8)val;
break;
default:
return BCME_BADARG;
}
}
/* At the end get the configuration and display */
mimo_ps_learning_cfg.version = WL_MIMO_PS_PS_LEARNING_CFG_V1;
err = wlu_iovar_set(wl, cmd->name,
(void*)&mimo_ps_learning_cfg, sizeof(wl_mimops_learning_cfg_t));
return err;
}
static int
wl_mimo_ps_learning_cfg_get_status(void *wl, const char *iovar)
{
wl_mimops_learning_cfg_t mimo_ps_learning_data;
int err;
err = wlu_iovar_get(wl, iovar, &mimo_ps_learning_data, sizeof(wl_mimops_learning_cfg_t));
if (err < 0)
return err;
printf("\n Current MIMO PS Learning data :- \n");
printf("\t BSSID %s\n",
wl_ether_etoa(&mimo_ps_learning_data.mimops_learning_data.BSSID));
printf("\t Start time stamp %d\n",
mimo_ps_learning_data.mimops_learning_data.startTimeStamp);
printf("\t End time stamp %d\n",
mimo_ps_learning_data.mimops_learning_data.endTimeStamp);
printf("\t Total MIMO above threshold %d\n",
mimo_ps_learning_data.mimops_learning_data.totalMIMO_above_rssi_threshold);
printf("\t Total MIMO below threshold %d\n",
mimo_ps_learning_data.mimops_learning_data.totalMIMO_below_rssi_threshold);
printf("\t Total SISO above threshold %d\n",
mimo_ps_learning_data.mimops_learning_data.totalSISO_above_rssi_threshold);
printf("\t Total SISO below threshold %d\n",
mimo_ps_learning_data.mimops_learning_data.totalSISO_below_rssi_threshold);
if (mimo_ps_learning_data.version == WL_MIMO_PS_PS_LEARNING_CFG_V1) {
printf("\t mimo_ps learning version %d\n",
mimo_ps_learning_data.version);
printf("\t mimo_ps learning rssi threshold value %d\n",
mimo_ps_learning_data.learning_rssi_threshold);
}
printf("\t mimo_ps no of packets to wait for %d\n",
mimo_ps_learning_data.no_of_packets_for_learning);
if (mimo_ps_learning_data.mimops_learning_data.reason ==
WL_MIMO_PS_PS_LEARNING_ABORTED)
printf("\t REASON : MIMO PS LEARNING ABORTED\n");
else if (mimo_ps_learning_data.mimops_learning_data.reason ==
WL_MIMO_PS_PS_LEARNING_COMPLETED)
printf("\t REASON : MIMO PS LEARNING COMPLETED\n");
else if (mimo_ps_learning_data.mimops_learning_data.reason ==
WL_MIMO_PS_PS_LEARNING_ONGOING)
printf("\t REASON : MIMO PS LEARNING IN PRPGRESS\n");
else
printf("\t REASON : UNKNOWN \n");
return err;
}
static int
wl_mimo_ps_cfg(void *wl, cmd_t *cmd, char **argv)
{
wl_mimops_cfg_t mimo_ps_cfg;
char *endptr;
int i;
long val;
int err;
/* toss the command name */
argv++;
if (!*argv) {
err = wlu_iovar_get(wl, cmd->name, &mimo_ps_cfg, sizeof(wl_mimops_cfg_t));
if (err < 0)
return err;
printf("\n Current MIMO PS CFG :- \n"
"\t active chains = %d\n \t mode = %d \n\t bandwidth = %d "
"\t apply changes after learning = %d\n",
mimo_ps_cfg.active_chains, mimo_ps_cfg.mode, mimo_ps_cfg.bandwidth,
mimo_ps_cfg.applychangesafterlearning);
return 0;
}
memset(&mimo_ps_cfg, 0, sizeof(wl_mimops_cfg_t));
for (i = 0; i < WL_MIMO_PS_MAX_PARAMS; i++, argv++) {
if (!*argv) {
break;
}
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
return BCME_BADARG;
}
switch (i) {
case 0:
mimo_ps_cfg.active_chains = (int8)val;
break;
case 1:
mimo_ps_cfg.mode = (int8)val;
break;
case 2:
mimo_ps_cfg.bandwidth = (int8)val;
break;
case 3:
mimo_ps_cfg.applychangesafterlearning = (int8) val;
break;
default:
return BCME_BADARG;
}
}
mimo_ps_cfg.version = WL_MIMO_PS_CFG_VERSION_1;
err = wlu_iovar_set(wl, cmd->name, (void*)&mimo_ps_cfg, sizeof(wl_mimops_cfg_t));
return err;
}
static int
wl_mimo_ps_status(void *wl, cmd_t *cmd, char **argv)
{
int err;
wl_mimo_ps_status_t mimo_ps_status;
/* toss the command name */
argv++;
err = wlu_iovar_get(wl, cmd->name, &mimo_ps_status, sizeof(wl_mimo_ps_status_t));
if (err < 0)
return err;
/* Check version */
if ((mimo_ps_status.version != WL_MIMO_PS_STATUS_VERSION_1) &&
(mimo_ps_status.version != WL_MIMO_PS_STATUS_VERSION_2) &&
(mimo_ps_status.version != WL_MIMO_PS_STATUS_VERSION_3)) {
printf("Unexpected version: %u\n", mimo_ps_status.version);
return BCME_VERSION;
}
/* dump */
err = wl_mimo_ps_status_dump(&mimo_ps_status);
return err;
}
static const char *mimo_ps_status_state_str_tbl[] = {
"STATE_NONE",
"INFORM_AP_IN_PROGRESS",
"INFORM_AP_DONE",
"LEARNING",
"HW_CONFIGURE",
"INFORM_AP_PENDING"
};
static const char *mimo_ps_status_bw_str_tbl[] = {
"FULL",
"20MHz",
"40MHz",
"80MHz",
"160MHz",
"8080MHz"
};
static const char *mimo_ps_status_hw_state_str_tbl[] = {
"NONE",
"LTE_COEX",
"MIMOPS_BSS",
"AWDL_BSS",
"SCAN",
"TXPPR",
"PWRTHOTTLE",
"TEMPSENSE",
"IOVAR",
"AP_BSS"
};
static const char *mimo_ps_status_mhf_flag[] = {
"Disabled",
"Enabled",
"Core Down",
"Unsupported"
};
static int
wl_mimo_ps_status_dump(wl_mimo_ps_status_t *status)
{
int err = BCME_OK;
uint8 ap_cap_bw;
if (status == NULL)
return BCME_ERROR;
ap_cap_bw = WL_MIMO_PS_STATUS_AP_CAP_BW(status->ap_cap);
/* AP capability (MIMO,SISO)/BW */
printf("AP capability: ");
err = wl_mimo_ps_status_assoc_status_dump(WL_MIMO_PS_STATUS_AP_CAP(status->ap_cap));
if (ap_cap_bw > (ARRAYSIZE(mimo_ps_status_bw_str_tbl)-1)) {
printf("/Invalid BW value=%d\n", ap_cap_bw);
err = BCME_BADARG;
} else {
if (WL_MIMO_PS_STATUS_AP_CAP(status->ap_cap) != WL_MIMO_PS_STATUS_ASSOC_NONE)
printf("/%s", mimo_ps_status_bw_str_tbl[ap_cap_bw]);
printf("\n");
}
/* Association */
printf("Association: ");
err = wl_mimo_ps_status_assoc_status_dump(status->association_status &
WL_MIMO_PS_STATUS_ASSOC_STATUS_MASK);
if (status->association_status & WL_MIMO_PS_STATUS_ASSOC_STATUS_VHT_WITHOUT_OMN)
printf("\t(AP without OMN)");
printf("\n");
/* mimo_ps_cfg state */
printf("MIMO PS state: ");
if (status->mimo_ps_state > (ARRAYSIZE(mimo_ps_status_state_str_tbl)-1)) {
printf("\tInvalid MIMO PS state=%d\n", status->mimo_ps_state);
err = BCME_BADARG;
} else {
printf("\t%s\n", mimo_ps_status_state_str_tbl[status->mimo_ps_state]);
}
/* mrc_state */
printf("MRC state: ");
if (status->mrc_state == WL_MIMO_PS_STATUS_MRC_NONE)
printf("\tNot active\n");
else if (status->mrc_state == WL_MIMO_PS_STATUS_MRC_ACTIVE)
printf("\tACTIVE\n");
else {
printf("\tInvalid MRC state\n");
err = BCME_BADARG;
}
/* bss information */
printf("bss_rxchain:\t0x%02x\n", status->bss_rxchain);
printf("bss_txchain:\t0x%02x\n", status->bss_txchain);
printf("bss_bw: ");
if (status->bss_bw > (ARRAYSIZE(mimo_ps_status_bw_str_tbl)-1)) {
printf("\tInvalid bandwidth value=%d\n", status->bss_bw);
err = BCME_BADARG;
} else
printf("\t%s\n", mimo_ps_status_bw_str_tbl[status->bss_bw]);
/* hw_state */
wl_mimo_ps_status_hw_state_dump(status->hw_state);
/* hw information */
printf("hw_rxchain:\t0x%02x\n", status->hw_rxchain);
printf("hw_txchain:\t0x%02x\n", status->hw_txchain);
printf("hw_bw: ");
if (status->hw_bw > (ARRAYSIZE(mimo_ps_status_bw_str_tbl)-1)) {
printf("\tInvalid bandwidth value=%d\n", status->hw_bw);
err = BCME_BADARG;
} else
printf("\t\t%s\n", mimo_ps_status_bw_str_tbl[status->hw_bw]);
if (status->version == WL_MIMO_PS_STATUS_VERSION_1)
return err;
printf("PM_BCNRX state: ");
if (status->pm_bcnrx_state > (ARRAYSIZE(mimo_ps_status_mhf_flag) - 1)) {
printf("\tInvalid value %d\n", status->pm_bcnrx_state);
} else {
printf("\t%s\n", mimo_ps_status_mhf_flag[status->pm_bcnrx_state]);
}
printf("SISO_BCMC_RX state: ");
if (status->siso_bcmc_rx_state > (ARRAYSIZE(mimo_ps_status_mhf_flag) - 1)) {
printf("\tInvalid value %d\n", status->siso_bcmc_rx_state);
} else {
char *astr = "";
if (status->siso_bcmc_rx_state == WL_MIMO_PS_STATUS_MHF_FLAG_ACTIVE) {
if ((status->pm_bcnrx_state == WL_MIMO_PS_STATUS_MHF_FLAG_NONE) ||
(status->pm_bcnrx_state == WL_MIMO_PS_STATUS_MHF_FLAG_INVALID))
astr = " (Inactive)";
else if (status->pm_bcnrx_state == WL_MIMO_PS_STATUS_MHF_FLAG_ACTIVE)
astr = " (Active)";
}
printf("\t%s%s\n", mimo_ps_status_mhf_flag[status->siso_bcmc_rx_state], astr);
}
printf("BSSBasicRateSet: %s\n", (status->basic_rates_present ? "Yes" : "No"));
return err;
}
static void
wl_mimo_ps_status_hw_state_dump(uint16 hw_state)
{
uint i, n;
printf("HW state: \t0x%04x\n", hw_state);
if (hw_state == 0) {
printf("\t\t%s\n", mimo_ps_status_hw_state_str_tbl[hw_state]);
return;
}
n = MIN(ARRAYSIZE(mimo_ps_status_hw_state_str_tbl), NBITS(hw_state));
for (i = 0; i < n; i++) {
if (hw_state & (0x1 << (i-1))) {
printf("\t\t%s\n", mimo_ps_status_hw_state_str_tbl[i]);
}
}
}
static int
wl_mimo_ps_status_assoc_status_dump(uint8 assoc_status)
{
switch (assoc_status) {
case WL_MIMO_PS_STATUS_ASSOC_NONE:
printf("\tNot associated");
break;
case WL_MIMO_PS_STATUS_ASSOC_SISO:
printf("\tSISO");
break;
case WL_MIMO_PS_STATUS_ASSOC_MIMO:
printf("\tMIMO");
break;
case WL_MIMO_PS_STATUS_ASSOC_LEGACY:
printf("\tLEGACY");
break;
default:
printf("Invalid AP cap/association status\n");
break;
}
return BCME_OK;
}
static int
wl_temp_throttle_control(void *wl, cmd_t *cmd, char **argv)
{
wl_temp_control_t temp_control;
char *endptr;
long val;
int err;
memset(&temp_control, 0, sizeof(wl_temp_control_t));
if (!*++argv) {
/* Get */
err = wlu_iovar_get(wl, cmd->name, &temp_control, sizeof(wl_temp_control_t));
if (err < 0) {
return err;
}
if (temp_control.enable == 0) {
printf("Temp control is off\n");
return BCME_OK;
}
printf("Current mode : 0x%x\n", temp_control.control_bit);
return BCME_OK;
} else {
/* Enable / Disable */
val = atoi(*argv);
temp_control.enable = val;
argv++;
if (temp_control.enable == 1) {
if (!*argv) {
return BCME_ERROR;
}
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if (val > 0xFF) {
printf("Out of range\n");
return BCME_OUTOFRANGECHAN;
}
temp_control.control_bit = val;
}
err = wlu_iovar_set(wl, cmd->name, &temp_control, sizeof(wl_temp_control_t));
return err;
}
}
static int
wl_ocl_status(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_OK;
ocl_status_info_t ocl_status;
/* toss the command name */
argv++;
err = wlu_iovar_get(wl, cmd->name, &ocl_status, sizeof(ocl_status));
if (err < 0)
return err;
/* Check version */
if (ocl_status.version != WL_OCL_STATUS_VERSION) {
printf("Unexpected version: %u\n", ocl_status.version);
return BCME_VERSION;
}
/* validate length */
if (ocl_status.len < (uint8) sizeof(ocl_status)) {
fprintf(stderr, "OCL Status: short len %d < %d\n",
ocl_status.len, (int)sizeof(ocl_status));
return BCME_ERROR;
}
/* dump fw/hw state */
wl_ocl_status_fw_dump(ocl_status.fw_status);
wl_ocl_status_hw_dump(ocl_status.hw_status);
printf("OCL Coremask : 0x%02x (Core-%d)\n",
ocl_status.coremask, ocl_status.coremask-1);
return err;
}
static const char *ocl_status_fw_state_str_tbl[] = {
"HOST",
"RSSI",
"LTEC",
"SISO",
"CAL",
"CHAN",
"ASPND"
};
static const char *ocl_status_hw_state_str_tbl[] = {
"OCL",
"MIMO",
"", "", "", "", "",
"Core Down"
};
static void
wl_ocl_status_fw_dump(uint16 fw_state)
{
uint i, n;
bool first = TRUE;
printf("FW Disable Reason : 0x%04x (", fw_state);
if (fw_state == 0) {
printf("%s)\n", "NONE");
return;
}
n = MIN(ARRAYSIZE(ocl_status_fw_state_str_tbl), NBITS(fw_state));
for (i = 0; i < n; i++) {
if (fw_state & (0x1 << (i))) {
printf("%s%s", first ? "": ", ", ocl_status_fw_state_str_tbl[i]);
first = FALSE;
}
}
printf(")\n");
}
static void
wl_ocl_status_hw_dump(uint8 hw_state)
{
uint i, n;
bool first = TRUE;
printf("HW Status Bits : 0x%02x ", hw_state);
n = MIN(ARRAYSIZE(ocl_status_hw_state_str_tbl), NBITS(hw_state));
for (i = 0; i < n; i++) {
if (hw_state & (0x1 << (i))) {
printf("%s%s", first ? "(": ", ", ocl_status_hw_state_str_tbl[i]);
first = FALSE;
}
}
printf("%s\n", hw_state ? ")" : "");
}