5.3.
3 Miniport Drivers
A Miniport driver is an add-on to a class driver that supports miniport drivers. It is
used so the miniport driver does not have to implement all of the functions required of a
driver for that class. The class driver provides the basic class functionality for the miniport
driver. A class driver is a driver that supports a group of devices of common functionality,
such as all HID devices or all network devices.
Miniport drivers are also called miniclass drivers or minidrivers, and are supported in
the Windows NT (2000) family, namely Windows 7 / Vista / Server 2008 / Server 2003 / XP
/ 2000 / NT 4.0.
Figure 5.4 Miniport Drivers
Windows 7/Vista/Server 2008/Server 2003/XP/2000/NT 4.0 provide several driver
classes (called ports) that handle the common functionality of their class. It is then up to the
user to add only the functionality that has to do with the inner workings of the specific
hardware. The NDIS miniport driver is one example of such a driver. The NDIS miniport
framework is used to create network drivers that hook up to NT's communication stacks, and
are therefore accessible to common communication calls used by applications. The Windows
NT kernel provides drivers for the various communication stacks and other code that is
common to communication cards. Due to the NDIS framework, the network card developer
does not have to write all of this code, only the code that is specific to the network card he is
developing.
5.4 Linux Device Drivers
Linux device drivers are based on the classic UNIX device driver mode. In addition,
Linux introduces some new characteristics. Under Linux, a block device can be accessed like
129
a character device, as in Unix, but also has a block-oriented interface that is invisible to the
user or application.
Traditionally, under Unix, device drivers are linked with the kernel, and the system is brought
down and restarted after installing a new driver. Linux introduces the concept of a
dynamically loadable driver called a module. Linux modules can be loaded or removed
dynamically without requiring the system to be shut down. A Linux driver can be written so
that it is statically linked or written in a modular form that allows it to be dynamically loaded.
This makes Linux memory usage very efficient because modules can be written to probe for
their own hardware and unload themselves if they cannot find the hardware they are looking
for.
Like UNIX device drivers, Linux device drivers are either layered or monolithic drivers.
5.4.1 The Entry Point of the Driver
Every device driver must have one main entry point, like the main() function in a C console
application. This entry point is called DriverEntry() in Windows and init_module() in Linux.
When the operating system loads the device driver, this driver entry procedure is called.
There is some global initialization that every driver needs to perform only once when
it is loaded for the first time. This global initialization is the responsibility of
the DriverEntry()/init_module() routine. The entry function also registers which driver
callbacks will be called by the operating system. These driver callbacks are operating system
requests for services from the driver. In Windows, these callbacks are called dispatch
routines, and in Linux they are called file operations. Each registered callback is called by the
operating system as a result of some criteria, such as disconnection of hardware, for example.
Operating systems differ in the ways they associate a device with a specific driver.
In Windows, the hardware–driver association is performed via an INF file, which
registers the device to work with the driver. This association is performed before
the DriverEntry() routine is called. The operating system recognizes the device, checks its
database to identify which INF file is associated with the device, and according to the INF
file, calls the driver's entry point.
In Linux, the hardware–driver association is defined in the driver's init_module()
routine. This routine includes a callback that indicates which hardware the driver is
designated to handle. The operating system calls the driver's entry point, based on the
definition in the code.
Communication between a user-mode application and the driver that drives the
hardware, is implemented differently for each operating system, using the the custom OS
Application Programming Interfaces (APIs).
On Windows, Windows CE, and Linux, the application can use the OS file-access
API to open a handle to the driver (e.g., using the Windows CreateFile() function or using the
Linux open() function), and then read and write from/to the device by passing the handle to
the relevant OS file-access functions (e.g., the Windows ReadFile() and WriteFile()
functions, or the Linux read() and write() functions).
130
The application sends requests to the driver via I/O control (IOCTL) calls, using the custom
OS APIs provided for this purpose (e.g., the Windows DeviceIoControl() function, or the
Linux ioctl() function).
The data passed between the driver and the application via the IOCTL calls is encapsulated
using custom OS mechanisms. For example, on Windows the data is passed via an I/O
Request Packet (IRP) structure, and is encapsulated by the I/O Manager.
5.5 Classes of Devices and Modules
The Linux way of looking at devices distinguishes between three fundamental device types.
Each module usually implements one of these types, and thus is classifiable as a char module,
a block module, or a network module. This division of modules into different types, or
classes, is not a rigid one; the programmer can choose to build huge modules implementing
different drivers in a single chunk of code. Good programmers, nonetheless, usually create a
different module for each new functionality they implement, because decomposition is a key
element of scalability and extendability. The three classes are:
5.5.1 Character devices
A character (char) device is one that can be accessed as a stream of bytes (like a file); a
char driver is in charge of implementing this behavior. Such a driver usually implements at
least the open, close, read, and write system calls. The 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 abstraction. 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.
Character devices transmit the data character by characters, like a mouse or a
keyboard.
A Character ('c') Device is one with which the Driver communicates by sending and
receiving single characters (bytes, octets).
Character driver has only one position current one. It can't move back and forth.
Block driver navigates back and forth between any location on media.
The read() and write() calls do not return until the operation is complete.
Character devices are those for which no buffering is performed, and
5.5.2 Block devices
Like char devices, block devices are accessed by filesystem nodes in the /dev directory. A
block device is a device (e.g., a disk) that can host a filesystem. In most Unix systems, a
block device can only handle I/O operations that transfer one or more whole blocks, which
are usually 512 bytes (or a larger power of two) bytes in length. Linux, instead, allows the
application to read and write a block device like a char device—it permits the transfer of any
131
number of bytes at a time. As a result, block and char devices differ only in the way data is
managed internally by the kernel, and thus in the kernel/driver software interface. Like a char
device, each block device is accessed through a filesystem node, and the difference between
them is transparent to the user. Block drivers have a completely different interface to the
kernel than char drivers.
These devices transfer unit of data storage called a block, USB drives, hard drives,
and CD ROMs
A Block ('b') Device is one with which the Driver communicates by sending entire
blocks of data. Examples for Character Devices: serial ports, parallel ports, sounds
cards.
A block driver provides access to devices that transfer randomly accessible data in
fixed-size blocks—disk drives, primarily.
The Linux kernel sees block devices as being fundamentally different from char
devices; as a result, block drivers have a distinct interface and their own particular
challenges.
block devices are those which are accessed through a cache.
Block devices must be random access, but character devices are not required to be,
though some are. Filesystems can only be mounted if they are on block devices.
5.5.3 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 interface. A network interface is in charge of
sending and receiving data packets, driven by the network subsystem of the kernel, without
knowing how individual transactions map to the actual packets being transmitted. Many
network connections (especially those using TCP) are stream-oriented, but network devices
are, usually, designed around the transmission and receipt of packets. A network driver
knows nothing about individual connections; it only handles packets. 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. There are other ways of classifying driver
modules that are orthogonal to the above device types. In general, some types of drivers work
with additional layers of kernel support functions for a given type of device. For example,
one can talk of universal serial bus (USB) modules, serial modules, SCSI modules, and so on.
Every USB device is driven by a USB module that works with the USB subsystem, but the
device itself shows up in the system as a char device (a USB serial port, say), a block device
(a USB memory card reader), or a network device (a USB Ethernet interface). Other classes
of device drivers have been added to the kernel in recent times, including FireWire drivers
132
and I2O drivers. In the same way that they handled USB and 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.
5.6 Security Issues
Security is an increasingly important concern in modern times. We will discuss
security-related issues as they come up throughout the book. There are a few general
concepts, however, that are worth mentioning now. Any security check in the system is
enforced by kernel code. If the kernel has security holes, then the system as a whole has
holes. In the official kernel distribution, only an authorized user can load modules; the system
call init_module checks if the invoking process is authorized to load a module into the kernel.
Thus, when running an official kernel, only the superuser, or an intruder who has succeeded
in becoming privileged, can exploit the power of privileged code. When possible, driver
writers should avoid encoding security policy in their code. Security is a policy issue that is
often best handled at higher levels within the kernel, under the control of the system
administrator. There are always exceptions, however.
5.7 Polling and Interrupts
Management of I/O devices is a very important part of the operating system - so
important and so varied that entire I/O subsystems are devoted to its operation. ( Consider the
range of devices on a modern computer, from mice, keyboards, disk drives, display adapters,
USB devices, network connections, audio I/O, printers, special devices for the handicapped,
and many special-purpose peripherals. )
I/O Subsystems must contend with two ( conflicting? ) trends: (1) The gravitation
towards standard interfaces for a wide range of devices, making it easier to add newly
developed devices to existing systems, and (2) the development of entirely new types
of devices, for which the existing standard interfaces are not always easy to apply.
Device drivers are modules that can be plugged into an OS to handle a particular
device or category of similar devices.
I/O Hardware
I/O devices can be roughly categorized as storage, communications, user-interface,
and other
Devices communicate with the computer via signals sent over wires or through the
air.
Devices connect with the computer via ports, e.g. a serial or parallel port.
A common set of wires connecting multiple devices is termed a bus.
o Buses include rigid protocols for the types of messages that can be sent across
the bus and the procedures for resolving contention issues.
o Figure 13.1 below illustrates three of the four bus types commonly found in a
modern PC:
133