Device Drivers, Part 2: Writing Your First Linux Driver in the Classroom

Sketching a driver

This article, which is part of the series on Linux device drivers, deals with the concept of dynamically loading drivers, first writing a Linux driver, before building and then loading it.

Shweta and Pugs reached their classroom late, to find their professor already in the middle of a lecture. Shweta sheepishly asked for his permission to enter. An annoyed Professor Gopi responded, “Come on! You guys are late again; what is your excuse, today?”

Pugs hurriedly replied that they had been discussing the very topic for that day’s class — device drivers in Linux. Pugs was more than happy when the professor said, “Good! Then explain about dynamic loading in Linux. If you get it right, the two of you are excused!” Pugs knew that one way to make his professor happy was to criticise Windows.

He explained, “As we know, a typical driver installation on Windows needs a reboot for it to get activated. That is really not acceptable; suppose we need to do it on a server? That’s where Linux wins. In Linux, we can load or unload a driver on the fly, and it is active for use instantly after loading. Also, it is instantly disabled when unloaded. This is called dynamic loading and unloading of drivers in Linux.”

This impressed the professor. “Okay! Take your seats, but make sure you are not late again.” The professor continued to the class, “Now you already know what is meant by dynamic loading and unloading of drivers, so I’ll show you how to do it, before we move on to write our first Linux driver.”

Dynamically loading drivers

These dynamically loadable drivers are more commonly called modules and built into individual files with a .ko (kernel object) extension. Every Linux system has a standard place under the root of the file system (/) for all the pre-built modules. They are organised similar to the kernel source tree structure, under /lib/modules/<kernel_version>/kernel, where <kernel_version> would be the output of the command uname -ron the system, as shown in Figure 1.

Linux pre-built modules

Figure 1: Linux pre-built modules

To dynamically load or unload a driver, use these commands, which reside in the /sbin directory, and must be executed with root privileges:

  • lsmod — lists currently loaded modules
  • insmod <module_file> — inserts/loads the specified module file
  • modprobe <module> — inserts/loads the module, along with any dependencies
  • rmmod <module> — removes/unloads the module

Let’s look at the FAT filesystem-related drivers as an example. Figure 2 demonstrates this complete process of experimentation. The module files would be fat.ko, vfat.ko, etc., in the fat (vfat for older kernels) directory under /lib/modules/`uname -r`/kernel/fs. If they are in compressed .gz format, you need to uncompress them with gunzip, before you can insmodthem.

Linux module operations

Figure 2: Linux module operations

The vfat module depends on the fat module, so fat.ko needs to be loaded first. To automatically perform decompression and dependency loading, use modprobe instead. Note that you shouldn’t specify the .ko extension to the module’s name, when using the modprobe command. rmmod is used to unload the modules.

Our first Linux driver

Before we write our first driver, let’s go over some concepts. A driver never runs by itself. It is similar to a library that is loaded for its functions to be invoked by a running application. It is written in C, but lacks a main() function. Moreover, it will be loaded/linked with the kernel, so it needs to be compiled in a similar way to the kernel, and the header files you can use are only those from the kernel sources, not from the standard /usr/include.

One interesting fact about the kernel is that it is an object-oriented implementation in C, as we will observe even with our first driver. Any Linux driver has a constructor and a destructor. The module’s constructor is called when the module is successfully loaded into the kernel, and the destructor when rmmod succeeds in unloading the module. These two are like normal functions in the driver, except that they are specified as the init and exit functions, respectively, by the macros module_init() and module_exit(), which are defined in the kernel header module.h.

/* ofd.c – Our First Driver code */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>

static int __init ofd_init(void) /* Constructor */
{
    printk(KERN_INFO "Namaskar: ofd registered");
    return 0;
}

static void __exit ofd_exit(void) /* Destructor */
{
    printk(KERN_INFO "Alvida: ofd unregistered");
}

module_init(ofd_init);
module_exit(ofd_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anil Kumar Pugalia <email_at_sarika-pugs_dot_com>");
MODULE_DESCRIPTION("Our First Driver");

Given above is the complete code for our first driver; let’s call it ofd.c. Note that there is no stdio.h (a user-space header); instead, we use the analogous kernel.h (a kernel space header). printk() is the equivalent of printf(). Additionally, version.h is included for the module version to be compatible with the kernel into which it is going to be loaded. The MODULE_* macros populate module-related information, which acts like the module’s “signature”.

Building our first Linux driver

Once we have the C code, it is time to compile it and create the module file ofd.ko. We use the kernel build system to do this. The following Makefile invokes the kernel’s build system from the kernel source, and the kernel’s Makefile will, in turn, invoke our first driver’s Makefile to build our first driver.

To build a Linux driver, you need to have the kernel source (or, at least, the kernel headers) installed on your system. The kernel source is assumed to be installed at /usr/src/linux. If it’s at any other location on your system, specify the location in the KERNEL_SOURCE variable in this Makefile.

# Makefile – makefile of our first driver

# if KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.
ifneq (${KERNELRELEASE},)
    obj-m := ofd.o
# Otherwise we were called directly from the command line.
# Invoke the kernel build system.
else
    KERNEL_SOURCE := /usr/src/linux
    PWD := $(shell pwd)
default:
    ${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} modules

clean:
    ${MAKE} -C ${KERNEL_SOURCE} SUBDIRS=${PWD} clean
endif

With the C code (ofd.c) and Makefile ready, all we need to do is invoke make to build our first driver (ofd.ko).

$ make
make -C /usr/src/linux SUBDIRS=... modules
make[1]: Entering directory `/usr/src/linux'
  CC [M]  .../ofd.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC      .../ofd.mod.o
  LD [M]  .../ofd.ko
make[1]: Leaving directory `/usr/src/linux'

Summing up

Once we have the ofd.ko file, perform the usual steps as the root user, or with sudo.

# su
# insmod ofd.ko
# lsmod | head -10

lsmod should show you the ofd driver loaded.

While the students were trying their first module, the bell rang, marking the end of the session. Professor Gopi concluded, “Currently, you may not be able to observe anything other than the lsmod listing showing the driver has loaded. Where’s the printk output gone? Find that out for yourselves, in the lab session, and update me with your findings. Also note that our first driver is a template for any driver you would write in Linux. Writing a specialised driver is just a matter of what gets filled into its constructor and destructor. So, our further learning will be to enhance this driver to achieve specific driver functionalities.”

  • montuviky

    Away-some post will love to read all (and more :) )

    • anil_pugalia

      Thanks for the appreciation. Go ahead & read all 24 are out in hard form.

  • vivek

    when I run make command, i got the following error

    make: Nothing to be done for `default’.

    I guess it could be the problem because of KERNEL_SOURCE. In my make file i have set KERNEL_SOURCE as follows:

    KERNEL_SOURCE := /usr/src/linux-headers-2.6.38-10

    • the_summer

      I had the same problem.
      For me the problem was copy and pasting the Makefile.
      Make sure that in front of the ${MAKE}… commands is an tab and not multiple spaces.
      After replacing spaces with tabs it worked for me. 

      • avik

        thanks for the solution

    • the_summer

      Just noticed:
      I don’t know if you use *buntu. But KERNEL_SOURCE := /usr/src/linux-headers-2.6.38-10 produces some errors.

      KERNEL_SOURCE := /usr/src/linux-headers-2.6.38-10-generic seems to work.

      • Raghu

        # Use this file as your “Makefile”

        obj-m +=ofd.o

        ofd-objs := ofd.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

        • Raghu

          add #include in ofd.c and remove 2nd line from above
          Makefile and compile..

  • montuviky

    I also had the same problem.

    So i manually complied it.

    make was like

    obj-m := ofd.o

    and compilation was :

    make -C /usr/src/linux-headers-2.6.38-10 SUBDIRS=$PWD modules.

    • Teja

      Thanx bro…

    • akash

      hey frnd what does this modules word signify in above make command ….
      do we need to put module path replacing this or directly run above cmmnd as it is … reply pls

      • http://twitter.com/anil_pugalia Anil Pugalia

        Its directly the above command as is.

  • Rakesh Babu

    Rakesh babu
    Dynamically load or unload a driver  means…..

  • Curious_Furious

    Where should the C program be placed? In which directory?
    In your Makefile, what will be the pwd?

    • anil_pugalia

      In any folder of its own.

  • PeterHiggs

    i liked it :) thanks pugs

    • http://www.facebook.com/anil.pugalia Anil Pugalia

      You are welcome

  • PeterHiggs

    hi pugs, all is well. But I need to know a little more about the Makefile syntax and how it works line by line… I can’t digest some code which is alien to me. I would be grateful if you can throw some light on this….

    • khamar

      wats answer to ur auestion?

    • http://www.facebook.com/anil.pugalia Anil Pugalia

      Try to read up “info make”

  • explorer

    when ubuntu is installed on desktop

    what does /usr/src/ dir contains ? i.e kerner source OR kernel headers?

    in my pc linux-headers-2.6.38-10-generic and linux-headers-2.6.38-10 directories are there. what are they.? they dont have any c source . if they are headers why there are two directories i.e what is generic means?

    • anil_pugalia

      They are headers. Sources can also be installed in the same folder using apt-get install linux-sources. Two headers are just the two variants created by the particular distro – ubuntu here

      • calixto

        its apt-get linux-source

        • calixto

          sorry apt-get install linux-source

          • anil_pugalia

            Thanks for the correction. I also corrected it.

  • akash

    how to check kernel location …

    • http://twitter.com/anil_pugalia Anil Pugalia

      I understand you mean the kernel source location. Typically, it would be under /usr/src (linux or build or …). Also, it may not be installed by default. So, you may have to install it first.

  • akash

    root@ubuntu:/usr/src# make -C /usr/src/linux-headers-3.2.0-29 SUBDIRS=$PWD modules.
    I run above command & found this error pls help as soon as possible .. pls

    make: Entering directory `/usr/src/linux-headers-3.2.0-29′

    ERROR: Kernel configuration is invalid.
    include/generated/autoconf.h or include/config/auto.conf are missing.
    Run ‘make oldconfig && make prepare’ on kernel src to fix it.

    • akash

      pls reply …m waiting

      • http://twitter.com/anil_pugalia Anil Pugalia

        So, as it is mentioned in the above error, do make oldconfig after going into the linux-headers-3.2.0.29 folder.

  • akash

    i was trying to build my driver …i got this error … :-

    root@ubuntu:/home/ayush/device# make -C /usr/src/linux-headers-3.2.0-29-generic-pae SUBDIRS=$PWD modules

    make: Entering directory `/usr/src/linux-headers-3.2.0-29-generic-pae’

    Building modules, stage 2.

    MODPOST 0 modules

    make: Leaving directory `/usr/src/linux-headers-3.2.0-29-generic-pae’

    Pls reply any solution … thnx in advance :)

    • http://twitter.com/anil_pugalia Anil Pugalia

      I believe you current directory /home/ayush/device contains the C files which you are trying to build. And your current kernel configuration is set to build & use drivers, i.e. “grep CONFIG_MODULE /usr/src/linux-headers-3.2.0-29-generic-pae/.config” should not be not set or empty.

      • janakiram

        I am also facing same problem as mentioned.. it’s showing

        CONFIG_MODULES=y
        # CONFIG_MODULE_FORCE_LOAD is not set
        CONFIG_MODULE_UNLOAD=y
        # CONFIG_MODULE_FORCE_UNLOAD is not set
        CONFIG_MODULE_SRCVERSION_ALL=y

        • http://twitter.com/anil_pugalia Anil Pugalia

          Use a makefile as suggested in the article. Basically you need the obj-m … line for a module to be built, i.e. obj-m := ofd.o. So, just giving make on command line may not work.

  • satheesaran

    Great example to start with. Thanks for the article :) Motivates to read more

    • http://twitter.com/anil_pugalia Anil Pugalia

      Those words are really inspiring for me.

  • yogesh

    why every function in the device driver is named as static. As static fuction are not visible outside of the file scope. Then, How these driver function gets called by user space application..

    • anil_pugalia

      You are correct that when static, functions are not visible outside of the file scope, but only by their names. Note that, they could be still accessed anywhere by their addresses, and that’s what we do with all these driver functions – populate their addresses into the struct file_operations and make it available to VFS & hence userspace, through the struct cdev.

  • manoj

    device drivers and it’s sources of Linux

  • tushar

    when i do make this error comes

    please tell solution

    sai@ubuntu:~/tushar$ make

    make -C /usr/src/linux-headers-3.5.0-17 SUBDIRS=/home/sai/tushar modules

    make[1]: Entering directory `/usr/src/linux-headers-3.5.0-17′

    ERROR: Kernel configuration is invalid.

    include/generated/autoconf.h or include/config/auto.conf are missing.

    Run ‘make oldconfig && make prepare’ on kernel src to fix it.

    WARNING: Symbol version dump /usr/src/linux-headers-3.5.0-17/Module.symvers

    is missing;

    modules will have no dependencies and modversions.

    Building modules, stage 2.

    /usr/src/linux-headers-3.5.0-17/scripts/Makefile.modpost:42:

    include/config/auto.conf: No such file or directory

    make[2]: *** No rule to make target `include/config/auto.conf’. Stop.

    make[1]: *** [modules]

    Error 2

    make[1]: Leaving directory `/usr/src/linux-headers-3.5.0-17′

    make: *** [default] Error 2

    • tushar

      please reply i am waiting

      • http://twitter.com/anil_pugalia Anil Pugalia

        Do as the message says: make oldconfig
        And then, make modules_prepare
        Both in the linux-headers folder.
        And then retry.

        • tushar

          1> when i do make oldconfig in linux-headers-3.5..0-17 folder i got this error

          HOSTCC scripts/basic/fixdep

          scripts/basic/fixdep.c:433:1: fatal error: opening dependency file scripts/basic/.fixdep.d: Permission denied

          compilation terminated.

          make[1]: *** [scripts/basic/fixdep] Error 1

          make: *** [scripts_basic] Error 2

          tushar@Sai:/usr/src/linux-headers-3.5.0-17$

          2> and after that if i do make modules_prepare i got this error

          HOSTCC scripts/basic/fixdep

          scripts/basic/fixdep.c:433:1: fatal error: opening dependency file scripts/basic/.fixdep.d: Permission denied

          compilation terminated.

          make[2]: *** [scripts/basic/fixdep] Error 1

          make[1]: *** [scripts_basic] Error 2

          make: *** No rule to make target `modules_prepare’. Stop.

          tushar@Sai:/usr/src/linux-headers-3.5.0-17$

          i am using ubuntu 12.10
          kernel version 3.5.0-17-generic

          PLZ PROVIDE SOLUTION SIR

          • tushar

            plz reply

          • http://twitter.com/anil_pugalia Anil Pugalia

            Use sudo, yaar.
            sudo make oldconfig

          • tushar

            1)when i do sudo make oldconfig then it is fine

            2) but when i do sudo make modules_prepare i got fooliowing error

            tushar@Sai:/usr/src/linux-headers-3.5.0-17$ sudo make modules_prepare

            scripts/kconfig/conf –silentoldconfig Kconfig

            make[1]: *** No rule to make target `/usr/src/linux-headers-3.5.0-17/arch/x86/syscalls/syscall_32.tbl’, needed by `arch/x86/syscalls/../include/generated/asm/unistd_32.h’. Stop.

            make: *** [archheaders] Error 2

            tushar@Sai:/usr/src/linux-headers-3.5.0-17$

            pls tell solution sir

          • http://twitter.com/anil_pugalia Anil Pugalia

            How about sudo make prepare?
            If not, then some issue with the headers package installation.

  • darcy.diao@gmail.com

    I use your code and compile successfully.
    But when insmod by “sudo insmod ofd.ko”, error prompt-” insmod: error inserting ‘ofd.ko’: -1 Invalid module format”.
    Can you tell me why?
    Thx.

    • http://twitter.com/anil_pugalia Anil Pugalia

      Make sure that the kernel headers you used to compile the driver has the same version as the running kernel.

    • darcy.diao@gmail.com

      I change the KERNEL_SOURCE to /lib/module/$(shell uname -r)/build.
      It is ok right now.

  • tushraj

    I use your code and compile by make command then i got following error

    tushar@tushar-SVE14112ENW:~/devdrv$ make
    make -C /usr/src/linux-headers-3.5.0-17 SUBDIRS=/home/tushar/devdrv modules
    make[1]: Entering directory `/usr/src/linux-headers-3.5.0-17′
    WARNING: Symbol version dump /usr/src/linux-headers-3.5.0-17/Module.symvers is missing;
    modules will have no dependencies and modversions.
    Building modules, stage 2.

    MODPOST 0 modules

    /bin/sh: 1: scripts/mod/modpost: not found

    make[2]: *** [__modpost] Error 127

    make[1]: *** [modules] Error 2

    make[1]: Leaving directory `/usr/src/linux-headers-3.5.0-17′

    make: *** [default] Error 2

    • http://twitter.com/anil_pugalia Anil Pugalia

      Check out the comments below for the solution to the same problem.

  • enigma

    obj-m := ofd.o should be obj-m += ofd.o ?

    • http://twitter.com/anil_pugalia Anil Pugalia

      In this particular case, even := is fine as, we are limiting the build to the drivers directory and more to our driver, alone.

  • janakiram

    I use your code and compile by make command then i got following errors

    make -C /usr/src/linux-headers-3.2.0-29 SUBDIRS=/home/shivaram/jani modules
    make[1]: Entering directory `/usr/src/linux-headers-3.2.0-29′

    ERROR: Kernel configuration is invalid.
    include/generated/autoconf.h or include/config/auto.conf are missing.
    Run ‘make oldconfig && make prepare’ on kernel src to fix it.

    WARNING: Symbol version dump /usr/src/linux-headers-3.2.0-29/Module.symvers
    is missing; modules will have no dependencies and modversions.

    Building modules, stage 2.
    /usr/src/linux-headers-3.2.0-29/scripts/Makefile.modpost:42: include/config/auto.conf: No such file or directory
    make[2]: *** No rule to make target `include/config/auto.conf’. Stop.
    make[1]: *** [modules] Error 2
    make[1]: Leaving directory `/usr/src/linux-headers-3.2.0-29′
    make: *** [default] Error 2s

    And also i tried with make oldconfig&&make prepare then i got problem like

    make: *** No rule to make target `oldconfig’. Stop.

    i installed ubuntu through vmware. one of my friend said it’s the problem u should install normal way..

    • http://twitter.com/anil_pugalia Anil Pugalia

      Check out the discussion threads below.

  • uday

    sir,i have created a makefile and added obj-m := ofd.o in it.

    After saving and exiting the terminal,i have given command like this

    make -C /usr/src/linux-headers-generic-pae-3.2.0-12 SUBDIRS=$PWD modules

    but it is showing “no rule to make”…pls help me.
    regards,
    uday.

    • http://twitter.com/anil_pugalia Anil Pugalia

      Make sure that you use *only tabs* to indent your makefile. You get such errors, if you have used spaces instead.

All published articles are released under Creative Commons Attribution-NonCommercial 3.0 Unported License, unless otherwise noted.
LINUX For You is powered by WordPress, which gladly sits on top of a CentOS-based LEMP stack.

Creative Commons License.