1/*-
2 * Copyright (c) 2008 Sam Leffler, Errno Consulting
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 *
29 * $FreeBSD$
30 */
31#include "diag.h"
32
33#include "ah.h"
34#include "ah_internal.h"
35#include "ah_eeprom_v1.h"
36#include "ah_eeprom_v3.h"
37#include "ah_eeprom_v14.h"
38
39#define	IS_VERS(op, v)		(eeprom.ee_version op (v))
40
41#include <getopt.h>
42#include <errno.h>
43#include <err.h>
44#include <stdlib.h>
45#include <string.h>
46#include <ctype.h>
47
48#ifndef DIR_TEMPLATE
49#define	DIR_TEMPLATE	"/usr/local/libdata/athprom"
50#endif
51
52struct	ath_diag atd;
53int	s;
54const char *progname;
55union {
56	HAL_EEPROM legacy;		/* format v3.x ... v5.x */
57	struct ar5416eeprom v14;	/* 11n format v14.x ... */
58} eep;
59#define	eeprom	eep.legacy
60#define	eepromN	eep.v14
61
62static void parseTemplate(FILE *ftemplate, FILE *fd);
63static uint16_t eeread(uint16_t);
64static void eewrite(uint16_t, uint16_t);
65
66static void
67usage()
68{
69	fprintf(stderr, "usage: %s [-i ifname] [-t pathname] [offset | offset=value]\n", progname);
70	exit(-1);
71}
72
73static FILE *
74opentemplate(const char *dir)
75{
76	char filename[PATH_MAX];
77	FILE *fd;
78
79	/* find the template using the eeprom version */
80	snprintf(filename, sizeof(filename), "%s/eeprom-%d.%d",
81	    dir, eeprom.ee_version >> 12, eeprom.ee_version & 0xfff);
82	fd = fopen(filename, "r");
83	if (fd == NULL && errno == ENOENT) {
84		/* retry with just the major version */
85		snprintf(filename, sizeof(filename), "%s/eeprom-%d",
86		    dir, eeprom.ee_version >> 12);
87		fd = fopen(filename, "r");
88		if (fd != NULL)		/* XXX verbose */
89			warnx("Using template file %s", filename);
90	}
91	return fd;
92}
93
94int
95main(int argc, char *argv[])
96{
97	FILE *fd = NULL;
98	const char *ifname;
99	int c;
100
101	s = socket(AF_INET, SOCK_DGRAM, 0);
102	if (s < 0)
103		err(1, "socket");
104	ifname = getenv("ATH");
105	if (!ifname)
106		ifname = ATH_DEFAULT;
107
108	progname = argv[0];
109	while ((c = getopt(argc, argv, "i:t:")) != -1)
110		switch (c) {
111		case 'i':
112			ifname = optarg;
113			break;
114		case 't':
115			fd = fopen(optarg, "r");
116			if (fd == NULL)
117				err(-1, "Cannot open %s", optarg);
118			break;
119		default:
120			usage();
121			/*NOTREACHED*/
122		}
123	argc -= optind;
124	argv += optind;
125
126	strncpy(atd.ad_name, ifname, sizeof (atd.ad_name));
127
128	if (argc != 0) {
129		for (; argc > 0; argc--, argv++) {
130			uint16_t off, val, oval;
131			char line[256];
132			char *cp;
133
134			cp = strchr(argv[0], '=');
135			if (cp != NULL)
136				*cp = '\0';
137			off = (uint16_t) strtoul(argv[0], NULL, 0);
138			if (off == 0 && errno == EINVAL)
139				errx(1, "%s: invalid eeprom offset %s",
140					progname, argv[0]);
141			if (cp == NULL) {
142				printf("%04x: %04x\n", off, eeread(off));
143			} else {
144				val = (uint16_t) strtoul(cp+1, NULL, 0);
145				if (val == 0 && errno == EINVAL)
146				errx(1, "%s: invalid eeprom value %s",
147					progname, cp+1);
148				oval = eeread(off);
149				printf("Write %04x: %04x = %04x? ",
150					off, oval, val);
151				fflush(stdout);
152				if (fgets(line, sizeof(line), stdin) != NULL &&
153				    line[0] == 'y')
154					eewrite(off, val);
155			}
156		}
157	} else {
158		atd.ad_id = HAL_DIAG_EEPROM;
159		atd.ad_out_data = (caddr_t) &eep;
160		atd.ad_out_size = sizeof(eep);
161		if (ioctl(s, SIOCGATHDIAG, &atd) < 0)
162			err(1, "ioctl: %s", atd.ad_name);
163		if (fd == NULL) {
164			fd = opentemplate(DIR_TEMPLATE);
165			if (fd == NULL)
166				fd = opentemplate(".");
167			if (fd == NULL)
168				errx(-1, "Cannot locate template file for "
169				    "v%d.%d EEPROM", eeprom.ee_version >> 12,
170				    eeprom.ee_version & 0xfff);
171		}
172		parseTemplate(fd, stdout);
173		fclose(fd);
174	}
175	return 0;
176}
177
178static u_int16_t
179eeread(u_int16_t off)
180{
181	u_int16_t eedata;
182
183	atd.ad_id = HAL_DIAG_EEREAD | ATH_DIAG_IN | ATH_DIAG_DYN;
184	atd.ad_in_size = sizeof(off);
185	atd.ad_in_data = (caddr_t) &off;
186	atd.ad_out_size = sizeof(eedata);
187	atd.ad_out_data = (caddr_t) &eedata;
188	if (ioctl(s, SIOCGATHDIAG, &atd) < 0)
189		err(1, "ioctl: %s", atd.ad_name);
190	return eedata;
191}
192
193static void
194eewrite(uint16_t off, uint16_t value)
195{
196	HAL_DIAG_EEVAL eeval;
197
198	eeval.ee_off = off;
199	eeval.ee_data = value;
200
201	atd.ad_id = HAL_DIAG_EEWRITE | ATH_DIAG_IN;
202	atd.ad_in_size = sizeof(eeval);
203	atd.ad_in_data = (caddr_t) &eeval;
204	atd.ad_out_size = 0;
205	atd.ad_out_data = NULL;
206	if (ioctl(s, SIOCGATHDIAG, &atd) < 0)
207		err(1, "ioctl: %s", atd.ad_name);
208}
209
210#define	MAXID	128
211int	lineno;
212int	bol;
213int	curmode = -1;
214int	curchan;
215int	curpdgain;	/* raw pdgain index */
216int	curlpdgain;	/* logical pdgain index */
217int	curpcdac;
218int	curctl;
219int	numChannels;
220const RAW_DATA_STRUCT_2413 *pRaw;
221const TRGT_POWER_INFO *pPowerInfo;
222const DATA_PER_CHANNEL *pDataPerChannel;
223const EEPROM_POWER_EXPN_5112 *pExpnPower;
224int	singleXpd;
225
226static int
227token(FILE *fd, char id[], int maxid, const char *what)
228{
229	int c, i;
230
231	i = 0;
232	for (;;) {
233		c = getc(fd);
234		if (c == EOF)
235			return EOF;
236		if (!isalnum(c) && c != '_') {
237			ungetc(c, fd);
238			break;
239		}
240		if (i == maxid-1) {
241			warnx("line %d, %s too long", lineno, what);
242			break;
243		}
244		id[i++] = c;
245	}
246	id[i] = '\0';
247	if (i != 0)
248		bol = 0;
249	return i;
250}
251
252static int
253skipto(FILE *fd, const char *what)
254{
255	char id[MAXID];
256	int c;
257
258	for (;;) {
259		c = getc(fd);
260		if (c == EOF)
261			goto bad;
262		if (c == '.' && bol) {		/* .directive */
263			if (token(fd, id, MAXID, ".directive") == EOF)
264				goto bad;
265			if (strcasecmp(id, what) == 0)
266				break;
267			continue;
268		}
269		if (c == '\\') {		/* escape next character */
270			c = getc(fd);
271			if (c == EOF)
272				goto bad;
273		}
274		bol = (c == '\n');
275		if (bol)
276			lineno++;
277	}
278	return 0;
279bad:
280	warnx("EOF with no matching .%s", what);
281	return EOF;
282}
283
284static int
285skipws(FILE *fd)
286{
287	int c, i;
288
289	i = 0;
290	while ((c = getc(fd)) != EOF && isblank(c))
291		i++;
292	if (c != EOF)
293		ungetc(c, fd);
294	if (i != 0)
295		bol = 0;
296	return 0;
297}
298
299static void
300setmode(int mode)
301{
302	EEPROM_POWER_EXPN_5112 *exp;
303
304	curmode = mode;
305	curchan = -1;
306	curctl = -1;
307	curpdgain = -1;
308	curlpdgain = -1;
309	curpcdac = -1;
310	switch (curmode) {
311	case headerInfo11A:
312		pPowerInfo = eeprom.ee_trgtPwr_11a;
313		pDataPerChannel = eeprom.ee_dataPerChannel11a;
314		break;
315	case headerInfo11B:
316		pPowerInfo = eeprom.ee_trgtPwr_11b;
317		pDataPerChannel = eeprom.ee_dataPerChannel11b;
318		break;
319	case headerInfo11G:
320		pPowerInfo = eeprom.ee_trgtPwr_11g;
321		pDataPerChannel = eeprom.ee_dataPerChannel11g;
322		break;
323	}
324	if (IS_VERS(<, AR_EEPROM_VER4_0))		/* nothing to do */
325		return;
326	if (IS_VERS(<, AR_EEPROM_VER5_0)) {
327		exp = &eeprom.ee_modePowerArray5112[curmode];
328		/* fetch indirect data*/
329		atd.ad_id = HAL_DIAG_EEPROM_EXP_11A+curmode;
330		atd.ad_out_size = roundup(
331			sizeof(u_int16_t) * exp->numChannels, sizeof(u_int32_t))
332		    + sizeof(EXPN_DATA_PER_CHANNEL_5112) * exp->numChannels;
333		atd.ad_out_data = (caddr_t) malloc(atd.ad_out_size);
334		if (ioctl(s, SIOCGATHDIAG, &atd) < 0)
335			err(1, "ioctl: %s", atd.ad_name);
336		exp->pChannels = (void *) atd.ad_out_data;
337		exp->pDataPerChannel = (void *)((char *)atd.ad_out_data +
338		   roundup(sizeof(u_int16_t) * exp->numChannels, sizeof(u_int32_t)));
339		pExpnPower = exp;
340		numChannels = pExpnPower->numChannels;
341		if (exp->xpdMask != 0x9) {
342			for (singleXpd = 0; singleXpd < NUM_XPD_PER_CHANNEL; singleXpd++)
343				if (exp->xpdMask == (1<<singleXpd))
344					break;
345		} else
346			singleXpd = 0;
347	} else if (IS_VERS(<, AR_EEPROM_VER14_2)) {
348		pRaw = &eeprom.ee_rawDataset2413[curmode];
349		numChannels = pRaw->numChannels;
350	}
351}
352
353int
354nextctl(int start)
355{
356	int i;
357
358	for (i = start; i < eeprom.ee_numCtls && eeprom.ee_ctl[i]; i++) {
359		switch (eeprom.ee_ctl[i] & 3) {
360		case 0: case 3:
361			if (curmode != headerInfo11A)
362				continue;
363			break;
364		case 1:
365			if (curmode != headerInfo11B)
366				continue;
367			break;
368		case 2:
369			if (curmode != headerInfo11G)
370				continue;
371			break;
372		}
373		return i;
374	}
375	return -1;
376}
377
378static void
379printAntennaControl(FILE *fd, int ant)
380{
381	fprintf(fd, "0x%02X", eeprom.ee_antennaControl[ant][curmode]);
382}
383
384static void
385printEdge(FILE *fd, int edge)
386{
387	const RD_EDGES_POWER *pRdEdgePwrInfo =
388	    &eeprom.ee_rdEdgesPower[curctl*NUM_EDGES];
389
390	if (pRdEdgePwrInfo[edge].rdEdge == 0)
391		fprintf(fd, " -- ");
392	else
393		fprintf(fd, "%04d", pRdEdgePwrInfo[edge].rdEdge);
394}
395
396static void
397printEdgePower(FILE *fd, int edge)
398{
399	const RD_EDGES_POWER *pRdEdgePwrInfo =
400	    &eeprom.ee_rdEdgesPower[curctl*NUM_EDGES];
401
402	if (pRdEdgePwrInfo[edge].rdEdge == 0)
403		fprintf(fd, " -- ");
404	else
405                fprintf(fd, "%2d.%d",
406		    pRdEdgePwrInfo[edge].twice_rdEdgePower / 2,
407                    (pRdEdgePwrInfo[edge].twice_rdEdgePower % 2) * 5);
408}
409
410static void
411printEdgeFlag(FILE *fd, int edge)
412{
413	const RD_EDGES_POWER *pRdEdgePwrInfo =
414	    &eeprom.ee_rdEdgesPower[curctl*NUM_EDGES];
415
416	if (pRdEdgePwrInfo[edge].rdEdge == 0)
417		fprintf(fd, "--");
418	else
419                fprintf(fd, " %1d", pRdEdgePwrInfo[edge].flag);
420}
421
422static int16_t
423getMaxPowerV5(const RAW_DATA_PER_CHANNEL_2413 *data)
424{
425	uint32_t i;
426	uint16_t numVpd;
427
428	for (i = 0; i < MAX_NUM_PDGAINS_PER_CHANNEL; i++) {
429		numVpd = data->pDataPerPDGain[i].numVpd;
430		if (numVpd > 0)
431			return data->pDataPerPDGain[i].pwr_t4[numVpd-1];
432	}
433	return 0;
434}
435
436static void
437printQuarterDbmPower(FILE *fd, int16_t power25dBm)
438{
439	fprintf(fd, "%2d.%02d", power25dBm / 4, (power25dBm % 4) * 25);
440}
441
442static void
443printHalfDbmPower(FILE *fd, int16_t power5dBm)
444{
445	fprintf(fd, "%2d.%d", power5dBm / 2, (power5dBm % 2) * 5);
446}
447
448static void
449printVpd(FILE *fd, int vpd)
450{
451	fprintf(fd, "[%3d]", vpd);
452}
453
454static void
455printPcdacValue(FILE *fd, int v)
456{
457	fprintf(fd, "%2d.%02d", v / EEP_SCALE, v % EEP_SCALE);
458}
459
460static void
461undef(const char *what)
462{
463	warnx("%s undefined for version %d.%d format EEPROM", what,
464	    eeprom.ee_version >> 12, eeprom.ee_version & 0xfff);
465}
466
467static int
468pdgain(int lpdgain)
469{
470	uint32_t mask;
471	int i, l = lpdgain;
472
473	if (IS_VERS(<, AR_EEPROM_VER5_0))
474		mask = pExpnPower->xpdMask;
475	else
476		mask = pRaw->xpd_mask;
477	for (i = 0; mask != 0; mask >>= 1, i++)
478		if ((mask & 1) && l-- == 0)
479			return i;
480	warnx("can't find logical pdgain %d", lpdgain);
481	return -1;
482}
483
484#define	COUNTRY_ERD_FLAG        0x8000
485#define WORLDWIDE_ROAMING_FLAG  0x4000
486
487void
488eevar(FILE *fd, const char *var)
489{
490#define	streq(a,b)	(strcasecmp(a,b) == 0)
491#define	strneq(a,b,n)	(strncasecmp(a,b,n) == 0)
492	if (streq(var, "mode")) {
493		fprintf(fd, "%s",
494		    curmode == headerInfo11A ? "11a" :
495		    curmode == headerInfo11B ? "11b" :
496		    curmode == headerInfo11G ? "11g" : "???");
497	} else if (streq(var, "version")) {
498		fprintf(fd, "%04x", eeprom.ee_version);
499	} else if (streq(var, "V_major")) {
500		fprintf(fd, "%2d", eeprom.ee_version >> 12);
501	} else if (streq(var, "V_minor")) {
502		fprintf(fd, "%2d", eeprom.ee_version & 0xfff);
503	} else if (streq(var, "earStart")) {
504		fprintf(fd, "%03x", eeprom.ee_earStart);
505	} else if (streq(var, "tpStart")) {
506		fprintf(fd, "%03x", eeprom.ee_targetPowersStart);
507	} else if (streq(var, "eepMap")) {
508		fprintf(fd, "%3d", eeprom.ee_eepMap);
509	} else if (streq(var, "exist32KHzCrystal")) {
510		fprintf(fd, "%3d", eeprom.ee_exist32kHzCrystal);
511	} else if (streq(var, "eepMap2PowerCalStart")) {
512		fprintf(fd , "%3d", eeprom.ee_eepMap2PowerCalStart);
513	} else if (streq(var, "Amode")) {
514		fprintf(fd , "%1d", eeprom.ee_Amode);
515	} else if (streq(var, "Bmode")) {
516		fprintf(fd , "%1d", eeprom.ee_Bmode);
517	} else if (streq(var, "Gmode")) {
518		fprintf(fd , "%1d", eeprom.ee_Gmode);
519	} else if (streq(var, "regdomain")) {
520		if ((eeprom.ee_regdomain & COUNTRY_ERD_FLAG) == 0)
521			fprintf(fd, "%03X ", eeprom.ee_regdomain >> 15);
522		else
523			fprintf(fd, "%-3dC", eeprom.ee_regdomain & 0xfff);
524	} else if (streq(var, "turbo2Disable")) {
525		fprintf(fd, "%1d", eeprom.ee_turbo2Disable);
526	} else if (streq(var, "turbo5Disable")) {
527		fprintf(fd, "%1d", eeprom.ee_turbo5Disable);
528	} else if (streq(var, "rfKill")) {
529		fprintf(fd, "%1d", eeprom.ee_rfKill);
530	} else if (streq(var, "disableXr5")) {
531		fprintf(fd, "%1d", eeprom.ee_disableXr5);
532	} else if (streq(var, "disableXr2")) {
533		fprintf(fd, "%1d", eeprom.ee_disableXr2);
534	} else if (streq(var, "turbo2WMaxPower5")) {
535		fprintf(fd, "%2d", eeprom.ee_turbo2WMaxPower5);
536	} else if (streq(var, "cckOfdmDelta")) {
537		fprintf(fd, "%2d", eeprom.ee_cckOfdmPwrDelta);
538	} else if (streq(var, "gainI")) {
539		fprintf(fd, "%2d", eeprom.ee_gainI[curmode]);
540	} else if (streq(var, "WWR")) {
541		fprintf(fd, "%1x",
542		    (eeprom.ee_regdomain & WORLDWIDE_ROAMING_FLAG) != 0);
543	} else if (streq(var, "falseDetectBackoff")) {
544		fprintf(fd, "0x%02x", eeprom.ee_falseDetectBackoff[curmode]);
545	} else if (streq(var, "deviceType")) {
546		fprintf(fd, "%1x", eeprom.ee_deviceType);
547	} else if (streq(var, "switchSettling")) {
548		if (IS_VERS(<, AR_EEPROM_VER14_2))
549			fprintf(fd, "0x%02x", eeprom.ee_switchSettling[curmode]);
550		else
551			fprintf(fd, "%3d", eepromN.modalHeader[curmode].switchSettling);
552	} else if (streq(var, "adcDesiredSize")) {
553		if (IS_VERS(<, AR_EEPROM_VER14_2))
554			fprintf(fd, "%2d", eeprom.ee_adcDesiredSize[curmode]);
555		else
556			fprintf(fd, "%3d", eepromN.modalHeader[curmode].adcDesiredSize);
557	} else if (streq(var, "xlnaGain")) {
558		fprintf(fd, "0x%02x", eeprom.ee_xlnaGain[curmode]);
559	} else if (streq(var, "txEndToXLNAOn")) {
560		fprintf(fd, "0x%02x", eeprom.ee_txEndToXLNAOn[curmode]);
561	} else if (streq(var, "thresh62")) {
562		if (IS_VERS(<, AR_EEPROM_VER14_2))
563			fprintf(fd, "0x%02x", eeprom.ee_thresh62[curmode]);
564		else
565			fprintf(fd, "%3d", eepromN.modalHeader[curmode].thresh62);
566	} else if (streq(var, "txEndToRxOn")) {
567		fprintf(fd, "%3d", eepromN.modalHeader[curmode].txEndToRxOn);
568	} else if (streq(var, "txEndToXPAOff")) {
569		if (IS_VERS(<, AR_EEPROM_VER14_2))
570			fprintf(fd, "0x%02x", eeprom.ee_txEndToXPAOff[curmode]);
571		else
572			fprintf(fd, "%3d", eepromN.modalHeader[curmode].txEndToXpaOff);
573	} else if (streq(var, "txFrameToXPAOn")) {
574		if (IS_VERS(<, AR_EEPROM_VER14_2))
575			fprintf(fd, "0x%02x", eeprom.ee_txFrameToXPAOn[curmode]);
576		else
577			fprintf(fd, "%3d", eepromN.modalHeader[curmode].txEndToRxOn);
578	} else if (streq(var, "pgaDesiredSize")) {
579		if (IS_VERS(<, AR_EEPROM_VER14_2))
580			fprintf(fd, "%2d", eeprom.ee_pgaDesiredSize[curmode]);
581		else
582			fprintf(fd, "%3d", eepromN.modalHeader[curmode].pgaDesiredSize);
583	} else if (streq(var, "noiseFloorThresh")) {
584		fprintf(fd, "%3d", eeprom.ee_noiseFloorThresh[curmode]);
585	} else if (strneq(var, "noiseFloorThreshCh", 18)) {
586		fprintf(fd, "%3d", eepromN.modalHeader[curmode].noiseFloorThreshCh[atoi(var+18)]);
587	} else if (strneq(var, "xlnaGainCh", 10)) {
588		fprintf(fd, "%3d", eepromN.modalHeader[curmode].xlnaGainCh[atoi(var+10)]);
589	} else if (streq(var, "xgain")) {
590		fprintf(fd, "0x%02x", eeprom.ee_xgain[curmode]);
591	} else if (streq(var, "xpd")) {
592		if (IS_VERS(<, AR_EEPROM_VER14_2))
593			fprintf(fd, "%1d", eeprom.ee_xpd[curmode]);
594		else
595			fprintf(fd, "%3d", eepromN.modalHeader[curmode].xpd);
596	} else if (streq(var, "txrxAtten")) {
597		fprintf(fd, "0x%02x", eeprom.ee_txrxAtten[curmode]);
598	} else if (streq(var, "capField")) {
599		fprintf(fd, "0x%04X", eeprom.ee_capField);
600	} else if (streq(var, "txrxAttenTurbo")) {
601		fprintf(fd, "0x%02x",
602		    eeprom.ee_txrxAtten[curmode != headerInfo11A]);
603	} else if (streq(var, "switchSettlingTurbo")) {
604		fprintf(fd, "0x%02X",
605		    eeprom.ee_switchSettlingTurbo[curmode != headerInfo11A]);
606	} else if (streq(var, "adcDesiredSizeTurbo")) {
607		fprintf(fd, "%2d",
608		    eeprom.ee_adcDesiredSizeTurbo[curmode != headerInfo11A]);
609	} else if (streq(var, "pgaDesiredSizeTurbo")) {
610		fprintf(fd, "%2d",
611		    eeprom.ee_pgaDesiredSizeTurbo[curmode != headerInfo11A]);
612	} else if (streq(var, "rxtxMarginTurbo")) {
613		fprintf(fd, "0x%02x",
614		    eeprom.ee_rxtxMarginTurbo[curmode != headerInfo11A]);
615	} else if (strneq(var, "antennaControl", 14)) {
616		printAntennaControl(fd, atoi(var+14));
617	} else if (strneq(var, "antCtrlChain", 12)) {
618		fprintf(fd, "0x%08X",
619		    eepromN.modalHeader[curmode].antCtrlChain[atoi(var+12)]);
620	} else if (strneq(var, "antGainCh", 9)) {
621		fprintf(fd, "%3d",
622		    eepromN.modalHeader[curmode].antennaGainCh[atoi(var+9)]);
623	} else if (strneq(var, "txRxAttenCh", 11)) {
624		fprintf(fd, "%3d",
625		    eepromN.modalHeader[curmode].txRxAttenCh[atoi(var+11)]);
626	} else if (strneq(var, "rxTxMarginCh", 12)) {
627		fprintf(fd, "%3d",
628		    eepromN.modalHeader[curmode].rxTxMarginCh[atoi(var+12)]);
629	} else if (streq(var, "xpdGain")) {
630		fprintf(fd, "%3d", eepromN.modalHeader[curmode].xpdGain);
631	} else if (strneq(var, "iqCalICh", 8)) {
632		fprintf(fd, "%3d",
633		    eepromN.modalHeader[curmode].iqCalICh[atoi(var+8)]);
634	} else if (strneq(var, "iqCalQCh", 8)) {
635		fprintf(fd, "%3d",
636		    eepromN.modalHeader[curmode].iqCalQCh[atoi(var+8)]);
637	} else if (streq(var, "pdGainOverlap")) {
638		printHalfDbmPower(fd, eepromN.modalHeader[curmode].pdGainOverlap);
639	} else if (streq(var, "ob1")) {
640		fprintf(fd, "%1d", eeprom.ee_ob1);
641	} else if (streq(var, "ob2")) {
642		fprintf(fd, "%1d", eeprom.ee_ob2);
643	} else if (streq(var, "ob3")) {
644		fprintf(fd, "%1d", eeprom.ee_ob3);
645	} else if (streq(var, "ob4")) {
646		fprintf(fd, "%1d", eeprom.ee_ob4);
647	} else if (streq(var, "db1")) {
648		fprintf(fd, "%1d", eeprom.ee_db1);
649	} else if (streq(var, "db2")) {
650		fprintf(fd, "%1d", eeprom.ee_db2);
651	} else if (streq(var, "db3")) {
652		fprintf(fd, "%1d", eeprom.ee_db3);
653	} else if (streq(var, "db4")) {
654		fprintf(fd, "%1d", eeprom.ee_db4);
655	} else if (streq(var, "obFor24")) {
656                fprintf(fd, "%1d", eeprom.ee_obFor24);
657	} else if (streq(var, "ob2GHz0")) {
658                fprintf(fd, "%1d", eeprom.ee_ob2GHz[0]);
659	} else if (streq(var, "dbFor24")) {
660                fprintf(fd, "%1d", eeprom.ee_dbFor24);
661	} else if (streq(var, "db2GHz0")) {
662                fprintf(fd, "%1d", eeprom.ee_db2GHz[0]);
663	} else if (streq(var, "obFor24g")) {
664                fprintf(fd, "%1d", eeprom.ee_obFor24g);
665	} else if (streq(var, "ob2GHz1")) {
666                fprintf(fd, "%1d", eeprom.ee_ob2GHz[1]);
667	} else if (streq(var, "dbFor24g")) {
668                fprintf(fd, "%1d", eeprom.ee_dbFor24g);
669	} else if (streq(var, "db2GHz1")) {
670                fprintf(fd, "%1d", eeprom.ee_db2GHz[1]);
671	} else if (streq(var, "ob")) {
672		fprintf(fd, "%3d", eepromN.modalHeader[curmode].ob);
673	} else if (streq(var, "db")) {
674		fprintf(fd, "%3d", eepromN.modalHeader[curmode].db);
675	} else if (streq(var, "xpaBiasLvl")) {
676		fprintf(fd, "%3d", eepromN.modalHeader[curmode].xpaBiasLvl);
677	} else if (streq(var, "pwrDecreaseFor2Chain")) {
678		printHalfDbmPower(fd, eepromN.modalHeader[curmode].pwrDecreaseFor2Chain);
679	} else if (streq(var, "pwrDecreaseFor3Chain")) {
680		printHalfDbmPower(fd, eepromN.modalHeader[curmode].pwrDecreaseFor3Chain);
681	} else if (streq(var, "txFrameToDataStart")) {
682		fprintf(fd, "%3d", eepromN.modalHeader[curmode].txFrameToDataStart);
683	} else if (streq(var, "txFrameToPaOn")) {
684		fprintf(fd, "%3d", eepromN.modalHeader[curmode].txFrameToPaOn);
685	} else if (streq(var, "ht40PowerIncForPdadc")) {
686		fprintf(fd, "%3d", eepromN.modalHeader[curmode].ht40PowerIncForPdadc);
687	} else if (streq(var, "checksum")) {
688                fprintf(fd, "0x%04X", eepromN.baseEepHeader.checksum);
689	} else if (streq(var, "length")) {
690                fprintf(fd, "0x%04X", eepromN.baseEepHeader.length);
691	} else if (streq(var, "regDmn0")) {
692                fprintf(fd, "0x%04X", eepromN.baseEepHeader.regDmn[0]);
693	} else if (streq(var, "regDmn1")) {
694                fprintf(fd, "0x%04X", eepromN.baseEepHeader.regDmn[1]);
695	} else if (streq(var, "txMask")) {
696                fprintf(fd, "0x%04X", eepromN.baseEepHeader.txMask);
697	} else if (streq(var, "rxMask")) {
698                fprintf(fd, "0x%04X", eepromN.baseEepHeader.rxMask);
699	} else if (streq(var, "rfSilent")) {
700                fprintf(fd, "0x%04X", eepromN.baseEepHeader.rfSilent);
701	} else if (streq(var, "btOptions")) {
702                fprintf(fd, "0x%04X", eepromN.baseEepHeader.blueToothOptions);
703	} else if (streq(var, "deviceCap")) {
704                fprintf(fd, "0x%04X", eepromN.baseEepHeader.deviceCap);
705	} else if (strneq(var, "macaddr", 7)) {
706                fprintf(fd, "%02X",
707		    eepromN.baseEepHeader.macAddr[atoi(var+7)]);
708	} else if (streq(var, "opCapFlags")) {
709                fprintf(fd, "0x%02X", eepromN.baseEepHeader.opCapFlags);
710	} else if (streq(var, "eepMisc")) {
711                fprintf(fd, "0x%02X", eepromN.baseEepHeader.eepMisc);
712	} else if (strneq(var, "binBuildNumber", 14)) {
713                fprintf(fd, "%3d",
714		    (eepromN.baseEepHeader.binBuildNumber >> (8*atoi(var+14)))
715		    & 0xff);
716	} else if (strneq(var, "custData", 8)) {
717		fprintf(fd, "%2.2X", eepromN.custData[atoi(var+8)]);
718	} else if (streq(var, "xpd_mask")) {
719		if (IS_VERS(<, AR_EEPROM_VER5_0))
720			fprintf(fd, "0x%02x", pExpnPower->xpdMask);
721		else
722			fprintf(fd, "0x%02x", pRaw->xpd_mask);
723	} else if (streq(var, "numChannels")) {
724		if (IS_VERS(<, AR_EEPROM_VER5_0))
725			fprintf(fd, "%2d", pExpnPower->numChannels);
726		else
727			fprintf(fd, "%2d", pRaw->numChannels);
728	} else if (streq(var, "freq")) {
729		if (IS_VERS(<, AR_EEPROM_VER5_0))
730			fprintf(fd, "%4d", pExpnPower->pChannels[curchan]);
731		else
732			fprintf(fd, "%4d", pRaw->pChannels[curchan]);
733	} else if (streq(var, "maxpow")) {
734		int16_t maxPower_t4;
735		if (IS_VERS(<, AR_EEPROM_VER5_0)) {
736			maxPower_t4 = pExpnPower->pDataPerChannel[curchan].maxPower_t4;
737		} else {
738			maxPower_t4 = pRaw->pDataPerChannel[curchan].maxPower_t4;
739			if (maxPower_t4 == 0)
740				maxPower_t4 = getMaxPowerV5(&pRaw->pDataPerChannel[curchan]);
741		}
742		printQuarterDbmPower(fd, maxPower_t4);
743	} else if (streq(var, "pd_gain")) {
744		fprintf(fd, "%4d", pRaw->pDataPerChannel[curchan].
745		    pDataPerPDGain[curpdgain].pd_gain);
746	} else if (strneq(var, "maxpwr", 6)) {
747		int vpd = atoi(var+6);
748		if (vpd < pRaw->pDataPerChannel[curchan].pDataPerPDGain[curpdgain].numVpd)
749			printQuarterDbmPower(fd, pRaw->pDataPerChannel[curchan].
750			    pDataPerPDGain[curpdgain].pwr_t4[vpd]);
751		else
752			fprintf(fd, "     ");
753	} else if (strneq(var, "pwr_t4_", 7)) {
754		printQuarterDbmPower(fd, pExpnPower->pDataPerChannel[curchan].
755		    pDataPerXPD[singleXpd].pwr_t4[atoi(var+7)]);
756	} else if (strneq(var, "Vpd", 3)) {
757		int vpd = atoi(var+3);
758		if (vpd < pRaw->pDataPerChannel[curchan].pDataPerPDGain[curpdgain].numVpd)
759			printVpd(fd, pRaw->pDataPerChannel[curchan].
760			    pDataPerPDGain[curpdgain].Vpd[vpd]);
761		else
762			fprintf(fd, "     ");
763	} else if (streq(var, "CTL")) {
764		fprintf(fd, "0x%2x", eeprom.ee_ctl[curctl] & 0xff);
765	} else if (streq(var, "ctlType")) {
766		static const char *ctlType[16] = {
767		    "11a base", "11b", "11g", "11a TURBO", "108g",
768		    "2GHT20", "5GHT20", "2GHT40", "5GHT40",
769		    "0x9", "0xa", "0xb", "0xc", "0xd", "0xe", "0xf",
770		};
771		fprintf(fd, "%8s", ctlType[eeprom.ee_ctl[curctl] & CTL_MODE_M]);
772	} else if (streq(var, "ctlRD")) {
773		static const char *ctlRD[8] = {
774		    "0x00", " FCC", "0x20", "ETSI",
775		    " MKK", "0x50", "0x60", "0x70"
776		};
777		fprintf(fd, "%s", ctlRD[(eeprom.ee_ctl[curctl] >> 4) & 7]);
778	} else if (strneq(var, "rdEdgePower", 11)) {
779		printEdgePower(fd, atoi(var+11));
780	} else if (strneq(var, "rdEdgeFlag", 10)) {
781		printEdgeFlag(fd, atoi(var+10));
782	} else if (strneq(var, "rdEdge", 6)) {
783		printEdge(fd, atoi(var+6));
784	} else if (strneq(var, "testChannel", 11)) {
785		fprintf(fd, "%4d", pPowerInfo[atoi(var+11)].testChannel);
786	} else if (strneq(var, "pwr6_24_", 8)) {
787		printHalfDbmPower(fd, pPowerInfo[atoi(var+8)].twicePwr6_24);
788	} else if (strneq(var, "pwr36_", 6)) {
789		printHalfDbmPower(fd, pPowerInfo[atoi(var+6)].twicePwr36);
790	} else if (strneq(var, "pwr48_", 6)) {
791		printHalfDbmPower(fd, pPowerInfo[atoi(var+6)].twicePwr48);
792	} else if (strneq(var, "pwr54_", 6)) {
793		printHalfDbmPower(fd, pPowerInfo[atoi(var+6)].twicePwr54);
794	} else if (strneq(var, "channelValue", 12)) {
795		fprintf(fd, "%4d", pDataPerChannel[atoi(var+12)].channelValue);
796	} else if (strneq(var, "pcdacMin", 8)) {
797		fprintf(fd, "%02d", pDataPerChannel[atoi(var+8)].pcdacMin);
798	} else if (strneq(var, "pcdacMax", 8)) {
799		fprintf(fd, "%02d", pDataPerChannel[atoi(var+8)].pcdacMax);
800	} else if (strneq(var, "pcdac", 5)) {
801		if (IS_VERS(<, AR_EEPROM_VER4_0)) {
802			fprintf(fd, "%02d", pDataPerChannel[atoi(var+5)].
803			    PcdacValues[curpcdac]);
804		} else if (IS_VERS(<, AR_EEPROM_VER5_0)) {
805			fprintf(fd, "%02d",
806			    pExpnPower->pDataPerChannel[curchan].
807				pDataPerXPD[singleXpd].pcdac[atoi(var+5)]);
808		} else
809			undef("pcdac");
810	} else if (strneq(var, "pwrValue", 8)) {
811		printPcdacValue(fd,
812		    pDataPerChannel[atoi(var+8)].PwrValues[curpcdac]);
813	} else if (streq(var, "singleXpd")) {
814		fprintf(fd, "%2d", singleXpd);
815	} else
816		warnx("line %u, unknown EEPROM variable \"%s\"", lineno, var);
817#undef strneq
818#undef streq
819}
820
821static void
822ifmode(FILE *ftemplate, const char *mode)
823{
824	if (strcasecmp(mode, "11a") == 0) {
825		if (IS_VERS(<, AR_EEPROM_VER14_2)) {
826			if (eeprom.ee_Amode)
827				setmode(headerInfo11A);
828			else
829				skipto(ftemplate, "endmode");
830			return;
831		}
832		if (IS_VERS(>=, AR_EEPROM_VER14_2)) {
833			if (eepromN.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11A)
834				setmode(headerInfo11A);
835			else
836				skipto(ftemplate, "endmode");
837			return;
838		}
839	} else if (strcasecmp(mode, "11g") == 0) {
840		if (IS_VERS(<, AR_EEPROM_VER14_2)) {
841			if (eeprom.ee_Gmode)
842				setmode(headerInfo11G);
843			else
844				skipto(ftemplate, "endmode");
845			return;
846		}
847		if (IS_VERS(>=, AR_EEPROM_VER14_2)) {
848			if (eepromN.baseEepHeader.opCapFlags & AR5416_OPFLAGS_11G)
849				setmode(headerInfo11B);		/* NB: 2.4GHz */
850			else
851				skipto(ftemplate, "endmode");
852			return;
853		}
854	} else if (strcasecmp(mode, "11b") == 0) {
855		if (IS_VERS(<, AR_EEPROM_VER14_2)) {
856			if (eeprom.ee_Bmode)
857				setmode(headerInfo11B);
858			else
859				skipto(ftemplate, "endmode");
860			return;
861		}
862	}
863	warnx("line %d, unknown/unexpected mode \"%s\" ignored",
864	    lineno, mode);
865	skipto(ftemplate, "endmode");
866}
867
868static void
869parseTemplate(FILE *ftemplate, FILE *fd)
870{
871	int c, i;
872	char id[MAXID];
873	long forchan, forpdgain, forctl, forpcdac;
874
875	lineno = 1;
876	bol = 1;
877	while ((c = getc(ftemplate)) != EOF) {
878		if (c == '#') {			/* comment */
879	skiptoeol:
880			while ((c = getc(ftemplate)) != EOF && c != '\n')
881				;
882			if (c == EOF)
883				return;
884			lineno++;
885			bol = 1;
886			continue;
887		}
888		if (c == '.' && bol) {		/* .directive */
889			if (token(ftemplate, id, MAXID, ".directive") == EOF)
890				return;
891			/* process directive */
892			if (strcasecmp(id, "ifmode") == 0) {
893				skipws(ftemplate);
894				if (token(ftemplate, id, MAXID, "id") == EOF)
895					return;
896				ifmode(ftemplate, id);
897			} else if (strcasecmp(id, "endmode") == 0) {
898				/* XXX free malloc'd indirect data */
899				curmode = -1;	/* NB: undefined */
900			} else if (strcasecmp(id, "forchan") == 0) {
901				forchan = ftell(ftemplate) - sizeof("forchan");
902				if (curchan == -1)
903					curchan = 0;
904			} else if (strcasecmp(id, "endforchan") == 0) {
905				if (++curchan < numChannels)
906					fseek(ftemplate, forchan, SEEK_SET);
907				else
908					curchan = -1;
909			} else if (strcasecmp(id, "ifpdgain") == 0) {
910				skipws(ftemplate);
911				if (token(ftemplate, id, MAXID, "pdgain") == EOF)
912					return;
913				curlpdgain = strtoul(id, NULL, 0);
914				if (curlpdgain >= pRaw->pDataPerChannel[curchan].numPdGains) {
915					skipto(ftemplate, "endpdgain");
916					curlpdgain = -1;
917				} else
918					curpdgain = pdgain(curlpdgain);
919			} else if (strcasecmp(id, "endpdgain") == 0) {
920				curlpdgain = curpdgain = -1;
921			} else if (strcasecmp(id, "forpdgain") == 0) {
922				forpdgain = ftell(ftemplate) - sizeof("forpdgain");
923				if (curlpdgain == -1) {
924					skipws(ftemplate);
925					if (token(ftemplate, id, MAXID, "pdgain") == EOF)
926						return;
927					curlpdgain = strtoul(id, NULL, 0);
928					if (curlpdgain >= pRaw->pDataPerChannel[curchan].numPdGains) {
929						skipto(ftemplate, "endforpdgain");
930						curlpdgain = -1;
931					} else
932						curpdgain = pdgain(curlpdgain);
933				}
934			} else if (strcasecmp(id, "endforpdgain") == 0) {
935				if (++curpdgain < pRaw->pDataPerChannel[curchan].numPdGains)
936					fseek(ftemplate, forpdgain, SEEK_SET);
937				else
938					curpdgain = -1;
939			} else if (strcasecmp(id, "forpcdac") == 0) {
940				forpcdac = ftell(ftemplate) - sizeof("forpcdac");
941				if (curpcdac == -1)
942					curpcdac = 0;
943			} else if (strcasecmp(id, "endforpcdac") == 0) {
944				if (++curpcdac < pDataPerChannel[0].numPcdacValues)
945					fseek(ftemplate, forpcdac, SEEK_SET);
946				else
947					curpcdac = -1;
948			} else if (strcasecmp(id, "forctl") == 0) {
949				forctl = ftell(ftemplate) - sizeof("forchan");
950				if (curctl == -1)
951					curctl = nextctl(0);
952			} else if (strcasecmp(id, "endforctl") == 0) {
953				curctl = nextctl(curctl+1);
954				if (curctl != -1)
955					fseek(ftemplate, forctl, SEEK_SET);
956			} else {
957				warnx("line %d, unknown directive %s ignored",
958				    lineno, id);
959			}
960			goto skiptoeol;
961		}
962		if (c == '$') {			/* $variable reference */
963			if (token(ftemplate, id, MAXID, "$var") == EOF)
964				return;
965			/* XXX not valid if variable depends on curmode */
966			eevar(fd, id);
967			continue;
968		}
969		if (c == '\\') {		/* escape next character */
970			c = getc(ftemplate);
971			if (c == EOF)
972				return;
973		}
974		fputc(c, fd);
975		bol = (c == '\n');
976		if (bol)
977			lineno++;
978	}
979}
980