








Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
We'll intro- duce new ideas gradually, starting off with very simple drivers and building upon them; every new concept will be accompanied by ...
Typology: Schemes and Mind Maps
1 / 14
This page cannot be seen from the preview
Don't miss anything!









As the popularity of the Linux system continues to grow, the interest in writing Linux device drivers steadily increases. Most of Linux is independent of the hard- ware it runs on, and most users can be (happily) unaware of hardware issues. But, for each piece of hardware supported by Linux, somebody somewhere has written a driver to make it work with the system. Without device drivers, there is no func- tioning system.
Device drivers take on a special role in the Linux kernel. They are distinct “black boxes” that make a particular piece of hardware respond to a well-defined internal programming interface; they hide completely the details of how the device works. User activities are performed by means of a set of standardized calls that are inde- pendent of the specific driver; mapping those calls to device-specific operations that act on real hardware is then the role of the device driver. This programming interface is such that drivers can be built separately from the rest of the kernel, and “plugged in” at runtime when needed. This modularity makes Linux drivers easy to write, to the point that there are now hundreds of them available.
There are a number of reasons to be interested in the writing of Linux device drivers. The rate at which new hardware becomes available (and obsolete!) alone guarantees that driver writers will be busy for the foreseeable future. Individuals may need to know about drivers in order to gain access to a particular device that is of interest to them. Hardware vendors, by making a Linux driver available for their products, can add the large and growing Linux user base to their potential markets. And the open source nature of the Linux system means that if the driver writer wishes, the source to a driver can be quickly disseminated to millions of users.
1
Chapter 1: An Introduction to Device Drivers
This book will teach you how to write your own drivers and how to hack around in related parts of the kernel. We have taken a device-independent approach; the programming techniques and interfaces are presented, whenever possible, without being tied to any specific device. Each driver is different; as a driver writer, you will need to understand your specific device well. But most of the principles and basic techniques are the same for all drivers. This book cannot teach you about your device, but it will give you a handle on the background you need to make your device work.
As you learn to write drivers, you will find out a lot about the Linux kernel in gen- eral; this may help you understand how your machine works and why things aren’t always as fast as you expect or don’t do quite what you want. We’ll intro- duce new ideas gradually, starting off with very simple drivers and building upon them; every new concept will be accompanied by sample code that doesn’t need special hardware to be tested.
This chapter doesn’t actually get into writing code. However, we introduce some background concepts about the Linux kernel that you’ll be glad you know later, when we do launch into programming.
The Role of the Device Driver
As a programmer, you will be able to make your own choices about your driver, choosing an acceptable trade-off between the programming time required and the flexibility of the result. Though it may appear strange to say that a driver is “flexi- ble,” we like this word because it emphasizes that the role of a device driver is providing mechanism , not policy.
The distinction between mechanism and policy is one of the best ideas behind the Unix design. Most programming problems can indeed be split into two parts: “what capabilities are to be provided” (the mechanism) and “how those capabili- ties can be used” (the policy). If the two issues are addressed by different parts of the program, or even by different programs altogether, the software package is much easier to develop and to adapt to particular needs.
For example, Unix management of the graphic display is split between the X server, which knows the hardware and offers a unified interface to user programs, and the window and session managers, which implement a particular policy with- out knowing anything about the hardware. People can use the same window man- ager on different hardware, and different users can run different configurations on the same workstation. Even completely different desktop environments, such as KDE and GNOME, can coexist on the same system. Another example is the lay- ered structure of TCP/IP networking: the operating system offers the socket abstraction, which implements no policy regarding the data to be transferred, while different servers are in charge of the services (and their associated policies).
2
Chapter 1: An Introduction to Device Drivers
Many device drivers, indeed, are released together with user programs to help with configuration and access to the target device. Those programs can range from simple utilities to complete graphical applications. Examples include the tunelp program, which adjusts how the parallel port printer driver operates, and the graphical cardctl utility that is part of the PCMCIA driver package. Often a client library is provided as well, which provides capabilities that do not need to be implemented as part of the driver itself.
The scope of this book is the kernel, so we’ll try not to deal with policy issues, or with application programs or support libraries. Sometimes we’ll talk about different policies and how to support them, but we won’t go into much detail about pro- grams using the device or the policies they enforce. You should understand, how- ever, that user programs are an integral part of a software package and that even policy-free packages are distributed with configuration files that apply a default behavior to the underlying mechanisms.
Splitting the Kernel
In a Unix system, several concurrent pr ocesses attend to different tasks. Each pro- cess asks for system resources, be it computing power, memory, network connec- tivity, or some other resource. The ker nel is the big chunk of executable code in charge of handling all such requests. Though the distinction between the different kernel tasks isn’t always clearly marked, the kernel’s role can be split, as shown in Figure 1-1, into the following parts:
Pr ocess management The kernel is in charge of creating and destroying processes and handling their connection to the outside world (input and output). Communication among different processes (through signals, pipes, or interprocess communica- tion primitives) is basic to the overall system functionality and is also handled by the kernel. In addition, the scheduler, which controls how processes share the CPU, is part of process management. More generally, the kernel’s process management activity implements the abstraction of several processes on top of a single CPU or a few of them.
Memory management The computer’s memory is a major resource, and the policy used to deal with it is a critical one for system performance. The kernel builds up a virtual addressing space for any and all processes on top of the limited available resources. The different parts of the kernel interact with the memory-manage- ment subsystem through a set of function calls, ranging from the simple mal- loc / fr ee pair to much more exotic functionalities.
Filesystems Unix is heavily based on the filesystem concept; almost everything in Unix can be treated as a file. The kernel builds a structured filesystem on top of unstructured hardware, and the resulting file abstraction is heavily used
4
features implemented as modules
Process management
Memory management
Filesystems Device control
Networking
Arch- dependent Code
Memory manager
Character devices
Network subsystem
CPU Memory
Concurrency, multitasking
Virtual memory
Files and dirs: the VFS
Kernel subsystems
Features implemented
Software support
Hardware
Block devices IF drivers
File system types
Ttys & device access Connectivity
Disks & CDs Consoles, etc.
Network interfaces
Figur e 1-1. A split view of the kernel
throughout the whole system. In addition, Linux supports multiple filesystem types, that is, different ways of organizing data on the physical medium. For example, diskettes may be formatted with either the Linux-standard ext filesystem or with the commonly used FAT filesystem.
Device control Almost every system operation eventually maps to a physical device. With the exception of the processor, memory, and a very few other entities, any and all device control operations are performed by code that is specific to the device being addressed. That code is called a device driver. The kernel must have embedded in it a device driver for every peripheral present on a system, from the hard drive to the keyboard and the tape streamer. This aspect of the ker- nel’s functions is our primary interest in this book.
Splitting the Kernel
5
text console ( /dev/console ) and the serial ports ( /dev/ttyS0 and friends) are examples of char devices, as they are well represented by the stream abstrac- tion. Char devices are accessed by means of filesystem nodes, such as /dev/tty1 and /dev/lp0. The only relevant difference between a char device and a regular file is that you can always move back and forth in the regular file, whereas most char devices are just data channels, which you can only access sequentially. There exist, nonetheless, char devices that look like data areas, and you can move back and forth in them; for instance, this usually applies to frame grabbers, where the applications can access the whole acquired image using mmap or lseek.
Block devices Like char devices, block devices are accessed by filesystem nodes in the /dev directory. A block device is something that can host a filesystem, such as a disk. In most Unix systems, a block device can be accessed only as multiples of a block, where a block is usually one kilobyte of data or another power of
Network interfaces Any network transaction is made through an interface, that is, a device that is able to exchange data with other hosts. Usually, an interface is a hardware device, but it might also be a pure software device, like the loopback inter- face. A network interface is in charge of sending and receiving data packets, driven by the network subsystem of the kernel, without knowing how individ- ual transactions map to the actual packets being transmitted. Though both Tel- net and FTP connections are stream oriented, they transmit using the same device; the device doesn’t see the individual streams, but only the data pack- ets.
Not being a stream-oriented device, a network interface isn’t easily mapped to a node in the filesystem, as /dev/tty1 is. The Unix way to provide access to interfaces is still by assigning a unique name to them (such as eth0), but that name doesn’t have a corresponding entry in the filesystem. Communication between the kernel and a network device driver is completely different from that used with char and block drivers. Instead of read and write , the kernel calls functions related to packet transmission.
Other classes of driver modules exist in Linux. The modules in each class exploit public services the kernel offers to deal with specific types of devices. Therefore,
Classes of Devices and Modules
7
Chapter 1: An Introduction to Device Drivers
one can talk of universal serial bus (USB) modules, serial modules, and so on. The most common nonstandard class of devices is that of SCSI*^ drivers. Although every peripheral connected to the SCSI bus appears in /dev as either a char device or a block device, the internal organization of the software is different.
Just as network interface cards provide the network subsystem with hardware- related functionality, so a SCSI controller provides the SCSI subsystem with access to the actual interface cable. SCSI is a communication protocol between the com- puter and peripheral devices, and every SCSI device responds to the same proto- col, independently of what controller board is plugged into the computer. The Linux kernel therefore embeds a SCSI implementation (i.e., the mapping of file operations to the SCSI communication protocol). The driver writer has to imple- ment the mapping between the SCSI abstraction and the physical cable. This map- ping depends on the SCSI controller and is independent of the devices attached to the SCSI cable.
Other classes of device drivers have been added to the kernel in recent times, including USB drivers, FireWire drivers, and I2O drivers. In the same way that they handled SCSI drivers, kernel developers collected class-wide features and exported them to driver implementers to avoid duplicating work and bugs, thus simplifying and strengthening the process of writing such drivers.
In addition to device drivers, other functionalities, both hardware and software, are modularized in the kernel. Beyond device drivers, filesystems are perhaps the most important class of modules in the Linux system. A filesystem type determines how information is organized on a block device in order to represent a tree of directories and files. Such an entity is not a device driver, in that there’s no explicit device associated with the way the information is laid down; the filesystem type is instead a software driver, because it maps the low-level data structures to higher- level data structures. It is the filesystem that determines how long a filename can be and what information about each file is stored in a directory entry. The filesys- tem module must implement the lowest level of the system calls that access direc- tories and files, by mapping filenames and paths (as well as other information, such as access modes) to data structures stored in data blocks. Such an interface is completely independent of the actual data transfer to and from the disk (or other medium), which is accomplished by a block device driver.
If you think of how strongly a Unix system depends on the underlying filesystem, you’ll realize that such a software concept is vital to system operation. The ability to decode filesystem information stays at the lowest level of the kernel hierarchy and is of utmost importance; even if you write a block driver for your new CD- ROM, it is useless if you are not able to run ls or cp on the data it hosts. Linux supports the concept of a filesystem module, whose software interface declares the different operations that can be performed on a filesystem inode, directory,
8
Chapter 1: An Introduction to Device Drivers
data. Such errors can compromise the entire system and must be avoided. Fortu- nately, avoiding these errors is usually relatively easy in the device driver context, in which the interface to the user is narrowly defined and highly controlled.
Some other general security ideas are worth keeping in mind. Any input received from user processes should be treated with great suspicion; never trust it unless you can verify it. Be careful with uninitialized memory; any memory obtained from the kernel should be zeroed or otherwise initialized before being made avail- able to a user process or device. Otherwise, information leakage could result. If your device interprets data sent to it, be sure the user cannot send anything that could compromise the system. Finally, think about the possible effect of device operations; if there are specific operations (e.g., reloading the firmware on an adapter board, formatting a disk) that could affect the system, those operations should probably be restricted to privileged users.
Be careful, also, when receiving software from third parties, especially when the kernel is concerned: because everybody has access to the source code, everybody can break and recompile things. Although you can usually trust precompiled ker- nels found in your distribution, you should avoid running kernels compiled by an untrusted friend—if you wouldn’t run a precompiled binary as root, then you’d better not run a precompiled kernel. For example, a maliciously modified kernel could allow anyone to load a module, thus opening an unexpected back door via cr eate_module.
Note that the Linux kernel can be compiled to have no module support whatso- ever, thus closing any related security holes. In this case, of course, all needed drivers must be built directly into the kernel itself. It is also possible, with 2.2 and later kernels, to disable the loading of kernel modules after system boot, via the capability mechanism.
Version Numbering
Before digging into programming, we’d like to comment on the version number- ing scheme used in Linux and which versions are covered by this book.
First of all, note that every software package used in a Linux system has its own release number, and there are often interdependencies across them: you need a particular version of one package to run a particular version of another package. The creators of Linux distributions usually handle the messy problem of matching packages, and the user who installs from a prepackaged distribution doesn’t need to deal with version numbers. Those who replace and upgrade system software, on the other hand, are on their own. Fortunately, almost all modern distributions support the upgrade of single packages by checking interpackage dependencies; the distribution’s package manager generally will not allow an upgrade until the dependencies are satisfied.
10
To run the examples we introduce during the discussion, you won’t need particu- lar versions of any tool but the kernel; any recent Linux distribution can be used to run our examples. We won’t detail specific requirements, because the file Docu- mentation/Changes in your kernel sources is the best source of such information if you experience any problem.
As far as the kernel is concerned, the even-numbered kernel versions (i.e., 2.2. x and 2.4. x ) are the stable ones that are intended for general distribution. The odd versions (such as 2.3. x ), on the contrary, are development snapshots and are quite ephemeral; the latest of them represents the current status of development, but becomes obsolete in a few days or so.
This book covers versions 2.0 through 2.4 of the kernel. Our focus has been to show all the features available to device driver writers in 2.4, the current version at the time we are writing. We also try to cover 2.2 thoroughly, in those areas where the features differ between 2.2 and 2.4. We also note features that are not available in 2.0, and offer workarounds where space permits. In general, the code we show is designed to compile and run on a wide range of kernel versions; in particular, it has all been tested with version 2.4.4, and, where applicable, with 2.2.18 and 2.0.38 as well.
This text doesn’t talk specifically about odd-numbered kernel versions. General users will never have a reason to run development kernels. Developers experi- menting with new features, however, will want to be running the latest develop- ment release. They will usually keep upgrading to the most recent version to pick up bug fixes and new implementations of features. Note, however, that there’s no guarantee on experimental kernels,*^ and nobody will help you if you have prob- lems due to a bug in a noncurrent odd-numbered kernel. Those who run odd- numbered versions of the kernel are usually skilled enough to dig in the code without the need for a textbook, which is another reason why we don’t talk about development kernels here.
Another feature of Linux is that it is a platform-independent operating system, not just “a Unix clone for PC clones” anymore: it is successfully being used with Alpha and SPARC processors, 68000 and PowerPC platforms, as well as a few more. This book is platform independent as far as possible, and all the code samples have been tested on several platforms, such as the PC brands, Alpha, ARM, IA-64, M68k, PowerPC, SPARC, SPARC64, and VR41xx (MIPS). Because the code has been tested on both 32-bit and 64-bit processors, it should compile and run on all other plat- forms. As you might expect, the code samples that rely on particular hardware don’t work on all the supported platforms, but this is always stated in the source code.
Version Numbering
11
When sample programs include parts of the kernel code, the GPL applies: the comments accompanying source code are very clear about that. This only happens for a pair of source files that are very minor to the topic of this book.
Joining the Kernel Development
Community
As you get into writing modules for the Linux kernel, you become part of a larger community of developers. Within that community, you can find not only people engaged in similar work, but also a group of highly committed engineers working toward making Linux a better system. These people can be a source of help, of ideas, and of critical review as well—they will be the first people you will likely turn to when you are looking for testers for a new driver.
The central gathering point for Linux kernel developers is the linux-ker nel mailing list. All major kernel developers, from Linus Torvalds on down, subscribe to this list. Please note that the list is not for the faint of heart: traffic as of this writing can run up to 200 messages per day or more. Nonetheless, following this list is essen- tial for those who are interested in kernel development; it also can be a top-qual- ity resource for those in need of kernel development help.
To join the linux-kernel list, follow the instructions found in the linux-kernel mail- ing list FAQ: http://www.tux.org/lkml. Please read the rest of the FAQ while you are at it; there is a great deal of useful information there. Linux kernel developers are busy people, and they are much more inclined to help people who have clearly done their homework first.
Overview of the Book
From here on, we enter the world of kernel programming. Chapter 2 introduces modularization, explaining the secrets of the art and showing the code for running modules. Chapter 3 talks about char drivers and shows the complete code for a memory-based device driver that can be read and written for fun. Using memory as the hardware base for the device allows anyone to run the sample code without the need to acquire special hardware.
Debugging techniques are vital tools for the programmer and are introduced in Chapter 4. Then, with our new debugging skills, we move to advanced features of char drivers, such as blocking operations, the use of select , and the important ioctl call; these topics are the subject of Chapter 5.
Before dealing with hardware management, we dissect a few more of the kernel’s software interfaces: Chapter 6 shows how time is managed in the kernel, and Chapter 7 explains memory allocation.
Overview of the Book
13
Chapter 1: An Introduction to Device Drivers
Next we focus on hardware. Chapter 8 describes the management of I/O ports and memory buffers that live on the device; after that comes interrupt handling, in Chapter 9. Unfortunately, not everyone will be able to run the sample code for these chapters, because some hardware support is actually needed to test the soft- ware interface to interrupts. We’ve tried our best to keep required hardware sup- port to a minimum, but you still need to put your hands on the soldering iron to build your hardware “device.” The device is a single jumper wire that plugs into the parallel port, so we hope this is not a problem.
Chapter 10 offers some additional suggestions about writing kernel software and about portability issues.
In the second part of this book, we get more ambitious; thus, Chapter 11 starts over with modularization issues, going deeper into the topic.
Chapter 12 then describes how block drivers are implemented, outlining the aspects that differentiate them from char drivers. Following that, Chapter 13 explains what we left out from the previous treatment of memory management: mmap and direct memory access (DMA). At this point, everything about char and block drivers has been introduced.
The third main class of drivers is introduced next. Chapter 14 talks in some detail about network interfaces and dissects the code of the sample network driver.
A few features of device drivers depend directly on the interface bus where the peripheral fits, so Chapter 15 provides an overview of the main features of the bus implementations most frequently found nowadays, with a special focus on PCI and USB support offered in the kernel.
Finally, Chapter 16 is a tour of the kernel source: it is meant to be a starting point for people who want to understand the overall design, but who may be scared by the huge amount of source code that makes up Linux.
14