Linux Device Driver Tutorial For Beginners
Linux operating system contains 3 main sections: Root File System, Kernel and Bootloader.
Root File System:
This part of the OS contains application binaries, libraries, scripts, config files and kernel loadable module files etc..
Kernel:
This part is the heart of OS, the Kernel is responsible for handling all the operations needed to run the OS such as memory management, process management, and input/output hardware operations etc..
Bootloader:
This is the first part to be executed by the CPU on boot. Bootloader contains the source code to initialize the system and start execute the kernel and contains commands for debugging and modifying the kernel environment, it also contains the commands to download and update the kernel and system images into the flash memory.
Drivers act as a bridge between hardware and a user application, the Kernel provides a mechanism called system calls to talk to the kernel. In Linux, drivers can be implemented in two ways, one is drivers can be compiled as part of the kernel and another one is drivers can be compiled as modules and loaded at runtime.
Let us start with a simple hello world kernel module. Here is the source code for a simple hello world kernel module.
hello.c
#include<linux/module.h> //needed for module_init and module_exit #include<linux/kernel.h> //needed for KERN_INFO #include<linux/init.h> //needed for the macros int __init hw_init(void) { printk(KERN_INFO"Hello Worldn"); return 0; } void __exit hw_exit(void) { printk(KERN_INFO"Bye Worldn"); } MODULE_LICENSE("GPL"); module_init(hw_init); module_exit(hw_exit);
Makefile
obj-m := hello.o all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Create a folder named hello and then place the hello.c and Makefile inside it. Open the terminal application and change directory to hello. Now run the command make and if it is successful then it should generate loadable kernel module file called hello.ko.
When you run make if you get output make: Nothing to be done for `all’. Then please make sure that in the Makefile you have entered tab(no spaces) before make -C. If make is successful you should get the output as shown below.
make[1]: Entering directory `/usr/src/linux-headers-3.13.0-128-generic' CC [M] /home/John/Desktop/hello/hello.o Building modules, stage 2. MODPOST 1 modules CC /home/John/Desktop/hello/hello.mod.o LD [M] /home/John/Desktop/mvs/pers/kern/hello/hello.ko make[1]: Leaving directory `/usr/src/linux-headers-3.13.0-128-generic'
Now let us test the module by loading it into the kernel. For loading and unloading kernel modules we need to have superuser permission. Use the following command to load the kernel module into the kernel.
sudo insmod hello.ko
To see the printk message you need to check the kernel log, to check the kernel log use the following command.
dmesg
This command will output the kernel log messages, at the end you should see that our message Hello World printed.
To unload the module use the following command.
sudo rmmod hello
To see the printk message use dmesg command again and in the kernel log you can see our message Bye World.
Now let us understand the source code.
hello.c
To start writing the kernel driver you can use any editor or ide of your choice but most commonly kernel developers prefer using vi editor.
Every kernel module should include the header file linux/module.h this has the declarations and macros for kernel functions such as module_init and module_exit etc. The two most necessary functions for a kernel driver are module_init and module_exit functions. The function whose pointer is passed to module_init will be executed when we load the module into the kernel, and the function whose pointer is passed to module_exit will be called when we unload or remove the module from the kernel.
Inside the kernel for debugging and printing the log, we use printk function which is similar to printf function which we use in the application. You can use the macros such as KERN_INFO, KERN_ERR etc.. to specify a log level.
If we are writing a driver to talk to a specific hardware then the init function should have the code to initialize the hardware before we start using it and exit function should have a code to clean up resources(Dynamic Memory etc) we used in the driver before we exit from the kernel.
Here in this example, we are just printing debug messages in init and exit functions.
Makefile
For building the kernel module we need to write a Makefile which will guide make utility how to compile the module. The syntax obj-m is used to tell the kernel makefile that the driver needs to be compiled as module using the specified object file. When you just run command make then the control comes to the all: section of the Makefile and if you run command make clean then the control goes to the clean: section of Makefile. From this Makefile we are actually running make inside the kernel source directory using option -C. Please make sure you have kernel source directory installed in your system. Here in this example we used command uname -r to find the current version of your system’s linux kernel.
We have used option M=$(PWD) to indicate in the kernel makefile that the source for driver is in present working directory and we are specifying the word modules to tell kernel makefile to just build modules and not to build the complete kernel source code. In clean: section of Makefile we are telling kernel makefile to clean the object files generated to build this module.
This should you get started compiling and running your first kernel module.