xref: /freebsd/tools/tools/ath/athprom/athprom.c (revision c697fb7f)
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 
52 struct	ath_diag atd;
53 int	s;
54 const char *progname;
55 union {
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 
62 static void parseTemplate(FILE *ftemplate, FILE *fd);
63 static uint16_t eeread(uint16_t);
64 static void eewrite(uint16_t, uint16_t);
65 
66 static void
67 usage()
68 {
69 	fprintf(stderr, "usage: %s [-i ifname] [-t pathname] [offset | offset=value]\n", progname);
70 	exit(-1);
71 }
72 
73 static FILE *
74 opentemplate(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 
94 int
95 main(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 
178 static u_int16_t
179 eeread(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 
193 static void
194 eewrite(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
211 int	lineno;
212 int	bol;
213 int	curmode = -1;
214 int	curchan;
215 int	curpdgain;	/* raw pdgain index */
216 int	curlpdgain;	/* logical pdgain index */
217 int	curpcdac;
218 int	curctl;
219 int	numChannels;
220 const RAW_DATA_STRUCT_2413 *pRaw;
221 const TRGT_POWER_INFO *pPowerInfo;
222 const DATA_PER_CHANNEL *pDataPerChannel;
223 const EEPROM_POWER_EXPN_5112 *pExpnPower;
224 int	singleXpd;
225 
226 static int
227 token(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 
252 static int
253 skipto(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;
279 bad:
280 	warnx("EOF with no matching .%s", what);
281 	return EOF;
282 }
283 
284 static int
285 skipws(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 
299 static void
300 setmode(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 
353 int
354 nextctl(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 
378 static void
379 printAntennaControl(FILE *fd, int ant)
380 {
381 	fprintf(fd, "0x%02X", eeprom.ee_antennaControl[ant][curmode]);
382 }
383 
384 static void
385 printEdge(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 
396 static void
397 printEdgePower(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 
410 static void
411 printEdgeFlag(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 
422 static int16_t
423 getMaxPowerV5(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 
436 static void
437 printQuarterDbmPower(FILE *fd, int16_t power25dBm)
438 {
439 	fprintf(fd, "%2d.%02d", power25dBm / 4, (power25dBm % 4) * 25);
440 }
441 
442 static void
443 printHalfDbmPower(FILE *fd, int16_t power5dBm)
444 {
445 	fprintf(fd, "%2d.%d", power5dBm / 2, (power5dBm % 2) * 5);
446 }
447 
448 static void
449 printVpd(FILE *fd, int vpd)
450 {
451 	fprintf(fd, "[%3d]", vpd);
452 }
453 
454 static void
455 printPcdacValue(FILE *fd, int v)
456 {
457 	fprintf(fd, "%2d.%02d", v / EEP_SCALE, v % EEP_SCALE);
458 }
459 
460 static void
461 undef(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 
467 static int
468 pdgain(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 
487 void
488 eevar(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 
821 static void
822 ifmode(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 
868 static void
869 parseTemplate(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