Linux Device Drivers: 01 Introduction

Welcome! This is the first installment in a series of blogs that I intend to write for the LDD3 (Linux Device Drivers, 3rd Edition) book by Corbet et al. The book is quite old and focuses on version 2.6 of the Linux kernel. Although most of the content is still valid for the latest kernel tree, some APIs are no longer supported.

Multiple open-source projects have ported the examples presented in the book to a newer kernel version (5.x). I wanted to write a series of blogs (10 - 15) that will concisely present the book’s subject matter along with the updated examples that can be tested on current systems. Through these blogs, I intend to teach myself device driver development while maintaining a summary that can be reviewed without rereading the book.

What is a Device Driver

A driver acts as a black box that sits between the kernel and the device (hardware). It has to expose a standard interface, through which the kernel can interact with the device by making appropriate function calls. How those functions are implemented is managed by the driver, and is hidden from the kernel. A driver can be built separately from the kernel and loaded whenever required.

Any piece of code that can be added to the kernel at runtime is called a module. A module can extend the kernel by providing new features not present when it was compiled. A device driver is also a type of module and deals with talking to hardware on behalf of the kernel.

In Linux, devices can be categorised into broadly three types:

  1. Character device: provide a stream of data, handled by a character (char) driver. Eg. keyboard, tty.
  2. Block device: a disk which can host a filesystem.
  3. Network device: NIC (Network Interface Card). Can send and receive data packets to other computers.

Let’s build a simple Hello World module that does nothing significant except for printing a few statements inside the kernel logs.

Setting up our environment

Since we will be testing our modules by loading them into the kernel, it is advisable to setup a VM (Virtual Machine) to develop and test our code. Modules run in the kernel stack, and any bug in our module code can cause the host operating system to freeze (although rare). Go ahead and install Ubuntu on a VM.

To build our modules, we require the kernel header files. They are available as a system package on most distributions and can be installed easily. However we will not take that route. instead we’ll download the linux source tree, that contains the header files, and compile our own kernel since that allows us to target our modules for a specific kernel version instead of the one running on the host. All examples in this series have been tested against v5.10.4.

wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.4.tar.xz
tar -xf linux-5.10.4.tar.xz

Before compiling the kernel, we need to configure. Let’s go ahead with the default configuration as it suits our needs.

make defconfig
make menuconfig

Compile the kernel:

make -j$(nproc) bzImage
make -j$(nproc) modules

Since we will be using the source tree to build our module, it is convenient to export a KERNELDIR environment. Append this to your .bashrc:

export KERNELDIR=/path/to/kernel/source

The Hello World Module

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

static char *whom = "Mom";
static int howmany = 1;

module_param(howmany, int,   S_IRUGO);
module_param(whom,    charp, S_IRUGO);

static
int __init m_init(void) {
    pr_debug("parameters test module is loaded\n");
    for (int i = 0; i < howmany; ++i) {
        pr_info("#%d Hello, %s\n", i, whom);
    }
    return 0;
}

static
void __exit m_exit(void) {
    pr_debug("parameters test module is unloaded\n");
}

module_init(m_init);
module_exit(m_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("d0u9");
MODULE_DESCRIPTION("Module parameters test program");



    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • Learncpp: 01 Functions and Files
  • Learncpp: 02 Scope, Duration, and Linkage