From b08eef856e095976440eeba4e3f97ab0dfe6e932 Mon Sep 17 00:00:00 2001 From: lordvicky Date: Tue, 14 Jul 2026 17:47:58 +0400 Subject: [PATCH 1/2] nix: add flake with NixOS module and vds package derivations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nixosModules.default replaces the entire installer flow on NixOS: vds_hcd built from the in-tree source against the configured kernel, vdsd/vdsctl built from vds/ (no glibc prebuilts), udev rules, vds group, wireplumber conf, and the vdsd unit — services.opends5.enable. The --install-system generator remains as the fallback for non-flake configurations; docs/INSTALLER.md gains a full NixOS section covering both paths and the update procedure. --- docs/INSTALLER.md | 71 +++++++++++++++++++++++++++++++++++++++++++- flake.nix | 43 +++++++++++++++++++++++++++ nix/nixos-module.nix | 65 ++++++++++++++++++++++++++++++++++++++++ nix/vds-module.nix | 40 +++++++++++++++++++++++++ nix/vds.nix | 54 +++++++++++++++++++++++++++++++++ 5 files changed, 272 insertions(+), 1 deletion(-) create mode 100644 flake.nix create mode 100644 nix/nixos-module.nix create mode 100644 nix/vds-module.nix create mode 100644 nix/vds.nix diff --git a/docs/INSTALLER.md b/docs/INSTALLER.md index f10b4e3..bd356e3 100644 --- a/docs/INSTALLER.md +++ b/docs/INSTALLER.md @@ -25,7 +25,7 @@ stream; used by the wizard). | Debian / Ubuntu | apt: dkms + `linux-headers-$(uname -r)` | | openSUSE | zypper: dkms + `kernel-default-devel` | | Bazzite / Silverblue | `rpm-ostree install --idempotent dkms kernel-devel`; requires one reboot, then re-run the installer | -| NixOS | No system changes: writes `./opends5-vds.nix` + `./opends5-vds-src/` for `boot.extraModulePackages`; apply with `nixos-rebuild switch` | +| NixOS | Nothing to run: use the flake (see "NixOS" below). The installer's fallback writes `./opends5-vds.nix` + `./opends5-vds-src/` for non-flake configs | | Anything else | Clean "unsupported" message; see docs/PORTING.md for manual steps | On all dkms-based platforms it then stages the module source to @@ -67,6 +67,75 @@ checkout) the step is skipped. The binaries are built in CI on Ubuntu 22.04 (glibc 2.35 baseline), so one build runs on every 2022-or-newer distribution. +## NixOS + +NixOS is configured declaratively, so the installer never escalates and never +modifies the system there. The repo ships a **flake** that replaces the whole +installer — kernel module *and* userspace — with one import. + +### Flake (recommended) + +Add the input and enable the service in your flake-based configuration: + +```nix +{ + inputs.opends5.url = "github:LordVicky/OpenDS5"; + + # in your nixosConfigurations.: + modules = [ + inputs.opends5.nixosModules.default + { + services.opends5 = { + enable = true; + users = [ "YOURNAME" ]; # members of the vds group (/dev/vds* access) + }; + } + ]; +} +``` + +Then `sudo nixos-rebuild switch`. This is the declarative equivalent of +everything the installer does on other distributions: + +- builds the `vds_hcd` kernel module from source against *your* configured + kernel (`boot.extraModulePackages`) and autoloads it — no DKMS, no MOK + signing; kernel bumps rebuild it automatically, +- builds `vdsd`/`vdsctl` from the `vds/` source tree (no glibc-prebuilt + binaries, no nix-ld), +- installs the udev rules, the system-wide wireplumber config, the `vds` + group, and the `vdsd` systemd unit. + +To run the companion app itself, enable AppImage support +(`programs.appimage = { enable = true; binfmt = true; };`) and run the +release AppImage; the setup wizard will detect a working system and skip +installation. + +**Updates**: the AppImage swap updates the app normally, but the driver is +pinned by your flake lock — after an app update, run +`nix flake update opends5 && sudo nixos-rebuild switch` to match. Until then +the running module is the locked version. + +Smoke-test the packages without a NixOS machine (any box with Nix): +`nix build .#vds` and `nix build .#vds-module`. + +### Generator fallback (non-flake configs) + +`--install-system` on NixOS runs entirely unprivileged and writes two things +to the current directory: `opends5-vds.nix` (a module that builds `vds_hcd` +via `boot.extraModulePackages`) and `opends5-vds-src/` (the module source). +Move both next to `configuration.nix`, add +`imports = [ ./opends5-vds.nix ];`, and `sudo nixos-rebuild switch`. + +The fallback covers the kernel module only. For userspace, mirror the flake +module by hand: create the `vds` group, add your user, install the udev rules +(`vds-bin/99-vds-dualsense-udev.rules`) via `services.udev.extraRules`, and +run `vdsd` as a systemd service built from the `vds/` source (the prebuilt +Ubuntu binaries need `programs.nix-ld.enable`). Re-run the generator and +rebuild after driver updates. + +Verify either path with `lsmod | grep vds_hcd` and `ls /dev/vds*` after the +rebuild (reboot if the kernel changed). + ## Setup wizard On Linux, the app checks at launch whether `vds_hcd` is loaded and diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..eaff979 --- /dev/null +++ b/flake.nix @@ -0,0 +1,43 @@ +{ + description = "OpenDS5 — virtual DualSense stack (vds_hcd kernel module + vdsd userspace)"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + version = + (builtins.fromJSON + (builtins.readFile ./ds5-bridge/companion/package.json)).version; + systems = [ "x86_64-linux" "aarch64-linux" ]; + forAllSystems = f: nixpkgs.lib.genAttrs systems + (system: f nixpkgs.legacyPackages.${system}); + in + { + # Single source of truth for the module/package version (the app + # version; matches what DKMS stamps on other distros). + opends5Version = version; + + packages = forAllSystems (pkgs: rec { + vds = pkgs.callPackage ./nix/vds.nix { inherit version; }; + # Standalone module build against the default nixpkgs kernel, mainly + # for `nix build .#vds-module` smoke tests. The NixOS module builds + # against the host's configured kernel instead. + vds-module = pkgs.callPackage ./nix/vds-module.nix { + kernel = pkgs.linuxPackages.kernel; + inherit version; + }; + default = vds; + }); + + nixosModules = rec { + opends5 = import ./nix/nixos-module.nix self; + default = opends5; + }; + + devShells = forAllSystems (pkgs: { + default = pkgs.mkShell { + inputsFrom = [ self.packages.${pkgs.stdenv.hostPlatform.system}.vds ]; + }; + }); + }; +} diff --git a/nix/nixos-module.nix b/nix/nixos-module.nix new file mode 100644 index 0000000..a5a21e9 --- /dev/null +++ b/nix/nixos-module.nix @@ -0,0 +1,65 @@ +# NixOS module for OpenDS5: kernel module + userspace daemon + udev rules +# + wireplumber config. The declarative equivalent of everything +# installer/opends5-install does on other distributions. +self: +{ config, lib, pkgs, ... }: + +let + cfg = config.services.opends5; +in +{ + options.services.opends5 = { + enable = lib.mkEnableOption "OpenDS5 virtual DualSense stack"; + + package = lib.mkOption { + type = lib.types.package; + default = self.packages.${pkgs.stdenv.hostPlatform.system}.vds; + defaultText = lib.literalExpression "opends5.packages..vds"; + description = "The vds userspace package (vdsd, vdsctl, rules)."; + }; + + users = lib.mkOption { + type = lib.types.listOf lib.types.str; + default = [ ]; + example = [ "alice" ]; + description = "Users added to the vds group (access to /dev/vds*)."; + }; + }; + + config = lib.mkIf cfg.enable { + boot.extraModulePackages = [ + (pkgs.callPackage ./vds-module.nix { + kernel = config.boot.kernelPackages.kernel; + version = self.opends5Version; + }) + ]; + boot.kernelModules = [ "vds_hcd" ]; + + users.groups.vds = { }; + users.users = lib.genAttrs cfg.users (_: { + extraGroups = [ "vds" ]; + }); + + services.udev.packages = [ cfg.package ]; + + environment.systemPackages = [ cfg.package ]; + + # Session-manager config for the controller's audio path; the system-wide + # conf.d is merged by wireplumber alongside any per-user config. + environment.etc."wireplumber/wireplumber.conf.d/99-vds-dualsense.conf".source = + "${cfg.package}/share/wireplumber/wireplumber.conf.d/99-vds-dualsense.conf"; + + systemd.services.vdsd = { + description = "vDS userspace daemon (OpenDS5)"; + after = [ "bluetooth.service" ]; + wants = [ "bluetooth.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "simple"; + ExecStart = "${cfg.package}/bin/vdsd"; + Restart = "on-failure"; + RestartSec = "1s"; + }; + }; + }; +} diff --git a/nix/vds-module.nix b/nix/vds-module.nix new file mode 100644 index 0000000..98b9ae4 --- /dev/null +++ b/nix/vds-module.nix @@ -0,0 +1,40 @@ +# vds_hcd kernel module, built out-of-tree against the given kernel. +# Instantiated by the NixOS module with config.boot.kernelPackages.kernel, +# so it always matches the configured kernel and rebuilds on kernel bumps. +{ lib +, stdenv +, kernel +, version +}: + +stdenv.mkDerivation { + pname = "vds_hcd"; + inherit version; + + # Kbuild references ../include, so the source root is the whole vds tree. + src = ../vds; + + hardeningDisable = [ "pic" ]; + nativeBuildInputs = kernel.moduleBuildDependencies; + + makeFlags = [ + "-C" "module" + "KERNELRELEASE=${kernel.modDirVersion}" + "KDIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build" + "VDS_VERSION=${version}" + ]; + + installPhase = '' + runHook preInstall + install -D module/vds_hcd.ko \ + $out/lib/modules/${kernel.modDirVersion}/extra/vds_hcd.ko + runHook postInstall + ''; + + meta = with lib; { + description = "vDS virtual USB host controller kernel module (OpenDS5)"; + homepage = "https://github.com/LordVicky/OpenDS5"; + license = licenses.mit; + platforms = platforms.linux; + }; +} diff --git a/nix/vds.nix b/nix/vds.nix new file mode 100644 index 0000000..f28cf94 --- /dev/null +++ b/nix/vds.nix @@ -0,0 +1,54 @@ +# Userspace stack: the vdsd daemon and vdsctl CLI, built from the in-tree +# vds/ source. Also installs the udev rules and wireplumber config so the +# NixOS module can reference them from one package. +{ lib +, stdenv +, cmake +, pkg-config +, bluez +, dbus +, libopus +, udev +, version +}: + +stdenv.mkDerivation { + pname = "vds"; + inherit version; + + src = ../vds; + + nativeBuildInputs = [ cmake pkg-config ]; + buildInputs = [ bluez dbus libopus udev ]; + + # generate-version.sh needs the git repo (absent in the sandbox); the build + # falls back to "unknown" without this. + postPatch = '' + cat > generate-version.sh < Date: Tue, 14 Jul 2026 17:52:22 +0400 Subject: [PATCH 2/2] ci: build the nix flake packages on flake/vds changes --- .github/workflows/nix-flake.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/nix-flake.yml diff --git a/.github/workflows/nix-flake.yml b/.github/workflows/nix-flake.yml new file mode 100644 index 0000000..8060c96 --- /dev/null +++ b/.github/workflows/nix-flake.yml @@ -0,0 +1,32 @@ +name: nix-flake + +on: + pull_request: + paths: + - 'flake.nix' + - 'flake.lock' + - 'nix/**' + - 'vds/**' + - '.github/workflows/nix-flake.yml' + push: + branches: [dev, main] + paths: + - 'flake.nix' + - 'flake.lock' + - 'nix/**' + - 'vds/**' + - '.github/workflows/nix-flake.yml' + workflow_dispatch: + +jobs: + flake: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v27 + - name: flake evaluates + run: nix flake check --no-build + - name: build userspace (vdsd, vdsctl) + run: nix build .#vds --print-build-logs + - name: build kernel module + run: nix build .#vds-module --print-build-logs