Custom kernel module on the RPi

When writing kernel modules, I prefer to have as much help from the editor as possible. This translates to: auto-complete for everything and effortless switching to the dependency code.

Assumptions

I’m assuming that you have Qt Creator installed. The simplest is to run the Qt installer for the Open source version.

Also that you know how to ssh to your RPi and copy files to it from your PC.

In the steps I’m using dev/sdX on purpose so it fails in case you just copy paste it. You can check your’s by running dmesg after plugging in the SD card.

Note If you’re running the environment in Virtualbox you might have to run Qt Creator with

./qtcreator -noload Welcome -noload QmlDesigner -noload QmlProfiler

to avoid some graphical glitches.

Steps

1. Prepare the initial SD card image

Insert the SD from the Raspberry in your PC.

Note If you’re using Virtualbox, apply the USB filters so it’s detected in the virtual machine.

~$ wget https://downloads.raspberrypi.org/raspbian_lite_latest
~$ mv raspbian_lite_latest raspbian_lite_latest.zip
~$ unzip raspbian_lite_latest.zip
~$ umount /dev/sdX*
~$ sudo dd if=2017-01-11-raspbian-jessie-lite.img of=/dev/sdX bs=2MB status=progress

The reason for the initial Raspbian install for it to take care of setting the partitioning and the rootfs and simplifies the initial setup for experimenting with module development.

2. Building the kernel and default modules

~$ sudo apt-get install build-essential gcc-arm-linux-gnueabihf
~$ git clone https://github.com/raspberrypi/linux.git linux-rpi
~$ cd linux-rpi
~/linux-rpi$ git checkout rpi-4.9.y
~/linux-rpi$ export ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
~/linux-rpi$ make bcm2709_defconfig
~/linux-rpi$ make KBUILD_VERBOSE=1 -j4 zImage modules dtbs | tee build.log

You might have noticed we’re not cloning Linux from the mainline repository. Using the official RPi Linux instead the vanilla spares us the platform specific issues. The config file we’re selecting (bcm2709_defconfig) doesn’t exist in the vanilla kernel.

The environmental variables (ARCH, CROSS_COMPILE) define that cross building for arm will take place once we run make.

We make a build.log which will be the basis for knowing what files to include in the Qt Creator project.

3. Installing the kernel and modules

~/linux-rpi$ mkdir ~/rpi-boot ~/rpi-rootfs
~/linux-rpi$ sudo mount /dev/sdb1 ~/rpi-boot
~/linux-rpi$ sudo mount /dev/sdb2 ~/rpi-rootfs
~/linux-rpi$ sudo cp arch/arm/boot/dts/*.dtb ~/rpi-boot
~/linux-rpi$ sudo cp arch/arm/boot/dts/overlays/*.dtb* ~/rpi-boot/overlays
~/linux-rpi$ sudo cp arch/arm/boot/zImage ~/rpi-boot/kernel7.img
~/linux-rpi$ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=~/rpi-rootfs  modules_install

Here we’re mounting the partitions from the SD card and copying the newly compiled kernel and modules.

4. Setting up the development environment on the PC

~/linux-rpi$ git clone https://github.com/TheMeaningfulEngineer/linux-in-qtcreator.git
~/linux-rpi$ cp linux-in-qtcreator/prepare_kernel_project.py .
~/linux-rpi$ chmod 755 prepare_kernel_project.py
~/linux-rpi$ ./prepare_kernel_project.py build.log linux-rpi
~/linux-rpi$ mkdir out-of-tree-modules
~/linux-rpi$ echo "out-of-tree-modules/our-module.c" >> linux-rpi.files

Two files need to be created in ~/linux/out-of-tree-modules

  • Makefile

    obj-m+=our-module.o
    all:
    	make -C ../ M=$(PWD) modules
    clean:
    	make -C . M=$(PWD) clean
    
  • our-module.c

    #include <linux/module.h>    // included for all kernel modules
    #include <linux/kernel.h>    // included for KERN_INFO
    #include <linux/init.h>      // included for __init and __exit macros
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Lakshmanan");
    MODULE_DESCRIPTION("A Simple Hello World module");
    
    static int __init hello_init(void)
    {
        printk(KERN_INFO "It's me, Mr. Kernel Module and I'm just being initialised\n");
        return 0;    // Non-zero return means that the module couldn't be loaded.
    }
    
    static void __exit hello_cleanup(void)
    {
        printk(KERN_INFO "Cleaning up module.\n");
    }
    
    module_init(hello_init);
    module_exit(hello_cleanup);
    

We used the script to generate a Qt Creator project structure, consisting only of the files used for the build. The out-of-tree-modules/our-module.c is manually added.

After that we created a sample kernel module source and a Makefile that will build the module using the Kbuild system residing on the kernel.

It is now possible to open both the kernel sources and the module in QtCreator.

Open Qt Creator -> Ctrl + o -> Select linux-rpi.creator

Give it some time until it indexes all the files. You can now use Ctrl + space for autocomplete or f2 to go to definitions of macros or functions.

5. Building and running

Be sure to replace YourRpiIP.

~/linux-rpi/out-of-tree-modules$ export ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
~/linux-rpi/out-of-tree-modules$ make
~/linux-rpi/out-of-tree-modules$ scp our-module.ko pi@YourIP:

pi@raspberrypi:~ $ sudo insmod our-module.ko
pi@raspberrypi:~ $ dmesg | grep "Mr. Kernel Module"

The compilation of the module is done by just invoking make. Because of the correctly set paths done in previous steps, it will know how to invoke the correct Makefiles from the kernel source directory and invoke the appropriate build steps.

Once it’s compiled, the module should be migrated to the RPi and run there. Given that it doesn’t really do anything, we just check if it printed the init message.

Below you can see how the result looks in my environment:

Written on January 15, 2017