How to add a local package to boot.extraModulePackages?

I’m attempting to build a kernel module but I’m not sure how to add to boot.extraModulePackages. I keep getting undefined variable whatever I put in there. I’ve tried the various options at Linux kernel - NixOS Wiki but I’m not sure which I’m supposed to be using. I guess I ultimately want to add it to nixpkgs but I’d like to get it to work locally first.

pkgs/by-name/fa/fanatecff/package.nix:

{ lib
, stdenv
, fetchFromGitHub
, kernel
, kmod
}:

stdenv.mkDerivation rec {
  pname = "hid-fanatecff";
  name = "hid-fanatecff-${kernel.version}";

  src = fetchFromGitHub {
    owner = "gotzl";
    repo = "hid-fanatecff";
    rev = "next";
    sha256 = "kdSlMyow4hKAY6siw4pqhBttIkjz1Tcq0MQh5HKxI2Q=";
  };

  hardeningDisable = [ "pic" "format" ];
  nativeBuildInputs = kernel.moduleBuildDependencies;

  patchPhase = ''
    mkdir -p $out/lib/udev/rules.d
    substituteInPlace Makefile --replace "/etc/udev/rules.d" "$out/lib/udev/rules.d"
    sed -i '/depmod/d' Makefile
  '';

  makeFlags = [
    "KVERSION=${kernel.modDirVersion}"
    "KDIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"
    "MODULEDIR=$(out)"
  ];

  meta = with lib; {
    description = "Linux kernel driver that aims to add force feedback support for FANATEC devices";
    homepage = "https://github.com/gotzl/hid-fanatecff";
    license = licenses.gpl2Only;
    maintainers = [ maintainers.philt ];
    platforms = platforms.linux;
  };
}

I tried to add it to a local fork of nixpkgs with:

  nixpkgs.config = {
    allowUnfree = true;
    packageOverrides = pkgs: {
      wip = import (fetchGit { url = "/data/code/nixpkgs"; shallow = true; }) {
        config = config.nixpkgs.config;
      };
    };
  };

  boot = {
    kernelPackages = pkgs.wip.linuxPackages;
    extraModulePackages = with config.boot.kernelPackages; [ fanatecff ];
  };

in a local configuration.nix but I still get undefined variable. I’m not entirely sure whether that kernelPackage should be the name of the ko file or the name of the nixpkg module I created.

How can I get this package loaded from my configuration.nix?

I am not entirely sure about how you have set this up, I am however fairly sure that you need to call callPackage at some point if you are working from a local checkout of the entire nixpkgs. Otherwise you might have to do that inside your overlay, in case you are defining one to get the new derivation working.

Is it probably also a conventional thing to call your main file default.nix

Edit: Assuming you are working from a local checkout, there is a file pkgs/top-level/all-packages.nix which I would normally look at first. However I am not super sure about kernel modules.

Thanks for your reply.

After failing to get it working in my configuration.nix I was trying to follow the last bit in the section Loading out-of-tree kernel modules. The examples in nixpkgs README all refer to package.nix though I myself have been using default.nix when creating derivations hooked up to my configuration.nix file.

I’d prefer to avoid nixpkgs until I have a working build but I know pretty much nothing about setting up kernel modules and especially how NixOS treats them. Most docs talk about patching pre-existing ones but nothing about how to set one up from scratch.

Perhaps I can find one that’s setup in nixpkgs and go from there.

I’ll start by going back through Packaging_out-of-tree_kernel_modules which at least got me building it. I just stuck on enabling it in configuration.nix.

You can then call your program using something like let yourprogram = config.boot.kernelPackages.callPackage ./your-derivation.nix {}; in … (or if you want to compile it for the default kernel used by nix you can use pkgs.linuxPackages.callPackage but be aware that if you install it on a system running another kernel it will not work) and install the module in the kernel using boot.extraModulePackages = [ yourprogram ];.

I re-read this paragraph and realized my mistake. I think it’s working now. I’ll report the fix once I’ve confirmed it’s working.

1 Like

So it builds correctly but I think it’s not installing the module correctly. Here’s what I have so far:

# src/hid-fanatecff/default.nix
{ lib
, stdenv
, fetchFromGitHub
, kernel
, kmod
, linuxConsoleTools
}:

stdenv.mkDerivation rec {
  pname = "hid-fanatecff";
  name = "hid-fanatecff-${kernel.version}";

  src = fetchFromGitHub {
    owner = "gotzl";
    repo = "hid-fanatecff";
    rev = "next";
    sha256 = "kdSlMyow4hKAY6siw4pqhBttIkjz1Tcq0MQh5HKxI2Q=";
  };

  hardeningDisable = [ "pic" "format" ];
  nativeBuildInputs = kernel.moduleBuildDependencies;

  patchPhase = ''
    mkdir -p $out/lib/udev/rules.d
    substituteInPlace Makefile --replace "/etc/udev/rules.d" "$out/lib/udev/rules.d"
    substituteInPlace fanatec.rules --replace "/usr/bin/evdev-joystick" "${linuxConsoleTools}/bin/evdev-joystick" --replace "GROUP:=\"plugdev\"" "TAG+=\"uaccess\""
    sed -i '/depmod/d' Makefile
  '';

  makeFlags = [
    "KVERSION=${kernel.modDirVersion}"
    "KDIR=${kernel.dev}/lib/modules/${kernel.modDirVersion}/build"
    "MODULEDIR=$(out)"
  ];
}
# configuration.nix
{ config, pkgs, ... }:

let fanatecff = pkgs.linuxPackages.callPackage ../hid-fanatecff/default.nix {};
in
{

  boot.extraModulePackages = [ fanatecff ];

  services.udev.packages = [ fanatecff ];
}

If I then do insmod hid-fanatec.ko I get insmod: ERROR: could not load module hid-fanatec.ko: No such file or directory. So I think I’m missing a step.

If I build locally in a shell (again following Developing_out-of-tree_kernel_modules then I can run insmod ./hid-fanatec.ko and then everything works.

I think the issue is around "MODULEDIR=$(out)" and boot.extraModulePackages = [ fanatecff ];. It doesn’t seem to be copying the module to the kernel dir I think.

Ah I think I just need to add it to kernel modules:

boot.kernelModules = [ "yourmodulename" ];

That didn’t work.

If I search for the kernel module it’s definitely built it but how does it end up in /lib/modules?

/nix/store/ggjqjv6fw8wiaw5xv8i0n9kg1yzq59r4-hid-fanatecff-0.0.2-6.1.65/hid-fanatec.ko
/nix/store/209p2y05zmqg1d3cq59j10558i96fhhv-kernel-modules/hid-fanatec.ko
/nix/store/agl0kv3kvaki75xmcha7vx8vq3izfmk3-hid-fanatecff-6.1.65/hid-fanatec.ko
/nix/store/169lcpg3130vrw2gmq208d9gklczp632-kernel-modules/hid-fanatec.ko

This from the wiki doesn’t fill me with confidence:

Loading out-of-tree kernel modules

As far as I understand, if you developed a kernel module, you should end up with having some .ko files inside a subfolder inside $out/lib/modules/${kernel.modDirVersion}. Now, if you want to make your module loadable inside the kernel by modprobe, you should do:
boot.extraModulePackages = [ yourmodulename ];

How does it get here?

$out/lib/modules/${kernel.modDirVersion}

with boot.extraModulePackages or boot.kernelModules?

Some help would be greatly appreciated. I feel like I’m close!

EDIT: I should point out that sudo modprobe /nix/store/169lcpg3130vrw2gmq208d9gklczp632-kernel-modules/hid-fanatec.ko works and the hid is detected by the correct driver. So I’m just missing the last piece of the puzzle to make this happen without needing modprobe (which is what I thought boot.kernelModules = [ "hid-fanatec" ]; would do.

I love how you are documenting essentially your development / debugging! I will need to reserve some time later to read through this :smiley:

I got it working!

Just needed the correct moduledir. I knew I was close!

"MODULEDIR=$(out)/lib/modules/${kernel.version}/kernel/drivers/hid"
1 Like

Up next. Getting it into nixpkgs.

1 Like