| /* |
| * wl rmc 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_relmcast.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_mcast_ackmac, wl_mcast_ackreq, wl_mcast_status; |
| static cmd_func_t wl_mcast_actf_time, wl_mcast_rssi_thresh, wl_mcast_stats; |
| static cmd_func_t wl_mcast_rssi_delta, wl_mcast_vsie, wl_mcast_ar_timeout; |
| |
| static cmd_t wl_rmc_cmds[] = { |
| { "rmc_ackmac", wl_mcast_ackmac, WLC_GET_VAR, WLC_SET_VAR, |
| "Set/Get ACK required multicast mac address\n" |
| "\tusage: wl rmc_ackmac -i [index] -t [multicast mac address]" |
| }, |
| { "rmc_ackreq", wl_mcast_ackreq, WLC_GET_VAR, WLC_SET_VAR, |
| "Set/Get ACK rmc_mode 0 disable, 1 enable transmitter, 2 enable initiator \n" |
| "\tusage: wl rmc_ackreq [mode]" |
| }, |
| { "rmc_txrate", wl_phy_rate, WLC_GET_VAR, WLC_SET_VAR, |
| "Set/Get a fixed transmit rate for the reliable multicast:\n" |
| "\tvalid values for 802.11ac are (6, 9, 12, 18, 24, 36, 48, 54)\n" |
| "\t-1 (default) means automatically determine the best rate" |
| }, |
| { "rmc_status", wl_mcast_status, WLC_GET_VAR, -1, |
| "Display reliable multicast client status" |
| }, |
| { "rmc_actf_time", wl_mcast_actf_time, WLC_GET_VAR, WLC_SET_VAR, |
| "Set/Get mcast action frame tx time period in ms\n" |
| "\tusage: wl rmc_actf_time [value]" |
| }, |
| { "rmc_ar_timeout", wl_mcast_ar_timeout, WLC_GET_VAR, WLC_SET_VAR, |
| "Set/Get rmc active receiver timeout in ms\n" |
| "\tusage: wl rmc_ar_timeout [duration in ms]" |
| }, |
| { "rmc_rssi_thresh", wl_mcast_rssi_thresh, WLC_GET_VAR, WLC_SET_VAR, |
| "Set/Get minimum rssi needed for a station to be an active receiver\n" |
| "\tusage: wl rmc_rssi_thresh [value]" |
| }, |
| { "rmc_stats", wl_mcast_stats, WLC_GET_VAR, WLC_SET_VAR, |
| "Display/Clear reliable multicast client statistical counters\n" |
| "\tusage: wl rmc_stats [arg]" |
| }, |
| { "rmc_rssi_delta", wl_mcast_rssi_delta, WLC_GET_VAR, WLC_SET_VAR, |
| "Display/Set RSSI delta to switch receive leader\n" |
| "\tusage: wl rmc_rssi_delta [arg]" |
| }, |
| { "rmc_vsie", wl_mcast_vsie, WLC_GET_VAR, WLC_SET_VAR, |
| "Display/Set vendor specific IE contents\n" |
| "\tusage: wl rmc_vsie [OUI] [Data]" |
| }, |
| { NULL, NULL, 0, 0, NULL } |
| }; |
| |
| static char *buf; |
| |
| /* module initialization */ |
| void |
| wluc_rmc_module_init(void) |
| { |
| /* get the global buf */ |
| buf = wl_get_buf(); |
| |
| /* register rmc commands */ |
| wl_module_cmds_register(wl_rmc_cmds); |
| } |
| |
| static int |
| wl_mcast_ackmac(void *wl, cmd_t *cmd, char **argv) |
| { |
| int ret = 0; |
| int i, err; |
| uint8 num_tr; |
| wl_rmc_entry_t ackmac_params; |
| wl_rmc_entry_table_t tablelist; |
| void *ptr = NULL; |
| wl_rmc_trans_in_network_t *reply = NULL; |
| miniopt_t to; |
| struct ether_addr* ea = NULL; |
| bool index_set = FALSE; bool mac_set = FALSE; |
| |
| /* muticast mac address - ipv4 & ipv6 resp. */ |
| uint8 mc_mac[6] = {0x1, 0x0, 0x5e, 0x7f, 0xff, 0xff}; |
| uint8 mc_mac_ipv6[6] = {0x33, 0x33, 0x0, 0x0, 0x0, 0x0}; |
| |
| /* index will tell us what to do */ |
| |
| if (!*++argv) { |
| /* Get and display all entries in the table */ |
| if ((ret = wlu_var_getbuf(wl, cmd->name, |
| NULL, |
| 0, |
| &ptr)) < 0) { |
| return ret; |
| } |
| reply = (wl_rmc_trans_in_network_t*)ptr; |
| num_tr = reply->num_tr; |
| |
| printf("\n Num of transmitters %02x ", num_tr); |
| printf("\n Transmitter Mac \t AR Mac"); |
| printf("\t rmc_ar_timeout\t amt_idx flag "); |
| |
| for (i = 0; (i < WL_RMC_MAX_NUM_TRS) && (i < num_tr); i++) { |
| printf("\n %s \t", wl_ether_etoa(&reply->trs[i].tr_mac)); |
| printf(" %s \t", wl_ether_etoa(&reply->trs[i].ar_mac)); |
| printf(" %d \t %d %04x", |
| reply->trs[i].artmo, reply->trs[i].amt_idx, reply->trs[i].flag); |
| } |
| |
| printf("\n"); |
| return 0; |
| } |
| |
| miniopt_init(&to, cmd->name, NULL, FALSE); |
| |
| while ((err = miniopt(&to, argv)) != -1) { |
| if (err == 1) { |
| return BCME_USAGE_ERROR; |
| } |
| argv += to.consumed; |
| /* Index for multicast address in RMC table */ |
| if (to.opt == 'i') { |
| /* wl rmc_ackmac -i 8 -t <control multi-cast address> */ |
| /* this is used to change the AF multi-cast address */ |
| if (!to.good_int || ((to.val != 8) && to.val)) { |
| fprintf(stderr, "%s: Invalid mode value\n", cmd->name); |
| err = -1; |
| goto exit; |
| } |
| tablelist.index = to.val; |
| index_set = TRUE; |
| } |
| |
| /* Add multicast address with index "i" to RMC table */ |
| if (to.opt == 't') { |
| if (!wl_ether_atoe(to.valstr, &ackmac_params.addr)) { |
| fprintf(stderr, |
| "%s: could not parse \"%s\" as a MAC address\n", |
| cmd->name, to.valstr); |
| err = -1; |
| goto exit; |
| } |
| mac_set = TRUE; |
| } |
| } |
| |
| if (index_set) { |
| if (!mac_set) { |
| printf("\n Invalid without mac address"); |
| ret = BCME_USAGE_ERROR; |
| } else if (tablelist.index == 8) { |
| /* Index 8 is for changing the destination address of the AF */ |
| /* This multi-cast address is the da of the action frame that */ |
| /* is sent out periodically */ |
| memcpy(&tablelist.entry[0].addr, &ackmac_params.addr, |
| sizeof(struct ether_addr)); |
| |
| /* insert to list */ |
| ea = &tablelist.entry[0].addr; |
| tablelist.entry[0].flag = RELMCAST_ENTRY_OP_ENABLE; |
| |
| /* for ipv4, initial three bytes in mc address are standard & |
| 2 bytes for ipv6 |
| */ |
| if ((!memcmp(ea, mc_mac, 3) && !(ea->octet[3] & 0x80)) || |
| !memcmp(ea, mc_mac_ipv6, 2)) { |
| |
| fprintf(stderr, |
| "\nAdding multi-cast mac %s\n", wl_ether_etoa(ea)); |
| |
| return wlu_var_setbuf(wl, cmd->name, |
| &tablelist, |
| sizeof(wl_rmc_entry_table_t)); |
| |
| } else { |
| |
| fprintf(stderr, "multicast mac started with" |
| "01:00:5e:0... or 33:33:...\n"); |
| ret = BCME_BADARG; |
| |
| } |
| } else { |
| printf("\n Invalid index "); |
| ret = BCME_USAGE_ERROR; |
| } |
| } |
| exit: |
| return ret; |
| } |
| |
| static int |
| wl_mcast_ackreq(void *wl, cmd_t *cmd, char **argv) |
| { |
| const char* fn_name = "wl_mcast_ackreq"; |
| int err, i = 0; |
| uint argc; |
| char *endptr = NULL; |
| void *ptr = NULL; |
| uint8 *reply_mode = NULL; |
| uint8 params_mode, old_mode; |
| wl_relmcast_status_t status; |
| |
| memset(¶ms_mode, 0, sizeof(uint8)); |
| /* toss the command name */ |
| argv++; |
| |
| if ((err = wlu_var_getbuf_sm(wl, cmd->name, ¶ms_mode, |
| sizeof(uint8), &ptr)) != BCME_OK) { |
| fprintf(stderr, "Error getting variable %s\n", argv[0]); |
| return err; |
| } |
| |
| reply_mode = (uint8 *)ptr; |
| old_mode = *reply_mode; |
| |
| if (!*argv) { |
| fprintf(stderr, "%s mode %d \n", fn_name, *reply_mode); |
| return err; |
| } |
| |
| /* arg count */ |
| for (argc = 0; argv[argc]; argc++) |
| ; |
| |
| /* required arg: mode disable, enable or initiator enabled */ |
| if (argc < 1) |
| return -1; |
| |
| /* get the new ackreq mode */ |
| params_mode = strtol(argv[0], &endptr, 0); |
| |
| if ((*endptr != '\0') || (params_mode > WL_RMC_MODE_INITIATOR)) |
| return -1; |
| |
| if (argc > 1) { |
| fprintf(stderr, |
| "%s: could not parse extra argument %s:\n", |
| fn_name, argv[1]); |
| |
| err = -1; |
| goto exit; |
| } |
| |
| if ((err = wlu_var_setbuf(wl, cmd->name, ¶ms_mode, sizeof(uint8))) != BCME_OK) { |
| goto out_of_here; |
| } |
| |
| if (params_mode == WL_RMC_MODE_INITIATOR || |
| ((params_mode == WL_RMC_MODE_RECEIVER) && |
| (old_mode == WL_RMC_MODE_INITIATOR))) { |
| |
| for (i = 0; i <= 10; i++) { |
| /* check status of the RCV bit to make sure ack are receive */ |
| if ((err = wlu_iovar_get(wl, "rmc_status", |
| (void *) &status, |
| (sizeof(wl_relmcast_status_t)))) < 0) { |
| return (err); |
| } |
| |
| if (status.err != (uint16)BCME_NOTREADY) { |
| if (status.err == (uint16)BCME_RXFAIL) { |
| fprintf(stderr, "%s: error in setting mode: no ack receive" |
| "%d tx code %d \n", |
| fn_name, params_mode, status.err); |
| |
| err = -1; |
| } else { |
| err = 0; |
| } |
| goto out_of_here; |
| } |
| |
| /* Allow ample time (by staying in loop) to get ACK |
| for previous TX frame |
| */ |
| { |
| volatile uint16 busycnt = -1; |
| /* There is no system call for sleep that will work for */ |
| /* all os flavors. When there is a common sleep call we */ |
| /* can replace the following busy wait loop */ |
| while (--busycnt) |
| ; |
| busycnt = -1; |
| } |
| } /* for loop */ |
| } |
| out_of_here: |
| if ((err < 0) || (i > 10)) |
| fprintf(stderr, "%s: Error setting %d err %d \n", fn_name, params_mode, err); |
| else |
| fprintf(stderr, "%s: setting %d err %d \n", fn_name, params_mode, err); |
| exit: |
| return 0; |
| } |
| |
| |
| static int |
| wl_mcast_status(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err, i; |
| wl_relmcast_status_t status; |
| |
| if (!*++argv) { |
| /* Get */ |
| if ((err = wlu_iovar_get(wl, cmd->name, (void *) &status, |
| (sizeof(wl_relmcast_status_t)))) < 0) |
| return (err); |
| |
| if (status.ver != WL_RMC_VER) { |
| printf("Wrong Version %d %d\n", WL_RMC_VER, status.ver); |
| } else if (status.num == 0) { |
| printf("No clients associated\n"); |
| } else { |
| for (i = 0; i < status.num; i++) { |
| printf("%s\t%d\t%c%c%c\n", |
| wl_ether_etoa(&status.clients[i].addr), |
| status.clients[i].rssi, |
| ((status.clients[i].flag & |
| WL_RMC_FLAG_ACTIVEACKER) ? 'A' : ' '), |
| ((status.clients[i].flag & |
| WL_RMC_FLAG_INBLACKLIST) ? 'B' : ' '), |
| ((status.clients[i].flag & |
| WL_RMC_FLAG_RELMCAST) ? 'R' : ' ')); |
| } |
| printf("Notification Frame TimePeriod: %d ms\n", status.actf_time); |
| } |
| } else { |
| printf("Cannot set reliable multicast status\n"); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| wl_mcast_actf_time(void *wl, cmd_t *cmd, char **argv) |
| { |
| int val, error = -1; |
| const char *name = cmd->name; |
| |
| /* toss the command name from the args */ |
| argv++; |
| |
| if (!*argv) { |
| error = wlu_iovar_getint(wl, name, &val); |
| if (error < 0) |
| return (error); |
| printf("Action Frame tx time period: %dms\n", val); |
| |
| } else { |
| |
| val = (uint16)strtol(*argv, NULL, 10); /* 10 is for base 10 (decimal) */ |
| |
| if (val >= WL_RMC_ACTF_TIME_MIN && |
| val <= WL_RMC_ACTF_TIME_MAX) { |
| |
| error = wlu_iovar_setint(wl, name, val); |
| |
| } else { |
| |
| printf("\"Out of range\": valid range %dms - %dms\n", |
| WL_RMC_ACTF_TIME_MIN, |
| WL_RMC_ACTF_TIME_MAX); |
| } |
| } |
| return error; |
| } |
| |
| static int |
| wl_mcast_ar_timeout(void *wl, cmd_t *cmd, char **argv) |
| { |
| int val, error = -1; |
| const char *name = cmd->name; |
| |
| /* toss the command name from the args */ |
| argv++; |
| |
| if (!*argv) { |
| error = wlu_iovar_getint(wl, name, &val); |
| if (error < 0) |
| return (error); |
| printf("Active Receiver time out: %dms\n", val); |
| |
| } else { |
| val = (uint16)strtol(*argv, NULL, 10); |
| if (val >= WL_RMC_ARTMO_MIN && |
| val <= WL_RMC_ARTMO_MAX) |
| error = wlu_iovar_setint(wl, name, val); |
| else |
| printf("\"Out of range\": valid range %dms - %dms\n", |
| WL_RMC_ARTMO_MIN, |
| WL_RMC_ARTMO_MAX); |
| } |
| return error; |
| } |
| |
| static int |
| wl_mcast_rssi_thresh(void *wl, cmd_t *cmd, char **argv) |
| { |
| int val, error = -1; |
| const char *name = cmd->name; |
| |
| /* toss the command name from the args */ |
| argv++; |
| |
| if (!*argv) { |
| error = wlu_iovar_getint(wl, name, &val); |
| if (error < 0) |
| return (error); |
| printf("rmc rssi: %d\n", val); |
| |
| } else { |
| val = (int8)strtol(*argv, NULL, 10); |
| error = wlu_iovar_setint(wl, name, val); |
| } |
| |
| return error; |
| } |
| |
| static int |
| wl_mcast_stats(void *wl, cmd_t *cmd, char **argv) |
| { |
| #define PRCNT(name) pbuf += sprintf(pbuf, "%s %u ", #name, dtoh16(cnts_v.name)) |
| #define PRCNT32(name) pbuf += sprintf(pbuf, "%s %u ", #name, dtoh32(cnts_v.name)) |
| wl_rmc_cnts_t *cnts = NULL; |
| wl_rmc_cnts_t cnts_v; |
| int err = BCME_OK; |
| uint8 argval, argc; |
| char *pbuf = buf; |
| char *endptr = NULL; |
| void *ptr = NULL; |
| |
| if (!*++argv) { |
| /* no arg - get and display all values */ |
| |
| if ((err = wlu_var_getbuf (wl, cmd->name, &cnts_v, sizeof(wl_rmc_cnts_t), &ptr))) |
| return (err); |
| |
| cnts = (wl_rmc_cnts_t*)ptr; |
| |
| memcpy((wl_rmc_cnts_t*)&cnts_v, cnts, sizeof(wl_rmc_cnts_t)); |
| cnts_v.version = dtoh16(cnts->version); |
| cnts_v.length = dtoh16(cnts->length); |
| |
| if (cnts_v.version != WL_RMC_CNT_VERSION) { |
| printf("\tIncorrect version of counters struct: expected %d; got %d\n", |
| WL_RMC_CNT_VERSION, cnts->version); |
| |
| return -1; |
| } |
| |
| PRCNT(dupcnt); PRCNT(ackreq_err); PRCNT(af_tx_err); PRNL(); |
| PRCNT(null_tx_err); PRCNT(af_unicast_tx_err); PRCNT(mc_no_amt_slot); PRNL(); |
| PRCNT(mc_not_mirrored); PRCNT(mc_existing_tr); PRCNT(mc_exist_in_amt); PRNL(); |
| PRCNT(mc_utilized); PRCNT(mc_taken_other_tr); PRCNT32(rmc_rx_frames_mac); PRNL(); |
| PRCNT32(rmc_tx_frames_mac); PRCNT32(mc_ar_role_selected); |
| PRCNT32(mc_ar_role_deleted); PRNL(); |
| PRCNT32(mc_noacktimer_expired); PRCNT32(mc_null_ar_cnt); |
| PRCNT(mc_no_wl_clk); PRNL(); |
| PRCNT(mc_tr_cnt_exceeded); PRNL(); |
| fputs(buf, stdout); |
| |
| } else { |
| |
| /* arg count */ |
| for (argc = 0; argv[argc]; argc++) |
| ; |
| argval = strtol(argv[0], &endptr, 0); |
| if (argval == 255) { |
| /* arg is -1, clear all the values */ |
| fprintf(stderr, "clearing rmc counters\n"); |
| err = wlu_var_setbuf(wl, cmd->name, &cnts_v, sizeof(wl_rmc_cnts_t)); |
| |
| } else { |
| fprintf(stderr, "Invalid arg, only -1" |
| "is allowed to clear counters\n"); |
| err = BCME_BADARG; |
| } |
| } |
| return err; |
| } |
| |
| static int |
| wl_mcast_rssi_delta(void *wl, cmd_t *cmd, char **argv) |
| { |
| int val, error = -1; |
| const char *name = cmd->name; |
| |
| /* toss the command name from the args */ |
| argv++; |
| |
| if (!*argv) { |
| error = wlu_iovar_getint(wl, name, &val); |
| if (error < 0) |
| return (error); |
| printf("rmc rssi delta: %d\n", val); |
| |
| } else { |
| val = (uint16)strtol(*argv, NULL, 10); |
| if (val >= 0) /* rssi delta value should be a whole number */ |
| error = wlu_iovar_setint(wl, name, val); |
| else |
| printf("\"Out of range\": rmc rssi delta should be >=0\n"); |
| } |
| return error; |
| } |
| |
| static int |
| wl_mcast_vsie(void *wl, cmd_t *cmd, char **argv) |
| { |
| int ret = 0; |
| void *ptr = NULL; |
| wl_rmc_vsie_t *reply = NULL; |
| wl_rmc_vsie_t vsie; |
| char *parse = NULL; |
| char tmp[4]; |
| int idx, cnt; |
| |
| if (!*++argv) { |
| |
| /* Get and display all entries in the table */ |
| if ((ret = wlu_var_getbuf(wl, cmd->name, |
| NULL, |
| 0, |
| &ptr)) < 0) { |
| return ret; |
| } |
| |
| reply = (wl_rmc_vsie_t*)ptr; |
| |
| printf("0x%x%x%x 0x%x", reply->oui[0], reply->oui[1], |
| reply->oui[2], reply->payload); |
| |
| } else { |
| |
| parse = *argv++; |
| |
| /* remove "0x" from the input string which is in hex */ |
| if (strlen(parse)/2 > DOT11_OUI_LEN) { |
| if (!strncmp("0x", parse, strlen("0x")) || |
| !strncmp("0X", parse, strlen("0X"))) { |
| parse += strlen("0x"); |
| } |
| } |
| |
| /* if OUI string is not 6 characters, simply reject */ |
| if (strlen(parse) != DOT11_OUI_LEN * 2) |
| return BCME_ERROR; |
| |
| /* parse oui string */ |
| for (idx = 0; idx < DOT11_OUI_LEN; idx++) { |
| for (cnt = 0; cnt < 2; cnt++) { |
| tmp[cnt] = *parse++; |
| } |
| tmp[cnt] = '\0'; |
| vsie.oui[idx] = (uint8)(strtoul(tmp, NULL, 16)); |
| } |
| |
| /* second argument is missing!! */ |
| if (!*argv) { |
| return BCME_ERROR; |
| } |
| |
| vsie.payload = (uint16)(strtoul(*argv, NULL, 16)); |
| ret = wlu_var_setbuf(wl, cmd->name, &vsie, sizeof(vsie)); |
| } |
| |
| return ret; |
| } |