1/*
2 * wlc - Broadcom Wireless Driver Control Utility
3 *
4 * Copyright (C) 2006 Felix Fietkau <nbd@nbd.name>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 */
16
17#include <sys/types.h>
18#include <sys/stat.h>
19#include <unistd.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <string.h>
23#include <fcntl.h>
24#include <glob.h>
25#include <ctype.h>
26
27#include <typedefs.h>
28#include <wlutils.h>
29#include <proto/802.11.h>
30
31#define VERSION "0.1"
32#define BUFSIZE 8192
33#define PTABLE_MAGIC 0xbadc0ded
34#define PTABLE_SLT1 1
35#define PTABLE_SLT2 2
36#define PTABLE_ACKW 3
37#define PTABLE_ADHM 4
38#define PTABLE_END 0xffffffff
39
40/*
41 * Copy each token in wordlist delimited by space into word
42 * Taken from Broadcom shutils.h
43 */
44#define foreach(word, wordlist, next) \
45	for (next = &wordlist[strspn(wordlist, " ")], \
46		 strncpy(word, next, sizeof(word)), \
47		 word[strcspn(word, " ")] = '\0', \
48		 word[sizeof(word) - 1] = '\0', \
49		 next = strchr(next, ' '); \
50		 strlen(word); \
51		 next = next ? &next[strspn(next, " ")] : "", \
52		 strncpy(word, next, sizeof(word)), \
53		 word[strcspn(word, " ")] = '\0', \
54		 word[sizeof(word) - 1] = '\0', \
55		 next = strchr(next, ' '))
56
57static char wlbuf[8192];
58static char interface[16] = "wl0";
59static unsigned long kmem_offset = 0;
60static int vif = 0, debug = 1, fromstdin = 0;
61
62typedef enum {
63	NONE =   0x00,
64
65	/* types */
66	PARAM_TYPE =    0x00f,
67	INT =    0x001,
68	STRING = 0x002,
69	MAC =    0x003,
70
71	/* options */
72	PARAM_OPTIONS = 0x0f0,
73	NOARG =  0x010,
74
75	/* modes */
76	PARAM_MODE =    0xf00,
77	GET =    0x100,
78	SET =    0x200,
79} wlc_param;
80
81struct wlc_call {
82	const char *name;
83	wlc_param param;
84	int (*handler)(wlc_param param, void *data, void *value);
85	union {
86		int num;
87		char *str;
88		void *ptr;
89	} data;
90	const char *desc;
91};
92
93/* can't use the system include because of the stupid broadcom header files */
94extern struct ether_addr *ether_aton(const char *asc);
95static inline int my_ether_ntoa(unsigned char *ea, char *buf)
96{
97	return sprintf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
98		ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
99}
100
101static int wlc_ioctl(wlc_param param, void *data, void *value)
102{
103	unsigned int *var = ((unsigned int *) data);
104	unsigned int ioc = *var;
105
106	if (param & NOARG) {
107		return wl_ioctl(interface, ioc, NULL, 0);
108	}
109	switch(param & PARAM_TYPE) {
110		case MAC:
111			return wl_ioctl(interface, ((param & SET) ? (ioc) : (ioc >> 16)) & 0xffff, value, 6);
112		case INT:
113			return wl_ioctl(interface, ((param & SET) ? (ioc) : (ioc >> 16)) & 0xffff, value, sizeof(int));
114		case STRING:
115			return wl_ioctl(interface, ((param & SET) ? (ioc) : (ioc >> 16)) & 0xffff, value, BUFSIZE);
116	}
117	return 0;
118}
119
120static int wlc_iovar(wlc_param param, void *data, void *value)
121{
122	int *val = (int *) value;
123	char *iov = *((char **) data);
124	int ret = 0;
125
126	if (param & SET) {
127		switch(param & PARAM_TYPE) {
128			case INT:
129				ret = wl_iovar_setint(interface, iov, *val);
130				break;
131			case MAC:
132				ret = wl_iovar_set(interface, iov, value, 6);
133				break;
134		}
135	}
136	if (param & GET) {
137		switch(param & PARAM_TYPE) {
138			case INT:
139				ret = wl_iovar_get(interface, iov, val, sizeof(int));
140				break;
141			case MAC:
142				ret = wl_iovar_get(interface, iov, value, 6);
143				break;
144		}
145	}
146
147	return ret;
148}
149
150static int wlc_bssiovar(wlc_param param, void *data, void *value)
151{
152	int *val = (int *) value;
153	char *iov = *((char **) data);
154	int ret = 0;
155
156	if (param & SET) {
157		switch(param & PARAM_TYPE) {
158			case INT:
159				ret = wl_bssiovar_setint(interface, iov, vif, *val);
160		}
161	}
162	if (param & GET) {
163		switch(param & PARAM_TYPE) {
164			case INT:
165				ret = wl_bssiovar_get(interface, iov, vif, val, sizeof(int));
166		}
167	}
168
169	return ret;
170}
171
172static int wlc_vif_enabled(wlc_param param, void *data, void *value)
173{
174	int *val = (int *) value;
175	int buf[3];
176	int ret = 0;
177
178	sprintf((char *) buf, "bss");
179	buf[1] = vif;
180	if (param & SET) {
181		buf[2] = (*val ? 1 : 0);
182		ret = wl_ioctl(interface, WLC_SET_VAR, buf, sizeof(buf));
183	} else if (param & GET) {
184		ret = wl_ioctl(interface, WLC_GET_VAR, buf, sizeof(buf));
185		*val = buf[0];
186	}
187
188	return ret;
189}
190
191static int wlc_ssid(wlc_param param, void *data, void *value)
192{
193	int ret = -1, ret2 = -1;
194	char *dest = (char *) value;
195	wlc_ssid_t ssid;
196
197	if ((param & PARAM_MODE) == GET) {
198		ret = wl_bssiovar_get(interface, "ssid", vif, &ssid, sizeof(ssid));
199
200		if (ret)
201			/* if we can't get the ssid through the bssiovar, try WLC_GET_SSID */
202			ret = wl_ioctl(interface, WLC_GET_SSID, &ssid, sizeof(ssid));
203
204		if (!ret) {
205			memcpy(dest, ssid.SSID, ssid.SSID_len);
206			dest[ssid.SSID_len] = 0;
207		}
208	} else if ((param & PARAM_MODE) == SET) {
209		strncpy(ssid.SSID, value, 32);
210		ssid.SSID_len = strlen(value);
211
212		if (ssid.SSID_len > 32)
213			ssid.SSID_len = 32;
214
215		if (vif == 0) {
216			/* for the main interface, also try the WLC_SET_SSID call */
217			ret2 = wl_ioctl(interface, WLC_SET_SSID, &ssid, sizeof(ssid));
218		}
219
220		ret = wl_bssiovar_set(interface, "ssid", vif, &ssid, sizeof(ssid));
221		ret = (!ret2 ? 0 : ret);
222	}
223
224	return ret;
225}
226
227static int wlc_int(wlc_param param, void *data, void *value)
228{
229	int *var = *((int **) data);
230	int *val = (int *) value;
231
232	if ((param & PARAM_MODE) == SET) {
233		*var = *val;
234	} else if ((param & PARAM_MODE) == GET) {
235		*val = *var;
236	}
237
238	return 0;
239}
240
241static int wlc_flag(wlc_param param, void *data, void *value)
242{
243	int *var = *((int **) data);
244
245	*var = 1;
246
247	return 0;
248}
249
250static int wlc_string(wlc_param param, void *data, void *value)
251{
252	char *var = *((char **) data);
253
254	if ((param & PARAM_MODE) == GET) {
255		strcpy(value, var);
256	}
257
258	return 0;
259}
260
261static int wlc_afterburner(wlc_param param, void *data, void *value)
262{
263	int *val = (int *) value;
264	int ret = 0;
265
266	if ((param & PARAM_MODE) == GET) {
267		ret = wl_iovar_get(interface, "afterburner", val, sizeof(int));
268	} else {
269		wl_iovar_setint(interface, "wlfeatureflag", (*val ? 3 : 0));
270		ret = wl_iovar_setint(interface, "afterburner", (*val ? 1 : 0));
271		wl_iovar_setint(interface, "afterburner_override", *val);
272	}
273
274	return ret;
275}
276
277static int wlc_maclist(wlc_param param, void *data, void *value)
278{
279	unsigned int *var = ((unsigned int *) data);
280	unsigned int ioc = *var;
281	int limit = (sizeof(wlbuf) - 4) / sizeof(struct ether_addr);
282	struct maclist *list = (struct maclist *) wlbuf;
283	char *str = (char *) value;
284	char astr[30], *p;
285	struct ether_addr *addr;
286	int isset = 0;
287	int ret;
288
289	if ((param & PARAM_MODE) == GET) {
290		list->count = limit;
291		ret = wl_ioctl(interface, (ioc >> 16) & 0xffff, wlbuf, sizeof(wlbuf));
292
293		if (!ret)
294			while (list->count) {
295				str += sprintf(str, "%s", ((((char *) value) == str) ? "" : " "));
296				str += my_ether_ntoa((unsigned char *) &list->ea[list->count-- - 1], str);
297			}
298
299		return ret;
300	} else {
301		while (*str && isspace(*str))
302			*str++;
303
304		if (*str == '+') {
305			str++;
306
307			list->count = limit;
308			if (wl_ioctl(interface, (ioc >> 16) & 0xffff, wlbuf, sizeof(wlbuf)) == 0)
309				isset = 1;
310
311			while (*str && isspace(*str))
312				str++;
313		}
314
315		if (!isset)
316			memset(wlbuf, 0, sizeof(wlbuf));
317
318		foreach(astr, str, p) {
319			if (list->count >= limit)
320				break;
321
322			if ((addr = ether_aton(astr)) != NULL)
323				memcpy(&list->ea[list->count++], addr, sizeof(struct ether_addr));
324		}
325
326		return wl_ioctl(interface, ioc & 0xffff, wlbuf, sizeof(wlbuf));
327	}
328}
329
330static int wlc_radio(wlc_param param, void *data, void *value)
331{
332	int *val = (int *) value;
333	int ret;
334
335	if ((param & PARAM_MODE) == GET) {
336		ret = wl_ioctl(interface, WLC_GET_RADIO, val, sizeof(int));
337		*val = ((*val & 1) ? 0 : 1);
338	} else {
339		*val = (1 << 16) | (*val ? 0 : 1);
340		ret = wl_ioctl(interface, WLC_SET_RADIO, val, sizeof(int));
341	}
342
343	return ret;
344}
345
346static int wlc_wsec_key(wlc_param param, void *null, void *value)
347{
348	wl_wsec_key_t wsec_key;
349	unsigned char *index = value;
350	unsigned char *key;
351	unsigned char *data;
352	unsigned char hex[3];
353
354	if ((param & PARAM_MODE) != SET)
355		return 0;
356
357	memset(&wsec_key, 0, sizeof(wsec_key));
358	if (index[0] == '=') {
359		wsec_key.flags = WL_PRIMARY_KEY;
360		index++;
361	}
362
363	if ((index[0] < '1') || (index[0] > '4') || (index[1] != ','))
364		return -1;
365
366	key = index + 2;
367	if (strncmp(key, "d:", 2) == 0) { /* delete key */
368	} else if (strncmp(key, "s:", 2) == 0) { /* ascii key */
369		key += 2;
370		wsec_key.len = strlen(key);
371
372		if ((wsec_key.len != 5) && (wsec_key.len != 13))
373			return -1;
374
375		strcpy(wsec_key.data, key);
376	} else { /* hex key */
377		wsec_key.len = strlen(key);
378		if ((wsec_key.len != 10) && (wsec_key.len != 26))
379			return -1;
380
381		wsec_key.len /= 2;
382		data = wsec_key.data;
383		hex[2] = 0;
384		do {
385			hex[0] = *(key++);
386			hex[1] = *(key++);
387			*(data++) = (unsigned char) strtoul(hex, NULL, 16);
388		} while (*key != 0);
389	}
390
391	return wl_bssiovar_set(interface, "wsec_key", vif, &wsec_key, sizeof(wsec_key));
392}
393
394static int wlc_cap(wlc_param param, void *data, void *value)
395{
396	char *iov = *((char **) data);
397
398	if (param & GET)
399		return wl_iovar_get(interface, iov, value, BUFSIZE);
400
401	return -1;
402}
403
404static int wlc_bssmax(wlc_param param, void *data, void *value)
405{
406	int *val = (int *) value;
407	char *iov = *((char **) data);
408	int ret = -1;
409
410	if (param & GET) {
411		ret = wl_iovar_get(interface, iov, wlbuf, BUFSIZE);
412		if (!ret) {
413			if (strstr(wlbuf, "mbss4"))
414				*val = 4;
415			else if (strstr(wlbuf, "mbss16"))
416				*val = 16;
417			else
418				*val = 1;
419		}
420	}
421
422	return ret;
423}
424
425static inline int cw2ecw(int cw)
426{
427	int i;
428	for (cw++, i = 0; cw; i++) cw >>=1;
429	return i - 1;
430}
431
432static int wlc_wme_ac(wlc_param param, void *data, void *value)
433{
434	char *type = *((char **) data);
435	char *settings = (char *) value;
436	char cmd[100], *p, *val;
437	edcf_acparam_t params[AC_COUNT];
438	int ret;
439	int intval;
440	int cur = -1;
441	char *buf = wlbuf;
442
443	if ((param & PARAM_MODE) != SET)
444		return -1;
445
446	memset(params, 0, sizeof(params));
447	ret = wl_iovar_get(interface, type, params, sizeof(params));
448	memset(buf, 0, BUFSIZE);
449	strcpy(buf, type);
450	buf += strlen(buf) + 1;
451
452	foreach(cmd, settings, p) {
453		val = strchr(cmd, '=');
454		if (val == NULL) {
455			if (strcmp(cmd, "be") == 0)
456				cur = AC_BE;
457			else if (strcmp(cmd, "bk") == 0)
458				cur = AC_BK;
459			else if (strcmp(cmd, "vi") == 0)
460				cur = AC_VI;
461			else if (strcmp(cmd, "vo") == 0)
462				cur = AC_VO;
463			else
464				return -1;
465
466			/* just in case */
467			params[cur].ACI = (params[cur].ACI & (0x3 << 5)) | (cur << 5);
468		} else {
469			*(val++) = 0;
470
471			intval = strtoul(val, NULL, 10);
472			if (strcmp(cmd, "cwmin") == 0)
473				params[cur].ECW = (params[cur].ECW & ~(0xf)) | cw2ecw(intval);
474			else if (strcmp(cmd, "ecwmin") == 0)
475				params[cur].ECW = (params[cur].ECW & ~(0xf)) | (intval & 0xf);
476			else if (strcmp(cmd, "cwmax") == 0)
477				params[cur].ECW = (params[cur].ECW & ~(0xf << 4)) | (cw2ecw(intval) << 4);
478			else if (strcmp(cmd, "ecwmax") == 0)
479				params[cur].ECW = (params[cur].ECW & ~(0xf << 4)) | ((intval & 0xf) << 4);
480			else if (strcmp(cmd, "aifsn") == 0)
481				params[cur].ACI = (params[cur].ACI & ~(0xf)) | (intval & 0xf);
482			else if (strcmp(cmd, "txop") == 0)
483				params[cur].TXOP = intval >> 5;
484			else if (strcmp(cmd, "force") == 0)
485				params[cur].ACI = (params[cur].ACI & ~(1 << 4)) | ((intval) ? (1 << 4) : 0);
486			else return -1;
487
488			memcpy(buf, &params[cur], sizeof(edcf_acparam_t));
489			wl_ioctl(interface, WLC_SET_VAR, wlbuf, BUFSIZE);
490		}
491	}
492	return ret;
493}
494
495static int wlc_ifname(wlc_param param, void *data, void *value)
496{
497	char *val = (char *) value;
498	int ret = 0;
499
500	if (param & SET) {
501		if (strlen(val) < 16)
502			strcpy(interface, val);
503		else ret = -1;
504	}
505	if (param & GET) {
506		strcpy(val, interface);
507	}
508
509	return ret;
510}
511
512static int wlc_wdsmac(wlc_param param, void *data, void *value)
513{
514	unsigned char mac[6];
515	int ret = 0;
516
517	ret = wl_ioctl(interface, WLC_WDS_GET_REMOTE_HWADDR, &mac, 6);
518	if (ret == 0)
519		my_ether_ntoa(mac, value);
520
521	return ret;
522}
523
524static int wlc_pmk(wlc_param param, void *data, void *value)
525{
526	int ret = -1;
527	char *str = (char *) value;
528	wsec_pmk_t pmk;
529
530	/* driver doesn't support GET */
531
532	if ((param & PARAM_MODE) == SET) {
533		strncpy(pmk.key, str, WSEC_MAX_PSK_LEN);
534		pmk.key_len = strlen(str);
535
536		if (pmk.key_len > WSEC_MAX_PSK_LEN)
537			pmk.key_len = WSEC_MAX_PSK_LEN;
538
539		pmk.flags = WSEC_PASSPHRASE;
540
541		ret = wl_ioctl(interface, WLC_SET_WSEC_PMK, &pmk, sizeof(pmk));
542	}
543
544	return ret;
545}
546
547static const struct wlc_call wlc_calls[] = {
548	{
549		.name = "version",
550		.param = STRING|NOARG,
551		.handler = wlc_string,
552		.data.str = VERSION,
553		.desc = "Version of this program"
554	},
555	{
556		.name = "debug",
557		.param = INT,
558		.handler = wlc_int,
559		.data.ptr = &debug,
560		.desc = "wlc debug level"
561	},
562	{
563		.name = "stdin",
564		.param = NOARG,
565		.handler = wlc_flag,
566		.data.ptr = &fromstdin,
567		.desc = "Accept input from stdin"
568	},
569	{
570		.name = "ifname",
571		.param = STRING,
572		.handler = wlc_ifname,
573		.desc = "interface to send commands to"
574	},
575	{
576		.name = "up",
577		.param = NOARG,
578		.handler = wlc_ioctl,
579		.data.num = WLC_UP,
580		.desc = "Bring the interface up"
581	},
582	{
583		.name = "down",
584		.param = NOARG,
585		.handler = wlc_ioctl,
586		.data.num = WLC_DOWN,
587		.desc = "Bring the interface down"
588	},
589	{
590		.name = "radio",
591		.param = INT,
592		.handler = wlc_radio,
593		.desc = "Radio enabled flag"
594	},
595	{
596		.name = "ap",
597		.param = INT,
598		.handler = wlc_ioctl,
599		.data.num = ((WLC_GET_AP << 16) | WLC_SET_AP),
600		.desc = "Access Point mode"
601	},
602	{
603		.name = "mssid",
604		.param = INT,
605		.handler = wlc_iovar,
606		.data.str = "mbss",
607		.desc = "Multi-ssid mode"
608	},
609	{
610		.name = "apsta",
611		.param = INT,
612		.handler = wlc_iovar,
613		.data.str = "apsta",
614		.desc = "AP+STA mode"
615	},
616	{
617		.name = "infra",
618		.param = INT,
619		.handler = wlc_ioctl,
620		.data.num = ((WLC_GET_INFRA << 16) | WLC_SET_INFRA),
621		.desc = "Infrastructure mode"
622	},
623	{
624		.name = "wet",
625		.param = INT,
626		.handler = wlc_ioctl,
627		.data.num = ((WLC_GET_WET << 16) | WLC_SET_WET),
628		.desc = "Wireless repeater mode",
629	},
630	{
631		.name = "statimeout",
632		.param = INT,
633		.handler = wlc_iovar,
634		.data.str = "sta_retry_time",
635		.desc = "STA connection timeout"
636	},
637	{
638		.name = "country",
639		.param = STRING,
640		.handler = wlc_ioctl,
641		.data.num = ((WLC_GET_COUNTRY << 16) | WLC_SET_COUNTRY),
642		.desc = "Country code"
643	},
644	{
645		.name = "channel",
646		.param = INT,
647		.handler = wlc_ioctl,
648		.data.num = ((WLC_GET_CHANNEL << 16) | WLC_SET_CHANNEL),
649		.desc = "Channel",
650	},
651	{
652		.name = "vlan_mode",
653		.param = INT,
654		.handler = wlc_bssiovar,
655		.data.str = "vlan_mode",
656		.desc = "Parse 802.1Q tags",
657	},
658	{
659		.name = "vif",
660		.param = INT,
661		.handler = wlc_int,
662		.data.ptr = &vif,
663		.desc = "Current vif index"
664	},
665	{
666		.name = "enabled",
667		.param = INT,
668		.handler = wlc_vif_enabled,
669		.desc = "vif enabled flag"
670	},
671	{
672		.name = "ssid",
673		.param = STRING,
674		.handler = wlc_ssid,
675		.desc = "Interface ESSID"
676	},
677	{
678		.name = "closed",
679		.param = INT,
680		.handler = wlc_bssiovar,
681		.data.str = "closednet",
682		.desc = "Hidden ESSID flag"
683	},
684	{
685		.name = "wsec",
686		.param = INT,
687		.handler = wlc_bssiovar,
688		.data.str = "wsec",
689		.desc = "Security mode flags"
690	},
691	{
692		.name = "wepkey",
693		.param = STRING,
694		.handler = wlc_wsec_key,
695		.desc = "Set/Remove WEP keys"
696	},
697	{
698		.name = "wepauth",
699		.param = INT,
700		.handler = wlc_ioctl,
701		.data.num = ((WLC_GET_AUTH << 16) | WLC_SET_AUTH),
702		.desc = "WEP authentication type. 0 = OpenSystem, 1 = SharedKey"
703	},
704	{
705		.name = "wsec_restrict",
706		.param = INT,
707		.handler = wlc_bssiovar,
708		.data.str = "wsec_restrict",
709		.desc = "Drop unencrypted traffic"
710	},
711	{
712		.name = "eap_restrict",
713		.param = INT,
714		.handler = wlc_bssiovar,
715		.data.str = "eap_restrict",
716		.desc = "Only allow 802.1X traffic until 802.1X authorized"
717	},
718	{
719		.name = "wpa_auth",
720		.param = INT,
721		.handler = wlc_bssiovar,
722		.data.str = "wpa_auth",
723		.desc = "WPA authentication modes"
724	},
725	{
726		.name = "ap_isolate",
727		.param = INT,
728		.handler = wlc_bssiovar,
729		.data.str = "ap_isolate",
730		.desc = "Isolate connected clients"
731	},
732	{
733		.name = "supplicant",
734		.param = INT,
735		.handler = wlc_iovar,
736		.data.str = "sup_wpa",
737		.desc = "Built-in WPA supplicant"
738	},
739	{
740		.name = "passphrase",
741		.param = STRING,
742		.handler = wlc_pmk,
743		.desc = "Passphrase for built-in WPA supplicant",
744	},
745	{
746		.name = "maxassoc",
747		.param = INT,
748		.handler = wlc_iovar,
749		.data.str = "maxassoc",
750		.desc = "Max. number of associated clients",
751	},
752	{
753		.name = "wme",
754		.param = INT,
755		.handler = wlc_iovar,
756		.data.str = "wme",
757		.desc = "WME enabled"
758	},
759	{
760		.name = "wme_ac_ap",
761		.param = STRING,
762		.handler = wlc_wme_ac,
763		.data.str = "wme_ac_ap",
764		.desc = "Set WME AC options for AP mode",
765	},
766	{
767		.name = "wme_ac_sta",
768		.param = STRING,
769		.handler = wlc_wme_ac,
770		.data.str = "wme_ac_sta",
771		.desc = "Set WME AC options for STA mode",
772	},
773	{
774		.name = "wme_noack",
775		.param = INT,
776		.handler = wlc_iovar,
777		.data.str = "wme_noack",
778		.desc = "WME ACK disable request",
779	},
780	{
781		.name = "802.11d",
782		.param = INT,
783		.handler = wlc_ioctl,
784		.data.num = ((WLC_GET_REGULATORY << 16) | WLC_SET_REGULATORY),
785		.desc = "Enable/disable 802.11d regulatory management",
786	},
787	{
788		.name = "802.11h",
789		.param = INT,
790		.handler = wlc_ioctl,
791		.data.num = ((WLC_GET_SPECT_MANAGMENT << 16) | WLC_SET_SPECT_MANAGMENT),
792		.desc = "Enable/disable 802.11h spectrum management",
793	},
794	{
795		.name = "fragthresh",
796		.param = INT,
797		.handler = wlc_iovar,
798		.data.str = "fragthresh",
799		.desc = "Fragmentation threshold",
800	},
801	{
802		.name = "rtsthresh",
803		.param = INT,
804		.handler = wlc_iovar,
805		.data.str = "rtsthresh",
806		.desc = "RTS threshold"
807	},
808	{
809		.name = "slottime",
810		.param = INT,
811		.handler = wlc_iovar,
812		.data.str = "acktiming",
813		.desc = "Slot time"
814	},
815	{
816		.name = "rxant",
817		.param = INT,
818		.handler = wlc_ioctl,
819		.data.num = ((WLC_GET_ANTDIV << 16) | WLC_SET_ANTDIV),
820		.desc = "Rx antenna selection"
821	},
822	{
823		.name = "txant",
824		.param = INT,
825		.handler = wlc_ioctl,
826		.data.num = ((WLC_GET_TXANT << 16) | WLC_SET_TXANT),
827		.desc = "Tx antenna selection"
828	},
829	{
830		.name = "dtim",
831		.param = INT,
832		.handler = wlc_ioctl,
833		.data.num = ((WLC_GET_DTIMPRD << 16) | WLC_SET_DTIMPRD),
834		.desc = "DTIM period",
835	},
836	{
837		.name = "bcn",
838		.param = INT,
839		.handler = wlc_ioctl,
840		.data.num = ((WLC_GET_BCNPRD << 16) | WLC_SET_BCNPRD),
841		.desc = "Beacon interval"
842	},
843	{
844		.name = "frameburst",
845		.param = INT,
846		.handler = wlc_ioctl,
847		.data.num = ((WLC_GET_FAKEFRAG << 16) | WLC_SET_FAKEFRAG),
848		.desc = "Framebursting"
849	},
850	{
851		.name = "monitor",
852		.param = INT,
853		.handler = wlc_ioctl,
854		.data.num = ((WLC_GET_MONITOR << 16) | WLC_SET_MONITOR),
855		.desc = "Monitor mode"
856	},
857	{
858		.name = "passive_scan",
859		.param = INT,
860		.handler = wlc_ioctl,
861		.data.num = ((WLC_GET_PASSIVE_SCAN << 16) | WLC_SET_PASSIVE_SCAN),
862		.desc = "Passive scan mode"
863	},
864	{
865		.name = "macfilter",
866		.param = INT,
867		.handler = wlc_ioctl,
868		.data.num = ((WLC_GET_MACMODE << 16) | WLC_SET_MACMODE),
869		.desc = "MAC filter mode (0:disabled, 1:deny, 2:allow)"
870	},
871	{
872		.name = "maclist",
873		.param = STRING,
874		.data.num = ((WLC_GET_MACLIST << 16) | WLC_SET_MACLIST),
875		.handler = wlc_maclist,
876		.desc = "MAC filter list"
877	},
878	{
879		.name = "autowds",
880		.param = INT,
881		.handler = wlc_ioctl,
882		.data.num = ((WLC_GET_LAZYWDS << 16) | WLC_SET_LAZYWDS),
883		.desc = "Automatic WDS"
884	},
885	{
886		.name = "wds",
887		.param = STRING,
888		.data.num = ((WLC_GET_WDSLIST << 16) | WLC_SET_WDSLIST),
889		.handler = wlc_maclist,
890		.desc = "WDS connection list"
891	},
892	{
893		.name = "wdstimeout",
894		.param = INT,
895		.handler = wlc_iovar,
896		.data.str = "wdstimeout",
897		.desc = "WDS link detection timeout"
898	},
899	{
900		.name = "wdsmac",
901		.param = STRING|NOARG,
902		.handler = wlc_wdsmac,
903		.desc = "MAC of the remote WDS endpoint (only with wds0.* interfaces)"
904	},
905	{
906		.name = "afterburner",
907		.param = INT,
908		.handler = wlc_afterburner,
909		.desc = "Broadcom Afterburner"
910	},
911	{
912		.name = "ibss_merge",
913		.param = INT,
914		.handler = wlc_iovar,
915		.data.str = "ibss_coalesce_allowed",
916		.desc = "Allow IBSS merges"
917	},
918	{
919		.name = "bssid",
920		.param = MAC,
921		.handler = wlc_ioctl,
922		.data.num = ((WLC_GET_BSSID << 16) | WLC_SET_BSSID),
923		.desc = "BSSID"
924	},
925	{
926		.name = "cur_etheraddr",
927		.param = MAC,
928		.handler = wlc_iovar,
929		.data.str = "cur_etheraddr",
930		.desc = "Current MAC Address"
931	},
932	{
933		.name = "default_bssid",
934		.param = MAC,
935		.handler = wlc_iovar,
936		.data.str = "perm_etheraddr",
937		.desc = "Default BSSID (read-only)"
938	},
939	{
940		.name = "assoclist",
941		.param = STRING,
942		.data.num = (WLC_GET_ASSOCLIST << 16),
943		.handler = wlc_maclist,
944		.desc = "MACs of associated stations"
945	},
946	{
947		.name = "gmode",
948		.param = INT,
949		.data.num = ((WLC_GET_GMODE << 16) | WLC_SET_GMODE),
950		.handler = wlc_ioctl,
951		.desc = "G Mode"
952	},
953	{
954		.name = "phytype",
955		.param = INT,
956		.data.num = (WLC_GET_PHYTYPE << 16),
957		.handler = wlc_ioctl,
958		.desc = "PHY Type (read-only)"
959	},
960	{
961		.name = "nmode",
962		.param = INT,
963		.handler = wlc_iovar,
964		.data.str = "nmode",
965		.desc = "N Mode"
966	},
967	{
968		.name = "nreqd",
969		.param = INT,
970		.handler = wlc_iovar,
971		.data.str = "nreqd",
972		.desc = "N Mode required"
973	},
974	{
975		.name = "chanspec",
976		.param = INT,
977		.handler = wlc_iovar,
978		.data.str = "chanspec",
979		.desc = "Channel Spec (See bcmwifi.h)"
980	},
981	{
982		.name = "band",
983		.param = INT,
984		.data.num = ((WLC_GET_BAND << 16) | WLC_SET_BAND),
985		.handler = wlc_ioctl,
986		.desc = "Band (0=auto, 1=5Ghz, 2=2.4GHz)"
987	},
988	{
989		.name = "cap",
990		.param = STRING|NOARG,
991		.handler = wlc_cap,
992		.data.str = "cap",
993		.desc = "Capabilities"
994	},
995	{
996		.name = "bssmax",
997		.param = INT|NOARG,
998		.handler = wlc_bssmax,
999		.data.str = "cap",
1000		.desc = "Number of VIF's supported"
1001	},
1002	{
1003		.name = "leddc",
1004		.param = INT,
1005		.handler = wlc_iovar,
1006		.data.str = "leddc",
1007		.desc = "LED Duty Cycle"
1008	},
1009
1010};
1011#define wlc_calls_size (sizeof(wlc_calls) / sizeof(struct wlc_call))
1012
1013static void usage(char *cmd)
1014{
1015	int i;
1016	fprintf(stderr, "Usage: %s <command> [<argument> ...]\n"
1017					"\n"
1018					"Available commands:\n", cmd);
1019	for (i = 0; i < wlc_calls_size; i++) {
1020		fprintf(stderr, "\t%-16s\t%s\n", wlc_calls[i].name ?: "", wlc_calls[i].desc ?: "");
1021	}
1022	fprintf(stderr, "\n");
1023	exit(1);
1024}
1025
1026static int do_command(const struct wlc_call *cmd, char *arg)
1027{
1028	static char buf[BUFSIZE];
1029	int set;
1030	int ret = 0;
1031	char *format, *end;
1032	int intval;
1033	void *ptr = (void *) buf;
1034
1035	if (debug >= 10) {
1036		fprintf(stderr, "do_command %-16s\t'%s'\n", cmd->name, arg);
1037	}
1038
1039	if ((arg == NULL) && ((cmd->param & PARAM_TYPE) != NONE)) {
1040		set = 0;
1041		ret = cmd->handler(cmd->param | GET, (void *) &cmd->data, (void *) buf);
1042		if (ret == 0) {
1043			switch(cmd->param & PARAM_TYPE) {
1044				case INT:
1045					intval = *((int *) buf);
1046
1047					if (intval > 65535)
1048						format = "0x%08x\n";
1049					else if (intval > 255)
1050						format = "0x%04x\n";
1051					else
1052						format = "%d\n";
1053
1054					fprintf(stdout, format, intval);
1055					break;
1056				case STRING:
1057					fprintf(stdout, "%s\n", buf);
1058					break;
1059				case MAC:
1060					my_ether_ntoa(buf, buf + 6);
1061					fprintf(stdout, "%s\n", buf + 6);
1062					break;
1063			}
1064		}
1065	} else { /* SET */
1066		set = 1;
1067		switch(cmd->param & PARAM_TYPE) {
1068			case INT:
1069				intval = strtoul(arg, &end, 0);
1070				if (end && !(*end)) {
1071					memcpy(buf, &intval, sizeof(intval));
1072				} else {
1073					fprintf(stderr, "%s: Invalid argument\n", cmd->name);
1074					return -1;
1075				}
1076				break;
1077			case STRING:
1078				strncpy(buf, arg, BUFSIZE);
1079				buf[BUFSIZE - 1] = 0;
1080				break;
1081			case MAC:
1082				ptr = ether_aton(arg);
1083				if (!ptr) {
1084					fprintf(stderr, "%s: Invalid mac address '%s'\n", cmd->name, arg);
1085					return -1;
1086				}
1087				break;
1088		}
1089
1090		ret = cmd->handler(cmd->param | SET, (void *) &cmd->data, ptr);
1091	}
1092
1093	if ((debug > 0) && (ret != 0))
1094		fprintf(stderr, "Command '%s %s' failed: %d\n", (set == 1 ? "set" : "get"), cmd->name, ret);
1095
1096	return ret;
1097}
1098
1099static struct wlc_call *find_cmd(char *name)
1100{
1101	int found = 0, i = 0;
1102
1103	while (!found && (i < wlc_calls_size)) {
1104		if (strcmp(name, wlc_calls[i].name) == 0)
1105			found = 1;
1106		else
1107			i++;
1108	}
1109
1110	return (struct wlc_call *) (found ? &wlc_calls[i] : NULL);
1111}
1112
1113int main(int argc, char **argv)
1114{
1115	static char buf[BUFSIZE];
1116	char *s, *s2;
1117	char *cmd = argv[0];
1118	struct wlc_call *call;
1119	int ret = 0;
1120
1121	if (argc < 2)
1122		usage(argv[0]);
1123
1124	for(interface[2] = '0'; (interface[2] < '3') && (wl_probe(interface) != 0); interface[2]++);
1125	if (interface[2] == '3') {
1126		fprintf(stderr, "No Broadcom wl interface found!\n");
1127		return -1;
1128	}
1129
1130	argv++;
1131	argc--;
1132	while ((argc > 0) && (argv[0] != NULL)) {
1133		if ((call = find_cmd(argv[0])) == NULL) {
1134			fprintf(stderr, "Invalid command: %s\n\n", argv[0]);
1135			usage(cmd);
1136		}
1137		if ((argc > 1) && (!(call->param & NOARG))) {
1138			ret = do_command(call, argv[1]);
1139			argv += 2;
1140			argc -= 2;
1141		} else {
1142			ret = do_command(call, NULL);
1143			argv++;
1144			argc--;
1145		}
1146	}
1147
1148	while (fromstdin && !feof(stdin)) {
1149		*buf = 0;
1150		fgets(buf, BUFSIZE - 1, stdin);
1151
1152		if (*buf == 0)
1153			continue;
1154
1155		if ((s = strchr(buf, '\r')) != NULL)
1156			*s = 0;
1157		if ((s = strchr(buf, '\n')) != NULL)
1158			*s = 0;
1159
1160		s = buf;
1161		while (isspace(*s))
1162			s++;
1163
1164		if (!*s)
1165			continue;
1166
1167		if ((s2 = strchr(s, ' ')) != NULL)
1168			*(s2++) = 0;
1169
1170		while (s2 && isspace(*s2))
1171			s2++;
1172
1173		if ((call = find_cmd(s)) == NULL) {
1174			fprintf(stderr, "Invalid command: %s\n", s);
1175			ret = -1;
1176		} else
1177			ret = do_command(call, ((call->param & NOARG) ? NULL : s2));
1178	}
1179
1180	return ret;
1181}
1182