-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy patheep_common.c
635 lines (561 loc) · 18.6 KB
/
eep_common.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
/*
* Copyright (c) 2012 Qualcomm Atheros, Inc.
* Copyright (c) 2013,2017-2021 Sergey Ryazanov <[email protected]>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "atheepmgr.h"
#include "eep_common.h"
const char * const sDeviceType[] = {
"UNKNOWN [0] ",
"Cardbus ",
"PCI ",
"MiniPCI ",
"Access Point",
"PCIExpress ",
"UNKNOWN [6] ",
"UNKNOWN [7] ",
};
const char * const sAccessType[] = {
"ReadWrite", "WriteOnly", "ReadOnly", "NoAccess"
};
const char * const eep_rates_cck[AR5416_NUM_TARGET_POWER_RATES_LEG] = {
"1 mbps", "2 mbps", "5.5 mbps", "11 mbps"
};
const char * const eep_rates_ofdm[AR5416_NUM_TARGET_POWER_RATES_LEG] = {
"6-24 mbps", "36 mbps", "48 mbps", "54 mbps"
};
const char * const eep_rates_ht[AR5416_NUM_TARGET_POWER_RATES_HT] = {
"MCS 0/8", "MCS 1/9", "MCS 2/10", "MCS 3/11",
"MCS 4/12", "MCS 5/13", "MCS 6/14", "MCS 7/15"
};
const char * const eep_ctldomains[] = {
"Unknown (0)", "FCC", "Unknown (2)", "ETSI",
"MKK", "Unknown (5)", "Unknown (6)", "Unknown (7)",
"Unknown (8)", "Unknown (9)", "Unknown (10)", "Unknown (11)",
"Unknown (12)", "Unknown (13)", "SD no ctl", "No ctl"
};
const char * const eep_ctlmodes[] = {
"5GHz OFDM", "2GHz CCK", "2GHz OFDM", "5GHz Turbo",
"2GHz Turbo", "2GHz HT20", "5GHz HT20", "2GHz HT40",
"5GHz HT40", "5GHz VHT20", "2GHz VHT20", "5GHz VHT40",
"2GHz VHT40", "5GHz VHT80", "Unknown (14)", "Unknown (15)"
};
/**
* Detect possible EEPROM I/O byteswapping and toggle I/O byteswap compensation
* if need it to consistently load EEPROM data.
*
* NB: all offsets are in 16-bits words
*/
bool __ar5416_toggle_byteswap(struct atheepmgr *aem, uint32_t eepmisc_off,
uint32_t binbuildnum_off)
{
uint16_t word;
int magic_is_be;
/* First check whether magic is Little-endian or not */
if (!EEP_READ(AR5416_EEPROM_MAGIC_OFFSET, &word)) {
printf("Toggle EEPROM I/O byteswap compensation\n");
return false;
}
magic_is_be = word != AR5416_EEPROM_MAGIC; /* Constant is LE */
/**
* Now read {opCapFlags,eepMisc} pair of fields that lay in the same
* EEPROM word. opCapFlags first bit indicates 5GHz support, while
* eepMisc first bit indicates BigEndian EEPROM data. Due to possible
* byteswap we are unable to distinguish between these two fields.
*
* So we have only two certain cases: if first bit in both octets are
* set, then we have a Big-endian EEPROM data with activated 5GHz
* support, and in opposite if bits in both octets are reset, then we
* have a Little-endian EEPROM with disabled 5GHz support. In both cases
* I/O byteswap could be detected using only magic field value: as soon
* as we know an EEPROM format, magic format mismatch indicates the I/O
* byteswap.
*
* If a first bit set only in one octet, then we are in an abiguity
* case:
*
* EEP Host I/O word &
* type 5GHz type swap 0x0101
* ------------------------------
* LE 1 LE N 0x0001
* LE 1 BE Y 0x0001
* BE 0 LE Y 0x0001
* BE 0 BE N 0x0001
* LE 1 LE Y 0x0100
* LE 1 BE N 0x0100
* BE 0 LE N 0x0100
* BE 0 BE Y 0x0100
*
* And we will need some more heuristic to solve it (see below).
*/
if (!EEP_READ(eepmisc_off, &word)) {
fprintf(stderr, "EEPROM misc field read failed\n");
return false;
}
word &= 0x0101; /* Clear all except 5GHz and BigEndian bits */
if (word == 0x0000) {/* Clearly not Big-endian EEPROM */
if (!magic_is_be)
goto skip_eeprom_io_swap;
if (aem->verbose > 1)
printf("Got byteswapped Little-endian EEPROM data\n");
goto toggle_eeprom_io_swap;
} else if (word == 0x0101) {/* Clearly Big-endian EEPROM */
if (magic_is_be)
goto skip_eeprom_io_swap;
if (aem->verbose > 1)
printf("Got byteswapped Big-endian EEPROM data\n");
goto toggle_eeprom_io_swap;
}
if (aem->verbose > 1)
printf("Data is possibly byteswapped\n");
/**
* Calibration software (ART) version in each seen AR5416/AR92xx EEPROMs
* is a 32-bits value that has a following format:
*
* .----------- Major version (always zero)
* | .--------- Minor version (always non-zero)
* | | .------- Build (always non-zero)
* | | | .----- Unused (always zero)
* | | | |
* 0xMMmmrr00
*
* For example: 0x00091500 is a cal. software v0.9.21. For Little-endian
* EEPROM this version will be saved as 0x00 0x15 0x09 0x00, and for
* Big-endian EEPROM this will be saved as 0x00 0x09 0x15 0x00. As you
* can see both formats have a common part: first byte is always zero,
* while a second one is always non-zero. But if data will be
* byteswapped in each 16-bits word, then we will have an opposite
* situation: first byte become a non-zero, while the second one become
* zero.
*
* Checking each octet of first 16-bits part of version field is our
* endian-agnostic way to detect the byteswapping.
*/
if (!EEP_READ(binbuildnum_off, &word)) {
fprintf(stderr, "Calibration software build read failed\n");
return false;
}
word = le16toh(word); /* Just to make a byteorder predictable */
/* First we check for byteswapped case */
if ((word & 0xff00) == 0 && (word & 0x00ff) != 0)
goto toggle_eeprom_io_swap;
/* Now check for non-byteswapped case */
if ((word & 0xff00) != 0 && (word & 0x00ff) == 0) {
if (aem->verbose > 1)
printf("Looks like there are no byteswapping\n");
goto skip_eeprom_io_swap;
}
/* We have some weird software version, giving up */
if (aem->verbose > 1)
printf("Unable to detect byteswap, giving up\n");
if (!magic_is_be) /* Prefer the Little-endian format */
goto skip_eeprom_io_swap;
toggle_eeprom_io_swap:
if (aem->verbose)
printf("Toggle EEPROM I/O byteswap compensation\n");
aem->eep_io_swap = !aem->eep_io_swap;
skip_eeprom_io_swap:
return true;
}
/**
* NB: size is in 16-bits words
*/
void ar5416_dump_eep_init(const struct ar5416_eep_init *ini, size_t size)
{
int i, maxregsnum;
printf("%-20s : 0x%04X\n", "Magic", ini->magic);
for (i = 0; i < 8; ++i)
printf("Region%d access : %s\n", i,
sAccessType[(ini->prot >> (i * 2)) & 0x3]);
printf("%-20s : 0x%04X\n", "Regs init data ptr", ini->iptr);
printf("\n");
EEP_PRINT_SUBSECT_NAME("Register(s) initialization data");
maxregsnum = (2 * size - offsetof(typeof(*ini), regs)) /
sizeof(ini->regs[0]);
for (i = 0; i < maxregsnum; ++i) {
if (ini->regs[i].addr == 0xffff)
break;
printf(" %04X: %04X%04X\n", ini->regs[i].addr,
ini->regs[i].val_high, ini->regs[i].val_low);
}
printf("\n");
}
static void
ar5416_dump_pwrctl_closeloop_item(const uint8_t *pwr, const uint8_t *vpd,
int maxicepts, int maxstoredgains,
int gainmask, int power_table_offset)
{
const char * const gains[AR5416_NUM_PD_GAINS] = {"4", "2", "1", "0.5"};
uint8_t mpwr[maxicepts * maxstoredgains];
uint8_t mvpd[ARRAY_SIZE(mpwr) * maxstoredgains];
/* Map of Mask Gain bit Index to Calibrated per-Gain icepts set Index */
int mgi2cgi[ARRAY_SIZE(gains)];
int cgii[maxstoredgains]; /* Array of indexes for merge */
int gainidx, ngains, pwridx, npwr; /* Indexes and index limits */
uint8_t pwrmin;
/**
* Index of bits in the gains mask is not the same as gain index in the
* calibration data. Calibration data are stored without gaps. And non
* available per-gain sets are skipped if gain is not enabled via the
* gains mask. E.g. if the gains mask have a value of 0x06 then you
* should use sets #0 and #1 from the calibration data. Where set #0 is
* corespond to gains mask bit #1 and set #1 coresponds to gains mask
* bit #3.
*
* To simplify further code we build a map of gain indexes to
* calibration data sets indexes using the gains mask. Also count a
* number of gains mask bit that are set aka number of configured
* gains.
*/
ngains = 0;
for (gainidx = 0; gainidx < ARRAY_SIZE(gains); ++gainidx) {
if (gainmask & (1 << gainidx)) {
mgi2cgi[gainidx] = ngains;
ngains++;
} else {
mgi2cgi[gainidx] = -1;
}
}
if (ngains > maxstoredgains) {
printf(" PD gain mask activates more gains then possible to store -- %d > %d\n",
ngains, maxstoredgains);
return;
}
/* Merge calibration per-gain power lists to filter duplicates */
memset(mpwr, 0xff, sizeof(mpwr));
memset(mvpd, 0xff, sizeof(mvpd));
memset(cgii, 0x00, sizeof(cgii));
for (pwridx = 0; pwridx < ARRAY_SIZE(mpwr); ++pwridx) {
pwrmin = 0xff;
/* Looking for unmerged yet power value */
for (gainidx = 0; gainidx < ngains; ++gainidx) {
if (cgii[gainidx] >= maxicepts)
continue;
if (pwr[gainidx * maxicepts + cgii[gainidx]] < pwrmin)
pwrmin = pwr[gainidx * maxicepts + cgii[gainidx]];
}
if (pwrmin == 0xff)
break;
mpwr[pwridx] = pwrmin;
/* Copy Vpd of all gains for this power */
for (gainidx = 0; gainidx < ngains; ++gainidx) {
if (cgii[gainidx] >= AR5416_PD_GAIN_ICEPTS ||
pwr[gainidx * maxicepts + cgii[gainidx]] != pwrmin)
continue;
mvpd[pwridx * maxstoredgains + gainidx] =
vpd[gainidx * maxicepts + cgii[gainidx]];
cgii[gainidx]++;
}
}
npwr = pwridx;
/* Print merged data */
printf(" Tx Power, dBm:");
for (pwridx = 0; pwridx < npwr; ++pwridx)
printf(" %5.2f", (double)mpwr[pwridx] / 4 +
power_table_offset);
printf("\n");
printf(" --------------");
for (pwridx = 0; pwridx < npwr; ++pwridx)
printf(" -----");
printf("\n");
for (gainidx = 0; gainidx < ARRAY_SIZE(gains); ++gainidx) {
if (!(gainmask & (1 << gainidx)))
continue;
printf(" Gain x%-3s VPD:", gains[gainidx]);
for (pwridx = 0; pwridx < npwr; ++pwridx) {
uint8_t vpd = mvpd[pwridx * maxstoredgains + mgi2cgi[gainidx]];
if (vpd == 0xff)
printf(" ");
else
printf(" %3u", vpd);
}
printf("\n");
}
}
/**
* Data is an array of per-chain & per-frequency sets of calibrations.
* Each set calibrations consists of two parts: first part contains a set of
* output power values, while the second part contains a corresponding power
* detector values. Each part (power and detector) has a similar structure, it
* is an array of per PD gain sets of measurements (icepts). Each type of
* values (power and detector) has the similar size of one octet.
*
* So to calculate position of per-chain & per-frequency data we should know
* size of this data. Such data block size is a sum of its parts, i.e. sum of
* output power data size and power detector data size. The size of each part
* is a multiplication of a number of PD gains (maxstoredgains) and of a number
* of calibration points (maxicepts).
*
* So having all this numbers we are able to easly calculate size of various
* elements and their positions.
*/
void ar5416_dump_pwrctl_closeloop(const uint8_t *freqs, int maxfreqs, int is_2g,
int maxchains, int chainmask,
const void *data, int maxicepts,
int maxstoredgains, int gainmask,
int power_table_offset)
{
/* Sizes of TxPower and Detector sets of data */
const int fpwrdatasz = maxicepts * maxstoredgains * sizeof(uint8_t);
const int fvpddatasz = maxicepts * maxstoredgains * sizeof(uint8_t);
const int fdatasz = fpwrdatasz + fvpddatasz; /* Per-chain & per-freq data sz */
const uint8_t *fdata, *fpwrdata, *fvpddata;
int chain, freq; /* Indexes */
for (chain = 0; chain < maxchains; ++chain) {
if (!(chainmask & (1 << chain)))
continue;
printf(" Chain %d:\n", chain);
printf("\n");
for (freq = 0; freq < maxfreqs; ++freq) {
if (freqs[freq] == AR5416_BCHAN_UNUSED)
break;
printf(" %4u MHz:\n", FBIN2FREQ(freqs[freq], is_2g));
fdata = data + fdatasz * (chain * maxfreqs + freq);
fpwrdata = fdata + 0;/* Power data begins immediatly */
fvpddata = fdata + fpwrdatasz; /* Skip power data */
ar5416_dump_pwrctl_closeloop_item(fpwrdata, fvpddata,
maxicepts,
maxstoredgains,
gainmask,
power_table_offset);
printf("\n");
}
}
}
void ar5416_dump_target_power(const struct ar5416_cal_target_power *caldata,
int maxchans, const char * const rates[],
int nrates, int is_2g)
{
#define MARGIN " "
#define TP_ITEM_SIZE (sizeof(struct ar5416_cal_target_power) + \
nrates * sizeof(uint8_t))
#define TP_NEXT_CHAN(__tp) ((void *)((uint8_t *)(__tp) + TP_ITEM_SIZE))
const struct ar5416_cal_target_power *tp;
int nchans, i, j;
printf(MARGIN "%10s, MHz:", "Freq");
tp = caldata;
for (j = 0; j < maxchans; ++j, tp = TP_NEXT_CHAN(tp)) {
if (tp->bChannel == AR5416_BCHAN_UNUSED)
break;
printf(" %4u", FBIN2FREQ(tp->bChannel, is_2g));
}
nchans = j;
printf("\n");
printf(MARGIN "----------------");
for (j = 0; j < nchans; ++j)
printf(" ----");
printf("\n");
for (i = 0; i < nrates; ++i) {
printf(MARGIN "%10s, dBm:", rates[i]);
tp = caldata;
for (j = 0; j < nchans; ++j, tp = TP_NEXT_CHAN(tp))
printf(" %4.1f", (double)tp->tPow2x[i] / 2);
printf("\n");
}
#undef TP_NEXT_CHAN
#undef TP_ITEM_SIZE
#undef MARGIN
}
void ar5416_dump_ctl_edges(const struct ar5416_cal_ctl_edges *edges,
int maxradios, int maxedges, int is_2g)
{
const struct ar5416_cal_ctl_edges *e;
int edge, rnum, open;
for (rnum = 0; rnum < maxradios; ++rnum) {
printf("\n");
if (maxradios > 1)
printf(" %d radio(s) Tx:\n", rnum + 1);
printf(" Edges, MHz:");
for (edge = 0, open = 1; edge < maxedges; ++edge) {
e = &edges[rnum * maxedges + edge];
if (!e->bChannel)
break;
printf(" %c%4u%c",
!CTL_EDGE_FLAGS(e->ctl) && open ? '[' : ' ',
FBIN2FREQ(e->bChannel, is_2g),
!CTL_EDGE_FLAGS(e->ctl) && !open ? ']' : ' ');
if (!CTL_EDGE_FLAGS(e->ctl))
open = !open;
}
printf("\n");
printf(" MaxTxPower, dBm:");
for (edge = 0; edge < maxedges; ++edge) {
e = &edges[rnum * maxedges + edge];
if (!e->bChannel)
break;
printf(" %4.1f ", (double)CTL_EDGE_POWER(e->ctl) / 2);
}
printf("\n");
}
}
void ar5416_dump_ctl(const uint8_t *index,
const struct ar5416_cal_ctl_edges *data,
int maxctl, int maxchains, int maxradios, int maxedges)
{
int i;
uint8_t ctl;
for (i = 0; i < maxctl; ++i) {
if (!index[i])
break;
ctl = index[i];
printf(" %s %s:\n", eep_ctldomains[ctl >> 4],
eep_ctlmodes[ctl & 0x0f]);
ar5416_dump_ctl_edges(data + i * (maxchains * maxedges),
maxradios, maxedges,
eep_ctlmodes[ctl & 0x0f][0] == '2'/*:)*/);
printf("\n");
}
}
void ar9300_comp_hdr_unpack(const uint8_t *p, struct ar9300_comp_hdr *hdr)
{
unsigned long value[4] = {p[0], p[1], p[2], p[3]};
hdr->comp = (value[0] >> 5) & 0x0007;
hdr->ref = (value[0] & 0x001f) | ((value[1] >> 2) & 0x0020);
hdr->len = ((value[1] << 4) & 0x07f0) | ((value[2] >> 4) & 0x000f);
hdr->maj = value[2] & 0x000f;
hdr->min = value[3] & 0x00ff;
}
uint16_t ar9300_comp_cksum(const uint8_t *data, int dsize)
{
int it, checksum = 0;
for (it = 0; it < dsize; it++) {
checksum += data[it];
checksum &= 0xffff;
}
return checksum;
}
static bool ar9300_uncompress_block(struct atheepmgr *aem, uint8_t *out,
int out_size, const uint8_t *in, int in_len)
{
int it;
int spot;
int offset;
int length;
spot = 0;
for (it = 0; it < in_len; it += length + 2) {
offset = in[it];
offset &= 0xff;
spot += offset;
length = in[it + 1];
length &= 0xff;
if (length > 0 && spot >= 0 && spot+length <= out_size) {
if (aem->verbose)
printf("Restore at %d: spot=%d offset=%d length=%d\n",
it, spot, offset, length);
memcpy(&out[spot], &in[it+2], length);
spot += length;
} else if (length > 0) {
fprintf(stderr,
"Bad restore at %d: spot=%d offset=%d length=%d\n",
it, spot, offset, length);
return false;
}
}
return true;
}
int ar9300_compress_decision(struct atheepmgr *aem, int it,
struct ar9300_comp_hdr *hdr, uint8_t *out,
const uint8_t *data, int out_size, int *pcurrref,
const uint8_t *(*tpl_lookup_cb)(int))
{
bool res;
switch (hdr->comp) {
case AR9300_COMP_NONE:
if (hdr->len != out_size) {
fprintf(stderr,
"EEPROM structure size mismatch memory=%d eeprom=%d\n",
out_size, hdr->len);
return -1;
}
memcpy(out, data, hdr->len);
if (aem->verbose)
printf("restored eeprom %d: uncompressed, length %d\n",
it, hdr->len);
break;
case AR9300_COMP_BLOCK:
if (hdr->ref != *pcurrref) {
const uint8_t *tpl;
tpl = tpl_lookup_cb(hdr->ref);
if (tpl == NULL) {
fprintf(stderr,
"can't find reference eeprom struct %d\n",
hdr->ref);
return -1;
}
memcpy(out, tpl, out_size);
*pcurrref = hdr->ref;
}
if (aem->verbose)
printf("Restore eeprom %d: block, reference %d, length %d\n",
it, hdr->ref, hdr->len);
res = ar9300_uncompress_block(aem, out, out_size,
data, hdr->len);
if (!res)
return -1;
break;
default:
fprintf(stderr, "unknown compression code %d\n", hdr->comp);
return -1;
}
return 0;
}
static void ar9300_dump_ctl_edges(const uint8_t *freqs, const uint8_t *data,
int maxedges, int is_2g)
{
int i, open;
printf(" Edges, MHz:");
for (i = 0, open = 1; i < maxedges; ++i) {
if (freqs[i] == 0xff || freqs[i] == 0x00)
continue;
printf(" %c%4u%c",
!CTL_EDGE_FLAGS(data[i]) && open ? '[' : ' ',
FBIN2FREQ(freqs[i], is_2g),
!CTL_EDGE_FLAGS(data[i]) && !open ? ']' : ' ');
if (!CTL_EDGE_FLAGS(data[i]))
open = !open;
}
printf("\n");
printf(" MaxTxPower, dBm:");
for (i = 0, open = 1; i < maxedges; ++i) {
if (freqs[i] == 0xff || freqs[i] == 0x00)
continue;
printf(" %4.1f ", (double)CTL_EDGE_POWER(data[i]) / 2);
}
printf("\n");
}
void ar9300_dump_ctl(const uint8_t *index, const uint8_t *freqs,
const uint8_t *data, int maxctl, int maxedges, int is_2g)
{
uint8_t ctl;
int i;
for (i = 0; i < maxctl; ++i) {
ctl = index[i];
if (ctl == 0xff || ctl == 0x00)
continue;
printf(" %s %s:\n", eep_ctldomains[ctl >> 4],
eep_ctlmodes[ctl & 0x0f]);
ar9300_dump_ctl_edges(freqs + maxedges * i,
data + maxedges * i,
maxedges, is_2g);
printf("\n");
}
}
uint16_t eep_calc_csum(const uint16_t *buf, size_t len)
{
uint16_t csum = 0;
size_t i;
for (i = 0; i < len; i++)
csum ^= *buf++;
return csum;
}