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 I
2C 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.
C
hapter 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 systemsdepend 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 I
2C 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.
Kernel Version
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 bootprocess 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.
Acknowledgments
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.
This book is dedicated to the ten million visually challenged citizens of
India
. All author proceeds will go to their cause.
前言
(宋宝华译)
上世纪
90
年代末期,我们
IBM
的一群同事进行了一项将
Linux
移植到一种智能手表上的工作。目标设备看起来是一个微不足道的小系统,但是移植
Linux
的任务却相当艰巨。在当时,内核中还不存在
MTD
子系统,这意味着在文件系统能够运行于这种手表的
Flash
存储器之前,我们不得不从头开始开发存储设备驱动。而由于当时内核的输入事件驱动接口尚未诞生,手表的触摸屏与用户空间应用程序的接口也变得非常复杂。同样地,由于手表的
LCD
采用帧缓冲驱动方式工作地并不好,让
X Windows
运行在手表的
LCD
上也十分困难。另外,对于一块防水的
Linux
手表而言,如果你不能躺在浴缸里实时获得股票行情,这块手表还有什么用?几年前,
Linux
集成了蓝牙,而当时我们却花费了数月的时间来完成将一种商用蓝牙协议栈移植到手表上的工作,从而使得这种手表具备了
Internet
联网能力。蹩脚的能量管理系统让这种手表使用电池仅能运行几个小时,我们不得不提前就能量管理进行开创性的研究。那时候,
Linux
红外项目
Linux-infrared
仍然不稳定,为了让一个红外键盘能作为手表的输入,我们不得不小心伺候红外协议栈。最后,由于当时还没有可接受的应用于消费类电子的编译器发布版,我们也不得不亲自编译出编译器,并交叉编译出要用到的应用程序集。
时光飞逝,当年嗷嗷待抚的婴儿已经成长为一个健壮的少年,过去我们编写了成千上万行代码并耗时一年完成的任务若采用现在的内核,只需要几天的时间就可以完成。但是,为了成为一名能解决很多问题的高手级的内核工程师,您还是需要理解今天的
Linux
内核提供的难以想象的特性和便利。
关于本书
在
Linux
内核源代码树提供的各个子系统中,
drivers/
目录是其中最大的一个分支,它比其他子系统大数倍。而随着各种新技术的广泛应用,内核中驱动的开发工作还在稳步加速。最新的
Linux
支持多达
70
余种设备驱动家族。
这本书主要讲解
Linux
设备驱动开发技术。它覆盖了目前内核所支持的主要的设备类型,这其中包括当年我在开发
Linux-on-Watch
项目时错过的设备。在讲解每个设备驱动的时候,本书先介绍一下与该驱动相关的技术,接着给出一个实际例子,最后列出相关的内核源代码。在踏入
Linux
驱动领域之前,本书事先对内核以及
Linux 2.6
的重要特性进行了介绍,重点讲解了设备驱动编写者感兴趣的内核知识。
读者对象
这本书面向要在
Linux
内核上开发新设备驱动的中级程序员。为了阅读本书,您需要具备操作系统相关的基本知识,知道什么是系统调用并理解为什么在内核开发中需要关注并发问题。本书假定您已经下载了
Linux
,浏览过
Linux
内核源代码,并至少阅读过一些相关的文档。另外,您必须能非常熟练地使用
C
语言。
各章概述
本书前
4
章为您阅读本书的剩余部分打下基础,接下来的
16
章讨论不同类型的
Linux
设备驱动,之后的
1
章描述设备驱动的调试技术,倒数第
2
章讲解设备驱动的维护和提交相关事宜,最后
1
章给出了当您接到一个新设备驱动开发任务的时候,要首先查验的项目清单。
第
1
章带您走入
Linux
的世界,这一章将加速读者下载内核源代码、进行小小的代码修改并建立一个可启动的
Linux
内核映像的步伐。
第
2
章,《
Linux
内核概览》,将引导读者轻松地进入
Linux
的内部,讲解一些必要的内核概念。这一章首先讲述了内核的启动过程,接下来描述了与驱动开发相关的内核
API
,譬如内核定时器、并发管理以及内存分配等。
第
3
章,《
Linux
内核驱动相关
API
》,讲解了对驱动开发有用的一系列内核
API
。这一章首先介绍了内核线程(它提供了一种在内核空间运行后台任务的能力),接下来讲解了一系列的辅助
API
(如链表、工作队列、完成函数、通知链等)。这些辅助
API
能简化代码,剔除内核中的冗余并有助于内核的长期维护。
第
4
章,《
Linux
驱动开发基础》,为您打下驾驭
Linux
驱动开发艺术的基础。这一章呈现了
PC
兼容系统以及嵌入式系统中相关的设备和驱动的鸟瞰图,并讲解了中断处理和内核设备模型等驱动基本概念。
第
5
章,《字符设备驱动》,介绍了
Linux
字符设备驱动的体系结构。这一章引入了几个新概念,譬如轮询、异步通知和
I
/
O
控制等。由于本书中介绍的大多数设备都可以看作
“
超级
”
字符设备,所以这些概念也与接下来的章节密切相关。
第
6
章,《串口驱动》,讲解了内核串口设备驱动的层次结构。
第
7
章,《输入设备驱动》,讲解了内核中为键盘、鼠标和触摸屏控制器等输入设备提供的输入子系统。
第
8
章,《
I2C
设备驱动》,讲解了通过
I2C
总线或
SMBus
总线与系统连接的设备(如
EEPROM
)的驱动。同时,这一章也对
SPI
和
1-wire
等串行接口的驱动进行了介绍。
第
9
章,《
PCMCIA
和
Compact Flash
设备驱动》,分析了
PCMCIA
子系统,该章将教会您如何编写含
PCMCIA
或
Compact Flash
组件的设备的驱动。
第
10
章,《
PCI
设备驱动》,描述了内核对
PCI
以及其衍生总线设备的支持。
第
11
章,《
USB
设备驱动》,探讨了
USB
的体系结构并讲解了如何利用
Linux
内核
USB
子系统的
API
来完成
USB
设备驱动。
第
12
章,《视频设备驱动》,讲解了
Linux
视频子系统。该章分析了内核提供的帧缓冲结构的优点,并给出了帧缓冲设备驱动的编写方法。
第
13
章,《音频设备驱动》,描述了
Linux
音频子系统的架构,并给出了音频设备驱动的编写方法。
第
14
章,《块设备驱动》,集中于描述存储设备(如硬盘)的驱动。在这一章里,您也将学习到
Linux
块子系统所支持的几种不同的
I
/
O
调度策略。
第
15
章,《网卡驱动》,分析了网络设备驱动,您将学习到内核中与网络相关的数据结构以及网络设备驱动与协议栈接口的方法。
第
16
章,《无线设备驱动》,描述了各种无线网络设备的驱动,如蓝牙、红外、无线局域网
WiFi
和蜂窝通信等。
第
17
章,《
MTD
设备驱动》,讲解了如何让
Flash
在嵌入式系统上运行起来的方法,这一章以讲解
PC
上的固件枢纽(
Firmware Hub
,译者注:简称
FWH
)结束。
第
18
章,《嵌入式
Linux
》,步入嵌入式
Linux
的世界。该章对嵌入式设备中
Bootloader
、内核以及设备驱动等主要的固件组成进行了介绍,展示了
Linux
在嵌入式领域日益增长的受欢迎程度,很有可能您将把您从本书中学到的
Linux
驱动开发技能应用于嵌入式领域。
第
19
章,《用户空间的设备驱动》,讲解了如何在用户空间驱动各种设备。一些设备(尤其是那些重策略、轻性能的设备)更适合在用户空间被驱动。这一章也分析了
Linux
进程调度对用户空间设备驱动响应时间的影响。
第
20
章,《其他的设备和驱动》,描述了之前尚未论及的设备驱动
系统,如错误侦测和校验(
EDAC
)、火线接口以及
ACPI
等。
第
21
章,《设备驱动的调试》,讲解了
Linux
内核代码的各种调试方法。
在这一章中,您也将学习到跟踪(
trace
)工具、内核探测点(
probe
)、崩溃转储(
crash-dump
)和剖析器(
profiler
)的使用方法。
当您在调试
Linux
驱动的时候,请用这些技能武装自己。
第
22
章,《维护和提交》,给出了设备驱动软件开发生命周期的概况。
第
23
章,给出了当你开始进行一个新设备驱动开发工作时,应该查验的工作项目清单。整本书伴随着对
“
下一步怎么做
”
的发问而结束。
设备驱动中有时候需要以汇编语言实现一些代码片段,因此,本书的附录
A
《
Linux
汇编》介绍了
Linux
汇编编程的不同方面。
X86
系统上的一些设备直接或间接地依赖于
BIOS
,因此,本书的附录
B
《
Linux
和
BIOS
》讲解了
Linux
如何与
BIOS
交互。附录
C
《
Seq
文件》描述了
2.6
内核提供的
Seq
文件这种用于监控和最终数据点的辅助接口。
本书总体上根据设备和总线的复杂度进行组织,同时也结合了章节中互相依赖的客观情况。我们以讲解基本的设备类型(如字符设备、串口和输入设备)开始,紧接着介绍简单的串行总线(如
I2C
和
SMBus
),之后再介绍
PCMCIA
、
PCI
和
USB
等外部
I
/
O
总线,而由于视频、音频、块和网络设备通常通过这些总线与处理器连接,因此,在介绍完这些总线之后,对这些设备的驱动进行了介绍。书中接下来的部分面向嵌入式
Linux
,覆盖了无线网络和
Flash
存储器等技术。本书的最后讨论了用户空间的设备驱动。
内核版本
本书总体上紧跟
2.6.23
/2.6.24
内核版本,书中列出的大部分代码都在
2.6.23
上进行过测试。如果您正在使用更新的版本,请通过类似
lwn.net
的网站去了解内核自从
2.6.23/2.6.24
后进行了哪些更改。
本书网站
笔者特意建立了
elinuxdd.com
这个网站来提供与本书相关的更新、勘误等信息。
(在直译的基础上进行了部分意译;对于部分国内已经习以为常的英文名词,不做翻译;部分英文单词根据国内通用的称谓进行了替换,如第
3
章标题从《
kernel facilities
》翻译为《内核驱动相关
API
》,如果翻译为《内核基础设施》,读者将不知所云;一些地方根据上下文语意添加了少量语句。)
本文转自 21cnbao 51CTO博客,原文链接:http://blog.51cto.com/21cnbao/120002,如需转载请自行联系原作者