1/*
2 * Wireless Network Adapter Configuration Utility
3 *
4 * Copyright (C) 2015, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
8 * the contents of this file may not be disclosed to third parties, copied
9 * or duplicated in any form, in whole or in part, without the prior
10 * written permission of Broadcom Corporation.
11 *
12 * $Id: wlconf.c 515105 2014-11-13 06:54:30Z $
13 */
14
15#include <typedefs.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <assert.h>
20#include <bcmnvram.h>
21#include <bcmutils.h>
22#include <bcmparams.h>
23#include <bcmdevs.h>
24#include <shutils.h>
25#include <wlutils.h>
26#include <wlioctl.h>
27#include <proto/802.1d.h>
28#include <bcmconfig.h>
29#include <bcmwifi_channels.h>
30#include <netconf.h>
31#include <nvparse.h>
32#include <arpa/inet.h>
33
34#if defined(linux)
35#include <sys/utsname.h>
36#endif
37
38/* phy types */
39#define	PHY_TYPE_A		0
40#define	PHY_TYPE_B		1
41#define	PHY_TYPE_G		2
42#define	PHY_TYPE_N		4
43#define	PHY_TYPE_LP		5
44#define PHY_TYPE_SSN		6
45#define	PHY_TYPE_HT		7
46#define	PHY_TYPE_LCN		8
47#define PHY_TYPE_AC		11
48#define	PHY_TYPE_NULL		0xf
49
50/* how many times to attempt to bring up a virtual i/f when
51 * we are in APSTA mode and IOVAR set of "bss" "up" returns busy
52 */
53#define MAX_BSS_UP_RETRIES 5
54
55/* notify the average dma xfer rate (in kbps) to the driver */
56#define AVG_DMA_XFER_RATE 120000
57
58/* parts of an idcode: */
59#define	IDCODE_MFG_MASK		0x00000fff
60#define	IDCODE_MFG_SHIFT	0
61#define	IDCODE_ID_MASK		0x0ffff000
62#define	IDCODE_ID_SHIFT		12
63#define	IDCODE_REV_MASK		0xf0000000
64#define	IDCODE_REV_SHIFT	28
65
66/*
67 * Debugging Macros
68 */
69#ifdef BCMDBG
70#define WLCONF_DBG(fmt, arg...)	printf("%s: "fmt, __FUNCTION__ , ## arg)
71#define WL_IOCTL(ifname, cmd, buf, len)					\
72	if ((ret = wl_ioctl(ifname, cmd, buf, len)))			\
73		fprintf(stderr, "%s:%d:(%s): %s failed, err = %d\n",	\
74		        __FUNCTION__, __LINE__, ifname, #cmd, ret);
75#define WL_SETINT(ifname, cmd, val)								\
76	if ((ret = wlconf_setint(ifname, cmd, val)))						\
77		fprintf(stderr, "%s:%d:(%s): setting %s to %d (0x%x) failed, err = %d\n",	\
78		        __FUNCTION__, __LINE__, ifname, #cmd, (int)val, (unsigned int)val, ret);
79#define WL_GETINT(ifname, cmd, pval)								\
80	if ((ret = wlconf_getint(ifname, cmd, pval)))						\
81		fprintf(stderr, "%s:%d:(%s): getting %s failed, err = %d\n",			\
82		        __FUNCTION__, __LINE__, ifname, #cmd, ret);
83#define WL_IOVAR_SET(ifname, iovar, param, paramlen)					\
84	if ((ret = wl_iovar_set(ifname, iovar, param, paramlen)))			\
85		fprintf(stderr, "%s:%d:(%s): setting iovar \"%s\" failed, err = %d\n",	\
86		        __FUNCTION__, __LINE__, ifname, iovar, ret);
87#define WL_IOVAR_GET(ifname, iovar, param, paramlen)					\
88	if ((ret = wl_iovar_get(ifname, iovar, param, paramlen)))			\
89		fprintf(stderr, "%s:%d:(%s): getting iovar \"%s\" failed, err = %d\n",	\
90		        __FUNCTION__, __LINE__, ifname, iovar, ret);
91#define WL_IOVAR_SETINT(ifname, iovar, val)							\
92	if ((ret = wl_iovar_setint(ifname, iovar, val)))					\
93		fprintf(stderr, "%s:%d:(%s): setting iovar \"%s\" to 0x%x failed, err = %d\n",	\
94		        __FUNCTION__, __LINE__, ifname, iovar, (unsigned int)val, ret);
95#define WL_IOVAR_GETINT(ifname, iovar, val)							\
96	if ((ret = wl_iovar_getint(ifname, iovar, val)))					\
97		fprintf(stderr, "%s:%d:(%s): getting iovar \"%s\" failed, err = %d\n",	\
98		        __FUNCTION__, __LINE__, ifname, iovar, ret);
99#define WL_BSSIOVAR_SETBUF(ifname, iovar, bssidx, param, paramlen, buf, buflen)			\
100	if ((ret = wl_bssiovar_setbuf(ifname, iovar, bssidx, param, paramlen, buf, buflen)))	\
101		fprintf(stderr, "%s:%d:(%s): setting bsscfg #%d iovar \"%s\" failed, err = %d\n", \
102		        __FUNCTION__, __LINE__, ifname, bssidx, iovar, ret);
103#define WL_BSSIOVAR_SET(ifname, iovar, bssidx, param, paramlen)					\
104	if ((ret = wl_bssiovar_set(ifname, iovar, bssidx, param, paramlen)))			\
105		fprintf(stderr, "%s:%d:(%s): setting bsscfg #%d iovar \"%s\" failed, err = %d\n", \
106		        __FUNCTION__, __LINE__, ifname, bssidx, iovar, ret);
107#define WL_BSSIOVAR_GET(ifname, iovar, bssidx, param, paramlen)					\
108	if ((ret = wl_bssiovar_get(ifname, iovar, bssidx, param, paramlen)))			\
109		fprintf(stderr, "%s:%d:(%s): getting bsscfg #%d iovar \"%s\" failed, err = %d\n", \
110		        __FUNCTION__, __LINE__, ifname, bssidx, iovar, ret);
111#define WL_BSSIOVAR_SETINT(ifname, iovar, bssidx, val)						\
112	if ((ret = wl_bssiovar_setint(ifname, iovar, bssidx, val)))				\
113		fprintf(stderr, "%s:%d:(%s): setting bsscfg #%d iovar \"%s\" " \
114				"to val 0x%x failed, err = %d\n",	\
115		        __FUNCTION__, __LINE__, ifname, bssidx, iovar, (unsigned int)val, ret);
116#else
117#define WLCONF_DBG(fmt, arg...)
118#define WL_IOCTL(name, cmd, buf, len)			(ret = wl_ioctl(name, cmd, buf, len))
119#define WL_SETINT(name, cmd, val)			(ret = wlconf_setint(name, cmd, val))
120#define WL_GETINT(name, cmd, pval)			(ret = wlconf_getint(name, cmd, pval))
121#define WL_IOVAR_SET(ifname, iovar, param, paramlen)	(ret = wl_iovar_set(ifname, iovar, \
122							param, paramlen))
123#define WL_IOVAR_GET(ifname, iovar, param, paramlen)	(ret = wl_iovar_get(ifname, iovar, \
124							param, paramlen))
125#define WL_IOVAR_SETINT(ifname, iovar, val)		(ret = wl_iovar_setint(ifname, iovar, val))
126#define WL_IOVAR_GETINT(ifname, iovar, val)		(ret = wl_iovar_getint(ifname, iovar, val))
127#define WL_BSSIOVAR_SETBUF(ifname, iovar, bssidx, param, paramlen, buf, buflen) \
128		(ret = wl_bssiovar_setbuf(ifname, iovar, bssidx, param, paramlen, buf, buflen))
129#define WL_BSSIOVAR_SET(ifname, iovar, bssidx, param, paramlen) \
130		(ret = wl_bssiovar_set(ifname, iovar, bssidx, param, paramlen))
131#define WL_BSSIOVAR_GET(ifname, iovar, bssidx, param, paramlen) \
132		(ret = wl_bssiovar_get(ifname, iovar, bssidx, param, paramlen))
133#define WL_BSSIOVAR_SETINT(ifname, iovar, bssidx, val)	(ret = wl_bssiovar_setint(ifname, iovar, \
134			bssidx, val))
135#endif /* BCMDBG */
136
137#define CHECK_PSK(mode) ((mode) & (WPA_AUTH_PSK | WPA2_AUTH_PSK))
138
139/* prototypes */
140struct bsscfg_list *wlconf_get_bsscfgs(char* ifname, char* prefix);
141int wlconf(char *name);
142int wlconf_down(char *name);
143
144static int
145wlconf_getint(char* ifname, int cmd, int *pval)
146{
147	return wl_ioctl(ifname, cmd, pval, sizeof(int));
148}
149
150static int
151wlconf_setint(char* ifname, int cmd, int val)
152{
153	return wl_ioctl(ifname, cmd, &val, sizeof(int));
154}
155
156static int
157wlconf_wds_clear(char *name)
158{
159	struct maclist maclist;
160	int    ret;
161
162	maclist.count = 0;
163	WL_IOCTL(name, WLC_SET_WDSLIST, &maclist, sizeof(maclist));
164
165	return ret;
166}
167
168/* set WEP key */
169static int
170wlconf_set_wep_key(char *name, char *prefix, int bsscfg_idx, int i)
171{
172	wl_wsec_key_t key;
173	char wl_key[] = "wlXXXXXXXXXX_keyXXXXXXXXXX";
174	char *keystr, hex[] = "XX";
175	unsigned char *data = key.data;
176	int ret = 0;
177
178	memset(&key, 0, sizeof(key));
179	key.index = i - 1;
180	sprintf(wl_key, "%skey%d", prefix, i);
181	keystr = nvram_safe_get(wl_key);
182
183	switch (strlen(keystr)) {
184	case WEP1_KEY_SIZE:
185	case WEP128_KEY_SIZE:
186		key.len = strlen(keystr);
187		strcpy((char *)key.data, keystr);
188		break;
189	case WEP1_KEY_HEX_SIZE:
190	case WEP128_KEY_HEX_SIZE:
191		key.len = strlen(keystr) / 2;
192		while (*keystr) {
193			strncpy(hex, keystr, 2);
194			*data++ = (unsigned char) strtoul(hex, NULL, 16);
195			keystr += 2;
196		}
197		break;
198	default:
199		key.len = 0;
200		break;
201	}
202
203	/* Set current WEP key */
204	if (key.len && i == atoi(nvram_safe_get(strcat_r(prefix, "key", wl_key))))
205		key.flags = WL_PRIMARY_KEY;
206
207	WL_BSSIOVAR_SET(name, "wsec_key", bsscfg_idx, &key, sizeof(key));
208
209	return ret;
210}
211
212static int
213wlconf_akm_options(char *prefix)
214{
215	char comb[32];
216	char *wl_akm;
217	int akm_ret_val = 0;
218	char akm[32];
219	char *next;
220
221	wl_akm = nvram_safe_get(strcat_r(prefix, "akm", comb));
222	foreach(akm, wl_akm, next) {
223		if (!strcmp(akm, "wpa"))
224			akm_ret_val |= WPA_AUTH_UNSPECIFIED;
225		if (!strcmp(akm, "psk"))
226			akm_ret_val |= WPA_AUTH_PSK;
227		if (!strcmp(akm, "wpa2"))
228			akm_ret_val |= WPA2_AUTH_UNSPECIFIED;
229		if (!strcmp(akm, "psk2"))
230			akm_ret_val |= WPA2_AUTH_PSK;
231		if (!strcmp(akm, "brcm_psk"))
232			akm_ret_val |= BRCM_AUTH_PSK;
233	}
234	return akm_ret_val;
235}
236
237/* Set up wsec */
238static int
239wlconf_set_wsec(char *ifname, char *prefix, int bsscfg_idx)
240{
241	char tmp[100];
242	int val = 0;
243	int akm_val;
244	int ret;
245
246	/* Set wsec bitvec */
247	akm_val = wlconf_akm_options(prefix);
248	if (akm_val != 0) {
249		if (nvram_match(strcat_r(prefix, "crypto", tmp), "tkip"))
250			val = TKIP_ENABLED;
251		else if (nvram_match(strcat_r(prefix, "crypto", tmp), "aes"))
252			val = AES_ENABLED;
253		else if (nvram_match(strcat_r(prefix, "crypto", tmp), "tkip+aes"))
254			val = TKIP_ENABLED | AES_ENABLED;
255	}
256	if (nvram_match(strcat_r(prefix, "wep", tmp), "enabled"))
257		val |= WEP_ENABLED;
258	WL_BSSIOVAR_SETINT(ifname, "wsec", bsscfg_idx, val);
259	/* Set wsec restrict if WSEC_ENABLED */
260	WL_BSSIOVAR_SETINT(ifname, "wsec_restrict", bsscfg_idx, val ? 1 : 0);
261
262	return 0;
263}
264
265static int
266wlconf_set_preauth(char *name, int bsscfg_idx, int preauth)
267{
268	uint cap;
269	int ret;
270
271	WL_BSSIOVAR_GET(name, "wpa_cap", bsscfg_idx, &cap, sizeof(uint));
272	if (ret != 0) return -1;
273
274	if (preauth)
275		cap |= WPA_CAP_WPA2_PREAUTH;
276	else
277		cap &= ~WPA_CAP_WPA2_PREAUTH;
278
279	WL_BSSIOVAR_SETINT(name, "wpa_cap", bsscfg_idx, cap);
280
281	return ret;
282}
283
284static void
285wlconf_set_radarthrs(char *name, char *prefix)
286{
287	wl_radar_thr_t  radar_thr;
288	int  i, ret, len;
289	char nv_buf[NVRAM_MAX_VALUE_LEN], *rargs, *v, *endptr;
290	char buf[WLC_IOCTL_SMLEN];
291
292	char *version = NULL;
293	char *thr0_20_lo = NULL, *thr1_20_lo = NULL;
294	char *thr0_40_lo = NULL, *thr1_40_lo = NULL;
295	char *thr0_80_lo = NULL, *thr1_80_lo = NULL;
296	char *thr0_20_hi = NULL, *thr1_20_hi = NULL;
297	char *thr0_40_hi = NULL, *thr1_40_hi = NULL;
298	char *thr0_80_hi = NULL, *thr1_80_hi = NULL;
299
300	char **locals[] = { &version, &thr0_20_lo, &thr1_20_lo, &thr0_40_lo, &thr1_40_lo,
301	&thr0_80_lo, &thr1_80_lo, &thr0_20_hi, &thr1_20_hi,
302	&thr0_40_hi, &thr1_40_hi, &thr0_80_hi, &thr1_80_hi };
303
304	rargs = nvram_safe_get(strcat_r(prefix, "radarthrs", nv_buf));
305	if (!rargs)
306		goto err;
307
308	len = strlen(rargs);
309	if ((len > NVRAM_MAX_VALUE_LEN) || (len == 0))
310		goto err;
311
312	memset(nv_buf, 0, sizeof(nv_buf));
313	strncpy(nv_buf, rargs, len);
314	v = nv_buf;
315	for (i = 0; i < (sizeof(locals) / sizeof(locals[0])); i++) {
316		*locals[i] = v;
317		while (*v && *v != ' ') {
318			v++;
319		}
320		if (*v) {
321			*v = 0;
322			v++;
323		}
324		if (v >= (nv_buf + len)) /* Check for complete list, if not caught later */
325			break;
326	}
327
328	/* Start building request */
329	memset(buf, 0, sizeof(buf));
330	strcpy(buf, "radarthrs");
331	/* Retrieve radar thrs parameters */
332	if (!version)
333		goto err;
334	radar_thr.version = atoi(version);
335	if (radar_thr.version > WL_RADAR_THR_VERSION)
336		goto err;
337
338	/* Retrieve ver 0 params */
339	if (!thr0_20_lo)
340		goto err;
341	radar_thr.thresh0_20_lo = (uint16)strtol(thr0_20_lo, &endptr, 0);
342	if (*endptr != '\0')
343		goto err;
344
345	if (!thr1_20_lo)
346		goto err;
347	radar_thr.thresh1_20_lo = (uint16)strtol(thr1_20_lo, &endptr, 0);
348	if (*endptr != '\0')
349		goto err;
350
351	if (!thr0_40_lo)
352		goto err;
353	radar_thr.thresh0_40_lo = (uint16)strtol(thr0_40_lo, &endptr, 0);
354	if (*endptr != '\0')
355		goto err;
356
357	if (!thr1_40_lo)
358		goto err;
359	radar_thr.thresh1_40_lo = (uint16)strtol(thr1_40_lo, &endptr, 0);
360	if (*endptr != '\0')
361		goto err;
362
363	if (!thr0_80_lo)
364		goto err;
365	radar_thr.thresh0_80_lo = (uint16)strtol(thr0_80_lo, &endptr, 0);
366	if (*endptr != '\0')
367		goto err;
368
369	if (!thr1_80_lo)
370		goto err;
371	radar_thr.thresh1_80_lo = (uint16)strtol(thr1_80_lo, &endptr, 0);
372	if (*endptr != '\0')
373		goto err;
374
375
376	if (radar_thr.version == 0) {
377		/*
378		 * Attempt a best effort update of ver 0 to ver 1 by updating
379		 * the appropriate values with the specified defaults.  The defaults
380		 * are from the reference design.
381		 */
382		radar_thr.version = WL_RADAR_THR_VERSION; /* avoid driver rejecting it */
383		radar_thr.thresh0_20_hi = 0x6ac;
384		radar_thr.thresh1_20_hi = 0x6cc;
385		radar_thr.thresh0_40_hi = 0x6bc;
386		radar_thr.thresh1_40_hi = 0x6e0;
387		radar_thr.thresh0_80_hi = 0x6b0;
388		radar_thr.thresh1_80_hi = 0x30;
389	} else {
390		/* Retrieve ver 1 params */
391		if (!thr0_20_hi)
392			goto err;
393		radar_thr.thresh0_20_hi = (uint16)strtol(thr0_20_hi, &endptr, 0);
394		if (*endptr != '\0')
395			goto err;
396
397		if (!thr1_20_hi)
398			goto err;
399		radar_thr.thresh1_20_hi = (uint16)strtol(thr1_20_hi, &endptr, 0);
400		if (*endptr != '\0')
401			goto err;
402
403		if (!thr0_40_hi)
404			goto err;
405		radar_thr.thresh0_40_hi = (uint16)strtol(thr0_40_hi, &endptr, 0);
406		if (*endptr != '\0')
407			goto err;
408
409		if (!thr1_40_hi)
410			goto err;
411		radar_thr.thresh1_40_hi = (uint16)strtol(thr1_40_hi, &endptr, 0);
412		if (*endptr != '\0')
413			goto err;
414
415		if (!thr0_80_hi)
416			goto err;
417		radar_thr.thresh0_80_hi = (uint16)strtol(thr0_80_hi, &endptr, 0);
418		if (*endptr != '\0')
419			goto err;
420
421		if (!thr1_80_hi)
422			goto err;
423		radar_thr.thresh1_80_hi = (uint16)strtol(thr1_80_hi, &endptr, 0);
424		if (*endptr != '\0')
425			goto err;
426
427	}
428
429	/* Copy radar parameters into buffer and plug them to the driver */
430	memcpy((char*)(buf + strlen(buf) + 1), (char*)&radar_thr, sizeof(wl_radar_thr_t));
431	WL_IOCTL(name, WLC_SET_VAR, buf, sizeof(buf));
432
433	return;
434
435err:
436	WLCONF_DBG("Did not parse radar thrs params, using driver defaults\n");
437	return;
438}
439
440/*
441 * This allows phy antenna selection to be retrieved from NVRAM
442 */
443static void
444wlconf_set_antsel(char *name, char *prefix)
445{
446	int	i, j, len, argc, ret;
447	char	buf[WLC_IOCTL_SMLEN];
448	wlc_antselcfg_t val = { {0}, 0};
449	char	*argv[ANT_SELCFG_MAX] = {};
450	char	nv_buf[NVRAM_MAX_VALUE_LEN], *argstr, *v, *endptr;
451
452	argstr = nvram_safe_get(strcat_r(prefix, "phy_antsel", nv_buf));
453	if (!argstr) {
454		return;
455	}
456	len = strlen(argstr);
457	if ((len == 0) || (len > NVRAM_MAX_VALUE_LEN)) {
458		return;
459	}
460
461	memset(nv_buf, 0, sizeof(nv_buf));
462	strncpy(nv_buf, argstr, len);
463	v = nv_buf;
464	for (argc = 0; argc < ANT_SELCFG_MAX; ) {
465		argv[argc++] = v;
466		while (*v && *v != ' ') {
467			v++;
468		}
469		if (*v) {
470			*v = 0;
471			v++;
472		}
473		if (v >= (nv_buf + len)) {
474			break;
475		}
476	}
477	if ((argc != 1) && (argc != ANT_SELCFG_MAX)) {
478		WLCONF_DBG("phy_antsel requires 1 or %d arguments\n", ANT_SELCFG_MAX);
479		return;
480	}
481
482	memset(buf, 0, sizeof(buf));
483	strcpy(buf, "phy_antsel");
484	for (i = 0, j = 0; i < ANT_SELCFG_MAX; i++) {
485		val.ant_config[i] = (uint8)strtol(argv[j], &endptr, 0);
486		if (*endptr != '\0') {
487			WLCONF_DBG("Invalid antsel argument\n");
488			return;
489		}
490		if (argc > 1) {
491			/* ANT_SELCFG_MAX argument format */
492			j++;
493		}
494	}
495
496	/* Copy antsel parameters into buffer and plug them to the driver */
497	memcpy((char*)(buf + strlen(buf) + 1), (char*)&val, sizeof(wlc_antselcfg_t));
498	WL_IOCTL(name, WLC_SET_VAR, buf, sizeof(buf));
499
500	return;
501}
502
503
504
505static void
506wlconf_set_current_txparam_into_nvram(char *name, char *prefix)
507{
508	int ret, aci;
509	wme_tx_params_t txparams[AC_COUNT];
510	char *nv[] = {"wme_txp_be", "wme_txp_bk", "wme_txp_vi", "wme_txp_vo"};
511	char data[50], tmp[50];
512
513	/* get the WME tx parameters */
514	WL_IOVAR_GET(name, "wme_tx_params", txparams, sizeof(txparams));
515
516	/* Set nvram accordingly */
517	for (aci = 0; aci < AC_COUNT; aci++) {
518		sprintf(data, "%d %d %d %d %d", txparams[aci].short_retry,
519			txparams[aci].short_fallback,
520			txparams[aci].long_retry,
521			txparams[aci].long_fallback,
522			txparams[aci].max_rate);
523
524		nvram_set(strcat_r(prefix, nv[aci], tmp), data);
525	}
526}
527
528/* Set up WME */
529static void
530wlconf_set_wme(char *name, char *prefix)
531{
532	int i, j, k;
533	int val, ret;
534	int phytype, gmode, no_ack, apsd, dp[2];
535	edcf_acparam_t *acparams;
536	/* Pay attention to buffer length requirements when using this */
537	char buf[WLC_IOCTL_SMLEN*2];
538	char *v, *nv_value, nv[100];
539	char nv_name[] = "%swme_%s_%s";
540	char *ac[] = {"be", "bk", "vi", "vo"};
541	char *cwmin, *cwmax, *aifsn, *txop_b, *txop_ag, *admin_forced, *oldest_first;
542	char **locals[] = { &cwmin, &cwmax, &aifsn, &txop_b, &txop_ag, &admin_forced,
543	                    &oldest_first };
544	struct {char *req; char *str;} mode[] = {{"wme_ac_ap", "ap"}, {"wme_ac_sta", "sta"},
545	                                         {"wme_tx_params", "txp"}};
546
547	/* query the phy type */
548	WL_IOCTL(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
549	/* get gmode */
550	gmode = atoi(nvram_safe_get(strcat_r(prefix, "gmode", nv)));
551
552	/* WME sta setting first */
553	for (i = 0; i < 2; i++) {
554		/* build request block */
555		memset(buf, 0, sizeof(buf));
556		strcpy(buf, mode[i].req);
557		/* put push wmeac params after "wme-ac" in buf */
558		acparams = (edcf_acparam_t *)(buf + strlen(buf) + 1);
559		dp[i] = 0;
560		for (j = 0; j < AC_COUNT; j++) {
561			/* get packed nvram parameter */
562			snprintf(nv, sizeof(nv), nv_name, prefix, mode[i].str, ac[j]);
563			nv_value = nvram_safe_get(nv);
564			strcpy(nv, nv_value);
565			/* unpack it */
566			v = nv;
567			for (k = 0; k < (sizeof(locals) / sizeof(locals[0])); k++) {
568				*locals[k] = v;
569				while (*v && *v != ' ')
570					v++;
571				if (*v) {
572					*v = 0;
573					v++;
574				}
575			}
576
577			/* update CWmin */
578			acparams->ECW &= ~EDCF_ECWMIN_MASK;
579			val = atoi(cwmin);
580			for (val++, k = 0; val; val >>= 1, k++);
581			acparams->ECW |= (k ? k - 1 : 0) & EDCF_ECWMIN_MASK;
582			/* update CWmax */
583			acparams->ECW &= ~EDCF_ECWMAX_MASK;
584			val = atoi(cwmax);
585			for (val++, k = 0; val; val >>= 1, k++);
586			acparams->ECW |= ((k ? k - 1 : 0) << EDCF_ECWMAX_SHIFT) & EDCF_ECWMAX_MASK;
587			/* update AIFSN */
588			acparams->ACI &= ~EDCF_AIFSN_MASK;
589			acparams->ACI |= atoi(aifsn) & EDCF_AIFSN_MASK;
590			/* update ac */
591			acparams->ACI &= ~EDCF_ACI_MASK;
592			acparams->ACI |= j << EDCF_ACI_SHIFT;
593			/* update TXOP */
594			if (phytype == PHY_TYPE_B || gmode == 0)
595				val = atoi(txop_b);
596			else
597				val = atoi(txop_ag);
598			acparams->TXOP = val / 32;
599			/* update acm */
600			acparams->ACI &= ~EDCF_ACM_MASK;
601			val = strcmp(admin_forced, "on") ? 0 : 1;
602			acparams->ACI |= val << 4;
603
604			/* configure driver */
605			WL_IOCTL(name, WLC_SET_VAR, buf, sizeof(buf));
606		}
607	}
608
609	/* set no-ack */
610	v = nvram_safe_get(strcat_r(prefix, "wme_no_ack", nv));
611	no_ack = strcmp(v, "on") ? 0 : 1;
612	WL_IOVAR_SETINT(name, "wme_noack", no_ack);
613
614	/* set APSD */
615	v = nvram_safe_get(strcat_r(prefix, "wme_apsd", nv));
616	apsd = strcmp(v, "on") ? 0 : 1;
617	WL_IOVAR_SETINT(name, "wme_apsd", apsd);
618
619	/* set per-AC discard policy */
620	strcpy(buf, "wme_dp");
621	WL_IOVAR_SETINT(name, "wme_dp", dp[1]);
622
623	/* WME Tx parameters setting */
624	{
625		wme_tx_params_t txparams[AC_COUNT];
626		char *srl, *sfbl, *lrl, *lfbl, *maxrate;
627		char **locals[] = { &srl, &sfbl, &lrl, &lfbl, &maxrate };
628
629		/* build request block */
630		memset(txparams, 0, sizeof(txparams));
631
632		for (j = 0; j < AC_COUNT; j++) {
633			/* get packed nvram parameter */
634			snprintf(nv, sizeof(nv), nv_name, prefix, mode[2].str, ac[j]);
635			nv_value = nvram_safe_get(nv);
636			strcpy(nv, nv_value);
637			/* unpack it */
638			v = nv;
639			for (k = 0; k < (sizeof(locals) / sizeof(locals[0])); k++) {
640				*locals[k] = v;
641				while (*v && *v != ' ')
642					v++;
643				if (*v) {
644					*v = 0;
645					v++;
646				}
647			}
648
649			/* update short retry limit */
650			txparams[j].short_retry = atoi(srl);
651
652			/* update short fallback limit */
653			txparams[j].short_fallback = atoi(sfbl);
654
655			/* update long retry limit */
656			txparams[j].long_retry = atoi(lrl);
657
658			/* update long fallback limit */
659			txparams[j].long_fallback = atoi(lfbl);
660
661			/* update max rate */
662			txparams[j].max_rate = atoi(maxrate);
663		}
664
665		/* set the WME tx parameters */
666		WL_IOVAR_SET(name, mode[2].req, txparams, sizeof(txparams));
667	}
668
669	return;
670}
671
672#if defined(linux) || defined(__NetBSD__)
673#include <unistd.h>
674static void
675sleep_ms(const unsigned int ms)
676{
677	usleep(1000*ms);
678}
679#elif defined(__ECOS)
680static void
681sleep_ms(const unsigned int ms)
682{
683	cyg_tick_count_t ostick;
684
685	ostick = ms / 10;
686	cyg_thread_delay(ostick);
687}
688#else
689#error "sleep_ms() not defined for this OS!!!"
690#endif /* defined(linux) */
691
692/*
693* The following condition(s) must be met when Auto Channel Selection
694* is enabled.
695*  - the I/F is up (change radio channel requires it is up?)
696*  - the AP must not be associated (setting SSID to empty should
697*    make sure it for us)
698*/
699static uint8
700wlconf_auto_channel(char *name)
701{
702	int chosen = 0;
703	wl_uint32_list_t request;
704	int phytype;
705	int ret;
706	int i;
707
708	/* query the phy type */
709	WL_GETINT(name, WLC_GET_PHYTYPE, &phytype);
710
711	request.count = 0;	/* let the ioctl decide */
712	WL_IOCTL(name, WLC_START_CHANNEL_SEL, &request, sizeof(request));
713	if (!ret) {
714		sleep_ms(phytype == PHY_TYPE_A ? 1000 : 750);
715		for (i = 0; i < 100; i++) {
716			WL_GETINT(name, WLC_GET_CHANNEL_SEL, &chosen);
717			if (!ret)
718				break;
719			sleep_ms(100);
720		}
721	}
722	WLCONF_DBG("interface %s: channel selected %d\n", name, chosen);
723	return chosen;
724}
725
726static chanspec_t
727wlconf_auto_chanspec(char *name)
728{
729	chanspec_t chosen = 0;
730	int temp = 0;
731	wl_uint32_list_t request;
732	int ret;
733	int i;
734
735	request.count = 0;	/* let the ioctl decide */
736	WL_IOCTL(name, WLC_START_CHANNEL_SEL, &request, sizeof(request));
737	if (!ret) {
738		/* this time needs to be < 1000 to prevent mpc kicking in for 2nd radio */
739		sleep_ms(500);
740		for (i = 0; i < 100; i++) {
741			WL_IOVAR_GETINT(name, "apcschspec", &temp);
742			if (!ret)
743				break;
744			sleep_ms(100);
745		}
746	}
747
748	chosen = (chanspec_t) temp;
749	WLCONF_DBG("interface %s: chanspec selected %04x\n", name, chosen);
750	return chosen;
751}
752
753/* PHY type/BAND conversion */
754#define WLCONF_PHYTYPE2BAND(phy)	((phy) == PHY_TYPE_A ? WLC_BAND_5G : WLC_BAND_2G)
755/* PHY type conversion */
756#define WLCONF_PHYTYPE2STR(phy)	((phy) == PHY_TYPE_A ? "a" : \
757				 (phy) == PHY_TYPE_B ? "b" : \
758				 (phy) == PHY_TYPE_LP ? "l" : \
759				 (phy) == PHY_TYPE_G ? "g" : \
760				 (phy) == PHY_TYPE_SSN ? "s" : \
761				 (phy) == PHY_TYPE_HT ? "h" : \
762				 (phy) == PHY_TYPE_AC ? "v" : \
763				 (phy) == PHY_TYPE_LCN ? "c" : "n")
764#define WLCONF_STR2PHYTYPE(ch)	((ch) == 'a' ? PHY_TYPE_A : \
765				 (ch) == 'b' ? PHY_TYPE_B : \
766				 (ch) == 'l' ? PHY_TYPE_LP : \
767				 (ch) == 'g' ? PHY_TYPE_G : \
768				 (ch) == 's' ? PHY_TYPE_SSN : \
769				 (ch) == 'h' ? PHY_TYPE_HT : \
770				 (ch) == 'v' ? PHY_TYPE_AC : \
771				 (ch) == 'c' ? PHY_TYPE_LCN : PHY_TYPE_N)
772
773#define PREFIX_LEN 32			/* buffer size for wlXXX_ prefix */
774
775#define WLCONF_PHYTYPE_11N(phy) ((phy) == PHY_TYPE_N 	|| (phy) == PHY_TYPE_SSN || \
776				 (phy) == PHY_TYPE_LCN 	|| (phy) == PHY_TYPE_HT || \
777				 (phy) == PHY_TYPE_AC)
778
779struct bsscfg_info {
780	int idx;			/* bsscfg index */
781	char ifname[PREFIX_LEN];	/* OS name of interface (debug only) */
782	char prefix[PREFIX_LEN];	/* prefix for nvram params (eg. "wl0.1_") */
783};
784
785struct bsscfg_list {
786	int count;
787	struct bsscfg_info bsscfgs[WL_MAXBSSCFG];
788};
789
790struct bsscfg_list *
791wlconf_get_bsscfgs(char* ifname, char* prefix)
792{
793	char var[80];
794	char tmp[100];
795	char *next;
796
797	struct bsscfg_list *bclist;
798	struct bsscfg_info *bsscfg;
799
800	bclist = (struct bsscfg_list*)malloc(sizeof(struct bsscfg_list));
801	if (bclist == NULL)
802		return NULL;
803	memset(bclist, 0, sizeof(struct bsscfg_list));
804
805	/* Set up Primary BSS Config information */
806	bsscfg = &bclist->bsscfgs[0];
807	bsscfg->idx = 0;
808	strncpy(bsscfg->ifname, ifname, PREFIX_LEN-1);
809	strcpy(bsscfg->prefix, prefix);
810	bclist->count = 1;
811
812	/* additional virtual BSS Configs from wlX_vifs */
813	foreach(var, nvram_safe_get(strcat_r(prefix, "vifs", tmp)), next) {
814		if (bclist->count == WL_MAXBSSCFG) {
815			WLCONF_DBG("wlconf(%s): exceeded max number of BSS Configs (%d)"
816			           "in nvram %s\n"
817			           "while configuring interface \"%s\"\n",
818			           ifname, WL_MAXBSSCFG, strcat_r(prefix, "vifs", tmp), var);
819			continue;
820		}
821		bsscfg = &bclist->bsscfgs[bclist->count];
822		if (get_ifname_unit(var, NULL, &bsscfg->idx) != 0) {
823			WLCONF_DBG("wlconfg(%s): unable to parse unit.subunit in interface "
824			           "name \"%s\"\n",
825			           ifname, var);
826			continue;
827		}
828		strncpy(bsscfg->ifname, var, PREFIX_LEN-1);
829		snprintf(bsscfg->prefix, PREFIX_LEN, "%s_", bsscfg->ifname);
830		bclist->count++;
831	}
832
833	return bclist;
834}
835
836static void
837wlconf_config_join_pref(char *name, int bsscfg_idx, int auth_val)
838{
839	int ret = 0, i = 0;
840
841	if ((auth_val & (WPA_AUTH_UNSPECIFIED | WPA2_AUTH_UNSPECIFIED)) ||
842	    CHECK_PSK(auth_val)) {
843		uchar pref[] = {
844		/* WPA pref, 14 tuples */
845		0x02, 0xaa, 0x00, 0x0e,
846		/* WPA2                 AES  (unicast)          AES (multicast) */
847		0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x04,
848		/* WPA                  AES  (unicast)          AES (multicast) */
849		0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x04,
850		/* WPA2                 AES  (unicast)          TKIP (multicast) */
851		0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x02,
852		/* WPA                  AES  (unicast)          TKIP (multicast) */
853		0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x02,
854		/* WPA2                 AES  (unicast)          WEP-40 (multicast) */
855		0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x01,
856		/* WPA                  AES  (unicast)          WEP-40 (multicast) */
857		0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x01,
858		/* WPA2                 AES  (unicast)          WEP-128 (multicast) */
859		0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x04, 0x00, 0x0f, 0xac, 0x05,
860		/* WPA                  AES  (unicast)          WEP-128 (multicast) */
861		0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x50, 0xf2, 0x05,
862		/* WPA2                 TKIP (unicast)          TKIP (multicast) */
863		0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x02, 0x00, 0x0f, 0xac, 0x02,
864		/* WPA                  TKIP (unicast)          TKIP (multicast) */
865		0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x50, 0xf2, 0x02,
866		/* WPA2                 TKIP (unicast)          WEP-40 (multicast) */
867		0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x02, 0x00, 0x0f, 0xac, 0x01,
868		/* WPA                  TKIP (unicast)          WEP-40 (multicast) */
869		0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x50, 0xf2, 0x01,
870		/* WPA2                 TKIP (unicast)          WEP-128 (multicast) */
871		0x00, 0x0f, 0xac, 0x01, 0x00, 0x0f, 0xac, 0x02, 0x00, 0x0f, 0xac, 0x05,
872		/* WPA                  TKIP (unicast)          WEP-128 (multicast) */
873		0x00, 0x50, 0xf2, 0x01, 0x00, 0x50, 0xf2, 0x02, 0x00, 0x50, 0xf2, 0x05,
874		/* RSSI pref */
875		0x01, 0x02, 0x00, 0x00,
876		};
877
878		if (CHECK_PSK(auth_val)) {
879			for (i = 0; i < pref[3]; i ++)
880				pref[7 + i * 12] = 0x02;
881		}
882
883		WL_BSSIOVAR_SET(name, "join_pref", bsscfg_idx, pref, sizeof(pref));
884	}
885
886}
887
888static void
889wlconf_security_options(char *name, char *prefix, int bsscfg_idx, bool id_supp,
890                        bool check_join_pref)
891{
892	int i;
893	int val;
894	int ret;
895	char tmp[100];
896	bool need_join_pref = FALSE;
897#define AUTOWPA(cfg) ((cfg) == (WPA_AUTH_PSK | WPA2_AUTH_PSK))
898
899	/* Set WSEC */
900	/*
901	* Need to check errors (card may have changed) and change to
902	* defaults since the new chip may not support the requested
903	* encryptions after the card has been changed.
904	*/
905	if (wlconf_set_wsec(name, prefix, bsscfg_idx)) {
906		/* change nvram only, code below will pass them on */
907		nvram_restore_var(prefix, "auth_mode");
908		nvram_restore_var(prefix, "auth");
909		/* reset wep to default */
910		nvram_restore_var(prefix, "crypto");
911		nvram_restore_var(prefix, "wep");
912		wlconf_set_wsec(name, prefix, bsscfg_idx);
913	}
914
915	val = wlconf_akm_options(prefix);
916	if (!nvram_match(strcat_r(prefix, "mode", tmp), "ap"))
917		need_join_pref = (check_join_pref || id_supp) && AUTOWPA(val);
918
919	if (need_join_pref)
920		wlconf_config_join_pref(name, bsscfg_idx, val);
921
922	/* enable in-driver wpa supplicant? */
923	if (id_supp && (CHECK_PSK(val))) {
924		wsec_pmk_t psk;
925		char *key;
926
927		if (((key = nvram_get(strcat_r(prefix, "wpa_psk", tmp))) != NULL) &&
928		    (strlen(key) < WSEC_MAX_PSK_LEN)) {
929			psk.key_len = (ushort) strlen(key);
930			psk.flags = WSEC_PASSPHRASE;
931			strcpy((char *)psk.key, key);
932			WL_IOCTL(name, WLC_SET_WSEC_PMK, &psk, sizeof(psk));
933		}
934		wl_iovar_setint(name, "sup_wpa", 1);
935	}
936
937	if (!need_join_pref)
938		WL_BSSIOVAR_SETINT(name, "wpa_auth", bsscfg_idx, val);
939
940	/* EAP Restrict if we have an AKM or radius authentication */
941	val = ((val != 0) || (nvram_match(strcat_r(prefix, "auth_mode", tmp), "radius")));
942	WL_BSSIOVAR_SETINT(name, "eap_restrict", bsscfg_idx, val);
943
944	/* Set WEP keys */
945	if (nvram_match(strcat_r(prefix, "wep", tmp), "enabled")) {
946		for (i = 1; i <= DOT11_MAX_DEFAULT_KEYS; i++)
947			wlconf_set_wep_key(name, prefix, bsscfg_idx, i);
948	}
949
950	/* Set 802.11 authentication mode - open/shared */
951	val = atoi(nvram_safe_get(strcat_r(prefix, "auth", tmp)));
952	WL_BSSIOVAR_SETINT(name, "auth", bsscfg_idx, val);
953#ifdef MFP
954		/* Set MFP */
955	val = WPA_AUTH_DISABLED;
956	WL_BSSIOVAR_GET(name, "wpa_auth", bsscfg_idx, &val, sizeof(val));
957	if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
958		val = atoi(nvram_safe_get(strcat_r(prefix, "mfp", tmp)));
959		WL_BSSIOVAR_SETINT(name, "mfp", bsscfg_idx, val);
960	}
961#endif
962}
963
964static void
965wlconf_set_ampdu_retry_limit(char *name, char *prefix)
966{
967	int i, j, ret, nv_len;
968	struct ampdu_retry_tid retry_limit;
969	char *nv_name[2] = {"ampdu_rtylimit_tid", "ampdu_rr_rtylimit_tid"};
970	char *iov_name[2] = {"ampdu_retry_limit_tid", "ampdu_rr_retry_limit_tid"};
971	char *retry, *v, *nv_value, nv[100], tmp[100];
972
973	/* Get packed AMPDU (rr) retry limit per-tid from NVRAM if present */
974	for (i = 0; i < 2; i++) {
975		nv_value = nvram_safe_get(strcat_r(prefix, nv_name[i], tmp));
976		nv_len = strlen(nv_value);
977		strcpy(nv, nv_value);
978
979		/* unpack it */
980		v = nv;
981		for (j = 0; nv_len >= 0 && j < NUMPRIO; j++) {
982			retry = v;
983			while (*v && *v != ' ') {
984				v++;
985				nv_len--;
986			}
987			if (*v) {
988				*v = 0;
989				v++;
990				nv_len--;
991			}
992			/* set the AMPDU retry limit per-tid */
993			retry_limit.tid = j;
994			retry_limit.retry = atoi(retry);
995			WL_IOVAR_SET(name, iov_name[i], &retry_limit, sizeof(retry_limit));
996		}
997	}
998
999	return;
1000}
1001
1002/*
1003 * When N-mode is ON, AMPDU, AMSDU are enabled/disabled
1004 * based on the nvram setting. Only one of the AMPDU or AMSDU options is enabled any
1005 * time. When N-mode is OFF or the device is non N-phy, AMPDU and AMSDU are turned off.
1006 *
1007 * WME/WMM is also set in this procedure as it depends on N.
1008 *     N ==> WMM is on by default
1009 *
1010 * Returns WME setting.
1011 */
1012static int
1013wlconf_ampdu_amsdu_set(char *name, char prefix[PREFIX_LEN], int nmode, int btc_mode, int ap)
1014{
1015	bool ampdu_valid_option = FALSE;
1016	bool amsdu_valid_option = FALSE;
1017	int  val, ampdu_option_val = OFF, amsdu_option_val = OFF;
1018	int rx_amsdu_in_ampdu_option_val = OFF;
1019	int wme_option_val = ON;  /* On by default */
1020	char caps[WLC_IOCTL_MEDLEN], var[80], *next, *wme_val;
1021	char buf[WLC_IOCTL_SMLEN];
1022	int len = (sizeof("amsdu") - 1);
1023	int ret, phytype;
1024	wlc_rev_info_t rev;
1025#ifdef linux
1026	struct utsname unamebuf;
1027
1028	uname(&unamebuf);
1029#endif
1030
1031	WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
1032
1033	WL_GETINT(name, WLC_GET_PHYTYPE, &phytype);
1034
1035	/* First, clear WMM settings to avoid conflicts */
1036	WL_IOVAR_SETINT(name, "wme", OFF);
1037
1038	/* Get WME setting from NVRAM if present */
1039	wme_val = nvram_get(strcat_r(prefix, "wme", caps));
1040	if (wme_val && !strcmp(wme_val, "off")) {
1041		wme_option_val = OFF;
1042	}
1043
1044	/* Set options based on capability */
1045	wl_iovar_get(name, "cap", (void *)caps, sizeof(caps));
1046	foreach(var, caps, next) {
1047		char *nvram_str;
1048		bool amsdu = 0;
1049
1050		/* Check for the capabilitiy 'amsdutx' */
1051		if (strncmp(var, "amsdutx", sizeof(var)) == 0) {
1052			var[len] = '\0';
1053			amsdu = 1;
1054		}
1055		nvram_str = nvram_get(strcat_r(prefix, var, buf));
1056		if (!nvram_str)
1057			continue;
1058
1059		if (!strcmp(nvram_str, "on"))
1060			val = ON;
1061		else if (!strcmp(nvram_str, "off"))
1062			val = OFF;
1063		else if (!strcmp(nvram_str, "auto"))
1064			val = AUTO;
1065		else
1066			continue;
1067
1068		if (strncmp(var, "ampdu", sizeof(var)) == 0) {
1069			ampdu_valid_option = TRUE;
1070			ampdu_option_val = val;
1071		}
1072
1073		if (amsdu) {
1074			amsdu_valid_option = TRUE;
1075			amsdu_option_val = val;
1076
1077			nvram_str = nvram_get(strcat_r(prefix, "rx_amsdu_in_ampdu", buf));
1078			if (nvram_str) {
1079				if (!strcmp(nvram_str, "on"))
1080					rx_amsdu_in_ampdu_option_val = ON;
1081				else if (!strcmp(nvram_str, "auto"))
1082					rx_amsdu_in_ampdu_option_val = AUTO;
1083			}
1084
1085			/*
1086			 * Some tests show problems when AMSDU is enabled on TX
1087			 * side together with ps-pretend.
1088			 * Ps-pretend is used on AP only.
1089			 * Let's disable AMSDU downstream unconditionally
1090			 * in devicemode=1.
1091			 * When ps-pretend be OK with AMSDU, below statement
1092			 * need to be removed.
1093			 */
1094			if (ap && !strcmp(nvram_safe_get("devicemode"), "1")) {
1095				amsdu_option_val = OFF;
1096			}
1097		}
1098	}
1099
1100	if (nmode != OFF) { /* N-mode is ON/AUTO */
1101		if (ampdu_valid_option) {
1102			if (ampdu_option_val != OFF) {
1103				WL_IOVAR_SETINT(name, "amsdu", OFF);
1104				WL_IOVAR_SETINT(name, "ampdu", ampdu_option_val);
1105			} else {
1106				WL_IOVAR_SETINT(name, "ampdu", OFF);
1107			}
1108
1109			wlconf_set_ampdu_retry_limit(name, prefix);
1110
1111#ifdef linux
1112			/* For MIPS routers set the num mpdu per ampdu limit to 32. We observed
1113			 * that having this value at 32 helps with bi-di thru'put as well.
1114			 */
1115			if ((phytype == PHY_TYPE_AC) &&
1116			    (strncmp(unamebuf.machine, "mips", 4) == 0)) {
1117#ifndef __CONFIG_USBAP__
1118				WL_IOVAR_SETINT(name, "ampdu_mpdu", 32);
1119#endif /* __CONFIG_USBAP__ */
1120			}
1121#endif /* linux */
1122		}
1123
1124		if (amsdu_valid_option) {
1125			if (amsdu_option_val != OFF) { /* AMPDU (above) has priority over AMSDU */
1126				if (rev.corerev >= 40) {
1127					WL_IOVAR_SETINT(name, "amsdu", amsdu_option_val);
1128				} else if (ampdu_option_val == OFF) {
1129					WL_IOVAR_SETINT(name, "ampdu", OFF);
1130					WL_IOVAR_SETINT(name, "amsdu", amsdu_option_val);
1131				}
1132			} else
1133				WL_IOVAR_SETINT(name, "amsdu", OFF);
1134		}
1135
1136		WL_IOVAR_SETINT(name, "rx_amsdu_in_ampdu", rx_amsdu_in_ampdu_option_val);
1137	} else {
1138		/* When N-mode is off or for non N-phy device, turn off AMPDU, AMSDU;
1139		 */
1140		wl_iovar_setint(name, "amsdu", OFF);
1141		wl_iovar_setint(name, "ampdu", OFF);
1142	}
1143
1144	if (wme_option_val) {
1145		WL_IOVAR_SETINT(name, "wme", wme_option_val);
1146		wlconf_set_wme(name, prefix);
1147	}
1148
1149	return wme_option_val;
1150}
1151
1152/* Get configured bandwidth cap. */
1153static int
1154wlconf_bw_cap(char *prefix, int bandtype)
1155{
1156	char *str, tmp[100];
1157	int bw_cap = WLC_BW_CAP_20MHZ;
1158
1159	if ((str = nvram_get(strcat_r(prefix, "bw_cap", tmp))) != NULL)
1160		bw_cap = atoi(str);
1161	else {
1162		/* Backward compatibility. Map to bandwidth cap bitmap values. */
1163		int val = atoi(nvram_safe_get(strcat_r(prefix, "nbw_cap", tmp)));
1164
1165		if (((bandtype == WLC_BAND_2G) && (val == WLC_N_BW_40ALL)) ||
1166		    ((bandtype == WLC_BAND_5G) &&
1167		     (val == WLC_N_BW_40ALL || val == WLC_N_BW_20IN2G_40IN5G)))
1168			bw_cap = WLC_BW_CAP_40MHZ;
1169		else
1170			bw_cap = WLC_BW_CAP_20MHZ;
1171	}
1172
1173	return bw_cap;
1174}
1175
1176/* Set up TxBF. Called when i/f is down. */
1177static void wlconf_set_txbf(char *name, char *prefix)
1178{
1179	char *str, tmp[100];
1180	wlc_rev_info_t rev;
1181	uint32 txbf_bfe_cap = 0;
1182	uint32 txbf_bfr_cap = 0;
1183	uint32 txbf_imp = 0;
1184	int ret = 0;
1185
1186	WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
1187
1188	if (rev.corerev < 40) return;	/* TxBF unsupported */
1189
1190	if ((str = nvram_get(strcat_r(prefix, "txbf_bfr_cap", tmp))) != NULL) {
1191		txbf_bfr_cap = atoi(str);
1192
1193		if (txbf_bfr_cap) {
1194			/* Turning TxBF on (order matters) */
1195			WL_IOVAR_SETINT(name, "txbf_bfr_cap", 1);
1196			WL_IOVAR_SETINT(name, "txbf", 1);
1197		} else {
1198			/* Similarly, turning TxBF off in reverse order */
1199			WL_IOVAR_SETINT(name, "txbf", 0);
1200			WL_IOVAR_SETINT(name, "txbf_bfr_cap", 0);
1201		}
1202	}
1203
1204	if ((str = nvram_get(strcat_r(prefix, "txbf_bfe_cap", tmp))) != NULL) {
1205		txbf_bfe_cap = atoi(str);
1206
1207		WL_IOVAR_SETINT(name, "txbf_bfe_cap", txbf_bfe_cap ? 1 : 0);
1208	}
1209
1210	if ((str = nvram_get(strcat_r(prefix, "txbf_imp", tmp))) != NULL) {
1211		txbf_imp = atoi(str);
1212
1213		WL_IOVAR_SETINT(name, "txbf_imp", txbf_imp);
1214	}
1215}
1216
1217/* Set up TxBF timer. Called when i/f is up. */
1218static void wlconf_set_txbf_timer(char *name, char *prefix)
1219{
1220	char *str, tmp[100];
1221	wlc_rev_info_t rev;
1222	uint32 txbf_timer = 0;
1223	int ret = 0;
1224
1225	WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
1226
1227	if (rev.corerev < 40) return;	/* TxBF unsupported */
1228
1229	if ((str = nvram_get(strcat_r(prefix, "txbf_timer", tmp))) != NULL) {
1230		txbf_timer = (uint32) atoi(str);
1231		WL_IOVAR_SETINT(name, "txbf_timer", txbf_timer);
1232	}
1233}
1234
1235/* Apply Traffic Management filter settings stored in NVRAM */
1236static void
1237trf_mgmt_settings(char *prefix, bool dwm_supported)
1238{
1239	char buffer[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_RULES+1)];
1240	char iobuff[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_RULES+1)+32];
1241	int i, filterlen, ret = 0;
1242	trf_mgmt_config_t trf_mgmt_config;
1243	trf_mgmt_filter_list_t *trf_mgmt_filter_list;
1244	netconf_trmgmt_t nettrm;
1245	trf_mgmt_filter_t *trfmgmt;
1246	char *wlifname;
1247	struct in_addr ipaddr, ipmask;
1248	char nvram_ifname[32];
1249	bool tm_filters_configured = FALSE;
1250	/* DWM variables */
1251	bool dwm_filters_configured = FALSE;
1252	char dscp_filter_buffer[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_DWM_RULES)];
1253	char dscp_filter_iobuff[sizeof(trf_mgmt_filter_t)*(MAX_NUM_TRF_MGMT_DWM_RULES)+
1254		sizeof("trf_mgmt_filters_add") + OFFSETOF(trf_mgmt_filter_list_t, filter)];
1255	int dscp_filterlen;
1256	trf_mgmt_filter_t *trf_mgmt_dwm_filter;
1257	trf_mgmt_filter_list_t *trf_mgmt_dwm_filter_list = NULL;
1258	netconf_trmgmt_t nettrm_dwm;
1259
1260	snprintf(nvram_ifname, sizeof(nvram_ifname), "%sifname", prefix);
1261	wlifname = nvram_get(nvram_ifname);
1262	if (!wlifname) {
1263		return;
1264	}
1265
1266	trf_mgmt_filter_list = (trf_mgmt_filter_list_t *)buffer;
1267	trfmgmt = &trf_mgmt_filter_list->filter[0];
1268
1269	/* Initialize the common parameters */
1270	memset(buffer, 0, sizeof(buffer));
1271	memset(&trf_mgmt_config, 0, sizeof(trf_mgmt_config_t));
1272
1273	/* no-rx packets, local subnet, don't override priority and no-traffic shape */
1274	trf_mgmt_config.flags = (TRF_MGMT_FLAG_NO_RX |
1275		TRF_MGMT_FLAG_MANAGE_LOCAL_TRAFFIC |
1276		TRF_MGMT_FLAG_DISABLE_SHAPING);
1277	(void) inet_aton("192.168.1.1", &ipaddr);         /* Dummy value */
1278	(void) inet_aton("255.255.255.0", &ipmask);       /* Dummy value */
1279	trf_mgmt_config.host_ip_addr = ipaddr.s_addr;     /* Dummy value */
1280	trf_mgmt_config.host_subnet_mask = ipmask.s_addr; /* Dummy value */
1281	trf_mgmt_config.downlink_bandwidth = 1;           /* Dummy value */
1282	trf_mgmt_config.uplink_bandwidth = 1;             /* Dummy value */
1283
1284	/* Read up to NUM_TFF_MGMT_FILTERS entries from NVRAM */
1285	for (i = 0; i < MAX_NUM_TRF_MGMT_RULES; i++) {
1286		if (get_trf_mgmt_port(prefix, i, &nettrm) == FALSE) {
1287			continue;
1288		}
1289		if (nettrm.match.flags == NETCONF_DISABLED) {
1290			continue;
1291		}
1292		trfmgmt->dst_port = ntohs(nettrm.match.dst.ports[0]);
1293		trfmgmt->src_port = ntohs(nettrm.match.src.ports[0]);
1294		trfmgmt->prot = nettrm.match.ipproto;
1295		if (nettrm.favored) {
1296			trfmgmt->flags |= TRF_FILTER_FAVORED;
1297		}
1298		trfmgmt->priority = nettrm.prio;
1299		memcpy(&trfmgmt->dst_ether_addr, &nettrm.match.mac, sizeof(struct ether_addr));
1300		if (nettrm.match.ipproto == IPPROTO_IP) {
1301			/* Enable MAC filter */
1302			trf_mgmt_config.flags |= TRF_MGMT_FLAG_FILTER_ON_MACADDR;
1303			trfmgmt->flags |= TRF_FILTER_MAC_ADDR;
1304		}
1305		trf_mgmt_filter_list->num_filters += 1;
1306		trfmgmt++;
1307	}
1308	if (trf_mgmt_filter_list->num_filters)
1309		tm_filters_configured = TRUE;
1310
1311	if (dwm_supported) {
1312		trf_mgmt_dwm_filter_list = (trf_mgmt_filter_list_t *)dscp_filter_buffer;
1313		trf_mgmt_dwm_filter = &trf_mgmt_dwm_filter_list->filter[0];
1314		memset(dscp_filter_buffer, 0, sizeof(dscp_filter_buffer));
1315
1316		/* Read up to NUM_TFF_MGMT_FILTERS entries from NVRAM */
1317		for (i = 0; i < MAX_NUM_TRF_MGMT_DWM_RULES; i++) {
1318			if (get_trf_mgmt_dwm(prefix, i, &nettrm_dwm) == FALSE) {
1319				continue;
1320			}
1321			if (nettrm_dwm.match.flags == NETCONF_DISABLED) {
1322				continue;
1323			}
1324
1325			trf_mgmt_dwm_filter->dscp = nettrm_dwm.match.dscp;
1326			if (nettrm_dwm.favored)
1327				trf_mgmt_dwm_filter->flags |= TRF_FILTER_FAVORED;
1328			trf_mgmt_dwm_filter->flags |= TRF_FILTER_DWM;
1329			trf_mgmt_dwm_filter->priority = nettrm_dwm.prio;
1330			trf_mgmt_dwm_filter++;
1331			trf_mgmt_dwm_filter_list->num_filters += 1;
1332		}
1333		if (trf_mgmt_dwm_filter_list->num_filters)
1334			dwm_filters_configured = TRUE;
1335	}
1336
1337	/* Disable traffic management module to initial known state */
1338	trf_mgmt_config.trf_mgmt_enabled = 0;
1339	WL_IOVAR_SET(wlifname, "trf_mgmt_config", &trf_mgmt_config, sizeof(trf_mgmt_config_t));
1340
1341	/* Add traffic management filter entries */
1342	if (tm_filters_configured || dwm_filters_configured) {
1343		/* Enable traffic management module  before adding filter mappings */
1344		trf_mgmt_config.trf_mgmt_enabled = 1;
1345		WL_IOVAR_SET(wlifname, "trf_mgmt_config", &trf_mgmt_config,
1346			sizeof(trf_mgmt_config_t));
1347
1348		if (tm_filters_configured) {
1349			/* Configure TM module filters mappings */
1350			filterlen = trf_mgmt_filter_list->num_filters *
1351				sizeof(trf_mgmt_filter_t) +
1352				OFFSETOF(trf_mgmt_filter_list_t, filter);
1353			wl_iovar_setbuf(wlifname, "trf_mgmt_filters_add", trf_mgmt_filter_list,
1354				filterlen, iobuff, sizeof(iobuff));
1355		}
1356
1357		if (dwm_filters_configured) {
1358			dscp_filterlen =  trf_mgmt_dwm_filter_list->num_filters *
1359				sizeof(trf_mgmt_filter_t) +
1360				OFFSETOF(trf_mgmt_filter_list_t, filter);
1361			wl_iovar_setbuf(wlifname, "trf_mgmt_filters_add", trf_mgmt_dwm_filter_list,
1362			    dscp_filterlen, dscp_filter_iobuff, sizeof(dscp_filter_iobuff));
1363		}
1364	}
1365}
1366
1367#ifdef TRAFFIC_MGMT_RSSI_POLICY
1368static void
1369trf_mgmt_rssi_policy(char *prefix)
1370{
1371	char *wlifname;
1372	char nvram_ifname[32];
1373	char rssi_policy[64];
1374	uint32 rssi_policy_value, ret;
1375
1376	/* Get wl interface name */
1377	snprintf(nvram_ifname, sizeof(nvram_ifname), "%sifname", prefix);
1378	if ((wlifname = nvram_get(nvram_ifname)) == NULL) {
1379		return;
1380	}
1381
1382	/* Get RSSI policy from NVRAM variable wlx_trf_mgmt_rssi_policy */
1383	snprintf(rssi_policy, sizeof(rssi_policy), "%strf_mgmt_rssi_policy", prefix);
1384	if (!nvram_invmatch(rssi_policy, ""))
1385		return;
1386	rssi_policy_value = atoi(nvram_get(rssi_policy));
1387
1388	/* Enable/Disable RSSI policy depending on value of  rssi_policy_value */
1389	WL_IOVAR_SETINT(wlifname, "trf_mgmt_rssi_policy", rssi_policy_value);
1390}
1391#endif /* TRAFFIC_MGMT_RSSI_POLICY */
1392
1393static int
1394wlconf_del_brcm_syscap_ie(char *name, int bsscfg_idx, char *oui)
1395{
1396	int iebuf_len = 0;
1397	vndr_ie_setbuf_t *ie_setbuf = NULL;
1398	int iecount, i;
1399
1400	char getbuf[2048] = {0};
1401	vndr_ie_buf_t *iebuf;
1402	vndr_ie_info_t *ieinfo;
1403	char *bufaddr;
1404	int buflen = 0;
1405	int found = 0;
1406	uint32 pktflag;
1407	uint32 frametype;
1408	int ret = 0;
1409
1410	frametype = VNDR_IE_BEACON_FLAG;
1411
1412	WL_BSSIOVAR_GET(name, "vndr_ie", bsscfg_idx, getbuf, 2048);
1413	iebuf = (vndr_ie_buf_t *)getbuf;
1414
1415	bufaddr = (char*)iebuf->vndr_ie_list;
1416
1417	for (i = 0; i < iebuf->iecount; i++) {
1418		ieinfo = (vndr_ie_info_t *)bufaddr;
1419		bcopy((char*)&ieinfo->pktflag, (char*)&pktflag, (int)sizeof(uint32));
1420		if (pktflag == frametype) {
1421			if (!memcmp(ieinfo->vndr_ie_data.oui, oui, DOT11_OUI_LEN)) {
1422				found = 1;
1423				bufaddr = (char*) &ieinfo->vndr_ie_data;
1424				buflen = (int)ieinfo->vndr_ie_data.len + VNDR_IE_HDR_LEN;
1425				break;
1426			}
1427		}
1428		bufaddr = (char *)(ieinfo->vndr_ie_data.oui + ieinfo->vndr_ie_data.len);
1429	}
1430
1431	if (!found)
1432		goto err;
1433
1434	iebuf_len = buflen + sizeof(vndr_ie_setbuf_t) - sizeof(vndr_ie_t);
1435	ie_setbuf = (vndr_ie_setbuf_t *)malloc(iebuf_len);
1436	if (!ie_setbuf) {
1437		WLCONF_DBG("memory alloc failure\n");
1438		ret = -1;
1439		goto err;
1440	}
1441
1442	memset(ie_setbuf, 0, iebuf_len);
1443
1444	/* Copy the vndr_ie SET command ("add"/"del") to the buffer */
1445	strcpy(ie_setbuf->cmd, "del");
1446
1447	/* Buffer contains only 1 IE */
1448	iecount = 1;
1449	memcpy(&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int));
1450
1451	memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, &frametype, sizeof(uint32));
1452
1453	memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data, bufaddr, buflen);
1454
1455	WL_BSSIOVAR_SET(name, "vndr_ie", bsscfg_idx, ie_setbuf, iebuf_len);
1456
1457err:
1458	if (ie_setbuf)
1459		free(ie_setbuf);
1460
1461	return ret;
1462}
1463
1464static int
1465wlconf_set_brcm_syscap_ie(char *name, int bsscfg_idx, char *oui, uchar *data, int datalen)
1466{
1467	vndr_ie_setbuf_t *ie_setbuf = NULL;
1468	unsigned int pktflag;
1469	int buflen, iecount;
1470	int ret = 0;
1471
1472	pktflag = VNDR_IE_BEACON_FLAG;
1473
1474	buflen = sizeof(vndr_ie_setbuf_t) + datalen - 1;
1475	ie_setbuf = (vndr_ie_setbuf_t *)malloc(buflen);
1476	if (!ie_setbuf) {
1477		WLCONF_DBG("memory alloc failure\n");
1478		ret = -1;
1479		goto err;
1480	}
1481
1482	memset(ie_setbuf, 0, buflen);
1483
1484	/* Copy the vndr_ie SET command ("add"/"del") to the buffer */
1485	strcpy(ie_setbuf->cmd, "add");
1486
1487	/* Buffer contains only 1 IE */
1488	iecount = 1;
1489	memcpy(&ie_setbuf->vndr_ie_buffer.iecount, &iecount, sizeof(int));
1490
1491	/* The packet flag bit field indicates the packets that will contain this IE */
1492	memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].pktflag, &pktflag, sizeof(uint32));
1493
1494	/* Now, add the IE to the buffer, +1: one byte OUI_TYPE */
1495	ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.len = DOT11_OUI_LEN + datalen;
1496
1497	memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.oui[0], oui, DOT11_OUI_LEN);
1498	if (datalen > 0)
1499		memcpy(&ie_setbuf->vndr_ie_buffer.vndr_ie_list[0].vndr_ie_data.data[0], data,
1500		       datalen);
1501
1502	ret = wlconf_del_brcm_syscap_ie(name, bsscfg_idx, oui);
1503	if (ret)
1504		goto err;
1505
1506	WL_BSSIOVAR_SET(name, "vndr_ie", (int)bsscfg_idx, ie_setbuf, buflen);
1507
1508err:
1509	if (ie_setbuf)
1510		free(ie_setbuf);
1511
1512	return (ret);
1513}
1514
1515/*
1516 * wlconf_process_sta_config_entry() - process a single sta_config settings entry.
1517 *
1518 *	This function processes a single sta_config settings entry by parsing the entry and
1519 *	calling the appropriate IOVAR(s) to apply the settings in the driver.
1520 *
1521 * Inputs:
1522 *	params	- address of a nul terminated ascii string buffer containing a single sta_config
1523 *		  settings entry in the form "xx:xx:xx:xx:xx:xx,prio[,steerflag]".
1524 *
1525 *	At least the mac address of the station to which the settings are to be applied needs
1526 *	to be present, with one or more setting values, in a specific order. New settings must
1527 *	be added at the end. Alternatively, we could allow "prio=<value>,steerflag=<value>" and
1528 *	so on, but that would take some more parsing and use more nvram space.
1529 *
1530 * Outputs:
1531 *	Driver settings may be updated.
1532 *
1533 * Returns:
1534 *	 This function returns a BCME_xxx status indicating success (BCME_OK) or failure.
1535 *
1536 * Side effects: The input buffer is trashed by the strsep() function.
1537 *
1538 */
1539static int
1540wlconf_process_sta_config_entry(char *ifname, char *param_list)
1541{
1542	enum {	/* Parameter index in comma-separated list of settings. */
1543		PARAM_MACADDRESS = 0,
1544		PARAM_PRIO	 = 1,
1545		PARAM_STEERFLAG	 = 2,
1546		PARAM_COUNT
1547	} param_idx = PARAM_MACADDRESS;
1548	char *param;
1549	struct ether_addr ea;
1550	char *end;
1551	uint32 value;
1552
1553	while ((param = strsep(&param_list, ","))) {
1554		switch (param_idx) {
1555
1556		case PARAM_MACADDRESS: /* MAC Address - parse into ea */
1557			if (!param || !ether_atoe(param, &ea.octet[0])) {
1558				return BCME_BADADDR;
1559			}
1560			break;
1561
1562		case PARAM_PRIO: /* prio value - parse and apply through "staprio" iovar */
1563			if (*param) { /* If no value is provided, do not configure the prio */
1564				wl_staprio_cfg_t staprio_arg;
1565				int ret;
1566
1567				value = strtol(param, &end, 0);
1568				if (*end != '\0') {
1569					return BCME_BADARG;
1570				}
1571				memset(&staprio_arg, 0, sizeof(staprio_arg));
1572				memcpy(&staprio_arg.ea, &ea, sizeof(ea));
1573				staprio_arg.prio = value; /* prio is byte sized, no htod() needed */
1574				WL_IOVAR_SET(ifname, "staprio", &staprio_arg, sizeof(staprio_arg));
1575			}
1576			break;
1577
1578		case PARAM_STEERFLAG:
1579			if (*param) {
1580				value = strtol(param, &end, 0);
1581				if (*end != '\0') {
1582					return BCME_BADARG;
1583				}
1584			}
1585			break;
1586
1587		default:
1588			/* Future use parameter already set in nvram config - ignore. */
1589			break;
1590		}
1591		++param_idx;
1592	}
1593
1594	if (param_idx <= PARAM_PRIO) { /* No mac address and/or no parameters at all, forget it. */
1595		return BCME_BADARG;
1596	}
1597
1598	return BCME_OK;
1599}
1600
1601#define VIFNAME_LEN 16
1602
1603#define AMPDU_DENSITY_8USEC 6
1604
1605/* configure the specified wireless interface */
1606int
1607wlconf(char *name)
1608{
1609	int restore_defaults, val, unit, phytype, bandtype, gmode = 0, ret = 0;
1610	int bcmerr;
1611	int error_bg, error_a;
1612	struct bsscfg_list *bclist = NULL;
1613	struct bsscfg_info *bsscfg = NULL;
1614	char tmp[100], tmp2[100], prefix[PREFIX_LEN];
1615	char var[80], *next, *str, *addr = NULL;
1616	/* Pay attention to buffer length requirements when using this */
1617	char buf[WLC_IOCTL_SMLEN*2] __attribute__ ((aligned(4)));
1618	char *country;
1619	char *country_rev;
1620	wlc_rev_info_t rev;
1621	channel_info_t ci;
1622	struct maclist *maclist;
1623	struct ether_addr *ea;
1624	wlc_ssid_t ssid;
1625	wl_rateset_t rs;
1626	unsigned int i;
1627	char eaddr[32];
1628	int ap, apsta, wds, sta = 0, wet = 0, mac_spoof = 0, wmf = 0;
1629	int rxchain_pwrsave = 0, radio_pwrsave = 0;
1630	wl_country_t country_spec = {{0}, 0, {0}};
1631	char *ba;
1632	char *preauth;
1633	int set_preauth;
1634	int wlunit = -1;
1635	int wlsubunit = -1;
1636	int wl_ap_build = 0; /* wl compiled with AP capabilities */
1637	char cap[WLC_IOCTL_SMLEN];
1638	char caps[WLC_IOCTL_MEDLEN];
1639	int btc_mode;
1640	uint32 leddc;
1641	uint nbw = WL_CHANSPEC_BW_20;
1642	int nmode = OFF; /* 802.11n support */
1643	char vif_addr[WLC_IOCTL_SMLEN];
1644	int max_no_vifs = 0;
1645	int wme_global;
1646	int max_assoc = -1;
1647	bool ure_enab = FALSE;
1648	bool radar_enab = FALSE;
1649	bool obss_coex = FALSE, psta, psr;
1650	chanspec_t chanspec = 0;
1651	int wet_tunnel_cap = 0, wet_tunnel_enable = 0;
1652	brcm_prop_ie_t brcm_syscap_ie;
1653
1654	/* wlconf doesn't work for virtual i/f, so if we are given a
1655	 * virtual i/f return 0 if that interface is in it's parent's "vifs"
1656	 * list otherwise return -1
1657	 */
1658	if (get_ifname_unit(name, &wlunit, &wlsubunit) == 0) {
1659		if (wlsubunit >= 0) {
1660			/* we have been given a virtual i/f,
1661			 * is it in it's parent i/f's virtual i/f list?
1662			 */
1663			sprintf(tmp, "wl%d_vifs", wlunit);
1664
1665			if (strstr(nvram_safe_get(tmp), name) == NULL)
1666				return -1; /* config error */
1667			else
1668				return 0; /* okay */
1669		}
1670	} else {
1671		return -1;
1672	}
1673
1674	/* clean up tmp */
1675	memset(tmp, 0, sizeof(tmp));
1676
1677	/* because of ifdefs in wl driver,  when we don't have AP capabilities we
1678	 * can't use the same iovars to configure the wl.
1679	 * so we use "wl_ap_build" to help us know how to configure the driver
1680	 */
1681	if (wl_iovar_get(name, "cap", (void *)caps, sizeof(caps)))
1682		return -1;
1683
1684	foreach(cap, caps, next) {
1685		if (!strcmp(cap, "ap")) {
1686			wl_ap_build = 1;
1687		} else if (!strcmp(cap, "mbss16"))
1688			max_no_vifs = 16;
1689		else if (!strcmp(cap, "mbss8"))
1690			max_no_vifs = 8;
1691		else if (!strcmp(cap, "mbss4"))
1692			max_no_vifs = 4;
1693		else if (!strcmp(cap, "wmf"))
1694			wmf = 1;
1695		else if (!strcmp(cap, "rxchain_pwrsave"))
1696			rxchain_pwrsave = 1;
1697		else if (!strcmp(cap, "radio_pwrsave"))
1698			radio_pwrsave = 1;
1699		else if (!strcmp(cap, "wet_tunnel"))
1700			wet_tunnel_cap = 1;
1701	}
1702
1703	/* Check interface (fail silently for non-wl interfaces) */
1704	if ((ret = wl_probe(name)))
1705		return ret;
1706
1707	/* Get MAC address */
1708	(void) wl_hwaddr(name, (uchar *)buf);
1709	memcpy(vif_addr, buf, ETHER_ADDR_LEN);
1710
1711	/* Get instance */
1712	WL_IOCTL(name, WLC_GET_INSTANCE, &unit, sizeof(unit));
1713	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
1714
1715	/* Restore defaults if per-interface parameters do not exist */
1716//	restore_defaults = !nvram_get(strcat_r(prefix, "ifname", tmp));
1717	restore_defaults = !strlen(nvram_safe_get(strcat_r(prefix, "ifname", tmp)));
1718//	nvram_validate_all(prefix, restore_defaults);
1719	nvram_set(strcat_r(prefix, "ifname", tmp), name);
1720	nvram_set(strcat_r(prefix, "hwaddr", tmp), ether_etoa((uchar *)buf, eaddr));
1721	snprintf(buf, sizeof(buf), "%d", unit);
1722	nvram_set(strcat_r(prefix, "unit", tmp), buf);
1723
1724	if (restore_defaults) {
1725		wlconf_set_current_txparam_into_nvram(name, prefix);
1726	}
1727#ifdef BCMDBG
1728	/* Apply message level */
1729	if (nvram_invmatch("wl_msglevel", "")) {
1730		val = (int)strtoul(nvram_get("wl_msglevel"), NULL, 0);
1731
1732		if (nvram_invmatch("wl_msglevel2", "")) {
1733			struct wl_msglevel2 msglevel64;
1734			msglevel64.low = val;
1735			val = (int)strtoul(nvram_get("wl_msglevel2"), NULL, 0);
1736			msglevel64.high = val;
1737			WL_IOVAR_SET(name, "msglevel", &msglevel64, sizeof(struct wl_msglevel2));
1738		}
1739		else
1740			WL_IOCTL(name, WLC_SET_MSGLEVEL, &val, sizeof(val));
1741	}
1742#endif
1743
1744	/* Bring the interface down */
1745	WL_IOCTL(name, WLC_DOWN, NULL, sizeof(val));
1746
1747	/* Disable all BSS Configs */
1748	for (i = 0; i < WL_MAXBSSCFG; i++) {
1749		struct {int bsscfg_idx; int enable;} setbuf;
1750		setbuf.bsscfg_idx = i;
1751		setbuf.enable = 0;
1752
1753		ret = wl_iovar_set(name, "bss", &setbuf, sizeof(setbuf));
1754		if (ret) {
1755			wl_iovar_getint(name, "bcmerror", &bcmerr);
1756			/* fail quietly on a range error since the driver may
1757			 * support fewer bsscfgs than we are prepared to configure
1758			 */
1759			if (bcmerr == BCME_RANGE)
1760				break;
1761		}
1762		if (ret) {
1763			WLCONF_DBG("%d:(%s): setting bsscfg #%d iovar \"bss\" to 0"
1764			           " (down) failed, ret = %d, bcmerr = %d\n",
1765			           __LINE__, name, i, ret, bcmerr);
1766		}
1767	}
1768
1769	/* Get the list of BSS Configs */
1770	bclist = wlconf_get_bsscfgs(name, prefix);
1771	if (bclist == NULL) {
1772		ret = -1;
1773		goto exit;
1774	}
1775
1776#ifdef BCMDBG
1777	strcat_r(prefix, "vifs", tmp);
1778	printf("BSS Config summary: primary -> \"%s\", %s -> \"%s\"\n", name, tmp,
1779	       nvram_safe_get(tmp));
1780	for (i = 0; i < bclist->count; i++) {
1781		printf("BSS Config \"%s\": index %d\n",
1782		       bclist->bsscfgs[i].ifname, bclist->bsscfgs[i].idx);
1783	}
1784#endif
1785
1786	/* create a wlX.Y_ifname nvram setting */
1787	for (i = 1; i < bclist->count; i++) {
1788		bsscfg = &bclist->bsscfgs[i];
1789#if defined(linux) || defined(__ECOS) || defined(__NetBSD__)
1790		strcpy(var, bsscfg->ifname);
1791#endif
1792		nvram_set(strcat_r(bsscfg->prefix, "ifname", tmp), var);
1793	}
1794
1795	str = nvram_safe_get(strcat_r(prefix, "mode", tmp));
1796
1797	/* If ure_disable is not present or is 1, ure is not enabled;
1798	 * that is, if it is present and 0, ure is enabled.
1799	 */
1800	if (!strcmp(nvram_safe_get("ure_disable"), "0")) { /* URE is enabled */
1801		ure_enab = TRUE;
1802	}
1803	if (wl_ap_build) {
1804		/* Enable MBSS mode if appropriate. */
1805		if (!ure_enab && strcmp(str, "psr")) {
1806#ifndef __CONFIG_USBAP__
1807			WL_IOVAR_SETINT(name, "mbss", (bclist->count >= 1));
1808#else
1809			WL_IOVAR_SETINT(name, "mbss", (bclist->count >= 2));
1810#endif
1811		} else
1812			WL_IOVAR_SETINT(name, "mbss", 0);
1813
1814		/*
1815		 * Set SSID for each BSS Config
1816		 */
1817		for (i = 0; i < bclist->count; i++) {
1818			bsscfg = &bclist->bsscfgs[i];
1819			strcat_r(bsscfg->prefix, "ssid", tmp);
1820			ssid.SSID_len = strlen(nvram_safe_get(tmp));
1821			if (ssid.SSID_len > sizeof(ssid.SSID))
1822				ssid.SSID_len = sizeof(ssid.SSID);
1823			strncpy((char *)ssid.SSID, nvram_safe_get(tmp), ssid.SSID_len);
1824			WLCONF_DBG("wlconfig(%s): configuring bsscfg #%d (%s) "
1825			           "with SSID \"%s\"\n", name, bsscfg->idx,
1826			           bsscfg->ifname, nvram_safe_get(tmp));
1827			WL_BSSIOVAR_SET(name, "ssid", bsscfg->idx, &ssid,
1828			                sizeof(ssid));
1829		}
1830	}
1831
1832	/* Create addresses for VIFs */
1833	if (!ure_enab && strcmp(str, "psr")) {
1834		/* set local bit for our MBSS vif base */
1835		ETHER_SET_LOCALADDR(vif_addr);
1836
1837		/* construct and set other wlX.Y_hwaddr */
1838		for (i = 1; i < max_no_vifs; i++) {
1839			snprintf(tmp, sizeof(tmp), "wl%d.%d_hwaddr", unit, i);
1840			addr = nvram_safe_get(tmp);
1841			if (!strcmp(addr, "")) {
1842				vif_addr[5] = (vif_addr[5] & ~(max_no_vifs-1))
1843				        | ((max_no_vifs-1) & (vif_addr[5]+1));
1844
1845				nvram_set(tmp, ether_etoa((uchar *)vif_addr, eaddr));
1846			}
1847		}
1848
1849		for (i = 0; i < bclist->count; i++) {
1850			bsscfg = &bclist->bsscfgs[i];
1851			/* Ignore primary */
1852			if (bsscfg->idx == 0)
1853				continue;
1854
1855			snprintf(tmp, sizeof(tmp), "wl%d.%d_hwaddr", unit, bsscfg->idx);
1856			ether_atoe(nvram_safe_get(tmp), (unsigned char *)eaddr);
1857			WL_BSSIOVAR_SET(name, "cur_etheraddr", bsscfg->idx, eaddr, ETHER_ADDR_LEN);
1858		}
1859	} else { /* One of URE or Proxy STA Repeater is enabled */
1860		/* URE/PSR is on, so set wlX.1 hwaddr is same as that of primary interface */
1861		snprintf(tmp, sizeof(tmp), "wl%d.1_hwaddr", unit);
1862		WL_BSSIOVAR_SET(name, "cur_etheraddr", 1, vif_addr,
1863		                ETHER_ADDR_LEN);
1864		nvram_set(tmp, ether_etoa((uchar *)vif_addr, eaddr));
1865	}
1866
1867	/* wlX_mode settings: AP, STA, WET, BSS/IBSS, APSTA */
1868	str = nvram_safe_get(strcat_r(prefix, "mode", tmp));
1869	ap = (!strcmp(str, "") || !strcmp(str, "ap"));
1870	apsta = (!strcmp(str, "apsta") ||
1871	         ((!strcmp(str, "sta") || !strcmp(str, "psr") || !strcmp(str, "wet")) &&
1872	          bclist->count > 1));
1873	sta = (!strcmp(str, "sta") && bclist->count == 1);
1874	wds = !strcmp(str, "wds");
1875	wet = !strcmp(str, "wet");
1876	mac_spoof = !strcmp(str, "mac_spoof");
1877	psta = !strcmp(str, "psta");
1878	psr = !strcmp(str, "psr");
1879
1880	/* set apsta var first, because APSTA mode takes precedence */
1881	WL_IOVAR_SETINT(name, "apsta", apsta);
1882
1883	/* Set AP mode */
1884	val = (ap || apsta || wds) ? 1 : 0;
1885	WL_IOCTL(name, WLC_SET_AP, &val, sizeof(val));
1886
1887	/* Turn WET mode ON or OFF based on selected mode */
1888	WL_IOCTL(name, WLC_SET_WET, &wet, sizeof(wet));
1889
1890	if (mac_spoof) {
1891		sta = 1;
1892		WL_IOVAR_SETINT(name, "mac_spoof", 1);
1893	}
1894
1895	/* For STA configurations, configure association retry time.
1896	 * Use specified time (capped), or mode-specific defaults.
1897	 */
1898	if (sta || wet || apsta || psta || psr) {
1899		char *sta_retry_time_name = "sta_retry_time";
1900		char *assoc_retry_max_name = "assoc_retry_max";
1901		struct {
1902			int val;
1903			int band;
1904		} roam;
1905
1906		str = nvram_safe_get(strcat_r(prefix, sta_retry_time_name, tmp));
1907		WL_IOVAR_SETINT(name, sta_retry_time_name, atoi(str));
1908
1909		/* Set the wlX_assoc_retry_max, but only if one was specified. */
1910		if ((str = nvram_get(strcat_r(prefix, assoc_retry_max_name, tmp)))) {
1911			WL_IOVAR_SETINT(name, assoc_retry_max_name, atoi(str));
1912		}
1913
1914		roam.val = WLC_ROAM_NEVER_ROAM_TRIGGER;
1915		roam.band = WLC_BAND_ALL;
1916		WL_IOCTL(name, WLC_SET_ROAM_TRIGGER, &roam, sizeof(roam));
1917	}
1918
1919	/* Retain remaining WET effects only if not APSTA */
1920	wet &= !apsta;
1921
1922	/* Set infra: BSS/IBSS (IBSS only for WET or STA modes) */
1923	val = 1;
1924	if (wet || sta || psta || psr)
1925		val = atoi(nvram_safe_get(strcat_r(prefix, "infra", tmp)));
1926	WL_IOCTL(name, WLC_SET_INFRA, &val, sizeof(val));
1927
1928	/* Set DWDS only for AP or STA modes */
1929	for (i = 0; i < bclist->count; i++) {
1930		val = 0;
1931		bsscfg = &bclist->bsscfgs[i];
1932
1933		if (ap || sta || psta || psr || (apsta && !wet)) {
1934			strcat_r(bsscfg->prefix, "dwds", tmp);
1935			val = atoi(nvram_safe_get(tmp));
1936		}
1937		WL_BSSIOVAR_SETINT(name, "dwds", bsscfg->idx, val);
1938	}
1939
1940	/* Set The AP MAX Associations Limit */
1941	if (ap || apsta) {
1942		max_assoc = val = atoi(nvram_safe_get(strcat_r(prefix, "maxassoc", tmp)));
1943		if (val > 0) {
1944			WL_IOVAR_SETINT(name, "maxassoc", val);
1945		} else { /* Get value from driver if not in nvram */
1946			WL_IOVAR_GETINT(name, "maxassoc", &max_assoc);
1947		}
1948	}
1949	if (!wet && !sta)
1950		WL_IOVAR_SETINT(name, "mpc", OFF);
1951
1952	/* Set the Proxy STA or Repeater mode */
1953	if (psta) {
1954		WL_IOVAR_SETINT(name, "psta", PSTA_MODE_PROXY);
1955		/* Set inactivity timer */
1956		str = nvram_get(strcat_r(prefix, "psta_inact", tmp));
1957		if (str) {
1958			val = atoi(str);
1959			WL_IOVAR_SETINT(name, "psta_inact", val);
1960		}
1961	} else if (psr) {
1962		WL_IOVAR_SETINT(name, "psta", PSTA_MODE_REPEATER);
1963		val = atoi(nvram_safe_get(strcat_r(prefix, "psr_mrpt", tmp)));
1964		WL_IOVAR_SETINT(name, "psta_mrpt", val);
1965	} else {
1966		WL_IOVAR_SETINT(name, "psta", PSTA_MODE_DISABLED);
1967	}
1968
1969	/* Turn WET tunnel mode ON or OFF */
1970	if ((ap || apsta) && (wet_tunnel_cap)) {
1971		if (atoi(nvram_safe_get(strcat_r(prefix, "wet_tunnel", tmp))) == 1) {
1972			WL_IOVAR_SETINT(name, "wet_tunnel", 1);
1973			wet_tunnel_enable = 1;
1974		} else {
1975			WL_IOVAR_SETINT(name, "wet_tunnel", 0);
1976		}
1977	}
1978
1979	for (i = 0; i < bclist->count; i++) {
1980		char *subprefix;
1981		bsscfg = &bclist->bsscfgs[i];
1982
1983		/* XXXMSSID: The note about setting preauth now does not seem right.
1984		 * NAS brings the BSS up if it runs, so setting the preauth value
1985		 * will make it in the bcn/prb. If that is right, we can move this
1986		 * chunk out of wlconf.
1987		 */
1988		/*
1989		 * Set The WPA2 Pre auth cap. only reason we are doing it here is the driver is down
1990		 * if we do it in the NAS we need to bring down the interface and up to make
1991		 * it affect in the  beacons
1992		 */
1993		if (ap || (apsta && bsscfg->idx != 0)) {
1994			set_preauth = 1;
1995			preauth = nvram_safe_get(strcat_r(bsscfg->prefix, "preauth", tmp));
1996			if (strlen (preauth) != 0) {
1997				set_preauth = atoi(preauth);
1998			}
1999			wlconf_set_preauth(name, bsscfg->idx, set_preauth);
2000		}
2001
2002		/* Clear BRCM System level Capability IE */
2003		memset(&brcm_syscap_ie, 0, sizeof(brcm_prop_ie_t));
2004		brcm_syscap_ie.type = BRCM_SYSCAP_IE_TYPE;
2005
2006		/* Add WET TUNNEL to IE */
2007		if (wet_tunnel_enable)
2008			brcm_syscap_ie.cap |= BRCM_SYSCAP_WET_TUNNEL;
2009
2010		subprefix = apsta ? prefix : bsscfg->prefix;
2011
2012		if (ap || (apsta && bsscfg->idx != 0)) {
2013			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "bss_maxassoc", tmp)));
2014			if (val > 0) {
2015				WL_BSSIOVAR_SETINT(name, "bss_maxassoc", bsscfg->idx, val);
2016			} else if (max_assoc > 0) { /* Set maxassoc same as global if not set */
2017				snprintf(var, sizeof(var), "%d", max_assoc);
2018				nvram_set(tmp, var);
2019			}
2020		}
2021
2022		/* Set network type */
2023		val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "closed", tmp)));
2024		WL_BSSIOVAR_SETINT(name, "closednet", bsscfg->idx, val);
2025
2026		/* Set the ap isolate mode */
2027		val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "ap_isolate", tmp)));
2028		WL_BSSIOVAR_SETINT(name, "ap_isolate", bsscfg->idx, val);
2029
2030		/* Set the WMF enable mode */
2031		if (wmf) {
2032			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "wmf_bss_enable", tmp)));
2033			WL_BSSIOVAR_SETINT(name, "wmf_bss_enable", bsscfg->idx, val);
2034
2035			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix,
2036				"wmf_psta_disable", tmp)));
2037			WL_BSSIOVAR_SETINT(name, "wmf_psta_disable", bsscfg->idx, val);
2038		}
2039
2040		/* Set the Multicast Reverse Translation enable mode */
2041		if (wet || psta || psr) {
2042			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix,
2043							 "mcast_regen_bss_enable", tmp)));
2044			WL_BSSIOVAR_SETINT(name, "mcast_regen_bss_enable", bsscfg->idx, val);
2045		}
2046
2047		if (rxchain_pwrsave) {
2048			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "rxchain_pwrsave_enable",
2049				tmp)));
2050			WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_enable", bsscfg->idx, val);
2051
2052			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix,
2053				"rxchain_pwrsave_quiet_time", tmp)));
2054			WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_quiet_time", bsscfg->idx, val);
2055
2056			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "rxchain_pwrsave_pps",
2057				tmp)));
2058			WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_pps", bsscfg->idx, val);
2059
2060			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix,
2061				"rxchain_pwrsave_stas_assoc_check", tmp)));
2062			WL_BSSIOVAR_SETINT(name, "rxchain_pwrsave_stas_assoc_check", bsscfg->idx,
2063				val);
2064		}
2065
2066		if (radio_pwrsave) {
2067			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "radio_pwrsave_enable",
2068				tmp)));
2069			WL_BSSIOVAR_SETINT(name, "radio_pwrsave_enable", bsscfg->idx, val);
2070
2071			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix,
2072				"radio_pwrsave_quiet_time", tmp)));
2073			WL_BSSIOVAR_SETINT(name, "radio_pwrsave_quiet_time", bsscfg->idx, val);
2074
2075			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "radio_pwrsave_pps",
2076				tmp)));
2077			WL_BSSIOVAR_SETINT(name, "radio_pwrsave_pps", bsscfg->idx, val);
2078			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "radio_pwrsave_level",
2079				tmp)));
2080			WL_BSSIOVAR_SETINT(name, "radio_pwrsave_level", bsscfg->idx, val);
2081
2082			val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix,
2083				"radio_pwrsave_stas_assoc_check", tmp)));
2084			WL_BSSIOVAR_SETINT(name, "radio_pwrsave_stas_assoc_check", bsscfg->idx,
2085				val);
2086		}
2087		val = atoi(nvram_safe_get(strcat_r(bsscfg->prefix, "aspm", tmp)));
2088		WL_BSSIOVAR_SETINT(name, "aspm", bsscfg->idx, val);
2089
2090		/* Configure SYSCAP IE to driver */
2091		if (brcm_syscap_ie.cap)  {
2092			wlconf_set_brcm_syscap_ie(name, bsscfg->idx,
2093				BRCM_PROP_OUI, (uchar *)&(brcm_syscap_ie.type),
2094				sizeof(brcm_syscap_ie.type) +
2095				sizeof(brcm_syscap_ie.cap));
2096		}
2097	}
2098
2099	/* Set up the country code */
2100	(void) strcat_r(prefix, "country_code", tmp);
2101	country = nvram_get(tmp);
2102	(void) strcat_r(prefix, "country_rev", tmp2);
2103	country_rev = nvram_get(tmp2);
2104	if ((country && country[0] != '\0') && (country_rev && country_rev[0] != '\0')) {
2105		/* Initialize the wl country parameter */
2106		strncpy(country_spec.country_abbrev, country, WLC_CNTRY_BUF_SZ-1);
2107		country_spec.country_abbrev[WLC_CNTRY_BUF_SZ-1] = '\0';
2108		strncpy(country_spec.ccode, country, WLC_CNTRY_BUF_SZ-1);
2109		country_spec.ccode[WLC_CNTRY_BUF_SZ-1] = '\0';
2110		country_spec.rev = atoi(country_rev);
2111
2112		WL_IOVAR_SET(name, "country", &country_spec, sizeof(country_spec));
2113	} else {
2114		/* Get the default country code if undefined */
2115		wl_iovar_get(name, "country", &country_spec, sizeof(country_spec));
2116
2117		/* Add the new NVRAM variable */
2118		nvram_set("wl_country_code", country_spec.ccode);
2119		(void) strcat_r(prefix, "country_code", tmp);
2120		nvram_set(tmp, country_spec.ccode);
2121		snprintf(buf, sizeof(buf),  "%d", country_spec.rev);
2122		nvram_set("wl_country_rev", buf);
2123		(void) strcat_r(prefix, "country_rev", tmp);
2124		nvram_set(tmp, buf);
2125	}
2126
2127	/* Change LED Duty Cycle */
2128	leddc = (uint32)strtoul(nvram_safe_get(strcat_r(prefix, "leddc", tmp)), NULL, 16);
2129	if (leddc)
2130		WL_IOVAR_SETINT(name, "leddc", leddc);
2131
2132	/* Enable or disable the radio */
2133	val = nvram_match(strcat_r(prefix, "radio", tmp), "0");
2134	val += WL_RADIO_SW_DISABLE << 16;
2135	WL_IOCTL(name, WLC_SET_RADIO, &val, sizeof(val));
2136
2137	/* Get supported phy types */
2138	WL_IOCTL(name, WLC_GET_PHYLIST, var, sizeof(var));
2139	nvram_set(strcat_r(prefix, "phytypes", tmp), var);
2140
2141	/* Get radio IDs */
2142	*(next = buf) = '\0';
2143	for (i = 0; i < strlen(var); i++) {
2144		/* Switch to band */
2145		val = WLCONF_STR2PHYTYPE(var[i]);
2146		if (WLCONF_PHYTYPE_11N(val)) {
2147			WL_GETINT(name, WLC_GET_BAND, &val);
2148		} else
2149			val = WLCONF_PHYTYPE2BAND(val);
2150		WL_IOCTL(name, WLC_SET_BAND, &val, sizeof(val));
2151		/* Get radio ID on this band */
2152		WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
2153		next += sprintf(next, "%sBCM%X", i ? " " : "",
2154		                (rev.radiorev & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT);
2155	}
2156	nvram_set(strcat_r(prefix, "radioids", tmp), buf);
2157
2158	/* Set band */
2159	str = nvram_get(strcat_r(prefix, "phytype", tmp));
2160	val = str ? WLCONF_STR2PHYTYPE(str[0]) : PHY_TYPE_G;
2161	/* For NPHY use band value from NVRAM */
2162	if (WLCONF_PHYTYPE_11N(val)) {
2163		str = nvram_get(strcat_r(prefix, "nband", tmp));
2164		if (str)
2165			val = atoi(str);
2166		else {
2167			WL_GETINT(name, WLC_GET_BAND, &val);
2168		}
2169	} else
2170		val = WLCONF_PHYTYPE2BAND(val);
2171
2172	WL_SETINT(name, WLC_SET_BAND, val);
2173
2174	/* Check errors (card may have changed) */
2175	if (ret) {
2176		/* default band to the first band in band list */
2177		val = WLCONF_STR2PHYTYPE(var[0]);
2178		val = WLCONF_PHYTYPE2BAND(val);
2179		WL_SETINT(name, WLC_SET_BAND, val);
2180	}
2181
2182	/* Store the resolved bandtype */
2183	bandtype = val;
2184
2185	/* Check errors again (will cover 5Ghz-only cards) */
2186	if (ret) {
2187		int list[3];
2188
2189		/* default band to the first band in band list */
2190		wl_ioctl(name, WLC_GET_BANDLIST, list, sizeof(list));
2191		WL_SETINT(name, WLC_SET_BAND, list[1]);
2192
2193		/* Read it back, and set bandtype accordingly */
2194		WL_GETINT(name, WLC_GET_BAND, &bandtype);
2195	}
2196
2197	/* Get current core revision */
2198	WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
2199	snprintf(buf, sizeof(buf), "%d", rev.corerev);
2200	nvram_set(strcat_r(prefix, "corerev", tmp), buf);
2201
2202	if ((rev.chipnum == BCM4716_CHIP_ID) || (rev.chipnum == BCM47162_CHIP_ID) ||
2203		(rev.chipnum == BCM4748_CHIP_ID) || (rev.chipnum == BCM4331_CHIP_ID) ||
2204		(rev.chipnum == BCM43431_CHIP_ID) || (rev.chipnum == BCM5357_CHIP_ID) ||
2205	        (rev.chipnum == BCM53572_CHIP_ID) || (rev.chipnum == BCM43236_CHIP_ID)) {
2206		int pam_mode = WLC_N_PREAMBLE_GF_BRCM; /* default GF-BRCM */
2207
2208		strcat_r(prefix, "mimo_preamble", tmp);
2209		if (nvram_match(tmp, "mm"))
2210			pam_mode = WLC_N_PREAMBLE_MIXEDMODE;
2211		else if (nvram_match(tmp, "gf"))
2212			pam_mode = WLC_N_PREAMBLE_GF;
2213		else if (nvram_match(tmp, "auto"))
2214			pam_mode = -1;
2215		WL_IOVAR_SETINT(name, "mimo_preamble", pam_mode);
2216	}
2217
2218	/* Making default ampdu_density to 8usec in order to improve throughput
2219	 * of very small packet sizes (64, 88, 128,..).
2220	 */
2221	if (rev.chipnum == BCM43217_CHIP_ID)
2222		WL_IOVAR_SETINT(name, "ampdu_rx_density", AMPDU_DENSITY_8USEC);
2223
2224	if ((rev.chipnum == BCM5357_CHIP_ID) || (rev.chipnum == BCM53572_CHIP_ID)) {
2225		val = atoi(nvram_safe_get("coma_sleep"));
2226		if (val > 0) {
2227			struct {int sleep; int delay;} setbuf;
2228			nvram_unset("coma_sleep");
2229			nvram_commit();
2230			setbuf.sleep = val;
2231			setbuf.delay = 1;
2232			WL_IOVAR_SET(name, "coma", &setbuf, sizeof(setbuf));
2233		}
2234	}
2235
2236	/* Get current phy type */
2237	WL_IOCTL(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
2238	snprintf(buf, sizeof(buf), "%s", WLCONF_PHYTYPE2STR(phytype));
2239	nvram_set(strcat_r(prefix, "phytype", tmp), buf);
2240
2241	/* Setup regulatory mode */
2242	strcat_r(prefix, "reg_mode", tmp);
2243	if (nvram_match(tmp, "off")) {
2244		val = 0;
2245		WL_IOCTL(name, WLC_SET_REGULATORY, &val, sizeof(val));
2246		WL_IOCTL(name, WLC_SET_RADAR, &val, sizeof(val));
2247		WL_IOCTL(name, WLC_SET_SPECT_MANAGMENT, &val, sizeof(val));
2248	} else if (nvram_match(tmp, "h") || nvram_match(tmp, "strict_h")) {
2249		val = 0;
2250		WL_IOCTL(name, WLC_SET_REGULATORY, &val, sizeof(val));
2251		val = 1;
2252		WL_IOCTL(name, WLC_SET_RADAR, &val, sizeof(val));
2253		radar_enab = TRUE;
2254		if (nvram_match(tmp, "h"))
2255			val = 1;
2256		else
2257			val = 2;
2258		WL_IOCTL(name, WLC_SET_SPECT_MANAGMENT, &val, sizeof(val));
2259
2260		/* Set the CAC parameters, if they exist in nvram. */
2261		if ((str = nvram_get(strcat_r(prefix, "dfs_preism", tmp)))) {
2262			val = atoi(str);
2263			wl_iovar_setint(name, "dfs_preism", val);
2264		}
2265		if ((str = nvram_get(strcat_r(prefix, "dfs_postism", tmp)))) {
2266			val = atoi(str);
2267			wl_iovar_setint(name, "dfs_postism", val);
2268		}
2269		val = atoi(nvram_safe_get(strcat_r(prefix, "tpc_db", tmp)));
2270		WL_IOCTL(name, WLC_SEND_PWR_CONSTRAINT, &val, sizeof(val));
2271
2272	} else if (nvram_match(tmp, "d")) {
2273		val = 0;
2274		WL_IOCTL(name, WLC_SET_RADAR, &val, sizeof(val));
2275		WL_IOCTL(name, WLC_SET_SPECT_MANAGMENT, &val, sizeof(val));
2276		val = 1;
2277		WL_IOCTL(name, WLC_SET_REGULATORY, &val, sizeof(val));
2278	}
2279
2280	/* set bandwidth capability for nphy and calculate nbw */
2281	if (WLCONF_PHYTYPE_11N(phytype)) {
2282		struct {
2283			int bandtype;
2284			uint8 bw_cap;
2285		} param;
2286
2287		/* Get the user nmode setting now */
2288		nmode = AUTO;	/* enable by default for NPHY */
2289		/* Set n mode */
2290		strcat_r(prefix, "nmode", tmp);
2291		if (nvram_match(tmp, "0"))
2292			nmode = OFF;
2293
2294		WL_IOVAR_SETINT(name, "nmode", (uint32)nmode);
2295
2296		if (nmode == OFF)
2297			val = WLC_BW_CAP_20MHZ;
2298		else
2299			val = wlconf_bw_cap(prefix, bandtype);
2300
2301		/* record the bw here */
2302		if (val == WLC_BW_CAP_80MHZ)
2303			nbw = WL_CHANSPEC_BW_80;
2304		else if (val == WLC_BW_CAP_40MHZ)
2305			nbw = WL_CHANSPEC_BW_40;
2306
2307		param.bandtype = bandtype;
2308		param.bw_cap = (uint8) val;
2309
2310		WL_IOVAR_SET(name, "bw_cap", &param, sizeof(param));
2311	} else {
2312		/* Save n mode to OFF */
2313		nvram_set(strcat_r(prefix, "nmode", tmp), "0");
2314	}
2315
2316	/* Use chanspec to set the channel */
2317	if ((str = nvram_get(strcat_r(prefix, "chanspec", tmp))) != NULL) {
2318		chanspec = wf_chspec_aton(str);
2319
2320		if (chanspec) {
2321			WL_IOVAR_SETINT(name, "chanspec", (uint32)chanspec);
2322		}
2323	}
2324
2325	/* Legacy method of setting channels (for compatibility) */
2326	/* Set channel before setting gmode or rateset */
2327	/* Manual Channel Selection - when channel # is not 0 */
2328	val = atoi(nvram_safe_get(strcat_r(prefix, "channel", tmp)));
2329	if ((chanspec == 0) && val && !WLCONF_PHYTYPE_11N(phytype)) {
2330		WL_SETINT(name, WLC_SET_CHANNEL, val);
2331		if (ret) {
2332			/* Use current channel (card may have changed) */
2333			WL_IOCTL(name, WLC_GET_CHANNEL, &ci, sizeof(ci));
2334			snprintf(buf, sizeof(buf), "%d", ci.target_channel);
2335			nvram_set(strcat_r(prefix, "channel", tmp), buf);
2336		}
2337	} else if ((chanspec == 0) && val && WLCONF_PHYTYPE_11N(phytype)) {
2338		uint channel;
2339		uint nctrlsb = 0;
2340		uint cspecbw = (bandtype == WLC_BAND_2G) ?
2341			WL_CHANSPEC_BAND_2G:WL_CHANSPEC_BAND_5G;
2342
2343		channel = val;
2344
2345		if (nbw == WL_CHANSPEC_BW_80) {
2346			/* Get Ctrl SB for 80MHz channel */
2347			str = nvram_safe_get(strcat_r(prefix, "nctrlsb", tmp));
2348
2349			/* Adjust the channel to be center channel */
2350			channel = channel + CH_40MHZ_APART - CH_10MHZ_APART;
2351
2352			if (!strcmp(str, "ll")) {
2353				nctrlsb = WL_CHANSPEC_CTL_SB_LL;
2354			} else if (!strcmp(str, "lu")) {
2355				nctrlsb = WL_CHANSPEC_CTL_SB_LU;
2356				channel -= CH_20MHZ_APART;
2357			} else if (!strcmp(str, "ul")) {
2358				nctrlsb = WL_CHANSPEC_CTL_SB_UL;
2359				channel -= 2 * CH_20MHZ_APART;
2360			} else if (!strcmp(str, "uu")) {
2361				nctrlsb = WL_CHANSPEC_CTL_SB_UU;
2362				channel -= 3 * CH_20MHZ_APART;
2363			}
2364
2365		} else if (nbw == WL_CHANSPEC_BW_40) {
2366			/* Get Ctrl SB for 40MHz channel */
2367			str = nvram_safe_get(strcat_r(prefix, "nctrlsb", tmp));
2368
2369			/* Adjust the channel to be center channel */
2370			if (!strcmp(str, "lower")) {
2371				nctrlsb = WL_CHANSPEC_CTL_SB_LOWER;
2372				channel = channel + 2;
2373			} else if (!strcmp(str, "upper")) {
2374				nctrlsb = WL_CHANSPEC_CTL_SB_UPPER;
2375				channel = channel - 2;
2376			}
2377		}
2378
2379		/* band | BW | CTRL SB | Channel */
2380		chanspec |= (cspecbw | nbw | nctrlsb | channel);
2381		WL_IOVAR_SETINT(name, "chanspec", (uint32)chanspec);
2382	}
2383
2384	/* Set up number of Tx and Rx streams */
2385	if (WLCONF_PHYTYPE_11N(phytype)) {
2386		int count;
2387		int streams;
2388
2389		/* Get the number of tx chains supported by the hardware */
2390		wl_iovar_getint(name, "hw_txchain", &count);
2391		/* update NVRAM with capabilities */
2392		snprintf(var, sizeof(var), "%d", count);
2393		nvram_set(strcat_r(prefix, "hw_txchain", tmp), var);
2394
2395		/* Verify that there is an NVRAM param for txstreams, if not create it and
2396		 * set it to hw_txchain
2397		 */
2398		streams = atoi(nvram_safe_get(strcat_r(prefix, "txchain", tmp)));
2399		if (streams == 0) {
2400			/* invalid - NVRAM needs to be fixed/initialized */
2401			nvram_set(strcat_r(prefix, "txchain", tmp), var);
2402			streams = count;
2403		}
2404		/* Apply user configured txstreams, use 1 if user disabled nmode */
2405		WL_IOVAR_SETINT(name, "txchain", streams);
2406
2407		wl_iovar_getint(name, "hw_rxchain", &count);
2408		/* update NVRAM with capabilities */
2409		snprintf(var, sizeof(var), "%d", count);
2410		nvram_set(strcat_r(prefix, "hw_rxchain", tmp), var);
2411
2412		/* Verify that there is an NVRAM param for rxstreams, if not create it and
2413		 * set it to hw_txchain
2414		 */
2415		streams = atoi(nvram_safe_get(strcat_r(prefix, "rxchain", tmp)));
2416		if (streams == 0) {
2417			/* invalid - NVRAM needs to be fixed/initialized */
2418			nvram_set(strcat_r(prefix, "rxchain", tmp), var);
2419			streams = count;
2420		}
2421
2422		/* Apply user configured rxstreams, use 1 if user disabled nmode */
2423		WL_IOVAR_SETINT(name, "rxchain", streams);
2424	}
2425
2426	/* Reset to hardware rateset (band may have changed) */
2427	WL_IOCTL(name, WLC_GET_RATESET, &rs, sizeof(wl_rateset_t));
2428	WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof(wl_rateset_t));
2429
2430	/* Set gmode */
2431	if (bandtype == WLC_BAND_2G) {
2432		int override = WLC_PROTECTION_OFF;
2433		int control = WLC_PROTECTION_CTL_OFF;
2434
2435		/* Set gmode */
2436		gmode = atoi(nvram_safe_get(strcat_r(prefix, "gmode", tmp)));
2437		WL_IOCTL(name, WLC_SET_GMODE, &gmode, sizeof(gmode));
2438
2439		/* Set gmode protection override and control algorithm */
2440		strcat_r(prefix, "gmode_protection", tmp);
2441		if (nvram_match(tmp, "auto")) {
2442			override = WLC_PROTECTION_AUTO;
2443			control = WLC_PROTECTION_CTL_OVERLAP;
2444		}
2445		WL_IOCTL(name, WLC_SET_GMODE_PROTECTION_OVERRIDE, &override, sizeof(override));
2446		WL_IOCTL(name, WLC_SET_PROTECTION_CONTROL, &control, sizeof(control));
2447	}
2448
2449	/* Set nmode_protection */
2450	if (WLCONF_PHYTYPE_11N(phytype)) {
2451		int override = WLC_PROTECTION_OFF;
2452		int control = WLC_PROTECTION_CTL_OFF;
2453
2454		/* Set n protection override and control algorithm */
2455		str = nvram_get(strcat_r(prefix, "nmode_protection", tmp));
2456		if (!str || !strcmp(str, "auto")) {
2457			override = WLC_PROTECTION_AUTO;
2458			control = WLC_PROTECTION_CTL_OVERLAP;
2459		}
2460
2461		WL_IOVAR_SETINT(name, "nmode_protection_override",
2462		                (uint32)override);
2463		WL_IOCTL(name, WLC_SET_PROTECTION_CONTROL, &control, sizeof(control));
2464	}
2465
2466	/* Set vlan_prio_mode */
2467	{
2468		uint32 mode = OFF; /* default */
2469
2470		strcat_r(prefix, "vlan_prio_mode", tmp);
2471
2472		if (nvram_match(tmp, "on"))
2473			mode = ON;
2474
2475		WL_IOVAR_SETINT(name, "vlan_mode", mode);
2476	}
2477
2478	/* Get bluetooth coexistance(BTC) mode */
2479	btc_mode = atoi(nvram_safe_get(strcat_r(prefix, "btc_mode", tmp)));
2480
2481	/* Set the AMPDU and AMSDU options based on the N-mode */
2482	wme_global = wlconf_ampdu_amsdu_set(name, prefix, nmode, btc_mode, ap);
2483
2484	/* Now that wme_global is known, check per-BSS disable settings */
2485	for (i = 0; i < bclist->count; i++) {
2486		char *subprefix;
2487		bsscfg = &bclist->bsscfgs[i];
2488
2489		subprefix = apsta ? prefix : bsscfg->prefix;
2490
2491		/* For each BSS, check WME; make sure wme is set properly for this interface */
2492		strcat_r(subprefix, "wme", tmp);
2493		nvram_set(tmp, wme_global ? "on" : "off");
2494
2495		str = nvram_safe_get(strcat_r(bsscfg->prefix, "wme_bss_disable", tmp));
2496		val = (str[0] == '1') ? 1 : 0;
2497		WL_BSSIOVAR_SETINT(name, "wme_bss_disable", bsscfg->idx, val);
2498	}
2499
2500	/*
2501	* Set operational capabilities required for stations
2502	* to associate to the BSS. Per-BSS setting.
2503	*/
2504	for (i = 0; i < bclist->count; i++) {
2505		bsscfg = &bclist->bsscfgs[i];
2506		str = nvram_safe_get(strcat_r(bsscfg->prefix, "bss_opmode_cap_reqd", tmp));
2507		val = atoi(str);
2508		WL_BSSIOVAR_SETINT(name, "mode_reqd", bsscfg->idx, val);
2509	}
2510
2511
2512	/* Get current rateset (gmode may have changed) */
2513	WL_IOCTL(name, WLC_GET_CURR_RATESET, &rs, sizeof(wl_rateset_t));
2514
2515	strcat_r(prefix, "rateset", tmp);
2516	if (nvram_match(tmp, "all")) {
2517		/* Make all rates basic */
2518		for (i = 0; i < rs.count; i++)
2519			rs.rates[i] |= 0x80;
2520	} else if (nvram_match(tmp, "12")) {
2521		/* Make 1 and 2 basic */
2522		for (i = 0; i < rs.count; i++) {
2523			if ((rs.rates[i] & 0x7f) == 2 || (rs.rates[i] & 0x7f) == 4)
2524				rs.rates[i] |= 0x80;
2525			else
2526				rs.rates[i] &= ~0x80;
2527		}
2528	}
2529
2530	if (phytype != PHY_TYPE_SSN && phytype != PHY_TYPE_LCN) {
2531		/* Set BTC mode */
2532		if (!wl_iovar_setint(name, "btc_mode", btc_mode)) {
2533			if (btc_mode == WL_BTC_PREMPT) {
2534				wl_rateset_t rs_tmp = rs;
2535				/* remove 1Mbps and 2 Mbps from rateset */
2536				for (i = 0, rs.count = 0; i < rs_tmp.count; i++) {
2537					if ((rs_tmp.rates[i] & 0x7f) == 2 ||
2538					    (rs_tmp.rates[i] & 0x7f) == 4)
2539						continue;
2540					rs.rates[rs.count++] = rs_tmp.rates[i];
2541				}
2542			}
2543		}
2544	}
2545
2546	/* Set rateset */
2547	WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof(wl_rateset_t));
2548
2549	/* Allow short preamble settings for the following:
2550	 * 11b - short/long
2551	 * 11g - short /long in GMODE_LEGACY_B and GMODE_AUTO gmodes
2552	 *	 GMODE_PERFORMANCE and GMODE_LRS will use short and long
2553	 *	 preambles respectively, by default
2554	 * 11n - short/long applicable in 2.4G band only
2555	 */
2556	if (phytype == PHY_TYPE_B ||
2557	    (WLCONF_PHYTYPE_11N(phytype) && (bandtype == WLC_BAND_2G)) ||
2558	    ((phytype == PHY_TYPE_G || phytype == PHY_TYPE_LP) &&
2559	     (gmode == GMODE_LEGACY_B || gmode == GMODE_AUTO))) {
2560		strcat_r(prefix, "plcphdr", tmp);
2561		if (nvram_match(tmp, "long"))
2562			val = WLC_PLCP_AUTO;
2563		else
2564			val = WLC_PLCP_SHORT;
2565		WL_IOCTL(name, WLC_SET_PLCPHDR, &val, sizeof(val));
2566	}
2567
2568	/* Set rate in 500 Kbps units */
2569	val = atoi(nvram_safe_get(strcat_r(prefix, "rate", tmp))) / 500000;
2570
2571	/* Convert Auto mcsidx to Auto rate */
2572	if (WLCONF_PHYTYPE_11N(phytype)) {
2573		int mcsidx = atoi(nvram_safe_get(strcat_r(prefix, "nmcsidx", tmp)));
2574
2575		/* -1 mcsidx used to designate AUTO rate */
2576		if (mcsidx == -1)
2577			val = 0;
2578	}
2579
2580	/* 1Mbps and 2 Mbps are not allowed in BTC pre-emptive mode */
2581	if (btc_mode == WL_BTC_PREMPT && (val == 2 || val == 4))
2582		/* Must b/g band.  Set to 5.5Mbps */
2583		val = 11;
2584
2585	/* it is band-blind. try both band */
2586	error_bg = wl_iovar_setint(name, "bg_rate", val);
2587	error_a = wl_iovar_setint(name, "a_rate", val);
2588
2589	if (error_bg && error_a) {
2590		/* both failed. Try default rate (card may have changed) */
2591		val = 0;
2592
2593		error_bg = wl_iovar_setint(name, "bg_rate", val);
2594		error_a = wl_iovar_setint(name, "a_rate", val);
2595
2596		snprintf(buf, sizeof(buf), "%d", val);
2597		nvram_set(strcat_r(prefix, "rate", tmp), buf);
2598	}
2599
2600	/* check if nrate needs to be applied */
2601	if (nmode != OFF) {
2602		uint32 nrate = 0;
2603		int mcsidx = atoi(nvram_safe_get(strcat_r(prefix, "nmcsidx", tmp)));
2604		bool ismcs = (mcsidx >= 0);
2605
2606		/* mcsidx of 32 is valid only for 40 Mhz */
2607		if (mcsidx == 32 && nbw == WL_CHANSPEC_BW_20) {
2608			mcsidx = -1;
2609			ismcs = FALSE;
2610			nvram_set(strcat_r(prefix, "nmcsidx", tmp), "-1");
2611		}
2612
2613		/* Use nrate iovar only for MCS rate. */
2614		if (ismcs) {
2615			nrate |= WL_RSPEC_ENCODE_HT;
2616			nrate |= mcsidx & WL_RSPEC_RATE_MASK;
2617
2618			WL_IOVAR_SETINT(name, "nrate", nrate);
2619		}
2620	}
2621
2622	/* Set multicast rate in 500 Kbps units */
2623	val = atoi(nvram_safe_get(strcat_r(prefix, "mrate", tmp))) / 500000;
2624	/* 1Mbps and 2 Mbps are not allowed in BTC pre-emptive mode */
2625	if (btc_mode == WL_BTC_PREMPT && (val == 2 || val == 4))
2626		/* Must b/g band.  Set to 5.5Mbps */
2627		val = 11;
2628
2629	/* it is band-blind. try both band */
2630	error_bg = wl_iovar_setint(name, "bg_mrate", val);
2631	error_a = wl_iovar_setint(name, "a_mrate", val);
2632
2633	if (error_bg && error_a) {
2634		/* Try default rate (card may have changed) */
2635		val = 0;
2636
2637		wl_iovar_setint(name, "bg_mrate", val);
2638		wl_iovar_setint(name, "a_mrate", val);
2639
2640		snprintf(buf, sizeof(buf), "%d", val);
2641		nvram_set(strcat_r(prefix, "mrate", tmp), buf);
2642	}
2643
2644	/* Set fragmentation threshold */
2645	val = atoi(nvram_safe_get(strcat_r(prefix, "frag", tmp)));
2646	wl_iovar_setint(name, "fragthresh", val);
2647
2648	/* Set RTS threshold */
2649	val = atoi(nvram_safe_get(strcat_r(prefix, "rts", tmp)));
2650	wl_iovar_setint(name, "rtsthresh", val);
2651
2652	/* Set DTIM period */
2653	val = atoi(nvram_safe_get(strcat_r(prefix, "dtim", tmp)));
2654	WL_IOCTL(name, WLC_SET_DTIMPRD, &val, sizeof(val));
2655
2656	/* Set beacon period */
2657	val = atoi(nvram_safe_get(strcat_r(prefix, "bcn", tmp)));
2658	WL_IOCTL(name, WLC_SET_BCNPRD, &val, sizeof(val));
2659
2660	/* Set beacon rotation */
2661	str = nvram_get(strcat_r(prefix, "bcn_rotate", tmp));
2662	if (!str) {
2663		/* No nvram variable found, use the default */
2664		str = nvram_default_get(strcat_r(prefix, "bcn_rotate", tmp));
2665	}
2666	val = atoi(str);
2667	wl_iovar_setint(name, "bcn_rotate", val);
2668
2669	/* Set framebursting mode */
2670	if (btc_mode == WL_BTC_PREMPT)
2671		val = FALSE;
2672	else
2673		val = nvram_match(strcat_r(prefix, "frameburst", tmp), "on");
2674	WL_IOCTL(name, WLC_SET_FAKEFRAG, &val, sizeof(val));
2675
2676	/* Set dynamic frameburst max station limit */
2677	str = nvram_get(strcat_r(prefix, "frameburst_dyn_max_stations", tmp));
2678	if (!str) { /* Fall back to previous name, frameburst_dyn */
2679		str = nvram_safe_get("frameburst_dyn");
2680	}
2681	wl_iovar_setint(name, "frameburst_dyn_max_stations", atoi(str));
2682
2683	/* Set STBC tx and rx mode */
2684	if (phytype == PHY_TYPE_N ||
2685		phytype == PHY_TYPE_HT ||
2686		phytype == PHY_TYPE_AC) {
2687		char *nvram_str = nvram_safe_get(strcat_r(prefix, "stbc_tx", tmp));
2688
2689		if (!strcmp(nvram_str, "auto")) {
2690			WL_IOVAR_SETINT(name, "stbc_tx", AUTO);
2691		} else if (!strcmp(nvram_str, "on")) {
2692			WL_IOVAR_SETINT(name, "stbc_tx", ON);
2693		} else if (!strcmp(nvram_str, "off")) {
2694			WL_IOVAR_SETINT(name, "stbc_tx", OFF);
2695		}
2696		val = atoi(nvram_safe_get(strcat_r(prefix, "stbc_rx", tmp)));
2697		WL_IOVAR_SETINT(name, "stbc_rx", val);
2698	}
2699
2700	/* Set RIFS mode based on framebursting */
2701	if (WLCONF_PHYTYPE_11N(phytype)) {
2702		char *nvram_str = nvram_safe_get(strcat_r(prefix, "rifs", tmp));
2703		if (!strcmp(nvram_str, "on"))
2704			wl_iovar_setint(name, "rifs", ON);
2705		else if (!strcmp(nvram_str, "off"))
2706			wl_iovar_setint(name, "rifs", OFF);
2707
2708		/* RIFS mode advertisement */
2709		nvram_str = nvram_safe_get(strcat_r(prefix, "rifs_advert", tmp));
2710		if (!strcmp(nvram_str, "auto"))
2711			wl_iovar_setint(name, "rifs_advert", AUTO);
2712		else if (!strcmp(nvram_str, "off"))
2713			wl_iovar_setint(name, "rifs_advert", OFF);
2714	}
2715
2716	/* Override BA mode only if set to on/off */
2717	ba = nvram_safe_get(strcat_r(prefix, "ba", tmp));
2718	if (!strcmp(ba, "on"))
2719		wl_iovar_setint(name, "ba", ON);
2720	else if (!strcmp(ba, "off"))
2721		wl_iovar_setint(name, "ba", OFF);
2722
2723	if (WLCONF_PHYTYPE_11N(phytype)) {
2724		val = AVG_DMA_XFER_RATE;
2725		wl_iovar_set(name, "avg_dma_xfer_rate", &val, sizeof(val));
2726	}
2727
2728	/*
2729	 * If no nvram variable exists to force non-aggregated mpdu regulation on/off,
2730	 * limit to 2G interfaces.
2731	 */
2732	str = nvram_get(strcat_r(prefix, "nar", tmp));
2733	if (str) {
2734		val = atoi(str);
2735	} else {
2736		val = (bandtype == WLC_BAND_2G) ? 1 : 0;
2737	}
2738	WLCONF_DBG("%sabling non-aggregated regulation on band %d\n", (val) ? "En":"Dis", bandtype);
2739	WL_IOVAR_SETINT(name, "nar", val);
2740	if (val) {
2741		/* nar is enabled on this interface, add tuneable parameters */
2742		str = nvram_get(strcat_r(prefix, "nar_handle_ampdu", tmp));
2743		if (str) {
2744			WL_IOVAR_SETINT(name, "nar_handle_ampdu", atoi(str));
2745		}
2746		str = nvram_get(strcat_r(prefix, "nar_transit_limit", tmp));
2747		if (str) {
2748			WL_IOVAR_SETINT(name, "nar_transit_limit", atoi(str));
2749		}
2750	}
2751
2752	/* Set up TxBF */
2753	wlconf_set_txbf(name, prefix);
2754
2755	/* set airtime fairness */
2756	val = 0;
2757	str = nvram_get(strcat_r(prefix, "atf", tmp));
2758	if (str) {
2759		val = atoi(str);
2760	}
2761	WL_IOVAR_SETINT(name, "atf", val);
2762
2763	str = nvram_get(strcat_r(prefix, "ampdu_atf_us", tmp));
2764	if (str) {
2765		val = atoi(str);
2766		if (val) {
2767			WL_IOVAR_SETINT(name, "ampdu_atf_us", val);
2768			WL_IOVAR_SETINT(name, "nar_atf_us", val);
2769		}
2770	}
2771
2772	/* set TAF */
2773	val = 0;
2774	str = nvram_get(strcat_r(prefix, "taf_enable", tmp));
2775	if (str && (bandtype == WLC_BAND_5G)) {
2776		val = atoi(str);
2777	}
2778	WL_IOVAR_SETINT(name, "taf", val);
2779
2780	val = 0;
2781	str = nvram_get(strcat_r(prefix, "taf_rule", tmp));
2782	if (str) {
2783		val = strtoul(str, NULL, 0);
2784		WL_IOVAR_SETINT(name, "taf_rule", val);
2785	}
2786
2787	/* Bring the interface back up */
2788	WL_IOCTL(name, WLC_UP, NULL, 0);
2789
2790	/* set ebos, interface must be up */
2791	val = 0;
2792	str = nvram_get(strcat_r(prefix, "ebos_enable", tmp));
2793	if (str) {
2794		val = atoi(str);
2795	}
2796	WL_IOVAR_SETINT(name, "ebos_enable", val);
2797
2798	val = 0;
2799	str = nvram_get(strcat_r(prefix, "ebos_flags", tmp));
2800	if (str) {
2801		val = strtoul(str, NULL, 0);
2802		WL_IOVAR_SETINT(name, "ebos_flags", val);
2803	}
2804
2805	val = 0;
2806	str = nvram_get(strcat_r(prefix, "ebos_prr_threshold", tmp));
2807	if (str) {
2808		val = strtoul(str, NULL, 0);
2809		WL_IOVAR_SETINT(name, "ebos_prr_threshold", val);
2810	}
2811
2812	val = 0;
2813	str = nvram_get(strcat_r(prefix, "ebos_prr_flags", tmp));
2814	if (str) {
2815		val = strtoul(str, NULL, 0);
2816		WL_IOVAR_SETINT(name, "ebos_prr_flags", val);
2817	}
2818
2819	val = 0;
2820	str = nvram_get(strcat_r(prefix, "ebos_prr_transit", tmp));
2821	if (str) {
2822		val = strtol(str, NULL, 0);
2823		WL_IOVAR_SETINT(name, "ebos_prr_transit", val);
2824	}
2825	val = 0;
2826
2827	str = nvram_get(strcat_r(prefix, "ebos_transit", tmp));
2828	if (str) {
2829		val = strtol(str, NULL, 0);
2830		WL_IOVAR_SETINT(name, "ebos_transit", val);
2831	}
2832
2833	/* set phy_percal_delay */
2834	val = atoi(nvram_safe_get(strcat_r(prefix, "percal_delay", tmp)));
2835	if (val) {
2836		wl_iovar_set(name, "phy_percal_delay", &val, sizeof(val));
2837	}
2838
2839	/* Set phy periodic cal if nvram present. Otherwise, use driver defaults. */
2840	str = nvram_get(strcat_r(prefix, "cal_period", tmp));
2841	if (str) {
2842		/* user specified phy cal period. */
2843		val = atoi(str);
2844		WL_IOVAR_SET(name, "cal_period", &val, sizeof(val));
2845	}
2846
2847	/* Set antenna */
2848	val = atoi(nvram_safe_get(strcat_r(prefix, "antdiv", tmp)));
2849	WL_IOCTL(name, WLC_SET_ANTDIV, &val, sizeof(val));
2850
2851	/* Set antenna selection */
2852	wlconf_set_antsel(name, prefix);
2853
2854	/* Set radar parameters if it is enabled */
2855	if (radar_enab) {
2856		wlconf_set_radarthrs(name, prefix);
2857	}
2858
2859	/* set pspretend */
2860	val = 0;
2861	if (ap) {
2862		/* Set pspretend for multi-ssid bss */
2863		for (i = 0; i < bclist->count; i++) {
2864			bsscfg = &bclist->bsscfgs[i];
2865			str = nvram_safe_get(strcat_r(bsscfg->prefix,
2866				"pspretend_retry_limit", tmp));
2867			if (str) {
2868				val = atoi(str);
2869				WL_BSSIOVAR_SETINT(name, "pspretend_retry_limit", bsscfg->idx, val);
2870			}
2871		}
2872
2873		/* now set it for primary bss */
2874		val = 0;
2875		str = nvram_get(strcat_r(prefix, "pspretend_retry_limit", tmp));
2876		if (str) {
2877			val = atoi(str);
2878		}
2879	}
2880	WL_IOVAR_SETINT(name, "pspretend_retry_limit", val);
2881
2882	val = 0;
2883	if (ap) {
2884		str = nvram_get(strcat_r(prefix, "pspretend_threshold", tmp));
2885		if (str) {
2886			val = atoi(str);
2887		}
2888	}
2889	WL_IOVAR_SETINT(name, "pspretend_threshold", val);
2890
2891	/* Set channel interference threshold value if it is enabled */
2892
2893	str = nvram_get(strcat_r(prefix, "glitchthres", tmp));
2894
2895	if (str) {
2896		int glitch_thres = atoi(str);
2897		if (glitch_thres > 0)
2898			WL_IOVAR_SETINT(name, "chanim_glitchthres", glitch_thres);
2899	}
2900
2901	str = nvram_get(strcat_r(prefix, "ccathres", tmp));
2902
2903	if (str) {
2904		int cca_thres = atoi(str);
2905		if (cca_thres > 0)
2906			WL_IOVAR_SETINT(name, "chanim_ccathres", cca_thres);
2907	}
2908
2909	str = nvram_get(strcat_r(prefix, "chanimmode", tmp));
2910
2911	if (str) {
2912		int chanim_mode = atoi(str);
2913		if (chanim_mode >= 0)
2914			WL_IOVAR_SETINT(name, "chanim_mode", chanim_mode);
2915	}
2916
2917	/* bcm_dcs (dynamic channel selection) settings */
2918	str = nvram_safe_get(strcat_r(prefix, "bcmdcs", tmp));
2919	if (!strcmp(str, "on"))
2920		wl_iovar_setint(name, "bcm_dcs", ON);
2921	else if (!strcmp(str, "off"))
2922		wl_iovar_setint(name, "bcm_dcs", OFF);
2923
2924	/* Overlapping BSS Coexistence aka 20/40 Coex. aka OBSS Coex.
2925	 * For an AP - Only use if 2G band AND user wants a 40Mhz chanspec.
2926	 * For a STA - Always
2927	 */
2928	if (WLCONF_PHYTYPE_11N(phytype)) {
2929		if (sta ||
2930		    ((ap || apsta) && (nbw == WL_CHANSPEC_BW_40) && (bandtype == WLC_BAND_2G))) {
2931			str = nvram_safe_get(strcat_r(prefix, "obss_coex", tmp));
2932			if (!str) {
2933				/* No nvram variable found, use the default */
2934				str = nvram_default_get(strcat_r(prefix, "obss_coex", tmp));
2935			}
2936			obss_coex = atoi(str);
2937		} else {
2938			/* Need to disable obss coex in case of 20MHz and/or
2939			 * in case of 5G.
2940			 */
2941			obss_coex = 0;
2942		}
2943#ifdef WLTEST
2944		/* force coex off for msgtest build */
2945		obss_coex = 0;
2946#endif
2947		WL_IOVAR_SETINT(name, "obss_coex", obss_coex);
2948	}
2949
2950	/* Set up TxBF timer */
2951	wlconf_set_txbf_timer(name, prefix);
2952
2953	/* Auto Channel Selection:
2954	 * 1. When channel # is 0 in AP mode, this determines our channel and 20Mhz vs. 40Mhz
2955	 * 2. If we're running OBSS Coex and the user specified a channel, Autochannel runs to
2956	 *    do an initial scan to help us make decisions about whether we can create a 40Mhz AP
2957	 */
2958	/* The following condition(s) must be met in order for Auto Channel Selection to work.
2959	 *  - the I/F must be up for the channel scan
2960	 *  - the AP must not be supporting a BSS (all BSS Configs must be disabled)
2961	 */
2962	if (ap || apsta) {
2963		int channel = chanspec ? wf_chspec_ctlchan(chanspec) : 0;
2964#ifdef EXT_ACS
2965		char tmp[100];
2966		char *ptr;
2967		char * str_val;
2968
2969		str_val = nvram_safe_get("acs_mode");
2970		if (!strcmp(str_val, "legacy"))
2971			goto legacy_mode;
2972
2973		snprintf(tmp, sizeof(tmp), "acs_ifnames");
2974		ptr = nvram_get(tmp);
2975		if (ptr)
2976			snprintf(buf, sizeof(buf), "%s %s", ptr, name);
2977		else
2978			strncpy(buf, name, sizeof(buf));
2979		nvram_set(tmp, buf);
2980		WL_IOVAR_SETINT(name, "chanim_mode", CHANIM_EXT);
2981		goto legacy_end;
2982
2983legacy_mode:
2984#endif /* EXT_ACS */
2985		if (obss_coex || channel == 0) {
2986			if (WLCONF_PHYTYPE_11N(phytype)) {
2987				chanspec_t chanspec;
2988				int pref_chspec;
2989
2990				if (channel != 0) {
2991					/* assumes that initial chanspec has been set earlier */
2992					/* Maybe we expand scope of chanspec from above so
2993					 * that we don't have to do the iovar_get here?
2994					 */
2995
2996					/* We're not doing auto-channel, give the driver
2997					 * the preferred chanspec.
2998					 */
2999					WL_IOVAR_GETINT(name, "chanspec", &pref_chspec);
3000					WL_IOVAR_SETINT(name, "pref_chanspec", pref_chspec);
3001				} else {
3002					WL_IOVAR_SETINT(name, "pref_chanspec", 0);
3003				}
3004
3005				chanspec = wlconf_auto_chanspec(name);
3006				if (chanspec != 0)
3007					WL_IOVAR_SETINT(name, "chanspec", chanspec);
3008			} else {
3009				/* select a channel */
3010				val = wlconf_auto_channel(name);
3011				/* switch to the selected channel */
3012				if (val != 0)
3013					WL_IOCTL(name, WLC_SET_CHANNEL, &val, sizeof(val));
3014			}
3015			/* set the auto channel scan timer in the driver when in auto mode */
3016			if (channel == 0) {
3017				val = 15;	/* 15 minutes for now */
3018			} else {
3019				val = 0;
3020			}
3021		} else {
3022			/* reset the channel scan timer in the driver when not in auto mode */
3023			val = 0;
3024		}
3025
3026		WL_IOCTL(name, WLC_SET_CS_SCAN_TIMER, &val, sizeof(val));
3027		WL_IOVAR_SETINT(name, "chanim_mode", CHANIM_ACT);
3028#ifdef EXT_ACS
3029legacy_end:
3030		;
3031#endif /* EXT_ACS */
3032		/* Apply sta_config configuration settings for this interface */
3033		foreach(var, nvram_safe_get("sta_config"), next) {
3034			wlconf_process_sta_config_entry(name, var);
3035		}
3036
3037	} /* AP or APSTA */
3038
3039	/* Security settings for each BSS Configuration */
3040	for (i = 0; i < bclist->count; i++) {
3041		bsscfg = &bclist->bsscfgs[i];
3042		wlconf_security_options(name, bsscfg->prefix, bsscfg->idx,
3043		                        mac_spoof, wet || sta || apsta || psta || psr);
3044	}
3045
3046	/*
3047	 * Finally enable BSS Configs or Join BSS
3048	 *
3049	 * AP: Enable BSS Config to bring AP up only when nas will not run
3050	 * STA: Join the BSS regardless.
3051	 */
3052	for (i = 0; i < bclist->count; i++) {
3053		struct {int bsscfg_idx; int enable;} setbuf;
3054		char vifname[VIFNAME_LEN];
3055		char *name_ptr = name;
3056
3057		setbuf.bsscfg_idx = bclist->bsscfgs[i].idx;
3058		setbuf.enable = 0;
3059
3060		bsscfg = &bclist->bsscfgs[i];
3061		if (nvram_match(strcat_r(bsscfg->prefix, "bss_enabled", tmp), "1")) {
3062			setbuf.enable = 1;
3063		}
3064
3065		/* Set the MAC list */
3066		maclist = (struct maclist *)buf;
3067		maclist->count = 0;
3068		if (!nvram_match(strcat_r(bsscfg->prefix, "macmode", tmp), "disabled")) {
3069			ea = maclist->ea;
3070			foreach(var, nvram_safe_get(strcat_r(bsscfg->prefix, "maclist", tmp)),
3071				next) {
3072				if (((char *)((&ea[1])->octet)) > ((char *)(&buf[sizeof(buf)])))
3073					break;
3074				if (ether_atoe(var, ea->octet)) {
3075					maclist->count++;
3076					ea++;
3077				}
3078			}
3079		}
3080
3081		if (setbuf.bsscfg_idx == 0) {
3082			name_ptr = name;
3083		} else { /* Non-primary BSS; changes name syntax */
3084			char tmp[VIFNAME_LEN];
3085			int len;
3086
3087			/* Remove trailing _ if present */
3088			memset(tmp, 0, sizeof(tmp));
3089			strncpy(tmp, bsscfg->prefix, VIFNAME_LEN - 1);
3090			if (((len = strlen(tmp)) > 0) && (tmp[len - 1] == '_')) {
3091				tmp[len - 1] = 0;
3092			}
3093			nvifname_to_osifname(tmp, vifname, VIFNAME_LEN);
3094			name_ptr = vifname;
3095		}
3096
3097		WL_IOCTL(name_ptr, WLC_SET_MACLIST, buf, sizeof(buf));
3098
3099		/* Set macmode for each VIF */
3100		(void) strcat_r(bsscfg->prefix, "macmode", tmp);
3101
3102		if (nvram_match(tmp, "deny"))
3103			val = WLC_MACMODE_DENY;
3104		else if (nvram_match(tmp, "allow"))
3105			val = WLC_MACMODE_ALLOW;
3106		else
3107			val = WLC_MACMODE_DISABLED;
3108
3109		WL_IOCTL(name_ptr, WLC_SET_MACMODE, &val, sizeof(val));
3110	}
3111
3112	ret = 0;
3113exit:
3114	if (bclist != NULL)
3115		free(bclist);
3116
3117	return ret;
3118}
3119
3120int
3121wlconf_down(char *name)
3122{
3123	int val, ret = 0;
3124	int i;
3125	int wlsubunit;
3126	int bcmerr;
3127	struct {int bsscfg_idx; int enable;} setbuf;
3128	int wl_ap_build = 0; /* 1 = wl compiled with AP capabilities */
3129	char cap[WLC_IOCTL_SMLEN];
3130	char caps[WLC_IOCTL_MEDLEN];
3131	char *next;
3132	wlc_ssid_t ssid;
3133
3134	/* wlconf doesn't work for virtual i/f */
3135	if (get_ifname_unit(name, NULL, &wlsubunit) == 0 && wlsubunit >= 0) {
3136		WLCONF_DBG("wlconf: skipping virtual interface \"%s\"\n", name);
3137		return 0;
3138	}
3139
3140	/* Check interface (fail silently for non-wl interfaces) */
3141	if ((ret = wl_probe(name)))
3142		return ret;
3143
3144	/* because of ifdefs in wl driver,  when we don't have AP capabilities we
3145	 * can't use the same iovars to configure the wl.
3146	 * so we use "wl_ap_build" to help us know how to configure the driver
3147	 */
3148	if (wl_iovar_get(name, "cap", (void *)caps, sizeof(caps)))
3149		return -1;
3150
3151	foreach(cap, caps, next) {
3152		if (!strcmp(cap, "ap")) {
3153			wl_ap_build = 1;
3154		}
3155	}
3156
3157	if (wl_ap_build) {
3158		/* Bring down the interface */
3159		WL_IOCTL(name, WLC_DOWN, NULL, sizeof(val));
3160
3161		/* Disable all BSS Configs */
3162		for (i = 0; i < WL_MAXBSSCFG; i++) {
3163			setbuf.bsscfg_idx = i;
3164			setbuf.enable = 0;
3165
3166			ret = wl_iovar_set(name, "bss", &setbuf, sizeof(setbuf));
3167			if (ret) {
3168				wl_iovar_getint(name, "bcmerror", &bcmerr);
3169				/* fail quietly on a range error since the driver may
3170				 * support fewer bsscfgs than we are prepared to configure
3171				 */
3172				if (bcmerr == BCME_RANGE)
3173					break;
3174			}
3175		}
3176	} else {
3177		WL_IOCTL(name, WLC_GET_UP, &val, sizeof(val));
3178		if (val) {
3179			/* Nuke SSID */
3180			ssid.SSID_len = 0;
3181			ssid.SSID[0] = '\0';
3182			WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid));
3183
3184			/* Bring down the interface */
3185			WL_IOCTL(name, WLC_DOWN, NULL, sizeof(val));
3186		}
3187	}
3188
3189	/* Nuke the WDS list */
3190	wlconf_wds_clear(name);
3191
3192	return 0;
3193}
3194
3195int
3196wlconf_start(char *name)
3197{
3198	int i, ii, unit, val, ret = 0;
3199	int wlunit = -1;
3200	int wlsubunit = -1;
3201	int ap, apsta, wds, sta = 0, wet = 0;
3202	int wl_ap_build = 0; /* wl compiled with AP capabilities */
3203	char buf[WLC_IOCTL_SMLEN];
3204	struct maclist *maclist;
3205	struct ether_addr *ea;
3206	struct bsscfg_list *bclist = NULL;
3207	struct bsscfg_info *bsscfg = NULL;
3208	wlc_ssid_t ssid;
3209	char cap[WLC_IOCTL_SMLEN], caps[WLC_IOCTL_MEDLEN];
3210	char var[80], tmp[100], prefix[PREFIX_LEN], *str, *next;
3211	int trf_mgmt_cap = 0, trf_mgmt_dwm_cap = 0;
3212	bool dwm_supported = FALSE;
3213
3214	/* Check interface (fail silently for non-wl interfaces) */
3215	if ((ret = wl_probe(name)))
3216		return ret;
3217
3218	/* wlconf doesn't work for virtual i/f, so if we are given a
3219	 * virtual i/f return 0 if that interface is in it's parent's "vifs"
3220	 * list otherwise return -1
3221	 */
3222	memset(tmp, 0, sizeof(tmp));
3223	if (get_ifname_unit(name, &wlunit, &wlsubunit) == 0) {
3224		if (wlsubunit >= 0) {
3225			/* we have been given a virtual i/f,
3226			 * is it in it's parent i/f's virtual i/f list?
3227			 */
3228			sprintf(tmp, "wl%d_vifs", wlunit);
3229
3230			if (strstr(nvram_safe_get(tmp), name) == NULL)
3231				return -1; /* config error */
3232			else
3233				return 0; /* okay */
3234		}
3235	}
3236	else {
3237		return -1;
3238	}
3239
3240	/* because of ifdefs in wl driver,  when we don't have AP capabilities we
3241	 * can't use the same iovars to configure the wl.
3242	 * so we use "wl_ap_build" to help us know how to configure the driver
3243	 */
3244	if (wl_iovar_get(name, "cap", (void *)caps, sizeof(caps)))
3245		return -1;
3246
3247	foreach(cap, caps, next) {
3248		if (!strcmp(cap, "ap"))
3249			wl_ap_build = 1;
3250
3251		if (!strcmp(cap, "traffic-mgmt"))
3252			trf_mgmt_cap = 1;
3253
3254		if (!strcmp(cap, "traffic-mgmt-dwm"))
3255			trf_mgmt_dwm_cap = 1;
3256	}
3257
3258	/* Get instance */
3259	WL_IOCTL(name, WLC_GET_INSTANCE, &unit, sizeof(unit));
3260	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
3261
3262
3263	/* Get the list of BSS Configs */
3264	if (!(bclist = wlconf_get_bsscfgs(name, prefix)))
3265		return -1;
3266
3267	/* wlX_mode settings: AP, STA, WET, BSS/IBSS, APSTA */
3268	str = nvram_safe_get(strcat_r(prefix, "mode", tmp));
3269	ap = (!strcmp(str, "") || !strcmp(str, "ap"));
3270	apsta = (!strcmp(str, "apsta") ||
3271	         ((!strcmp(str, "sta") || !strcmp(str, "psr") || !strcmp(str, "wet")) &&
3272	          bclist->count > 1));
3273	sta = (!strcmp(str, "sta") && bclist->count == 1);
3274	wds = !strcmp(str, "wds");
3275	wet = !strcmp(str, "wet");
3276	if (!strcmp(str, "mac_spoof") || !strcmp(str, "psta") || !strcmp(str, "psr"))
3277		sta = 1;
3278
3279	/* Retain remaining WET effects only if not APSTA */
3280	wet &= !apsta;
3281
3282	/* AP only config, code copied as-is from wlconf function */
3283	if (ap || apsta || wds) {
3284		/* Set lazy WDS mode */
3285		val = atoi(nvram_safe_get(strcat_r(prefix, "lazywds", tmp)));
3286		WL_IOCTL(name, WLC_SET_LAZYWDS, &val, sizeof(val));
3287
3288		/* Set the WDS list */
3289		maclist = (struct maclist *) buf;
3290		maclist->count = 0;
3291		ea = maclist->ea;
3292		foreach(var, nvram_safe_get(strcat_r(prefix, "wds", tmp)), next) {
3293			if (((char *)(ea->octet)) > ((char *)(&buf[sizeof(buf)])))
3294				break;
3295			ether_atoe(var, ea->octet);
3296			maclist->count++;
3297			ea++;
3298		}
3299		WL_IOCTL(name, WLC_SET_WDSLIST, buf, sizeof(buf));
3300
3301		/* Set WDS link detection timeout */
3302		val = atoi(nvram_safe_get(strcat_r(prefix, "wds_timeout", tmp)));
3303		wl_iovar_setint(name, "wdstimeout", val);
3304	}
3305
3306	/*
3307	 * Finally enable BSS Configs or Join BSS
3308	 * code copied as-is from wlconf function
3309	 */
3310	for (i = 0; i < bclist->count; i++) {
3311		struct {int bsscfg_idx; int enable;} setbuf;
3312
3313		setbuf.bsscfg_idx = bclist->bsscfgs[i].idx;
3314		setbuf.enable = 0;
3315
3316		bsscfg = &bclist->bsscfgs[i];
3317		if (nvram_match(strcat_r(bsscfg->prefix, "bss_enabled", tmp), "1")) {
3318			setbuf.enable = 1;
3319		}
3320
3321		/*  bring up BSS  */
3322		if (ap || apsta || sta || wet) {
3323			for (ii = 0; ii < MAX_BSS_UP_RETRIES; ii++) {
3324				if (wl_ap_build) {
3325					WL_IOVAR_SET(name, "bss", &setbuf, sizeof(setbuf));
3326				}
3327				else {
3328					strcat_r(prefix, "ssid", tmp);
3329					ssid.SSID_len = strlen(nvram_safe_get(tmp));
3330					if (ssid.SSID_len > sizeof(ssid.SSID))
3331						ssid.SSID_len = sizeof(ssid.SSID);
3332					strncpy((char *)ssid.SSID, nvram_safe_get(tmp),
3333						ssid.SSID_len);
3334					WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid));
3335				}
3336				if (apsta && (ret != 0))
3337					sleep_ms(1000);
3338				else
3339					break;
3340			}
3341		}
3342
3343	}
3344	if ((ap || apsta || sta) && (trf_mgmt_cap)) {
3345		if (trf_mgmt_dwm_cap && ap)
3346			dwm_supported = TRUE;
3347		trf_mgmt_settings(prefix, dwm_supported);
3348	}
3349
3350#ifdef TRAFFIC_MGMT_RSSI_POLICY
3351	if ((ap || apsta) && (trf_mgmt_cap)) {
3352		trf_mgmt_rssi_policy(prefix);
3353	}
3354#endif /* TRAFFIC_MGMT_RSSI_POLICY */
3355
3356#ifdef __CONFIG_EMF__
3357	if (nvram_match(strcat_r(bsscfg->prefix, "wmf_bss_enable", tmp), "1")) {
3358		val = atoi(nvram_safe_get(strcat_r(prefix, "wmf_ucigmp_query", tmp)));
3359		wl_iovar_setint(name, "wmf_ucast_igmp_query", val);
3360		val = atoi(nvram_safe_get(strcat_r(prefix, "wmf_mdata_sendup", tmp)));
3361		wl_iovar_setint(name, "wmf_mcast_data_sendup", val);
3362		val = atoi(nvram_safe_get(strcat_r(prefix, "wmf_ucast_upnp", tmp)));
3363		wl_iovar_setint(name, "wmf_ucast_upnp", val);
3364		val = atoi(nvram_safe_get(strcat_r(prefix, "wmf_igmpq_filter", tmp)));
3365		wl_iovar_setint(name, "wmf_igmpq_filter", val);
3366	}
3367#endif /* __CONFIG_EMF__ */
3368
3369	if (bclist != NULL)
3370		free(bclist);
3371
3372	return ret;
3373}
3374
3375#if defined(linux)
3376static int
3377wlconf_security(char *name)
3378{
3379	int unit, bsscfg_idx;
3380	char prefix[PREFIX_LEN];
3381	char tmp[100], *str;
3382
3383	/* Get the interface subunit */
3384	if (get_ifname_unit(name, &unit, &bsscfg_idx) != 0) {
3385		WLCONF_DBG("wlconfig(%s): unable to parse unit.subunit in interface "
3386		           "name \"%s\"\n", name, name);
3387		return -1;
3388	}
3389
3390	/* Configure security parameters for the newly created interface */
3391	snprintf(prefix, sizeof(prefix), "wl%d_", unit);
3392	str = nvram_safe_get(strcat_r(prefix, "mode", tmp));
3393	wlconf_security_options(name, prefix, bsscfg_idx, FALSE,
3394	                        !strcmp(str, "psta") || !strcmp(str, "psr"));
3395
3396	return 0;
3397}
3398
3399int
3400main(int argc, char *argv[])
3401{
3402	/* Check parameters and branch based on action */
3403	if (argc == 3 && !strcmp(argv[2], "up"))
3404		return wlconf(argv[1]);
3405	else if (argc == 3 && !strcmp(argv[2], "down"))
3406		return wlconf_down(argv[1]);
3407	else if (argc == 3 && !strcmp(argv[2], "start"))
3408	  return wlconf_start(argv[1]);
3409	else if (argc == 3 && !strcmp(argv[2], "security"))
3410	  return wlconf_security(argv[1]);
3411	else {
3412		fprintf(stderr, "Usage: wlconf <ifname> up|down\n");
3413		return -1;
3414	}
3415}
3416#endif /*  defined(linux) */
3417