You are not logged in.

#1 2026-10-09 14:15:52

grigorianworks
Member
Registered: 2026-10-09
Posts: 1

[WORKAROUND] ath11k WCN6855 stuck in US regdomain, no 2.4GHz ch 12-13

Hi all,

sharing a workaround for a problem I couldn't find a working solution for.

Hardware: Qualcomm QCNFA765 (WCN6855 hw2.1, [c]17cb:1103[/c], subsystem [c]105b:e0cc[/c]), driver [c]ath11k_pci[/c]
Kernel: 7.2.9-arch1-1, linux-firmware 20260916 (fw WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.41)

Symptom

Networks on 2.4 GHz channel 12/13 can't be joined (NetworkManager: [c]association took too long[/c] / [c]ssid-not-found[/c]). The phy is self-managed and stuck in US, setting a country has no effect:

$ dmesg | grep ath11k
ath11k_pci 0000:02:00.0: Failed to set the requested Country regulatory setting
ath11k_pci 0000:02:00.0: failed to process regulatory info -22

$ iw reg get
global
country RU: DFS-UNSET
...
phy#0 (self-managed)
country US: DFS-FCC
	(2402 - 2472 @ 40), (N/A, 20), (N/A)
	(2457 - 2472 @ 15), (N/A, 20), (N/A), PASSIVE-SCAN
...
$ iw phy | grep -E '\[1[23]\]'
			* 2467.0 MHz [12] (disabled)
			* 2472.0 MHz [13] (disabled)

[c]iw reg set XX[/c] and [c]WIRELESS_REGDOM[/c] in [c]/etc/conf.d/wireless-regdom[/c] don't help — the firmware rejects the country change (status != PASS) and keeps US.

What does NOT work (to save you time)

  • Editing board data (board-2.bin): the BDF payload has a [c]regDmn[/c] field, but it is 0 in every single board entry shipped in linux-firmware (incl. the CN variants). The US default seems to come from chip OTP.

  • Editing regdb.bin (or the regdb blob inside board-2.bin): the 16-bit XOR checksum is easy to fix, but there is a 32-byte trailer that is not a plain SHA-256 — with a modified regdb the firmware crashes right after boot ([c]firmware crashed: MHI_CB_EE_RDDM[/c]). Revert immediately if you try this.

What works: small ath11k patch

ath11k builds the cfg80211 regdomain from the rule list the firmware reports. Extending the 2.4 GHz rule up to 2482 MHz on the host side is enough — the firmware happily scans and associates on channel 13 when the host hands it the channel. Tested: connected on 2472 MHz, stable.

Legal note: channels 12-13 are allowed in most of Europe, Russia and many other countries, but not in the US/Canada. Only use this where they are legal. The patch adds a module parameter [c]ru_2ghz_ch13[/c] (default on) so it can be disabled with [c]ath11k.ru_2ghz_ch13=0[/c].

--- a/reg.c
+++ b/reg.c
@@ -628,6 +628,10 @@
 	}
 }

+static bool ath11k_ru_2ghz_ch13 = true;
+module_param_named(ru_2ghz_ch13, ath11k_ru_2ghz_ch13, bool, 0444);
+MODULE_PARM_DESC(ru_2ghz_ch13, "Extend 2.4 GHz rules up to 2482 MHz (channels 12-13)");
+
 struct ieee80211_regdomain *
 ath11k_reg_build_regd(struct ath11k_base *ab,
 		      struct cur_regulatory_info *reg_info, bool intersect,
@@ -639,7 +643,7 @@
 	u8 i = 0, j = 0, k = 0;
 	u8 num_rules;
 	u16 max_bw;
-	u32 flags, reg_6ghz_number, max_bw_6ghz;
+	u32 flags, reg_6ghz_number, max_bw_6ghz, end_freq;
 	char alpha2[3];

 	num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules;
@@ -735,9 +739,17 @@
 		flags |= ath11k_map_fw_reg_flags(reg_rule->flags);
 		flags |= ath11k_map_fw_phy_flags(reg_info->phybitmap);

+		end_freq = reg_rule->end_freq;
+		if (ath11k_ru_2ghz_ch13 && reg_rule->start_freq < 2500 &&
+		    end_freq >= 2457 && end_freq < 2482) {
+			/* local override: open 2.4 GHz channels 12-13 (RU) */
+			end_freq = 2482;
+			flags &= ~NL80211_RRF_NO_IR;
+		}
+
 		ath11k_reg_update_rule(tmp_regd->reg_rules + i,
 				       reg_rule->start_freq,
-				       reg_rule->end_freq, max_bw,
+				       end_freq, max_bw,
 				       reg_rule->ant_gain, reg_rule->reg_power,
 				       reg_rule->psd_eirp, flags);

Quick test (non-persistent, reboot reverts)

Needs [c]linux-headers[/c] matching your kernel. Get the ath11k sources for your exact kernel version (only the driver directory is needed, ~2.5 MB — no need to download the whole tarball), from https://git.kernel.org/pub/scm/linux/ke … k?h=v7.2.9, plus [c]../spectral_common.h[/c] and [c]../testmode_i.h[/c] from the parent [c]ath/[/c] directory. Layout:

ath/spectral_common.h
ath/testmode_i.h
ath/ath11k/*.c *.h Makefile
cd ath/ath11k
patch -p1 < ru-ch13.patch
make -C /lib/modules/$(uname -r)/build M=$PWD modules

sudo modprobe -r ath11k_pci ath11k
sudo modprobe -a mac80211 cfg80211 qmi_helpers mhi
sudo insmod ./ath11k.ko && sudo insmod ./ath11k_pci.ko
iw phy | grep -E '\[1[23]\]'     # should no longer say (disabled)

The module is unsigned/out-of-tree, so the kernel gets tainted (OE). With Secure Boot / lockdown enforcing module signatures you'd need to sign it yourself.

Persistent: DKMS

Since ath11k changes between kernel releases, the DKMS PRE_BUILD script fetches pristine ath11k sources for the target kernel from git.kernel.org and applies the patch. If the fetch or patch fails, the build fails and the stock module is used (you just lose ch 12-13).

[c]/usr/src/ath11k-ru-1.0/dkms.conf[/c]

PACKAGE_NAME="ath11k-ru"
PACKAGE_VERSION="1.0"
PRE_BUILD="fetch.sh ${kernelver}"
MAKE[0]="make KVER=${kernelver}"
BUILT_MODULE_NAME[0]="ath11k"
BUILT_MODULE_LOCATION[0]="ath/ath11k/"
DEST_MODULE_LOCATION[0]="/updates/dkms"
BUILT_MODULE_NAME[1]="ath11k_pci"
BUILT_MODULE_LOCATION[1]="ath/ath11k/"
DEST_MODULE_LOCATION[1]="/updates/dkms"
AUTOINSTALL="yes"

[c]/usr/src/ath11k-ru-1.0/Makefile[/c] (recipe lines must start with a TAB)

KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build

all:
	$(MAKE) -C $(KDIR) M=$(CURDIR)/ath/ath11k modules

clean:
	-$(MAKE) -C $(KDIR) M=$(CURDIR)/ath/ath11k clean

[c]/usr/src/ath11k-ru-1.0/fetch.sh[/c] ([c]chmod +x[/c])

#!/bin/bash
# Fetch pristine ath11k sources matching the target kernel and apply the ch12-13 patch.
set -euo pipefail
kver=$1
base=${kver%%-*}
tag=v${base%.0}
cd "$(dirname "$0")"
if [ "$(cat ath/ath11k/.ver 2>/dev/null)" != "$tag" ]; then
	B=https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
	P=drivers/net/wireless/ath
	rm -rf ath && mkdir -p ath/ath11k
	list=$(curl -fsS --retry 3 "$B/tree/$P/ath11k?h=$tag" | grep -oE '>([A-Za-z0-9_.-]+\.[ch]|Makefile)<' | tr -d '<>' | sort -u)
	[ -n "$list" ]
	for f in $list; do
		curl -fsS --retry 3 "$B/plain/$P/ath11k/$f?h=$tag" -o "ath/ath11k/$f"
	done
	for f in spectral_common.h testmode_i.h; do
		curl -fsS --retry 3 "$B/plain/$P/$f?h=$tag" -o "ath/$f"
	done
	echo "$tag" > ath/ath11k/.ver
fi
patch -d ath/ath11k -p1 --forward < ru-ch13.patch

Put the patch above as [c]/usr/src/ath11k-ru-1.0/ru-ch13.patch[/c], then:

sudo dkms install ath11k-ru/1.0 -k $(uname -r)
sudo modprobe -r ath11k_pci ath11k && sudo modprobe ath11k_pci
modinfo -n ath11k                                  # -> .../updates/dkms/ath11k.ko.zst
cat /sys/module/ath11k/parameters/ru_2ghz_ch13     # -> Y

Uninstall: [c]sudo dkms remove ath11k-ru/1.0 --all && sudo rm -rf /usr/src/ath11k-ru-1.0[/c], then reload the module.

Note: kernel updates need network access during the DKMS build (fetch.sh downloads ~2.5 MB).

Open question

Has anyone found where the US default actually comes from on these cards (OTP? ACPI?) or a firmware-side way to make it accept other countries? The "proper" fix would be the firmware accepting the country code — the RU entry is present in the shipped regdb.bin, it just gets rejected with -22. Might be worth reporting to linux-wireless / ath11k.

Feedback and reports from other WCN6855 / QCA206x owners welcome.

Offline

Board footer

Powered by FluxBB