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