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