You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3871 lines
114 KiB
C
3871 lines
114 KiB
C
/*
|
|
* Wireless Network Adapter Configuration Utility
|
|
*
|
|
* Copyright (C) 2015, Broadcom Corporation
|
|
* All Rights Reserved.
|
|
*
|
|
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
|
|
* 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 Corporation.
|
|
*
|
|
* $Id: wlconf.c 525064 2015-01-08 21:08:25Z $
|
|
*/
|
|
|
|
#include <typedefs.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include <bcmnvram.h>
|
|
#include <bcmutils.h>
|
|
#include <bcmparams.h>
|
|
#include <bcmdevs.h>
|
|
#include <shutils.h>
|
|
#include <wlutils.h>
|
|
#include <wlioctl.h>
|
|
#include <proto/802.1d.h>
|
|
#include <bcmconfig.h>
|
|
#include <bcmwifi_channels.h>
|
|
#include <netconf.h>
|
|
#include <nvparse.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#if defined(linux)
|
|
#include <sys/utsname.h>
|
|
#endif
|
|
|
|
#ifdef TCONFIG_DPSTA
|
|
#include <dpsta_linux.h>
|
|
#include <errno.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#endif
|
|
|
|
/* phy types */
|
|
#define PHY_TYPE_A 0
|
|
#define PHY_TYPE_B 1
|
|
#define PHY_TYPE_G 2
|
|
#define PHY_TYPE_N 4
|
|
#define PHY_TYPE_LP 5
|
|
#define PHY_TYPE_SSN 6
|
|
#define PHY_TYPE_HT 7
|
|
#define PHY_TYPE_LCN 8
|
|
#define PHY_TYPE_AC 11
|
|
#define PHY_TYPE_NULL 0xf
|
|
|
|
/* how many times to attempt to bring up a virtual i/f when
|
|
* we are in APSTA mode and IOVAR set of "bss" "up" returns busy
|
|
*/
|
|
#define MAX_BSS_UP_RETRIES 5
|
|
|
|
/* notify the average dma xfer rate (in kbps) to the driver */
|
|
#define AVG_DMA_XFER_RATE 120000
|
|
|
|
/* parts of an idcode: */
|
|
#define IDCODE_MFG_MASK 0x00000fff
|
|
#define IDCODE_MFG_SHIFT 0
|
|
#define IDCODE_ID_MASK 0x0ffff000
|
|
#define IDCODE_ID_SHIFT 12
|
|
#define IDCODE_REV_MASK 0xf0000000
|
|
#define IDCODE_REV_SHIFT 28
|
|
|
|
/*
|
|
* Debugging Macros
|
|
*/
|
|
#ifdef BCMDBG
|
|
#define WLCONF_DBG(fmt, arg...) printf("%s: "fmt, __FUNCTION__ , ## arg)
|
|
#define WL_IOCTL(ifname, cmd, buf, len) \
|
|
if ((ret = wl_ioctl(ifname, cmd, buf, len))) \
|
|
fprintf(stderr, "%s:%d:(%s): %s failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, #cmd, ret);
|
|
#define WL_SETINT(ifname, cmd, val) \
|
|
if ((ret = wlconf_setint(ifname, cmd, val))) \
|
|
fprintf(stderr, "%s:%d:(%s): setting %s to %d (0x%x) failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, #cmd, (int)val, (unsigned int)val, ret);
|
|
#define WL_GETINT(ifname, cmd, pval) \
|
|
if ((ret = wlconf_getint(ifname, cmd, pval))) \
|
|
fprintf(stderr, "%s:%d:(%s): getting %s failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, #cmd, ret);
|
|
#define WL_IOVAR_SET(ifname, iovar, param, paramlen) \
|
|
if ((ret = wl_iovar_set(ifname, iovar, param, paramlen))) \
|
|
fprintf(stderr, "%s:%d:(%s): setting iovar \"%s\" failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, iovar, ret);
|
|
#define WL_IOVAR_GET(ifname, iovar, param, paramlen) \
|
|
if ((ret = wl_iovar_get(ifname, iovar, param, paramlen))) \
|
|
fprintf(stderr, "%s:%d:(%s): getting iovar \"%s\" failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, iovar, ret);
|
|
#define WL_IOVAR_SETINT(ifname, iovar, val) \
|
|
if ((ret = wl_iovar_setint(ifname, iovar, val))) \
|
|
fprintf(stderr, "%s:%d:(%s): setting iovar \"%s\" to 0x%x failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, iovar, (unsigned int)val, ret);
|
|
#define WL_IOVAR_GETINT(ifname, iovar, val) \
|
|
if ((ret = wl_iovar_getint(ifname, iovar, val))) \
|
|
fprintf(stderr, "%s:%d:(%s): getting iovar \"%s\" failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, iovar, ret);
|
|
#define WL_BSSIOVAR_SETBUF(ifname, iovar, bssidx, param, paramlen, buf, buflen) \
|
|
if ((ret = wl_bssiovar_setbuf(ifname, iovar, bssidx, param, paramlen, buf, buflen))) \
|
|
fprintf(stderr, "%s:%d:(%s): setting bsscfg #%d iovar \"%s\" failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, bssidx, iovar, ret);
|
|
#define WL_BSSIOVAR_SET(ifname, iovar, bssidx, param, paramlen) \
|
|
if ((ret = wl_bssiovar_set(ifname, iovar, bssidx, param, paramlen))) \
|
|
fprintf(stderr, "%s:%d:(%s): setting bsscfg #%d iovar \"%s\" failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, bssidx, iovar, ret);
|
|
#define WL_BSSIOVAR_GET(ifname, iovar, bssidx, param, paramlen) \
|
|
if ((ret = wl_bssiovar_get(ifname, iovar, bssidx, param, paramlen))) \
|
|
fprintf(stderr, "%s:%d:(%s): getting bsscfg #%d iovar \"%s\" failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, bssidx, iovar, ret);
|
|
#define WL_BSSIOVAR_SETINT(ifname, iovar, bssidx, val) \
|
|
if ((ret = wl_bssiovar_setint(ifname, iovar, bssidx, val))) \
|
|
fprintf(stderr, "%s:%d:(%s): setting bsscfg #%d iovar \"%s\" " \
|
|
"to val 0x%x failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, bssidx, iovar, (unsigned int)val, ret);
|
|
#ifdef __CONFIG_DHDAP__
|
|
#define DHD_BSSIOVAR_SETINT(ifname, iovar, bssidx, val) \
|
|
if ((ret = dhd_bssiovar_setint(ifname, iovar, bssidx, val))) \
|
|
fprintf(stderr, "%s:%d:(%s): setting bsscfg #%d iovar \"%s\" " \
|
|
"to val 0x%x failed, err = %d\n", \
|
|
__FUNCTION__, __LINE__, ifname, bssidx, iovar, (unsigned int)val, ret);
|
|
#endif
|
|
#else
|
|
#define WLCONF_DBG(fmt, arg...)
|
|
#define WL_IOCTL(name, cmd, buf, len) (ret = wl_ioctl(name, cmd, buf, len))
|
|
#define WL_SETINT(name, cmd, val) (ret = wlconf_setint(name, cmd, val))
|
|
#define WL_GETINT(name, cmd, pval) (ret = wlconf_getint(name, cmd, pval))
|
|
#define WL_IOVAR_SET(ifname, iovar, param, paramlen) (ret = wl_iovar_set(ifname, iovar, \
|
|
param, paramlen))
|
|
#define WL_IOVAR_GET(ifname, iovar, param, paramlen) (ret = wl_iovar_get(ifname, iovar, \
|
|
param, paramlen))
|
|
#define WL_IOVAR_SETINT(ifname, iovar, val) (ret = wl_iovar_setint(ifname, iovar, val))
|
|
#define WL_IOVAR_GETINT(ifname, iovar, val) (ret = wl_iovar_getint(ifname, iovar, val))
|
|
#define WL_BSSIOVAR_SETBUF(ifname, iovar, bssidx, param, paramlen, buf, buflen) \
|
|
(ret = wl_bssiovar_setbuf(ifname, iovar, bssidx, param, paramlen, buf, buflen))
|
|
#define WL_BSSIOVAR_SET(ifname, iovar, bssidx, param, paramlen) \
|
|
(ret = wl_bssiovar_set(ifname, iovar, bssidx, param, paramlen))
|
|
#define WL_BSSIOVAR_GET(ifname, iovar, bssidx, param, paramlen) \
|
|
(ret = wl_bssiovar_get(ifname, iovar, bssidx, param, paramlen))
|
|
#define WL_BSSIOVAR_SETINT(ifname, iovar, bssidx, val) (ret = wl_bssiovar_setint(ifname, iovar, \
|
|
bssidx, val))
|
|
#ifdef __CONFIG_DHDAP__
|
|
#define DHD_BSSIOVAR_SETINT(ifname, iovar, bssidx, val) (ret = dhd_bssiovar_setint(ifname, iovar, \
|
|
bssidx, val))
|
|
#endif
|
|
#endif /* BCMDBG */
|
|
|
|
#define CHECK_PSK(mode) ((mode) & (WPA_AUTH_PSK | WPA2_AUTH_PSK))
|
|
#ifdef __CONFIG_DHDAP__
|
|
#define DHD_MAX_ASSOC_VAL 32
|
|
#define DHD_BSS_MAXASSOC_VAL 32
|
|
#endif
|
|
|
|
#ifdef MFP
|
|
#define WL_MFP_DISABLE 0X00
|
|
#define WL_MFP_CAPABLE 0X01
|
|
#define WL_MFP_REQUIRED 0X02
|
|
#endif /* ifdef MFP */
|
|
|
|
/* prototypes */
|
|
struct bsscfg_list *wlconf_get_bsscfgs(char* ifname, char* prefix);
|
|
int wlconf(char *name);
|
|
int wlconf_down(char *name);
|
|
|
|
static int
|
|
wlconf_getint(char* ifname, int cmd, int *pval)
|
|
{
|
|
return wl_ioctl(ifname, cmd, pval, sizeof(int));
|
|
}
|
|
|
|
static int
|
|
wlconf_setint(char* ifname, int cmd, int val)
|
|
{
|
|
return wl_ioctl(ifname, cmd, &val, sizeof(int));
|
|
}
|
|
|
|
static int
|
|
wlconf_wds_clear(char *name)
|
|
{
|
|
struct maclist maclist;
|
|
int ret;
|
|
|
|
maclist.count = 0;
|
|
WL_IOCTL(name, WLC_SET_WDSLIST, &maclist, sizeof(maclist));
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* set WEP key */
|
|
static int
|
|
wlconf_set_wep_key(char *name, char *prefix, int bsscfg_idx, int i)
|
|
{
|
|
wl_wsec_key_t key;
|
|
char wl_key[] = "wlXXXXXXXXXX_keyXXXXXXXXXX";
|
|
char *keystr, hex[] = "XX";
|
|
unsigned char *data = key.data;
|
|
int ret = 0;
|
|
|
|
memset(&key, 0, sizeof(key));
|
|
key.index = i - 1;
|
|
sprintf(wl_key, "%skey%d", prefix, i);
|
|
keystr = nvram_safe_get(wl_key);
|
|
|
|
switch (strlen(keystr)) {
|
|
case WEP1_KEY_SIZE:
|
|
case WEP128_KEY_SIZE:
|
|
key.len = strlen(keystr);
|
|
strcpy((char *)key.data, keystr);
|
|
break;
|
|
case WEP1_KEY_HEX_SIZE:
|
|
case WEP128_KEY_HEX_SIZE:
|
|
key.len = strlen(keystr) / 2;
|
|
while (*keystr) {
|
|
strncpy(hex, keystr, 2);
|
|
*data++ = (unsigned char) strtoul(hex, NULL, 16);
|
|
keystr += 2;
|
|
}
|
|
break;
|
|
default:
|
|
key.len = 0;
|
|
break;
|
|
}
|
|
|
|
/* Set current WEP key */
|
|
if (key.len && i == atoi(nvram_safe_get(strlcat_r(prefix, "key", wl_key, sizeof(wl_key)))))
|
|
key.flags = WL_PRIMARY_KEY;
|
|
|
|
WL_BSSIOVAR_SET(name, "wsec_key", bsscfg_idx, &key, sizeof(key));
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
wlconf_akm_options(char *prefix)
|
|
{
|
|
char comb[32];
|
|
char *wl_akm;
|
|
int akm_ret_val = 0;
|
|
char akm[32];
|
|
char *next;
|
|
|
|
wl_akm = nvram_safe_get(strlcat_r(prefix, "akm", comb, sizeof(comb)));
|
|
foreach(akm, wl_akm, next) {
|
|
if (!strcmp(akm, "wpa"))
|
|
akm_ret_val |= WPA_AUTH_UNSPECIFIED;
|
|
if (!strcmp(akm, "psk"))
|
|
akm_ret_val |= WPA_AUTH_PSK;
|
|
if (!strcmp(akm, "wpa2"))
|
|
akm_ret_val |= WPA2_AUTH_UNSPECIFIED;
|
|
if (!strcmp(akm, "psk2"))
|
|
akm_ret_val |= WPA2_AUTH_PSK;
|
|
if (!strcmp(akm, "brcm_psk"))
|
|
akm_ret_val |= BRCM_AUTH_PSK;
|
|
}
|
|
return akm_ret_val;
|
|
}
|
|
|
|
/* Set up wsec */
|
|
static int
|
|
wlconf_set_wsec(char *ifname, char *prefix, int bsscfg_idx)
|
|
{
|
|
char tmp[100];
|
|
int val = 0;
|
|
int akm_val;
|
|
int ret;
|
|
|
|
/* Set wsec bitvec */
|
|
akm_val = wlconf_akm_options(prefix);
|
|
if (akm_val != 0) {
|
|
if (nvram_match(strlcat_r(prefix, "crypto", tmp, sizeof(tmp)), "tkip"))
|
|
val = TKIP_ENABLED;
|
|
else if (nvram_match(strlcat_r(prefix, "crypto", tmp, sizeof(tmp)), "aes"))
|
|
val = AES_ENABLED;
|
|
else if (nvram_match(strlcat_r(prefix, "crypto", tmp, sizeof(tmp)), "tkip+aes"))
|
|
val = TKIP_ENABLED | AES_ENABLED;
|
|
}
|
|
if (nvram_match(strlcat_r(prefix, "wep", tmp, sizeof(tmp)), "enabled"))
|
|
val |= WEP_ENABLED;
|
|
WL_BSSIOVAR_SETINT(ifname, "wsec", bsscfg_idx, val);
|
|
/* Set wsec restrict if WSEC_ENABLED */
|
|
WL_BSSIOVAR_SETINT(ifname, "wsec_restrict", bsscfg_idx, val ? 1 : 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
wlconf_set_preauth(char *name, int bsscfg_idx, int preauth)
|
|
{
|
|
uint cap;
|
|
int ret;
|
|
|
|
WL_BSSIOVAR_GET(name, "wpa_cap", bsscfg_idx, &cap, sizeof(uint));
|
|
if (ret != 0) return -1;
|
|
|
|
if (preauth)
|
|
cap |= WPA_CAP_WPA2_PREAUTH;
|
|
else
|
|
cap &= ~WPA_CAP_WPA2_PREAUTH;
|
|
|
|
WL_BSSIOVAR_SETINT(name, "wpa_cap", bsscfg_idx, cap);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
wlconf_dfs_pref_chan_options(char *name)
|
|
{
|
|
char val[32], *next;
|
|
wl_dfs_forced_t *dfs_frcd = NULL;
|
|
uint ioctl_size;
|
|
chanspec_t chanspec;
|
|
int ret;
|
|
wl_dfs_forced_t inp;
|
|
|
|
dfs_frcd = (wl_dfs_forced_t *) malloc(WL_DFS_FORCED_PARAMS_MAX_SIZE);
|
|
if (!dfs_frcd) {
|
|
return;
|
|
}
|
|
|
|
memset(dfs_frcd, 0, WL_DFS_FORCED_PARAMS_MAX_SIZE);
|
|
memset(&inp, 0, sizeof(wl_dfs_forced_t));
|
|
|
|
inp.version = DFS_PREFCHANLIST_VER;
|
|
wl_iovar_getbuf(name, "dfs_channel_forced", &inp, sizeof(wl_dfs_forced_t),
|
|
dfs_frcd, WL_DFS_FORCED_PARAMS_MAX_SIZE);
|
|
|
|
if (dfs_frcd->version != DFS_PREFCHANLIST_VER) {
|
|
free(dfs_frcd);
|
|
return;
|
|
}
|
|
|
|
dfs_frcd->chspec_list.num = 0;
|
|
foreach(val, nvram_safe_get("wl_dfs_pref"), next) {
|
|
if (atoi(val)) {
|
|
chanspec = wf_chspec_aton(val);
|
|
/* Maximum 6 entries supported in UI */
|
|
if (dfs_frcd->chspec_list.num > 6) {
|
|
free(dfs_frcd);
|
|
return;
|
|
}
|
|
dfs_frcd->chspec_list.list[dfs_frcd->chspec_list.num++] = chanspec;
|
|
}
|
|
}
|
|
|
|
ioctl_size = WL_DFS_FORCED_PARAMS_FIXED_SIZE +
|
|
(dfs_frcd->chspec_list.num * sizeof(chanspec_t));
|
|
dfs_frcd->version = DFS_PREFCHANLIST_VER;
|
|
WL_IOVAR_SET(name, "dfs_channel_forced", dfs_frcd, ioctl_size);
|
|
|
|
free(dfs_frcd);
|
|
return;
|
|
}
|
|
|
|
static void
|
|
wlconf_set_radarthrs(char *name, char *prefix)
|
|
{
|
|
wl_radar_thr_t radar_thr;
|
|
int i, ret, len;
|
|
char nv_buf[NVRAM_MAX_VALUE_LEN], *rargs, *v, *endptr;
|
|
char buf[WLC_IOCTL_SMLEN];
|
|
|
|
char *version = NULL;
|
|
char *thr0_20_lo = NULL, *thr1_20_lo = NULL;
|
|
char *thr0_40_lo = NULL, *thr1_40_lo = NULL;
|
|
char *thr0_80_lo = NULL, *thr1_80_lo = NULL;
|
|
char *thr0_20_hi = NULL, *thr1_20_hi = NULL;
|
|
char *thr0_40_hi = NULL, *thr1_40_hi = NULL;
|
|
char *thr0_80_hi = NULL, *thr1_80_hi = NULL;
|
|
|
|
char **locals[] = { &version, &thr0_20_lo, &thr1_20_lo, &thr0_40_lo, &thr1_40_lo,
|
|
&thr0_80_lo, &thr1_80_lo, &thr0_20_hi, &thr1_20_hi,
|
|
&thr0_40_hi, &thr1_40_hi, &thr0_80_hi, &thr1_80_hi };
|
|
|
|
rargs = nvram_safe_get(strlcat_r(prefix, "radarthrs", nv_buf, sizeof(nv_buf)));
|
|
if (!rargs)
|
|
goto err;
|
|
|
|
len = strlen(rargs);
|
|
if ((len > NVRAM_MAX_VALUE_LEN) || (len == 0))
|
|
goto err;
|
|
|
|
memset(nv_buf, 0, sizeof(nv_buf));
|
|
strncpy(nv_buf, rargs, len);
|
|
v = nv_buf;
|
|
for (i = 0; i < (sizeof(locals) / sizeof(locals[0])); i++) {
|
|
*locals[i] = v;
|
|
while (*v && *v != ' ') {
|
|
v++;
|
|
}
|
|
if (*v) {
|
|
*v = 0;
|
|
v++;
|
|
}
|
|
if (v >= (nv_buf + len)) /* Check for complete list, if not caught later */
|
|
break;
|
|
}
|
|
|
|
/* Start building request */
|
|
memset(buf, 0, sizeof(buf));
|
|
strcpy(buf, "radarthrs");
|
|
/* Retrieve radar thrs parameters */
|
|
if (!version)
|
|
goto err;
|
|
radar_thr.version = atoi(version);
|
|
if (radar_thr.version > WL_RADAR_THR_VERSION)
|
|
goto err;
|
|
|
|
/* Retrieve ver 0 params */
|
|
if (!thr0_20_lo)
|
|
goto err;
|
|
radar_thr.thresh0_20_lo = (uint16)strtol(thr0_20_lo, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr1_20_lo)
|
|
goto err;
|
|
radar_thr.thresh1_20_lo = (uint16)strtol(thr1_20_lo, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr0_40_lo)
|
|
goto err;
|
|
radar_thr.thresh0_40_lo = (uint16)strtol(thr0_40_lo, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr1_40_lo)
|
|
goto err;
|
|
radar_thr.thresh1_40_lo = (uint16)strtol(thr1_40_lo, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr0_80_lo)
|
|
goto err;
|
|
radar_thr.thresh0_80_lo = (uint16)strtol(thr0_80_lo, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr1_80_lo)
|
|
goto err;
|
|
radar_thr.thresh1_80_lo = (uint16)strtol(thr1_80_lo, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
|
|
if (radar_thr.version == 0) {
|
|
/*
|
|
* Attempt a best effort update of ver 0 to ver 1 by updating
|
|
* the appropriate values with the specified defaults. The defaults
|
|
* are from the reference design.
|
|
*/
|
|
radar_thr.version = WL_RADAR_THR_VERSION; /* avoid driver rejecting it */
|
|
radar_thr.thresh0_20_hi = 0x6ac;
|
|
radar_thr.thresh1_20_hi = 0x6cc;
|
|
radar_thr.thresh0_40_hi = 0x6bc;
|
|
radar_thr.thresh1_40_hi = 0x6e0;
|
|
radar_thr.thresh0_80_hi = 0x6b0;
|
|
radar_thr.thresh1_80_hi = 0x30;
|
|
} else {
|
|
/* Retrieve ver 1 params */
|
|
if (!thr0_20_hi)
|
|
goto err;
|
|
radar_thr.thresh0_20_hi = (uint16)strtol(thr0_20_hi, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr1_20_hi)
|
|
goto err;
|
|
radar_thr.thresh1_20_hi = (uint16)strtol(thr1_20_hi, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr0_40_hi)
|
|
goto err;
|
|
radar_thr.thresh0_40_hi = (uint16)strtol(thr0_40_hi, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr1_40_hi)
|
|
goto err;
|
|
radar_thr.thresh1_40_hi = (uint16)strtol(thr1_40_hi, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr0_80_hi)
|
|
goto err;
|
|
radar_thr.thresh0_80_hi = (uint16)strtol(thr0_80_hi, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
if (!thr1_80_hi)
|
|
goto err;
|
|
radar_thr.thresh1_80_hi = (uint16)strtol(thr1_80_hi, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Copy radar parameters into buffer and plug them to the driver */
|
|
memcpy((char*)(buf + strlen(buf) + 1), (char*)&radar_thr, sizeof(wl_radar_thr_t));
|
|
WL_IOCTL(name, WLC_SET_VAR, buf, sizeof(buf));
|
|
|
|
return;
|
|
|
|
err:
|
|
WLCONF_DBG("Did not parse radar thrs params, using driver defaults\n");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This allows phy antenna selection to be retrieved from NVRAM
|
|
*/
|
|
static void
|
|
wlconf_set_antsel(char *name, char *prefix)
|
|
{
|
|
int i, j, len, argc, ret;
|
|
char buf[WLC_IOCTL_SMLEN];
|
|
wlc_antselcfg_t val = { {0}, 0};
|
|
char *argv[ANT_SELCFG_MAX] = {};
|
|
char nv_buf[NVRAM_MAX_VALUE_LEN], *argstr, *v, *endptr;
|
|
|
|
argstr = nvram_safe_get(strlcat_r(prefix, "phy_antsel", nv_buf, sizeof(nv_buf)));
|
|
if (!argstr) {
|
|
return;
|
|
}
|
|
len = strlen(argstr);
|
|
if ((len == 0) || (len > NVRAM_MAX_VALUE_LEN)) {
|
|
return;
|
|
}
|
|
|
|
memset(nv_buf, 0, sizeof(nv_buf));
|
|
strncpy(nv_buf, argstr, len);
|
|
v = nv_buf;
|
|
for (argc = 0; argc < ANT_SELCFG_MAX; ) {
|
|
argv[argc++] = v;
|
|
while (*v && *v != ' ') {
|
|
v++;
|
|
}
|
|
if (*v) {
|
|
*v = 0;
|
|
v++;
|
|
}
|
|
if (v >= (nv_buf + len)) {
|
|
break;
|
|
}
|
|
}
|
|
if ((argc != 1) && (argc != ANT_SELCFG_MAX)) {
|
|
WLCONF_DBG("phy_antsel requires 1 or %d arguments\n", ANT_SELCFG_MAX);
|
|
return;
|
|
}
|
|
|
|
memset(buf, 0, sizeof(buf));
|
|
strcpy(buf, "phy_antsel");
|
|
for (i = 0, j = 0; i < ANT_SELCFG_MAX; i++) {
|
|
val.ant_config[i] = (uint8)strtol(argv[j], &endptr, 0);
|
|
if (*endptr != '\0') {
|
|
WLCONF_DBG("Invalid antsel argument\n");
|
|
return;
|
|
}
|
|
if (argc > 1) {
|
|
/* ANT_SELCFG_MAX argument format */
|
|
j++;
|
|
}
|
|
}
|
|
|
|
/* Copy antsel parameters into buffer and plug them to the driver */
|
|
memcpy((char*)(buf + strlen(buf) + 1), (char*)&val, sizeof(wlc_antselcfg_t));
|
|
WL_IOCTL(name, WLC_SET_VAR, buf, sizeof(buf));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
wlconf_set_current_txparam_into_nvram(char *name, char *prefix)
|
|
{
|
|
int ret, aci;
|
|
wme_tx_params_t txparams[AC_COUNT];
|
|
char *nv[] = {"wme_txp_be", "wme_txp_bk", "wme_txp_vi", "wme_txp_vo"};
|
|
char data[50], tmp[50];
|
|
|
|
/* get the WME tx parameters */
|
|
WL_IOVAR_GET(name, "wme_tx_params", txparams, sizeof(txparams));
|
|
|
|
/* Set nvram accordingly */
|
|
for (aci = 0; aci < AC_COUNT; aci++) {
|
|
sprintf(data, "%d %d %d %d %d", txparams[aci].short_retry,
|
|
txparams[aci].short_fallback,
|
|
txparams[aci].long_retry,
|
|
txparams[aci].long_fallback,
|
|
txparams[aci].max_rate);
|
|
|
|
nvram_set(strlcat_r(prefix, nv[aci], tmp, sizeof(tmp)), data);
|
|
}
|
|
}
|
|
|
|
/* Set up WME */
|
|
static void
|
|
wlconf_set_wme(char *name, char *prefix)
|
|
{
|
|
int i, j, k;
|
|
int val, ret;
|
|
int phytype, gmode, no_ack, apsd, dp[2];
|
|
edcf_acparam_t *acparams;
|
|
/* Pay attention to buffer length requirements when using this */
|
|
char buf[WLC_IOCTL_SMLEN*2];
|
|
char *v, *nv_value, nv[100];
|
|
char nv_name[] = "%swme_%s_%s";
|
|
char *ac[] = {"be", "bk", "vi", "vo"};
|
|
char *cwmin, *cwmax, *aifsn, *txop_b, *txop_ag, *admin_forced, *oldest_first;
|
|
char **locals[] = { &cwmin, &cwmax, &aifsn, &txop_b, &txop_ag, &admin_forced,
|
|
&oldest_first };
|
|
struct {char *req; char *str;} mode[] = {{"wme_ac_ap", "ap"}, {"wme_ac_sta", "sta"},
|
|
{"wme_tx_params", "txp"}};
|
|
|
|
/* query the phy type */
|
|
WL_IOCTL(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
|
|
/* get gmode */
|
|
gmode = atoi(nvram_safe_get(strlcat_r(prefix, "gmode", nv, sizeof(nv))));
|
|
|
|
/* WME sta setting first */
|
|
for (i = 0; i < 2; i++) {
|
|
/* build request block */
|
|
memset(buf, 0, sizeof(buf));
|
|
strncpy(buf, mode[i].req, sizeof(buf)-1);
|
|
/* put push wmeac params after "wme-ac" in buf */
|
|
acparams = (edcf_acparam_t *)(buf + strlen(buf) + 1);
|
|
dp[i] = 0;
|
|
for (j = 0; j < AC_COUNT; j++) {
|
|
/* get packed nvram parameter */
|
|
snprintf(nv, sizeof(nv), nv_name, prefix, mode[i].str, ac[j]);
|
|
nv_value = nvram_safe_get(nv);
|
|
strncpy(nv, nv_value, sizeof(nv)-1);
|
|
nv[sizeof(nv)-1] = '\0';
|
|
/* unpack it */
|
|
v = nv;
|
|
for (k = 0; k < (sizeof(locals) / sizeof(locals[0])); k++) {
|
|
*locals[k] = v;
|
|
while (*v && *v != ' ')
|
|
v++;
|
|
if (*v) {
|
|
*v = 0;
|
|
v++;
|
|
}
|
|
}
|
|
|
|
/* update CWmin */
|
|
acparams->ECW &= ~EDCF_ECWMIN_MASK;
|
|
val = atoi(cwmin);
|
|
for (val++, k = 0; val; val >>= 1, k++);
|
|
acparams->ECW |= (k ? k - 1 : 0) & EDCF_ECWMIN_MASK;
|
|
/* update CWmax */
|
|
acparams->ECW &= ~EDCF_ECWMAX_MASK;
|
|
val = atoi(cwmax);
|
|
for (val++, k = 0; val; val >>= 1, k++);
|
|
acparams->ECW |= ((k ? k - 1 : 0) << EDCF_ECWMAX_SHIFT) & EDCF_ECWMAX_MASK;
|
|
/* update AIFSN */
|
|
acparams->ACI &= ~EDCF_AIFSN_MASK;
|
|
acparams->ACI |= atoi(aifsn) & EDCF_AIFSN_MASK;
|
|
/* update ac */
|
|
acparams->ACI &= ~EDCF_ACI_MASK;
|
|
acparams->ACI |= j << EDCF_ACI_SHIFT;
|
|
/* update TXOP */
|
|
if (phytype == PHY_TYPE_B || gmode == 0)
|
|
val = atoi(txop_b);
|
|
else
|
|
val = atoi(txop_ag);
|
|
acparams->TXOP = val / 32;
|
|
/* update acm */
|
|
acparams->ACI &= ~EDCF_ACM_MASK;
|
|
val = strcmp(admin_forced, "on") ? 0 : 1;
|
|
acparams->ACI |= val << 4;
|
|
|
|
/* configure driver */
|
|
WL_IOCTL(name, WLC_SET_VAR, buf, sizeof(buf));
|
|
}
|
|
}
|
|
|
|
/* set no-ack */
|
|
v = nvram_safe_get(strlcat_r(prefix, "wme_no_ack", nv, sizeof(nv)));
|
|
no_ack = strcmp(v, "on") ? 0 : 1;
|
|
WL_IOVAR_SETINT(name, "wme_noack", no_ack);
|
|
|
|
/* set APSD */
|
|
v = nvram_safe_get(strlcat_r(prefix, "wme_apsd", nv, sizeof(nv)));
|
|
apsd = strcmp(v, "on") ? 0 : 1;
|
|
WL_IOVAR_SETINT(name, "wme_apsd", apsd);
|
|
|
|
/* set per-AC discard policy */
|
|
strcpy(buf, "wme_dp");
|
|
WL_IOVAR_SETINT(name, "wme_dp", dp[1]);
|
|
|
|
/* WME Tx parameters setting */
|
|
{
|
|
wme_tx_params_t txparams[AC_COUNT];
|
|
char *srl, *sfbl, *lrl, *lfbl, *maxrate;
|
|
char **locals[] = { &srl, &sfbl, &lrl, &lfbl, &maxrate };
|
|
|
|
/* build request block */
|
|
memset(txparams, 0, sizeof(txparams));
|
|
|
|
for (j = 0; j < AC_COUNT; j++) {
|
|
/* get packed nvram parameter */
|
|
snprintf(nv, sizeof(nv), nv_name, prefix, mode[2].str, ac[j]);
|
|
nv_value = nvram_safe_get(nv);
|
|
strncpy(nv, nv_value, sizeof(nv)-1);
|
|
nv[sizeof(nv)-1] = '\0';
|
|
/* unpack it */
|
|
v = nv;
|
|
for (k = 0; k < (sizeof(locals) / sizeof(locals[0])); k++) {
|
|
*locals[k] = v;
|
|
while (*v && *v != ' ')
|
|
v++;
|
|
if (*v) {
|
|
*v = 0;
|
|
v++;
|
|
}
|
|
}
|
|
|
|
/* update short retry limit */
|
|
txparams[j].short_retry = atoi(srl);
|
|
|
|
/* update short fallback limit */
|
|
txparams[j].short_fallback = atoi(sfbl);
|
|
|
|
/* update long retry limit */
|
|
txparams[j].long_retry = atoi(lrl);
|
|
|
|
/* update long fallback limit */
|
|
txparams[j].long_fallback = atoi(lfbl);
|
|
|
|
/* update max rate */
|
|
txparams[j].max_rate = atoi(maxrate);
|
|
}
|
|
|
|
/* set the WME tx parameters */
|
|
WL_IOVAR_SET(name, mode[2].req, txparams, sizeof(txparams));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#if defined(linux) || defined(__NetBSD__)
|
|
#include <unistd.h>
|
|
static void
|
|
sleep_ms(const unsigned int ms)
|
|
{
|
|
usleep(1000*ms);
|
|
}
|
|
#elif defined(__ECOS)
|
|
static void
|
|
sleep_ms(const unsigned int ms)
|
|
{
|
|
cyg_tick_count_t ostick;
|
|
|
|
ostick = ms / 10;
|
|
cyg_thread_delay(ostick);
|
|
}
|
|
#else
|
|
#error "sleep_ms() not defined for this OS!!!"
|
|
#endif /* defined(linux) */
|
|
|
|
/*
|
|
* The following condition(s) must be met when Auto Channel Selection
|
|
* is enabled.
|
|
* - the I/F is up (change radio channel requires it is up?)
|
|
* - the AP must not be associated (setting SSID to empty should
|
|
* make sure it for us)
|
|
*/
|
|
static uint8
|
|
wlconf_auto_channel(char *name)
|
|
{
|
|
int chosen = 0;
|
|
wl_uint32_list_t request;
|
|
int phytype;
|
|
int ret;
|
|
int i;
|
|
|
|
/* query the phy type */
|
|
WL_GETINT(name, WLC_GET_PHYTYPE, &phytype);
|
|
|
|
request.count = 0; /* let the ioctl decide */
|
|
WL_IOCTL(name, WLC_START_CHANNEL_SEL, &request, sizeof(request));
|
|
if (!ret) {
|
|
sleep_ms(phytype == PHY_TYPE_A ? 1000 : 750);
|
|
for (i = 0; i < 100; i++) {
|
|
WL_GETINT(name, WLC_GET_CHANNEL_SEL, &chosen);
|
|
if (!ret)
|
|
break;
|
|
sleep_ms(100);
|
|
}
|
|
}
|
|
WLCONF_DBG("interface %s: channel selected %d\n", name, chosen);
|
|
return chosen;
|
|
}
|
|
|
|
static chanspec_t
|
|
wlconf_auto_chanspec(char *name)
|
|
{
|
|
chanspec_t chosen = 0;
|
|
int temp = 0;
|
|
wl_uint32_list_t request;
|
|
int ret;
|
|
int i;
|
|
|
|
request.count = 0; /* let the ioctl decide */
|
|
WL_IOCTL(name, WLC_START_CHANNEL_SEL, &request, sizeof(request));
|
|
if (!ret) {
|
|
/* this time needs to be < 1000 to prevent mpc kicking in for 2nd radio */
|
|
sleep_ms(500);
|
|
for (i = 0; i < 100; i++) {
|
|
WL_IOVAR_GETINT(name, "apcschspec", &temp);
|
|
if (!ret)
|
|
break;
|
|
sleep_ms(100);
|
|
}
|
|
}
|
|
|
|
chosen = (chanspec_t) temp;
|
|
WLCONF_DBG("interface %s: chanspec selected %04x\n", name, chosen);
|
|
return chosen;
|
|
}
|
|
|
|
/* PHY type/BAND conversion */
|
|
#define WLCONF_PHYTYPE2BAND(phy) ((phy) == PHY_TYPE_A ? WLC_BAND_5G : WLC_BAND_2G)
|
|
/* PHY type conversion */
|
|
#define WLCONF_PHYTYPE2STR(phy) ((phy) == PHY_TYPE_A ? "a" : \
|
|
(phy) == PHY_TYPE_B ? "b" : \
|
|
(phy) == PHY_TYPE_LP ? "l" : \
|
|
(phy) == PHY_TYPE_G ? "g" : \
|
|
(phy) == PHY_TYPE_SSN ? "s" : \
|
|
(phy) == PHY_TYPE_HT ? "h" : \
|
|
(phy) == PHY_TYPE_AC ? "v" : \
|
|
(phy) == PHY_TYPE_LCN ? "c" : "n")
|
|
#define WLCONF_STR2PHYTYPE(ch) ((ch) == 'a' ? PHY_TYPE_A : \
|
|
(ch) == 'b' ? PHY_TYPE_B : \
|
|
(ch) == 'l' ? PHY_TYPE_LP : \
|
|
(ch) == 'g' ? PHY_TYPE_G : \
|
|
(ch) == 's' ? PHY_TYPE_SSN : \
|
|
(ch) == 'h' ? PHY_TYPE_HT : \
|
|
(ch) == 'v' ? PHY_TYPE_AC : \
|
|
(ch) == 'c' ? PHY_TYPE_LCN : PHY_TYPE_N)
|
|
|
|
#define PREFIX_LEN 32 /* buffer size for wlXXX_ prefix */
|
|
|
|
#define WLCONF_PHYTYPE_11N(phy) ((phy) == PHY_TYPE_N || (phy) == PHY_TYPE_SSN || \
|
|
(phy) == PHY_TYPE_LCN || (phy) == PHY_TYPE_HT || \
|
|
(phy) == PHY_TYPE_AC)
|
|
|
|
struct bsscfg_info {
|
|
int idx; /* bsscfg index */
|
|
char ifname[PREFIX_LEN]; /* OS name of interface (debug only) */
|
|
char prefix[PREFIX_LEN]; /* prefix for nvram params (eg. "wl0.1_") */
|
|
};
|
|
|
|
struct bsscfg_list {
|
|
int count;
|
|
struct bsscfg_info bsscfgs[WL_MAXBSSCFG];
|
|
};
|
|
|
|
struct bsscfg_list *
|
|
wlconf_get_bsscfgs(char* ifname, char* prefix)
|
|
{
|
|
char var[80];
|
|
char tmp[100];
|
|
char *next;
|
|
|
|
struct bsscfg_list *bclist;
|
|
struct bsscfg_info *bsscfg;
|
|
|
|
bclist = (struct bsscfg_list*)malloc(sizeof(struct bsscfg_list));
|
|
if (bclist == NULL)
|
|
return NULL;
|
|
memset(bclist, 0, sizeof(struct bsscfg_list));
|
|
|
|
/* Set up Primary BSS Config information */
|
|
bsscfg = &bclist->bsscfgs[0];
|
|
bsscfg->idx = 0;
|
|
strncpy(bsscfg->ifname, ifname, PREFIX_LEN-1);
|
|
strncpy(bsscfg->prefix, prefix, sizeof(bsscfg->prefix) - 1);
|
|
bclist->count = 1;
|
|
|
|
/* additional virtual BSS Configs from wlX_vifs */
|
|
foreach(var, nvram_safe_get(strlcat_r(prefix, "vifs", tmp, sizeof(tmp))), next) {
|
|
if (bclist->count == WL_MAXBSSCFG) {
|
|
WLCONF_DBG("wlconf(%s): exceeded max number of BSS Configs (%d)"
|
|
"in nvram %s\n"
|
|
"while configuring interface \"%s\"\n",
|
|
ifname, WL_MAXBSSCFG, strlcat_r(prefix, "vifs", tmp, sizeof(tmp)), var);
|
|
continue;
|
|
}
|
|
bsscfg = &bclist->bsscfgs[bclist->count];
|
|
if (get_ifname_unit(var, NULL, &bsscfg->idx) != 0) {
|
|
WLCONF_DBG("wlconfg(%s): unable to parse unit.subunit in interface "
|
|
"name \"%s\"\n",
|
|
ifname, var);
|
|
continue;
|
|
}
|
|
strncpy(bsscfg->ifname, var, PREFIX_LEN-1);
|
|
snprintf(bsscfg->prefix, PREFIX_LEN, "%s_", bsscfg->ifname);
|
|
bclist->count++;
|
|
}
|
|
|
|
return bclist;
|
|
}
|
|
|
|
static void
|
|
wlconf_config_join_pref(char *name, int bsscfg_idx, int auth_val)
|
|
{
|
|
int ret = 0, i = 0;
|
|
|
|
if ((auth_val & (WPA_AUTH_UNSPECIFIED | WPA2_AUTH_UNSPECIFIED)) ||
|
|
CHECK_PSK(auth_val)) {
|
|
uchar pref[] = {
|
|
/* WPA pref, 14 tuples */
|
|
0x02, 0xaa, 0x00, 0x0e,
|
|
/* WPA2 AES (unicast) AES (multicast) */
|
|
0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x04,
|
|
/* WPA AES (unicast) AES (multicast) */
|
|
0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x04,
|
|
/* WPA2 AES (unicast) TKIP (multicast) */
|
|
0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x02,
|
|
/* WPA AES (unicast) TKIP (multicast) */
|
|
0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x02,
|
|
/* WPA2 AES (unicast) WEP-40 (multicast) */
|
|
0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x01,
|
|
/* WPA AES (unicast) WEP-40 (multicast) */
|
|
0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x01,
|
|
/* WPA2 AES (unicast) WEP-128 (multicast) */
|
|
0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x05,
|
|
/* WPA AES (unicast) WEP-128 (multicast) */
|
|
0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x05,
|
|
/* WPA2 TKIP (unicast) TKIP (multicast) */
|
|
0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x02, 0x00, 0x0f, 0xac, 0x02,
|
|
/* WPA TKIP (unicast) TKIP (multicast) */
|
|
0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x50, 0xf2, 0x02,
|
|
/* WPA2 TKIP (unicast) WEP-40 (multicast) */
|
|
0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x02, 0x00, 0x0f, 0xac, 0x01,
|
|
/* WPA TKIP (unicast) WEP-40 (multicast) */
|
|
0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x50, 0xf2, 0x01,
|
|
/* WPA2 TKIP (unicast) WEP-128 (multicast) */
|
|
0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x02, 0x00, 0x0f, 0xac, 0x05,
|
|
/* WPA TKIP (unicast) WEP-128 (multicast) */
|
|
0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x50, 0xf2, 0x05,
|
|
/* RSSI pref */
|
|
0x01, 0x02, 0x00, 0x00,
|
|
};
|
|
|
|
if (CHECK_PSK(auth_val)) {
|
|
for (i = 0; i < pref[3]; i ++)
|
|
pref[7 + i * 12] = 0x02;
|
|
}
|
|
|
|
WL_BSSIOVAR_SET(name, "join_pref", bsscfg_idx, pref, sizeof(pref));
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
wlconf_security_options(char *name, char *prefix, int bsscfg_idx, bool id_supp,
|
|
bool check_join_pref)
|
|
{
|
|
int i;
|
|
int val;
|
|
int ret;
|
|
char tmp[100];
|
|
bool need_join_pref = FALSE;
|
|
#define AUTOWPA(cfg) ((cfg) == (WPA_AUTH_PSK | WPA2_AUTH_PSK))
|
|
|
|
/* Set WSEC */
|
|
/*
|
|
* Need to check errors (card may have changed) and change to
|
|
* defaults since the new chip may not support the requested
|
|
* encryptions after the card has been changed.
|
|
*/
|
|
if (wlconf_set_wsec(name, prefix, bsscfg_idx)) {
|
|
/* change nvram only, code below will pass them on */
|
|
nvram_restore_var(prefix, "auth_mode");
|
|
nvram_restore_var(prefix, "auth");
|
|
/* reset wep to default */
|
|
nvram_restore_var(prefix, "crypto");
|
|
nvram_restore_var(prefix, "wep");
|
|
wlconf_set_wsec(name, prefix, bsscfg_idx);
|
|
}
|
|
|
|
val = wlconf_akm_options(prefix);
|
|
if (!nvram_match(strlcat_r(prefix, "mode", tmp, sizeof(tmp)), "ap"))
|
|
need_join_pref = (check_join_pref || id_supp) && AUTOWPA(val);
|
|
|
|
if (need_join_pref)
|
|
wlconf_config_join_pref(name, bsscfg_idx, val);
|
|
|
|
/* enable in-driver wpa supplicant? */
|
|
if (id_supp && (CHECK_PSK(val))) {
|
|
wsec_pmk_t psk;
|
|
char *key;
|
|
|
|
if (((key = nvram_get(strlcat_r(prefix, "wpa_psk", tmp, sizeof(tmp)))) != NULL) &&
|
|
(strlen(key) < WSEC_MAX_PSK_LEN)) {
|
|
psk.key_len = (ushort) strlen(key);
|
|
psk.flags = WSEC_PASSPHRASE;
|
|
strcpy((char *)psk.key, key);
|
|
WL_IOCTL(name, WLC_SET_WSEC_PMK, &psk, sizeof(psk));
|
|
}
|
|
if (wl_iovar_setint(name, "sup_wpa", 1)) {
|
|
WLCONF_DBG("(%s):wl_iovar_setint(sup_wpa,1) failed\n", name);
|
|
}
|
|
}
|
|
|
|
if (!need_join_pref)
|
|
WL_BSSIOVAR_SETINT(name, "wpa_auth", bsscfg_idx, val);
|
|
|
|
/* EAP Restrict if we have an AKM or radius authentication */
|
|
val = ((val != 0) || (nvram_match(strlcat_r(prefix, "auth_mode", tmp, sizeof(tmp)), "radius")));
|
|
WL_BSSIOVAR_SETINT(name, "eap_restrict", bsscfg_idx, val);
|
|
|
|
/* Set WEP keys */
|
|
if (nvram_match(strlcat_r(prefix, "wep", tmp, sizeof(tmp)), "enabled")) {
|
|
for (i = 1; i <= DOT11_MAX_DEFAULT_KEYS; i++)
|
|
wlconf_set_wep_key(name, prefix, bsscfg_idx, i);
|
|
}
|
|
|
|
/* Set 802.11 authentication mode - open/shared */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "auth", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "auth", bsscfg_idx, val);
|
|
#ifdef MFP
|
|
/* Set MFP */
|
|
val = WPA_AUTH_DISABLED;
|
|
WL_BSSIOVAR_GET(name, "wpa_auth", bsscfg_idx, &val, sizeof(val));
|
|
if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
|
|
int phytype;
|
|
char var[8];
|
|
int band;
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "mfp", tmp, sizeof(tmp))));
|
|
if (val == -1) {
|
|
val = WL_MFP_DISABLE;
|
|
WL_GETINT(name, WLC_GET_PHYTYPE, &phytype);
|
|
WL_GETINT(name, WLC_GET_BAND, &band);
|
|
/* for 11AC-5G , enable MFP capable field */
|
|
if ((phytype == PHY_TYPE_AC) && (band == WLC_BAND_5G) &&
|
|
(nvram_match(strlcat_r(prefix, "mode", tmp, sizeof(tmp)), "ap")))
|
|
val = WL_MFP_CAPABLE;
|
|
snprintf(var, sizeof(var), "%d", val);
|
|
nvram_set(strlcat_r(prefix, "mfp", tmp, sizeof(tmp)), var);
|
|
}
|
|
WL_BSSIOVAR_SETINT(name, "mfp", bsscfg_idx, val);
|
|
}
|
|
#endif /* ifdef MFP */
|
|
}
|
|
|
|
static void
|
|
wlconf_set_ampdu_retry_limit(char *name, char *prefix)
|
|
{
|
|
int i, j, ret, nv_len;
|
|
struct ampdu_retry_tid retry_limit;
|
|
char *nv_name[2] = {"ampdu_rtylimit_tid", "ampdu_rr_rtylimit_tid"};
|
|
char *iov_name[2] = {"ampdu_retry_limit_tid", "ampdu_rr_retry_limit_tid"};
|
|
char *retry, *v, *nv_value, nv[100], tmp[100];
|
|
|
|
/* Get packed AMPDU (rr) retry limit per-tid from NVRAM if present */
|
|
for (i = 0; i < 2; i++) {
|
|
nv_value = nvram_safe_get(strlcat_r(prefix, nv_name[i], tmp, sizeof(tmp)));
|
|
nv_len = strlen(nv_value);
|
|
strcpy(nv, nv_value);
|
|
|
|
/* unpack it */
|
|
v = nv;
|
|
for (j = 0; nv_len >= 0 && j < NUMPRIO; j++) {
|
|
retry = v;
|
|
while (*v && *v != ' ') {
|
|
v++;
|
|
nv_len--;
|
|
}
|
|
if (*v) {
|
|
*v = 0;
|
|
v++;
|
|
nv_len--;
|
|
}
|
|
/* set the AMPDU retry limit per-tid */
|
|
retry_limit.tid = j;
|
|
retry_limit.retry = atoi(retry);
|
|
WL_IOVAR_SET(name, iov_name[i], &retry_limit, sizeof(retry_limit));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* When N-mode is ON, AMPDU, AMSDU are enabled/disabled
|
|
* based on the nvram setting. Only one of the AMPDU or AMSDU options is enabled any
|
|
* time. When N-mode is OFF or the device is non N-phy, AMPDU and AMSDU are turned off.
|
|
*
|
|
* WME/WMM is also set in this procedure as it depends on N.
|
|
* N ==> WMM is on by default
|
|
*
|
|
* Returns WME setting.
|
|
*/
|
|
static int
|
|
wlconf_ampdu_amsdu_set(char *name, char prefix[PREFIX_LEN], int nmode, int btc_mode)
|
|
{
|
|
bool ampdu_valid_option = FALSE;
|
|
bool amsdu_valid_option = FALSE;
|
|
int val, ampdu_option_val = OFF, amsdu_option_val = OFF;
|
|
int wme_option_val = ON; /* On by default */
|
|
char caps[WLC_IOCTL_MEDLEN], var[80], *next, *wme_val;
|
|
char buf[WLC_IOCTL_SMLEN];
|
|
int len = (sizeof("amsdu") - 1);
|
|
int ret, phytype;
|
|
wlc_rev_info_t rev;
|
|
#ifdef __CONFIG_DHDAP__
|
|
int is_dhd = 0;
|
|
#endif
|
|
#ifdef linux
|
|
struct utsname unamebuf;
|
|
|
|
uname(&unamebuf);
|
|
#endif
|
|
|
|
#ifdef __CONFIG_DHDAP__
|
|
is_dhd = !dhd_probe(name);
|
|
#endif
|
|
WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
|
|
|
|
WL_GETINT(name, WLC_GET_PHYTYPE, &phytype);
|
|
|
|
/* First, clear WMM settings to avoid conflicts */
|
|
WL_IOVAR_SETINT(name, "wme", OFF);
|
|
|
|
/* Get WME setting from NVRAM if present */
|
|
wme_val = nvram_get(strlcat_r(prefix, "wme", caps, sizeof(caps)));
|
|
if (wme_val && !strcmp(wme_val, "off")) {
|
|
wme_option_val = OFF;
|
|
}
|
|
|
|
/* Set options based on capability */
|
|
WL_IOVAR_GET(name, "cap", (void *)caps, sizeof(caps));
|
|
if (ret == 0) {
|
|
foreach(var, caps, next) {
|
|
char *nvram_str;
|
|
bool amsdu = 0;
|
|
|
|
/* Check for the capabilitiy 'amsdutx' */
|
|
if (strncmp(var, "amsdutx", sizeof(var)) == 0) {
|
|
var[len] = '\0';
|
|
amsdu = 1;
|
|
}
|
|
nvram_str = nvram_get(strlcat_r(prefix, var, buf, sizeof(buf)));
|
|
if (!nvram_str)
|
|
continue;
|
|
|
|
if (!strcmp(nvram_str, "on"))
|
|
val = ON;
|
|
else if (!strcmp(nvram_str, "off"))
|
|
val = OFF;
|
|
else if (!strcmp(nvram_str, "auto"))
|
|
val = AUTO;
|
|
else
|
|
continue;
|
|
|
|
if (strncmp(var, "ampdu", sizeof(var)) == 0) {
|
|
ampdu_valid_option = TRUE;
|
|
ampdu_option_val = val;
|
|
}
|
|
|
|
if (amsdu) {
|
|
amsdu_valid_option = TRUE;
|
|
amsdu_option_val = val;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (nmode != OFF) { /* N-mode is ON/AUTO */
|
|
if (ampdu_valid_option) {
|
|
if (ampdu_option_val != OFF) {
|
|
WL_IOVAR_SETINT(name, "amsdu", OFF);
|
|
WL_IOVAR_SETINT(name, "ampdu", ampdu_option_val);
|
|
} else {
|
|
WL_IOVAR_SETINT(name, "ampdu", OFF);
|
|
}
|
|
|
|
wlconf_set_ampdu_retry_limit(name, prefix);
|
|
|
|
#ifdef linux
|
|
/* For MIPS routers set the num mpdu per ampdu limit to 32. We observed
|
|
* that having this value at 32 helps with bi-di thru'put as well.
|
|
*/
|
|
if ((phytype == PHY_TYPE_AC) &&
|
|
(strncmp(unamebuf.machine, "mips", 4) == 0)) {
|
|
#ifndef __CONFIG_USBAP__
|
|
WL_IOVAR_SETINT(name, "ampdu_mpdu", 32);
|
|
#endif /* __CONFIG_USBAP__ */
|
|
}
|
|
#endif /* linux */
|
|
}
|
|
|
|
if (amsdu_valid_option) {
|
|
if (amsdu_option_val != OFF) { /* AMPDU (above) has priority over AMSDU */
|
|
if (rev.corerev >= 40) {
|
|
WL_IOVAR_SETINT(name, "amsdu", amsdu_option_val);
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* 43602 dhdap, ampdu_mpdu=32 gives good tput with amsdu */
|
|
if (is_dhd)
|
|
WL_IOVAR_SETINT(name, "ampdu_mpdu", 32);
|
|
#endif
|
|
} else if (ampdu_option_val == OFF) {
|
|
WL_IOVAR_SETINT(name, "ampdu", OFF);
|
|
WL_IOVAR_SETINT(name, "amsdu", amsdu_option_val);
|
|
}
|
|
} else {
|
|
WL_IOVAR_SETINT(name, "amsdu", OFF);
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* Use default ampdu_mpdu=-1 when amsdu is disabled */
|
|
if (is_dhd)
|
|
WL_IOVAR_SETINT(name, "ampdu_mpdu", -1);
|
|
#endif
|
|
}
|
|
} else {
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* Use default ampdu_mpdu=-1 when amsdu is disabled */
|
|
if (is_dhd)
|
|
WL_IOVAR_SETINT(name, "ampdu_mpdu", -1);
|
|
#endif
|
|
}
|
|
} else {
|
|
/* When N-mode is off or for non N-phy device, turn off AMPDU, AMSDU;
|
|
*/
|
|
WL_IOVAR_SETINT(name, "amsdu", OFF);
|
|
WL_IOVAR_SETINT(name, "ampdu", OFF);
|
|
}
|
|
|
|
if (wme_option_val) {
|
|
WL_IOVAR_SETINT(name, "wme", wme_option_val);
|
|
wlconf_set_wme(name, prefix);
|
|
}
|
|
|
|
return wme_option_val;
|
|
}
|
|
|
|
/* Get configured bandwidth cap. */
|
|
static int
|
|
wlconf_bw_cap(char *prefix, int bandtype)
|
|
{
|
|
char *str, tmp[100];
|
|
int bw_cap = WLC_BW_CAP_20MHZ;
|
|
|
|
if ((str = nvram_get(strlcat_r(prefix, "bw_cap", tmp, sizeof(tmp)))) != NULL)
|
|
bw_cap = atoi(str);
|
|
else {
|
|
/* Backward compatibility. Map to bandwidth cap bitmap values. */
|
|
int val = atoi(nvram_safe_get(strlcat_r(prefix, "nbw_cap", tmp, sizeof(tmp))));
|
|
|
|
if (((bandtype == WLC_BAND_2G) && (val == WLC_N_BW_40ALL)) ||
|
|
((bandtype == WLC_BAND_5G) &&
|
|
(val == WLC_N_BW_40ALL || val == WLC_N_BW_20IN2G_40IN5G)))
|
|
bw_cap = WLC_BW_CAP_40MHZ;
|
|
else
|
|
bw_cap = WLC_BW_CAP_20MHZ;
|
|
}
|
|
|
|
return bw_cap;
|
|
}
|
|
|
|
/* Set up TxBF. Called when i/f is down. */
|
|
static void wlconf_set_txbf(char *name, char *prefix)
|
|
{
|
|
char *str, tmp[100];
|
|
wlc_rev_info_t rev;
|
|
uint32 txbf_bfe_cap = 0;
|
|
uint32 txbf_bfr_cap = 0;
|
|
uint32 txbf_imp = 0;
|
|
int ret = 0;
|
|
|
|
WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
|
|
|
|
if (rev.corerev < 40) return; /* TxBF unsupported */
|
|
|
|
if ((str = nvram_get(strlcat_r(prefix, "txbf_bfr_cap", tmp, sizeof(tmp)))) != NULL) {
|
|
txbf_bfr_cap = atoi(str);
|
|
|
|
if (txbf_bfr_cap) {
|
|
/* Turning TxBF on (order matters) */
|
|
WL_IOVAR_SETINT(name, "txbf_bfr_cap", 1);
|
|
WL_IOVAR_SETINT(name, "txbf", 1);
|
|
} else {
|
|
/* Similarly, turning TxBF off in reverse order */
|
|
WL_IOVAR_SETINT(name, "txbf", 0);
|
|
WL_IOVAR_SETINT(name, "txbf_bfr_cap", 0);
|
|
}
|
|
}
|
|
|
|
if ((str = nvram_get(strlcat_r(prefix, "txbf_bfe_cap", tmp, sizeof(tmp)))) != NULL) {
|
|
txbf_bfe_cap = atoi(str);
|
|
|
|
WL_IOVAR_SETINT(name, "txbf_bfe_cap", txbf_bfe_cap ? 1 : 0);
|
|
}
|
|
|
|
if ((str = nvram_get(strlcat_r(prefix, "txbf_imp", tmp, sizeof(tmp)))) != NULL) {
|
|
txbf_imp = atoi(str);
|
|
|
|
WL_IOVAR_SETINT(name, "txbf_imp", txbf_imp);
|
|
}
|
|
}
|
|
|
|
/* Set up TxBF timer. Called when i/f is up. */
|
|
static void wlconf_set_txbf_timer(char *name, char *prefix)
|
|
{
|
|
char *str, tmp[100];
|
|
wlc_rev_info_t rev;
|
|
uint32 txbf_timer = 0;
|
|
int ret = 0;
|
|
|
|
WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
|
|
|
|
if (rev.corerev < 40) return; /* TxBF unsupported */
|
|
|
|
if ((str = nvram_get(strlcat_r(prefix, "txbf_timer", tmp, sizeof(tmp)))) != NULL) {
|
|
txbf_timer = (uint32) atoi(str);
|
|
WL_IOVAR_SETINT(name, "txbf_timer", txbf_timer);
|
|
}
|
|
}
|
|
|
|
/* Apply Traffic Management filter settings stored in NVRAM */
|
|
static void
|
|
trf_mgmt_settings(char *prefix, bool dwm_supported)
|
|
{
|
|
char buffer[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_RULES+1)];
|
|
char iobuff[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_RULES+1)+32];
|
|
int i, filterlen, ret = 0;
|
|
trf_mgmt_config_t trf_mgmt_config;
|
|
trf_mgmt_filter_list_t *trf_mgmt_filter_list;
|
|
netconf_trmgmt_t nettrm;
|
|
trf_mgmt_filter_t *trfmgmt;
|
|
char *wlifname;
|
|
struct in_addr ipaddr, ipmask;
|
|
char nvram_ifname[32];
|
|
bool tm_filters_configured = FALSE;
|
|
/* DWM variables */
|
|
bool dwm_filters_configured = FALSE;
|
|
char dscp_filter_buffer[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_DWM_RULES)];
|
|
char dscp_filter_iobuff[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_DWM_RULES)+
|
|
sizeof("trf_mgmt_filters_add") + OFFSETOF(trf_mgmt_filter_list_t, filter)];
|
|
int dscp_filterlen;
|
|
trf_mgmt_filter_t *trf_mgmt_dwm_filter;
|
|
trf_mgmt_filter_list_t *trf_mgmt_dwm_filter_list = NULL;
|
|
netconf_trmgmt_t nettrm_dwm;
|
|
bool is_dhd = 0;
|
|
|
|
snprintf(nvram_ifname, sizeof(nvram_ifname), "%sifname", prefix);
|
|
wlifname = nvram_get(nvram_ifname);
|
|
if (!wlifname) {
|
|
return;
|
|
}
|
|
|
|
trf_mgmt_filter_list = (trf_mgmt_filter_list_t *)buffer;
|
|
trfmgmt = &trf_mgmt_filter_list->filter[0];
|
|
|
|
/* Initialize the common parameters */
|
|
memset(buffer, 0, sizeof(buffer));
|
|
memset(&trf_mgmt_config, 0, sizeof(trf_mgmt_config_t));
|
|
|
|
/* no-rx packets, local subnet, don't override priority and no-traffic shape */
|
|
trf_mgmt_config.flags = (TRF_MGMT_FLAG_NO_RX |
|
|
TRF_MGMT_FLAG_MANAGE_LOCAL_TRAFFIC |
|
|
TRF_MGMT_FLAG_DISABLE_SHAPING);
|
|
(void) inet_aton("192.168.1.1", &ipaddr); /* Dummy value */
|
|
(void) inet_aton("255.255.255.0", &ipmask); /* Dummy value */
|
|
trf_mgmt_config.host_ip_addr = ipaddr.s_addr; /* Dummy value */
|
|
trf_mgmt_config.host_subnet_mask = ipmask.s_addr; /* Dummy value */
|
|
trf_mgmt_config.downlink_bandwidth = 1; /* Dummy value */
|
|
trf_mgmt_config.uplink_bandwidth = 1; /* Dummy value */
|
|
|
|
/* Read up to NUM_TFF_MGMT_FILTERS entries from NVRAM */
|
|
for (i = 0; i < MAX_NUM_TRF_MGMT_RULES; i++) {
|
|
if (get_trf_mgmt_port(prefix, i, &nettrm) == FALSE) {
|
|
continue;
|
|
}
|
|
if (nettrm.match.flags == NETCONF_DISABLED) {
|
|
continue;
|
|
}
|
|
trfmgmt->dst_port = ntohs(nettrm.match.dst.ports[0]);
|
|
trfmgmt->src_port = ntohs(nettrm.match.src.ports[0]);
|
|
trfmgmt->prot = nettrm.match.ipproto;
|
|
if (nettrm.favored) {
|
|
trfmgmt->flags |= TRF_FILTER_FAVORED;
|
|
}
|
|
trfmgmt->priority = nettrm.prio;
|
|
memcpy(&trfmgmt->dst_ether_addr, &nettrm.match.mac, sizeof(struct ether_addr));
|
|
if (nettrm.match.ipproto == IPPROTO_IP) {
|
|
/* Enable MAC filter */
|
|
trf_mgmt_config.flags |= TRF_MGMT_FLAG_FILTER_ON_MACADDR;
|
|
trfmgmt->flags |= TRF_FILTER_MAC_ADDR;
|
|
}
|
|
trf_mgmt_filter_list->num_filters += 1;
|
|
trfmgmt++;
|
|
}
|
|
if (trf_mgmt_filter_list->num_filters)
|
|
tm_filters_configured = TRUE;
|
|
|
|
if (dwm_supported) {
|
|
trf_mgmt_dwm_filter_list = (trf_mgmt_filter_list_t *)dscp_filter_buffer;
|
|
trf_mgmt_dwm_filter = &trf_mgmt_dwm_filter_list->filter[0];
|
|
memset(dscp_filter_buffer, 0, sizeof(dscp_filter_buffer));
|
|
|
|
/* Read up to NUM_TFF_MGMT_FILTERS entries from NVRAM */
|
|
for (i = 0; i < MAX_NUM_TRF_MGMT_DWM_RULES; i++) {
|
|
if (get_trf_mgmt_dwm(prefix, i, &nettrm_dwm) == FALSE) {
|
|
continue;
|
|
}
|
|
if (nettrm_dwm.match.flags == NETCONF_DISABLED) {
|
|
continue;
|
|
}
|
|
|
|
trf_mgmt_dwm_filter->dscp = nettrm_dwm.match.dscp;
|
|
if (nettrm_dwm.favored)
|
|
trf_mgmt_dwm_filter->flags |= TRF_FILTER_FAVORED;
|
|
trf_mgmt_dwm_filter->flags |= TRF_FILTER_DWM;
|
|
trf_mgmt_dwm_filter->priority = nettrm_dwm.prio;
|
|
trf_mgmt_dwm_filter++;
|
|
trf_mgmt_dwm_filter_list->num_filters += 1;
|
|
}
|
|
if (trf_mgmt_dwm_filter_list->num_filters)
|
|
dwm_filters_configured = TRUE;
|
|
}
|
|
|
|
#ifdef __CONFIG_DHDAP__
|
|
is_dhd = !dhd_probe(wlifname);
|
|
#endif
|
|
if (!is_dhd) {
|
|
/* Disable traffic management module to initial known state */
|
|
trf_mgmt_config.trf_mgmt_enabled = 0;
|
|
WL_IOVAR_SET(wlifname, "trf_mgmt_config", &trf_mgmt_config,
|
|
sizeof(trf_mgmt_config_t));
|
|
/* Add traffic management filter entries */
|
|
if (tm_filters_configured || dwm_filters_configured) {
|
|
/* Enable traffic management module before adding filter mappings */
|
|
trf_mgmt_config.trf_mgmt_enabled = 1;
|
|
WL_IOVAR_SET(wlifname, "trf_mgmt_config", &trf_mgmt_config,
|
|
sizeof(trf_mgmt_config_t));
|
|
if (tm_filters_configured) {
|
|
/* Configure TM module filters mappings */
|
|
filterlen = trf_mgmt_filter_list->num_filters *
|
|
sizeof(trf_mgmt_filter_t) +
|
|
OFFSETOF(trf_mgmt_filter_list_t, filter);
|
|
wl_iovar_setbuf(wlifname, "trf_mgmt_filters_add",
|
|
trf_mgmt_filter_list,
|
|
filterlen, iobuff, sizeof(iobuff));
|
|
}
|
|
|
|
if (dwm_filters_configured) {
|
|
dscp_filterlen = trf_mgmt_dwm_filter_list->num_filters *
|
|
sizeof(trf_mgmt_filter_t) +
|
|
OFFSETOF(trf_mgmt_filter_list_t, filter);
|
|
wl_iovar_setbuf(wlifname, "trf_mgmt_filters_add",
|
|
trf_mgmt_dwm_filter_list,
|
|
dscp_filterlen, dscp_filter_iobuff,
|
|
sizeof(dscp_filter_iobuff));
|
|
}
|
|
}
|
|
} else {
|
|
#ifdef __CONFIG_DHDAP__
|
|
/*
|
|
* for dhd interfaces we are supporting dwm filters only, dwm filters are for
|
|
* dscp to wmm prio mapping. The following traffic management functions are not
|
|
* supported, port and protocol based filters, traffic shaping, RSSI based filters.
|
|
*/
|
|
|
|
if (dwm_filters_configured) {
|
|
dscp_filterlen = trf_mgmt_dwm_filter_list->num_filters *
|
|
sizeof(trf_mgmt_filter_t) +
|
|
OFFSETOF(trf_mgmt_filter_list_t, filter);
|
|
dhd_iovar_setbuf(wlifname, "trf_mgmt_filters_add", trf_mgmt_dwm_filter_list,
|
|
dscp_filterlen, dscp_filter_iobuff, sizeof(dscp_filter_iobuff));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef TRAFFIC_MGMT_RSSI_POLICY
|
|
static void
|
|
trf_mgmt_rssi_policy(char *prefix)
|
|
{
|
|
char *wlifname;
|
|
char nvram_ifname[32];
|
|
char rssi_policy[64];
|
|
uint32 rssi_policy_value, ret;
|
|
|
|
/* Get wl interface name */
|
|
snprintf(nvram_ifname, sizeof(nvram_ifname), "%sifname", prefix);
|
|
if ((wlifname = nvram_get(nvram_ifname)) == NULL) {
|
|
return;
|
|
}
|
|
|
|
/* Get RSSI policy from NVRAM variable wlx_trf_mgmt_rssi_policy */
|
|
snprintf(rssi_policy, sizeof(rssi_policy), "%strf_mgmt_rssi_policy", prefix);
|
|
if (!nvram_invmatch(rssi_policy, ""))
|
|
return;
|
|
rssi_policy_value = atoi(nvram_get(rssi_policy));
|
|
|
|
/* Enable/Disable RSSI policy depending on value of rssi_policy_value */
|
|
WL_IOVAR_SETINT(wlifname, "trf_mgmt_rssi_policy", rssi_policy_value);
|
|
}
|
|
#endif /* TRAFFIC_MGMT_RSSI_POLICY */
|
|
|
|
static int
|
|
wlconf_del_brcm_syscap_ie(char *name, int bsscfg_idx, char *oui)
|
|
{
|
|
int iebuf_len = 0;
|
|
vndr_ie_setbuf_t *ie_setbuf = NULL;
|
|
int iecount, i;
|
|
|
|
char getbuf[2048] = {0};
|
|
vndr_ie_buf_t *iebuf;
|
|
vndr_ie_info_t *ieinfo;
|
|
char *bufaddr;
|
|
int buflen = 0;
|
|
int found = 0;
|
|
uint32 pktflag;
|
|
uint32 frametype;
|
|
int ret = 0;
|
|
|
|
frametype = VNDR_IE_BEACON_FLAG;
|
|
|
|
WL_BSSIOVAR_GET(name, "vndr_ie", bsscfg_idx, getbuf, 2048);
|
|
iebuf = (vndr_ie_buf_t *)getbuf;
|
|
|
|
bufaddr = (char*)iebuf->vndr_ie_list;
|
|
|
|
for (i = 0; i < iebuf->iecount; i++) {
|
|
ieinfo = (vndr_ie_info_t *)bufaddr;
|
|
bcopy((char*)&ieinfo->pktflag, (char*)&pktflag, (int)sizeof(uint32));
|
|
if (pktflag == frametype) {
|
|
if (!memcmp(ieinfo->vndr_ie_data.oui, oui, DOT11_OUI_LEN)) {
|
|
found = 1;
|
|
bufaddr = (char*) &ieinfo->vndr_ie_data;
|
|
buflen = (int)ieinfo->vndr_ie_data.len + VNDR_IE_HDR_LEN;
|
|
break;
|
|
}
|
|
}
|
|
bufaddr = (char *)(ieinfo->vndr_ie_data.oui + ieinfo->vndr_ie_data.len);
|
|
}
|
|
|
|
if (!found)
|
|
goto err;
|
|
|
|
iebuf_len = buflen + sizeof(vndr_ie_setbuf_t) - sizeof(vndr_ie_t);
|
|
ie_setbuf = (vndr_ie_setbuf_t *)malloc(iebuf_len);
|
|
if (!ie_setbuf) {
|
|
WLCONF_DBG("memory alloc failure\n");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
memset(ie_setbuf, 0, iebuf_len);
|
|
|
|
/* Copy the vndr_ie SET command ("add"/"del") to the buffer */
|
|
strcpy(ie_setbuf->cmd, "del");
|
|
|
|
/* Buffer contains only 1 IE */
|
|
iecount = 1;
|
|
memcpy(&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int));
|
|
|
|
memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, &frametype, sizeof(uint32));
|
|
|
|
memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data, bufaddr, buflen);
|
|
|
|
WL_BSSIOVAR_SET(name, "vndr_ie", bsscfg_idx, ie_setbuf, iebuf_len);
|
|
|
|
err:
|
|
if (ie_setbuf)
|
|
free(ie_setbuf);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
wlconf_set_brcm_syscap_ie(char *name, int bsscfg_idx, char *oui, uchar *data, int datalen)
|
|
{
|
|
vndr_ie_setbuf_t *ie_setbuf = NULL;
|
|
unsigned int pktflag;
|
|
int buflen, iecount;
|
|
int ret = 0;
|
|
|
|
pktflag = VNDR_IE_BEACON_FLAG;
|
|
|
|
buflen = sizeof(vndr_ie_setbuf_t) + datalen - 1;
|
|
ie_setbuf = (vndr_ie_setbuf_t *)malloc(buflen);
|
|
if (!ie_setbuf) {
|
|
WLCONF_DBG("memory alloc failure\n");
|
|
ret = -1;
|
|
goto err;
|
|
}
|
|
|
|
memset(ie_setbuf, 0, buflen);
|
|
|
|
/* Copy the vndr_ie SET command ("add"/"del") to the buffer */
|
|
strcpy(ie_setbuf->cmd, "add");
|
|
|
|
/* Buffer contains only 1 IE */
|
|
iecount = 1;
|
|
memcpy(&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int));
|
|
|
|
/* The packet flag bit field indicates the packets that will contain this IE */
|
|
memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag, sizeof(uint32));
|
|
|
|
/* Now, add the IE to the buffer, +1: one byte OUI_TYPE */
|
|
ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = DOT11_OUI_LEN + datalen;
|
|
|
|
memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[0], oui, DOT11_OUI_LEN);
|
|
if (datalen > 0)
|
|
memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data[0], data,
|
|
datalen);
|
|
|
|
ret = wlconf_del_brcm_syscap_ie(name, bsscfg_idx, oui);
|
|
if (ret)
|
|
goto err;
|
|
|
|
WL_BSSIOVAR_SET(name, "vndr_ie", (int)bsscfg_idx, ie_setbuf, buflen);
|
|
|
|
err:
|
|
if (ie_setbuf)
|
|
free(ie_setbuf);
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* wlconf_process_sta_config_entry() - process a single sta_config settings entry.
|
|
*
|
|
* This function processes a single sta_config settings entry by parsing the entry and
|
|
* calling the appropriate IOVAR(s) to apply the settings in the driver.
|
|
*
|
|
* Inputs:
|
|
* params - address of a nul terminated ascii string buffer containing a single sta_config
|
|
* settings entry in the form "xx:xx:xx:xx:xx:xx,prio[,steerflag]".
|
|
*
|
|
* At least the mac address of the station to which the settings are to be applied needs
|
|
* to be present, with one or more setting values, in a specific order. New settings must
|
|
* be added at the end. Alternatively, we could allow "prio=<value>,steerflag=<value>" and
|
|
* so on, but that would take some more parsing and use more nvram space.
|
|
*
|
|
* Outputs:
|
|
* Driver settings may be updated.
|
|
*
|
|
* Returns:
|
|
* This function returns a BCME_xxx status indicating success (BCME_OK) or failure.
|
|
*
|
|
* Side effects: The input buffer is trashed by the strsep() function.
|
|
*
|
|
*/
|
|
static int
|
|
wlconf_process_sta_config_entry(char *ifname, char *param_list)
|
|
{
|
|
enum { /* Parameter index in comma-separated list of settings. */
|
|
PARAM_MACADDRESS = 0,
|
|
PARAM_PRIO = 1,
|
|
PARAM_STEERFLAG = 2,
|
|
PARAM_COUNT
|
|
} param_idx = PARAM_MACADDRESS;
|
|
char *param;
|
|
struct ether_addr ea;
|
|
char *end;
|
|
uint32 value;
|
|
|
|
while ((param = strsep(¶m_list, ","))) {
|
|
switch (param_idx) {
|
|
|
|
case PARAM_MACADDRESS: /* MAC Address - parse into ea */
|
|
if (!param || !ether_atoe(param, &ea.octet[0])) {
|
|
return BCME_BADADDR;
|
|
}
|
|
break;
|
|
|
|
case PARAM_PRIO: /* prio value - parse and apply through "staprio" iovar */
|
|
if (*param) { /* If no value is provided, do not configure the prio */
|
|
wl_staprio_cfg_t staprio_arg;
|
|
int ret;
|
|
|
|
value = strtol(param, &end, 0);
|
|
if (*end != '\0') {
|
|
return BCME_BADARG;
|
|
}
|
|
memset(&staprio_arg, 0, sizeof(staprio_arg));
|
|
memcpy(&staprio_arg.ea, &ea, sizeof(ea));
|
|
staprio_arg.prio = value; /* prio is byte sized, no htod() needed */
|
|
WL_IOVAR_SET(ifname, "staprio", &staprio_arg, sizeof(staprio_arg));
|
|
}
|
|
break;
|
|
|
|
case PARAM_STEERFLAG:
|
|
if (*param) {
|
|
value = strtol(param, &end, 0);
|
|
if (*end != '\0') {
|
|
return BCME_BADARG;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
/* Future use parameter already set in nvram config - ignore. */
|
|
break;
|
|
}
|
|
++param_idx;
|
|
}
|
|
|
|
if (param_idx <= PARAM_PRIO) { /* No mac address and/or no parameters at all, forget it. */
|
|
return BCME_BADARG;
|
|
}
|
|
|
|
return BCME_OK;
|
|
}
|
|
|
|
#define VIFNAME_LEN 16
|
|
|
|
static void
|
|
wlconf_set_taf(char *name, bool enable)
|
|
{
|
|
#if defined(__CONFIG_TOAD__)
|
|
static const char toad_ifnames[] = "toad_ifnames";
|
|
#endif
|
|
char iobuf[sizeof( "taf" ) /* Need some space for the iovar itself, + nul */
|
|
+ sizeof(wl_taf_define_t)-1 /* struct size -1 byte for text[1] declaration */
|
|
+ sizeof("enable") /* "enable" string + terminating nul byte */
|
|
+ sizeof("0") /* "0" or "1" string + terminating nul byte */
|
|
+ sizeof("")]; /* Final terminating nul byte */
|
|
|
|
wl_taf_define_t *taf_def;
|
|
char *ptr;
|
|
int ret; /* Variable name "ret" is required by WL_IOCTL() */
|
|
|
|
memset(iobuf, 0, sizeof(iobuf));
|
|
strcpy(iobuf, "taf");
|
|
|
|
taf_def = (wl_taf_define_t *)&iobuf[ sizeof("taf") ];
|
|
memset(&taf_def->ea, 0xff, sizeof(taf_def->ea));
|
|
|
|
taf_def->version = 1;
|
|
|
|
ptr = taf_def->text;
|
|
ptr += sprintf(ptr, "enable"); /* Set up the command name argument */
|
|
*ptr++ = '\0'; /* add the NUL separator */
|
|
*ptr++ = (enable) ? '1' : '0'; /* enable or disable taf */
|
|
*ptr++ = '\0'; /* add the NUL separator */
|
|
*ptr = '\0'; /* and another NUL to terminate the argument list */
|
|
|
|
WL_IOCTL(name, WLC_SET_VAR, iobuf, sizeof(iobuf));
|
|
|
|
#if defined(__CONFIG_TOAD__)
|
|
|
|
/* Update the "toad_ifnames" nvram variable by adding or removing the interface name */
|
|
|
|
ptr = nvram_get(toad_ifnames);
|
|
|
|
if (!ptr && enable) { /* Creating nvram with the first ifname */
|
|
|
|
nvram_set(toad_ifnames, name);
|
|
|
|
} else if (ptr && enable) { /* Possibly need to add a new ifname */
|
|
|
|
if (!strstr(ptr, name)) {
|
|
char *tmp;
|
|
|
|
tmp = malloc(strlen(ptr) + sizeof(" ") + strlen(name));
|
|
|
|
if (tmp) {
|
|
sprintf(tmp, "%s %s", ptr, name);
|
|
nvram_set(toad_ifnames, tmp);
|
|
free(tmp);
|
|
}
|
|
}
|
|
|
|
} else if (ptr && !enable) { /* Possibly need to remove an existing ifname */
|
|
|
|
if (strstr(ptr, name)) {
|
|
int tlen;
|
|
|
|
tlen = strlen(ptr) - strlen(name);
|
|
if (tlen == 0) {
|
|
nvram_unset(toad_ifnames);
|
|
} else {
|
|
char *tmp;
|
|
tmp = malloc(tlen + sizeof(""));
|
|
if (tmp) {
|
|
char ifname[VIFNAME_LEN];
|
|
char *next;
|
|
char *cp;
|
|
|
|
memset(tmp, 0, tlen + sizeof(""));
|
|
cp = tmp;
|
|
|
|
foreach(ifname, ptr, next) {
|
|
if (strcmp(ifname, name) != 0) {
|
|
cp += sprintf(cp, "%s%s",
|
|
(cp == tmp) ? "":" ", name);
|
|
}
|
|
}
|
|
nvram_set(toad_ifnames, tmp);
|
|
free(tmp);
|
|
}
|
|
}
|
|
}
|
|
} /* otherwise this must be (!ptr && !enable), no action needed here. */
|
|
|
|
#endif /* __CONFIG_TOAD__ */
|
|
|
|
}
|
|
|
|
#define AMPDU_DENSITY_8USEC 6
|
|
|
|
/* configure the specified wireless interface */
|
|
int
|
|
wlconf(char *name)
|
|
{
|
|
int restore_defaults, val, unit, phytype, bandtype, gmode = 0, ret = 0;
|
|
int bcmerr;
|
|
int error_bg, error_a;
|
|
struct bsscfg_list *bclist = NULL;
|
|
struct bsscfg_info *bsscfg = NULL;
|
|
char tmp[100], tmp2[100], prefix[PREFIX_LEN];
|
|
char var[80], *next, *str, *addr = NULL;
|
|
/* Pay attention to buffer length requirements when using this */
|
|
char buf[WLC_IOCTL_SMLEN*2] __attribute__ ((aligned(4)));
|
|
char *country;
|
|
char *country_rev;
|
|
wlc_rev_info_t rev;
|
|
channel_info_t ci;
|
|
struct maclist *maclist;
|
|
struct ether_addr *ea;
|
|
wlc_ssid_t ssid;
|
|
wl_rateset_t rs;
|
|
unsigned int i;
|
|
char eaddr[32];
|
|
int ap, apsta, wds, sta = 0, wet = 0, mac_spoof = 0, wmf = 0;
|
|
int rxchain_pwrsave = 0, radio_pwrsave = 0;
|
|
wl_country_t country_spec = {{0}, 0, {0}};
|
|
char *ba;
|
|
char *preauth;
|
|
int set_preauth;
|
|
int wlunit = -1;
|
|
int wlsubunit = -1;
|
|
int wl_ap_build = 0; /* wl compiled with AP capabilities */
|
|
char cap[WLC_IOCTL_SMLEN];
|
|
char caps[WLC_IOCTL_MEDLEN];
|
|
int btc_mode;
|
|
uint32 leddc;
|
|
uint nbw = WL_CHANSPEC_BW_20;
|
|
int nmode = OFF; /* 802.11n support */
|
|
char vif_addr[WLC_IOCTL_SMLEN];
|
|
int max_no_vifs = 0;
|
|
int wme_global;
|
|
int max_assoc = -1;
|
|
bool ure_enab = FALSE;
|
|
bool radar_enab = FALSE;
|
|
bool obss_coex = FALSE, psta, psr;
|
|
chanspec_t chanspec = 0;
|
|
int wet_tunnel_cap = 0, wet_tunnel_enable = 0;
|
|
brcm_prop_ie_t brcm_syscap_ie;
|
|
#ifdef __CONFIG_DHDAP__
|
|
int is_dhd;
|
|
int cfg_max_assoc = -1;
|
|
#endif
|
|
|
|
/* wlconf doesn't work for virtual i/f, so if we are given a
|
|
* virtual i/f return 0 if that interface is in it's parent's "vifs"
|
|
* list otherwise return -1
|
|
*/
|
|
if (get_ifname_unit(name, &wlunit, &wlsubunit) == 0) {
|
|
if (wlsubunit >= 0) {
|
|
/* we have been given a virtual i/f,
|
|
* is it in it's parent i/f's virtual i/f list?
|
|
*/
|
|
sprintf(tmp, "wl%d_vifs", wlunit);
|
|
|
|
if (strstr(nvram_safe_get(tmp), name) == NULL)
|
|
return -1; /* config error */
|
|
else
|
|
return 0; /* okay */
|
|
}
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
/* clean up tmp */
|
|
memset(tmp, 0, sizeof(tmp));
|
|
|
|
/* Check interface (fail silently for non-wl interfaces) */
|
|
if ((ret = wl_probe(name)))
|
|
return ret;
|
|
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* Check if interface uses dhd adapter */
|
|
is_dhd = !dhd_probe(name);
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
|
|
/* Bring up the interface temporarily before issuing iovars. */
|
|
/* This will ensure all the cores are fully initialized */
|
|
WL_IOCTL(name, WLC_UP, NULL, 0);
|
|
|
|
/* because of ifdefs in wl driver, when we don't have AP capabilities we
|
|
* can't use the same iovars to configure the wl.
|
|
* so we use "wl_ap_build" to help us know how to configure the driver
|
|
*/
|
|
if (wl_iovar_get(name, "cap", (void *)caps, sizeof(caps)))
|
|
return -1;
|
|
|
|
foreach(cap, caps, next) {
|
|
if (!strcmp(cap, "ap")) {
|
|
wl_ap_build = 1;
|
|
} else if (!strcmp(cap, "mbss16"))
|
|
max_no_vifs = 16;
|
|
else if (!strcmp(cap, "mbss8"))
|
|
max_no_vifs = 8;
|
|
else if (!strcmp(cap, "mbss4"))
|
|
max_no_vifs = 4;
|
|
else if (!strcmp(cap, "wmf"))
|
|
wmf = 1;
|
|
else if (!strcmp(cap, "rxchain_pwrsave"))
|
|
rxchain_pwrsave = 1;
|
|
else if (!strcmp(cap, "radio_pwrsave"))
|
|
radio_pwrsave = 1;
|
|
else if (!strcmp(cap, "wet_tunnel"))
|
|
wet_tunnel_cap = 1;
|
|
}
|
|
|
|
/* Get MAC address */
|
|
(void) wl_hwaddr(name, (uchar *)buf);
|
|
memcpy(vif_addr, buf, ETHER_ADDR_LEN);
|
|
|
|
/* Get instance */
|
|
WL_IOCTL(name, WLC_GET_INSTANCE, &unit, sizeof(unit));
|
|
snprintf(prefix, sizeof(prefix), "wl%d_", unit);
|
|
|
|
/* Restore defaults if per-interface parameters do not exist */
|
|
// restore_defaults = !nvram_get(strlcat_r(prefix, "ifname", tmp, sizeof(tmp)));
|
|
restore_defaults = !strlen(nvram_safe_get(strlcat_r(prefix, "ifname", tmp, sizeof(tmp))));
|
|
nvram_validate_all(prefix, restore_defaults);
|
|
nvram_set(strlcat_r(prefix, "ifname", tmp, sizeof(tmp)), name);
|
|
nvram_set(strlcat_r(prefix, "hwaddr", tmp, sizeof(tmp)), ether_etoa((uchar *)buf, eaddr));
|
|
snprintf(buf, sizeof(buf), "%d", unit);
|
|
nvram_set(strlcat_r(prefix, "unit", tmp, sizeof(tmp)), buf);
|
|
|
|
if (restore_defaults) {
|
|
wlconf_set_current_txparam_into_nvram(name, prefix);
|
|
}
|
|
|
|
/* Apply message level */
|
|
if (nvram_invmatch("wl_msglevel", "")) {
|
|
val = (int)strtoul(nvram_get("wl_msglevel"), NULL, 0);
|
|
|
|
if (nvram_invmatch("wl_msglevel2", "")) {
|
|
struct wl_msglevel2 msglevel64;
|
|
msglevel64.low = val;
|
|
val = (int)strtoul(nvram_get("wl_msglevel2"), NULL, 0);
|
|
msglevel64.high = val;
|
|
WL_IOVAR_SET(name, "msglevel", &msglevel64, sizeof(struct wl_msglevel2));
|
|
}
|
|
else
|
|
WL_IOCTL(name, WLC_SET_MSGLEVEL, &val, sizeof(val));
|
|
}
|
|
|
|
/* Bring the interface down */
|
|
WL_IOCTL(name, WLC_DOWN, NULL, 0);
|
|
|
|
/* Disable all BSS Configs */
|
|
for (i = 0; i < WL_MAXBSSCFG; i++) {
|
|
struct {int bsscfg_idx; int enable;} setbuf;
|
|
setbuf.bsscfg_idx = i;
|
|
setbuf.enable = 0;
|
|
|
|
ret = wl_iovar_set(name, "bss", &setbuf, sizeof(setbuf));
|
|
if (ret) {
|
|
wl_iovar_getint(name, "bcmerror", &bcmerr);
|
|
/* fail quietly on a range error since the driver may
|
|
* support fewer bsscfgs than we are prepared to configure
|
|
*/
|
|
if (bcmerr == BCME_RANGE)
|
|
break;
|
|
}
|
|
if (ret) {
|
|
WLCONF_DBG("%d:(%s): setting bsscfg #%d iovar \"bss\" to 0"
|
|
" (down) failed, ret = %d, bcmerr = %d\n",
|
|
__LINE__, name, i, ret, bcmerr);
|
|
}
|
|
}
|
|
|
|
/* Get the list of BSS Configs */
|
|
bclist = wlconf_get_bsscfgs(name, prefix);
|
|
if (bclist == NULL) {
|
|
ret = -1;
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef BCMDBG
|
|
strlcat_r(prefix, "vifs", tmp, sizeof(tmp));
|
|
printf("BSS Config summary: primary -> \"%s\", %s -> \"%s\"\n", name, tmp,
|
|
nvram_safe_get(tmp));
|
|
for (i = 0; i < bclist->count; i++) {
|
|
printf("BSS Config \"%s\": index %d\n",
|
|
bclist->bsscfgs[i].ifname, bclist->bsscfgs[i].idx);
|
|
}
|
|
#endif
|
|
|
|
/* create a wlX.Y_ifname nvram setting */
|
|
for (i = 1; i < bclist->count; i++) {
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
#if defined(linux) || defined(__ECOS) || defined(__NetBSD__)
|
|
strcpy(var, bsscfg->ifname);
|
|
#endif
|
|
nvram_set(strlcat_r(bsscfg->prefix, "ifname", tmp, sizeof(tmp)), var);
|
|
}
|
|
|
|
str = nvram_safe_get(strlcat_r(prefix, "mode", tmp, sizeof(tmp)));
|
|
|
|
/* If ure_disable is not present or is 1, ure is not enabled;
|
|
* that is, if it is present and 0, ure is enabled.
|
|
*/
|
|
if (!strcmp(nvram_safe_get("ure_disable"), "0")) { /* URE is enabled */
|
|
ure_enab = TRUE;
|
|
}
|
|
if (wl_ap_build) {
|
|
/* Enable MBSS mode if appropriate. */
|
|
if (!ure_enab && strcmp(str, "psr")) {
|
|
#ifndef __CONFIG_USBAP__
|
|
WL_IOVAR_SETINT(name, "mbss", (bclist->count >= 1));
|
|
#else
|
|
WL_IOVAR_SETINT(name, "mbss", (bclist->count >= 2));
|
|
#endif /* __CONFIG_USBAP__ */
|
|
} else
|
|
WL_IOVAR_SETINT(name, "mbss", 0);
|
|
|
|
/*
|
|
* Set SSID for each BSS Config
|
|
*/
|
|
for (i = 0; i < bclist->count; i++) {
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
strlcat_r(bsscfg->prefix, "ssid", tmp, sizeof(tmp));
|
|
ssid.SSID_len = strlen(nvram_safe_get(tmp));
|
|
if (ssid.SSID_len > sizeof(ssid.SSID))
|
|
ssid.SSID_len = sizeof(ssid.SSID);
|
|
strncpy((char *)ssid.SSID, nvram_safe_get(tmp), ssid.SSID_len);
|
|
WLCONF_DBG("wlconfig(%s): configuring bsscfg #%d (%s) "
|
|
"with SSID \"%s\"\n", name, bsscfg->idx,
|
|
bsscfg->ifname, nvram_safe_get(tmp));
|
|
WL_BSSIOVAR_SET(name, "ssid", bsscfg->idx, &ssid,
|
|
sizeof(ssid));
|
|
}
|
|
}
|
|
|
|
/* Create addresses for VIFs */
|
|
if (!ure_enab && strcmp(str, "psr")) {
|
|
/* set local bit for our MBSS vif base */
|
|
ETHER_SET_LOCALADDR(vif_addr);
|
|
|
|
/* construct and set other wlX.Y_hwaddr */
|
|
for (i = 1; i < max_no_vifs; i++) {
|
|
snprintf(tmp, sizeof(tmp), "wl%d.%d_hwaddr", unit, i);
|
|
addr = nvram_safe_get(tmp);
|
|
if (!strcmp(addr, "")) {
|
|
vif_addr[5] = (vif_addr[5] & ~(max_no_vifs-1))
|
|
| ((max_no_vifs-1) & (vif_addr[5]+1));
|
|
nvram_set(tmp, ether_etoa((uchar *)vif_addr, eaddr));
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < bclist->count; i++) {
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
/* Ignore primary */
|
|
if (bsscfg->idx == 0)
|
|
continue;
|
|
|
|
snprintf(tmp, sizeof(tmp), "wl%d.%d_hwaddr", unit, bsscfg->idx);
|
|
ether_atoe(nvram_safe_get(tmp), (unsigned char *)eaddr);
|
|
WL_BSSIOVAR_SET(name, "cur_etheraddr", bsscfg->idx, eaddr, ETHER_ADDR_LEN);
|
|
}
|
|
} else { /* One of URE or Proxy STA Repeater is enabled */
|
|
/* URE/PSR is on, so set wlX.1 hwaddr is same as that of primary interface */
|
|
snprintf(tmp, sizeof(tmp), "wl%d.1_hwaddr", unit);
|
|
WL_BSSIOVAR_SET(name, "cur_etheraddr", 1, vif_addr,
|
|
ETHER_ADDR_LEN);
|
|
nvram_set(tmp, ether_etoa((uchar *)vif_addr, eaddr));
|
|
}
|
|
|
|
/* wlX_mode settings: AP, STA, WET, BSS/IBSS, APSTA */
|
|
str = nvram_safe_get(strlcat_r(prefix, "mode", tmp, sizeof(tmp)));
|
|
ap = (!strcmp(str, "") || !strcmp(str, "ap"));
|
|
apsta = (!strcmp(str, "apsta") ||
|
|
((!strcmp(str, "sta") || !strcmp(str, "psr") || !strcmp(str, "wet")) &&
|
|
bclist->count > 1));
|
|
sta = (!strcmp(str, "sta") && bclist->count == 1);
|
|
wds = !strcmp(str, "wds");
|
|
wet = !strcmp(str, "wet");
|
|
mac_spoof = !strcmp(str, "mac_spoof");
|
|
psta = !strcmp(str, "psta");
|
|
psr = !strcmp(str, "psr");
|
|
|
|
/* set apsta var first, because APSTA mode takes precedence */
|
|
WL_IOVAR_SETINT(name, "apsta", apsta);
|
|
|
|
/* Set AP mode */
|
|
val = (ap || apsta || wds) ? 1 : 0;
|
|
WL_IOCTL(name, WLC_SET_AP, &val, sizeof(val));
|
|
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd) {
|
|
dhd_iovar_setint(name, "wet", wet);
|
|
} else
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
/* Turn WET mode ON or OFF based on selected mode */
|
|
WL_IOCTL(name, WLC_SET_WET, &wet, sizeof(wet));
|
|
|
|
if (mac_spoof) {
|
|
sta = 1;
|
|
WL_IOVAR_SETINT(name, "mac_spoof", 1);
|
|
}
|
|
|
|
/* For STA configurations, configure association retry time.
|
|
* Use specified time (capped), or mode-specific defaults.
|
|
*/
|
|
if (sta || wet || apsta || psta || psr) {
|
|
char *sta_retry_time_name = "sta_retry_time";
|
|
char *assoc_retry_max_name = "assoc_retry_max";
|
|
struct {
|
|
int val;
|
|
int band;
|
|
} roam;
|
|
|
|
str = nvram_safe_get(strlcat_r(prefix, sta_retry_time_name, tmp, sizeof(tmp)));
|
|
WL_IOVAR_SETINT(name, sta_retry_time_name, atoi(str));
|
|
|
|
/* Set the wlX_assoc_retry_max, but only if one was specified. */
|
|
if ((str = nvram_get(strlcat_r(prefix, assoc_retry_max_name, tmp, sizeof(tmp))))) {
|
|
WL_IOVAR_SETINT(name, assoc_retry_max_name, atoi(str));
|
|
}
|
|
|
|
roam.val = WLC_ROAM_NEVER_ROAM_TRIGGER;
|
|
roam.band = WLC_BAND_ALL;
|
|
WL_IOCTL(name, WLC_SET_ROAM_TRIGGER, &roam, sizeof(roam));
|
|
}
|
|
|
|
/* Retain remaining WET effects only if not APSTA */
|
|
wet &= !apsta;
|
|
|
|
/* Set infra: BSS/IBSS (IBSS only for WET or STA modes) */
|
|
val = 1;
|
|
if (wet || sta || psta || psr)
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "infra", tmp, sizeof(tmp))));
|
|
WL_IOCTL(name, WLC_SET_INFRA, &val, sizeof(val));
|
|
|
|
/* Set DWDS only for AP or STA modes */
|
|
for (i = 0; i < bclist->count; i++) {
|
|
val = 0;
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
|
|
if (ap || sta || psta || psr || (apsta && !wet)) {
|
|
strlcat_r(bsscfg->prefix, "dwds", tmp, sizeof(tmp));
|
|
val = atoi(nvram_safe_get(tmp));
|
|
}
|
|
WL_BSSIOVAR_SETINT(name, "dwds", bsscfg->idx, val);
|
|
}
|
|
|
|
/* Set The AP MAX Associations Limit */
|
|
if (ap || apsta) {
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* check if we have driver maxassoc tuneable value */
|
|
cfg_max_assoc = atoi(nvram_safe_get(strlcat_r(prefix, "cfg_maxassoc", tmp, sizeof(tmp))));
|
|
if (cfg_max_assoc <= 0) {
|
|
WL_IOVAR_GETINT(name, "maxassoc", &cfg_max_assoc);
|
|
/* save to nvram */
|
|
snprintf(var, sizeof(var), "%d", cfg_max_assoc);
|
|
nvram_set(tmp, var);
|
|
}
|
|
#endif
|
|
|
|
max_assoc = val = atoi(nvram_safe_get(strlcat_r(prefix, "maxassoc", tmp, sizeof(tmp))));
|
|
if (val > 0) {
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* fix for max_assoc value greater than 32 for DHD */
|
|
if ((val > cfg_max_assoc) && is_dhd) {
|
|
val = cfg_max_assoc;
|
|
snprintf(var, sizeof(var), "%d", val);
|
|
nvram_set(tmp, var);
|
|
}
|
|
#endif
|
|
WL_IOVAR_SETINT(name, "maxassoc", val);
|
|
} else { /* Get value from driver if not in nvram */
|
|
WL_IOVAR_GETINT(name, "maxassoc", &max_assoc);
|
|
}
|
|
}
|
|
if (!wet && !sta)
|
|
WL_IOVAR_SETINT(name, "mpc", OFF);
|
|
|
|
/* Set the Proxy STA or Repeater mode */
|
|
if (psta) {
|
|
WL_IOVAR_SETINT(name, "psta", PSTA_MODE_PROXY);
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd)
|
|
dhd_iovar_setint(name, "psta", PSTA_MODE_PROXY);
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
/* Set inactivity timer */
|
|
str = nvram_get(strlcat_r(prefix, "psta_inact", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
val = atoi(str);
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd)
|
|
dhd_iovar_setint(name, "psta_inact", val);
|
|
else
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
WL_IOVAR_SETINT(name, "psta_inact", val);
|
|
}
|
|
} else if (psr) {
|
|
WL_IOVAR_SETINT(name, "psta", PSTA_MODE_REPEATER);
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd)
|
|
dhd_iovar_setint(name, "psta", PSTA_MODE_REPEATER);
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "psr_mrpt", tmp, sizeof(tmp))));
|
|
WL_IOVAR_SETINT(name, "psta_mrpt", val);
|
|
} else {
|
|
WL_IOVAR_SETINT(name, "psta", PSTA_MODE_DISABLED);
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd)
|
|
dhd_iovar_setint(name, "psta", PSTA_MODE_DISABLED);
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
}
|
|
|
|
#ifdef __CONFIG_GMAC3__
|
|
if (psta || psr)
|
|
DHD_BSSIOVAR_SETINT(name, "dev_def", 0, 1);
|
|
#endif
|
|
|
|
/* Turn WET tunnel mode ON or OFF */
|
|
if ((ap || apsta) && (wet_tunnel_cap)) {
|
|
if (atoi(nvram_safe_get(strlcat_r(prefix, "wet_tunnel", tmp, sizeof(tmp)))) == 1) {
|
|
WL_IOVAR_SETINT(name, "wet_tunnel", 1);
|
|
wet_tunnel_enable = 1;
|
|
} else {
|
|
WL_IOVAR_SETINT(name, "wet_tunnel", 0);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < bclist->count; i++) {
|
|
char *subprefix;
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
|
|
/* XXXMSSID: The note about setting preauth now does not seem right.
|
|
* NAS brings the BSS up if it runs, so setting the preauth value
|
|
* will make it in the bcn/prb. If that is right, we can move this
|
|
* chunk out of wlconf.
|
|
*/
|
|
/*
|
|
* Set The WPA2 Pre auth cap. only reason we are doing it here is the driver is down
|
|
* if we do it in the NAS we need to bring down the interface and up to make
|
|
* it affect in the beacons
|
|
*/
|
|
if (ap || (apsta && bsscfg->idx != 0)) {
|
|
set_preauth = 1;
|
|
preauth = nvram_safe_get(strlcat_r(bsscfg->prefix, "preauth", tmp, sizeof(tmp)));
|
|
if (strlen (preauth) != 0) {
|
|
set_preauth = atoi(preauth);
|
|
}
|
|
wlconf_set_preauth(name, bsscfg->idx, set_preauth);
|
|
}
|
|
|
|
/* Clear BRCM System level Capability IE */
|
|
memset(&brcm_syscap_ie, 0, sizeof(brcm_prop_ie_t));
|
|
brcm_syscap_ie.type = BRCM_SYSCAP_IE_TYPE;
|
|
|
|
/* Add WET TUNNEL to IE */
|
|
if (wet_tunnel_enable)
|
|
brcm_syscap_ie.cap |= BRCM_SYSCAP_WET_TUNNEL;
|
|
|
|
subprefix = apsta ? prefix : bsscfg->prefix;
|
|
|
|
if (ap || (apsta && bsscfg->idx != 0)) {
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "bss_maxassoc", tmp, sizeof(tmp))));
|
|
if (val > 0) {
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* fix for val greater than 32 for DHD */
|
|
if (is_dhd && (val > cfg_max_assoc)) {
|
|
val = cfg_max_assoc;
|
|
/* rewrite the nvram contents */
|
|
snprintf(var, sizeof(var), "%d", val);
|
|
nvram_set(tmp, var);
|
|
}
|
|
#endif
|
|
WL_BSSIOVAR_SETINT(name, "bss_maxassoc", bsscfg->idx, val);
|
|
} else if (max_assoc > 0) { /* Set maxassoc same as global if not set */
|
|
snprintf(var, sizeof(var), "%d", max_assoc);
|
|
nvram_set(tmp, var);
|
|
}
|
|
}
|
|
|
|
/* Set network type */
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "closed", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "closednet", bsscfg->idx, val);
|
|
|
|
/* Set the ap isolate mode */
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "ap_isolate", tmp, sizeof(tmp))));
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd) {
|
|
DHD_BSSIOVAR_SETINT(name, "ap_isolate", bsscfg->idx, val);
|
|
} else
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
WL_BSSIOVAR_SETINT(name, "ap_isolate", bsscfg->idx, val);
|
|
|
|
/* Set the MAC filter based probe response mode */
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "probresp_mf", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "probresp_mac_filter", bsscfg->idx, val);
|
|
|
|
/* Set the WMF enable mode */
|
|
if (wmf ||
|
|
#ifdef __CONFIG_DHDAP__
|
|
is_dhd ||
|
|
#endif
|
|
0) {
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd) {
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"wmf_bss_enable", tmp, sizeof(tmp))));
|
|
DHD_BSSIOVAR_SETINT(name, "wmf_bss_enable", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"wmf_psta_disable", tmp, sizeof(tmp))));
|
|
DHD_BSSIOVAR_SETINT(name, "wmf_psta_disable", bsscfg->idx, val);
|
|
|
|
} else {
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "wmf_bss_enable", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "wmf_bss_enable", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"wmf_psta_disable", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "wmf_psta_disable", bsscfg->idx, val);
|
|
#ifdef __CONFIG_DHDAP__
|
|
}
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
}
|
|
|
|
/* Set the Multicast Reverse Translation enable mode */
|
|
if (wet || psta || psr) {
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"mcast_regen_bss_enable", tmp, sizeof(tmp))));
|
|
#ifdef __CONFIG_DHDAP__
|
|
if (is_dhd) {
|
|
DHD_BSSIOVAR_SETINT(name, "mcast_regen_bss_enable",
|
|
bsscfg->idx, val);
|
|
} else {
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
WL_BSSIOVAR_SETINT(name, "mcast_regen_bss_enable",
|
|
bsscfg->idx, val);
|
|
#ifdef __CONFIG_DHDAP__
|
|
}
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
}
|
|
|
|
if (rxchain_pwrsave) {
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "rxchain_pwrsave_enable",
|
|
tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_enable", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"rxchain_pwrsave_quiet_time", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_quiet_time", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "rxchain_pwrsave_pps",
|
|
tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_pps", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"rxchain_pwrsave_stas_assoc_check", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_stas_assoc_check", bsscfg->idx,
|
|
val);
|
|
}
|
|
|
|
if (radio_pwrsave) {
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "radio_pwrsave_enable",
|
|
tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "radio_pwrsave_enable", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"radio_pwrsave_quiet_time", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "radio_pwrsave_quiet_time", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "radio_pwrsave_pps",
|
|
tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "radio_pwrsave_pps", bsscfg->idx, val);
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "radio_pwrsave_level",
|
|
tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "radio_pwrsave_level", bsscfg->idx, val);
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"radio_pwrsave_stas_assoc_check", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "radio_pwrsave_stas_assoc_check", bsscfg->idx,
|
|
val);
|
|
}
|
|
val = atoi(nvram_safe_get(strlcat_r(bsscfg->prefix, "aspm", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "aspm", bsscfg->idx, val);
|
|
|
|
/* Configure SYSCAP IE to driver */
|
|
if (brcm_syscap_ie.cap) {
|
|
wlconf_set_brcm_syscap_ie(name, bsscfg->idx,
|
|
BRCM_PROP_OUI, (uchar *)&(brcm_syscap_ie.type),
|
|
sizeof(brcm_syscap_ie.type) +
|
|
sizeof(brcm_syscap_ie.cap));
|
|
}
|
|
}
|
|
|
|
/* Set up the country code */
|
|
(void) strlcat_r(prefix, "country_code", tmp, sizeof(tmp));
|
|
country = nvram_get(tmp);
|
|
(void) strlcat_r(prefix, "country_rev", tmp2, sizeof(tmp2));
|
|
country_rev = nvram_get(tmp2);
|
|
if ((country && country[0] != '\0') && (country_rev && country_rev[0] != '\0')) {
|
|
/* Initialize the wl country parameter */
|
|
strncpy(country_spec.country_abbrev, country, WLC_CNTRY_BUF_SZ-1);
|
|
country_spec.country_abbrev[WLC_CNTRY_BUF_SZ-1] = '\0';
|
|
strncpy(country_spec.ccode, country, WLC_CNTRY_BUF_SZ-1);
|
|
country_spec.ccode[WLC_CNTRY_BUF_SZ-1] = '\0';
|
|
country_spec.rev = atoi(country_rev);
|
|
|
|
WL_IOVAR_SET(name, "country", &country_spec, sizeof(country_spec));
|
|
} else {
|
|
/* Get the default country code if undefined */
|
|
wl_iovar_get(name, "country", &country_spec, sizeof(country_spec));
|
|
|
|
/* Add the new NVRAM variable */
|
|
nvram_set("wl_country_code", country_spec.ccode);
|
|
(void) strlcat_r(prefix, "country_code", tmp, sizeof(tmp));
|
|
nvram_set(tmp, country_spec.ccode);
|
|
snprintf(buf, sizeof(buf), "%d", country_spec.rev);
|
|
nvram_set("wl_country_rev", buf);
|
|
(void) strlcat_r(prefix, "country_rev", tmp, sizeof(tmp));
|
|
nvram_set(tmp, buf);
|
|
}
|
|
|
|
/* Change LED Duty Cycle */
|
|
leddc = (uint32)strtoul(nvram_safe_get(strlcat_r(prefix, "leddc", tmp, sizeof(tmp))), NULL, 16);
|
|
if (leddc)
|
|
WL_IOVAR_SETINT(name, "leddc", leddc);
|
|
|
|
/* Enable or disable the radio */
|
|
val = nvram_match(strlcat_r(prefix, "radio", tmp, sizeof(tmp)), "0");
|
|
val += WL_RADIO_SW_DISABLE << 16;
|
|
WL_IOCTL(name, WLC_SET_RADIO, &val, sizeof(val));
|
|
|
|
/* Get supported phy types */
|
|
WL_IOCTL(name, WLC_GET_PHYLIST, var, sizeof(var));
|
|
nvram_set(strlcat_r(prefix, "phytypes", tmp, sizeof(tmp)), var);
|
|
|
|
/* Get radio IDs */
|
|
*(next = buf) = '\0';
|
|
for (i = 0; i < strlen(var); i++) {
|
|
/* Switch to band */
|
|
val = WLCONF_STR2PHYTYPE(var[i]);
|
|
if (WLCONF_PHYTYPE_11N(val)) {
|
|
WL_GETINT(name, WLC_GET_BAND, &val);
|
|
} else
|
|
val = WLCONF_PHYTYPE2BAND(val);
|
|
WL_IOCTL(name, WLC_SET_BAND, &val, sizeof(val));
|
|
/* Get radio ID on this band */
|
|
WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
|
|
next += sprintf(next, "%sBCM%X", i ? " " : "",
|
|
(rev.radiorev & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT);
|
|
}
|
|
nvram_set(strlcat_r(prefix, "radioids", tmp, sizeof(tmp)), buf);
|
|
|
|
/* Set band */
|
|
str = nvram_get(strlcat_r(prefix, "phytype", tmp, sizeof(tmp)));
|
|
val = str ? WLCONF_STR2PHYTYPE(str[0]) : PHY_TYPE_G;
|
|
/* For NPHY use band value from NVRAM */
|
|
if (WLCONF_PHYTYPE_11N(val)) {
|
|
str = nvram_get(strlcat_r(prefix, "nband", tmp, sizeof(tmp)));
|
|
if (str)
|
|
val = atoi(str);
|
|
else {
|
|
WL_GETINT(name, WLC_GET_BAND, &val);
|
|
}
|
|
} else
|
|
val = WLCONF_PHYTYPE2BAND(val);
|
|
|
|
WL_SETINT(name, WLC_SET_BAND, val);
|
|
|
|
/* Check errors (card may have changed) */
|
|
if (ret) {
|
|
/* default band to the first band in band list */
|
|
val = WLCONF_STR2PHYTYPE(var[0]);
|
|
val = WLCONF_PHYTYPE2BAND(val);
|
|
WL_SETINT(name, WLC_SET_BAND, val);
|
|
}
|
|
|
|
/* Store the resolved bandtype */
|
|
bandtype = val;
|
|
|
|
/* Check errors again (will cover 5Ghz-only cards) */
|
|
if (ret) {
|
|
int list[3];
|
|
|
|
/* default band to the first band in band list */
|
|
wl_ioctl(name, WLC_GET_BANDLIST, list, sizeof(list));
|
|
WL_SETINT(name, WLC_SET_BAND, list[1]);
|
|
|
|
/* Read it back, and set bandtype accordingly */
|
|
WL_GETINT(name, WLC_GET_BAND, &bandtype);
|
|
}
|
|
|
|
/* Get current core revision */
|
|
WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
|
|
snprintf(buf, sizeof(buf), "%d", rev.corerev);
|
|
nvram_set(strlcat_r(prefix, "corerev", tmp, sizeof(tmp)), buf);
|
|
|
|
if ((rev.chipnum == BCM4716_CHIP_ID) || (rev.chipnum == BCM47162_CHIP_ID) ||
|
|
(rev.chipnum == BCM4748_CHIP_ID) || (rev.chipnum == BCM4331_CHIP_ID) ||
|
|
(rev.chipnum == BCM43431_CHIP_ID) || (rev.chipnum == BCM5357_CHIP_ID) ||
|
|
(rev.chipnum == BCM53572_CHIP_ID) || (rev.chipnum == BCM43236_CHIP_ID)) {
|
|
int pam_mode = WLC_N_PREAMBLE_GF_BRCM; /* default GF-BRCM */
|
|
|
|
strlcat_r(prefix, "mimo_preamble", tmp, sizeof(tmp));
|
|
if (nvram_match(tmp, "mm"))
|
|
pam_mode = WLC_N_PREAMBLE_MIXEDMODE;
|
|
else if (nvram_match(tmp, "gf"))
|
|
pam_mode = WLC_N_PREAMBLE_GF;
|
|
else if (nvram_match(tmp, "auto"))
|
|
pam_mode = -1;
|
|
WL_IOVAR_SETINT(name, "mimo_preamble", pam_mode);
|
|
}
|
|
|
|
/* Making default ampdu_density to 8usec in order to improve throughput
|
|
* of very small packet sizes (64, 88, 128,..).
|
|
*/
|
|
if (rev.chipnum == BCM43217_CHIP_ID)
|
|
WL_IOVAR_SETINT(name, "ampdu_rx_density", AMPDU_DENSITY_8USEC);
|
|
|
|
if ((rev.chipnum == BCM5357_CHIP_ID) || (rev.chipnum == BCM53572_CHIP_ID)) {
|
|
val = atoi(nvram_safe_get("coma_sleep"));
|
|
if (val > 0) {
|
|
struct {int sleep; int delay;} setbuf;
|
|
nvram_unset("coma_sleep");
|
|
nvram_commit();
|
|
setbuf.sleep = val;
|
|
setbuf.delay = 1;
|
|
WL_IOVAR_SET(name, "coma", &setbuf, sizeof(setbuf));
|
|
}
|
|
}
|
|
|
|
/* Get current phy type */
|
|
WL_IOCTL(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
|
|
snprintf(buf, sizeof(buf), "%s", WLCONF_PHYTYPE2STR(phytype));
|
|
nvram_set(strlcat_r(prefix, "phytype", tmp, sizeof(tmp)), buf);
|
|
|
|
/* Setup regulatory mode */
|
|
strlcat_r(prefix, "reg_mode", tmp, sizeof(tmp));
|
|
if (nvram_match(tmp, "off")) {
|
|
val = 0;
|
|
WL_IOCTL(name, WLC_SET_REGULATORY, &val, sizeof(val));
|
|
WL_IOCTL(name, WLC_SET_RADAR, &val, sizeof(val));
|
|
WL_IOCTL(name, WLC_SET_SPECT_MANAGMENT, &val, sizeof(val));
|
|
} else if (nvram_match(tmp, "h") || nvram_match(tmp, "strict_h")) {
|
|
val = 0;
|
|
WL_IOCTL(name, WLC_SET_REGULATORY, &val, sizeof(val));
|
|
val = 1;
|
|
WL_IOCTL(name, WLC_SET_RADAR, &val, sizeof(val));
|
|
radar_enab = TRUE;
|
|
if (nvram_match(tmp, "h"))
|
|
val = 1;
|
|
else
|
|
val = 2;
|
|
WL_IOCTL(name, WLC_SET_SPECT_MANAGMENT, &val, sizeof(val));
|
|
|
|
/* Set the CAC parameters, if they exist in nvram. */
|
|
if ((str = nvram_get(strlcat_r(prefix, "dfs_preism", tmp, sizeof(tmp))))) {
|
|
val = atoi(str);
|
|
wl_iovar_setint(name, "dfs_preism", val);
|
|
}
|
|
if ((str = nvram_get(strlcat_r(prefix, "dfs_postism", tmp, sizeof(tmp))))) {
|
|
val = atoi(str);
|
|
wl_iovar_setint(name, "dfs_postism", val);
|
|
}
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "tpc_db", tmp, sizeof(tmp))));
|
|
WL_IOCTL(name, WLC_SEND_PWR_CONSTRAINT, &val, sizeof(val));
|
|
wlconf_dfs_pref_chan_options(name);
|
|
} else if (nvram_match(tmp, "d")) {
|
|
val = 0;
|
|
WL_IOCTL(name, WLC_SET_RADAR, &val, sizeof(val));
|
|
WL_IOCTL(name, WLC_SET_SPECT_MANAGMENT, &val, sizeof(val));
|
|
val = 1;
|
|
WL_IOCTL(name, WLC_SET_REGULATORY, &val, sizeof(val));
|
|
}
|
|
|
|
/* set bandwidth capability for nphy and calculate nbw */
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
struct {
|
|
int bandtype;
|
|
uint8 bw_cap;
|
|
} param;
|
|
|
|
/* Get the user nmode setting now */
|
|
nmode = AUTO; /* enable by default for NPHY */
|
|
/* Set n mode */
|
|
strlcat_r(prefix, "nmode", tmp, sizeof(tmp));
|
|
if (nvram_match(tmp, "0"))
|
|
nmode = OFF;
|
|
|
|
WL_IOVAR_SETINT(name, "nmode", (uint32)nmode);
|
|
|
|
if (nmode == OFF)
|
|
val = WLC_BW_CAP_20MHZ;
|
|
else
|
|
val = wlconf_bw_cap(prefix, bandtype);
|
|
|
|
/* record the bw here */
|
|
if (val == WLC_BW_CAP_80MHZ)
|
|
nbw = WL_CHANSPEC_BW_80;
|
|
else if (val == WLC_BW_CAP_40MHZ)
|
|
nbw = WL_CHANSPEC_BW_40;
|
|
|
|
param.bandtype = bandtype;
|
|
param.bw_cap = (uint8) val;
|
|
|
|
WL_IOVAR_SET(name, "bw_cap", ¶m, sizeof(param));
|
|
} else {
|
|
/* Save n mode to OFF */
|
|
nvram_set(strlcat_r(prefix, "nmode", tmp, sizeof(tmp)), "0");
|
|
}
|
|
|
|
/* Use chanspec to set the channel */
|
|
if ((str = nvram_get(strlcat_r(prefix, "chanspec", tmp, sizeof(tmp)))) != NULL) {
|
|
chanspec = wf_chspec_aton(str);
|
|
|
|
if (chanspec) {
|
|
WL_IOVAR_SETINT(name, "chanspec", (uint32)chanspec);
|
|
}
|
|
}
|
|
|
|
/* Legacy method of setting channels (for compatibility) */
|
|
/* Set channel before setting gmode or rateset */
|
|
/* Manual Channel Selection - when channel # is not 0 */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "channel", tmp, sizeof(tmp))));
|
|
if ((chanspec == 0) && val && !WLCONF_PHYTYPE_11N(phytype)) {
|
|
WL_SETINT(name, WLC_SET_CHANNEL, val);
|
|
if (ret) {
|
|
/* Use current channel (card may have changed) */
|
|
WL_IOCTL(name, WLC_GET_CHANNEL, &ci, sizeof(ci));
|
|
snprintf(buf, sizeof(buf), "%d", ci.target_channel);
|
|
nvram_set(strlcat_r(prefix, "channel", tmp, sizeof(tmp)), buf);
|
|
}
|
|
} else if ((chanspec == 0) && val && WLCONF_PHYTYPE_11N(phytype)) {
|
|
uint channel;
|
|
uint nctrlsb = 0;
|
|
uint cspecbw = (bandtype == WLC_BAND_2G) ?
|
|
WL_CHANSPEC_BAND_2G:WL_CHANSPEC_BAND_5G;
|
|
|
|
channel = val;
|
|
|
|
if (nbw == WL_CHANSPEC_BW_80) {
|
|
/* Get Ctrl SB for 80MHz channel */
|
|
str = nvram_safe_get(strlcat_r(prefix, "nctrlsb", tmp, sizeof(tmp)));
|
|
|
|
/* Adjust the channel to be center channel */
|
|
channel = channel + CH_40MHZ_APART - CH_10MHZ_APART;
|
|
|
|
if (!strcmp(str, "ll")) {
|
|
nctrlsb = WL_CHANSPEC_CTL_SB_LL;
|
|
} else if (!strcmp(str, "lu")) {
|
|
nctrlsb = WL_CHANSPEC_CTL_SB_LU;
|
|
channel -= CH_20MHZ_APART;
|
|
} else if (!strcmp(str, "ul")) {
|
|
nctrlsb = WL_CHANSPEC_CTL_SB_UL;
|
|
channel -= 2 * CH_20MHZ_APART;
|
|
} else if (!strcmp(str, "uu")) {
|
|
nctrlsb = WL_CHANSPEC_CTL_SB_UU;
|
|
channel -= 3 * CH_20MHZ_APART;
|
|
}
|
|
|
|
} else if (nbw == WL_CHANSPEC_BW_40) {
|
|
/* Get Ctrl SB for 40MHz channel */
|
|
str = nvram_safe_get(strlcat_r(prefix, "nctrlsb", tmp, sizeof(tmp)));
|
|
|
|
/* Adjust the channel to be center channel */
|
|
if (!strcmp(str, "lower")) {
|
|
nctrlsb = WL_CHANSPEC_CTL_SB_LOWER;
|
|
channel = channel + 2;
|
|
} else if (!strcmp(str, "upper")) {
|
|
nctrlsb = WL_CHANSPEC_CTL_SB_UPPER;
|
|
channel = channel - 2;
|
|
}
|
|
}
|
|
|
|
/* band | BW | CTRL SB | Channel */
|
|
chanspec |= (cspecbw | nbw | nctrlsb | channel);
|
|
WL_IOVAR_SETINT(name, "chanspec", (uint32)chanspec);
|
|
}
|
|
|
|
/* Set up number of Tx and Rx streams */
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
int count;
|
|
int streams;
|
|
|
|
/* Get the number of tx chains supported by the hardware */
|
|
wl_iovar_getint(name, "hw_txchain", &count);
|
|
/* update NVRAM with capabilities */
|
|
snprintf(var, sizeof(var), "%d", count);
|
|
nvram_set(strlcat_r(prefix, "hw_txchain", tmp, sizeof(tmp)), var);
|
|
|
|
/* Verify that there is an NVRAM param for txstreams, if not create it and
|
|
* set it to hw_txchain
|
|
*/
|
|
streams = atoi(nvram_safe_get(strlcat_r(prefix, "txchain", tmp, sizeof(tmp))));
|
|
if (streams == 0) {
|
|
/* invalid - NVRAM needs to be fixed/initialized */
|
|
nvram_set(strlcat_r(prefix, "txchain", tmp, sizeof(tmp)), var);
|
|
streams = count;
|
|
}
|
|
/* Apply user configured txstreams, use 1 if user disabled nmode */
|
|
WL_IOVAR_SETINT(name, "txchain", streams);
|
|
|
|
wl_iovar_getint(name, "hw_rxchain", &count);
|
|
/* update NVRAM with capabilities */
|
|
snprintf(var, sizeof(var), "%d", count);
|
|
nvram_set(strlcat_r(prefix, "hw_rxchain", tmp, sizeof(tmp)), var);
|
|
|
|
/* Verify that there is an NVRAM param for rxstreams, if not create it and
|
|
* set it to hw_txchain
|
|
*/
|
|
streams = atoi(nvram_safe_get(strlcat_r(prefix, "rxchain", tmp, sizeof(tmp))));
|
|
if (streams == 0) {
|
|
/* invalid - NVRAM needs to be fixed/initialized */
|
|
nvram_set(strlcat_r(prefix, "rxchain", tmp, sizeof(tmp)), var);
|
|
streams = count;
|
|
}
|
|
|
|
/* Apply user configured rxstreams, use 1 if user disabled nmode */
|
|
WL_IOVAR_SETINT(name, "rxchain", streams);
|
|
}
|
|
|
|
/* Reset to hardware rateset (band may have changed) */
|
|
WL_IOCTL(name, WLC_GET_RATESET, &rs, sizeof(wl_rateset_t));
|
|
WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof(wl_rateset_t));
|
|
|
|
/* Set gmode */
|
|
if (bandtype == WLC_BAND_2G) {
|
|
int override = WLC_PROTECTION_OFF;
|
|
int control = WLC_PROTECTION_CTL_OFF;
|
|
|
|
/* Set gmode */
|
|
gmode = atoi(nvram_safe_get(strlcat_r(prefix, "gmode", tmp, sizeof(tmp))));
|
|
WL_IOCTL(name, WLC_SET_GMODE, &gmode, sizeof(gmode));
|
|
|
|
/* Set gmode protection override and control algorithm */
|
|
strlcat_r(prefix, "gmode_protection", tmp, sizeof(tmp));
|
|
if (nvram_match(tmp, "auto")) {
|
|
override = WLC_PROTECTION_AUTO;
|
|
control = WLC_PROTECTION_CTL_OVERLAP;
|
|
}
|
|
WL_IOCTL(name, WLC_SET_GMODE_PROTECTION_OVERRIDE, &override, sizeof(override));
|
|
WL_IOCTL(name, WLC_SET_PROTECTION_CONTROL, &control, sizeof(control));
|
|
}
|
|
|
|
/* Set nmode_protection */
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
int override = WLC_PROTECTION_OFF;
|
|
int control = WLC_PROTECTION_CTL_OFF;
|
|
|
|
/* Set n protection override and control algorithm */
|
|
str = nvram_get(strlcat_r(prefix, "nmode_protection", tmp, sizeof(tmp)));
|
|
if (!str || !strcmp(str, "auto")) {
|
|
override = WLC_PROTECTION_AUTO;
|
|
control = WLC_PROTECTION_CTL_OVERLAP;
|
|
}
|
|
|
|
WL_IOVAR_SETINT(name, "nmode_protection_override",
|
|
(uint32)override);
|
|
WL_IOCTL(name, WLC_SET_PROTECTION_CONTROL, &control, sizeof(control));
|
|
}
|
|
|
|
/* Set vlan_prio_mode */
|
|
{
|
|
uint32 mode = OFF; /* default */
|
|
|
|
strlcat_r(prefix, "vlan_prio_mode", tmp, sizeof(tmp));
|
|
|
|
if (nvram_match(tmp, "on"))
|
|
mode = ON;
|
|
|
|
WL_IOVAR_SETINT(name, "vlan_mode", mode);
|
|
}
|
|
|
|
/* Get bluetooth coexistance(BTC) mode */
|
|
btc_mode = atoi(nvram_safe_get(strlcat_r(prefix, "btc_mode", tmp, sizeof(tmp))));
|
|
|
|
/* Set the AMPDU and AMSDU options based on the N-mode */
|
|
wme_global = wlconf_ampdu_amsdu_set(name, prefix, nmode, btc_mode);
|
|
|
|
/* Now that wme_global is known, check per-BSS disable settings */
|
|
for (i = 0; i < bclist->count; i++) {
|
|
char *subprefix;
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
|
|
subprefix = apsta ? prefix : bsscfg->prefix;
|
|
|
|
/* For each BSS, check WME; make sure wme is set properly for this interface */
|
|
strlcat_r(subprefix, "wme", tmp, sizeof(tmp));
|
|
nvram_set(tmp, wme_global ? "on" : "off");
|
|
|
|
str = nvram_safe_get(strlcat_r(bsscfg->prefix, "wme_bss_disable", tmp, sizeof(tmp)));
|
|
val = (str[0] == '1') ? 1 : 0;
|
|
WL_BSSIOVAR_SETINT(name, "wme_bss_disable", bsscfg->idx, val);
|
|
}
|
|
|
|
/*
|
|
* Set operational capabilities required for stations
|
|
* to associate to the BSS. Per-BSS setting.
|
|
*/
|
|
for (i = 0; i < bclist->count; i++) {
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
str = nvram_safe_get(strlcat_r(bsscfg->prefix, "bss_opmode_cap_reqd", tmp, sizeof(tmp)));
|
|
val = atoi(str);
|
|
WL_BSSIOVAR_SETINT(name, "mode_reqd", bsscfg->idx, val);
|
|
}
|
|
|
|
|
|
/* Get current rateset (gmode may have changed) */
|
|
WL_IOCTL(name, WLC_GET_CURR_RATESET, &rs, sizeof(wl_rateset_t));
|
|
|
|
strlcat_r(prefix, "rateset", tmp, sizeof(tmp));
|
|
if (nvram_match(tmp, "all")) {
|
|
/* Make all rates basic */
|
|
for (i = 0; i < rs.count; i++)
|
|
rs.rates[i] |= 0x80;
|
|
} else if (nvram_match(tmp, "12")) {
|
|
/* Make 1 and 2 basic */
|
|
for (i = 0; i < rs.count; i++) {
|
|
if ((rs.rates[i] & 0x7f) == 2 || (rs.rates[i] & 0x7f) == 4)
|
|
rs.rates[i] |= 0x80;
|
|
else
|
|
rs.rates[i] &= ~0x80;
|
|
}
|
|
}
|
|
|
|
if (phytype != PHY_TYPE_SSN && phytype != PHY_TYPE_LCN) {
|
|
/* Set BTC mode */
|
|
if (!wl_iovar_setint(name, "btc_mode", btc_mode)) {
|
|
if (btc_mode == WL_BTC_PREMPT) {
|
|
wl_rateset_t rs_tmp = rs;
|
|
/* remove 1Mbps and 2 Mbps from rateset */
|
|
for (i = 0, rs.count = 0; i < rs_tmp.count; i++) {
|
|
if ((rs_tmp.rates[i] & 0x7f) == 2 ||
|
|
(rs_tmp.rates[i] & 0x7f) == 4)
|
|
continue;
|
|
rs.rates[rs.count++] = rs_tmp.rates[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set rateset */
|
|
WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof(wl_rateset_t));
|
|
|
|
/* Allow short preamble settings for the following:
|
|
* 11b - short/long
|
|
* 11g - short /long in GMODE_LEGACY_B and GMODE_AUTO gmodes
|
|
* GMODE_PERFORMANCE and GMODE_LRS will use short and long
|
|
* preambles respectively, by default
|
|
* 11n - short/long applicable in 2.4G band only
|
|
*/
|
|
if (phytype == PHY_TYPE_B ||
|
|
(WLCONF_PHYTYPE_11N(phytype) && (bandtype == WLC_BAND_2G)) ||
|
|
((phytype == PHY_TYPE_G || phytype == PHY_TYPE_LP) &&
|
|
(gmode == GMODE_LEGACY_B || gmode == GMODE_AUTO))) {
|
|
strlcat_r(prefix, "plcphdr", tmp, sizeof(tmp));
|
|
if (nvram_match(tmp, "long"))
|
|
val = WLC_PLCP_AUTO;
|
|
else
|
|
val = WLC_PLCP_SHORT;
|
|
WL_IOCTL(name, WLC_SET_PLCPHDR, &val, sizeof(val));
|
|
}
|
|
|
|
/* Set rate in 500 Kbps units */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "rate", tmp, sizeof(tmp)))) / 500000;
|
|
|
|
/* Convert Auto mcsidx to Auto rate */
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
int mcsidx = atoi(nvram_safe_get(strlcat_r(prefix, "nmcsidx", tmp, sizeof(tmp))));
|
|
|
|
/* -1 mcsidx used to designate AUTO rate */
|
|
if (mcsidx == -1)
|
|
val = 0;
|
|
}
|
|
|
|
/* 1Mbps and 2 Mbps are not allowed in BTC pre-emptive mode */
|
|
if (btc_mode == WL_BTC_PREMPT && (val == 2 || val == 4))
|
|
/* Must b/g band. Set to 5.5Mbps */
|
|
val = 11;
|
|
|
|
/* it is band-blind. try both band */
|
|
error_bg = wl_iovar_setint(name, "2g_rate", val);
|
|
error_a = wl_iovar_setint(name, "5g_rate", val);
|
|
|
|
if (error_bg && error_a) {
|
|
/* both failed. Try default rate (card may have changed) */
|
|
val = 0;
|
|
|
|
error_bg = wl_iovar_setint(name, "2g_rate", val);
|
|
error_a = wl_iovar_setint(name, "5g_rate", val);
|
|
|
|
snprintf(buf, sizeof(buf), "%d", val);
|
|
nvram_set(strlcat_r(prefix, "rate", tmp, sizeof(tmp)), buf);
|
|
}
|
|
|
|
/* check if nrate needs to be applied */
|
|
if (nmode != OFF) {
|
|
uint32 nrate = 0;
|
|
int mcsidx = atoi(nvram_safe_get(strlcat_r(prefix, "nmcsidx", tmp, sizeof(tmp))));
|
|
bool ismcs = (mcsidx >= 0);
|
|
|
|
/* mcsidx of 32 is valid only for 40 Mhz */
|
|
if (mcsidx == 32 && nbw == WL_CHANSPEC_BW_20) {
|
|
mcsidx = -1;
|
|
ismcs = FALSE;
|
|
nvram_set(strlcat_r(prefix, "nmcsidx", tmp, sizeof(tmp)), "-1");
|
|
}
|
|
|
|
/* Use nrate iovar only for MCS rate. */
|
|
if (ismcs) {
|
|
nrate |= WL_RSPEC_ENCODE_HT;
|
|
nrate |= mcsidx & WL_RSPEC_RATE_MASK;
|
|
|
|
WL_IOVAR_SETINT(name, "nrate", nrate);
|
|
}
|
|
}
|
|
|
|
/* Set multicast rate in 500 Kbps units */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "mrate", tmp, sizeof(tmp)))) / 500000;
|
|
/* 1Mbps and 2 Mbps are not allowed in BTC pre-emptive mode */
|
|
if (btc_mode == WL_BTC_PREMPT && (val == 2 || val == 4))
|
|
/* Must b/g band. Set to 5.5Mbps */
|
|
val = 11;
|
|
|
|
/* it is band-blind. try both band */
|
|
error_bg = wl_iovar_setint(name, "2g_mrate", val);
|
|
error_a = wl_iovar_setint(name, "5g_mrate", val);
|
|
|
|
if (error_bg && error_a) {
|
|
/* Try default rate (card may have changed) */
|
|
val = 0;
|
|
|
|
WL_IOVAR_SETINT(name, "2g_mrate", val);
|
|
WL_IOVAR_SETINT(name, "5g_mrate", val);
|
|
|
|
snprintf(buf, sizeof(buf), "%d", val);
|
|
nvram_set(strlcat_r(prefix, "mrate", tmp, sizeof(tmp)), buf);
|
|
}
|
|
|
|
/* Set fragmentation threshold */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "frag", tmp, sizeof(tmp))));
|
|
wl_iovar_setint(name, "fragthresh", val);
|
|
|
|
/* Set RTS threshold */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "rts", tmp, sizeof(tmp))));
|
|
wl_iovar_setint(name, "rtsthresh", val);
|
|
|
|
/* Set DTIM period */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "dtim", tmp, sizeof(tmp))));
|
|
WL_IOCTL(name, WLC_SET_DTIMPRD, &val, sizeof(val));
|
|
|
|
/* Set beacon period */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "bcn", tmp, sizeof(tmp))));
|
|
WL_IOCTL(name, WLC_SET_BCNPRD, &val, sizeof(val));
|
|
|
|
/* Set SW probe response */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "probresp_sw", tmp, sizeof(tmp))));
|
|
wl_iovar_setint(name, "probresp_sw", val);
|
|
|
|
#ifdef TCONFIG_WLCONF_VHT /* prepare for future change; right now we use wl util to apply it */
|
|
/* Update vht_features only if explicitly updated by NVRAM */
|
|
if (phytype == PHY_TYPE_AC) {
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "vhtmode", tmp, sizeof(tmp))));
|
|
if (val != -1) {
|
|
WL_IOVAR_SETINT(name, "vhtmode", val);
|
|
}
|
|
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "vht_features", tmp, sizeof(tmp))));
|
|
if (val != -1) {
|
|
WL_IOVAR_SETINT(name, "vht_features", val);
|
|
}
|
|
}
|
|
#endif /* TCONFIG_WLCONF_VHT */
|
|
|
|
/* Set beacon rotation */
|
|
str = nvram_get(strlcat_r(prefix, "bcn_rotate", tmp, sizeof(tmp)));
|
|
if (!str) {
|
|
/* No nvram variable found, use the default */
|
|
str = nvram_default_get(strlcat_r(prefix, "bcn_rotate", tmp, sizeof(tmp)));
|
|
}
|
|
val = atoi(str);
|
|
wl_iovar_setint(name, "bcn_rotate", val);
|
|
|
|
/* Set framebursting mode */
|
|
if (btc_mode == WL_BTC_PREMPT)
|
|
val = FALSE;
|
|
else
|
|
val = nvram_match(strlcat_r(prefix, "frameburst", tmp, sizeof(tmp)), "on");
|
|
WL_IOCTL(name, WLC_SET_FAKEFRAG, &val, sizeof(val));
|
|
|
|
/* Set STBC tx and rx mode */
|
|
if (phytype == PHY_TYPE_N ||
|
|
phytype == PHY_TYPE_HT ||
|
|
phytype == PHY_TYPE_AC) {
|
|
char *nvram_str = nvram_safe_get(strlcat_r(prefix, "stbc_tx", tmp, sizeof(tmp)));
|
|
|
|
if (!strcmp(nvram_str, "auto")) {
|
|
WL_IOVAR_SETINT(name, "stbc_tx", AUTO);
|
|
} else if (!strcmp(nvram_str, "on")) {
|
|
WL_IOVAR_SETINT(name, "stbc_tx", ON);
|
|
} else if (!strcmp(nvram_str, "off")) {
|
|
WL_IOVAR_SETINT(name, "stbc_tx", OFF);
|
|
}
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "stbc_rx", tmp, sizeof(tmp))));
|
|
WL_IOVAR_SETINT(name, "stbc_rx", val);
|
|
}
|
|
|
|
/* Set RIFS mode based on framebursting */
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
char *nvram_str = nvram_safe_get(strlcat_r(prefix, "rifs", tmp, sizeof(tmp)));
|
|
if (!strcmp(nvram_str, "on"))
|
|
wl_iovar_setint(name, "rifs", ON);
|
|
else if (!strcmp(nvram_str, "off"))
|
|
wl_iovar_setint(name, "rifs", OFF);
|
|
|
|
/* RIFS mode advertisement */
|
|
nvram_str = nvram_safe_get(strlcat_r(prefix, "rifs_advert", tmp, sizeof(tmp)));
|
|
if (!strcmp(nvram_str, "auto"))
|
|
wl_iovar_setint(name, "rifs_advert", AUTO);
|
|
else if (!strcmp(nvram_str, "off"))
|
|
wl_iovar_setint(name, "rifs_advert", OFF);
|
|
}
|
|
|
|
/* Override BA mode only if set to on/off */
|
|
ba = nvram_safe_get(strlcat_r(prefix, "ba", tmp, sizeof(tmp)));
|
|
if (!strcmp(ba, "on"))
|
|
wl_iovar_setint(name, "ba", ON);
|
|
else if (!strcmp(ba, "off"))
|
|
wl_iovar_setint(name, "ba", OFF);
|
|
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
val = AVG_DMA_XFER_RATE;
|
|
wl_iovar_set(name, "avg_dma_xfer_rate", &val, sizeof(val));
|
|
}
|
|
|
|
/*
|
|
* If no nvram variable exists to force non-aggregated mpdu regulation on/off,
|
|
* limit to 2G interfaces.
|
|
*/
|
|
str = nvram_get(strlcat_r(prefix, "nar", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
val = atoi(str);
|
|
} else {
|
|
val = (bandtype == WLC_BAND_2G) ? 1 : 0;
|
|
}
|
|
WLCONF_DBG("%sabling non-aggregated regulation on band %d\n", (val) ? "En":"Dis", bandtype);
|
|
WL_IOVAR_SETINT(name, "nar", val);
|
|
if (val) {
|
|
/* nar is enabled on this interface, add tuneable parameters */
|
|
str = nvram_get(strlcat_r(prefix, "nar_handle_ampdu", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
WL_IOVAR_SETINT(name, "nar_handle_ampdu", atoi(str));
|
|
}
|
|
str = nvram_get(strlcat_r(prefix, "nar_transit_limit", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
WL_IOVAR_SETINT(name, "nar_transit_limit", atoi(str));
|
|
}
|
|
}
|
|
|
|
/* Set up TxBF */
|
|
wlconf_set_txbf(name, prefix);
|
|
|
|
/* set airtime fairness */
|
|
val = 0;
|
|
str = nvram_get(strlcat_r(prefix, "atf", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
val = atoi(str);
|
|
}
|
|
WL_IOVAR_SETINT(name, "atf", val);
|
|
|
|
str = nvram_get(strlcat_r(prefix, "ampdu_atf_us", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
val = atoi(str);
|
|
if (val) {
|
|
WL_IOVAR_SETINT(name, "ampdu_atf_us", val);
|
|
WL_IOVAR_SETINT(name, "nar_atf_us", val);
|
|
}
|
|
}
|
|
|
|
/* set TAF */
|
|
val = 0;
|
|
str = nvram_get(strlcat_r(prefix, "taf_enable", tmp, sizeof(tmp)));
|
|
if (str && (bandtype == WLC_BAND_5G)) {
|
|
val = atoi(str);
|
|
}
|
|
wlconf_set_taf(name, val);
|
|
|
|
/* Bring the interface back up */
|
|
WL_IOCTL(name, WLC_UP, NULL, 0);
|
|
|
|
/* set phy_percal_delay */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "percal_delay", tmp, sizeof(tmp))));
|
|
if (val) {
|
|
wl_iovar_set(name, "phy_percal_delay", &val, sizeof(val));
|
|
}
|
|
|
|
/* Set phy periodic cal if nvram present. Otherwise, use driver defaults. */
|
|
str = nvram_get(strlcat_r(prefix, "cal_period", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
/*
|
|
* If cal_period is "-1 / Auto"
|
|
* - For corerev >= 40, set cal_period to 0
|
|
* - For corerev < 40, use driver defaults.
|
|
* Else
|
|
* - Use the value specified in the nvram.
|
|
*/
|
|
val = atoi(str);
|
|
if (val == -1) {
|
|
if (rev.corerev >= 40) {
|
|
val = 0;
|
|
WL_IOVAR_SET(name, "cal_period", &val, sizeof(val));
|
|
}
|
|
} else {
|
|
WL_IOVAR_SET(name, "cal_period", &val, sizeof(val));
|
|
}
|
|
}
|
|
|
|
/* Set antenna */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "antdiv", tmp, sizeof(tmp))));
|
|
WL_IOCTL(name, WLC_SET_ANTDIV, &val, sizeof(val));
|
|
|
|
/* Set antenna selection */
|
|
wlconf_set_antsel(name, prefix);
|
|
|
|
/* Set radar parameters if it is enabled */
|
|
if (radar_enab) {
|
|
wlconf_set_radarthrs(name, prefix);
|
|
}
|
|
|
|
/* set pspretend */
|
|
val = 0;
|
|
if (ap) {
|
|
/* Set pspretend for multi-ssid bss */
|
|
for (i = 0; i < bclist->count; i++) {
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
str = nvram_safe_get(strlcat_r(bsscfg->prefix,
|
|
"pspretend_retry_limit", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
val = atoi(str);
|
|
WL_BSSIOVAR_SETINT(name, "pspretend_retry_limit", bsscfg->idx, val);
|
|
}
|
|
}
|
|
|
|
/* now set it for primary bss */
|
|
val = 0;
|
|
str = nvram_get(strlcat_r(prefix, "pspretend_retry_limit", tmp, sizeof(tmp)));
|
|
if (str) {
|
|
val = atoi(str);
|
|
}
|
|
}
|
|
WL_IOVAR_SETINT(name, "pspretend_retry_limit", val);
|
|
|
|
/* Set channel interference threshold value if it is enabled */
|
|
|
|
str = nvram_get(strlcat_r(prefix, "glitchthres", tmp, sizeof(tmp)));
|
|
|
|
if (str) {
|
|
int glitch_thres = atoi(str);
|
|
if (glitch_thres > 0)
|
|
WL_IOVAR_SETINT(name, "chanim_glitchthres", glitch_thres);
|
|
}
|
|
|
|
str = nvram_get(strlcat_r(prefix, "ccathres", tmp, sizeof(tmp)));
|
|
|
|
if (str) {
|
|
int cca_thres = atoi(str);
|
|
if (cca_thres > 0)
|
|
WL_IOVAR_SETINT(name, "chanim_ccathres", cca_thres);
|
|
}
|
|
|
|
str = nvram_get(strlcat_r(prefix, "chanimmode", tmp, sizeof(tmp)));
|
|
|
|
if (str) {
|
|
int chanim_mode = atoi(str);
|
|
if (chanim_mode >= 0)
|
|
WL_IOVAR_SETINT(name, "chanim_mode", chanim_mode);
|
|
}
|
|
|
|
/* bcm_dcs (dynamic channel selection) settings */
|
|
str = nvram_safe_get(strlcat_r(prefix, "bcmdcs", tmp, sizeof(tmp)));
|
|
if (!strcmp(str, "on"))
|
|
wl_iovar_setint(name, "bcm_dcs", ON);
|
|
else if (!strcmp(str, "off"))
|
|
wl_iovar_setint(name, "bcm_dcs", OFF);
|
|
|
|
/* Overlapping BSS Coexistence aka 20/40 Coex. aka OBSS Coex.
|
|
* For an AP - Only use if 2G band AND user wants a 40Mhz chanspec.
|
|
* For a STA - Always
|
|
*/
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
if (sta ||
|
|
((ap || apsta) && (nbw == WL_CHANSPEC_BW_40) && (bandtype == WLC_BAND_2G))) {
|
|
str = nvram_safe_get(strlcat_r(prefix, "obss_coex", tmp, sizeof(tmp)));
|
|
if (!str) {
|
|
/* No nvram variable found, use the default */
|
|
str = nvram_default_get(strlcat_r(prefix, "obss_coex", tmp, sizeof(tmp)));
|
|
}
|
|
obss_coex = atoi(str);
|
|
} else {
|
|
/* Need to disable obss coex in case of 20MHz and/or
|
|
* in case of 5G.
|
|
*/
|
|
obss_coex = 0;
|
|
}
|
|
#ifdef WLTEST
|
|
/* force coex off for msgtest build */
|
|
obss_coex = 0;
|
|
#endif
|
|
WL_IOVAR_SETINT(name, "obss_coex", obss_coex);
|
|
}
|
|
|
|
/* Set up TxBF timer */
|
|
wlconf_set_txbf_timer(name, prefix);
|
|
|
|
/* Auto Channel Selection:
|
|
* 1. When channel # is 0 in AP mode, this determines our channel and 20Mhz vs. 40Mhz
|
|
* 2. If we're running OBSS Coex and the user specified a channel, Autochannel runs to
|
|
* do an initial scan to help us make decisions about whether we can create a 40Mhz AP
|
|
*/
|
|
/* The following condition(s) must be met in order for Auto Channel Selection to work.
|
|
* - the I/F must be up for the channel scan
|
|
* - the AP must not be supporting a BSS (all BSS Configs must be disabled)
|
|
*/
|
|
if (ap || apsta) {
|
|
int channel = chanspec ? wf_chspec_ctlchan(chanspec) : 0;
|
|
#ifdef EXT_ACS
|
|
char tmp[100];
|
|
char *ptr;
|
|
char * str_val;
|
|
|
|
str_val = nvram_safe_get("acs_mode");
|
|
if (!strcmp(str_val, "legacy"))
|
|
goto legacy_mode;
|
|
|
|
snprintf(tmp, sizeof(tmp), "acs_ifnames");
|
|
ptr = nvram_get(tmp);
|
|
if (ptr) {
|
|
if (!find_in_list(ptr, name)) {
|
|
snprintf(buf, sizeof(buf), "%s %s", ptr, name);
|
|
nvram_set(tmp, buf);
|
|
}
|
|
} else {
|
|
strncpy(buf, name, sizeof(buf));
|
|
nvram_set(tmp, buf);
|
|
}
|
|
|
|
WL_IOVAR_SETINT(name, "chanim_mode", CHANIM_EXT);
|
|
goto legacy_end;
|
|
|
|
legacy_mode:
|
|
#endif /* EXT_ACS */
|
|
if (obss_coex || channel == 0) {
|
|
if (WLCONF_PHYTYPE_11N(phytype)) {
|
|
chanspec_t chanspec;
|
|
int pref_chspec;
|
|
|
|
if (channel != 0) {
|
|
/* assumes that initial chanspec has been set earlier */
|
|
/* Maybe we expand scope of chanspec from above so
|
|
* that we don't have to do the iovar_get here?
|
|
*/
|
|
|
|
/* We're not doing auto-channel, give the driver
|
|
* the preferred chanspec.
|
|
*/
|
|
WL_IOVAR_GETINT(name, "chanspec", &pref_chspec);
|
|
WL_IOVAR_SETINT(name, "pref_chanspec", pref_chspec);
|
|
} else {
|
|
WL_IOVAR_SETINT(name, "pref_chanspec", 0);
|
|
}
|
|
|
|
chanspec = wlconf_auto_chanspec(name);
|
|
if (chanspec != 0)
|
|
WL_IOVAR_SETINT(name, "chanspec", chanspec);
|
|
} else {
|
|
/* select a channel */
|
|
val = wlconf_auto_channel(name);
|
|
/* switch to the selected channel */
|
|
if (val != 0)
|
|
WL_IOCTL(name, WLC_SET_CHANNEL, &val, sizeof(val));
|
|
}
|
|
/* set the auto channel scan timer in the driver when in auto mode */
|
|
if (channel == 0) {
|
|
val = 15; /* 15 minutes for now */
|
|
} else {
|
|
val = 0;
|
|
}
|
|
} else {
|
|
/* reset the channel scan timer in the driver when not in auto mode */
|
|
val = 0;
|
|
}
|
|
|
|
WL_IOCTL(name, WLC_SET_CS_SCAN_TIMER, &val, sizeof(val));
|
|
WL_IOVAR_SETINT(name, "chanim_mode", CHANIM_ACT);
|
|
#ifdef EXT_ACS
|
|
legacy_end:
|
|
;
|
|
#endif /* EXT_ACS */
|
|
/* Apply sta_config configuration settings for this interface */
|
|
foreach(var, nvram_safe_get("sta_config"), next) {
|
|
wlconf_process_sta_config_entry(name, var);
|
|
}
|
|
|
|
} /* AP or APSTA */
|
|
|
|
/* Security settings for each BSS Configuration */
|
|
for (i = 0; i < bclist->count; i++) {
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
wlconf_security_options(name, bsscfg->prefix, bsscfg->idx,
|
|
mac_spoof, wet || sta || apsta || psta || psr);
|
|
}
|
|
|
|
/*
|
|
* Finally enable BSS Configs or Join BSS
|
|
*
|
|
* AP: Enable BSS Config to bring AP up only when nas will not run
|
|
* STA: Join the BSS regardless.
|
|
*/
|
|
for (i = 0; i < bclist->count; i++) {
|
|
struct {int bsscfg_idx; int enable;} setbuf;
|
|
char vifname[VIFNAME_LEN];
|
|
char *name_ptr = name;
|
|
|
|
setbuf.bsscfg_idx = bclist->bsscfgs[i].idx;
|
|
setbuf.enable = 0;
|
|
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
if (nvram_match(strlcat_r(bsscfg->prefix, "bss_enabled", tmp, sizeof(tmp)), "1")) {
|
|
setbuf.enable = 1;
|
|
}
|
|
|
|
/* Set the MAC list */
|
|
maclist = (struct maclist *)buf;
|
|
maclist->count = 0;
|
|
if (!nvram_match(strlcat_r(bsscfg->prefix, "macmode", tmp, sizeof(tmp)), "disabled")) {
|
|
ea = maclist->ea;
|
|
foreach(var, nvram_safe_get(strlcat_r(bsscfg->prefix, "maclist", tmp, sizeof(tmp))),
|
|
next) {
|
|
if (((char *)((&ea[1])->octet)) > ((char *)(&buf[sizeof(buf)])))
|
|
break;
|
|
if (ether_atoe(var, ea->octet)) {
|
|
maclist->count++;
|
|
ea++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (setbuf.bsscfg_idx == 0) {
|
|
name_ptr = name;
|
|
} else { /* Non-primary BSS; changes name syntax */
|
|
char tmp[VIFNAME_LEN];
|
|
int len;
|
|
|
|
/* Remove trailing _ if present */
|
|
memset(tmp, 0, sizeof(tmp));
|
|
strncpy(tmp, bsscfg->prefix, VIFNAME_LEN - 1);
|
|
if (((len = strlen(tmp)) > 0) && (tmp[len - 1] == '_')) {
|
|
tmp[len - 1] = 0;
|
|
}
|
|
nvifname_to_osifname(tmp, vifname, VIFNAME_LEN);
|
|
name_ptr = vifname;
|
|
}
|
|
|
|
WL_IOCTL(name_ptr, WLC_SET_MACLIST, buf, sizeof(buf));
|
|
|
|
/* Set macmode for each VIF */
|
|
(void) strlcat_r(bsscfg->prefix, "macmode", tmp, sizeof(tmp));
|
|
|
|
if (nvram_match(tmp, "deny"))
|
|
val = WLC_MACMODE_DENY;
|
|
else if (nvram_match(tmp, "allow"))
|
|
val = WLC_MACMODE_ALLOW;
|
|
else
|
|
val = WLC_MACMODE_DISABLED;
|
|
|
|
WL_IOCTL(name_ptr, WLC_SET_MACMODE, &val, sizeof(val));
|
|
}
|
|
|
|
ret = 0;
|
|
exit:
|
|
if (bclist != NULL)
|
|
free(bclist);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
wlconf_down(char *name)
|
|
{
|
|
int val, ret = 0;
|
|
int i;
|
|
int wlsubunit;
|
|
int bcmerr;
|
|
struct {int bsscfg_idx; int enable;} setbuf;
|
|
int wl_ap_build = 0; /* 1 = wl compiled with AP capabilities */
|
|
char cap[WLC_IOCTL_SMLEN];
|
|
char caps[WLC_IOCTL_MEDLEN];
|
|
char *next;
|
|
wlc_ssid_t ssid;
|
|
|
|
/* wlconf doesn't work for virtual i/f */
|
|
if (get_ifname_unit(name, NULL, &wlsubunit) == 0 && wlsubunit >= 0) {
|
|
WLCONF_DBG("wlconf: skipping virtual interface \"%s\"\n", name);
|
|
return 0;
|
|
}
|
|
|
|
/* Check interface (fail silently for non-wl interfaces) */
|
|
if ((ret = wl_probe(name)))
|
|
return ret;
|
|
|
|
/* because of ifdefs in wl driver, when we don't have AP capabilities we
|
|
* can't use the same iovars to configure the wl.
|
|
* so we use "wl_ap_build" to help us know how to configure the driver
|
|
*/
|
|
if (wl_iovar_get(name, "cap", (void *)caps, sizeof(caps)))
|
|
return -1;
|
|
|
|
foreach(cap, caps, next) {
|
|
if (!strcmp(cap, "ap")) {
|
|
wl_ap_build = 1;
|
|
}
|
|
}
|
|
|
|
if (wl_ap_build) {
|
|
/* Bring down the interface */
|
|
WL_IOCTL(name, WLC_DOWN, NULL, 0);
|
|
|
|
/* Disable all BSS Configs */
|
|
for (i = 0; i < WL_MAXBSSCFG; i++) {
|
|
setbuf.bsscfg_idx = i;
|
|
setbuf.enable = 0;
|
|
|
|
ret = wl_iovar_set(name, "bss", &setbuf, sizeof(setbuf));
|
|
if (ret) {
|
|
wl_iovar_getint(name, "bcmerror", &bcmerr);
|
|
/* fail quietly on a range error since the driver may
|
|
* support fewer bsscfgs than we are prepared to configure
|
|
*/
|
|
if (bcmerr == BCME_RANGE)
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
WL_IOCTL(name, WLC_GET_UP, &val, sizeof(val));
|
|
if (val) {
|
|
/* Nuke SSID */
|
|
ssid.SSID_len = 0;
|
|
ssid.SSID[0] = '\0';
|
|
WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid));
|
|
|
|
/* Bring down the interface */
|
|
WL_IOCTL(name, WLC_DOWN, NULL, 0);
|
|
}
|
|
}
|
|
|
|
/* Nuke the WDS list */
|
|
wlconf_wds_clear(name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
wlconf_start(char *name)
|
|
{
|
|
int i, ii, unit, val, ret = 0;
|
|
int wlunit = -1;
|
|
int wlsubunit = -1;
|
|
int ap, apsta, wds, sta = 0, wet = 0;
|
|
int wl_ap_build = 0; /* wl compiled with AP capabilities */
|
|
char buf[WLC_IOCTL_SMLEN];
|
|
struct maclist *maclist;
|
|
struct ether_addr *ea;
|
|
struct bsscfg_list *bclist = NULL;
|
|
struct bsscfg_info *bsscfg = NULL;
|
|
wlc_ssid_t ssid;
|
|
char cap[WLC_IOCTL_SMLEN], caps[WLC_IOCTL_MEDLEN];
|
|
char var[80], tmp[100], prefix[PREFIX_LEN], *str, *next;
|
|
int trf_mgmt_cap = 0, trf_mgmt_dwm_cap = 0;
|
|
bool dwm_supported = FALSE;
|
|
#ifdef __CONFIG_DHDAP__
|
|
int is_dhd = 0;
|
|
#endif
|
|
|
|
/* Check interface (fail silently for non-wl interfaces) */
|
|
if ((ret = wl_probe(name)))
|
|
return ret;
|
|
|
|
/* wlconf doesn't work for virtual i/f, so if we are given a
|
|
* virtual i/f return 0 if that interface is in it's parent's "vifs"
|
|
* list otherwise return -1
|
|
*/
|
|
memset(tmp, 0, sizeof(tmp));
|
|
if (get_ifname_unit(name, &wlunit, &wlsubunit) == 0) {
|
|
if (wlsubunit >= 0) {
|
|
/* we have been given a virtual i/f,
|
|
* is it in it's parent i/f's virtual i/f list?
|
|
*/
|
|
sprintf(tmp, "wl%d_vifs", wlunit);
|
|
|
|
if (strstr(nvram_safe_get(tmp), name) == NULL)
|
|
return -1; /* config error */
|
|
else
|
|
return 0; /* okay */
|
|
}
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
|
|
/* because of ifdefs in wl driver, when we don't have AP capabilities we
|
|
* can't use the same iovars to configure the wl.
|
|
* so we use "wl_ap_build" to help us know how to configure the driver
|
|
*/
|
|
if (wl_iovar_get(name, "cap", (void *)caps, sizeof(caps)))
|
|
return -1;
|
|
|
|
foreach(cap, caps, next) {
|
|
if (!strcmp(cap, "ap"))
|
|
wl_ap_build = 1;
|
|
|
|
if (!strcmp(cap, "traffic-mgmt"))
|
|
trf_mgmt_cap = 1;
|
|
|
|
if (!strcmp(cap, "traffic-mgmt-dwm"))
|
|
trf_mgmt_dwm_cap = 1;
|
|
}
|
|
|
|
/* Get instance */
|
|
WL_IOCTL(name, WLC_GET_INSTANCE, &unit, sizeof(unit));
|
|
snprintf(prefix, sizeof(prefix), "wl%d_", unit);
|
|
|
|
/* Get the list of BSS Configs */
|
|
if (!(bclist = wlconf_get_bsscfgs(name, prefix)))
|
|
return -1;
|
|
|
|
/* wlX_mode settings: AP, STA, WET, BSS/IBSS, APSTA */
|
|
str = nvram_safe_get(strlcat_r(prefix, "mode", tmp, sizeof(tmp)));
|
|
ap = (!strcmp(str, "") || !strcmp(str, "ap"));
|
|
apsta = (!strcmp(str, "apsta") ||
|
|
((!strcmp(str, "sta") || !strcmp(str, "psr") || !strcmp(str, "wet")) &&
|
|
bclist->count > 1));
|
|
sta = (!strcmp(str, "sta") && bclist->count == 1);
|
|
wds = !strcmp(str, "wds");
|
|
wet = !strcmp(str, "wet");
|
|
if (!strcmp(str, "mac_spoof") || !strcmp(str, "psta") || !strcmp(str, "psr"))
|
|
sta = 1;
|
|
|
|
/* Retain remaining WET effects only if not APSTA */
|
|
wet &= !apsta;
|
|
|
|
/* AP only config, code copied as-is from wlconf function */
|
|
if (ap || apsta || wds) {
|
|
/* Set lazy WDS mode */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "lazywds", tmp, sizeof(tmp))));
|
|
WL_IOCTL(name, WLC_SET_LAZYWDS, &val, sizeof(val));
|
|
|
|
/* Set the WDS list */
|
|
maclist = (struct maclist *) buf;
|
|
maclist->count = 0;
|
|
ea = maclist->ea;
|
|
foreach(var, nvram_safe_get(strlcat_r(prefix, "wds", tmp, sizeof(tmp))), next) {
|
|
if (((char *)(ea->octet)) > ((char *)(&buf[sizeof(buf)])))
|
|
break;
|
|
ether_atoe(var, ea->octet);
|
|
maclist->count++;
|
|
ea++;
|
|
}
|
|
WL_IOCTL(name, WLC_SET_WDSLIST, buf, sizeof(buf));
|
|
|
|
/* Set WDS link detection timeout */
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wds_timeout", tmp, sizeof(tmp))));
|
|
wl_iovar_setint(name, "wdstimeout", val);
|
|
}
|
|
|
|
/*
|
|
* Finally enable BSS Configs or Join BSS
|
|
* code copied as-is from wlconf function
|
|
*/
|
|
for (i = 0; i < bclist->count; i++) {
|
|
struct {int bsscfg_idx; int enable;} setbuf;
|
|
|
|
setbuf.bsscfg_idx = bclist->bsscfgs[i].idx;
|
|
setbuf.enable = 0;
|
|
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
if (nvram_match(strlcat_r(bsscfg->prefix, "bss_enabled", tmp, sizeof(tmp)), "1")) {
|
|
setbuf.enable = 1;
|
|
}
|
|
|
|
/* bring up BSS */
|
|
if (ap || apsta || sta || wet) {
|
|
for (ii = 0; ii < MAX_BSS_UP_RETRIES; ii++) {
|
|
if (wl_ap_build) {
|
|
WL_IOVAR_SET(name, "bss", &setbuf, sizeof(setbuf));
|
|
}
|
|
else {
|
|
strlcat_r(prefix, "ssid", tmp, sizeof(tmp));
|
|
ssid.SSID_len = strlen(nvram_safe_get(tmp));
|
|
if (ssid.SSID_len > sizeof(ssid.SSID))
|
|
ssid.SSID_len = sizeof(ssid.SSID);
|
|
strncpy((char *)ssid.SSID, nvram_safe_get(tmp),
|
|
ssid.SSID_len);
|
|
WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid));
|
|
}
|
|
if (apsta && (ret != 0))
|
|
sleep_ms(1000);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
if ((ap || apsta || sta) && (trf_mgmt_cap)) {
|
|
if (trf_mgmt_dwm_cap && ap)
|
|
dwm_supported = TRUE;
|
|
trf_mgmt_settings(prefix, dwm_supported);
|
|
}
|
|
|
|
#ifdef TRAFFIC_MGMT_RSSI_POLICY
|
|
if ((ap || apsta) && (trf_mgmt_cap)) {
|
|
trf_mgmt_rssi_policy(prefix);
|
|
}
|
|
#endif /* TRAFFIC_MGMT_RSSI_POLICY */
|
|
|
|
#ifdef __CONFIG_EMF__
|
|
#ifdef __CONFIG_DHDAP__
|
|
/* WMF will be managed in DHD for FDAP */
|
|
/* Check if interface uses dhd adapter */
|
|
is_dhd = !dhd_probe(name);
|
|
|
|
if (is_dhd) {
|
|
if (nvram_match(strlcat_r(bsscfg->prefix, "wmf_bss_enable", tmp, sizeof(tmp)), "1")) {
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_ucigmp_query", tmp, sizeof(tmp))));
|
|
(void)dhd_iovar_setint(name, "wmf_ucast_igmp_query", val);
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_mdata_sendup", tmp, sizeof(tmp))));
|
|
(void)dhd_iovar_setint(name, "wmf_mcast_data_sendup", val);
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_ucast_upnp", tmp, sizeof(tmp))));
|
|
(void)dhd_iovar_setint(name, "wmf_ucast_upnp", val);
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_igmpq_filter", tmp, sizeof(tmp))));
|
|
(void)dhd_iovar_setint(name, "wmf_igmpq_filter", val);
|
|
}
|
|
} else
|
|
#endif /* __CONFIG_DHDAP__ */
|
|
for (i = 0; i < bclist->count; i++) {
|
|
if (nvram_match(strlcat_r(bsscfg->prefix, "wmf_bss_enable", tmp, sizeof(tmp)), "1")) {
|
|
bsscfg = &bclist->bsscfgs[i];
|
|
strncpy(prefix, bsscfg->prefix, PREFIX_LEN - 1);
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_ucigmp_query", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "wmf_ucast_igmp_query", bsscfg->idx, val);
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_mdata_sendup", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "wmf_mcast_data_sendup", bsscfg->idx, val);
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_ucast_upnp", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "wmf_ucast_upnp", bsscfg->idx, val);
|
|
val = atoi(nvram_safe_get(strlcat_r(prefix, "wmf_igmpq_filter", tmp, sizeof(tmp))));
|
|
WL_BSSIOVAR_SETINT(name, "wmf_igmpq_filter", bsscfg->idx, val);
|
|
}
|
|
}
|
|
#endif /* __CONFIG_EMF__ */
|
|
|
|
if (bclist != NULL)
|
|
free(bclist);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#if defined(linux)
|
|
static int
|
|
wlconf_security(char *name)
|
|
{
|
|
int unit, bsscfg_idx;
|
|
char prefix[PREFIX_LEN];
|
|
char tmp[100], *str;
|
|
|
|
/* Get the interface subunit */
|
|
if (get_ifname_unit(name, &unit, &bsscfg_idx) != 0) {
|
|
WLCONF_DBG("wlconfig(%s): unable to parse unit.subunit in interface "
|
|
"name \"%s\"\n", name, name);
|
|
return -1;
|
|
}
|
|
|
|
/* Configure security parameters for the newly created interface */
|
|
snprintf(prefix, sizeof(prefix), "wl%d_", unit);
|
|
str = nvram_safe_get(strlcat_r(prefix, "mode", tmp, sizeof(tmp)));
|
|
wlconf_security_options(name, prefix, bsscfg_idx, FALSE,
|
|
!strcmp(str, "psta") || !strcmp(str, "psr"));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef TCONFIG_DPSTA
|
|
static int
|
|
wlconf_dpsta_enable(int argc, char *argv[])
|
|
{
|
|
struct ifreq ifr;
|
|
dpsta_enable_info_t dpinfo = { 0 };
|
|
int ret = 0;
|
|
int s;
|
|
|
|
printf("%s: if:%s, enable %s, lan_uif %s, policy %s, uif0 %s, uif1 %s\n",
|
|
__FUNCTION__, argv[1], argv[3], argv[4], argv[5], argv[6], argv[7]);
|
|
|
|
/* open socket to kernel */
|
|
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
perror("socket");
|
|
return -errno;
|
|
}
|
|
|
|
strncpy(ifr.ifr_name, argv[1], IFNAMSIZ);
|
|
|
|
dpinfo.enable = strcmp(argv[3], "1") ? FALSE : TRUE;
|
|
dpinfo.lan_uif = strtoul(argv[4], NULL, 0);
|
|
dpinfo.policy = strtoul(argv[5], NULL, 0);
|
|
strncpy((char *)dpinfo.upstream_if[0], argv[6], IFNAMSIZ);
|
|
strncpy((char *)dpinfo.upstream_if[1], argv[7], IFNAMSIZ);
|
|
|
|
ifr.ifr_data = (caddr_t) &dpinfo;
|
|
|
|
if (ioctl(s, DPSTA_CMD_ENABLE, &ifr) < 0) {
|
|
ret = -errno;
|
|
printf("%s: ioctl fail, ret %d \n", __FUNCTION__, ret);
|
|
}
|
|
|
|
/* cleanup */
|
|
close(s);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
wlconf_dpsta_iovar(int argc, char *argv[], uint8 cmd)
|
|
{
|
|
struct ifreq ifr;
|
|
int ret = 0;
|
|
int s, i;
|
|
dpsta_var_t var = { 0 };
|
|
|
|
/* open socket to kernel */
|
|
if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
|
perror("socket");
|
|
return -errno;
|
|
}
|
|
|
|
strncpy(ifr.ifr_name, argv[1], IFNAMSIZ);
|
|
|
|
var.cmd = cmd;
|
|
|
|
switch (cmd) {
|
|
case DPSTA_IOV_UIF:
|
|
var.set = 0;
|
|
var.len = IFNAMSIZ;
|
|
break;
|
|
case DPSTA_IOV_MSGLEVEL:
|
|
if (argc == 3)
|
|
var.set = 0;
|
|
else if (argc == 4) {
|
|
var.set = 1;
|
|
var.arg = strtoul(argv[3], NULL, 0);
|
|
} else {
|
|
printf("%s: argc wrong when DPSTA_IOV_MSGLEVEL\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
var.len = sizeof(int);
|
|
break;
|
|
case DPSTA_IOV_DPINFO:
|
|
if (argc == 3)
|
|
var.set = 0;
|
|
else {
|
|
printf("%s: argc wrong when DPSTA_IOV_DPINFO\n", __FUNCTION__);
|
|
return -1;
|
|
}
|
|
var.len = sizeof(dpsta_enable_info_t);
|
|
break;
|
|
default:
|
|
printf("%s: unknown cmd\n", __FUNCTION__);
|
|
break;
|
|
}
|
|
|
|
ifr.ifr_data = (caddr_t)&var;
|
|
|
|
if ((ret = ioctl(s, DPSTA_CMD_SETGETVAR, (caddr_t)&ifr)) < 0) {
|
|
ret = -errno;
|
|
printf("%s: ioctl fail, ret %d \n", __FUNCTION__, ret);
|
|
}
|
|
|
|
/* output if success get */
|
|
if (!var.set && !ret) {
|
|
if (cmd == DPSTA_IOV_UIF)
|
|
printf("%s: %s\n", __FUNCTION__, var.uif);
|
|
else if (cmd == DPSTA_IOV_MSGLEVEL)
|
|
printf("%s: %x\n", __FUNCTION__, var.arg);
|
|
else if (cmd == DPSTA_IOV_DPINFO) {
|
|
printf("%s: dpinfo %sabled lan_uif %d policy %d ",
|
|
__FUNCTION__, var.dpinfo.enable ? "en" : "dis",
|
|
var.dpinfo.lan_uif, var.dpinfo.policy);
|
|
for(i = 0; i < DPSTA_NUM_UPSTREAM_IF; i++) {
|
|
if (var.dpinfo.upstream_if[i] && var.dpinfo.upstream_if[i][0] != '\0')
|
|
printf("uif[%d] %s ", i, var.dpinfo.upstream_if[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
|
|
/* cleanup */
|
|
close(s);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
/* Check parameters and branch based on action */
|
|
if (argc == 3 && !strcmp(argv[2], "up"))
|
|
return wlconf(argv[1]);
|
|
else if (argc == 3 && !strcmp(argv[2], "down"))
|
|
return wlconf_down(argv[1]);
|
|
else if (argc == 3 && !strcmp(argv[2], "start"))
|
|
return wlconf_start(argv[1]);
|
|
else if (argc == 3 && !strcmp(argv[2], "security"))
|
|
return wlconf_security(argv[1]);
|
|
#ifdef TCONFIG_DPSTA
|
|
else if (!strcmp(argv[2], "enable")) {
|
|
if (argc == 8)
|
|
return wlconf_dpsta_enable(argc, argv);
|
|
else {
|
|
fprintf(stderr, "Usage: wlconf dpsta enable <enable: 1|0> <lan_uif> <policy> <uif0: ethX> <uif1: ethX>\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else if (!strcmp(argv[2], "uif")) {
|
|
if (argc == 3)
|
|
return wlconf_dpsta_iovar(argc, argv, DPSTA_IOV_UIF);
|
|
else {
|
|
fprintf(stderr, "Only get is allowed!\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else if (argc >= 3 && !strcmp(argv[2], "msglevel"))
|
|
return wlconf_dpsta_iovar(argc, argv, DPSTA_IOV_MSGLEVEL);
|
|
else if (argc == 3 && !strcmp(argv[2], "dpinfo"))
|
|
return wlconf_dpsta_iovar(argc, argv, DPSTA_IOV_DPINFO);
|
|
#endif
|
|
else {
|
|
fprintf(stderr, "Usage: wlconf <ifname> up|down\n");
|
|
return -1;
|
|
}
|
|
}
|
|
#endif /* defined(linux) */
|