How to write a linux device driver

A beautiful guide for the Hello World of the device driver programming

Introduction

I am writing this guide to lend a hand to everyone who has some curiosity about the device driver programming or like to play a bit with the kernel and write some sample kernel modules.

Scared poor penguin because of a kernel panic

Prerequisites

Before start coding in your favorite editor you will need:

Kernel headers

The kernel headers for your current kernel: normally you won’t have it on your system unless you have compiled your own kernel or you have an insane liking for crashing your system and repairing it. To install them issue as root or with sudo and according to your linux distro:

# Arch, my fav :)
$ pacman -S linux-headers
# Debian & Ubuntu
$ apt-get install linux-headers-$(uname -r)
/usr/lib/modules/$(uname -r)/build/include/linux 

Editor

I like Atom with some C plugins for small projects and testing, it is fast to install and the plugins are easily available and installable.

cp -r /usr/lib/modules/$(uname -r)/build/include/linux .

The program

This is the hello world of the device driver programming. Note that the code is not mine, you can find it on internet in several places. So in your development directory create a file called myDriver.c with this content:

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int hello_init(void){
printk(KERN_ALERT "Hello, world\n");
return 0;
}
static void hello_exit(void){
printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
  1. MODULE_LICENSE("GPL") it basically tells the kernel that this driver is under GPL license. If the module is licensed under a proprietary license the kernel will complain saying it is tainted. Read more here.
  2. hello_init(void) prints “Hello, world” in the kernel messages log with a log level of KERN_ALERT (this level is set this severe to make it visible in the logs, lesser server level may pass unnoticed)
  3. hello_exit(void) prints “Goodbye, cruel world” when called in the kernel log with same log level.
  4. module_init() tells the kernel which function to call when the module is loaded. Yeap, you guessed it right, it will be hello_init() ;)
  5. module_exit() defines which function to call when the module is removed. It is usually used for clean up all the things (memory reserved and stuff) you did in module_init()

Compiling

Unfortunately, compiling kernel modules isn’t as easy as gcc -W -Wall myDriver.c -o myDriver . To compile kernel modules, the proper approach is using kbuild. However this may lead into very complicated makefiles, so for the purposes of this guide we will use a very simplified makefile. Create a file called makefile and write on it:

obj-m += myDriver.oall:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
$ ls
linux Makefile myDriver.c
$ make 
make -C /lib/modules/4.12.8-2-ARCH/build M=/path/to/your/working/directory modules
make[1]: se entra en el directorio '/usr/lib/modules/4.12.8-2-ARCH/build'
CC [M] /path/to/your/working/directory/myDriver.o
Building modules, stage 2.
MODPOST 1 modules
CC /path/to/your/working/directory/myDriver.mod.o
LD [M] /path/to/your/working/directory/myDriver.ko
make[1]: se sale del directorio '/usr/lib/modules/4.12.8-2-ARCH/build'

Loading and removing the module

For loading and removing the modules there are different tools: insmod, rmmod and modprobe. We are going to use the first two because are simpler. modprobe is more intelligent and includes many features such as loading an inserted module at next boot and many others, and it is intended more for module management rather than module development. In here we are going to use insmod for loading and rmmod for removing modules.

$ sudo insmod myDriver.ko
$ dmesg | tail
[11715.453239] . . .
[11715.944197] . . .
[11716.972902] . . .
[11716.996946] . . .
[11716.999696] . . .
[11717.001283] . . .
[11717.048479] . . .
[11717.049223] . . .
[11717.049305] . . .
[12149.377145] Hello, world
$ lsmod
Module Size Used by
myDriver 16384 0
hid_generic 16384 0
usbhid 45056 0
...
$ cat /proc/modules
myDriver 16384 0 - Live 0xffffffffc0c8b000 (O)
hid_generic 16384 0 - Live 0xffffffffc0c86000
usbhid 45056 0 - Live 0xffffffffc0c92000
...
$ sudo rmmod myDriver
$ dmesg | tail
[11715.944197] . . .
[11716.972902] . . .
[11716.996946] . . .
[11716.999696] . . .
[11717.001283] . . .
[11717.048479] . . .
[11717.049223] . . .
[11717.049305] . . .
[12149.377145] Hello, world
[13368.510294] Goodbye, cruel world

Last words

I hope this little superficial tutorial has shed some light into how to get your first linux module loaded and removed.

Senior Research Engineer in Deep Learning. MSc in Data Processing and AI. A philosophic seeker interested in the meaning of everything from faith to AI