blob: dacafb3245e40815c7113259d4d4d1b0cc9142a7 [file] [log] [blame]
/*
* wl p2p 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_p2p.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"
#ifdef LINUX
#include <time.h>
#endif /* LINUX */
static cmd_func_t wl_p2p_state;
static cmd_func_t wl_p2p_scan;
static cmd_func_t wl_p2p_ifadd;
static cmd_func_t wl_p2p_ifdel;
static cmd_func_t wl_p2p_ifupd;
static cmd_func_t wl_p2p_if;
static cmd_func_t wl_p2p_ops;
static cmd_func_t wl_p2p_noa;
static cmd_t wl_p2p_cmds[] = {
{ "p2p_ssid", wl_ssid, WLC_GET_VAR, WLC_SET_VAR,
"set WiFi P2P wildcard ssid.\n"
"\tUsage: wl p2p_ssid <ssid>"
},
{ "p2p_state", wl_p2p_state, -1, WLC_SET_VAR,
"set WiFi P2P discovery state.\n"
"\tUsage: wl p2p_state <state> [<chanspec> <dwell time>]"
},
{ "p2p_scan", wl_p2p_scan, -1, WLC_SET_VAR,
"initiate WiFi P2P scan.\n"
"\tUsage: wl p2p_scan S|E <scan parms>\n"
SCAN_USAGE
},
{ "p2p_ifadd", wl_p2p_ifadd, -1, WLC_SET_VAR,
"add WiFi P2P interface\n"
"\tUsage: wl p2p_ifadd <MAC-address> go|client|dyngo [chanspec]\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_ifdel", wl_p2p_ifdel, -1, WLC_SET_VAR,
"delete WiFi P2P interface\n"
"\tUsage: wl p2p_ifdel <MAC-address>\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_ifupd", wl_p2p_ifupd, -1, WLC_SET_VAR,
"update an interface to WiFi P2P interface\n"
"\tUsage: wl p2p_ifupd <MAC-address> go|client\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_if", wl_p2p_if, WLC_GET_VAR, -1,
"query WiFi P2P interface bsscfg index\n"
"\tUsage: wl p2p_if <MAC-address>\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "p2p_noa", wl_p2p_noa, WLC_GET_VAR, WLC_SET_VAR,
"set/get WiFi P2P NoA schedule\n"
"\tUsage: wl p2p_noa <type> <type-specific-params>\n"
"\t\ttype 0: Scheduled Absence (on GO): <type> <action> <action-specific-params>\n"
"\t\t\taction -1: Cancel the schedule: <type> <action>\n"
"\t\t\taction 0,1,2: <type> <action> <option> <option-specific-params>\n"
"\t\t\t\taction 0: Do nothing during absence periods\n"
"\t\t\t\taction 1: Sleep during absence periods\n"
"\t\t\t\toption 0: <start:tsf> <interval> <duration> <count> ...\n"
"\t\t\t\toption 1 [<start-percentage>] <duration-percentage>\n"
"\t\t\t\toption 2 <start:tsf-offset> <interval> <duration> <count>\n"
"\t\ttype 1: Requested Absence (on GO): \n"
"\t\t\taction -1: Cancel the schedule: <type> <action>\n"
"\t\t\taction 2: <type> <action> <option> <option-specific-params>\n"
"\t\t\t\taction 2: Turn off GO beacons and probe responses during absence period\n"
"\t\t\t\toption 2 <start:tsf-offset> <interval> <duration> <count>"
},
{ "p2p_ops", wl_p2p_ops, WLC_GET_VAR, WLC_SET_VAR,
"set/get WiFi P2P OppPS and CTWindow\n"
"\tUsage: wl p2p_ops <ops> [<ctw>]\n"
"\t\t<ops>:\n"
"\t\t\t0: Disable OppPS\n"
"\t\t\t1: Enable OppPS\n"
"\t\t<ctw>:\n"
"\t\t\t10 and up to beacon interval"
},
{ "p2p_da_override", wl_iov_mac, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set WiFi P2P device interface addr\n"
"\tUsage: wl p2p_da_override <MAC-address>\n"
"MAC-address: xx:xx:xx:xx:xx:xx\n"
"(When MAC-address is set to 00:00:00:00:00:00, default da restored)"
},
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/* module initialization */
void
wluc_p2p_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register p2p commands */
wl_module_cmds_register(wl_p2p_cmds);
}
static int
wl_p2p_state(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_disc_st_t st;
int count;
char *endptr;
argv++;
count = ARGCNT(argv);
if (count < 1)
return BCME_USAGE_ERROR;
st.state = (uint8) strtol(argv[0], &endptr, 0);
if (st.state == WL_P2P_DISC_ST_LISTEN) {
if (count != 3)
return BCME_USAGE_ERROR;
if ((st.chspec = wf_chspec_aton(argv[1])) == 0) {
fprintf(stderr, "error parsing chanspec arg \"%s\"\n", argv[1]);
return BCME_BADARG;
}
st.chspec = wl_chspec_to_driver(st.chspec);
if (st.chspec == INVCHANSPEC) {
return BCME_USAGE_ERROR;
}
st.dwell = (uint16) strtol(argv[2], &endptr, 0);
}
return wlu_var_setbuf(wl, cmd->name, &st, sizeof(st));
}
static int
wl_p2p_scan(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_scan_t *params = NULL;
int params_size = 0;
int malloc_size = 0;
int sparams_size = 0;
int err = 0;
if (*(argv + 1) != NULL) {
malloc_size = sizeof(wl_p2p_scan_t);
switch (toupper(**(argv + 1))) {
case 'S':
malloc_size += WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
break;
case 'E':
malloc_size += OFFSETOF(wl_escan_params_t, params) +
WL_SCAN_PARAMS_FIXED_SIZE + WL_NUMCHANNELS * sizeof(uint16);
break;
}
}
if (malloc_size == 0) {
fprintf(stderr, "wrong syntax, need 'S' or 'E'\n");
return BCME_BADARG;
}
malloc_size += WL_SCAN_PARAMS_SSID_MAX * sizeof(wlc_ssid_t);
params = (wl_p2p_scan_t *)malloc(malloc_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan params\n", malloc_size);
return BCME_NOMEM;
}
memset(params, 0, malloc_size);
switch (toupper(**(argv + 1))) {
case 'S': {
wl_scan_params_t *sparams = (wl_scan_params_t *)(params+1);
sparams_size = malloc_size - sizeof(wl_p2p_scan_t);
params->type = 'S';
if ((err = wl_scan_prep(wl, cmd, argv + 1, sparams, &sparams_size)) == 0)
params_size = sizeof(wl_p2p_scan_t) + sparams_size;
break;
}
case 'E': {
wl_escan_params_t *eparams = (wl_escan_params_t *)(params+1);
sparams_size = malloc_size - sizeof(wl_p2p_scan_t) - sizeof(wl_escan_params_t);
params->type = 'E';
eparams->version = htod32(ESCAN_REQ_VERSION);
eparams->action = htod16(WL_SCAN_ACTION_START);
#if defined(linux)
srand((unsigned)time(NULL));
eparams->sync_id = htod16(rand() & 0xffff);
#else
eparams->sync_id = htod16(4321);
#endif /* #if defined(linux) */
if ((err = wl_scan_prep(wl, cmd, argv + 1, &eparams->params, &sparams_size)) == 0)
params_size = sizeof(wl_p2p_scan_t) + sizeof(wl_escan_params_t) +
sparams_size;
break;
}
}
if (!err)
err = wlu_iovar_setbuf(wl, cmd->name, params, params_size, buf, WLC_IOCTL_MAXLEN);
free(params);
return err;
}
static int
wl_p2p_ifadd(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_if_t ifreq;
int count;
argv++;
count = ARGCNT(argv);
if (count < 2)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &ifreq.addr))
return BCME_USAGE_ERROR;
if (stricmp(argv[1], "go") == 0)
ifreq.type = WL_P2P_IF_GO;
else if (stricmp(argv[1], "client") == 0)
ifreq.type = WL_P2P_IF_CLIENT;
else if (stricmp(argv[1], "dyngo") == 0)
ifreq.type = WL_P2P_IF_DYNBCN_GO;
else
return BCME_USAGE_ERROR;
if (ifreq.type == WL_P2P_IF_GO || ifreq.type == WL_P2P_IF_DYNBCN_GO) {
if (count > 2) {
if ((ifreq.chspec = wf_chspec_aton(argv[2])) == 0) {
fprintf(stderr, "error parsing chanspec arg \"%s\"\n", argv[2]);
return BCME_BADARG;
}
ifreq.chspec = wl_chspec_to_driver(ifreq.chspec);
if (ifreq.chspec == INVCHANSPEC) {
return BCME_BADARG;
}
}
else
ifreq.chspec = 0;
}
return wlu_var_setbuf(wl, cmd->name, &ifreq, sizeof(ifreq));
}
static int
wl_p2p_ifdel(void *wl, cmd_t *cmd, char **argv)
{
struct ether_addr addr;
int count;
argv++;
count = ARGCNT(argv);
if (count != 1)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &addr))
return BCME_USAGE_ERROR;
return wlu_var_setbuf(wl, cmd->name, &addr, sizeof(addr));
}
static int
wl_p2p_ifupd(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_if_t ifreq;
int count;
int ret;
int bsscfg_idx = 0;
int consumed = 0;
argv++;
/* parse a bsscfg_idx option if present */
if ((ret = wl_cfg_option(argv, cmd->name, &bsscfg_idx, &consumed)) != 0)
return ret;
argv += consumed;
if (consumed == 0)
bsscfg_idx = -1;
count = ARGCNT(argv);
if (count < 2)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &ifreq.addr))
return BCME_USAGE_ERROR;
if (stricmp(argv[1], "go") == 0)
ifreq.type = WL_P2P_IF_GO;
else if (stricmp(argv[1], "client") == 0)
ifreq.type = WL_P2P_IF_CLIENT;
else
return BCME_USAGE_ERROR;
ifreq.chspec = 0;
if (bsscfg_idx == -1)
return wlu_var_setbuf(wl, cmd->name, &ifreq, sizeof(ifreq));
return wlu_bssiovar_setbuf(wl, cmd->name, bsscfg_idx,
&ifreq, sizeof(ifreq),
buf, WLC_IOCTL_MAXLEN);
}
static int
wl_p2p_if(void *wl, cmd_t *cmd, char **argv)
{
struct ether_addr addr;
int count;
wl_p2p_ifq_t *ptr;
int err;
argv++;
count = ARGCNT(argv);
if (count != 1)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(argv[0], &addr))
return BCME_USAGE_ERROR;
err = wlu_var_getbuf(wl, cmd->name, &addr, sizeof(addr), (void*)&ptr);
if (err >= 0)
printf("%u %s\n", dtoh32(ptr->bsscfgidx), (ptr->ifname));
return err;
}
static int
wl_p2p_ops(void *wl, cmd_t *cmd, char **argv)
{
wl_p2p_ops_t ops;
int count;
char *endptr;
argv++;
count = ARGCNT(argv);
if (count < 1) {
wl_p2p_ops_t *ops;
int err;
err = wlu_var_getbuf(wl, cmd->name, NULL, 0, (void *)&ops);
if (err != BCME_OK) {
fprintf(stderr, "%s: error %d\n", cmd->name, err);
return err;
}
printf("ops: %u ctw: %u\n", ops->ops, ops->ctw);
return BCME_OK;
}
ops.ops = (uint8) strtol(argv[0], &endptr, 0);
if (ops.ops != 0) {
if (count != 2)
return BCME_USAGE_ERROR;
ops.ctw = (uint8) strtol(argv[1], &endptr, 0);
}
else
ops.ctw = 0;
return wlu_var_setbuf(wl, cmd->name, &ops, sizeof(ops));
}
static int
wl_p2p_noa(void *wl, cmd_t *cmd, char **argv)
{
int count;
wl_p2p_sched_t *noa;
int len;
int i;
char *endptr;
argv ++;
strcpy(buf, cmd->name);
count = ARGCNT(argv);
if (count < 2) {
int err = wlu_get(wl, WLC_GET_VAR, buf, WLC_IOCTL_MAXLEN);
wl_p2p_sched_t *sched;
int i;
if (err != BCME_OK) {
fprintf(stderr, "%s: error %d\n", cmd->name, err);
return err;
}
sched = (wl_p2p_sched_t *)buf;
for (i = 0; i < 16; i ++) {
if (sched->desc[i].count == 0)
break;
printf("start: %u interval: %u duration: %u count: %u\n",
sched->desc[i].start, sched->desc[i].interval,
sched->desc[i].duration, sched->desc[i].count);
}
return BCME_OK;
}
len = strlen(buf);
noa = (wl_p2p_sched_t *)&buf[len + 1];
len += 1;
noa->type = (uint8)strtol(argv[0], &endptr, 0);
len += sizeof(noa->type);
noa->action = (uint8)strtol(argv[1], &endptr, 0);
len += sizeof(noa->action);
argv += 2;
count -= 2;
/* action == -1 is to cancel the current schedule */
if (noa->action == WL_P2P_SCHED_ACTION_RESET) {
/* the fixed portion of wl_p2p_sched_t with action == WL_P2P_SCHED_ACTION_RESET
* is required to cancel the curret schedule.
*/
len += (char *)&noa->desc[0] - ((char *)buf + len);
}
/* Take care of any special cases only and let all other cases fall through
* as normal 'start/interval/duration/count' descriptions.
* All cases start with 'type' 'action' 'option'.
* Any count value greater than 255 is to repeat unlimited.
*/
else {
switch (noa->type) {
case WL_P2P_SCHED_TYPE_ABS:
case WL_P2P_SCHED_TYPE_REQ_ABS:
if (count < 1)
return BCME_USAGE_ERROR;
noa->option = (uint8)strtol(argv[0], &endptr, 0);
len += sizeof(noa->option);
argv += 1;
count -= 1;
break;
}
/* add any paddings before desc field */
len += (char *)&noa->desc[0] - ((char *)buf + len);
switch (noa->type) {
case WL_P2P_SCHED_TYPE_ABS:
switch (noa->option) {
case WL_P2P_SCHED_OPTION_BCNPCT:
if (count == 1) {
noa->desc[0].duration = htod32(strtol(argv[0], &endptr, 0));
noa->desc[0].start = 100 - noa->desc[0].duration;
}
else if (count == 2) {
noa->desc[0].start = htod32(strtol(argv[0], &endptr, 0));
noa->desc[0].duration = htod32(strtol(argv[1], &endptr, 0));
}
else {
fprintf(stderr, "Usage: wl p2p_noa 0 %d 1 "
"<start-pct> <duration-pct>\n",
noa->action);
return BCME_USAGE_ERROR;
}
len += sizeof(wl_p2p_sched_desc_t);
break;
default:
if (count < 4 || (count % 4) != 0) {
fprintf(stderr, "Usage: wl p2p_noa 0 %d 0 "
"<start> <interval> <duration> <count> ...\n",
noa->action);
return BCME_USAGE_ERROR;
}
goto normal;
}
break;
default:
if (count != 4) {
fprintf(stderr, "Usage: wl p2p_noa 1 %d "
"<start> <interval> <duration> <count> ...\n",
noa->action);
return BCME_USAGE_ERROR;
}
/* fall through... */
normal:
for (i = 0; i < count; i += 4) {
noa->desc[i / 4].start = htod32(strtoul(argv[i], &endptr, 0));
noa->desc[i / 4].interval = htod32(strtol(argv[i + 1], &endptr, 0));
noa->desc[i / 4].duration = htod32(strtol(argv[i + 2], &endptr, 0));
noa->desc[i / 4].count = htod32(strtol(argv[i + 3], &endptr, 0));
len += sizeof(wl_p2p_sched_desc_t);
}
break;
}
}
return wlu_set(wl, WLC_SET_VAR, buf, len);
}