Current Linux kernels support a huge number of devices. Device drivers account for half of the size of the source tree (actually two-thirds if you exclude architecture-specific code that you are not using). They account for almost 1500 C-language files and more than 800 headers.

The drivers directory itself doesn’t host any source file, only subdirectories (and, obviously, a makefile).

Structuring the huge amount of source code is not easy, and the developers haven’t followed any strict rules. The original division between drivers/char and drivers/block is inefficient nowadays, and more directories have been created according to several different requirements. Still, the most generic char and block drivers are found in drivers/char and drivers/block, so we’ll start by visiting those two.


The drivers/char directory is perhaps the most important in the drivers hierarchy, because it hosts a lot of driver-independent code.

The generic tty layer (as well as line disciplines, tty software drivers, and similar features) is implemented in this directory. console.c defines the linux terminal type (by implementing its specific escape sequences and keyboard encoding). vt.c defines the virtual consoles, including code for switching from one virtual console to another. Selection support (the cut-and-paste capability of the Linux text console) is implemented by selection.c; the default line discipline is implemented by n_tty.c.

There are other files that, despite what you might expect, are device independent. lp.c implements a generic parallel port printer driver that includes a console-on-line-printer capability. It remains device independent by using the parport device driver to map operations to actual hardware (as seen in Figure 2-2). Similarly, keyboard.c implements the higher levels of keyboard handling; it exports the handle_scancode function so that platform-specific keyboard drivers (like pc_keyb.c, in the same directory) can benefit from generalized management. mem.c implements /dev/mem, /dev/null, and /dev/zero, basic resources you can’t do without.

Actually, since mem.c is never left out of the compilation process, it has been elected as the home of chr_dev_init, which in turn initializes several other device drivers if they have been selected for compilation.

There are other device-independent and platform-independent source files in drivers/char. If you are interested in looking at the role of each source file, the best place to start is the makefile for this directory, an interesting and pretty much self-explanatory file.


Like the preceding drivers/char directory, drivers/block has been present in Linux development for a long time. It used to host all block device drivers, and for this reason it included some device-independent code that is still present.

The most important file is ll_rw_blk.c (low-level read-write block). It implements all the request management functions that we described in Chapter 12.

A relatively new entry in this directory is blkpg.c (added as of 2.3.3). The file implements generic code for partition and geometry handling in block devices. Its code, together with the fs/partitions directory described earlier, replaces what was earlier part of “generic hard disk” support. The file called genhd.c still exists, but now includes only the generic initialization function for block drivers (similar to the one for char drivers that is part of mem.c). One of the public functions exported by blkpg.c is blk_ioctl, covered by Section 12.6 in Chapter 12.

The last device-independent file found in drivers/block is elevator.o. This file implements the mechanism to change the elevator function associated with a block device driver. The functionality can be exploited by means of ioctl commands briefly introduced in Section 12.6.

In addition to the hardware-dependent device drivers you would expect to find in drivers/block, the directory also includes software device drivers that are inherently cross-platform, just like the sbull and spull drivers that we introduced in this book. They are the RAM disk rd.c, the “network block device” nbd.c, and the loopback block device loop.c. The loopback device is used to mount files as if they were block devices. (See the manpage for mount, where it describes the -o loop option.) The network block device can be used to access remote resources as block devices (thus allowing, for example, a remote swap device).

Other files in the directory implement drivers for specific hardware, such as the various different floppy drives, the old-fashioned x86 XT disk controller, and a few more. Most of the important families of block drivers have been moved to a separate directory.


The IDE family of device drivers used to live in drivers/block but has expanded to the point where they were moved into a separate directory. As a matter of fact, the IDE interface has been enhanced and extended over time in order to support more than just conventional hard disks. For example, IDE tapes are now supported as well.

The drivers/ide directory is a whole world of its own, with some generalized code and its own programming interface. You’ll note in the directory some files that are just a few kilobytes long; they include only the IDE controller detection code, and rely on the generalized IDE driver for everything else. They are interesting reading if you are curious about IDE drivers.


This directory is concerned with implementing RAID functionality and the Logical Volume Manager abstraction. The code registers its own char and block major numbers, so it can be considered a driver just like those traditional drivers; nonetheless, the code has been kept separate because it has nothing to do with direct hardware management.


This directory hosts the generic CD-ROM interface. Both the IDE and SCSI cdrom drivers rely on drivers/cdrom/cdrom.c for some of their functionality. The main entry points to the file are register_cdrom and unregister_cdrom; the caller passes them a pointer to struct cdrom_device_info as the main object involved in CD-ROM management.

Other files in this directory are concerned with specific hardware drives that are neither IDE nor SCSI. Those devices are pretty rare nowadays, as they have been made obsolete by modern IDE controllers.


Everything related to the SCSI bus has always been placed in this directory. This includes both controller-independent support for specific devices (such as hard drives and tapes) and drivers for specific SCSI controller boards.

Management of the SCSI bus interface is scattered in several files: scsi.c, hosts.c, scsi_ioctl.c, and a dozen more. If you are interested in the whole list, you’d better browse the makefile, where scsi_mod-objs is defined. All public entry points to this group of files have been collected in scsi_syms.c.

Code that supports a specific type of hardware drive plugs into the SCSI core system by calling scsi_register_module with an argument of MODULE_SCSI_DEV. This is how disk support is added to the core system by sd.c, CD-ROM support by sr.c (which, internally, refers to the cdrom_ class of functions), tape support by st.c, and generic devices by sg.c.

The “generic” driver is used to provide user-space programs with direct access to SCSI devices. The underlying device can be virtually anything; currently both CD burners and scanner programs rely on the SCSI generic device to access the hardware they drive. By opening the /dev/sg devices, a user-space driver can do anything it needs without specific support in the kernel.

Host adapters (i.e., SCSI controller hardware) can be plugged into the core system by calling scsi_register_module with an argument of MODULE_SCSI_HA. Most drivers currently do that by using the scsi_module.c facility to register themselves: the driver’s source file defines its (static) data structures and then includes scsi_module.c. This file defines standard initialization and cleanup functions, based on <linux/init.h> and the init calls mechanisms. This technique allows drivers to serve as either modules or compiled-in functions without any #ifdef lines.

Interestingly, one of the host adapters supported in drivers/scsi is the IDE SCSI emulation code, a software host adapter that maps to IDE devices. It is used, as an example, for CD mastering: the system sees all of the drives as SCSI devices, and the user-space program need only be SCSI aware.

Please note that several SCSI drivers have been contributed to Linux by the manufacturers rather than by your preferred hacker community; therefore not all of them are fun reading.


As you might expect, this directory is the home for most interface adapters. Unlike drivers/scsi, this directory doesn’t include the actual communication protocols, which live in the top-level net directory tree. Nonetheless, there’s still some bit of software abstraction implemented in drivers/net, namely, the implementation of the various line disciplines used by serial-based network communication.

The line discipline is the software layer responsible for the data that traverses the communication line. Every tty device has a line discipline attached. Each line discipline is identified by a number, and the number, as usual, is specified using a symbolic name. The default Linux line discipline is N_TTY, that is, the normal tty management routines, defined in drivers/char/n_tty.c.

When PPP, SLIP, or other communication protocols are concerned, however, the default line discipline must be replaced. User-space programs switch the discipline to N_PPP or N_SLIP, and the default will be restored when the device is finally closed. The reason that pppd and slattach don’t exit, after setting up the communication link is just this: as soon as they exit, the device is closed and the default line discipline gets restored.

The job of initializing network drivers hasn’t yet been transferred to the init calls mechanism, because some subtle technical details prevent the switch. Initialization is therefore still performed the old way: the Space.c file performs the initialization by scanning a list of known hardware and probing for it. The list is controlled by #ifdef directives that select which devices are actually included at compile time.


Like drivers/scsi and drivers/net, this directory includes all the drivers for sound cards. The contents of the directory are somewhat similar to the SCSI directory: a few files make up the core sound system, and individual device drivers stack on top of it. The core sound system is in charge of requesting the major number SOUND_MAJOR and dispatching any use of it to the underlying device drivers. A hardware driver plugs into the core by calling sound_install_audiodrv, declared in dev_table.c.

The list of device-independent files in this directory is pretty long, since it includes generic support for mixers, generic support for sequencers, and so on. To those who want to probe further, we suggest using the makefile as a reference to what is what.


Here you find all the frame buffer video devices. The directory is concerned with video output, not video input. Like /drivers/sound, the whole directory implements a single char device driver; a core frame buffer system dispatches actual access to the various frame buffers available on the computer.

The entry point to /dev/fb devices is in fbmem.c. The file registers the major number and maintains an internal list of which frame buffer device is in charge of each minor number. A hardware driver registers itself by calling register_framebuffer, passing a pointer to struct fb_info. The data structure includes everything that’s needed for specific device management. It includes the open and release methods, but no read, write, or mmap; these methods are implemented in a generalized way in fbmem.c itself.

In addition to frame buffer memory, this directory is in charge of frame buffer consoles. Because the layout of pixels in frame buffer memory is standardized to some extent, kernel developers have been able to implement generic console support for the various layouts of display memory. Once a hardware driver registers its own struct fb_info, it automatically gets a text console attached to it, according to its declared layout of video memory.

Unfortunately, there is no real standardization in this area, so the kernel currently supports 17 different screen layouts; they range from the fairly standard 16-bit and 32-bit color displays to the hairy VGA and Mac pixel placements. The files concerned with placing text on frame buffers are called fbcon- name .c.

When the first frame buffer device is registered, the function register_framebuffer calls take_over_console (exported by drivers/char/console.c) in order to actually set up the current frame buffer as the system console. At boot time, before frame buffer initialization, the console is either the native text screen or, if none is there, the first serial port. The command line starting the kernel, of course, can override the default by selecting a specific console device. Kernel developers created take_over_console to add support for frame buffer consoles without complicating the boot code. (Usually frame buffer drivers depend on PCI or equivalent support, so they can’t be active too early during the boot process.) The take_over_console feature, however, is not limited to frame buffers; it’s available to any code involving any hardware. If you want to transmit kernel messages using a Morse beeper or UDP network packets, you can do that by calling take_over_console from your kernel module.


Input management is another facility meant to simplify and standardize activities that are common to several drivers, and to offer a unified interface to user space. The core file here is called input.c. It registers itself as a char driver using INPUT_MAJOR as its major number. Its role is collecting events from low-level device drivers and dispatching them to higher layers.

The input interface is defined in <linux/input.h>. Each low-level driver registers itself by calling input_register_device. After registration, users are able to feed new events to the system by calling input_event.

Higher-level modules can register with input.c by calling input_register_handler and specifying what kind of events they are interested in. This is, for example, how keybdev.c expresses its interest in keyboard events (which it ultimately feeds to driver/char/keyboard.c).

A high-level module can also register its own minor numbers so it can use its own file operations and become the owner of an input-related special file in /dev. Currently, however, third-party modules can’t easily register minor numbers, and the feature can be used reliably only by the files in drivers/input. Minor numbers can currently be used to support mice, joysticks, and generic even channels in user space.


This directory, introduced as of version 2.4.0-test7, collects other communication media, currently radio and video input devices. Both the media/radio and media/video drivers currently stack on video/videodev.c, which implements the “Video For Linux” API.

video/videodev.c is a generic container. It requests a major number and makes it available to hardware drivers. Individual low-level drivers register by calling video_register_device. They pass a pointer to their own struct video_device and an integer that specifies the type of device. Supported devices are frame grabbers (VFL_TYPE_GRABBER), radios (VFL_TYPE_RADIO), teletext devices (VFL_TYPE_VTX), and undecoded vertical-blank information (VFL_TYPE_VBI).

Bus-Specific Directories

Some of the subdirectories of drivers are specific to devices that plug into a particular bus architecture. They have been separated from the generic char and block directories because quite a good deal of code is generic to the bus architecture (as opposed to specific to the hardware device).

The least populated of these directories is drivers/pci. It contains only code that talks with PCI controllers (or to system BIOS), whereas PCI hardware drivers are scattered all over the place. The PCI interface is so widespread that it makes no sense to relegate PCI cards to a specific place.

If you are wondering whether ISA has a specific directory, the answer is no. There are no specific ISA support files because the bus offers no resource management or standardization to build a software layer over it. ISA hardware drivers fit best in drivers/char or drivers/sound or elsewhere.

Other bus-specific directories range from less known internal computer buses to widely used external interface standards.

The former class includes drivers/sbus, drivers/nubus, drivers/zorro (the bus used in Amiga computers), drivers/dio (the bus of the HP300 class of computers), and drivers/tc (Turbo Channel, used in MIPS DECstations). Whereas sbus includes both SBus support functions and drivers for some SBus devices, the others include only support functions. Hardware drivers based on all of these buses live in drivers/net, drivers/scsi, or wherever is appropriate for the actual hardware (with the exception of a few SBus drivers, as noted). A few of these buses are currently used by just one driver.

Directories devoted to external buses include drivers/usb, drivers/pcmcia, drivers/parport (generic cross-platform parallel port support, which defines a whole new class of device drivers), drivers/isdn (all ISDN controllers supported by Linux and their common support functions), drivers/atm (the same, for ATM network connections), and drivers/ieee1394 (FireWire).

Platform-Specific Directories

Sometimes, a computer platform has its own directory tree in the drivers hierarchy. This has tended to happen when kernel development for that platform has proceeded alongside the main source tree without being merged for a while. In these cases, keeping the directory tree separate helped in maintaining the code. Examples include drivers/acorn (old ARM-based computers), drivers/macintosh, drivers/sgi (Silicon Graphics workstations), and drivers/s390 (IBM mainframes). There is little of value, usually, in looking at that code, unless you are interested in the specific platform.

Other Subdirectories

There are other subdirectories in drivers, but they are, in our opinion, currently of minor interest and very specific use. drivers/mtd implements a Memory Technology Device layer, which is used to manage solid-state disks (flash memories and other kinds of EEPROM). drivers/i2c offers an implementation of the i2c protocol, which is the “Inter Integrated Circuit” two-wire bus used internally by several modern peripherals, especially frame grabbers. drivers/i2o, similarly, handles I2O devices (a proprietary high-speed communication standard for certain PCI devices, which has been unveiled under pressure from the free software community). drivers/pnp is a collection of common ISA Plug-and-Play code from various drivers, but fortunately the PnP hack is not really used nowadays by manufacturers.

Under drivers/ you also find initial support for new device classes that are currently implemented by a very small range of devices.

That’s the case for fiber channel support (drivers/fc4) and drivers/telephony. There’s even an empty directory drivers/misc, which claims to be “for misc devices that really don’t fit anywhere else.” The directory is empty of code, but hosts an (empty) makefile with the comment just quoted.

The Linux kernel is so huge that it’s impossible to cover it all in a few pages. Moreover, it is a moving target, and once you think you are finished, you find that the new patch released by your preferred hackers includes a whole lot of new material. It may well be that the misc directory in 2.4 is not empty anymore as you read this.

Although we consider it unlikely, it may even happen that 2.6 or 3.0 will turn out to be pretty different from 2.4; unfortunately, this edition of the book won’t automatically update itself to cover the new releases and will become obsolete over time. Despite our best efforts to cover the current version of the kernel, both in this chapter and in the whole book, there’s no substitute for direct reference to the source code.

Get Linux Device Drivers, Second Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.