WiFi bringup: in-kernel WCN3990 firmware chain (2.4 + 5 GHz) - #124
Closed
greatgitsby wants to merge 10 commits into
Closed
WiFi bringup: in-kernel WCN3990 firmware chain (2.4 + 5 GHz)#124greatgitsby wants to merge 10 commits into
greatgitsby wants to merge 10 commits into
Conversation
greatgitsby
force-pushed
the
wifi
branch
2 times, most recently
from
July 2, 2026 13:03
0c322e6 to
12fd0b2
Compare
vamOS System ProfileChanges vs master
Directory size changes (>1MB)
Top 10 Directories
Category Breakdown
Top 10 Packages by Size
|
| File | Size |
|---|---|
| /usr/lib/llvm/21/lib/libLLVM.so.21.1 | 124.3MB |
| /usr/lib/llvm/21/lib/libMLIR.so.21.1 | 89.7MB |
| /usr/lib/llvm/21/bin/mlir-transform-opt | 82.9MB |
| /usr/lib/llvm/21/lib/libclang-cpp.so.21.1 | 56MB |
| /usr/lib/llvm/21/bin/mlir-translate | 53.2MB |
| /usr/bin/uv | 52.6MB |
| /usr/lib/llvm/21/bin/llvm-exegesis | 52MB |
| /usr/lib/llvm/21/bin/llvm-bolt-binary-analysis | 50.6MB |
| /usr/lib/gcc/aarch64-linux-gnu/14.2/gnat1 | 40.1MB |
| /usr/bin/gdb | 39.1MB |
| /usr/lib/libgallium-26.1.3.so | 37.7MB |
| /usr/lib/gcc/aarch64-linux-gnu/14.2/cc1plus | 37.3MB |
| /usr/lib/libllvm-qcom.so | 35.2MB |
| /usr/lib/gcc/aarch64-linux-gnu/14.2/cc1 | 35.2MB |
| /usr/lib/libRusticlOpenCL.so.1.0.0 | 34.3MB |
| /usr/lib/gcc/aarch64-linux-gnu/14.2/lto1 | 33.9MB |
| /usr/bin/lto-dump | 33.9MB |
| /usr/local/venv/bin/ruff | 31.8MB |
| /usr/share/icu/78.3/icudt78l.dat | 31.5MB |
| /usr/lib/llvm/21/lib/libclang.so.21.1.7 | 30.2MB |
| /usr/lib/llvm/21/bin/c-index-test | 28.6MB |
| /usr/local/venv/lib/python3.12/site-packages/gcc_arm_none_eabi/toolchain/libexec/gcc/arm-none-eabi/13.2.1/cc1 | 28.2MB |
| /usr/lib/libz3.so | 27.9MB |
| /usr/bin/run | 26.7MB |
| /usr/local/venv/lib/python3.12/site-packages/numpy.libs/libscipy_openblas64_-71e1b124.so | 23.6MB |
| /usr/comma/updater | 23.5MB |
| /usr/comma/setup | 23.5MB |
| /usr/comma/reset | 23.5MB |
| /usr/local/uv/python/cpython-3.12.13-linux-aarch64-gnu/bin/python3.12 | 22.2MB |
| /usr/local/uv/python/cpython-3.12.13-linux-aarch64-gnu/lib/libpython3.12.so.1.0 | 22.1MB |
Bring up wlan0 on comma four (WCN3990/ath10k_snoc) without any Qualcomm userspace, by making the kernel drive the entire WCN3990 firmware chain itself. On the unmodified legacy rootfs the proprietary QRTR daemons are inert, so the kernel must: auto-boot the modem, answer servreg (in-kernel pd-mapper), serve wlanmdsp.mbn over TFTP-over-QRTR (new in-kernel tqftpserv), and let the modem publish the QMI WLFW service ath10k_snoc waits on. - 0012: remoteproc/qcom_q6v5_mss auto_boot=true (nothing in userspace starts the modem; firmware is built-in so no rootfs wait is needed). - 0013: new drivers/soc/qcom/qcom_tqftpserv.c, a minimal read-only in-kernel TFTP-over-QRTR server (service 4096) mirroring andersson/tqftpserv: blksize/tsize/wsize/rsize/seek/timeoutms options, windowed reads, path translation into the kernel firmware loader. Also makes QCOM_PD_MAPPER select QCOM_PDR_HELPERS so servreg/PDR stay built-in on a modules-less rootfs. - vamos.config: PD_MAPPER/PDR_HELPERS/PDR_MSG/QMI_HELPERS/TQFTPSERV all =y; extend EXTRA_FIRMWARE with mba.mbn, modem_nm.mbn, board-2.bin, firmware-5.bin, wlanmdsp.mbn. Builds clean; boot.img produced. Whole QRTR/modem/wlan config chain verified =y.
Adds drivers/soc/qcom/qcom_rmtfs_serv.c publishing QMI service 14 (rmtfs) so the SDM845 modem remoteproc no longer self-fatals after ~40s for lack of EFS storage. Read-only RAM-shadow backend over the modemst1/modemst2/fsc/fsg partitions; RW_IOVEC payload moves through the qcom,rmtfs-mem region. Enables CONFIG_QCOM_RMTFS_SERV=y in the vamos config fragment. Do-not-push local branch work.
Two on-device fixes to patch 0015: 1. qmi_send_response() was passed sizeof(decoded struct) as the encode buffer length; the encoded TLVs (3-byte header per element) are larger, so encoding failed with -ETOOSMALL (seen as 'failed to send response: -525' on the OPEN response, whose 14 encoded bytes exceeded the 12-byte struct). All five responses now use explicit encoded *_MAX_LEN macros; the handle's recv buffer is a generous 4096 covering RW_IOVEC. 2. The modem's first OPEN races UFS probe, and answering with a QMI failure makes the modem fatal instantly. OPEN now polls for the backing partition (250ms, up to 60s) before answering; blocking is safe on the QMI handle's own ordered workqueue with the modem as sole client.
Round-3 on-device fixes to patch 0015: 1. fsc on the comma four is ~1.5GiB (GPT ground truth), tripping the 16MiB sanity refusal and instantly fataling the modem. Now clamp instead of refuse: shadow only the first 16MiB (RMTFS_SHADOW_CAP); reads beyond the shadow return zeros and writes beyond it are accepted and dropped, each logged once per caller. The rmtfs protocol never reports a size to the modem, so the cap is invisible on the wire. 2. caller_id collision (fs1 and fsg both got id 0): the del_client QMI op freed ALL of a node's callers whenever any client socket on that node closed — and the modem opens each caller from its own transient socket (0:8, 0:12, ...) while its other QMI clients disconnect constantly. Userspace rmtfs treats DEL_CLIENT and BYE as no-ops; callers are keyed on (node, caller_id) and survive socket closes. del_client is now a no-op; per-node release happens only on BYE (node truly gone) and net_reset. RW_IOVEC dispatch keys on caller_id with a node check, matching userspace storage_get().
…257) The modem's first RW_IOVEC carries 257 iovec entries with a 16-bit wire length prefix; our qmi_elem_info capped the array at 255 (copied from the userspace linux-msm/rmtfs IDL, which specifies 255 with a u8 prefix — the modem firmware's own IDL clearly allows more, since a u8 prefix cannot even represent 257). Decode failed with 'Data len 257 > max spec 255', the modem got no response and fataled at +40s. Raise RMTFS_MAX_IOVEC to 512 (struct, elem_info max, and bounds check all derive from it), make the u16 length prefix explicit via the DATA_LEN element's elem_size (the kernel codec selects the prefix width from it; our previous sizeof(u32) already decoded u16, which is how 257 was read correctly), and grow the QMI receive buffer to 8192 for the ~6.2KB encoded request. The decoded request is heap-allocated by qmi_interface from decoded_size; the handler's per-sector stack buffer is unaffected. The RW_IOVEC response remains a single result TLV.
After the smp2p handover the modem sends TFTP WRQ to its /readwrite/ area and retries in a tight loop (~16-32ms, incrementing source ports) until the write succeeds; it never proceeds to the wlanmdsp.mbn RRQ while rejected. Extend patch 0014: - Implement WRQ per RFC 1350/2347/7440 mirroring andersson/tqftpserv handle_wrq/handle_writer: OACK (or ACK 0 without options), strict in-sequence DATA, ACK at each window boundary (block % wsize == 0) or on the final short block; transfer completes on payload < blksize. - Backing store is RAM only (no rootfs writes): path-keyed list, 1 MiB per file, 8 MiB total, oldest evicted under pressure (logged). Any path accepted after ".." sanitization, stored under its virtual path. - RRQs consult the RAM store before the request_firmware() candidates, so the modem can read back what it wrote (served from an owned copy so eviction cannot race an in-flight transfer). - pr_info WRQ path+size on start/completion; repeat/reject logs are rate-limited. Remaining rejects are only genuinely malformed requests (bad mode, non-NUL-terminated fields, "..", or > 1 MiB writes). - Fix a latent bug: EAGAIN from a drained socket no longer tears down an idle client, and each client socket is drained until EAGAIN per wakeup (windowed transfers queue several packets between wakeups).
The generic linux-firmware wlanmdsp.mbn (WLAN.HL.2.0-01387-QCAHLSWMTPLZ-1, 2018-07-19) crashes the WLAN Q6/DSP on 5 GHz DFS channels with 'halphy_caldb.c:3021: Assertion 0 failed', producing the ath10k 'failed to synchronize setup for vdev 0 restart 0: -110' / vdev-start failure loop. 2.4 GHz is unaffected. wlanmdsp.mbn is the vendor-signed WLAN firmware image sideloaded to the modem DSP via tqftpserv; per the ath10k list it is signed for a specific SoC/device and the generic linux-firmware copy is not calibration-matched to this board's RF, so its 5 GHz PHY cal DB asserts. Board data is not the cause: the board-2.bin entry ath10k selects (bus=snoc,qmi-board-id=ff,qmi-chip-id=30214) is already byte-identical to the device's /firmware/image/bdwlan.bin. Replace the embedded wlanmdsp.mbn with the device's own downstream image (WLAN.HL.2.0.c10-00459-QCAHLSWMTPLZ-1, from /firmware/image/wlanmdsp.mbn). firmware-5.bin and board-2.bin are unchanged (same WLAN.HL.2.0 interface descriptor). No config change: the EXTRA_FIRMWARE path is identical, only the blob contents differ. This blob also feeds the rootfs /lib/firmware copy via the Dockerfile, so both the built-in and userspace copies match.
5 GHz DFS vdev start still crashed the WLAN DSP (halphy_caldb.c:3026
assert) after the wlanmdsp swap. Root cause: wrong board/cal data.
Mainline was serving the generic default board-2.bin entry
(bus=snoc,qmi-board-id=ff,qmi-chip-id=30214 == generic bdwlan.bin),
but AGNOS does NOT use bdwlan.bin: comma ships RF-tuned board files
in /usr/comma/wlan/ (bdwlan.b00, bdwlan.b01, agnos-builder PRs #199
'Custom bdwlan per SOM type' and #377 'B01 wlan config') and patches
cnss_daemon to select /usr/comma/wlan/bdwlan.b%02x by the SOM id read
from /sys/.../vendor:gpio-som-id/som_id (4 GPIO straps, tlmm 105-108,
bit0 first; driver: agnos-kernel-sdm845 gpio_som_id.c). The legacy
mici DT has no wlan board-id; QMI board_id is 0xff on both stacks.
mici SOM id determined as 1 -> bdwlan.b01: tlmm gpio105 (bit0) reads 1
against a pull-down (hard-strapped high) while gpio106-108 read 0;
only b00/b01 exist, so id=1 is the only consistent hit. bdwlan.b01
differs from generic bdwlan.bin in 298 bytes (RF/cal tuning).
Implementation:
- append two variant-keyed entries to board-2.bin (upstream entries
untouched):
bus=snoc,...,qmi-chip-id=30214,variant=comma-mici -> bdwlan.b01
bus=snoc,...,qmi-chip-id=30214,variant=comma-tizi -> bdwlan.b00
- set qcom,calibration-variant in each board's &wifi DT node; 6.18
ath10k_core_check_dt picks it up and tries the variant name first.
tizi->b00 mapping is inferred from PR ordering (b00 predates b01);
tizi was unreachable to verify its SOM id. mici is verified as above.
Watch on flash-test dmesg: 'board_file api 2' with non-zero crc32,
no halphy_caldb assert, vdev 0 starts on freq 5500.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings up WiFi on mainline with unmodified AGNOS userspace: in-kernel rmtfs and tqftpserv provide the modem firmware chain, and device-matched wlanmdsp.mbn plus SOM-tuned board data (via qcom,calibration-variant) fix the 5 GHz DFS firmware crash. Verified on mici: cold boot associates on ch100 (DFS) with no errors.