Add AIC8800 USB Wi-Fi driver as a NixOS module
This commit introduces the AIC8800 USB Wi-Fi driver, including firmware installation and configurable udev rules for USB disk ejection and power management. The driver supports kernel versions 6.17 and above, ensuring compatibility with recent changes in the Linux kernel. Documentation is also provided in README.md for usage and configuration.
This commit is contained in:
parent
2f1b1cd315
commit
bafd15d665
3 changed files with 315 additions and 0 deletions
104
README.md
Normal file
104
README.md
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
# AIC8800 USB Wi-Fi Driver for NixOS
|
||||||
|
|
||||||
|
This flake provides the AIC8800 USB Wi-Fi driver (used by UGREEN AX900 and similar adapters) as a NixOS module.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ Kernel 6.17+ compatibility (includes fixes for timer and cfg80211 API changes)
|
||||||
|
- ✅ Automatic firmware installation
|
||||||
|
- ✅ Configurable udev rules for USB disk ejection and power management
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### In your flake.nix
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
aic8800 = {
|
||||||
|
url = "git+https://git.tvpdev.de/Tsubajashi/alc8800";
|
||||||
|
# Or use a local path:
|
||||||
|
# url = "path:/path/to/alc8800";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, aic8800, ... }: {
|
||||||
|
nixosConfigurations.your-host = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs = {
|
||||||
|
aic8800-src = aic8800; # Pass the flake source
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
aic8800.nixosModules.default
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### In your configuration.nix
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
hardware.aic8800 = {
|
||||||
|
enable = true;
|
||||||
|
# Optional: enable udev rules (default: true)
|
||||||
|
enableUdevRules = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternative: Using as a flake input with specialArgs
|
||||||
|
|
||||||
|
If you're already using `aic8800-src` as a flake input:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
inputs = {
|
||||||
|
aic8800-src = {
|
||||||
|
url = "git+https://git.tvpdev.de/Tsubajashi/alc8800";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
aic8800 = {
|
||||||
|
url = "git+https://git.tvpdev.de/Tsubajashi/alc8800";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, aic8800, aic8800-src, ... }: {
|
||||||
|
nixosConfigurations.your-host = nixpkgs.lib.nixosSystem {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
specialArgs = {
|
||||||
|
aic8800-src = aic8800-src; # Use the flake = false input
|
||||||
|
};
|
||||||
|
modules = [
|
||||||
|
aic8800.nixosModules.default
|
||||||
|
./configuration.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## What it does
|
||||||
|
|
||||||
|
When enabled, the module:
|
||||||
|
|
||||||
|
1. **Builds the kernel modules** (`aic_load_fw` and `aic8800_fdrv`) for your kernel version
|
||||||
|
2. **Installs firmware** to `/lib/firmware/aic8800D80/`
|
||||||
|
3. **Loads the modules** automatically at boot
|
||||||
|
4. **Configures udev rules** to:
|
||||||
|
- Automatically eject USB storage devices that appear when the adapter is plugged in (switches to Wi-Fi mode)
|
||||||
|
- Keep the Wi-Fi interface awake to avoid autosuspend issues
|
||||||
|
|
||||||
|
## Kernel Compatibility
|
||||||
|
|
||||||
|
The driver includes compatibility fixes for Linux kernel 6.17+:
|
||||||
|
- Timer API changes (`del_timer` → `timer_delete`)
|
||||||
|
- cfg80211 API changes (additional parameter for spurious frame handlers)
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you get an error about `aic8800-src` not being found, make sure you're passing it via `specialArgs` in your flake.nix.
|
||||||
|
|
||||||
58
flake.nix
Normal file
58
flake.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{
|
||||||
|
description = "AIC8800 USB Wi-Fi driver for Linux";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }:
|
||||||
|
let
|
||||||
|
supportedSystems = [ "x86_64-linux" "aarch64-linux" ];
|
||||||
|
forAllSystems = nixpkgs.lib.genAttrs supportedSystems;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
packages = forAllSystems (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
in
|
||||||
|
{
|
||||||
|
aic8800-firmware = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "aic8800-firmware";
|
||||||
|
version = "2022-12-19";
|
||||||
|
src = ./.;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out/lib/firmware
|
||||||
|
cp -r usr/src/AIC8800/fw/aic8800D80 $out/lib/firmware/
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with pkgs.lib; {
|
||||||
|
description = "Firmware blobs for AIC8800 USB adapters";
|
||||||
|
license = licenses.unfreeRedistributableFirmware;
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
nixosModules = {
|
||||||
|
default = { config, lib, pkgs, ... }: import ./nixos-module.nix {
|
||||||
|
inherit config lib pkgs;
|
||||||
|
aic8800-src = self;
|
||||||
|
};
|
||||||
|
aic8800 = { config, lib, pkgs, ... }: import ./nixos-module.nix {
|
||||||
|
inherit config lib pkgs;
|
||||||
|
aic8800-src = self;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
overlays = {
|
||||||
|
default = final: prev: {
|
||||||
|
aic8800-firmware = self.packages.${prev.system}.aic8800-firmware;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
153
nixos-module.nix
Normal file
153
nixos-module.nix
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
{ config, lib, pkgs, aic8800-src ? null, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.hardware.aic8800;
|
||||||
|
|
||||||
|
# Get source from function argument, specialArgs, or throw error
|
||||||
|
aic8800Src = if aic8800-src != null then
|
||||||
|
"${aic8800-src}/usr/src/AIC8800"
|
||||||
|
else if builtins.hasAttr "_module" config &&
|
||||||
|
builtins.hasAttr "args" config._module &&
|
||||||
|
builtins.hasAttr "aic8800-src" config._module.args then
|
||||||
|
"${config._module.args.aic8800-src}/usr/src/AIC8800"
|
||||||
|
else
|
||||||
|
throw ''
|
||||||
|
aic8800-src must be provided either:
|
||||||
|
1. As a function argument when importing the module, or
|
||||||
|
2. Via specialArgs in your flake.nix:
|
||||||
|
specialArgs = { aic8800-src = inputs.aic8800-src; };
|
||||||
|
'';
|
||||||
|
|
||||||
|
aic8800Firmware = pkgs.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "aic8800-firmware";
|
||||||
|
version = "2022-12-19";
|
||||||
|
src = aic8800Src;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
mkdir -p $out/lib/firmware
|
||||||
|
cp -r fw/aic8800D80 $out/lib/firmware/
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Firmware blobs for AIC8800 USB adapters";
|
||||||
|
license = licenses.unfreeRedistributableFirmware;
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
aic8800Driver = config.boot.kernelPackages.callPackage
|
||||||
|
({ stdenv,
|
||||||
|
lib,
|
||||||
|
kernel,
|
||||||
|
python3
|
||||||
|
}: stdenv.mkDerivation {
|
||||||
|
pname = "aic8800-driver";
|
||||||
|
version = "2022-12-19";
|
||||||
|
|
||||||
|
src = aic8800Src;
|
||||||
|
|
||||||
|
nativeBuildInputs = (kernel.moduleBuildDependencies or [ ]) ++ [ python3 ];
|
||||||
|
|
||||||
|
postPatch = ''
|
||||||
|
python3 <<'EOF'
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
header = Path('drivers/aic8800/aic8800_fdrv/aicwf_usb.h')
|
||||||
|
lines = header.read_text().splitlines()
|
||||||
|
|
||||||
|
define_line = '#define USB_PRODUCT_ID_AIC8800D80 0x8d80'
|
||||||
|
if all('USB_PRODUCT_ID_AIC8800D80' not in line for line in lines):
|
||||||
|
out_lines = []
|
||||||
|
inserted = 0
|
||||||
|
for line in lines:
|
||||||
|
out_lines.append(line)
|
||||||
|
if line.strip().startswith('#define USB_PRODUCT_ID_AIC8800D81'):
|
||||||
|
out_lines.append(define_line)
|
||||||
|
inserted += 1
|
||||||
|
if inserted == 0:
|
||||||
|
raise SystemExit('marker for USB product IDs missing')
|
||||||
|
header.write_text('\n'.join(out_lines) + '\n')
|
||||||
|
|
||||||
|
table = Path('drivers/aic8800/aic8800_fdrv/aicwf_usb.c')
|
||||||
|
table_text = table.read_text()
|
||||||
|
table_anchor = '{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800D81, 0xff, 0xff, 0xff)},\n'
|
||||||
|
table_entry = '{USB_DEVICE_AND_INTERFACE_INFO(USB_VENDOR_ID_AIC, USB_PRODUCT_ID_AIC8800D80, 0xff, 0xff, 0xff)},\n'
|
||||||
|
|
||||||
|
if table_entry not in table_text:
|
||||||
|
if table_anchor not in table_text:
|
||||||
|
raise SystemExit('USB table anchor missing')
|
||||||
|
table.write_text(table_text.replace(table_anchor, table_anchor + table_entry, 1))
|
||||||
|
EOF
|
||||||
|
'';
|
||||||
|
|
||||||
|
hardeningDisable = [ "pic" "format" ];
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
runHook preBuild
|
||||||
|
make -C drivers/aic8800 \
|
||||||
|
KDIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build \
|
||||||
|
ARCH=${stdenv.hostPlatform.linuxArch}
|
||||||
|
runHook postBuild
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
modDir=$out/lib/modules/${kernel.modDirVersion}/kernel/drivers/net/wireless
|
||||||
|
install -Dm644 drivers/aic8800/aic_load_fw/aic_load_fw.ko \
|
||||||
|
"$modDir/aic_load_fw.ko"
|
||||||
|
install -Dm644 drivers/aic8800/aic8800_fdrv/aic8800_fdrv.ko \
|
||||||
|
"$modDir/aic8800_fdrv.ko"
|
||||||
|
mkdir -p $out/lib/firmware
|
||||||
|
cp -r --no-preserve=mode,ownership fw/aic8800D80 $out/lib/firmware/
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "UGREEN / AIC8800 USB Wi-Fi driver";
|
||||||
|
homepage = "https://www.ugreen.com";
|
||||||
|
license = licenses.unfreeRedistributableFirmware;
|
||||||
|
platforms = platforms.linux;
|
||||||
|
};
|
||||||
|
}) { inherit (pkgs) python3; };
|
||||||
|
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.hardware.aic8800 = {
|
||||||
|
enable = mkEnableOption "AIC8800 USB Wi-Fi driver support";
|
||||||
|
|
||||||
|
enableUdevRules = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable udev rules for automatic USB disk ejection and power management";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
boot.extraModulePackages = [ aic8800Driver ];
|
||||||
|
boot.kernelModules = lib.mkAfter [ "aic_load_fw" "aic8800_fdrv" ];
|
||||||
|
|
||||||
|
hardware.firmware = [ aic8800Firmware ];
|
||||||
|
|
||||||
|
services.udev.extraRules = mkIf cfg.enableUdevRules (lib.mkAfter ''
|
||||||
|
# UGREEN AX900 shows up as a tiny storage device; ejecting it flips to Wi-Fi mode
|
||||||
|
KERNEL=="sd*", ACTION=="add", SUBSYSTEM=="block", \
|
||||||
|
ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5721", \
|
||||||
|
SYMLINK+="aicudisk", RUN+="${pkgs.util-linux}/bin/eject /dev/%k"
|
||||||
|
KERNEL=="sd*", ACTION=="add", SUBSYSTEM=="block", \
|
||||||
|
ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5723", \
|
||||||
|
SYMLINK+="tendaudisk", RUN+="${pkgs.util-linux}/bin/eject /dev/%k"
|
||||||
|
KERNEL=="sd*", ACTION=="add", SUBSYSTEM=="block", \
|
||||||
|
ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5724", \
|
||||||
|
SYMLINK+="ugreenudisk", RUN+="${pkgs.util-linux}/bin/eject /dev/%k"
|
||||||
|
|
||||||
|
# Keep the Wi-Fi interface awake to avoid round-trip spikes after autosuspend
|
||||||
|
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="a69c", ATTR{idProduct}=="8d80", \
|
||||||
|
TEST=="power/control", ATTR{power/control}="on"
|
||||||
|
'');
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Add table
Reference in a new issue