-
-
Notifications
You must be signed in to change notification settings - Fork 19
Expand file tree
/
Copy pathRadioManagementModule.cpp
More file actions
2508 lines (2253 loc) · 95.5 KB
/
Copy pathRadioManagementModule.cpp
File metadata and controls
2508 lines (2253 loc) · 95.5 KB
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
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#include "RadioManagementModule.h"
#include "Hal8812PhyReg.h"
#include "InitTimer.h"
#include "registry_priv.h"
extern "C" {
#include "ieee80211_radiotap.h"
}
#include <chrono>
#include <cstdlib>
#include <map>
#include <thread>
#include <unordered_map>
#include <vector>
namespace {
int get_40mhz_center_channel(int channel) {
static const std::unordered_map<int, int> channel_map = {
// 2.4GHz 40MHz configuration: only one valid center channel (6)
{4, 6}, // primary below center (extension above)
{8, 6}, // primary above center (extension below)
// 5GHz UNII-1
{36, 38}, {40, 38},
// 5GHz UNII-1 / UNII-2
{44, 46}, {48, 46},
// 5GHz UNII-2A
{52, 54}, {56, 54}, {60, 62}, {64, 62},
// 5GHz UNII-2C
{100, 102}, {104, 102},
{108, 110}, {112, 110},
{116, 118}, {120, 118},
{124, 126}, {128, 126},
// 5GHz UNII-2E (if applicable)
{132, 134}, {136, 134},
{140, 142}, {144, 142},
// 5GHz UNII-3
{149, 151}, {153, 155},
{157, 159}, {161, 163}
};
auto it = channel_map.find(channel);
return (it != channel_map.end()) ? it->second : channel;
}
}
RadioManagementModule::RadioManagementModule(
RtlUsbAdapter device, std::shared_ptr<EepromManager> eepromManager,
Logger_t logger)
: _device{device}, _eepromManager{eepromManager}, _logger{logger},
_pwrTrk{device, eepromManager, this, logger},
_iqk{device, eepromManager, this, logger},
_iqk8814{device, eepromManager, this, logger} {}
void RadioManagementModule::RunIQK() {
_iqk.Calibrate(_currentChannel, current_band_type, /*is_recovery=*/false);
}
uint32_t RadioManagementModule::phy_query_bb_reg_public(uint16_t regAddr,
uint32_t bitMask) {
return phy_query_bb_reg(regAddr, bitMask);
}
void RadioManagementModule::InitPwrTrack() { _pwrTrk.Init(); }
void RadioManagementModule::TickPwrTrack() {
_pwrTrk.TickThermalMeter(current_band_type, _currentChannel);
}
ThermalStatus RadioManagementModule::ReadThermalStatus() {
/* Live thermal meter: RF path A, register 0x42, bits [15:10]. phy_query_rf_reg
* already masks + shifts the bits down, so the result is the raw 6-bit reading.
* Same register the 8812A power-track loop samples; here we read it standalone
* (no chip-type gate, no BB-swing write) so the probe works on every Jaguar. */
ThermalStatus s;
uint32_t rf = phy_query_rf_reg(RfPath::RF_PATH_A, 0x42, 0xfc00u);
s.raw = static_cast<uint8_t>(rf & 0x3F);
s.baseline = _eepromManager->GetEepromThermalMeter();
if (s.baseline == 0xFF) {
s.valid = false;
s.delta = 0;
} else {
s.valid = true;
s.delta = static_cast<int>(s.raw) - static_cast<int>(s.baseline);
}
return s;
}
void RadioManagementModule::hw_var_rcr_config(uint32_t rcr) {
_device.rtw_write32(REG_RCR, rcr);
}
#define _HW_STATE_NOLINK_ 0x00
void RadioManagementModule::SetMonitorMode() {
rtw_hal_set_msr(_HW_STATE_NOLINK_);
hw_var_set_monitor();
}
void RadioManagementModule::rtw_hal_set_msr(uint8_t net_type) {
switch (_hwPort) {
case HwPort::HW_PORT0: {
/*REG_CR - BIT[17:16]-Network Type for port 0*/
auto val8 = _device.rtw_read8(MSR) & 0x0C;
val8 |= net_type;
_device.rtw_write8(MSR, val8);
break;
}
case HwPort::HW_PORT1: {
/*REG_CR - BIT[19:18]-Network Type for port 1*/
auto val8 = _device.rtw_read8(MSR) & 0x03;
val8 |= net_type << 2;
_device.rtw_write8(MSR, val8);
break;
}
default:
throw std::logic_error("not yet implemented");
break;
}
}
void RadioManagementModule::hw_var_set_monitor() {
/* Receive all type */
uint32_t rcr_bits = RCR_AAP | RCR_APM | RCR_AM | RCR_AB | RCR_APWRMGT |
RCR_ADF | RCR_ACF | RCR_AMF | RCR_APP_PHYST_RXFF;
/* Append FCS */
rcr_bits |= RCR_APPFCS;
/* DEVOURER_RX_KEEP_CORRUPTED: also pass frames whose 802.11 FCS (CRC32) or
* decryption-ICV check failed. By default the chip drops them at the WMAC
* filter — fine for clean-or-missing IP traffic, but it also hides any
* partial-bit-error information that a FEC layer could otherwise use. With
* the bits below set the frames reach the host with `crc_err` / `icv_err`
* set on the RX descriptor; FrameParser surfaces them so a consumer like
* tools/precoder/corruption_analysis.py can characterise the corruption.
* Guarded by the same env var as the demo's filter — keep them in lockstep
* so a noisy RX never surprises an IP-stack consumer that didn't ask for
* it. */
if (std::getenv("DEVOURER_RX_KEEP_CORRUPTED") != nullptr) {
rcr_bits |= RCR_ACRC32 | RCR_AICV;
}
// rtw_hal_get_hwreg(adapterState, HW_VAR_RCR, pHalData.rcr_backup);
hw_var_rcr_config(rcr_bits);
/* Receive all data frames */
uint16_t value_rxfltmap2 = 0xFFFF;
_device.rtw_write16(REG_RXFLTMAP2, value_rxfltmap2);
}
static uint8_t rtw_get_center_ch(uint8_t channel, ChannelWidth_t chnl_bw,
uint8_t chnl_offset) {
uint8_t center_ch = channel;
if (chnl_bw == ChannelWidth_t::CHANNEL_WIDTH_80) {
if (channel == 36 || channel == 40 || channel == 44 || channel == 48)
center_ch = 42;
else if (channel == 52 || channel == 56 || channel == 60 || channel == 64)
center_ch = 58;
else if (channel == 100 || channel == 104 || channel == 108 ||
channel == 112)
center_ch = 106;
else if (channel == 116 || channel == 120 || channel == 124 ||
channel == 128)
center_ch = 122;
else if (channel == 132 || channel == 136 || channel == 140 ||
channel == 144)
center_ch = 138;
else if (channel == 149 || channel == 153 || channel == 157 ||
channel == 161)
center_ch = 155;
else if (channel == 165 || channel == 169 || channel == 173 ||
channel == 177)
center_ch = 171;
else if (channel <= 14)
center_ch = 7;
} else if (chnl_bw == ChannelWidth_t::CHANNEL_WIDTH_40) {
center_ch = get_40mhz_center_channel(center_ch);
} else if (chnl_bw == ChannelWidth_t::CHANNEL_WIDTH_20) {
center_ch = channel;
} else {
throw std::logic_error("not yet implemented");
}
return center_ch;
}
void RadioManagementModule::set_channel_bwmode(uint8_t channel,
uint8_t channel_offset,
ChannelWidth_t bwmode) {
uint8_t center_ch, chnl_offset80 = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
_logger->info("[{}] ch = {}, offset = {}, bwmode = {}", __func__, unsigned(channel),
unsigned(channel_offset), (int)bwmode);
center_ch = rtw_get_center_ch(channel, bwmode, channel_offset);
if (bwmode == ChannelWidth_t::CHANNEL_WIDTH_80) {
if (center_ch > channel) {
chnl_offset80 = HAL_PRIME_CHNL_OFFSET_LOWER;
} else if (center_ch < channel) {
chnl_offset80 = HAL_PRIME_CHNL_OFFSET_UPPER;
} else {
chnl_offset80 = HAL_PRIME_CHNL_OFFSET_DONT_CARE;
}
}
rtw_hal_set_chnl_bw(center_ch, bwmode, channel_offset,
chnl_offset80); /* set center channel */
}
void RadioManagementModule::rtw_hal_set_chnl_bw(uint8_t channel,
ChannelWidth_t Bandwidth,
uint8_t Offset40,
uint8_t Offset80) {
PHY_SetSwChnlBWMode8812(channel, Bandwidth, Offset40, Offset80);
}
void RadioManagementModule::PHY_SetSwChnlBWMode8812(uint8_t channel,
ChannelWidth_t Bandwidth,
uint8_t Offset40,
uint8_t Offset80) {
PHY_HandleSwChnlAndSetBW8812(true, true, channel, Bandwidth, Offset40,
Offset80, channel);
}
void RadioManagementModule::PHY_HandleSwChnlAndSetBW8812(
bool bSwitchChannel, bool bSetBandWidth, uint8_t ChannelNum,
ChannelWidth_t ChnlWidth, uint8_t ChnlOffsetOf40MHz,
uint8_t ChnlOffsetOf80MHz, uint8_t CenterFrequencyIndex1) {
_logger->info(
"[{}] bSwitchChannel {}, bSetBandWidth {}", __func__,
bSwitchChannel, bSetBandWidth);
/* check is swchnl or setbw */
if (!bSwitchChannel && !bSetBandWidth) {
_logger->error("[{}]: not switch channel and not set bandwidth", __func__);
return;
}
/* skip change for channel or bandwidth is the same */
if (bSwitchChannel) {
if (_currentChannel != ChannelNum) {
_swChannel = true;
}
}
if (bSetBandWidth) {
if (_channelBwInitialized == false) {
_channelBwInitialized = true;
_setChannelBw = true;
} else if ((_currentChannelBw != ChnlWidth) ||
(_cur40MhzPrimeSc != ChnlOffsetOf40MHz) ||
(_cur80MhzPrimeSc != ChnlOffsetOf80MHz) ||
(_currentCenterFrequencyIndex != CenterFrequencyIndex1)) {
_setChannelBw = true;
}
}
if (!_setChannelBw && !_swChannel && _needIQK != true) {
_logger->error("[{}]: _swChannel {}, _setChannelBw {}", __func__,
_swChannel, _setChannelBw);
return;
}
if (_swChannel) {
_currentChannel = ChannelNum;
_currentCenterFrequencyIndex = ChannelNum;
}
if (_setChannelBw) {
_currentChannelBw = ChnlWidth;
_cur40MhzPrimeSc = ChnlOffsetOf40MHz;
_cur80MhzPrimeSc = ChnlOffsetOf80MHz;
_currentCenterFrequencyIndex = CenterFrequencyIndex1;
}
/* Switch workitem or set timer to do switch channel or setbandwidth operation
*/
phy_SwChnlAndSetBwMode8812();
}
void RadioManagementModule::phy_SwChnlAndSetBwMode8812() {
InitTimer timer(_logger, "channel_set");
if (_swChannel) {
phy_SwChnl8812();
_swChannel = false;
}
timer.stage("sw_chnl");
if (_setChannelBw) {
phy_PostSetBwMode8812();
_setChannelBw = false;
}
timer.stage("set_bw");
/* 8814A uses a packed single-DWord write to BB 0x1998 per (path,rate)
* instead of the 8812's per-rate per-byte register fanout (see the
* CHIP_8814A branch at the top of PHY_SetTxPowerIndex_8812A). The
* earlier "skip TX power on 8814" workaround was a symptom of the
* 8812 register layout being wrong for 8814 — every write hit the
* wrong bits and the chip's BB stalled on each one. Setting
* DEVOURER_SKIP_TXPWR=1 keeps the old skip behaviour as an escape
* hatch (e.g. for RX-only experiments). */
if (std::getenv("DEVOURER_SKIP_TXPWR")) {
_logger->info("DEVOURER_SKIP_TXPWR=1 — skipping TX power setup");
} else {
PHY_SetTxPowerLevel8812(_currentChannel);
}
timer.stage("tx_power");
/* Run phydm thermal-meter pwrtrk once per channel-set. Mirrors the
* upstream watchdog tick — reads RF[A][0x42], folds into the
* thermal-value rolling average, walks the delta-swing table for
* the (band, channel) bucket, and writes the resulting BB-swing
* index to 0xc1c[31:21] / 0xe1c[31:21].
*
* 8812A-only. The delta-swing tables + `PowerTracking8812a` logic
* came from `aircrack-ng/rtl8812au/hal/phydm/halrf/rtl8812a/
* halrf_8812a_ce.c`. The 8821AU has its own `halrf_8821a_ce.c`
* variant with different per-band tables and 1T1R-specific math —
* running 8812A code on 8821A produced wrong values at ch6 BB
* 0xc1c[31:21] (T1 8821 canary diff caught it as kern 0x200/0dB vs
* dev 0x1C8/-1dB). Until the 8821 pwrtrk is ported, skip the tick
* on non-8812 chips. */
if (_eepromManager->version_id.ICType == CHIP_8812) {
_pwrTrk.TickThermalMeter(current_band_type, _currentChannel);
}
/* Kernel cross-validation oracle: when DEVOURER_DUMP_CANARY=1
* is set, dump the canary BB/MAC/RF registers after channel-set is
* complete. Output format matches `iwpriv <iface> read 4,<addr>` /
* `iwpriv <iface> rfr <path> <addr>` so kernel and devourer dumps
* can be diffed line-by-line.
*
* BB 0xc1c[31:21] / 0xe1c[31:21] are now phydm-managed; the value
* is thermal-meter-driven so small (~1-step) divergence is expected
* if devourer and kernel sampled the chip at non-identical
* temperatures. Capture both dumps within a few seconds for clean
* parity.
*
* The IQK trigger BELOW runs phydm I/Q calibration which touches
* RF[*][0x0] / RF[*][0x8f] + BB IQK-output regs (0xc90 / 0xe90 /
* 0xcc4 / etc). We capture the canary AFTER IQK so it reflects the
* post-calibration state — matching kernel semantics where IQK is
* part of the channel-set callback. */
const auto dump_canary = [this]() {
/* Per-chip canary set. Each Jaguar variant has a different active
* RF/path footprint:
* - 8821AU is 1T1R AC: only path-A exists physically. Path-B BB-AGC
* mirror (0xe20-0xe40) and RF[B] reads return BB-init defaults
* or sentinel zero — including them just clutters the diff.
* - 8812AU is 2T2R: paths A + B are both active.
* - 8814AU is 4T4R: paths A + B + C + D BB-table state is active.
* RF[C]/RF[D] are write-only by HW design (reads return
* sentinel/zero) so we skip RF reads for paths C and D.
* The MAC anchor set is chip-independent (same regs across the
* family). */
/* Shared anchors — PHY anchors + MAC: same for every Jaguar chip. */
static const uint16_t bb_anchors[] = {
0x808, 0x80c, 0x82c, 0x830, 0x834, 0x838, 0x84c, 0x860, 0x8ac,
0x8b0, 0x8c4};
static const uint16_t bb_pathA[] = {
/* Page-C path A: TX-AGC + AGC core + IQK/DPK output regs */
0xc00, 0xc1c, 0xc20, 0xc24, 0xc28, 0xc2c, 0xc30, 0xc34, 0xc38,
0xc3c, 0xc40, 0xc50, 0xc54, 0xc60, 0xc64, 0xc68, 0xc6c, 0xc70,
0xc10, 0xc14, 0xc90, 0xc94};
static const uint16_t bb_pathB[] = {
/* Page-C path B: TX-AGC mirror of path-A + IQK/DPK output regs */
0xe1c, 0xe20, 0xe24, 0xe28, 0xe2c, 0xe30, 0xe34, 0xe38, 0xe3c,
0xe40, 0xe50, 0xe54, 0xe10, 0xe14, 0xe90, 0xe94};
static const uint16_t bb_pathC[] = {
/* 8814A path-C BB-table (0x18xx range, see hal/Hal8814PhyReg.h) */
0x1820, 0x1824, 0x1828, 0x182c, 0x1830, 0x1834, 0x1838, 0x183c,
0x1840, 0x181c, 0x1850};
static const uint16_t bb_pathD[] = {
/* 8814A path-D BB-table (0x1Axx range, see hal/Hal8814PhyReg.h) */
0x1a20, 0x1a24, 0x1a28, 0x1a2c, 0x1a30, 0x1a34, 0x1a38, 0x1a3c,
0x1a40, 0x1a1c, 0x1a50};
static const uint16_t mac_canary[] = {0x40, 0xcf, 0xf0, 0x100,
0x102, 0x420, 0x4c8, 0x508,
0x522, 0x550, 0x560, 0x610,
0x614};
static const uint32_t rf_canary[] = {0x00, 0x05, 0x18, 0x42, 0x65, 0x8f};
/* Chip-dependent path mask. */
const auto ictype = _eepromManager->version_id.ICType;
const bool has_pathB = (ictype != CHIP_8821);
const bool has_pathCD = (ictype == CHIP_8814A);
_logger->info("=== DEVOURER_DUMP_CANARY (post channel-set ch={}) ===",
unsigned(_currentChannel));
for (uint16_t a : bb_anchors)
_logger->info("BB 0x{:03x} = 0x{:08X}", a, _device.rtw_read32(a));
for (uint16_t a : bb_pathA)
_logger->info("BB 0x{:03x} = 0x{:08X}", a, _device.rtw_read32(a));
if (has_pathB)
for (uint16_t a : bb_pathB)
_logger->info("BB 0x{:03x} = 0x{:08X}", a, _device.rtw_read32(a));
if (has_pathCD) {
for (uint16_t a : bb_pathC)
_logger->info("BB 0x{:04x} = 0x{:08X}", a, _device.rtw_read32(a));
for (uint16_t a : bb_pathD)
_logger->info("BB 0x{:04x} = 0x{:08X}", a, _device.rtw_read32(a));
}
for (uint16_t a : mac_canary)
_logger->info("MAC 0x{:03x} = 0x{:08X}", a, _device.rtw_read32(a));
for (uint32_t a : rf_canary)
_logger->info("RF[A] 0x{:02x} = 0x{:05X}", a,
phy_query_rf_reg(RfPath::RF_PATH_A, a, 0xfffffu));
if (has_pathB)
for (uint32_t a : rf_canary)
_logger->info("RF[B] 0x{:02x} = 0x{:05X}", a,
phy_query_rf_reg(RfPath::RF_PATH_B, a, 0xfffffu));
_logger->info("=== END DEVOURER_DUMP_CANARY ===");
};
/* Trigger I/Q calibration. Set by `phy_SwBand8812` on band
* transitions and (post-init) on the very first channel-set via
* `HalModule::rtl8812au_hal_init` → `ArmIQKOnNextChannelSet`.
* Optional override: `DEVOURER_FORCE_IQK=1` runs IQK on every
* channel-set (used for canary-diff workflow against kernel). */
if ((_needIQK || std::getenv("DEVOURER_FORCE_IQK")) &&
!std::getenv("DEVOURER_DISABLE_IQK")) {
if (_eepromManager->version_id.ICType == CHIP_8812) {
_iqk.Calibrate(_currentChannel, current_band_type,
/*is_recovery=*/false);
} else if (_eepromManager->version_id.ICType == CHIP_8814A) {
_iqk8814.Calibrate(_currentChannel, current_band_type,
/*is_recovery=*/false);
}
}
_needIQK = false;
timer.stage("iqk");
timer.total();
/* Canary dump runs LAST so it captures the post-IQK / post-pwrtrk
* state — same observation order as kernel iface reads via
* `iwpriv read 4,<addr>`. */
if (std::getenv("DEVOURER_DUMP_CANARY")) {
dump_canary();
}
}
void RadioManagementModule::phy_set_rf_reg(RfPath eRFPath, uint16_t RegAddr,
uint32_t BitMask, uint32_t Data) {
uint32_t data = Data;
//_logger->debug("RFREG;{};{:X};{:X};{:X}", (uint8_t)eRFPath, (uint)RegAddr,
// BitMask, data);
if (BitMask == 0) {
return;
}
/* RF data is 20 bits only */
if (BitMask != bLSSIWrite_data_Jaguar) {
uint32_t Original_Value, BitShift;
Original_Value = phy_RFSerialRead(eRFPath, RegAddr);
BitShift = PHY_CalculateBitShift(BitMask);
data = ((Original_Value) & (~BitMask)) | (data << (int)BitShift);
}
phy_RFSerialWrite(eRFPath, RegAddr, data);
}
struct BbRegisterDefinition {
/// LSSI data
uint32_t Rf3WireOffset;
/// wire parameter control2
uint32_t RfHSSIPara2;
/// LSSI RF readback data SI mode
uint16_t RfLSSIReadBack;
/// LSSI RF readback data PI mode 0x8b8-8bc for Path A and B
uint16_t RfLSSIReadBackPi;
};
/* RTL8814AU path C / D BB register offsets — from hal/Hal8814PhyReg.h.
* Inlined here to avoid pulling the full 8814 PHY register header (which has
* extensive #define overlap with Hal8812PhyReg.h on shared Jaguar symbols). */
constexpr uint32_t rC_LSSIWrite_8814A = 0x1890;
constexpr uint32_t rD_LSSIWrite_8814A = 0x1A90;
constexpr uint16_t rC_SIRead_8814A = 0xd88;
constexpr uint16_t rD_SIRead_8814A = 0xdC8;
constexpr uint16_t rC_PIRead_8814A = 0xd84;
constexpr uint16_t rD_PIRead_8814A = 0xdC4;
std::map<RfPath, BbRegisterDefinition> PhyRegDef = {
{RfPath::RF_PATH_A,
{
.Rf3WireOffset = rA_LSSIWrite_Jaguar,
.RfHSSIPara2 = rHSSIRead_Jaguar,
.RfLSSIReadBack = rA_SIRead_Jaguar,
.RfLSSIReadBackPi = rA_PIRead_Jaguar,
}},
{RfPath::RF_PATH_B,
{
.Rf3WireOffset = rB_LSSIWrite_Jaguar,
.RfHSSIPara2 = rHSSIRead_Jaguar,
.RfLSSIReadBack = rB_SIRead_Jaguar,
.RfLSSIReadBackPi = rB_PIRead_Jaguar,
}},
{RfPath::RF_PATH_C,
{
.Rf3WireOffset = rC_LSSIWrite_8814A,
.RfHSSIPara2 = rHSSIRead_Jaguar,
.RfLSSIReadBack = rC_SIRead_8814A,
.RfLSSIReadBackPi = rC_PIRead_8814A,
}},
{RfPath::RF_PATH_D,
{
.Rf3WireOffset = rD_LSSIWrite_8814A,
.RfHSSIPara2 = rHSSIRead_Jaguar,
.RfLSSIReadBack = rD_SIRead_8814A,
.RfLSSIReadBackPi = rD_PIRead_8814A,
}}};
uint32_t RadioManagementModule::phy_query_bb_reg(uint16_t regAddr,
uint32_t bitMask) {
return PHY_QueryBBReg8812(regAddr, bitMask);
}
uint32_t RadioManagementModule::PHY_QueryBBReg8812(uint16_t regAddr,
uint32_t bitMask) {
uint32_t ReturnValue, OriginalValue, BitShift;
/* RTW_INFO("--.PHY_QueryBBReg8812(): RegAddr(%#x), BitMask(%#x)\n", RegAddr,
* BitMask); */
OriginalValue = _device.rtw_read32(regAddr);
BitShift = PHY_CalculateBitShift(bitMask);
ReturnValue = (OriginalValue & bitMask) >> (int)BitShift;
/* RTW_INFO("BBR MASK=0x%x Addr[0x%x]=0x%x\n", BitMask, RegAddr,
* OriginalValue); */
return ReturnValue;
}
uint32_t RadioManagementModule::phy_query_rf_reg(RfPath eRFPath,
uint32_t RegAddr,
uint32_t BitMask) {
uint32_t val = phy_RFSerialRead(eRFPath, RegAddr);
if (BitMask != 0 && BitMask != 0xFFFFFFFFu) {
val = (val & BitMask) >> PHY_CalculateBitShift(BitMask);
}
return val;
}
uint32_t RadioManagementModule::phy_RFSerialRead(RfPath eRFPath,
uint32_t Offset) {
if (_eepromManager->version_id.ICType == CHIP_8814A) {
/* Kernel phy_RFRead_8814A (rtl8814a_phycfg.c:86-122): 8814 RF
* registers are read back through per-path direct BB shadow blocks,
* NOT the 8812 HSSI/LSSI serial mechanism below. This is also the
* only read path that works for paths C/D — the SI readback returns
* garbage there, which corrupted every masked (read-modify-write) RF
* write on those paths: the channel and bandwidth RMWs of RF 0x18
* destroyed all bits outside their masks on C/D at every channel
* set. */
static constexpr uint16_t kDirectBase[4] = {0x2800, 0x2c00, 0x3800,
0x3c00};
const uint16_t direct_addr = (uint16_t)(
kDirectBase[static_cast<int>(eRFPath) & 3] + (Offset & 0xff) * 4);
return phy_query_bb_reg(direct_addr, 0xfffff /* bRFRegOffsetMask */);
}
uint32_t retValue;
BbRegisterDefinition pPhyReg = PhyRegDef[eRFPath];
bool bIsPIMode = false;
/* <20120809, Kordan> CCA OFF(when entering), asked by James to avoid reading
* the wrong value. */
/* <20120828, Kordan> Toggling CCA would affect RF 0x0, skip it! */
if (Offset != 0x0 && !(IS_C_CUT(_eepromManager->version_id))) {
_device.phy_set_bb_reg(rCCAonSec_Jaguar, 0x8, 1);
}
Offset &= 0xff;
if (eRFPath == RfPath::RF_PATH_A) {
bIsPIMode = phy_query_bb_reg(0xC00, 0x4) != 0;
} else if (eRFPath == RfPath::RF_PATH_B) {
bIsPIMode = phy_query_bb_reg(0xE00, 0x4) != 0;
}
_device.phy_set_bb_reg((ushort)pPhyReg.RfHSSIPara2, bHSSIRead_addr_Jaguar,
Offset);
if (IS_C_CUT(_eepromManager->version_id)) {
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
if (bIsPIMode) {
retValue = phy_query_bb_reg(pPhyReg.RfLSSIReadBackPi, rRead_data_Jaguar);
/* RTW_INFO("[PI mode] RFR-%d Addr[0x%x]=0x%x\n", eRFPath,
* pPhyReg.rfLSSIReadBackPi, retValue); */
} else {
retValue = phy_query_bb_reg(pPhyReg.RfLSSIReadBack, rRead_data_Jaguar);
/* RTW_INFO("[SI mode] RFR-%d Addr[0x%x]=0x%x\n", eRFPath,
* pPhyReg.RfLSSIReadBack, retValue); */
}
/* <20120809, Kordan> CCA ON(when exiting), asked by James to avoid reading
* the wrong value. */
/* <20120828, Kordan> Toggling CCA would affect RF 0x0, skip it! */
if (Offset != 0x0 && !(IS_C_CUT(_eepromManager->version_id))) {
_device.phy_set_bb_reg(rCCAonSec_Jaguar, 0x8, 0);
}
return retValue;
}
void RadioManagementModule::phy_RFSerialWrite(RfPath eRFPath, uint32_t Offset,
uint32_t Data) {
BbRegisterDefinition pPhyReg = PhyRegDef[eRFPath];
Offset &= 0xff;
/* Shadow Update */
/* PHY_RFShadowWrite(adapterState, eRFPath, Offset, Data); */
/* Put write addr in [27:20] and write data in [19:00] */
auto dataAndAddr = ((Offset << 20) | (Data & 0x000fffff)) & 0x0fffffff;
/* Write Operation */
/* TODO: Dynamically determine whether using PI or SI to write RF registers.
*/
_device.phy_set_bb_reg((ushort)pPhyReg.Rf3WireOffset, bMaskDWord,
dataAndAddr);
/* RTW_INFO("RFW-%d Addr[0x%x]=0x%x\n", eRFPath, pPhyReg.Rf3WireOffset,
* DataAndAddr); */
}
void RadioManagementModule::PHY_SwitchWirelessBand8812(BandType Band) {
ChannelWidth_t current_bw = _currentChannelBw;
bool eLNA_2g = _eepromManager->ExternalLNA_2G;
const bool is_8821 =
_eepromManager->version_id.ICType == CHIP_8821;
_logger->info("[{}] {}", __func__, Band == BandType::BAND_ON_2_4G ? "2.4G" : "5G");
current_band_type = Band;
if (Band == BandType::BAND_ON_2_4G) {
/* 2.4G band */
_device.phy_set_bb_reg(rOFDMCCKEN_Jaguar, bOFDMEN_Jaguar | bCCKEN_Jaguar,
0x03);
if (is_8821) {
phy_SetRFEReg8821(Band);
} else {
/* <20131128, VincentL> Remove 0x830[3:1] setting when switching 2G/5G,
* requested by Yn. */
_device.phy_set_bb_reg(rBWIndication_Jaguar, 0x3,
0x1); /* 0x834[1:0] = 0x1 */
/* set PD_TH_20M for BB Yn user guide R27 */
_device.phy_set_bb_reg(rPwed_TH_Jaguar,
BIT13 | BIT14 | BIT15 | BIT16 | BIT17,
0x17); /* 0x830[17:13]=5'b10111 */
/* set PWED_TH for BB Yn user guide R29 */
if (current_bw == ChannelWidth_t::CHANNEL_WIDTH_20 &&
_eepromManager->version_id.RFType == RF_TYPE_1T1R &&
eLNA_2g == false) {
/* 0x830[3:1]=3'b010 */
_device.phy_set_bb_reg(rPwed_TH_Jaguar, BIT1 | BIT2 | BIT3, 0x02);
} else {
/* 0x830[3:1]=3'b100 */
_device.phy_set_bb_reg(rPwed_TH_Jaguar, BIT1 | BIT2 | BIT3, 0x04);
}
}
/* AGC table select. 8821AU uses the path-A TX scale knob at 0xC1C[11:8];
* 8812/8814 use the AGC table select bit at 0x82C[1:0]. */
if (is_8821 && IS_NORMAL_CHIP(_eepromManager->version_id)) {
_device.phy_set_bb_reg(rA_TxScale_Jaguar, 0xF00, 0);
} else {
_device.phy_set_bb_reg(rAGC_table_Jaguar, 0x3, 0);
}
if (!is_8821) {
phy_SetRFEReg8812(Band);
}
/* <20131106, Kordan> Workaround to fix CCK FA for scan issue. */
/* if( pHalData.bMPMode == FALSE) */
_device.phy_set_bb_reg(rTxPath_Jaguar, 0xf0, 0x1);
_device.phy_set_bb_reg(rCCK_RX_Jaguar, 0x0f000000, 0x1);
/* CCK_CHECK_en */
_device.rtw_write8(
REG_CCK_CHECK_8812,
(uint8_t)(_device.rtw_read8(REG_CCK_CHECK_8812) & (~BIT7)));
} else {
/* 5G band */
/* CCK_CHECK_en */
_device.rtw_write8(REG_CCK_CHECK_8812,
(uint8_t)(_device.rtw_read8(REG_CCK_CHECK_8812) | BIT7));
uint16_t count = 0;
uint16_t reg41A = _device.rtw_read16(REG_TXPKT_EMPTY);
reg41A &= 0x30;
while ((reg41A != 0x30) && (count < 50)) {
using namespace std::chrono_literals;
std::this_thread::sleep_for(50ms);
reg41A = _device.rtw_read16(REG_TXPKT_EMPTY);
reg41A &= 0x30;
count++;
}
if (count != 0) {
_logger->info("PHY_SwitchWirelessBand8812(): Switch to 5G Band. Count = "
"{:4X} reg41A={:4X}",
count, reg41A);
}
/* 2012/02/01, Sinda add registry to switch workaround without long-run
* verification for scan issue. */
_device.phy_set_bb_reg(rOFDMCCKEN_Jaguar, bOFDMEN_Jaguar | bCCKEN_Jaguar,
0x03);
if (is_8821) {
phy_SetRFEReg8821(Band);
} else {
/* <20131128, VincentL> Remove 0x830[3:1] setting when switching 2G/5G,
* requested by Yn. */
_device.phy_set_bb_reg(rBWIndication_Jaguar, 0x3,
0x2); /* 0x834[1:0] = 0x2 */
/* set PD_TH_20M for BB Yn user guide R27 */
_device.phy_set_bb_reg(rPwed_TH_Jaguar,
BIT13 | BIT14 | BIT15 | BIT16 | BIT17,
0x15); /* 0x830[17:13]=5'b10101 */
/* set PWED_TH for BB Yn user guide R29 */
/* 0x830[3:1]=3'b100 */
_device.phy_set_bb_reg(rPwed_TH_Jaguar, BIT1 | BIT2 | BIT3, 0x04);
}
/* AGC table select (same family-split as 2.4G branch). */
if (is_8821 && IS_NORMAL_CHIP(_eepromManager->version_id)) {
_device.phy_set_bb_reg(rA_TxScale_Jaguar, 0xF00, 1);
} else {
_device.phy_set_bb_reg(rAGC_table_Jaguar, 0x3, 1);
}
if (!is_8821) {
phy_SetRFEReg8812(Band);
}
/* <20131106, Kordan> Workaround to fix CCK FA for scan issue. */
/* if( pHalData.bMPMode == FALSE) */
_device.phy_set_bb_reg(rTxPath_Jaguar, 0xf0, 0x0);
_device.phy_set_bb_reg(rCCK_RX_Jaguar, 0x0f000000, 0xF);
}
phy_SetBBSwingByBand_8812A(Band);
}
/* 8821AU has a single RFE pinmux register set (path A only). The 2.4G/5G
* paths split on EFUSE ExternalLNA_2G — when an external LNA is present the
* pinmux is configured for the EXT_LNA cell of the RFE control plane (mux
* values 0x2 with INV[20]=1). Ported from svpcom/rtl8812au v5.2.20 via
* PR#22. */
void RadioManagementModule::phy_SetRFEReg8821(BandType Band) {
if (Band == BandType::BAND_ON_2_4G) {
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, 0xF000, 0x7);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, 0xF0, 0x7);
if (_eepromManager->ExternalLNA_2G) {
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, BIT20, 1);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, BIT22, 0);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, BIT2 | BIT1 | BIT0, 0x2);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, BIT10 | BIT9 | BIT8, 0x2);
} else {
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, BIT20, 0);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, BIT22, 0);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, BIT2 | BIT1 | BIT0, 0x7);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, BIT10 | BIT9 | BIT8, 0x7);
}
} else {
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, 0xF000, 0x5);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, 0xF0, 0x4);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, BIT20, 0);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, BIT22, 0);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, BIT2 | BIT1 | BIT0, 0x7);
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, BIT10 | BIT9 | BIT8, 0x7);
}
}
void RadioManagementModule::phy_SetRFEReg8812(BandType Band) {
uint32_t u1tmp = 0;
if (Band == BandType::BAND_ON_2_4G) {
switch (_eepromManager->rfe_type) {
case 0:
case 2:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x000);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x000);
break;
case 1: {
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x000);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x000);
} break;
case 3:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x54337770);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x54337770);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
_device.phy_set_bb_reg(r_ANTSEL_SW_Jaguar, 0x00000303, 0x1);
break;
case 4:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x001);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x001);
break;
case 5:
_device.rtw_write8(rA_RFE_Pinmux_Jaguar + 2, 0x77);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
u1tmp = _device.rtw_read8(rA_RFE_Inv_Jaguar + 3);
u1tmp &= ~BIT0;
_device.rtw_write8(rA_RFE_Inv_Jaguar + 3, (uint8_t)(u1tmp));
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x000);
break;
case 6:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x07772770);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x07772770);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMaskDWord, 0x00000077);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMaskDWord, 0x00000077);
break;
default:
break;
}
} else {
switch (_eepromManager->rfe_type) {
case 0:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77337717);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77337717);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
break;
case 1: {
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77337717);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77337717);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x000);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x000);
} break;
case 2:
case 4:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77337777);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77337777);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
break;
case 3:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x54337717);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x54337717);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
_device.phy_set_bb_reg(r_ANTSEL_SW_Jaguar, 0x00000303, 0x1);
break;
case 5:
_device.rtw_write8(rA_RFE_Pinmux_Jaguar + 2, 0x33);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77337777);
u1tmp = _device.rtw_read8(rA_RFE_Inv_Jaguar + 3);
_device.rtw_write8(rA_RFE_Inv_Jaguar + 3, (uint8_t)(u1tmp |= BIT0));
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMask_RFEInv_Jaguar, 0x010);
break;
case 6:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x07737717);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x07737717);
_device.phy_set_bb_reg(rA_RFE_Inv_Jaguar, bMaskDWord, 0x00000077);
_device.phy_set_bb_reg(rB_RFE_Inv_Jaguar, bMaskDWord, 0x00000077);
break;
default:
break;
}
}
}
/* Port of upstream `PHY_SetRFEReg8814A` band-switch path (bInit=false)
* from `aircrack-ng/rtl8812au/hal/rtl8814a/rtl8814a_phycfg.c:1567`.
* 8814AU has its own RFE pinmux for all four paths (A/B/C/D) at
* 0xCB0 / 0xEB0 / 0x18B4 / 0x1AB4 plus the 0x1ABC[27:20] tail;
* the 8812 RFE function never touches the path-C/D regs so running
* it on 8814 leaves the LNA in SW-managed mode (visible as RF[A] 0x00
* bit 15 = 1 in canary diff) and the path-C/D antenna mux unprogrammed.
*
* rfe_type comes from EFUSE. Cases 0/1/2 are the only ones upstream
* 8814A handles; other rfe_type values fall through to case 0/default. */
void RadioManagementModule::phy_SetRFEReg8814A(BandType Band) {
const auto rfe_type = _eepromManager->rfe_type;
if (Band == BandType::BAND_ON_2_4G) {
switch (rfe_type) {
case 2:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x72707270);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x72707270);
_device.phy_set_bb_reg(0x18B4, bMaskDWord, 0x72707270); /* rC_RFE_Pinmux */
_device.phy_set_bb_reg(0x1AB4, bMaskDWord, 0x77707770); /* rD_RFE_Pinmux */
_device.phy_set_bb_reg(0x1ABC, 0x0FF00000, 0x72); /* [27:20] */
break;
case 1:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(0x18B4, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(0x1AB4, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(0x1ABC, 0x0FF00000, 0x77);
break;
case 0:
default:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x77777777);
_device.phy_set_bb_reg(0x18B4, bMaskDWord, 0x77777777);
/* Upstream case-0/default skips rD_RFE_Pinmux entirely. */
_device.phy_set_bb_reg(0x1ABC, 0x0FF00000, 0x77);
break;
}
} else {
switch (rfe_type) {
case 2:
/* Kernel PHY_SetRFEReg8814A 5G case 2: 0x37173717 on A/B/C — the
* previous 0x33173717 carried rfe-1's [27:24] nibble (copy slip
* between adjacent cases; flagged independently by two audit
* passes against the rtl8814au reference). */
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x37173717);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x37173717);
_device.phy_set_bb_reg(0x18B4, bMaskDWord, 0x37173717);
_device.phy_set_bb_reg(0x1AB4, bMaskDWord, 0x77177717);
_device.phy_set_bb_reg(0x1ABC, 0x0FF00000, 0x37);
break;
case 1:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x33173317);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x33173317);
_device.phy_set_bb_reg(0x18B4, bMaskDWord, 0x33173317);
_device.phy_set_bb_reg(0x1AB4, bMaskDWord, 0x77177717);
_device.phy_set_bb_reg(0x1ABC, 0x0FF00000, 0x33);
break;
case 0:
default:
_device.phy_set_bb_reg(rA_RFE_Pinmux_Jaguar, bMaskDWord, 0x54775477);
_device.phy_set_bb_reg(rB_RFE_Pinmux_Jaguar, bMaskDWord, 0x54775477);
_device.phy_set_bb_reg(0x18B4, bMaskDWord, 0x54775477);
_device.phy_set_bb_reg(0x1AB4, bMaskDWord, 0x54775477);
_device.phy_set_bb_reg(0x1ABC, 0x0FF00000, 0x54);
break;
}
}
}
void RadioManagementModule::InitRFEGpio8814A() {
/* Mirror of the kernel PHY_SetRFEReg8814A(bInit=TRUE) branch
* (rtl8814a_phycfg.c:1026-1039, called once from usb_halinit.c:1279).
* Enables the GPIO pins that physically drive the external RFE (PA + T/R
* antenna switch). devourer's per-band phy_SetRFEReg8814A programs only the
* RFE pinmux *functions* (0xCB0/0xEB0/0x18B4/0x1AB4); without this one-time
* pin-select the pins are never enabled as RFE outputs, so the external
* PA/T-R switch never engages on TX — TX submits succeed (err:0) but nothing
* reaches the air, while RX still works (issue surfaced after the chip
* stopped inheriting a prior kernel-set GPIO state). */
const auto rfe_type = _eepromManager->rfe_type;
constexpr uint16_t REG_GPIO_IO_SEL_8814A = 0x0042; /* byte 2 of the 0x40 dword */
_device.phy_set_bb_reg(0x1994, 0xf, 0xf); /* 0x1994[3:0] = 0xf */
const uint8_t sel = _device.rtw_read8(REG_GPIO_IO_SEL_8814A);
/* rfe_type 1/2 -> 0x40[23:20]=0xf (0x42 |= 0xf0); type 0 -> [23:22]=11b (0xc0) */
const uint8_t orv = (rfe_type == 0) ? 0xc0 : 0xf0;
_device.rtw_write8(REG_GPIO_IO_SEL_8814A, (uint8_t)(sel | orv));
_logger->info("8814A RFE GPIO pin-select (rfe_type={}, 0x42 |= 0x{:02x})",
(int)rfe_type, orv);
}
/* Port of upstream `phy_SetBwRegAdc_8814A`
* (rtl8814a_phycfg.c:1454). Programs rRFMOD_Jaguar (0x8AC) bits [1:0]
* per bandwidth; both bands write the same value here. */
void RadioManagementModule::phy_SetBwRegAdc_8814A(BandType Band,
ChannelWidth_t bw) {
(void)Band;
uint32_t val;
switch (bw) {
case ChannelWidth_t::CHANNEL_WIDTH_20:
val = 0x0;
break;
case ChannelWidth_t::CHANNEL_WIDTH_40:
val = 0x1;
break;
case ChannelWidth_t::CHANNEL_WIDTH_80:
val = 0x2;
break;
default: