| /* |
| * wl btcx 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_btcx.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 <miniopt.h> |
| #include "wlu_common.h" |
| #include "wlu.h" |
| |
| static cmd_func_t wl_btc_profile; |
| |
| #define WL_BTCPROFILE_USAGE \ |
| "Usage:\n" \ |
| "\t -p profile_index [-e mode_strong_wl_bt_rssi] [-f mode_weak_wl_rssi]" \ |
| " [-g mode_weak_bt_rssi] [-h mode_weak_wl_bt_rssi]\n" \ |
| "\t [-W mode_wl_hi_lo_rssi_thresh] [-w mode_wl_lo_hi_rssi_thresh]" \ |
| " [-B mode_bt_hi_lo_rssi_thresh] [-b mode_bt_lo_hi_rssi_thresh]\n" \ |
| "\t [-D desense_wl_hi_lo_rssi_thresh] [-d desense_wl_lo_hi_rssi_thresh]\n" \ |
| "\t [-A ack_pwr_wl_hi_lo_rssi_thresh] [-a ack_pwr_wl_lo_hi_rssi_thresh]\n" \ |
| "\t [-T tx_pwr_wl_hi_lo_rssi_thresh] [-t tx_pwr_wl_lo_hi_rssi_thresh]\n" \ |
| "\t [-l desense_level[0] desense_level[1] desense_level[2]] : desense level per chain\n" \ |
| "\t [-X ack_pwr_strong_wl[0] ack_pwr_strong_wl[1] ack_pwr_strong_wl[2]] :" \ |
| " ACK power per chain at strong RSSI\n" \ |
| "\t [-x ack_pwr_weak_wl[0] ack_pwr_weak_wl[1] ack_pwr_weak_wl[2]] :" \ |
| " ACK power per chain at weak RSSI\n" \ |
| "\t [-Y tx_pwr_strong_wl[0] tx_pwr_strong_wl[1] tx_pwr_strong_wl[2]] :" \ |
| " Tx power per chain at strong RSSI\n" \ |
| "\t [-y tx_pwr_weak_wl[0] tx_pwr_weak_wl[1] tx_pwr_weak_wl[2]] :" \ |
| " Tx power per chain at weak RSSI\n\n" |
| |
| static cmd_t wl_btcx_cmds[] = { |
| { "btc_params", wlu_reg2args, WLC_GET_VAR, WLC_SET_VAR, "g/set BT Coex parameters"}, |
| { "btc_flags", wlu_reg2args, WLC_GET_VAR, WLC_SET_VAR, "g/set BT Coex flags"}, |
| { "btc_profile", wl_btc_profile, WLC_GET_VAR, WLC_SET_VAR, WL_BTCPROFILE_USAGE}, |
| { NULL, NULL, 0, 0, NULL } |
| }; |
| |
| static char *buf; |
| |
| /* module initialization */ |
| void |
| wluc_btcx_module_init(void) |
| { |
| (void)g_swap; |
| |
| /* get the global buf */ |
| buf = wl_get_buf(); |
| |
| /* register btcx commands */ |
| wl_module_cmds_register(wl_btcx_cmds); |
| } |
| |
| static void |
| wl_btc_print_profile(wlc_btcx_profile_v1_t *pbtcx_profile) |
| { |
| int i; |
| uint8 chain_attr_count = pbtcx_profile->chain_attr_count; |
| /* Basic profile information */ |
| printf("Profile Version: %u\n" |
| "Profile Size: %u\n" |
| "Profile Initialized: %s\n" |
| "Number of Tx chains: %u\n" |
| "Profile Index: %u\n" |
| "BTC mode under strong WLAN and BT RSSI: %u\n" |
| "BTC mode under weak WLAN RSSI: %u\n" |
| "BTC mode under weak BT RSSI: %u\n" |
| "BTC mode under weak WLAN and BT RSSI: %u\n", |
| pbtcx_profile->version, |
| pbtcx_profile->length, |
| pbtcx_profile->init ? "Yes" : "No", |
| pbtcx_profile->chain_attr_count, |
| pbtcx_profile->profile_index, |
| pbtcx_profile->mode_strong_wl_bt, |
| pbtcx_profile->mode_weak_wl, |
| pbtcx_profile->mode_weak_bt, |
| pbtcx_profile->mode_weak_wl_bt); |
| /* Threshold information */ |
| printf("WLAN strong to weak RSSI threshold for mode change: %d\n" |
| "WLAN weak to strong RSSI threshold for mode change: %d\n" |
| "BT strong to weak RSSI threshold for mode change: %d\n" |
| "BT weak to strong RSSI threshold for mode change: %d\n" |
| "WLAN strong to weak RSSI threshold for desense: %d\n" |
| "WLAN weak to strong RSSI threshold for desense: %d\n" |
| "WLAN strong to weak RSSI threshold for ACK power: %d\n" |
| "WLAN weak to strong RSSI threshold for ACK power: %d\n" |
| "WLAN strong to weak RSSI threshold for Tx power: %d\n" |
| "WLAN weak to strong RSSI threshold for Tx power: %d\n", |
| pbtcx_profile->mode_wl_hi_lo_rssi_thresh, |
| pbtcx_profile->mode_wl_lo_hi_rssi_thresh, |
| pbtcx_profile->mode_bt_hi_lo_rssi_thresh, |
| pbtcx_profile->mode_bt_lo_hi_rssi_thresh, |
| pbtcx_profile->desense_wl_hi_lo_rssi_thresh, |
| pbtcx_profile->desense_wl_lo_hi_rssi_thresh, |
| pbtcx_profile->ack_pwr_wl_hi_lo_rssi_thresh, |
| pbtcx_profile->ack_pwr_wl_lo_hi_rssi_thresh, |
| pbtcx_profile->tx_pwr_wl_hi_lo_rssi_thresh, |
| pbtcx_profile->tx_pwr_wl_lo_hi_rssi_thresh); |
| /* Desense and Power values per core */ |
| printf("--- Desense and power limits per core ---\n"); |
| for (i = 0; i < chain_attr_count; i++) { |
| printf("Core %u:\t desense level:%d\t" |
| "ACK power strong RSSI:%d\t" |
| "ACK power weak RSSI:%d\t" |
| "Tx power strong RSSI:%d\t" |
| "Tx power weak RSSI:%d\n", i, |
| pbtcx_profile->chain_attr[i].desense_level, |
| pbtcx_profile->chain_attr[i].ack_pwr_strong_rssi, |
| pbtcx_profile->chain_attr[i].ack_pwr_weak_rssi, |
| pbtcx_profile->chain_attr[i].tx_pwr_strong_rssi, |
| pbtcx_profile->chain_attr[i].tx_pwr_weak_rssi); |
| } |
| } |
| |
| /* This IOVAR does a read-modify-write of the user specified profile */ |
| /* Since the utility does not know about the number of Tx chains on the device */ |
| /* We use the MAX_UCM_CHAINS to size the variable length array to pull the IO buffer */ |
| /* Once the buffer is modified by the user, the wlu layer passes the buffer back to driver */ |
| /* This buffer is sized by the number of TX chains (chain_attr_count) on the device */ |
| static int |
| wl_btc_profile(void *wl, cmd_t *cmd, char **argv) |
| { |
| void *ptr = NULL; |
| wlc_btcx_profile_v1_t *pbtcx_profile = NULL; |
| wlc_btcx_chain_attr_t *chain_attr; |
| uint8 chain_attr_count; |
| int8 val; |
| int err, idx, opt_err, ncon; |
| char *endptr = NULL; |
| miniopt_t to; |
| size_t ucm_prof_sz = sizeof(*pbtcx_profile) |
| + MAX_UCM_CHAINS*sizeof(pbtcx_profile->chain_attr[1]); |
| |
| /* discard the command name */ |
| ++argv; |
| miniopt_init(&to, __FUNCTION__, NULL, FALSE); |
| |
| /* First extract the profile index */ |
| opt_err = miniopt(&to, argv); |
| if (opt_err == 0) { |
| if (to.opt == 'p') { |
| if (!to.good_int) { |
| fprintf(stderr, |
| "%s: could not parse \"%s\" as an int for" |
| " the profile index\n", __FUNCTION__, to.valstr); |
| err = BCME_BADARG; |
| goto exit; |
| } |
| if (to.uval >= MAX_UCM_PROFILES) { |
| fprintf(stderr, "Profile selected is out of range.\n"); |
| err = BCME_RANGE; |
| goto exit; |
| } else { |
| idx = to.uval; |
| } |
| } else { |
| fprintf(stderr, "Invalid first option. " |
| "Please select a profile first.\n"); |
| err = BCME_BADOPTION; |
| goto exit; |
| } |
| } else { |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| /* Get the relevant btc profile */ |
| if (ucm_prof_sz > WLC_IOCTL_SMLEN) { |
| err = BCME_BUFTOOSHORT; |
| goto exit; |
| } |
| |
| if ((err = wlu_var_getbuf_sm(wl, cmd->name, &idx, sizeof(idx), &ptr)) < 0) { |
| fprintf(stderr, "%s: getbuf ioctl failed\n", __FUNCTION__); |
| err = BCME_ERROR; |
| goto exit; |
| } |
| |
| /* WL utility will pull MAX_UCM_CHAINS size variable array */ |
| /* however the FW mallocs only chain_attr_count size variable array */ |
| pbtcx_profile = calloc(1, ucm_prof_sz); |
| if (pbtcx_profile == NULL) { |
| err = BCME_NOMEM; |
| goto exit; |
| } |
| memcpy(pbtcx_profile, ptr, ucm_prof_sz); |
| |
| if (pbtcx_profile->version != UCM_PROFILE_VERSION_1) { |
| err = BCME_VERSION; |
| goto exit; |
| } |
| |
| chain_attr_count = pbtcx_profile->chain_attr_count; |
| if (chain_attr_count > MAX_UCM_CHAINS) { |
| fprintf(stderr, "number of Tx chains (%u) on the device exceeds" |
| " the maximum supported chains (%u)\n", chain_attr_count, |
| MAX_UCM_CHAINS); |
| err = BCME_ERROR; |
| goto exit; |
| } |
| |
| argv += to.consumed; |
| chain_attr = pbtcx_profile->chain_attr; |
| |
| /* Get: no more arguments */ |
| if (*argv == NULL) { |
| wl_btc_print_profile(pbtcx_profile); |
| } else { |
| /* Set: more arguments to be parsed */ |
| while ((opt_err = miniopt(&to, argv)) != -1) { |
| if (opt_err == 1) { |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| if (!to.good_int) { |
| fprintf(stderr, "could not parse %s as an int for" |
| " option -%c\n", to.valstr, to.opt); |
| err = BCME_BADARG; |
| goto exit; |
| } |
| |
| switch (to.opt) { |
| case 'e' : |
| pbtcx_profile->mode_strong_wl_bt = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'f' : |
| pbtcx_profile->mode_weak_wl = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'g' : |
| pbtcx_profile->mode_weak_bt = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'h' : |
| pbtcx_profile->mode_weak_wl_bt = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'W' : |
| pbtcx_profile->mode_wl_hi_lo_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'w' : |
| pbtcx_profile->mode_wl_lo_hi_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'B' : |
| pbtcx_profile->mode_bt_hi_lo_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'b' : |
| pbtcx_profile->mode_bt_lo_hi_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'D' : |
| pbtcx_profile->desense_wl_hi_lo_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'd' : |
| pbtcx_profile->desense_wl_lo_hi_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'A' : |
| pbtcx_profile->ack_pwr_wl_hi_lo_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'a' : |
| pbtcx_profile->ack_pwr_wl_lo_hi_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'T' : |
| pbtcx_profile->tx_pwr_wl_hi_lo_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 't' : |
| pbtcx_profile->tx_pwr_wl_lo_hi_rssi_thresh = to.val; |
| ncon = to.consumed; |
| break; |
| |
| case 'l' : |
| argv++; |
| for (idx = 0; idx < chain_attr_count; idx++) { |
| if (*(argv+idx) != NULL) { |
| val = (int8)strtoul(*(argv+idx), |
| &endptr, 0); |
| if (*endptr != '\0') { |
| err = BCME_BADOPTION; |
| goto exit; |
| } |
| chain_attr[idx].desense_level = val; |
| } else { |
| err = BCME_BADLEN; |
| goto exit; |
| } |
| } |
| ncon = idx; |
| break; |
| |
| case 'X' : |
| argv++; |
| for (idx = 0; idx < chain_attr_count; idx++) { |
| if (*(argv+idx) != NULL) { |
| val = (int8)strtoul(*(argv+idx), |
| &endptr, 0); |
| if (*endptr != '\0') { |
| err = BCME_BADOPTION; |
| goto exit; |
| } |
| chain_attr[idx].ack_pwr_strong_rssi = val; |
| } else { |
| err = BCME_BADLEN; |
| goto exit; |
| } |
| } |
| ncon = idx; |
| break; |
| |
| case 'x' : |
| argv++; |
| for (idx = 0; idx < chain_attr_count; idx++) { |
| if (*(argv+idx) != NULL) { |
| val = (int8)strtoul(*(argv+idx), |
| &endptr, 0); |
| if (*endptr != '\0') { |
| err = BCME_BADOPTION; |
| goto exit; |
| } |
| chain_attr[idx].ack_pwr_weak_rssi = val; |
| } else { |
| err = BCME_BADLEN; |
| goto exit; |
| } |
| } |
| ncon = idx; |
| break; |
| |
| case 'Y' : |
| argv++; |
| for (idx = 0; idx < chain_attr_count; idx++) { |
| if (*(argv+idx) != NULL) { |
| val = (int8)strtoul(*(argv+idx), |
| &endptr, 0); |
| if (*endptr != '\0') { |
| err = BCME_BADOPTION; |
| goto exit; |
| } |
| chain_attr[idx].tx_pwr_strong_rssi = val; |
| } else { |
| err = BCME_BADLEN; |
| goto exit; |
| } |
| } |
| ncon = idx; |
| break; |
| |
| case 'y' : |
| argv++; |
| for (idx = 0; idx < chain_attr_count; idx++) { |
| if (*(argv+idx) != NULL) { |
| val = (int8)strtoul(*(argv+idx), |
| &endptr, 0); |
| if (*endptr != '\0') { |
| err = BCME_BADOPTION; |
| goto exit; |
| } |
| chain_attr[idx].tx_pwr_weak_rssi = val; |
| } else { |
| err = BCME_BADLEN; |
| goto exit; |
| } |
| } |
| ncon = idx; |
| break; |
| |
| default : |
| fprintf(stderr, "Invalid option %c\n", to.opt); |
| } |
| argv += ncon; |
| } |
| |
| /* write back using var length array size of pbtcx_profile->chain_attr_count */ |
| if ((err = wlu_var_setbuf(wl, cmd->name, pbtcx_profile, sizeof(*pbtcx_profile) |
| + chain_attr_count*sizeof(pbtcx_profile->chain_attr[1])) < 0)) { |
| fprintf(stderr, "%s: fail to set %d\n", __FUNCTION__, err); |
| err = BCME_ERROR; |
| goto exit; |
| } |
| } |
| err = BCME_OK; |
| |
| exit: |
| if (pbtcx_profile != NULL) |
| free(pbtcx_profile); |
| return err; |
| } |