Write a Linux Hardware Device Driver
Learn to write and install a Linux device driver to control a hardware card. We detail what functions need to be written, outline the supporting kernel functions that are available, explain how to initialize the driver and how memory is requested and allocated in an efficient manner, and provide a ``real'' driver example.
Introduction to Linux
Linux is a 32-bit multitasking, multimedia operating system with complete source code, developed b y the free software community on the Internet. Linux is a clone of the Unix operating system that runs on Intel 80386/80486/Pentium computers. It supports a wide range of software, from TEX to the X Window System, to the GNU C/C++ compiler, to TCP/IP. The Linux system is mostly compatible at the source level with a number of Unix standards including IEEE POSIX.1, System V, and BSD. Linux also provides a complete Unix programming environment, including standard libraries, programming tools, compilers, and debuggers.
A device driver consists of a set of routines that control a peripheral device attached to a workstation. The operating system normally provides a uniform interface to all peripheral devices. Linux and Unix present peripheral devices at a sufficiently high level of abstraction by observing that a large proportion of I/O devices can be represented as a sequence of bytes. Linux and Unix use the file--which is a well understood data structure for handling byte sequences--to represent I/O devices.
Linux I/O Subsystem
Figure 1 shows the Linux architecture in the most general terms. Here, the kernel is shown wrapped around the hardware to depict that it is the software component that has direct access to--and control over--the system hardware, including the processor, primary memory, and I/O devices.
Linux System Calls
The kernel is not a separate task under Linux. It is as if each process has a copy of the kernel. When a user process executes a system call, it does not transfer control to another process, but changes its execution mode from user to kernel mode. In kernel mode, while executing the system call, the process has access to the kernel address space, and through supporting functions , it has access to the address space of the user executing the call.
Figure 3 depicts the I/O Subsystem. The Linux kernel implements a device-independent I/O system that serves all devices. A device driver provides the I/O system with a standard interface to the hardware, hiding the unique characteristics of the hardware device from the user to the greatest extent possible.
Listing 1 illustrates a user program that employs some basic system calls to read characters from a device into a buffer. When a system call is requested, the kernel transfers control to the appropriate device driver routine that executes on behalf of the calling user process (as shown previously with Figure 3).
All devices look like files on a Linux system. In fact, the
user-level interface to a device is called a ‘special file
These special files (often called device nodes) reside in the
This example indicates that: ‘lp0’ is a character type device (the first letter of the file mode field is ‘c’), the major number is 6, and minor device number 0 is assigned to the device.
Major device numbers are used by the Linux system to map I/O requests to the driver code, thereby deciding which device driver to execute, when a user reads from or writes to the special file. The minor numbers are entirely under the control of the driver writer, and usually refer to ‘sub-devices’ of the device. These sub-devices may be separate units attached to a controller. Thus, a disk device driver may, for example, communicate with a hardware controller (the device) which has several disk drives (sub-devices) attached.
Figure 4 outlines the flow of execution of a system call within th e Linux operating system.
A device driver is a collection of subroutines and data within the kernel that constitutes the software interface to an I/O device. When the kernel recognizes that a particular action is required from the device, it calls the appropriate driver routine, which passes control from the user process to the driver routine. Control is returned to the user process when the driver routine has completed. A device driver may be shared simultaneously by user applications and must be protected to ensure its own integrity.
Figure 5 shows the relationship between device driver and the Linux system.
A device driver provides the following features:
Character and Block Device Drivers
Character and block device drivers are the two main types of peripheral drivers. A disk drive is an example of a block device, whereas, terminals and line printers are examples of character devices.
A block device driver is accessed by user programs through a
system buffer that acts as a data cache. Specific allocation and
memory management routines are not necessary as the system
transfers the data to/from the device. Character device drivers
communicate directly with the user program, as there is no
buffering performed. Linux transfers control to the appropriate
device driver when a user program requests a data transfer
between a section of its memory and a device. The device driver
is responsible for transferring the d
ata. Within Linux, the
source for character drivers is kept in the
Kernel Programming Environment
A Linux user process executes in a space isolated from critical system data and other user processes. This protected environment provides security to protect the process from mistakes in other processes. By contrast, a device driver executes in kernel mode, which places few limits on its freedom of action. The driver is assumed to be correct and responsible. A driver has to be part of the kernel in order to service interrupts and access device hardware. A driver should process interrupts efficiently to preserve the scheduler’s ability to balance the demands on the system. It should also use system buffers responsibly to avoid degrading system performance.
A device driver contains both interrupt and synchronous sections. The interrupt section deals with re al-time events and is driven by interrupts from devices. The synchronous section, which comprises the remainder of the driver, only executes when the process which it serves is also active. When a device requests some software service, it generates an ``interrupt.'' The interrupt handler must determine the cause of the interrupt and take appropriate action.
A Linux process might have to wait for an event to occur
before it can proceed. For example, a process might wait for
requested information to be written to a hardware device before
continuing. One way that processes can coordinate their actions
with events is through
Special care must be taken if two or more processes, such as
the synchronous and interrupt portions of a device driver, share
common data. The shared data area must be treated as a critical
section. The critical section is protected by ensuring that
processes only have mutually exclusive access to the shared data.
Mutually exclusive access to a critical section can be
implemented by using the Linux kernel routines
cli() Critical Section Operations sti ()
Virtual File system Switch (VFS)
The principal interface between a device driver and the rest of the Linux kernel comprises a set of standard entry points and driver-specific data structures (see Figure 6 ).
illustrates how the
entry points are registered with the Virtual File system Switch
The table below contains most of the common supporting functions available for writing device drivers. See also the Kernel Hackers' Guide [John93] for a more detailed ex planation:
The name of the driver should be a short string. Throughout
this article we have used "xxx" as our device name. For
instance, the parallel (printer) device is the ``lp'' device,
the floppies are the ``fd'' devices, and the SCSI disks are the
``sd'' devices. To avoid name space confusion, the entry point
names are formed by concatenating this unique driver prefix with
a generic name that describes the routine. For instance,
Accessing Hardware Memory
A Linux user process can not access physical memory directly. The memory management sc heme--which is a demand paged virtual memory system--means that each process has its own address space (user virtual address space) that begins at virtual location zero. The kernel has its own distinct address space known as the system virtual address space.
The device driver copies data between the kernel'’s address
space and the user program'’s address space whenever the user
The transfer of data betwee
n the memory accessible to the
kernel and the device itself is machine-dependent. Some machines
require that the CPU execute special I/O instructions to move
data between a device register and addressable memory--often
called direct memory access (DMA). Another scheme, known as
memory mapped I/O, implements the device interface as one or more
locations in the memory address space. The most common method
uses I/O instructions, provided by the system to allow drivers
access the data in a general way. Linux provides
unsigned char inb(int port) outb(char data, int port)
Writing a Character Device Driver
shows a sample
Listing 5 presents an example of a complete device driver (for the bus mouse). The source listing contains the code for a typical bus mouse driver, such as the Logitec bus mouse or the Microsoft bus mouse.
Device Driver Initialization
In order that the device driver is correctly initialized when
the operating system is booted, the
mem_start = xxx_init(mem_start);
and resave the file back to disk.
Installing the Driver in the Kernel
A character device driver has to be archived into the
The following steps are required to recompile the Linux kernel:
Device File Creation
In order to access the device using system calls, a special
file is created. The driver files are normally stored in
In this article, we have detailed how to write a hardware character device driver for the Linux operating system. We have outlined how to access hardware memory. We have also presented the kernel programming environment, as well as the supporting functions available to write a device driver. A number of worked examples were also presented to aid the programmer in developing his/her own device driver(s).
[Bach86] Bach, M; The Design of the Unix Operating System ; Englewood Cliffs, NJ: Prentice Hall, 1986.
[Swit93] Robert Switzer, University of Gottingen, Germany; Operating Systems, A Practical Approach ; Prentice Hall 1993.
[YCI94] The Linux Bible, The GNU Testament-2nd Edition ; Yggdrasil Computing Incorporated, Version 2.1.1, 10 July 1994.