blob: bdd00ce485a0b420b7ca0687c0c454457aacda4b [file] [log] [blame]
/*
* 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(&params_mode, 0, sizeof(uint8));
/* toss the command name */
argv++;
if ((err = wlu_var_getbuf_sm(wl, cmd->name, &params_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, &params_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;
}