summary refs log tree commit diff
path: root/nixos/modules/hardware
diff options
context:
space:
mode:
Diffstat (limited to 'nixos/modules/hardware')
-rw-r--r--nixos/modules/hardware/acpilight.nix25
-rw-r--r--nixos/modules/hardware/all-firmware.nix93
-rw-r--r--nixos/modules/hardware/bladeRF.nix28
-rw-r--r--nixos/modules/hardware/brillo.nix22
-rw-r--r--nixos/modules/hardware/ckb-next.nix53
-rw-r--r--nixos/modules/hardware/corectrl.nix62
-rw-r--r--nixos/modules/hardware/cpu/amd-microcode.nix29
-rw-r--r--nixos/modules/hardware/cpu/intel-microcode.nix29
-rw-r--r--nixos/modules/hardware/cpu/intel-sgx.nix69
-rw-r--r--nixos/modules/hardware/device-tree.nix205
-rw-r--r--nixos/modules/hardware/digitalbitbox.nix30
-rw-r--r--nixos/modules/hardware/flirc.nix12
-rw-r--r--nixos/modules/hardware/gkraken.nix18
-rw-r--r--nixos/modules/hardware/gpgsmartcards.nix37
-rw-r--r--nixos/modules/hardware/hackrf.nix23
-rw-r--r--nixos/modules/hardware/i2c.nix43
-rw-r--r--nixos/modules/hardware/keyboard/teck.nix16
-rw-r--r--nixos/modules/hardware/keyboard/zsa.nix24
-rw-r--r--nixos/modules/hardware/ksm.nix38
-rw-r--r--nixos/modules/hardware/ledger.nix14
-rw-r--r--nixos/modules/hardware/logitech.nix96
-rw-r--r--nixos/modules/hardware/mcelog.nix35
-rw-r--r--nixos/modules/hardware/network/ath-user-regd.nix31
-rw-r--r--nixos/modules/hardware/network/b43.nix30
-rw-r--r--nixos/modules/hardware/network/broadcom-43xx.nix3
-rw-r--r--nixos/modules/hardware/network/intel-2200bg.nix30
-rw-r--r--nixos/modules/hardware/network/smc-2632w/default.nix9
-rw-r--r--nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis8
-rw-r--r--nixos/modules/hardware/network/zydas-zd1211.nix5
-rw-r--r--nixos/modules/hardware/nitrokey.nix27
-rw-r--r--nixos/modules/hardware/onlykey/default.nix33
-rw-r--r--nixos/modules/hardware/onlykey/onlykey.udev18
-rw-r--r--nixos/modules/hardware/opengl.nix157
-rw-r--r--nixos/modules/hardware/openrazer.nix146
-rw-r--r--nixos/modules/hardware/opentabletdriver.nix69
-rw-r--r--nixos/modules/hardware/pcmcia.nix60
-rw-r--r--nixos/modules/hardware/printers.nix130
-rw-r--r--nixos/modules/hardware/raid/hpsa.nix61
-rw-r--r--nixos/modules/hardware/rtl-sdr.nix23
-rw-r--r--nixos/modules/hardware/sata.nix100
-rw-r--r--nixos/modules/hardware/sensor/hddtemp.nix81
-rw-r--r--nixos/modules/hardware/sensor/iio.nix35
-rw-r--r--nixos/modules/hardware/steam-hardware.nix32
-rw-r--r--nixos/modules/hardware/system-76.nix89
-rw-r--r--nixos/modules/hardware/tuxedo-keyboard.nix35
-rw-r--r--nixos/modules/hardware/ubertooth.nix29
-rw-r--r--nixos/modules/hardware/uinput.nix19
-rw-r--r--nixos/modules/hardware/usb-wwan.nix39
-rw-r--r--nixos/modules/hardware/video/amdgpu-pro.nix70
-rw-r--r--nixos/modules/hardware/video/bumblebee.nix93
-rw-r--r--nixos/modules/hardware/video/capture/mwprocapture.nix56
-rw-r--r--nixos/modules/hardware/video/displaylink.nix76
-rw-r--r--nixos/modules/hardware/video/hidpi.nix16
-rw-r--r--nixos/modules/hardware/video/nvidia.nix391
-rw-r--r--nixos/modules/hardware/video/radeon.nix3
-rw-r--r--nixos/modules/hardware/video/switcheroo-control.nix18
-rw-r--r--nixos/modules/hardware/video/uvcvideo/default.nix64
-rw-r--r--nixos/modules/hardware/video/uvcvideo/uvcdynctrl-udev-rules.nix45
-rw-r--r--nixos/modules/hardware/video/webcam/facetimehd.nix44
-rw-r--r--nixos/modules/hardware/wooting.nix12
-rw-r--r--nixos/modules/hardware/xone.nix23
-rw-r--r--nixos/modules/hardware/xpadneo.nix29
62 files changed, 3240 insertions, 0 deletions
diff --git a/nixos/modules/hardware/acpilight.nix b/nixos/modules/hardware/acpilight.nix
new file mode 100644
index 00000000000..2de448a265c
--- /dev/null
+++ b/nixos/modules/hardware/acpilight.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.acpilight;
+in
+{
+  options = {
+    hardware.acpilight = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable acpilight.
+          This will allow brightness control via xbacklight from users in the video group
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = with pkgs; [ acpilight ];
+    services.udev.packages = with pkgs; [ acpilight ];
+  };
+}
diff --git a/nixos/modules/hardware/all-firmware.nix b/nixos/modules/hardware/all-firmware.nix
new file mode 100644
index 00000000000..5b60b17312f
--- /dev/null
+++ b/nixos/modules/hardware/all-firmware.nix
@@ -0,0 +1,93 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware;
+in {
+
+  imports = [
+    (mkRenamedOptionModule [ "networking" "enableRT73Firmware" ] [ "hardware" "enableRedistributableFirmware" ])
+    (mkRenamedOptionModule [ "networking" "enableIntel3945ABGFirmware" ] [ "hardware" "enableRedistributableFirmware" ])
+    (mkRenamedOptionModule [ "networking" "enableIntel2100BGFirmware" ] [ "hardware" "enableRedistributableFirmware" ])
+    (mkRenamedOptionModule [ "networking" "enableRalinkFirmware" ] [ "hardware" "enableRedistributableFirmware" ])
+    (mkRenamedOptionModule [ "networking" "enableRTL8192cFirmware" ] [ "hardware" "enableRedistributableFirmware" ])
+  ];
+
+  ###### interface
+
+  options = {
+
+    hardware.enableAllFirmware = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Turn on this option if you want to enable all the firmware.
+      '';
+    };
+
+    hardware.enableRedistributableFirmware = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Turn on this option if you want to enable all the firmware with a license allowing redistribution.
+      '';
+    };
+
+    hardware.wirelessRegulatoryDatabase = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Load the wireless regulatory database at boot.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkMerge [
+    (mkIf (cfg.enableAllFirmware || cfg.enableRedistributableFirmware) {
+      hardware.firmware = with pkgs; [
+        linux-firmware
+        intel2200BGFirmware
+        rtl8192su-firmware
+        rt5677-firmware
+        rtl8723bs-firmware
+        rtl8761b-firmware
+        rtw88-firmware
+        zd1211fw
+        alsa-firmware
+        sof-firmware
+        libreelec-dvb-firmware
+      ] ++ optional (pkgs.stdenv.hostPlatform.isAarch32 || pkgs.stdenv.hostPlatform.isAarch64) raspberrypiWirelessFirmware
+        ++ optionals (versionOlder config.boot.kernelPackages.kernel.version "4.13") [
+        rtl8723bs-firmware
+      ] ++ optionals (versionOlder config.boot.kernelPackages.kernel.version "5.16") [
+        rtw89-firmware
+      ];
+      hardware.wirelessRegulatoryDatabase = true;
+    })
+    (mkIf cfg.enableAllFirmware {
+      assertions = [{
+        assertion = !cfg.enableAllFirmware || (config.nixpkgs.config.allowUnfree or false);
+        message = ''
+          the list of hardware.enableAllFirmware contains non-redistributable licensed firmware files.
+            This requires nixpkgs.config.allowUnfree to be true.
+            An alternative is to use the hardware.enableRedistributableFirmware option.
+        '';
+      }];
+      hardware.firmware = with pkgs; [
+        broadcom-bt-firmware
+        b43Firmware_5_1_138
+        b43Firmware_6_30_163_46
+        b43FirmwareCutter
+        xow_dongle-firmware
+      ] ++ optional pkgs.stdenv.hostPlatform.isx86 facetimehd-firmware;
+    })
+    (mkIf cfg.wirelessRegulatoryDatabase {
+      hardware.firmware = [ pkgs.wireless-regdb ];
+    })
+  ];
+}
diff --git a/nixos/modules/hardware/bladeRF.nix b/nixos/modules/hardware/bladeRF.nix
new file mode 100644
index 00000000000..35b74b8382e
--- /dev/null
+++ b/nixos/modules/hardware/bladeRF.nix
@@ -0,0 +1,28 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.bladeRF;
+
+in
+
+{
+  options.hardware.bladeRF = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables udev rules for BladeRF devices. By default grants access
+        to users in the "bladerf" group. You may want to install the
+        libbladeRF package.
+      '';
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.libbladeRF ];
+    users.groups.bladerf = {};
+  };
+}
diff --git a/nixos/modules/hardware/brillo.nix b/nixos/modules/hardware/brillo.nix
new file mode 100644
index 00000000000..e970c948099
--- /dev/null
+++ b/nixos/modules/hardware/brillo.nix
@@ -0,0 +1,22 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.brillo;
+in
+{
+  options = {
+    hardware.brillo = {
+      enable = mkEnableOption ''
+        Enable brillo in userspace.
+        This will allow brightness control from users in the video group.
+      '';
+    };
+  };
+
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.brillo ];
+    environment.systemPackages = [ pkgs.brillo ];
+  };
+}
diff --git a/nixos/modules/hardware/ckb-next.nix b/nixos/modules/hardware/ckb-next.nix
new file mode 100644
index 00000000000..b2bbd77c9d7
--- /dev/null
+++ b/nixos/modules/hardware/ckb-next.nix
@@ -0,0 +1,53 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.ckb-next;
+
+in
+  {
+    imports = [
+      (mkRenamedOptionModule [ "hardware" "ckb" "enable" ] [ "hardware" "ckb-next" "enable" ])
+      (mkRenamedOptionModule [ "hardware" "ckb" "package" ] [ "hardware" "ckb-next" "package" ])
+    ];
+
+    options.hardware.ckb-next = {
+      enable = mkEnableOption "the Corsair keyboard/mouse driver";
+
+      gid = mkOption {
+        type = types.nullOr types.int;
+        default = null;
+        example = 100;
+        description = ''
+          Limit access to the ckb daemon to a particular group.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.ckb-next;
+        defaultText = literalExpression "pkgs.ckb-next";
+        description = ''
+          The package implementing the Corsair keyboard/mouse driver.
+        '';
+      };
+    };
+
+    config = mkIf cfg.enable {
+      environment.systemPackages = [ cfg.package ];
+
+      systemd.services.ckb-next = {
+        description = "Corsair Keyboards and Mice Daemon";
+        wantedBy = ["multi-user.target"];
+        serviceConfig = {
+          ExecStart = "${cfg.package}/bin/ckb-next-daemon ${optionalString (cfg.gid != null) "--gid=${builtins.toString cfg.gid}"}";
+          Restart = "on-failure";
+        };
+      };
+    };
+
+    meta = {
+      maintainers = with lib.maintainers; [ kierdavis ];
+    };
+  }
diff --git a/nixos/modules/hardware/corectrl.nix b/nixos/modules/hardware/corectrl.nix
new file mode 100644
index 00000000000..3185f6486c7
--- /dev/null
+++ b/nixos/modules/hardware/corectrl.nix
@@ -0,0 +1,62 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.programs.corectrl;
+in
+{
+  options.programs.corectrl = {
+    enable = mkEnableOption ''
+      A tool to overclock amd graphics cards and processors.
+      Add your user to the corectrl group to run corectrl without needing to enter your password
+    '';
+
+    gpuOverclock = {
+      enable = mkEnableOption ''
+        true
+      '';
+      ppfeaturemask = mkOption {
+        type = types.str;
+        default = "0xfffd7fff";
+        example = "0xffffffff";
+        description = ''
+          Sets the `amdgpu.ppfeaturemask` kernel option.
+          In particular, it is used here to set the overdrive bit.
+          Default is `0xfffd7fff` as it is less likely to cause flicker issues.
+          Setting it to `0xffffffff` enables all features.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable (lib.mkMerge [
+    {
+      environment.systemPackages = [ pkgs.corectrl ];
+
+      services.dbus.packages = [ pkgs.corectrl ];
+
+      users.groups.corectrl = { };
+
+      security.polkit.extraConfig = ''
+        polkit.addRule(function(action, subject) {
+            if ((action.id == "org.corectrl.helper.init" ||
+                 action.id == "org.corectrl.helperkiller.init") &&
+                subject.local == true &&
+                subject.active == true &&
+                subject.isInGroup("corectrl")) {
+                    return polkit.Result.YES;
+            }
+        });
+      '';
+    }
+
+    (lib.mkIf cfg.gpuOverclock.enable {
+      # https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/gpu/drm/amd/include/amd_shared.h#n169
+      # The overdrive bit
+      boot.kernelParams = [ "amdgpu.ppfeaturemask=${cfg.gpuOverclock.ppfeaturemask}" ];
+    })
+  ]);
+
+  meta.maintainers = with lib.maintainers; [ artturin ];
+}
diff --git a/nixos/modules/hardware/cpu/amd-microcode.nix b/nixos/modules/hardware/cpu/amd-microcode.nix
new file mode 100644
index 00000000000..621c7066bfe
--- /dev/null
+++ b/nixos/modules/hardware/cpu/amd-microcode.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.cpu.amd.updateMicrocode = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Update the CPU microcode for AMD processors.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.hardware.cpu.amd.updateMicrocode {
+    # Microcode updates must be the first item prepended in the initrd
+    boot.initrd.prepend = mkOrder 1 [ "${pkgs.microcodeAmd}/amd-ucode.img" ];
+  };
+
+}
diff --git a/nixos/modules/hardware/cpu/intel-microcode.nix b/nixos/modules/hardware/cpu/intel-microcode.nix
new file mode 100644
index 00000000000..acce565fd80
--- /dev/null
+++ b/nixos/modules/hardware/cpu/intel-microcode.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+
+  ###### interface
+
+  options = {
+
+    hardware.cpu.intel.updateMicrocode = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Update the CPU microcode for Intel processors.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.hardware.cpu.intel.updateMicrocode {
+    # Microcode updates must be the first item prepended in the initrd
+    boot.initrd.prepend = mkOrder 1 [ "${pkgs.microcodeIntel}/intel-ucode.img" ];
+  };
+
+}
diff --git a/nixos/modules/hardware/cpu/intel-sgx.nix b/nixos/modules/hardware/cpu/intel-sgx.nix
new file mode 100644
index 00000000000..1355ee753f0
--- /dev/null
+++ b/nixos/modules/hardware/cpu/intel-sgx.nix
@@ -0,0 +1,69 @@
+{ config, lib, ... }:
+with lib;
+let
+  cfg = config.hardware.cpu.intel.sgx;
+  defaultPrvGroup = "sgx_prv";
+in
+{
+  options.hardware.cpu.intel.sgx.enableDcapCompat = mkOption {
+    description = ''
+      Whether to enable backward compatibility for SGX software build for the
+      out-of-tree Intel SGX DCAP driver.
+
+      Creates symbolic links for the SGX devices <literal>/dev/sgx_enclave</literal>
+      and <literal>/dev/sgx_provision</literal> to make them available as
+      <literal>/dev/sgx/enclave</literal>  and <literal>/dev/sgx/provision</literal>,
+      respectively.
+    '';
+    type = types.bool;
+    default = true;
+  };
+
+  options.hardware.cpu.intel.sgx.provision = {
+    enable = mkEnableOption "access to the Intel SGX provisioning device";
+    user = mkOption {
+      description = "Owner to assign to the SGX provisioning device.";
+      type = types.str;
+      default = "root";
+    };
+    group = mkOption {
+      description = "Group to assign to the SGX provisioning device.";
+      type = types.str;
+      default = defaultPrvGroup;
+    };
+    mode = mkOption {
+      description = "Mode to set for the SGX provisioning device.";
+      type = types.str;
+      default = "0660";
+    };
+  };
+
+  config = mkMerge [
+    (mkIf cfg.provision.enable {
+      assertions = [
+        {
+          assertion = hasAttr cfg.provision.user config.users.users;
+          message = "Given user does not exist";
+        }
+        {
+          assertion = (cfg.provision.group == defaultPrvGroup) || (hasAttr cfg.provision.group config.users.groups);
+          message = "Given group does not exist";
+        }
+      ];
+
+      users.groups = optionalAttrs (cfg.provision.group == defaultPrvGroup) {
+        "${cfg.provision.group}" = { };
+      };
+
+      services.udev.extraRules = with cfg.provision; ''
+        SUBSYSTEM=="misc", KERNEL=="sgx_provision", OWNER="${user}", GROUP="${group}", MODE="${mode}"
+      '';
+    })
+    (mkIf cfg.enableDcapCompat {
+      services.udev.extraRules = ''
+        SUBSYSTEM=="misc", KERNEL=="sgx_enclave",   SYMLINK+="sgx/enclave"
+        SUBSYSTEM=="misc", KERNEL=="sgx_provision", SYMLINK+="sgx/provision"
+      '';
+    })
+  ];
+}
diff --git a/nixos/modules/hardware/device-tree.nix b/nixos/modules/hardware/device-tree.nix
new file mode 100644
index 00000000000..be67116ad50
--- /dev/null
+++ b/nixos/modules/hardware/device-tree.nix
@@ -0,0 +1,205 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.deviceTree;
+
+  overlayType = types.submodule {
+    options = {
+      name = mkOption {
+        type = types.str;
+        description = ''
+          Name of this overlay
+        '';
+      };
+
+      dtsFile = mkOption {
+        type = types.nullOr types.path;
+        description = ''
+          Path to .dts overlay file, overlay is applied to
+          each .dtb file matching "compatible" of the overlay.
+        '';
+        default = null;
+        example = literalExpression "./dts/overlays.dts";
+      };
+
+      dtsText = mkOption {
+        type = types.nullOr types.str;
+        default = null;
+        description = ''
+          Literal DTS contents, overlay is applied to
+          each .dtb file matching "compatible" of the overlay.
+        '';
+        example = ''
+          /dts-v1/;
+          /plugin/;
+          / {
+                  compatible = "raspberrypi";
+                  fragment@0 {
+                          target-path = "/soc";
+                          __overlay__ {
+                                  pps {
+                                          compatible = "pps-gpio";
+                                          status = "okay";
+                                  };
+                          };
+                  };
+          };
+        '';
+      };
+
+      dtboFile = mkOption {
+        type = types.nullOr types.path;
+        default = null;
+        description = ''
+          Path to .dtbo compiled overlay file.
+        '';
+      };
+    };
+  };
+
+  # this requires kernel package
+  dtbsWithSymbols = pkgs.stdenv.mkDerivation {
+    name = "dtbs-with-symbols";
+    inherit (cfg.kernelPackage) src nativeBuildInputs depsBuildBuild;
+    patches = map (patch: patch.patch) cfg.kernelPackage.kernelPatches;
+    buildPhase = ''
+      patchShebangs scripts/*
+      substituteInPlace scripts/Makefile.lib \
+        --replace 'DTC_FLAGS += $(DTC_FLAGS_$(basetarget))' 'DTC_FLAGS += $(DTC_FLAGS_$(basetarget)) -@'
+      make ${pkgs.stdenv.hostPlatform.linux-kernel.baseConfig} ARCH="${pkgs.stdenv.hostPlatform.linuxArch}"
+      make dtbs ARCH="${pkgs.stdenv.hostPlatform.linuxArch}"
+    '';
+    installPhase = ''
+      make dtbs_install INSTALL_DTBS_PATH=$out/dtbs  ARCH="${pkgs.stdenv.hostPlatform.linuxArch}"
+    '';
+  };
+
+  filterDTBs = src: if isNull cfg.filter
+    then "${src}/dtbs"
+    else
+      pkgs.runCommand "dtbs-filtered" {} ''
+        mkdir -p $out
+        cd ${src}/dtbs
+        find . -type f -name '${cfg.filter}' -print0 \
+          | xargs -0 cp -v --no-preserve=mode --target-directory $out --parents
+      '';
+
+  # Compile single Device Tree overlay source
+  # file (.dts) into its compiled variant (.dtbo)
+  compileDTS = name: f: pkgs.callPackage({ dtc }: pkgs.stdenv.mkDerivation {
+    name = "${name}-dtbo";
+
+    nativeBuildInputs = [ dtc ];
+
+    buildCommand = ''
+      dtc -I dts ${f} -O dtb -@ -o $out
+    '';
+  }) {};
+
+  # Fill in `dtboFile` for each overlay if not set already.
+  # Existence of one of these is guarded by assertion below
+  withDTBOs = xs: flip map xs (o: o // { dtboFile =
+    if isNull o.dtboFile then
+      if !isNull o.dtsFile then compileDTS o.name o.dtsFile
+      else compileDTS o.name (pkgs.writeText "dts" o.dtsText)
+    else o.dtboFile; } );
+
+in
+{
+  imports = [
+    (mkRemovedOptionModule [ "hardware" "deviceTree" "base" ] "Use hardware.deviceTree.kernelPackage instead")
+  ];
+
+  options = {
+      hardware.deviceTree = {
+        enable = mkOption {
+          default = pkgs.stdenv.hostPlatform.linux-kernel.DTB or false;
+          type = types.bool;
+          description = ''
+            Build device tree files. These are used to describe the
+            non-discoverable hardware of a system.
+          '';
+        };
+
+        kernelPackage = mkOption {
+          default = config.boot.kernelPackages.kernel;
+          defaultText = literalExpression "config.boot.kernelPackages.kernel";
+          example = literalExpression "pkgs.linux_latest";
+          type = types.path;
+          description = ''
+            Kernel package containing the base device-tree (.dtb) to boot. Uses
+            device trees bundled with the Linux kernel by default.
+          '';
+        };
+
+        name = mkOption {
+          default = null;
+          example = "some-dtb.dtb";
+          type = types.nullOr types.str;
+          description = ''
+            The name of an explicit dtb to be loaded, relative to the dtb base.
+            Useful in extlinux scenarios if the bootloader doesn't pick the
+            right .dtb file from FDTDIR.
+          '';
+        };
+
+        filter = mkOption {
+          type = types.nullOr types.str;
+          default = null;
+          example = "*rpi*.dtb";
+          description = ''
+            Only include .dtb files matching glob expression.
+          '';
+        };
+
+        overlays = mkOption {
+          default = [];
+          example = literalExpression ''
+            [
+              { name = "pps"; dtsFile = ./dts/pps.dts; }
+              { name = "spi";
+                dtsText = "...";
+              }
+              { name = "precompiled"; dtboFile = ./dtbos/example.dtbo; }
+            ]
+          '';
+          type = types.listOf (types.coercedTo types.path (path: {
+            name = baseNameOf path;
+            dtboFile = path;
+          }) overlayType);
+          description = ''
+            List of overlays to apply to base device-tree (.dtb) files.
+          '';
+        };
+
+        package = mkOption {
+          default = null;
+          type = types.nullOr types.path;
+          internal = true;
+          description = ''
+            A path containing the result of applying `overlays` to `kernelPackage`.
+          '';
+        };
+      };
+  };
+
+  config = mkIf (cfg.enable) {
+
+    assertions = let
+      invalidOverlay = o: isNull o.dtsFile && isNull o.dtsText && isNull o.dtboFile;
+    in lib.singleton {
+      assertion = lib.all (o: !invalidOverlay o) cfg.overlays;
+      message = ''
+        deviceTree overlay needs one of dtsFile, dtsText or dtboFile set.
+        Offending overlay(s):
+        ${toString (map (o: o.name) (builtins.filter invalidOverlay cfg.overlays))}
+      '';
+    };
+
+    hardware.deviceTree.package = if (cfg.overlays != [])
+      then pkgs.deviceTree.applyOverlays (filterDTBs dtbsWithSymbols) (withDTBOs cfg.overlays)
+      else (filterDTBs cfg.kernelPackage);
+  };
+}
diff --git a/nixos/modules/hardware/digitalbitbox.nix b/nixos/modules/hardware/digitalbitbox.nix
new file mode 100644
index 00000000000..097448a74f4
--- /dev/null
+++ b/nixos/modules/hardware/digitalbitbox.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.digitalbitbox;
+in
+
+{
+  options.hardware.digitalbitbox = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables udev rules for Digital Bitbox devices.
+      '';
+    };
+
+    package = mkOption {
+      type = types.package;
+      default = pkgs.digitalbitbox;
+      defaultText = literalExpression "pkgs.digitalbitbox";
+      description = "The Digital Bitbox package to use. This can be used to install a package with udev rules that differ from the defaults.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ cfg.package ];
+  };
+}
diff --git a/nixos/modules/hardware/flirc.nix b/nixos/modules/hardware/flirc.nix
new file mode 100644
index 00000000000..94ec715b9fa
--- /dev/null
+++ b/nixos/modules/hardware/flirc.nix
@@ -0,0 +1,12 @@
+{ config, lib, pkgs, ... }:
+let
+  cfg = config.hardware.flirc;
+in
+{
+  options.hardware.flirc.enable = lib.mkEnableOption "software to configure a Flirc USB device";
+
+  config = lib.mkIf cfg.enable {
+    environment.systemPackages = [ pkgs.flirc ];
+    services.udev.packages = [ pkgs.flirc ];
+  };
+}
diff --git a/nixos/modules/hardware/gkraken.nix b/nixos/modules/hardware/gkraken.nix
new file mode 100644
index 00000000000..97d15369db0
--- /dev/null
+++ b/nixos/modules/hardware/gkraken.nix
@@ -0,0 +1,18 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.gkraken;
+in
+{
+  options.hardware.gkraken = {
+    enable = mkEnableOption "gkraken's udev rules for NZXT AIO liquid coolers";
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = with pkgs; [
+      gkraken
+    ];
+  };
+}
diff --git a/nixos/modules/hardware/gpgsmartcards.nix b/nixos/modules/hardware/gpgsmartcards.nix
new file mode 100644
index 00000000000..6e5fcda6b85
--- /dev/null
+++ b/nixos/modules/hardware/gpgsmartcards.nix
@@ -0,0 +1,37 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  # gnupg's manual describes how to setup ccid udev rules:
+  #   https://www.gnupg.org/howtos/card-howto/en/ch02s03.html
+  # gnupg folks advised me (https://dev.gnupg.org/T5409) to look at debian's rules:
+  # https://salsa.debian.org/debian/gnupg2/-/blob/debian/main/debian/scdaemon.udev
+
+  # the latest rev of the entire debian gnupg2 repo as of 2021-04-28
+  # the scdaemon.udev file was last commited on 2021-01-05 (7817a03):
+  scdaemonUdevRev = "01898735a015541e3ffb43c7245ac1e612f40836";
+
+  scdaemonRules = pkgs.fetchurl {
+    url = "https://salsa.debian.org/debian/gnupg2/-/raw/${scdaemonUdevRev}/debian/scdaemon.udev";
+    sha256 = "08v0vp6950bz7galvc92zdss89y9vcwbinmbfcdldy8x72w6rqr3";
+  };
+
+  # per debian's udev deb hook (https://man7.org/linux/man-pages/man1/dh_installudev.1.html)
+  destination = "60-scdaemon.rules";
+
+  scdaemonUdevRulesPkg = pkgs.runCommandNoCC "scdaemon-udev-rules" {} ''
+    loc="$out/lib/udev/rules.d/"
+    mkdir -p "''${loc}"
+    cp "${scdaemonRules}" "''${loc}/${destination}"
+  '';
+
+  cfg = config.hardware.gpgSmartcards;
+in {
+  options.hardware.gpgSmartcards = {
+    enable = mkEnableOption "udev rules for gnupg smart cards";
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ scdaemonUdevRulesPkg ];
+  };
+}
diff --git a/nixos/modules/hardware/hackrf.nix b/nixos/modules/hardware/hackrf.nix
new file mode 100644
index 00000000000..7f03b765bbd
--- /dev/null
+++ b/nixos/modules/hardware/hackrf.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.hardware.hackrf;
+
+in
+{
+  options.hardware.hackrf = {
+    enable = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enables hackrf udev rules and ensures 'plugdev' group exists.
+        This is a prerequisite to using HackRF devices without being root, since HackRF USB descriptors will be owned by plugdev through udev.
+      '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    services.udev.packages = [ pkgs.hackrf ];
+    users.groups.plugdev = { };
+  };
+}
diff --git a/nixos/modules/hardware/i2c.nix b/nixos/modules/hardware/i2c.nix
new file mode 100644
index 00000000000..ff14b4b1c89
--- /dev/null
+++ b/nixos/modules/hardware/i2c.nix
@@ -0,0 +1,43 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.i2c;
+in
+
+{
+  options.hardware.i2c = {
+    enable = mkEnableOption ''
+      i2c devices support. By default access is granted to users in the "i2c"
+      group (will be created if non-existent) and any user with a seat, meaning
+      logged on the computer locally.
+    '';
+
+    group = mkOption {
+      type = types.str;
+      default = "i2c";
+      description = ''
+        Grant access to i2c devices (/dev/i2c-*) to users in this group.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+
+    boot.kernelModules = [ "i2c-dev" ];
+
+    users.groups = mkIf (cfg.group == "i2c") {
+      i2c = { };
+    };
+
+    services.udev.extraRules = ''
+      # allow group ${cfg.group} and users with a seat use of i2c devices
+      ACTION=="add", KERNEL=="i2c-[0-9]*", TAG+="uaccess", GROUP="${cfg.group}", MODE="660"
+    '';
+
+  };
+
+  meta.maintainers = [ maintainers.rnhmjoj ];
+
+}
diff --git a/nixos/modules/hardware/keyboard/teck.nix b/nixos/modules/hardware/keyboard/teck.nix
new file mode 100644
index 00000000000..091ddb81962
--- /dev/null
+++ b/nixos/modules/hardware/keyboard/teck.nix
@@ -0,0 +1,16 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.keyboard.teck;
+in
+{
+  options.hardware.keyboard.teck = {
+    enable = mkEnableOption "non-root access to the firmware of TECK keyboards";
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.teck-udev-rules ];
+  };
+}
+
diff --git a/nixos/modules/hardware/keyboard/zsa.nix b/nixos/modules/hardware/keyboard/zsa.nix
new file mode 100644
index 00000000000..bb69cfa0bf0
--- /dev/null
+++ b/nixos/modules/hardware/keyboard/zsa.nix
@@ -0,0 +1,24 @@
+{ config, lib, pkgs, ... }:
+
+let
+  inherit (lib) mkOption mkIf types;
+  cfg = config.hardware.keyboard.zsa;
+in
+{
+  options.hardware.keyboard.zsa = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables udev rules for keyboards from ZSA like the ErgoDox EZ, Planck EZ and Moonlander Mark I.
+        You need it when you want to flash a new configuration on the keyboard
+        or use their live training in the browser.
+        You may want to install the wally-cli package.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.zsa-udev-rules ];
+  };
+}
diff --git a/nixos/modules/hardware/ksm.nix b/nixos/modules/hardware/ksm.nix
new file mode 100644
index 00000000000..829c3532c45
--- /dev/null
+++ b/nixos/modules/hardware/ksm.nix
@@ -0,0 +1,38 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.ksm;
+
+in {
+  imports = [
+    (mkRenamedOptionModule [ "hardware" "enableKSM" ] [ "hardware" "ksm" "enable" ])
+  ];
+
+  options.hardware.ksm = {
+    enable = mkEnableOption "Kernel Same-Page Merging";
+    sleep = mkOption {
+      type = types.nullOr types.int;
+      default = null;
+      description = ''
+        How many milliseconds ksmd should sleep between scans.
+        Setting it to <literal>null</literal> uses the kernel's default time.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    systemd.services.enable-ksm = {
+      description = "Enable Kernel Same-Page Merging";
+      wantedBy = [ "multi-user.target" ];
+      script =
+        ''
+          echo 1 > /sys/kernel/mm/ksm/run
+        '' + optionalString (cfg.sleep != null)
+        ''
+          echo ${toString cfg.sleep} > /sys/kernel/mm/ksm/sleep_millisecs
+        '';
+    };
+  };
+}
diff --git a/nixos/modules/hardware/ledger.nix b/nixos/modules/hardware/ledger.nix
new file mode 100644
index 00000000000..41abe74315a
--- /dev/null
+++ b/nixos/modules/hardware/ledger.nix
@@ -0,0 +1,14 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.ledger;
+
+in {
+  options.hardware.ledger.enable = mkEnableOption "udev rules for Ledger devices";
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.ledger-udev-rules ];
+  };
+}
diff --git a/nixos/modules/hardware/logitech.nix b/nixos/modules/hardware/logitech.nix
new file mode 100644
index 00000000000..3ebe6aacf5d
--- /dev/null
+++ b/nixos/modules/hardware/logitech.nix
@@ -0,0 +1,96 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.logitech;
+
+  vendor = "046d";
+
+  daemon = "g15daemon";
+
+in
+{
+  imports = [
+    (mkRenamedOptionModule [ "hardware" "logitech" "enable" ] [ "hardware" "logitech" "wireless" "enable" ])
+    (mkRenamedOptionModule [ "hardware" "logitech" "enableGraphical" ] [ "hardware" "logitech" "wireless" "enableGraphical" ])
+  ];
+
+  options.hardware.logitech = {
+
+    lcd = {
+      enable = mkEnableOption "Logitech LCD Devices";
+
+      startWhenNeeded = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Only run the service when an actual supported device is plugged.
+        '';
+      };
+
+      devices = mkOption {
+        type = types.listOf types.str;
+        default = [ "0a07" "c222" "c225" "c227" "c251" ];
+        description = ''
+          List of USB device ids supported by g15daemon.
+          </para>
+          <para>
+          You most likely do not need to change this.
+        '';
+      };
+    };
+
+    wireless = {
+      enable = mkEnableOption "Logitech Wireless Devices";
+
+      enableGraphical = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Enable graphical support applications.";
+      };
+    };
+  };
+
+  config = lib.mkIf (cfg.wireless.enable || cfg.lcd.enable) {
+    environment.systemPackages = []
+      ++ lib.optional cfg.wireless.enable pkgs.ltunify
+      ++ lib.optional cfg.wireless.enableGraphical pkgs.solaar;
+
+    services.udev = {
+      # ltunifi and solaar both provide udev rules but the most up-to-date have been split
+      # out into a dedicated derivation
+
+      packages = []
+      ++ lib.optional cfg.wireless.enable pkgs.logitech-udev-rules
+      ++ lib.optional cfg.lcd.enable pkgs.g15daemon;
+
+      extraRules = ''
+        # nixos: hardware.logitech.lcd
+      '' + lib.concatMapStringsSep "\n" (
+        dev:
+          ''ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="${vendor}", ATTRS{idProduct}=="${dev}", TAG+="systemd", ENV{SYSTEMD_WANTS}+="${daemon}.service"''
+      ) cfg.lcd.devices;
+    };
+
+    systemd.services."${daemon}" = lib.mkIf cfg.lcd.enable {
+      description = "Logitech LCD Support Daemon";
+      documentation = [ "man:g15daemon(1)" ];
+      wantedBy = lib.mkIf (! cfg.lcd.startWhenNeeded) "multi-user.target";
+
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = "${pkgs.g15daemon}/bin/g15daemon";
+        # we patch it to write to /run/g15daemon/g15daemon.pid instead of
+        # /run/g15daemon.pid so systemd will do the cleanup for us.
+        PIDFile = "/run/${daemon}/g15daemon.pid";
+        PrivateTmp = true;
+        PrivateNetwork = true;
+        ProtectHome = "tmpfs";
+        ProtectSystem = "full"; # strict doesn't work
+        RuntimeDirectory = daemon;
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/hardware/mcelog.nix b/nixos/modules/hardware/mcelog.nix
new file mode 100644
index 00000000000..13ad238870c
--- /dev/null
+++ b/nixos/modules/hardware/mcelog.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  meta.maintainers = with maintainers; [ grahamc ];
+  options = {
+
+    hardware.mcelog = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the Machine Check Exception logger.
+        '';
+      };
+    };
+
+  };
+
+  config = mkIf config.hardware.mcelog.enable {
+    systemd = {
+      packages = [ pkgs.mcelog ];
+
+      services.mcelog = {
+        wantedBy = [ "multi-user.target" ];
+        serviceConfig = {
+          ProtectHome = true;
+          PrivateNetwork = true;
+          PrivateTmp = true;
+        };
+      };
+    };
+  };
+}
diff --git a/nixos/modules/hardware/network/ath-user-regd.nix b/nixos/modules/hardware/network/ath-user-regd.nix
new file mode 100644
index 00000000000..b5ade5ed501
--- /dev/null
+++ b/nixos/modules/hardware/network/ath-user-regd.nix
@@ -0,0 +1,31 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  kernelVersion = config.boot.kernelPackages.kernel.version;
+  linuxKernelMinVersion = "5.8";
+  kernelPatch = pkgs.kernelPatches.ath_regd_optional // {
+    extraConfig = ''
+      ATH_USER_REGD y
+    '';
+  };
+in
+{
+  options.networking.wireless.athUserRegulatoryDomain = mkOption {
+    default = false;
+    type = types.bool;
+    description = ''
+      If enabled, sets the ATH_USER_REGD kernel config switch to true to
+      disable the enforcement of EEPROM regulatory restrictions for ath
+      drivers. Requires at least Linux ${linuxKernelMinVersion}.
+    '';
+  };
+
+  config = mkIf config.networking.wireless.athUserRegulatoryDomain {
+    assertions = singleton {
+      assertion = lessThan 0 (builtins.compareVersions kernelVersion linuxKernelMinVersion);
+      message = "ATH_USER_REGD patch for kernels older than ${linuxKernelMinVersion} not ported yet!";
+    };
+    boot.kernelPatches = [ kernelPatch ];
+  };
+}
diff --git a/nixos/modules/hardware/network/b43.nix b/nixos/modules/hardware/network/b43.nix
new file mode 100644
index 00000000000..eb03bf223cc
--- /dev/null
+++ b/nixos/modules/hardware/network/b43.nix
@@ -0,0 +1,30 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let kernelVersion = config.boot.kernelPackages.kernel.version; in
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableB43Firmware = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Turn on this option if you want firmware for the NICs supported by the b43 module.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = mkIf config.networking.enableB43Firmware {
+    hardware.firmware = [ pkgs.b43Firmware_5_1_138 ];
+  };
+
+}
diff --git a/nixos/modules/hardware/network/broadcom-43xx.nix b/nixos/modules/hardware/network/broadcom-43xx.nix
new file mode 100644
index 00000000000..c92b7a0509d
--- /dev/null
+++ b/nixos/modules/hardware/network/broadcom-43xx.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableRedistributableFirmware = true;
+}
diff --git a/nixos/modules/hardware/network/intel-2200bg.nix b/nixos/modules/hardware/network/intel-2200bg.nix
new file mode 100644
index 00000000000..17b973474c9
--- /dev/null
+++ b/nixos/modules/hardware/network/intel-2200bg.nix
@@ -0,0 +1,30 @@
+{ config, pkgs, lib, ... }:
+
+{
+
+  ###### interface
+
+  options = {
+
+    networking.enableIntel2200BGFirmware = lib.mkOption {
+      default = false;
+      type = lib.types.bool;
+      description = ''
+        Turn on this option if you want firmware for the Intel
+        PRO/Wireless 2200BG to be loaded automatically.  This is
+        required if you want to use this device.
+      '';
+    };
+
+  };
+
+
+  ###### implementation
+
+  config = lib.mkIf config.networking.enableIntel2200BGFirmware {
+
+    hardware.firmware = [ pkgs.intel2200BGFirmware ];
+
+  };
+
+}
diff --git a/nixos/modules/hardware/network/smc-2632w/default.nix b/nixos/modules/hardware/network/smc-2632w/default.nix
new file mode 100644
index 00000000000..b00286464f3
--- /dev/null
+++ b/nixos/modules/hardware/network/smc-2632w/default.nix
@@ -0,0 +1,9 @@
+{lib, ...}:
+
+{
+  hardware = {
+    pcmcia = {
+      firmware = [ (lib.cleanSource ./firmware) ];
+    };
+  };
+}
diff --git a/nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis b/nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis
new file mode 100644
index 00000000000..5f13088c373
--- /dev/null
+++ b/nixos/modules/hardware/network/smc-2632w/firmware/cis/SMC2632W-v1.02.cis
@@ -0,0 +1,8 @@
+  vers_1 5.0, "SMC", "SMC2632W", "Version 01.02", ""
+  manfid 0x0156, 0x0002
+  funcid network_adapter
+  cftable_entry 0x01 [default]
+    Vcc Vmin 3000mV Vmax 3300mV Iavg 300mA Ipeak 300mA
+    Idown 10mA
+    io 0x0000-0x003f [lines=6] [16bit]
+    irq mask 0xffff [level] [pulse]
diff --git a/nixos/modules/hardware/network/zydas-zd1211.nix b/nixos/modules/hardware/network/zydas-zd1211.nix
new file mode 100644
index 00000000000..5dd7f30ed82
--- /dev/null
+++ b/nixos/modules/hardware/network/zydas-zd1211.nix
@@ -0,0 +1,5 @@
+{pkgs, ...}:
+
+{
+  hardware.firmware = [ pkgs.zd1211fw ];
+}
diff --git a/nixos/modules/hardware/nitrokey.nix b/nixos/modules/hardware/nitrokey.nix
new file mode 100644
index 00000000000..baa07203118
--- /dev/null
+++ b/nixos/modules/hardware/nitrokey.nix
@@ -0,0 +1,27 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.nitrokey;
+
+in
+
+{
+  options.hardware.nitrokey = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enables udev rules for Nitrokey devices. By default grants access
+        to users in the "nitrokey" group. You may want to install the
+        nitrokey-app package, depending on your device and needs.
+      '';
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [ pkgs.nitrokey-udev-rules ];
+  };
+}
diff --git a/nixos/modules/hardware/onlykey/default.nix b/nixos/modules/hardware/onlykey/default.nix
new file mode 100644
index 00000000000..07358c8a878
--- /dev/null
+++ b/nixos/modules/hardware/onlykey/default.nix
@@ -0,0 +1,33 @@
+{ config, lib, ... }:
+
+with lib;
+
+{
+
+  ####### interface
+
+  options = {
+
+    hardware.onlykey = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable OnlyKey device (https://crp.to/p/) support.
+        '';
+      };
+    };
+
+  };
+
+  ## As per OnlyKey's documentation piece (hhttps://docs.google.com/document/d/1Go_Rs218fKUx-j_JKhddbSVTqY6P0vQO831t2MKCJC8),
+  ## it is important to add udev rule for OnlyKey for it to work on Linux
+
+  ####### implementation
+
+  config = mkIf config.hardware.onlykey.enable {
+    services.udev.extraRules = builtins.readFile ./onlykey.udev;
+  };
+
+
+}
diff --git a/nixos/modules/hardware/onlykey/onlykey.udev b/nixos/modules/hardware/onlykey/onlykey.udev
new file mode 100644
index 00000000000..9c8873aafc9
--- /dev/null
+++ b/nixos/modules/hardware/onlykey/onlykey.udev
@@ -0,0 +1,18 @@
+# UDEV Rules for OnlyKey, https://docs.crp.to/linux.html
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", ENV{MTP_NO_PROBE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", MODE:="0666"
+KERNEL=="ttyACM*", ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="60fc", MODE:="0666"
+
+
+# The udev rules were updated upstream without an explanation as you can
+# see in [this comment][commit]. Assuming that hey have changed the
+# idVendor/idProduct, I've kept the old values.
+# TODO: Contact them upstream.
+#
+# [commit]: https://github.com/trustcrypto/trustcrypto.github.io/commit/0bcf928adaea559e75efa02ebd1040f0a15f611d
+#
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", ENV{ID_MM_DEVICE_IGNORE}="1"
+ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789A]?", ENV{MTP_NO_PROBE}="1"
+SUBSYSTEMS=="usb", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789ABCD]?", GROUP="plugdev"
+KERNEL=="ttyACM*", ATTRS{idVendor}=="16c0", ATTRS{idProduct}=="04[789B]?", GROUP="plugdev"
diff --git a/nixos/modules/hardware/opengl.nix b/nixos/modules/hardware/opengl.nix
new file mode 100644
index 00000000000..0d8aaf73459
--- /dev/null
+++ b/nixos/modules/hardware/opengl.nix
@@ -0,0 +1,157 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.opengl;
+
+  kernelPackages = config.boot.kernelPackages;
+
+  videoDrivers = config.services.xserver.videoDrivers;
+
+  package = pkgs.buildEnv {
+    name = "opengl-drivers";
+    paths = [ cfg.package ] ++ cfg.extraPackages;
+  };
+
+  package32 = pkgs.buildEnv {
+    name = "opengl-drivers-32bit";
+    paths = [ cfg.package32 ] ++ cfg.extraPackages32;
+  };
+
+in
+
+{
+
+  imports = [
+    (mkRenamedOptionModule [ "services" "xserver" "vaapiDrivers" ] [ "hardware" "opengl" "extraPackages" ])
+    (mkRemovedOptionModule [ "hardware" "opengl" "s3tcSupport" ] ''
+      S3TC support is now always enabled in Mesa.
+    '')
+  ];
+
+  options = {
+
+    hardware.opengl = {
+      enable = mkOption {
+        description = ''
+          Whether to enable OpenGL drivers. This is needed to enable
+          OpenGL support in X11 systems, as well as for Wayland compositors
+          like sway and Weston. It is enabled by default
+          by the corresponding modules, so you do not usually have to
+          set it yourself, only if there is no module for your wayland
+          compositor of choice. See services.xserver.enable and
+          programs.sway.enable.
+        '';
+        type = types.bool;
+        default = false;
+      };
+
+      driSupport = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Whether to enable accelerated OpenGL rendering through the
+          Direct Rendering Interface (DRI).
+        '';
+      };
+
+      driSupport32Bit = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          On 64-bit systems, whether to support Direct Rendering for
+          32-bit applications (such as Wine).  This is currently only
+          supported for the <literal>nvidia</literal> as well as
+          <literal>Mesa</literal>.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        internal = true;
+        description = ''
+          The package that provides the OpenGL implementation.
+        '';
+      };
+
+      package32 = mkOption {
+        type = types.package;
+        internal = true;
+        description = ''
+          The package that provides the 32-bit OpenGL implementation on
+          64-bit systems. Used when <option>driSupport32Bit</option> is
+          set.
+        '';
+      };
+
+      extraPackages = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExpression "with pkgs; [ vaapiIntel libvdpau-va-gl vaapiVdpau intel-ocl ]";
+        description = ''
+          Additional packages to add to OpenGL drivers. This can be used
+          to add OpenCL drivers, VA-API/VDPAU drivers etc.
+        '';
+      };
+
+      extraPackages32 = mkOption {
+        type = types.listOf types.package;
+        default = [];
+        example = literalExpression "with pkgs.pkgsi686Linux; [ vaapiIntel libvdpau-va-gl vaapiVdpau ]";
+        description = ''
+          Additional packages to add to 32-bit OpenGL drivers on
+          64-bit systems. Used when <option>driSupport32Bit</option> is
+          set. This can be used to add OpenCL drivers, VA-API/VDPAU drivers etc.
+        '';
+      };
+
+      setLdLibraryPath = mkOption {
+        type = types.bool;
+        internal = true;
+        default = false;
+        description = ''
+          Whether the <literal>LD_LIBRARY_PATH</literal> environment variable
+          should be set to the locations of driver libraries. Drivers which
+          rely on overriding libraries should set this to true. Drivers which
+          support <literal>libglvnd</literal> and other dispatch libraries
+          instead of overriding libraries should not set this.
+        '';
+      };
+    };
+
+  };
+
+  config = mkIf cfg.enable {
+
+    assertions = [
+      { assertion = cfg.driSupport32Bit -> pkgs.stdenv.isx86_64;
+        message = "Option driSupport32Bit only makes sense on a 64-bit system.";
+      }
+      { assertion = cfg.driSupport32Bit -> (config.boot.kernelPackages.kernel.features.ia32Emulation or false);
+        message = "Option driSupport32Bit requires a kernel that supports 32bit emulation";
+      }
+    ];
+
+    systemd.tmpfiles.rules = [
+      "L+ /run/opengl-driver - - - - ${package}"
+      (
+        if pkgs.stdenv.isi686 then
+          "L+ /run/opengl-driver-32 - - - - opengl-driver"
+        else if cfg.driSupport32Bit then
+          "L+ /run/opengl-driver-32 - - - - ${package32}"
+        else
+          "r /run/opengl-driver-32"
+      )
+    ];
+
+    environment.sessionVariables.LD_LIBRARY_PATH = mkIf cfg.setLdLibraryPath
+      ([ "/run/opengl-driver/lib" ] ++ optional cfg.driSupport32Bit "/run/opengl-driver-32/lib");
+
+    hardware.opengl.package = mkDefault pkgs.mesa.drivers;
+    hardware.opengl.package32 = mkDefault pkgs.pkgsi686Linux.mesa.drivers;
+
+    boot.extraModulePackages = optional (elem "virtualbox" videoDrivers) kernelPackages.virtualboxGuestAdditions;
+  };
+}
diff --git a/nixos/modules/hardware/openrazer.nix b/nixos/modules/hardware/openrazer.nix
new file mode 100644
index 00000000000..bd9fc485e17
--- /dev/null
+++ b/nixos/modules/hardware/openrazer.nix
@@ -0,0 +1,146 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.openrazer;
+  kernelPackages = config.boot.kernelPackages;
+
+  toPyBoolStr = b: if b then "True" else "False";
+
+  daemonExe = "${pkgs.openrazer-daemon}/bin/openrazer-daemon --config ${daemonConfFile}";
+
+  daemonConfFile = pkgs.writeTextFile {
+    name = "razer.conf";
+    text = ''
+      [General]
+      verbose_logging = ${toPyBoolStr cfg.verboseLogging}
+
+      [Startup]
+      sync_effects_enabled = ${toPyBoolStr cfg.syncEffectsEnabled}
+      devices_off_on_screensaver = ${toPyBoolStr cfg.devicesOffOnScreensaver}
+      mouse_battery_notifier = ${toPyBoolStr cfg.mouseBatteryNotifier}
+
+      [Statistics]
+      key_statistics = ${toPyBoolStr cfg.keyStatistics}
+    '';
+  };
+
+  dbusServiceFile = pkgs.writeTextFile rec {
+    name = "org.razer.service";
+    destination = "/share/dbus-1/services/${name}";
+    text = ''
+      [D-BUS Service]
+      Name=org.razer
+      Exec=${daemonExe}
+      SystemdService=openrazer-daemon.service
+    '';
+  };
+
+  drivers = [
+    "razerkbd"
+    "razermouse"
+    "razerfirefly"
+    "razerkraken"
+    "razermug"
+    "razercore"
+  ];
+in
+{
+  options = {
+    hardware.openrazer = {
+      enable = mkEnableOption ''
+        OpenRazer drivers and userspace daemon.
+      '';
+
+      verboseLogging = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable verbose logging. Logs debug messages.
+        '';
+      };
+
+      syncEffectsEnabled = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Set the sync effects flag to true so any assignment of
+          effects will work across devices.
+        '';
+      };
+
+      devicesOffOnScreensaver = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Turn off the devices when the systems screensaver kicks in.
+        '';
+      };
+
+      mouseBatteryNotifier = mkOption {
+        type = types.bool;
+        default = true;
+        description = ''
+          Mouse battery notifier.
+        '';
+      };
+
+      keyStatistics = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Collects number of keypresses per hour per key used to
+          generate a heatmap.
+        '';
+      };
+
+      users = mkOption {
+        type = with types; listOf str;
+        default = [];
+        description = ''
+          Usernames to be added to the "openrazer" group, so that they
+          can start and interact with the OpenRazer userspace daemon.
+        '';
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    boot.extraModulePackages = [ kernelPackages.openrazer ];
+    boot.kernelModules = drivers;
+
+    # Makes the man pages available so you can succesfully run
+    # > systemctl --user help openrazer-daemon
+    environment.systemPackages = [ pkgs.python3Packages.openrazer-daemon.man ];
+
+    services.udev.packages = [ kernelPackages.openrazer ];
+    services.dbus.packages = [ dbusServiceFile ];
+
+    # A user must be a member of the openrazer group in order to start
+    # the openrazer-daemon. Therefore we make sure that the group
+    # exists.
+    users.groups.openrazer = {
+      members = cfg.users;
+    };
+
+    systemd.user.services.openrazer-daemon = {
+      description = "Daemon to manage razer devices in userspace";
+      unitConfig.Documentation = "man:openrazer-daemon(8)";
+        # Requires a graphical session so the daemon knows when the screensaver
+        # starts. See the 'devicesOffOnScreensaver' option.
+        wantedBy = [ "graphical-session.target" ];
+        partOf = [ "graphical-session.target" ];
+        serviceConfig = {
+          Type = "dbus";
+          BusName = "org.razer";
+          ExecStart = "${daemonExe} --foreground";
+          Restart = "always";
+      };
+    };
+  };
+
+  meta = {
+    maintainers = with lib.maintainers; [ roelvandijk ];
+  };
+}
diff --git a/nixos/modules/hardware/opentabletdriver.nix b/nixos/modules/hardware/opentabletdriver.nix
new file mode 100644
index 00000000000..caba934ebe7
--- /dev/null
+++ b/nixos/modules/hardware/opentabletdriver.nix
@@ -0,0 +1,69 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.opentabletdriver;
+in
+{
+  meta.maintainers = with lib.maintainers; [ thiagokokada ];
+
+  options = {
+    hardware.opentabletdriver = {
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable OpenTabletDriver udev rules, user service and blacklist kernel
+          modules known to conflict with OpenTabletDriver.
+        '';
+      };
+
+      blacklistedKernelModules = mkOption {
+        type = types.listOf types.str;
+        default = [ "hid-uclogic" "wacom" ];
+        description = ''
+          Blacklist of kernel modules known to conflict with OpenTabletDriver.
+        '';
+      };
+
+      package = mkOption {
+        type = types.package;
+        default = pkgs.opentabletdriver;
+        defaultText = literalExpression "pkgs.opentabletdriver";
+        description = ''
+          OpenTabletDriver derivation to use.
+        '';
+      };
+
+      daemon = {
+        enable = mkOption {
+          default = true;
+          type = types.bool;
+          description = ''
+            Whether to start OpenTabletDriver daemon as a systemd user service.
+          '';
+        };
+      };
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ cfg.package ];
+
+    services.udev.packages = [ cfg.package ];
+
+    boot.blacklistedKernelModules = cfg.blacklistedKernelModules;
+
+    systemd.user.services.opentabletdriver = with pkgs; mkIf cfg.daemon.enable {
+      description = "Open source, cross-platform, user-mode tablet driver";
+      wantedBy = [ "graphical-session.target" ];
+      partOf = [ "graphical-session.target" ];
+
+      serviceConfig = {
+        Type = "simple";
+        ExecStart = "${cfg.package}/bin/otd-daemon -c ${cfg.package}/lib/OpenTabletDriver/Configurations";
+        Restart = "on-failure";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/hardware/pcmcia.nix b/nixos/modules/hardware/pcmcia.nix
new file mode 100644
index 00000000000..aef35a28e54
--- /dev/null
+++ b/nixos/modules/hardware/pcmcia.nix
@@ -0,0 +1,60 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  pcmciaUtils = pkgs.pcmciaUtils.passthru.function {
+    inherit (config.hardware.pcmcia) firmware config;
+  };
+
+in
+
+
+{
+  ###### interface
+
+  options = {
+
+    hardware.pcmcia = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable this option to support PCMCIA card.
+        '';
+      };
+
+      firmware = mkOption {
+        type = types.listOf types.path;
+        default = [];
+        description = ''
+          List of firmware used to handle specific PCMCIA card.
+        '';
+      };
+
+      config = mkOption {
+        default = null;
+        type = types.nullOr types.path;
+        description = ''
+          Path to the configuration file which maps the memory, IRQs
+          and ports used by the PCMCIA hardware.
+        '';
+      };
+    };
+
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.pcmcia.enable {
+
+    boot.kernelModules = [ "pcmcia" ];
+
+    services.udev.packages = [ pcmciaUtils ];
+
+    environment.systemPackages = [ pcmciaUtils ];
+
+  };
+
+}
diff --git a/nixos/modules/hardware/printers.nix b/nixos/modules/hardware/printers.nix
new file mode 100644
index 00000000000..ef07542950b
--- /dev/null
+++ b/nixos/modules/hardware/printers.nix
@@ -0,0 +1,130 @@
+{ config, lib, pkgs, ... }:
+with lib;
+let
+  cfg = config.hardware.printers;
+  ppdOptionsString = options: optionalString (options != {})
+    (concatStringsSep " "
+      (mapAttrsToList (name: value: "-o '${name}'='${value}'") options)
+    );
+  ensurePrinter = p: ''
+    ${pkgs.cups}/bin/lpadmin -p '${p.name}' -E \
+      ${optionalString (p.location != null) "-L '${p.location}'"} \
+      ${optionalString (p.description != null) "-D '${p.description}'"} \
+      -v '${p.deviceUri}' \
+      -m '${p.model}' \
+      ${ppdOptionsString p.ppdOptions}
+  '';
+  ensureDefaultPrinter = name: ''
+    ${pkgs.cups}/bin/lpadmin -d '${name}'
+  '';
+
+  # "graph but not # or /" can't be implemented as regex alone due to missing lookahead support
+  noInvalidChars = str: all (c: c != "#" && c != "/") (stringToCharacters str);
+  printerName = (types.addCheck (types.strMatching "[[:graph:]]+") noInvalidChars)
+    // { description = "printable string without spaces, # and /"; };
+
+
+in {
+  options = {
+    hardware.printers = {
+      ensureDefaultPrinter = mkOption {
+        type = types.nullOr printerName;
+        default = null;
+        description = ''
+          Ensures the named printer is the default CUPS printer / printer queue.
+        '';
+      };
+      ensurePrinters = mkOption {
+        description = ''
+          Will regularly ensure that the given CUPS printers are configured as declared here.
+          If a printer's options are manually changed afterwards, they will be overwritten eventually.
+          This option will never delete any printer, even if removed from this list.
+          You can check existing printers with <command>lpstat -s</command>
+          and remove printers with <command>lpadmin -x &lt;printer-name&gt;</command>.
+          Printers not listed here can still be manually configured.
+        '';
+        default = [];
+        type = types.listOf (types.submodule {
+          options = {
+            name = mkOption {
+              type = printerName;
+              example = "BrotherHL_Workroom";
+              description = ''
+                Name of the printer / printer queue.
+                May contain any printable characters except "/", "#", and space.
+              '';
+            };
+            location = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "Workroom";
+              description = ''
+                Optional human-readable location.
+              '';
+            };
+            description = mkOption {
+              type = types.nullOr types.str;
+              default = null;
+              example = "Brother HL-5140";
+              description = ''
+                Optional human-readable description.
+              '';
+            };
+            deviceUri = mkOption {
+              type = types.str;
+              example = literalExpression ''
+                "ipp://printserver.local/printers/BrotherHL_Workroom"
+                "usb://HP/DESKJET%20940C?serial=CN16E6C364BH"
+              '';
+              description = ''
+                How to reach the printer.
+                <command>lpinfo -v</command> shows a list of supported device URIs and schemes.
+              '';
+            };
+            model = mkOption {
+              type = types.str;
+              example = literalExpression ''
+                "gutenprint.''${lib.versions.majorMinor (lib.getVersion pkgs.gutenprint)}://brother-hl-5140/expert"
+              '';
+              description = ''
+                Location of the ppd driver file for the printer.
+                <command>lpinfo -m</command> shows a list of supported models.
+              '';
+            };
+            ppdOptions = mkOption {
+              type = types.attrsOf types.str;
+              example = {
+                PageSize = "A4";
+                Duplex = "DuplexNoTumble";
+              };
+              default = {};
+              description = ''
+                Sets PPD options for the printer.
+                <command>lpoptions [-p printername] -l</command> shows suported PPD options for the given printer.
+              '';
+            };
+          };
+        });
+      };
+    };
+  };
+
+  config = mkIf (cfg.ensurePrinters != [] && config.services.printing.enable) {
+    systemd.services.ensure-printers = let
+      cupsUnit = if config.services.printing.startWhenNeeded then "cups.socket" else "cups.service";
+    in {
+      description = "Ensure NixOS-configured CUPS printers";
+      wantedBy = [ "multi-user.target" ];
+      requires = [ cupsUnit ];
+      after = [ cupsUnit ];
+
+      serviceConfig = {
+        Type = "oneshot";
+        RemainAfterExit = true;
+      };
+
+      script = concatMapStringsSep "\n" ensurePrinter cfg.ensurePrinters
+        + optionalString (cfg.ensureDefaultPrinter != null) (ensureDefaultPrinter cfg.ensureDefaultPrinter);
+    };
+  };
+}
diff --git a/nixos/modules/hardware/raid/hpsa.nix b/nixos/modules/hardware/raid/hpsa.nix
new file mode 100644
index 00000000000..c4977e3fd70
--- /dev/null
+++ b/nixos/modules/hardware/raid/hpsa.nix
@@ -0,0 +1,61 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  hpssacli = pkgs.stdenv.mkDerivation rec {
+    pname = "hpssacli";
+    version = "2.40-13.0";
+
+    src = pkgs.fetchurl {
+      url = "https://downloads.linux.hpe.com/SDR/downloads/MCP/Ubuntu/pool/non-free/${pname}-${version}_amd64.deb";
+      sha256 = "11w7fwk93lmfw0yya4jpjwdmgjimqxx6412sqa166g1pz4jil4sw";
+    };
+
+    nativeBuildInputs = [ pkgs.dpkg ];
+
+    unpackPhase = "dpkg -x $src ./";
+
+    installPhase = ''
+      mkdir -p $out/bin $out/share/doc $out/share/man
+      mv opt/hp/hpssacli/bld/{hpssascripting,hprmstr,hpssacli} $out/bin/
+      mv opt/hp/hpssacli/bld/*.{license,txt}                   $out/share/doc/
+      mv usr/man                                               $out/share/
+
+      for file in $out/bin/*; do
+        chmod +w $file
+        patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" \
+                 --set-rpath ${lib.makeLibraryPath [ pkgs.stdenv.cc.cc ]} \
+                 $file
+      done
+    '';
+
+    dontStrip = true;
+
+    meta = with lib; {
+      description = "HP Smart Array CLI";
+      homepage = "https://downloads.linux.hpe.com/SDR/downloads/MCP/Ubuntu/pool/non-free/";
+      license = licenses.unfreeRedistributable;
+      platforms = [ "x86_64-linux" ];
+      maintainers = with maintainers; [ volth ];
+    };
+  };
+in {
+  ###### interface
+
+  options = {
+    hardware.raid.HPSmartArray = {
+      enable = mkEnableOption "HP Smart Array kernel modules and CLI utility";
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.raid.HPSmartArray.enable {
+
+    boot.initrd.kernelModules = [ "sg" ]; /* hpssacli wants it */
+    boot.initrd.availableKernelModules = [ "hpsa" ];
+
+    environment.systemPackages = [ hpssacli ];
+  };
+}
diff --git a/nixos/modules/hardware/rtl-sdr.nix b/nixos/modules/hardware/rtl-sdr.nix
new file mode 100644
index 00000000000..e85fc04e29b
--- /dev/null
+++ b/nixos/modules/hardware/rtl-sdr.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+let
+  cfg = config.hardware.rtl-sdr;
+
+in {
+  options.hardware.rtl-sdr = {
+    enable = lib.mkOption {
+      type = lib.types.bool;
+      default = false;
+      description = ''
+        Enables rtl-sdr udev rules, ensures 'plugdev' group exists, and blacklists DVB kernel modules.
+        This is a prerequisite to using devices supported by rtl-sdr without being root, since rtl-sdr USB descriptors will be owned by plugdev through udev.
+       '';
+    };
+  };
+
+  config = lib.mkIf cfg.enable {
+    boot.blacklistedKernelModules = [ "dvb_usb_rtl28xxu" "e4000" "rtl2832" ];
+    services.udev.packages = [ pkgs.rtl-sdr ];
+    users.groups.plugdev = {};
+  };
+}
diff --git a/nixos/modules/hardware/sata.nix b/nixos/modules/hardware/sata.nix
new file mode 100644
index 00000000000..81592997d6e
--- /dev/null
+++ b/nixos/modules/hardware/sata.nix
@@ -0,0 +1,100 @@
+{ config, lib, pkgs, ... }:
+let
+  inherit (lib) mkEnableOption mkIf mkOption types;
+
+  cfg = config.hardware.sata.timeout;
+
+  buildRule = d:
+    lib.concatStringsSep ", " [
+      ''ACTION=="add"''
+      ''SUBSYSTEM=="block"''
+      ''ENV{ID_${lib.toUpper d.idBy}}=="${d.name}"''
+      ''TAG+="systemd"''
+      ''ENV{SYSTEMD_WANTS}="${unitName d}"''
+    ];
+
+  devicePath = device:
+    "/dev/disk/by-${device.idBy}/${device.name}";
+
+  unitName = device:
+    "sata-timeout-${lib.strings.sanitizeDerivationName device.name}";
+
+  startScript =
+    pkgs.writeShellScript "sata-timeout.sh" ''
+      set -eEuo pipefail
+
+      device="$1"
+
+      ${pkgs.smartmontools}/bin/smartctl \
+        -l scterc,${toString cfg.deciSeconds},${toString cfg.deciSeconds} \
+        --quietmode errorsonly \
+        "$device"
+    '';
+
+in
+{
+  meta.maintainers = with lib.maintainers; [ peterhoeg ];
+
+  options.hardware.sata.timeout = {
+    enable = mkEnableOption "SATA drive timeouts";
+
+    deciSeconds = mkOption {
+      example = 70;
+      type = types.int;
+      description = ''
+        Set SCT Error Recovery Control timeout in deciseconds for use in RAID configurations.
+
+        Values are as follows:
+           0 = disable SCT ERT
+          70 = default in consumer drives (7 seconds)
+
+        Maximum is disk dependant but probably 60 seconds.
+      '';
+    };
+
+    drives = mkOption {
+      description = "List of drives for which to configure the timeout.";
+      type = types.listOf
+        (types.submodule {
+          options = {
+            name = mkOption {
+              description = "Drive name without the full path.";
+              type = types.str;
+            };
+
+            idBy = mkOption {
+              description = "The method to identify the drive.";
+              type = types.enum [ "path" "wwn" ];
+              default = "path";
+            };
+          };
+        });
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.extraRules = lib.concatMapStringsSep "\n" buildRule cfg.drives;
+
+    systemd.services = lib.listToAttrs (map
+      (e:
+        lib.nameValuePair (unitName e) {
+          description = "SATA timeout for ${e.name}";
+          wantedBy = [ "sata-timeout.target" ];
+          serviceConfig = {
+            Type = "oneshot";
+            ExecStart = "${startScript} '${devicePath e}'";
+            PrivateTmp = true;
+            PrivateNetwork = true;
+            ProtectHome = "tmpfs";
+            ProtectSystem = "strict";
+          };
+        }
+      )
+      cfg.drives);
+
+    systemd.targets.sata-timeout = {
+      description = "SATA timeout";
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+}
diff --git a/nixos/modules/hardware/sensor/hddtemp.nix b/nixos/modules/hardware/sensor/hddtemp.nix
new file mode 100644
index 00000000000..df3f75e229a
--- /dev/null
+++ b/nixos/modules/hardware/sensor/hddtemp.nix
@@ -0,0 +1,81 @@
+{ config, lib, pkgs, ... }:
+let
+  inherit (lib) mkIf mkOption types;
+
+  cfg = config.hardware.sensor.hddtemp;
+
+  wrapper = pkgs.writeShellScript "hddtemp-wrapper" ''
+    set -eEuo pipefail
+
+    file=/var/lib/hddtemp/hddtemp.db
+
+    drives=(${toString (map (e: ''$(realpath ${lib.escapeShellArg e}) '') cfg.drives)})
+
+    cp ${pkgs.hddtemp}/share/hddtemp/hddtemp.db $file
+    ${lib.concatMapStringsSep "\n" (e: "echo ${lib.escapeShellArg e} >> $file") cfg.dbEntries}
+
+    exec ${pkgs.hddtemp}/bin/hddtemp ${lib.escapeShellArgs cfg.extraArgs} \
+      --daemon \
+      --unit=${cfg.unit} \
+      --file=$file \
+      ''${drives[@]}
+  '';
+
+in
+{
+  meta.maintainers = with lib.maintainers; [ peterhoeg ];
+
+  ###### interface
+
+  options = {
+    hardware.sensor.hddtemp = {
+      enable = mkOption {
+        description = ''
+          Enable this option to support HDD/SSD temperature sensors.
+        '';
+        type = types.bool;
+        default = false;
+      };
+
+      drives = mkOption {
+        description = "List of drives to monitor. If you pass /dev/disk/by-path/* entries the symlinks will be resolved as hddtemp doesn't like names with colons.";
+        type = types.listOf types.str;
+      };
+
+      unit = mkOption {
+        description = "Celcius or Fahrenheit";
+        type = types.enum [ "C" "F" ];
+        default = "C";
+      };
+
+      dbEntries = mkOption {
+        description = "Additional DB entries";
+        type = types.listOf types.str;
+        default = [ ];
+      };
+
+      extraArgs = mkOption {
+        description = "Additional arguments passed to the daemon.";
+        type = types.listOf types.str;
+        default = [ ];
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf cfg.enable {
+    systemd.services.hddtemp = {
+      description = "HDD/SSD temperature";
+      wantedBy = [ "multi-user.target" ];
+      serviceConfig = {
+        Type = "forking";
+        ExecStart = wrapper;
+        StateDirectory = "hddtemp";
+        PrivateTmp = true;
+        ProtectHome = "tmpfs";
+        ProtectSystem = "strict";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/hardware/sensor/iio.nix b/nixos/modules/hardware/sensor/iio.nix
new file mode 100644
index 00000000000..8b3ba87a7d9
--- /dev/null
+++ b/nixos/modules/hardware/sensor/iio.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+    hardware.sensor.iio = {
+      enable = mkOption {
+        description = ''
+          Enable this option to support IIO sensors with iio-sensor-proxy.
+
+          IIO sensors are used for orientation and ambient light
+          sensors on some mobile devices.
+        '';
+        type = types.bool;
+        default = false;
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.sensor.iio.enable {
+
+    boot.initrd.availableKernelModules = [ "hid-sensor-hub" ];
+
+    environment.systemPackages = with pkgs; [ iio-sensor-proxy ];
+
+    services.dbus.packages = with pkgs; [ iio-sensor-proxy ];
+    services.udev.packages = with pkgs; [ iio-sensor-proxy ];
+    systemd.packages = with pkgs; [ iio-sensor-proxy ];
+  };
+}
diff --git a/nixos/modules/hardware/steam-hardware.nix b/nixos/modules/hardware/steam-hardware.nix
new file mode 100644
index 00000000000..6218c9ffbb9
--- /dev/null
+++ b/nixos/modules/hardware/steam-hardware.nix
@@ -0,0 +1,32 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.steam-hardware;
+
+in
+
+{
+  options.hardware.steam-hardware = {
+    enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = "Enable udev rules for Steam hardware such as the Steam Controller, other supported controllers and the HTC Vive";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    services.udev.packages = [
+      pkgs.steamPackages.steam
+    ];
+
+    # The uinput module needs to be loaded in order to trigger the udev rules
+    # defined in the steam package for setting permissions on /dev/uinput.
+    #
+    # If the udev rules are not triggered, some controllers won't work with
+    # steam.
+    boot.kernelModules = [ "uinput" ];
+  };
+}
diff --git a/nixos/modules/hardware/system-76.nix b/nixos/modules/hardware/system-76.nix
new file mode 100644
index 00000000000..ca40ee0ebb3
--- /dev/null
+++ b/nixos/modules/hardware/system-76.nix
@@ -0,0 +1,89 @@
+{ config, lib, options, pkgs, ... }:
+
+let
+  inherit (lib) literalExpression mkOption mkEnableOption types mkIf mkMerge optional versionOlder;
+  cfg = config.hardware.system76;
+  opt = options.hardware.system76;
+
+  kpkgs = config.boot.kernelPackages;
+  modules = [ "system76" "system76-io" ] ++ (optional (versionOlder kpkgs.kernel.version "5.5") "system76-acpi");
+  modulePackages = map (m: kpkgs.${m}) modules;
+  moduleConfig = mkIf cfg.kernel-modules.enable {
+    boot.extraModulePackages = modulePackages;
+
+    boot.kernelModules = modules;
+
+    services.udev.packages = modulePackages;
+  };
+
+  firmware-pkg = pkgs.system76-firmware;
+  firmwareConfig = mkIf cfg.firmware-daemon.enable {
+    # Make system76-firmware-cli usable by root from the command line.
+    environment.systemPackages = [ firmware-pkg ];
+
+    services.dbus.packages = [ firmware-pkg ];
+
+    systemd.services.system76-firmware-daemon = {
+      description = "The System76 Firmware Daemon";
+
+      serviceConfig = {
+        ExecStart = "${firmware-pkg}/bin/system76-firmware-daemon";
+
+        Restart = "on-failure";
+      };
+
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+
+  power-pkg = config.boot.kernelPackages.system76-power;
+  powerConfig = mkIf cfg.power-daemon.enable {
+    # Make system76-power usable by root from the command line.
+    environment.systemPackages = [ power-pkg ];
+
+    services.dbus.packages = [ power-pkg ];
+
+    systemd.services.system76-power = {
+      description = "System76 Power Daemon";
+      serviceConfig = {
+        ExecStart = "${power-pkg}/bin/system76-power daemon";
+        Restart = "on-failure";
+        Type = "dbus";
+        BusName = "com.system76.PowerDaemon";
+      };
+      wantedBy = [ "multi-user.target" ];
+    };
+  };
+in {
+  options = {
+    hardware.system76 = {
+      enableAll = mkEnableOption "all recommended configuration for system76 systems";
+
+      firmware-daemon.enable = mkOption {
+        default = cfg.enableAll;
+        defaultText = literalExpression "config.${opt.enableAll}";
+        example = true;
+        description = "Whether to enable the system76 firmware daemon";
+        type = types.bool;
+      };
+
+      kernel-modules.enable = mkOption {
+        default = cfg.enableAll;
+        defaultText = literalExpression "config.${opt.enableAll}";
+        example = true;
+        description = "Whether to make the system76 out-of-tree kernel modules available";
+        type = types.bool;
+      };
+
+      power-daemon.enable = mkOption {
+        default = cfg.enableAll;
+        defaultText = literalExpression "config.${opt.enableAll}";
+        example = true;
+        description = "Whether to enable the system76 power daemon";
+        type = types.bool;
+      };
+    };
+  };
+
+  config = mkMerge [ moduleConfig firmwareConfig powerConfig ];
+}
diff --git a/nixos/modules/hardware/tuxedo-keyboard.nix b/nixos/modules/hardware/tuxedo-keyboard.nix
new file mode 100644
index 00000000000..97af7c61f3c
--- /dev/null
+++ b/nixos/modules/hardware/tuxedo-keyboard.nix
@@ -0,0 +1,35 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.tuxedo-keyboard;
+  tuxedo-keyboard = config.boot.kernelPackages.tuxedo-keyboard;
+in
+  {
+    options.hardware.tuxedo-keyboard = {
+      enable = mkEnableOption ''
+          Enables the tuxedo-keyboard driver.
+
+          To configure the driver, pass the options to the <option>boot.kernelParams</option> configuration.
+          There are several parameters you can change. It's best to check at the source code description which options are supported.
+          You can find all the supported parameters at: <link xlink:href="https://github.com/tuxedocomputers/tuxedo-keyboard#kernelparam" />
+
+          In order to use the <literal>custom</literal> lighting with the maximumg brightness and a color of <literal>0xff0a0a</literal> one would put pass <option>boot.kernelParams</option> like this:
+
+          <programlisting>
+          boot.kernelParams = [
+           "tuxedo_keyboard.mode=0"
+           "tuxedo_keyboard.brightness=255"
+           "tuxedo_keyboard.color_left=0xff0a0a"
+          ];
+          </programlisting>
+      '';
+    };
+
+    config = mkIf cfg.enable
+    {
+      boot.kernelModules = ["tuxedo_keyboard"];
+      boot.extraModulePackages = [ tuxedo-keyboard ];
+    };
+  }
diff --git a/nixos/modules/hardware/ubertooth.nix b/nixos/modules/hardware/ubertooth.nix
new file mode 100644
index 00000000000..637fddfb37d
--- /dev/null
+++ b/nixos/modules/hardware/ubertooth.nix
@@ -0,0 +1,29 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  cfg = config.hardware.ubertooth;
+
+  ubertoothPkg = pkgs.ubertooth.override {
+    udevGroup = cfg.group;
+  };
+in {
+  options.hardware.ubertooth = {
+    enable = mkEnableOption "Enable the Ubertooth software and its udev rules.";
+
+    group = mkOption {
+      type = types.str;
+      default = "ubertooth";
+      example = "wheel";
+      description = "Group for Ubertooth's udev rules.";
+    };
+  };
+
+  config = mkIf cfg.enable {
+    environment.systemPackages = [ ubertoothPkg ];
+
+    services.udev.packages = [ ubertoothPkg ];
+    users.groups.${cfg.group} = {};
+  };
+}
diff --git a/nixos/modules/hardware/uinput.nix b/nixos/modules/hardware/uinput.nix
new file mode 100644
index 00000000000..55e86bfa6bd
--- /dev/null
+++ b/nixos/modules/hardware/uinput.nix
@@ -0,0 +1,19 @@
+{ config, pkgs, lib, ... }:
+
+let
+  cfg = config.hardware.uinput;
+in {
+  options.hardware.uinput = {
+    enable = lib.mkEnableOption "uinput support";
+  };
+
+  config = lib.mkIf cfg.enable {
+    boot.kernelModules = [ "uinput" ];
+
+    users.groups.uinput = {};
+
+    services.udev.extraRules = ''
+      SUBSYSTEM=="misc", KERNEL=="uinput", MODE="0660", GROUP="uinput", OPTIONS+="static_node=uinput"
+    '';
+  };
+}
diff --git a/nixos/modules/hardware/usb-wwan.nix b/nixos/modules/hardware/usb-wwan.nix
new file mode 100644
index 00000000000..679a6c6497c
--- /dev/null
+++ b/nixos/modules/hardware/usb-wwan.nix
@@ -0,0 +1,39 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  ###### interface
+
+  options = {
+
+    hardware.usbWwan = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable this option to support USB WWAN adapters.
+        '';
+      };
+    };
+  };
+
+  ###### implementation
+
+  config = mkIf config.hardware.usbWwan.enable {
+    # Attaches device specific handlers.
+    services.udev.packages = with pkgs; [ usb-modeswitch-data ];
+
+    # Triggered by udev, usb-modeswitch creates systemd services via a
+    # template unit in the usb-modeswitch package.
+    systemd.packages = with pkgs; [ usb-modeswitch ];
+
+    # The systemd service requires the usb-modeswitch-data. The
+    # usb-modeswitch package intends to discover this via the
+    # filesystem at /usr/share/usb_modeswitch, and merge it with user
+    # configuration in /etc/usb_modeswitch.d. Configuring the correct
+    # path in the package is difficult, as it would cause a cyclic
+    # dependency.
+    environment.etc."usb_modeswitch.d".source = "${pkgs.usb-modeswitch-data}/share/usb_modeswitch";
+  };
+}
diff --git a/nixos/modules/hardware/video/amdgpu-pro.nix b/nixos/modules/hardware/video/amdgpu-pro.nix
new file mode 100644
index 00000000000..d784befc9b8
--- /dev/null
+++ b/nixos/modules/hardware/video/amdgpu-pro.nix
@@ -0,0 +1,70 @@
+# This module provides the proprietary AMDGPU-PRO drivers.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  drivers = config.services.xserver.videoDrivers;
+
+  enabled = elem "amdgpu-pro" drivers;
+
+  package = config.boot.kernelPackages.amdgpu-pro;
+  package32 = pkgs.pkgsi686Linux.linuxPackages.amdgpu-pro.override { kernel = null; };
+
+  opengl = config.hardware.opengl;
+
+in
+
+{
+
+  config = mkIf enabled {
+
+    nixpkgs.config.xorg.abiCompat = "1.20";
+
+    services.xserver.drivers = singleton
+      { name = "amdgpu"; modules = [ package ]; display = true; };
+
+    hardware.opengl.package = package;
+    hardware.opengl.package32 = package32;
+    hardware.opengl.setLdLibraryPath = true;
+
+    boot.extraModulePackages = [ package.kmod ];
+
+    boot.kernelPackages = pkgs.linuxKernel.packagesFor
+      (pkgs.linuxKernel.kernels.linux_5_10.override {
+        structuredExtraConfig = {
+          DEVICE_PRIVATE = kernel.yes;
+          KALLSYMS_ALL = kernel.yes;
+        };
+      });
+
+    hardware.firmware = [ package.fw ];
+
+    system.activationScripts.setup-amdgpu-pro = ''
+      ln -sfn ${package}/opt/amdgpu{,-pro} /run
+    '';
+
+    system.requiredKernelConfig = with config.lib.kernelConfig; [
+      (isYes "DEVICE_PRIVATE")
+      (isYes "KALLSYMS_ALL")
+    ];
+
+    boot.initrd.extraUdevRulesCommands = ''
+      cp -v ${package}/etc/udev/rules.d/*.rules $out/
+    '';
+
+    environment.systemPackages =
+      [ package.vulkan ] ++
+      # this isn't really DRI, but we'll reuse this option for now
+      optional config.hardware.opengl.driSupport32Bit package32.vulkan;
+
+    environment.etc = {
+      "modprobe.d/blacklist-radeon.conf".source = package + "/etc/modprobe.d/blacklist-radeon.conf";
+      amd.source = package + "/etc/amd";
+    };
+
+  };
+
+}
diff --git a/nixos/modules/hardware/video/bumblebee.nix b/nixos/modules/hardware/video/bumblebee.nix
new file mode 100644
index 00000000000..b6af4f80445
--- /dev/null
+++ b/nixos/modules/hardware/video/bumblebee.nix
@@ -0,0 +1,93 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.bumblebee;
+
+  kernel = config.boot.kernelPackages;
+
+  useNvidia = cfg.driver == "nvidia";
+
+  bumblebee = pkgs.bumblebee.override {
+    inherit useNvidia;
+    useDisplayDevice = cfg.connectDisplay;
+  };
+
+  useBbswitch = cfg.pmMethod == "bbswitch" || cfg.pmMethod == "auto" && useNvidia;
+
+  primus = pkgs.primus.override {
+    inherit useNvidia;
+  };
+
+in
+
+{
+
+  options = {
+    hardware.bumblebee = {
+
+      enable = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Enable the bumblebee daemon to manage Optimus hybrid video cards.
+          This should power off secondary GPU until its use is requested
+          by running an application with optirun.
+        '';
+      };
+
+      group = mkOption {
+        default = "wheel";
+        example = "video";
+        type = types.str;
+        description = "Group for bumblebee socket";
+      };
+
+      connectDisplay = mkOption {
+        default = false;
+        type = types.bool;
+        description = ''
+          Set to true if you intend to connect your discrete card to a
+          monitor. This option will set up your Nvidia card for EDID
+          discovery and to turn on the monitor signal.
+
+          Only nvidia driver is supported so far.
+        '';
+      };
+
+      driver = mkOption {
+        default = "nvidia";
+        type = types.enum [ "nvidia" "nouveau" ];
+        description = ''
+          Set driver used by bumblebeed. Supported are nouveau and nvidia.
+        '';
+      };
+
+      pmMethod = mkOption {
+        default = "auto";
+        type = types.enum [ "auto" "bbswitch" "switcheroo" "none" ];
+        description = ''
+          Set preferred power management method for unused card.
+        '';
+      };
+
+    };
+  };
+
+  config = mkIf cfg.enable {
+    boot.blacklistedKernelModules = [ "nvidia-drm" "nvidia" "nouveau" ];
+    boot.kernelModules = optional useBbswitch "bbswitch";
+    boot.extraModulePackages = optional useBbswitch kernel.bbswitch ++ optional useNvidia kernel.nvidia_x11.bin;
+
+    environment.systemPackages = [ bumblebee primus ];
+
+    systemd.services.bumblebeed = {
+      description = "Bumblebee Hybrid Graphics Switcher";
+      wantedBy = [ "multi-user.target" ];
+      before = [ "display-manager.service" ];
+      serviceConfig = {
+        ExecStart = "${bumblebee}/bin/bumblebeed --use-syslog -g ${cfg.group} --driver ${cfg.driver} --pm-method ${cfg.pmMethod}";
+      };
+    };
+  };
+}
diff --git a/nixos/modules/hardware/video/capture/mwprocapture.nix b/nixos/modules/hardware/video/capture/mwprocapture.nix
new file mode 100644
index 00000000000..76cb4c6ee9b
--- /dev/null
+++ b/nixos/modules/hardware/video/capture/mwprocapture.nix
@@ -0,0 +1,56 @@
+{ config, lib, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.mwProCapture;
+
+  kernelPackages = config.boot.kernelPackages;
+
+in
+
+{
+
+  options.hardware.mwProCapture.enable = mkEnableOption "Magewell Pro Capture family kernel module";
+
+  config = mkIf cfg.enable {
+
+    boot.kernelModules = [ "ProCapture" ];
+
+    environment.systemPackages = [ kernelPackages.mwprocapture ];
+
+    boot.extraModulePackages = [ kernelPackages.mwprocapture ];
+
+    boot.extraModprobeConfig = ''
+      # Set the png picture to be displayed when no input signal is detected.
+      options ProCapture nosignal_file=${kernelPackages.mwprocapture}/res/NoSignal.png
+
+      # Set the png picture to be displayed when an unsupported input signal is detected.
+      options ProCapture unsupported_file=${kernelPackages.mwprocapture}/res/Unsupported.png
+
+      # Set the png picture to be displayed when an loking input signal is detected.
+      options ProCapture locking_file=${kernelPackages.mwprocapture}/res/Locking.png
+
+      # Message signaled interrupts switch
+      #options ProCapture disable_msi=0
+
+      # Set the debug level
+      #options ProCapture debug_level=0
+
+      # Force init switch eeprom
+      #options ProCapture init_switch_eeprom=0
+
+      # Min frame interval for VIDIOC_ENUM_FRAMEINTERVALS (default: 166666(100ns))
+      #options ProCapture enum_frameinterval_min=166666
+
+      # VIDIOC_ENUM_FRAMESIZES type (1: DISCRETE; 2: STEPWISE; otherwise: CONTINUOUS )
+      #options ProCapture enum_framesizes_type=0
+
+      # Parameters for internal usage
+      #options ProCapture internal_params=""
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/hardware/video/displaylink.nix b/nixos/modules/hardware/video/displaylink.nix
new file mode 100644
index 00000000000..912f53da836
--- /dev/null
+++ b/nixos/modules/hardware/video/displaylink.nix
@@ -0,0 +1,76 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  enabled = elem "displaylink" config.services.xserver.videoDrivers;
+
+  evdi = config.boot.kernelPackages.evdi;
+
+  displaylink = pkgs.displaylink.override {
+    inherit evdi;
+  };
+
+in
+
+{
+
+  config = mkIf enabled {
+
+    boot.extraModulePackages = [ evdi ];
+    boot.kernelModules = [ "evdi" ];
+
+    environment.etc."X11/xorg.conf.d/40-displaylink.conf".text = ''
+      Section "OutputClass"
+        Identifier  "DisplayLink"
+        MatchDriver "evdi"
+        Driver      "modesetting"
+        Option      "AccelMethod" "none"
+      EndSection
+    '';
+
+    # make the device available
+    services.xserver.displayManager.sessionCommands = ''
+      ${lib.getBin pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource 1 0
+    '';
+
+    # Those are taken from displaylink-installer.sh and from Arch Linux AUR package.
+
+    services.udev.packages = [ displaylink ];
+
+    powerManagement.powerDownCommands = ''
+      #flush any bytes in pipe
+      while read -n 1 -t 1 SUSPEND_RESULT < /tmp/PmMessagesPort_out; do : ; done;
+
+      #suspend DisplayLinkManager
+      echo "S" > /tmp/PmMessagesPort_in
+
+      #wait until suspend of DisplayLinkManager finish
+      if [ -f /tmp/PmMessagesPort_out ]; then
+        #wait until suspend of DisplayLinkManager finish
+        read -n 1 -t 10 SUSPEND_RESULT < /tmp/PmMessagesPort_out
+      fi
+    '';
+
+    powerManagement.resumeCommands = ''
+      #resume DisplayLinkManager
+      echo "R" > /tmp/PmMessagesPort_in
+    '';
+
+    systemd.services.dlm = {
+      description = "DisplayLink Manager Service";
+      after = [ "display-manager.service" ];
+      conflicts = [ "getty@tty7.service" ];
+
+      serviceConfig = {
+        ExecStart = "${displaylink}/bin/DisplayLinkManager";
+        Restart = "always";
+        RestartSec = 5;
+        LogsDirectory = "displaylink";
+      };
+    };
+
+  };
+
+}
diff --git a/nixos/modules/hardware/video/hidpi.nix b/nixos/modules/hardware/video/hidpi.nix
new file mode 100644
index 00000000000..ac72b652504
--- /dev/null
+++ b/nixos/modules/hardware/video/hidpi.nix
@@ -0,0 +1,16 @@
+{ lib, pkgs, config, ...}:
+with lib;
+
+{
+  options.hardware.video.hidpi.enable = mkEnableOption "Font/DPI configuration optimized for HiDPI displays";
+
+  config = mkIf config.hardware.video.hidpi.enable {
+    console.font = lib.mkDefault "${pkgs.terminus_font}/share/consolefonts/ter-v32n.psf.gz";
+
+    # Needed when typing in passwords for full disk encryption
+    console.earlySetup = mkDefault true;
+    boot.loader.systemd-boot.consoleMode = mkDefault "1";
+
+    # TODO Find reasonable defaults X11 & wayland
+  };
+}
diff --git a/nixos/modules/hardware/video/nvidia.nix b/nixos/modules/hardware/video/nvidia.nix
new file mode 100644
index 00000000000..a81220a92a1
--- /dev/null
+++ b/nixos/modules/hardware/video/nvidia.nix
@@ -0,0 +1,391 @@
+# This module provides the proprietary NVIDIA X11 / OpenGL drivers.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+  nvidia_x11 = let
+    drivers = config.services.xserver.videoDrivers;
+    isDeprecated = str: (hasPrefix "nvidia" str) && (str != "nvidia");
+    hasDeprecated = drivers: any isDeprecated drivers;
+  in if (hasDeprecated drivers) then
+    throw ''
+      Selecting an nvidia driver has been modified for NixOS 19.03. The version is now set using `hardware.nvidia.package`.
+    ''
+  else if (elem "nvidia" drivers) then cfg.package else null;
+
+  enabled = nvidia_x11 != null;
+  cfg = config.hardware.nvidia;
+
+  pCfg = cfg.prime;
+  syncCfg = pCfg.sync;
+  offloadCfg = pCfg.offload;
+  primeEnabled = syncCfg.enable || offloadCfg.enable;
+  nvidiaPersistencedEnabled =  cfg.nvidiaPersistenced;
+  nvidiaSettings = cfg.nvidiaSettings;
+in
+
+{
+  imports =
+    [
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "enable" ] [ "hardware" "nvidia" "prime" "sync" "enable" ])
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "allowExternalGpu" ] [ "hardware" "nvidia" "prime" "sync" "allowExternalGpu" ])
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "nvidiaBusId" ] [ "hardware" "nvidia" "prime" "nvidiaBusId" ])
+      (mkRenamedOptionModule [ "hardware" "nvidia" "optimus_prime" "intelBusId" ] [ "hardware" "nvidia" "prime" "intelBusId" ])
+    ];
+
+  options = {
+    hardware.nvidia.powerManagement.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Experimental power management through systemd. For more information, see
+        the NVIDIA docs, on Chapter 21. Configuring Power Management Support.
+      '';
+    };
+
+    hardware.nvidia.powerManagement.finegrained = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Experimental power management of PRIME offload. For more information, see
+        the NVIDIA docs, chapter 22. PCI-Express runtime power management.
+      '';
+    };
+
+    hardware.nvidia.modesetting.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable kernel modesetting when using the NVIDIA proprietary driver.
+
+        Enabling this fixes screen tearing when using Optimus via PRIME (see
+        <option>hardware.nvidia.prime.sync.enable</option>. This is not enabled
+        by default because it is not officially supported by NVIDIA and would not
+        work with SLI.
+      '';
+    };
+
+    hardware.nvidia.prime.nvidiaBusId = mkOption {
+      type = types.str;
+      default = "";
+      example = "PCI:1:0:0";
+      description = ''
+        Bus ID of the NVIDIA GPU. You can find it using lspci; for example if lspci
+        shows the NVIDIA GPU at "01:00.0", set this option to "PCI:1:0:0".
+      '';
+    };
+
+    hardware.nvidia.prime.intelBusId = mkOption {
+      type = types.str;
+      default = "";
+      example = "PCI:0:2:0";
+      description = ''
+        Bus ID of the Intel GPU. You can find it using lspci; for example if lspci
+        shows the Intel GPU at "00:02.0", set this option to "PCI:0:2:0".
+      '';
+    };
+
+    hardware.nvidia.prime.amdgpuBusId = mkOption {
+      type = types.str;
+      default = "";
+      example = "PCI:4:0:0";
+      description = ''
+        Bus ID of the AMD APU. You can find it using lspci; for example if lspci
+        shows the AMD APU at "04:00.0", set this option to "PCI:4:0:0".
+      '';
+    };
+
+    hardware.nvidia.prime.sync.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable NVIDIA Optimus support using the NVIDIA proprietary driver via PRIME.
+        If enabled, the NVIDIA GPU will be always on and used for all rendering,
+        while enabling output to displays attached only to the integrated Intel GPU
+        without a multiplexer.
+
+        Note that this option only has any effect if the "nvidia" driver is specified
+        in <option>services.xserver.videoDrivers</option>, and it should preferably
+        be the only driver there.
+
+        If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
+        specified (<option>hardware.nvidia.prime.nvidiaBusId</option> and
+        <option>hardware.nvidia.prime.intelBusId</option>).
+
+        If you enable this, you may want to also enable kernel modesetting for the
+        NVIDIA driver (<option>hardware.nvidia.modesetting.enable</option>) in order
+        to prevent tearing.
+
+        Note that this configuration will only be successful when a display manager
+        for which the <option>services.xserver.displayManager.setupCommands</option>
+        option is supported is used.
+      '';
+    };
+
+    hardware.nvidia.prime.sync.allowExternalGpu = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Configure X to allow external NVIDIA GPUs when using optimus.
+      '';
+    };
+
+    hardware.nvidia.prime.offload.enable = mkOption {
+      type = types.bool;
+      default = false;
+      description = ''
+        Enable render offload support using the NVIDIA proprietary driver via PRIME.
+
+        If this is enabled, then the bus IDs of the NVIDIA and Intel GPUs have to be
+        specified (<option>hardware.nvidia.prime.nvidiaBusId</option> and
+        <option>hardware.nvidia.prime.intelBusId</option>).
+      '';
+    };
+
+    hardware.nvidia.nvidiaSettings = mkOption {
+      default = true;
+      type = types.bool;
+      description = ''
+        Whether to add nvidia-settings, NVIDIA's GUI configuration tool, to
+        systemPackages.
+      '';
+    };
+
+    hardware.nvidia.nvidiaPersistenced = mkOption {
+      default = false;
+      type = types.bool;
+      description = ''
+        Update for NVIDA GPU headless mode, i.e. nvidia-persistenced. It ensures all
+        GPUs stay awake even during headless mode.
+      '';
+    };
+
+    hardware.nvidia.package = lib.mkOption {
+      type = lib.types.package;
+      default = config.boot.kernelPackages.nvidiaPackages.stable;
+      defaultText = literalExpression "config.boot.kernelPackages.nvidiaPackages.stable";
+      description = ''
+        The NVIDIA X11 derivation to use.
+      '';
+      example = literalExpression "config.boot.kernelPackages.nvidiaPackages.legacy_340";
+    };
+  };
+
+  config = let
+      igpuDriver = if pCfg.intelBusId != "" then "modesetting" else "amdgpu";
+      igpuBusId = if pCfg.intelBusId != "" then pCfg.intelBusId else pCfg.amdgpuBusId;
+  in mkIf enabled {
+    assertions = [
+      {
+        assertion = primeEnabled -> pCfg.intelBusId == "" || pCfg.amdgpuBusId == "";
+        message = ''
+          You cannot configure both an Intel iGPU and an AMD APU. Pick the one corresponding to your processor.
+        '';
+      }
+
+      {
+        assertion = primeEnabled -> pCfg.nvidiaBusId != "" && (pCfg.intelBusId != "" || pCfg.amdgpuBusId != "");
+        message = ''
+          When NVIDIA PRIME is enabled, the GPU bus IDs must configured.
+        '';
+      }
+
+      {
+        assertion = offloadCfg.enable -> versionAtLeast nvidia_x11.version "435.21";
+        message = "NVIDIA PRIME render offload is currently only supported on versions >= 435.21.";
+      }
+
+      {
+        assertion = !(syncCfg.enable && offloadCfg.enable);
+        message = "Only one NVIDIA PRIME solution may be used at a time.";
+      }
+
+      {
+        assertion = !(syncCfg.enable && cfg.powerManagement.finegrained);
+        message = "Sync precludes powering down the NVIDIA GPU.";
+      }
+
+      {
+        assertion = cfg.powerManagement.finegrained -> offloadCfg.enable;
+        message = "Fine-grained power management requires offload to be enabled.";
+      }
+
+      {
+        assertion = cfg.powerManagement.enable -> (
+          builtins.pathExists (cfg.package.out + "/bin/nvidia-sleep.sh") &&
+          builtins.pathExists (cfg.package.out + "/lib/systemd/system-sleep/nvidia")
+        );
+        message = "Required files for driver based power management don't exist.";
+      }
+    ];
+
+    # If Optimus/PRIME is enabled, we:
+    # - Specify the configured NVIDIA GPU bus ID in the Device section for the
+    #   "nvidia" driver.
+    # - Add the AllowEmptyInitialConfiguration option to the Screen section for the
+    #   "nvidia" driver, in order to allow the X server to start without any outputs.
+    # - Add a separate Device section for the Intel GPU, using the "modesetting"
+    #   driver and with the configured BusID.
+    # - OR add a separate Device section for the AMD APU, using the "amdgpu"
+    #   driver and with the configures BusID.
+    # - Reference that Device section from the ServerLayout section as an inactive
+    #   device.
+    # - Configure the display manager to run specific `xrandr` commands which will
+    #   configure/enable displays connected to the Intel iGPU / AMD APU.
+
+    services.xserver.useGlamor = mkDefault offloadCfg.enable;
+
+    services.xserver.drivers = let
+    in optional primeEnabled {
+      name = igpuDriver;
+      display = offloadCfg.enable;
+      modules = optional (igpuDriver == "amdgpu") [ pkgs.xorg.xf86videoamdgpu ];
+      deviceSection = ''
+        BusID "${igpuBusId}"
+        ${optionalString syncCfg.enable ''Option "AccelMethod" "none"''}
+      '';
+    } ++ singleton {
+      name = "nvidia";
+      modules = [ nvidia_x11.bin ];
+      display = !offloadCfg.enable;
+      deviceSection = optionalString primeEnabled
+        ''
+          BusID "${pCfg.nvidiaBusId}"
+          ${optionalString syncCfg.allowExternalGpu "Option \"AllowExternalGpus\""}
+          ${optionalString cfg.powerManagement.finegrained "Option \"NVreg_DynamicPowerManagement=0x02\""}
+        '';
+      screenSection =
+        ''
+          Option "RandRRotation" "on"
+          ${optionalString syncCfg.enable "Option \"AllowEmptyInitialConfiguration\""}
+        '';
+    };
+
+    services.xserver.serverLayoutSection = optionalString syncCfg.enable ''
+      Inactive "Device-${igpuDriver}[0]"
+    '' + optionalString offloadCfg.enable ''
+      Option "AllowNVIDIAGPUScreens"
+    '';
+
+    services.xserver.displayManager.setupCommands = optionalString syncCfg.enable ''
+      # Added by nvidia configuration module for Optimus/PRIME.
+      ${pkgs.xorg.xrandr}/bin/xrandr --setprovideroutputsource ${igpuDriver} NVIDIA-0
+      ${pkgs.xorg.xrandr}/bin/xrandr --auto
+    '';
+
+    environment.etc."nvidia/nvidia-application-profiles-rc" = mkIf nvidia_x11.useProfiles {
+      source = "${nvidia_x11.bin}/share/nvidia/nvidia-application-profiles-rc";
+    };
+
+    # 'nvidia_x11' installs it's files to /run/opengl-driver/...
+    environment.etc."egl/egl_external_platform.d".source =
+      "/run/opengl-driver/share/egl/egl_external_platform.d/";
+
+    hardware.opengl.package = mkIf (!offloadCfg.enable) nvidia_x11.out;
+    hardware.opengl.package32 = mkIf (!offloadCfg.enable) nvidia_x11.lib32;
+    hardware.opengl.extraPackages = [
+      pkgs.nvidia-vaapi-driver
+    ] ++ optional offloadCfg.enable nvidia_x11.out;
+    hardware.opengl.extraPackages32 = [
+      pkgs.pkgsi686Linux.nvidia-vaapi-driver
+    ] ++ optional offloadCfg.enable nvidia_x11.lib32;
+
+    environment.systemPackages = [ nvidia_x11.bin ]
+      ++ optionals cfg.nvidiaSettings [ nvidia_x11.settings ]
+      ++ optionals nvidiaPersistencedEnabled [ nvidia_x11.persistenced ];
+
+    systemd.packages = optional cfg.powerManagement.enable nvidia_x11.out;
+
+    systemd.services = let
+      baseNvidiaService = state: {
+        description = "NVIDIA system ${state} actions";
+
+        path = with pkgs; [ kbd ];
+        serviceConfig = {
+          Type = "oneshot";
+          ExecStart = "${nvidia_x11.out}/bin/nvidia-sleep.sh '${state}'";
+        };
+      };
+
+      nvidiaService = sleepState: (baseNvidiaService sleepState) // {
+        before = [ "systemd-${sleepState}.service" ];
+        requiredBy = [ "systemd-${sleepState}.service" ];
+      };
+
+      services = (builtins.listToAttrs (map (t: nameValuePair "nvidia-${t}" (nvidiaService t)) ["hibernate" "suspend"]))
+        // {
+          nvidia-resume = (baseNvidiaService "resume") // {
+            after = [ "systemd-suspend.service" "systemd-hibernate.service" ];
+            requiredBy = [ "systemd-suspend.service" "systemd-hibernate.service" ];
+          };
+        };
+    in optionalAttrs cfg.powerManagement.enable services
+      // optionalAttrs nvidiaPersistencedEnabled {
+        "nvidia-persistenced" = mkIf nvidiaPersistencedEnabled {
+          description = "NVIDIA Persistence Daemon";
+          wantedBy = [ "multi-user.target" ];
+          serviceConfig = {
+            Type = "forking";
+            Restart = "always";
+            PIDFile = "/var/run/nvidia-persistenced/nvidia-persistenced.pid";
+            ExecStart = "${nvidia_x11.persistenced}/bin/nvidia-persistenced --verbose";
+            ExecStopPost = "${pkgs.coreutils}/bin/rm -rf /var/run/nvidia-persistenced";
+          };
+        };
+      };
+
+    systemd.tmpfiles.rules = optional config.virtualisation.docker.enableNvidia
+        "L+ /run/nvidia-docker/bin - - - - ${nvidia_x11.bin}/origBin"
+      ++ optional (nvidia_x11.persistenced != null && config.virtualisation.docker.enableNvidia)
+        "L+ /run/nvidia-docker/extras/bin/nvidia-persistenced - - - - ${nvidia_x11.persistenced}/origBin/nvidia-persistenced";
+
+    boot.extraModulePackages = [ nvidia_x11.bin ];
+
+    # nvidia-uvm is required by CUDA applications.
+    boot.kernelModules = [ "nvidia-uvm" ] ++
+      optionals config.services.xserver.enable [ "nvidia" "nvidia_modeset" "nvidia_drm" ];
+
+    # If requested enable modesetting via kernel parameter.
+    boot.kernelParams = optional (offloadCfg.enable || cfg.modesetting.enable) "nvidia-drm.modeset=1"
+      ++ optional cfg.powerManagement.enable "nvidia.NVreg_PreserveVideoMemoryAllocations=1";
+
+    services.udev.extraRules =
+      ''
+        # Create /dev/nvidia-uvm when the nvidia-uvm module is loaded.
+        KERNEL=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidiactl c $$(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 255'"
+        KERNEL=="nvidia_modeset", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-modeset c $$(grep nvidia-frontend /proc/devices | cut -d \  -f 1) 254'"
+        KERNEL=="card*", SUBSYSTEM=="drm", DRIVERS=="nvidia", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia%n c $$(grep nvidia-frontend /proc/devices | cut -d \  -f 1) %n'"
+        KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
+        KERNEL=="nvidia_uvm", RUN+="${pkgs.runtimeShell} -c 'mknod -m 666 /dev/nvidia-uvm-tools c $$(grep nvidia-uvm /proc/devices | cut -d \  -f 1) 0'"
+      '' + optionalString cfg.powerManagement.finegrained ''
+        # Remove NVIDIA USB xHCI Host Controller devices, if present
+        ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c0330", ATTR{remove}="1"
+
+        # Remove NVIDIA USB Type-C UCSI devices, if present
+        ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x0c8000", ATTR{remove}="1"
+
+        # Remove NVIDIA Audio devices, if present
+        ACTION=="add", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x040300", ATTR{remove}="1"
+
+        # Enable runtime PM for NVIDIA VGA/3D controller devices on driver bind
+        ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="auto"
+        ACTION=="bind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="auto"
+
+        # Disable runtime PM for NVIDIA VGA/3D controller devices on driver unbind
+        ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030000", TEST=="power/control", ATTR{power/control}="on"
+        ACTION=="unbind", SUBSYSTEM=="pci", ATTR{vendor}=="0x10de", ATTR{class}=="0x030200", TEST=="power/control", ATTR{power/control}="on"
+      '';
+
+    boot.extraModprobeConfig = mkIf cfg.powerManagement.finegrained ''
+      options nvidia "NVreg_DynamicPowerManagement=0x02"
+    '';
+
+    boot.blacklistedKernelModules = [ "nouveau" "nvidiafb" ];
+
+    services.acpid.enable = true;
+
+  };
+
+}
diff --git a/nixos/modules/hardware/video/radeon.nix b/nixos/modules/hardware/video/radeon.nix
new file mode 100644
index 00000000000..c92b7a0509d
--- /dev/null
+++ b/nixos/modules/hardware/video/radeon.nix
@@ -0,0 +1,3 @@
+{
+  hardware.enableRedistributableFirmware = true;
+}
diff --git a/nixos/modules/hardware/video/switcheroo-control.nix b/nixos/modules/hardware/video/switcheroo-control.nix
new file mode 100644
index 00000000000..199adb2ad8f
--- /dev/null
+++ b/nixos/modules/hardware/video/switcheroo-control.nix
@@ -0,0 +1,18 @@
+{ config, pkgs, lib, ... }:
+
+with lib;
+let
+  pkg = [ pkgs.switcheroo-control ];
+  cfg = config.services.switcherooControl;
+in {
+  options.services.switcherooControl = {
+    enable = mkEnableOption "switcheroo-control, a D-Bus service to check the availability of dual-GPU";
+  };
+
+  config = mkIf cfg.enable {
+    services.dbus.packages = pkg;
+    environment.systemPackages = pkg;
+    systemd.packages = pkg;
+    systemd.targets.multi-user.wants = [ "switcheroo-control.service" ];
+  };
+}
diff --git a/nixos/modules/hardware/video/uvcvideo/default.nix b/nixos/modules/hardware/video/uvcvideo/default.nix
new file mode 100644
index 00000000000..338062cf69b
--- /dev/null
+++ b/nixos/modules/hardware/video/uvcvideo/default.nix
@@ -0,0 +1,64 @@
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.services.uvcvideo;
+
+  uvcdynctrl-udev-rules = packages: pkgs.callPackage ./uvcdynctrl-udev-rules.nix {
+    drivers = packages;
+    udevDebug = false;
+  };
+
+in
+
+{
+
+  options = {
+    services.uvcvideo.dynctrl = {
+
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Whether to enable <command>uvcvideo</command> dynamic controls.
+
+          Note that enabling this brings the <command>uvcdynctrl</command> tool
+          into your environment and register all dynamic controls from
+          specified <command>packages</command> to the <command>uvcvideo</command> driver.
+        '';
+      };
+
+      packages = mkOption {
+        type = types.listOf types.path;
+        example = literalExpression "[ pkgs.tiscamera ]";
+        description = ''
+          List of packages containing <command>uvcvideo</command> dynamic controls
+          rules. All files found in
+          <filename><replaceable>pkg</replaceable>/share/uvcdynctrl/data</filename>
+          will be included.
+
+          Note that these will serve as input to the <command>libwebcam</command>
+          package which through its own <command>udev</command> rule will register
+          the dynamic controls from specified packages to the <command>uvcvideo</command>
+          driver.
+        '';
+        apply = map getBin;
+      };
+    };
+  };
+
+  config = mkIf cfg.dynctrl.enable {
+
+    services.udev.packages = [
+      (uvcdynctrl-udev-rules cfg.dynctrl.packages)
+    ];
+
+    environment.systemPackages = [
+      pkgs.libwebcam
+    ];
+
+  };
+}
diff --git a/nixos/modules/hardware/video/uvcvideo/uvcdynctrl-udev-rules.nix b/nixos/modules/hardware/video/uvcvideo/uvcdynctrl-udev-rules.nix
new file mode 100644
index 00000000000..a808429c999
--- /dev/null
+++ b/nixos/modules/hardware/video/uvcvideo/uvcdynctrl-udev-rules.nix
@@ -0,0 +1,45 @@
+{ buildEnv
+, libwebcam
+, makeWrapper
+, runCommand
+, drivers ? []
+, udevDebug ? false
+}:
+
+let
+  version = "0.0.0";
+
+  dataPath = buildEnv {
+    name = "uvcdynctrl-with-drivers-data-path";
+    paths = drivers ++ [ libwebcam ];
+    pathsToLink = [ "/share/uvcdynctrl/data" ];
+    ignoreCollisions = false;
+  };
+
+  dataDir = "${dataPath}/share/uvcdynctrl/data";
+  udevDebugVarValue = if udevDebug then "1" else "0";
+in
+
+runCommand "uvcdynctrl-udev-rules-${version}"
+{
+  inherit dataPath;
+  buildInputs = [
+    makeWrapper
+    libwebcam
+  ];
+  dontPatchELF = true;
+  dontStrip = true;
+  preferLocalBuild = true;
+}
+''
+  mkdir -p "$out/lib/udev"
+  makeWrapper "${libwebcam}/lib/udev/uvcdynctrl" "$out/lib/udev/uvcdynctrl" \
+    --set NIX_UVCDYNCTRL_DATA_DIR "${dataDir}" \
+    --set NIX_UVCDYNCTRL_UDEV_DEBUG "${udevDebugVarValue}"
+
+  mkdir -p "$out/lib/udev/rules.d"
+  cat "${libwebcam}/lib/udev/rules.d/80-uvcdynctrl.rules" | \
+    sed -r "s#RUN\+\=\"([^\"]+)\"#RUN\+\=\"$out/lib/udev/uvcdynctrl\"#g" > \
+    "$out/lib/udev/rules.d/80-uvcdynctrl.rules"
+''
+
diff --git a/nixos/modules/hardware/video/webcam/facetimehd.nix b/nixos/modules/hardware/video/webcam/facetimehd.nix
new file mode 100644
index 00000000000..d311f600c31
--- /dev/null
+++ b/nixos/modules/hardware/video/webcam/facetimehd.nix
@@ -0,0 +1,44 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+
+  cfg = config.hardware.facetimehd;
+
+  kernelPackages = config.boot.kernelPackages;
+
+in
+
+{
+
+  options.hardware.facetimehd.enable = mkEnableOption "facetimehd kernel module";
+
+  config = mkIf cfg.enable {
+
+    assertions = singleton {
+      assertion = versionAtLeast kernelPackages.kernel.version "3.19";
+      message = "facetimehd is not supported for kernels older than 3.19";
+    };
+
+    boot.kernelModules = [ "facetimehd" ];
+
+    boot.blacklistedKernelModules = [ "bdc_pci" ];
+
+    boot.extraModulePackages = [ kernelPackages.facetimehd ];
+
+    hardware.firmware = [ pkgs.facetimehd-firmware ];
+
+    # unload module during suspend/hibernate as it crashes the whole system
+    powerManagement.powerDownCommands = ''
+      ${pkgs.kmod}/bin/lsmod | ${pkgs.gnugrep}/bin/grep -q "^facetimehd" && ${pkgs.kmod}/bin/rmmod -f -v facetimehd
+    '';
+
+    # and load it back on resume
+    powerManagement.resumeCommands = ''
+      ${pkgs.kmod}/bin/modprobe -v facetimehd
+    '';
+
+  };
+
+}
diff --git a/nixos/modules/hardware/wooting.nix b/nixos/modules/hardware/wooting.nix
new file mode 100644
index 00000000000..ee550cbbf6b
--- /dev/null
+++ b/nixos/modules/hardware/wooting.nix
@@ -0,0 +1,12 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+{
+  options.hardware.wooting.enable =
+    mkEnableOption "Enable support for Wooting keyboards";
+
+  config = mkIf config.hardware.wooting.enable {
+    environment.systemPackages = [ pkgs.wootility ];
+    services.udev.packages = [ pkgs.wooting-udev-rules ];
+  };
+}
diff --git a/nixos/modules/hardware/xone.nix b/nixos/modules/hardware/xone.nix
new file mode 100644
index 00000000000..89690d8c6fb
--- /dev/null
+++ b/nixos/modules/hardware/xone.nix
@@ -0,0 +1,23 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+  cfg = config.hardware.xone;
+in
+{
+  options.hardware.xone = {
+    enable = mkEnableOption "the xone driver for Xbox One and Xbobx Series X|S accessories";
+  };
+
+  config = mkIf cfg.enable {
+    boot = {
+      blacklistedKernelModules = [ "xpad" "mt76x2u" ];
+      extraModulePackages = with config.boot.kernelPackages; [ xone ];
+    };
+    hardware.firmware = [ pkgs.xow_dongle-firmware ];
+  };
+
+  meta = {
+    maintainers = with maintainers; [ rhysmdnz ];
+  };
+}
diff --git a/nixos/modules/hardware/xpadneo.nix b/nixos/modules/hardware/xpadneo.nix
new file mode 100644
index 00000000000..dbc4ba21256
--- /dev/null
+++ b/nixos/modules/hardware/xpadneo.nix
@@ -0,0 +1,29 @@
+{ config, lib, ... }:
+
+with lib;
+let
+  cfg = config.hardware.xpadneo;
+in
+{
+  options.hardware.xpadneo = {
+    enable = mkEnableOption "the xpadneo driver for Xbox One wireless controllers";
+  };
+
+  config = mkIf cfg.enable {
+    boot = {
+      # Must disable Enhanced Retransmission Mode to support bluetooth pairing
+      # https://wiki.archlinux.org/index.php/Gamepad#Connect_Xbox_Wireless_Controller_with_Bluetooth
+      extraModprobeConfig =
+        mkIf
+          config.hardware.bluetooth.enable
+          "options bluetooth disable_ertm=1";
+
+      extraModulePackages = with config.boot.kernelPackages; [ xpadneo ];
+      kernelModules = [ "hid_xpadneo" ];
+    };
+  };
+
+  meta = {
+    maintainers = with maintainers; [ kira-bruneau ];
+  };
+}