Auto build driver when updating Linux kernel with dkms

May 4, 2023

I am a media consumption enthusiast. Let's just say I watch movies a lot. Usually, people like me will have a big home theater setup with a large TV, a large speaker set, a media player with huge HDD storage. But that sounds like too much work, since you have to update the hard drive constantly with new catalogues. So I built my server out of an old i3 laptop, a SSD, 2 HDD lying around with a dock and an Ethernet USB dongle. I chose 2.5 Gbps dongle version because I wanted to future-proof my server. But that proved to be my undoing, because the OS I chose for my server, Debian, had an older version of the driver that didn't support the RTL8156 chipset. It might support an older one, the RTL8153, which is the 1 Gbps variant. So I searched for its driver. It's quite a little adventure.

First time compiling from source

A Google search for RTL8156 driver for linux led me to the manufacturer's driver page to download the driver. However, the compressed file I downloaded (in this instance .tar.bz2) didn't have any .deb file to install. Instead I was left with a bunch of .h, .c files with a Makefile. I thought: "Wait. This is a source code!".

So far, I have mostly installed apps and drivers through apt so this was a new territory. I did some researches and tried compiling the driver. In the source code folder, I typed:

make -j4

With -j4 being number of jobs to run simultaneously, 4 is the number of threads of my CPU. After it finished, the result was r8152.ko file, a kernel module. I plugged in the Ethernet USB. It didn't work. Then I tried loading the module into the kernel like this article suggested:

insmod r8152.ko

Then it worked. My server recognized it.

ip a

3: enx00e04e3b0ac0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000

I edited the interfaces file in /etc/network to assign it a static address by adding:

allow-hotplug enx00e04e3b0ac0

iface enx00e04e3b0ac0 inet static

address 192.168.4.3/24

gateway 192.168.4.1

For then on, I could access my server in my house through that IP, 192.168.4.3.

But I discovered that:

- At start-up, I have to insmod like that to load the module. This was solved by simply copying that module to /lib/modules/$(uname -r)/kernel/drivers/net/usb. Didn't have to insmod anymore. But that wasn't all.

- Every time I update the server with apt update, apt upgrade, and there is a kernel update, I have to compile module from source, copy it over again or else I lose the driver. So I often delay updating my server, which is bad because you want to keep your kernel up-to-date on bugfix, vulnerabilities.

I needed a way to make it compile every time there is a kernel update. So is there a way?

dkms - Dynamic Kernel Module Support

Nowadays, I use an Ubuntu desktop as my main driver. I often see something along the line of dkms for my Nvidia card anytime there is a kernel update. After rebooting, the video card still works with the new kernel. Naturally, I started looking into this "dkms".

According to this article, dkms "is a framework where device driver source can reside outside the kernel source tree so that it is very easy to rebuild modules as you upgrade kernels". So this is the stuff I was looking for. The author also encountered dkms similarly to me, but by following guides to install his Wifi driver.

So with the source code of my Ethernet USB, I copied it to /usr/src. I also created a file inside the source code's folder, dkms.conf with the following content:

PACKAGE_NAME="r8152"

PACKAGE_VERSION="2.15.0"

BUILT_MODULE_NAME[0]="r8152"

MAKE="'make' -j$(nproc) KVER=${kernelver}"

CLEAN="'make' clean"

DEST_MODULE_LOCATION[0]="/kernel/drivers/net/usb/"

AUTOINSTALL="YES"

PACKAGE_NAME gives the name to the entire package of modules.

PACKAGE_VERSION give the version of the entire package of modules being installed with dkms. These two form the name of the folder that contains the source code, *r8152-2.15.0.

BUILT_MODULE_NAME gives the name of the module just after it is built.

DEST_MODULE_LOCATION specifies the destination where a module should be installed to, once compiled. From the example: /lib/modules/$(uname -r)/kernel/drivers/net/usb.

Note that for each module within a dkms package, the numeric value of # must be the same for each of BUILT_MODULE_NAME and DEST_MODULE_LOCATION and that the numbering should start at 0. So each module goes with its own destination

MAKE and CLEAN correspond to commands used to run when dkms builds (make) the module and cleans up after.

AUTOINSTALL is set to yes so that the service /etc/rc.d/init.d/dkms_autoinstaller will automatically try to install this module when upgrading kernel.

After that, I ran three commands:

- sudo dkms add r8152/2.15.0

- sudo dkms build r8152/2.15.0

- sudo dkms install r8152/2.15.0

And it installed the driver to the kernel. From then on, it will build and reinstall anytime I upgrade the kernel.

Result:

sudo dkms add r8152/2.15.0

Creating symlink /var/lib/dkms/r8152/2.15.0/source ->

/usr/src/r8152-2.15.0

DKMS: add completed.

sudo dkms build r8152/2.15.0

Kernel preparation unnecessary for this kernel. Skipping...

Building module:

cleaning build area...

'make' -j4 KVER=5.10.0-22-amd64......

cleaning build area...

DKMS: build completed.

sudo dkms install r8152/2.15.0

r8152.ko:

Running module version sanity check.

Good news! Module version v2.15.0 for r8152.ko

exactly matches what is already found in kernel 5.10.0-22-amd64.

DKMS will not replace this module.

You may override by specifying --force.

depmod....

DKMS: install completed.

dkms status

broadcom-sta, 6.30.223.271, 5.10.0-20-amd64, x86_64: installed

broadcom-sta, 6.30.223.271, 5.10.0-22-amd64, x86_64: installed

[r8152, 2.15.0, 5.10.0-22-amd64, x86_64: installed]

If you wish to remove the driver, type in "dkms remove r8152/2.15.0 --all".

References:

- man dkms

- Realtek USB FE / GBE / 2.5G / Gaming Ethernet Family Controller Software

- rtl8821CU