An introduction to Linux Device Drivers – #1

Today we’ll be discussing a basic c code, the famous hello.c, which will be loaded as a kernel module and discuss some of the basic aspects related to it.

#include <linux/init.h>

#include <linux/module.h>

MODULE_LICENSE (“GPL”); // included in module.h, tells about the license the code is having.

static int __init hello_init (void)

{

printk (KERN_ALERT “Hello, world\n”);

return 0;

}

static void __exit hello_exit (void)

{

printk (KERN_ALERT “Goodbye\n”);

}

module_init (hello_init);     // hello_init is the initialization function

module_exit (hello_exit);     //hello_exit is the exit function

The header <linux/init.h> contain various declarations and definitions related to the loading and cleaning up of modules. The macros module_init and module_exit are declared in the <linux/init.h>. The argument functions passed to module_init( ) and module_exit( ) are executed at the loading and unloading times respectively of a module. The initialization function, basically, sets up the device to be used later. The exit function cleans up the device ( opposite to initialization).

Now coming to the __init and __exit terms used in the code. The __init in static int __init hello_init (void) specifies that the hello_init function is executed only at module load time. The module loader drops the initialization function for other uses once the module is loaded. The initialization function better be static since they are not meant to be visible outside the specific file ( not mandatory though). Similar explanation goes for __exit. This makes the exit function executed only at exit time.

The printk function, at a first glance, might look identical to printf of <stdio.h>, but printk lets you set priority levels for the message, like KERN_ALERT, KERN_INFO, etc. We’ll discuss about them later.

How to compile the code and execute it. Let us assume that you named the source file above as hello.c. Use any suitable text editor to write a Makefile as below :

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
rm -f *~

I hope you have the kernel source in your system. If not you can get the kernel source from kernel.org. If you are using Fedora, you can do:

# yum install kernel-devel

To compile, in the current directory ( where there is hello.c and the Makefile) do :

$ make

Then change to super user by : $su <enter>

and do : # insmod ./hello.ko

If you do : # tail -f /var/log/messages (You can open this in a separate terminal to view the system logs)

then you will be able to see a line saying “Hello, world”. This is what your hello_init( ) was supposed to do at module load time. You can also see the module name “hello” among the list of loaded modules doing lsmod. Now you can unload the module do :

# rmmod hello

Now in /var/log/messages, you will see “Goodbye”. This is due to hello_exit( ).

continued …

Resolved Kernel boot problem :D

I am using Fedora 10 and I was running the kernel 2.6.27.15-170.2.24.fc10.i686 and I was happy until I did a ‘yum update’. My kernel got updated to kernel 2.6.27.19-170.2.35.fc10.i686, and then the system won’t boot the latest kernel. While booting it gave a mesage :

Unable to access resume device (/dev/dm-1)

mount : could not find filesystem ‘/dev/root’

I wondered what had happened? But, the old kernel 2.6.27.15-170.2.24.fc10.i686 seemed to work perfectly. I became fanatic to resolve this issue. I did some googling, and finally came to know that the issue was with /etc/fstab and my Fedora 10 being installed on LVM. Here is my previous fstab details :

# /etc/fstab: static file system information.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>

tmpfs    /dev/shm    tmpfs    defaults    0    0
devpts    /dev/pts    devpts    gid=5,mode=620    0    0
sysfs    /sys    sysfs    defaults    0    0
proc    /proc    proc    defaults    0    0
/dev/dm-0    /    ext3    defaults    1    1
#Entry for /dev/sda7 :
UUID=3bb42530-5757-4f5a-9c10-16580ee6994a    /boot    ext3    defaults    1    2
#Entry for /dev/sda5 :
UUID=08563C60563C50A4    /media/Personal_Data    ntfs-3g    defaults,locale=en_US.UTF-8    0    0
#Entry for /dev/sda2 :
UUID=FCB60995B6095196    /media/c:    ntfs-3g    defaults,locale=en_US.UTF-8    0    0
/dev/dm-1    swap    swap    defaults    0    0

But there were no /dev/dm-0 and /dev/dm-1 in /dev/ folder. The solution was to replace dm-0 by VolGroup00/LogVol00 and dm-1 by VolGroup00/LogVol01. Actually in LVM, the root is denoted by /dev/VolGroup00/LogVol00 and swap by /dev/VolGroup00/LogVol01. I made the necessary changes. Now, /etc/fstab looks like this:

NOTE : For the following steps, root access will be required, do ‘su -’ first.

# /etc/fstab: static file system information.
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>

tmpfs    /dev/shm    tmpfs    defaults    0    0
devpts    /dev/pts    devpts    gid=5,mode=620    0    0
sysfs    /sys    sysfs    defaults    0    0
proc    /proc    proc    defaults    0    0
/dev/VolGroup00/LogVol00    /    ext3    defaults    1    1
#Entry for /dev/sda7 :
UUID=3bb42530-5757-4f5a-9c10-16580ee6994a    /boot    ext3    defaults    1    2
#Entry for /dev/sda5 :
UUID=08563C60563C50A4    /media/Personal_Data    ntfs-3g    defaults,locale=en_US.UTF-8    0    0
#Entry for /dev/sda2 :
UUID=FCB60995B6095196    /media/c:    ntfs-3g    defaults,locale=en_US.UTF-8    0    0
/dev/VolGroup00/LogVol01    swap    swap    defaults    0    0

Now I just needed to create initial ramdisk images for preloading modules. So I did :

mkinitrd -f -v /boot/initrd-2.6.27.19-170.2.35.fc10.i686.img 2.6.27.19-170.2.35.fc10.i686

and also for the previous kernel

mkinitrd -f -v /boot/initrd-2.6.27.15-170.2.24.fc10.i686.img 2.6.27.15-170.2.24.fc10.i686

Then I did a reboot, and booting with the kernels (2.6.27.19-170.2.35 and 2.6.27.15-170.2.24) were as smooth as before.

That was all.

Thank you

Regards

rtnpro