Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@ See code for all available configurations.
| [Lenovo Yoga Slim 7 14ILL10](lenovo/yoga/7/14ILL10) | `<nixos-hardware/lenovo/yoga/7/14ILL10>` | `lenovo-yoga-7-14ILL10` |
| [LENOVO Yoga 7 Slim Gen8](lenovo/yoga/7/slim/gen8) | `<nixos-hardware/lenovo/yoga/7/slim/gen8>` | `lenovo-yoga-7-slim-gen8` |
| [Linglong Nova Studio](linglong/nova-studio) | `<nixos-hardware/linglong/nova-studio>` | `linglong-nova-studio` |
| [Linux Firmware for ath12k](linux-firmware/ath12k/README.md) | `<nixos-hardware/linux-firmware/ath12k>` | `linux-firmware-ath12k-patched` |
| [MSI B550-A PRO](msi/b550-a-pro) | `<nixos-hardware/msi/b550-a-pro>` | `msi-b550-a-pro` |
| [MSI B350 TOMAHAWK](msi/b350-tomahawk) | `<nixos-hardware/msi/b350-tomahawk>` | `msi-b350-tomahawk` |
| [MSI B550 TOMAHAWK](msi/b550-tomahawk) | `<nixos-hardware/msi/b550-tomahawk>` | `msi-b550-tomahawk` |
Expand Down
1 change: 1 addition & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@
letsnote-cf-lx3 = import ./panasonic/letsnote/cf-lx3;
letsnote-cf-lx4 = import ./panasonic/letsnote/cf-lx4;
linglong-nova-studio = import ./linglong/nova-studio;
linux-firmware-ath12k-patched = import ./linux-firmware/ath12k;
malibal-aon-s1-intel = import ./malibal/aon/s1;
mechrevo-gm5hg0a = import ./mechrevo/GM5HG0A;
microchip-icicle-kit = import ./microchip/icicle-kit;
Expand Down
5 changes: 5 additions & 0 deletions linux-firmware/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Linux Firmware

Module for patching files in the Linux firmware.

See [ath12k](ath12k/README.md) for an example when this might be needed.
81 changes: 81 additions & 0 deletions linux-firmware/ath12k/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# linux-firmware/ath12k
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you you know any laptop module where this apply? I mainly want to get some basic evaluation coverage for this code.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a good example?
I don't know of any laptop that comes with a wifi chip with unsupported device ids as I have a desktop. I did try to plug the module into a few laptops of mine in case it was my desktop motherboard's fault, but all my computers ended up not recognizing this chip exactly the same.
Because the code merely adds the device id you define to the json and doesn't override anything, you could test it on a computer with Qualcomm wifi chip that already works and make sure that it still works when using this module.
Sorry, this is indeed a very quirky module for very quirky hardware, let me know if there is anything else I can do.

Copy link
Member

@Mic92 Mic92 Dec 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's time to find an alternative to parsing our README.md table to figure out what needs to be evaluated and instead use flake outputs to import each module into its own nixos configuration. Than your module would be tested automatically.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I didn't know that the readme was being parsed automatically.
Is there anything you want me to do?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mic92 Do you think this can be merged or is there anything you'd like me to change?


## The problem

The [Linux Firmware](https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/tree/ath12k) already contains the [ath12k firmware](https://git.codelinaro.org/clo/ath-firmware/ath12k-firmware) which contains various `board-2.bin`files which are containers of the actual firmware files. The kernel module tries to load a firmware file from within the `board-2.bin` container based on the device ID that the WiFi card reports. As itt turns out, there are WiFi cards with device IDs that aren't included in any of the firmware files in the `board-2.bin` container.

See the following bug reports on the [Kernel's Bugzilla](https://bugzilla.kernel.org/):
1. [Bug 217971](https://bugzilla.kernel.org/show_bug.cgi?id=217971)
1. [Bug 219319](https://bugzilla.kernel.org/show_bug.cgi?id=219319)
1. [Bug 218741](https://bugzilla.kernel.org/show_bug.cgi?id=218741)
1. [Bug 219239](https://bugzilla.kernel.org/show_bug.cgi?id=219239)
1. [Bug 220897](https://bugzilla.kernel.org/show_bug.cgi?id=220897)

## The solution

The solution is pointed out in some of the issues mentioned above - patch the `board-2.bin` with the device ID that your WiFi card reports. To do that, unpack the `board-2.bin` with [`bdecoder`](https://raw.githubusercontent.com/qca/qca-swiss-army-knife/99ecb87c5f808e98096eeddd5d804eeb0cf51d18/tools/scripts/ath12k/ath12k-bdencoder), edit the JSON and repack it. Then install the firmware as you normally would according to the [official readme](https://git.codelinaro.org/clo/ath-firmware/ath12k-firmware/-/blob/main/README.md?ref_type=heads).

The only problem is that it's not clear to which firmware file you should add the device ID of your WiFi card. Unfortunately, my best answer is keep trying until you find one that works.

## The Nix Solution

1. Import this module.
2. Run `sudo dmesg|grep -i ath12k`, you should see a line to the effect of:
```
[ 15.743621] ath12k_pci 0000:07:00.0: failed to fetch board data for X from ath12k/Y
```
3. Place this in your NixOS Configuration:
```nix
linux-firmware-ath12k-patched = {
enable = true;
fw-name = "X";
fw-board2 = "Y";
};
```
4. Build your NixOS Configuration, you should see an error to the effect of:
```
┏━ 4 Errors:
┃ > "bus=pci,vendor=17cb,device=1107,subsystem-vendor=17aa,subsystem-device=e0e6,qmi-chip-id=2,qmi-board-id=255,variant=LE.bin",
┃ > "bus=pci,vendor=17cb,device=1107,subsystem-vendor=17aa,subsystem-device=e0e6,qmi-chip-id=2,qmi-board-id=255,variant=LE_Eiger.bin",
┃ > "bus=pci,vendor=17cb,device=1107,subsystem-vendor=105b,subsystem-device=e0ea,qmi-chip-id=2,qmi-board-id=255,variant=AC_RAY16ZPB.bin",
┃ > "bus=pci,vendor=17cb,device=1107,subsystem-vendor=105b,subsystem-device=e10d,qmi-chip-id=2,qmi-board-id=255.bin"
┃ > ]
┃ > ================================================================================
┃ > Fatal error: please choose one of the values above and set them as the fw-file
┃ > Traceback (most recent call last):
┃ > File "<stdin>", line 18, in <module>
┃ > RuntimeError: Fatal error: please choose one of the values above and set them as the fw-file
┃ For full logs, run:
┃ nix log /nix/store/gha0dc3ziaz16pfv321iyzchr380m4v9-linux-firmware-ath12k-patched-0-unstable-2025-12-05.drv
┣━ Dependency Graph:
┃ ┌─ ⏸ linux-zen-6.18.1-modules-shrunk waiting for 1 ⏵
┃ ┌─ ⏸ initrd-linux-zen-6.18.1
┃ ┌─ ⏸ boot.json
┃ │ ┌─ ⏵ linux-firmware-ath12k-patched-0-unstable-2025-12-05 (buildPhase)
┃ │ ┌─ ⏸ linux-firmware-ath12k-patched-0-unstable-2025-12-05-zstd
┃ │ ┌─ ⏸ firmware
┃ │ ┌─ ⏸ etc-modprobe.d-firmware.conf
┃ │ ├─ ⏵ system-units
┃ ├─ ⏸ etc
┃ ⏸ nixos-system-ron-desktop-25.11.20251225.5900a0a
┣━━━ Builds
┗━ ∑ ⏵ 2 │ ✔ 1 │ ⏸ 8 │ ⚠ Exited with 4 errors reported by nix at 22:43:30 after 7s
Error:
0: Failed to build configuration
1: Command exited with status Exited(1)

Location:
src/commands.rs:693
```
5. Run the command printed below the line that says "For full logs, run:" to view the full log. Scroll up to see the list of firmware files that this module can assign your device ID to. You can either scroll up further to look at the original JSON and try to find a similar device ID to what you have and use that, or copy the list of possible values to a file and try each one until one of them work.
6. Assuming you chose a firmware file in the previous step (a string that ends with '.bin'), add the following line to your configuration and apply it:
```nix
linux-firmware-ath12k-patched = {
/* enable, fw-name, fw-board2 as before */
fw-file = "some-very-long-file-name.bin";
};
```
7. Reboot and examine `sudo dmesg|grep -i ath12k`. If there are no errors, try to scan and connect to a WiFi network. Make sure you can see all the networks you expect to see (e.g. 2.4Ghz, 5 Ghz and 6Ghz). If anything fails, go back to step 5.

If you've tried all the firmware files and some things don't work properly, as far as I know, I'm afraid you're out of luck. For example, I got 2.4Ghz and 5Ghz (6Ghz untested) to work, but Bluetooth isn't working.
115 changes: 115 additions & 0 deletions linux-firmware/ath12k/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
{
pkgs,
lib,
config,
...
}:
{
options = {
linux-firmware-ath12k-patched = {
enable = lib.mkEnableOption "enables linux-firmware-ath12k-patched";
fw-name = lib.mkOption {
type = lib.types.str;
description = ''
The device name of your WiFi card. That is, if your dmesg says
"failed to fetch board data for X from ath12k/Y" then this is X
'';
example = ''bus=pci,vendor=17cb,device=1107,subsystem-vendor=1eac,subsystem-device=8001,qmi-chip-id=2,qmi-board-id=255'';
};
fw-board2 = lib.mkOption {
type = lib.types.str;
description = ''
The path to the board-2.bin you'd like to patch RELATIVE TO the
ath12k folder. That is, if your dmesg says
"failed to fetch board data for X from ath12k/Y" then this is Y.
'';
example = ''WCN7850/hw2.0/board-2.bin'';
};
fw-file = lib.mkOption {
type = lib.types.str;
default = "";
description = ''
This is the firmware file you'd like to use. To see a list of
available files leave this setting blank, which will cause the
build to fail and the list of files to be printed to the build log..
'';
example = ''bus=pci,vendor=17cb,device=1107,subsystem-vendor=17aa,subsystem-device=e0e6,qmi-chip-id=2,qmi-board-id=255.bin'';
};
};
};

config = lib.mkIf config.linux-firmware-ath12k-patched.enable {
hardware.firmware =
with pkgs;
with config.linux-firmware-ath12k-patched;
[
(stdenvNoCC.mkDerivation rec {
pname = "linux-firmware-ath12k-patched";
version = "0-unstable-2025-12-05";

src = fetchgit {
url = "https://git.codelinaro.org/clo/ath-firmware/ath12k-firmware.git";
hash = "sha256-DIoj91XELnFj4xludxJCZRHctS4fYQ2khiFWrPdKHoo=";
rev = "8253b9185d292ba1ad27654f74b7a3abc68fca9c";
};

bdecoder = fetchurl {
url = "https://raw.githubusercontent.com/qca/qca-swiss-army-knife/99ecb87c5f808e98096eeddd5d804eeb0cf51d18/tools/scripts/ath12k/ath12k-bdencoder";
hash = "sha256-/cyNyWKNZ+UA1Jah3iLoLhNt3q7DJmnqGzrdk/KYBlI=";
};

fw-repo = fetchurl {
url = "https://raw.githubusercontent.com/qca/qca-swiss-army-knife/99ecb87c5f808e98096eeddd5d804eeb0cf51d18/tools/scripts/ath12k/ath12k-fw-repo";
hash = "sha256-7NvmddwqLiVeB+A2moll1YFH1/0j8rTtgDXGtmJ6hIA=";
};

nativeBuildInputs = [
python3
];

buildPhase = ''
mkdir board-2 && cd $_
export RES_DIR=$PWD
python3 $bdecoder --extract $src/${fw-board2}
python3 << EOF
import json
with open('board-2.json', 'r') as f:
board2 = json.load(f)
data = '${fw-file}'
name = '${fw-name}'
if not data:
print("="*80)
print('=== Dumping JSON for ${fw-board2}')
print("="*80)
print(json.dumps(board2, indent=4))
print("="*80)
print('=== Dumping list of available options for fw-file')
print("="*80)
print(json.dumps([d['data'] for d in board2[0]['board']], indent=4))
print("="*80)
error_string = 'Fatal error: please choose one of the values above and set them as the fw-file'
print(error_string)
raise RuntimeError(error_string)
for b in board2[0]['board']:
if b['data'] == data:
b['names'].append(name)
with open('board-2.json', 'w') as f:
json.dump(board2, f)
EOF
python3 $bdecoder -c ./board-2.json -o patched-board-2.bin
'';

installPhase = ''
runHook preInstall

cd $src
mkdir -p $out//lib/firmware
python3 ${fw-repo} --install $out//lib/firmware
cp $RES_DIR/patched-board-2.bin $out/lib/firmware/ath12k/${fw-board2}

runHook postInstall
'';
})
];
};
}