blob: 1b82e03cec5454d46ba5ee99b23fd18bdd7829ac [file] [log] [blame]
/*
* wl dynamic btcoex 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_btcdyn.c $
*/
#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>
static cmd_func_t wl_btcoex_dynctl;
static cmd_func_t wl_btcoex_dynctl_status;
static cmd_func_t wl_btcoex_dynctl_sim;
#define WL_BTCDYNCTL_USAGE \
"Usage:\n" \
"\t [-d dflt_dsns_level] [-l low_dsns_level] [-m mid_dsns_level] [-h high_dsns_level]\n" \
"\t [-c default_btc_mode\n" \
"\t [-s mode_switching_btrssi_hysteresis in dBm\n\n" \
"\t [-f dynctl_flags] : bit0: Dynctl on/off, bit1-desense on/off," \
" bit2-m_switch on/off, bit7-dryrun on/off\n" \
"\t [-j row_idx:btcmode,bt_pwr,wl_rssi_high,wl_rssi_low] : set one row in mode table\n" \
"\t [-k row_idx:btcmode,bt_pwr,wl_rssi_high,wl_rssi_low] : set one row in desense table\n" \
"\t [-n number of active rows in mode switching table\n" \
"\t [-o number of active rows in desense switching table\n\n"
#define WL_BTCDYNCTL_STATUS_USAGE \
"Usage: command doeesn't take any arguments]\n"
#define WL_BTCDYNCTL_SIM_USAGE \
"Usage: wl btc_dynctl_sim [1|0] [-b bt_sim_pwr] [-r bt_sim_rssi] [-w wl_sim_rssi] \n" \
/* bycdyn wl commands */
static cmd_t wl_btcdyn_cmds[] = {
{ "btc_dynctl", wl_btcoex_dynctl, WLC_GET_VAR, WLC_SET_VAR, WL_BTCDYNCTL_USAGE},
{ "btc_dynctl_status", wl_btcoex_dynctl_status, WLC_GET_VAR, -1, WL_BTCDYNCTL_STATUS_USAGE},
{ "btc_dynctl_sim", wl_btcoex_dynctl_sim, WLC_GET_VAR, WLC_SET_VAR, WL_BTCDYNCTL_SIM_USAGE},
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/* module initialization */
void
wluc_btcdyn_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register btcdyn commands */
wl_module_cmds_register(wl_btcdyn_cmds);
}
static int fill_thd_row(btc_thr_data_t *thd, char *str)
{
char delim[] = ":,\0";
int err = BCME_OK;
int idx = atoi(strtok(str, delim));
char *s_mode, *s_bt_pwr, *s_bt_rssi, *s_wl_rssi_high, *s_wl_rssi_low;
if (idx >= DCTL_TROWS_MAX) {
fprintf(stderr, " invalid index:%d the data:%s\n", idx, str);
err = BCME_BADARG;
return err;
}
s_mode = strtok(NULL, delim);
s_bt_pwr = strtok(NULL, delim);
s_bt_rssi = strtok(NULL, delim);
s_wl_rssi_high = strtok(NULL, delim);
s_wl_rssi_low = strtok(NULL, delim);
if (!s_mode || !s_bt_pwr || !s_bt_rssi || !s_wl_rssi_high ||
!s_wl_rssi_low) {
fprintf(stderr, " invalid row data format or number of parameters\n");
err = BCME_BADARG;
return err;
}
/* all present, convert them to int */
thd[idx].mode = atoi(s_mode);
thd[idx].bt_pwr = atoi(s_bt_pwr);
thd[idx].bt_rssi = atoi(s_bt_rssi);
thd[idx].wl_rssi_high = atoi(s_wl_rssi_high);
thd[idx].wl_rssi_low = atoi(s_wl_rssi_low);
printf("written table entry:%d: mode:%d, bt_pwr:%d, bt_rssi:%d,"
" wl_rssi_high:%d, wl_rssi_low:%d\n",
idx, thd[idx].mode, thd[idx].bt_pwr, thd[idx].bt_rssi,
thd[idx].wl_rssi_high, thd[idx].wl_rssi_low);
return err;
}
/*
* get/set dynamic BTCOEX profile (desense, modesw etc)
*/
static int
wl_btcoex_dynctl(void *wl, cmd_t *cmd, char **argv)
{
int err = 0, opt_err;
miniopt_t to;
void *ptr;
dctl_prof_t *profile = NULL;
UNUSED_PARAMETER(cmd);
/* get current profile */
if ((err = wlu_var_getbuf(wl,
"btc_dynctl", NULL, 0, &ptr) < 0)) {
fprintf(stderr, "btc_dynctl: getbuf ioctl failed\n");
return err;
}
if (*++argv) {
/* some arguments are present, do set or help */
if (argv[0][0] == 'h' ||
argv[0][0] == '?') {
err = BCME_USAGE_ERROR;
goto exit;
}
profile = calloc(1, sizeof(dctl_prof_t) + sizeof(dynctl_status_t));
if (profile == NULL)
return BCME_NOMEM;
/* copy current profile values into buf allocated for set ioctl */
bcopy(ptr, profile, sizeof(dctl_prof_t));
miniopt_init(&to, __FUNCTION__, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 'c') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" default_btc_mode\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
profile->default_btc_mode = to.val;
}
if (to.opt == 'd') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" dflt_dsns_level\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
profile->dflt_dsns_level = to.val;
}
if (to.opt == 'l') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" low_dsns_level\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
profile->low_dsns_level = to.val;
}
if (to.opt == 'm') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" mid_dsns_level\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
profile->mid_dsns_level = to.val;
}
if (to.opt == 'h') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" high_dsns_level\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
profile->high_dsns_level = to.val;
}
if (to.opt == 'f') {
/* set dynctl flags */
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" flags \n", to.valstr);
err = BCME_BADARG;
goto exit;
}
profile->flags = to.val;
}
/* modify mode switching table */
if (to.opt == 'j') {
if (fill_thd_row(profile->msw_data, to.valstr) != BCME_OK)
goto exit;
}
/* modify desense level table */
if (to.opt == 'k') {
if (fill_thd_row(profile->dsns_data, to.valstr) != BCME_OK)
goto exit;
}
/* set number of active rows in msw table */
if (to.opt == 'n') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" msw_rows\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
/* check for array bounds */
if (to.val > DCTL_TROWS_MAX) {
fprintf(stderr, "array index:%d >= MAX:%d !\n",
to.val, DCTL_TROWS_MAX);
err = BCME_BADARG;
goto exit;
}
profile->msw_rows = to.val;
}
/* set number of active rows in desense table */
if (to.opt == 'o') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" dsns_rows\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
/* check for array bounds */
if (to.val > DCTL_TROWS_MAX) {
fprintf(stderr, "array index;%d >= MAX:%d !\n",
to.val, DCTL_TROWS_MAX);
err = BCME_BADARG;
goto exit;
}
profile->dsns_rows = to.val;
}
if (to.opt == 's') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" btrssi_hysteresis \n", to.valstr);
err = BCME_BADARG;
goto exit;
}
profile->msw_btrssi_hyster = to.val;
}
} /* end of miniopt while loop */
/* write back modified dctl_prof_t structure */
if ((err = wlu_var_setbuf(wl, "btc_dynctl", profile,
sizeof(dctl_prof_t)) < 0)) {
printf("btc_dynctl: fail to set %d\n", err);
}
} else { /* display current dynctl profile */
int i;
profile = ptr;
/* get: just display the current profile values */
printf(" === btc dynctl: ===\n");
printf("DYNCTL(0x%x): %s, Desense:%s, Mode:%s\n",
profile->flags,
IS_DYNCTL_ON(profile)?"On":"Off",
IS_DESENSE_ON(profile)?"On":"Off",
IS_MSWITCH_ON(profile)?"On":"Off");
printf("dflt_dsns_level:%d\n", profile->dflt_dsns_level);
printf("low_dsns_level:%d\n", profile->low_dsns_level);
printf("mid_dsns_level:%d\n", profile->mid_dsns_level);
printf("high_dsns_level:%d\n", profile->high_dsns_level);
printf("btrssi msw hysteresis %d dBm\n", profile->msw_btrssi_hyster);
printf("default_btc_mode:%d\n", profile->default_btc_mode);
printf("--- coex mode switch data table "
"msw_rows:%d ---\n", profile->msw_rows);
for (i = 0; i < MIN(DCTL_TROWS_MAX, profile->msw_rows); i++) {
printf("row:%d: btcmode:%d, bt_pwr:%d, bt_rssi:%d,"
" wl_rssi_high:%d, wl_rssi_low:%d\n",
i,
profile->msw_data[i].mode,
profile->msw_data[i].bt_pwr,
profile->msw_data[i].bt_rssi,
profile->msw_data[i].wl_rssi_high,
profile->msw_data[i].wl_rssi_low);
}
printf("--- wl desense data table "
"dsns_rows:%d ---\n", profile->dsns_rows);
for (i = 0; i < MIN(DCTL_TROWS_MAX, profile->dsns_rows); i++) {
printf("row:%d: btcmode:%d, bt_pwr:%d, bt_rssi:%d,"
" wl_rssi_high:%d, wl_rssi_low:%d\n",
i,
profile->dsns_data[i].mode,
profile->dsns_data[i].bt_pwr,
profile->dsns_data[i].bt_rssi,
profile->dsns_data[i].wl_rssi_high,
profile->dsns_data[i].wl_rssi_low);
}
profile = NULL;
}
exit:
if (profile)
free(profile);
return err;
}
/*
* get dynamic BTCOEX status
*/
static int
wl_btcoex_dynctl_status(void *wl, cmd_t *cmd, char **argv)
{
dynctl_status_t status;
void *ptr;
int err;
UNUSED_PARAMETER(cmd);
if (*++argv != NULL) {
fprintf(stderr, "command doesn't accept any params\n");
return BCME_USAGE_ERROR;
}
/* get current profile */
if ((err = wlu_var_getbuf(wl,
"btc_dynctl_status", NULL, 0, &ptr) < 0)) {
printf("btc_dynctl: getbuf ioctl failed\n");
return err;
}
/* copy the status from ioctl buffer */
bcopy(ptr, &status, sizeof(dynctl_status_t));
printf("--- btc dynctl status ---:\n");
printf("simulation mode:%s\n", status.sim_on?"On":"Off");
printf("bt_pwr_shm %x\n", dtoh16(status.bt_pwr_shm));
printf("bt_pwr:%d dBm\n", status.bt_pwr);
printf("bt_rssi:%d dBm\n", status.bt_rssi);
printf("wl_rssi:%d dBm\n", status.wl_rssi);
printf("dsns_level:%d\n", status.dsns_level);
printf("btc_mode:%d\n", status.btc_mode);
return err;
}
/*
* get/set dynamic BTCOEX simulation mode
*/
static int
wl_btcoex_dynctl_sim(void *wl, cmd_t *cmd, char **argv)
{
int err = 0, opt_err;
miniopt_t to;
void *ptr;
dynctl_sim_t *sim = NULL;
UNUSED_PARAMETER(cmd);
/* get current profile */
if ((err = wlu_var_getbuf(wl,
"btc_dynctl_sim", NULL, 0, &ptr) < 0)) {
printf("btc_dynctl: getbuf ioctl failed\n");
return err;
}
if (*++argv) {
/* some arguments are present, do set or help */
if (argv[0][0] == 'h' ||
argv[0][0] == '?') {
err = BCME_USAGE_ERROR;
goto exit;
}
sim = calloc(1, sizeof(dynctl_sim_t));
if (sim == NULL)
return BCME_NOMEM;
/* copy current ioctl buf into local var */
bcopy(ptr, sim, sizeof(dynctl_sim_t));
/* 1st parameter is on/off ( 0 | 1) */
if (*argv[0] == '1') {
/* sim enabled */
sim->sim_on = TRUE;
}
else if (*argv[0] == '0') {
/* sim disabled */
sim->sim_on = FALSE;
}
++argv;
miniopt_init(&to, __FUNCTION__, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto exit;
}
argv += to.consumed;
if (to.opt == 'b') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" sim_btpwr\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
sim->btpwr = to.val;
}
if (to.opt == 'r') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" sim_btrssi\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
sim->btrssi = to.val;
}
if (to.opt == 'w') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" sim_wlrssi\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
sim->wlrssi = to.val;
}
}
/* write back modified structure */
if ((err = wlu_var_setbuf(wl, "btc_dynctl_sim", sim,
sizeof(dynctl_sim_t)) < 0)) {
printf("%s: failed to set %d\n", cmd->name, err);
}
} else {
/* get */
sim = ptr;
printf("btc dynctl simulation mode:%s\n BT pwr:%ddBm\n"
" BT rssi:%ddBm\n WL ssi:%ddBm\n",
sim->sim_on?"On":"Off",
sim->btpwr, sim->btrssi, sim->wlrssi);
sim = NULL;
}
exit:
if (sim)
free(sim);
return err;
}