blob: c79ff61d93949c4d31902e51771cda54636c0276 [file] [log] [blame]
/*
* wl wowl 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_wowl.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"
static cmd_func_t wl_nshostip;
static cmd_func_t wl_wowl_pattern, wl_wowl_wakeind, wl_wowl_pkt, wl_wowl_status;
static cmd_func_t wl_wowl_wake_reason, wl_wowl_extended_magic, wl_wowl_radio_duty_cycle;
static cmd_func_t wl_wowl_wog_appid;
static cmd_func_t wl_wowl_wog_resp;
static cmd_t wl_wowl_cmds[] = {
{ "ns_hostip", wl_nshostip, WLC_GET_VAR, WLC_SET_VAR,
"Add a ns-ip address or display then"},
{ "ns_hostip_clear", wl_var_void, -1, WLC_SET_VAR,
"Clear all ns-ip addresses"},
{ "wowl", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/disable WOWL events\n"
" 0 - Clear all events\n"
"Bit 0 - Wakeup on Magic Packet\n"
"Bit 1 - Wakeup on NetPattern (use 'wl wowl_pattern' to configure pattern)\n"
"Bit 2 - Wakeup on loss-of-link due to Disassociation/Deauth\n"
"Bit 3 - Wakeup on retrograde tsf\n"
"Bit 4 - Wakeup on loss of beacon (use 'wl wowl_bcn_loss' to configure time)"},
{ "wowl_radio_duty_cycle", wl_wowl_radio_duty_cycle, -1, -1,
"usage: wowl_radio_duty_cycle [ wake interval , sleep interval]\n"
"No options -- lists existing power intervals\n"
"wake interval -- Time of radio on period in MS \n"
"sleep interval -- Time of sleep period in MS "
},
{ "wowl_bcn_loss", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Set #of seconds of beacon loss for wakeup event"},
{ "wowl_pattern", wl_wowl_pattern, -1, -1,
"usage: wowl_pattern [ [clr | [[ add | del ] offset mask value ]]]\n"
"No options -- lists existing pattern list\n"
"add -- Adds the pattern to the list\n"
"del -- Removes a pattern from the list\n"
"clr -- Clear current list\n"
"offset -- Starting offset for the pattern\n"
"mask -- Mask to be used for pattern. Bit i of mask => byte i of the pattern\n"
"value -- Value of the pattern"
},
{ "wowl_wakeind", wl_wowl_wakeind, WLC_GET_VAR, WLC_SET_VAR,
"usage: wowl_wakeind [clear]\n"
"Shows last system wakeup event indications from PCI and D11 cores\n"
"clear - Clear the indications"
},
{ "wowl_status", wl_wowl_status, WLC_GET_VAR, -1,
"usage: wowl_status [clear]\n"
"Shows last system wakeup setting"
},
{"wowl_pkt", wl_wowl_pkt, -1, -1,
"Send a wakeup frame to wakup a sleeping STA in WAKE mode\n"
"Usage: wl wowl_pkt <len> <dst ea | bcast | ucast <STA ea>>"
"[ magic [<STA ea>] | net <offset> <pattern> <reason code> ]\n"
"e.g. To send bcast magic frame -- "
"wl wowl_pkt 102 bcast magic 00:90:4c:AA:BB:CC\n"
" To send ucast magic frame -- "
"wl wowl_pkt 102 ucast 00:90:4c:aa:bb:cc magic\n"
" To send a frame with L2 unicast - "
"wl wowl_pkt 102 00:90:4c:aa:bb:cc net 0 0x00904caabbcc 0x03\n"
" NOTE: offset for netpattern frame starts from \"Dest EA\" of ethernet frame."
"So dest ea will be used only when offset is >= 6\n"
" To send a eapol identity frame with L2 unicast - "
"wl wowl_pkt 102 00:90:4c:aa:bb:cc eapid id-string"},
{"wowl_ext_magic", wl_wowl_extended_magic, WLC_GET_VAR, WLC_SET_VAR,
"Set 6-byte extended magic pattern\n"
"Usage: wl wowl_ext_magic 0x112233445566"},
{ "wowl_wakeup_reason", wl_wowl_wake_reason, WLC_GET_VAR, -1 /* Set not reqd */,
"Returns pattern id and associated wakeup reason"},
{ "wowl_rls_wake_pkt", wl_var_void, -1, WLC_SET_VAR,
"Release packet that triggered the host wake up"},
{ "wowl_wog", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"WOG(Wake On Googlecast) on/off\n"
"Setting is available only under wowl mode deativated\n"
"\tUsage:\n\t wl wowl_wog [0|1]\n"},
{ "wowl_wog_appid", wl_wowl_wog_appid, WLC_GET_VAR, WLC_SET_VAR,
"App/Del/Clear/List Application IDs\n"
"\tUsage:\n\t wl wowl_wog_appid add <_APPID>\n"
"\t wl wowl_wog_appid del <_APPID>\n"
"\t wl wowl_wog_appid clear\n"
"\t wl wowl_wog_appid list\n"
"\t wl wowl_wog_appid maxcnt [max_appid_count]\n"
"\t\t max_appid_count > 1 and if maxcnt is changed AppId list will be cleared.\n"
"\t\t if maxcnt is same with before, maxcnt won't be applied.\n"},
{ "wowl_wog_resp", wl_wowl_wog_resp, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get Wake On Googlecast Response\n"
"\tUsage:\n\t wl wowl_wog_resp devname <devname>"
" ip <x.x.x.x> [ttl <ttl>]\n"
"\t txt id <uuid> [ca <capability>] [st <receiver_status_flag>] "
"[ve <version>] [md <model_name>]\n"
"\t [pk <public_key>] [fn <friendly_name>] "
"[rs <receiver_status>] [ttl <txt_ttl>]\n"
"\t [srv port <port> [ttl <srv_ttl>]]\n"
"\t [a ttl]\n"
"\n\t wl wowl_wog_resp show\n\n"},
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/* module initialization */
void
wluc_wowl_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register wowl commands */
wl_module_cmds_register(wl_wowl_cmds);
}
/*
* If a host IP address is given, add it to the host-cache,
* e.g. "wl nd_hostip fe00:0:0:0:0:290:1fc0:18c0 ".
* If no address is given, dump all the addresses.
*/
static int
wl_nshostip(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0, i;
struct ipv6_addr ipa_set, *ipa_get, null_ipa;
uint16 *ip_addr;
if (!*++argv) {
/* Get */
void *ptr = NULL;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
ip_addr = (uint16*)ptr;
memset(null_ipa.addr, 0, IPV6_ADDR_LEN);
for (ipa_get = (struct ipv6_addr *)ptr;
memcmp(null_ipa.addr, ipa_get->addr, IPV6_ADDR_LEN) != 0;
ipa_get++) {
/* Print ipv6 Addr */
for (i = 0; i < 8; i++)
{
printf("%x", ntoh16(ip_addr[i]));
if (i < 7)
printf(":");
}
}
printf("\r\n");
ip_addr += 8;
}
else {
/* Add */
if (!wl_atoipv6(*argv, &ipa_set))
return -1;
/* we add one ip-addr at a time */
return wlu_var_setbuf(wl, cmd->name,
&ipa_set, IPV6_ADDR_LEN);
}
return ret;
}
static int
wl_wowl_status(void *wl, cmd_t *cmd, char **argv)
{
int flags_prev = 0;
int err;
UNUSED_PARAMETER(cmd);
argv++;
if ((err = wlu_iovar_getint(wl, "wowl_status", &flags_prev)))
return err;
printf("Status of last wakeup:\n");
printf("\tflags:0x%x\n", flags_prev);
if (flags_prev & WL_WOWL_BCN)
printf("\t\tWake-on-Loss-of-Beacons enabled\n");
if (flags_prev & WL_WOWL_MAGIC)
printf("\t\tWake-on-Magic frame enabled\n");
if (flags_prev & WL_WOWL_NET)
printf("\t\tWake-on-Net pattern enabled\n");
if (flags_prev & WL_WOWL_DIS)
printf("\t\tWake-on-Deauth enabled\n");
if (flags_prev & WL_WOWL_RETR)
printf("\t\tRetrograde TSF enabled\n");
if (flags_prev & WL_WOWL_TST)
printf("\t\tTest-mode enabled\n");
printf("\n");
return 0;
}
static int
wl_wowl_wakeind(void *wl, cmd_t *cmd, char **argv)
{
wl_wowl_wakeind_t wake = {0, 0};
int err;
UNUSED_PARAMETER(cmd);
argv++;
if (*argv) {
if (strcmp(*argv, "clear"))
return BCME_USAGE_ERROR;
memset(&wake, 0, sizeof(wake));
memcpy(&wake, *argv, strlen("clear"));
err = wlu_iovar_set(wl, "wowl_wakeind", &wake, sizeof(wl_wowl_wakeind_t));
return err;
}
if ((err = wlu_iovar_get(wl, "wowl_wakeind", &wake, sizeof(wl_wowl_wakeind_t))) < 0)
return err;
if (wake.pci_wakeind)
printf("PCI Indication set\n");
wake.ucode_wakeind = dtoh32(wake.ucode_wakeind);
if (wake.ucode_wakeind != 0) {
printf("MAC Indication set\n");
if ((wake.ucode_wakeind & WL_WOWL_MAGIC) == WL_WOWL_MAGIC)
printf("\tMAGIC packet received\n");
if ((wake.ucode_wakeind & WL_WOWL_NET) == WL_WOWL_NET)
printf("\tPacket received with Netpattern\n");
if ((wake.ucode_wakeind & WL_WOWL_DIS) == WL_WOWL_DIS)
printf("\tDisassociation/Deauth received\n");
if ((wake.ucode_wakeind & WL_WOWL_RETR) == WL_WOWL_RETR)
printf("\tRetrograde TSF detected\n");
if ((wake.ucode_wakeind & WL_WOWL_BCN) == WL_WOWL_BCN)
printf("\tBeacons Lost\n");
if ((wake.ucode_wakeind & WL_WOWL_TST) == WL_WOWL_TST)
printf("\tTest Mode\n");
if ((wake.ucode_wakeind & WL_WOWL_M1) == WL_WOWL_M1)
printf("\tPTK Refresh received.\n");
if ((wake.ucode_wakeind & WL_WOWL_EAPID) == WL_WOWL_EAPID)
printf("\tEAP-Identity request received\n");
if ((wake.ucode_wakeind & WL_WOWL_GTK_FAILURE) == WL_WOWL_GTK_FAILURE)
printf("\tWake on GTK failure.\n");
if ((wake.ucode_wakeind & WL_WOWL_EXTMAGPAT) == WL_WOWL_EXTMAGPAT)
printf("\tExtended Magic Packet received.\n");
if ((wake.ucode_wakeind & WL_WOWL_KEYROT) == WL_WOWL_KEYROT)
printf("\tKey Rotation Packet received.\n");
if ((wake.ucode_wakeind & (WL_WOWL_NET | WL_WOWL_MAGIC | WL_WOWL_EXTMAGPAT))) {
if ((wake.ucode_wakeind & WL_WOWL_BCAST) == WL_WOWL_BCAST)
printf("\t\tBroadcast/Mcast frame received\n");
else
printf("\t\tUnicast frame received\n");
}
}
if (!wake.pci_wakeind && wake.ucode_wakeind == 0)
printf("No wakeup indication set\n");
return 0;
}
/* Used by NINTENDO2 */
static int
wl_wowl_wake_reason(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
wl_wr_t wr;
UNUSED_PARAMETER(cmd);
if (!*++argv) {
err = wlu_iovar_get(wl, "wakeup_reason", &wr, sizeof(wl_wr_t));
if (err)
return err;
if (wr.reason && wr.reason < REASON_LAST) {
printf("ID: %d\t", wr.id);
if (wr.reason == LCD_ON)
printf("Reason: LCD_ON\n");
else if (wr.reason == LCD_OFF)
printf("Reason: LCD_OFF\n");
else if (wr.reason == DRC1_WAKE)
printf("Reason: DRC1_WAKE\n");
else if (wr.reason == DRC2_WAKE)
printf("Reason: DRC2_WAKE\n");
}
else
printf("Unknown wakeup Reason\n");
}
return err;
}
/* Send a wakeup frame to sta in WAKE mode */
static int
wl_wowl_pkt(void *wl, cmd_t *cmd, char **argv)
{
char *arg = buf;
const char *str;
char *dst;
uint tot = 0;
uint16 type, pkt_len;
int dst_ea = 0; /* 0 == manual, 1 == bcast, 2 == ucast */
char *ea[ETHER_ADDR_LEN];
if (!*++argv)
return BCME_USAGE_ERROR;
UNUSED_PARAMETER(cmd);
str = "wowl_pkt";
strncpy(arg, str, strlen(str));
arg[strlen(str)] = '\0';
dst = arg + strlen(str) + 1;
tot += strlen(str) + 1;
pkt_len = (uint16)htod32(strtoul(*argv, NULL, 0));
*((uint16*)dst) = pkt_len;
dst += sizeof(pkt_len);
tot += sizeof(pkt_len);
if (!*++argv) {
printf("Dest of the packet needs to be provided\n");
return BCME_USAGE_ERROR;
}
/* Dest of the frame */
if (!strcmp(*argv, "bcast")) {
dst_ea = 1;
if (!wl_ether_atoe("ff:ff:ff:ff:ff:ff", (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
} else if (!strcmp(*argv, "ucast")) {
dst_ea = 2;
if (!*++argv) {
printf("EA of ucast dest of the packet needs to be provided\n");
return BCME_USAGE_ERROR;
}
if (!wl_ether_atoe(*argv, (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
/* Store it */
memcpy(ea, dst, ETHER_ADDR_LEN);
} else if (!wl_ether_atoe(*argv, (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
dst += ETHER_ADDR_LEN;
tot += ETHER_ADDR_LEN;
if (!*++argv) {
printf("type - magic/net needs to be provided\n");
return BCME_USAGE_ERROR;
}
if (strncmp(*argv, "magic", strlen("magic")) == 0)
type = WL_WOWL_MAGIC;
else if (strncmp(*argv, "net", strlen("net")) == 0)
type = WL_WOWL_NET;
else if (strncmp(*argv, "eapid", strlen("eapid")) == 0)
type = WL_WOWL_EAPID;
else
return BCME_USAGE_ERROR;
*((uint16*)dst) = type;
dst += sizeof(type);
tot += sizeof(type);
if (type == WL_WOWL_MAGIC) {
if (pkt_len < MAGIC_PKT_MINLEN)
return BCME_BADARG;
if (dst_ea == 2)
memcpy(dst, ea, ETHER_ADDR_LEN);
else {
if (!*++argv)
return BCME_USAGE_ERROR;
if (!wl_ether_atoe(*argv, (struct ether_addr *)dst))
return BCME_USAGE_ERROR;
}
tot += ETHER_ADDR_LEN;
} else if (type == WL_WOWL_NET) {
wl_wowl_pattern_t *wl_pattern;
wl_pattern = (wl_wowl_pattern_t *)dst;
if (!*++argv) {
printf("Starting offset not provided\n");
return BCME_USAGE_ERROR;
}
wl_pattern->offset = (uint)htod32(strtoul(*argv, NULL, 0));
wl_pattern->masksize = 0;
wl_pattern->patternoffset = (uint)htod32(sizeof(wl_wowl_pattern_t));
dst += sizeof(wl_wowl_pattern_t);
if (!*++argv) {
printf("pattern not provided\n");
return BCME_USAGE_ERROR;
}
wl_pattern->patternsize =
(uint)htod32(wl_pattern_atoh((char *)(uintptr)*argv, dst));
dst += wl_pattern->patternsize;
tot += sizeof(wl_wowl_pattern_t) + wl_pattern->patternsize;
wl_pattern->reasonsize = 0;
if (*++argv) {
wl_pattern->reasonsize =
(uint)htod32(wl_pattern_atoh((char *)(uintptr)*argv, dst));
tot += wl_pattern->reasonsize;
}
} else { /* eapid */
if (!*++argv) {
printf("EAPOL identity string not provided\n");
return BCME_USAGE_ERROR;
}
*dst++ = strlen(*argv);
strncpy(dst, *argv, strlen(*argv));
tot += 1 + strlen(*argv);
}
return (wlu_set(wl, WLC_SET_VAR, arg, tot));
}
static int
wl_wowl_pattern(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint i, j;
uint8 *ptr;
wl_wowl_pattern_t *wl_pattern;
UNUSED_PARAMETER(cmd);
if (*++argv) {
char *arg = buf;
const char *str;
char *dst;
uint tot = 0;
if (strcmp(*argv, "add") != 0 && strcmp(*argv, "del") != 0 &&
strcmp(*argv, "clr") != 0) {
return BCME_USAGE_ERROR;
}
str = "wowl_pattern";
strncpy(arg, str, strlen(str));
arg[strlen(str)] = '\0';
dst = arg + strlen(str) + 1;
tot += strlen(str) + 1;
str = *argv;
strncpy(dst, str, strlen(str));
tot += strlen(str) + 1;
if (strcmp(str, "clr") != 0) {
wl_pattern = (wl_wowl_pattern_t *)(dst + strlen(str) + 1);
dst = (char*)wl_pattern + sizeof(wl_wowl_pattern_t);
if (!*++argv) {
printf("Starting offset not provided\n");
return BCME_USAGE_ERROR;
}
wl_pattern->offset = htod32(strtoul(*argv, NULL, 0));
if (!*++argv) {
printf("Mask not provided\n");
return BCME_USAGE_ERROR;
}
/* Parse the mask */
str = *argv;
wl_pattern->masksize = htod32(wl_pattern_atoh((char *)(uintptr)str, dst));
if (wl_pattern->masksize == (uint)-1)
return BCME_USAGE_ERROR;
dst += wl_pattern->masksize;
wl_pattern->patternoffset = htod32((sizeof(wl_wowl_pattern_t) +
wl_pattern->masksize));
if (!*++argv) {
printf("Pattern value not provided\n");
return BCME_USAGE_ERROR;
}
/* Parse the value */
str = *argv;
wl_pattern->patternsize =
htod32(wl_pattern_atoh((char *)(uintptr)str, dst));
if (wl_pattern->patternsize == (uint)-1)
return BCME_USAGE_ERROR;
tot += sizeof(wl_wowl_pattern_t) + wl_pattern->patternsize +
wl_pattern->masksize;
}
return (wlu_set(wl, WLC_SET_VAR, arg, tot));
} else {
wl_wowl_pattern_list_t *list;
if ((err = wlu_iovar_get(wl, "wowl_pattern", buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
list = (wl_wowl_pattern_list_t *)buf;
printf("#of patterns :%d\n", list->count);
ptr = (uint8 *)list->pattern;
for (i = 0; i < list->count; i++) {
uint8 *pattern;
wl_pattern = (wl_wowl_pattern_t *)ptr;
printf("Pattern %d:\n", i+1);
printf("ID :0x%x\n"
"Offset :%d\n"
"Masksize :%d\n"
"Mask :0x",
(uint32)wl_pattern->id, wl_pattern->offset, wl_pattern->masksize);
pattern = ((uint8 *)wl_pattern + sizeof(wl_wowl_pattern_t));
for (j = 0; j < wl_pattern->masksize; j++)
printf("%02x", pattern[j]);
printf("\n"
"PatternSize:%d\n"
"Pattern :0x", wl_pattern->patternsize);
/* Go to end to find pattern */
pattern = ((uint8*)wl_pattern + wl_pattern->patternoffset);
for (j = 0; j < wl_pattern->patternsize; j++)
printf("%02x", pattern[j]);
printf("\n\n");
ptr += (wl_pattern->masksize + wl_pattern->patternsize +
sizeof(wl_wowl_pattern_t));
}
}
return err;
}
static int
wl_wowl_radio_duty_cycle(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
char *endptr = NULL;
int argc;
wowl_radio_duty_cycle_t cfg;
UNUSED_PARAMETER(cmd);
for (argc = 0; argv[argc]; argc++);
argc--;
if (argc != 0 && argc != 2) {
printf("required args: wake interval and sleep interval\n");
return BCME_USAGE_ERROR;
}
if (argc == 0)
{
err = wl_var_get(wl, cmd, argv);
if (err < 0)
return err;
printf(" wake interval =%d ms\n sleep interval = %d ms\n",
((wowl_radio_duty_cycle_t*)buf)->wake_interval,
((wowl_radio_duty_cycle_t*)buf)->sleep_interval);
}
else
{
cfg.wake_interval = htod16((uint16)(strtoul(argv[1], &endptr, 0)));
cfg.sleep_interval = htod16((uint16)(strtoul(argv[2], &endptr, 0)));
err = wlu_var_setbuf(wl, cmd->name, &cfg, sizeof(wowl_radio_duty_cycle_t));
printf(" wake interval = %d ms\n sleep interval = %d ms\n",
cfg.wake_interval, cfg.sleep_interval);
}
return err;
}
static int
wl_wowl_extended_magic(void *wl, cmd_t *cmd, char **argv)
{
char *arg = buf;
const char *str;
char *dst;
uint tot;
int ret;
str = "wowl_ext_magic";
strncpy(arg, str, strlen(str));
arg[strlen(str)] = '\0';
if (*++argv) {
dst = arg + strlen(str) + 1;
tot = strlen(str) + 1;
ret = wl_pattern_atoh(*argv, dst);
if (ret == -1)
return BCME_USAGE_ERROR;
if (ret != 6) {
printf("Extended magic pattern must be 6-byte length\n");
return BCME_USAGE_ERROR;
}
tot += 6;
ret = wlu_set(wl, cmd->set, arg, tot);
return ret;
}
if ((ret = wlu_get(wl, cmd->get, arg, WLC_IOCTL_MAXLEN)) < 0)
return ret;
printf("0x");
for (ret = 0; ret < 6; ret++)
printf("%02x", (uint8)arg[ret]);
printf("\n");
return 0;
}
static int
wl_wowl_wog_appid(void *wl, cmd_t *cmd, char **argv)
{
wog_appid_iov_t *wog_appid;
char *appid, *command;
uint len;
int ret = BCME_OK;
if (!*++argv) {
return BCME_USAGE_ERROR;
}
wog_appid = (wog_appid_iov_t *)malloc(sizeof(*wog_appid));
if (wog_appid == NULL) {
return BCME_NOMEM;
}
memset(wog_appid, 0, sizeof(*wog_appid));
wog_appid->ver = htod32(WOG_APPID_IOV_VER);
command = *argv;
if (!strncmp(command, "add", 4) || !strncmp(command, "del", 4)) {
appid = *++argv;
if (!appid) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
len = strlen(appid);
if (!len || appid[0] != '_') {
printf("Appid should start with '_'\n");
ret = BCME_USAGE_ERROR;
goto EXIT;
}
wog_appid->cnt = htod32(1);
wog_appid->operation = (!strncmp(command, "add", 4))? htod32(WOG_APPID_ADD) :
htod32(WOG_APPID_DEL);
if (len > MAX_DNS_LABEL) {
len = MAX_DNS_LABEL;
}
memcpy(wog_appid->appids[0].appID, appid, len);
wog_appid->appids[0].appID[len] = 0;
ret = wlu_iovar_set(wl, cmd->name, wog_appid, sizeof(*wog_appid));
} else if (!strncmp(command, "clear", 6)) {
wog_appid->operation = htod32(WOG_APPID_CLEAR);
ret = wlu_iovar_set(wl, cmd->name, wog_appid, sizeof(*wog_appid));
} else if (!strncmp(command, "list", 5)) {
wog_appid_iov_t *list;
uint i;
wog_appid->operation = htod32(WOG_APPID_LIST);
ret = wlu_iovar_getbuf(wl, cmd->name, wog_appid, sizeof(*wog_appid),
buf, WLC_IOCTL_MAXLEN);
if (ret != BCME_OK) {
goto EXIT;
}
list = (wog_appid_iov_t *)buf;
list->cnt = dtoh32(list->cnt);
printf("AppID count : %d\n", list->cnt);
for (i = 0; i < list->cnt; i++) {
printf("AppID[%d]=%s\n", i, list->appids[i].appID);
}
} else if (!strncmp(command, "maxcnt", 7)) {
int max_count;
wog_appid->operation = htod32(WOG_MAX_APPID_CNT);
argv++;
if (*argv) {
max_count = atoi(*argv);
if (max_count < 1) {
printf("Maximum AppID count should be greater than 0\n");
ret = BCME_USAGE_ERROR;
goto EXIT;
}
wog_appid->cnt = htod32(max_count);
ret = wlu_iovar_set(wl, cmd->name, wog_appid, sizeof(*wog_appid));
} else {
wog_appid_iov_t *appid;
ret = wlu_iovar_getbuf(wl, cmd->name, wog_appid, sizeof(*wog_appid),
buf, WLC_IOCTL_SMLEN);
if (ret != BCME_OK) {
goto EXIT;
}
appid = (wog_appid_iov_t *)buf;
appid->cnt = dtoh32(appid->cnt);
printf("Maximum AppID count : %d\n", appid->cnt);
}
} else {
ret = BCME_USAGE_ERROR;
}
EXIT:
if (wog_appid) {
free(wog_appid);
}
return ret;
}
#define WOG_RESP_MAX_CMD_LEN 11
#define WOG_RESP_MAX_SUBCMD 10
typedef struct wowl_wog_resp_subcmd {
int mandatory;
char subname[WOG_RESP_MAX_CMD_LEN + 1];
} wowl_wog_resp_subcmd_t;
#define WOG_CMD_DEVNAME 0
#define WOG_CMD_IP 1
#define WOG_CMD_TTL 2
#define WOG_CMD_TXT 3
#define WOG_CMD_SRV 4
#define WOG_CMD_A 5
#define WOG_SUBCMD_TXT_ID 0
#define WOG_SUBCMD_TXT_CA 1
#define WOG_SUBCMD_TXT_ST 2
#define WOG_SUBCMD_TXT_VE 3
#define WOG_SUBCMD_TXT_MD 4
#define WOG_SUBCMD_TXT_PK 5
#define WOG_SUBCMD_TXT_FN 6
#define WOG_SUBCMD_TXT_RS 7
#define WOG_SUBCMD_TXT_TTL 8
#define WOG_SUBCMD_SRV_PORT 0
#define WOG_SUBCMD_SRV_TTL 1
#define WOG_SUBCMD_A_TTL 0
typedef struct wowl_wog_resp_cmd {
int mandatory;
char cname[WOG_RESP_MAX_CMD_LEN + 1];
int subcnt;
wowl_wog_resp_subcmd_t subcmd[WOG_RESP_MAX_SUBCMD];
} wowl_wog_resp_cmd_t;
const wowl_wog_resp_cmd_t wog_resp_cmdlist[] = {
{1, "devname", 0, {{0, {0, }}, }},
{1, "ip", 0, {{0, {0, }}, }},
{0, "ttl", 0, {{0, {0, }}, }},
{1, "txt", 9,
{{1, "id"}, {0, "ca"}, {0, "st"}, {0, "ve"}, {0, "md"},
{0, "pk"}, {0, "fn"}, {0, "rs"}, {0, "ttl"}, }
},
{0, "srv", 2, {{1, "port"}, {0, "ttl"}, }},
{0, "a", 1, {{1, "ttl"}, }}
};
static int
wl_wowl_wog_parse_txt(char ***argv, wog_sd_resp_t *resp,
int subcnt, wowl_wog_resp_subcmd_t *sublist)
{
int ret = BCME_OK;
int subidx;
int i;
int val;
char *stop;
while (**argv) {
subidx = -1;
for (i = 0; i < subcnt; i++) {
if (!strncmp(**argv, sublist[i].subname,
WOG_RESP_MAX_CMD_LEN + 1)) {
subidx = i;
break;
}
}
/* Toss cmd to upper layer */
if (subidx < 0) {
goto EXIT;
}
if (!*++*argv) {
printf("no subparam for txt record.\n");
return BCME_USAGE_ERROR;
}
switch (subidx) {
case WOG_SUBCMD_TXT_ID:
strncpy(resp->txt.id, **argv, GCAST_UUID_LEN);
resp->txt.id[GCAST_UUID_LEN] = 0;
break;
case WOG_SUBCMD_TXT_CA:
val = strtoul(**argv, &stop, 16);
if (val > 0x0f ||
(!val && stop == **argv)) {
printf("TXT capability should be 0 to f and hex value\n");
return BCME_BADARG;
}
resp->txt.capability = ***argv;
break;
case WOG_SUBCMD_TXT_ST:
val = strtoul(**argv, NULL, 16);
if ((val != 0) && (val != 1)) {
printf("TXT receiver_status_flag should be 0 or 1\n");
return BCME_BADARG;
}
resp->txt.receiver_status_flag = ***argv;
break;
case WOG_SUBCMD_TXT_VE:
val = strtoul(**argv, NULL, 16);
if (val < 2 || val > 255) {
printf("TXT version >= 2 && version <= 255\n");
return BCME_BADARG;
}
snprintf(resp->txt.ver, GCAST_VER_LEN + 1, "%02x", val);
break;
case WOG_SUBCMD_TXT_MD:
strncpy(resp->txt.model_name, **argv, GCAST_MAX_MODEL_NAME_LEN);
resp->txt.model_name[GCAST_MAX_MODEL_NAME_LEN] = 0;
break;
case WOG_SUBCMD_TXT_PK:
strncpy(resp->txt.public_key, **argv, GCAST_PUBLICKEY_ID_LEN);
resp->txt.public_key[GCAST_PUBLICKEY_ID_LEN] = 0;
break;
case WOG_SUBCMD_TXT_FN:
strncpy(resp->txt.friendly_name, **argv, GCAST_MAX_FNAME_LEN);
resp->txt.friendly_name[GCAST_MAX_FNAME_LEN] = 0;
break;
case WOG_SUBCMD_TXT_RS:
strncpy(resp->txt.receiver_status, **argv, GCAST_MAX_RS_LEN);
resp->txt.receiver_status[GCAST_MAX_RS_LEN] = 0;
break;
case WOG_SUBCMD_TXT_TTL:
val = atoi(**argv);
if (val <= 0) {
printf("TXT ttl > 0\n");
return BCME_BADARG;
}
resp->txt.ttl = htod32(val);
break;
}
sublist[subidx].mandatory = 0;
++*argv;
}
EXIT:
--*argv; /* rewind for upper layer */
for (i = 0; i < subcnt; i++) {
if (sublist[i].mandatory) {
return BCME_USAGE_ERROR;
}
}
return ret;
}
static int
wl_wowl_wog_parse_srv(char ***argv, wog_sd_resp_t *resp,
int subcnt, wowl_wog_resp_subcmd_t *sublist)
{
int ret = BCME_OK;
int subidx;
int i, val;
while (**argv) {
subidx = -1;
for (i = 0; i < subcnt; i++) {
if (!strncmp(**argv, sublist[i].subname,
WOG_RESP_MAX_CMD_LEN + 1)) {
subidx = i;
break;
}
}
/* Toss cmd to upper layer */
if (subidx < 0) {
goto EXIT;
}
if (!*++*argv) {
printf("no subparam for SRV record.\n");
return BCME_USAGE_ERROR;
}
switch (subidx) {
case WOG_SUBCMD_SRV_PORT:
val = atoi(**argv);
if (val <= 0 || val > 0xffff) {
printf("SRV port : 0 < port && port <= 0xffff\n");
return BCME_BADARG;
}
resp->srv.port = htod16(val);
break;
case WOG_SUBCMD_SRV_TTL:
val = atoi(**argv);
if (val <= 0) {
printf("SRV ttl > 0\n");
return BCME_BADARG;
}
resp->srv.ttl = htod32(val);
break;
}
sublist[subidx].mandatory = 0;
++*argv;
}
EXIT:
--*argv; /* rewind for upper layer */
for (i = 0; i < subcnt; i++) {
if (sublist[i].mandatory) {
printf("No input for srv port or ttl\n");
return BCME_USAGE_ERROR;
}
}
return ret;
}
static int
wl_wowl_wog_parse_a(char ***argv, wog_sd_resp_t *resp,
int subcnt, wowl_wog_resp_subcmd_t *sublist)
{
int ret = BCME_OK;
int subidx;
int i, val;
while (**argv) {
subidx = -1;
for (i = 0; i < subcnt; i++) {
if (!strncmp(**argv, sublist[i].subname,
WOG_RESP_MAX_CMD_LEN + 1)) {
subidx = i;
break;
}
}
/* Toss cmd to upper layer */
if (subidx < 0) {
goto EXIT;
}
if (!*++*argv) {
printf("no subparam for A record.\n");
return BCME_USAGE_ERROR;
}
switch (subidx) {
case WOG_SUBCMD_A_TTL:
val = atoi(**argv);
if (val <= 0) {
printf("A ttl > 0\n");
return BCME_BADARG;
}
resp->a.ttl = htod32(val);
break;
}
sublist[subidx].mandatory = 0;
++*argv;
}
EXIT:
--*argv; /* rewind for upper layer */
for (i = 0; i < subcnt; i++) {
if (sublist[i].mandatory) {
printf("No input for a ttl\n");
return BCME_USAGE_ERROR;
}
}
return ret;
}
static int
wl_wowl_wog_resp(void *wl, cmd_t *cmd, char **argv)
{
wog_sd_resp_t *resp = NULL;
int ret = BCME_OK;
int cmdcnt;
int cmdidx;
int i, val;
struct ipv4_addr ip;
wowl_wog_resp_cmd_t *cmdlist = NULL;
UNUSED_PARAMETER(wl);
UNUSED_PARAMETER(cmd);
if (!*++argv) {
return BCME_USAGE_ERROR;
}
resp = (wog_sd_resp_t *)malloc(sizeof(*resp));
cmdlist = (wowl_wog_resp_cmd_t *)malloc(sizeof(wog_resp_cmdlist));
if (!resp || !cmdlist) {
ret = BCME_NOMEM;
goto EXIT;
}
memset(resp, 0, sizeof(*resp));
memcpy(cmdlist, wog_resp_cmdlist, sizeof(wog_resp_cmdlist));
cmdcnt = sizeof(wog_resp_cmdlist)/sizeof(wowl_wog_resp_cmd_t);
resp->ver = WOG_SD_RESP_VER;
if (!strncmp(*argv, "show", 5)) {
wog_sd_resp_t *r;
ret = wlu_iovar_getbuf(wl, cmd->name, NULL, 0, buf, WLC_IOCTL_MAXLEN);
if (ret != BCME_OK) {
goto EXIT;
}
r = (wog_sd_resp_t *)buf;
if (r->ver != WOG_SD_RESP_VER) {
ret = BCME_VERSION;
goto EXIT;
}
printf("device name=%s\n"
"ip=%d.%d.%d.%d, answer ttl=%d\n"
"txt id=%s, capability=%c, receiver_status_flag=%c, "
"version=%s, model_name=%s, public_key=%s, "
"friendly_name=%s, receiver_status=%s, txt ttl=%d\n"
"srv port=%d, srv ttl=%d\n"
"a ttl=%d\n",
r->device_name,
r->ip[0], r->ip[1], r->ip[2], r->ip[3],
dtoh32(r->ptr_ttl),
r->txt.id, r->txt.capability, r->txt.receiver_status_flag,
r->txt.ver, r->txt.model_name, r->txt.public_key,
r->txt.friendly_name, r->txt.receiver_status,
dtoh32(r->txt.ttl),
dtoh16(r->srv.port), dtoh32(r->srv.ttl),
dtoh32(r->a.ttl));
goto EXIT;
}
while (*argv) {
cmdidx = -1;
for (i = 0; i < cmdcnt; i++) {
if (!strncmp(*argv, cmdlist[i].cname,
WOG_RESP_MAX_CMD_LEN + 1)) {
cmdidx = i;
break;
}
}
if (cmdidx < 0 || !*++argv) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
switch (cmdidx) {
case WOG_CMD_DEVNAME:
strncpy(resp->device_name, *argv, MAX_DNS_LABEL);
resp->device_name[MAX_DNS_LABEL] = 0;
break;
case WOG_CMD_IP:
if (!wl_atoip(*argv, &ip)) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
memcpy(resp->ip, ip.addr, IPV4_ADDR_LEN);
break;
case WOG_CMD_TTL:
val = atoi(*argv);
if (val <= 0) {
printf("PTR ttl > 0\n");
ret = BCME_BADARG;
goto EXIT;
}
resp->ptr_ttl = htod32(val);
break;
case WOG_CMD_TXT:
if (wl_wowl_wog_parse_txt(&argv, resp,
cmdlist[WOG_CMD_TXT].subcnt,
cmdlist[WOG_CMD_TXT].subcmd) != BCME_OK) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
break;
case WOG_CMD_SRV:
if (wl_wowl_wog_parse_srv(&argv, resp,
cmdlist[WOG_CMD_SRV].subcnt,
cmdlist[WOG_CMD_SRV].subcmd) != BCME_OK) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
break;
case WOG_CMD_A:
if (wl_wowl_wog_parse_a(&argv, resp,
cmdlist[WOG_CMD_A].subcnt,
cmdlist[WOG_CMD_A].subcmd) != BCME_OK) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
break;
default:
ret = BCME_USAGE_ERROR;
goto EXIT;
}
cmdlist[cmdidx].mandatory = 0;
argv++;
}
/* Check if all mendatory fields exist or not */
for (i = 0; i < cmdcnt; i++) {
if (cmdlist[i].mandatory) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
}
ret = wlu_iovar_set(wl, cmd->name, resp, sizeof(*resp));
EXIT:
if (resp) {
free(resp);
}
if (cmdlist) {
free(cmdlist);
}
return ret;
}