Skip to content

Instantly share code, notes, and snippets.

@glats
Created March 3, 2026 02:32
Show Gist options
  • Select an option

  • Save glats/55ca54811235bc150b10fdebd8d0a0e5 to your computer and use it in GitHub Desktop.

Select an option

Save glats/55ca54811235bc150b10fdebd8d0a0e5 to your computer and use it in GitHub Desktop.
Stabilizing 5 GHz Wi-Fi on OnePlus 5 -- postmarketOS edge

Stabilizing 5 GHz Wi-Fi on OnePlus 5 -- postmarketOS edge

Device: OnePlus 5 (oneplus-cheeseburger, msm8998)
Wi-Fi chip: WCN3990 (ath10k_snoc)
Kernel: linux-postmarketos-qcom-msm8998 (Linux 6.0.x)
OS: postmarketOS edge (Alpine edge + systemd)

All commands in this guide run directly on the device as root (via doas sh or doas -s). The device is assumed to have a working internet connection (e.g. via 2.4 GHz Wi-Fi or USB tethering).

No internet on the device? You can download files on a host PC and transfer them via scp over USB networking (scp file user@172.16.42.1:/tmp/), then run the install commands on the device via ssh user@172.16.42.1.

What this guide fixes

  1. regulatory.db signature rejection -- the kernel only trusts the sforshee signing key, but Alpine edge ships a wens-signed regdb. Without the fix, cfg80211 refuses to load regulatory rules and 5 GHz channels are either missing or passive-scan only.

  2. Outdated WCN3990 board file -- firmware-oneplus-msm8998 ships an older board-2.bin; replacing it with the one from linux-firmware-ath10k provides better calibration data.

  3. NetworkManager defaults that can destabilize the link (powersave, MAC randomization, scan randomization).

What this guide does NOT fix

The ath10k_snoc driver still experiences periodic firmware crashes on 5 GHz:

ath10k_snoc: failed to install key ... -110
qcom-q6v5-mss: fatal error received: ... wlan_process
ath10k_snoc: firmware crashed!

The driver recovers automatically, but the connection drops briefly each time. This is a deeper firmware/driver issue not solvable from userspace. The steps below bring you to the best known state for stability and for reporting upstream.


Prerequisites

  • postmarketOS edge installed and booting.
  • Internet access on the device (2.4 GHz Wi-Fi, USB tethering, etc.).
  • Root access via doas.

Install required tools and create a working directory:

doas apk add wget tar zstd
mkdir -p /tmp/5ghz-fix && cd /tmp/5ghz-fix

Step 1: Fix regulatory.db

Background

The kernel config enforces signed regdb verification:

  • CONFIG_CFG80211_REQUIRE_SIGNED_REGDB=y
  • CONFIG_CFG80211_USE_KERNEL_REGDB_KEYS=y

The built-in trusted key is sforshee: 00b28ddf47aef9cea7. Alpine edge wireless-regdb (2024.01.23+) is signed by wens (a different upstream key), so the kernel rejects it. The last sforshee-signed release is 2023.09.01.

1a. Download and extract the regdb files

Option A -- from kernel.org (always available):

cd /tmp/5ghz-fix
wget "https://mirrors.edge.kernel.org/pub/software/network/wireless-regdb/wireless-regdb-2023.09.01.tar.xz"
tar -xf wireless-regdb-2023.09.01.tar.xz
cp wireless-regdb-2023.09.01/regulatory.db     regulatory.db
cp wireless-regdb-2023.09.01/regulatory.db.p7s regulatory.db.p7s

Option B -- from Alpine v3.19 mirror (if still available):

cd /tmp/5ghz-fix
wget -O wireless-regdb.apk \
  "https://dl-cdn.alpinelinux.org/alpine/v3.19/main/aarch64/wireless-regdb-2023.09.01-r0.apk"
tar -xOf wireless-regdb.apk lib/firmware/regulatory.db     > regulatory.db
tar -xOf wireless-regdb.apk lib/firmware/regulatory.db.p7s > regulatory.db.p7s

1b. Verify checksums

sha256sum regulatory.db regulatory.db.p7s

Expected:

0a4abd7ae20d07bb70642937ccb2293a72a6504730eea45a698882599f586368  regulatory.db
bcd81aed039ea6b9b6f3726fbf26911a0caf4a5d894210e0fa2effb384d6b326  regulatory.db.p7s

1c. Install

doas install -m 0644 /tmp/5ghz-fix/regulatory.db     /lib/firmware/regulatory.db
doas install -m 0644 /tmp/5ghz-fix/regulatory.db.p7s /lib/firmware/regulatory.db.p7s

On pmos, /lib is typically a symlink to /usr/lib, so both paths are covered by a single install.

1d. Reboot and verify

doas reboot

After reboot:

dmesg | grep -E 'cfg80211:.*(X\.509|regulatory)'

Good -- you should see:

cfg80211: Loading compiled-in X.509 certificates for regulatory database
cfg80211: Loaded X.509 cert 'sforshee: 00b28ddf47aef9cea7'

Bad -- if you see this, the regdb was not replaced correctly:

cfg80211: loaded regulatory.db is malformed or signature is missing/invalid

Step 2: Persist your regulatory domain

Without this, the kernel defaults to country 00 (world regulatory) which restricts 5 GHz channels.

echo "options cfg80211 ieee80211_regdom=CL" | doas tee /etc/modprobe.d/wifi-regdom.conf

Replace CL with your two-letter country code (e.g. US, DE, BR).

Reboot for the module parameter to take effect:

doas reboot

Verify after reboot:

iw reg get | head -20

You should see country CL: (or your code) in the global section with 5 GHz frequency ranges listed.

Note: If you are already associated with an AP that announces its own country code (e.g. US), the phy#0 section will show the AP's country instead of yours. This is normal -- the modprobe default only applies before association and as a fallback when the AP does not advertise a country. What matters is that it is not country 00 (world regulatory).


Step 3: Replace WCN3990 board file

Background

firmware-oneplus-msm8998 ships /lib/firmware/ath10k/WCN3990/hw1.0/board-2.bin but it is older than the version in linux-firmware-ath10k. The linux-firmware-ath10k package ships its version as board-2.bin.zst (compressed), which ath10k_snoc on this kernel does not decompress automatically. We decompress it manually and install it.

3a. Install the firmware package

doas apk add linux-firmware-ath10k

3b. Decompress and install the board file

cd /tmp/5ghz-fix
zstd -d -f /lib/firmware/ath10k/WCN3990/hw1.0/board-2.bin.zst -o board-2.bin

Verify:

sha256sum board-2.bin

Expected (for the 20260221-r0 package):

867e1010787764020653812167d93f5952cbbea05f576209d953d8c9322f18aa  board-2.bin

Install it over both board file paths:

FW_DIR=/lib/firmware/ath10k/WCN3990/hw1.0
doas install -o root -g root -m 0644 /tmp/5ghz-fix/board-2.bin $FW_DIR/board-2.bin
doas install -o root -g root -m 0644 /tmp/5ghz-fix/board-2.bin $FW_DIR/board.bin

The -o root -g root flags ensure the files are owned by root. Without them, the files may end up owned by your user, which works but is inconsistent with the rest of /lib/firmware/.

3c. Reboot and verify

doas reboot

After reboot:

dmesg | grep 'ath10k_snoc.*board_file'

You should see a line like:

ath10k_snoc 18800000.wifi: board_file api 2 bmi_id N/A crc32 XXXXXXXX

A nonzero crc32 value indicates the board file was loaded. A crc32 00000000 after a fresh boot (not after a crash recovery) means the board file is not being read correctly.


Step 4: NetworkManager tweaks (recommended)

These reduce unnecessary driver interactions that can trigger firmware crashes.

4a. Disable Wi-Fi powersave globally

doas mkdir -p /etc/NetworkManager/conf.d

echo "[connection]
wifi.powersave = 2" | doas tee /etc/NetworkManager/conf.d/wifi-powersave.conf

wifi.powersave = 2 means "disable". The default (3) lets NM enable powersave, which on ath10k can cause timeouts. This file applies globally to all current and future Wi-Fi connections. Without it, you would need to disable powersave individually on each new connection profile.

4b. Disable MAC randomization and set a fixed MAC

By default, NetworkManager randomizes the Wi-Fi MAC on every connection (stable mode from /usr/lib/NetworkManager/conf.d/50-random-mac.conf). This override disables that and sets a fixed MAC instead:

echo "[device]
wifi.scan-rand-mac-address=no

[connection]
wifi.cloned-mac-address=XX:XX:XX:XX:XX:XX
ethernet.cloned-mac-address=preserve" | doas tee /etc/NetworkManager/conf.d/99-no-random-mac.conf

Replace XX:XX:XX:XX:XX:XX with your desired MAC address.

What this does:

  • scan-rand-mac-address=no -- disables MAC randomization during Wi-Fi scans.
  • wifi.cloned-mac-address=XX:XX:XX:XX:XX:XX -- forces NetworkManager to always use this specific MAC for all Wi-Fi connections.
  • ethernet.cloned-mac-address=preserve -- leaves ethernet interfaces untouched.
  • Files in /etc/NetworkManager/conf.d/ override /usr/lib/NetworkManager/conf.d/, and the 99- prefix ensures this file is read last.

4c. Set the MAC on the specific Wi-Fi connection

This ensures the per-connection setting also matches, in case it was previously set to something else (stable, permanent, random, etc.):

doas nmcli connection modify "YOUR_5GHZ_SSID" \
  802-11-wireless.cloned-mac-address XX:XX:XX:XX:XX:XX

Replace YOUR_5GHZ_SSID with your connection name (find it with nmcli connection show) and XX:XX:XX:XX:XX:XX with the same MAC as above.

4d. Restart NetworkManager

doas systemctl restart NetworkManager

Verification checklist

Run this after all steps are done and the device has been rebooted:

echo '=== 1. regulatory.db signature ==='
dmesg | grep -E 'cfg80211:.*(X\.509|regulatory\.db)' | tail -5

echo ''
echo '=== 2. Regulatory domain ==='
iw reg get 2>/dev/null | head -10

echo ''
echo '=== 3. ath10k firmware and board ==='
dmesg | grep -E 'ath10k_snoc.*(firmware ver|board_file|qmi fw_version)' | tail -5

echo ''
echo '=== 4. Wi-Fi link status ==='
iw dev wlan0 link 2>/dev/null || echo 'wlan0 not connected'

echo ''
echo '=== 5. Wi-Fi powersave ==='
iw dev wlan0 get power_save 2>/dev/null || echo 'N/A'

echo ''
echo '=== 6. 5 GHz channels available ==='
echo "$(iw phy phy0 channels 2>/dev/null | grep -c '5[0-9]\{3\} MHz \[') total 5 GHz channels"

echo ''
echo '=== 7. MAC address ==='
ip link show wlan0 | grep ether

What to expect

Check Good Bad
1. regdb Loaded X.509 cert 'sforshee', no malformed error malformed or signature is missing/invalid
2. regdom country CL: (your code) in global section, not country 00:. The phy#0 section may show the AP's country instead -- this is normal. country 00: or only 2.4 GHz
3. board crc32 nonzero on first boot line crc32 00000000 on first boot
4. link freq: 5xxx when connected to 5 GHz AP Not connected
5. powersave Power save: off Power save: on
6. channels 20+ total 5 GHz channels (some may be DFS/No IR) 0 or very few
7. MAC The fixed MAC you configured Random/different on each boot

Things you must NOT do

  • Do NOT use ath10k_core cryptmode=1 -- it causes cryptmode > 0 requires raw mode support from firmware and the driver refuses to probe entirely.

  • Do NOT write to /sys/kernel/debug/remoteproc/remoteproc*/crash -- this has pushed the device into Qualcomm EDL (Emergency Download) mode in testing.

  • Do NOT run iw reg set XX as a substitute for the modprobe option -- it has no effect when the regdb was rejected at boot, and once associated to an AP the driver uses the AP's country code anyway.


Maintenance notes

  • apk upgrade will overwrite regulatory.db with the latest wireless-regdb from Alpine edge (wens-signed), breaking step 1. After upgrading, re-apply step 1.

  • apk upgrade may overwrite board-2.bin if firmware-oneplus-msm8998 is updated. After upgrading, check if step 3 needs re-applying.

  • The /etc/NetworkManager/conf.d/ overrides (steps 4a, 4b) survive package upgrades since /etc/ is not managed by apk.

  • The proper long-term fix for the regdb issue is for the postmarketOS kernel to either embed the wens key or disable CONFIG_CFG80211_REQUIRE_SIGNED_REGDB.


Related upstream issues

  • regulatory.db signing mismatch: kernel trusts sforshee but Alpine ships wens-signed regdb. Same issue on Qualcomm Debian images: qualcomm-linux/qcom-deb-images#174

  • WCN3990 board file packaging: firmware-oneplus-msm8998 ships outdated uncompressed board-2.bin, linux-firmware-ath10k ships newer version but only as .zst.


Conclusion: 5 GHz is unusable on this device

After applying every fix in this guide (correct regdb, regulatory domain, updated board file, powersave off, MAC pinned, scan randomization off, infinite autoconnect retries), the 5 GHz band remains unusable for sustained traffic.

The firmware that crashes

Field Value
Chip Qualcomm WCN3990 (ath10k_snoc)
Firmware version 1.0.0.483 (api 5)
Build ID WLAN.HL.1.0.c6-00126-QCAHLSWMTPLZ-1.211883.1.278648.1
Build date 2020-04-14
Package firmware-oneplus-msm8998-10.0.1-r4 (extracted from stock OxygenOS)
Source https://github.com/JamiKettunen/firmware-mainline-oneplus5

This firmware is a closed-source Qualcomm blob that runs on the modem DSP (qcom-q6v5-mss). Version 1.0.0.483 is the only version available for WCN3990 on this device. There is no newer release and no way to patch it.

Observed crash behavior

In testing, the firmware crashed 14 times in ~3.5 hours on 5 GHz, with crash cascades of 3 crashes in 20 seconds. All crashes share the same program counter:

err_qdi.c:450:EX:wlan_process:1:WLAN RT:2070:PC=b00bfa9c
err_qdi.c:450:EX:wlan_process:1:WLAN RT:1073:PC=b00bfa9c
err_qdi.c:450:EF:wlan_process:1:cmnos_thread.c:3242:Asserted in wlan_vdev.c:_w

The single crash PC (b00bfa9c) points to a specific, reproducible bug in the firmware. The wlan_vdev.c assertion is likely a secondary failure triggered by the crash-recovery loop.

Each crash kills the data path for 10-15 seconds. The ath10k_snoc driver recovers the link, but NetworkManager often declares the connection failed before recovery completes. A sustained operation like apk update cannot complete without being interrupted by a crash.

What was tried and why it does not help

Mitigation Effect
Correct regdb + regulatory domain Unlocks 5 GHz channels, does not prevent crashes
Updated board file from linux-firmware-ath10k Better calibration, does not prevent crashes
Powersave off Removes one crash trigger, crashes still occur
MAC pinning + no scan randomization Reduces driver interactions, crashes still occur
Infinite autoconnect retries NM reconnects after crash, but each drop is 10-15s
devcoredump crash trap + auto-reconnect Fastest possible recovery, still ~10s downtime per crash

Recommendation

Use 2.4 GHz. It is the only stable band with the WCN3990 firmware on ath10k_snoc / Linux 6.0.x. The 5 GHz band is fundamentally broken for sustained traffic due to a bug in the closed-source Qualcomm firmware that cannot be fixed from userspace or the kernel.

The 5 GHz connection profile should be kept with autoconnect=no in case a future kernel or firmware update improves the situation, but it should not be used for anything that requires reliability.

Alternatives for better throughput:

  • USB Wi-Fi dongle with a well-supported chipset (mt76, rtl8xxxu).
  • USB tethering from another device.
  • Future kernel upgrade (though the firmware remains the bottleneck).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment