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