/* 
 * no license, extracted from wag54gv2-AU_v1.00.39 GPL
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include <typedefs.h>
#include <bcmnvram.h>
#include <bcmutils.h>
#include <shutils.h>
#include <wlutils.h>

/* phy types */
#define	PHY_TYPE_A		0
#define	PHY_TYPE_B		1
#define	PHY_TYPE_G		2
#define	PHY_TYPE_NULL		0xf

/* parts of an idcode: */
#define	IDCODE_MFG_MASK		0x00000fff
#define	IDCODE_MFG_SHIFT	0
#define	IDCODE_ID_MASK		0x0ffff000
#define	IDCODE_ID_SHIFT		12
#define	IDCODE_REV_MASK		0xf0000000
#define	IDCODE_REV_SHIFT	28

#define WL_IOCTL(name, cmd, buf, len) ((void) wl_ioctl((name), (cmd), (buf), (len)))

/* set WEP key */
static int
wlconf_set_wep_key(char *name, char *prefix, int i)
{
	wsec_key_t key;
	char wl_key[] = "wlXXXXXXXXXX_keyXXXXXXXXXX";
	char *keystr, hex[] = "XX";
	unsigned char *data = key.data;
	int ret = 0;

	memset(&key, 0, sizeof(key));
	key.index = i - 1;
	sprintf(wl_key, "%skey%d", prefix, i);
	keystr = nvram_safe_get(wl_key);

	switch (strlen(keystr)) {
	case WEP1_KEY_SIZE:
	case WEP128_KEY_SIZE:
		key.len = strlen(keystr);
		strcpy(key.data, keystr);
		break;
	case WEP1_KEY_HEX_SIZE:
	case WEP128_KEY_HEX_SIZE:
		key.len = strlen(keystr) / 2;
		while (*keystr) {
			strncpy(hex, keystr, 2);
			*data++ = (unsigned char) strtoul(hex, NULL, 16);
			keystr += 2;
		}
		break;
	default:
		key.len = 0;
		break;
	}

	/* Set current WEP key */
	if (key.len && i == atoi(nvram_safe_get(strcat_r(prefix, "key", wl_key))))
		key.flags = WSEC_PRIMARY_KEY;

	WL_IOCTL(name, WLC_SET_KEY, &key, sizeof(key));

	return ret;
}

extern struct nvram_tuple router_defaults[];

/* Keep this table in order */
static struct {
	int locale;
	char **names;
	char *abbr;
} countries[] = {
	{ WLC_WW,  ((char *[]) { "Worldwide", "WW", NULL }), "AU" },
	{ WLC_THA, ((char *[]) { "Thailand", "THA", NULL }), "TH" },
	{ WLC_ISR, ((char *[]) { "Israel", "ISR", NULL }), "IL" },
	{ WLC_JDN, ((char *[]) { "Jordan", "JDN", NULL }), "JO" },
	{ WLC_PRC, ((char *[]) { "China", "P.R. China", "PRC", NULL }), "CN" },
	{ WLC_JPN, ((char *[]) { "Japan", "JPN", NULL }), "JP" },
	{ WLC_FCC, ((char *[]) { "USA", "Canada", "ANZ", "New Zealand", "FCC", NULL }), "US" },
	{ WLC_EUR, ((char *[]) { "Europe", "EUR", NULL }), "DE" },
	{ WLC_USL, ((char *[]) { "USA Low", "USALow", "USL", NULL }), "US" },
	{ WLC_JPH, ((char *[]) { "Japan High", "JapanHigh", "JPH", NULL }), "JP" },
	{ WLC_ALL, ((char *[]) { "All", "AllTheChannels", NULL }), "All" },
};

/* validate/restore all per-interface related variables */
static void
wlconf_validate_all(char *prefix, bool restore)
{
	struct nvram_tuple *t;
	char tmp[100];
	char *v;
	for (t = router_defaults; t->name; t++) {
		if (!strncmp(t->name, "wl_", 3)) {
			strcat_r(prefix, &t->name[3], tmp);
			if (!restore && nvram_get(tmp))
				continue;
			v = nvram_get(t->name);
			nvram_set(tmp, v ? v : t->value);
		}
	}
}

/* restore specific per-interface variable */
static void
wlconf_restore_var(char *prefix, char *name)
{
	struct nvram_tuple *t;
	char tmp[100];
	for (t = router_defaults; t->name; t++) {
		if (!strncmp(t->name, "wl_", 3) && !strcmp(&t->name[3], name)) {
			nvram_set(strcat_r(prefix, name, tmp), t->value);
			break;
		}
	}
}

/* Set up wsec */
static int
wlconf_set_wsec(char *ifname, char *prefix)
{
	char tmp[100];
	int val;
	strcat_r(prefix, "wep", tmp);
	if (nvram_match(tmp, "wep") || nvram_match(tmp, "on") || nvram_match(tmp, "restricted"))
		val = WEP_ENABLED;
	else if (nvram_match(tmp, "tkip"))
		val = TKIP_ENABLED;
	else if (nvram_match(tmp, "aes"))
		val = AES_ENABLED;
	else if (nvram_match(tmp, "tkip+aes"))
		val = TKIP_ENABLED | AES_ENABLED;
	else
		val = 0;
	return wl_ioctl(ifname, WLC_SET_WSEC, &val, sizeof(val));
}

/*
* For debugging only
*/
#define WLCONF_DBG(fmt, arg...)

#if defined(linux)
#include <unistd.h>
static void
sleep_ms(const unsigned int ms)
{
	usleep(1000*ms);
}
#endif

/*
* The following condition(s) must be met when Auto Channel Selection 
* is enabled.
*  - the I/F is up (change radio channel requires it is up?)
*  - the AP must not be associated (setting SSID to empty should 
*    make sure it for us)
*/
static uint8
wlconf_auto_channel(char *name)
{
	int chosen = 0;
	wl_uint32_list_t request;
	int phytype;
	/* query the phy type */
	wl_ioctl(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
	request.count = 0;	/* let the ioctl decide */
	if (!wl_ioctl(name, WLC_START_CHANNEL_SEL, &request, sizeof(request))) {
		sleep_ms(phytype == PHY_TYPE_A ? 1000 : 750);
		while (wl_ioctl(name, WLC_GET_CHANNEL_SEL, &chosen, sizeof(chosen)))
			sleep_ms(100);
	}
	WLCONF_DBG("interface %s: channel selected %d\n", name, chosen);
	return chosen;
}

/* PHY type/BAND conversion */
#define WLCONF_PHYTYPE2BAND(phy)	((phy) == PHY_TYPE_A ? WLC_BAND_A : WLC_BAND_B)
/* PHY type conversion */
#define WLCONF_PHYTYPE2STR(phy)	((phy) == PHY_TYPE_A ? "a" : \
				 (phy) == PHY_TYPE_B ? "b" : "g")
#define WLCONF_STR2PHYTYPE(phy)	((phy) && (phy)[0] == 'a' ? PHY_TYPE_A : \
				 (phy) && (phy)[0] == 'b' ? PHY_TYPE_B : PHY_TYPE_G)
				 
/* configure the specified wireless interface */
int
wlconf(char *name)
{
	int restore_defaults, val, unit, phytype, gmode = 0, ret = 0;
	char tmp[100], prefix[] = "wlXXXXXXXXXX_";
	char var[80], *next, phy[] = "a", *str;
	unsigned char buf[WLC_IOCTL_MAXLEN];
	char *country;
	wlc_rev_info_t rev;
	channel_info_t ci;
	struct maclist *maclist;
	struct ether_addr *ea;
	wlc_ssid_t ssid;
	wl_rateset_t rs;
	unsigned int i;
	char eaddr[32];
	int ap, sta = 0, wet = 0;
	char country_code[4];

	/* Check interface (fail silently for non-wl interfaces) */
	if ((ret = wl_probe(name)))
		return ret;

	/* Get MAC address */
	(void) wl_hwaddr(name, buf);

	/* Get instance */
	WL_IOCTL(name, WLC_GET_INSTANCE, &unit, sizeof(unit));
	snprintf(prefix, sizeof(prefix), "wl%d_", unit);

	/* Restore defaults if per-interface parameters do not exist */
	restore_defaults = !nvram_get(strcat_r(prefix, "ifname", tmp));
	wlconf_validate_all(prefix, restore_defaults);
	nvram_set(strcat_r(prefix, "ifname", tmp), name);
	nvram_set(strcat_r(prefix, "hwaddr", tmp), ether_etoa(buf, eaddr));
	snprintf(buf, sizeof(buf), "%d", unit);
	nvram_set(strcat_r(prefix, "unit", tmp), buf);

	/*
	* Nuke SSID first so that the AP won't be associated when WLC_UP.
	* This must be done here if Auto Channel Selection is enabled.
	*/
	WL_IOCTL(name, WLC_GET_UP, &val, sizeof(val));
	if (val) {
		/* Nuke SSID  */
		ssid.SSID_len = 0;
		ssid.SSID[0] = '\0';
		WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid));

		/* Bring the interface down */
		WL_IOCTL(name, WLC_DOWN, NULL, sizeof(val));
	}

	/* Set mode : AP, STA */
	ap = nvram_match(strcat_r(prefix, "mode", tmp), "ap");
	val = (ap + nvram_match(strcat_r(prefix, "mode", tmp), "wds")) ? 1 : 0;
	WL_IOCTL(name, WLC_SET_AP, &val, sizeof(val));

	/* Set STA specific parameters */
	if (!ap) {
		/* Set mode: WET */
		if ((wet = nvram_match(strcat_r(prefix, "mode", tmp), "wet")))
			WL_IOCTL(name, WLC_SET_WET, &wet, sizeof(wet));
		/* Set infra: BSS/IBSS */
		if (wet || (sta = nvram_match(strcat_r(prefix, "mode", tmp), "sta"))) {
			val = atoi(nvram_safe_get(strcat_r(prefix, "infra", tmp)));
			WL_IOCTL(name, WLC_SET_INFRA, &val, sizeof(val));
		}
	}

	/* Set network type */
	val = atoi(nvram_safe_get(strcat_r(prefix, "closed", tmp)));
	WL_IOCTL(name, WLC_SET_CLOSED, &val, sizeof(val));

	/* Set up the country code */
	(void) strcat_r(prefix, "country_code", tmp);
	country = nvram_get(tmp);
	if (country) {
		strncpy(country_code, country, sizeof(country_code));
		WL_IOCTL(name, WLC_SET_COUNTRY, country_code, strlen(country_code)+1);
	}
	else {
		/* If country_code doesn't exist, check for country to be backward compatible */
		(void) strcat_r(prefix, "country", tmp);
		country = nvram_safe_get(tmp);
		for (val = 0; val < ARRAYSIZE(countries); val++) {
			char **synonym;
			for (synonym = countries[val].names; *synonym; synonym++)
				if (!strcmp(country, *synonym))
					break;
			if (*synonym)
				break;
		}

		/* Get the default country code if undefined or invalid and set the NVRAM */
		if (val >= ARRAYSIZE(countries)) {
			WL_IOCTL(name, WLC_GET_COUNTRY, country_code, sizeof(country_code));
		}
		else {
			strncpy(country_code, countries[val].abbr, sizeof(country_code));
			WL_IOCTL(name, WLC_SET_COUNTRY, country_code, strlen(country_code)+1);
		}

		/* Add the new NVRAM variable */
		nvram_set("wl_country_code", country_code);
		(void) strcat_r(prefix, "country_code", tmp);
		nvram_set(tmp, country_code);
	}
	
	/* Set the MAC list */
	maclist = (struct maclist *) buf;
	maclist->count = 0;
	if (!nvram_match(strcat_r(prefix, "macmode", tmp), "disabled")) {
		ea = maclist->ea;
		foreach(var, nvram_safe_get(strcat_r(prefix, "maclist", tmp)), next) {
			if ((&ea[1])->octet > &buf[sizeof(buf)])
				break;
			if (ether_atoe(var, ea->octet)) {
				maclist->count++;
				ea++;
			}
		}
	}
	WL_IOCTL(name, WLC_SET_MACLIST, buf, sizeof(buf));

	/* Set the MAC list mode */
	(void) strcat_r(prefix, "macmode", tmp);
	if (nvram_match(tmp, "deny"))
		val = WLC_MACMODE_DENY;
	else if (nvram_match(tmp, "allow"))
		val = WLC_MACMODE_ALLOW;
	else
		val = WLC_MACMODE_DISABLED;
	WL_IOCTL(name, WLC_SET_MACMODE, &val, sizeof(val));

	/* Enable or disable the radio */
	val = nvram_match(strcat_r(prefix, "radio", tmp), "0");
	WL_IOCTL(name, WLC_SET_RADIO, &val, sizeof(val));

	/* Get supported phy types */
	WL_IOCTL(name, WLC_GET_PHYLIST, var, sizeof(var));
	nvram_set(strcat_r(prefix, "phytypes", tmp), var);

	/* Get radio IDs */
	*(next = buf) = '\0';
	for (i = 0; i < strlen(var); i++) {
		/* Switch to band */
		phy[0] = var[i];
		val = WLCONF_STR2PHYTYPE(phy);
		val = WLCONF_PHYTYPE2BAND(val);
		WL_IOCTL(name, WLC_SET_BAND, &val, sizeof(val));
		/* Get radio ID on this band */
		WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
		next += sprintf(next, "%sBCM%X", i ? " " : "",
				(rev.radiorev & IDCODE_ID_MASK) >> IDCODE_ID_SHIFT);
	}
	nvram_set(strcat_r(prefix, "radioids", tmp), buf);

	/* Set band */
	str = nvram_get(strcat_r(prefix, "phytype", tmp));
	val = WLCONF_STR2PHYTYPE(str);
	val = WLCONF_PHYTYPE2BAND(val);
	/* Check errors (card may have changed) */
	if (wl_ioctl(name, WLC_SET_BAND, &val, sizeof(val))) {
		/* default band to the first band in band list */
		phy[0] = var[0];
		val = WLCONF_STR2PHYTYPE(phy);
		val = WLCONF_PHYTYPE2BAND(val);
		WL_IOCTL(name, WLC_SET_BAND, &val, sizeof(val));
	}

	/* Get current core revision */
	WL_IOCTL(name, WLC_GET_REVINFO, &rev, sizeof(rev));
	snprintf(buf, sizeof(buf), "%d", rev.corerev);
	nvram_set(strcat_r(prefix, "corerev", tmp), buf);

	/* Get current phy type */
	WL_IOCTL(name, WLC_GET_PHYTYPE, &phytype, sizeof(phytype));
	snprintf(buf, sizeof(buf), "%s", WLCONF_PHYTYPE2STR(phytype));
	nvram_set(strcat_r(prefix, "phytype", tmp), buf);

	/* Set channel before setting gmode or rateset */
	/* Manual Channel Selection - when channel # is not 0 */
	val = atoi(nvram_safe_get(strcat_r(prefix, "channel", tmp)));
	if (val) {
		if (wl_ioctl(name, WLC_SET_CHANNEL, &val, sizeof(val))) {
			/* Use current channel (card may have changed) */
			WL_IOCTL(name, WLC_GET_CHANNEL, &ci, sizeof(ci));
			snprintf(buf, sizeof(buf), "%d", ci.target_channel);
			nvram_set(strcat_r(prefix, "channel", tmp), buf);
		}
	}
	
	/* Reset to hardware rateset (band may have changed) */
	WL_IOCTL(name, WLC_GET_RATESET, &rs, sizeof (wl_rateset_t));
	WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof (wl_rateset_t));

	/* Set gmode */
	if (phytype == PHY_TYPE_G) {

		/* Set gmode */

		gmode = atoi(nvram_safe_get(strcat_r(prefix, "gmode", tmp)));
		if (gmode == GMODE_AFTERBURNER) {
			if (wl_get_int(name, "abcap", &val) || !val) {
				gmode = GMODE_AUTO;
				snprintf(buf, sizeof(buf), "%d", gmode);
				nvram_set(tmp, buf);
			}
		}
		WL_IOCTL(name, WLC_SET_GMODE, &gmode, sizeof(gmode));

		/* Set gmode protection override and control algorithm */
		if (gmode != GMODE_AFTERBURNER) {
			int override = WLC_G_PROTECTION_OFF;
			int control = WLC_G_PROTECTION_CTL_OFF;
			strcat_r(prefix, "gmode_protection", tmp);
			if (nvram_match(tmp, "auto")) {
				override = WLC_G_PROTECTION_AUTO;
				control = WLC_G_PROTECTION_CTL_OVERLAP;
			}
			WL_IOCTL(name, WLC_SET_GMODE_PROTECTION_OVERRIDE, &override, sizeof(override));
			WL_IOCTL(name, WLC_SET_GMODE_PROTECTION_CONTROL, &control, sizeof(control));
		}
	}

	/* Get current rateset (gmode may have changed) */
	WL_IOCTL(name, WLC_GET_CURR_RATESET, &rs, sizeof (wl_rateset_t));

	strcat_r(prefix, "rateset", tmp);
	if (nvram_match(tmp, "all"))  {
		/* Make all rates basic */
		for (i = 0; i < rs.count; i++)
			rs.rates[i] |= 0x80;
	} else if (nvram_match(tmp, "12")) {
		/* Make 1 and 2 basic */
		for (i = 0; i < rs.count; i++) {
			if ((rs.rates[i] & 0x7f) == 2 || (rs.rates[i] & 0x7f) == 4)
				rs.rates[i] |= 0x80;
			else
				rs.rates[i] &= ~0x80;
		}
	}

	/* Set rateset */
	WL_IOCTL(name, WLC_SET_RATESET, &rs, sizeof (wl_rateset_t));

	/* Allow short preamble override for b cards */
	if (phytype == PHY_TYPE_B || gmode == 0) {
		strcat_r(prefix, "plcphdr", tmp);		
		if (nvram_match(tmp, "long"))
			val = WLC_PLCP_AUTO;
		else
			val = WLC_PLCP_SHORT;
		WL_IOCTL(name, WLC_SET_PLCPHDR, &val, sizeof(val));
	}

	/* Set rate in 500 Kbps units */
	val = atoi(nvram_safe_get(strcat_r(prefix, "rate", tmp))) / 500000;
	if (wl_ioctl(name, WLC_SET_RATE, &val, sizeof(val))) {
		/* Try default rate (card may have changed) */
		val = 0;
		WL_IOCTL(name, WLC_SET_RATE, &val, sizeof(val));
		snprintf(buf, sizeof(buf), "%d", val);
		nvram_set(strcat_r(prefix, "rate", tmp), buf);
	}

	/* Set fragmentation threshold */
	val = atoi(nvram_safe_get(strcat_r(prefix, "frag", tmp)));
	WL_IOCTL(name, WLC_SET_FRAG, &val, sizeof(val));

	/* Set RTS threshold */
	val = atoi(nvram_safe_get(strcat_r(prefix, "rts", tmp)));
	WL_IOCTL(name, WLC_SET_RTS, &val, sizeof(val));

	/* Set DTIM period */
	val = atoi(nvram_safe_get(strcat_r(prefix, "dtim", tmp)));
	WL_IOCTL(name, WLC_SET_DTIMPRD, &val, sizeof(val));

	/* Set beacon period */
	val = atoi(nvram_safe_get(strcat_r(prefix, "bcn", tmp)));
	WL_IOCTL(name, WLC_SET_BCNPRD, &val, sizeof(val));

	/* Set lazy WDS mode */
	val = atoi(nvram_safe_get(strcat_r(prefix, "lazywds", tmp)));
	WL_IOCTL(name, WLC_SET_LAZYWDS, &val, sizeof(val));

	/* Set the WDS list */
	maclist = (struct maclist *) buf;
	maclist->count = 0;
	ea = maclist->ea;
	foreach(var, nvram_safe_get(strcat_r(prefix, "wds", tmp)), next) {
		if (ea->octet > &buf[sizeof(buf)])
			break;
		ether_atoe(var, ea->octet);
		maclist->count++;
		ea++;
	}
	WL_IOCTL(name, WLC_SET_WDSLIST, buf, sizeof(buf));

	/* Set framebursting mode */
	val = nvram_match(strcat_r(prefix, "frameburst", tmp), "on");
	WL_IOCTL(name, WLC_SET_FAKEFRAG, &val, sizeof(val));
	
	/* Bring the interface back up */
	WL_IOCTL(name, WLC_UP, NULL, 0);

	/* Set antenna */
	val = atoi(nvram_safe_get(strcat_r(prefix, "antdiv", tmp)));
	WL_IOCTL(name, WLC_SET_ANTDIV, &val, sizeof(val));

	/* Auto Channel Selection - when channel # is 0 in AP mode */
	/*
	* The following condition(s) must be met in order for 
	* Auto Channel Selection to work.
	*  - the I/F must be up (change radio channel requires it is up?)
	*  - the AP must not be associated (setting SSID to empty should 
	*    make sure it for us)
	*/
	if (ap) {
		if (!(val = atoi(nvram_safe_get(strcat_r(prefix, "channel", tmp))))) {
			/* select a channel */
			val = wlconf_auto_channel(name);
			/* switch to the selected channel */
			WL_IOCTL(name, WLC_SET_CHANNEL, &val, sizeof(val));
			/* set the auto channel scan timer in the driver when in auto mode */
			val = 15;	/* 15 minutes for now */
			WL_IOCTL(name, WLC_SET_CS_SCAN_TIMER, &val, sizeof(val));
		}
		else {
			/* reset the channel scan timer in the driver when not in auto mode */
			val = 0;
			WL_IOCTL(name, WLC_SET_CS_SCAN_TIMER, &val, sizeof(val));
		}
	}

	/* Set WEP keys */
	for (i = 1; i <= WLC_MAX_DEFAULT_KEYS; i++)
		wlconf_set_wep_key(name, prefix, i);

	/* Set WSEC */
	/*
	* Need to check errors (card may have changed) and change to
	* defaults since the new chip may not support the requested 
	* encryptions after the card has been changed.
	*/
	if (wlconf_set_wsec(name, prefix)) {
		/* change nvram only, code below will pass them on */
		wlconf_restore_var(prefix, "auth_mode");
		wlconf_restore_var(prefix, "auth");
		/* reset wep to default */
		wlconf_restore_var(prefix, "wep");
		wlconf_set_wsec(name, prefix);
	}

	/* Set WPA authentication mode - radius/wpa/psk */
	strcat_r(prefix, "auth_mode", tmp);
	if (nvram_match(tmp, "radius"))
		val = WPA_AUTH_DISABLED;
	else if (nvram_match(tmp, "wpa"))
		val = WPA_AUTH_UNSPECIFIED;
	else if (nvram_match(tmp, "psk"))
		val = WPA_AUTH_PSK;
	else /* if (nvram_match(tmp, "disabled")) */
		val = WPA_AUTH_DISABLED;
	WL_IOCTL(name, WLC_SET_WPA_AUTH, &val, sizeof(val));
	
	/* Set non-WPA authentication mode - open/shared */
	val = atoi(nvram_safe_get(strcat_r(prefix, "auth", tmp)));
	WL_IOCTL(name, WLC_SET_AUTH, &val, sizeof(val));

	/* Set WEP restrict if WEP is not disabled */
	val = nvram_invmatch(strcat_r(prefix, "wep", tmp), "off");
	WL_IOCTL(name, WLC_SET_WEP_RESTRICT, &val, sizeof(val));

	/* Set SSID/Join network */
	if (ap | sta | wet) {
		strcat_r(prefix, "ssid", tmp);
		ssid.SSID_len = strlen(nvram_safe_get(tmp));
		if (ssid.SSID_len > sizeof(ssid.SSID))
			ssid.SSID_len = sizeof(ssid.SSID);
		strncpy(ssid.SSID, nvram_safe_get(tmp), ssid.SSID_len);
	} else {
		/* A zero length SSID turns off the AP */
		ssid.SSID_len = 0;
		ssid.SSID[0] = '\0';
	}		
	WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid));
	
	return 0;
}

int
wlconf_down(char *name)
{
	int val, ret = 0;
	unsigned char buf[WLC_IOCTL_MAXLEN];
	struct maclist *maclist;
	wlc_ssid_t ssid;

	/* Check interface (fail silently for non-wl interfaces) */
	if ((ret = wl_probe(name)))
		return ret;

	/* Bring down the interface */
	WL_IOCTL(name, WLC_GET_UP, &val, sizeof(val));
	if (val) {
		/* Nuke SSID  */
		ssid.SSID_len = 0;
		ssid.SSID[0] = '\0';
		WL_IOCTL(name, WLC_SET_SSID, &ssid, sizeof(ssid));

		/* Bring the interface down */
		WL_IOCTL(name, WLC_DOWN, NULL, sizeof(val));
	}

	/* Nuke the WDS list */
	maclist = (struct maclist *) buf;
	maclist->count = 0;
	WL_IOCTL(name, WLC_SET_WDSLIST, buf, sizeof(buf));

	return 0;
}

int
main(int argc, char *argv[])
{
	int ret = -1;
	
	/* Check parameters and branch based on action */
	if (argc == 3 && !strcmp(argv[2], "up"))
		ret = wlconf(argv[1]);
	else if (argc == 3 && !strcmp(argv[2], "down"))
		ret = wlconf_down(argv[1]);
	else {
		fprintf(stderr, "Usage: wlconf <ifname> up|down\n");
		return -1;
	}
	/* Check result */
	if (ret) {
		fprintf(stderr, "wlconf: %s failed (%d)\n", argv[1], ret);
		return ret;
	}
	return 0;
}