]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - tools/tools/ath/athprom/athprom.c
zfs: merge openzfs/zfs@d96e29576
[FreeBSD/FreeBSD.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 <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 }