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