It was the late 1990s and at IBM, we were putting the
Linux kernel on a wrist watch. The target device was
tiny, but the task was turning out to be tough. The
Memory Technology Devices subsystem didn't exist in
the kernel, which meant that before a filesystem could
start life on the watch's flash memory, we had to
develop the necessary storage driver from
scratch. Interfacing the watch's touch screen with user
applications was complicated since the
kernel's input event driver interface hadn't been
conceived yet. Getting X-Windows to run on the watch's
LCD wasn't easy since it didn't work well with frame
buffer drivers. Of what use is a water-proof Linux wrist
watch if you can't stream stock quotes from your bath
tub? Bluetooth integration with Linux was several years
away, and months were spent porting a proprietary
Bluetooth stack to Internet-enable the watch. Power
management support was good enough only to squeeze a few
hours of juice from the watch's battery, hence we had
work cut out on that front too. Linux-Infrared was still
unstable, so we had to coax the stack before we could
use an Infrared keyboard for data entry. And we had to
compile the compiler and cross-compile a compact
application-set since there were no accepted
distributions in the consumer electronics space.
Fast forward to the present: The baby penguin has
grown into a healthy teenager. What took thousands of
lines of code and a year in development back then, can
be accomplished in a few days with the current
kernels. But to become a versatile kernel engineer who
can magically weave solutions, you need to understand
the myriad features and facilities that Linux offers
today.
Among the various subsystems residing in the kernel
source tree, the
drivers/ directory constitutes the single largest
chunk and is several times bigger than the others. With
new and diverse technologies arriving in popular form
factors, the development of new device drivers in the
kernel is accelerating steadily. The latest kernels
support over 70 device driver families.
This book is about writing Linux device drivers. It
covers the design and development of major device
classes supported by the kernel, including those I
missed during my Linux-on-Watch days. The discussion of
each driver family starts by looking at the
corresponding technology, moves on to develop a
practical example, and ends by looking at relevant
kernel source files. But before foraying into the world
of device drivers, the book introduces you to the kernel
and discusses the important features of 2.6 Linux,
emphasizing those portions that are of special interest
to device driver writers.
This book is intended for the intermediate-level programmer eager to tweak the kernel to enable new devices. You should have a working knowledge of operating system concepts. For example, you should know what a system call is, and why concurrency issues have to be factored in while writing kernel code. The book assumes that you have downloaded Linux on your system, poked through the kernel sources, and at least skimmed through some related documentation. And you should be pretty good in C.
The
first four chapters prepare you to digest the rest of
the book. The next sixteen chapters discuss drivers
for different device families. A chapter that
describes device driver debugging techniques comes
next. The penultimate chapter provides perspective on
maintenance and delivery. We will shut down by walking
through a checklist that summarizes how to set forth
on your way to Linux-enablement when you get hold of a
new device.
Chapter 1,
"Introduction" starts our tryst with
Linux. It hurries you through downloading the kernel
sources, making trivial code changes, and building a
bootable kernel image.
Chapter 2, "A Peek Inside the
Kernel", takes a brisk look into the innards of
the Linux kernel and teaches you some must-know
kernel concepts. It first takes you through the boot
process and then describes kernel services
particularly relevant to driver development such as
kernel timers, concurrency management, and memory
allocation.
Chapter 3, "Kernel
Facilities", examines several kernel services that are
useful components in the tool box of driver
developers. The chapter starts by looking at kernel
threads, which is a way to implement background tasks
inside the kernel. It then moves on to helper interfaces
such as linked lists, work queues, completion functions,
and notifier chains. These helper facilities simplify your
code, weed out redundancies from the kernel, and help
long-term maintenance.
Chapter 4, "Laying the
Groundwork", builds the foundation for mastering the
art of writing Linux device drivers. It introduces devices
and drivers by giving you a bird's eye view of the
architecture of a typical PC-compatible system and an
embedded device. It then looks at basic driver concepts
such as interrupt handling and the kernel's device model.
Chapter 5, "Character
Drivers", looks at the architecture of character device
drivers. Several concepts introduced in this chapter such as polling,
asynchronous notification, and I/O control, are relevant to subsequent
chapters as well, since many device classes discussed in the rest of
the book are "super" character devices.
Chapter 6, "Serial Drivers", explains the
kernel layer that handles serial devices.
Chapter 7, "Input Drivers", discusses the
kernel's input subsystem
that is responsible for servicing devices such as keyboards, mice, and
touch screen controllers.
Chapter 8, "The Inter-Integrated Circuit
Protocol", dissects drivers for devices such as EEPROMs that are
connected to a system's I2C bus or SMBus. This chapter also
looks at other serial interfaces such as SPI bus and 1-wire bus.
Chapter 9, "PCMCIA and Compact Flash",
delves into the PCMCIA subsystem. It teaches you to write drivers for
devices having a PCMCIA or Compact Flash form factor.
Chapter 10, "Peripheral Component
Interconnect", looks at kernel support for PCI and its
derivatives.
Chapter 11, "Universal Serial Bus",
explores USB architecture and explains how you can use the services of
the Linux-USB subsystem to write drivers for USB devices.
Chapter 12, "Video Drivers", examines the
Linux-Video subsystem. It finds out the advantages offered by the
frame buffer abstraction and teaches you to write frame buffer
drivers.
Chapter 13, "Audio Drivers", describes the
Linux-Audio framework and explains how to implement audio drivers.
Chapter 14 , "Block Drivers", focuses on
drivers for storage devices such as hard disks. In this chapter, you
will also learn about the different I/O schedulers supported by the
Linux Block subsystem.
Chapter 15, "Network Interface Cards", is
devoted to network device drivers. You will learn about kernel
networking data structures and how to interface network drivers with
protocol layers.
Chapter 16, "Linux without Wires", looks
at driving different wireless technologies such as Bluetooth,
Infrared, WiFi, and cellular communication.
Chapter 17, "Memory Technology Devices",
discusses flash memory enablement on embedded devices. The chapter
ends by examining drivers for the Firmware Hub found on PC systems.
Chapter 18, "Embedding Linux", steps into
the world of embedded Linux. It takes you through the main firmware
components of an embedded solution such as bootloader, kernel, and
device drivers. Given the soaring popularity of Linux in the embedded
space, it's more likely that you will use the device driver skills
that you acquire from this book to enable embedded systems.
Chapter 19, "Drivers in User Space", looks
at driving different types of devices from user space. Some device
drivers, especially ones that are heavy on policy and light on
performance requirements, are better off residing in user land. This
chapter also explains how the Linux process scheduler affects the
response times of user mode drivers.
Chapter 20, "More Devices and Drivers", takes a
tour of a potpourri of driver families not covered thus far, such as
Error Detection And Correction (EDAC), FireWire, and ACPI.
Chapter 21, "Debugging Device Drivers",
teaches about different types of debuggers that you can use to debug
kernel code. In this chapter, you will also learn to use trace tools,
kernel probes, crash-dump, and profilers. When you develop a driver,
be armed with the driver debugging skills that you learn in this
chapter.
Chapter 22, "Maintenance and Delivery",
provides perspective on the software development life cycle.
Chapter 23, "Shutting Down", takes you
through a checklist of work items when you embark on Linux-enabling a
new device. The book ends by pondering What next?
Device drivers sometimes need to implement code snippets
in assembly, so Appendix A takes a look at the different facets
of assembly programming on Linux. Some device drivers
on x86-based systems depend directly or indirectly on the BIOS,
so Appendix B teaches you how Linux interacts with the
BIOS. Appendix C describes seq files, a kernel helper
interface introduced in the 2.6 kernel that device drivers can use to
monitor and trend data points.
The book is generally organized according to device and
bus complexity, coupled with practical reasons of dependencies between
chapters. So, we start off with basic device classes such as
character, serial, and input. Next, we look at simple serial buses
such as I2C and SMBus. External I/O buses such as PCMCIA,
PCI, and USB follow. Video, audio, block, and network devices usually
interface with the processor via these I/O buses, so we look at them
soon after. The next portions of the book are oriented towards
embedded Linux, and cover technologies such as wireless networking and
flash memory. User space drivers are discussed towards the end of the
book.
This book is generally up to date as of the 2.6.23/2.6.24 kernel versions. Most code listings in this book have been tested on a 2.6.23 kernel. If you are using a later version, look at Linux websites such as lwn.net to learn about the kernel changes since 2.6.23/24.
Source code, function names, and shell commands, are written like
this.
The shell prompt used
is bash>. Filename
are written in italics like
this. Italics are also used to introduce new terms.
Some chapters modify original kernel source files while
implementing code examples. To clearly point out the changes, newly
inserted code lines are prefixed with '+', and any deleted code
lines with '-'.
Sometimes, for simplicity, the book uses
generic references. So if the text points you to
the arch/your-arch/ directory, it should be
translated for example, to arch/i386/ if you
are compiling the kernel for
the x86 architecture. Similarly, any mention of
the include/asm-your-arch/ directory should be
read as include/asm-arm/ if you are, for
instance, building the kernel for the ARM
architecture. The '*' symbol and 'X' are
occasionally used as wild card characters in
filenames. So, if a chapter asks you to look
at include/linux/time*.h, look at the header
files, time.h,
timer.h, times.h and timex.h,
residing in the include/linux/ directory. If a
section talks about /dev/input/eventX
or /sys/devices/platform/i8042/serioX/, X
is the interface number that the kernel assigns to
your device in the context of your system
configuration.
The 'à'
symbol is sometimes inserted between command or kernel
output to attach explanations.
Simple regular expressions are
occasionally used to compactly list function
prototypes. For example, the section "Direct
Memory Access" in Chapter 10, "Peripheral
Component Interconnect" refers to
pci_[map|unmap|dma_sync]_single() instead of
explicitly citing pci_map_single(), pci_umap_single(),
and pci_dma_sync_single().
Several chapters refer you to user space configuration
files. For example, the section that describes the
boot process opens
/etc/rc.sysinit, while the chapter that
discusses Bluetooth opens /etc/bluetooth/pin.
The exact names and locations of such files might,
however, vary according to the Linux distribution you
use.
First, I raise my hat to my editors at Prentice Hall: Debra Williams
Cauley, Anne Goebel, and Keith Cline. Without their supporting work,
this book would not have materialized. I thank Mark Taub for his
interest in this project and for initiating it.
Several sources have contributed to my learning in the past decade:
the many teammates with whom I worked on Linux projects, the mighty
kernel sources, mailing lists, and the Internet. All these have played
a part in helping me write this book.
Martin Streicher of Linux Magazine changed me from a full-time
coder to a spare-time writer when he offered me the magazine's
"Gearheads" kernel column. I gratefully acknowledge the many lessons
in technical writing that I've learned from him.
I owe a special debt of gratitude to my technical reviewers. Vamsi
Krishna patiently read through each chapter of the manuscript. His
numerous suggestions have made this a better book. Jim Lieb provided
valuable feedback on several chapters. Arnold Robbins reviewed the
first few chapters and provided insightful comments.
Finally, I thank my parents and my wife for their love and
support. And thanks to my baby daughter for constantly reminding me to
spend cycles on the book by her wobbly walk that bears an uncanny
resemblance to that of a penguin.