Bending bits...

Bytes and Words...

Simple Linux char driver

This is the second blog post from a series dedicated to Linux drivers that started with Linux drivers and cross compilation. I aim to publish some skeletons of drivers for different interfaces and modules. I’m not a Linux kernel developer so I can’t say that I will strictly follow the best practices in kernel development, but I have years of experience in bare-metal embedded driver development. Thus, I will follow the best practices in C programming and make the best use of my knowledge in electronics and embedded. Those being said, this series will not be a tutorial in Linux driver development.

In this blog post, I will start with the implementation of 2 simple char drivers that do nothing, except of logging parameters in kernel log. To build the drivers, I use a busybox and a minimalistic Linux kernel built according to the steps form the aforementioned blog post. The kernel, busybox and the drivers are built using the ARM aarch64-linux-gnu compiler. I will use the following Makefile

ifneq ($(KERNELRELEASE),)
obj-m += simple.o
else
KDIR := /home/mihai/qemu/linux-6.12.54
PWD := $(shell pwd)
CROSS=/bin/aarch64-linux-gnu-

default:
        @echo '   Building driver for kernel 6.12'
        $(MAKE) -C $(KDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=$(CROSS) modules

clean:
        make -C $(KDIR) M=$(PWD) clean

endif

to build the drivers. Between blog posts, the only changes will be in the variable obj-m which contain the name of object. The KDIR should point to the location of your built Linux kernel (feel free to change the path accordingly), while CROSS points to the location of the compiler. On my Debian machine, the arm compiler is in /bin. If on your machine is in another location, just change path.

Simple hello world driver

#include <linux/init.h>
#include <linux/module.h>


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sebastian Ardelean");
MODULE_DESCRIPTION("A simple hello world Linux char driver");
MODULE_VERSION("0.1");


static int __init simple_init(void)
{
    printk(KERN_INFO "Simple: Hello World!\n");
    return 0;
}

static void __exit simple_exit(void)
{
    printk(KERN_INFO "Simple: Goodbye world! :(\n");
}

module_init(simple_init);
module_exit(simple_exit);

After built, to test the driver run the command sudo insmod simple.ko to insert the driver. In kernel logs (run dmesg) one should see the line [ 2696.101472] Simple: Hello World!. To remove the driver run the command sudo rmmod simple and in kernel logs one should see the message [ 2772.318862] Simple: Goodbye world! :(.

Simple hello world driver with parameters

This driver is similar to the previous one, the only difference is that it uses module parameters.

#include <linux/init.h>
#include <linux/module.h>


MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sebastian Ardelean");
MODULE_DESCRIPTION("A simple hello world Linux char driver");
MODULE_VERSION("0.1");

static char *message = "world";
module_param(message, charp, S_IRUSR | S_IWUSR);
MODULE_PARM_DESC(message, "message parameter");


static int __init simple_init(void)
{
    printk(KERN_INFO "Simple: Hello %s!\n", message);
    return 0;
}

static void __exit simple_exit(void)
{
    printk(KERN_INFO "Simple: Goodbye %s! :(\n", message);
}

module_init(simple_init);
module_exit(simple_exit);

If you insert the driver with the command sudo insmod simple.ko message="SOMETHING" followed by the command to remove it, in kernel logs one should see the lines

[ 3190.552534] Simple: Hello SOMETHING!
[ 3201.806278] Simple: Goodbye SOMETHING! :(