1/*
2 * Common code for dhd utility, hacked from wl utility
3 *
4 * Copyright (C) 1999-2010, Broadcom Corporation
5 *
6 *      Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 *      As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module.  An independent module is a module which is not
17 * derived from this software.  The special exception does not apply to any
18 * modifications of the software.
19 *
20 *      Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: dhdu.c,v 1.52.2.10.2.6.2.14 2010/01/19 07:24:15 Exp $
25 */
26
27/* For backwards compatibility, the absense of the define 'BWL_NO_FILESYSTEM_SUPPORT'
28 * implies that a filesystem is supported.
29 */
30#if !defined(BWL_NO_FILESYSTEM_SUPPORT)
31#define BWL_FILESYSTEM_SUPPORT
32#endif
33
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <string.h>
38#include <ctype.h>
39#include <assert.h>
40
41#include <typedefs.h>
42#include <epivers.h>
43#include <proto/ethernet.h>
44#include <dhdioctl.h>
45#include <sdiovar.h>
46#include <bcmutils.h>
47#include <bcmendian.h>
48#include "dhdu.h"
49
50
51#include "miniopt.h"
52
53#include <errno.h>
54
55#include <trxhdr.h>
56
57#define stricmp strcasecmp
58#define strnicmp strncasecmp
59
60
61static cmd_func_t dhd_var_void;
62static cmd_func_t dhd_varint, dhd_varstr;
63static cmd_func_t dhd_var_getandprintstr, dhd_var_getint, dhd_var_get;
64static cmd_func_t dhd_var_setint;
65
66static cmd_func_t dhd_version, dhd_list, dhd_msglevel;
67
68#ifdef SDTEST
69static cmd_func_t dhd_pktgen;
70#endif
71static cmd_func_t dhd_sprom;
72static cmd_func_t dhd_sdreg;
73static cmd_func_t dhd_sd_msglevel, dhd_sd_blocksize, dhd_sd_mode, dhd_sd_reg;
74static cmd_func_t dhd_dma_mode;
75static cmd_func_t dhd_membytes, dhd_download, dhd_upload, dhd_vars, dhd_idleclock, dhd_idletime;
76static cmd_func_t dhd_logstamp;
77
78static int dhd_var_getbuf(void *dhd, char *iovar, void *param, int param_len, void **bufptr);
79static int dhd_var_setbuf(void *dhd, char *iovar, void *param, int param_len);
80
81static uint dhd_iovar_mkbuf(char *name, char *data, uint datalen,
82                            char *buf, uint buflen, int *perr);
83static int dhd_iovar_getint(void *dhd, char *name, int *var);
84static int dhd_iovar_setint(void *dhd, char *name, int var);
85
86#if defined(BWL_FILESYSTEM_SUPPORT)
87static int file_size(char *fname);
88static int read_vars(char *fname, char *buf, int buf_maxlen);
89#endif
90
91
92
93/* dword align allocation */
94static union {
95	char bufdata[DHD_IOCTL_MAXLEN];
96	uint32 alignme;
97} bufstruct_dhd;
98static char *buf = (char*) &bufstruct_dhd.bufdata;
99
100/* integer output format, default to signed integer */
101static uint8 int_fmt;
102
103typedef struct {
104	uint value;
105	char *string;
106} dbg_msg_t;
107
108static int dhd_do_msglevel(void *dhd, cmd_t *cmd, char **argv, dbg_msg_t *dbg_msg);
109
110/* Actual command table */
111cmd_t dhd_cmds[] = {
112	{ "cmds", dhd_list, -1, -1,
113	"generate a short list of available commands"},
114	{ "version", dhd_version, DHD_GET_VAR, -1,
115	"get version information" },
116	{ "msglevel", dhd_msglevel, DHD_GET_VAR, DHD_SET_VAR,
117	"get/set message bits" },
118	{ "wlmsglevel", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
119	"get/set wl message(in dhd) bits" },
120	{ "bcmerrorstr", dhd_var_getandprintstr, DHD_GET_VAR, -1,
121	"errorstring"},
122	{ "wdtick", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
123	"watchdog tick time (ms units)"},
124	{ "intr", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
125	"use interrupts on the bus"},
126	{ "pollrate", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
127	"number of ticks between bus polls (0 means no polling)"},
128	{ "idletime", dhd_idletime, DHD_GET_VAR, DHD_SET_VAR,
129	"number of ticks for activity timeout (-1: immediate, 0: never)"},
130	{ "idleclock", dhd_idleclock, DHD_GET_VAR, DHD_SET_VAR,
131	"idleclock active | stopped | <N>\n"
132	"\tactive (0)   - do not request any change to the SD clock\n"
133	"\tstopped (-1) - request SD clock be stopped on activity timeout\n"
134	"\t<N> (other)  - an sd_divisor value to request on activity timeout\n"},
135	{ "sd1idle", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
136	"change mode to SD1 when turning off clock at idle"},
137	{ "forceeven", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
138	"force SD tx/rx buffers to be even"},
139	{ "readahead", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
140	"enable readahead feature (look for next frame len in headers)"},
141	{ "sdrxchain", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
142	"enable packet chains to SDIO stack for glom receive"},
143	{ "alignctl", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
144	"align control frames"},
145	{ "sdalign", dhd_varint, DHD_GET_VAR, -1,
146	"display the (compiled in) alignment target for sd requests"},
147	{ "txbound", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
148	"get/set maximum number of tx frames per scheduling"},
149	{ "rxbound", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
150	"get/set maximum number of rx frames per scheduling"},
151	{ "txminmax", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
152	"get/set maximum number of tx frames per scheduling while rx frames outstanding"},
153	{ "dump", dhd_varstr, DHD_GET_VAR, -1,
154	"dump information"},
155	{ "clearcounts", dhd_var_void, -1, DHD_SET_VAR,
156	"reset the bus stats shown in the dhd dump"},
157	{ "logdump", dhd_varstr, DHD_GET_VAR, -1,
158	"dump the timestamp logging buffer"},
159	{ "logcal", dhd_varint, -1, DHD_SET_VAR,
160	"logcal <n>  -- log around an osl_delay of <n> usecs"},
161	{ "logstamp", dhd_logstamp, -1, DHD_SET_VAR,
162	"logstamp [<n1>] [<n2>]  -- add a message to the log"},
163	{ "memsize", dhd_varint, DHD_GET_VAR, -1,
164	"display size of onchip SOCRAM"},
165	{ "membytes", dhd_membytes, DHD_GET_VAR, DHD_SET_VAR,
166	"membytes [-h | -r | -i] <address> <length> [<bytes>]\n"
167	"\tread or write data in the dongle ram\n"
168	"\t-h   <bytes> is a sequence of hex digits, else a char string\n"
169	"\t-r   output as a raw write rather than hexdump display\n"},
170	{ "download", dhd_download, -1, DHD_SET_VAR,
171	"download [-a <address>] [--noreset] [--norun] <binfile> [<varsfile>]\n"
172	"\tdownload file to specified dongle ram address and start CPU\n"
173	"\toptional vars file will replace vars parsed from the CIS\n"
174	"\t--noreset    do not reset SOCRAM core before download\n"
175	"\t--norun      do not start dongle CPU after download\n"
176	"\tdefault <address> is 0\n"},
177	{ "vars", dhd_vars, DHD_GET_VAR, DHD_SET_VAR,
178	"vars [<file>]\n"
179	"\toverride SPROM vars with <file> (before download)\n"},
180	{ "upload", dhd_upload, -1, -1,
181	"upload [-a <address> ] <file> [<size>]\n"
182	"\tupload dongle RAM content into a file\n"
183	"\tdefault <address> is 0, default <size> is RAM size"},
184	{ "srdump", dhd_sprom, DHD_GET_VAR, -1,
185	"display SPROM content" },
186	{ "srwrite", dhd_sprom, -1, DHD_SET_VAR,
187	"write data or file content to SPROM\n"
188	"\tsrwrite <word-offset> <word-value> ...\n"
189	"\tsrwrite [-c] <srom-file-path>\n"
190	"\t  -c means write regardless of crc"},
191	{ "sleep", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
192	"enter/exit simulated host sleep (bus powerdown w/OOB wakeup)"},
193#ifdef SDTEST
194	{ "extloop", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
195	"external loopback: convert all tx data to echo test frames"},
196	{ "pktgen", dhd_pktgen, DHD_GET_VAR, DHD_SET_VAR,
197	"configure/report pktgen status (SDIO)\n"
198	"\t-f N     frequency: send/recv a burst every N ticks\n"
199	"\t-c N     count: send/recv N packets each burst\n"
200	"\t-t N     total: stop after a total of N packets\n"
201	"\t-p N     print: display counts on console every N bursts\n"
202	"\t-m N     min: set minimum length of packet data\n"
203	"\t-M N     Max: set maximum length of packet data\n"
204	"\t-l N     len: set fixed length of packet data\n"
205	"\t-s N     stop after N tx failures\n"
206	"\t-d dir   test direction/type:\n"
207	"\t            send -- send packets discarded by dongle\n"
208	"\t            echo -- send packets to be echoed by dongle\n"
209	"\t            burst -- request bursts (of size <-c>) from dongle\n"
210	"\t              one every <-f> ticks, until <-t> total requests\n"
211	"\t            recv -- request dongle enter continuous send mode,\n"
212	"\t              read up to <-c> pkts every <-f> ticks until <-t>\n"
213	"\t              total reads\n"},
214#endif /* SDTEST */
215	{ "sdreg", dhd_sdreg, DHD_GET_VAR, DHD_SET_VAR,
216	"g/set sdpcmdev core register (f1) across SDIO (CMD53)"},
217	{ "sbreg", dhd_sdreg, DHD_GET_VAR, DHD_SET_VAR,
218	"g/set any backplane core register (f1) across SDIO (CMD53)"},
219	{ "sd_cis", dhd_var_getandprintstr, DHD_GET_VAR, -1,
220	"dump sdio CIS"},
221	{ "sd_devreg", dhd_sd_reg, DHD_GET_VAR, DHD_SET_VAR,
222	"g/set device register across SDIO bus (CMD52)"},
223	{ "sd_hostreg", dhd_sd_reg, DHD_GET_VAR, DHD_SET_VAR,
224	"g/set local controller register"},
225	{ "sd_blocksize", dhd_sd_blocksize, DHD_GET_VAR, DHD_SET_VAR,
226	"g/set block size for a function"},
227	{ "sd_blockmode", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
228	"g/set blockmode"},
229	{ "sd_ints", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
230	"g/set client ints"},
231	{ "sd_dma", dhd_dma_mode, DHD_GET_VAR, DHD_SET_VAR,
232	"g/set dma usage: [PIO | SDMA | ADMA1 | ADMA2]"},
233	{ "sd_yieldcpu", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
234	"allow blocking (yield of CPU) on data xfer"},
235	{ "sd_minyield", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
236	"minimum xfer size to allow CPU yield"},
237	{ "sd_forcerb", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
238	"force readback when changing local interrupt settings"},
239	{ "sd_numints", dhd_varint, DHD_GET_VAR, -1,
240	"number of device interrupts"},
241	{ "sd_numlocalints", dhd_varint, DHD_GET_VAR, -1,
242	"number of non-device interrupts"},
243	{ "sd_divisor", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
244	"set the divisor for SDIO clock generation"},
245	{ "sd_power", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
246	"set the SD Card slot power"},
247	{ "sd_clock", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
248	"turn on/off the SD Clock"},
249	{ "sd_crc", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
250	"turn on/off CRC checking in SPI mode"},
251	{ "sd_mode", dhd_sd_mode, DHD_GET_VAR, DHD_SET_VAR,
252	"g/set SDIO bus mode (spi, sd1, sd4)"},
253	{ "sd_highspeed", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
254	"set the high-speed clocking mode"},
255	{ "sd_msglevel", dhd_sd_msglevel, DHD_GET_VAR, DHD_SET_VAR,
256	"g/set debug message level"},
257	{ "sd_hciregs", dhd_varstr, DHD_GET_VAR, -1,
258	"display host-controller interrupt registers"},
259	{ "sdiod_drive", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
260	"SDIO Device drive strength in milliamps. (0=tri-state, 1-12mA)"},
261	{ "devreset", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
262	"Move device into or out of reset state (1/reset, or 0/operational)"},
263	{ "connstatus", dhd_varstr, DHD_GET_VAR, -1,
264	"get status of last connection attempt" },
265	{ "ioctl_timeout", dhd_varint, DHD_GET_VAR, DHD_SET_VAR,
266	"IOCTL response timeout (milliseconds)."},
267	{ NULL, NULL, 0, 0, NULL }
268};
269
270cmd_t dhd_varcmd = {"var", dhd_varint, -1, -1, "unrecognized name, type -h for help"};
271char *dhdu_av0;
272
273#if defined(BWL_FILESYSTEM_SUPPORT)
274static int
275file_size(char *fname)
276{
277	FILE *fp;
278	long size = -1;
279
280	/* Can't use stat() because of Win CE */
281
282	if ((fp = fopen(fname, "rb")) == NULL ||
283	    fseek(fp, 0, SEEK_END) < 0 ||
284	    (size = ftell(fp)) < 0)
285		fprintf(stderr, "Could not determine size of %s: %s\n",
286		        fname, strerror(errno));
287
288	if (fp != NULL)
289		fclose(fp);
290
291	return (int)size;
292}
293#endif   /* BWL_FILESYSTEM_SUPPORT */
294
295
296/* parse/validate the command line arguments */
297/*
298* pargv is updated upon return if the first argument is an option.
299 * It remains intact otherwise.
300 */
301int
302dhd_option(char ***pargv, char **pifname, int *phelp)
303{
304	char *ifname = NULL;
305	int help = FALSE;
306	int status = CMD_OPT;
307	char **argv = *pargv;
308
309	int_fmt = INT_FMT_DEC;
310
311	while (*argv) {
312		/* select different adapter */
313		if (!strcmp(*argv, "-a") || !strcmp(*argv, "-i")) {
314			char *opt = *argv++;
315			ifname = *argv;
316			if (!ifname) {
317				fprintf(stderr,
318					"error: expected interface name after option %s\n", opt);
319				status = CMD_ERR;
320				break;
321			}
322		}
323
324		/* integer output format */
325		else if (!strcmp(*argv, "-d"))
326			int_fmt = INT_FMT_DEC;
327		else if (!strcmp(*argv, "-u"))
328			int_fmt = INT_FMT_UINT;
329		else if (!strcmp(*argv, "-x"))
330			int_fmt = INT_FMT_HEX;
331
332		/* command usage */
333		else if (!strcmp(*argv, "-h"))
334			help = TRUE;
335
336		/* done with generic options */
337		else {
338			status = CMD_DHD;
339			break;
340		}
341
342		/* consume the argument */
343		argv ++;
344		break;
345	}
346
347	*phelp = help;
348	*pifname = ifname;
349	*pargv = argv;
350
351	return status;
352}
353
354void
355dhd_cmd_usage(cmd_t *cmd)
356{
357	if (strlen(cmd->name) >= 8)
358		fprintf(stderr, "%s\n\t%s\n\n", cmd->name, cmd->help);
359	else
360		fprintf(stderr, "%s\t%s\n\n", cmd->name, cmd->help);
361}
362
363/* Dump out short list of commands */
364static int
365dhd_list(void *dhd, cmd_t *garb, char **argv)
366{
367	cmd_t *cmd;
368	int nrows, i, len;
369	char *buf;
370	int letter, col, row, pad;
371
372	UNUSED_PARAMETER(dhd);
373	UNUSED_PARAMETER(garb);
374	UNUSED_PARAMETER(argv);
375
376	for (cmd = dhd_cmds, nrows = 0; cmd->name; cmd++)
377		    nrows++;
378
379	nrows /= 4;
380	nrows++;
381
382	len = nrows * 80 + 2;
383	buf = malloc(len);
384	if (buf == NULL) {
385		fprintf(stderr, "Failed to allocate buffer of %d bytes\n", len);
386		return COMMAND_ERROR;
387	}
388	for (i = 0; i < len; i++)
389		*(buf+i) = 0;
390
391	row = col = 0;
392	for (letter = 'a'; letter < 'z'; letter++) {
393		for (cmd = dhd_cmds; cmd->name; cmd++) {
394			if (cmd->name[0] == letter || cmd->name[0] == letter - 0x20) {
395				strcat(buf+row*80, cmd->name);
396				pad = 18 * (col + 1) - strlen(buf+row*80);
397				if (pad < 1)
398					pad = 1;
399				for (; pad; pad--)
400					strcat(buf+row*80, " ");
401				row++;
402				if (row == nrows) {
403					col++; row = 0;
404				}
405			}
406		}
407	}
408	for (row = 0; row < nrows; row++)
409		printf("%s\n", buf+row*80);
410
411	printf("\n");
412	free(buf);
413	return (0);
414}
415
416void
417dhd_cmds_usage(cmd_t *port_cmds)
418{
419	cmd_t *port_cmd;
420	cmd_t *cmd;
421
422	/* print usage of port commands */
423	for (port_cmd = port_cmds; port_cmd && port_cmd->name; port_cmd++)
424		/* Check for wc_cmd */
425		dhd_cmd_usage(port_cmd);
426
427	/* print usage of common commands without port counterparts */
428	for (cmd = dhd_cmds; cmd->name; cmd++) {
429		/* search if port counterpart exists */
430		for (port_cmd = port_cmds; port_cmd && port_cmd->name; port_cmd++)
431			if (!strcmp(port_cmd->name, cmd->name))
432				break;
433		if (!port_cmd || !port_cmd->name)
434			dhd_cmd_usage(cmd);
435	}
436}
437
438void
439dhd_usage(cmd_t *port_cmds)
440{
441	fprintf(stderr,
442	        "Usage: %s [-a|i <adapter>] [-h] [-d|u|x] <command> [arguments]\n",
443		dhdu_av0);
444
445	fprintf(stderr, "\n");
446	fprintf(stderr, "  -h		this message\n");
447	fprintf(stderr, "  -a, -i	adapter name or number\n");
448	fprintf(stderr, "  -d		display values as signed integer\n");
449	fprintf(stderr, "  -u		display values as unsigned integer\n");
450	fprintf(stderr, "  -x		display values as hexdecimal\n");
451	fprintf(stderr, "\n");
452
453	dhd_cmds_usage(port_cmds);
454}
455
456int
457dhd_check(void *dhd)
458{
459	int ret;
460	int val;
461
462	if ((ret = dhd_get(dhd, DHD_GET_MAGIC, &val, sizeof(int)) < 0))
463		return ret;
464	if (val != DHD_IOCTL_MAGIC)
465		return -1;
466	if ((ret = dhd_get(dhd, DHD_GET_VERSION, &val, sizeof(int)) < 0))
467		return ret;
468	if (val > DHD_IOCTL_VERSION) {
469		fprintf(stderr, "Version mismatch, please upgrade\n");
470		return -1;
471	}
472	return 0;
473}
474
475void
476dhd_printint(int val)
477{
478	switch (int_fmt) {
479	case INT_FMT_UINT:
480		printf("%u\n", val);
481		break;
482	case INT_FMT_HEX:
483		printf("0x%x\n", val);
484		break;
485	case INT_FMT_DEC:
486	default:
487		printf("%d\n", val);
488		break;
489	}
490}
491
492/* pretty hex print a contiguous buffer (tweaked from wlu) */
493void
494dhd_hexdump(uchar *buf, uint nbytes, uint saddr)
495{
496	char line[256];
497	char* p;
498	uint i;
499
500	if (nbytes == 0) {
501		printf("\n");
502		return;
503	}
504
505	p = line;
506	for (i = 0; i < nbytes; i++) {
507		if (i % 16 == 0) {
508			p += sprintf(p, "%08x: ", saddr + i);	/* line prefix */
509		}
510		p += sprintf(p, "%02x ", buf[i]);
511		if (i % 16 == 15) {
512			uint j;
513			p += sprintf(p, "  ");
514			for (j = i-15; j <= i; j++)
515				p += sprintf(p, "%c",
516				             ((buf[j] >= 0x20 && buf[j] <= 0x7f) ? buf[j] : '.'));
517			printf("%s\n", line);		/* flush line */
518			p = line;
519		}
520	}
521
522	/* flush last partial line */
523	if (p != line)
524		printf("%s\n", line);
525}
526
527
528#ifdef SDTEST
529static int
530dhd_pktgen(void *dhd, cmd_t *cmd, char **argv)
531{
532	int ret = 0;
533	void *ptr = NULL;
534	dhd_pktgen_t pktgen;
535	char *str;
536
537	UNUSED_PARAMETER(dhd);
538	UNUSED_PARAMETER(cmd);
539
540	/* Get current settings */
541	if ((ret = dhd_var_getbuf(dhd, "pktgen", NULL, 0, &ptr)) != 0)
542		return ret;
543	memcpy(&pktgen, ptr, sizeof(pktgen));
544
545	if (pktgen.version != DHD_PKTGEN_VERSION) {
546		fprintf(stderr, "pktgen version mismatch (module %d app %d)\n",
547		        pktgen.version, DHD_PKTGEN_VERSION);
548		return COMMAND_ERROR;
549	}
550
551	/* Presence of args implies a set, else a get */
552	if (*++argv) {
553		miniopt_t opts;
554		int opt_err;
555
556		/* Initialize option parser */
557		miniopt_init(&opts, "pktgen", "", FALSE);
558
559		while ((opt_err = miniopt(&opts, argv)) != -1) {
560			if (opt_err == 1) {
561				fprintf(stderr, "pktgen options error\n");
562				ret = -1;
563				goto exit;
564			}
565			argv += opts.consumed;
566
567			if (!opts.good_int && opts.opt != 'd') {
568				fprintf(stderr, "invalid integer %s\n", opts.valstr);
569				ret = -1;
570				goto exit;
571			}
572
573			switch (opts.opt) {
574			case 'f':
575				pktgen.freq = opts.uval;
576				break;
577			case 'c':
578				pktgen.count = opts.uval;
579				break;
580			case 'p':
581				pktgen.print = opts.uval;
582				break;
583			case 't':
584				pktgen.total = opts.uval;
585				break;
586			case 's':
587				pktgen.stop = opts.uval;
588				break;
589			case 'm':
590				pktgen.minlen = opts.uval;
591				break;
592			case 'M':
593				pktgen.maxlen = opts.uval;
594				break;
595			case 'l': case 'L':
596				pktgen.minlen = pktgen.maxlen = opts.uval;
597				break;
598			case 'd':
599				if (!strcmp(opts.valstr, "send"))
600					pktgen.mode = DHD_PKTGEN_SEND;
601				else if (!strcmp(opts.valstr, "echo"))
602					pktgen.mode = DHD_PKTGEN_ECHO;
603				else if (!strcmp(opts.valstr, "burst"))
604					pktgen.mode = DHD_PKTGEN_RXBURST;
605				else if (!strcmp(opts.valstr, "recv"))
606					pktgen.mode = DHD_PKTGEN_RECV;
607				else {
608					fprintf(stderr, "unrecognized dir mode %s\n",
609					        opts.valstr);
610					return USAGE_ERROR;
611				}
612				break;
613
614			default:
615				fprintf(stderr, "option parsing error (key %s valstr %s)\n",
616				        opts.key, opts.valstr);
617				ret = USAGE_ERROR;
618				goto exit;
619			}
620		}
621
622		if (pktgen.maxlen < pktgen.minlen) {
623			fprintf(stderr, "min/max error (%d/%d)\n", pktgen.minlen, pktgen.maxlen);
624			ret = -1;
625			goto exit;
626		}
627
628		/* Set the new values */
629		ret = dhd_var_setbuf(dhd, "pktgen", &pktgen, sizeof(pktgen));
630	} else {
631		printf("Counts: %d send attempts, %d received, %d tx failures\n",
632		       pktgen.numsent, pktgen.numrcvd, pktgen.numfail);
633	}
634
635	/* Show configuration in either case */
636	switch (pktgen.mode) {
637	case DHD_PKTGEN_ECHO: str = "echo"; break;
638	case DHD_PKTGEN_SEND: str = "send"; break;
639	case DHD_PKTGEN_RECV: str = "recv"; break;
640	case DHD_PKTGEN_RXBURST: str = "burst"; break;
641	default: str = "UNKNOWN"; break;
642	}
643
644	printf("Config: mode %s %d pkts (len %d-%d) each %d ticks\n",
645	       str, pktgen.count, pktgen.minlen, pktgen.maxlen, pktgen.freq);
646
647	/* Second config line for optional items */
648	str = "        ";
649	if (pktgen.total) {
650		printf("%slimit %d", str, pktgen.total);
651		str = ", ";
652	}
653	if (pktgen.print) {
654		printf("%sprint every %d ticks", str, (pktgen.freq * pktgen.print));
655		str = ", ";
656	}
657	if (pktgen.stop) {
658		printf("%sstop after %d tx failures", str, pktgen.stop);
659		str = ", ";
660	}
661	if (str[0] == ',')
662		printf("\n");
663
664exit:
665	return ret;
666}
667#endif /* SDTEST */
668
669static dbg_msg_t dhd_sd_msgs[] = {
670	{SDH_ERROR_VAL,	"error"},
671	{SDH_TRACE_VAL,	"trace"},
672	{SDH_INFO_VAL,	"info"},
673	{SDH_DATA_VAL,	"data"},
674	{SDH_CTRL_VAL,	"control"},
675	{SDH_LOG_VAL,	"log"},
676	{SDH_DMA_VAL,	"dma"},
677	{0,		NULL}
678};
679
680static int
681dhd_sd_msglevel(void *dhd, cmd_t *cmd, char **argv)
682{
683	return dhd_do_msglevel(dhd, cmd, argv, dhd_sd_msgs);
684}
685
686static int
687dhd_sd_blocksize(void *dhd, cmd_t *cmd, char **argv)
688{
689	int ret;
690	int argc;
691	char *endptr = NULL;
692	void *ptr = NULL;
693	int func, size;
694
695	/* arg count */
696	for (argc = 0; argv[argc]; argc++);
697	argc--;
698
699	if (argc < 1 || argc > 2) {
700		printf("required args: function [size] (size 0 means max)\n");
701		return USAGE_ERROR;
702	}
703
704	func = strtol(argv[1], &endptr, 0);
705	if (*endptr != '\0') {
706		printf("Invaild function: %s\n", argv[1]);
707		return USAGE_ERROR;
708	}
709
710	if (argc > 1) {
711		size = strtol(argv[2], &endptr, 0);
712		if (*endptr != '\0') {
713			printf("Invalid size: %s\n", argv[1]);
714			return USAGE_ERROR;
715		}
716	}
717
718	if (argc == 1) {
719		if ((ret = dhd_var_getbuf(dhd, cmd->name, &func, sizeof(func), &ptr)) >= 0)
720			printf("Function %d block size: %d\n", func, *(int*)ptr);
721	} else {
722		printf("Setting function %d block size to %d\n", func, size);
723		size &= 0x0000ffff; size |= (func << 16);
724		ret = dhd_var_setbuf(dhd, cmd->name, &size, sizeof(size));
725	}
726
727	return (ret);
728}
729
730static int
731dhd_sd_mode(void *wl, cmd_t *cmd, char **argv)
732{
733	int ret;
734	int argc;
735	int sdmode;
736
737	/* arg count */
738	for (argc = 0; argv[argc]; argc++);
739	argc--;
740
741	if (argv[1]) {
742		if (!strcmp(argv[1], "spi")) {
743			strcpy(argv[1], "0");
744		} else if (!strcmp(argv[1], "sd1")) {
745			strcpy(argv[1], "1");
746		} else if (!strcmp(argv[1], "sd4")) {
747			strcpy(argv[1], "2");
748		} else {
749			return USAGE_ERROR;
750		}
751
752		ret = dhd_var_setint(wl, cmd, argv);
753
754	} else {
755		if ((ret = dhd_var_get(wl, cmd, argv))) {
756			return (ret);
757		} else {
758			sdmode = *(int32*)buf;
759
760			printf("SD Mode is: %s\n",
761			       sdmode == 0 ? "SPI"
762			       : sdmode == 1 ? "SD1"
763				   : sdmode == 2 ? "SD4" : "Unknown");
764		}
765	}
766
767	return (ret);
768}
769
770static int
771dhd_dma_mode(void *wl, cmd_t *cmd, char **argv)
772{
773	int ret;
774	int argc;
775	int dmamode;
776
777	/* arg count */
778	for (argc = 0; argv[argc]; argc++);
779	argc--;
780
781	if (argv[1]) {
782		if (!stricmp(argv[1], "pio")) {
783			strcpy(argv[1], "0");
784		} else if (!strcmp(argv[1], "0")) {
785		} else if (!stricmp(argv[1], "dma")) {
786			strcpy(argv[1], "1");
787		} else if (!stricmp(argv[1], "sdma")) {
788			strcpy(argv[1], "1");
789		} else if (!strcmp(argv[1], "1")) {
790		} else if (!stricmp(argv[1], "adma1")) {
791			strcpy(argv[1], "2");
792		} else if (!stricmp(argv[1], "adma")) {
793			strcpy(argv[1], "3");
794		} else if (!stricmp(argv[1], "adma2")) {
795			strcpy(argv[1], "3");
796		} else {
797			return USAGE_ERROR;
798		}
799
800		ret = dhd_var_setint(wl, cmd, argv);
801
802	} else {
803		if ((ret = dhd_var_get(wl, cmd, argv))) {
804			return (ret);
805		} else {
806			dmamode = *(int32*)buf;
807
808			printf("DMA Mode is: %s\n",
809			       dmamode == 0 ? "PIO"
810			       : dmamode == 1 ? "SDMA"
811			       : dmamode == 2 ? "ADMA1"
812			       : dmamode == 3 ? "ADMA2"
813			       : "Unknown");
814		}
815	}
816
817	return (ret);
818}
819
820
821static int
822dhd_sdreg(void *dhd, cmd_t *cmd, char **argv)
823{
824	int ret;
825	sdreg_t sdreg;
826	uint argc;
827	char *ptr = NULL;
828
829	UNUSED_PARAMETER(cmd);
830
831	bzero(&sdreg, sizeof(sdreg));
832
833	/* arg count */
834	for (argc = 0; argv[argc]; argc++);
835	argc--;
836
837	/* required args: offset (will default size) */
838	if (argc < 1) {
839		printf("required args: offset[/size] [value]\n");
840		return USAGE_ERROR;
841	}
842
843	sdreg.offset = strtoul(argv[1], &ptr, 0);
844	if (*ptr && *ptr != '/') {
845		printf("Bad arg: %s\n", argv[1]);
846		return USAGE_ERROR;
847	}
848
849	/* read optional /size */
850	if (*ptr == '/') {
851		sdreg.func = strtol((ptr+1), &ptr, 0);
852		if (*ptr || ((sdreg.func != 2) && sdreg.func != 4)) {
853			printf("Bad size option?\n");
854			return USAGE_ERROR;
855		}
856	}
857	else {
858		sdreg.func = 4;
859		printf("Defaulting to register size 4\n");
860	}
861
862	if (argc > 1) {
863		sdreg.value = strtoul(argv[2], &ptr, 0);
864		if (*ptr) {
865			printf("Bad value: %s\n", argv[2]);
866			return USAGE_ERROR;
867		}
868	}
869
870	if (argc <= 1) {
871		ret = dhd_var_getbuf(dhd, argv[0], &sdreg, sizeof(sdreg), (void**)&ptr);
872		if (ret >= 0)
873			printf("0x%0*x\n", (2 * sdreg.func), *(int *)ptr);
874	} else {
875		ret = dhd_var_setbuf(dhd, argv[0], &sdreg, sizeof(sdreg));
876	}
877
878	return (ret);
879}
880
881static int
882dhd_membytes(void *dhd, cmd_t *cmd, char **argv)
883{
884	int ret = -1;
885	uint argc;
886	char *ptr;
887	int params[2];
888	uint addr;
889	uint len;
890	int align;
891
892	int rawout, hexin;
893
894	miniopt_t opts;
895	int opt_err;
896
897	/* Parse command-line options */
898	miniopt_init(&opts, "membytes", "rh", FALSE);
899
900	rawout = hexin = 0;
901
902	argv++;
903	while ((opt_err = miniopt(&opts, argv)) != -1) {
904		if (opt_err == 1) {
905			fprintf(stderr, "membytes options error\n");
906			ret = -1;
907			goto exit;
908		}
909
910		if (opts.positional)
911			break;
912
913		argv += opts.consumed;
914
915		if (opts.opt == 'h') {
916			hexin = 1;
917		} else if (opts.opt == 'r') {
918			rawout = 1;
919		} else {
920			fprintf(stderr, "membytes command error\n");
921			ret = -1;
922			goto exit;
923		}
924	}
925
926	/* arg count */
927	for (argc = 0; argv[argc]; argc++);
928
929	/* required args: address size [<bytes>]] */
930	if (argc < 2) {
931		fprintf(stderr, "required args: address size [<bytes>]\n");
932		return USAGE_ERROR;
933	}
934	if (argc < 3 && hexin) {
935		fprintf(stderr, "missing <bytes> arg implies by -h\n");
936		return USAGE_ERROR;
937	}
938	if ((argc > 2) && (rawout)) {
939		fprintf(stderr, "can't have input <bytes> arg with -r or -i\n");
940		return USAGE_ERROR;
941	}
942
943	/* read address */
944	addr = strtoul(argv[0], &ptr, 0);
945	if (*ptr) {
946		fprintf(stderr, "Bad arg: %s\n", argv[0]);
947		return USAGE_ERROR;
948	}
949
950	/* read size */
951	len = strtoul(argv[1], &ptr, 0);
952	if (*ptr) {
953		fprintf(stderr, "Bad value: %s\n", argv[1]);
954		return USAGE_ERROR;
955	}
956
957	align = addr & 0x03;
958	if (align && argc > 2) {
959		fprintf(stderr, "Can only write starting at long-aligned addresses.\n");
960		return USAGE_ERROR;
961	}
962
963	/* get can just use utility function, set must copy custom buffer */
964	if (argc == 2) {
965		uint chunk = DHD_IOCTL_MAXLEN;
966		for (addr -= align, len += align; len; addr += chunk, len -= chunk, align = 0) {
967			chunk = MIN(chunk, len);
968			params[0] = addr; params[1] = ROUNDUP(chunk, 4);
969			ret = dhd_var_getbuf(dhd, "membytes",
970			                     params, (2 * sizeof(int)), (void**)&ptr);
971			if (ret < 0)
972				goto exit;
973
974			if (rawout) {
975				fwrite(ptr + align, sizeof(char), chunk - align, stdout);
976			} else {
977				dhd_hexdump((uchar*)ptr + align, chunk - align, addr + align);
978			}
979		}
980	} else {
981		uint patlen = strlen(argv[2]);
982		uint chunk, maxchunk;
983		char *sptr;
984
985		if (hexin) {
986			char *inptr, *outptr;
987			if (patlen & 1) {
988				fprintf(stderr, "Hex (-h) must consist of whole bytes\n");
989				ret = USAGE_ERROR;
990				goto exit;
991			}
992
993			for (inptr = outptr = argv[2]; patlen; patlen -= 2) {
994				int n1, n2;
995
996				n1 = (int)((unsigned char)*inptr++);
997				n2 = (int)((unsigned char)*inptr++);
998				if (!isxdigit(n1) || !isxdigit(n2)) {
999					fprintf(stderr, "invalid hex digit %c\n",
1000					        (isxdigit(n1) ? n2 : n1));
1001					ret = USAGE_ERROR;
1002					goto exit;
1003				}
1004				n1 = isdigit(n1) ? (n1 - '0')
1005				        : ((islower(n1) ? (toupper(n1)) : n1) - 'A' + 10);
1006				n2 = isdigit(n2) ? (n2 - '0')
1007				        : ((islower(n2) ? (toupper(n2)) : n2) - 'A' + 10);
1008				*outptr++ = (n1 * 16) + n2;
1009			}
1010
1011			patlen = outptr - argv[2];
1012		}
1013
1014		sptr = argv[2];
1015		maxchunk = DHD_IOCTL_MAXLEN - (strlen(cmd->name) + 1 + (2 * sizeof(int)));
1016
1017		while (len) {
1018			chunk = (len > maxchunk) ? (maxchunk & ~0x3) : len;
1019
1020			/* build the iovar command */
1021			memset(buf, 0, DHD_IOCTL_MAXLEN);
1022			strcpy(buf, cmd->name);
1023			ptr = buf + strlen(buf) + 1;
1024			params[0] = addr; params[1] = chunk;
1025			memcpy(ptr, params, (2 * sizeof(int)));
1026			ptr += (2 * sizeof(int));
1027			addr += chunk; len -= chunk;
1028
1029			while (chunk--) {
1030				*ptr++ = *sptr++;
1031				if (sptr >= (argv[2] + patlen))
1032					sptr = argv[2];
1033			}
1034
1035			ret = dhd_set(dhd, DHD_SET_VAR, &buf[0], (ptr - buf));
1036			if (ret < 0)
1037				goto exit;
1038		}
1039	}
1040
1041exit:
1042	return ret;
1043}
1044
1045static int
1046dhd_idletime(void *dhd, cmd_t *cmd, char **argv)
1047{
1048	int32 idletime;
1049	char *endptr = NULL;
1050	int err = 0;
1051
1052	if (argv[1]) {
1053		if (!strcmp(argv[1], "never")) {
1054			idletime = 0;
1055		} else if (!strcmp(argv[1], "immediate") || !strcmp(argv[1], "immed")) {
1056			idletime = DHD_IDLE_IMMEDIATE;
1057		} else {
1058			idletime = strtol(argv[1], &endptr, 0);
1059			if (*endptr != '\0') {
1060				fprintf(stderr, "invalid number %s\n", argv[1]);
1061				err = -1;
1062			}
1063		}
1064		if ((idletime < 0) && (idletime != DHD_IDLE_IMMEDIATE)) {
1065			fprintf(stderr, "invalid value %s\n", argv[1]);
1066			err = -1;
1067		}
1068
1069		if (!err) {
1070			strcpy(buf, "idletime");
1071			endptr = buf + strlen(buf) + 1;
1072			memcpy(endptr, &idletime, sizeof(uint32));
1073			endptr += sizeof(uint32);
1074			err = dhd_set(dhd, DHD_SET_VAR, &buf[0], (endptr - buf));
1075		}
1076	} else {
1077		if ((err = dhd_var_get(dhd, cmd, argv))) {
1078			return err;
1079		} else {
1080			idletime = *(int32*)buf;
1081
1082			if (idletime == 0) {
1083				printf("0 (never)\n");
1084			} else if (idletime == DHD_IDLE_IMMEDIATE) {
1085				printf("-1 (immediate)\n");
1086			} else if (idletime > 0) {
1087				printf("%d\n", idletime);
1088			} else printf("%d (invalid)\n", idletime);
1089		}
1090	}
1091	return err;
1092}
1093
1094static int
1095dhd_idleclock(void *dhd, cmd_t *cmd, char **argv)
1096{
1097	int32 idleclock;
1098	char *endptr = NULL;
1099	int err = 0;
1100
1101	if (argv[1]) {
1102		if (!strcmp(argv[1], "active")) {
1103			idleclock = DHD_IDLE_ACTIVE;
1104		} else if (!strcmp(argv[1], "stopped")) {
1105			idleclock = DHD_IDLE_STOP;
1106		} else {
1107			idleclock = strtol(argv[1], &endptr, 0);
1108			if (*endptr != '\0') {
1109				fprintf(stderr, "invalid number %s\n", argv[1]);
1110				err = USAGE_ERROR;
1111			}
1112		}
1113
1114		if (!err) {
1115			strcpy(buf, "idleclock");
1116			endptr = buf + strlen(buf) + 1;
1117			memcpy(endptr, &idleclock, sizeof(int32));
1118			endptr += sizeof(int32);
1119			err = dhd_set(dhd, DHD_SET_VAR, &buf[0], (endptr - buf));
1120		}
1121	} else {
1122		if ((err = dhd_var_get(dhd, cmd, argv))) {
1123			return err;
1124		} else {
1125			idleclock = *(int32*)buf;
1126
1127			if (idleclock == DHD_IDLE_ACTIVE)
1128				printf("Idleclock %d (active)\n", idleclock);
1129			else if (idleclock == DHD_IDLE_STOP)
1130				printf("Idleclock %d (stopped)\n", idleclock);
1131			else
1132				printf("Idleclock divisor %d\n", idleclock);
1133		}
1134	}
1135	return err;
1136}
1137
1138/* Word count for a 4kb SPROM */
1139#define SPROM_WORDS 256
1140
1141static int
1142dhd_sprom(void *dhd, cmd_t *cmd, char **argv)
1143{
1144#if !defined(BWL_FILESYSTEM_SUPPORT)
1145	return (-1);
1146#else
1147	int ret, i;
1148	uint argc;
1149	char *endptr;
1150	char *bufp, *countptr;
1151	uint16 *wordptr;
1152	uint offset, words, bytes;
1153	bool nocrc = FALSE;
1154
1155	char *fname;
1156	FILE *fp;
1157
1158	UNUSED_PARAMETER(cmd);
1159
1160	/* arg count */
1161	for (argc = 0; argv[argc]; argc++);
1162	argc--;
1163
1164	/* init buffer */
1165	bufp = buf;
1166	memset(bufp, 0, DHD_IOCTL_MAXLEN);
1167	strcpy(bufp, "sprom");
1168	bufp += strlen("sprom") + 1;
1169
1170	if (strcmp(argv[0], "srdump") == 0) {
1171		if (argc) {
1172			fprintf(stderr, "Command srdump doesn't take args\n");
1173			return USAGE_ERROR;
1174		}
1175		offset = 0;
1176		words = SPROM_WORDS;
1177		bytes = 2 * words;
1178
1179		memcpy(bufp, &offset, sizeof(int));
1180		bufp += sizeof(int);
1181		memcpy(bufp, &bytes, sizeof(int));
1182		bufp += sizeof(int);
1183
1184		if (!ISALIGNED((uintptr)bufp, sizeof(uint16))) {
1185			fprintf(stderr, "Internal error: unaligned word buffer\n");
1186			return COMMAND_ERROR;
1187		}
1188	} else {
1189		if (strcmp(argv[0], "srwrite") != 0) {
1190			fprintf(stderr, "Unimplemented sprom command: %s\n", argv[0]);
1191			return USAGE_ERROR;
1192		}
1193
1194		if (argc == 0) {
1195			return USAGE_ERROR;
1196		} else if ((argc == 1) ||
1197		           ((argc == 2) && ((nocrc = !strcmp(argv[1], "-c"))))) {
1198
1199			fname = nocrc ? argv[2] : argv[1];
1200
1201			/* determine and validate file size */
1202			if ((ret = file_size(fname)) < 0)
1203				return COMMAND_ERROR;
1204
1205			bytes = ret;
1206			offset = 0;
1207			words = bytes / 2;
1208
1209			if (bytes != 2 * SPROM_WORDS) {
1210				fprintf(stderr, "Bad file size\n");
1211				return COMMAND_ERROR;
1212			}
1213
1214			memcpy(bufp, &offset, sizeof(int));
1215			bufp += sizeof(int);
1216			memcpy(bufp, &bytes, sizeof(int));
1217			bufp += sizeof(int);
1218
1219			if (!ISALIGNED((uintptr)bufp, sizeof(uint16))) {
1220				fprintf(stderr, "Internal error: unaligned word buffer\n");
1221				return COMMAND_ERROR;
1222			}
1223
1224			if ((fp = fopen(fname, "rb")) == NULL) {
1225				fprintf(stderr, "Could not open %s: %s\n",
1226				        fname, strerror(errno));
1227				return COMMAND_ERROR;
1228			}
1229
1230			if (fread((uint16*)bufp, sizeof(uint16), words, fp) != words) {
1231				fprintf(stderr, "Could not read %d bytes from %s\n",
1232				        words * 2, fname);
1233				fclose(fp);
1234				return COMMAND_ERROR;
1235			}
1236
1237			fclose(fp);
1238
1239			if (!nocrc &&
1240			    hndcrc8((uint8*)bufp, bytes, CRC8_INIT_VALUE) != CRC8_GOOD_VALUE) {
1241				fprintf(stderr, "CRC check failed: 0x%02x, should be 0x%02x.\n",
1242				        ((uint8*)bufp)[bytes-1],
1243				        ~hndcrc8((uint8*)bufp, bytes - 1, CRC8_INIT_VALUE) & 0xff);
1244				return COMMAND_ERROR;
1245			}
1246
1247			ltoh16_buf(bufp, bytes);
1248		} else {
1249			offset = strtoul(*++argv, &endptr, 0) * 2;
1250			if (*endptr != '\0') {
1251				fprintf(stderr, "offset %s is not an integer\n", *argv);
1252				return USAGE_ERROR;
1253			}
1254
1255			memcpy(bufp, &offset, sizeof(int));
1256			bufp += sizeof(int);
1257			countptr = bufp;
1258			bufp += sizeof(int);
1259
1260			if (!ISALIGNED((uintptr)bufp, sizeof(uint16))) {
1261				fprintf(stderr, "Internal error: unaligned word buffer\n");
1262				return COMMAND_ERROR;
1263			}
1264
1265			for (words = 0, wordptr = (uint16*)bufp; *++argv; words++) {
1266				*wordptr++ = (uint16)strtoul(*argv, &endptr, 0);
1267				if (*endptr != '\0') {
1268					fprintf(stderr, "value %s is not an integer\n", *argv);
1269					return USAGE_ERROR;
1270				}
1271				if (words > SPROM_WORDS) {
1272					fprintf(stderr, "max of %d words\n", SPROM_WORDS);
1273					return USAGE_ERROR;
1274				}
1275			}
1276
1277			bytes = 2 * words;
1278			memcpy(countptr, &bytes, sizeof(int));
1279		}
1280	}
1281
1282	if (argc) {
1283		ret = dhd_set(dhd, DHD_SET_VAR, buf,
1284		              (strlen("sprom") + 1) + (2 * sizeof(int)) + bytes);
1285		return (ret);
1286	} else {
1287		ret = dhd_get(dhd, DHD_GET_VAR, buf,
1288		              (strlen("sprom") + 1) + (2 * sizeof(int)) + bytes);
1289		if (ret < 0) {
1290			return ret;
1291		}
1292
1293		for (i = 0; i < (int)words; i++) {
1294			if ((i % 8) == 0)
1295				printf("\n  srom[%03d]:  ", i);
1296			printf("0x%04x  ", ((uint16*)buf)[i]);
1297		}
1298		printf("\n");
1299	}
1300
1301	return 0;
1302#endif /* BWL_FILESYSTEM_SUPPORT */
1303}
1304
1305/*
1306 * read_vars: reads an environment variables file into a buffer,
1307 * reformatting them and returning the length (-1 on error).
1308 *
1309 * The input text file consists of lines of the form "<var>=<value>\n".
1310 * CRs are ignored, as are blank lines and comments beginning with '#'.
1311 *
1312 * The output buffer consists of blocks of the form "<var>=<value>\0"
1313 * (the newlines have been replaced by NULs)
1314 *
1315 * Todo: allow quoted variable names and quoted values.
1316*/
1317
1318#if defined(BWL_FILESYSTEM_SUPPORT)
1319static int
1320read_vars(char *fname, char *buf, int buf_maxlen)
1321{
1322	FILE *fp;
1323	int buf_len, slen;
1324	char line[256], *s, *e;
1325	int line_no = 0;
1326
1327	if ((fp = fopen(fname, "rb")) == NULL) {
1328		fprintf(stderr, "Cannot open NVRAM file %s: %s\n",
1329		        fname, strerror(errno));
1330		exit(1);
1331	}
1332
1333	buf_len = 0;
1334
1335	while (fgets(line, sizeof(line), fp) != NULL) {
1336		bool found_eq = FALSE;
1337
1338		/* Ensure line length is limited */
1339		line[sizeof(line) - 1] = 0;
1340
1341		/* Skip any initial white space */
1342		for (s = line; *s == ' ' || *s == '\t'; s++)
1343			;
1344
1345		/* Determine end of string */
1346		for (e = s; *e != 0 && *e != '#' && *e != '\r' && *e != '\n'; e++)
1347			if (*e == '=')
1348				found_eq = TRUE;
1349
1350		/* Strip any white space from end of string */
1351		while (e > s && (e[-1] == ' ' || e[-1] == '\t'))
1352			e--;
1353
1354		slen = e - s;
1355
1356		/* Skip lines that end up blank */
1357		if (slen == 0)
1358			continue;
1359
1360		if (!found_eq) {
1361			fprintf(stderr, "Invalid line %d in NVRAM file %s\n", line_no, fname);
1362			fclose(fp);
1363			return -1;
1364		}
1365
1366		if (buf_len + slen + 1 > buf_maxlen) {
1367			fprintf(stderr, "NVRAM file %s too long\n", fname);
1368			fclose(fp);
1369			return -1;
1370		}
1371
1372		memcpy(buf + buf_len, s, slen);
1373		buf_len += slen;
1374		buf[buf_len++] = 0;
1375	}
1376
1377	fclose(fp);
1378
1379	return buf_len;
1380}
1381#endif   /* BWL_FILESYSTEM_SUPPORT */
1382
1383static int
1384dhd_vars(void *dhd, cmd_t *cmd, char **argv)
1385{
1386	int ret;
1387	uint argc;
1388	char *bufp;
1389	char *vname;
1390
1391	UNUSED_PARAMETER(cmd);
1392
1393	/* arg count */
1394	for (argc = 0; argv[argc]; argc++);
1395	argc--;
1396
1397	switch (argc) {
1398	case 0: /* get */
1399	{
1400		if ((ret = dhd_var_getbuf(dhd, "vars", NULL, 0, (void**)&bufp)))
1401			break;
1402		while (*bufp) {
1403			printf("%s\n", bufp);
1404			bufp += strlen(bufp) + 1;
1405		}
1406	}
1407	break;
1408
1409#if defined(BWL_FILESYSTEM_SUPPORT)
1410	case 1: /* set */
1411	{
1412		uint nvram_len;
1413		vname = argv[1];
1414
1415		bufp = buf;
1416		strcpy(bufp, "vars");
1417		bufp += strlen("vars") + 1;
1418
1419		if ((ret = read_vars(vname, bufp,
1420		                           DHD_IOCTL_MAXLEN - (strlen("vars") + 3))) < 0) {
1421			ret = -1;
1422			break;
1423		}
1424
1425		nvram_len = ret;
1426		bufp += nvram_len;
1427		*bufp++ = 0;
1428
1429		ret = dhd_set(dhd, DHD_SET_VAR, buf, bufp - buf);
1430	}
1431	break;
1432#endif   /* BWL_FILESYSTEM_SUPPORT */
1433
1434	default:
1435		ret = -1;
1436		break;
1437	}
1438
1439	return ret;
1440}
1441
1442#define MEMBLOCK 2048
1443
1444/* Check that strlen("membytes")+1 + 2*sizeof(int32) + MEMBLOCK <= DHD_IOCTL_MAXLEN */
1445#if (MEMBLOCK + 17 > DHD_IOCTL_MAXLEN)
1446#error MEMBLOCK/DHD_IOCTL_MAXLEN sizing
1447#endif
1448
1449
1450#if defined(BWL_FILESYSTEM_SUPPORT)
1451static int
1452dhd_load_file_bytes(void *dhd, cmd_t *cmd, FILE *fp, int fsize, int start)
1453{
1454	int tot_len = 0;
1455	uint read_len;
1456	char *bufp;
1457	uint len;
1458	uint8 memblock[MEMBLOCK];
1459	int ret;
1460
1461	UNUSED_PARAMETER(cmd);
1462
1463	while (tot_len < fsize) {
1464		read_len = fsize - tot_len;
1465		if (read_len >= MEMBLOCK)
1466			read_len = MEMBLOCK;
1467		len = fread(memblock, sizeof(uint8), read_len, fp);
1468		if ((len < read_len) && !feof(fp)) {
1469			fprintf(stderr, "%s: error reading file\n", __FUNCTION__);
1470			return -1;
1471
1472		}
1473
1474		bufp = buf;
1475		memset(bufp, 0, DHD_IOCTL_MAXLEN);
1476		strcpy(bufp, "membytes");
1477		bufp += strlen("membytes") + 1;
1478		memcpy(bufp, &start, sizeof(int));
1479		bufp += sizeof(int);
1480		memcpy(bufp, &len, sizeof(int));
1481		bufp += sizeof(int);
1482		memcpy(bufp, memblock, len);
1483
1484		ret = dhd_set(dhd, DHD_SET_VAR, &buf[0], (bufp - buf + len));
1485
1486		if (ret) {
1487			fprintf(stderr, "%s: error %d on writing %d membytes at 0x%08x\n",
1488			        __FUNCTION__, ret, len, start);
1489			return -1;
1490		}
1491		start += len;
1492		tot_len += len;
1493	}
1494	return 0;
1495}
1496#endif   /* BWL_FILESYSTEM_SUPPORT */
1497
1498static int
1499dhd_download(void *dhd, cmd_t *cmd, char **argv)
1500{
1501#if !defined(BWL_FILESYSTEM_SUPPORT)
1502	return (-1);
1503#else
1504	bool reset = TRUE;
1505	bool run = TRUE;
1506	char *fname = NULL;
1507	char *vname = NULL;
1508	uint32 start = 0;
1509	int ret = 0;
1510	int fsize;
1511
1512	FILE *fp = NULL;
1513	uint32 memsize;
1514	char *memszargs[] = { "memsize", NULL };
1515
1516	char *bufp;
1517
1518	miniopt_t opts;
1519	int opt_err;
1520	uint nvram_len;
1521	struct trx_header trx_hdr;
1522	bool trx_file = FALSE;
1523
1524	/* Parse command-line options */
1525	miniopt_init(&opts, "download", "", TRUE);
1526
1527	argv++;
1528	while ((opt_err = miniopt(&opts, argv)) != -1) {
1529		if (opt_err == 1) {
1530			fprintf(stderr, "download options error\n");
1531			ret = -1;
1532			goto exit;
1533		}
1534		argv += opts.consumed;
1535
1536		if (opts.opt == 'a') {
1537			if (!opts.good_int) {
1538				fprintf(stderr, "invalid address %s\n", opts.valstr);
1539				ret = -1;
1540				goto exit;
1541			}
1542			start = (uint32)opts.uval;
1543		} else if (opts.positional) {
1544			if (fname && vname) {
1545				fprintf(stderr, "extra positional arg, %s\n",
1546				        opts.valstr);
1547				ret = -1;
1548				goto exit;
1549			}
1550			if (fname)
1551				vname = opts.valstr;
1552			else
1553				fname = opts.valstr;
1554		} else if (!opts.opt) {
1555			if (!strcmp(opts.key, "noreset")) {
1556				reset = FALSE;
1557			} else if (!strcmp(opts.key, "norun")) {
1558				run = FALSE;
1559			} else {
1560				fprintf(stderr, "unrecognized option %s\n", opts.valstr);
1561				ret = -1;
1562				goto exit;
1563			}
1564		} else {
1565			fprintf(stderr, "unrecognized option %c\n", opts.opt);
1566			ret = -1;
1567			goto exit;
1568		}
1569	}
1570
1571	/* validate arguments */
1572	if (!fname) {
1573		fprintf(stderr, "filename required\n");
1574		ret = -1;
1575		goto exit;
1576	}
1577
1578
1579	/* validate file size compared to memory size */
1580	if ((fsize = file_size(fname)) < 0) {
1581		ret = -1;
1582		goto exit;
1583	}
1584	/* read the file and push blocks down to memory */
1585	if ((fp = fopen(fname, "rb")) == NULL) {
1586		fprintf(stderr, "%s: unable to open %s: %s\n",
1587		        __FUNCTION__, fname, strerror(errno));
1588		ret = -1;
1589		goto exit;
1590	}
1591	/* Verify the file is a regular bin file or trx file */
1592	{
1593		uint32 tmp_len;
1594		uint32 trx_hdr_len = sizeof(struct trx_header);
1595		tmp_len = fread(&trx_hdr, sizeof(uint8), trx_hdr_len, fp);
1596		if (tmp_len == trx_hdr_len) {
1597			if (trx_hdr.magic == TRX_MAGIC) {
1598				fprintf(stderr, "TRX file\n");
1599				trx_file = TRUE;
1600				/* trx header file format: image_size, rom_img_size, rom_load_adr */
1601				fprintf(stderr, "dongle RAM Image size %d\n", trx_hdr.offsets[0]);
1602				fprintf(stderr, "dongle ROM Image size %d\n", trx_hdr.offsets[1]);
1603				fprintf(stderr, "dongle ROM Loadaddr 0x%x\n", trx_hdr.offsets[2]);
1604				fsize = trx_hdr.offsets[0] + trx_hdr.offsets[1];
1605				fprintf(stderr, "filesize is %d\n", fsize);
1606			}
1607			else
1608				fseek(fp, 0, SEEK_SET);
1609		}
1610		else
1611			fseek(fp, 0, SEEK_SET);
1612	}
1613
1614	if ((ret = dhd_var_get(dhd, NULL, memszargs))) {
1615		fprintf(stderr, "%s: error obtaining memsize\n", __FUNCTION__);
1616		goto exit;
1617	}
1618
1619	memsize = *(uint32*)buf;
1620
1621	if (memsize && ((uint32)fsize > memsize)) {
1622		fprintf(stderr, "%s: file %s too large (%d > %d)\n",
1623		        __FUNCTION__, fname, fsize, memsize);
1624		ret = -1;
1625		goto exit;
1626	}
1627
1628	/* do the download reset if not suppressed */
1629	if (reset) {
1630		if ((ret = dhd_iovar_setint(dhd, "download", TRUE))) {
1631			fprintf(stderr, "%s: failed to put dongle in download mode\n",
1632			        __FUNCTION__);
1633			goto exit;
1634		}
1635	}
1636	if (trx_file)
1637		fsize = trx_hdr.offsets[0];
1638	/* Load the ram image */
1639	if (dhd_load_file_bytes(dhd, cmd, fp, fsize, start)) {
1640		fprintf(stderr, "%s: error loading the ramimage at addr 0x%x\n",
1641			__FUNCTION__, start);
1642			ret = -1;
1643			goto exit;
1644		}
1645	if (trx_file) {
1646		/* Load the rom library */
1647		start = trx_hdr.offsets[2];
1648		fsize = trx_hdr.offsets[1];
1649
1650		fprintf(stderr, "setting the maxsocram value 0x%x \n", start);
1651		if (dhd_iovar_setint(dhd, "maxsocram", start)) {
1652			fprintf(stderr, "%s: setting the maxram size to %d failed\n",
1653				__FUNCTION__,  start);
1654			goto exit;
1655		}
1656
1657		if (dhd_load_file_bytes(dhd, cmd, fp, fsize, start)) {
1658			fprintf(stderr, "%s: error loading the rom library at addr 0x%x\n",
1659				__FUNCTION__,  start);
1660			goto exit;
1661		}
1662
1663	}
1664
1665	fclose(fp);
1666	fp = NULL;
1667
1668	/* download the vars file if specified */
1669	if (vname) {
1670		bufp = buf;
1671		strcpy(bufp, "vars");
1672		bufp += strlen("vars") + 1;
1673
1674		if ((ret = read_vars(vname, bufp,
1675		                           DHD_IOCTL_MAXLEN - (strlen("vars") + 3))) < 0) {
1676			ret = -1;
1677			goto exit;
1678		}
1679
1680		nvram_len = ret;
1681		bufp += nvram_len;
1682		*bufp++ = 0;
1683
1684		ret = dhd_set(dhd, DHD_SET_VAR, buf, (bufp - buf));
1685		if (ret) {
1686			fprintf(stderr, "%s: error %d on delivering vars\n",
1687			        __FUNCTION__, ret);
1688			goto exit;
1689		}
1690	}
1691
1692	/* start running the downloaded code if not suppressed */
1693	if (run) {
1694		if ((ret = dhd_iovar_setint(dhd, "download", FALSE))) {
1695			fprintf(stderr, "%s: failed to take dongle out of download mode\n",
1696			        __FUNCTION__);
1697			goto exit;
1698		}
1699	}
1700
1701exit:
1702	if (fp)
1703		fclose(fp);
1704
1705	return ret;
1706#endif /* BWL_FILESYSTEM_SUPPORT */
1707}
1708
1709static int
1710dhd_upload(void *dhd, cmd_t *cmd, char **argv)
1711{
1712#if !defined(BWL_FILESYSTEM_SUPPORT)
1713	return (-1);
1714#else
1715	char *fname = NULL;
1716	uint32 start = 0;
1717	uint32 size = 0;
1718	int ret = 0;
1719
1720	FILE *fp;
1721	uint32 memsize;
1722	char *memszargs[] = { "memsize", NULL };
1723
1724	uint len;
1725
1726	miniopt_t opts;
1727	int opt_err;
1728
1729	UNUSED_PARAMETER(cmd);
1730	UNUSED_PARAMETER(argv);
1731
1732	/* Parse command-line options */
1733	miniopt_init(&opts, "upload", "", TRUE);
1734
1735	argv++;
1736	while ((opt_err = miniopt(&opts, argv)) != -1) {
1737		if (opt_err == 1) {
1738			fprintf(stderr, "upload options error\n");
1739			ret = -1;
1740			goto exit;
1741		}
1742		argv += opts.consumed;
1743
1744		if (opts.opt == 'a') {
1745			if (!opts.good_int) {
1746				fprintf(stderr, "invalid address %s\n", opts.valstr);
1747				ret = -1;
1748				goto exit;
1749			}
1750			start = (uint32)opts.uval;
1751		} else if (opts.positional) {
1752			if (!fname) {
1753				fname = opts.valstr;
1754			} else if (opts.good_int) {
1755				size = (uint32)opts.uval;
1756			} else {
1757				fprintf(stderr, "upload options error\n");
1758				ret = -1;
1759				goto exit;
1760			}
1761		} else if (!opts.opt) {
1762			fprintf(stderr, "unrecognized option %s\n", opts.valstr);
1763			ret = -1;
1764			goto exit;
1765		} else {
1766			fprintf(stderr, "unrecognized option %c\n", opts.opt);
1767			ret = -1;
1768			goto exit;
1769		}
1770	}
1771
1772	/* validate arguments */
1773	if (!fname) {
1774		fprintf(stderr, "filename required\n");
1775		ret = -1;
1776		goto exit;
1777	}
1778
1779	if ((ret = dhd_var_get(dhd, NULL, memszargs))) {
1780		fprintf(stderr, "%s: error obtaining memsize\n", __FUNCTION__);
1781		goto exit;
1782	}
1783	memsize = *(uint32*)buf;
1784
1785	if (!memsize)
1786		memsize = start + size;
1787
1788	if (start + size > memsize) {
1789		fprintf(stderr, "%s: %d bytes at 0x%x exceeds ramsize 0x%x\n",
1790		        __FUNCTION__, size, start, memsize);
1791		ret = -1;
1792		goto exit;
1793	}
1794
1795	if ((fp = fopen(fname, "wb")) == NULL) {
1796		fprintf(stderr, "%s: Could not open %s: %s\n",
1797		        __FUNCTION__, fname, strerror(errno));
1798		ret = -1;
1799		goto exit;
1800	}
1801
1802	/* default size to full RAM */
1803	if (!size)
1804		size = memsize - start;
1805
1806	/* read memory and write to file */
1807	while (size) {
1808		char *ptr;
1809		int params[2];
1810
1811		len = MIN(MEMBLOCK, size);
1812
1813		params[0] = start;
1814		params[1] = len;
1815		ret = dhd_var_getbuf(dhd, "membytes", params, 2 * sizeof(int), (void**)&ptr);
1816		if (ret) {
1817			fprintf(stderr, "%s: failed reading %d membytes from 0x%08x\n",
1818			        __FUNCTION__, len, start);
1819			break;
1820		}
1821
1822		if (fwrite(ptr, sizeof(*ptr), len, fp) != len) {
1823			fprintf(stderr, "%s: error writing to file %s\n", __FUNCTION__, fname);
1824			ret = -1;
1825			break;
1826		}
1827
1828		start += len;
1829		size -= len;
1830	}
1831
1832	fclose(fp);
1833exit:
1834	return ret;
1835#endif /* BWL_FILESYSTEM_SUPPORT */
1836}
1837
1838static int
1839dhd_logstamp(void *dhd, cmd_t *cmd, char **argv)
1840{
1841	int ret;
1842	char *endptr = NULL;
1843	uint argc;
1844	int valn[2] = {0, 0};
1845
1846	/* arg count */
1847	for (argc = 0; argv[argc]; argc++);
1848	argc--; argv++;
1849
1850	if (argc > 2)
1851		return USAGE_ERROR;
1852
1853	if (argc) {
1854		valn[0] = strtol(argv[0], &endptr, 0);
1855		if (*endptr != '\0') {
1856			printf("bad val1: %s\n", argv[0]);
1857			return USAGE_ERROR;
1858		}
1859	}
1860
1861	if (argc > 1) {
1862		valn[1] = strtol(argv[1], &endptr, 0);
1863		if (*endptr != '\0') {
1864			printf("bad val2: %s\n", argv[1]);
1865			return USAGE_ERROR;
1866		}
1867	}
1868
1869	ret = dhd_var_setbuf(dhd, cmd->name, valn, argc * sizeof(int));
1870
1871	return (ret);
1872}
1873
1874static int
1875dhd_sd_reg(void *dhd, cmd_t *cmd, char **argv)
1876{
1877	int ret;
1878	sdreg_t sdreg;
1879	char *endptr = NULL;
1880	uint argc;
1881	void *ptr = NULL;
1882
1883	bzero(&sdreg, sizeof(sdreg));
1884
1885	/* arg count */
1886	for (argc = 0; argv[argc]; argc++);
1887	argc--;
1888
1889	/* hostreg: offset [value]; devreg: func offset [value] */
1890	if (!strcmp(cmd->name, "sd_hostreg")) {
1891		argv++;
1892		if (argc < 1) {
1893			printf("required args: offset [value]\n");
1894			return USAGE_ERROR;
1895		}
1896
1897	} else if (!strcmp(cmd->name, "sd_devreg")) {
1898		argv++;
1899		if (argc < 2) {
1900			printf("required args: func offset [value]\n");
1901			return USAGE_ERROR;
1902		}
1903
1904		sdreg.func = strtoul(*argv++, &endptr, 0);
1905		if (*endptr != '\0') {
1906			printf("Invalid function number\n");
1907			return USAGE_ERROR;
1908		}
1909	} else {
1910		return USAGE_ERROR;
1911	}
1912
1913	sdreg.offset = strtoul(*argv++, &endptr, 0);
1914	if (*endptr != '\0') {
1915		printf("Invalid offset value\n");
1916		return USAGE_ERROR;
1917	}
1918
1919	/* third arg: value */
1920	if (*argv) {
1921		sdreg.value = strtoul(*argv, &endptr, 0);
1922		if (*endptr != '\0') {
1923			printf("Invalid value\n");
1924			return USAGE_ERROR;
1925		}
1926	}
1927
1928	/* no third arg means get, otherwise set */
1929	if (!*argv) {
1930		if ((ret = dhd_var_getbuf(dhd, cmd->name, &sdreg, sizeof(sdreg), &ptr)) >= 0)
1931			printf("0x%x\n", *(int *)ptr);
1932	} else {
1933		ret = dhd_var_setbuf(dhd, cmd->name, &sdreg, sizeof(sdreg));
1934	}
1935
1936	return (ret);
1937}
1938
1939static dbg_msg_t dhd_msgs[] = {
1940	{DHD_ERROR_VAL,	"error"},
1941	{DHD_ERROR_VAL, "err"},
1942	{DHD_TRACE_VAL, "trace"},
1943	{DHD_INFO_VAL,	"inform"},
1944	{DHD_INFO_VAL,	"info"},
1945	{DHD_INFO_VAL,	"inf"},
1946	{DHD_DATA_VAL,	"data"},
1947	{DHD_CTL_VAL,	"ctl"},
1948	{DHD_TIMER_VAL,	"timer"},
1949	{DHD_HDRS_VAL,	"hdrs"},
1950	{DHD_BYTES_VAL,	"bytes"},
1951	{DHD_INTR_VAL,	"intr"},
1952	{DHD_LOG_VAL,	"log"},
1953	{DHD_GLOM_VAL,	"glom"},
1954	{DHD_EVENT_VAL,	"event"},
1955	{DHD_BTA_VAL,	"bta"},
1956	{0,		NULL}
1957};
1958
1959static int
1960dhd_msglevel(void *dhd, cmd_t *cmd, char **argv)
1961{
1962	return dhd_do_msglevel(dhd, cmd, argv, dhd_msgs);
1963}
1964
1965static int
1966dhd_do_msglevel(void *dhd, cmd_t *cmd, char **argv, dbg_msg_t *dbg_msg)
1967{
1968	int ret, i;
1969	uint val, last_val = 0, msglevel = 0, msglevel_add = 0, msglevel_del = 0;
1970	char *endptr = NULL;
1971
1972	if ((ret = dhd_iovar_getint(dhd, cmd->name, (int*)&msglevel)) < 0)
1973		return (ret);
1974
1975	if (!*++argv) {
1976		printf("0x%x ", msglevel);
1977		for (i = 0; (val = dbg_msg[i].value); i++) {
1978			if ((msglevel & val) && (val != last_val))
1979				printf(" %s", dbg_msg[i].string);
1980			last_val = val;
1981		}
1982		printf("\n");
1983		return (0);
1984	}
1985
1986	while (*argv) {
1987		char *s = *argv;
1988		if (*s == '+' || *s == '-')
1989			s++;
1990		else
1991			msglevel_del = ~0;	/* make the whole list absolute */
1992		val = strtoul(s, &endptr, 0);
1993		/* not a plain integer if not all the string was parsed by strtoul */
1994		if (*endptr != '\0') {
1995			for (i = 0; (val = dbg_msg[i].value); i++)
1996				if (stricmp(dbg_msg[i].string, s) == 0)
1997					break;
1998			if (!val)
1999				goto usage;
2000		}
2001		if (**argv == '-')
2002			msglevel_del |= val;
2003		else
2004			msglevel_add |= val;
2005		++argv;
2006	}
2007
2008	msglevel &= ~msglevel_del;
2009	msglevel |= msglevel_add;
2010
2011	return (dhd_iovar_setint(dhd, cmd->name, msglevel));
2012
2013usage:
2014	fprintf(stderr, "msg values may be a list of numbers or names from the following set.\n");
2015	fprintf(stderr, "Use a + or - prefix to make an incremental change.");
2016
2017	for (i = 0; (val = dbg_msg[i].value); i++) {
2018		if (val != last_val)
2019			fprintf(stderr, "\n0x%04x %s", val, dbg_msg[i].string);
2020		else
2021			fprintf(stderr, ", %s", dbg_msg[i].string);
2022		last_val = val;
2023	}
2024	fprintf(stderr, "\n");
2025
2026	return 0;
2027}
2028
2029static char *
2030ver2str(unsigned int vms, unsigned int vls)
2031{
2032	static char verstr[100];
2033	unsigned int maj, year, month, day, build;
2034
2035	maj = (vms >> 16) & 0xFFFF;
2036	if (maj > 1000) {
2037		/* it is probably a date... */
2038		year = (vms >> 16) & 0xFFFF;
2039		month = vms & 0xFFFF;
2040		day = (vls >> 16) & 0xFFFF;
2041		build = vls & 0xFFFF;
2042		sprintf(verstr, "%d/%d/%d build %d",
2043			month, day, year, build);
2044	} else {
2045		/* it is a tagged release. */
2046		sprintf(verstr, "%d.%d RC%d.%d",
2047			(vms>>16)&0xFFFF, vms&0xFFFF,
2048			(vls>>16)&0xFFFF, vls&0xFFFF);
2049	}
2050	return verstr;
2051}
2052
2053static int
2054dhd_version(void *dhd, cmd_t *cmd, char **argv)
2055{
2056	int ret;
2057	char *ptr;
2058
2059	UNUSED_PARAMETER(cmd);
2060	UNUSED_PARAMETER(argv);
2061
2062	/* Display the application version info */
2063	printf("%s: %s\n", dhdu_av0,
2064		ver2str((EPI_MAJOR_VERSION << 16)| EPI_MINOR_VERSION,
2065		(EPI_RC_NUMBER << 16) | EPI_INCREMENTAL_NUMBER));
2066
2067	if ((ret = dhd_var_getbuf(dhd, cmd->name, NULL, 0, (void**)&ptr)) < 0)
2068		return ret;
2069
2070	/* Display the returned string */
2071	printf("%s\n", ptr);
2072
2073	return 0;
2074}
2075
2076static int
2077dhd_var_setint(void *dhd, cmd_t *cmd, char **argv)
2078{
2079	int32 val;
2080	int len;
2081	char *varname;
2082	char *endptr = NULL;
2083	char *p;
2084
2085	if (cmd->set == -1) {
2086		printf("set not defined for %s\n", cmd->name);
2087		return COMMAND_ERROR;
2088	}
2089
2090	if (!*argv) {
2091		printf("set: missing arguments\n");
2092		return USAGE_ERROR;
2093	}
2094
2095	varname = *argv++;
2096
2097	if (!*argv) {
2098		printf("set: missing value argument for set of \"%s\"\n", varname);
2099		return USAGE_ERROR;
2100	}
2101
2102	val = strtol(*argv, &endptr, 0);
2103	if (*endptr != '\0') {
2104		/* not all the value string was parsed by strtol */
2105		printf("set: error parsing value \"%s\" as an integer for set of \"%s\"\n",
2106			*argv, varname);
2107		return USAGE_ERROR;
2108	}
2109
2110	strcpy(buf, varname);
2111	p = buf;
2112	while (*p != '\0') {
2113		*p = tolower(*p);
2114		p++;
2115	}
2116
2117	/* skip the NUL */
2118	p++;
2119
2120	memcpy(p, &val, sizeof(uint));
2121	len = (p - buf) +  sizeof(uint);
2122
2123	return (dhd_set(dhd, DHD_SET_VAR, &buf[0], len));
2124}
2125
2126static int
2127dhd_var_get(void *dhd, cmd_t *cmd, char **argv)
2128{
2129	char *varname;
2130	char *p;
2131
2132	UNUSED_PARAMETER(cmd);
2133
2134	if (!*argv) {
2135		printf("get: missing arguments\n");
2136		return USAGE_ERROR;
2137	}
2138
2139	varname = *argv++;
2140
2141	if (*argv) {
2142		printf("get: error, extra arg \"%s\"\n", *argv);
2143		return USAGE_ERROR;
2144	}
2145
2146	strcpy(buf, varname);
2147	p = buf;
2148	while (*p != '\0') {
2149		*p = tolower(*p);
2150		p++;
2151	}
2152	return (dhd_get(dhd, DHD_GET_VAR, &buf[0], DHD_IOCTL_MAXLEN));
2153}
2154
2155static int
2156dhd_var_getint(void *dhd, cmd_t *cmd, char **argv)
2157{
2158	int err;
2159	int32 val;
2160
2161	if (cmd->get == -1) {
2162		printf("get not defined for %s\n", cmd->name);
2163		return COMMAND_ERROR;
2164	}
2165
2166	if ((err = dhd_var_get(dhd, cmd, argv)))
2167		return (err);
2168
2169	val = *(int32*)buf;
2170
2171	if (val < 10)
2172		printf("%d\n", val);
2173	else
2174		printf("%d (0x%x)\n", val, val);
2175
2176	return (0);
2177}
2178
2179static int
2180dhd_var_getandprintstr(void *dhd, cmd_t *cmd, char **argv)
2181{
2182	int err;
2183
2184	if ((err = dhd_var_get(dhd, cmd, argv)))
2185		return (err);
2186
2187	printf("%s\n", buf);
2188	return (0);
2189}
2190
2191
2192void
2193dhd_printlasterror(void *dhd)
2194{
2195	char *cmd[2] = {"bcmerrorstr"};
2196
2197	if (dhd_var_get(dhd, NULL, cmd) != 0) {
2198		fprintf(stderr, "%s: \nError getting the last error\n", dhdu_av0);
2199	} else {
2200		fprintf(stderr, "%s: %s\n", dhdu_av0, buf);
2201	}
2202}
2203
2204static int
2205dhd_varint(void *dhd, cmd_t *cmd, char *argv[])
2206{
2207	if (argv[1])
2208		return (dhd_var_setint(dhd, cmd, argv));
2209	else
2210		return (dhd_var_getint(dhd, cmd, argv));
2211}
2212
2213static int
2214dhd_var_getbuf(void *dhd, char *iovar, void *param, int param_len, void **bufptr)
2215{
2216	int len;
2217
2218	memset(buf, 0, DHD_IOCTL_MAXLEN);
2219	strcpy(buf, iovar);
2220
2221	/* include the NUL */
2222	len = strlen(iovar) + 1;
2223
2224	if (param_len)
2225		memcpy(&buf[len], param, param_len);
2226
2227	*bufptr = buf;
2228
2229	return dhd_get(dhd, DHD_GET_VAR, &buf[0], DHD_IOCTL_MAXLEN);
2230}
2231
2232static int
2233dhd_var_setbuf(void *dhd, char *iovar, void *param, int param_len)
2234{
2235	int len;
2236
2237	memset(buf, 0, DHD_IOCTL_MAXLEN);
2238	strcpy(buf, iovar);
2239
2240	/* include the NUL */
2241	len = strlen(iovar) + 1;
2242
2243	if (param_len)
2244		memcpy(&buf[len], param, param_len);
2245
2246	len += param_len;
2247	return dhd_set(dhd, DHD_SET_VAR, &buf[0], len);
2248}
2249
2250static int
2251dhd_var_void(void *dhd, cmd_t *cmd, char **argv)
2252{
2253	UNUSED_PARAMETER(argv);
2254
2255	if (cmd->set < 0)
2256		return USAGE_ERROR;
2257
2258	return dhd_var_setbuf(dhd, cmd->name, NULL, 0);
2259}
2260
2261/*
2262 * format an iovar buffer
2263 */
2264static uint
2265dhd_iovar_mkbuf(char *name, char *data, uint datalen, char *buf, uint buflen, int *perr)
2266{
2267	uint len;
2268
2269	len = strlen(name) + 1;
2270
2271	/* check for overflow */
2272	if ((len + datalen) > buflen) {
2273		*perr = BCME_BUFTOOSHORT;
2274		return 0;
2275	}
2276
2277	strcpy(buf, name);
2278
2279	/* append data onto the end of the name string */
2280	if (datalen > 0)
2281		memcpy(&buf[len], data, datalen);
2282
2283	len += datalen;
2284
2285	*perr = 0;
2286	return len;
2287}
2288
2289static int
2290dhd_iovar_getint(void *dhd, char *name, int *var)
2291{
2292	char ibuf[DHD_IOCTL_SMLEN];
2293	int error;
2294
2295	dhd_iovar_mkbuf(name, NULL, 0, ibuf, sizeof(ibuf), &error);
2296	if (error)
2297		return error;
2298
2299	if ((error = dhd_get(dhd, DHD_GET_VAR, &ibuf, sizeof(ibuf))) < 0)
2300		return error;
2301
2302	memcpy(var, ibuf, sizeof(int));
2303
2304	return 0;
2305}
2306
2307static int
2308dhd_iovar_setint(void *dhd, char *name, int var)
2309{
2310	int len;
2311	char ibuf[DHD_IOCTL_SMLEN];
2312	int error;
2313
2314	len = dhd_iovar_mkbuf(name, (char *)&var, sizeof(var), ibuf, sizeof(ibuf), &error);
2315	if (error)
2316		return error;
2317
2318	if ((error = dhd_set(dhd, DHD_SET_VAR, &ibuf, len)) < 0)
2319		return error;
2320
2321	return 0;
2322}
2323
2324static int
2325dhd_varstr(void *dhd, cmd_t *cmd, char **argv)
2326{
2327	int error;
2328	char *str;
2329
2330	if (!*++argv) {
2331		void *ptr;
2332		if ((error = dhd_var_getbuf(dhd, cmd->name, NULL, 0, &ptr)) < 0)
2333			return (error);
2334
2335		str = (char *)ptr;
2336		printf("%s\n", str);
2337		return (0);
2338	} else {
2339		str = *argv;
2340		/* iovar buffer length includes NUL */
2341		return dhd_var_setbuf(dhd, cmd->name, str, strlen(str) + 1);
2342	}
2343}
2344