From 292d28a2afff4a145c30bd886bdb92172c0a45d4 Mon Sep 17 00:00:00 2001 From: Roman Lut <11955117+RomanLut@users.noreply.github.com> Date: Tue, 19 May 2026 12:20:29 +0200 Subject: [PATCH] working rtl8821au support --- hal/basic_types.h | 10 + src/EepromManager.cpp | 123 ++------ src/FrameParser.h | 4 + src/HalModule.cpp | 625 ++++++++++++++++++++++++++-------------- src/HalModule.h | 2 + src/Radiotap.c | 4 +- src/RtlJaguarDevice.cpp | 10 +- src/RtlJaguarDevice.h | 14 +- src/RtlUsbAdapter.cpp | 172 +++++------ src/RtlUsbAdapter.h | 15 +- 10 files changed, 572 insertions(+), 407 deletions(-) diff --git a/hal/basic_types.h b/hal/basic_types.h index 514b8dc..b3209f2 100644 --- a/hal/basic_types.h +++ b/hal/basic_types.h @@ -24,12 +24,22 @@ #define le16_to_cpu(x) ((__u16)(x)) +#ifndef LE_BITS_TO_4BYTE #define LE_BITS_TO_4BYTE(__pStart, __BitOffset, __BitLen) \ ((LE_P4BYTE_TO_HOST_4BYTE(__pStart) >> (__BitOffset)) & \ BIT_LEN_MASK_32(__BitLen)) +#endif + +#ifndef LE_P4BYTE_TO_HOST_4BYTE #define LE_P4BYTE_TO_HOST_4BYTE(__pStart) (le32_to_cpu(*((u32 *)(__pStart)))) +#endif + +#ifndef BIT_LEN_MASK_32 #define BIT_LEN_MASK_32(__BitLen) ((u32)(0xFFFFFFFF >> (32 - (__BitLen)))) +#endif +#ifndef ReadLE2Byte #define ReadLE2Byte(_ptr) le16_to_cpu(*((u16 *)(_ptr))) +#endif #endif /* BASIC_TYPES_H */ diff --git a/src/EepromManager.cpp b/src/EepromManager.cpp index bf239de..e2d2663 100644 --- a/src/EepromManager.cpp +++ b/src/EepromManager.cpp @@ -80,119 +80,48 @@ void EepromManager::LateInitFor8814A() { LNAType_2G, LNAType_5G); } -/* RTL8821AU is Jaguar-family silicon (CHIP_8821 = 7 in Realtek's HalVerDef), - * 1T1R AC + BT combo. It responds to REG_SYS_CFG with an 8812-shaped value - * (low byte 0x35), so plain SYS_CFG-bit inspection misidentifies it as 8812. - * Distinguish by VID:PID at probe time. Genuine 8821AU SKUs we know about: - * - TP-Link Archer T2U Plus / T4U / etc: 2357:0120 / 011E / 0122 - * - Realtek reference PIDs: 0bda:0820 / 0821 / 0823 / 8822 - * - OEM rebadges (Senao, ASUS, Edimax, D-Link, Hawking, …): - * 0411:0242 / 029B (Senao) - * 0846:9052 (ASUS) - * 0e66:0023 (Hawking) - * 2001:3314 / 3318 (D-Link) - * 20f4:804b (TRENDnet) - * 7392:a811/a812/a813/b611 (Edimax) - * 3823:6249 (HP/MaxNet) - * 2019:ab32 (Planex) - * 04bb:0953 (I-O Data) - * 056e:4007 / 400e / 400f (ELECOM) - * - * NOT included on purpose: - * - 0bda:0811 / a811 / b811 — those are 8811AU (1T1R cut of 8812 silicon); - * they ride on CHIP_8812 + RFType=1T1R, NOT through the 8821 HAL. - * - 0bda:c811 — RTL8811CU, unrelated silicon, not supported here. */ -static bool is_rtl8821a_pid(uint16_t vid, uint16_t pid) { - const uint32_t key = (static_cast(vid) << 16) | pid; - switch (key) { - case 0x0BDA0820: - case 0x0BDA0821: - case 0x0BDA0823: - case 0x0BDA8822: - case 0x0411'0242: - case 0x0411'029B: - case 0x04BB'0953: - case 0x056E'4007: - case 0x056E'400E: - case 0x056E'400F: - case 0x0846'9052: - case 0x0E66'0023: - case 0x2001'3314: - case 0x2001'3318: - case 0x2019'AB32: - case 0x20F4'804B: - case 0x2357'011E: - case 0x2357'0120: - case 0x2357'0122: - case 0x3823'6249: - case 0x7392'A811: - case 0x7392'A812: - case 0x7392'A813: - case 0x7392'B611: - return true; - default: - return false; - } -} - void EepromManager::read_chip_version_8812a(RtlUsbAdapter device) { uint32_t value32 = device.rtw_read32(REG_SYS_CFG); _logger->info("read_chip_version_8812a SYS_CFG(0x{:X})=0x{:08X}", REG_SYS_CFG, value32); - const uint16_t pid = device.idProduct(); - const uint16_t vid = device.idVendor(); - const bool is_8814a = (pid == 0x8813); - const bool is_8821 = !is_8814a && is_rtl8821a_pid(vid, pid); - - HAL_IC_TYPE_E ic_type; - if (is_8814a) { - ic_type = CHIP_8814A; - } else if (is_8821) { - ic_type = CHIP_8821; - } else { - ic_type = CHIP_8812; - } - - /* RFType: - * 8814AU → 4T4R RF (baseband caps at 3 spatial streams) - * 8821AU → always 1T1R (Jaguar-family AC+BT combo, single chain) - * 8812/8811AU → SYS_CFG bit 27 (RF_TYPE_ID): set on 1T1R, clear on 2T2R */ - HAL_RF_TYPE_E rf_type; - if (is_8814a) { - rf_type = RF_TYPE_4T4R; - } else if (is_8821) { - rf_type = RF_TYPE_1T1R; - } else { - rf_type = - (value32 & RF_TYPE_ID) ? RF_TYPE_1T1R : RF_TYPE_2T2R; - } + const bool is_8814a = device.idProduct() == 0x8813; + const bool is_8821 = !is_8814a && device.IsRtl8821A(); version_id = { - .ICType = ic_type, + .ICType = is_8814a ? CHIP_8814A : (is_8821 ? CHIP_8821 : CHIP_8812), .ChipType = (value32 & RTL_ID) ? TEST_CHIP : NORMAL_CHIP, - .VendorType = (value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC, - .RFType = rf_type, + .VendorType = CHIP_VENDOR_TSMC, + .RFType = is_8814a ? RF_TYPE_4T4R + : (is_8821 ? RF_TYPE_1T1R : RF_TYPE_2T2R), }; - /* Manual override: force 1T1R even when SYS_CFG reports 2T2R - * (useful if EFUSE/strap is mis-burnt on a 1T1R board). Only meaningful - * for the 8812-family; 8814AU keeps its 4T4R RFType, 8821AU is already 1T1R. */ + if (is_8821) { + uint32_t vendor = (value32 & EXT_VENDOR_ID) >> EXT_VENDOR_ID_SHIFT; + switch (vendor) { + case 1: + version_id.VendorType = CHIP_VENDOR_SMIC; + break; + case 2: + version_id.VendorType = CHIP_VENDOR_UMC; + break; + default: + version_id.VendorType = CHIP_VENDOR_TSMC; + break; + } + } else if (!is_8814a) { + version_id.VendorType = + (value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC; + } + if (!is_8814a && !is_8821 && registry_priv::special_rf_path == 1) { - version_id.RFType = RF_TYPE_1T1R; + version_id.RFType = RF_TYPE_1T1R; /* RF_1T1R; */ } - /* IC version (CUT). Upstream rtw88's rtw88xxa_read_efuse does `cut_version - * += 1` ONLY for 8812A (chip->id == RTW_CHIP_TYPE_8812A) — for 8814A the - * raw chip-reported value is used as-is. Our previous unconditional +1 - * was breaking PhyTableLoader's check_positive matching on 8814 (the - * "cut_for_para" byte at c1[27:24] never matched the table's expected - * value), which is why AGC_TBL_081C reads back as 0 (not loaded) on our - * chip while rtw88's same chip shows 0x017e0303. */ const uint32_t raw_cut = (value32 & CHIP_VER_RTL_MASK) >> CHIP_VER_RTL_SHIFT; version_id.CUTVersion = - (HAL_CUT_VERSION_E)(is_8814a ? raw_cut : (raw_cut + 1)); + (HAL_CUT_VERSION_E)(is_8814a ? raw_cut : (raw_cut + (is_8821 ? 0 : 1))); version_id.ROMVer = 0; /* ROM code version. */ diff --git a/src/FrameParser.h b/src/FrameParser.h index 77caf8d..275066f 100644 --- a/src/FrameParser.h +++ b/src/FrameParser.h @@ -34,9 +34,13 @@ typedef unsigned int u32; #define WriteLE2Byte(_ptr, _val) ((*((u16*)(_ptr))) = cpu_to_le16(_val)) #define WriteLE1Byte(_ptr, _val) ((*((u8*)(_ptr))) = ((u8)(_val))) +#ifndef BIT_LEN_MASK_32 #define BIT_LEN_MASK_32(__BitLen) ((u32)(0xFFFFFFFF >> (32 - (__BitLen)))) +#endif +#ifndef LE_P4BYTE_TO_HOST_4BYTE #define LE_P4BYTE_TO_HOST_4BYTE(__pStart) (le32_to_cpu(*((u32*)(__pStart)))) +#endif #define BIT_OFFSET_LEN_MASK_32(__BitOffset, __BitLen) ((u32)(BIT_LEN_MASK_32(__BitLen) << (__BitOffset))) diff --git a/src/HalModule.cpp b/src/HalModule.cpp index bbdbbe3..502ed8c 100644 --- a/src/HalModule.cpp +++ b/src/HalModule.cpp @@ -105,15 +105,23 @@ bool HalModule::rtl8812au_hal_init() { * REG_OPT_CTRL+2=0x05/0x07). The 8814 pre-fwdl state is set up by the * 242-op rtw88-mimic inside FirmwareDownload_8814A instead. * + * 8821AU keeps the local power-on flow, but skips the 8812 RF path reset + * pokes here. The Linux RTL8821A path powers it through the normal flow and + * later loads only the single RF-A table. + * * POST-fwdl init flow (Queue/Page/WMAC/MSR/Aggregation/BB/RF) is run for * both chips — these write to chip-version-agnostic registers and the * functions that do diverge dispatch internally on ICType. */ const bool is_8814a = _eepromManager->version_id.ICType == CHIP_8814A; + const bool is_8821 = _eepromManager->version_id.ICType == CHIP_8821; if (!is_8814a) { - _device.rtw_write8(REG_RF_CTRL, 5); - _device.rtw_write8(REG_RF_CTRL, 7); - _device.rtw_write8(REG_RF_B_CTRL_8812, 5); - _device.rtw_write8(REG_RF_B_CTRL_8812, 7); + if (!is_8821) + { + _device.rtw_write8(REG_RF_CTRL, 5); + _device.rtw_write8(REG_RF_CTRL, 7); + _device.rtw_write8(REG_RF_B_CTRL_8812, 5); + _device.rtw_write8(REG_RF_B_CTRL_8812, 7); + } // If HW didn't go through a complete de-initial procedure, // it probably occurs some problem for double initial procedure. @@ -130,7 +138,10 @@ bool HalModule::rtl8812au_hal_init() { * runs LLT init AFTER fw boot, and doing it pre-fw on 8814 breaks the * beacon-queue fwdl path (the chip silently rejects bulk OUT writes). */ if (_eepromManager->version_id.ICType != CHIP_8814A) { - if (!InitLLTTable8812A(TX_PAGE_BOUNDARY_8812)) { + const uint8_t txpktbuf_bndy = is_8821 + ? TX_PAGE_BOUNDARY_8821 + : TX_PAGE_BOUNDARY_8812; + if (!InitLLTTable8812A(txpktbuf_bndy)) { _logger->error("InitLLTTable8812A failed"); return false; } @@ -198,11 +209,22 @@ bool HalModule::rtl8812au_hal_init() { llt); } } else { - _InitQueueReservedPage_8812AUsb(); - _InitTxBufferBoundary_8812AUsb(); + if (is_8821) + { + _InitQueueReservedPage_8821AUsb(); + _InitTxBufferBoundary_8821AUsb(); + } + else + { + _InitQueueReservedPage_8812AUsb(); + _InitTxBufferBoundary_8812AUsb(); + } _InitQueuePriority_8812AUsb(); _InitPageBoundary_8812AUsb(); - _InitTransferPageSize_8812AUsb(); + if (!is_8821) + { + _InitTransferPageSize_8812AUsb(); + } } // Get Rx PHY status in order to report RSSI and others. @@ -228,6 +250,15 @@ bool HalModule::rtl8812au_hal_init() { value8 = _device.rtw_read8(REG_CR); _device.rtw_write8(REG_CR, (uint8_t)(value8 | MACTXEN | MACRXEN)); + if (is_8821) + { + uint8_t sysCfg3 = _device.rtw_read8(REG_SYS_CFG + 3); + if ((sysCfg3 & BIT0) != 0) + { + _device.rtw_write8(0x7c, (uint8_t)(_device.rtw_read8(0x7c) | BIT6)); + } + } + _device.rtw_write16(REG_PKT_VO_VI_LIFE_TIME, 0x0400); /* unit: 256us. 256ms */ _device.rtw_write16(REG_PKT_BE_BK_LIFE_TIME, 0x0400); /* unit: 256us. 256ms */ @@ -404,174 +435,6 @@ bool HalModule::rtl8812au_hal_init() { uint16_t cr_final = static_cast(cr_observed | cr_min); _device.rtw_write16(REG_CR, cr_final); _device.rtw_write16(REG_RXFLTMAP2, 0xFFFF); - _logger->info( - "post-init final: REG_CR observed=0x{:04x} written=0x{:04x} " - "REG_RXFLTMAP2=0xFFFF", - cr_observed, cr_final); - - if (is_8814a) { - /* Program MAC address to REG_MACID (0x0610). usbmon-trace diff vs - * kernel-driver shows kernel writes 6 individual bytes at 0x610..0x615 - * during init; devourer never writes REG_MACID at all. Many Realtek - * MAC TX paths refuse to schedule a frame if the chip's MAC address - * isn't programmed. Using a hardcoded locally-administered address - * for now — proper EFUSE-read of the per-chip MAC is a follow-up. */ - static const uint8_t kHardcodedMac[6] = {0x02, 0x0d, 0xb0, 0xc7, 0xe4, 0xb3}; - 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 - * present in the kernel path and absent from devourer. Applied as a - * batch to bring devourer's chip state into MAC-TX-ready shape. - * - * REG_RRSR (0x0440) = 0xff0f0000 Response Rate Set - * 0x04bc = 0x00 TX queue gate - * REG_QUEUE_CTRL (0x04c6) = 0x04 Queue control - * REG_TX_PTCL_CTRL (0x520) = 0x0f2f0000 TX protocol control - * REG_RD_CTRL (0x0524) = 0x0f4fff00 RD control - * 0x0670 = 0x000000c0 NAV-related - * RA-table init at 0x0990-0x09a4 - */ - _device.rtw_write32(0x0440, 0xff0f0000u); /* REG_RRSR */ - _device.rtw_write8(0x04bc, 0x00); - _device.rtw_write8(0x04c6, 0x04); /* REG_QUEUE_CTRL */ - _device.rtw_write32(0x0520, 0x0f2f0000u); /* REG_TX_PTCL_CTRL */ - _device.rtw_write32(0x0524, 0x0f4fff00u); /* REG_RD_CTRL */ - _device.rtw_write32(0x0670, 0x000000c0u); - /* Rate-adaptation table init (final values from trace). */ - _device.rtw_write32(0x0990, 0xffff1027u); - _device.rtw_write32(0x0994, 0x0001484cu); - _device.rtw_write32(0x0998, 0x24282c30u); - _device.rtw_write32(0x099c, 0x34383c40u); - _device.rtw_write32(0x09a0, 0x44000000u); - _device.rtw_write32(0x09a4, 0x80000800u); - _logger->info("8814A: REG_MACID + trace-derived post-fwdl writes applied"); - } - - if (is_8814a) { - /* TX-validation diagnostic. Read back the registers that gate USB→TX - * dataflow to confirm what state the chip is actually in at the end of - * init. One log per register to dodge the Logger format helper's - * placeholder-overflow truncation. */ - using namespace rtl8814a; - _logger->info("8814A TX-state CR = 0x{:04x}", _device.rtw_read16(REG_CR)); - _logger->info("8814A TX-state TXPAUSE(0x522) = 0x{:02x}", - _device.rtw_read8(0x0522)); - _logger->info("8814A TX-state FWHW_TXQ_CTRL(0x420) = 0x{:08x}", - _device.rtw_read32(0x0420)); - _logger->info("8814A TX-state FIFOPAGE_CTRL_2 = 0x{:08x}", - _device.rtw_read32(REG_FIFOPAGE_CTRL_2_8814A)); - _logger->info("8814A TX-state MGQ_PGBNDY = 0x{:04x}", - _device.rtw_read16(REG_MGQ_PGBNDY_8814A)); - _logger->info("8814A TX-state FIFOPAGE_INFO_1(HPQ) = 0x{:08x}", - _device.rtw_read32(REG_FIFOPAGE_INFO_1_8814A)); - _logger->info("8814A TX-state FIFOPAGE_INFO_5(PUB) = 0x{:08x}", - _device.rtw_read32(REG_FIFOPAGE_INFO_5_8814A)); - _logger->info("8814A TX-state MCUFWDL = 0x{:08x}", - _device.rtw_read32(0x0080)); - _logger->info("8814A TX-state TXDMA_STATUS(0x210) = 0x{:08x}", - _device.rtw_read32(0x0210)); - _logger->info("8814A TX-state TXDMA_OFFSET_CHK(0x20C) = 0x{:08x}", - _device.rtw_read32(0x020C)); - /* 8-bit read of 0x423 is unreliable on 8814; surface both 8-bit and the - * byte-3 of the 32-bit FWHW_TXQ_CTRL word for comparison. */ - _logger->info("8814A TX-state HWSEQ_CTRL(0x423,8bit) = 0x{:02x}", - _device.rtw_read8(0x0423)); - _logger->info("8814A TX-state HWSEQ_CTRL(byte3 of 0x420 32bit) = 0x{:02x}", - (_device.rtw_read32(REG_FWHW_TXQ_CTRL) >> 24) & 0xFF); - _logger->info("8814A TX-state TCR(0x604) = 0x{:08x}", - _device.rtw_read32(0x0604)); - _logger->info("8814A TX-state RCR(0x608) = 0x{:08x}", - _device.rtw_read32(0x0608)); - } if (is_8814a && std::getenv("DEVOURER_OOT_REPLAY")) { /* DEVOURER_OOT_REPLAY=1 enables verbatim replay of the kernel @@ -771,14 +634,31 @@ bool HalModule::HalPwrSeqCmdParsing(WLAN_PWR_CFG *PwrSeqCmd) { * queue bulk OUTs (BIT15 of REG_FIFOPAGE_CTRL_2 stays clear) — which * blocks the IDDMA copy that loads firmware into the 8051's DMEM/IMEM. * - * Fab/cut filtering is intentionally relaxed to ALL_MSK for now: most - * pwr-seq entries are flagged ALL_MSK on both axes, and the CUT - * extraction from SYS_CFG isn't trustworthy across the Jaguar family. */ + * RTL8821AU normal silicon must use the A-cut path. Running the test-chip + * entries too can leave a replugged adapter in a different power state + * than the working Linux driver path. */ const uint8_t kIntfBit = PWR_INTF_USB_MSK; if (!(GET_PWR_CFG_INTF_MASK(PwrCfgCmd) & kIntfBit)) { AryIdx++; continue; } + uint8_t cutMask = PWR_CUT_ALL_MSK; + if (_eepromManager->version_id.ICType == CHIP_8821) + { + cutMask = _eepromManager->version_id.ChipType == NORMAL_CHIP + ? PWR_CUT_A_MSK + : PWR_CUT_TESTCHIP_MSK; + } + if (!(GET_PWR_CFG_CUT_MASK(PwrCfgCmd) & cutMask)) + { + AryIdx++; + continue; + } + if (!(GET_PWR_CFG_FAB_MASK(PwrCfgCmd) & PWR_FAB_ALL_MSK)) + { + AryIdx++; + continue; + } switch (PwrCfgCmd.cmd) { case PWR_CMD_READ: break; @@ -919,57 +799,289 @@ bool HalModule::phy_BB8814_Config_ParaFile() { return true; } -/* RTL8821AU MAC/BB/RF init via PhyTableLoader (shared phydm conditional - * encoding with 8814; arrays defined in hal/Hal8821PhyReg.h, ported from - * svpcom/rtl8812au v5.2.20). The tables are flat static uint32_t arrays so we - * compute length with std::size at the call site. */ -void HalModule::odm_read_and_config_mp_8821a_mac_reg() { - auto ctx = _eepromManager->GetPhyContext(); - PhyTableLoader::Load(array_mp_8821a_mac_reg, - sizeof(array_mp_8821a_mac_reg) / sizeof(uint32_t), ctx, - [this](uint32_t addr, uint32_t value) { - _device.rtw_write8(static_cast(addr), - static_cast(value)); - }); +//=================================================================================== +//=================================================================================== +// Replays the local RTL8821A MAC table with the legacy conditional parser. +void HalModule::odm_read_and_config_mp_8821a_mac_reg() +{ + u32 i = 0; + u8 c_cond; + bool is_matched = true, is_skipped = false; + u32 array_len = ARRAY_LENGTH(array_mp_8821a_mac_reg); + u32 *array = array_mp_8821a_mac_reg; + + u32 v1 = 0, v2 = 0, pre_v1 = 0, pre_v2 = 0; + + while ((i + 1) < array_len) + { + v1 = array[i]; + v2 = array[i + 1]; + + if (v1 & (BIT(31) | BIT(30))) + { + if (v1 & BIT(31)) + { + c_cond = (u8)((v1 & (BIT(29) | BIT(28))) >> 28); + if (c_cond == 3) + { + is_matched = true; + is_skipped = false; + } + else if (c_cond == 2) + { + is_matched = is_skipped ? false : true; + } + else + { + pre_v1 = v1; + pre_v2 = v2; + } + } + else if (v1 & BIT(30)) + { + if (is_skipped == false) + { + if (check_positive(pre_v1, pre_v2, v2)) + { + is_matched = true; + is_skipped = true; + } + else + { + is_matched = false; + is_skipped = false; + } + } + else + { + is_matched = false; + } + } + } + else if (is_matched) + { + odm_write_1byte((uint16_t)v1, (uint8_t)v2); + } + i = i + 2; + } } -void HalModule::odm_read_and_config_mp_8821a_phy_reg() { - auto ctx = _eepromManager->GetPhyContext(); - PhyTableLoader::Load(array_mp_8821a_phy_reg, - sizeof(array_mp_8821a_phy_reg) / sizeof(uint32_t), ctx, - [this](uint32_t addr, uint32_t value) { - odm_config_bb_phy_8812a(addr, 0xFFFFFFFFu, value); - }); +//=================================================================================== +//=================================================================================== +// Replays the local RTL8821A BB table with the legacy conditional parser. +void HalModule::odm_read_and_config_mp_8821a_phy_reg() +{ + uint32_t i = 0; + uint8_t c_cond; + bool is_matched = true, is_skipped = false; + uint32_t array_len = ARRAY_LENGTH(array_mp_8821a_phy_reg); + + uint32_t pre_v1 = 0, pre_v2 = 0; + + while ((i + 1) < array_len) + { + auto v1 = array_mp_8821a_phy_reg[i]; + auto v2 = array_mp_8821a_phy_reg[i + 1]; + + if ((v1 & (BIT31 | BIT30)) != 0) + { + if ((v1 & BIT31) != 0) + { + c_cond = (uint8_t)((v1 & (BIT29 | BIT28)) >> 28); + if (c_cond == 3) + { + is_matched = true; + is_skipped = false; + } + else if (c_cond == 2) + { + is_matched = is_skipped ? false : true; + } + else + { + pre_v1 = v1; + pre_v2 = v2; + } + } + else if ((v1 & BIT30) != 0) + { + if (is_skipped == false) + { + if (check_positive(pre_v1, pre_v2, v2)) + { + is_matched = true; + is_skipped = true; + } + else + { + is_matched = false; + is_skipped = false; + } + } + else + { + is_matched = false; + } + } + } + else if (is_matched) + { + odm_config_bb_phy_8812a(v1, 0xffffffff, v2); + } + + i = i + 2; + } } -void HalModule::odm_read_and_config_mp_8821a_agc_tab() { - auto ctx = _eepromManager->GetPhyContext(); - PhyTableLoader::Load(array_mp_8821a_agc_tab, - sizeof(array_mp_8821a_agc_tab) / sizeof(uint32_t), ctx, - [this](uint32_t addr, uint32_t value) { - odm_config_bb_agc_8812a(addr, 0xFFFFFFFFu, value); - }); +//=================================================================================== +//=================================================================================== +// Replays the local RTL8821A AGC table with the legacy conditional parser. +void HalModule::odm_read_and_config_mp_8821a_agc_tab() +{ + uint32_t i = 0; + uint8_t c_cond; + bool is_matched = true, is_skipped = false; + uint32_t array_len = ARRAY_LENGTH(array_mp_8821a_agc_tab); + + uint32_t pre_v1 = 0, pre_v2 = 0; + + while ((i + 1) < array_len) + { + auto v1 = array_mp_8821a_agc_tab[i]; + auto v2 = array_mp_8821a_agc_tab[i + 1]; + + if ((v1 & (BIT31 | BIT30)) != 0) + { + if ((v1 & BIT31) != 0) + { + c_cond = (uint8_t)((v1 & (BIT29 | BIT28)) >> 28); + if (c_cond == 3) + { + is_matched = true; + is_skipped = false; + } + else if (c_cond == 2) + { + is_matched = is_skipped ? false : true; + } + else + { + pre_v1 = v1; + pre_v2 = v2; + } + } + else if ((v1 & BIT30) != 0) + { + if (is_skipped == false) + { + if (check_positive(pre_v1, pre_v2, v2)) + { + is_matched = true; + is_skipped = true; + } + else + { + is_matched = false; + is_skipped = false; + } + } + else + { + is_matched = false; + } + } + } + else if (is_matched) + { + odm_config_bb_agc_8812a(v1, 0xffffffff, v2); + } + + i = i + 2; + } } -void HalModule::odm_read_and_config_mp_8821a_radioa() { - auto ctx = _eepromManager->GetPhyContext(); - PhyTableLoader::Load( - array_mp_8821a_radioa, - sizeof(array_mp_8821a_radioa) / sizeof(uint32_t), ctx, - [this](uint32_t addr, uint32_t value) { - odm_config_rf_reg_8812a(addr, value, RfPath::RF_PATH_A, - static_cast(addr)); - }); +//=================================================================================== +//=================================================================================== +// Replays the local RTL8821A RF-A table with the legacy conditional parser. +void HalModule::odm_read_and_config_mp_8821a_radioa() +{ + uint32_t i = 0; + uint8_t c_cond; + bool is_matched = true, is_skipped = false; + uint32_t array_len = ARRAY_LENGTH(array_mp_8821a_radioa); + + uint32_t pre_v1 = 0, pre_v2 = 0; + + while ((i + 1) < array_len) + { + auto v1 = array_mp_8821a_radioa[i]; + auto v2 = array_mp_8821a_radioa[i + 1]; + + if ((v1 & (BIT31 | BIT30)) != 0) + { + if ((v1 & BIT31) != 0) + { + c_cond = (uint8_t)((v1 & (BIT29 | BIT28)) >> 28); + if (c_cond == 3) + { + is_matched = true; + is_skipped = false; + } + else if (c_cond == 2) + { + is_matched = is_skipped ? false : true; + } + else + { + pre_v1 = v1; + pre_v2 = v2; + } + } + else if ((v1 & BIT30) != 0) + { + if (is_skipped == false) + { + if (check_positive(pre_v1, pre_v2, v2)) + { + is_matched = true; + is_skipped = true; + } + else + { + is_matched = false; + is_skipped = false; + } + } + else + { + is_matched = false; + } + } + } + else if (is_matched) + { + odm_config_rf_radio_a_8812a(v1, v2); + } + + i = i + 2; + } } -bool HalModule::phy_BB8821_Config_ParaFile() { +//=================================================================================== +//=================================================================================== +// Loads the local RTL8821A BB and AGC tables. +bool HalModule::phy_BB8821_Config_ParaFile() +{ odm_read_and_config_mp_8821a_phy_reg(); odm_read_and_config_mp_8821a_agc_tab(); return true; } -void HalModule::phy_RF6052_Config_ParaFile_8821() { - /* RTL8821AU is single-chain (1T1R AC+BT combo); only path A is initialised. */ +//=================================================================================== +//=================================================================================== +// Loads the single-chain RTL8821A RF-A table. +void HalModule::phy_RF6052_Config_ParaFile_8821() +{ odm_read_and_config_mp_8821a_radioa(); } @@ -1224,6 +1336,55 @@ void HalModule::_InitQueueReservedPage_8812AUsb() { _device.rtw_write32(REG_RQPN, value32); } +//=================================================================================== +//=================================================================================== +// Programs 8821AU TX queue page reservations using the working Linux layout. +void HalModule::_InitQueueReservedPage_8821AUsb() +{ + uint32_t numHQ = 0; + uint32_t numLQ = 0; + uint32_t numNQ = 0; + + if (registry_priv::wifi_spec) + { + if (_device.OutEpQueueSel & TxSele::TX_SELE_HQ) + { + numHQ = WMM_NORMAL_PAGE_NUM_HPQ_8821; + } + if (_device.OutEpQueueSel & TxSele::TX_SELE_LQ) + { + numLQ = WMM_NORMAL_PAGE_NUM_LPQ_8821; + } + if (_device.OutEpQueueSel & TxSele::TX_SELE_NQ) + { + numNQ = WMM_NORMAL_PAGE_NUM_NPQ_8821; + } + } + else + { + if (_device.OutEpQueueSel & TxSele::TX_SELE_HQ) + { + numHQ = NORMAL_PAGE_NUM_HPQ_8821; + } + if (_device.OutEpQueueSel & TxSele::TX_SELE_LQ) + { + numLQ = NORMAL_PAGE_NUM_LPQ_8821; + } + if (_device.OutEpQueueSel & TxSele::TX_SELE_NQ) + { + numNQ = NORMAL_PAGE_NUM_NPQ_8821; + } + } + + const uint32_t numPubQ = TX_TOTAL_PAGE_NUMBER_8821 - numHQ - numLQ - numNQ; + const uint8_t value8 = (uint8_t)_NPQ(numNQ); + _device.rtw_write8(REG_RQPN_NPQ, value8); + + const uint32_t value32 = + _HPQ(numHQ) | _LPQ(numLQ) | _PUBQ(numPubQ) | LD_RQPN; + _device.rtw_write32(REG_RQPN, value32); +} + void HalModule::_InitTxBufferBoundary_8812AUsb() { uint8_t txPageBoundary8812 = TX_PAGE_BOUNDARY_8812; @@ -1234,6 +1395,22 @@ void HalModule::_InitTxBufferBoundary_8812AUsb() { _device.rtw_write8(REG_TDECTRL + 1, txPageBoundary8812); } +//=================================================================================== +//=================================================================================== +// Programs 8821AU TX buffer boundaries using the working Linux layout. +void HalModule::_InitTxBufferBoundary_8821AUsb() +{ + const uint8_t txpktbuf_bndy = registry_priv::wifi_spec + ? WMM_NORMAL_TX_PAGE_BOUNDARY_8821 + : TX_PAGE_BOUNDARY_8821; + + _device.rtw_write8(REG_BCNQ_BDNY, txpktbuf_bndy); + _device.rtw_write8(REG_MGQ_BDNY, txpktbuf_bndy); + _device.rtw_write8(REG_WMAC_LBK_BF_HD, txpktbuf_bndy); + _device.rtw_write8(REG_TRXFF_BNDY, txpktbuf_bndy); + _device.rtw_write8(REG_TDECTRL + 1, txpktbuf_bndy); +} + void HalModule::_InitQueuePriority_8812AUsb() { /* 8814AU upstream collapses 3-out and 4-out into the same Three-EP priority * init. 8812 has a distinct Four-EP variant. */ @@ -1711,6 +1888,7 @@ void HalModule::_InitBeaconMaxError_8812A() { void HalModule::_InitBurstPktLen() { uint8_t speedvalue, provalue, temp; + const bool is_8821 = _eepromManager->version_id.ICType == CHIP_8821; _device.rtw_write8(0xf050, 0x01); /* usb3 rx interval */ _device.rtw_write16( @@ -1719,7 +1897,7 @@ void HalModule::_InitBurstPktLen() { _device.rtw_write8(0x289, 0xf5); /* for rxdma control */ /* 0x456 = 0x70, sugguested by Zhilin */ - _device.rtw_write8(REG_AMPDU_MAX_TIME_8812, 0x70); + _device.rtw_write8(REG_AMPDU_MAX_TIME_8812, is_8821 ? 0x5e : 0x70); _device.rtw_write32(REG_AMPDU_MAX_LENGTH_8812, 0xffffffff); _device.rtw_write8(REG_USTIME_TSF, 0x50); @@ -1727,6 +1905,12 @@ void HalModule::_InitBurstPktLen() { speedvalue = _device.rtw_read8(0xff); /* check device operation speed: SS 0xff bit7 */ + if (is_8821) + { + // The working rtl8812au 8821AU path takes the USB2 branch here even when + // the host register does not report BIT7. + speedvalue = BIT7; + } if ((speedvalue & BIT7) != 0) { /* USB2/1.1 Mode */ @@ -1768,6 +1952,11 @@ void HalModule::_InitBurstPktLen() { _device.rtw_write16(REG_MAX_AGGR_NUM, 0x1f1f); _device.rtw_write8(REG_FWHW_TXQ_CTRL, (uint8_t)(_device.rtw_read8(REG_FWHW_TXQ_CTRL) & (~BIT7))); + if (is_8821 && !registry_priv::wifi_spec) + { + _device.rtw_write8(REG_FWHW_TXQ_CTRL, 0x80); + _device.rtw_write32(REG_FAST_EDCA_CTRL, 0x03087777); + } // AMPDUBurstMode is always false // if (pHalData.AMPDUBurstMode) diff --git a/src/HalModule.h b/src/HalModule.h index 0fbc5dd..0695500 100644 --- a/src/HalModule.h +++ b/src/HalModule.h @@ -82,7 +82,9 @@ class HalModule { bool check_positive(int32_t condition1, int32_t condition2, int32_t condition4); void _InitQueueReservedPage_8812AUsb(); + void _InitQueueReservedPage_8821AUsb(); void _InitTxBufferBoundary_8812AUsb(); + void _InitTxBufferBoundary_8821AUsb(); void _InitQueuePriority_8812AUsb(); void _InitNormalChipTwoOutEpPriority_8812AUsb(); void _InitNormalChipRegPriority_8812AUsb(uint16_t beQ, uint16_t bkQ, diff --git a/src/Radiotap.c b/src/Radiotap.c index 0e3a3ec..65e50dd 100644 --- a/src/Radiotap.c +++ b/src/Radiotap.c @@ -221,7 +221,9 @@ int ieee80211_radiotap_iterator_init( iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present); iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header); iterator->_reset_on_ext = 0; - iterator->_next_bitmap = &radiotap_header->it_present; // NOLINT(clang-diagnostic-address-of-packed-member) + iterator->_next_bitmap = + (__le32 *)((uint8_t *)radiotap_header + + offsetof(struct ieee80211_radiotap_header, it_present)); iterator->_next_bitmap++; iterator->_vns = vns; iterator->current_namespace = &radiotap_ns; diff --git a/src/RtlJaguarDevice.cpp b/src/RtlJaguarDevice.cpp index e298ca6..3f56107 100644 --- a/src/RtlJaguarDevice.cpp +++ b/src/RtlJaguarDevice.cpp @@ -224,12 +224,20 @@ bool RtlJaguarDevice::send_packet(const uint8_t *packet, size_t length) { return resp; } +//=================================================================================== +//=================================================================================== +// Starts monitor mode and dispatches parsed packets until should_stop is set. void RtlJaguarDevice::Init(Action_ParsedRadioPacket packetProcessor, - SelectedChannel channel) { + SelectedChannel channel, + std::function started) { _packetProcessor = packetProcessor; StartWithMonitorMode(channel); SetMonitorChannel(channel); + if (started) + { + started(); + } _logger->info("Listening air..."); while (!should_stop) { diff --git a/src/RtlJaguarDevice.h b/src/RtlJaguarDevice.h index 2738c31..9ea425a 100644 --- a/src/RtlJaguarDevice.h +++ b/src/RtlJaguarDevice.h @@ -19,12 +19,11 @@ extern "C" using Action_ParsedRadioPacket = std::function; -/* RtlJaguarDevice is the orchestrator for the Realtek "Jaguar" 802.11ac family - * — RTL8812AU (2T2R), RTL8811AU (1T1R cut), and RTL8814AU (4T4R RF / 3-SS - * baseband). The chip is identified at construction time via SYS_CFG bits and - * USB PID; this class drives bring-up, RX, and TX for whichever member of the - * family is present. */ -class RtlJaguarDevice { +//=================================================================================== +//=================================================================================== +// Drives bring-up, RX, and TX for Realtek Jaguar USB WiFi adapters. +class RtlJaguarDevice +{ std::shared_ptr _eepromManager; std::shared_ptr _radioManagement; SelectedChannel _channel; @@ -36,7 +35,8 @@ class RtlJaguarDevice { public: RtlJaguarDevice(RtlUsbAdapter device, Logger_t logger); - void Init(Action_ParsedRadioPacket packetProcessor, SelectedChannel channel); + void Init(Action_ParsedRadioPacket packetProcessor, SelectedChannel channel, + std::function started = nullptr); void SetMonitorChannel(SelectedChannel channel); void InitWrite(SelectedChannel channel); void SetTxPower(uint8_t power); diff --git a/src/RtlUsbAdapter.cpp b/src/RtlUsbAdapter.cpp index 89c1b3e..c7630c1 100644 --- a/src/RtlUsbAdapter.cpp +++ b/src/RtlUsbAdapter.cpp @@ -10,14 +10,17 @@ #include "FrameParser.h" #include "Hal8812PhyReg.h" #include "logger.h" -#include #include #include using namespace std::chrono_literals; +//=================================================================================== +//=================================================================================== +// Initializes the Realtek USB adapter state from the claimed libusb device. RtlUsbAdapter::RtlUsbAdapter(libusb_device_handle *dev_handle, Logger_t logger) - : _dev_handle{dev_handle}, _logger{logger} { + : _dev_handle{dev_handle}, _logger{logger} +{ libusb_device_descriptor desc{}; if (libusb_get_device_descriptor(libusb_get_device(_dev_handle), &desc) == LIBUSB_SUCCESS) { @@ -30,13 +33,17 @@ RtlUsbAdapter::RtlUsbAdapter(libusb_device_handle *dev_handle, Logger_t logger) if (usbSpeed > LIBUSB_SPEED_HIGH) // USB 3.0 { - rxagg_usb_size = 0x3; // 16KB - rxagg_usb_timeout = 0x01; - } else { - /* the setting to reduce RX FIFO overflow on USB2.0 and increase rx - * throughput */ - rxagg_usb_size = 0x1; // 8KB - rxagg_usb_timeout = 0x01; + // Match the working rtl8812au USB RX aggregation settings. Smaller + // thresholds can leave RTL8821AU monitor traffic stuck in the RXDMA path. + rxagg_usb_size = 0x7; + rxagg_usb_timeout = 0x1a; + } + else + { + // Working rtl8812au uses this USB2.0 setting when preallocated RX buffers + // are not enabled. + rxagg_usb_size = 0x5; + rxagg_usb_timeout = 0x20; } GetChipOutEP8812(); @@ -50,6 +57,14 @@ RtlUsbAdapter::RtlUsbAdapter(libusb_device_handle *dev_handle, Logger_t logger) (AutoloadFailFlag ? "Fail" : "OK")); } +//=================================================================================== +//=================================================================================== +// Returns true when this USB ID needs the local RTL8821A HAL and firmware path. +bool RtlUsbAdapter::IsRtl8821A() const +{ + return chipType == RtlChipType::RTL8821; +} + /* $ lsusb -v -d 0bda:8812 Endpoint Descriptor: @@ -64,8 +79,12 @@ RtlUsbAdapter::RtlUsbAdapter(libusb_device_handle *dev_handle, Logger_t logger) bInterval 0 */ -std::vector RtlUsbAdapter::infinite_read() { - static constexpr int BUF_SIZE = 16 * 1024; +//=================================================================================== +//=================================================================================== +// Reads pending packets from the adapter bulk IN endpoint and parses RX frames. +std::vector RtlUsbAdapter::infinite_read() +{ + static constexpr int BUF_SIZE = 32 * 1024; uint8_t buffer[BUF_SIZE] = {}; int actual_length = 0; int rc; @@ -73,18 +92,10 @@ std::vector RtlUsbAdapter::infinite_read() { rc = libusb_bulk_transfer(_dev_handle, _bulk_in_ep, buffer, sizeof(buffer), &actual_length, USB_TIMEOUT * 10); - if (rc < 0) { - /* Rate-limit the error log: a fast-failing rc (e.g. LIBUSB_ERROR_NO_DEVICE - * after the chip dropped off USB) used to spin the outer Init() loop at - * full CPU, producing multi-GB log spam in a few seconds. Log every - * Nth failure + sleep enough to keep the loop sane until the caller's - * should_stop fires. */ - static uint64_t err_count = 0; - if ((err_count++ % 100) == 0) { - _logger->error("libusb_bulk_transfer failed with error: {} (count={})", - rc, err_count); - } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + if (rc < 0 || actual_length <= 0) + { + std::this_thread::sleep_for(50ms); + return {}; } std::vector packets; @@ -264,7 +275,11 @@ const char *RtlUsbAdapter::strUsbSpeed() { } } -void RtlUsbAdapter::InitDvObj() { +//=================================================================================== +//=================================================================================== +// Reads USB descriptors and records the bulk endpoints exposed by this adapter. +void RtlUsbAdapter::InitDvObj() +{ libusb_device *dev = libusb_get_device(_dev_handle); usbSpeed = (enum libusb_speed)libusb_get_device_speed(dev); _logger->info("Running USB bus at {}", strUsbSpeed()); @@ -274,6 +289,43 @@ void RtlUsbAdapter::InitDvObj() { if (ret < 0) { return; } + _idVendor = desc.idVendor; + _idProduct = desc.idProduct; + UsbVendorId = desc.idVendor; + UsbProductId = desc.idProduct; + + switch ((uint32_t(UsbVendorId) << 16) | UsbProductId) + { + case 0x0BDA0811: + case 0x0BDA0821: + case 0x0BDA8822: + case 0x0BDAA811: + case 0x0BDA0820: + case 0x0BDA0823: + case 0x04110242: + case 0x0411029B: + case 0x04BB0953: + case 0x056E4007: + case 0x056E400E: + case 0x056E400F: + case 0x08469052: + case 0x0E660023: + case 0x20013314: + case 0x20013318: + case 0x2019AB32: + case 0x2357011E: + case 0x23570120: + case 0x23570122: + case 0x38236249: + case 0x7392A811: + case 0x7392A812: + case 0x7392A813: + chipType = RtlChipType::RTL8821; + break; + default: + chipType = RtlChipType::RTL8812; + break; + } for (uint8_t k = 0; k < desc.bNumConfigurations; k++) { libusb_config_descriptor *config; @@ -283,11 +335,13 @@ void RtlUsbAdapter::InitDvObj() { } if (!config->bNumInterfaces) { + libusb_free_config_descriptor(config); continue; } const libusb_interface *interface = &config->interface[0]; if (!interface->altsetting) { + libusb_free_config_descriptor(config); continue; } const libusb_interface_descriptor *interface_desc = @@ -299,11 +353,6 @@ void RtlUsbAdapter::InitDvObj() { uint8_t endPointAddr = endpoint->bEndpointAddress; const bool is_bulk = (endpoint->bmAttributes & 0b11) == LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK; - _logger->info("endpoint[{}]: addr=0x{:X} attrs=0x{:X} bulk={} in={}", - (int)j, (int)endPointAddr, (int)endpoint->bmAttributes, - is_bulk ? 1 : 0, - (endPointAddr & LIBUSB_ENDPOINT_IN) ? 1 : 0); - if (is_bulk && !(endPointAddr & LIBUSB_ENDPOINT_IN)) { numOutPipes++; _bulk_out_eps.push_back(endPointAddr); @@ -316,26 +365,14 @@ void RtlUsbAdapter::InitDvObj() { if (is_bulk && (endPointAddr & LIBUSB_ENDPOINT_IN) && !found_bulk_in) { _bulk_in_ep = endPointAddr; found_bulk_in = true; - _logger->info("selected bulk IN endpoint: 0x{:X}", (int)_bulk_in_ep); - } - } - if (!_bulk_out_eps.empty()) { - std::string ep_list; - for (auto ep : _bulk_out_eps) { - char buf[8]; - snprintf(buf, sizeof(buf), "0x%02X ", ep); - ep_list += buf; } - _logger->info("bulk OUT endpoints: {}", ep_list); } /* Clear any HALT state on the bulk IN endpoint. The fwdl sequence and * USB reset can leave the IN EP in a stalled state from the chip side; * without clear_halt the chip's USB engine would never push RX bytes * even though the host's libusb_bulk_transfer succeeds at submission. */ if (found_bulk_in) { - int hr = libusb_clear_halt(_dev_handle, _bulk_in_ep); - _logger->info("libusb_clear_halt(bulk IN 0x{:X}) rc={}", (int)_bulk_in_ep, - hr); + libusb_clear_halt(_dev_handle, _bulk_in_ep); } libusb_free_config_descriptor(config); @@ -387,8 +424,11 @@ void transfer_callback(struct libusb_transfer *transfer) { libusb_free_transfer(transfer); } -bool RtlUsbAdapter::send_packet(uint8_t *packet, size_t length) { - +//=================================================================================== +//=================================================================================== +// Submits one packet to the selected bulk OUT endpoint. +bool RtlUsbAdapter::send_packet(uint8_t *packet, size_t length) +{ libusb_transfer *transfer = libusb_alloc_transfer(0); if (!transfer) { _logger->error("Failed to allocate transfer"); @@ -409,42 +449,11 @@ bool RtlUsbAdapter::send_packet(uint8_t *packet, size_t length) { return 0x02; }(); - /* On the FIRST send only, dump the bulk-OUT bytes to compare against - * the OOT-driver wire trace. */ - static bool first_pkt_dump = true; - if (first_pkt_dump) { - first_pkt_dump = false; - /* Clear any HALT state on the TX EP. The fwdl process can leave the - * TX EP in a stalled state from the chip side; without clear_halt the - * USB controller would NAK every subsequent bulk OUT URB. */ - int chr = libusb_clear_halt(_dev_handle, tx_ep); - _logger->info("libusb_clear_halt(EP 0x{:02X}) rc={}", (int)tx_ep, chr); - size_t dump_len = std::min(length, 64); - char hex[64 * 2 + 1] = {0}; - for (size_t k = 0; k < dump_len; ++k) { - static const char hd[] = "0123456789abcdef"; - hex[2*k] = hd[packet[k] >> 4]; - hex[2*k+1] = hd[packet[k] & 0xF]; - } - _logger->info("first TX bulk-OUT len={} bytes: {}", length, hex); - } - - /* On the FIRST send only, dump chip state via vendor reads. Surfaces any - * register clobber between init-end and first TX (e.g. SetMonitorChannel - * could be resetting REG_CR or related). */ - static bool first_dump = true; - if (first_dump) { - first_dump = false; - uint16_t cr = rtw_read16(0x0100); - uint8_t txpause = rtw_read8(0x0522); - uint32_t txdma_off_chk = rtw_read32(0x020C); - uint32_t fwhw_txq = rtw_read32(0x0420); - uint32_t mcufwdl = rtw_read32(0x0080); - uint32_t hci_susp = rtw_read32(0xFE10); /* USB_HCPWM / USB suspend ctrl */ - _logger->info("pre-1st-TX: CR=0x{:04x} TXPAUSE=0x{:02x} TXDMA_OFFC=0x{:08x}", - cr, txpause, txdma_off_chk); - _logger->info("pre-1st-TX: FWHW_TXQ=0x{:08x} MCUFWDL=0x{:08x} HCIPWR=0x{:08x}", - fwhw_txq, mcufwdl, hci_susp); + static bool first_pkt_setup = true; + if (first_pkt_setup) + { + first_pkt_setup = false; + libusb_clear_halt(_dev_handle, tx_ep); } libusb_fill_bulk_transfer(transfer, _dev_handle, tx_ep, packet, length, @@ -458,7 +467,7 @@ bool RtlUsbAdapter::send_packet(uint8_t *packet, size_t length) { * every completion with status=-2 (ENOENT/cancelled), data_len=0. */ transfer->flags |= LIBUSB_TRANSFER_ADD_ZERO_PACKET; auto start = std::chrono::high_resolution_clock::now(); - int rc = rc = libusb_submit_transfer(transfer); + int rc = libusb_submit_transfer(transfer); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = end - start; if (rc == LIBUSB_SUCCESS) { @@ -491,7 +500,6 @@ int RtlUsbAdapter::bulk_send_sync_ep(uint8_t ep, uint8_t *packet, size_t length, actual, (int)length); return rc; } - _logger->info("bulk_send EP {} OK {} bytes", (int)ep, actual); return actual; } diff --git a/src/RtlUsbAdapter.h b/src/RtlUsbAdapter.h index 1691614..b98abd1 100644 --- a/src/RtlUsbAdapter.h +++ b/src/RtlUsbAdapter.h @@ -31,7 +31,16 @@ enum TxSele { TX_SELE_EQ = 1 << (3), /* Extern Queue */ }; -class RtlUsbAdapter { +enum class RtlChipType { + RTL8812, + RTL8821, +}; + +//=================================================================================== +//=================================================================================== +// Wraps a claimed Realtek USB WiFi adapter and exposes register/RX/TX helpers. +class RtlUsbAdapter +{ libusb_device_handle *_dev_handle; Logger_t _logger; @@ -48,6 +57,7 @@ class RtlUsbAdapter { * 0x09. send_packet picks index 0 (HQ-equivalent) by default; DEVOURER_TX_EP * env override still wins for diagnostic bisection. */ std::vector _bulk_out_eps; + RtlChipType chipType = RtlChipType::RTL8812; uint16_t _idVendor = 0; uint16_t _idProduct = 0; @@ -58,12 +68,15 @@ class RtlUsbAdapter { uint16_t idVendor() const { return _idVendor; } uint16_t idProduct() const { return _idProduct; } + uint16_t UsbVendorId = 0; + uint16_t UsbProductId = 0; bool AutoloadFailFlag = false; bool EepromOrEfuse = false; uint8_t OutEpQueueSel; uint8_t OutEpNumber; uint8_t rxagg_usb_size; uint8_t rxagg_usb_timeout; + bool IsRtl8821A() const; bool send_packet(uint8_t* packet, size_t length); /* Synchronous bulk-OUT transfer that blocks until completion or timeout. * Returns the number of bytes actually transferred, or negative on error. */