]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - tools/tools/ath/athprom/athprom.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / tools / tools / ath / athprom / athprom.c
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 }