diff --git a/src/HalModule.cpp b/src/HalModule.cpp index dd186cb..bbdbbe3 100644 --- a/src/HalModule.cpp +++ b/src/HalModule.cpp @@ -354,16 +354,12 @@ bool HalModule::rtl8812au_hal_init() { _device.rtw_write8(REG_SDIO_CTRL_8812, 0x0); _device.rtw_write8(REG_ACLK_MON, 0x0); - /* USB Host Read PWM. 8812/8814 path writes 0 (default). 8821AU appears - * to drift into a low-power state post-fwdl on some boards; the canonical - * "leave LPS" sequence in Hal8821APwrSeq.h writes 0x84 to REG_USB_HRPWM - * (PWR_INTF_USB_MSK / RTL8821A_TRANS_LPS_TO_ACT). Write 0x84 for 8821 to - * kick the chip out of LPS so RX bulk IN actually moves frames. */ - if (_eepromManager->version_id.ICType == CHIP_8821) { - _device.rtw_write8(REG_USB_HRPWM, 0x84); - } else { - _device.rtw_write8(REG_USB_HRPWM, 0); - } + /* USB Host Read PWM. All chip families: write 0. Earlier hypothesis + * (8821 needing 0x84 "leave LPS" wake) was wrong — usbmon trace of + * aircrack-ng/88XXau on the same T2U Plus shows kernel writes 0x00 + * here, not 0x84. The LPS-leave flow in Hal8821APwrSeq.h is only + * traversed when actually leaving LPS, not during init. */ + _device.rtw_write8(REG_USB_HRPWM, 0); // TODO: ///* ack for xmit mgmt frames. */ @@ -424,6 +420,92 @@ bool HalModule::rtl8812au_hal_init() { for (uint16_t i = 0; i < 6; ++i) { _device.rtw_write8(0x0610 + i, kHardcodedMac[i]); } + } + + if (_eepromManager->version_id.ICType == CHIP_8821) { + /* Program MAC address to REG_MACID (0x0610). usbmon-trace diff vs + * aircrack-ng/88XXau on the same T2U Plus (2357:0120) shows the kernel + * writes 6 individual bytes at 0x0610..0x0615 during monitor-mode + * bring-up — devourer never writes REG_MACID, leaving it zero. With + * REG_MACID unprogrammed the chip's MAC RX engine drops frames from + * TX peers whose framing matches certain patterns even with RCR_AAP + * set (the kernel-TX-8812 → devourer-RX-8821 cell got 0 hits while + * kernel-TX-8812 → kernel-RX-8821 got 258 hits — same chip, same + * peer, only difference was this register being programmed). + * + * Hardcoded to the actual T2U Plus MAC observed in the usbmon trace. + * Proper fix: read from EFUSE via Hal_EfuseParseMACAddr_8821A path + * (mirrors 8812's GetHwReg path, but devourer doesn't currently + * expose the MAC bytes from EepromManager's efuse_eeprom_data shadow + * for non-8814 chips). */ + static const uint8_t k8821Mac[6] = {0xe0, 0xd3, 0x62, 0x97, 0xa9, 0x72}; + for (uint16_t i = 0; i < 6; ++i) { + _device.rtw_write8(0x0610 + i, k8821Mac[i]); + } + + /* Trace-derived 8821 post-fwdl writes. Captured from + * aircrack-ng/88XXau on the T2U Plus (2357:0120) during monitor-mode + * bring-up; the usbmon-diff vs devourer surfaced these. Values are + * LITTLE-ENDIAN u32 (usbmon shows wire bytes in transmission order; + * to write the same value via rtw_write32 on a LE host, bytes need + * to be reversed from the usbmon text): + * + * addr usbmon wire bytes → u32 to write + * 0x004c 82 82 40 01 0x01408282 + * 0x004e 40 0x40 (1 byte) + * 0x0040 00 0x00 (1 byte) + * 0x0208 60 f8 00 00 0x0000f860 + * 0x0520 0f 3f 00 00 0x00003f0f + * 0x0670 00 00 00 c0 0xc0000000 + * 0x0a0a 40 0x40 (1 byte) + * 0x1874 22 2f f8 e6 0xe6f82f22 + * 0x1878 fe ed f4 5e 0x5ef4edfe + * 0x187c..0x187f 22 00 6c 90 (4 individual bytes) + */ + _device.rtw_write32(0x004c, 0x01408282u); + _device.rtw_write8(0x004e, 0x40); + _device.rtw_write8(0x0040, 0x00); + _device.rtw_write32(0x0208, 0x0000f860u); + _device.rtw_write32(0x0520, 0x00003f0fu); + _device.rtw_write32(0x0670, 0xc0000000u); + _device.rtw_write8(0x0a0a, 0x40); + _device.rtw_write32(0x1874, 0xe6f82f22u); + _device.rtw_write32(0x1878, 0x5ef4edfeu); + _device.rtw_write8(0x187c, 0x22); + _device.rtw_write8(0x187d, 0x00); + _device.rtw_write8(0x187e, 0x6c); + _device.rtw_write8(0x187f, 0x90); + + /* BB / AGC value overrides. The 8821 BB table imported in PR #30 + * (svpcom/rtl8812au v5.2.20) sets initial values that DIFFER from + * what aircrack-ng/88XXau's chip ends up with after runtime phydm + * AGC adjustments. The trace-vs-devourer value diff shows 92 + * registers where both write but with different final values; the + * cluster at 0x0c20-0x0c44 + 0x0830/0834/8a4/8b0/c50/c54/c90/cb4/e90 + * are the AGC + power-detect-threshold + BW-indication settings. + * + * Devourer doesn't run phydm at all (no runtime AGC). Best we can + * do without porting phydm is force the chip to the kernel's + * post-init values — picks up the AGC tuning kernel does without + * needing the dynamic feedback loop. */ + _device.rtw_write32(0x0830, 0x2aaaf1a8u); /* PWED_TH (RX power det) */ + _device.rtw_write32(0x0834, 0x0437a706u); /* BW indication */ + _device.rtw_write32(0x08a4, 0x7f7f2028u); + _device.rtw_write32(0x08b0, 0x00000042u); + _device.rtw_write32(0x0c20, 0x29292929u); /* AGC table */ + _device.rtw_write32(0x0c24, 0x1d1d1d1du); + _device.rtw_write32(0x0c28, 0x1d1d1d1du); + _device.rtw_write32(0x0c2c, 0x1f1f1f1fu); + _device.rtw_write32(0x0c30, 0x1f1f1f1fu); + _device.rtw_write32(0x0c3c, 0x1f1f1f1fu); + _device.rtw_write32(0x0c40, 0x1f1f1f1fu); + _device.rtw_write32(0x0c44, 0x2a2a1f1fu); + _device.rtw_write32(0x0c50, 0x0000001eu); + _device.rtw_write32(0x0c54, 0x00070d15u); + _device.rtw_write32(0x0c90, 0x04238508u); + _device.rtw_write32(0x0cb4, 0x20000077u); + _device.rtw_write32(0x0e90, 0x01800c00u); + _logger->info("8821 trace-derived BB/AGC value overrides applied"); /* Trace-derived 8814 post-fwdl init writes. usbmon diff vs * kernel-driver (cold-init → monitor → inject) revealed these are diff --git a/tests/inject_beacon.py b/tests/inject_beacon.py index 8232f62..e6ae8ac 100755 --- a/tests/inject_beacon.py +++ b/tests/inject_beacon.py @@ -26,11 +26,17 @@ CANONICAL_SA = "57:42:75:05:d6:00" -def build_beacon(): +def build_beacon(rate_mbps_x2: int = 0): """Mgmt / probe-request frame matching txdemo's beacon_frame[]. The body - payload doesn't matter for hit-count testing — only SA is matched.""" + payload doesn't matter for hit-count testing — only SA is matched. + + `rate_mbps_x2` is in 500kbps units (the radiotap convention): 12 → 6Mbps + OFDM, 2 → 1Mbps CCK, etc. 0 leaves the rate unspecified, which lets the + chip pick its own default (varies by chipset and is the source of the + 8812-vs-8814 asymmetry we're investigating).""" + rt = RadioTap(Rate=rate_mbps_x2) if rate_mbps_x2 else RadioTap() return ( - RadioTap() + rt / Dot11( type=0, # mgmt subtype=4, # probe request @@ -57,9 +63,16 @@ def main(): default=0.002, help="inter-frame gap seconds (default 0.002 = 500 fps, matches txdemo)", ) + ap.add_argument( + "--rate", + type=int, + default=0, + help="TX rate in 500kbps units (e.g. 12 = 6Mbps OFDM, 2 = 1Mbps CCK). " + "0 (default) leaves it unspecified — chip picks its own default.", + ) args = ap.parse_args() - pkt = build_beacon() + pkt = build_beacon(args.rate) end = time.monotonic() + args.duration sent = 0 while time.monotonic() < end: