An introduction to block device drivers

Last month, we inaugurated a column on Linux kernel programming with an article on how to write Linux device drivers without doing any kernel programming. This month we touch the kernel as we explore block device drivers.
blk.h

We have already seen several macros which are very helpful in writing block device drivers. Many of these are defined in drivers/block/blk.h, and have to be specially set up.

At the top of the device driver, after including the standard include files your driver needs (which must include linux/major.h and linux/blkdrv.h), you should write the following lines:

#define MAJOR_NR FOO_MAJOR
#include "blk.h"

This, in turn, requires that you define FOO_MAJOR to be the major number of the device you are writing in linux/major.h.

Now you need to edit blk.h. One section of blk.h, right near the top, includes definitions of macros that depend on the definition of MAJOR_NR. Add an entry to the end which looks like this:

#elif (MAJOR_NR == FOO_MAJOR)
#define DEVICE_NAME "foobar"
#define DEVICE_REQUEST do_foo_request
#define DEVICE_NR(device) (MINOR(device) >> 6)
#define DEVICE_ON(device)
#define DEVICE_OFF(device)
#endif

These are the required macros for each block device driver. There are more macros that can be defined; they are explained in the KHG.

DEVICE_NAME is the name of the driver. The AT hard drive driver uses the abbreviation “hd” in most places; for example, the request() procedure is called do_hd_request(). However, its DEVICE_NAME is “harddisk”. Similarly, the floppy driver, “fd”, has a DEVICE_NAME of “floppy”. Other drivers are even more descriptive; read blk.h and follow suit.

DEVICE_REQUEST is the request() procedure for the driver.

DEVICE_NR is used to determine the actual physical device. For example, the standard AT hard disk driver uses 64 minor devices for each physical device, so DEVICE_NR is defined as (MINOR(device)>6). The SCSI disk driver uses 16 minor device numbers per physical device, so for it, DEVICE_NR is defined as (MINOR(device)>4). If you have only one minor device number per physical device, define DEVICE_NR as (MINOR(device)).

DEVICE_ON and DEVICE_OFF are only used for devices that have to be turned on and off. The floppy driver is the only driver that uses this capability. You will most likely want to define these to be nothing at all.

All these macros, as well as many others, can be used in your driver where appropriate. blk.h includes a lot of macros, and studying how they are used in other drivers will help you use them in your own driver. I won't document them fully here, but I will briefly mention some of them to make your life easier.

DEVICE_INTR, SET_INTR, and CLEAR_INTR make support for interrupt-driven devices much easier. DEVICE_ TIMEOUT, SET_TIMER, and CLEAR_TIMER help you set limits on how long may be taken to satisfy a request.

The First Shall Be the Last

I've saved the first, and perhaps most important, thing for last. Before you can read or write a single block, the kernel has to be notified that the device exists. All device drivers are required to implement an initialization function, and there are some special requirements for block device drivers. Here is a sample idealized initialization function:

long foo_init(long mem_start, int length)
{
  if (register_blkdev(FOO_MAJOR,"foo", & foo_fops)) {
    printk("FOOBAR: Unable to get major %d.\n",
           FOO_MAJOR);
    return 0;
  }
  if (!foo_exists()) {
    /* the foobar device doesn't exist */
    return 0;
  }
  /* initialize hardware if necessary */
  /* notify user device found */
  printk("FOOBAR: Found at address %d.\n",
           foo_addr());
  /* tell buffer cache how to process requests */
  blk_dev[FOO_MAJOR].request_fn = DEVICE_REQUEST;
  /* specify the blocksize */
  blksize_size[MAJOR_NR] = 1024;
  return(size_of_memory_reserved);
}

The three things here that are specific to block device drivers are:

  • register_blkdev() registers the file operations structure with the Virtual Filesystem Switch (VFS), which is the system that manages access to files.

  • blk_dev tells the buffer cache where the request procedure is.

  • blksize_size tells the buffer cache what size blocks to request.

It is worth noting that the hardware device detection and initialization, which I have denoted as foo_exists() here, is very delicate code. If you can rely on a string somewhere in the BIOS of the computer to determine whether the device exists and where it is, it's relatively easy. However, if you have to check various I/O ports, you can hang the computer by writing the wrong value to the wrong port, or even reading the wrong port. Check only well-known ports if you must check ports, and provide kernel command-line arguments for other ports. To do this, read init/main.c and add a section of your own. If you can't figure out how to do it, an explanation is forthcoming in the next version of the KHG.

Of course, none of this initialization will happen if foo_init() is never called. Add a prototype to the top of blk.h with the other prototypes, and add a call to foo_init() in ll_rw_blk.c in the blk_dev_init() function. That call should be protected by #ifdef CONFIG_FOO like the rest of the *_init() functions there, and a corresponding line should be added to the config.in file:

bool `Foobar disk support' CONFIG_FOO y

drivers/block/Makefile should have a section added that looks like this:

ifdef CONFIG_FOO
OBJS := $(OBJS) foo.o
SRCS := $(SRCS) foo.c
endif

This done, configuration should work correctly. Your device driver file does not need to have any references to CONFIG_FOO; the only specific reference to it is commented out in ll_rw_blk.c, and the makefile only builds it if it has been configured in.

Now all you have to do is write and debug your own new block device driver. I wish you the best of luck, and I hope that this whirlwind tour has given you a head start.

Other Resources

Michael K. Johnson is the editor of Linux Journal, and is also the author of the Linux Kernel Hackers' Guide (the KHG). He is using this column to develop and expand on the KHG.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

page 3?

Anonymous's picture

This article has 3 pages, but it seems to end on page 2, because page 3 is empty.

Useful for a newbie

Sivakumar.R.J's picture

hi...
its really useful for a newbie entering into a block device driver, I am a just a beginner of it.. looking some more information regarding block driver in this forum.. thanks for the article..

Webinar
One Click, Universal Protection: Implementing Centralized Security Policies on Linux Systems

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Learn More

Sponsored by Bit9

Webinar
Linux Backup and Recovery Webinar

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.

Learn More

Sponsored by Storix