rtl8192cu-fw

Findings — RTL8188CUS/RTL8192CU firmware (rtl8192cufw_TMSC.bin 88.2)

Status: first dig, 2026-11-05. Workbench was ~/fwre; this repo is the durable home. Symbols: annotations.tsv.

Container format

Validated against struct rtl8xxxu_firmware_header in the kernel’s rtl8xxxu driver (drivers/net/wireless/realtek/rtl8xxxu/rtl8xxxu.h in older kernels; split into core.c/regs.h later):

offset size field TMSC value
0x00 u16 signature 0x88C1 (MP A-cut; B-cut = 0x88C2)
0x02 u8 category 0x02
0x03 u8 function 0x05
0x04 u16 major version 88
0x06 u8 minor version 2
0x08 4×u8 month/date/hour/min (BCD) Dec 5, 17:12
0x0c u16 ramcodesize 16094 = filesize − 32
0x10 u32 svn_idx 137364 (Realtek internal SVN)
0x14–1f   reserved 0

Payload starts at 0x20. Variants: rtl8192cufw_A.bin built 17:11, _B.bin 17:10 (same day, one build run), rtl8192cufw.bin is a different lineage (v63.0, built Dec 9). rtl8xxxu picks TMSC (TSMC fab) vs rtl8192cufw.bin (UMC) from SYS_CFG; rtlwifi picks A/B by chip cut.

Download protocol (host side, from kernel drivers)

Memory map (empirically validated)

8051, Harvard architecture. Load base 0x4000 gives 97% of absolute xref targets inside the image on correct instruction boundaries (neighbors 0x2000/0x3000/0x5000: 90–94%).

Code map

H2C command protocol (host ↔ firmware), fully decoded

Driver side (rtlwifi rtl8192c/fw_common.c + reg values): the host has four 4-byte mailboxes — REG_HMEBOX_0..3 at XDATA 0x01D0/0x01D4/0x01D8/0x01DC (command id byte + 3 arg bytes, extended args in HMEBOX_EXT at 0x0088+ for 5–7 byte commands) — and announces “box N written” by setting bit N in REG_HMETFR = 0x01CC. The host considers a box free again when the corresponding HMETFR bit is cleared and MCUTST_1 (0x01C0)+N reads 0.

Firmware side (confirmed by disassembly):

The firmware understands six command IDs that Linux never sends (32, 44, 45, 46, 59, 75 — presumably used by Realtek’s Windows driver; candidates: BT coexistence, AP mode offload, RF calibration). Conversely, Linux’s SETPWRMODE (1), AP_OFFLOAD (0), MACID_PS_MODE (7), PWRM (15), CTW (24) and KEEP_ALIVE_CTRL (48) are not in this table — they must route through a second dispatch (open: the pre-dispatch call at 0x5EE4, or one of the other switch walkers at 0x43BA, 0x4DD9, 0x6532, 0x6933).

Handler semantics, first pass (register names via kernel headers)

Correction from the first pass: the dispatch switch value is staged at 0x8AD1 (not 0x8AD0 — MOV R7,[0x8AD1] feeds the dispatch call at 0x565D). Annotations fixed accordingly.

Second dispatch: status

The only caller of the second table walker (0x43BA, 16-bit cases, 4-byte entries {target_hi, target_lo, case_hi, case_lo}) is at 0x4FFF with its table at 0x5002 — but that region reads/writes REG_TXPAUSE and REG_FMTHR (0x01C8, the fw-to-MAC mailbox), i.e. it is TX/queue management, not H2C. Where SETPWRMODE etc. actually land remains open. The linear sweep desyncs across inline data tables in exactly the candidate regions (0x6C30 cluster reads 0x8AD0/0x8AD1 repeatedly — per-mailbox sequencing, or a desync artifact); recursive descent is the prerequisite for further progress.

Open leads

  1. Recursive descent in tools/dis8051.py — the prerequisite: linear sweep desyncs on inline tables (bit-mask table 0x4666, 0x5002 table region, 0x6C30 cluster). Roots: 0x4000 vectors, 0x50C6 main, all FUNC_ xref targets; unreachable bytes become data. Also: function census (size/xrefs) into analysis/functions.tsv.
  2. Second H2C dispatch: where do cmds 0/1/7/15/24/48 land? Re-examine 0x5EE4 and the 0x6C30 cluster after recursive descent.
  3. Name the six Linux-unknown commands by reading their handlers (0x56A6, 0x759A, 0x541E, 0x7881, 0x7AC2, 0x7C2B) with register annotations.
  4. 0x60F2 interrupt handler: RX/USB path, deadlock candidates.
  5. Group-key/rekey path: correlate with the field observation that outages cluster after the AP’s hourly group rekey.
  6. 8051 emulator (SIM): run the image against stubbed registers, replay a captured pre-hang usbmon trace — the only debugger this chip will ever give us. (Capture is running on the affected Pi since 2026-11-05: usbmon-wifi.service, see doorpi’s PI.md — wedges now leave evidence.)

Tooling notes