| /* |
| * wl ht 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_ht.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 <miniopt.h> |
| |
| static cmd_func_t wl_bw_cap; |
| static cmd_func_t wl_nrate; |
| static cmd_func_t wl_cur_mcsset; |
| static cmd_func_t wl_txmcsset; |
| static cmd_func_t wl_rxmcsset; |
| |
| static cmd_t wl_ht_cmds[] = { |
| { "bw_cap", wl_bw_cap, WLC_GET_VAR, WLC_SET_VAR, |
| "Get/set the per-band bandwidth.\n" |
| "Usage: wl bw_cap <2g|5g> [<cap>]\n" |
| "\t2g|5g - Band: 2.4GHz or 5GHz respectively\n" |
| "cap:\n" |
| "\t0x1 - 20MHz\n" |
| "\t0x3 - 20/40MHz\n" |
| "\t0x7 - 20/40/80MHz\n" |
| "\t0xf - 20/40/80/160MHz\n" |
| "\t0xff - Unrestricted" }, |
| { "cur_mcsset", wl_cur_mcsset, WLC_GET_VAR, -1, |
| "Get the current mcs set" |
| }, |
| { "mimo_ps", wl_varint, WLC_GET_VAR, WLC_SET_VAR, |
| "get/set mimo power save mode, (0=Dont send MIMO, 1=proceed MIMO with RTS, 2=N/A, " |
| "3=No restriction)"}, |
| { "ofdm_txbw", wl_varint, WLC_GET_VAR, WLC_SET_VAR, |
| "get/set ofdm txbw (2=20Mhz(lower), 3=20Mhz upper, 4(not allowed), 5=40Mhz dup)"}, |
| { "cck_txbw", wl_varint, WLC_GET_VAR, WLC_SET_VAR, |
| "get/set cck txbw (2=20Mhz(lower), 3=20Mhz upper)"}, |
| { "frameburst", wl_int, WLC_GET_FAKEFRAG, WLC_SET_FAKEFRAG, |
| "Disable/Enable frameburst mode" }, |
| { "nrate", wl_nrate, WLC_GET_VAR, WLC_SET_VAR, |
| "\"auto\" to clear a rate override, or:\n" |
| "-r legacy rate (CCK, OFDM)\n" |
| "-m HT MCS index\n" |
| "-s stf mode (0=SISO,1=CDD,2=STBC,3=SDM)\n" |
| "-w Override MCS only to support STA's with/without STBC capability"}, |
| { "mimo_txbw", wl_varint, WLC_GET_VAR, WLC_SET_VAR, |
| "get/set mimo txbw (2=20Mhz(lower), 3=20Mhz upper, 4=40Mhz, 4=40Mhz(DUP)\n" |
| "\t6=80Mhz(20LL), 7=80Mhz(20LU), 8=80Mhz(20UL), 9=80Mhz(20UU)\n" |
| "\t10=80Mhz(40L), 11=80Mhz(40U), 12=80Mhz)"}, |
| { "txmcsset", wl_txmcsset, WLC_GET_VAR, -1, "get Transmit MCS rateset for 11N device"}, |
| { "rxmcsset", wl_rxmcsset, WLC_GET_VAR, -1, "get Receive MCS rateset for 11N device"}, |
| { NULL, NULL, 0, 0, NULL } |
| }; |
| |
| static char *buf; |
| |
| /* IOCtl version read from targeted driver */ |
| static int ioctl_version; |
| |
| |
| /* module initialization */ |
| void |
| wluc_ht_module_init(void) |
| { |
| (void)g_swap; |
| |
| /* get global ioctl_version */ |
| ioctl_version = wl_get_ioctl_version(); |
| |
| /* get the global buf */ |
| buf = wl_get_buf(); |
| |
| /* register ht commands */ |
| wl_module_cmds_register(wl_ht_cmds); |
| } |
| |
| /* Format a ratespec for "nrate" output |
| * Will handle both current wl_ratespec and legacy (ioctl_version 1) nrate ratespec |
| */ |
| static void |
| wl_nrate_print(uint32 rspec) |
| { |
| const char * rspec_auto = "auto"; |
| uint encode, rate, txexp = 0, bw_val; |
| const char* stbc = ""; |
| const char* ldpc = ""; |
| const char* sgi = ""; |
| const char* bw = ""; |
| int stf; |
| |
| if (rspec == 0) { |
| encode = WL_RSPEC_ENCODE_RATE; |
| } else if (ioctl_version == 1) { |
| encode = (rspec & OLD_NRATE_MCS_INUSE) ? WL_RSPEC_ENCODE_HT : WL_RSPEC_ENCODE_RATE; |
| stf = (int)((rspec & OLD_NRATE_STF_MASK) >> OLD_NRATE_STF_SHIFT); |
| rate = (rspec & OLD_NRATE_RATE_MASK); |
| |
| if (rspec & OLD_NRATE_OVERRIDE) { |
| if (rspec & OLD_NRATE_OVERRIDE_MCS_ONLY) |
| rspec_auto = "fixed mcs only"; |
| else |
| rspec_auto = "fixed"; |
| } |
| } else { |
| int siso; |
| encode = (rspec & WL_RSPEC_ENCODING_MASK); |
| rate = (rspec & WL_RSPEC_RATE_MASK); |
| txexp = (rspec & WL_RSPEC_TXEXP_MASK) >> WL_RSPEC_TXEXP_SHIFT; |
| stbc = ((rspec & WL_RSPEC_STBC) != 0) ? " stbc" : ""; |
| ldpc = ((rspec & WL_RSPEC_LDPC) != 0) ? " ldpc" : ""; |
| sgi = ((rspec & WL_RSPEC_SGI) != 0) ? " sgi" : ""; |
| bw_val = (rspec & WL_RSPEC_BW_MASK); |
| |
| if (bw_val == WL_RSPEC_BW_20MHZ) { |
| bw = "bw20"; |
| } else if (bw_val == WL_RSPEC_BW_40MHZ) { |
| bw = "bw40"; |
| } else if (bw_val == WL_RSPEC_BW_80MHZ) { |
| bw = "bw80"; |
| } else if (bw_val == WL_RSPEC_BW_160MHZ) { |
| bw = "bw160"; |
| } |
| else if (bw_val == WL_RSPEC_BW_10MHZ) { |
| bw = "bw10"; |
| } else if (bw_val == WL_RSPEC_BW_5MHZ) { |
| bw = "bw5"; |
| } else if (bw_val == WL_RSPEC_BW_2P5MHZ) { |
| bw = "bw2.5"; |
| } |
| |
| /* initialize stf mode to an illegal value and |
| * fix to a backward compatable value if possible |
| */ |
| stf = -1; |
| /* for stf calculation, determine if the rate is single stream. |
| * Legacy rates WL_RSPEC_ENCODE_RATE are single stream, and |
| * HT rates for mcs 0-7 are single stream |
| */ |
| siso = (encode == WL_RSPEC_ENCODE_RATE) || |
| ((encode == WL_RSPEC_ENCODE_HT) && rate < 8); |
| |
| /* calc a value for nrate stf mode */ |
| if (txexp == 0) { |
| if ((rspec & WL_RSPEC_STBC) && siso) { |
| /* STF mode STBC */ |
| stf = OLD_NRATE_STF_STBC; |
| } else { |
| /* STF mode SISO or SDM */ |
| stf = (siso) ? OLD_NRATE_STF_SISO : OLD_NRATE_STF_SDM; |
| } |
| } else if (txexp == 1 && siso) { |
| /* STF mode CDD */ |
| stf = OLD_NRATE_STF_CDD; |
| } |
| |
| if (rspec & WL_RSPEC_OVERRIDE_RATE) { |
| rspec_auto = "fixed"; |
| } |
| } |
| |
| if (encode == WL_RSPEC_ENCODE_RATE) { |
| if (rspec == 0) { |
| printf("auto\n"); |
| } else { |
| printf("legacy rate %d%s Mbps stf mode %d %s\n", |
| rate/2, (rate % 2)?".5":"", stf, rspec_auto); |
| } |
| } else if (encode == WL_RSPEC_ENCODE_HT) { |
| printf("mcs index %d stf mode %d %s\n", |
| rate, stf, rspec_auto); |
| } else { |
| uint vht = (rspec & WL_RSPEC_VHT_MCS_MASK); |
| uint Nss = (rspec & WL_RSPEC_VHT_NSS_MASK) >> WL_RSPEC_VHT_NSS_SHIFT; |
| printf("vht mcs %d Nss %d Tx Exp %d %s%s%s%s %s\n", |
| vht, Nss, txexp, bw, stbc, ldpc, sgi, rspec_auto); |
| } |
| } |
| |
| static int |
| wl_nrate(void *wl, cmd_t *cmd, char **argv) |
| { |
| miniopt_t to; |
| const char* fn_name = "wl_nrate"; |
| bool mcs_set = FALSE, legacy_set = FALSE, stf_set = FALSE; |
| bool mcs_only = FALSE; |
| int err, opt_err; |
| uint32 val = 0; |
| uint32 rate = 0; |
| uint32 nrate = 0; |
| uint32 mcs = 0; |
| uint stf = 0; /* (0=SISO,1=CDD,2=STBC,3=SDM) */ |
| |
| /* toss the command name */ |
| argv++; |
| |
| if (!*argv) { |
| if (cmd->get < 0) |
| return -1; |
| if ((err = wlu_iovar_getint(wl, "nrate", (int*)&val)) < 0) |
| return err; |
| |
| /* print a user readable line for the nrate rspec */ |
| wl_nrate_print(val); |
| |
| return 0; |
| } |
| |
| /* check for a single argument of "auto" or -1 */ |
| if ((!strcmp(argv[0], "auto") || !strcmp(argv[0], "-1")) && |
| argv[1] == NULL) { |
| /* clear the nrate override */ |
| err = wlu_iovar_setint(wl, "nrate", 0); |
| goto exit; |
| } |
| |
| 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 == 'r') { |
| if (!to.good_int) { |
| /* special case check for "-r 5.5" */ |
| if (!strcmp(to.valstr, "5.5")) { |
| to.val = 11; |
| } else { |
| fprintf(stderr, |
| "%s: could not parse \"%s\" as a rate value\n", |
| fn_name, to.valstr); |
| err = BCME_BADARG; |
| goto exit; |
| } |
| } else |
| to.val = to.val*2; |
| if (mcs_set) { |
| fprintf(stderr, "%s: cannot use -r and -m\n", fn_name); |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| legacy_set = TRUE; |
| rate = to.val; |
| } |
| if (to.opt == 'm') { |
| if (!to.good_int) { |
| fprintf(stderr, |
| "%s: could not parse \"%s\" as an int for mcs\n", |
| fn_name, to.valstr); |
| err = BCME_BADARG; |
| goto exit; |
| } |
| if (legacy_set) { |
| fprintf(stderr, "%s: cannot use -r and -m\n", fn_name); |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| mcs_set = TRUE; |
| mcs = to.val; |
| } |
| if (to.opt == 's') { |
| if (!to.good_int) { |
| fprintf(stderr, |
| "%s: could not parse \"%s\" as an int for stf mode\n", |
| fn_name, to.valstr); |
| err = BCME_BADARG; |
| goto exit; |
| } |
| stf = to.val; |
| stf_set = TRUE; |
| } |
| if (to.opt == 'w') { |
| mcs_only = TRUE; |
| } |
| } |
| |
| if ((mcs_only && !mcs_set) || (mcs_only && (stf_set || legacy_set))) { |
| fprintf(stderr, "%s: can use -w only with -m\n", fn_name); |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| if (!stf_set) { |
| if (legacy_set) |
| stf = OLD_NRATE_STF_SISO; /* SISO */ |
| else if (mcs_set) { |
| if (GET_11N_MCS_NSS(mcs & OLD_NRATE_RATE_MASK) == 1) |
| stf = OLD_NRATE_STF_SISO; /* SISO */ |
| else |
| stf = OLD_NRATE_STF_SDM; /* SDM */ |
| } |
| } |
| |
| if (!legacy_set && !mcs_set) { |
| fprintf(stderr, "%s: you need to set a legacy or mcs rate\n", fn_name); |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| if (ioctl_version == 1) { |
| if (legacy_set) { |
| nrate = rate; |
| } else { |
| nrate = mcs; |
| nrate |= OLD_NRATE_MCS_INUSE; |
| if (mcs_only) { |
| nrate |= OLD_NRATE_OVERRIDE_MCS_ONLY; |
| } |
| } |
| |
| nrate |= (stf << OLD_NRATE_STF_SHIFT) & OLD_NRATE_STF_MASK; |
| } else { |
| uint tx_exp = 0; |
| |
| /* set the ratespec encoding type and basic rate value */ |
| if (legacy_set) { |
| nrate = WL_RSPEC_ENCODE_RATE; /* 11abg */ |
| nrate |= rate; |
| } else { |
| nrate = WL_RSPEC_ENCODE_HT; /* 11n HT */ |
| nrate |= mcs; |
| } |
| |
| /* decode nrate stf value into tx expansion and STBC */ |
| if (stf == OLD_NRATE_STF_CDD) { |
| tx_exp = 1; |
| } else if (stf == OLD_NRATE_STF_STBC) { |
| nrate |= WL_RSPEC_STBC; |
| } |
| |
| nrate |= (tx_exp << WL_RSPEC_TXEXP_SHIFT); |
| } |
| |
| err = wlu_iovar_setint(wl, "nrate", (int)nrate); |
| |
| exit: |
| return err; |
| } /* wl_nrate */ |
| |
| /* Set per-band bandwidth */ |
| static int wl_bw_cap(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err = 0; |
| struct { |
| uint32 band; |
| uint32 bw_cap; |
| } param = { 0, 0 }; |
| char *s = NULL; |
| void *ptr = NULL; |
| |
| /* Skip the command name */ |
| argv++; |
| |
| if (*argv) { |
| if (!strcmp(*argv, "a") || !strcmp(*argv, "5") || !strcmp(*argv, "5g")) { |
| param.band = WLC_BAND_5G; |
| } else if (!strcmp(*argv, "b") || !strcmp(*argv, "2") || !strcmp(*argv, "2g")) { |
| param.band = WLC_BAND_2G; |
| } else { |
| fprintf(stderr, |
| "%s: invalid band %s\n", |
| cmd->name, *argv); |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| argv++; |
| |
| if (*argv) { |
| /* Optional 2nd arg is used to set the bandwidth cap */ |
| s = NULL; |
| |
| param.bw_cap = (uint32) strtoul(*argv, &s, 0); |
| if (s && *s != '\0') { |
| fprintf(stderr, "%s: invalid bandwidth '%s'\n", |
| cmd->name, *argv); |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| } |
| } else { |
| fprintf(stderr, "%s: band unspecified\n", cmd->name); |
| err = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| if (param.bw_cap == 0) { |
| if ((err = wlu_var_getbuf(wl, cmd->name, ¶m, sizeof(param), &ptr)) < 0) |
| return err; |
| |
| printf("0x%x\n", *((uint32 *)ptr)); |
| } else { |
| err = wlu_var_setbuf(wl, cmd->name, ¶m, sizeof(param)); |
| } |
| |
| exit: |
| return err; |
| } |
| |
| static int |
| wl_cur_mcsset(void *wl, cmd_t *cmd, char **argv) |
| { |
| int ret; |
| |
| UNUSED_PARAMETER(cmd); |
| UNUSED_PARAMETER(argv); |
| |
| memset(buf, 0, WLC_IOCTL_SMLEN); |
| ret = wlu_iovar_get(wl, "cur_mcsset", &buf[0], MCSSET_LEN); |
| if (ret < 0) |
| return ret; |
| |
| wl_print_mcsset(buf); |
| |
| return ret; |
| } |
| |
| static int |
| wl_txmcsset(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| |
| if ((err = wl_var_get(wl, cmd, argv)) < 0) |
| return err; |
| wl_print_mcsset(buf); |
| |
| return err; |
| } |
| |
| static int |
| wl_rxmcsset(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| |
| if ((err = wl_var_get(wl, cmd, argv)) < 0) |
| return err; |
| |
| wl_print_mcsset(buf); |
| |
| return err; |
| } |