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