blob: 80b07b0f02100b6c1f8e2a344c2a7bf3ffbb6a74 [file] [log] [blame]
/*
* wl rrm 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_rrm.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_rrm;
static cmd_func_t wl_rrm_nbr_req;
static cmd_func_t wl_rrm_bcn_req;
static cmd_func_t wl_rrm_chload_req;
static cmd_func_t wl_rrm_noise_req;
static cmd_func_t wl_rrm_frame_req;
static cmd_func_t wl_rrm_stat_req;
static cmd_func_t wl_rrm_stat_rpt;
static cmd_func_t wl_rrm_lm_req;
static cmd_func_t wl_rrm_nbr_list;
static cmd_func_t wl_rrm_nbr_del_nbr;
static cmd_func_t wl_rrm_nbr_add_nbr;
static cmd_func_t wl_rrm_config;
static cmd_t wl_rrm_cmds[] = {
{ "rrm", wl_rrm, WLC_GET_VAR, WLC_SET_VAR,
"enable or disable RRM feature\n"
"\tUsage: wl rrm [0/1] to disable/enable RRM feature"},
{ "rrm_bcn_req", wl_rrm_bcn_req, -1, WLC_SET_VAR,
"send 11k beacon measurement request\n"
"\tUsage: wl rrm_bcn_req [bcn mode] [da] [duration] [random int] [channel] [ssid]"
" [repetitions]"},
{ "rrm_chload_req", wl_rrm_chload_req, -1, WLC_SET_VAR,
"send 11k channel load measurement request\n"
"\tUsage: wl rrm_chload_req [regulatory] [da] [duration] [random int] [channel]"
" [repetitions]"},
{ "rrm_noise_req", wl_rrm_noise_req, -1, WLC_SET_VAR,
"send 11k noise measurement request\n"
"\tUsage: wl rrm_noise_req [regulatory] [da] [duration] [random int] [channel]"
" [repetitions] "},
{ "rrm_frame_req", wl_rrm_frame_req, -1, WLC_SET_VAR,
"send 11k frame measurement request\n"
"\tUsage: wl rrm_frame_req [regulatory] [da] [duration] [random int] [channel] [ta]"
" [repetitions]"},
{ "rrm_stat_req", wl_rrm_stat_req, -1, WLC_SET_VAR,
"send 11k stat measurement request\n"
"\tUsage: wl rrm_stat_req [da] [random int] [duration] [peer] [group id] [repetitions]"},
{ "rrm_stat_rpt", wl_rrm_stat_rpt, -1, WLC_GET_VAR,
"Read 11k stat measurement report from STA\n"
"\tUsage: wl rrm_stat_rpt [mac]"},
{ "rrm_lm_req", wl_rrm_lm_req, -1, WLC_SET_VAR,
"send 11k link measurement request\n"
"\tUsage: wl rrm_lm_req [da]"},
{ "rrm_nbr_req", wl_rrm_nbr_req, -1, WLC_SET_VAR,
"send 11k neighbor report measurement request\n"
"\tUsage: wl rrm_nbr_req [ssid]"},
{ "rrm_nbr_list", wl_rrm_nbr_list, WLC_GET_VAR, -1,
"get 11k neighbor report list\n"
"\tUsage: wl rrm_nbr_list"},
{ "rrm_nbr_del_nbr", wl_rrm_nbr_del_nbr, -1, WLC_SET_VAR,
"delete node from 11k neighbor report list\n"
"\tUsage: wl rrm_nbr_del_nbr [bssid]"},
{ "rrm_nbr_add_nbr", wl_rrm_nbr_add_nbr, -1, WLC_SET_VAR,
"add node to 11k neighbor report list\n"
"\tUsage: wl rrm_nbr_add_nbr [bssid] [bssid info] [regulatory] [channel] [phytype]"},
{ "rrm_config", wl_rrm_config, -1, WLC_SET_VAR,
"Configure information (LCI/Civic location) for self\n"
"\tUsage: wl rrm_config lci [lci_location]\n"
"\tUsage: wl rrm_config civic [civic_location]"},
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/* module initialization */
void
wluc_rrm_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register rrm commands */
wl_module_cmds_register(wl_rrm_cmds);
}
/* RM Enable Capabilities */
static dbg_msg_t rrm_msgs[] = {
{DOT11_RRM_CAP_LINK, "Link_Measurement"}, /* bit0 */
{DOT11_RRM_CAP_NEIGHBOR_REPORT, "Neighbor_Report"}, /* bit1 */
{DOT11_RRM_CAP_PARALLEL, "Parallel_Measurement"}, /* bit2 */
{DOT11_RRM_CAP_REPEATED, "Repeated_Measurement"}, /* bit3 */
{DOT11_RRM_CAP_BCN_PASSIVE, "Beacon_Passive"}, /* bit4 */
{DOT11_RRM_CAP_BCN_ACTIVE, "Beacon_Active"}, /* bit5 */
{DOT11_RRM_CAP_BCN_TABLE, "Beacon_Table"}, /* bit6 */
{DOT11_RRM_CAP_BCN_REP_COND, "Beacon_measurement_Reporting_Condition"}, /* bit7 */
{DOT11_RRM_CAP_FM, "Frame_Measurement"}, /* bit8 */
{DOT11_RRM_CAP_CLM, "Channel_load_Measurement"}, /* bit9 */
{DOT11_RRM_CAP_NHM, "Noise_Histogram_measurement"}, /* bit10 */
{DOT11_RRM_CAP_SM, "Statistics_Measurement"}, /* bit11 */
{DOT11_RRM_CAP_LCIM, "LCI_Measurement"}, /* bit12 */
{DOT11_RRM_CAP_LCIA, "LCI_Azimuth"}, /* bit13 */
{DOT11_RRM_CAP_TSCM, "Tx_Stream_Category_Measurement"}, /* bit14 */
{DOT11_RRM_CAP_TTSCM, "Triggered_Tx_stream_Category_Measurement"}, /* bit15 */
{DOT11_RRM_CAP_AP_CHANREP, "AP_Channel_Report"}, /* bit16 */
{DOT11_RRM_CAP_RMMIB, "RM_MIB"}, /* bit17 */
/* bit 18-26, unused */
{DOT11_RRM_CAP_MPTI, "Measurement_Pilot_Transmission_Information"}, /* bit27 */
{DOT11_RRM_CAP_NBRTSFO, "Neighbor_Report_TSF_Offset"}, /* bit28 */
{DOT11_RRM_CAP_RCPI, "RCPI_Measurement"}, /* bit29 */
{DOT11_RRM_CAP_RSNI, "RSNI_Measurement"}, /* bit30 */
{DOT11_RRM_CAP_BSSAAD, "BSS_Average_Access_Delay"}, /* bit31 */
{DOT11_RRM_CAP_BSSAAC, "BSS_Available_Admission_Capacity"}, /* bit32 */
{DOT11_RRM_CAP_AI, "Antenna_Information"}, /* bit33 */
{DOT11_RRM_CAP_FTM_RANGE, "FTM_Range_Reporting"}, /* bit34 */
{DOT11_RRM_CAP_CIVIC_LOC, "Civic_Location_Measurement"}, /* bit35 */
{0, NULL}
};
static bool rrm_input_validation(uint val, uint hval, dbg_msg_t *dbg_msg)
{
int i;
uint32 flag = 0;
if ((val == 0) && (hval == 0))
return TRUE;
for (i = 0; dbg_msg[i].value <= DOT11_RRM_CAP_BSSAAD; i++)
flag |= 1 << dbg_msg[i].value;
flag = ~flag;
if (val & flag)
return FALSE;
flag = 0;
if (hval != 0) {
for (; dbg_msg[i].value; i++) {
flag |= 1 << (dbg_msg[i].value - DOT11_RRM_CAP_BSSAAC);
}
flag = ~flag;
if (hval & flag)
return FALSE;
}
return TRUE;
}
static int
wl_rrm(void *wl, cmd_t *cmd, char **argv)
{
int err, i;
uint hval = 0, val = 0, len, found, rmcap_del = 0, rmcap2_del = 0;
uint rmcap_add = 0, rmcap2_add = 0;
char *endptr = NULL;
dbg_msg_t *dbg_msg = rrm_msgs;
void *ptr = NULL;
dot11_rrm_cap_ie_t rrm_cap, *reply;
uint high = 0, low = 0, bit = 0, hbit = 0;
const char *cmdname = "rrm";
char *s = NULL;
char t[32], c[32] = "0x";
UNUSED_PARAMETER(cmd);
err = wlu_var_getbuf_sm(wl, cmdname, &rrm_cap, sizeof(rrm_cap), &ptr);
if (err < 0)
return err;
reply = (dot11_rrm_cap_ie_t *)ptr;
high = reply->cap[4];
low = reply->cap[0] | (reply->cap[1] << 8) | (reply->cap[2] << 16) | (reply->cap[3] << 24);
if (!*++argv) {
if (high != 0)
printf("0x%x%08x", high, low);
else
printf("0x%x ", low);
for (i = 0; ((bit = dbg_msg[i].value) <= DOT11_RRM_CAP_BSSAAD); i++) {
if (low & (1 << bit))
printf(" %s", dbg_msg[i].string);
}
for (; (hbit = dbg_msg[i].value); i++) {
if (high & (1 << (hbit - DOT11_RRM_CAP_BSSAAC)))
printf(" %s", dbg_msg[i].string);
}
printf("\n");
return err;
}
while (*argv) {
s = *argv;
found = 0;
if (*s == '+' || *s == '-')
s++;
else {
/* used for clearing previous value */
rmcap_del = ~0;
rmcap2_del = ~0;
}
val = strtoul(s, &endptr, 0);
/* Input is decimal number or hex with prefix 0x and > 32 bits */
if (val == 0xFFFFFFFF) {
if (!(*s == '0' && *(s+1) == 'x')) {
fprintf(stderr,
"Msg bits >32 take only numerical input in hex\n");
val = 0;
} else {
/* Input number with prefix 0x */
len = strlen(s);
hval = strtoul(strncpy(t, s, len-8), &endptr, 0);
*endptr = 0;
s = s + strlen(t);
s = strcat(c, s);
val = strtoul(s, &endptr, 0);
/* Input number > 64bit */
if (hval == 0xFFFFFFFF) {
fprintf(stderr, "Invalid entry for RM Capabilities\n");
hval = 0;
val = 0;
}
}
}
/* validet the input number */
if (!rrm_input_validation(val, hval, dbg_msg))
goto usage;
/* Input is a string */
if (*endptr != '\0') {
for (i = 0; ((bit = dbg_msg[i].value) <= DOT11_RRM_CAP_BSSAAD); i++) {
if (stricmp(dbg_msg[i].string, s) == 0) {
found = 1;
break;
}
}
if (!found) {
for (; (hbit = dbg_msg[i].value); i++) {
if (stricmp(dbg_msg[i].string, s) == 0)
break;
}
if (hbit)
hval = 1 << (hbit - DOT11_RRM_CAP_BSSAAC);
else
hval = 0;
} else {
val = 1 << bit;
}
if (!val && !hval)
goto usage;
}
if (**argv == '-') {
rmcap_del |= val;
if (!found)
rmcap2_del |= hval;
}
else {
rmcap_add |= val;
if (!found)
rmcap2_add |= hval;
}
++argv;
}
low &= ~rmcap_del;
high &= ~rmcap2_del;
low |= rmcap_add;
high |= rmcap2_add;
rrm_cap.cap[4] = high;
rrm_cap.cap[0] = low & 0x000000ff;
rrm_cap.cap[1] = (low & 0x0000ff00) >> 8;
rrm_cap.cap[2] = (low & 0x00ff0000) >> 16;
rrm_cap.cap[3] = (low & 0xff000000) >> 24;
err = wlu_var_setbuf(wl, cmdname, &rrm_cap, sizeof(dot11_rrm_cap_ie_t));
return err;
usage:
fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
fprintf(stderr, "Use a + or - prefix to make an incremental change.");
for (i = 0; (bit = dbg_msg[i].value) <= DOT11_RRM_CAP_BSSAAD; i++) {
fprintf(stderr, "\n0x%04x %s", (1 << bit), dbg_msg[i].string);
}
for (; (hbit = dbg_msg[i].value); i++) {
hbit -= DOT11_RRM_CAP_BSSAAC;
fprintf(stderr, "\n0x%x00000000 %s", (1 << hbit), dbg_msg[i].string);
}
fprintf(stderr, "\n");
return BCME_OK;
}
static int
wl_rrm_stat_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_stat_req";
statreq_t sreq_buf;
UNUSED_PARAMETER(cmd);
memset(&sreq_buf, 0, sizeof(statreq_t));
if (argv[1]) {
/* da */
if (!wl_ether_atoe(argv[1], &sreq_buf.da)) {
printf("wl_rrm_stat_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* random interval */
if (argv[2]) {
sreq_buf.random_int = htod32(strtoul(argv[2], NULL, 0));
}
/* duration */
if (argv[3]) {
sreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* peer address */
if (argv[4]) {
if (!wl_ether_atoe(argv[4], &sreq_buf.peer)) {
printf("wl_rrm_stat_req parsing peer failed\n");
return BCME_USAGE_ERROR;
}
}
/* group id */
if (argv[5]) {
sreq_buf.group_id =
htod32(strtoul(argv[5], NULL, 0));
}
/* repetitions */
if (argv[6]) {
sreq_buf.reps = htod32(strtoul(argv[6], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &sreq_buf, sizeof(sreq_buf));
return err;
}
static int
wl_rrm_stat_rpt(void *wl, cmd_t *cmd, char **argv)
{
int ret = BCME_USAGE_ERROR;
statrpt_t *rpt_ptr, rpt;
int cnt;
if (!*++argv) return -1;
/* get sta mac address */
if (!wl_ether_atoe(*argv++, &rpt.addr))
goto done;
rpt.ver = htod16(WL_RRM_RPT_VER);
memset(buf, 0, WLC_IOCTL_MEDLEN);
if ((ret = wlu_iovar_getbuf(wl, cmd->name, (void *) &rpt,
sizeof(rpt), buf, WLC_IOCTL_MEDLEN)) < 0) {
fprintf(stderr, "ERROR: cmd:%s\n", cmd->name);
goto done;
}
/* display the sta info */
rpt_ptr = (statrpt_t *)buf;
rpt_ptr->ver = dtoh16(rpt_ptr->ver);
/* Report unrecognized version */
if (rpt_ptr->ver != WL_RRM_RPT_VER) {
fprintf(stderr, "ERROR: Mismatch ver[%d] Driver ver[%d]\n",
WL_RRM_RPT_VER, rpt_ptr->ver);
goto done;
}
printf("ver:%d timestamp:%u flag:%d len:%d\n",
rpt_ptr->ver, dtoh32(rpt_ptr->timestamp),
dtoh16(rpt_ptr->flag), rpt_ptr->len);
for (cnt = 0; cnt < rpt_ptr->len; cnt++) {
if (cnt % 8 == 0)
printf("\n[%d]:", cnt);
printf("[0x%02x][%d] ", rpt_ptr->data[cnt], (signed char)(rpt_ptr->data[cnt]));
}
printf("\n");
done:
return ret;
}
static int
wl_rrm_frame_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_frame_req";
framereq_t freq_buf;
UNUSED_PARAMETER(cmd);
memset(&freq_buf, 0, sizeof(framereq_t));
if (argv[1]) {
/* Regulatory class */
freq_buf.reg = htod32(strtoul(argv[1], NULL, 0));
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &freq_buf.da)) {
printf("wl_rrm_frame_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
freq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
freq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
freq_buf.chan = htod32(strtoul(argv[5], NULL, 0));
}
/* transmit address */
if (argv[6]) {
if (!wl_ether_atoe(argv[6], &freq_buf.ta)) {
printf("wl_rrm_frame_req parsing ta failed\n");
return BCME_USAGE_ERROR;
}
}
/* repetitions */
if (argv[7]) {
freq_buf.reps = htod32(strtoul(argv[7], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &freq_buf, sizeof(freq_buf));
return err;
}
static int
wl_rrm_chload_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_chload_req";
rrmreq_t chreq_buf;
UNUSED_PARAMETER(cmd);
memset(&chreq_buf, 0, sizeof(rrmreq_t));
if (argv[1]) {
/* Regulatory class */
chreq_buf.reg = htod32(strtoul(argv[1], NULL, 0));
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &chreq_buf.da)) {
printf("wl_rrm_chload_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
chreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
chreq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
chreq_buf.chan = htod32(strtoul(argv[5], NULL, 0));
}
/* repetitions */
if (argv[6]) {
chreq_buf.reps = htod32(strtoul(argv[6], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &chreq_buf, sizeof(chreq_buf));
return err;
}
static int
wl_rrm_noise_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_noise_req";
rrmreq_t nreq_buf;
UNUSED_PARAMETER(cmd);
printf("wl_rrm_noise_req\n");
memset(&nreq_buf, 0, sizeof(rrmreq_t));
if (argv[1]) {
/* Regulatory class */
nreq_buf.reg = htod32(strtoul(argv[1], NULL, 0));
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &nreq_buf.da)) {
printf("wl_rrm_noise_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
nreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
nreq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
nreq_buf.chan = htod32(strtoul(argv[5], NULL, 0));
}
/* repetitions */
if (argv[6]) {
nreq_buf.reps = htod32(strtoul(argv[6], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &nreq_buf, sizeof(nreq_buf));
return err;
}
static int
wl_rrm_bcn_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
uint8 mode = 0;
const char *cmdname = "rrm_bcn_req";
bcnreq_t bcnreq_buf;
wlc_ssid_t ssid;
UNUSED_PARAMETER(cmd);
memset(&bcnreq_buf, 0, sizeof(bcnreq_t));
if (argv[1]) {
/* bcn mode: ACTIVE/PASSIVE/SCAN_CACHE */
mode = htod32(strtoul(argv[1], NULL, 0));
if (mode > 2) {
printf("wl_rrm_bcn_req parsing bcn mode failed\n");
return BCME_BADARG;
}
bcnreq_buf.bcn_mode = mode;
}
/* da */
if (argv[2]) {
if (!wl_ether_atoe(argv[2], &bcnreq_buf.da)) {
printf("wl_rrm_bcn_req parsing da failed\n");
return BCME_USAGE_ERROR;
}
}
/* duration */
if (argv[3]) {
bcnreq_buf.dur = htod32(strtoul(argv[3], NULL, 0));
}
/* random interval */
if (argv[4]) {
bcnreq_buf.random_int = htod32(strtoul(argv[4], NULL, 0));
}
/* channel */
if (argv[5]) {
bcnreq_buf.channel = htod32(strtoul(argv[5], NULL, 0));
}
printf("wl_rrm_bcn_req:bcn mode: %d, duration: %d, "
"chan: %d\n", mode,
bcnreq_buf.dur, bcnreq_buf.channel);
/* SSID */
if (argv[6]) {
uint32 len;
len = strlen(argv[6]);
if (len > DOT11_MAX_SSID_LEN) {
printf("ssid too long\n");
return BCME_BADARG;
}
memset(&ssid, 0, sizeof(wlc_ssid_t));
memcpy(ssid.SSID, argv[6], len);
ssid.SSID_len = len;
memcpy(&bcnreq_buf.ssid, &ssid, sizeof(wlc_ssid_t));
}
/* repetitions */
if (argv[7]) {
bcnreq_buf.reps = htod32(strtoul(argv[7], NULL, 0));
}
err = wlu_iovar_set(wl, cmdname, &bcnreq_buf, sizeof(bcnreq_buf));
return err;
}
static int
wl_rrm_lm_req(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_lm_req";
struct ether_addr da;
UNUSED_PARAMETER(cmd);
if (argv[1]) {
if (!wl_ether_atoe(argv[1], &da)) {
printf("wl_rrm_lm_req parsing arg1 failed\n");
return BCME_USAGE_ERROR;
}
}
err = wlu_iovar_set(wl, cmdname, &da, sizeof(da));
return err;
}
static int
wl_rrm_nbr_req(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wlc_ssid_t ssid;
UNUSED_PARAMETER(cmd);
strcpy(buf, "rrm_nbr_req");
buflen = strlen("rrm_nbr_req") + 1;
if (*++argv) {
uint32 len;
len = strlen(*argv);
if (len > DOT11_MAX_SSID_LEN) {
printf("ssid too long\n");
return BCME_BADARG;
}
memset(&ssid, 0, sizeof(wlc_ssid_t));
memcpy(ssid.SSID, *argv, len);
ssid.SSID_len = len;
memcpy(&buf[buflen], &ssid, sizeof(wlc_ssid_t));
buflen += sizeof(wlc_ssid_t);
}
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
/* For mapping customer's user space command, two calls of the same iovar. */
static int
wl_rrm_nbr_list(void *wl, cmd_t *cmd, char **argv)
{
int err = 0, buflen, i;
uint16 list_cnt;
nbr_element_t *nbr_elt;
uint8 *ptr;
UNUSED_PARAMETER(cmd);
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "rrm_nbr_list");
buflen = strlen("rrm_nbr_list") + 1;
if (*++argv != NULL)
return BCME_USAGE_ERROR;
if ((err = wlu_get(wl, WLC_GET_VAR, buf, buflen)) < 0)
return err;
list_cnt = *(uint16 *)buf;
if (list_cnt == 0)
return err;
memset(buf, 0, WLC_IOCTL_MAXLEN);
strcpy(buf, "rrm_nbr_list");
buflen = strlen("rrm_nbr_list") + 1;
memcpy(&buf[buflen], &list_cnt, sizeof(uint16));
printf("RRM Neighbor Report List:\n");
if ((err = wlu_get(wl, WLC_GET_VAR, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
ptr = (uint8 *)buf;
for (i = 0; i < list_cnt; i++) {
nbr_elt = (nbr_element_t *)ptr;
printf("AP %2d: ", i + 1);
printf("bssid %02x:%02x:%02x:%02x:%02x:%02x ", nbr_elt->bssid.octet[0],
nbr_elt->bssid.octet[1], nbr_elt->bssid.octet[2], nbr_elt->bssid.octet[3],
nbr_elt->bssid.octet[4], nbr_elt->bssid.octet[5]);
printf("bssid_info %08x ", load32_ua(&nbr_elt->bssid_info));
printf("reg %2d channel %3d phytype %d\n", nbr_elt->reg,
nbr_elt->channel, nbr_elt->phytype);
ptr += TLV_HDR_LEN + DOT11_NEIGHBOR_REP_IE_FIXED_LEN;
}
return err;
}
static int
wl_rrm_nbr_del_nbr(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
const char *cmdname = "rrm_nbr_del_nbr";
struct ether_addr ea;
UNUSED_PARAMETER(cmd);
if (*++argv == NULL) {
printf("no bssid specified\n");
return BCME_USAGE_ERROR;
} else {
if (!wl_ether_atoe(*argv, &ea)) {
printf("Incorrect bssid format\n");
return BCME_ERROR;
}
err = wlu_iovar_set(wl, cmdname, &ea, sizeof(ea));
}
return err;
}
static int
wl_rrm_nbr_add_nbr(void *wl, cmd_t *cmd, char **argv)
{
int argc;
int err = 0;
const char *cmdname = "rrm_nbr_add_nbr";
nbr_element_t nbr_elt;
UNUSED_PARAMETER(cmd);
memset(&nbr_elt, 0, sizeof(nbr_element_t));
for (argc = 0; argv[argc]; argc++)
;
if (argc != 6)
return BCME_USAGE_ERROR;
/* bssid */
if (!wl_ether_atoe(argv[1], &nbr_elt.bssid)) {
printf("wl_rrm_nbr_add_nbr parsing bssid failed\n");
return BCME_USAGE_ERROR;
}
/* bssid info */
nbr_elt.bssid_info = htod32(strtoul(argv[2], NULL, 0));
/* Regulatory class */
nbr_elt.reg = htod32(strtoul(argv[3], NULL, 0));
/* channel */
nbr_elt.channel = htod32(strtoul(argv[4], NULL, 0));
/* phytype */
nbr_elt.phytype = htod32(strtoul(argv[5], NULL, 0));
nbr_elt.id = DOT11_MNG_NEIGHBOR_REP_ID;
nbr_elt.len = DOT11_NEIGHBOR_REP_IE_FIXED_LEN;
err = wlu_iovar_set(wl, cmdname, &nbr_elt, TLV_HDR_LEN + DOT11_NEIGHBOR_REP_IE_FIXED_LEN);
return err;
}
static int
wl_rrm_parse_location(char *loc_arg, char *bufptr, int buflen)
{
int len = 0;
char *ptr = loc_arg;
char hex[] = "XX";
if (!loc_arg) {
printf("%s: Location data is missing\n", __FUNCTION__);
len = -1;
goto done;
}
len = strlen(loc_arg)/2;
if ((len <= 0) || (len > buflen)) {
len = -1;
goto done;
}
if ((uint16)strlen(ptr) != len*2) {
printf("%s: Invalid length. Even number of characters expected.\n",
__FUNCTION__);
len = -1;
goto done;
}
while (*ptr) {
strncpy(hex, ptr, 2);
*bufptr++ = (char) strtoul(hex, NULL, 16);
ptr += 2;
}
done:
return len;
}
static int
wl_rrm_self_lci_civic(void *wl, cmd_t *cmd, char **argv, int argc, int cmd_id)
{
int err = BCME_OK;
int len = 0;
char *bufptr;
wl_rrm_config_ioc_t *rrm_config_cmd = NULL;
wl_rrm_config_ioc_t rrm_config_param;
int malloc_len = sizeof(*rrm_config_cmd) + TLV_BODY_LEN_MAX -
DOT11_MNG_IE_MREP_FIXED_LEN;
rrm_config_cmd = (wl_rrm_config_ioc_t *) calloc(1, malloc_len);
if (rrm_config_cmd == NULL) {
printf("Failed to allocate buffer of %d bytes\n", malloc_len);
err = BCME_NOMEM;
goto done;
}
rrm_config_cmd->id = cmd_id;
if (argc == 3) {
bufptr = (char *)&rrm_config_cmd->data[0];
len = wl_rrm_parse_location(argv[1], bufptr, TLV_BODY_LEN_MAX -
DOT11_MNG_IE_MREP_FIXED_LEN);
if (len <= 0) {
printf("%s: parsing location arguments failed\n", __FUNCTION__);
err = BCME_USAGE_ERROR;
goto done;
}
rrm_config_cmd->len = len;
err = wlu_iovar_set(wl, cmd->name, (void *)rrm_config_cmd,
WL_RRM_CONFIG_MIN_LENGTH + len);
goto done;
} else if (argc == 2) {
memset(&rrm_config_param, 0, sizeof(rrm_config_param));
rrm_config_param.id = cmd_id;
err = wlu_iovar_getbuf(wl, cmd->name, (void *)&rrm_config_param,
sizeof(rrm_config_param), (void *)rrm_config_cmd, malloc_len);
if (err < 0) {
printf("wlu_iov_get for \"%s\" returned error %d\n", cmd->name, err);
goto done;
}
prhex(cmd_id == WL_RRM_CONFIG_GET_CIVIC ? "Self Civic data" : "Self LCI data",
rrm_config_cmd->data, rrm_config_cmd->len);
printf("\n");
} else {
err = BCME_USAGE_ERROR;
}
done:
if (rrm_config_cmd)
free(rrm_config_cmd);
return err;
}
static int
wl_rrm_config(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_OK;
int argc = 0;
UNUSED_PARAMETER(cmd);
if (!argv[1]) {
printf("%s: no element type given, must be lci or civic\n", __FUNCTION__);
err = BCME_USAGE_ERROR;
goto done;
}
for (argc = 0; argv[argc]; argc++)
;
if (strcmp(argv[1], "lci") == 0) {
argv++;
err = wl_rrm_self_lci_civic(wl, cmd, argv, argc,
(argc == 3) ? WL_RRM_CONFIG_SET_LCI : WL_RRM_CONFIG_GET_LCI);
} else if (strcmp(argv[1], "civic") == 0) {
argv++;
err = wl_rrm_self_lci_civic(wl, cmd, argv, argc,
(argc == 3) ? WL_RRM_CONFIG_SET_CIVIC : WL_RRM_CONFIG_GET_CIVIC);
} else {
printf("%s: incorrect element type, not lci or civic\n", __FUNCTION__);
err = BCME_USAGE_ERROR;
}
done:
return err;
}