387
Contents Chapter 1 - NT Drivers TM 1.1 Kinds of Drivers in Windows NT Systems 1-2 1.2 NT Driver Design Goals 1-3 1.2.1 Portability 1-3 1.2.2 Configurability 1-4 1.2.3 Always Preemptible and Always Interruptible 1-5 1.2.4 Multiprocessor-safe 1-7 1.2.5 Object-based 1-8 1.2.6 Packet-driven I/O with Reusable IRPs 1-10 1.2.7 Supporting Asynchronous I/O 1-11 Figures - Chapter 1 TM Figure 1.1 Windows NT Component Overview 1-1

Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

  • Upload
    others

  • View
    21

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 1 - NT Drivers

TM1.1 Kinds of Drivers in Windows NT Systems 1-2

1.2 NT Driver Design Goals 1-31.2.1 Portability 1-31.2.2 Configurability 1-41.2.3 Always Preemptible and Always Interruptible 1-51.2.4 Multiprocessor-safe 1-71.2.5 Object-based 1-81.2.6 Packet-driven I/O with Reusable IRPs 1-101.2.7 Supporting Asynchronous I/O 1-11

Figures - Chapter 1

TMFigure 1.1 Windows NT Component Overview 1-1

Page 2: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 2 - Layered I/O, IRPs, and I/O Objects

2.1 End-user I/O Requests and NT File Objects 2-12.1.1 Points to Consider 2-5

2.2 IRPs and Driver-specific I/O Stack Locations 2-62.2.1 Points to Consider 2-12

2.3 Driver Objects and Standard Driver Routines 2-132.3.1 Points to Consider 2-19

2.4 Device Configurations and Layered NT Drivers 2-212.4.1 Interactive Devices 2-21

2.4.1.1 Video Adapter Configurations and Driver Layers 2-222.4.1.2 Keyboard and Mouse Configurations and Driver Layers 2-24

2.4.2 Parallel and Serial Devices and Driver Layers 2-262.4.3 Sound Device and Driver Layers 2-282.4.4 Mass Storage Devices and Driver Layers 2-30

2.4.4.1 "AT" Disk Devices 2-302.4.4.2 Floppy Devices 2-322.4.4.3 SCSI Devices 2-34

2.4.5 Points to Consider 2-37

2.5 NT Objects with Device, Configuration, or Layer Dependencies 2-372.5.1 Video Driver's Device Objects 2-392.5.2 Keyboard and Mouse Drivers' Device Objects 2-402.5.3 Parallel and Serial Drivers' Device Objects 2-412.5.4 Sound Driver's Device Objects 2-422.5.5 "AT" Disk Driver's Device and Controller Objects 2-442.5.6 Floppy Driver's Device Objects 2-462.5.7 SCSI Drivers' Device Objects 2-492.5.8 Points to Consider 2-52

Page 3: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Figures - Chapter 2

Figure 2.1 NT File Objects Represent Files, Volumes, and Devices 2-2Figure 2.2 Opening an NT File Object 2-4Figure 2.3 Processing IRPs in Layered Drivers 2-6Figure 2.4 Driver Objects 2-14Figure 2.5 Video Hardware Configuration 2-22Figure 2.6 Video Driver Layers 2-23Figure 2.7 Keyboard and Mouse Hardware Configurations 2-24Figure 2.8 Keyboard and Mouse Driver Layers 2-25Figure 2.9 Parallel and Serial Hardware Configurations 2-26Figure 2.10 Parallel and Serial Driver Layers 2-27Figure 2.11 Sound Device Hardware Configuration 2-28Figure 2.12 Sound Driver Layers 2-29Figure 2.13 "AT" Disk Controller Hardware Configuration 2-30Figure 2.14 "AT" Disk Driver Layers 2-31Figure 2.15 Floppy Controller Hardware Configuration 2-32Figure 2.16 Floppy Driver Layers 2-33Figure 2.17 SCSI Bus Hardware Configuration 2-34Figure 2.18 SCSI Device Driver Layers 2-35Figure 2.19 Video Device Object 2-39Figure 2.20 Keyboard and Mouse Device Objects 2-40Figure 2.21 Parallel and Serial Device Objects 2-41Figure 2.22 Sound Device Objects 2-42Figure 2.23 "AT" Disk Device Objects and Controller Object 2-44Figure 2.24 Floppy Device Objects 2-46Figure 2.25 SCSI Device Objects 2-50

Page 4: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 3 - NT Objects and Support for Drivers

3.1 NT Executive Components and NT Drivers 3-1

3.2 Device Objects and Device Extensions 3-73.2.1 Defining Device Extensions 3-93.2.2 Creating a Device Object and Device Extension 3-103.2.3 Initializing Driver-specific Device Objects and Device Extensions 3-113.2.4 Setting Up Access to User Buffers 3-13

3.2.4.1 Using Buffered I/O 3-133.2.4.2 Using Direct I/O 3-153.2.4.3 Using Neither Direct Nor Buffered I/O 3-18

3.3 Adapter Objects and DMA 3-203.3.1 Map Registers 3-213.3.2 Getting an NT Adapter Object 3-233.3.3 Splitting Transfer Requests 3-263.3.4 Using System DMA 3-28

3.3.4.1 Packet-based System DMA 3-283.3.4.2 Common-buffer System DMA 3-34

3.3.5 Using Busmaster DMA 3-383.3.5.1 Packet-based DMA 3-393.3.5.2 Common-buffer DMA 3-46

3.4 Controller Objects 3-483.4.1 Creating a Controller Object with a Controller Extension 3-493.4.2 Allocating the Controller for I/O Operations 3-51

3.5 Interrupt Objects 3-543.5.1 Getting a System-assigned Interrupt Vector, DIRQL, and Processor Mask 3-543.5.2 Registering an ISR 3-56

3.6 DPC Objects 3-593.6.1 Registering and Queueing a DpcForIsr Routine 3-593.6.2 Registering and Queueing a CustomDpc Routine 3-62

3.7 Timer Objects with Associated DPCs 3-643.7.1 Registering and Enabling an IoTimer Routine 3-653.7.2 Registering and Queueing a CustomTimerDpc Routine 3-67

Page 5: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3.8 Device Queue Objects and Interlocked Queues 3-703.8.1 Setting up a Device Queue Object and Queueing IRPs 3-713.8.2 Setting up an Interlocked Queue and Queueing IRPs 3-73

3.9 Kernel Dispatcher Objects for Drivers 3-763.9.1 Timer Objects 3-783.9.2 Event Objects 3-803.9.3 Semaphore Objects 3-833.9.4 Mutex Objects 3-86

Figures - Chapter 3

Figure 3.1 Executive Component Support for Drivers 3-2Figure 3.2 Device Object 3-8Figure 3.3 Buffered I/O for User Buffers 3-14Figure 3.4 Direct I/O on User Buffers 3-16Figure 3.5 Physical, Logical, and Virtual Address Mappings 3-22Figure 3.6 Getting an Adapter Object 3-24Figure 3.7 Allocating the System DMA Controller 3-30Figure 3.8 Programming the System DMA Controller 3-32Figure 3.9 Allocating a Common Buffer for System DMA 3-34Figure 3.10 Allocating an Adapter Object for Busmaster DMA 3-40Figure 3.11 Setting Up a Logical Range for DMA 3-44Figure 3.12 Allocating a Common Buffer for Busmaster DMA 3-46Figure 3.13 Controller Object 3-50Figure 3.14 Allocating a Controller Object for I/O 3-52Figure 3.15 Getting a System Vector and IRQL 3-55Figure 3.16 Setting Up Interrupt Objects 3-56Figure 3.17 Using a DPC Object for a DpcForIsr Routine 3-60Figure 3.18 Using a DPC Object for a CustomDpc Routine 3-62Figure 3.19 Using a Timer Object for an IoTimer Routine 3-66Figure 3.20 Using Timer and DPC Objects for a CustomTimerDpc Routine 3-68Figure 3.21 Using a Device Queue Object 3-72Figure 3.22 Using an Interlocked Queue 3-74Figure 3.23 Waiting on a Timer Object 3-78Figure 3.24 Waiting on an Event Object 3-80Figure 3.25 Waiting on a Semaphore Object 3-84Figure 3.26 Waiting on a Mutex Object 3-88

Page 6: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Contents TMChapter 4 - Basic NT Driver Structure

4.1 Standard Driver Routines 4-1

4.2 Device Drivers' Staged IRP Processing 4-8

4.3 Intermediate Drivers' Staged IRP Processing 4-13

4.4 Designing and Developing an NT Driver 4-164.4.1 Naming Device Objects 4-164.4.2 Choosing Names for Driver Routines 4-174.4.3 Starting Design 4-184.4.4 Starting Development 4-20

Figures - Chapter 4

Figure 4.1 Standard NT Driver Routines 4-2Figure 4.2 IRP Path through Device Driver Routines (DMA) 4-9Figure 4.3 Calling IoStartPacket 4-10Figure 4.4 Calling IoStartNextPacket and IoCompleteRequest 4-12Figure 4.5 IRP Path through Intermediate Driver Routines 4-14

Page 7: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 5 - DriverEntry and Reinitialize Routines

5.1 DriverEntry Routine Requirements 5-1

5.2 Reinitialize Routine Requirements 5-4

5.3 Implementing a DriverEntry Routine 5-4

5.4 Implementing a Reinitialize Routine 5-4

Page 8: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 6 - Dispatch Routines

6.1 Dispatch Routine Requirements 6-1

6.2 Implementing DispatchCreate and DispatchClose Routines 6-4

6.3 Implementing a DispatchCleanup Routine 6-4

6.4 Implementing a DispatchRead Routine 6-4

6.5 Implementing a DispatchWrite Routine 6-4

6.6 Implementing a DispatchDeviceControl Routine 6-4

6.7 Implementing a DispatchInternalDeviceControl Routine 6-4

Page 9: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 7 - StartIo Routine or Queue-management Routines

7.1 Queueing IRPs 7-1

7.2 StartIo Routine Requirements 7-2

7.3 Requirements for Using Interlocked Queues or Device Queues 7-4

7.4 Implementing a StartIo Routine 7-6

7.5 Managing Interlocked Queues 7-6

7.6 Managing Device Queues 7-6

Page 10: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 8 - Interrupt Service Routine

8.1 ISR Requirements 8-1

8.2 Implementing an ISR 8-2

Page 11: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 9 - DpcForIsr Routine and CustomDpc Routines

9.1 DpcForIsr and CustomDpc Routine Requirements 9-1

9.2 Implementing a DpcForIsr Routine 9-3

9.3 Implementing CustomDpc Routines 9-3

Page 12: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 10 - SynchCritSection Routines

10.1 SynchCritSection Routine Requirements 10-2

10.2 Implementing a SynchCritSection Routine 10-2

Page 13: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 11 - AdapterControl and ControllerControl Routines

11.1 Driver Control Routines 11-2

11.2 AdapterControl Routine Requirements 11-3

11.3 ControllerControl Routine Requirements 11-4

11.4 Implementing an AdapterControl Routine 11-5

11.5 Implementing a ControllerControl Routine 11-5

Page 14: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 13 - IoCompletion Routines

13.1 IoCompletion Routine Requirements 13-2

13.2 Implementing IoCompletion Routines 13-2

Page 15: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 13 - IoCompletion Routines

13.1 IoCompletion Routine Requirements 13-2

13.2 Implementing IoCompletion Routines 13-2

Page 16: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 14 - IoTimer and CustomTimerDpc Routines

14.1 IoTimer Routine Requirements 14-2

14.2 CustomTimerDpc Routine Requirements 14-3

14.3 Implementing IoTimer Routines 14-4

14.4 Implementing CustomTimerDpc Routines 14-4

Page 17: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 15 - Unload Routine

15.1 Unload Routine Requirements 15-2

15.2 Implementing an Unload Routine 15-2

Page 18: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

ContentsChapter 16 - Common Driver Design Issues

16.1 Managing Hardware Priorities 16-1

16.2 Using Spin Locks 16-416.2.1 Providing Storage for Spin Locks and Protected Data 16-516.2.2 Initializing Spin Locks 16-616.2.3 Calling Support Routines That Use Spin Locks 16-716.2.4 Releasing Spin Locks Promptly 16-716.2.5 Preventing Fatal Errors or Deadlocks While Using Spin Locks 16-11

16.3 Polling the Device 16-12

16.4 Managing Memory Usage 16-1316.4.1 Using System Memory 16-1316.4.2 Using the Kernel Stack 16-1816.4.3 Using Zone Buffers 16-18

16.5 Maintaining Cache Coherency for DMA and PIO Operations 16-2016.5.1 Flushing Cached Data during DMA Operations 16-2016.5.2 Flushing Cached Data during PIO Operations 16-22

16.6 Error Logging and NTSTATUS Values 16-22

16.7 Handling Removable Media 16-28

16.8 Using the Configuration Registry 16-3016.8.1 Registry Paths Supplied to NT Drivers 16-31

16.8.1.1 RegistryPath, DriverName, and Device Object Names 16-3316.8.1.2 Load-control Sets for NT Drivers 16-3416.8.1.3 HardwareDatabase and the System DeviceMap 16-37

16.8.2 Getting System-supplied Device Description Data 16-3816.8.3 Claiming Hardware Resources 16-4516.8.4 Setting Up Driver-specific, User-visible Error Logging 16-4916.8.5 Using the RegistryPath Parameters 16-50

16.9 Setting Up Symbolic Links 16-5216.9.1 Making a Named Device Object Visible to User-mode Applications 16-5216.9.2 Creating Symbolic Links between Boot Devices and ARC Names 16-54

Page 19: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Figures - Chapter 16

Figure 16.1 Default IRQLs for Driver Routines 16-2Figure 16.2 Using an Interrupt Spin Lock 16-8Figure 16.3 NT Virtual Memory Spaces and Physical Memory 16-14Figure 16.4 Read and Write Operations Using DMA 16-21Figure 16.5 NTSTATUS layout 16-25Figure 16.6 Configuration Registry Paths for NT Drivers 16-32Figure 16.7 Getting Information from the Registry for Device Drivers 16-40Figure 16.8 Setting Information in the Registry by Device Drivers 16-46

Page 20: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Appendix BSCSI Drivers

B.1 Designing a SCSI Class Driver B-2B.1.1 DriverEntry Routine B-3

B.1.1.1 GetInquiryData Routines B-3B.1.1.2 ClaimDevice Routines B-4B.1.1.3 Setting up Device Extensions B-5B.1.1.4 GetCapabilities Routines B-6

B.1.2 Dispatch Routines B-7B.1.2.1 BuildSrb Routines B-8B.1.2.2 SplitTransferRequest Routine B-9

B.1.3 IoCompletion Routines B-11B.1.3.1 ReleaseQueue Routines B-12B.1.3.2 InterpretRequestSense Routines B-13B.1.3.3 RetryRequest Routines B-13

B.2 Designing a SCSI Filter Driver B-14B.2.1 DriverEntry Routine B-15B.2.2 Dispatch Routines B-16B.2.3 IoCompletion Routines B-17

B.3 Designing a SCSI Tape Subclass Driver B-18

B.4 Designing a SCSI Miniport Driver B-20B.4.1 DriverEntry Routine B-21

B.4.1.1 HwFindAdapter Routines B-22B.4.1.2 HwInitialize Routine B-25

B.4.2 HwStartIo Routine B-26B.4.3 HwDmaStarted Routine B-29B.4.4 HwResetBus Routine B-30B.4.5 HwInterrupt Routine B-30

B.4.5.1 HwEnableInterruptsCallback Routine B-31B.4.5.2 HwDisableInterruptsCallback Routine B-32

B.4.6 HwTimer Routine B-32B.4.7 HwAdapterState Routine B-33B.4.8 Error Handling B-33

Page 21: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 1NT Drivers TMFigure 1.1 illustrates the major components of the Windows NT operating systemenvironment.

User Mode

Kernel Mode

ObjectManager

Kernel

st_over

POSIXClient

Applications Windows Client

Executive

SecurityReferenceMonitor

(servers )

ProtectedSubsystems

OS/2Subsystem

POSIXSubsystem

SecuritySubsystem

Hardware Abstraction Layer

OS/2Client

Hardware

Win32Subsystem

LogonProcess

LocalConfiguration ProcessStructure

I/O Manager MemoryManager

File System andDevice Drivers

ExecutiveSupport

System Services

LANManager

Manager ProcedureCall

TMFigure 1.1 Windows NT Component Overview

As Figure 1.1 shows, this environment includes some components that run in user mode andothers that run in kernel mode. File system and device drivers are shown at the lower left,included with the kernel-mode I/O Manager.

Page 22: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

1-2 Kernel-mode Driver Design Guide

This chapter explains what Windows NT kernel-mode drivers are, what the design goals forthese drivers are, and how these drivers necessarily differ from other operating systems'drivers in order to meet their design goals. For more general information about the WindowsNT operating system environment, see Inside Windows NT, published by Microsoft Press.

TM1.1 Kinds of Drivers in Windows NT Systems

In the Windows NT system, there are two basic kinds of drivers: TM 1 User-mode drivers, such as Win32 graphics display and printer drivers, Win32

TMmultimedia driver handlers, virtual device drivers (VDDs) for MS-DOS applicationswith application-dedicated devices, or another protected subsystem's drivers. User-modedrivers are subsystem-specific and are not discussed in this manual. See the SubsystemDriver Design Guide for more information about Win32 drivers and VDDs.

2 Kernel-mode drivers for logical, virtual, or physical devices. These are called NT drivers(or executive drivers), because they are part of the NT executive: the underlying, "newtechnology" microkernel-based operating system that supports one or more protectedsubsystems. In the rest of this manual, NT is frequently used to mean the NT executive.

As Figure 1.1 showed, NT includes a number of kernel-mode components with well definedfunctionality isolated in each component. Those of most interest to NT device driver writersare the Kernel, I/O Manager, Hardware Abstraction Layer (HAL), Configuration Manager,Memory Manager, Executive Support, and Process Structure components. Additionalcomponents of interest to NT file system driver writers include the Object Manager, Security(Reference) Monitor, and Cache Manager (not shown in Figure 1.1).

Like NT itself, NT drivers are implemented as discrete, modular components with a welldefined set of required functionality. All NT drivers have a set of system-defined standarddriver routines and some number of internal routines, determined by the driver writer.

There are three basic types of NT drivers. Each type has a slightly different structure and quitedifferent functionality: 1 Device drivers (also called lowest-level drivers), such as a keyboard or disk driver that

directly controls a physical device. 2 Intermediate drivers, such as a virtual disk, mirror, or device-specific class driver, that

depend on support from device drivers. 3 File system drivers (FSDs), such as the system-supplied FAT, HPFS, NTFS, or CDFS

drivers, that also depend on support from lower-level drivers. While a particular NT filesystem driver may or may not get support from one or more intermediate drivers, everyNT file system driver ultimately depends on support from one or more device drivers.

Page 23: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Drivers 1-3

NT network drivers can also be classified as one of these types of drivers. For example, an NTserver or redirector is a specialized file system driver, a transport driver is a type ofintermediate NT driver, and a physical netcard driver (sometimes called a media accesscontroller driver) is an NT device driver. However, NT provides specialized interfaces andsupport for network drivers, such as the NDIS3.0 (Network Device Interface Specification,Version 3.0) for drivers of physical net cards.

While this manual provides some useful overviews and background information for NT filesystem and network driver writers, it is primarily a design guide for writers of NT device andintermediate drivers. Network and file system driver writers should also consultsupplementary documentation for these kinds of drivers.

1.2 NT Driver Design Goals

NT drivers share many of the design goals of NT, in particular, those of the NT I/O Manager.These design goals include the following: ■ Portability from one platform to another ■ Configurability of hardware and software ■ Always preemptible and always interruptible ■ Multiprocessor-safe on multiprocessor platforms ■ Object-based ■ Packet-driven I/O with reusable I/O request packets (IRPs) ■ Supporting asynchronous I/O

The following sections explain some of the implications of these design goals for NT drivers.

1.2.1 Portability

Most NT components are coded entirely in C, with only very small pieces of the HAL andKernel written in assembly language, so that NT is readily portable across CISC- and RISC-based platforms. An NT driver is also coded in C so that it can be recompiled with an NT-compatible C compiler, relinked, and run on different NT platforms without recoding parts ofthe driver or replacing modules in the driver.

Driver writers should not use the features of any particular NT-compatible C compiler if theycannot assume that the same features will be supported by other NT-compatible compilers. Ingeneral, driver writers should code to the ANSI C standard but avoid writing code withdependencies on anything this standard describes as "implementation defined."

Page 24: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

1-4 Kernel-mode Driver Design Guide

In particular, driver writers must avoid dependencies on data types whose size or layout canvary from one platform to another, must avoid calling any standard C runtime library functionthat maintains state, and must avoid calling any other standard C runtime library function forwhich NT provides an alternative function.

NT supplies a set of include files defining NT-specific data types and constants that drivers(and all other NT kernel-mode components) use to help maintain their portability from one NTplatform to another. NT drivers should include both ntdef.h (for these basic NT types) andntddk.h (the master DDK kernel-mode include file), which pulls in the appropriate processor-architecture-specific include file, such as i386.h or mips.h, when the driver is compiled withthe I386 or MIPS compiler directive. Note that i386.h covers both Intel 80386- and 80486-based NT platforms; mips.h covers both MIPS R3000- and R4000-based NT platforms. NTdriver writers should isolate any platform-dependent definitions within #ifdef statements, sothat each driver can be compiled and linked for the appropriate CISC-based or RISC-basedNT platform.

NT exports two NT-specific runtime libraries, containing functions that begin with the prefixRtl. NT driver writers can use the same kernel-mode Rtl routines that the NT executivecomponents do, but NT drivers cannot call user-mode Rtl routines because NT drivers alwaysrun in kernel mode. NT file system drivers can also call the NT-supplied file system runtimelibrary's FsRtl routines.

Each NT executive component exports a set of kernel-mode support routines that other kernel-mode components, including NT drivers, call. If the underlying implementation of such asupport routine changes over time, its callers remain portable because the interface to thedefining component does not change.

1.2.2 Configurability

Because NT is a portable operating system, designed to run on both CISC-based and RISC-based machines, devices and their drivers also must be both hardware- and software-configurable.

For example, a given disk device might be attached to either of two EISA buses in onemachine running NT, but the same device might be limited to a single EISA bus in anothermachine running NT. Such a device is hardware-configurable, and, to remain portable, itsdriver must not contain hard-coded machine-dependent values.

The driver of such a disk device might provide hardware-level support to one or more NT filesystems in any given machine, depending on how the user partitions the disk. In addition, anNT intermediate driver might support fault tolerance (disk mirroring or duplexing) for higher-level file system drivers in machines with sufficient mass storage capacity. In other words, thesame physical disk driver could provide support to more than one type of higher-level driverin some NT machines but not in others. Such a disk driver is software-configurable, as are allintermediate and file system drivers that ultimately depend on support from NT device drivers.

Page 25: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Drivers 1-5

The NT HAL component, implemented as a dynamic-link library, is responsible for allhardware-level, platform-specific support needed by every other component in the system,including NT drivers. The HAL exports routines that hide platform-specific hardware detailsabout caches, I/O buses, interrupts, and so forth, to provide an interface between the platform'shardware and the system's software. For example, an NT device driver must call a HAL-supplied routine to map the bus-relative interrupt vector at which its device interrupts in agiven machine to the system-assigned vector the driver will use.

The NT Configuration Manager component provides a database, called the configurationregistry, of information about the hardware, peripheral devices, and drivers in a givenmachine. NT drivers can use this database to get information about both the hardwareconfiguration of the machine and other drivers in the system. NT drivers also supplyinformation about their devices and machine-specific configurations in the configurationregistry database.

1.2.3 Always Preemptible and Always Interruptible

The NT Kernel component determines when a particular sequence of code is run according toone of the following prioritizing criteria: ■ The Kernel-defined runtime priority scheme for threads. Every thread in the system has

an associated priority attribute. In general, most threads in the system have variablepriority attributes: they are always preemptible and are scheduled to run round-robin withall other threads currently at the same priority level. Some threads in the system havereal-time priority attributes: these time-critical threads are preemptible by any threadwith a higher real-time priority attribute, but otherwise run until they relinquish control.

Whatever its priority attribute, any thread in the system can be preempted when hardware(or certain kinds of software) interrupts occur.

■ The Kernel-defined interrupt request level (IRQL) to which a particular interrupt vectoris assigned on a given platform. The NT Kernel also prioritizes hardware and softwareinterrupts so that some kernel-mode code runs at higher IRQLs, thereby making somekernel-mode code, including most NT drivers, have a higher scheduling priority than allthreads in the system.

The particular IRQL at which a piece of kernel-mode code executes determines itshardware priority. Such a piece of code is always interruptible: an interrupt with a higherIRQL value can occur at any time, thereby causing another piece of kernel-mode code(with a higher IRQL) to be run immediately on that processor. In other words, when apiece of code runs at a given IRQL, the Kernel masks off all interrupt vectors with alesser or equal IRQL value on the processor.

Page 26: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

1-6 Kernel-mode Driver Design Guide

In general, threads run at PASSIVE_LEVEL IRQL: no interrupt vectors are masked. Softwareinterrupts are assigned relatively low IRQL values (APC_LEVEL, DISPATCH_LEVEL, or,for kernel debugging, WAKE_LEVEL). Device interrupts have higher IRQL values, and theKernel reserves the highest IRQL values for system-critical interrupts such as the systemclock or bus-error interrupts.

Some kernel-mode routines run at PASSIVE_LEVEL IRQL because kernel-mode componentscan also set up their own threads. Certain standard driver routines usually run atPASSIVE_LEVEL IRQL, as well. However, most NT driver routines run either atDISPATCH_LEVEL IRQL or, for a device driver, at device IRQL (also called DIRQL).

For performance reasons (avoiding context switches), very few NT drivers actually set uptheir own threads, except for file system drivers. Consequently, NT driver routines generallyexecute in the context of whatever thread happens to be current when the driver is called to getsome work done.

While any kernel-mode routine that is run at higher than PASSIVE_LEVEL IRQL has ahigher priority than all threads in the system, every routine in an NT driver is interruptible:any kernel-mode routine running at a particular IRQL retains control of the processor only ifno interrupt with a higher IRQL value occurs while that routine is running.

Even an NT device driver's interrupt service routine (ISR) can be interrupted by anotherroutine (for example, by another driver's ISR) that runs at higher IRQL. Unlike the drivers insome PC operating systems, an NT driver's ISR is not the workhorse routine that does almostall I/O processing because an NT driver's ISR does not necessarily retain control of the CPU itis currently running on until it returns.

Instead, an NT device driver must carry out most of its I/O operations at a lower IRQL thanthe DIRQL of its ISR. For good overall system performance, all kernel-mode routines that runat high IRQLs must relinquish control of the processor(s) very quickly. Such routines do onlywhat must be done at high IRQL and generally queue a deferred procedure call (DPC) tocomplete any operations that can be done at a lower IRQL (DISPATCH_LEVEL).

For an introduction to the system-defined standard routines for NT drivers, see Chapter 2. Foran overview of these routines, see Chapter 4.

Page 27: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Drivers 1-7

1.2.4 Multiprocessor-safe

In an NT multiprocessor platform, the following conditions hold: ■ All CPUs are identical, and either all have identical coprocessors or none has a

coprocessor. ■ All CPUs share memory and have uniform access to memory. ■ In a symmetric platform, every CPU can access memory, take an interrupt, and access I/O

control registers. In an asymmetric platform, one CPU takes all interrupts for a set ofslave CPUs.

NT is designed to run unchanged on uniprocessor and symmetric multiprocessor platforms,and NT drivers should be designed to do likewise.

To run safely on a symmetric multiprocessor platform, any operating system must solve thisproblem: how to guarantee that code executing on one processor does not simultaneouslyaccess and modify data that is being accessed and modified from another processor. Forexample, an NT device driver's ISR that is handling a device interrupt on one processor mustensure that it has exclusive access to critical, driver-defined data (or the device registers) incase its device interrupts simultaneously on another processor.

Furthermore, NT drivers' I/O operations that are serialized in a uniprocessor machine can beoverlapped in a symmetric multiprocessor machine. That is, a given driver's routine thatprocesses incoming I/O requests can be executing on one processor while another routine thatcommunicates with the device executes concurrently on another processor. Whether they arerunning on a uniprocessor or multiprocessor machine, this situation requires NT drivers tosynchronize access to any driver-defined data or system-provided resources that are sharedamong driver routines and to synchronize access to the physical device, if any.

The NT Kernel component exports a mechanism, called a spin lock, that is used to protectshared data (or device registers) from simultaneous access by one or more routines runningconcurrently on a symmetric multiprocessor platform. The Kernel enforces two policiesregarding the use of spin locks: 1 One and only one routine can hold a particular spin lock at any given moment, and only

the holder of a spin lock can access the data it protects. Another routine must acquire thespin lock in order to access the same data, and the spin lock cannot be acquired until thecurrent holder releases it.

2 Like a hardware or software interrupt vector, the Kernel assigns each spin lock in thesystem an associated IRQL value. A kernel-mode routine can acquire a particular spinlock only when the routine runs at the spin lock's IRQL.

These policies prevent a driver routine that usually runs at a lower IRQL, but holds the spinlock, from being preempted by a higher priority driver routine that is trying to acquire thesame spin lock, thereby causing a deadlock.

Page 28: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

1-8 Kernel-mode Driver Design Guide

The IRQL assigned to a spin lock is generally set to that of the highest-IRQL routine that canacquire the spin lock. For example, an NT device driver's ISR frequently shares a storage areawith the driver's DPC routine, which calls a critical-section routine to access the shared area.In this case, the spin lock protecting the shared area has an IRQL equal to the DIRQL at whichthe device interrupts. While the critical-section routine holds the spin lock and accesses theshared area at DIRQL, the ISR cannot be run in a uniprocessor machine because the deviceinterrupt is masked off (see the preceding section). While the critical-section routine holds thespin lock and accesses the shared data at DIRQL, the ISR still cannot acquire the spin lock ina symmetric multiprocessor machine.

Note that a set of threads can synchronize access to shared data or resources by waiting on oneof the NT Kernel's dispatcher objects: an event, mutex, semaphore, timer, or another thread.However, most NT drivers do not set up their own threads because they get betterperformance by avoiding context switches. Consequently, time-critical kernel-mode supportroutines and NT drivers must use the Kernel's spin locks to synchronize access to shared dataor resources whenever they run at DISPATCH_LEVEL IRQL (or at DIRQL).

For more information about using spin locks, see Chapter 16. For more information about theKernel's dispatcher objects, see Chapter 3.

1.2.5 Object-based

NT is an object-based system. Various components in the NT executive define one or moreobject types. Each component exports support routines that manipulate instances of its objecttypes when these routines are called. No component is allowed to access any instance ofanother component's object types directly. Each component must call the exported supportroutines in order to use another component's objects.

Strict adherence to these conventions allows NT to be both portable and flexible. For example,a future release of NT could contain a wholly or partially recoded Kernel component thatdefined the same object types (possibly with entirely different internal structures) andexported a set of support routines with the same names and parameters as the existing set.Such a new version of the NT Kernel component would have no effect on the portability ofany other executive component in the existing system. In other words, the NT executivecomponents do not practice back-door communication, and NT drivers also must eschew thispractice in order to remain portable and configurable.

NT drivers and their devices are also object-based. To all other components in the system,including user-mode code, a device is represented as one of the NT I/O Manager's file objects.Within the I/O system, each driver's logical, virtual, and/or physical devices are represented asdevice objects. Within the I/O Manager, each NT driver's load image is represented as a driverobject. The I/O Manager defines the object types for file objects, device objects, and driverobjects, as well as for adapter objects to represent system DMA controllers or busmasteradapters and for controller objects to represent a physical controller for similar devices.

Page 29: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Drivers 1-9

Like any other executive component, NT drivers use NT objects by calling kernel-modesupport routines exported by the I/O Manager and other NT components. NT kernel-modesupport routines generally have names that identify the specific object each manipulates andthe operation each performs on that object. These support routine names have the form: PrefixOperationObject where Prefix identifies the NT component that exports the routine and, usually, the

component that defined the object type. Most prefixes have two letters. Operation describes what is done to the object. Object identifies the type of object.

For example, the IoCreateDevice routine, which each NT driver calls one or more times whenit initializes, creates a device object to represent a physical, logical, or virtual device.

For convenience, one NT component can export routines that call another component'ssupport routines. The I/O Manager, in particular, exports certain routines that make it easier todevelop NT drivers. For example, the IoConnectInterrupt routine, which device drivers callto register their ISRs when these drivers initialize, actually calls down to the Kernel's supportroutines for interrupt objects.

The Glossary of this manual contains definitions for the set of NT objects most useful to NTdriver writers. Appendix A summarizes the kernel-mode support routines that are most usefulto NT device and intermediate drivers.

Page 30: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

1-10 Kernel-mode Driver Design Guide

1.2.6 Packet-driven I/O with Reusable IRPs

The I/O Manager's chief job is to accept I/O requests (usually from user-mode applications),create I/O request packets (IRPs) to represent them, route the IRPs to the appropriate NTdrivers, track them until they are completed, and return status to the original caller. The I/OManager uses IRPs to communicate with NT drivers and to allow NT drivers to communicatewith each other.

Note that some IRPs might be routed to more than one NT driver. For example, a request toopen a file on a disk might go first to a file system driver, through an intermediate mirrordriver, and ultimately to a physical disk driver (see Sections 1.1 and 1.2.2).

Therefore, each IRP has a fixed part and one or more driver-specific I/O stack locations: ■ In the fixed part (or header), the I/O Manager maintains information about the original

request, such as the caller's parameters, the address of the device object on which a file isopen, etc. The fixed part also contains an I/O status block, in which drivers setinformation about the status of the requested operation.

■ In the I/O stack location, the I/O Manager sets driver-specific parameters such as theparticular operation requested (represented as function codes), and context used by thecorresponding driver to determine what it is supposed to be doing. In turn, higher-leveldrivers set up the I/O stack location of the next-lower-level drivers, if any.

As a given IRP is processed through each driver's set of standard routines, each routine canaccess that driver's I/O stack location in the IRP, thereby reusing the IRP at each stage of thedriver's operations. In addition, higher-level NT drivers can create (or reuse) IRPs to sendrequests down to lower-level drivers.

For more information about the processing of IRPs, see Chapter 2. For more informationabout device-type-specific IRP function codes, see Appendix B.

Page 31: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Drivers 1-11

1.2.7 Supporting Asynchronous I/O

The I/O Manager provides asynchronous I/O support so that the originator of an I/O request(usually a user-mode application, but sometimes another driver) can continue executing, ratherthan waiting for its I/O request to complete. Providing asynchronous I/O support improvesoverall system I/O throughput, as well as the performance of any code that makes an I/Orequest.

As a consequence, NT drivers do not necessarily process I/O requests in the same order theywere sent to the I/O Manager. The I/O Manager or a higher-level driver can reorder I/Orequests as they are received or can split a large data transfer request into smaller transferrequests. Moreover, an NT driver can overlap I/O request processing, particularly in asymmetric multiprocessor platform (see Section 1.2.4).

Note that an NT driver's processing of a given I/O request is not necessarily serialized. That is,an NT driver does not process each IRP to completion before it starts processing the nextrequest, as drivers naturally do in a single-tasking operating system written for uniprocessormachines.

Instead, NT drivers respond to the current IRP as it is passed to the driver's standard routinesby carrying out whatever routine-specific operations are necessary to satisfy the currentrequest. However, each NT driver is required to help the I/O Manager (and any higher-leveldrivers processing the same IRPs) to track the status of each request by setting the I/O statusblock in the IRP. NT drivers can also maintain status information about their current I/Ooperations in a special part of their device objects, called a device extension.

For an overview of IRP processing and of how device objects represent the physical andlogical devices of NT drivers, see Chapter 2. For more detailed information about deviceobjects and device extensions, see Chapter 3.

Page 32: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 2Layered I/O, IRPs, and I/O Objects

Every operating system has an implicit or explicit I/O model for handling the flow of data to TMand from peripheral devices. The Windows NT executive I/O model has the followinggeneral features: ■ The NT I/O Manager presents a consistent interface to all kernel-mode drivers, including

device, logical, virtual, and file system drivers. All I/O requests to NT drivers are sent asI/O request packets (IRPs).

■ I/O operations are layered. That is, the I/O Manager exports system services, which user-mode protected subsystems call to carry out I/O operations on behalf of their applicationsand/or end users. The I/O Manager intercepts these calls and routes one or more IRPsthrough (possibly layered) NT drivers to physical devices.

■ The I/O Manager defines a set of standard routines, some optional, for NT driver writersto implement. All NT drivers follow a relatively consistent implementation model, giventhe differences among peripheral devices and the differing functionality required ofphysical, virtual, logical, and file system drivers.

■ Like NT itself, NT drivers are object-based. Drivers, their devices, and system hardwareare represented as NT objects. The I/O Manager and other NT components export kernel-mode support routines that NT drivers can call to get work done by manipulating theappropriate objects.

This chapter introduces the NT I/O model, supplying an overview of how kernel-mode driversfit into the system, of how NT drivers process IRPs, of the system-defined standard driverroutines, of common device configurations and corresponding layered drivers, and of the NTobjects that represent devices, drivers, and system I/O hardware.

2.1 End-user I/O Requests and NT File Objects

NT drivers are hidden from end users by a protected subsystem that implements an already TMfamiliar programming interface, such as Windows or POSIX. Devices are visible to user-mode code, which includes protected subsystems, only as file objects controlled by the NT I/OManager. Figure 2.1 illustrates this relationship between an end user, a subsystem, and the NTI/O Manager.

Page 33: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-2 Kernel-mode Driver Design Guide

Figure 2.1 NT File Objects Represent Files, Volumes, and Devices

Page 34: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-3

TMNote that a Windows NT protected subsystem, such as the Win32 subsystem, supplies adisplay driver to handle interactive I/O with its end users and with applications native to thatsubsystem. The subsystem display driver shown in Figure 2.1 depends on support from the NTvideo adapter, keyboard, and mouse device drivers. This subsystem also supplies one or moreprinter drivers that set up output files to be spooled to the NT parallel port driver (or other NTdevice driver that sends output to a printer). These subsystem-specific drivers, like thesubsystem, run in user mode.

A protected subsystem insulates its end users and applications from having to know anythingabout NT kernel-mode components, including NT drivers. In turn, the NT I/O Managerinsulates protected subsystems from having to know anything about platform-specific deviceconfigurations or about NT drivers' implementations.

The NT I/O Manager's layered approach also insulates most NT drivers from having to knowanything about: ■ Whether an I/O request originated in the Win32, POSIX, or any particular protected

subsystem ■ Whether a given protected subsystem has particular kinds of user-mode device drivers ■ What any protected subsystem's I/O model and interface to device drivers is

The I/O Manager supplies NT drivers with a single I/O model, a set of kernel-mode supportroutines these drivers can use to carry out I/O operations, and a consistent interface betweenthe originator of an I/O request and the NT drivers that must respond to it.

As shown in Figure 2.1, a subsystem and its native applications or subsystem-specific driverscan access an NT driver's device or a file on a mass storage device only through file objecthandles supplied by the NT I/O Manager. A subsystem's request to open such a file object andto obtain a handle for I/O to a device or a data file is made by calling the NT I/O systemservices to open a named file, which can have a subsystem-specific alias (symbolic link) to thekernel-mode name for the file object. The NT I/O Manager, which exports these services, isthen responsible for locating or creating the file object that represents the device or data fileand for locating the appropriate NT driver(s). Figure 2.2 shows an overview of what happenswhen a subsystem opens a file object representing a data file on behalf of an application.

Page 35: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-4 Kernel-mode Driver Design Guide

Figure 2.2 Opening an NT File Object 1 The subsystem calls an NT I/O system service to open a named file. 2 The I/O Manager calls the Object Manager to look up the named file and to help it

resolve any symbolic links for the file object. It also calls the Security Reference Monitorto check that the subsystem has the correct access rights to open that file object.

Page 36: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-5

3 If the volume is not yet mounted, the I/O Manager suspends the open request, calling oneor more NT file systems until one of them recognizes the file object as something it hasstored on one of the mass storage devices the file system uses. When the file system hasmounted the volume, the I/O Manager resumes the request.

4 The I/O Manager allocates memory for and initializes an I/O request packet (IRP) for theopen (for NT drivers, a "create") request.

5 The I/O Manager calls the file system driver, passing it the IRP. The file system driveraccesses its I/O stack location in the IRP to determine what operation it must carry out,checks parameters, determines if the requested file is in cache, and, if not, sets up thenext-lower driver's I/O stack location in the IRP.

6 Both drivers process the IRP and complete the requested I/O operation, calling kernel-mode support routines supplied by the I/O Manager and by other NT executivecomponents (not shown in Figure 2.2).

7 The drivers return the IRP to the I/O Manager with the I/O status in the IRP indicatingwhether the operation succeeded or why it failed.

8 The I/O Manager gets the I/O status from the IRP, so it can return NT status informationto the original caller.

9 The I/O Manager frees the completed IRP.10 The I/O Manager returns a handle for the file object to the subsystem if the open

operation was successful. If there was an error, it returns appropriate status to thesubsystem.

After a subsystem successfully opens a file object that represents a data file, a device, or avolume, the subsystem uses the returned file object handle to request that operations (mostcommonly read, write, or device I/O control requests) be carried out by calling the I/O systemservices. The I/O Manager routes these requests as IRPs sent to appropriate NT drivers.

2.1.1 Points to Consider

Keep the following points in mind when designing an NT driver: ■ NT drivers can be layered, and more than one NT driver can process a single I/O request

(IRP). ■ NT drivers communicate the success or failure of a requested I/O operation in the I/O

status block of the IRP. The I/O Manager communicates the success or failure of arequested I/O operation to a user-mode requestor.

■ NT drivers need not (and should not) be designed to provide application-specific support.A protected subsystem or its subsystem-specific, user-mode drivers supply this kind of TMsupport. There is one exception to this rule: an MS-DOS application that relies on anapplication-dedicated device can require an NT driver to control the device and a closelycoupled Win32 user-mode virtual device driver (VDD). For more information aboutVDDs, see the Subsystem Driver Design Guide.

Page 37: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-6 Kernel-mode Driver Design Guide

2.2 IRPs and Driver-specific I/O Stack Locations

Figure 2.2 shows an IRP with two I/O stack locations, but an IRP can have any number of I/Ostack locations, depending on how many layered drivers will handle a given request.

Figure 2.3 illustrates in more detail how the drivers of Figure 2.2 use I/O support routines toprocess the IRP for a read or write request. Figure 2.3 also shows the IRP I/O stack locationfor a lowest-level driver, such as a physical disk driver, in more detail.

Figure 2.3 Processing IRPs in Layered Drivers

Page 38: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-7

1 The I/O Manager calls the file system driver (FSD) with the IRP it has allocated for thesubsystem's read/write request. The FSD uses its own I/O stack location in the IRP todetermine what operation it should carry out.

2 The FSD can break the original request into smaller requests (possibly for more than onedevice driver) by calling an I/O support routine one or more times to make associatedIRPs, which are returned to the FSD with zero-filled I/O stack location(s) for lower-leveldriver(s). At its discretion, the FSD can reuse the original IRP, rather than creatingassociated IRPs as shown in Figure 2.3, by setting up the next-lower driver's I/O stacklocation in the original (also called master) IRP and passing it on to lower drivers.

3 For each associated IRP, the FSD in Figure 2.3 calls an I/O support routine to register acompletion routine in case a lower-level driver encounters an error in processing therequest. The I/O Manager will call the FSD-supplied completion routine only if an erroroccurs, unless the FSD requests that its completion routine be called whenever the lowerdriver(s) complete the IRP successfully and/or cancel the IRP. (If the FSD does notregister its completion routine with a set of associated IRPs, the I/O Manager completesthe master IRP automatically when all associated IRPs are completed by lower drivers.)

Next, the FSD calls an I/O support routine to access the next-lower-level driver's I/Ostack location in the associated IRP in order to set up the request for that driver, whichhappens to be the lowest-level driver in Figure 2.3. The FSD then calls an I/O supportroutine to pass the associated IRP on to the next driver.

4 When it is called with the associated IRP, the physical device driver checks its I/O stacklocation to determine what operation (IRP_MJ_xxx) it should carry out on the targetdevice (represented by the device object in its I/O stack location and passed with the IRPto the driver). This driver can assume that the I/O Manager has routed the IRP to an entrypoint that the driver defined for the IRP_MJ_xxx operation (here IRP_MJ_READ orIRP_MJ_WRITE) and that the higher-level driver has checked the validity of otherarguments for the request.

If there were no higher-level driver, the device driver would check whether the inputarguments for an IRP_MJ_xxx operation are valid. If they are, the driver usually calls I/Osupport routines to tell the I/O Manager that a device operation is pending and to eitherqueue or pass the IRP on to another driver-supplied routine that accesses the target device(here, a physical or logical device: the disk or a partition on the disk).

5 The I/O Manager determines whether the driver is already busy processing another IRPfor the target device, queues the associated IRP if it is, and returns. Otherwise, the I/OManager routes the associated IRP to a driver-supplied routine that starts the I/Ooperation on its device. (At this stage, both drivers in Figure 2.3 and the I/O Managerreturn control.)

6 When the device interrupts, the driver's interrupt service routine (ISR) does only as muchwork as it must to stop the device from interrupting and to save necessary context aboutthe operation. The ISR then calls an I/O support routine with the IRP to queue a driver-supplied DPC (deferred procedure call) routine to complete the requested operation at alower hardware priority than the ISR.

Page 39: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-8 Kernel-mode Driver Design Guide

7 When the driver's DPC gets control, it uses the context (passed in the ISR's call toIoRequestDpc) to complete the I/O operation. The DPC calls a support routine todequeue the next IRP (if any) and to pass that IRP on to the driver-supplied routine thatstarts I/O operations on the device (see Step 5). The DPC then sets status about the justcompleted operation in the IRP's I/O status block and returns it to the I/O Manager.

8 The I/O Manager zeroes the lowest-level driver's I/O stack location in the IRP and callsthe file system's registered completion routine (see Step 3) with the associated IRP ifthere was an error. This completion routine checks the I/O status block to determinewhether to retry the request or to update any internal state maintained about the originalrequest and complete the associated IRP. The file system can collect status informationfor all associated IRPs it sends on to lower-level drivers in order to set I/O status andcomplete the master IRP. When it has completed the master IRP, the I/O Manager returnsNT status to the original requestor of the I/O operation.

Figure 2.3 also shows two I/O stack locations in the master IRP because it shows two NTdrivers, a file system driver and a mass storage device driver. The I/O Manager gives each NTdriver in a chain of layered drivers an I/O stack location of its own in every IRP. Theassociated IRPs in Figure 2.3 do not have a stack location for the FSD that created them. Anyhigher-level driver that makes associated IRPs or allocates new ones for lower-level driversalso determines how many I/O stack locations the new IRPs should have (according to theStackSize value of the next-lower driver's device object).

As shown in Figure 2.3, each driver-specific I/O stack location in an IRP contains thefollowing general information: ■ The major function code (IRP_MJ_xxx), indicating the basic operation the driver should

carry out. ■ For some major function codes handled by NT file system drivers, a minor function code

(IRP_MN_xxx), indicating which sub-case of the basic operation the FSD should carryout.

■ A set of operation-specific arguments, such as the length and starting location of a bufferinto which or from which the driver transfers data.

■ A pointer to the driver-created device object, representing the target (physical, logical, orvirtual) device for the requested operation.

■ A pointer to the file object, representing an open file, device, directory, or volume. (AnNT file system driver accesses the file object through its I/O stack location in IRPs. OtherNT drivers usually ignore the file object.)

Page 40: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-9

The set of IRP major and minor function codes that a particular NT driver handles can bedevice-type-specific. However, NT device drivers usually handle at least the following set ofbasic requests: ■ IRP_MJ_CREATE - open the target device object (indicating that it is present and

available for I/O operations). ■ IRP_MJ_READ - transfer data from the device. ■ IRP_MJ_WRITE - transfer data to the device. ■ IRP_MJ_DEVICE_CONTROL - set up (or reset) the device, according to a system-

defined, device-type-specific I/O control code. ■ IRP_MJ_CLOSE - close the target device object.

For a summary of the major function codes and device I/O control codes that NT drivers forparticular kinds of devices are required to handle, see Appendix B.

In general, the I/O Manager sends IRPs with at least two I/O stack locations to mass storagedevice drivers, because an NT file system is layered over NT drivers for mass storage devices.The I/O Manager sends IRPs with a single stack location to any physical device driver that hasno drivers layered above it.

However, the NT I/O Manager provides support for adding a new driver to any chain ofexisting drivers in the system. For example, an intermediate mirror driver that backs up dataon a given disk might be inserted between a pair of drivers, such as those shown in Figure 2.3.When this new driver attaches itself to the device driver, the I/O Manager adjusts the numberof I/O stack locations in all IRPs it sends to the file system, mirror, and disk device drivers.(Every associated IRP that the file system in Figure 2.3 created would also contain another I/Ostack location for the new mirror driver.)

Note that this support for adding new NT drivers to an existing chain implies certainrestrictions on a given driver's access to the I/O stack locations in IRPs: ■ A higher-level driver in a chain of layered drivers can safely access only its own and the

next-lower-level driver's I/O stack locations in any IRP. Such a driver must set up the I/Ostack location for the next-lower-level driver in IRPs. However, the designer of such ahigher-level driver cannot predict when (or whether) a new driver will be added to theexisting chain just below the driver.

Every writer of a higher-level NT driver must assume that any subsequently added driverwill handle the same IRP major function codes (IRP_MJ_xxx) as the displaced next-lower-level driver did.

■ The lowest-level driver in a chain of layered drivers can safely access only its own I/Ostack location in any IRP. The designer of such a driver cannot predict when (or whether)a new driver will be added to the existing chain just above the device driver.

Every writer of a lowest-level NT driver must assume that the driver can continue toprocess IRPs using the information passed in its own I/O stack location, whatever theoriginating source of a given IRP and however many drivers are layered above it.

Page 41: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-10 Kernel-mode Driver Design Guide

Like the file system driver shown in Figure 2.3, any new driver that is added to a chain ofexisting drivers can do all of the following: ■ Set its own completion routine into an IRP that checks the I/O status block to determine

whether lower drivers completed the IRP successfully, cancelled the IRP, completed itwith an error, or completed it with any combination of these options. Such a driver'scompletion routine can also update any IRP-specific state the driver might have saved,release any operation-specific resources the driver might have allocated, and so forth,before completing the IRP. Such a completion routine can even reregister itself and reusethe IRP to send another request to the next-lower-level driver before allowing the IRP tocomplete.

■ Call I/O support routines to allocate new IRPs or to make associated IRPs. However, NTintermediate drivers, which are chained somewhere between a highest-level driver (suchas the file system in Figure 2.3) and a lowest-level (physical device) driver, cannot callIoMakeAssociatedIrp. Only the highest-level driver in a chain can create associatedIRPs. However, NT intermediate drivers can call other support routines to create IRPsthat they send down to lower drivers.

■ Set up the next-lower-level driver's I/O stack location in the IRPs it creates and sendrequests to the next-lower-level driver.

■ Pass any incoming requests on to lower drivers by setting up the next-lower driver's I/Ostack location in each IRP and calling IoCallDriver.

See Appendix A for a summary of the support routines that intermediate and lowest-leveldrivers might call, and Appendix B for a summary of request-specific and device-specificinformation about IRPs.

As shown in Figure 2.3, an NT file system is a two-part driver: 1 A file system driver (FSD), which executes in the context of a user-mode thread that calls

an I/O system service. The I/O Manager sends the corresponding IRP to the FSD. If theFSD sets up a completion routine for an IRP, the completion routine is not necessarilycalled in the original user-mode thread's context.

2 A set of file system threads (sometimes called an FSP for file system process). An FSDcan create a set of driver-dedicated system threads or use system worker threads in orderto get work done without tying up user-mode threads that make I/O requests. (An FSDcould set up its own process address space in which its threads execute, but NT-suppliedFSDs avoid this practice to conserve system memory.)

NT file systems use system threads to set up and manage internal work queues of IRPs thatthey send to one or more lower-level drivers (possibly for different devices).

Page 42: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-11

While the physical device driver shown in Figure 2.3 processes each IRP in stages through aset of discrete, driver-supplied routines, it does not use system threads, as the file system does.A physical device driver does not need its own thread context unless setting up its device forI/O is such a protracted process that it has a noticable effect on system performance. Few NTdevice or intermediate drivers need to set up their own system threads, and those that do pay aperformance penalty caused by context switches to their threads.

Most NT drivers, like the physical device driver in Figure 2.3, execute in the context ofwhatever thread happens to be current. Consequently, NT drivers usually maintain state abouttheir I/O operations and the devices they service in a driver-defined part of their deviceobjects, called a device extension. Each device object represents a physical, logical, or virtualdevice for which a particular NT driver carries out I/O requests. For guidelines about howdifferent kinds of device drivers use device objects to represent their respective physical,logical, and virtual devices, see Section 2.5 later in this chapter. For detailed informationabout creating and setting up a device object, see Chapter 3.

As Figure 2.3 also shows, most NT drivers process each IRP in stages through a driver-supplied set of system-defined standard routines, but drivers at different levels in a chainnecessarily have different standard routines. For example, only lowest-level NT drivers handleinterrupts from a physical device, so only a lowest-level driver would have an ISR and a DPCthat completes interrupt-driven I/O operations. On the other hand, a lowest-level driver cannotregister a completion routine for a given IRP as higher-level drivers can, so only a higher-leveldriver would have one or more completion routines like the file system driver in Figure 2.3.See Section 2.3 for a brief introduction to the system-defined routines that NT drivers canhave. See Chapter 4 for an overview of these routines and subsequent chapters for routine-specific requirements.

Page 43: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-12 Kernel-mode Driver Design Guide

2.2.1 Points to Consider

Keep the following points in mind when designing an NT driver: ■ A new NT driver must handle the same set of IRP_MJ_xxx as any system-supplied driver

it replaces. The I/O Manager returns STATUS_INVALID_DEVICE_REQUEST for agiven I/O request to a target device whose driver does not define an entry point for thatIRP_MJ_xxx. A device driver must also handle the same I/O control codes forIRP_MJ_DEVICE_CONTROL requests as any system-supplied driver it replaces. Inother words, a new NT device driver must not "break applications" by implementing lessfunctionality than an existing driver for the same type of device.

■ An intermediate driver inserted into a chain of existing drivers should recognize the sameset of IRP_MJ_xxx as the driver it displaces. Such a driver can simply pass on IRPs forthose requests that it does not process to the next-lower-level driver. However, a newintermediate driver must not "break the chain" for drivers above and below it byneglecting to define an entry point for an IRP_MJ_xxx request that the newly displaced,next-lower-level driver does handle.

■ A lowest-level device driver can access only its own I/O stack location in any IRP that itis sent. A higher-level driver can access only its own and the next-lower-level driver's I/Ostack locations in any IRP that it is sent.

■ Every NT driver communicates information to higher-level drivers (and ultimately, touser-mode applications via the I/O Manager) only in the I/O status blocks of IRPs. Anydriver that attempts to implement back-door communication with a particular higher (orlower) driver compromises its portability and its interoperability with other NT driversfrom one NT platform or version to the next.

■ A pair of NT drivers can define a set of device-specific (also called private) I/O controlcodes for IRP_MJ_INTERNAL_DEVICE_CONTROL requests that the higher of thepair can send down to the lower of the pair. However, such a pair of drivers must followall of the preceding guidelines if they are to remain portable and interoperable with otherNT drivers from one NT platform or version to another. If you design a pair of NTdrivers with such a private interface, consider the set of I/O control codes to be definedcarefully. Make them as generally useful as possible and design your paired drivers tofollow the preceding guidelines, so that you (or someone else) can reuse, replace, ordisplace either or both of your new drivers easily as they migrate from one NT platformor version to another.

Page 44: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-13

2.3 Driver Objects and Standard Driver Routines

Figure 2.4 illustrates a driver object, representing an NT driver, with the set of system-defined(or standard) routines that lowest-level and higher-level drivers can or must have. Figure 2.4also shows which of these standard routines are passed an IRP when they are called. On entry,every standard routine that is passed an IRP is also given a pointer to the target device objectfor the I/O request.

Page 45: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-14 Kernel-mode Driver Design Guide

Figure 2.4 Driver Objects

The I/O Manager defines the driver object type and uses driver objects to register and trackinformation about the loaded images of NT drivers. Note the one-to-one correspondencebetween the Dispatch entry points in the driver object and the major function codes(IRP_MJ_xxx), which are passed in the I/O stack locations of IRPs.

Page 46: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-15

As shown previously in Figure 2.3, the I/O Manager routes each IRP first to a driver-suppliedDispatch routine, and a device driver's Dispatch routine usually calls an I/O support routine(IoStartPacket) to queue (or pass on) each IRP with valid arguments to the driver's StartIoroutine, which starts the requested I/O operation on a particular device. (Higher-level NTdrivers usually do not have StartIo routines, but they can.)

When an NT driver is loaded, its DriverEntry routine defines one or more Dispatch entrypoints in the input driver object so that the I/O Manager can route IRPs to the appropriatedriver-supplied Dispatch routine. The DriverEntry routine also defines its StartIo and Unloadentry points (if any) in the driver object. A DriverEntry (or optional Reinitialize) routine alsocan use a field in the driver object (not shown in Figure 2.4) to get information from and/or setinformation in the NT configuration registry database. For more information about howdrivers use the configuration registry, see Chapter 16.

Like all NT objects, a driver object is opaque: only the defining NT component (here, the I/OManager) "knows" an object type's internal structure and can access all the data an objectcontains directly. The defining component usually exports support routines that drivers (andother NT components) can call to manipulate that component's objects. For example, the NTKernel component exports support routines that the I/O Manager calls to initialize and connectinterrupt objects when a lowest-level NT driver registers an interrupt service routine (ISR),shown in Figure 2.4 as DDInterruptService. To maintain driver portability, follow thisimplementation guideline: ■ NT drivers always use the system-supplied support routines to manipulate NT objects.

The defining NT component can change the internal structure of its object types at anytime.

However, the I/O Manager exports no support routines to manipulate driver objects. Driverobjects are used only by the I/O Manager to keep track of currently loaded NT drivers. Somefields within a driver object are opaque: known only to the I/O Manager and used only by theI/O Manager. Others are partially opaque: driver writers must know certain field names todefine Dispatch, StartIo, and Unload entry points and to use the NT configuration registry.Nevertheless, to maintain the portability of their drivers from one NT platform to another,driver writers should neither attempt to use unpublished fields within a driver object nor makeassumptions about the locations of any driver object fields that are named in this manual.

Every NT driver (except video and SCSI miniport drivers, as explained in Sections 2.4.1.1 and2.4.3.3) must define the following entry points in its driver object: ■ At least one Dispatch entry point in order to get IRPs requesting I/O operations ■ If the driver can be loaded (or replaced) dynamically, an Unload entry point in order to

free any resources that the driver has allocated before the driver is unloaded (Drivers thatcannot be replaced while the system is running, such as a keyboard driver, need notsupply an Unload routine.)

Page 47: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-16 Kernel-mode Driver Design Guide

While an NT device driver is not required to define a StartIo entry point if it is designed to setup and manage its own queue(s) of IRPs, most NT device drivers define a StartIo entry pointin their driver objects and rely on the I/O Manager to queue IRPs bound for their StartIoroutines. In fact, few lowest-level system-supplied drivers are designed without a StartIoroutine, even when they set up and maintain their own supplemental queues for IRPs.

Higher-level NT drivers can have a StartIo routine but, for better performance, seldom do.Instead, most NT file system drivers set up and maintain internal queues of IRPs. Otherhigher-level NT drivers either have internal queues for IRPs or simply pass IRPs on to lowerdrivers from their Dispatch routines after setting up the I/O stack location for the next-lowerdriver in each IRP, and, possibly, setting up the higher-level driver's IoCompletion routine fora given IRP.

Unlike most NT objects, which are wholly opaque outside the defining NT component, adriver object is partially opaque to NT drivers because each driver becomes part of the NT I/Osystem when the driver is loaded. When an NT driver's DriverEntry routine is called, it setsDispatch, StartIo (if any), and Unload (if any) entry points directly in the driver object asfollows:

DriverObject->MajorFunction[IRP_MJ_xxx] = DDDispatchXxx; : :DriverObject->MajorFunction[IRP_MJ_yyy] = DDDispatchYyy; : :DriverObject->DriverStartIo = DDStartIo;DriverObject->DriverUnload = DDUnload; : :

An NT driver can define several Dispatch entry points, but it can define only one StartIo entrypoint and one Unload entry point in its driver object. At the driver writer's discretion, an NTdriver writer can implement one or more Dispatch routines and define corresponding entrypoints in the driver object as any one of the following: ■ A single Dispatch entry point for all IRP_MJ_xxx the driver handles ■ A set of Dispatch entry points for nonintersecting subsets of all IRP_MJ_xxx the driver

handles ■ A separate Dispatch entry point for each IRP_MJ_xxx the driver handles

As Figure 2.4 shows, NT drivers have other standard routines along with those for which theyset entry points in their respective driver objects. Most standard driver routines and some ofthe configuration-dependent objects they use are defined by the I/O Manager. The ISR,SynchCritSection routine, and those shown in Figure 2.4 with names containing the word"custom" are defined by the NT Kernel component.

Page 48: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-17

With few exceptions (SCSI and video miniport drivers), every NT driver's DriverEntry routinemust also create one or more device objects representing the physical, logical, or virtualdevice(s) for which it carries out I/O requests. As Figure 2.4 shows, the I/O Managermaintains information about driver-created device objects in the corresponding driver object.Most NT drivers use the device extension of each device object they create to maintaindevice-specific state about their I/O operations and to store pointers to any system resourcesthat they must allocate in order to have other standard routines. For example, theDDCustomTimerDpc routine shown in Figure 2.4 requires the driver to supply storage forKernel-defined timer and DPC objects.

As mentioned in Section 2.2, the set of standard driver routines for lowest-level drivers shownon the left in Figure 2.4 is necessarily different from the set for higher-level drivers. Some ofthe routines shown in Figure 2.4 are device-dependent or configuration-dependentrequirements. Others are optional: a driver writer may choose to implement such a routinedepending on the nature or configuration of the driver's device(s), on the driver's design, andon the driver's position in a chain of layered drivers. The standard NT driver routines includethe following:________________________________________________________________________Routine Driver Level________________________________________________________________________

InterruptService lowest-level onlyDrivers for physical devices that generate interrupts must have an ISR. The ISR muststop the device from interrupting. Then, it should do only what is necessary to save stateand queue a DPC to complete interrupt-driven I/O operations at a lower hardware priority(IRQL) than that at which the ISR executes.

DpcForIsr lowest-level onlyDrivers that have an ISR should also have a DPC (deferred procedure call) to completeinterrupt-driven I/O operations.

SynchCritSection lowest-level onlyAny device driver whose routines share data (or device registers) with its ISR must haveone or more SynchCritSection routines to access the shared data in a multiprocessor-safemanner.

Cancel any level (highest driver in any chain)Drivers in which IRPs might remain queued for an indefinite interval (so a user couldcancel a previously submitted I/O request) must have one or more Cancel routines tocomplete user-cancelled I/O requests. Examples of NT drivers that should have Cancelroutines are keyboard, mouse, parallel, serial, and sound device drivers (or driverslayered over them), and file system drivers.

Page 49: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-18 Kernel-mode Driver Design Guide

AdapterControl lowest-level onlyAny driver whose device uses system or (non-SCSI) busmaster DMA must have anAdapterControl routine in order to carry out transfer operations between its device andsystem physical memory.

ControllerControl lowest-level onlyA device driver that must synchronize operations through a physical controller (such asan "AT" disk controller) to similar devices can have a ControllerControl routine.

IoCompletion intermediate or highest-levelHigher-level drivers that monitor on an IRP-specific basis how lower-level driverscarried out particular requests can have one or more IoCompletion routines. (See Section2.2 for a description of how higher-level drivers can use an IoCompletion routine.)

IoTimer any levelDrivers that need to be called periodically to determine if a device operation has timedout, to update some driver-defined variable (such as a counter), or for some other reasoncan have an IoTimer routine. An IoTimer routine is actually a DPC routine, associatedwith a device object, that the I/O Manager calls once per second. An NT driver can havean IoTimer routine for each device object it creates.

CustomTimerDpc any levelDrivers that need to be called periodically at finer-grained intervals than once per secondor at variable intervals can have a CustomTimerDpc routine, rather than an IoTimerroutine. NT drivers can also have one or more CustomTimerDpc routines in addition totheir IoTimer routines.

CustomDpc lowest-level onlyAny driver that needs to finish an interrupt-driven I/O operation later at a lower hardwarepriority (IRQL) can have a CustomDpc routine. Few lowest-level drivers haveCustomDpc routines to be queued from their ISRs unless their devices require more thanone DpcForIsr routine to complete a varied set of interrupt-driven I/O operations.Note that the CustomTimerDpc and IoTimer routines shown in Figure 2.4 are actuallysystem-defined CustomDpc routines that execute after a clock interrupt occurs.

Reinitialize any levelAny driver that needs to intialize itself in stages can have a Reinitialize routine. AReinitialize routine is called after the DriverEntry routine has returned control and otherNT drivers have initialized themselves.

By convention, the system-supplied NT drivers prepend an identifying, driver- or device-specific prefix to the name of every standard routine except DriverEntry, shown in Figure 2.4as "DD." Following this convention makes it easier to debug and maintain NT drivers.

Page 50: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-19

2.3.1 Points to Consider

Keep the following points in mind when designing an NT driver: ■ An NT driver must have at least one Dispatch routine and must define the Dispatch entry

point in its driver object for each IRP_MJ_xxx the driver handles. An NT driver can haveup to as many Dispatch routines as the IRP_MJ_xxx function codes the driver handles.

■ An NT driver must have an Unload routine and define one Unload entry point in itsdriver object if the driver can be replaced while the system is running. An Unload routineis responsible for releasing any system resources (such as NT objects or driver-allocatedmemory) that the driver is using before the driver itself is unloaded from the system.

■ An NT driver can have a StartIo routine and define one StartIo entry point in its driverobject. Any lowest-level driver that does not have a StartIo routine must set up andmanage queues of IRPs sent to its Dispatch routine(s) unless it can complete every IRP itgets within its Dispatch routine(s). Higher-level drivers can have a StartIo routine, butseldom do because higher-level drivers usually pass IRPs on to lower-level driversdirectly from their Dispatch routines.

■ The only exceptions to the preceding requirements are NT SCSI and video miniportdrivers. For general information about how SCSI miniport drivers fit into the system, seeSections 2.4.4.3 and 2.5.7 later in this chapter, and see the Kernel-mode Driver Referencefor SCSI-driver-specific information. For more information about how video miniportdrivers fit into the system, see Sections 2.4.1.1 and 2.5.1 later in this chapter, and see alsothe Kernel-mode Driver Reference.

■ Whether an NT driver has any other kind of standard routine depends on its functionalityand on how that driver fits into the system (for example, whether it interoperates withsystem-supplied drivers). The remaining sections in this chapter show how various kindsof NT-supplied drivers are configured in NT machines, how they are layered, and howcertain NT objects represent physical, logical, and virtual devices, system DMAcontrollers, and device controllers. Use these sections as a guide to developing new NTdevice and intermediate drivers or as a guide to replacing system-supplied drivers withnew device drivers.

Page 51: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-20 Kernel-mode Driver Design Guide

See also the following: ■ Section 2.5 for how various kinds of NT device drivers use device objects to represent

physical, logical, and virtual devices and for more information about NT objects thatrepresent system DMA controllers or device controllers

■ Chapter 3 for detailed information about NT objects that drivers can use ■ Chapter 4 for an overview of the standard NT driver routines and Chapters 5-15 for

routine-specific requirements ■ Chapter 16 for more information about how NT drivers manage the system-defined

hardware priorities at which driver routines execute (See Chapter 16 also for moreinformation about how NT drivers use the configuration registry.)

■ Appendix B for more information about IRP_MJ_xxx that NT device and intermediatedrivers must handle

■ The Glossary for a summary of NT terminology and acronyms used in this manual

Page 52: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-21

2.4 Device Configurations and Layered NT Drivers

For the most common kinds of PC devices, NT supplies a sample set of fully functionalkernel-mode drivers. Particular system-supplied drivers can be used as models whendeveloping new drivers for similar kinds of devices. However, these system-supplied drivershad an additional design requirement: to make it easy to develop new device drivers.Consequently, many of the NT-supplied drivers are layered so that certain drivers can bereused to support new drivers for similar devices.

In most cases, the reusable drivers are higher-level NT drivers that handle hardware-independent operations for a system-supplied lowest-level, device-specific driver. Note thatthe system-supplied reusable drivers do not preclude the development of new intermediatedrivers to be added to a chain of existing drivers, as explained in Section 2.2.

Whether a new (or replacement) NT device driver must be a lowest-level or higher-leveldriver depends partly on the hardware configuration of devices in a given NT platform, andpartly on how much support a new driver can get from the system-supplied drivers.

2.4.1 Interactive Devices

NT drivers for video, keyboard, and mouse devices (or for other pointing devices, such astrackballs or light pens) provide hardware input/output support for a subsystem-supplieddisplay driver that runs in user mode on top of NT, as explained in Section 2.1.

Page 53: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-22 Kernel-mode Driver Design Guide

2.4.1.1 Video Adapter Configurations and Driver Layers

Figure 2.5 illustrates a representative hardware configuration for the video device shownpreviously in Figure 2.1.

Figure 2.5 Video Hardware Configuration

Figure 2.6 illustrates the corresponding layered drivers for I/O operations on the video deviceshown in Figure 2.5.

Page 54: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-23

Figure 2.6 Video Driver Layers

The NT video port driver shown in Figure 2.6 is implemented as a dynamic-link library so thatan adapter-specific, but OS-independent, miniport driver can call the VideoPortXxx routinesto manage all OS-dependent operations. NT provides this support to make video miniport TMdrivers source compatible on x86-based machines running 32-bit MS DOS , which willprovide its own OS-dependent set of VideoPortXxx routines with the same names andparameters. For more information about these routines, see the Kernel-mode Driver Reference.

An NT video adapter is an exclusive device: its miniport driver is dedicated to supporting asingle subsystem's display driver after it obtains a handle for the file object that represents theNT video device, as explained in Section 2.1. For performance reasons, the NT-supplied videoport driver gives the Win32 subsystem a "privileged" handle (with special access rights) forthe video device so the subsystem display driver can access the video frame buffer directlyand, in some NT platforms, the adapter registers as well.

Page 55: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-24 Kernel-mode Driver Design Guide

The video miniport driver is responsible for supporting certain device control operations thatthe corresponding display driver need not handle. The I/O Manager sends IRPs to the NT-supplied video port driver for these device control requests. The NT video port driver handlesaccess checking, memory mapping, and synchronization when it passes I/O requests on to theminiport driver as video request packets (VRPs).

Because a subsystem-supplied display driver also routes keyboard and mouse data to the enduser's screen, the keyboard and mouse are also exclusive NT devices. Until a given displaydriver releases these exclusive file object handles by calling an I/O system service, othersubsystems' display drivers must "go through" the owning display driver (using the NT LPCmechanism) to perform any necessary I/O to their end user's screen.

2.4.1.2 Keyboard and Mouse Configurations and Driver Layers

Figure 2.7 shows two possible hardware configurations for the keyboard and mouse devices: 1 Each connected directly somewhere on the system bus 2 Both connected through a keyboard and auxiliary device controller

Figure 2.7 Keyboard and Mouse Hardware Configurations

Figure 2.8 illustrates the corresponding layered drivers for I/O operations on the devicesshown in Figure 2.7.

Page 56: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-25

Figure 2.8 Keyboard and Mouse Driver Layers

Note that NT keyboard and mouse devices, whatever the hardware configuration, can use thesystem-supplied, higher-level keyboard class and mouse class drivers to handle hardware-independent operations. These are called class drivers because each supplies system-requiredbut hardware-independent support for a particular class of device.

A corresponding port driver implements the device-specific support to carry out required I/Ooperations on the physical device. The system-supplied keyboard and auxiliary device portdriver for X86-based NT platforms manages device-specific operations for both mouse andkeyboard. In a hardware configuration where each device is separately connected, as shown inFigure 2.7, the system-supplied class drivers can be layered over separate device-specific portdrivers, or a single driver for each device could be implemented as a separate, monolithic(lowest-level) driver.

Page 57: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-26 Kernel-mode Driver Design Guide

2.4.2 Parallel and Serial Devices and Driver Layers

Figure 2.9 illustrates a representative hardware configuration for parallel and serial devices.

Figure 2.9 Parallel and Serial Hardware Configurations

Both the parallel and serial devices are connected directly on the system bus.

Figure 2.10 shows the corresponding layered drivers for I/O operations on the devices shownin Figure 2.9.

Page 58: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-27

Figure 2.10 Parallel and Serial Driver Layers

The parallel and serial drivers shown in Figure 2.10 are both lowest-level device drivers andhighest-level NT drivers.

As mentioned in Section 2.1, the parallel driver writes files, which usually have been set up bya subsystem-specific printer driver and spooled by the subsystem, out through a parallel portto a printer.

When an end-user application connects to a parallel or serial port, the user-mode subsystemcalls an NT I/O system service to open the file object representing the appropriate port. If thecall succeeds, that port becomes an exclusive device: dedicated for use by the application untilit disconnects from the port and the subsystem releases the file object handle.

Page 59: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-28 Kernel-mode Driver Design Guide

2.4.3 Sound Device and Driver Layers

Figure 2.11 illustrates a representative hardware configuration for a sound device.

Figure 2.11 Sound Device Hardware Configuration

Note that the sound device is connected to a system DMA controller channel. NT coordinatesaccess to the system's DMA controller (or controllers) and to appropriate DMA channelsand/or registers for NT drivers of all devices that are connected to the system DMAcontroller(s).

Figure 2.12 illustrates the corresponding driver for the sound device.

Page 60: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-29

Figure 2.12 Sound Driver Layers

The system-supplied sound driver is a highest-level NT driver that provides support for user-mode multimedia audio applications. The Multimedia component of the Win32 subsystem islayered between such an application and four system-supplied audio driver handlers, thewave-input, wave-output, midi-input, and midi-output dynamic-link library entry points. Inaddition, a sound driver can provide support for a Multimedia Aux driver handler thatresponds to applications' volume-control requests.

Page 61: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-30 Kernel-mode Driver Design Guide

2.4.4 Mass Storage Devices and Driver Layers

As mentioned in Section 2.2, NT drivers for most kinds of mass storage devices have at leastone higher-level driver layered above them: an NT file system driver, like the FSD shownpreviously in Figure 2.3. While NT file systems have much in common with all higher-level(intermediate or highest-level class) drivers, NT places additional requirements on andsupplies additional support for file systems.

The following subsections illustrate representative hardware configurations for common massstorage devices and the corresponding layered drivers, including which system-supplied filesystems are layered above particular system-supplied device drivers. Note that NT alsosupplies a file system, called the RAW file system, that is layered above a driver for a massstorage device (such as disk, virtual disk, floppy, CD-ROM, or tape devices) that no other filesystem recognizes when a file object on that device is first opened.

2.4.4.1 "AT" Disk Devices

Figure 2.13 illustrates a representative hardware configuration for an "AT" (WD1003-compatible) disk controller.

Figure 2.13 "AT" Disk Controller Hardware Configuration

Page 62: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-31

Note that an "AT" disk controller is assumed to support at most two disk devices, which mighthave different geometries. Figure 2.14 illustrates the corresponding layered drivers for thesedisks.

Figure 2.14 "AT" Disk Driver Layers

The system-supplied "AT" disk driver is designed to manage an unlimited number of diskcontrollers, each with up to two disks attached. This driver supports any NT file system whosepartition resides on its disks. Depending on how a particular end user partitions the disks inher or his machine, more than one NT file system driver might be layered over the system-supplied "AT" disk driver, including any of the NT-supplied FAT, NTFS, and HPFS FSDs.

Page 63: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-32 Kernel-mode Driver Design Guide

2.4.4.2 Floppy Devices

Figure 2.15 illustrates a representative hardware configuration for a floppy controller.

Figure 2.15 Floppy Controller Hardware Configuration

Note that the floppy controller is connected to a system DMA controller channel, and that thedisk drives shown in Figure 2.15 might be for different-sized media. As mentioned in Section2.4.3, NT coordinates access to the system's DMA controller (or controllers) and toappropriate DMA channels and/or registers.

Figure 2.16 illustrates the corresponding layered drivers for the floppy controller and drives.

Page 64: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-33

Figure 2.16 Floppy Driver Layers

The system-supplied floppy driver assumes that it must support up to four floppy drives,although few machines have more than the two floppy drives shown in Figure 2.15.

The NT-supplied FAT file system handles files on unpartitionable removable media. WhileNT supplies other file systems for disk media, they do not support file I/O on diskettes.

Page 65: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-34 Kernel-mode Driver Design Guide

2.4.4.3 SCSI Devices

Figure 2.17 illustrates a representative hardware configuration for a SCSI bus.

Figure 2.17 SCSI Bus Hardware Configuration

The host bus adapter (HBA) for this bus is connected to a system DMA controller. However,Figure 2.17 shows only a partial set of the HBA's target controllers (TIDs for "targetidentifier") and logical units (LUs) where SCSI devices could be connected on the bus. Figure2.17 also does not show the second SCSI bus that an HBA can drive.

Figure 2.18 illustrates the corresponding layered drivers for a representative set of SCSIdevices. NT-supplied drivers are shown within solid lines. Those shown within dotted linesindicate where new drivers might be added to an existing chain of NT drivers.

Page 66: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-35

Figure 2.18 SCSI Device Driver Layers

The NT-supplied SCSI port driver, implemented as a dynamic-link library, exports a set ofsupport routines for use by one or more HBA-specific miniport drivers, depending on howmany SCSI buses a given platform contains. HBA-specific miniport drivers that use theScsiPortXxx routines can link their binaries against either the NT SCSI port driver or an MS-DOS-supplied SCSI port driver that exports ScsiPortXxx routines and runs on the samehardware. In other words, the HBA miniport drivers are operating-system-independent,because an MS-DOS- or NT-supplied SCSI port driver manages all operating-system-dependent requirements on their behalf.

Page 67: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-36 Kernel-mode Driver Design Guide

The NT SCSI port driver also defines an interface to all SCSI (class) device drivers, includingthe system-supplied disk, tape, and CD-ROM class drivers. This NT-defined interfaceinsulates every SCSI class driver from having to know anything about the HBAs driving buseson which their respective devices are connected. In other words, the NT SCSI port drivermakes SCSI devices physically portable to any SCSI bus in an NT system without changingthe code in their corresponding class drivers.

Like the "AT" disk driver described in Section 2.4.4.1, the system-supplied disk class driversupports any file system whose partition resides on its disk(s). A subsystem-level utility cansend NT-defined I/O device control requests through the system-supplied RAW file systemdown to the system-supplied tape class driver. The system-supplied CDFS (CD-ROM filesystem) is layered over the CD-ROM class driver.

If a new type (toaster) of SCSI device is added to the system, its driver must be an NT SCSIclass driver. Developers of SCSI class drivers must use the NT-defined SCSI class/portinterface for their devices, whether they write a class driver for a new type of peripheraldevice, write a new class driver for a type of device that already has a system-supplied classdriver, or replace an existing class driver.

Note that the NT-supplied SCSI drivers assume compliance with the ANSI SCSI-II standard,in particular, compliance with the common command set incorporated by this standard. Thedisk, tape, and CD-ROM filter drivers, shown in Figure 2.18, indicate where SCSI devicedrivers could be inserted into each chain if conditions such as the following hold for thedevice: ■ The device requires SCSI CDBs (command descriptor blocks) in a format different from

the SCSI-II CDB format. ■ The device requires some other nonstandard interface. ■ The device has certain features that other devices of its type do not support.

For such cases, there are two alternatives: 1 A vendor-specific SCSI filter driver (SFD) can be inserted between the system-supplied

class and port drivers, as shown in Figure 2.18, to intercept requests for its device and totranslate requests to and completion data from the device as necessary.

2 A vendor-specific class driver can be added to the set of class drivers layered over the NTport driver, like the toaster class driver shown in Figure 2.18.

Other value-added drivers can be inserted above any SCSI class driver. For example a fault-tolerant disk driver might be inserted above the NT SCSI disk class driver to handle diskmirroring or duplexing. Such a value-added driver can ignore the NT-defined class/portinterface, but must supply Dispatch entry points for each IRP_MJ_xxx that an NT devicedriver for the underlying peripheral device must handle (see Appendix B for device-type-specific information).

See the Kernel-mode Driver Reference for more information about the ScsiPortXxx supportroutines for HBA-specific miniport drivers and the NT-defined SCSI class/port interface.

Page 68: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-37

2.4.5 Points to Consider

Keep the following points in mind when designing an NT driver: ■ A replacement device (lowest-level) driver must implement the same functionality as the

driver it replaces. In other words, a replacement port driver must use the system-definedinterface between itself and any system-supplied class driver that it reuses (and viceversa).

■ A new intermediate driver to be inserted between any pair of system-supplied driversmust interoperate with those drivers. In other words, such a new driver must comply withthe requirements outlined at the ends of Sections 2.2 and 2.3.

2.5 NT Objects with Device, Configuration, or Layer Dependencies

As mentioned in Section 2.3, each NT driver (except for video and SCSI miniport drivers)must create at least one device object by calling an I/O support routine when the driver isloaded. Some NT drivers must create more than one device object: whatever a particulardriver's level in a chain of layered drivers, a separate device object represents each physical,logical, and/or virtual device for which the driver handles I/O requests. For the video andSCSI miniport drivers, the corresponding NT-supplied port driver creates the necessary deviceobjects.

In other words, the driver object previously shown in Figure 2.4 represents a single NT driverthat could have created more than one device object. When it is called, each driver routine thatis given an IRP is also given a pointer to the target device object for the I/O request. Most NTdrivers use the device extension of the target device object to maintain necessary device stateinformation or driver-determined context data about the current I/O request they areprocessing.

Because most NT device and intermediate drivers execute in an arbitrary thread context (thatof whatever thread happens to be current), a device extension is each driver's primary place tomaintain device state and all other driver-specific or device-specific data the driver needs. Forexample, any NT driver that implements a CustomTimerDpc or CustomDpc routine (as shownin Figure 2.4) usually provides storage for the required Kernel-defined timer, and/or DPCobjects in a device extension. Every NT driver that has an ISR must provide storage for apointer to a set of Kernel-defined interrupt objects, and most NT device drivers store thispointer in a device extension. Each NT driver determines the size of the device extensionwhen it creates a device object, and each driver defines the contents of its own deviceextension(s).

Page 69: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-38 Kernel-mode Driver Design Guide

Certain other NT objects that drivers use are also configuration-dependent, device-dependent,and/or design-dependent. For example, a lowest-level driver whose device is connected to asystem DMA controller (as shown previously in Figures 2.11 and 2.15) must explicitlyassociate its device object with an adapter object that represents a DMA controller channel tocarry out transfer operations. Such a driver provides storage (usually in a device extension) fora pointer to an adapter object and has an AdapterControl routine, as shown in Figure 2.4, thatcalls system-supplied adapter object support routines to accomplish DMA transfers.

A lowest-level driver for a set of similar devices coordinated by a physical controller (such asthe "AT" disk controller shown in Figure 2.13) can create a controller object and use it tosynchronize I/O operations between the attached devices. Such a driver implements aControllerControl routine, as shown in Figure 2.4, that calls the I/O Manager's controllerobject support routines.

Chapter 3 describes device, adapter, and controller objects separately in some detail, alongwith all the other NT objects a driver might use (or must use, such as interrupt objects forlowest-level NT drivers whose physical devices generate interrupts). This section introducesdevice, adapter, and controller objects by showing the correspondence between these objectsand the representative hardware configurations and system-supplied device drivers shown inFigures 2.5 through 2.18. Note that every driver in the following figures creates at least onedevice object, except for the video and SCSI miniport drivers. Because miniport drivers aredesigned to execute also under MS-DOS, the NT-supplied port driver creates and manages allnecessary NT objects on their behalf.

Page 70: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-39

2.5.1 Video Driver's Device Objects

Figure 2.19 illustrates the device object that represents the video adapter shown previously inFigure 2.5. The NT-supplied video port driver shown in Figure 2.6 creates this device objecton behalf of a video miniport driver by calling an I/O support routine.

Figure 2.19 Video Device Object

When it is loaded, the NT video port driver creates a device object to represent the videoadapter within the system. The NT video port driver also sets up any necessary platform-specific support, such as mapping the I/O space or device registers to system memory in someNT platforms.

In machines with more than one video adapter, the NT video port driver creates a separatedevice object to represent each adapter. To an NT protected subsystem and its displaydriver(s), these adapter-specific device objects appear to represent separate instantiations ofthe NT video port driver. Each display driver acquires a handle for the corresponding videominiport driver's device by opening the file object associated with the appropriate deviceobject. After the display driver obtains the handle, the display driver calls the Win32subsystem's native functions to control the device. Its device I/O requests are sent as IRPs tothe NT video port driver, which provides all necessary NT-dependent, kernel-mode supportfor both the display and miniport drivers.

Page 71: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-40 Kernel-mode Driver Design Guide

2.5.2 Keyboard and Mouse Drivers' Device Objects

Figure 2.20 illustrates the device objects that represent the keyboard and mouse devices shownpreviously in Figure 2.7. The keyboard and mouse drivers shown in Figure 2.8 create thesedevice objects by calling an I/O support routine.

Figure 2.20 Keyboard and Mouse Device Objects

For the keyboard and mouse devices, both their respective port and class drivers create deviceobjects. The port driver creates a device object to represent the physical port. Each class drivercreates its own device object to represent the keyboard or mouse device as a target for I/Orequests. Each class driver calls an I/O support routine to get a pointer to the next-lower-leveldriver's device object, so the class driver can chain itself above the port driver, and so eachclass driver can send I/O requests down to the port driver (and to its physical target deviceobject).

As shown previously in Figure 2.8, each port driver is a lowest-level driver, so every portdriver whose device issues interrupts must set up interrupt object(s) and register an ISR. Notethat a dual-device port driver, like the driver for the keyboard and auxiliary device controllershown in Figure 2.7, must set up device-specific interrupt objects when each device uses adifferent interrupt vector. The writer of such a driver can either implement separate ISRs foreach device or implement a single ISR for both devices.

Page 72: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-41

2.5.3 Parallel and Serial Drivers' Device Objects

Figure 2.21 illustrates the device objects that represent the parallel and serial devices shownpreviously in Figure 2.9. The parallel and serial drivers shown in Figure 2.10 create thesedevice objects by calling an I/O support routine.

Figure 2.21 Parallel and Serial Device Objects

Note that the parallel and serial device drivers each create a set of logical device objects: onefor each parallel or serial port. Note also that neither driver creates a physical device object forthe parallel or serial controller. The target of a parallel or serial I/O operation is always aparticular port, so each port must be represented by an NT device object. Figure 2.21 happensto show three parallel and two serial ports, but the system-supplied parallel and serial driverscan support any number of ports, subject to hardware limitations.

Page 73: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-42 Kernel-mode Driver Design Guide

2.5.4 Sound Driver's Device Objects

Figure 2.22 illustrates the device objects that represent the sound device shown previously inFigure 2.11. The sound driver shown in Figure 2.12 creates these device objects by calling anI/O support routine.

Figure 2.22 Sound Device Objects

Page 74: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-43

Like the parallel and serial drivers described in Section 2.5.3, the system-supplied sounddriver creates a set of logical device objects: one each for wave-input, wave-output, midi-input, and midi-output operations. If the driver also supports a user-mode Aux driver handler,it creates a device object to represent it.

Note that the sound driver does not create a device object to represent its physical device,because the sound device itself is never the target of an application I/O request. Instead, user-mode multimedia applications make requests for wave/midi input/output operations and thesound driver provides the necessary hardware-level support to carry out these requests.

An adapter object represents the system DMA controller channel to which the sound card isconnected (as shown in Figure 2.11). NT creates as many adapter objects as the system needsto represent all DMA controller channels or non-SCSI-busmaster adapters in a given machine.An adapter object is wholly opaque to drivers: the sound driver must use the system's adaptersupport routines to accomplish its DMA transfers of wave data; it cannot access the adapterobject by another means. Consequently, this driver has an AdapterControl routine, as shownpreviously in Figure 2.4.

Page 75: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-44 Kernel-mode Driver Design Guide

2.5.5 "AT" Disk Driver's Device and Controller Objects

Figure 2.23 illustrates the device objects that represent the disks and the controller object thatrepresents the disk controller shown previously in Figure 2.13. The "AT" disk driver shown inFigure 2.14 creates these device objects and the controller object by calling I/O supportroutines.

Figure 2.23 "AT" Disk Device Objects and Controller Object

Note that this driver creates a device object for each disk, plus some number of (logical)device objects representing disk partitions. Figure 2.23 happens to show the first disk with onepartition and the second with two, but the number of partitions would vary from disk to disk,depending on how different users partition the disk(s) in their respective machines.

Page 76: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-45

The system-supplied "AT" disk driver uses the controller object shown in Figure 2.23 tosynchronize I/O operations between the attached disks, so it implements a ControllerControlroutine, as shown previously in Figure 2.4. While this driver is like the keyboard and auxiliarydevice port driver (see Section 2.5.2) in handling interrupts for two devices, the disk controlleris actually the interrupting device. Consequently, this driver calls the I/O Manager's interruptsupport routine once to register its ISR.

Note that a controller object is by no means the only mechanism an NT device driver can useto synchronize I/O operations among its physical, logical, or virtual devices. NT suppliesdrivers with many ways to synchronize I/O operations and to synchronize access to any dataor hardware shared by a given driver's routines. For example, the Kernel-defined event andsemaphore objects shown in the next figure, and the spin lock associated with a driver'sinterrupt object(s) are three such mechanisms.

Note also that the port driver of a class/port pair is responsible for handling synchronizationbetween (or among) dissimilar devices attached to a device controller, while the class driver(s)can ignore synchronization issues. For example, the keyboard and auxiliary device port driver(see Section 2.5.2) processes IRPs sent independently (and at random) by both the keyboardand mouse class drivers above it, so the keyboard and auxiliary device port driver mustmanage the synchronization of operations between the keyboard and mouse devices.

Page 77: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-46 Kernel-mode Driver Design Guide

2.5.6 Floppy Driver's Device Objects

Figure 2.24 illustrates the device objects that represent the floppy drives shown previously inFigure 2.15. The floppy driver shown in Figure 2.16 creates these device objects by calling anI/O support routine.

Figure 2.24 Floppy Device Objects

An adapter object represents the system DMA controller channel to which the floppycontroller is connected (as shown in Figure 2.15). Like the sound driver described in Section2.5.4, this driver has an AdapterControl routine, as shown previously in Figure 2.4.

Page 78: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-47

However, much of the system-supplied floppy driver's work is done by a device-dedicatedthread. Because programming the floppy controller takes a long time between interrupts (andsome controller operations don't even cause an interrupt), the system-supplied floppy driversets up its own kernel-mode thread that is "blocked" (waiting on the semaphore object shownin Figure 2.24) until the floppy driver is called to carry out an I/O operation. Then, the floppydriver inserts incoming IRPs into an interlocked work queue shared with its device-dedicatedthread, and sets the semaphore object to the Signaled state, thereby "unblocking" the floppythread.

When it is run, the floppy thread removes an IRP from the queue, starts the requestedoperation on the device, and waits on the event object shown in Figure 2.24. The floppy driverhandles interrupts from the controller, queueing a DpcForIsr routine, shown in Figure 2.4, thatnotifies the floppy thread (by setting the event to the Signaled state) to complete I/Oprocessing and the IRP.

Unlike most NT drivers, the system-supplied floppy driver does not have a StartIo routinebecause it sets up an interlocked work queue and manages its own queuing of IRPs. Using aninterlocked work queue prevents the floppy driver and thread from simultaneous attempts toinsert and remove IRPs in the queue. The NT Executive Support component supplies routinesfor managing entries in an interlocked work queue, and the driver allocates the storage for anexecutive spin lock, which is required to use an interlocked work queue. Executive Supportroutines manage access to entries in this interlocked queue with the spin lock as follows: ■ While a support routine holds the spin lock for the system-supplied floppy driver, the

floppy thread cannot remove an IRP from the queue. ■ While another support routine holds the spin lock for the floppy thread, the floppy driver

cannot insert an IRP into the queue.

Spin locks are (opaque) synchronization mechanisms, which NT drivers can use for operationsthat must be atomic or for driver-defined data, device registers, or driver-allocated resourcesthat must be protected from simultaneous access by more than one driver routine running in amultiprocessor machine. Every lowest-level NT driver with an ISR also uses a spin lock that isassociated with its interrupt object(s).

The NT Kernel supplies support routines for initializing spin locks, for acquiring and releasingspin locks, for synchronizing access to data protected by an interrupt spin lock (because thedata is shared between the driver's ISR and other driver routines), and for creating and usingthe event and semaphore objects shown in Figure 2.24. (For more information about usingspin locks, see also Chapter 16.)

Note that only an NT thread can wait for a nonzero interval on Kernel-defined dispatcherobjects, which includes semaphore, event, mutex, and timer objects (as well as thread objects).However, every NT driver's DriverEntry routine executes within a system thread context, soNT drivers can wait for nonzero intervals on dispatcher objects when they initializethemselves.

Page 79: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-48 Kernel-mode Driver Design Guide

The system-supplied floppy driver's thread uses the contiguous buffer shown in Figure 2.24 tocontain data for format operations. The NT Memory Management component providessupport routines for allocating contiguous and/or noncached memory, for explicitly lockingdown user buffers, and for mapping virtual and physical addresses. However, the I/O Manageror an NT file system takes care of user buffer locking and address mappings for most NT massstorage drivers. The I/O Manager buffers user data and takes care of address mappings forcertain other NT device and higher-level drivers.

As Figure 2.24 shows, the system-supplied floppy driver is somewhat like the NT file systemshown previously in Figure 2.3, because it sets up a device-dedicated system thread. The NTProcess Structure component supplies a support routine for creating device-dedicated systemthreads. NT file system drivers also call routines provided by the Executive Supportcomponent in order to use system worker threads to get work done.

However, most NT drivers avoid setting up a device-dedicated thread like the system-suppliedfloppy driver and avoid using system worker threads, as well. Only drivers for devices thatcarry out long operations without notifying the driver should use threads to wait for nonzero(and possibly indefinite) intervals on NT dispatcher objects. Without a device-dedicatedthread, the driver for such a device would have to stall, polling its device for device-statechanges while executing in the context of some arbitrary thread (possibly the original user-mode calling thread that requested the I/O operation, a file system's thread, or some otherthread that happened to be current). Such a driver would waste the quantum allotted to thecurrent thread, which could get no work done while the driver kept control. Such a driverwould also waste many CPU cycles that could be better used by other NT kernel-modecomponents, including other drivers.

Thus, the system-supplied floppy driver improves overall system performance and I/Othroughput at a cost to its own performance: context switches to the floppy thread. NT driversfor newer and faster devices can usually wait for device-state changes without any noticeableimpact on system performance. See Chapter 16 for more information about polling a deviceand for general guidelines about whether to implement a driver with device-dedicatedthread(s).

Page 80: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-49

2.5.7 SCSI Drivers' Device Objects

Figure 2.25 illustrates a sample set of device objects that represent peripheral devices on theSCSI bus shown previously in Figure 2.17. The system-supplied SCSI port and class driversand the toaster class driver shown in Figure 2.18 would create these device objects by callingan I/O support routine.

Page 81: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-50 Kernel-mode Driver Design Guide

Figure 2.25 SCSI Device Objects

Page 82: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-51

The NT SCSI port driver creates a device object representing the HBA within the system. Inmachines with more than one SCSI HBA, the NT port driver creates a separate device objectfor each HBA. To an NT SCSI class driver, these HBA-specific device objects appear torepresent separate, HBA-specific, instantiations of the NT SCSI port driver. When it loads,each class driver determines which SCSI bus has connected devices of its type by accessingthe SCSI INQUIRY data collected by the NT SCSI port driver. Each SCSI class driver chainsitself over one or more HBA-specific NT port driver instantiations, depending on which HBAshave devices of that class connected on their buses.

NT defines a class/port interface between any SCSI class driver and the system-supplied portdriver that insulates class drivers from having to deal with HBA-specific hardwarepeculiarities. After a class driver locates its devices, that driver's SCSI requests are sent asIRPs containing NT-defined SCSI request blocks (SRBs) to the appropriate instantiation(s) ofthe system-supplied port driver.

Each class driver shown in Figure 2.18 creates at least one device object representing itsdevice connected to a corresponding logical unit on the SCSI bus. Like the "AT" disk driver(see Figure 2.23), the SCSI disk class driver also creates a (logical) device object to representeach additional partition on its disks.

Note that a new SCSI filter driver (see Figure 2.18) to be inserted between the NT SCSI portand class drivers must make itself appear to represent both of the following: ■ Another class driver to the NT SCSI port driver below it: the new filter driver must use

the system-defined SCSI class/port interface to send IRPs with SRBs through the NTSCSI port driver to the appropriate HBA-specific miniport driver(s). Consequently, thenew filter driver must create one or more device objects to represent its physical and/orlogical device(s) like any other NT SCSI class driver.

■ Another HBA-specific port driver to the class driver above it: the new filter driver mustcreate one or more HBA-specific device objects, so the higher-level class driver can findits kind of device connected on the pseudo-HBA's (or pseudo-HBAs') SCSI bus(es).

As mentioned in Section 2.4.4.3, adding a new SCSI device to an NT machine does notrequire a filter driver to be inserted between a system-supplied class driver for the same typeof device and the NT SCSI port driver. Instead, the driver writer can create a separate classdriver for the device.

As Figure 2.25 shows, adding a SCSI device of a new class to an NT machine requires itsdriver to create at least one device object. Possibly the new class driver must create more thanone device object, depending on the nature of the (toaster) device. Such a driver must beimplemented as an NT SCSI class driver. For more information about the NT-defined SCSIclass/port interface and requirements for class drivers, see the Kernel-mode Driver Reference.

The system-supplied SCSI port driver also sets up any HBA-specific interrupt object(s),handles DMA, and, in effect, insulates each HBA-specific miniport driver from having toknow anything about NT objects. Miniport drivers interact only with their respective HBAsand with the OS-dependent port driver.

Page 83: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-52 Kernel-mode Driver Design Guide

2.5.8 Points to Consider

Keep the following points in mind when designing an NT driver: ■ Except for video and SCSI miniport drivers, every NT driver must create a device object

to represent each physical, logical, or virtual device that could be a target for an I/Orequest.

For example, a replacement parallel or serial driver should create a logical device objectfor each port. By contrast, a new virtual disk driver that presented every hard disk in themachine as one big disk device could create a single device object to represent its disk,plus some number of device objects representing disk partitions for the file systemslayered over such an intermediate driver.

■ For most NT device and intermediate drivers, the device extension part of a device objectis each driver's primary (and frequently only) global data storage area. (The driver-specific I/O stack location in IRPs can be considered an operation-specific local storagearea for some kinds of data.) Many NT drivers maintain device state and all other driver-specific or device-specific data and resources a driver needs in the driver-defined deviceextension(s) of one or more driver-created device objects.

■ NT drivers of devices that use DMA must have an AdapterControl routine and call thesystem-supplied support routines that manipulate adapter objects in order to carry outDMA transfers.

■ Drivers whose physical devices are connected to a device controller may or may notrepresent the physical controller with a driver-created controller object and implement aControllerControl routine to synchronize operations between attached devices. Generally,drivers use controller objects to synchronize operations to attached devices if thefollowing criteria hold:

1 The controller does not carry out long operations without interrupting, so the driverdoes not need to create a device-dedicated thread.

2 The devices connected to the controller are similar. That is, they are not deviceswith entirely different physical properties or operational functionality (such as thekeyboard and mouse devices that can be connected to the keyboard and auxiliarydevice controller shown in Figure 2.7).

3 The driver is designed to be monolithic: single-layered in relation to the devicecontroller and attached physical devices, rather than being designed as a port driver(for the controller) with one or more class drivers (for each attached device) layeredover the port driver.

Page 84: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Layered I/O, IRPs, and I/O Objects 2-53

Consider the synchronization of operations for the system-supplied floppy, SCSI disk,and "AT" disk drivers described in this section:

■ Floppy DriverThis driver creates a device-dedicated thread because setting up its device controllerfor an I/O operation takes so much time (up to 375ms per command byte for up to 9individually loaded bytes per I/O operation). This driver synchronizes all I/Ooperations for one or more floppy drives with its device-dedicated thread. Thefloppy driver queues IRPs to the floppy thread, which waits to complete alloperations necessary to carry out each I/O request before starting the next IRP onthe floppy controller. In effect, the floppy driver and thread synchronize operationsbetween (or among) floppy drives by serializing IRPs in the driver's interlockedwork queue and processing only one request to completion at a time.

■ SCSI Disk DriverThis driver is required to be an NT class driver. It sends requests to its disks throughthe NT SCSI port driver to HBAs driving buses with connected disk devices. TheNT SCSI port driver is responsible for synchronizing operations for all SCSI classdrivers, while the HBAs driving the SCSI buses are responsible for synchronizinghardware operations among peripheral devices on their respective buses. The NTSCSI port driver creates a set of supplemental device queues to hold IRPs sent downby the higher-level class drivers, in order to manage the synchronization of requeststo the SCSI peripheral devices on each HBA's bus(es). The NT SCSI disk classdriver simply sends each IRP down to the NT port driver, using the interface itdefines, without having to synchronize operations among its target disks. At itsdiscretion, each SCSI class driver sets up its IoCompletion routine for IRPs whenthe driver needs to determine the completion status of any particular operation itsends down to the NT SCSI port driver.

■ "AT" Disk DriverThis driver is a monolithic (single-module) driver that uses a controller object tosynchronize operations between attached disk devices. This driver programs thedevice controller (and communicates directly only with the controller hardware) inorder to carry out I/O requests to the physical disks. However, its physical devicesmight have been managed by a disk class driver layered above a (controller) portdriver, instead of by a monolithic driver.

Page 85: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

2-54 Kernel-mode Driver Design Guide

■ A driver writer with a SCSI peripheral device whose type is already handled by a system-supplied class driver has a choice about where to layer the driver:

1 The new driver can be inserted between the system-supplied class and port drivers,provided that the driver is designed with any necessary additional device objects torepresent a pseudo-HBA to the higher-level class driver. Note that such a drivermust also create any device objects it needs to represent its physical and/or logicaldevices, like any NT class driver.

2 The new driver can be layered over the NT port driver as another class driver thatdefines only the set of device objects it needs to represent its physical and/or logicaldevices.

■ NT drivers cannot (and must not attempt to) wait for a nonzero interval on a Kernel-defined dispatcher object in an arbitrary thread context. In other words, an NT driver canwait on a Kernel-defined event, semaphore, mutex, or timer object within its DriverEntry(or Reinitialize) routine but not within any other standard routine it has.

However, an NT driver can create a device-dedicated thread, which can wait on adispatcher object that the driver's other routines (except an ISR or SynchCritSectionroutine) can set to the Signaled state and reset to the Not-Signaled state. As a generalguideline, if you expect that your new device driver will often need to stall for longerthan 50 microseconds while it waits for device-state changes during I/O operations,consider implementing a driver with a device-dedicated thread, or use executive workerthreads.

Page 86: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 3 TMNT Objects and Support for Drivers

All NT drivers use certain NT objects. For example, every NT driver (except the SCSI andvideo miniport drivers, as explained in Chapter 2) must create and set up a device object torepresent each logical, virtual, and/or physical device it services.

As discussed in Chapter 2, only certain kinds of NT drivers are required to use particular NTobjects. For example, whether a given driver uses an adapter object depends on whether itsdevice uses DMA.

Any NT driver can use other NT objects, depending on the driver's design.

This chapter describes the NT objects that NT drivers use.

3.1 NT Executive Components and NT Drivers

Figure 3.1 summarizes the relationship between a chain of layered drivers and the NTexecutive components whose support routines NT drivers can call. See also Appendix A for asummary of the NT kernel-mode support routines that are useful to NT device andintermediate drivers.

Page 87: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-2 Kernel-mode Driver Design Guide

Figure 3.1 Executive Component Support for Drivers

Page 88: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-3

As explained in Chapter 2, the number of drivers in a chain of layered NT drivers dependssomewhat on the nature of the device. For example, the system-supplied drivers for SCSImass-storage devices form at least triple-driver chains, with the class and port driverscorresponding to the intermediate class and lowest-level physical device drivers shown inFigure 3.1, and with at least one file system driver layered over each class driver. (Within NT,the HBA-specific SCSI miniport driver is considered part of the NT-supplied SCSI portdriver.)

In addition, the system-supplied fault tolerant disk driver, ftdisk, might be layered between theSCSI disk class driver and file system driver(s) if the user decides to enable disk mirroring,striping, or the creation of volume sets. The NT ftdisk driver is optional, but it wouldcorrespond to the logical/virtual intermediate device driver shown in Figure 3.1.

By contrast, the system-supplied parallel and serial drivers are not part of a driver chain. Eachcorresponds to the lowest-level physical device driver shown in Figure 3.1. Each is an NTlowest-level physical device driver with respect to its set of standard driver routines. Each isalso a highest-level NT driver with respect to its position relative to the I/O Manager.

As mentioned in Chapter 2, lowest-level physical device drivers have different standard driverroutines from higher-level drivers, so Figure 3.1 shows certain objects (interrupt, controller,and adapter) close to the hardware and to the lowest-level drivers that can use these objects.The Hardware Abstraction Layer (HAL) supplies platform-specific support to the I/OManager, the Kernel, and to most lowest-level drivers. When a lowest-level driver initializes,it calls HalGetInterruptVector to obtain platform-specific arguments (system vector,DIRQL, and processor mask) so the driver can call IoConnectInterrupt to set up interruptobject(s) and register its ISR. If a device driver uses DMA, it supplies configuration-specificinformation, such as the type of bus to which its device is connected, and callsHalGetAdapter to obtain a pointer to the appropriate adapter object.

During initialization, NT drivers can call support routines provided by the ConfigurationManager to get (and supply) information in the configuration registry. Drivers also can callroutines supplied by the Object Manager to set up symbolic links between their named deviceobjects and the subsystem-specific "logical names" for devices to be stored in the registry. NTdevice drivers must call the I/O Manager's support routines to get and set information in theconfiguration registry. For more information about using the configuration registry, seeChapter 16.

Page 89: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-4 Kernel-mode Driver Design Guide

Like the I/O Manager, the Configuration Manager, Object Manager, Process Structure,Memory Manager, Executive Support, and Kernel components each define a set of opaqueobject types and/or data structures that drivers can use by calling the appropriate kernel-modesupport routines. Physical, logical, and virtual device driver writers can ignore the NT LPC(local procedure call) and Security (reference monitor) components. The I/O Manager or anNT file system driver performs any necessary inter-process communication and securityaccess checks before an IRP is sent to lower-level drivers. (The I/O Manager and ObjectManager also perform symbolic link resolutions before sending IRPs to lower drivers.)

Note that the I/O Manager provides support routines for manipulating certain objects and datastructures that are defined by other executive components, including interrupt objects, DPCobjects, timer objects and memory descriptor lists (MDLs). These I/O support routines help toimplement certain standard driver routines, and to map buffers, which can be associated withdriver-customized IRPs sent to lower-level drivers. For most driver writers, the I/O supportroutines are all their drivers need to do any of the following: ■ Register a standard InterruptService routine by calling IoConnectInterrupt and

deregister the ISR when the driver unloads by calling IoDisconnectInterrupt. ■ Register a standard DpcForIsr routine by calling IoInitializeDpcRequest, and request a

call to the DpcForIsr from the ISR by calling IoRequestDpc. ■ Register a standard IoTimer routine by calling IoInitializeTimer, and enable or disable

once-per-second calls to the IoTimer routine by calling IoStartTimer or IoStopTimer,respectively.

■ Break a too large buffer, already mapped by an MDL, into smaller mapped buffers bycalling IoBuildPartialMdl in order to carry out DMA operations on a device withlimited data transfer capabilities.

Except for NT file system drivers, the I/O Manager also sets up an associated device queueobject for each device object that drivers create when they call IoCreateDevice. It providesI/O support routines that drivers call to have IRPs routed to their StartIo entry points. TheIoStartPacket and IoStartNextPacket routines call the Kernel's device queue supportroutines on a driver's behalf.

However, an NT driver can call many of the same Kernel support routines as the I/O Managerdoes if the driver designer finds doing this necessary. Depending on the design (or on thedevice), a driver can do any of the following: ■ Set up a timer object and an associated DPC object that can be explicitly queued as a

CustomTimerDpc routine. For example, a driver might have a CustomTimerDpc, ratherthan an IoTimer routine, if its device requires variable time-out intervals or an intervalthat must be finer-grained than once per second.

■ Set up one or more DPC objects and explicitly queue driver-supplied CustomDpcroutines from the ISR. For example, a serial driver might queue a CustomDpc thatcancels pending I/O requests when its ISR detects a device error.

Page 90: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-5

■ Set up a timer object on which a device-dedicated thread or worker-thread callbackroutine can wait for a driver-specified interval. Drivers that set up their own threads (likethe floppy driver described in Chapter 2) also can have their threads wait on Kernel-defined mutex, semaphore, or event objects, as can file system drivers' worker-threadcallback routines. Drivers can set up device-dedicated threads by callingPsCreateSystemThread.

■ Set up additional device queue objects. For example, the NT SCSI port driver (describedin Chapter 2) sets up a single device object to represent an HBA, and the I/O Managerroutes IRPs through (or from) appropriate class drivers into the device queue associatedwith that HBA device object. However, the NT SCSI port driver sets up an additionaldevice queue object for each logical unit on an HBA-specific SCSI bus that is claimed bya class driver. The port driver uses these additional device queues to sort incoming IRPsinto logical-unit-specific device queues.

Because every DriverEntry routine is called in the context of a system thread, a higher-leveldriver can also wait on a Kernel event object when it loads, after the driver has chained itselfabove the next-lower-level driver by calling IoGetDeviceObjectPointer and getting a pointerto the lower driver's device object or by calling IoAttachDevice to alias its device object to alower driver's device object. For example, a class driver's DriverEntry routine might callIoBuildSynchronousFsdRequest or IoBuildDeviceIoControlRequest to set up an IRP withan associated event and, then, call the port driver (using IoCallDriver). The class driver couldwait on the event by calling KeWaitForSingleObject, while the port driver below it gathers(or sets) device-state information that the class driver needs to complete its initialization.

Drivers that use the I/O Manager's interrupt, DPC, and timer support routines can rely on theI/O Manager to provide storage for any necessary Kernel-defined objects. However, the NTKernel does not allocate memory on behalf of callers to its support routines. Consequently,any executive code (including NT drivers) that directly calls Kernel support routines mustprovide storage for the Kernel-defined objects it uses. For example, a driver that has astandard CustomTimerDpc routine must provide storage for the timer and DPC objects itneeds to call the Kernel's support routines that manipulate these objects.

Drivers that have an ISR must allocate storage for an interrupt object pointer, whose value isreturned by IoConnectInterrupt. The I/O Manager provides storage for and initializes thedriver's interrupt objects (up to one per processor in a symmetric multiprocessor machine).The Kernel actually defines the interrupt object type with an associated interrupt spin lock andsupplies KeSynchronizeExecution, which device drivers call with a driver-suppliedSynchCritSection routine to guarantee multiprocessor-safe access to data shared betweenanother driver routine and the driver's ISR.

Page 91: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-6 Kernel-mode Driver Design Guide

An NT driver can also allocate storage for, initialize, and use an executive spin lock, callingKeAcquireSpinLock and KeReleaseSpinLock to manage multiprocessor-safe access to datashared among driver routines other than an ISR. Note that a spin lock is not an NT object. Anexecutive or interrupt spin lock is, however, a Kernel-defined synchronization mechanism thatuses memory. For guidelines on how to use spin locks, see Chapter 16.

The Executive Support component, shown in Figure 3.1 between the Kernel and MemoryManagement components, provides certain support routines that require an executive spinlock, for which the caller must supply the storage. Any Executive Support routine thatcontains the word "interlocked" requires an executive spin lock as an argument. The ExecutiveSupport component also provides routines for drivers that allocate system-space (pool)memory and for drivers, such as file system drivers, that use system worker threads.

Drivers usually allocate storage for their Kernel-defined objects and spin locks (if any) in thedevice extensions of the device objects they create. Some drivers allocate storage in acontroller extension if, like the "AT" disk driver described in Chapter 2, they have aControllerControl routine. For more information about controller objects, see Section 3.4.

Most intermediate and lowest-level drivers use only a device or controller extension tomaintain necessary device state and to provide storage for other driver-determined data, suchas Kernel-defined objects, spin locks, interlocked queues, and other driver-defined data.However, drivers like the floppy driver, described in Chapter 2, can use Memory Managementroutines when they initialize to allocate contiguous or noncached internal buffers if theirdevices cannot be serviced adequately without using such buffers. For more information aboutusing memory, see Chapter 16.

Whether a driver uses Memory Management routines to manipulate MDLs depends somewhaton the nature of the device. Each driver determines whether it uses MDLs to access userbuffers when it sets up its device object(s), as explained in Section 3.2, next.

Page 92: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-7

3.2 Device Objects and Device Extensions

Like the driver object described in Chapter 2, a device object is partially opaque to NT drivers.Driver writers must know about certain field names and system-defined symbolic constantsassociated with device objects because their drivers must access these data fields through theDeviceObject pointer returned by IoCreateDevice and passed to most standard driverroutines.

Nevertheless, driver writers should be aware that the location of any data field that isaccessible through a DeviceObject pointer can change from one NT platform to another.Driver writers should also consider that "unpublished" fields within an NT object should neverbe accessed by drivers. Any driver that has dependencies on object field locations or thataccesses undocumented fields within an object also compromises its own portability from oneNT platform or version to the next.

Figure 3.2 illustrates an NT device object, representing a physical, logical, or virtual devicewithin the system.

Page 93: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-8 Kernel-mode Driver Design Guide

Figure 3.2 Device Object

Figure 3.2 shows field names and constants that are particularly important for physical andintermediate device drivers.

Page 94: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-9

3.2.1 Defining Device Extensions

For most NT intermediate and physical device drivers, the device extension is the mostimportant data structure associated with a device object. Its internal structure is driver-defined,used to maintain device state information, to provide storage for any Kernel-defined objectsthe driver uses, and to hold any data the driver must have resident (and in system space) tocarry out its I/O operations.

As mentioned in Chapter 2, every standard driver routine that is given an IRP is also given apointer to a DeviceObject that represents the target device for an I/O operation. Consequently,any driver routine given an IRP can access the corresponding DeviceExtension through thispointer to the appropriate DeviceObject. Usually, a pointer to the DeviceObject is also theinput argument to a lowest-level driver's ISR.

Figure 3.2 shows a representative set of driver-defined data for the device extension of alowest-level driver's device object. A higher-level driver would not provide storage for aninterrupt object pointer (to be passed to IoConnectInterrupt and KeSynchronizeExecution),but it would provide storage for timer and DPC objects if the driver has a CustomTimerDpcroutine. A higher-level driver also might provide storage for an executive spin lock andinterlocked work queue. A lowest-level driver must supply storage for an interrupt spin lock ifits ISR handles interrupts for two different devices on different vectors. (For more informationabout registering an ISR, see Section 3.5).

Most NT drivers find it convenient to store pointers to their device objects in their deviceextensions. Higher-level drivers almost always store pointers to the next-lower-level drivers'device objects in their device extensions. A higher-level driver must pass a pointer to the next-lower driver's device object to IoCallDriver, after it has set up the lower driver's I/O stacklocation in an IRP. Note also that any higher-level driver that creates IRPs for lower-leveldrivers must specify how many stack locations the new IRPs will need. In particular, if ahigher-level driver calls IoMakeAssociatedIrp, IoAllocateIrp, or IoInitializeIrp, it mustaccess the target DeviceObject of the next-lower-level driver to read its StackSize value, inorder to supply the correct StackSize as an argument to these support routines.

While a higher-level driver can read data from the next-lower-level driver's device objectthrough the pointer returned by IoGetDeviceObjectPointer, it should not attempt to writedata to the lower driver's device object. Furthermore, a higher-level driver should neverattempt to access the lower driver's device extension for the following reasons: ■ There is no safe way to synchronize access to a single device extension between two

drivers. ■ A pair of drivers that implement such a back-door communication scheme cannot be

upgraded individually, cannot have an intermediate driver inserted between them withoutchanging existing driver source, and cannot be recompiled and moved readily from oneNT platform to the next.

Page 95: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-10 Kernel-mode Driver Design Guide

To preserve their interoperability with lower-level drivers from one NT platform or version tothe next, higher-level drivers either must reuse the IRPs given them or must create new IRPs,and they must use IoCallDriver to communicate requests to lower-level drivers.

3.2.2 Creating a Device Object and Device Extension

A driver determines the size of a device extension when its DriverEntry routine callsIoCreateDevice to set up a device object. As shown in Figure 3.2, the caller also passesarguments that determine the following when IoCreateDevice sets up a device object: ■ A system-defined constant, indicating the Type of device represented by the device

object. (For a list of the system-defined FILE_DEVICE_xxx constants, see Appendix B.) ■ One or more ORed, system-defined constants indicating the Characteristics for certain

kinds of devices (floppy, CD-ROM, and WORM removable-media devices). Otherwise,the Characteristics value is zero. (NT drivers for removable-media devices also mustOR the DeviceObject Flags with DO_VERIFY_VOLUME if they detect (or suspect)that the media has changed during I/O operations. For more information about how tohandle removable media, see Chapter 16.)

■ A Boolean value that specifies whether a bit in the DeviceObject Flags should be setwith DO_EXCLUSIVE, indicating the driver services an exclusive device. (For anexplanation of exclusive devices, see the description of interactive device drivers inChapter 2.)

■ A pointer to the DriverObject, which was input to the driver's DriverEntry routine,associating the driver object with the physical, logical, or virtual device the callerservices.

■ A pointer to a Unicode string naming the device. A Name must be provided when thedriver creates a device object if the corresponding physical, logical, or virtual devicemust be made "visible" to user-mode callers as a file object, or if a higher-level drivermust (or may) chain itself to the creating driver. User-mode callers, including protectedsubsystems, cannot carry out device I/O operations without obtaining a handle for anamed file object associated with the device object. Higher-level drivers cannot chainthemselves to the driver of an unnamed device by calling IoGetDeviceObjectPointer orIoAttachDevice. However, a file system driver can chain itself to an unnamed (massstorage) device object during a mount operation through a volume parameter block(VPB).

Page 96: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-11

For every NT driver except file system drivers, the I/O Manager also sets up an associateddevice queue object for every successful call to IoCreateDevice. The device queue objectassociated with a device object represents a queue of IRPs bound for a driver's StartIo entrypoint after the driver is loaded. Drivers that manage their own internal IRP queues, such as thesystem-supplied floppy driver described in Chapter 2 do not use the device queue(s)associated with their device objects. Note that drivers can create additional device queueobjects like the NT SCSI port driver, as mentioned in Section 3.1. For more information aboutKernel-defined device queue objects, see Section 3.8.

If the call to IoCreateDevice succeeds, the I/O Manager provides storage for the device objectitself and for all other data structures associated with the device object, including the driver'sdevice extension, which it initializes with zeros.

3.2.3 Initializing Driver-specific Device Objects and Device Extensions

After IoCreateDevice returns, giving the caller a pointer to a DeviceObject that contains apointer to the DeviceExtension, the driver can initialize any Kernel-defined objects and othersystem-defined data structures for which it has provided storage in the DeviceExtension. Forexample, a lowest-level driver would call IoConnectInterrupt with the address of thePtrToInterruptObject(s) shown in Figure 3.2 to register its ISR, as well as Kernel-suppliedsupport routines to initialize the DpcObject and TimerObject in order to set up aCustomTimerDpc routine. A driver would also call KeInitializeSpinLock, passing a pointerto the ExecutiveSpinLock, so the driver could call ExInterlockedInsertTailList andExInterlockedRemoveHeadList to manage the queueing of IRPs in the interlocked workqueue shown in Figure 3.2.

For general guidelines on initializing and using spin locks, see Chapter 16. For moreinformation about interrupt, DPC, and timer objects, see Sections 3.5 and 3.7. (See alsoSections 3.6 and 3.9 for other ways to use DPC and timer objects.)

NT drivers also must set up certain fields in the device object for their respective devices.

IoCreateDevice sets the StackSize field of a newly created device object to 1. A lowest-levelNT driver can ignore this field. After any higher-level driver has chained itself over anotherdriver by successfully calling IoGetDeviceObjectPointer, the higher-level driver must resetthe StackSize field to that of the next-lower-level driver's device object plus 1. This ensuresthat IRPs sent to the higher-level driver will contain a driver-specific I/O stack location, plusthe correct number of I/O stack locations for all lower-level drivers in the chain.

Page 97: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-12 Kernel-mode Driver Design Guide

IoCreateDevice sets the AlignmentRequirement field of a newly created device object to theprocessor's data cache line size minus 1. To ensure that I/O buffers are aligned correctly,lowest-level physical device drivers must do the following: 1 Subtract 1 from the alignment requirement of the device. 2 Compare the result of Step 1 with the current value of the DeviceObject

AlignmentRequirement field. 3 If the device's alignment requirement is greater, reset AlignmentRequirement to the

result of Step 1. Otherwise, leave the AlignmentRequirement value as set byIoCreateDevice.

After any higher-level driver has chained itself over another driver by callingIoGetDeviceObjectPointer, the higher-level driver must reset the AlignmentRequirementfield of its newly created device object to that of the next-lower-level driver's device object.

Note that the I/O Manager updates the StackSize and AlignmentRequirement fields forcallers of IoAttachDevice. That is, the caller-created device object passed to IoAttachDevicehas its AligmentRequirement reset to that of the lower-driver's device object. The StackSizefor the caller's aliased device object is set to the value of the lower driver's device object plus1. However, after a file system driver mounts the volume containing the file object thatrepresents a lower driver's device object, an intermediate driver cannot chain itself betweenthe file system and the lower driver by calling IoAttachDevice.

If the driver creates more than one device object, the I/O Manager links the subsequentlycreated device objects to the driver object by maintaining DeviceObject NextDevice pointersin the device objects.

An intermediate or lowest-level device driver also sets a bit in the DeviceObject Flags fieldby ORing it either with DO_DIRECT_IO or with DO_BUFFERED_IO (shown in Figure 3.2)in every device object it creates. Highest-level drivers of logical or virtual devices can avoidsetting Flags for either buffered or direct I/O if the driver writer decides the additional workinvolved will pay off in better driver performance. An intermediate driver should set up theFlags field of its device object to match that of the next-lower driver's device object.

Setting up the DeviceObject Flags field with DO_DIRECT_IO or DO_BUFFERED_IOdetermines how the I/O Manager passes access to user buffers in data transfer requests sent tothe driver.

Page 98: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-13

3.2.4 Setting Up Access to User Buffers

Most physical, logical, and virtual device drivers set a bit in the DeviceObject Flags field,shown in Figure 3.2, by ORing the Flags either with DO_BUFFERED_IO or withDO_DIRECT_IO in each device object they create. NT drivers, including file system drivers,must choose one of the following three ways to access user buffers for data transfers: 1 If a driver services an interactive (slow) device and/or usually transfers relatively small

chunks of data at a time, the driver should set up its device object(s) requesting bufferedI/O. Using buffered I/O for small, interactive transfers improves overall physical memoryusage, because the Memory Manager component need not lock down at least a fullphysical page for each transfer, as it does for drivers that request direct I/O.

Generally, NT video, keyboard, mouse, serial, and parallel drivers request buffered I/O. 2 If a driver services a device that can transfer large chunks of data at a time, the driver

should set up its device object(s) requesting direct I/O. Using direct I/O for largetransfers improves a driver's performance both by reducing its interrupt overhead and byeliminating the memory allocation and copying operations inherent in buffered I/O.

Generally, NT mass storage device drivers request direct I/O for transfer requests,including lowest-level drivers that use DMA or programmed I/O (PIO), as well as anyintermediate drivers chained above them. Note that even drivers that request direct I/Ouse buffered I/O for certain requests. In particular, device-specific I/O control codes (seeAppendix B) for IRP_MJ_DEVICE_CONTROL requests that involve small transfers ofdata are usually buffered, whether the driver has ORed its DeviceObject Flags withDO_DIRECT_IO or not.

3 A driver writer can choose to set up device object(s) requesting neither direct norbuffered I/O if and only if the driver will always be called in the context of the original,user-mode thread that requests an I/O operation.

The following subsections describe how ORing the DeviceObject Flags field withDO_BUFFERED_IO, DO_DIRECT_IO, or neither affects data transfer requests sent to NTdrivers, including how the data can be stored in physical memory and how a driver can accessthat memory.

3.2.4.1 Using Buffered I/O

Figure 3.3 illustrates how the I/O Manager sets up an IRP, requesting a transfer operation, fordrivers that OR their DeviceObject Flags with DO_BUFFERED_IO. As Figure 3.3 shows,some range of user-space virtual addresses represents the current thread's buffer, whosecontents might be stored somewhere within a range of page-based physical addresses. Figure3.3 also shows an overview of how drivers can use the Irp SystemBuffer to transfer data for aread request.

Page 99: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-14 Kernel-mode Driver Design Guide

Figure 3.3 Buffered I/O for User Buffers

Page 100: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-15

As shown in Figure 3.3, when a driver has ORed the DeviceObject Flags field withDO_BUFFERED_IO: 1 The I/O Manager services the current thread's read request, for which the thread passes a

range of user-space virtual addresses representing a buffer. 2 The I/O Manager checks the user-supplied buffer for accessibility and calls

ExAllocatePool to create a resident system-space buffer the size of the user-suppliedbuffer. It provides access to the newly allocated SystemBuffer in the IRP it sends to thedriver. (If Figure 3.3 showed a write request, the I/O Manager would copy data from theuser buffer into the system buffer before it sent the IRP to the driver.)

3 For the read request shown in Figure 3.3, the driver reads data from the device into thesystem-space buffer.

4 When the driver has called IoCompleteRequest with the IRP and the original thread isagain current, the I/O Manager copies the read-in data from the system buffer into theuser buffer. It also calls ExFreePool to release the system buffer and disposes of the IRP.

After the I/O Manager has created a system-space buffer for the driver, the requesting threadcan be swapped out and its physical memory can be reused by another thread, possibly by athread belonging to another process. However, the system-space virtual address rangesupplied in the IRP remains valid until the driver calls IoCompleteRequest with the IRP.

NT drivers for devices that do not transfer large amounts of data at a time, such as interactivedevices, can use buffered I/O. However, NT drivers that transfer large amounts of data at atime (in particular, drivers that do multi-page transfers) should not attempt to use buffered I/O.As the system runs, nonpaged system pool can become fragmented so that the I/O Managercannot allocate large, contiguous system-space buffers to send in IRPs for such a driver.

Note that all NT drivers use buffered I/O for certain IRP_MJ_xxx. Even NT drivers that set uptheir device objects for direct I/O use buffered I/O for most requests except IRP_MJ_READ,IRP_MJ_WRITE, and, possibly, certain IRP_MJ_DEVICE_CONTROL requests that requirelarge data transfers.

3.2.4.2 Using Direct I/O

Figure 3.4 illustrates how the I/O Manager sets up an IRP, requesting a transfer operation, fordrivers that OR the DeviceObject Flags field with DO_DIRECT_IO. As Figure 3.4 shows,some range of user-space virtual addresses represents the current thread's buffer, whosecontents might actually be stored on some number of physically discontiguous pages. Amemory descriptor list (MDL) is created to describe this buffer. As shown in Figure 3.4, anMDL is a Memory-Manager-defined data structure that maps a particular virtual address rangeto one or more paged-based physical address ranges. Figure 3.4 also shows an overview ofhow drivers can use the Irp MdlAddress to transfer data for a read request.

Page 101: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-16 Kernel-mode Driver Design Guide

Figure 3.4 Direct I/O on User Buffers

Page 102: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-17

As shown in Figure 3.4, when a driver has ORed the DeviceObject Flags field withDO_DIRECT_IO: 1 The I/O Manager (or an NT file system driver) services the current thread's read request,

for which the thread passes a range of user-space virtual addresses representing a buffer. 2 The I/O Manager checks the user-supplied buffer for accessibility and locks down the

corresponding physical pages by calling MmProbeAndLockPages with an MDL, whichspecifies the range of virtual addresses for the user buffer. MmProbeAndLockPagesalso fills in the corresponding physical address range(s) in the MDL. As Figure 3.4shows, an MDL for a virtual range can have several corresponding page-based physicaladdress entries, and the virtual range for a buffer might begin and end at some byte offsetfrom the start of the first and last pages described by an MDL.

As Figure 3.4 also shows, the I/O Manager provides access to this MDL (MdlAddress)in an IRP that requests a transfer operation. Until the I/O Manager or file system callsMmUnlockPages after the driver completes the IRP, the physical pages described in theMDL remain locked down and "assigned to" the buffer. However, the virtual addresses insuch an MDL can become invisible (and invalid), even before the IRP is sent to thedevice driver or to any intermediate driver that might be layered above the device driver.

3 If the device driver uses system or busmaster DMA, it calls MmGetMdlVirtualAddresswith the Irp MdlAddress to get an index for the MDL's page-based entries. If the deviceuses PIO and the driver requires system (virtual) addresses, the driver callsMmGetSystemAddressForMdl with the Irp MdlAddress to doubly map the user-spacevirtual addresses in the MDL to a system-space address range (AliasBuff in Figure 3.4).

4 If the device driver uses system or busmaster DMA, it calls IoMapTransfer in order toread data from the device directly into physical memory when the driver'sAdapterControl routine has access to a DMA channel and/or map registers. If the deviceuses PIO, the driver uses the doubly mapped MDL's system-space virtual address rangeto read data into memory.

When the driver completes the IRP by calling IoCompleteRequest, the I/O Manager or filesystem releases the MDL's doubly mapped system-space range if the driver calledMmGetSystemAddressForMdl, unlocks the pages described in the MDL, and disposes ofthe MDL, along with the IRP, on the driver's behalf. For better performance, NT driversshould avoid doubly mapping MDL physical addresses to system space, as described in Step3, unless they must use virtual addresses. Releasing a doubly mapped system-space addressrange causes every processor in the machine to have its data cache flushed.

Page 103: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-18 Kernel-mode Driver Design Guide

Note that the current user thread's buffer(s) and the thread itself are guaranteed to be residentin physical memory only while that thread is current, and that the buffer's contents could bepaged out to secondary storage while another process's threads are run. When anotherprocess's thread is run, the system physical memory for the requesting thread's buffer can beoverwritten unless the NT Memory Manager has locked down and preserved thecorresponding physical pages that contain the original thread's buffer.

However, the original thread's virtual addresses for its buffer do not remain visible whileanother thread is current, even if the Memory Manager does preserve the buffer's physicalpages. Consequently, NT drivers cannot use a virtual address returned byMmGetMdlVirtualAddress to access memory. Callers of this routine must pass its results toIoMapTransfer (along with the Irp MdlAddress) in order to transfer data using DMA.Drivers that call IoMapTransfer must use an adapter object, described in Section 3.3, next.For more information on maintaining cache coherency during DMA transfers, see alsoChapter 16.

3.2.4.3 Using Neither Direct Nor Buffered I/O

Setting up device objects for neither direct nor buffered I/O causes the I/O Manager to passthe original user-space virtual addresses in IRPs sent to the driver. Consequently, onlyhighest-level drivers, such as NT file systems, can set up their device objects without setting abit in the DeviceObject Flags field by ORing it with DO_DIRECT_IO orDO_BUFFERED_IO, because the driver must be executing in the context of the calling threadin order to access its buffer(s) safely.

Note that an intermediate or lowest-level driver cannot always meet this condition. Forexample, if a requesting thread waits on the completion of an I/O operation or if a higher-leveldriver (particularly a file system) is layered over the intermediate or lowest-level driver, sucha driver's routines are unlikely to be called in the context of the requesting thread.

Page 104: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-19

When the I/O Manager sends an IRP with the current thread's user-space virtual addresses fora buffer, a driver that did not OR its DeviceObject Flags with DO_BUFFERED_IO orDO_DIRECT_IO must do the following: 1 Check the validity of the user buffer's address range and check whether the appropriate

read or write access is permitted to the buffer. Note that the driver must wrap its accessesto the buffer's address range within a driver-supplied exception handler in case a userthread attempts to change the access rights for the buffer while the driver is accessingmemory.

2 Do one of the following: ■ Carry out its own double-buffering operations, as the I/O Manager does for drivers

that use buffered I/O. ■ Create its own MDLs and lock down the buffer by calling Memory Manager's

support routines as the I/O Manager does for drivers that use direct I/O. ■ Perform all necessary operations on the user buffer directly in the context of the

calling thread. Note that the driver must wrap its access to the buffer within a driver-supplied exception handler in case a user thread changes either the access rights forthe buffer or the data in the buffer while the driver is accessing memory.

In effect, such a driver must choose on a per-IRP basis whether to do buffered I/O, direct I/O,or I/O in the context of the calling thread and handle any exceptions that might occur in auser-mode thread context. Such a driver must manage its own user buffer accesses, double-buffering operations, and memory mappings, as necessary, instead of letting the I/O Managerhandle these operations for the driver.

Page 105: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-20 Kernel-mode Driver Design Guide

3.3 Adapter Objects and DMA

As mentioned in Section 3.2.4.2, an NT device driver must use a system-created adapterobject if it sets up its device objects for direct I/O and its device uses DMA.

Two kinds of lowest-level NT drivers must use adapter objects: 1 Drivers whose devices use the system DMA controller (also called slave devices) - such a

device is said to "use system DMA." 2 Drivers whose devices are busmaster adapters - such a device, which arbitrates with the

system for use of the I/O bus, is said to "use busmaster DMA."

As these kinds of device drivers initialize at system boot, they call the NT HardwareAbstraction Layer (HAL) to create a platform-specific set of adapter objects, representingDMA controller channels and busmaster devices. The NT HAL, like the NT-supplied SCSIport driver described in Chapter 2, is implemented as a dynamic link library so that drivers canlink themselves against each platform-specific HAL (or NT SCSI port driver) and call thesame support routines on every NT platform.

For any machine running NT, the set of HAL-created adapter objects usually includes thefollowing: ■ An adapter object for each system DMA controller channel to which a slave device is

attached ■ An adapter object for each busmaster device in the machine

Note that the NT-supplied SCSI port driver is responsible for creating and managing adapterobjects for SCSI HBAs. An HBA-specific miniport driver supplies the necessary data for theNT SCSI port driver to create a corresponding adapter object when the miniport driver'sHwGetConfigInfo routine executes. For more information about SCSI miniport drivers, seethe Kernel-mode Driver Reference.

Page 106: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-21

3.3.1 Map Registers

An NT HAL must set up adapter objects that support DMA for a wide variety of DMAdevices and types of I/O buses on different platforms. For example, most ISA DMAcontrollers, slave devices, and busmaster devices do not have enough address lines to accessthe full 4-gigabyte system physical address space of a 32-bit CPU. By contrast, EISA DMAdevices generally have more than enough address lines to access the full system physicaladdress space in 32-bit microprocessors. Consequently, the HAL provides mappings betweenthe logical address ranges that these DMA devices can access and system physical addressranges.

The HAL uses map registers, associated with NT adapter objects, to represent its mappings ofdevice-accessible logical address ranges to system physical address ranges. During DMAtransfers, the HAL uses each map register to alias a device-accessible logical page to a page ofphysical memory in the CPU. In effect, map registers provide scatter/gather support for NTdrivers that use DMA whether their devices have scatter/gather capabilities or not.

Figure 3.5 illustrates such a physical-to-logical address mapping for the driver of an ISADMA device without scatter/gather capabilities.

Page 107: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-22 Kernel-mode Driver Design Guide

Figure 3.5 Physical, Logical, and Virtual Address Mappings

As Figure 3.5 shows, three map registers are used to alias three paged ranges of data in systemphysical memory to three page-sized ranges of low-order logical addresses for an ISA DMAdevice. During DMA operations, the device would use logical addresses to access systemmemory. For a comparable EISA DMA device, three map registers would also be used forthree page-sized ranges of data. However, the mapped logical address ranges would notnecessarily be identical to the corresponding physical address ranges, so an EISA devicewould also use logical addresses to access system memory.

Note also the correspondence between a map register and a virtual-to-physical entry in theMDL shown in Figure 3.5: ■ Each map register and each virtual entry in an MDL maps at most a full physical page of

data for a DMA transfer operation. ■ Each map register and each virtual entry in an MDL might map less than a full page of

data. For example, the initial virtual entry in an MDL can map to an offset from thephysical page boundary, as shown previously in Figure 3.4.

■ Each map register and each virtual entry in an MDL maps, at the very least, one byte.

Page 108: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-23

In IRPs requesting a read or write operation, each virtual entry in the opaque-to-drivers MDLat Irp MdlAddress represents a page boundary in the system physical memory for a userbuffer. Similarly, each additional map register needed for a single DMA transfer represents apage boundary in the device-accessible logical address range aliased to system physicalmemory.

On every platform, each NT adapter object has an associated set of one or more map registerslocated at a platform-specific (and opaque-to-drivers) base address. From an NT driver's pointof view, the MapRegisterBase shown in Figure 3.5 is a handle for a set of map registers thatcould be hardware registers in a chip, in a system DMA controller, or in a busmaster adapter,or could even be virtual registers in system memory.

However, the number of map registers available with an adapter object can vary for differentdevices and NT platforms. For example, the HAL can make more map registers available todrivers that use system DMA on some platforms than on others because the DMA controllersin different NT platforms have different capabilities.

3.3.2 Getting an NT Adapter Object

When it initializes, an NT driver that uses system or busmaster DMA calls HalGetAdapter inorder to get a pointer to a HAL-created adapter object and to determine the maximum numberof map registers available for each transfer operation. Figure 3.6 illustrates such a call toHalGetAdapter.

Page 109: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-24 Kernel-mode Driver Design Guide

Figure 3.6 Getting an Adapter Object

As Figure 3.6 shows, the driver supplies certain kinds of information to HalGetAdapter in anNT-defined DEVICE_DESCRIPTION structure. The required data includes information aboutthe features of the driver's device, such as whether the device is a busmaster, whether it hasscatter/gather capabilities, and how many bytes of data the device can transfer at a time(MaximumLength). Note that MaximumLength effectively specifies the maximum number ofmap registers the driver could use for each transfer operation, assuming the HAL could makean unlimited number of map registers available on every NT platform.

Page 110: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-25

The required DEVICE_DESCRIPTION data also includes certain platform-specificinformation, which the driver can obtain from the NT configuration registry, such as theplatform-specific number of the bus that a driver of a busmaster device controls. For moreinformation about using the configuration registry, see Chapter 16.

As Figure 3.6 also shows, the DEVICE_DESCRIPTION structure includes some fields thatmight not be pertinent to every kind of DMA device on every NT platform. For example, anEISA or ISA DMA device would not use a particular DmaPort as a microchannel-type devicewould. Each NT driver should supply appropriate values for DEVICE_DESCRIPTION fieldsthat are pertinent and should set the values for all other fields to zero.

The driver of a slave device should not claim it supports scatter/gather unless the device iscapable of waiting for the system DMA controller to be reprogrammed when a request mustbe broken up into two or more DMA operations.

As Figure 3.6 also shows, HalGetAdapter returns both a pointer to an adapter object and aplatform-specific or device-specific value indicating how many map registers are availablewith the adapter object for each DMA transfer operation. As mentioned in Section 3.3.1, thisvalue can vary from device to device and from platform to platform. Generally, the HALassigns values according to the following criteria: ■ If possible, the HAL returns a value that is one more than the number of map registers

needed to transfer MaximumLength bytes, as specified in the driver's call toHalGetAdapter.

■ Otherwise, the HAL returns a lesser value that is as large as possible for the particularplatform.

In other words, the HAL usually gives each driver enough map registers to maximize DMAthroughput for its device, but the HAL can return a lesser value on some platforms.

Page 111: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-26 Kernel-mode Driver Design Guide

3.3.3 Splitting Transfer Requests

Any NT driver might need to split up a transfer request and carry out more than one DMAtransfer operation to satisfy a given IRP, depending on the following: ■ The number of map registers available to the driver on a given platform. ■ The value of Length (bytes of data to be transferred) in the driver's I/O stack location in

the current IRP. ■ The number of page boundaries in system physical memory for the buffer into which or

from which the driver is to transfer data. ■ Any device-specific constraints on the driver's DMA operations.

NT drivers can use the macro ADDRESS_AND_SIZE_TO_SPAN_PAGES to determine howmany map registers are needed to transfer all the data requested in a given IRP, as follows: 1 Call MmGetMdlVirtualAddress, passing a pointer to the MDL at Irp MdlAddress, to

get the starting virtual address for the buffer. (Note that the driver must not attempt toaccess memory using this virtual address. The value returned byMmGetMdlVirtualAddress is an index into the MDL, not necessarily a valid address.)

2 Pass the returned index and the value of Length (in the driver's I/O stack location of theIRP) to ADDRESS_AND_SIZE_TO_SPAN_PAGES.

If the value returned by ADDRESS_AND_SIZE_TO_SPAN_PAGES is greater than thenumber of available map registers returned by HalGetAdapter, the driver cannot transfer allrequested data for this IRP in a single DMA operation. It must do the following: ■ Split the buffer into pieces that are sized to suit the number of available map registers and

any device-specific DMA constraints. ■ Carry out as many DMA operations as it takes to satisfy the transfer request.

Page 112: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-27

For example, suppose ADDRESS_AND_SIZE_TO_SPAN_PAGES indicates that 12 mapregisters are needed to satisfy a given transfer request but a driver has only five available mapregisters on this platform and no device-specific DMA constraints. Such a driver must carryout three (12/5 + 1 = 3) DMA transfer operations (and call IoMapTransfer at least threetimes) in order to transfer all the data requested by this IRP.

The system-supplied drivers use various techniques to split up a DMA transfer when there arenot enough map registers for a given IRP's request. For example, the system-supplied SCSIclass drivers split up large transfer requests for the underlying NT SCSI port driver. A SCSIclass driver allocates an additional IRP for each piece of a given transfer request, registers itsIoCompletion routine with each IRP to track the status of the full transfer request, and sendsthe IRP(s) on to the port driver with IoCallDriver, as explained in Chapter 2.

However, other class/port drivers can use this technique only if the class driver can determinehow many map registers are available to the port driver on each NT platform. In other words,such a pair of drivers must define a private interface to pass configuration information aboutthe number of available map registers from the port driver to the class driver.

Other class/port drivers need not use this technique, and a monolithic device driver must splitup large transfer requests for itself. Such drivers usually split a large request into pieces andcarry out a sequence of DMA operations in order to satisfy the current IRP.

The following sections, which describe how monolithic drivers of DMA devices use NT-supplied support routines to satisfy transfer requests, assumes the following: ■ The driver has a standard StartIo routine, rather than setting up and managing an internal

queue of IRPs. ■ The driver has an internal routine to split those transfer requests for which an insufficient

number of map registers is available and has no device-specific DMA constraints.

In other words, these sections describe the simplest possible technique for NT device drivers'DMA operations, but individual NT drivers do not necessarily use exactly the sametechniques. For any NT driver of a DMA device, the driver writer determines which driverroutine(s) should split up large DMA transfer requests, depending on the driver model(class/port, monolithic, etc.), on the device's features, and on any device-specific DMAconstraints the driver must handle.

Page 113: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-28 Kernel-mode Driver Design Guide

3.3.4 Using System DMA

NT drivers of slave devices use one of the following types of system-provided DMA support: ■ Packet-based DMA if the driver need not use the system DMA controller(s)'

autoinitialize mode ■ Common-buffer DMA if the driver does use the autoinitialize mode

The next two subsections explain each of these types of DMA support separately.

3.3.4.1 Packet-based System DMA

NT drivers of slave devices that use packet-based DMA call the following general sequence ofsupport routines as they process an IRP requesting a DMA transfer: 1 KeFlushIoBuffers just before attempting to allocate the system DMA controller (See Chapter 16 for more information about maintaining cache coherency during DMA

with KeFlushIoBuffers and IoFlushAdapterBuffers.) 2 IoAllocateAdapterChannel when the driver is ready to program its device for DMA and

needs the system DMA controller 3 MmGetMdlVirtualAddress to get an index into the MDL, required as an argument in

the initial call to IoMapTransfer, and IoMapTransfer to program the the system DMAcontroller for the transfer operation

Note that a driver might need to call IoMapTransfer more than once to transfer all therequested data, as explained in Section 3.3.3.

4 IoFlushAdapterBuffers just after each DMA transfer operation to/from the slave device If a driver must call IoMapTransfer more than once to transfer all the requested data, it

must call IoFlushAdapterBuffers as many times as it calls IoMapTransfer. 5 IoFreeAdapterChannel as soon as all the requested data has been transferred

The PADAPTER_OBJECT type pointer, returned by HalGetAdapter, is a required argumentto each of these routines except KeFlushIoBuffers and MmGetMdlVirtualAddress, whichrequire a pointer to the MDL at Irp MdlAddress.

Page 114: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-29

Individual NT drivers call this sequence of support routines at different points, depending onhow each driver is implemented to service its device. For example, one driver's StartIo routinemight make the call to IoAllocateAdapterChannel, another driver might make this call froma routine that removes IRPs from a driver-created interlocked queue, and still another drivermight make this call when the slave device indicates it is ready to transfer data.

3.3.4.1.1 Allocating an Adapter Channel for Packet-based DMA

A driver calls KeFlushIoBuffers and IoAllocateAdapterChannel after its Dispatch entrypoint for IRP_MJ_READ and/or IRP_MJ_WRITE requests (or any other request that requiresa DMA transfer) has already checked the validity of the IRP's parameters (if necessary),possibly queued the IRP to another driver routine for further processing, and the transferrequest is the current IRP requiring a device I/O operation. Figure 3.7 illustrates such a call toIoAllocateAdapterChannel.

Page 115: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-30 Kernel-mode Driver Design Guide

Figure 3.7 Allocating the System DMA Controller

The driver routine that calls IoAllocateAdapterChannel must be executing atDISPATCH_LEVEL IRQL when this call occurs. For more information about support-routine-specific IRQL requirements, see Appendix A. For more information about thehardware priorities at which NT drivers' standard routines execute, see Chapter 16.

As Figure 3.7 shows, the driver must supply more than a pointer to the adapter object returnedby HalGetAdapter when it calls IoAllocateAdapterChannel. Along with a pointer to thedevice object, it must supply the entry point for its AdapterControl routine and a pointer toany driver-determined context information the AdapterControl routine will use.

IoAllocateAdapterChannel queues the driver's AdapterControl routine, which executes whenthe system DMA controller is assigned to this driver and a set of map registers (described inSection 3.3.1) has been allocated for the driver's DMA operation(s).

Page 116: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-31

On entry, an AdapterControl routine is given pointers to the DeviceObject and Context passedin the call to IoAllocateAdapterChannel, as well as a handle (PVOID MapRegisterBase) forthe allocated map registers, as shown in Figure 3.7. The AdapterControl routine also is given apointer to the CurrentIrp if the driver has a StartIo routine. If the driver manages its ownqueueing of IRPs (instead of having a StartIo routine), the driver can include a pointer to theCurrentIrp as part of the Context data it passes when it calls IoAllocateAdapterChannel.

The AdapterControl routine usually does the following: ■ Saves (or initializes) whatever context the driver maintains about DMA operations, such

as the MapRegisterBase handle the driver must pass to IoMapTransfer andIoFlushAdapterBuffers

■ Calls MmGetMdlVirtualAddress followed by IoMapTransfer (described in the nextsection)

■ Sets up the slave device to start the transfer operation ■ Returns the value KeepObject.

As Figure 3.7 shows, an AdapterControl routine must return a system-defined value of typeIO_ALLOCATION_ACTION. For NT drivers that use system DMA, the AdapterControlroutine must return the value KeepObject. This allows the driver to retain "ownership" of thesystem DMA controller and allocated map registers until it has transferred all the requesteddata.

Note that an AdapterControl routine cannot wait for the slave device to carry out the DMAoperation, so an AdapterControl routine must at least do the following: ■ Save context information, particularly the MapRegisterBase handle, in the driver's device

extension, controller extension, or other driver-accessible storage area (nonpaged poolallocated by the driver).

■ Return KeepObject.

Another driver routine (probably the DpcForIsr) must call IoFlushAdapterBuffers when eachDMA transfer operation is complete. The DpcForIsr (or other driver routine) also must callIoMapTransfer (and IoFlushAdapterBuffers) again if it is necessary for the driver to set upthe DMA controller more than once to satisfy the current IRP's transfer request.

When a driver has satisfied the current IRP's request, it must call IoFreeAdapterChannel.This routine should be called immediately following the last call to IoFlushAdapterBuffersfor the current IRP, so that other drivers can use the system DMA controller for their transferoperations.

Note that the driver of a slave device with scatter/gather capabilities should also returnKeepObject from its AdapterControl routine. Such a device must be capable of waiting whilethe system DMA controller is reprogrammed between DMA operations when the driver mustsplit up a given DMA request. On some platforms, these kinds of devices can transfer at mosta page of data per DMA operation because the HAL can assign only a single map register tothe driver of such a device.

Page 117: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-32 Kernel-mode Driver Design Guide

3.3.4.1.2 Setting Up the System DMA Controller for Packet-based DMA

When IoAllocateAdapterChannel transfers control to a driver's AdapterControl routine, thedriver "owns" the system DMA controller and a set of map registers. However, the DMAcontroller must be set up for a transfer operation by calling the following routines: 1 MmGetMdlVirtualAddress with the MDL at Irp MdlAddress to get an index for the

system physical address where the transfer should start The return value is a required argument (PVOID CurrentVa) to IoMapTransfer. 2 IoMapTransfer to set up the system DMA controller before the driver sets up its device

for the transfer operation

Figure 3.8 illustrates such a call to IoMapTransfer.

Figure 3.8 Programming the System DMA Controller

Page 118: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-33

As Figure 3.8 shows, the driver supplies the following arguments to IoMapTransfer: ■ The PADAPTER_OBJECT pointer returned by HalGetAdapter (see Section 3.3.2) ■ A pointer to the MDL at Irp MdlAddress for the current IRP ■ The MapRegisterBase handle passed to the driver's AdapterControl routine by

IoAllocateAdapterChannel ■ The value returned by MmGetMdlVirtualAddress if this is the first call to

IoMapTransfer for this IRP Otherwise, the driver should supply an updated CurrentVa value, indicating where in the

buffer the next transfer operation should start. (How to calculate an updated CurrentVa isdescribed later.)

■ A pointer to a variable (PULONG Length, shown in Figure 3.8 asIrpLengthNextTransfer)

If the driver can transfer all the requested data with a single call to IoMapTransfer forthis IRP and has no device-specific constraints on its DMA operations, Length can be setto the value of Length in the driver's I/O stack location of the IRP. At most, the Length inbytes can be (PAGE_SIZE * the number of available map registers returned byHalGetAdapter). Otherwise, the driver must split up the request, as explained in Section3.3.3 and must update the value of Length in subsequent calls to IoMapTransfer for thecurrent IRP.

■ A Boolean value, indicating the direction of the transfer operation (TRUE for a requestedtransfer from system memory to the device)

IoMapTransfer returns a logical address, which drivers that use system DMA should ignore.When IoMapTransfer returns control, the driver should set up its device for the DMAoperation. When the device indicates that its current DMA operation has completed, the drivershould call IoFlushAdapterBuffers (usually in the DpcForIsr routine).

Note that the DpcForIsr (or another driver routine that completes a DMA operation) callsIoFlushAdapterBuffers to ensure that any data cached in the system DMA controller is readinto system memory or written out to the device. The same routine also must callIoMapTransfer again if it is necessary to reprogram the system DMA controller to transfermore data for the current IRP and IoFlushAdapterBuffers again following each transferoperation.

Page 119: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-34 Kernel-mode Driver Design Guide

If the driver must call IoMapTransfer more than once for the current IRP, it supplies thesame adapter object pointer, MDL pointer, MapRegisterBase handle, and transfer direction inevery call. However, the driver must update the CurrentVa and Length arguments before itmakes the second and any subsequent calls to IoMapTransfer. To calculate an updated valuefor each of these arguments, use the following formulas: ■ CurrentVa = CurrentVa + (Length requested in the preceding call to IoMapTransfer) ■ Length = Minimum (remaining Length to be transferred, (PAGE_SIZE * number of

available map registers returned by HalGetAdapter))

The context information each driver maintains about its DMA transfers, such as theIrpLengthNextTransfer, IrpLengthDMAedSoFar, and MdlCurrentVa shown in Figure 3.8,depends on the driver writer.

When all the requested transfer is complete (or the driver must return an error status for theIRP), the driver should call IoFreeAdapterChannel promptly to release the system DMAcontroller for other drivers (and this driver) to use.

3.3.4.2 Common-buffer System DMA

A driver that uses the system DMA controller(s)' autoinitialize mode must allocate memoryfor a buffer into which or from which DMA transfers can be carried out. Such a driver mustcall HalAllocateCommonBuffer when it initializes in order to provide this buffer. Figure 3.9illustrates such a driver's call to HalAllocateCommonBuffer.

Figure 3.9 Allocating a Common Buffer for System DMA

Page 120: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-35

As Figure 3.9 shows, the driver must pass a pointer to the adapter object, returned byHalGetAdapter, along with the Length in bytes requested for its buffer. To use memoryeconomically, the input Length either should be less than or equal to PAGE_SIZE or shouldbe an integral multiple of PAGE_SIZE. If the call succeeds, HalAllocateCommonBufferreturns two different types of pointers to the buffer: 1 The logical address of the buffer (BuffLogicalAddress in Figure 3.9), for which the driver

must provide storage but which it can ignore thereafter 2 The virtual address of the buffer (BuffVirtualAddress), which the driver should also store

so that it can build an MDL describing its buffer for DMA operations

If HalAllocateCommonBuffer returns a NULL pointer, the driver should free any systemresources it has already claimed, call IoDeleteDevice to release any device objects it hasalready created, and so forth. The DriverEntry routine also should returnSTATUS_INSUFFICIENT_RESOURCES, because the driver must not be loaded when itcannot respond to data transfer requests.

Otherwise, the driver must call IoAllocateMdl with the virtual address of the buffer returnedby HalAllocateCommonBuffer and the Length of its buffer to allocate an MDL. It shouldthen call MmBuildMdlForNonPagedPool with the pointer returned by IoAllocateMdl tomap the address range for its buffer.

NT drivers of slave devices that use a common buffer for DMA call the following generalsequence of support routines as they process an IRP requesting a DMA transfer: 1 At the driver writer's discretion, RtlMoveMemory to copy data from a locked-down user

buffer into the driver-allocated common buffer for a transfer to the device 2 IoAllocateAdapterChannel when the driver is ready to program its device for DMA and

needs the system DMA controller 3 IoMapTransfer with the MDL, describing the driver-allocated common buffer, to

program the the system DMA controller for the transfer operation Note that such a driver calls IoMapTransfer only once to set up the system DMA

controller to use its common buffer. During a transfer, the driver can callHalReadDmaCounter to determine how many bytes remain to be transferred, and ifnecessary, call RtlMoveMemory to copy more data to/from a user buffer.

4 IoFlushAdapterBuffers when the driver has completed its DMA transfer to/from theslave device

5 IoFreeAdapterChannel as soon as all the requested data has been transferred

The PADAPTER_OBJECT type pointer, returned by HalGetAdapter, is a required argumentto each of these routines.

Page 121: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-36 Kernel-mode Driver Design Guide

Individual NT drivers call this sequence of support routines at different points, depending onhow each driver is implemented to service its device. For example, one driver's StartIo routinemight make the call to IoAllocateAdapterChannel, another driver might make this call froma routine that removes IRPs from a driver-created interlocked queue, and still another drivermight make this call when the slave device indicates it is ready to transfer data.

3.3.4.2.1 Allocating an Adapter Channel for Common-buffer System DMA

A driver calls IoAllocateAdapterChannel after its Dispatch entry point for IRP_MJ_READand/or IRP_MJ_WRITE requests (or any other request that requires a DMA transfer) hasalready checked the validity of the IRP's parameters (if necessary), queued one or more IRPsto another driver routine for further processing, and possibly loaded its common buffer withdata to be transferred. (See Figure 3.7 for an illustration of a call toIoAllocateAdapterChannel.)

The driver routine that calls IoAllocateAdapterChannel must be executing atDISPATCH_LEVEL IRQL when the call occurs. For more information about support-routine-specific IRQL requirements, see Appendix A. For more information about the hardwarepriorities at which NT drivers' standard routines execute, see Chapter 16.

IoAllocateAdapterChannel queues the driver's AdapterControl routine, which executes whenthe system DMA controller is assigned to this driver and a set of map registers (described inSection 3.3.1) has been allocated for the driver's DMA operation(s).

On entry, an AdapterControl routine is given pointers to the device object and context passedin the call to IoAllocateAdapterChannel, as well as a handle for the allocated mapregister(s). The AdapterControl routine also is given a pointer to the current IRP if the driverhas a StartIo routine. If the driver manages its own queueing of IRPs (instead of having aStartIo routine), the driver can include a pointer to the current IRP as part of the context data itpasses when it calls IoAllocateAdapterChannel.

The AdapterControl routine usually does the following: ■ Saves (or initializes) whatever context the driver maintains about DMA operations, such

as the MapRegisterBase handle the driver must pass to IoMapTransfer andIoFlushAdapterBuffers

■ Calls IoMapTransfer with the MDL describing the driver-allocated common buffer ■ Sets up the slave device to start the transfer operation ■ Returns the value KeepObject

For NT drivers that use the system DMA controller(s)' autoinitialize mode, theAdapterControl routine must return the value KeepObject. This allows the driver to retain"ownership" of the system DMA controller and allocated map register(s) until it hastransferred all the data.

Page 122: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-37

Note that an AdapterControl routine cannot wait for the slave device to carry out the DMAoperation, so an AdapterControl routine must at least do the following: ■ Save context information, particularly the MapRegisterBase handle, in the driver's device

extension, controller extension, or other driver-accessible storage area (nonpaged poolallocated by the driver).

■ Return KeepObject.

Another driver routine (probably the DpcForIsr) must call IoFlushAdapterBuffers andIoFreeAdapterChannel when the DMA transfer operation is complete.

3.3.4.2.2 Setting Up the System DMA Controller for Common-buffer DMA

When IoAllocateAdapterChannel transfers control to a driver's AdapterControl routine, thedriver "owns" the system DMA controller and a set of map registers. However, the driver mustcall IoMapTransfer to set up the system DMA controller to use the driver-allocated commonbuffer before the driver sets up its device for the transfer operation. (See Figure 3.8 for anillustration of calls to IoMapTransfer.)

The driver supplies the following arguments to IoMapTransfer: ■ The PADAPTER_OBJECT pointer returned by HalGetAdapter (see Section 3.3.2) ■ A pointer to the MDL describing the driver-allocated common buffer (see Section

3.3.4.2) ■ The MapRegisterBase handle passed to the driver's AdapterControl routine by

IoAllocateAdapterChannel ■ A pointer to a variable (PULONG Length) indicating the size in bytes of the driver-

allocated common buffer ■ A Boolean value, indicating the direction of the transfer operation (TRUE for a requested

transfer from system memory to the device)

IoMapTransfer returns a logical address, which drivers that use system DMA should ignore.When IoMapTransfer returns control, the driver should set up its device for the DMAoperation. Note that the driver calls IoMapTransfer only once, but continues to copy databetween its common buffer and a locked-down user buffer until the requested transfer is done.

Such a driver can call HalReadDmaCounter to determine how many bytes currently remainto be transferred in the common buffer, so the driver can continue to fill the buffer with userdata or copy data from the common buffer to the user buffer.

When all the requested transfer is complete (or the driver must return an error status for theIRP), the driver calls IoFlushAdapterBuffers to ensure that any data cached in the systemDMA controller is read into system memory or written out to the device. Then, the drivershould call IoFreeAdapterChannel promptly to release the system DMA controller for otherdrivers (and this driver) to use.

Page 123: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-38 Kernel-mode Driver Design Guide

3.3.5 Using Busmaster DMA

NT drivers of busmaster devices can use the following kinds of system-supplied DMAsupport: ■ Packet-based DMA if the busmaster card is such that the driver can determine when a

DMA transfer operation is done and/or when to begin another transfer operation for agiven request

■ Common-buffer DMA (also called continuous DMA) if the card is such that the drivercannot determine readily when a transfer operation will begin or when a transfer iscomplete, or if a single buffer area is used continuously or repeatedly for DMA transfers

Depending on the nature of the busmaster device, some NT drivers use packet-based DMAexclusively, some use common-buffer DMA exclusively, and some use both. For example, thedriver of a busmaster device that uses a mailbox scheme to communicate status informationand commands might use a common buffer for the mailboxes shared between the driver anddevice, together with packet-based DMA for data transfers.

Note that setting up a common buffer can tie up some (or all, depending on the size of therequested buffer) of the map registers associated with the adapter object that represents thebusmaster card, so consider the following a design guideline: ■ Use common-buffer DMA economically.

Setting up common-buffer data economically, such as in page-sized chunks or in a singleallocation, leaves more map registers available for the driver's packet-based DMA operations,if any. It also leaves more system memory free for other purposes, which yields better overalldriver and system performance.

The following sections explain each kind of busmaster DMA separately.

Page 124: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-39

3.3.5.1 Packet-based DMA

To use packet-based DMA, NT drivers of busmaster DMA devices call the following generalsequence of support routines as they process an IRP requesting a DMA transfer: 1 KeFlushIoBuffers just before attempting to allocate map registers for a transfer request (See Chapter 16 for more information about maintaining cache coherency during DMA

with KeFlushIoBuffers and IoFlushAdapterBuffers.) 2 IoAllocateAdapterChannel when the driver is ready to program its device for DMA 3 MmGetMdlVirtualAddress to get an index into the MDL, required as an argument to

IoMapTransfer, and IoMapTransfer to make the system physical memory that backsthe IRP's buffer device-accessible

Note that any driver might need to carry out more than one transfer operation in order tosatisfy the current IRP, as explained in Section 3.3.3. Drivers whose devices do not havescatter/gather capabilities can call IoMapTransfer once per transfer operation. Driverswhose devices have scatter/gather capabilities can call IoMapTransfer more than onceto set up a transfer operation.

4 IoFlushAdapterBuffers at the end of each DMA transfer operation to/from the targetdevice, in order to determine whether all the requested data has been completelytransferred

5 IoFreeMapRegisters as soon as all DMA operations for the current IRP are done (all therequested data has been completely transferred)

The PADAPTER_OBJECT-type pointer, returned by HalGetAdapter, is a required argumentto IoAllocateAdapterChannel. However, drivers of busmaster devices pass a NULLPADAPTER_OBJECT pointer to IoMapTransfer and IoFlushAdapterBuffers.KeFlushIoBuffers and MmGetMdlVirtualAddress require a pointer to the MDL at IrpMdlAddress.

Individual NT drivers call this sequence of support routines at different points, depending onhow each driver is implemented to service its device. For example, one driver's StartIo routinemight make the call to IoAllocateAdapterChannel, while another driver might make this callfrom a routine that removes IRPs from a driver-created interlocked queue or device queue.

Page 125: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-40 Kernel-mode Driver Design Guide

3.3.5.1.1 Allocating the Busmaster Adapter Object

A driver calls KeFlushIoBuffers and IoAllocateAdapterChannel after its Dispatch entrypoint for IRP_MJ_READ and/or IRP_MJ_WRITE requests (or any other request that requiresa DMA transfer) has already checked the validity of the IRP's parameters (if necessary),possibly queued the IRP to another driver routine for further processing, and the transferrequest is the current IRP requiring a device I/O operation. Figure 3.10 illustrates such a callto IoAllocateAdapterChannel.

Figure 3.10 Allocating an Adapter Object for Busmaster DMA

The driver routine that calls IoAllocateAdapterChannel must be executing atDISPATCH_LEVEL IRQL when the call occurs. For more information about the hardwarepriorities at which NT drivers' standard routines execute, see Chapter 16. For moreinformation about support-routine-specific IRQL requirements, see Appendix A.

Page 126: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-41

As Figure 3.10 shows, the driver must supply more than a pointer to the adapter objectreturned by HalGetAdapter when it calls IoAllocateAdapterChannel. Along with a pointerto the device object, it must supply the entry point for its AdapterControl routine and a pointerto any driver-determined context data the AdapterControl routine will use.

IoAllocateAdapterChannel queues the driver's AdapterControl routine, which executes whenthe adapter object is free and a set of map registers (described in Section 3.3.1) has beenallocated for the driver's DMA operation(s) to/from the target device.

On entry, an AdapterControl routine is given pointers to the DeviceObject and Context passedin the call to IoAllocateAdapterChannel, as well as a handle (PVOID MapRegisterBase) forthe allocated map registers, as shown in Figure 3.10. The AdapterControl routine also is givena pointer to the CurrentIrp if the driver has a StartIo routine. If the driver manages its ownqueueing of IRPs (instead of having a StartIo routine), the driver can include a pointer to theCurrentIrp as part of the Context data it passes when it calls IoAllocateAdapterChannel.

For the driver of a busmaster device without scatter/gather capabilities, the AdapterControlroutine usually does the following: ■ Saves (or initializes) whatever context the driver maintains about DMA operations, such

as the MapRegisterBase handle the driver must pass to IoMapTransfer andIoFlushAdapterBuffers, the length in bytes of the requested transfer, and so forth

■ Calls MmGetMdlVirtualAddress followed by IoMapTransfer (described in the nextsubsection) to get the logical address its device can use to start the transfer operation

■ Sets up the busmaster adapter to start the transfer operation ■ Returns the value DeallocateObjectKeepRegisters

For the driver of a busmaster device with scatter/gather capabilities, the AdapterControlroutine usually does the following: ■ Saves (or initializes) whatever state the driver maintains about DMA operations, such as

the MapRegisterBase handle the driver must pass to IoMapTransfer andIoFlushAdapterBuffers, the length in bytes of the requested transfer, and so forth

■ Calls MmGetMdlVirtualAddress followed by IoMapTransfer (described in the nextsubsection) to get the logical address its device can use to start the transfer operation

The AdapterControl routine can call IoMapTransfer repeatedly until it has used all theavailable map registers to build a scatter/gather list for the busmaster card.

■ Sets up the busmaster adapter to start the transfer operation ■ Returns the value DeallocateObjectKeepRegisters

Page 127: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-42 Kernel-mode Driver Design Guide

As Figure 3.10 shows, an AdapterControl routine must return a system-defined value of typeIO_ALLOCATION_ACTION. For NT drivers that use busmaster DMA, the AdapterControlroutine must return the value DeallocateObjectKeepRegisters, which allows the driver toretain the allocated map registers for the target device object until it has transferred all therequested data for this IRP.

Note that an AdapterControl routine cannot wait for the busmaster device to complete a DMAoperation. Whether the busmaster device supports scatter/gather or not, the AdapterControlroutine must at least do the following: ■ Save necessary context information, particularly the MapRegisterBase handle in the

driver's device extension, controller extension, or other driver-accessible storage area(nonpaged pool, allocated by the driver).

■ Return DeallocateObjectKeepRegisters.

Another driver routine (probably the DpcForIsr) must call IoFlushAdapterBuffers when eachDMA transfer operation is done. This routine also must set up any additional DMA operationsnecessary to satisfy the current IRP.

When the driver has satisfied the current IRP's transfer request, it must callIoFreeMapRegisters. This call should occur immediately following the last call toIoFlushAdapterBuffers for the current IRP, so that the driver can service other DMArequests, possibly for other devices on the bus.

Page 128: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-43

3.3.5.1.2 Setting Up a Transfer Operation

When IoAllocateAdapterChannel transfers control to a driver's AdapterControl routine, ithas allocated a set of map registers. However, system physical memory for the current IRP'stransfer request must be mapped to the busmaster card's logical address range by calling thefollowing routines: 1 MmGetMdlVirtualAddress with the MDL at Irp MdlAddress to get an index for the

system physical address where the transfer should start The return value is a required argument (PVOID CurrentVa) to IoMapTransfer. 2 IoMapTransfer to map the system physical address ranges for the IRP's buffer to the

busmaster card's logical address range before the driver sets up the card for the transferoperation

Figure 3.11 illustrates such a call to IoMapTransfer.

Page 129: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-44 Kernel-mode Driver Design Guide

Figure 3.11 Setting Up a Logical Range for DMA

As Figure 3.11 shows, the driver supplies the following arguments to IoMapTransfer: ■ A PADAPTER_OBJECT pointer with the value NULL ■ A pointer to the MDL at Irp MdlAddress for the current IRP ■ The MapRegisterBase handle passed to the driver's AdapterControl routine by

IoAllocateAdapterChannel (see the preceding subsection) ■ The value returned by MmGetMdlVirtualAddress if this is the first call to

IoMapTransfer for this IRP Otherwise, the driver should supply an updated CurrentVa value, indicating the next

physical-to-logical mapping to be done. (How to calculate an updated CurrentVa isdescribed later.)

■ Access to a variable (PULONG Length, shown in Figure 3.11 as IrpLengthNextTransfer) If the driver has enough map registers to transfer all the requested data in a single DMA

operation (and no device-specific constraints on its DMA operations), Length can be setto the value of Length in the driver's I/O stack location of the IRP. At most, the Length inbytes can be (PAGE_SIZE * the number of map registers returned by HalGetAdapter).Otherwise, the driver must split up the request, as explained in Section 3.3.3, and mustupdate the value of Length in subsequent calls to IoMapTransfer for the current IRP.

Page 130: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-45

(How to calculate an updated Length value is also described later.) ■ A Boolean value, indicating the direction of the transfer operation (TRUE for a requested

transfer from memory to the device)

IoMapTransfer returns a logical address at which the driver can program the busmaster cardto begin the transfer operation.

If the driver must call IoMapTransfer more than once to satisfy the current IRP, it suppliesthe same NULL PADAPTER_OBJECT pointer, PMDL pointer, MapRegisterBase handle, andtransfer direction in every call to IoMapTransfer. However, the driver must supply updatedCurrentVa and Length values in its second and subsequent calls to IoMapTransfer. Use thefollowing formulas to calculate these values: ■ CurrentVa = CurrentVa + (Length requested in preceding call to IoMapTransfer) ■ Length = Minimum (remaining Length to be transferred, (PAGE_SIZE * number of

available map registers returned by HalGetAdapter))

The context information each driver maintains about its DMA transfers, such as theIrpLengthNextTransfer, IrpLengthDMAedSoFar, and MdlCurrentVa shown in Figure 3.11,depends on the driver writer.

For drivers of devices with scatter/gather capabilities, the PULONG Length parameter toIoMapTransfer is both an input and output parameter. On return from IoMapTransfer, itindicates how many bytes of data the system has mapped. That is, the return value of Length,in combination with the returned logical address, indicates the range of logical addresses thebusmaster card can use for this piece of the transfer in this DMA operation. Since Length isoverwritten by IoMapTransfer, follow this implementation guideline: ■ Never pass a pointer to the Length in the driver's I/O stack location of an IRP as the

Length argument to IoMapTransfer if your device supports scatter/gather. Doing thiscould destroy the value in the current IRP, making it impossible to determine whether thedriver has transferred all the requested data.

At the end of each DMA operation, the driver must call IoFlushAdapterBuffers with aNULL adapter object pointer and the MapRegisterBase handle to be sure that all the data hasbeen transferred (see Chapter 16), and to release the physical-to-logical mapping(s) for thecurrent DMA operation. If the driver must set up additional DMA operations to satisfy thecurrent transfer request, it must call IoFlushAdapterBuffers after each operation is complete.

Page 131: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-46 Kernel-mode Driver Design Guide

When all the requested transfer is complete (or the driver must return an error status for theIRP), the driver should call IoFreeMapRegisters promptly in order to get the best possiblethroughput for the busmaster card. The driver should pass a NULL PADAPTER_OBJECTpointer to IoFreeMapRegisters.

3.3.5.2 Common-buffer DMA

To set up a common buffer for DMA, an NT driver's DriverEntry routine must callHalAllocateCommonBuffer with the PADAPTER_OBJECT type pointer returned byHalGetAdapter. An NT driver should allocate a common buffer only if it will use the bufferrepeatedly for its DMA operations while the driver remains loaded. Figure 3.12 illustratessuch a call to HalAllocateCommonBuffer.

Figure 3.12 Allocating a Common Buffer for Busmaster DMA

The requested size for the buffer, shown in Figure 3.12 as LengthForBuffer, determines howmany map registers must be used to provide a virtual-to-logical mapping for the buffer. Atmost, the number of map registers required is the value of (BYTES_TO_PAGES(LengthForBuffer)). This value cannot be greater than the number of map registers returned byHalGetAdapter. (For a list of the macros, defined by the NT Memory Manager, that driverscan use, see Appendix A.)

Page 132: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-47

In addition, the caller must supply the following: ■ A Boolean that indicates whether caching should be enabled ■ A pointer to a driver-defined variable that will contain the device-accessible logical

address for the buffer on return from HalAllocateCommonBuffer

If the call succeeds, this support routine also returns a driver-accessible virtual address for thebuffer, which the driver should save. HalAllocateCommonBuffer returns NULL if it cannotallocate memory for the buffer.

If necessary, the driver can call HalFlushCommonBuffer before it accesses data read into thebuffer from the busmaster card. HalFlushCommonBuffer returns a Boolean, indicatingwhether it has successfully flushed all data that might be cached in the card's internal buffer(s)into the common buffer.

When the driver unloads, it must call HalFreeCommonBuffer to release each common bufferit has allocated.

Page 133: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-48 Kernel-mode Driver Design Guide

3.4 Controller Objects

As its name suggests, an NT controller object usually represents a physical device controllerwith attached devices of the same type. Like the "AT" disk driver described in Chapter 2, anNT device driver can use a controller object to synchronize I/O operations through a physicalcontroller to/from attached devices that must be represented by named device objects as thetargets for I/O requests. For more information about creating device objects, see Section 3.2.

Drivers of devices with I/O channels also might use a controller object to synchronize theirI/O operations between (or among) the channels of the device. For example, a sound devicedriver might create device objects to represent each midi/wave input and output channel anduse a controller object to synchronize its midi/wave device I/O operations.

Note that a controller object has neither a name nor an associated file object, as an NT deviceobject does. Therefore, a controller object is invisible to user-mode code, which cannot makedevice I/O requests without getting a handle for the file object that represents the target deviceobject. A controller object is also invisible to higher-level drivers, which cannot attach theirown device objects to a controller object. In other words, neither the I/O Manager nor ahigher-level driver can set up an IRP requesting I/O on a device represented by a controllerobject.

Drivers of physical devices with features like the "AT" disk controller are not required to use acontroller object to synchronize their device I/O operations. For example, a driver writer couldtry something like the following synchronization technique instead of using a controllerobject: ■ Set up named device objects to represent the devices that are targets for I/O requests.

Maintain state (perhaps a set of DeviceBusy flags) about which device object is the targetof the current I/O operation in each device extension (or in a single device extension).Carry out I/O for the currently busy device object and requeue incoming IRPs for otherdevice objects until the current IRP is completed.

Page 134: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-49

This technique serializes IRP processing for all the driver's target device objects. It also forcesthe driver to complete the current IRP before its StartIo routine can begin processing the nextIRP, which can inhibit driver performance. If the device is such that certain operations can beoverlapped, using a controller object can increase a driver's I/O throughput, because thissynchronization technique allows the driver to determine whether it can overlap operationsjust before it sets up the physical device. For example, a disk controller might allow the driverto overlap seeks on one disk with read/write operations on another disk.

Moreover, using a controller object is a relatively easy way to synchronize I/O operations formore than one target device object through a single physical device, such as an "AT" diskcontroller. Using a controller object allows the driver to synchronize I/O across a set of nameddevice objects without having to maintain state about every device and the device controller inone or more device extensions and without having to requeue IRPs. For more informationabout setting up and managing internal queues, see Section 3.8 later in this chapter, and seealso Chapter 7.

3.4.1 Creating a Controller Object with a Controller Extension

If an NT driver uses a controller object, it must call IoCreateController when the driver isloaded. Figure 3.13 illustrates such a call.

Page 135: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-50 Kernel-mode Driver Design Guide

Figure 3.13 Controller Object

Like the device extension for a driver-created device object, every controller object has acontroller extension whose size is driver-determined and whose structure is driver-defined. Inaddition to whatever device-specific state information the driver maintains about the physicalcontroller (or device with channels), Figure 3.13 shows a representative set of driver-defineddata for a controller extension.

The PtrToControllerObject returned by IoCreateController must be passed in the driver'scalls to IoAllocateController and IoFreeController, described in section 3.4.2, next. (If thedriver is unloaded, it also must pass this pointer to IoDeleteController.)

Page 136: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-51

Most drivers that use controller objects find it convenient to store pointers to their deviceobjects (or device extensions) in the controller extension. A driver might store pointers to thecontroller extension in each of its device extensions, as well.

If the physical controller generates interrupts (rather than the attached devices), the driver canuse the controller extension as storage for the PtrToInterruptObject(s) returned byIoConnectInterrupt. For more information about interrupt objects, see Section 3.5, later inthis chapter.

3.4.2 Allocating the Controller for I/O Operations

After a driver that uses a controller object has initialized, it is ready to process IRPs sent to itstarget device objects. Whenever the current IRP requires the driver to program its physicaldevice for an I/O operation, the driver calls IoAllocateController. Figure 3.14 illustrates sucha call.

Page 137: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-52 Kernel-mode Driver Design Guide

Figure 3.14 Allocating a Controller Object for I/O

The driver routine that calls IoAllocateController must be executing at DISPATCH_LEVELIRQL when the call occurs. A driver that makes this call from its StartIo routine is alreadyrunning at DISPATCH_LEVEL. For more information about the hardware priorities at whichNT drivers' standard routines execute, see Chapter 16. For more information about support-routine-specific IRQL requirements, see Appendix A.

As Figure 3.14 shows, the driver must supply more than a pointer to the controller objectreturned by IoCreateController when it calls IoAllocateController. Along with this pointer,it must pass pointers to the device object representing the target of the current I/O request, to adriver-supplied ControllerControl routine, and to whatever Context its ControllerControlroutine will need to set up the device for the requested I/O operation.

IoAllocateController queues the driver-supplied ControllerControl routine if the devicerepresented by the controller object is already busy doing I/O for a target device object.Otherwise, the ControllerControl routine is called immediately with the input parametersshown in Figure 3.14.

Page 138: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-53

The input Context pointer is passed to the driver's ControllerControl routine when it is run.Consider the following design guidelines to determine the location of such a context area: ■ The driver-supplied context area should not be in the controller extension unless the

driver processes each IRP to completion before starting another operation on the physicalcontroller. Otherwise, a context area in the controller extension could be overwritten byother driver routines (or on receipt of a new IRP).

■ Even if the driver overlaps a device I/O operation for another device object, a contextarea in the device extension of the target device object cannot be overwritten.

■ If another I/O request is made for a particular device object and the driver has a StartIoroutine, a context area in its device extension also cannot be overwritten because theincoming IRP will be queued when the driver calls IoStartPacket, and the incoming IRPwill remain in the device queue until the driver calls IoStartNextPacket just before itcompletes the CurrentIrp for that device object.

The I/O Manager passes a pointer to the CurrentIrp if the driver has a StartIo routine. If, likethe system-supplied floppy driver described in Chapter 2, a driver manages its own queuing ofIRPs instead of implementing a StartIo routine, the I/O Manager cannot give theControllerControl routine a pointer to the CurrentIrp. When it calls IoAllocateController,such a driver must include the current IRP as part of the Context data it passes.

The ControllerControl routine sets up the physical controller for the CurrentIrp's requesteddevice I/O operation. As shown in Figure 3.14, the ControllerControl routine returns a valueof type IO_ALLOCATION_ACTION, which can be either of the following system-definedvalues: ■ If the ControllerControl routine can start another operation on the physical controller, it

should return DeallocateObject so the driver can overlap the next requested I/Ooperation. For example, if the ControllerControl routine can program a disk controller fora seek operation on one disk and, then, complete the IRP and return DeallocateObject,the ControllerControl routine can be called again to program the disk controller for atransfer operation on the other disk (if any transfer requests currently are queued to theother disk).

■ If the current IRP requires further processing by other driver routines, theControllerControl routine must return KeepObject. For example, if the driver programs adisk controller for a transfer operation, it cannot complete the IRP until the transfer iscomplete, so the ControllerControl routine must return KeepObject.

When a ControllerControl routine returns KeepObject, usually the driver's ISR runs when thedevice interrupts, and its DpcForIsr routine completes the I/O operation and the CurrentIrp.Whenever the ControllerControl routine returns KeepObject, the routine that will completethe CurrentIrp must call IoFreeController. Such a driver routine should callIoFreeController as soon as possible so that its next device I/O operation can be set uppromptly.

Page 139: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-54 Kernel-mode Driver Design Guide

3.5 Interrupt Objects

Each NT driver of a physical device that generates interrupts must register its interrupt serviceroutine (ISR) when the driver is loaded. The NT Kernel defines the interrupt object type totrack information about each interrupt vector, its system-assigned hardware priority (IRQL),the ISR to be called when the interrupt occurs, and so forth.

Every NT driver should be portable across all NT platforms, including symmetricmultiprocessor machines with several I/O buses of various types (ISA, EISA, and so forth).Consequently, the following is configurable when NT device drivers and the system itself areloaded: ■ The set of processors on which interrupts can occur - for any NT platform, this

determines how many interrupt objects must be set up to register a device driver's ISR. Insymmetric multiprocessor machines, each device driver might require an interrupt objectper processor. However, the system can perform load balancing by restricting anyparticular device interrupt to a subset of available processors.

■ The system-assigned interrupt vectors for every device on each I/O bus in the machine -for NT machines with more than one I/O bus, the bus-relative interrupt vectors of thedevices on each I/O bus must be mapped to a set of system-assigned interrupt vectors.

■ The hardware priority (IRQL) assigned to each system interrupt vector - for an NT devicedriver, the DIRQL value assigned to its mapped system vector determines the hardwarepriority at which that driver's ISR executes.

For all NT platforms, the Hardware Abstraction Layer (HAL) can remap all bus-relativedevice interrupt vectors to a set of machine-specific interrupt vectors, each with a system-assigned DIRQL value. As already shown in Figure 3.1, a platform-specific HAL provideshardware-level support to both the Kernel and the I/O Manager for the I/O buses, peripherals,and processors in each NT machine. The HAL also provides platform-specific support for NTdrivers that use interrupt objects, as explained in the next section.

3.5.1 Getting a System-assigned Interrupt Vector, DIRQL, and Processor Mask

When an NT device driver is loaded, the NT configuration registry contains whateverhardware information the system can detect about the processors, I/O buses, and peripheraldevices in the machine. An NT device driver must use the registry to determine the system-assigned number (and, for some devices, the system-defined interface type) of the bus towhich its device is attached, along with the bus-relative vector and/or IRQL for the deviceitself. For more information about how device drivers can get this hardware configurationinformation, see Chapter 16.

Page 140: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-55

The HAL is responsible for remapping bus-relative device interrupt vectors to system interruptvectors with system-assigned DIRQL values, so NT device drivers call a HAL-suppliedsupport routine to get required parameters for setting up interrupt objects and registering theirISRs. Figure 3.15 illustrates a call to HalGetInterruptVector.

Figure 3.15 Getting a System Vector and IRQL

As shown in Figure 3.15, HalGetInterruptVector maps driver-supplied values for a device'sbus-relative interrupt vector and IRQL to DeviceSystemVector and SystemDirql values. Seethe section on the configuration registry in Chapter 16 for more information about how devicedrivers can obtain the BusInterfaceType, BusNumber, BusRelativeVector, andBusRelativeIrql arguments to HalGetInterruptVector.

HalGetInterruptVector also returns a ProcessorMask that indicates the platform-specific setof processors on which devices can interrupt. NT device drivers must pass this value to the I/OManager so it can set up a Kernel-defined interrupt object for each enabled processor whenthe driver registers its ISR, as explained in the next section.

Page 141: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-56 Kernel-mode Driver Design Guide

3.5.2 Registering an ISR

An NT device driver must call IoConnectInterrupt when it initializes in order to register itsISR. Figure 3.16 illustrates this call.

Figure 3.16 Setting Up Interrupt Objects

Page 142: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-57

As Figure 3.16 shows, when a device driver calls IoConnectInterrupt, the I/O Manager callsthe Kernel to set up the driver's interrupt object(s): 1 Unless the ISR handles more than one interrupt vector for different devices, the

DriverEntry routine registers the ISR with the following parameters: ■ A pointer to the driver's storage area for the interrupt object pointer (returned by

IoConnectInterrupt and a required argument to KeSynchronizeExecution) ■ A NULL spin lock pointer (unless the driver itself has allocated storage for and

already initialized an InterruptSpinLock) ■ A pointer to the ServiceContext area the ISR will use when it is called and the entry

point for the ISR ■ The (mapped) DeviceSystemVector obtained from HalGetInterruptVector ■ The SystemDirql assigned to that vector for this machine, also obtained from

HalGetInterruptVector ■ The same IRQL value for the SynchronizeIrql argument ■ The ProcessorMask, indicating the set of processors on which the device can

interrupt in this machine, also obtained from HalGetInterruptVector ■ Whether the interrupt mode is level-sensitive or latched ■ Whether the device can share the vector ■ Whether to save the floating-point registers when the device interrupts 2 The I/O Manager allocates sufficient resident memory for as many interrupt objects as

the input ProcessorMask indicates, which could be as many interrupt objects asprocessors in symmetric multiprocessor machines or could be a lesser number, dependingon the ProcessorMask. The I/O Manager also provides storage for an interrupt spin lockand initializes it if the driver passes a NULL pointer to IoConnectInterrupt (see Step 1).

3 The I/O Manager calls KeInitializeInterrupt for each interrupt object. 4 The I/O Manager also calls KeConnectInterrupt for each initialized interrupt object to

connect it to a particular processor on which the device can interrupt in the machine.(This call actually sets the given system vector in the Kernel's interrupt dispatch table(IDT) for a particular processor, as hinted in Figure 3.16.)

5 When all necessary interrupt object(s) have been initialized and connected on theprocessor(s), IoConnectInterrupt returns a pointer to the set of interrupt objects. NTdriver routines must pass the returned PointerToInterruptObject(s) in their calls toKeSynchronizeExecution.

6 As Figure 3.16 shows, when a device interrupt occurs on a given processor, the driver'sISR is run on that processor at DIRQL, and given a pointer to the ServiceContext thatwas set up when the driver called IoConnectInterrupt.

Page 143: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-58 Kernel-mode Driver Design Guide

If an NT driver supplies an ISR that handles interrupts from more than one device, each with adifferent interrupt vector, its DriverEntry routine must register the ISR as follows: 1 The DriverEntry routine must call KeInitializeSpinLock with a pointer to driver-

provided storage for an InterruptSpinLock, which must be in resident memory (in adevice extension, controller extension, or nonpaged pool allocated by the driver).

2 For each vector the ISR handles, the DriverEntry routine must callHalGetInterruptVector and IoConnectInterrupt as described in Step 1, except for thefollowing arguments:

■ The driver must pass a pointer to its storage for the InterruptSpinLock, rather than aNULL spin lock pointer.

■ The driver must specify a SynchronizeIrql value that is the highest SystemDirqlassigned to any device whose interrupts the ISR handles.

An ISR that handles interrupts for more than one device must run at the highest DIRQLassigned to the DeviceSystemVectors it handles, as must any driver-suppliedSynchCritSection routine that accesses device registers (or data) shared with the ISR.However, an NT driver that must handle interrupt vectors from more than one device can bedesigned with more than one ISR. An NT device driver can have an ISR for each vector ithandles.

Because an ISR runs at relatively high hardware priority (at DIRQL), it must return control asquickly as possible. Consequently, an NT driver's ISR should do as little I/O processing as itcan: stop the device from generating interrupts, save whatever context is necessary about theoperation, and queue a DPC to complete the interrupt-driven I/O operation at a lower IRQL.For more information about DPC objects, see Section 3.6, next.

For more information about using an interrupt spin lock and KeSynchronizeExecution, seethe section on spin locks in Chapter 16. For more information about ISRs andSynchCritSection routines, see also Chapters 4, 8, and 10.

Page 144: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-59

3.6 DPC Objects

Like the interrupt object type described in the preceding section, the DPC object type isdefined by the NT Kernel. A DPC object represents a deferred procedure call: a caller-supplied routine that is run later at a lower hardware priority (IRQL).

Any NT device driver that registers an ISR must use one or more DPC objects to register itsDpcForIsr and/or CustomDpc routine(s). A driver-supplied DpcForIsr or CustomDpc routineis responsible for completing interrupt-driven I/O operations so the ISR can return control asquickly as possible.

Some system-supplied device drivers have only a single DpcForIsr, and such a driver uses asingle DPC object. At the driver writer's discretion, an NT device driver can have one or moreCustomDpc routines, each using a DPC object, in addition to or as a substitute for a DpcForIsrroutine.

The I/O Manager provides support for registering a DpcForIsr and for queueing this routinefrom the ISR, as explained in the next section. The Kernel provides support for registering aCustomDpc and for queueing this routine from the ISR, as explained in Section 3.6.2.

3.6.1 Registering and Queueing a DpcForIsr Routine

An NT device driver can register its DpcForIsr by calling IoInitializeDpcRequest when thedriver is loaded. After the driver is loaded and handling interrupt-driven I/O requests, the ISRcan call IoRequestDpc just before it returns control to have the DpcForIsr routine queued forexecution. Figure 3.17 illustrates calls to these routines.

Page 145: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-60 Kernel-mode Driver Design Guide

Figure 3.17 Using a DPC Object for a DpcForIsr Routine

As Figure 3.17 shows, calling IoInitializeDpcRequest associates a Kernel DPC object with adriver-supplied DpcForIsr routine and a driver-created device object. The I/O Managerallocates memory for the DPC object and calls KeInitializeDpc on the driver's behalf.

When the ISR is called to handle a device interrupt at DIRQL, it should return control to thesystem as soon as possible for better overall system and driver performance. Usually, an ISRmerely stops the device from generating more interrupts, gathers whatever context information

Page 146: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-61

the DpcForIsr routine needs to complete the operation that caused the interrupt, callsIoRequestDpc, and returns. For more information about the functionality required of an ISR,see Chapter 8.

As Figure 3.17 shows, the ISR passes a pointer to the device object, representing the devicefor which the operation was carried out, a pointer to the CurrentIrp, and a pointer to a driver-determined Context for the operation to IoRequestDpc. The I/O Manager callsKeInsertQueueDpc on the driver's behalf, and the corresponding DPC object is queued untilIRQL falls below DISPATCH_LEVEL on a processor. Then, the Kernel dequeues the DPCobject and the driver's DpcForIsr is run on the processor at DISPATCH_LEVEL IRQL.

On entry, the DpcForIsr is given pointers to the DPC object and the DeviceObject, CurrentIrp,and Context passed in the ISR's call to IoRequestDpc. The Context area must be in residentmemory. Such a context area is usually in the device extension of the DeviceObject, but it canbe in a controller extension if the driver uses a controller object (see Section 3.4) or innonpaged pool allocated by the driver. The DpcForIsr is responsible for doing whatever isnecessary to complete the I/O requested in the CurrentIrp. For more information about thefunctionality required of a DpcForIsr, see Chapter 9.

Note that the ISR and DpcForIsr can be run concurrently in symmetric multiprocessormachines, so NT device driver writers should follow these guidelines: ■ The ISR must call IoRequestDpc just before it returns control. Otherwise, the DpcForIsr

might be run on another processor before the ISR has finished setting up the Context areafor the DpcForIsr.

■ The ISR could be called again if the device interrupts while or before the DpcForIsr isrun. When a driver uses the device extension (see Section 3.2) to maintain context aboutits device I/O operations, the DpcForIsr should never call IoStartNextPacket for theinput device object (nor dequeue an IRP for the input device object, if the driver managesits own IRP queueing) until just before it calls IoCompleteRequest with the CurrentIrp.Otherwise, the driver's StartIo or queue-management routine(s) might start a device I/Ooperation that overwrote the shared Context area before the DpcForIsr could completethe current operation.

■ The DpcForIsr (and any other driver routine that shares a context area with the ISR) mustcall KeSynchronizeExecution with a driver-supplied SynchCritSection routine in orderto access any context area shared with the ISR in a multiprocessor-safe manner.

Even in a uniprocessor machine, the ISR could be called again if the device interrupts while orbefore the DpcForIsr is run. Device driver writers should follow the preceding guidelines inevery NT machine so their drivers remain portable across NT platforms.

For more information about StartIo routines and managing internal queues of IRPs, seeChapter 7. See also Chapter 4 for more information about IoStartNextPacket and Section 3.8for more information about device queue objects. For more information aboutSynchCritSection routines, see Chapter 10. See also the section about using spin locks inChapter 16 for more information about KeSynchronizeExecution.

Page 147: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-62 Kernel-mode Driver Design Guide

3.6.2 Registering and Queueing a CustomDpc Routine

An NT device driver can register a CustomDpc routine by calling KeInitializeDpc when thedriver is loaded. Just before it returns control, the ISR can call KeInsertQueueDpc to havethe CustomDpc routine queued for execution. Figure 3.18 illustrates calls to these routines.

Figure 3.18 Using a DPC Object for a CustomDpc Routine

Page 148: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-63

As Figure 3.18 shows, a driver that has a CustomDpc routine must provide the storage for aDPC object. Because the driver must pass a pointer to the DPC object from its ISR, thestorage must be in resident system-space memory. Most NT device drivers with CustomDpcroutines provide storage for their DPC objects in the device extension of a driver-createddevice object (see Section 3.2), but the storage can be in a controller extension if the driveruses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver.

When the driver calls KeInitializeDpc, it must pass the entry point for its CustomDpc routineand pointers to the driver-allocated storage for the DPC object and to the driver-definedDeferredContext area, which will be passed to the CustomDpc routine when it is called.Because it must be accessible at raised IRQL, the DeferredContext area also must be inresident memory.

Note that a CustomDpc routine is not associated with a given device object as the DpcForIsrroutine is when the driver calls IoInitializeDpcRequest (see Section 3.6.1). Nevertheless, theDeferredContext for a CustomDpc routine almost always includes pointers to the target deviceobject and current IRP, because a CustomDpc routine has the same functional requirements asa DpcForIsr routine: to complete an interrupt-driven I/O operation at a lower IRQL than theISR.

As Figure 3.18 shows, the ISR passes pointers to the DPC object and to two additionalarguments to KeInsertQueueDpc. If all processors in the machine currently have coderunning at IRQL greater than or equal to DISPATCH_LEVEL, the DPC object is queued untilthe IRQL falls below DISPATCH_LEVEL on a processor. Then, the Kernel dequeues theDPC object and the driver's CustomDpc routine is run on the processor atDISPATCH_LEVEL IRQL.

The CustomDpc routine is responsible for doing whatever is necessary to complete the I/Ooperation that caused the interrupt. For more information about the functionality required of aCustomDpc, see Chapter 9.

Note that the ISR and CustomDpc can be run concurrently in a symmetric multiprocessormachine, so NT driver writers who implement CustomDpc routines should follow theguidelines set out in Section 3.6.1.

Page 149: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-64 Kernel-mode Driver Design Guide

3.7 Timer Objects with Associated DPCs

Any NT driver can have one or more IoTimer or CustomTimerDpc routines to be calledperiodically for whatever purpose the driver writer decides. Each of these types of standarddriver routines is actually a DPC (see Section 3.6), queued for execution after some number ofclock interrupts have been handled by the system.

The NT Kernel defines the timer object type, and the I/O Manager sets up a timer object thatcan be associated with each driver-created device object to provide support for drivers' time-out routine(s). At the driver writer's discretion, an NT driver can have a separate IoTimerroutine for each device object that the driver creates (see Section 3.2). An IoTimer routine iscalled once per second after the driver enables the timer until the driver disables the timer. TheI/O Manager provides support routines for registering an IoTimer routine with a given deviceobject and for enabling and disabling such a timer.

As an alternative, the driver can have one or more CustomTimerDpc routines that can becalled at finer-grained, driver-determined intervals. The Kernel provides support routines forregistering a CustomTimerDpc routine and for queueing the CustomTimerDpc to be calledafter a given timer interval has expired.

Page 150: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-65

The following subsection explains how to set up an IoTimer routine and enable the timer.Section 3.7.2 explains how to register a CustomTimerDpc routine and how to queue thisroutine to be called after setting a timer interval.

3.7.1 Registering and Enabling an IoTimer Routine

Any NT driver can register an IoTimer routine after it creates one or more device objects bycalling IoInitializeTimer when the driver is loaded. After the driver has initialized, it canenable the timer by calling IoStartTimer. Figure 3.19 illustrates these calls.

Page 151: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-66 Kernel-mode Driver Design Guide

Figure 3.19 Using a Timer Object for an IoTimer Routine

As Figure 3.19 shows, the driver calls IoInitializeTimer with the entry point of its IoTimerroutine and pointers to a driver-created device object and TimerContext area in which thedriver maintains whatever context its IoTimer routine uses. The I/O Manager associates thedevice object with a Kernel timer object, which the I/O Manager sets up to time out everysecond.

After the driver calls IoStartTimer, its IoTimer routine is called once per second until thedriver calls IoStopTimer. On entry, the IoTimer routine is given pointers to the associateddevice object and TimerContext area that was set up when the driver called IoInitializeTimer.

Page 152: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-67

Because an IoTimer routine is run at DISPATCH_LEVEL IRQL, its TimerContext area mustbe in resident, system-space memory. Most NT drivers that have IoTimer routines use thedevice extension of the associated device object as such a context area, but it can be in acontroller extension if the driver uses a controller object (see Section 3.4) or in nonpaged poolallocated by the driver.

Driver writers who implement an IoTimer routine should follow these guidelines concerningthe TimerContext area: ■ If the IoTimer routine shares its TimerContext area with the driver's ISR, it must call

KeSynchronizeExecution with a SynchCritSection routine in order to access theTimerContext in a multiprocessor-safe manner. For more information aboutSynchCritSection routines, see Chapter 10. See also the section about using spin locks inChapter 16 for more information about KeSynchronizeExecution.

■ If the IoTimer routine does not share its TimerContext area with an ISR, but does share itwith another driver routine, it must protect this context area with an executive spin lockin order to access the TimerContext in a multiprocessor-safe manner. For moreinformation about using executive spin locks, see Chapter 16.

For more information about the functionality required of an IoTimer routine, see Chapter 14.

3.7.2 Registering and Queueing a CustomTimerDpc Routine

Any NT driver can register a CustomTimerDpc routine when the driver is loaded by callingthe following two routines: 1 KeInitializeDpc to set up its CustomTimerDpc routine 2 KeInitializeTimer to set up a timer object

After the driver has initialized, it can call KeSetTimer to set up the timer object with aninterval and with the DPC object in order to have its CustomTimerDpc called when the giveninterval expires. Figure 3.20 illustrates these calls.

Page 153: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-68 Kernel-mode Driver Design Guide

Figure 3.20 Using Timer and DPC Objects for a CustomTimerDpc Routine

As Figure 3.20 shows, the driver must supply storage both for a DPC object and a timer objectif it has a CustomTimerDpc routine. Most drivers that have CustomTimerDpc routinesprovide the storage for these objects in the device extension of a driver-created device object(see Section 3.2) or in other resident memory, allocated by the driver.

The DriverEntry routine must first initialize the DPC object by calling KeInitializeDpc, andthen initialize the timer object by calling KeInitializeTimer. For more information about callsto KeInitializeDpc, see Section 3.6.2, earlier in this chapter. As Figure 3.20 shows, the drivermust pass a pointer to its storage for the timer object when it calls KeInitializeTimer.

Page 154: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-69

After the driver has initialized, it can queue the CustomTimerDpc to be called after a driver-determined interval by calling KeSetTimer with pointers to the DPC object, timer object, anda given DueTime (expressed in units of 10 milliseconds), as shown in Figure 3.20. A positivevalue for DueTime specifies an absolute time at which the CustomTimerDpc routine shouldbe called. A negative value for DueTime specifies an interval relative to the current systemtime. The call to KeSetTimer queues the timer object for the given interval, as shown inFigure 3.20: 1 When the given DueTime expires, the timer object is set to the Signaled state. 2 If every processor in the machine is currently running code at an IRQL greater than or

equal to DISPATCH_LEVEL, the DPC object associated with the timer object is put inthe DPC queue. Otherwise, the CustomTimerDpc routine is called.

3 If the DPC object was queued when the DueTime interval expired, the CustomTimerDpcroutine is called as soon the IRQL on any processor in the machine falls belowDISPATCH_LEVEL. The CustomTimerDpc routine, like all DPCs, is run atDISPATCH_LEVEL IRQL.

A driver writer who implements a CustomTimerDpc must follow the same guidelinesconcerning the DeferredContext area as for the TimerContext area of an IoTimer routine (seeSection 6.7.1).

Note that a driver's call to KeSetTimer causes its CustomTimerDpc to be run only once. ACustomTimerDpc routine is not called automatically at any particular interval following a callto a support routine, as is the case for an IoTimer routine. On the other hand, the granularity ofa Kernel timer object interval is approximately 10 milliseconds, so a driver with aCustomTimerDpc has more control over the intervals at which its CustomTimerDpc routine isrun.

Page 155: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-70 Kernel-mode Driver Design Guide

3.8 Device Queue Objects and Interlocked Queues

Except for NT file system drivers, the I/O Manager associates a device queue object with eachdevice object that an NT driver creates (see Section 3.2). Most NT device drivers call the I/OManager's support routines to use an associated device queue that holds IRPs whenever deviceI/O requests for a target device object come in faster than the driver can process them tocompletion. Most NT intermediate drivers simply pass IRPs on to lower drivers as fast as theycome in, so intermediate drivers almost never use device queue objects. For more informationabout the I/O Manager's built-in support for IRP queueing in the device queue associated witha device object, see Chapter 4.

However, NT driver writers can design their drivers to manage internal queues of IRPs byexplicitly setting up one or more device queue objects or interlocked queues. As mentioned inSection 3.1, the system-supplied SCSI port driver is an example of a driver that uses devicequeue objects for internal queues. This driver sets up device queue objects as supplementalqueues: that is, in addition to the device queue associated with the device object it creates torepresent an HBA. The NT SCSI port driver uses its supplemental device queues to hold IRPsbound for particular logical units on the HBA-controlled SCSI bus(es). As mentioned inChapter 2, the system-supplied floppy driver is an example of a driver that uses an interlockedqueue. This driver sets up an interlocked queue into which and from which its device-dedicated thread inserts and removes IRPs.

Page 156: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-71

The NT Kernel defines the device queue object type. The Executive Support componentprovides routines for inserting and removing IRPs in interlocked queues. The next subsectionexplains how to set up and use a device queue object. Section 3.8.2 explains how to set up anduse an interlocked queue.

3.8.1 Setting up a Device Queue Object and Queueing IRPs

A driver can set up a device queue object by calling KeInitializeDeviceQueue when thedriver is loaded. After the driver is initialized, it inserts IRPs into this queue by callingKeInsertDeviceQueue or KeInsertByKeyDeviceQueue. Figure 3.21 illustrates these calls.

Page 157: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-72 Kernel-mode Driver Design Guide

Figure 3.21 Using a Device Queue Object

As Figure 3.21 shows, the driver must provide the storage for a device queue object. Mostdrivers that set up a device queue object provide the necessary storage in the device extensionof a driver-created device object (see Section 3.2), but it can be in a controller extension if thedriver uses a controller object (see Section 3.4) or in nonpaged pool allocated by the driver.The DriverEntry routine must call KeInitializeDeviceQueue, passing a pointer to its storagefor the device queue object.

After the driver has initialized, it can insert an IRP into the device queue by callingKeInsertDeviceQueue, which places the IRP at the tail of the queue, orKeInsertByKeyDeviceQueue, which places the IRP into the queue according to a driver-determined SortKey value, as shown in Figure 3.21. Each of these support routines returns aBoolean value (Busy or Not-Busy) indicating the device queue's state: whether there areentries already queued.

Page 158: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-73

When an IRP is sent to the driver and the queue state is set to Not-Busy on returned fromKeInsert..DeviceQueue, the caller must pass the IRP on for further processing to anotherdriver routine. However, the call to KeInsert..DeviceQueue with such an IRP changes thestate of the device queue to Busy, so the next IRP to come in is inserted in the queue.

When the device queue state is set to Busy, the driver can dequeue an IRP for furtherprocessing by calling one of the following routines: ■ KeRemoveDeviceQueue to remove the IRP at the head of the queue ■ KeRemoveByKeyDeviceQueue to remove an IRP chosen according to a driver-

determined SortKey value ■ KeRemoveEntryDeviceQueue to remove a particular IRP in the queue

An attempt to remove an entry from a device queue that is empty but whose state is set toBusy causes the state transition to Not-Busy. An attempt to remove an entry from a devicequeue whose state is Not-Busy causes a fatal error.

A device queue object is protected by an executive spin lock (not shown in Figure 3.21), so adriver that uses a device queue object can insert IRPs into the queue and remove them in amultiprocessor-safe manner from any driver routine running at less than or equal toDISPATCH_LEVEL IRQL. Note that this IRQL restriction precludes callingKeInsertDeviceQueue, KeInsertByKeyDeviceQueue, KeRemoveDeviceQueue,KeRemoveByKeyDeviceQueue, and KeRemoveEntryDeviceQueue from a device driver'sISR or SynchCritSection routine.

For more information about managing IRQLs and using executive spin locks, see Chapter 16.See also Appendix A for support-routine-specific IRQL requirements.

3.8.2 Setting up an Interlocked Queue and Queueing IRPs

As a general rule, NT drivers with device-dedicated threads or drivers that use executiveworker threads (such as most NT file system drivers) are the most likely types of NT drivers tomanage their own internal queueing of IRPs in an interlocked queue. To set up such a queue,the driver must initialize the list head for the queue and an executive spin lock. Figure 3.22illustrates an interlocked queue, the routines the driver must call to set up such a queue, and aset of ExInterlockedXxx routines it can call to insert IRPs into and remove IRPs from thequeue.

Page 159: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-74 Kernel-mode Driver Design Guide

Figure 3.22 Using an Interlocked Queue

As Figure 3.22 shows, the driver must provide the storage for the interlocked queue itself andfor the following in order to set up its interlocked queue: ■ An ExecutiveSpinLock that the DriverEntry routine must initialize by calling

KeInitializeSpinLock when the driver is loaded ■ The ListHead for the queue that the DriverEntry routine must initialize by calling

InitializeListHead (one of the NT-supplied, kernel-mode runtime library routines, whichdoes not have the Rtl prefix)

Most NT drivers that use interlocked queues provide the necessary storage in the deviceextension of a driver-created device object, but such a queue and executive spin lock can be ina controller extension if the driver uses a controller object (see Section 3.4) or in nonpagedpool allocated by the driver. A queue with a ListHead of type LIST_ENTRY, as shown inFigure 3.22, is a doubly linked list. One with a ListHead of type SINGLE_LIST_ENTRY is asingly linked list.

Page 160: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-75

After the driver has initialized, it can insert an IRP into its queue by calling either of thefollowing routines if the ListHead is of type LIST_ENTRY, as shown in Figure 3.22: ■ ExInterlockedInsertTailList to place the IRP at the end of the queue ■ ExInterlockedInsertHeadList to place the IRP at the front of the queue, which drivers

usually call only when they must retry a particular request

The driver must pass pointers to the IRP, as well as to the ListHead and ExecutiveSpinLockinitialized by the DriverEntry routine, to each of these ExInterlockedInsertXxx routines. Itmust pass pointers only to the ListHead and ExecutiveSpinLock when the driver dequeues anIRP by calling ExInterlockedRemoveHeadList.

A driver that never retries operations can use ExInterlockedPushEntryList andExInterlockedPopEntryList to manage its queueing of IRPs internally in a singly linkedinterlocked queue. Any driver that uses such an interlocked queue also must provide residentstorage for the queue, for a ListHead of type SINGLE_LIST_ENTRY, and for anExecutiveSpinLock, as shown in Figure 3.22, and must set up its queue in a similar mannerwhen the driver initializes.

Because its interlocked queue is protected by the ExecutiveSpinLock, the driver can insertIRPs into its queue and remove them in a multiprocessor-safe manner from any driver routinerunning at less than or equal to DISPATCH_LEVEL IRQL. The driver's calls to theExInterlocked..List routines raise IRQL on the current processor to DISPATCH_LEVEL,which precludes calling any of these routines from a device driver's ISR or SynchCritSectionroutine.

For more information about managing IRQLs and using executive spin locks, see Chapter 16.See also Appendix A for support-routine-specific IRQL requirements.

Page 161: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-76 Kernel-mode Driver Design Guide

3.9 Kernel Dispatcher Objects for Drivers

As already mentioned in Section 3.7, the NT Kernel defines the object type for timer objects,which can be associated with driver-supplied DPC routines. A timer object is one of a set ofKernel-defined object types called dispatcher objects, which can be used as synchronizationmechanisms within a nonarbitrary thread context. The NT Kernel also defines the types forother dispatcher objects, including events, semaphores, and mutexes.

Every Kernel-defined dispatcher object has a state that is either set to Signaled or set to Not-Signaled. Threads can synchronize their operations by having one (or more) threads callKeWaitForSingleObject or KeWaitForMultipleObjects with dispatcher object(s) andwaiting until another routine (or thread) sets the dispatcher object(s) to the Signaled state.When a thread calls the former of these routines with a dispatcher object, the thread is put intoa wait state until the dispatcher object is set to the Signaled state. When a thread calls the latterof these routines with one or more dispatcher objects, the thread is put into a wait state untilone or all of the dispatcher objects are set to the Signaled state.

Whenever a dispatcher object is set to the Signaled state, the Kernel changes the state of anythread waiting on that object to ready: the thread will be scheduled to run according to itscurrent priority and the current availability of processor(s) for any thread with that priority. Asmentioned in Chapter 1, most standard NT driver routines are run ahead of all threads that arecurrently in the ready state, regardless of the threads' priorities.

For most NT device and intermediate drivers, the Kernel's dispatcher objects can be used forsynchronization only within their DriverEntry, Reinitialize, or certain Dispatch routines thatare run in the context of a system thread. The Dispatch routines of a highest-level NT driveralso are run in a thread context, usually that of a user-mode thread requesting an I/O operation.Lower-level drivers' Dispatch routines that initiate synchronous I/O operations, such as create,flush, shutdown, close, and some device control operations, can wait on a dispatcher object forthe completion of the requested operation.

However, lower-level drivers' Dispatch routines cannot wait on a Kernel-defined dispatcherobject for the completion of a transfer operation to or from a paging device: that is, Dispatchroutines for read/write requests generally cannot wait on a dispatcher object, nor can aDispatch routine for any device control request with an I/O control code whose transfer-typevalue is other than METHOD_BUFFERED. For more information about device-type-specificrequests that NT drivers must support, see Appendix B.

Page 162: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-77

Note that every other standard NT driver routine is run in an arbitrary thread context: that ofwhatever thread happens to be current when the driver routine is called to process an IRP or tohandle a device interrupt. Moreover, most standard driver routines are run at raised IRQL,either at DISPATCH_LEVEL, or for device drivers, at DIRQL. NT driver designers alwaysshould keep in mind the following fact: ■ It is a fatal error to wait on a Kernel dispatcher object for a nonzero interval at raised

IRQL.

Driver Dispatch routines that initiate asynchronous I/O operations to a device and all standardroutines that run at raised IRQL cannot use dispatcher objects to synchronize their processingof IRPs. For more information about the IRQLs at which standard driver routines are run, seeChapter 16.

Consequently, most NT device and intermediate drivers, which usually do not set up their owndriver-created threads or use system worker threads (as NT file system drivers do), can useone of these dispatcher objects only while the driver is initializing or in a Dispatch routine fora synchronous I/O request. For example, the DriverEntry routines of NT-supplied SCSI classdrivers frequently set up an event object so they can do the following: 1 Call IoBuildSynchronousFsdRequest with the pointer to an initialized event object 2 Set up the IRP that this routine creates and send it with IoCallDriver down to the NT

SCSI port driver to request configuration information 3 Call KeWaitForSingleObject to wait on the event until the port driver returns the IRP

with IoCompleteRequest, which sets the event to the Signaled state indicating that therequested information (or an error status) is available for the class driver

The following subsections describe each of these dispatcher objects for driver writers whoeither design drivers with device-dedicated threads (or worker-thread callback routines) ordesign drivers that must wait on one or more dispatcher objects in their DriverEntry,Reinitialize, or Dispatch routines for synchronous I/O operations. Note that such a driver, ineffect, "steals cycles" from another thread's quantum if the driver does not set up its ownthread.

For simplicity, the following subsections illustrate calls to KeWaitForSingleObject. Formore information about KeWaitForMultipleObjects, see the Kernel-mode Driver Reference.

Page 163: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-78 Kernel-mode Driver Design Guide

3.9.1 Timer Objects

Any NT driver can set up a timer object that it can use within a thread context to time outoperations within the driver's other routines. When the driver initializes, it must callKeInitializeTimer to set up the timer object. After the driver is loaded, it can callKeSetTimer to set up a time-out interval for an operation and KeWaitForSingleObject witha pointer to the timer object to have the thread put into a wait state while other driver routinesprocess an I/O request. Figure 3.23 illustrates these calls.

Figure 3.23 Waiting on a Timer Object

As Figure 3.23 shows, the driver must provide storage for a timer object, which must beinitialized in the DriverEntry or Reinitialize routine.

Within the context of a particular thread, such as a driver-created thread or a thread requestinga synchronous I/O operation, the driver can wait on its timer object as shown in Figure 3.23: 1 The driver calls KeSetTimer with a pointer to the timer object and a given DueTime,

expressed in units of 10 milliseconds. A positive value for DueTime specifies an absolutetime at which the timer object should be removed from the Kernel's timer object queueand set to the Signaled state. A negative value for DueTime specifies an interval relative

Page 164: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-79

to the current system time. (Note that the driver passes a NULL pointer for the DPCobject previously shown in Figure 3.20 when it calls KeSetTimer if it waits on the timerobject instead of queueing its CustomTimerDpc routine.)

2 The driver calls KeWaitForSingleObject with a pointer to the timer object, which putsthe thread into a wait state while the timer object is in the Kernel's timer object queue.

3 The given DueTime expires. 4 The Kernel dequeues the timer object, sets it to the Signaled state, and changes the

thread's state from waiting to ready. 5 The Kernel dispatches the thread for execution as soon as a processor is available: that is,

no other thread with a higher priority is currently in the ready state and there are nokernel-mode routines to be run at raised hardware priority (IRQL > PASSIVE_LEVEL).

NT driver routines that run at raised IRQL can time out requests by using a timer object withan associated DPC object, as already described in Section 3.6, to queue a driver-suppliedCustomTimerDpc routine. Only driver routines that run within a nonarbitrary thread contextcan wait for a nonzero interval on a timer object, as shown in Figure 3.23.

Some NT drivers create their own threads and set their thread's base priority to the lowest real-time priority value. Others, particularly NT file system drivers, use system worker threadswith a base priority that is usually set to the highest variable priority value. The Kernelschedules a thread with the lowest real-time priority to run ahead of every thread with avariable priority, which includes almost every user-mode thread in the system.

Like every other thread in the system, a driver-created thread is represented by a Kernel threadobject, which is also a dispatcher object. Consequently, a driver need not have its driver-created thread use a timer object to voluntarily put itself into a wait state for a given interval.Instead, the thread can call KeDelayExecutionThread with a caller-supplied interval. Formore information about this technique, see the section on polling a device in Chapter 16. Seealso the Kernel-mode Driver Reference for the specifics of callingKeDelayExecutionThread.

NT drivers' DriverEntry and Reinitialize routines also run in a system thread context, so NTdrivers can call KeWaitForSingleObject with a driver-initialized timer object orKeDelayExecutionThread while they are initializing. A device driver can callKeStallExecutionProcessor for a very short interval (preferably something less than 50microseconds) if it must wait for the device to update state during its initialization.

However, higher-level NT drivers generally use another synchronization mechanism in theirDriverEntry and/or Reinitialize routines instead of using a timer object. Higher-level NTdrivers should always be designed to layer themselves over any lower-level driver of aparticular type (or types) of device. Therefore, a higher-level driver tends to become slow-to-load if it waits on a timer object or calls KeDelayExecutionThread, because such a drivermust wait for an interval long enough to accommodate the slowest possible device supportingit. Note that a "safe" interval for such a wait is also very difficult to determine.

Page 165: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-80 Kernel-mode Driver Design Guide

3.9.2 Event Objects

As already mentioned in Section 3.9, some of the system-supplied drivers use an event objectin their DriverEntry routines to wait while the next-lower driver processes an IRP set up bythe waiting driver. NT drivers that have threads or driver Dispatch routines that wait on thecompletion of a synchronous I/O request can also use an event object to synchronizeoperations between their threads and other driver routines. Any NT driver that uses an eventobject must call KeInitializeEvent before it waits on, sets, or resets the event. Figure 3.24illustrates how a driver with a thread can use an event object for synchronization.

Figure 3.24 Waiting on an Event Object

As Figure 3.24 shows, the driver must provide the storage for an event object. The driver canuse the device extension of a driver-created device object (see Section 3.2), the controllerextension if it uses a controller object (see Section 3.4), or nonpaged pool allocated by thedriver.

Page 166: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-81

When the DriverEntry (or Reinitialize) routine calls KeInitializeEvent, it must pass a pointerto the driver's storage for the event object. In addition, the caller must specify the initial State(Signaled or Not-Signaled) for the event object, as shown in Figure 3.24. The driver also mustspecify the Type for its event, which can be either of the following: ■ A SynchronizationEvent - when such an event is set to the Signaled state, a single

thread that is waiting on the event becomes elgible for execution and the event's state isautomatically reset to Not-Signaled. This type of event is sometimes called anautoclearing event, because its Signaled state is automatically reset.

■ A NotificationEvent - when such an event is set to the Signaled state, all threads thatwere waiting on the event become elgible for execution and the event remains in theSignaled state until an explicit reset to Not-Signaled occurs: that is, there is a call toKeResetEvent with the given event object.

Except for file system drivers, very few NT drivers must have a single driver thread, let alonea set of threads that might synchronize their operations by waiting on an event that protects ashared resource. Most of the system-supplied drivers that use event objects to wait for thecompletion of an I/O operation set the Type to NotificationEvent. An event object for IRPsthat a driver creates with IoBuildSynchronousFsdRequest orIoBuildDeviceIoControlRequest is almost always initialized as a notification-type eventbecause the caller will wait on the event for notification that its request has been satisfied byone or more lower-level drivers.

After the driver has initialized, its thread, if any, and other routines can synchronize theiroperations on the event. For example, a driver with a thread that manages the queueing ofIRPs, such as the system-supplied floppy driver, might synchronize IRP processing on anevent, as shown in Figure 3.24: 1 The thread, which has dequeued an IRP for processing on the device, calls

KeWaitForSingleObject with a pointer to the initialized event object. 2 Other driver routines carry out device I/O operations necessary to satisfy the IRP, and,

when these operations are complete, the driver's DpcForIsr routine calls KeSetEventwith a pointer to the event object, a driver-determined priority boost for the thread(Increment in Figure 3.24), and a Boolean Wait set to FALSE. Calling KeSetEvent setsthe event object to the Signaled state, thereby changing the waiting thread's state toready.

3 The Kernel dispatches the thread for execution as soon as a processor is available: that is,no other thread with a higher priority is currently in the ready state and there are nokernel-mode routines to be run at raised hardware priority (IRQL > PASSIVE_LEVEL).The thread can now complete the IRP (if the DpcForIsr hasn't already calledIoCompleteRequest with the IRP), and dequeue another IRP to be processed on thedevice.

Page 167: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-82 Kernel-mode Driver Design Guide

Calling KeSetEvent with the Wait parameter set to TRUE indicates the caller's intention toimmediately call a KeWait..Object(s) routine on return from KeSetEvent. Note that anystandard driver routine that runs at an IRQL greater than PASSIVE_LEVEL cannot wait for anonzero interval on an any dispatcher object(s) without bringing down the system (see Section3.6.1), but such a routine can call KeSetEvent while running at an IRQL less than or equal toDISPATCH_LEVEL.

For more information about the hardware priorities at which standard driver routines run, seeChapter 16. For support-routine-specific IRQL requirements, see Appendix A.

Page 168: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-83

3.9.3 Semaphore Objects

NT drivers can use a semaphore object to synchronize operations between their threads andother driver routines (for example, to put a driver thread into a wait state when there are nooutstanding I/O requests for the driver). Any NT driver that uses a semaphore object must callKeInitializeSemaphore before it waits on or releases the semaphore. Figure 3.25 illustrateshow a driver with a thread can use a semaphore object.

Page 169: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-84 Kernel-mode Driver Design Guide

Figure 3.25 Waiting on a Semaphore Object

As Figure 3.25 shows, the driver must provide the storage for a semaphore object. The drivercan use the device extension of a driver-created device object (see Section 3.2), the controllerextension if it uses a controller object (see Section 3.4), or nonpaged pool allocated by thedriver.

When the DriverEntry (or Reinitialize) routine calls KeInitializeSemaphore, it must pass apointer to the driver's storage for the semaphore object. In addition, the caller must specify aCount for the semaphore object, as shown in Figure 3.25, that determines its initial state(nonzero for Signaled).

The caller also must specify a Limit for the semaphore, which can be either of the following: ■ A Limit of 1 - when such a semaphore is set to the Signaled state, a single thread waiting

on the semaphore becomes elgible for execution and can access whatever resource isprotected by the semaphore. This type of semaphore is also called a binary semaphorebecause a thread either does or does not have exclusive access to the semaphore-protected resource.

Page 170: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-85

■ A Limit greater than 1 - when such a semaphore is set to the Signaled state, some numberof threads waiting on the semaphore object become elgible for execution and can accesswhatever resource is protected by the semaphore. This type of semaphore is called acounting semaphore because the routine that sets the semaphore to the Signaled state alsospecifies how many waiting threads can have their states changed from waiting to ready(up to the Limit set when the semaphore was initialized or some number less than thispre-set Limit).

Except for file system drivers, very few NT drivers have a single driver thread, let alone a setof threads that might wait on a semaphore. Very few system-supplied drivers use semaphoreobjects, and, of those that do, even fewer use a binary semaphore. A binary semaphore, whileseeming to have similar functionality to a mutex object, does not provide the built-inprotection agains deadlocks that a mutex object has for system threads running inmultiprocessor machines. For more information about mutex objects, see Section 3.9.4, next.

However, the Dispatch routines of highest-level NT drivers, which are run in the context ofthe thread requesting an I/O operation, and lower-level driver Dispatch routines forsynchronous I/O operations might use a semaphore to protect a resource shared among theDispatch routines or with a driver-created thread.

After the driver has initialized, a driver can synchronize operations on the semaphore thatprotects the shared resource. For example, a driver with a device-dedicated thread thatmanages the queueing of IRPs, such as the system-supplied floppy driver, might synchronizeIRP queueing on a semaphore, as shown in Figure 3.25: 1 The thread calls KeWaitForSingleObject with a pointer to the initialized semaphore

object to put itself into a wait state. 2 IRPs begin to come in that require device I/O operations, so the driver's Dispatch routines

insert each such IRP into an interlocked queue (see Section 3.8.2) under spin-lock controland call KeReleaseSemaphore with a pointer to the semaphore object, a driver-determined priority boost for the thread (Increment in Figure 3.25), an Adjustment of 1that is added to the semaphore's count as each IRP is queued, and a Boolean Wait set toFALSE. A nonzero semaphore count sets the semaphore object to the Signaled state,thereby changing the waiting thread's state to ready.

3 The Kernel dispatches the thread for execution as soon as a processor is available: that is,no other thread with a higher priority is currently in the ready state and there are nokernel-mode routines to be run at raised hardware priority (IRQL > PASSIVE_LEVEL).The thread removes an IRP from the interlocked queue, passes it on to other driverroutines for further processing, and calls KeWaitForSingleObject again.

If the semaphore is still set to the Signaled state (its count remains nonzero, indicatingthat more IRPs are in the driver's interlocked queue), the Kernel again changes thethread's state from waiting to ready. By using a counting semaphore in this manner, thedriver's thread "knows" there is an IRP to be to be removed from the interlocked queuewhenever the thread is run.

Page 171: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-86 Kernel-mode Driver Design Guide

Calling KeReleaseSemaphore with the Wait parameter set to TRUE indicates the caller'sintention to immediately call a KeWait..Object(s) routine on return fromKeReleaseSemaphore. Note that any standard driver routine that runs at an IRQL greaterthan PASSIVE_LEVEL cannot wait for a nonzero interval on any dispatcher object(s) withoutbringing down the system (see Section 3.6.1), but such a routine can callKeReleaseSemaphore while running at an IRQL less than or equal to DISPATCH_LEVEL.

For more information about the hardware priorities at which standard driver routines run, seeChapter 16. For support-routine-specific IRQL requirements, see Appendix A.

3.9.4 Mutex Objects

As already mentioned in Section 3.9.3, NT mutex objects have built-in features that providesystem (kernel-mode only) threads mutually-exclusive, deadlock-free access to sharedresources in symmetric multiprocessor machines. Every NT mutex object has two basic built-in features that provide this protection for resources shared between (or among) systemthreads: 1 Ownership - The Kernel assigns ownership of a given mutex to a single thread at a time,

which has the following effects ■ Raises the owning thread's priority to the lowest real-time priority if that thread's

priority is not already in the real-time range ■ Prevents the owning thread's process from leaving the balance set, that is from being

paged out to secondary storage ■ Prevents the delivery of normal kernel-mode asynchronous procedure calls (APCs),

that is, from being preempted by an APC unless the Kernel issues an APC_LEVELsoftware interrupt to run a special kernel APC (such as the I/O Manager's IRPcompletion routine that returns results to the original requestor of an I/O operation)

Note also that a thread can acquire ownership of a mutex object that it already owns(recursive ownership), but that a mutex object is not set to the Signaled state until thethread releases its ownership completely, that is, explicitly releases the mutex as manytimes as ownership was acquired.

The Kernel never permits a thread that owns a mutex to cause a transition to user modewithout first releasing the mutex and setting it to the Signaled state. (If a driver routinethat owns a mutex returns control to the I/O Manager before releasing ownership of themutex, the Kernel brings down the system.)

Page 172: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-87

2 Level - The Kernel tracks attempts to acquire ownership of each mutex object, accordingto the mutex object's Level, which is a value assigned to the mutex object when it wasinitialized. The Level of a given mutex object determines whether a thread can acquireownership of the mutex at any particular time, according to the following policy:

■ While a thread owns one mutex, the Kernel does not allow that thread to attempt theacquisition of a second mutex with an assigned Level of a lower value; that is, theKernel returns an error at any such attempt.

Thus, the owner of a mutex cannot attempt to acquire another mutex with a lower Levelthat is already owned by another thread trying to acquire the first thread's mutex. If theKernel's policy were not enforced, this scenario would cause deadlocks because neitherthread could acquire ownership of the other thread's currently held mutex.

As its name suggests, a mutex object is a synchronization mechanism designed to ensuremutually exclusive access to a single resource shared between two (or among more) kernel-mode threads. Consequently, only highest-level drivers, such as file system drivers that useexecutive worker threads, or drivers with driver-created threads are likely to use a mutexobject. Such a driver must call KeInitializeMutex once before it waits on or releases itsmutex object. Figure 3.25 illustrates how two system threads might use a mutex object.

Page 173: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

3-88 Kernel-mode Driver Design Guide

Figure 3.26 Waiting on a Mutex Object

As Figure 3.26 shows, the driver must provide the storage for a mutex object. Because driverscan wait on and release mutexes only within a thread context, a driver that creates its ownsystem threads can provide storage for its mutex objects in paged pool, allocated by the driver.If the driver uses executive worker threads or waits on the mutex in its Dispatch routines, itshould provide the storage in the device extension of a driver-created device object (seeSection 3.2), in the controller extension if it uses a controller object (see Section 3.4), or innonpaged pool allocated by the driver.

When the DriverEntry (or Reinitialize) routine calls KeInitializeMutex it must pass a pointerto the driver's storage for the mutex object, which the Kernel initializes to the Signaled state.In addition, the caller must specify a Level for the mutex object, as shown in Figure 3.26.

Page 174: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

NT Objects and Support for Drivers 3-89

After the driver has initialized, it can manage mutually exclusive access to a shared resourceas shown in Figure 3.26. For example, a highest-level driver's Dispatch routines and driverthread might use a mutex to protect a driver-created queue for IRPs. Note that the initial stateof a mutex object is always set to the Signaled state. Therefore, as Figure 3.26 shows: 1 A Dispatch routine's initial call to KeWaitForSingleObject with a pointer to the mutex

object puts the thread immediately into the ready state, gives the thread ownership of themutex, and resets the mutex state to Not-Signaled. As soon as the Dispatch routineresumes running, it can safely insert an IRP into the mutex-protected queue.

2 When a second thread (another Dispatch routine, driver-supplied worker-thread callbackroutine, or driver-created thread) calls WaitForSingleObject with the mutex, the secondthread is put into the wait state.

3 When the Dispatch routine finishes queueing the IRP, it calls KeReleaseMutex with themutex object and a Boolean Wait value, which indicates whether it intends to callKeWaitForSingleObject with the mutex as soon as KeReleaseMutex returns control.

4 Assuming the Dispatch routine released its ownership of the mutex (Wait set to FALSE),the mutex is set to the Signaled state by the first thread's call to KeReleaseMutex. Themutex currently has no owner, so the Kernel determines whether another thread iswaiting on the mutex. If so, the Kernel makes the second thread the mutex owner,possibly boosts the thread's priority to the lowest real-time priority value, and changes itsstate to ready.

5 The Kernel dispatches the second thread for execution as soon as a processor is available:that is, no other thread with a higher priority is currently in the ready state and there areno kernel-mode routines to be run at raised hardware priority (IRQL >PASSIVE_LEVEL). The second thread (a Dispatch routine queueing an IRP, or thedriver's worker-thread callback routine or driver-created thread dequeueing an IRP) cannow safely access the shared resource until it calls KeReleaseMutex.

If a thread acquires ownership of a mutex object recursively, the owning thread mustexplicitly call KeReleaseMutex as many times as it waited on the mutex in order to set themutex object to the Signaled state.

Calling KeReleaseMutex with the Wait parameter set to TRUE indicates the caller's intentionto immediately call a KeWait..Object(s) routine on return from KeReleaseMutex. Note thatany standard driver routine that runs at an IRQL greater than PASSIVE_LEVEL cannot waitfor a nonzero interval on any dispatcher object(s) without bringing down the system (seeSection 3.6.1), but such a routine can call KeReleaseMutex if it owns the mutex whilerunning at an IRQL less than or equal to DISPATCH_LEVEL.

For more information about the hardware priorities at which standard driver routines run, seeChapter 16. For support-routine IRQL requirements, see Appendix A.

Page 175: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 4Basic NT Driver Structure

The basic structure of every NT driver consists of a set of required standard driver routines,plus some number of optional standard routines and internal routines, as determined by thetype of driver and the driver's designer. The common set of standard routines allows NTdrivers to process IRPs by calling kernel-mode support routines.

This chapter presents an overview of the standard routines for NT drivers, so that driverwriters can determine the following: ■ The set of standard driver routines they must implement to process IRPs. ■ The set of standard driver routines they can implement, depending on the nature of the

device or the functionality required of the driver. ■ General advice about how to begin development of an NT driver.

For more information about any of the support routines mentioned in this chapter, seeAppendix A and the Kernel-mode Driver Reference. For more information about the basic,required functionality of each standard routine, see Chapters 5 through 15.

4.1 Standard Driver Routines

As mentioned in Chapter 2, all NT driver writers must implement certain system-definedstandard routines and can implement others, along with any internal routines that the driverdesigner chooses. Figure 4.1 again shows the NT driver object, representing a driver's loadimage, and the set of standard routines for NT drivers.

Page 176: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-2 Kernel-mode Driver Design Guide

Figure 4.1 Standard NT Driver Routines

As Figure 4.1 shows, all NT drivers must have a basic set of standard routines in order toprocess IRPs. Otherwise, the set of standard routines that can or must be implemented dependson whether the driver services a physical device or is layered over such a device driver, aswell as on the nature of the underlying physical device.

Page 177: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-3

Every NT driver must have the following types of routines, defined by the NT I/O Manager:

DriverEntry

NTSTATUS (*PDRIVER_INITIALIZE) ( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath );

Every NT driver must have an initialization routine, which should be namedDriverEntry. For an overview of this routine's functional requirements, see Chapter5.

Dispatch

NTSTATUS (*PDRIVER_DISPATCH) ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

Every NT driver must have at least one Dispatch routine, but any driver can have aseparate Dispatch routine for each IRP major function code (IRP_MJ_xxx) ithandles. For an overview of Dispatch routines' functional requirements, see Chapter6. For device-type-specific information about which major function codes NTdrivers must handle, see Appendix B.

StartIo (or Queue-management)

VOID (*PDRIVER_STARTIO) ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

If an NT device driver cannot complete all possible requests in its Dispatch routines,it either must have a StartIo routine or must set up one or more driver-createdinternal queues and manage its own queueing of IRPs. Any higher-level driver canhave a StartIo routine and/or driver-created internal queues of IRPs. For anoverview of the StartIo routine's functional requirements and of alternate queueingtechniques, see Chapter 7.

Page 178: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-4 Kernel-mode Driver Design Guide

Depending on the driver's level and the nature of the underlying physical device, an NT drivercan (or must) have the following types of routines, defined by the NT I/O Manager or Kernel:

Reinitialize

VOID (*PDRIVER_REINITIALIZE) ( IN PDRIVER_OBJECT DriverObject, IN PVOID Context, IN ULONG Count );

In addition to its DriverEntry routine, any driver can have a Reinitialize routine tobe called one or more times later in the system boot process. For more informationabout optional Reinitialize routines, see Chapter 5.

InterruptService

BOOLEAN (*PKSERVICE_ROUTINE) ( IN PKINTERRUPT Interrupt, IN PVOID ServiceContext // usually points to device object );

Any (lowest-level) driver of a physical device that generates interrupts must have anISR. For an overview of an ISR's functional requirements, see Chapter 8.

DpcForIsr or CustomDpc

VOID (*PIO_DPC_ROUTINE) ( IN PKDPC Dpc, IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );

Page 179: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-5

VOID (*PKDEFERRED_ROUTINE) ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 );

Any driver that has an ISR must have a DpcForIsr or CustomDpc routine, and canhave additional CustomDpc routines. For an overview of the DpcForIsr routine'sfunctional requirements and more information about CustomDpc routines, seeChapter 9.

SynchCritSection

BOOLEAN (*PKSYNCHRONIZE_ROUTINE) ( IN PVOID SynchronizeContext );

Any lowest-level device driver with data or device registers shared between its ISRand other driver routines should have one or more SynchCritSection routines. For anoverview of a SynchCritSection routine's functional requirements, see Chapter 10.

AdapterControl and/or ControllerControl

IO_ALLOCATION_ACTION (*PDRIVER_CONTROL) ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context );

Any driver whose device uses system DMA or packet-based busmaster DMA musthave an AdapterControl routine. Any device driver that must synchronize operationsthrough a physical controller to similar devices (or device channels) can have aControllerControl routine. For an overview of these routines' functionalrequirements, see Chapter 11.

Page 180: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-6 Kernel-mode Driver Design Guide

Cancel

VOID (*PDRIVER_CANCEL) ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

Keyboard, mouse, serial, parallel, and sound device drivers, as well as file systemdrivers, have Cancel routines. Any highest-level driver in which IRPs might remainqueued for an indefinite interval (so that a user could cause a previously submittedI/O request to be cancelled) must have one or more Cancel routines. For anoverview of a Cancel routine's functional requirements, see Chapter 12.

IoCompletion

NTSTATUS (*PIO_COMPLETION_ROUTINE) ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context );

Any intermediate (or highest-level) driver can have one or more IoCompletionroutines. Other driver routines can set up the IoCompletion routine to be calledwhen all lower-level drivers have finished processing a given IRP. For a generaldiscussion of how higher-level drivers can use IoCompletion routines, see Chapter2. See also Section 4.1.2 for an overview of how a representative intermediatedriver's IoCompletion routine processes IRPs. For more information about anIoCompletion routine's functional requirements, see Chapter 13.

IoTimer (and/or CustomTimerDpc)

VOID (*PIO_TIMER_ROUTINE) ( IN PDEVICE_OBJECT DeviceObject, IN PVOID Context );

Page 181: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-7

VOID (*PKDEFERRED_ROUTINE) ( IN PKDPC Dpc, IN PVOID DeferredContext, IN PVOID SystemArgument1, IN PVOID SystemArgument2 );

To monitor whether an I/O operation has timed out, any driver can have IoTimerand/or CustomTimerDpc routines. An IoTimer routine is called once per secondwhen the driver has enabled the timer. A CustomTimerDpc routine can be called atfiner-grained intervals or at variable intervals. For more information aboutCustomTimerDpc routines and an overview of timer routines' functionalrequirements, see Chapter 14.

Unload

VOID (*PDRIVER_UNLOAD) ( IN PDRIVER_OBJECT DriverObject );

An NT driver must have an Unload routine if it can be unloaded while the system isrunning. For an overview of the Unload routine's functional requirements, seeChapter 15.

As the preceding type declarations for standard driver routines show, the current IRP andtarget device object are input arguments to many standard routines defined by the I/OManager. Each NT driver processes each IRP in stages through its set of standard routines.The next two sections describe the general path for an IRP through a lowest-level physicaldevice driver and through an intermediate driver, respectively.

Page 182: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-8 Kernel-mode Driver Design Guide

4.2 Device Drivers' Staged IRP Processing

As shown in Figure 4.1, lowest-level device drivers have certain standard routines that higher-level NT drivers need not have. The set of standard routines for lowest-level device driversalso varies according to the nature of the device each controls, whether a particular driver usesdirect or buffered I/O (as explained in Chapter 3), and according to the design of each driver.Figure 4.2 illustrates the path an IRP takes through the standard routines of an NT massstorage device driver with the following characteristics: ■ The device generates interrupts at the end of each I/O operation, so the driver has ISR

and DpcForIsr routines. ■ The driver has a StartIo routine, rather than setting up internal queues for IRPs and

managing its own queueing. ■ The driver uses DMA, so it sets its device objects' Flags for direct I/O, as described in

Chapter 3, and has an AdapterControl routine.

Page 183: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-9

Figure 4.2 IRP Path through Device Driver Routines (DMA)

As Figure 4.2 shows, an IRP is sent first to the driver's Dispatch routine for the given majorfunction code (IRP_MJ_xxx). Note that an NT driver is never sent an IRP with an unknownfunction code, because each driver sets its Dispatch routine(s) in the driver object on anIRP_MJ_xxx-specific basis. However, a Dispatch routine that handles more than oneIRP_MJ_xxx, handles an IRP_MJ_xxx with minor subfunctions (IRP_MN_xxx), or handlesdevice control requests (IRP_MJ_DEVICE_CONTROL and/orIRP_MJ_INTERNAL_DEVICE_CONTROL), and every other driver routine that processeseach IRP must call IoGetCurrentIrpStackLocation in order to determine what to do andwhat arguments to use.

Page 184: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-10 Kernel-mode Driver Design Guide

The IRP shown in Figure 4.2 requests a data transfer operation (IRP_MJ_READ orIRP_MJ_WRITE), and this driver's I/O stack location is the lowest in the IRP, with anindefinite number of higher-level drivers' I/O stack locations shown shaded. For simplicity,calls to IoGetCurrentIrpStackLocation from the DispatchReadWrite, StartIo,AdapterControl, and DpcForIsr routines are not shown in Figure 4.2.

Assuming the arguments for the read/write request are valid, the Dispatch routine would callIoMarkIrpPending to indicate that the IRP is not yet completed, and IoStartPacket to queueor pass the IRP on to the driver's StartIo routine for further processing; the Dispatch routinewould also return STATUS_PENDING. Figure 4.3 illustrates such a call to IoStartPacket.

Figure 4.3 Calling IoStartPacket

Page 185: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-11

If the driver is currently busy processing another IRP on the device, the IRP will be put intothe device queue associated with the device object. If the driver is not busy and the devicequeue is empty, the StartIo routine will be called immediately with the input IRP.

Note that the driver of a mass storage device would not supply a Cancel routine when it callsIoStartPacket, because a file system layered over such a driver handles the cancelation of fileI/O requests. (The highest-level driver in a chain of layered NT drivers usually handles thecancelation of IRPs, but sometimes an intermediate driver handles cancelations.) The devicedriver shown in Figure 4.2 might supply a Key value to impose a driver-determined order onIRPs in the device queue.

Assuming the StartIo routine shown in Figure 4.2 finds that the transfer request can be doneby a single DMA operation, the StartIo routine calls IoAllocateAdapterChannel with thedriver's AdapterControl routine. When the system DMA controller is available, theAdapterControl routine is called to set up the transfer operation, as shown in Figure 4.2. TheAdapterControl routine calls IoMapTransfer with a pointer to the buffer (described in theIRP) to set up the system DMA controller and, then, programs the device for the DMAoperation and returns. For more detailed information about using DMA, see the section onadapter objects in Chapter 3.

When the device interrupts to indicate its transfer operation is complete, the driver's ISR stopsthe device from generating interrupts and calls IoRequestDpc, as shown in Figure 4.2. Thiscall queues the DpcForIsr routine to complete as much of the transfer operation as possible ata lower hardware priority (IRQL).

When the DpcForIsr routine has done its processing for the transfer, it callsIoStartNextPacket promptly so the driver's StartIo routine will be called with the next IRP inthe device queue, if any are queued. The DpcForIsr sets the IRP's I/O status block, which isdefined as the following type:

typedef struct _IO_STATUS_BLOCK { NTSTATUS Status; ULONG Information;} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

Assuming the transfer operation was successful, the DpcForIsr shown in Figure 4.2 would setSTATUS_SUCCESS in the Status field and the number of bytes transferred in theInformation field of the IRP's I/O status block. Last, the DpcForIsr calls IoCompleteRequestwith the IRP. For more information about NTSTATUS-type values, which certain standarddriver routines return (see Section 4.1), and for information about logging errors, see Chapter16.

Figure 4.4 illustrates this driver's calls to IoStartNextPacket and IoCompleteRequest.

Page 186: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-12 Kernel-mode Driver Design Guide

Figure 4.4 Calling IoStartNextPacket and IoCompleteRequest

Note that drivers should call IoStartNextPacket (or IoStartNextPacketByKey) to begin thenext requested I/O operation as soon as possible, preferably before they callIoCompleteRequest and certainly before they return control. If no IRPs are currently in thedevice queue, IoStartNextPacket merely returns to the caller.

Each NT driver also must set the I/O status block in the IRP before callingIoCompleteRequest, in order to supply information to any interested higher-level drivers and,ultimately, to the original requestor of the I/O operation. If the driver can complete an IRP inits Dispatch routine, it calls IoCompleteRequest with a PriorityBoost ofIO_NO_INCREMENT because the driver can assume that the original requestor did not waiton its I/O operation. Otherwise, the driver supplies a system-defined and device-type-specificvalue that boosts the requestor's runtime priority to compensate for the time the requestorwaited on I/O.

Page 187: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-13

When a driver calls IoCompleteRequest with a given IRP, the I/O Manager fills that driver'sI/O stack location with zeros before calling the next higher-level driver (if any) that has set itsIoCompletion routine in the IRP. A higher-level driver's IoCompletion routine can check onlythe IRP's I/O status block to determine how all lower drivers handled a given request.

4.3 Intermediate Drivers' Staged IRP Processing

As already shown in Figure 4.1, higher-level NT drivers have a different set of standardroutines than lowest-level physical device drivers with an overlapping subset of standardroutines common to both types of drivers. The set of routines for intermediate and highest-level NT drivers also varies according to the nature of the underlying physical device, whetherthe underlying device driver uses direct or buffered I/O (as explained in Chapter 3), andaccording to the design chosen by each driver writer.

Figure 4.5 illustrates the path an IRP might take through the standard routines of an NT driverlayered somewhere over the device driver described in the preceding section. The intermediatemirror driver shown in Figure 4.5 has the following characteristics: ■ The driver is layered over more than one physical device, and, possibly over more than

one device driver. ■ The driver sometimes creates IRPs for lower-level drivers, depending on the requested

operation. ■ The driver has at least one NT file system driver layered over it.

Page 188: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-14 Kernel-mode Driver Design Guide

Figure 4.5 IRP Path through Intermediate Driver Routines

As Figure 4.5 shows, an IRP is sent first to the intermediate driver's Dispatch routine for thegiven major function code (IRP_MJ_xxx). The IRP shown in Figure 4.5 also requests a datatransfer operation (IRP_MJ_WRITE), and this intermediate driver's I/O stack location isshown in the middle, with an indefinite number of I/O stack locations for higher- and lower-level drivers shown shaded.

This driver mirrors write requests to another physical device but performs load balancing forread requests by sending them alternately to the driver(s) of devices with mirrored partitions.For write requests, it creates IRPs for both the original device and for a second device onwhich the data is mirrored. When the Dispatch routine calls IoAllocateIrp, it specifies thenumber of I/O stack locations needed in each new IRP, getting the appropriate value from thedevice objects of each driver just below the mirror driver (for details, see the section on deviceobjects in Chapter 3).

Page 189: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-15

Consequently, the intermediate driver's Dispatch routine callsIoGetCurrentIrpStackLocation with the original IRP to determine what it should be doingand to check arguments, if necessary. It calls IoSetNextIrpStackLocation to establish its ownstack location in each newly created IRP and IoGetCurrentIrpStackLocation to create acontext for itself that it uses later (in the IoCompletion routine). Then, it callsIoGetNextIrpStackLocation with each newly created IRP so that it can set up the nextlower-level drivers' I/O stack locations in the IRPs it allocated. The mirror driver's Dispatchroutine copies the IRP function codes and arguments (pointer to the transfer buffer, length inbytes to be transferred for IRP_MJ_WRITE) into the I/O stack locations for the next-lowerdrivers. These drivers, in turn, will set up the I/O stack locations for the drivers just belowthemselves (if any).

Assuming the arguments for the write request are valid, the Dispatch routine in Figure 4.5calls IoSetCompletionRoutine with each IRP it created. Note that a higher-level NT drivercan request that its IoCompletion routine be called when lower drivers complete an IRP withone, some, or any of STATUS_SUCCESS, STATUS_CANCELLED, or an error status. Sincethe driver in Figure 4.5 must dispose of the IRPs it created, this driver sets its IoCompletionroutine to be called when lower drivers complete its IRPs, regardless of the completion statusvalue. Because the driver in Figure 4.5 mirrors in parallel, it passes both IRPs that it createdon to the next-lower-level drivers by calling IoCallDriver twice, once for each target deviceobject representing a mirrored partition.

When either set of lower-level drivers completes the requested operation, the intermediatemirror driver's IoCompletion routine is called. This routine must determine whether the otherset of lower drivers has also completed the requested operation, so the mirror driver maintainsa count in its own I/O stack location of the original IRP.

Assuming that the I/O status block indicates that one set of lower drivers has completed theDupIRP1 shown in Figure 4.5 successfully, the mirror driver's IoCompletion routinedecrements its count but cannot complete the original IRP until it decrements the count tozero. If the decremented count is not yet zero, the IoCompletion routine calls IoFreeIrp withthe first-returned IRP (DupIRP1 in Figure 4.5) that the driver created and returnsSTATUS_MORE_PROCESSING_REQUIRED.

When the mirror driver's IoCompletion routine is called again with the DupIRP2 shown inFigure 4.5, it decrements the count in the original IRP and determines that both sets of lower-level drivers have carried out the requested operations. Assuming the I/O status block inDupIRP2 is set with STATUS_SUCCESS, the IoCompletion routine copies the returned IRP'sI/O status block into the original IRP, frees the second IRP it created, callsIoCompleteRequest with the original IRP, and returnsSTATUS_MORE_PROCESSING_REQUIRED to forestall completion processing forDupIRP2.

If either set of lower-level drivers does not complete the mirror driver's IRPs successfully, themirror driver's IoCompletion routine would log an error and attempt appropriate mirrored-datarecovery. For more information about logging errors, see Chapter 16.

Page 190: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-16 Kernel-mode Driver Design Guide

4.4 Designing and Developing an NT Driver

The following subsections offer general advice about getting the design and developmentprocess started.

4.4.1 Naming Device Objects

NT drivers' device objects have names for two basic reasons: 1 So that higher-level NT drivers can layer themselves over lower-level NT drivers. 2 So that user-mode applications can carry out I/O operations.

As explained in Chapter 5, the DriverEntry routine of each higher-level NT driver must pass apointer to the name of the next-lower-level driver's device object when it callsIoGetDeviceObjectPointer or IoAttachDevice. NT drivers at every level should name theirdevice objects to support the addition of value-added, higher-level drivers to the system.

Any driver of a (physical or logical) device to which user-mode code can direct I/O requestsmust name the device object(s) it creates. For example, when an application (or end user) calls TM TMa Win32 function to connect to a printer port (such as LPT1), the Windows NT Win32subsystem translates the application I/O request into a call to one of the NT I/O Manager'ssystem services with the name of a file object that represents the printer port. The NT ObjectManager and I/O Manager then parse the input file name to identify the target device for theI/O operation, and the I/O Manager sets up an IRP for the NT parallel device driver.

Consequently, NT uses a simple naming scheme to identify the devices in every machine. Ingeneral, each device object that can be the target of an I/O request or can be connected to by ahigher-level NT driver has a name of the form: \Device\GenericDeviceTypeDigit where GenericDeviceType indicates the kind of device, such as Harddisk, Keyboard, Tape,

etc. and The Digit suffix indicates the zero-based number of this device, depending on how

many other devices of the same type have already been named.

Every NT driver must follow these naming conventions for device objects. Note that the Digitsuffix guarantees that each device object representing a single type of device has a uniquename in every NT machine. For more information about how to assign an appropriate Digitsuffix to a named device object, see the section on using the configuration registry in Chapter16.

Page 191: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-17

The DriverEntry routine is responsible for setting up a unique-to-the-machine Unicode stringto name each device object that the driver creates, except for any device object that cannot bea target for an I/O request or cannot be connected to by a higher-level driver.

4.4.2 Choosing Names for Driver Routines

Except for the DriverEntry routine, the names used for the standard driver routines in thismanual were chosen to describe the functionality of each type of standard routine, and, insome cases, to provide hints about which types of NT objects a driver with certain standardroutines must use. You can name the standard driver routines anything you like in your driver.

However, any NT driver is easier to develop, debug, and test when the driver writer uses anaming scheme that distinguishes that driver's routines from those of other drivers. An NTdriver is also easier to debug and test if the driver writer uses names that distinguish betweenthe driver's entry points and its internal routines.

Consequently, most system-supplied NT drivers have routine names that follow theseguidelines: ■ Prepend a driver-specific prefix to each routine, preferably one prefix for all standard

routines and another for all internal routines. ■ Create a name for each routine that indicates its functionality (or, when debugging and

testing, where in the driver a given IRP is being processed).

For example, the system-supplied keyboard class driver prepends "KeyboardClass" to itsstandard driver routines, and "Kbd" to its internal routines. Other system-supplied drivers usea single identifying prefix on every routine, but prepend the lower-case letter "p" (for private)to this prefix for their internal routines.

Each routine in the system-supplied keyboard class driver also has a name that indicates theroutine's functionality. This driver's Dispatch routines are named according to theIRP_MJ_xxx each routine handles: KeyboardClassOpenClose, KeyboardClassRead,KeyboardClassDeviceControl, and so forth. Other standard routines in this driver have namessuch as KeyboardClassStartIo and KeyboardClassCancel to aid the driver's developer intracing the path of IRPs through the driver. These naming conventions also aid system testengineers in evaluating the performance of this driver, in finding out whether any particulardriver routine is a performance bottleneck, and in finding the exact location of any bug ifproblems occur in this driver during regression testing.

Page 192: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-18 Kernel-mode Driver Design Guide

4.4.3 Starting Design

The following lists a rather general set of design criteria that any NT driver writer can use tostart designing a driver: 1 Before writing any code, see Appendix B to determine which IRP major function codes

your driver must handle: ■ If you are designing a device driver, your driver must handle the same set of

IRP_MJ_xxx (and device I/O control codes) as every other NT driver for the sametype of peripheral device.

■ If you are designing an intermediate NT driver, identify the underlying physicaldevice (or devices) your driver will be layered over, because a higher-level drivermust have Dispatch entry points for every IRP_MJ_xxx that the drivers under it musthandle, in order to set up the next-lower driver's I/O stack location in IRPs and passeach IRP on to lower drivers with IoCallDriver.

When you have identified the IRP_MJ_xxx your driver must handle, you can determinethe upper limit on the number of Dispatch routines your driver might have. You can alsostart considering whether to combine particular IRP_MJ_xxx into convenient subsets tobe handled by particular Dispatch routines.

For example, most lowest-level and intermediate NT drivers need do little or no device orIoCompletion processing for an IRP_MJ_CREATE (equivalent to an "open targetdevice" request for these types of drivers) or IRP_MJ_CLOSE request. IRPs containingIRP_MJ_CREATE usually just establish the existence of the target device object forhigher-level drivers, and for user-mode protected subsystems via the I/O Manager'ssystem services. For lower-level drivers, IRPs containing IRP_MJ_CLOSE usuallyindicate that a user-mode subsystem on behalf of an application has closed the file handlefor the device to which it was sending I/O requests.

For a create/close request, most NT device and intermediate drivers set the IRP's I/Ostatus block with STATUS_SUCCESS in the Status field and zero in the Informationfield and, then, call IoCompleteRequest with the IRP from their Dispatch routines.Consequently, many lowest-level and intermediate NT drivers have a combined Dispatchroutine for create/close requests. However, NT file system drivers do considerably moreprocessing for create/close requests, and a serial device driver usually resets its device fora create request.

If you are designing a higher-level driver, consider the set of IRP major function codesyour driver must handle and determine the set of requests for which you might need toimplement an IoCompletion routine. As a general rule, a higher-level NT driver does notneed to have an IoCompletion routine for each kind of request. It needs IoCompletionroutines only for those IRP_MJ_xxx that could require further processing in the higher-level driver, depending on how lower drivers handle a given IRP_MJ_xxx request.

Page 193: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-19

2 Next, consider how many named device objects your driver must create: ■ A lowest-level device driver must create a named device object for each physical or

logical device that could be the target of an I/O request. As discussed in Chapter 2,some lowest-level drivers must create an indeterminate number of device objects.For example, a disk driver must create a device object for each physical disk andadditional device objects for partitions on its disks.

■ A higher-level driver must create a named device object to represent its own virtualdevice, so that still-higher-level drivers can connect their device objects to thisdriver's device object(s). In addition, a higher-level driver usually creates a set ofvirtual or logical device objects that correspond to the named device objects createdby the next-lower driver(s).

Note that you can develop your driver in stages, as explained in the next section, so anunder-development driver need not create every device object that the fully developeddriver will have. Nevertheless, determining how many device objects the driver musteventually create helps to identify any synchronization problems the driver writer mustsolve. Doing this can also help the driver writer in defining the driver-determinedcontents and structure of the device extension(s) for the driver's device objects.

For example, if the device extension will contain context shared between a devicedriver's ISR and its other routines, consider setting up the device extension to isolate allthe shared context in a part of the device extension. The driver's non-ISR routines canthen access other areas in the device extension without having to callKeSynchronizeExecution (see Chapter 10). However, if one non-ISR routine shares anarea with another, the driver extension would likely contain an executive spin lock, usedto protect that area from simultaneous access by both routines (see the section on usingspin locks in Chapter 16).

3 If you are writing a driver for a physical device, determine whether setting up the devicefor I/O requires the driver to wait for longer than around 50 microseconds for device statechanges:

■ If so, your driver will need to have a device-dedicated thread and manage its owninternal queues of IRPs (as the system-supplied floppy driver does).

■ Otherwise, your driver can have a StartIo routine and call IoStartPacket from itsDispatch routines and IoStartNextPacket or IoStartNextPacketByKey from itsDpcForIsr (or CustomDpc) routine, as described in Section 4.2, thereby relying onthe I/O Manager to manage the queueing of IRPs for your driver.

Page 194: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

4-20 Kernel-mode Driver Design Guide

4.4.4 Starting Development

It is easier to develop any driver if the driver is coded in stages. The following is a "cookbook"for getting a skeleton NT driver up and running quickly: 1 Write a DriverEntry routine that calls IoCreateDevice to create a single named device

object (see Chapter 3 for how to set up a device object). ■ Next, write a skeleton Dispatch routine for IRP_MJ_CREATE requests (see the

preceding section for a description of the minimum work a DispatchCreate routinemust do), and revise the DriverEntry routine to set the DispatchCreate entry point inthe driver object (see Chapter 2 for how to set an entry point in the driver object).

For any request, a higher-level NT driver's DriverEntry routine must establish aconnection to a next-lower-level driver (see Chapter 5), and its Dispatch routinemust set up the next-lower driver's I/O stack location in IRPs.

■ Compile and link your driver. ■ Test this skeleton driver by doing the following: ■ Set up a symbolic link between a Windows NT logical device name (such as

"LPT1" or drive "E:") and the target device object's name, in the configurationregistry. Use regedit to add a new symbolic link (of type REG_SZ) between a"DOS" device name and the target device object's name in the key\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Session Manager\DOS Devices. You must reboot the machine for this to takeeffect, but the newly created symbolic link is persistent across system bootsthereafter in your machine.

(Later, the fully developed driver must set up any necessary symbolic linkswith the corresponding protected subsystem(s) device names in theconfiguration registry. For more information about using the configurationregistry, see Chapter 16.)

■ Call the Win32 function CreateFile with the name of the appropriate deviceobject to be sure your driver was loaded and that the NT I/O Manager can sendIRPs to your driver's DispatchCreate routine.

2 Revise the DriverEntry routine to create other named device objects (if any) that thedriver will need and to set up a Dispatch entry point for IRP_MJ_CLOSE requests. Notethat the skeleton Dispatch routine for IRP_MJ_CREATE requests can usually handleIRP_MJ_CLOSE requests for an NT device or intermediate driver, so whether you writea separate DispatchClose routine is entirely up to you.

■ Compile and link your driver. ■ Test the driver as described in Item 1 to be sure that all the driver's new device

objects can be opened and closed with calls to the Win32 CreateFile andCloseHandle functions.

Page 195: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Basic NT Driver Structure 4-21

3 Add another Dispatch routine for an IRP_MJ_xxx that requires more processing than acreate/close request. For example, write a DispatchRead routine that reads data from thedevice into memory or that intercepts read requests for a set of lower drivers (that is,handles IRP_MJ_READ requests).

■ Revise the DriverEntry routine to set up any objects the new DispatchRead routinewill require and to set the new DispatchRead entry point in the driver object. Revisethe internal structure of the device extension, as necessary.

For a read request, a lowest-level driver would need to have at least skeleton StartIo,ISR, and DpcForIsr routines, would possibly need to have a SynchCritSectionroutine, and would need to have a skeleton AdapterControl routine if the device usesDMA, so revise as follows:

■ The device extension should provide storage for an interrupt object pointer, forany context area to be shared between the ISR and StartIo or DpcForIsrroutines, plus storage for adapter object information if the device uses DMA.

■ The DriverEntry routine must set the StartIo entry point in the driver object,and set up the driver's other new routines. In a lowest-level driver, theDriverEntry routine also must initialize the physical device. Note that the ISRcould be called immediately following a successful return fromIoConnectInterrupt, so the device-initialization code might need to disableinterrupts at the device before the DriverEntry routine registers the ISR (and re-enable interrupts at the device later).

For a read request, a higher-level driver might need to have a skeleton IoCompletionroutine that, at least, checks the I/O status block for STATUS_SUCCESS and callsIoCompleteRequest with the IRP.

■ Test this set of routines by calling the Win32 CreateFile and ReadFile routines, andby using the NT kernel debugger to trace the path of IRPs through the driver's newstandard routines (see Section 4.2 or 4.3).

Continue to add functionality to your driver by coding new Dispatch routines (and, in higher-level drivers, new IoCompletion routines) to handle required IRP_MJ_xxx, by revising theDriverEntry routine and device extension (or other driver-allocated storage) as required, andby fleshing out existing routines in the driver. Test the driver at each development stage, anduse the NT kernel debugger to trace IRPs through your driver to discover any bugs.

Page 196: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 5DriverEntry and Reinitialize Routines

This chapter summarizes the required functionality of NT drivers' standard DriverEntryroutines. For driver writers whose drivers must be initialized in stages, it also summarizes therequired functionality of a standard Reinitialize routine.

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing DriverEntry and Reinitialize routines.//

5.1 DriverEntry Routine Requirements

Each NT driver must have an initialization routine in order to be loaded. The NT I/O Managerdefines the prototype for this initialization routine, as shown in Chapter 4. A DriverEntryroutine is run in the context of a system thread at IRQL PASSIVE_LEVEL.

Each NT driver should have an initialization routine explicitly named DriverEntry. Otherwise,the driver writer must define the name of its initialization routine for the linker so that thedriver's transfer address is linked into the NT OS loader. Naming a driver's initializationroutine DriverEntry creates this linkage automatically.

Note that an NT driver should never link itself with the C runtime library, thereby bloating thedriver's load image with a set of functions the driver need not and, in some cases, should notcall. In particular, an NT driver cannot remain multiprocessor-safe if it calls any C runtimefunction that maintains state. NT drivers use the system-supplied support routines, includingthe NT kernel-mode Rtl (for Runtime library) functions, instead of the C runtimes.

The DriverEntry routine is responsible for exporting the driver's other entry points, forinitializing the NT objects the driver uses, and for setting up any other system resources thatthe driver uses. The DriverEntry routine of an NT device driver uses the NT configurationregistry to get some of the information it needs to initialize itself and to set information in theregistry for other drivers and/or protected subsystems to use. For more information aboutusing the configuration registry, see Chapter 16.

Page 197: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

5-2 Kernel-mode Driver Design Guide

In a lowest-level NT driver, the DriverEntry routine is also responsible for initializing thephysical device. In a higher-level NT driver, the DriverEntry routine is also responsible forconnecting to the next-lower-level driver(s), using one of the following techniques: ■ To establish a connection between paired class/port drivers, the class driver calls

IoGetDeviceObjectPointer with the name of the port driver's (primary) device object(s).For example, a SCSI class driver calls IoGetDeviceObjectPointer repeatedly, passing anupdated Unicode name string "\Device\ScsiPortDigit" where Digit is a zero-based countenumerating the SCSI HBAs in the machine. Then, the class driver uses the returneddevice object pointers to send get-configuration requests to the NT-supplied SCSI portdriver, in order to determine which HBAs control a bus with attached devices of the classdriver's type.

■ To establish a connection between an intermediate driver and a lower-level driver, theintermediate driver creates a device object and calls IoAttachDevice with the name of alower driver's device object and its newly created device object. This allows theintermediate driver to intercept requests bound for the lower driver's device by aliasingthe target device object to its own device object.

If the system itself can boot from the driver's device, the DriverEntry routine is alsoresponsible for connecting the boot device (the driver's primary device object that represents adisk, floppy, or CD-ROM device) to the corresponding ARC device by callingIoAssignArcName. For more information about the ARC firmware and the correspondingfunctionality in CISC-based NT platforms, see Chapter 16.

Every DriverEntry routine must do the following: ■ Fill in the driver object entry points for the driver's Dispatch, StartIo (if any), and Unload

(if any) routines. (See Chapter 2 for how to do this. For more information about Dispatch,StartIo, and Unload routines, see Chapters 6, 7, and 15, respectively.)

■ Create a device object for each physical, logical, or virtual device the driver services.(See Chapter 3 for how to set up a device object.)

■ Claim any necessary hardware resources or find appropriate lower-level drivers in the TMWindows NT configuration registry, and set up any necessary symbolic links betweenthe names of the driver's device objects and the protected subsystem names for the samedevice objects. For some types of drivers, the Hardware Abstraction Layer (HAL) or I/OManager provides support for setting up these symbolic links. Other types of drivers mustset them up in the configuration registry. (See Chapter 16 for more information aboutusing the configuration registry.)

■ Register the driver's other entry points and set up the objects necessary for any otherstandard routines the driver has. (For routine-specific requirements, see Section 5.2 andChapters 7-9, 11, and 14.)

Page 198: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

DriverEntry and Reinitialize Routines 5-3

■ Create and/or initialize any other system-defined objects, types, or resources the driveruses, such as the following:

■ If the driver has a device-dedicated thread, the DriverEntry routine must initializeany Kernel-defined dispatcher object that the thread waits on by calling theappropriate KeInitializeXxx routine with a pointer to the event, semaphore, mutex,or timer object for which the driver provides the storage. (See Chapter 3 for moreinformation about waiting on dispatcher objects.)

Because it executes in a system-thread context, the DriverEntry routine itself canwait for a nonzero interval on a dispatcher object, which must be initialized beforethe wait begins. For example, class drivers that call down to a corresponding portdriver during initialization usually wait on an event object they associate with anIRP, which they create with IoBuildSynchronousFsdRequest and pass withIoCallDriver to the port driver for the physical device.

■ If the driver uses an executive spin lock, the DriverEntry routine must callKeInitializeSpinLock before passing the spin lock to any other support routine.(See Chapter 16 for guidelines on how to use spin locks, and Chapter 8 for moreinformation about whether a driver must supply storage for an interrupt spin lock.)

If an NT device or intermediate driver has a device-dedicated thread, its DriverEntry routinemust call PsCreateSystemThread with the entry point for the driver's thread. If the driveruses executive worker threads, as many NT file system drivers do, it must supply a callbackroutine of type WORKER_THREAD_ROUTINE, which takes a single input PVOIDParameter.

Note that an NT driver must provide storage, usually in the device extension of a deviceobject, for any Kernel-defined objects and executive spin locks it uses. NT drivers also mustprovide storage for pointers to certain objects obtained from the I/O Manager or HAL, asexplained in Chapter 3.

Most device and intermediate drivers provide storage in their device or controller extensionsfor every NT object and system resource they use. However, a driver designer might decide toallocate additional system-space memory for the driver's needs. If so, the DriverEntry routinecan call one (or more) of the following routines: ■ ExAllocatePool for paged or nonpaged system-space memory. ■ MmAllocateNonCachedMemory or MmAllocateContiguousMemory for cache-

aligned nonpaged system-space memory (used for I/O buffers). ■ HalAllocateCommonBuffer for drivers of devices that use "continuous" DMA.

For more information about using memory, see Chapter 16. For more information about usingcommon buffers for DMA, see the section on adapter objects in Chapter 3.

Page 199: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

5-4 Kernel-mode Driver Design Guide

5.2 Reinitialize Routine Requirements

For convenience, an NT driver can initialize itself in stages by supplying a Reinitialize routineto be called some time after its DriverEntry routine returns control. Like the DriverEntryroutine, a Reinitialize routine is run in a system thread context at PASSIVE_LEVEL IRQL.

For example, the NT-supplied fault-tolerant disk driver, ftdisk, is loaded early in the bootprocess in case its support is needed to boot the system from a mirrored partition. When theftdisk driver's DriverEntry routine is called, it can layer the ftdisk driver only over driver(s) forthe device from which the system is loaded. Consequently, the ftdisk driver has a Reinitializeroutine to be called later in the boot process after other lowest-level mass storage devicedrivers (and any drivers layered above them but below the ftdisk driver) have been initialized.This driver's Reinitialize routine layers the ftdisk driver over the remaining drivers of devicesthat have mirrored or striped partitions by attaching ftdisk-created device objects to the lowerdrivers' device objects (see the preceding section).

If a driver has a Reinitialize routine, its DriverEntry routine causes the Reinitialize routine tobe run later by calling IoRegisterDriverReinitialization one or more times. The DriverEntryroutine is responsible for setting up any context data to be passed to the Reinitialize routinebefore it calls IoRegisterDriverReinitialization. If the Reinitialize routine uses theconfiguration registry, the context data should include the RegistryPath pointer that waspassed to the DriverEntry routine, because this pointer is not an input argument to theReinitialize routine (as shown in Chapter 4). For more information about using the NTconfiguration registry, see Chapter 16.

Note that the DriverEntry routine must call IoRegisterDriverReinitialization as many timesas the driver's Reinitialize routine should be run. The Count, which is passed in to theReinitialize routine (see the declaration in Chapter 4), indicates how many times this routinehas been called, including the current call.

5.3 Implementing a DriverEntry Routine

//t.b.w.//

5.4 Implementing a Reinitialize Routine

//t.b.w.//

Page 200: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 6Dispatch Routines

This chapter summarizes the required functionality of NT drivers' standard Dispatch routines.

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing the most common kinds of Dispatch routines.//

6.1 Dispatch Routine Requirements

The driver writer determines how many Dispatch routines the driver has. An NT driver canhave as many Dispatch routines as the IRP_MJ_xxx function codes the driver handles. Ingeneral, most NT drivers must handle some or all of the following requests: ■ IRP_MJ_CREATE indicates that a user-mode protected subsystem (possibly on behalf of

an application or subsystem-specific, user-mode driver) has requested a handle for thefile object that represents the target device object, or that a higher-level NT driver isconnecting or attaching its device object to the target device object.

■ IRP_MJ_CLEANUP indicates that the handle for the file object, representing the targetdevice object, is being released, so any IRPs currently queued to the target device shouldbe cancelled. (Only certain types of drivers cancel IRPs. See Chapter 12 for moreinformation.)

■ IRP_MJ_CLOSE indicates that the handle of the file object that represents the targetdevice object or a pointer to the target device object has been released, and that there areno outstanding references to the file object handle or device object pointer.

■ IRP_MJ_READ indicates an I/O request to transfer data from the underlying physicaldevice to the system.

■ IRP_MJ_WRITE indicates an I/O request to transfer data from the system to theunderlying physical device.

■ IRP_MJ_DEVICE_CONTROL indicates a request sent to the device driver, with apublicly defined, device-type-specific I/O control code requesting a device-type-specificoperation.

■ IRP_MJ_INTERNAL_DEVICE_CONTROL indicates a request sent to the device driverfrom a closely coupled higher-level driver, usually with a privately defined, driver- anddevice-type-specific I/O control code requesting a device-type-specific operation. (Onlycertain types of NT drivers are required to handle publicly defined internal device controlrequests, including certain SCSI drivers and video miniport drivers.)

Page 201: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

6-2 Kernel-mode Driver Design Guide

The required functionality of a particular Dispatch routine varies, depending on theIRP_MJ_xxx it handles, on the individual driver's level in a chain of layered drivers, and onthe type of underlying physical device. The Dispatch routine entry points a given driver mustsupply also varies according to the type and functionality of the underlying physical device.For device-type-specific information about IRP function codes that NT drivers must handle,see Appendix B. See also the Kernel-mode Driver Reference for more information about thesystem-defined interface for SCSI and video miniport drivers.

Processing any IRP begins in the Dispatch routine that the DriverEntry routine set up for thegiven IRP_MJ_xxx. A Dispatch routine is run in an arbitrary thread context atPASSIVE_LEVEL IRQL.

The Dispatch routine must first determine what to do if any of the following conditions hold: ■ The Dispatch routine handles more than one major function code. ■ It must handle a set of minor function codes for certain major function codes

(IRP_MN_xxx, as the NT-supplied SCSI port driver and NT file system drivers do). ■ It handles IRP_MJ_DEVICE_CONTROL or

IRP_MJ_INTERNAL_DEVICE_CONTROL requests, which have a set of device-type-specific I/O control codes (see Appendix B).

To determine which operation is requested and what arguments (if any) it must use, theDispatch routine calls IoGetCurrentIrpStackLocation to get a pointer to its own I/O stacklocation in the input IRP. Higher-level drivers' Dispatch routines also callIoGetNextIrpStackLocation to get a pointer to the next-lower driver's I/O stack location inthe IRP, which they must set up for the next-lower driver, as explained in Chapter 4.

Depending on the nature of the request, the Dispatch routine then does one of the following: ■ If the requested operation can be completed immediately, the Dispatch routine sets the

Status and Information fields in the IRP's I/O status block. Then, the Dispatch routinecalls IoCompleteRequest with the IRP, and returns the appropriate value forNTSTATUS.

■ If the requested operation requires further processing, the Dispatch routine usually checksthe validity of any arguments passed in the driver's I/O stack location of the IRP.Generally, a higher-level driver (if any) has already checked the arguments for arequested operation, but lower-level drivers can perform their own "sanity checks" onarguments as well. If a driver discovers an invalid argument, its Dispatch routine sets anappropriate error value in the Status field of the I/O status block (setting theInformation field to zero) and completes the IRP immediately as described in thepreceding paragraph, returning an appropriate error value for NTSTATUS.

Page 202: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Dispatch Routines 6-3

■ If all arguments are valid for the given request, a lowest-level driver's Dispatch routinecalls IoMarkIrpPending and queues the IRP for further processing by callingIoStartPacket, unless the driver manages its own internal IRP queueing. (See Chapter 7for more information about driver-managed queues.) If the driver does not have a StartIoroutine but handles cancelable IRPs, it must call IoAcquireCancelSpinLock,IoSetCancelRoutine with the entry point for a Cancel routine and the IRP, andIoReleaseCancelSpinLock before queueing the IRP for further processing. (See Chapter12 for more information about Cancel routines.) Then, the Dispatch routine returnsSTATUS_PENDING.

If the IRP contains the function code IRP_MJ_CLEANUP, the DispatchCleanup routineis responsible for cancelling all IRPs currently in the device queue for the target deviceobject. (A cleanup request indicates that an application is being terminated or has closeda file handle for the file object that represents the driver's device object. When theDispatchCleanup routine returns, the driver's DispatchClose routine is called next.)

A higher-level driver's Dispatch routine must set up the I/O stack location for the next-lower driver, whether in the current IRP or by creating one or more additional IRPs,possibly for an additional set of lower drivers, as already described in Chapter 4. Whenthe Dispatch routine has set up the next-lower driver's (or drivers') I/O stack location(s),it sets its own IoCompletion routine in the IRP(s) by calling IoSetCompletionRoutine ifthe driver created an IRP, which the IoCompletion routine must free, or if the driverneeds to check on how lower drivers complete the IRP. Then, the Dispatch routine callsIoCallDriver to pass the IRP(s) on to the next-lower driver(s) and, ultimately, to theunderlying physical device(s). The Dispatch routine returns NTSTATUS.

Note that an NT intermediate driver cannot create IRPs for lower-level drivers by callingIoMakeAssociatedIrp. If an intermediate driver needs to create IRPs for lower drivers, itshould call IoAllocateIrp, IoBuildDeviceIoRequest, IoBuildSynchronousFsdRequest, orIoBuildAsynchronousFsdRequest. (IoBuildSynchronousFsdRequest can be called only bya driver-created thread, or in the system thread context during initialization, to build IRPs forread, write, or flush requests.)

For any read/write (or other) request that requires DMA transfer operations, an NT SCSI classdriver's Dispatch routine is responsible for splitting large transfer requests, if necessary, intosmaller requests that the NT SCSI port driver (and HBA-specific miniport driver) can handlein a single DMA operation. For more information about using DMA, see the section onadapter objects in Chapter 3.

Page 203: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

6-4 Kernel-mode Driver Design Guide

6.2 Implementing DispatchCreate and DispatchClose Routines

//t.b.w.//

6.3 Implementing a DispatchCleanup Routine

//t.b.w.//

6.4 Implementing a DispatchRead Routine

//t.b.w./5

6.5 Implementing a DispatchWrite Routine

//t.b.w.//

6.6 Implementing a DispatchDeviceControl Routine

//t.b.w.//

6.7 Implementing a DispatchInternalDeviceControl Routine

//t.b.w.//

Page 204: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 7StartIo or Queue Management Routines

This chapter summarizes the required functionality of NT device drivers' standard StartIoroutines. For driver designers who decide to set up internal queues for IRPs in their drivers, italso summarizes the support NT provides for doing this.

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing StartIo routines and managing driver-createdinternal queues for IRPs.//

7.1 Queueing IRPs

Because the NT I/O Manager supports asynchronous I/O in a multitasking, multiprocessorsystem, I/O requests to a given device can come in faster than the corresponding device drivercan process them to completion. Consequently, IRPs must be queued when a particular devicedriver is already busy processing another IRP. The NT I/O Manager provides support for thenecessary queueing (as explained in Chapter 4) if the driver has a StartIo routine. However, anNT driver writer can design a device or intermediate driver that manages its own queueing ofIRPs with or without implementing a StartIo routine.

For example, the NT-supplied SCSI port driver has a StartIo routine and also sets up andmanages a device queue for each target device (corresponding to a SCSI logical unit) on anHBA-driven SCSI bus in the machine. This driver uses its supplemental device queues to sortIRPs sent down from the SCSI class drivers into device-type-specific queues in order tobalance I/O for SCSI devices with different performance characteristics and to overlap I/O todifferent devices on the bus. When the port driver calls KeInsertDeviceQueue with an IRPand a target device object, it checks the returned Boolean value to determine whether thedevice is already busy (that is, IRPs are currently queued). If the target device is not busy, thedriver's Dispatch routine calls IoStartPacket to pass the IRP on to its StartIo routine, whichsends the request over the bus to the target logical unit.

NT drivers with device-dedicated threads and those that use executive worker threads,including most NT file system drivers, usually set up an interlocked queue for IRPs in thedevice extensions of their device objects. Such a queue is shared by the driver's thread(s) andby other driver routines that process IRPs. The driver's Dispatch routine inserts IRPs into thequeue and a driver-created thread or the driver's worker-thread callback routine removes themby calling the ExInterlocked..List routines.

Page 205: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

7-2 Kernel-mode Driver Design Guide

For example, the system-supplied floppy driver uses such an interlocked queue. Its device-dedicated thread handles the same processing of IRPs that is done by other device drivers'StartIo routines and some of the same processing of IRPs that is done by other device drivers'DpcForIsr routines.

7.2 StartIo Routine Requirements

As its name suggests, a StartIo routine in a lowest-level driver is responsible for starting anI/O operation on the physical device. A StartIo routine is run in an arbitrary thread context atIRQL DISPATCH_LEVEL. Running at IRQL DISPATCH_LEVEL restricts the set ofsupport routines the StartIo routine can call. For more information about managing IRQLs, seeChapter 16.

When a lowest-level driver's StartIo routine is called, it can assume that the target devicerepresented by the input device object is not busy: either a Dispatch routine has just calledIoStartPacket (see Chapter 4) and there were no IRPs in the associated device queue, or theDpcForIsr routine is completing another request and has just called IoStartNextPacket.

The StartIo routine is responsible for calling IoGetCurrentIrpStackLocation with the inputIRP and, then, doing whatever request-specific processing is necessary to start the I/Ooperation on the device, which can include the following: ■ Setting up or updating any status information about the current request that the driver

maintains in the device extension of the target device object (or elsewhere in nonpagedpool allocated by the driver). For example, if the driver maintains an InterruptExpectedflag about the current transfer operation, the StartIo routine might set this flag. If thedriver maintains a time-out counter for the current operation, the StartIo routine might setup this value, or the StartIo routine might queue the driver's CustomTimerDpc, describedin Chapter 14.

■ If necessary, translating the IRP's arguments into device-specific values. For example, adisk driver might need to calculate the starting sector for a transfer operation and whetherthe requested length of the transfer will cross a particular sector boundary or exceed thetransfer capacity of its device.

■ If the device uses DMA, checking whether the requested Length (number of bytes to betransferred, found in the driver's I/O stack location of the IRP) should be split intosmaller transfer requests, as explained in the section on adapter objects in Chapter 3. TheStartIo routine is also responsible for calling KeFlushIoBuffers and, if the driver usespacket-based DMA, for calling IoAllocateAdapterChannel with the driver'sAdapterControl routine, described in Chapter 11. (For more information aboutmaintaining cache coherency during DMA, see also Chapter 16.)

■ If the device uses PIO, mapping the user-buffer pointer passed in the IRP to a system-space pointer with MmGetSystemAddressForMdl, as explained in Chapter 3. (Formore information about maintaining cache coherency during PIO, see also Chapter 16.)

Page 206: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

StartIo Routine or Queue-management Routines 7-3

■ If the driver uses a controller object, calling IoAllocateController with theControllerControl routine, described in Chapter 11.

■ If the driver handles cancelations of IRPs, calling IoAcquireCancelSpinLock andchecking whether the input IRP has already been cancelled. If the IRP could be cancelledduring further processing, the StartIo routine calls IoSetCancelRoutine with the IRP andits Cancel routine, described in Chapter 12. The StartIo routine also callsIoReleaseCancelSpinLock.

Usually, the StartIo routine of a lowest-level NT driver must synchronize access to anymemory or device registers it shares with the driver's ISR by callingKeSynchronizeExecution with a SynchCritSection routine, described in Chapter 10. In otherwords, the StartIo routine causes the SynchCritSection routine to actually program thephysical device for I/O at DIRQL. For more information about how driver routines sharememory with the ISR or share memory among routines other than the ISR in a multiprocessor-safe way, see the section on using spin locks in Chapter 16.

A higher-level NT driver can have a StartIo routine at the driver writer's discretion. In ahigher-level driver, a StartIo routine has the following effects: ■ Incoming IRPs can be queued by calling IoStartPacket from the driver's Dispatch

routines and IoStartNextPacket from its IoCompletion routines, thereby having IRPs beprocessed one at a time through the StartIo routine.

■ The driver's I/O throughput could be slower during periods of heavy I/O demand,because the StartIo routine can become a bottleneck.

■ Such a StartIo routine reduces overall system throughput because any StartIo routine isrun at raised IRQL. (For more information about the IRQLs at which driver routines arerun, see Chapter 16.)

Most higher-level NT drivers do not have a StartIo routine because it slows IRP processingboth for the driver itself and for all drivers above and below it. Instead, most higher-leveldrivers simply send IRPs to the underlying devices from their Dispatch routines (see Chapter6) and do any necessary clean-up processing in their IoCompletion routines (see Chapter 13).Other higher-level NT drivers set up internal queues for IRPs that request particular kinds ofoperations.

Page 207: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

7-4 Kernel-mode Driver Design Guide

7.3 Requirements for Using Interlocked Queues or Device Queues

A lowest-level NT device driver either must have a StartIo routine or must set up and manageits own internal queue(s) of IRPs. A higher-level driver can have its own internal queues ofIRPs (or even a StartIo routine) at the discretion of the driver's designer. Depending on thefunctionality required, the driver writer might implement both a StartIo routine and set up oneor more driver-managed supplemental queues for IRPs.

NT provides support for the following kinds of internal queues that NT drivers can use: ■ An interlocked queue into which the driver inserts IRPs by calling

ExInterlockedInsertTailList or ExInterlockedInsertHeadList, and from which adevice-dedicated thread or another driver routine removes IRPs by callingExInterlockedRemoveHeadList.

■ A device queue associated with a device object into which the driver can insert IRPs bycalling KeInsertDeviceQueue or KeInsertByKeyDeviceQueue, and from which it canremove IRPs by calling KeRemoveDeviceQueue, KeRemoveByKeyDeviceQueue, orKeRemoveEntryDeviceQueue. If the driver has a StartIo routine, it can remove IRPsfrom the device queue by calling IoStartNextPacket or IoStartNextPacketByKey, asdescribed in Chapter 4.

An interlocked queue is simpler to manage, particularly for drivers with device-dedicatedthreads. On the other hand, device queues give the driver more control over whether a givenIRP is placed in the queue (or passed immediately to another routine for further processing)and over the ordering of entries in the device queue. The KeInsert..DeviceQueue routinesreturn a Boolean value, indicating whether the target device (object) is busy: there are IRPswaiting to be processed in the device queue. In addition, the driver can impose a driver-determined order on the processing of IRPs in a device queue by callingKeInsertByKeyDeviceQueue and can remove a particular entry by callingKeRemoveEntryDeviceQueue.

Note that any driver that uses either kind of queue must provide storage for the necessary NTobjects and resources. NT drivers usually provide this storage in the device extensions of theirdevice objects, but can call ExAllocatePool with PoolType NonPagedPool to allocate thestorage or can provide storage in a controller extension if they use controller objects. For moreinformation about allocating pool memory, see Chapter 16. For more information aboutcontroller objects, see Chapter 3.

Page 208: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

StartIo Routine or Queue-management Routines 7-5

If it uses either kind of queue, an NT driver must provide storage for the following: ■ For an interlocked queue, storage both for an executive spin lock, required as an

argument to the ExInterlocked..List routines, and for the queue header. For moreinformation about using spin locks, see Chapter 16.

■ For a device queue, storage for a Kernel-defined device queue object. While each devicequeue object has an associated executive spin lock and a queue header, storage for theseresources is allocated automatically when the driver provides storage for the devicequeue object.

The DriverEntry routine must set up either kind of queue, as follows: ■ For drivers that use an interlocked queue, the DriverEntry routine must initialize the

queue header by calling InitializeListHead (an NT-supplied kernel-mode runtime libraryroutine). It must also initialize the executive spin lock by calling KeInitializeSpinLock.For more information about setting up an interlocked queue, see Chapter 3.

■ For drivers that use a device queue, the DriverEntry routine must callKeInitializeDeviceQueue to initialize the device queue object. For more informationabout setting up a device queue object, see Chapter 3.

IRPs can be inserted into or removed from either kind of queue by calling the appropriateExInterlocked..List or Ke..DeviceQueue support routine with the corresponding IRP-specific argument:

&Irp->Tail.Overlay.ListEntry

or&Irp->Tail.Overlay.DeviceQueueEntry

NT drivers can also set up internal queues or buffers for driver-defined data structures. Forexample, a keyboard driver might set up a ring buffer for data coming in from its physicaldevice. If your driver needs an internal queue for an indefinite (but limited at any givenmoment) number of fixed-size entries, consider setting up a zone buffer. For a summary of theEx...Zone routines, see Appendix A. For details, see the Kernel-mode Driver Reference.

Page 209: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

7-6 Kernel-mode Driver Design Guide

7.4 Implementing a StartIo Routine

//t.b.w.//

7.5 Managing Interlocked Queues

//t.b.w.//

7.6 Managing Device Queues

//t.b.w.//

Page 210: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 8Interrupt Service Routine

This chapter summarizes the required functionality of an NT device driver's standard interruptservice routine (ISR).

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing an ISR.//

8.1 ISR Requirements

Every NT driver of a physical device that generates interrupts must have an interrupt serviceroutine. Because an NT driver's ISR runs at a relatively high hardware priority (DIRQL), itshould stop the device from interrupting and return control as quickly as possible. Running theISR at DIRQL restricts the set of support routines the ISR can call. For more informationabout managing IRQLs, see Chapter 16.

Note that an NT driver's ISR is always interruptible. When a device driver's ISR runs, everyinterrupt with an equivalent or lower IRQL value is masked off on the current processor.However, another device with a higher DIRQL can interrupt or a high-IRQL system interrupt TMcan occur at any time in a Windows NT machine. Windows NT is quite different from someoperating systems with respect to driver ISRs. NT device drivers actually get betterperformance if their ISRs return control as quickly as possible, rather than attempting to retaincontrol of a CPU and doing as much I/O processing as possible in their ISRs, particularly insymmetric multiprocessor machines.

Instead, the ISR should save any necessary status information about or context for theoperation that caused the interrupt. Then, it should queue the driver's DpcForIsr routine or aCustomDpc routine to complete the operation at a lower IRQL (usually, atDISPATCH_LEVEL).

Every NT driver that has an ISR must also have a DpcForIsr or CustomDpc routine. At thedriver writer's discretion, the driver can also have additional CustomDpc routines, used tocomplete particular kinds of interrupt-driven I/O operations. For more information aboutDpcForIsr and CustomDpc routines, see Chapter 9.

Note also that in a uniprocessor machine, the ISR must return before the DpcForIsr or aCustomDpc routine can execute. However, it is possible for the ISR and DpcForIsr (or aCustomDpc) to run concurrently in a symmetric multiprocessor machine.

If any other driver routine shares data, device registers, or context information with the ISR,the driver must also have one or more SynchCritSection routines, described in Chapter 10.

Page 211: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

8-2 Kernel-mode Driver Design Guide

As shown in Chapter 4, the ISR returns a Boolean, indicating whether the driver's devicegenerated the interrupt. For drivers whose devices share an interrupt vector, the ISR shouldreturn FALSE as soon as it determines that its device was not the source of an interrupt.Otherwise, an ISR is responsible for the following: ■ Stopping the device from generating another interrupt. ■ Gathering whatever context information the DpcForIsr (or CustomDpc) routine will need

to complete I/O processing for the current operation. ■ Storing this context in an area accessible to the DpcForIsr or CustomDpc routine (usually

in the device extension). ■ If the driver has a DpcForIsr routine, calling IoRequestDpc with the IRP, target device

object, and a pointer to the saved context. IoRequestDpc queues the DpcForIsr routine tobe run as soon as IRQL falls below DISPATCH_LEVEL on a processor.

■ If the driver has a CustomDpc routine, calling KeInsertQueueDpc with a pointer to theDPC object (associated with the CustomDpc routine) and pointer(s) to any saved contextthe CustomDpc routine will need to complete the operation. The CustomDpc routine isrun as soon as IRQL falls below DISPATCH_LEVEL on a processor.

■ Returning TRUE to indicate that its device generated the interrupt.

An NT device driver must provide storage for at least one interrupt object pointer if it has anISR. If an NT driver has a single ISR that handles interrupts for more than one device ondifferent vectors, it must provide storage for an interrupt spin lock to be associated with everyset of interrupt objects for all its devices. Such a driver must also provide storage for as manyinterrupt object pointers as the IRQs it handles.

An NT device driver's DriverEntry routine must register its ISR(s) by callingIoConnectInterrupt when the driver is loaded. For more information about registering anISR, see the section on interrupt objects in Chapter 3.

8.2 Implementing an ISR

//t.b.w.//

Page 212: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 9DpcForIsr Routine and CustomDpc Routines

This chapter summarizes the required functionality of an NT device driver's standardDpcForIsr routine and/or CustomDpc routines.

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing a DpcForIsr or CustomDpc.//

9.1 DpcForIsr and CustomDpc Routine Requirements

If an NT driver has an ISR, it must also have a DpcForIsr or CustomDpc routine to completeits processing of interrupt-driven I/O operations. A DpcForIsr or CustomDpc routine is run inan arbitrary thread context at DISPATCH_LEVEL IRQL. Running at IRQLDISPATCH_LEVEL restricts the set of support routines a DpcForIsr or CustomDpc routinecan call. For more information about managing IRQLs, see Chapter 16.

Depending on the driver's design, it can have a single DpcForIsr to complete all interrupt-driven I/O operations, have both a DpcForIsr and a set of operation-specific CustomDpcroutines to be queued from its ISR, or have a set of CustomDpc routines. For example, theNT-supplied serial driver has no DpcForIsr, but has two CustomDpc routines that completethe processing of read and write operations and another CustomDpc routine that is queuedwhen the ISR detects a device-error condition.

However, most NT drivers with ISRs have only a DpcForIsr routine for simplicity. On entry,the DpcForIsr is given a pointer to the current IRP, target device object, and contextinformation passed in the ISR's call to IoRequestDpc. Any context information passed to aCustomDpc routine is driver-determined.

A DpcForIsr routine is generally responsible for the following: ■ Completing the I/O processing requested by the input IRP for the input target device

object. ■ Seeing that the next IRP is processed as soon as possible, usually by calling

IoStartNextPacket (see Chapter 4) or IoStartNextPacketByKey so the driver's StartIoroutine will start the next requested operation on the target device.

■ Setting the I/O status block in the IRP and calling IoCompleteRequest.

Page 213: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

9-2 Kernel-mode Driver Design Guide

Depending on the driver, the DpcForIsr or CustomDpc routine can do any of the following inorder to complete an interrupt-driven I/O operation: ■ Retry an operation that has timed-out or failed and/or log an error. (For more information

about handling I/O errors, see the section on error logging in Chapter 16.) ■ If the driver uses buffered I/O, as described in Chapter 3, transfer data read in from the

device to the system buffer (whose address is passed in the IRP). ■ If the driver uses packet-based DMA, call IoFlushAdapterBuffers after each transfer

operation, and call IoFreeAdapterChannel or IoFreeMapRegisters when a requestedtransfer is complete. If a requested transfer is only partly satisfied by a single DMAoperation, the DpcForIsr is usually responsible for setting up one or more DMAoperations until the current IRP's requested Length (number of bytes) has been fullytransferred. (For more information about using DMA, see the section on adapter objectsin Chapter 3. See also the section on maintaining cache coherency during DMA inChapter 16.)

■ If the driver has a ControllerControl routine, described in Chapter 11, callIoFreeController when a requested operation is complete.

Usually, the driver's ISR saves context information for the DpcForIsr and/or CustomDpcroutines. As mentioned in the preceding list, sometimes the DpcForIsr or CustomDpc routineis responsible for retrying operations, setting up another DMA transfer operation to satisfy agiven request, or otherwise accessing the same device registers or context area as the ISRdoes. If any driver routine shares context information or device registers with the ISR, thatroutine must call KeSynchronizeExecution with a SynchCritSection routine, described inChapter 10, in order to access the shared context safely.

If the driver has a CustomDpc routine, it must provide storage for a Kernel-defined DPCobject (of type KDPC).

The DriverEntry routine must register the DpcForIsr and each CustomDpc routine the driverhas as follows: ■ Call IoInitializeDpcRequest with the entry point for the DpcForIsr and a pointer to the

device object representing a device from which an interrupt can occur. ■ Call KeInitializeDpc with a pointer to the driver's storage for the DPC object, the entry

point for the CustomDpc routine, and a pointer to a context area (to be passed to theCustomDpc routine when it is run).

For more information about DPC objects, see Chapter 3.

Page 214: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

DpcForIsr Routine and CustomDpc Routines 9-3

9.2 Implementing a DpcForIsr Routine

//t.b.w.//

9.3 Implementing CustomDpc Routines

//t.b.w.//

Page 215: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 10SynchCritSection Routines

As its name suggests, a SynchCritSection routine is a critical section of code whose access toa shared context area must be synchronized with possible accesses by other driver routines, inparticular an interrupt service routine (ISR). For an NT device driver, usually its StartIo andDpcForIsr routines must access some of the same memory or device registers as the driver'sISR. Depending on the driver's device or its design, an AdapterControl or ControllerControlroutine also might access memory and/or device registers shared with its ISR.

If any non-ISR routine attempted to access such a shared area directly, the device couldinterrupt while that routine was programming device registers or updating contextinformation. In other words, the ISR might change the data or device registers out from underthe StartIo, DpcForIsr, AdapterControl, or ControllerControl routine whenever a deviceinterrupt occurred.

This chapter summarizes the required functionality of an NT device driver's standardSynchCritSection routine(s).

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing SynchCritSection routine(s).//

See Chapters 4 and 7 through 11 for more information about StartIo, interrupt service,DpcForIsr, AdapterControl, and ControllerControl routines.

Page 216: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

10-2 Kernel-mode Driver Design Guide

10.1 SynchCritSection Routine Requirements

Every non-ISR routine in an NT device driver must synchronize its access to areas shared withits ISR by calling KeSynchronizeExecution with a pointer to the interrupt object and aSynchCritSection routine. Making this call protects the shared data or device registers with thespin lock that is associated with the driver's interrupt object(s). Holding the spin lock masksdevice interrupts on the SynchCritSection routine's processor, and prevents the shared areafrom being simultaneously accessed from the ISR (or another SynchCritSection routine) insymmetric multiprocessor machines.

A SynchCritSection routine, like an ISR, is run at the DIRQL of the associated interruptobject(s). Running at DIRQL restricts the set of support routines a SynchCritSection routinecan call. For more information about managing IRQLs, see Chapter 16.

Like the ISR, a SynchCritSection routine must execute as quickly as possible, doing only whatis necessary to set up device registers, update context data, and so forth, before returningcontrol to the routine that called KeSynchronizeExecution.

For more information about the interaction of the ISR and driver routines that callKeSynchronizeExecution, see the section on using spin locks in Chapter 16. For adescription of KeSynchronizeExecution, see also Appendix A and/or the Kernel-modeDriver Reference.

10.2 Implementing a SynchCritSection Routine

//t.b.w.//

Page 217: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 11AdapterControl and ControllerControl Routines

Almost every NT driver of a DMA device must have an AdapterControl routine: ■ Every NT driver of a DMA device that is attached to a system DMA controller must have

an AdapterControl routine. ■ Any NT driver of a busmaster DMA device must have an AdapterControl routine unless

that driver's designer decides to use the system's common-buffer DMA supportexclusively.

At the driver writer's discretion, an NT device driver can have a ControllerControl routine inorder to synchronize its operations to similar devices (or device channels) through a singlephysical controller. For example, the system-supplied "AT" disk driver uses aControllerControl routine to manage operations on the disks that are attached to the diskcontroller.

This chapter summarizes the required functionality of an NT device driver's standardAdapterControl and/or ControllerControl routines.

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing AdapterControl and ControllerControlroutines.//

Page 218: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

11-2 Kernel-mode Driver Design Guide

11.1 Driver Control Routines

The I/O Manager defines a single type (DRIVER_CONTROL) for AdapterControl andControllerControl routines because each is called indirectly, usually from the StartIo routine,when a driver needs to synchronize access to a physical device that might already be busy: ■ When a driver calls IoAllocateAdapterChannel, its AdapterControl routine is run

immediately if the system DMA controller or busmaster adapter is available for a DMAoperation. Otherwise, the AdapterControl routine is queued until the DMA controller orbusmaster adapter is free.

■ When a driver calls IoAllocateController, its ControllerControl routine is runimmediately if the device represented by the controller object is available for an I/Ooperation. Otherwise, the ControllerControl routine is queued until the device is free.

An AdapterControl or ControllerControl routine must return a value of typeIO_ALLOCATION_ACTION, defined as follows:

typedef enum _IO_ALLOCATION_ACTION { KeepObject = 1, DeallocateObject, DeallocateObjectKeepRegisters} IO_ALLOCATION_ACTION, *PIO_ALLOCATION_ACTION;

The value DeallocateObjectKeepRegisters is meaningful only for a driver that uses packet-based busmaster DMA. For a ControllerControl routine, the input PVOID MapRegisterBase(see the DRIVER_CONTROL definition in Chapter 4) is a system-reserved value.

AdapterControl and ControllerControl routines are run in an arbitrary thread context atDISPATCH_LEVEL IRQL. Running at IRQL DISPATCH_LEVEL restricts the set ofsupport routines an AdapterControl or ControllerControl routine can call. For moreinformation about managing IRQLs, see Chapter 16.

Page 219: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

AdapterControl and ControllerControl Routines 11-3

11.2 AdapterControl Routine Requirements

At a minimum, an AdapterControl routine must do the following: ■ Save the input MapRegisterBase handle, which the driver must pass to

IoFlushAdapterBuffers when each DMA transfer operation is complete, and any othercontext information that the driver needs to carry out one or more DMA transferoperations for the current IRP.

■ Return the appropriate IO_ALLOCATION_ACTION value: ■ KeepObject if the device uses system DMA. ■ DeallocateObjectKeepRegisters if the device uses packet-based busmaster DMA.

Depending on the driver's design, its AdapterControl routine can also do the following beforeit returns control: ■ Call IoMapTransfer to set up the system DMA controller or to obtain a physical-to-

logical address mapping for a busmaster device. ■ Program the device for a transfer operation, usually by calling KeSynchronizeExecution

with a SynchCritSection routine, described in Chapter 10.

If it has an AdapterControl routine, the driver must provide storage for context information tobe used in its DMA operations, a pointer to an adapter object, and a ULONG (for the system-determined number of available map registers) in a device extension, controller extension, orin nonpaged pool allocated by the driver.

The DriverEntry routine must set up the adapter object for the device's DMA capabilities byfilling in a DEVICE_DESCRIPTION structure and calling HalGetAdapter. The returnedadapter object pointer and the entry point of the driver's AdapterControl routine must bepassed in calls to IoAllocateAdapterChannel. The platform-specific number of map registersreturned by HalGetAdapter determines whether the driver must split up a given transferrequest and carry out more than one DMA operation to satisfy that IRP. For more informationabout DMA transfers, see the section on adapter objects in Chapter 3.

Page 220: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

11-4 Kernel-mode Driver Design Guide

11.3 ControllerControl Routine Requirements

As its name implies, a ControllerControl routine is associated with an NT controller object.When the ControllerControl routine executes, it can assume that the physical devicerepresented by the controller object is free and that the controller extension is not beingaccessed by another driver routine unless the controller extension contains context that isshared with the driver's ISR.

Usually, a ControllerControl routine updates (or initializes) whatever context the drivermaintains in the device extension of the target device object (or in the controller extension)and programs the physical device for the requested I/O operation. If the device or controllerextension can be accessed from the ISR, the ControllerControl routine must callKeSynchronizeExecution with a SynchCritSection routine, described in Chapter 10, toprogram the device or to set up context shared with the ISR.

If the driver has a Cancel routine, a ControllerControl routine should also callIoAcquireCancelSpinLock to check whether the current IRP has already been cancelled. If ithas, the ControllerControl routine should set STATUS_CANCELLED in the I/O status block,call IoReleaseCancelSpinLock and complete the IRP with IoCompleteRequest. Otherwise,the driver should call IoSetCancelRoutine and IoReleaseCancelSpinLock and, then,program the device. For more information about Cancel routines, see Chapter 12.

For most interrupt-driven I/O operations (except overlapped operations on different devicesattached to the physical controller), a ControllerControl routine should return KeepObjectbecause the DpcForIsr or CustomDpc routine completes the operation (and the IRP). As soonas the I/O operation is done, the routine that will complete the IRP should callIoFreeController so that the next request can be processed as quickly as possible.

If the ControllerControl routine itself completes an IRP or if it can set up an operation (such asa disk seek) for a target device object (disk) that could be overlapped with an operation foranother device object, it should return DeallocateObject.

The driver should provide storage for a pointer to the controller object in a device extension orin other (nonpaged pool) storage allocated by the driver. The driver writer must define theinternal structure of the controller extension.

The DriverEntry routine must call IoCreateController to set up the controller object,specifying the driver-determined size for the controller extension. The returned controllerobject pointer and the entry point of the ControllerControl routine must be passed in thedriver's calls to IoAllocateController. See Chapter 3 for more information about controllerobjects.

Page 221: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

AdapterControl and ControllerControl Routines 11-5

11.4 Implementing an AdapterControl Routine

//t.b.w.//

11.5 Implementing a ControllerControl Routine

//t.b.w.//

Page 222: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 12Cancel Routines

Any NT driver that might hold an IRP for an indefinite interval, so that a user could cancel apreviously submitted I/O request (possibly by closing an application that has an outstandingI/O request), should have one or more Cancel routines. The number of Cancel routines adriver should have depends on the driver's design. In general, a driver should have a Cancelroutine for each place in the driver in which an IRP might be held for an indefinite interval(also called held in a cancelable state).

Usually, the highest-level driver in a chain of layered NT drivers must have one or moreCancel routines. Note that most NT drivers for interactive devices, such as keyboard, mouse,serial, parallel, and sound drivers, must have Cancel routines. NT drivers of mass storagedevices need not have Cancel routines unless there is no driver layered over them. In otherwords, it is the responsibility of an NT file system driver to handle the cancelation of file I/Orequests, so the input IRPs to lower-level mass storage drivers are not cancelable.

This chapter summarizes the required functionality of an NT driver's standard Cancelroutine(s).

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing a Cancel routine.//

Page 223: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

12-2 Kernel-mode Driver Design Guide

12.1 Cancel Routine Requirements

If an NT driver handles cancelable IRPs, it is responsible for setting the appropriate Cancelroutine in each IRP that will be put into a cancelable state as it is processed through thedriver's routines. For example: ■ The Dispatch routine should specify a Cancel routine's entry point when it calls

IoStartPacket if the driver has a StartIo routine. Otherwise, the Dispatch routine mustcall IoAcquireCancelSpinLock, IoSetCancelRoutine with the IRP and the entry pointfor a Cancel routine, and IoReleaseCancelSpinLock before queueing the IRP for furtherprocessing.

■ The StartIo routine (or any other routine that starts an operation on the device) must callIoAcquireCancelSpinLock and check whether the IRP has already been cancelled andanother driver routine is currently cancelling the requested I/O. If so, the StartIo routineshould call IoReleaseCancelSpinLock and return. Otherwise, it should callIoSetCancelRoutine before releasing the cancel spin lock and starting the requestedoperation on the target device object.

■ In a similar manner, any routine in the driver that passes an IRP on for further processingshould set up a Cancel routine if the requested operation could be cancelled before it iscompleted.

A Cancel routine is run in an arbitrary thread context at DISPATCH_LEVEL IRQL. Runningat IRQL DISPATCH_LEVEL restricts the set of support routines a Cancel routine can call.For more information about managing IRQLs, see Chapter 16.

A driver's Cancel routine is called with the cancel spin lock held, so a Cancel routine must notcall IoAcquireCancelSpinLock (unless it calls IoReleaseCancelSpinLock first). Instead, theCancel routine usually does the following: ■ Tests whether the input IRP matches the target device object's CurrentIrp field. If it

does, the input IRP has been cancelled so the Cancel routine callsIoReleaseCancelSpinLock, IoStartNextPacket to start processing the next request ifthe driver has a StartIo routine, IoCompleteRequest with the input IRP, and returns.

■ Otherwise, the Cancel routine calls KeRemoveDeviceQueue to pull the next IRP fromthe device queue, sets the I/O status block with STATUS_CANCELLED in the Statusfield and zero in the Information field, and calls IoReleaseCancelSpinLock. Then, theCancel routine calls IoCompleteRequest with the IRP.

Note that NT drivers with Cancel routines usually handle IRP_MJ_CLEANUP requests aswell. Their DispatchCleanup routines are responsible for cancelling all IRPs currently in thedevice queue. If such a driver manages its own queueing of IRPs, its DispatchCleanup routinemust cancel all queued IRPs for the input target device object.

Page 224: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Cancel Routines 12-3

12.2 Implementing a Cancel Routine

//t.b.w.//

Page 225: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 13IoCompletion Routines

At the discretion of the driver designer, a higher-level NT driver can have as manyIoCompletion routines as the driver needs to complete particular IRP_MJ_xxx that the lowerdriver(s) under it handle.

This chapter summarizes the required functionality of an NT device driver's standardIoCompletion routine(s).

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing IoCompletion routine(s).//

Page 226: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

13-2 Kernel-mode Driver Design Guide

13.1 IoCompletion Routine Requirements

The purpose of an IoCompletion routine is to monitor what lower-level drivers did with agiven IRP and to do any driver-determined cleanup processing necessary for a given requestthat the higher-level driver has passed down to the next-lower driver.

An IoCompletion routine is run in an arbitrary thread context, usually at DISPATCH_LEVELIRQL. Running at IRQL DISPATCH_LEVEL restricts the set of support routines anIoCompletion routine can call. For more information about managing IRQLs, see Chapter 16.

Depending on its design, a higher-level NT driver can call IoSetCompletionRoutine tospecify whether its IoCompletion routine will be called when lower drivers complete a givenIRP with any of the following NTSTATUS-type values: ■ STATUS_SUCCESS indicating that all lower-level drivers completed the requested

operation successfully and satisfied the IRP. ■ STATUS_CANCELLED indicating that a lower-level driver cancelled the IRP. ■ STATUS_xxx indicating that a lower-level driver encountered an error and failed the

IRP. ■ Any combination of the preceding conditions.

For more information about NTSTATUS-type values and I/O errors, see Chapter 16. See alsothe Kernel-mode Driver Reference for more detailed information about callingIoSetCompletionRoutine.

Any driver that allocates IRPs for lower drivers must free the IRPs it created, before it callsIoCompleteRequest with the original IRP, as described in Chapter 4. Note that returningSTATUS_MORE_PROCESSING_REQUIRED from the IoCompletion routine allows anintermediate driver to free a driver-created IRP without calling IoCompleteRequest with thatIRP. As already mentioned in Chapter 6, intermediate NT drivers cannot callIoMakeAssociatedIrp to create IRPs for lower drivers.

Any highest-level driver that calls IoMakeAssociatedIrp to create IRP(s) for lower driverscan return the master IRP to the NT I/O Manager and rely on the I/O Manager to complete itwhen all associated IRPs have been completed by lower drivers. However, if the driver callsIoSetCompletionRoutine for the associated IRPs it creates, the I/O Manager does notcomplete the master IRP if the driver returns STATUS_MORE_PROCESSING_REQUIREDfrom its IoCompletion routine.

13.2 Implementing IoCompletion Routines

//t.b.w.//

Page 227: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 14IoTimer and CustomTimerDpc Routines

An NT driver can have as many IoTimer routines as the device objects it creates and anynumber of CustomTimerDpc routines. NT drivers can use these routines to monitor whetheran I/O operation timed out, to restart device operations that have timed out, to log errors whenan operation has timed out or a device error has occurred, or for any other purpose the driverdesigner chooses.

This chapter summarizes the required functionality of an NT device driver's standard IoTimerand/or CustomTimerDpc routines.

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing an IoTimer and/or CustomTimerDpc routine.//

Page 228: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

14-2 Kernel-mode Driver Design Guide

14.1 IoTimer Routine Requirements

An IoTimer routine is called once per second after the driver calls IoStartTimer with thetarget device object, unless the driver disables the timer with a call to IoStopTimer. Thedriver can re-enable the timer with another call to IoStartTimer, so that its IoTimer routinewill again be called automatically at one-second intervals.

An IoTimer routine is run in an arbitrary thread context at DISPATCH_LEVEL IRQL.Running at IRQL DISPATCH_LEVEL restricts the set of support routines an IoTimer routinecan call. For more information about managing IRQLs, see Chapter 16.

Every IoTimer routine is associated with a driver-created device object. Consequently, an NTdriver can have a single IoTimer routine for all its device objects, a separate IoTimer routinefor each of its device objects, or some number of IoTimer routines associated withnonintersecting subsets of the driver's device objects, at the discretion of the driver designer.

After the driver has created its device objects, the DriverEntry routine must set up its IoTimerroutine(s) as follows: ■ Call IoInitializeTimer with the entry point for the IoTimer routine, a pointer (of type

PVOID) to any context area the IoTimer routine will use, and a pointer to the deviceobject (representing the device on which an operation could time out).

Calling IoInitializeTimer registers the driver's IoTimer routine for the given device object.For more information about creating device objects and the timer object associated with anIoTimer routine, see Chapter 3. See also the Kernel-mode Driver Reference for details aboutIoInitializeTimer.

The PVOID Context passed to IoInitializeTimer sets up a context area that is passed to theIoTimer routine when it is called. Such a context area must be in resident memory, allocatedby the driver. Usually, this context area is in the associated device object's device extension. Ifthe IoTimer routine shares context information with another driver routine, its context areamust be protected by a spin lock. For more information about using spin locks, see Chapter16.

Page 229: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

IoTimer and CustomTimerDpc Routines 14-3

14.2 CustomTimerDpc Routine Requirements

The granularity of a timer object interval is 10 milliseconds, so a CustomTimerDpc can becalled at driver-determined variable intervals and at finer-grained intervals than an IoTimerroutine. A CustomTimerDpc routine is called once after the (delta) interval that was specifiedwhen the driver called KeSetTimer expires. In other words, the driver must call KeSetTimereach time its CustomTimerDpc must be run, because a CustomTimerDpc is not calledautomatically like an IoTimer routine. The driver can call KeCancelTimer to disable analready set timer object and dequeue a CustomTimerDpc, provided its call occurs before theexpiration of the interval it passed to KeSetTimer.

Like an IoTimer routine, a CustomTimerDpc routine is run in an arbitrary thread context atDISPATCH_LEVEL IRQL. Running at IRQL DISPATCH_LEVEL restricts the set ofsupport routines an CustomTimerDpc routine can call. For more information about managingIRQLs, see Chapter 16.

A driver that has CustomTimerDpc routines must provide storage for both a timer object (oftype KTIMER) and a DPC object (of type KDPC) for each CustomTimerDpc it has.

The DriverEntry routine must set up each CustomTimerDpc routine as follows: ■ Call KeInitializeDpc to set up the DPC object with the CustomTimerDpc routine, its

pointer (of type PVOID) to the DeferredContext area, and additional pointers (also oftype PVOID) to the SystemArgument1 and SystemArgument2 parameters (if any) thedriver uses.

■ Call KeInitializeTimer to set up the timer object with the CustomTimerDpc routine.

The PVOID DeferredContext passed to KeInitializeDpc sets up a context area that is passedto the CustomTimerDpc routine when it is called, as do the SystemArgument1 andSystemArgument2 pointers. Such context area(s) must be in resident memory, allocated by thedriver. Usually, such a context area is in a driver-created device object's device extension. Formore information about setting up a CustomTimerDpc, see the section on timer objects withassociated DPCs in Chapter 3, and see also the Kernel-mode Driver Reference.

If the CustomTimerDpc routine shares context information with another driver routine, itscontext area must be protected by a spin lock. For more information about using spin locks,see Chapter 16.

As shown by its definition in Chapter 4, a CustomTimerDpc has no particular associationswith a driver-created device object. However, a driver can associate a CustomTimerDpcroutine with a driver-created device object by passing a pointer to the device object when itcalls KeInitializeDpc.

Page 230: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

14-4 Kernel-mode Driver Design Guide

14.3 Implementing IoTimer Routines

//t.b.w.//

14.4 Implementing CustomTimerDpc Routines

//t.b.w.//

Page 231: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 15Unload Routine

Any NT driver that can be replaced, or unloaded and reloaded, while the system is runningmust have an Unload routine.

This chapter summarizes the required functionality of an NT driver's standard Unload routine.

//This chapter is incomplete. The final DDK manual will also contain moreinformation about implementing an Unload routine.//

Page 232: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

15-2 Kernel-mode Driver Design Guide

15.1 Unload Routine Requirements

As its name suggests, an Unload routine is responsible for freeing all the objects and resourcesthe driver uses before the driver itself is unloaded. In other words, the Unload routine mustcall the following support routines: ■ IoDisconnectInterrupt if the driver has registered an ISR with IoConnectInterrupt. ■ ExFreePool if the DriverEntry (or another routine) called ExAllocatePool. ■ MmFreeNonCachedMemory if the DriverEntry routine called

MmAllocateNonCachedMemory. ■ MmFreeContiguousMemory if the DriverEntry routine called

MmAllocateContiguousMemory. ■ HalFreeCommonBuffer if the DriverEntry routine called HalAllocateCommonBuffer. ■ IoReportResourceUsage one or more times if the DriverEntry routine called

IoReportResourceUsage to claim hardware resources in the configuration registry foritself and/or for any of its device objects. For more information about claiming resourcesin the NT configuration registry and about releasing claimed resources, see Chapter 16.

If the DriverEntry routine called PsCreateSystemThread, the Unload routine also must causethe driver-created thread to be run so that the thread can call PsTerminateSystemThreadbefore the driver is unloaded from the system.

Finally, the Unload routine must call IoDeleteDevice for each device object that theDriverEntry routine created by calling IoCreateDevice, and it must call IoDeleteController ifthe DriverEntry routine called IoCreateController.

Note that any Kernel-defined object for which the driver provides storage in a deviceextension is automatically freed when the Unload routine calls IoDeleteDevice with thecorresponding device object. In general, any object that the DriverEntry routine set up bycalling KeInitializeXxx can be freed by a call to IoDeleteDevice if the driver provided storagefor that object in its device extension. For example, if a driver has a CustomTimerDpc routineand has provided storage for the necessary DPC and timer objects in its device extension, thecall to IoDeleteDevice releases that driver's claim on these system resources.

In a similar manner, any Kernel-defined object for which the driver provides storage in acontroller extension is automatically freed when the Unload routine calls IoDeleteControllerwith the corresponding controller object.

For a summary of the support routines mentioned in this section, see Appendix A. For details,see the Kernel-mode Driver Reference.

15.2 Implementing an Unload Routine

//t.b.w.//

Page 233: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Chapter 16Common Driver Design Issues

This chapter contains some information of general interest to all NT driver writers, includingthe following: ■ A summary of the default hardware priorities (IRQLs) at which standard NT driver

routines run and some guidelines for calling support routines at appropriate IRQLs ■ General guidelines about using spin locks to synchronize access to data or resources

shared by driver routines ■ General guidelines about allocating system-space memory, using the kernel stack, and

using zone buffers ■ How NT drivers should handle I/O errors and how NTSTATUS values are defined TM ■ How NT drivers use the Windows NT configuration registry to initialize themselves in

a manner that keeps them portable and configurable across NT platforms

This chapter also discusses design issues that are device-type-specific or design-specific,including the following: ■ For device driver designers, whether to have the driver poll the device or set up a thread

(or use a worker-thread callback routine) that waits on a Kernel-defined dispatcher object(an event, semaphore, mutex, timer, or thread)

■ For DMA or PIO device driver designers, how to maintain cache coherency duringtransfer operations

■ For removable-media device driver designers, how to handle user-induced errors, such assupplying the wrong media or removing the media on which a file is open

■ For disk, floppy, and CD-ROM device driver designers, how to set up a symbolic linkbetween the driver's name for a device object that represents a boot-from device and thecorresponding ARC name for the device

16.1 Managing Hardware Priorities

The IRQL at which a particular driver routine is run determines which kernel-mode supportroutines it can call. For example, some support routines require that the caller be running atDISPATCH_LEVEL IRQL. Others cannot be called safely if the caller is running at raisedIRQL: that is, at any IRQL higher than PASSIVE_LEVEL.

Figure 16.1 illustrates the default IRQLs at which the most commonly implemented standarddriver routines are run and the Kernel-defined ordering of low-to-high IRQL values.

Page 234: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-2 Kernel-mode Driver Design Guide

Figure 16.1 Default IRQLs for Driver Routines

As Figure 16.1 shows, lowest-level NT drivers process IRPs while running at one of threeIRQLs: 1 PASSIVE_LEVEL, with no interrupts masked off on the processor, in the driver's

Dispatch routine(s); DriverEntry and Reinitialize routines are run at PASSIVE_LEVEL,as are any driver-created system threads or driver-supplied worker-thread callbackroutines.

Page 235: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-3

2 DISPATCH_LEVEL, with DISPATCH_LEVEL (and APC_LEVEL) interrupts maskedoff on the processor, in the StartIo routine; AdapterControl, ControllerControl, IoTimer,Cancel, and CustomTimerDpc routines are run at DISPATCH_LEVEL, as are DpcForIsrand CustomDpc routines.

3 Device IRQL (DIRQL), with all interrupts at less than or equal to the IRQL of thedriver's interrupt object(s) masked off on the processor, in the ISR and SynchCritSectionroutines.

Higher-level NT drivers process IRPs while running at either of two IRQLs: 1 PASSIVE_LEVEL, with no interrupts masked off on the processor, in the driver's

Dispatch routine(s); DriverEntry and Reinitialize routines are run at PASSIVE_LEVEL,as are any driver-created system threads or driver-supplied worker-thread callbackroutines.

2 DISPATCH_LEVEL, with DISPATCH_LEVEL (and APC_LEVEL) interrupts maskedoff on the processor, in the driver's IoCompletion routine(s); IoTimer, Cancel, andCustomTimerDpc routines are run at DISPATCH_LEVEL.

Most standard driver routines are run at an IRQL that allows them simply to call theappropriate support routines. For example, a device driver must call IoAllocateAdapter orIoAllocateController while running at DISPATCH_LEVEL IRQL. Since most NT devicedrivers call these routines from a StartIo routine, usually they are running atDISPATCH_LEVEL already.

Note that a device driver that has no StartIo routine (because it sets up and manages its ownqueue(s) of IRPs), is not necessarily running at DISPATCH_LEVEL IRQL when it needs tocall IoAllocateAdapter (or IoAllocateController). Such a driver must nest its call toIoAllocateAdapter between calls to KeRaiseIrql and KeLowerIrql in order to run at thecorrect IRQL when it calls IoAllocateAdapter and to restore the original IRQL value whenthe calling routine regains control.

Consider the following in order to call support routines at appropriate IRQLs and to managehardware priorities successfully in NT drivers: ■ Calling KeRaiseIrql with an input NewIrql value that is less than the current IRQL

causes a fatal error. Calling KeLowerIrql except to restore the original IRQL (i.e., aftera call to KeRaiseIrql) also causes a fatal error.

■ While running at raised IRQL, calling KeWaitForSingleObject orKeWaitForMultipleObjects with Kernel-defined dispatcher object(s) in order to waitfor a nonzero interval causes a fatal error. Only driver routines that run in a system threadcontext at PASSIVE_LEVEL, such as driver-created threads or the DriverEntry andReinitialize routines, can safely wait on events, semaphores, mutexes, timers, or threadsfor a nonzero interval.

Page 236: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-4 Kernel-mode Driver Design Guide

■ Any routine that is running at greater than APC_LEVEL IRQL can neither allocatememory from paged pool nor access memory in paged pool safely. If a routine running atIRQL > APC_LEVEL causes a page fault, it is a fatal error.

■ Calling a support routine that uses a spin lock raises IRQL on the current processor eitherto DISPATCH_LEVEL or to DIRQL if the caller is not already running at raised IRQL.For more information about using spin locks, see Section 16.2, next.

■ Routines that run at raised IRQL should execute as quickly as possible. The higher theIRQL at which a routine runs, the more important it is for overall performance reasons totune that routine to execute as quickly as possible. For example, any driver that callsKeRaiseIrql should make the reciprocal call to KeLowerIrql as soon as it can.

Appendix A contains a summary of support routines that device and intermediate drivers cancall, together with support-routine-specific information about IRQL requirements for callingthese routines.

16.2 Using Spin Locks

Spin locks are Kernel-defined, kernel-mode-only synchronization mechanisms, exported as anopaque type: KSPIN_LOCK. A spin lock can be used to protect shared data or resources fromsimultaneous access by routines that can execute concurrently and at raised IRQL in amultiprocessor machine.

Many NT kernel-mode components use spin locks, including NT drivers. Any kind of NTdriver might use one or more executive spin locks. For example, most NT file systems use aninterlocked work queue in the FSD's device extension to store IRPs that are processed both bythe file system's worker-thread callback routines and by the FSD. An interlocked work queueis protected by an executive spin lock, used to resolve contention among the FSD, trying toinsert IRPs into the queue, and any threads simultaneously trying to remove IRPs. The system-supplied floppy driver uses two executive spin locks. One executive spin lock protects aninterlocked work queue shared with this driver's device-dedicated thread; the other protects atimer object shared by three driver routines.

Every NT driver that implements an ISR uses an interrupt spin lock to protect any data orhardware shared between its ISR and its other routines, most commonly internal, critical-section routines called from a driver's StartIo and DpcForIsr routines. An interrupt spin lock isassociated with the set of interrupt objects created when such a driver callsIoConnectInterrupt, as explained in the section on interrupt objects in Chapter 3.

Page 237: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-5

All NT drivers that use a spin lock should follow these guidelines: ■ Provide the storage for any data or resource protected by a spin lock and for the

corresponding spin lock in resident system-space memory (nonpaged pool, as shown laterin Figure 16.3). A driver must provide the storage for any executive spin locks it uses.However, a driver need not provide the storage for an interrupt spin lock unless it mustcreate two sets of interrupt objects because its ISR handles interrupts for more than onedevice on different vectors (see Chapter 3).

■ Call KeInitializeSpinLock to initialize each spin lock for which the driver providesstorage before using it to synchronize access to the shared data or resource it protects.

■ Call every support routine that uses a spin lock at an appropriate IRQL, generally at <=DISPATCH_LEVEL for executive spin locks or at <= DIRQL for an interrupt spin lockassociated with the driver's interrupt object(s).

■ Implement routines to execute as quickly as possible while they hold a spin lock. Noroutine should hold a spin lock for longer than 25 microseconds.

■ Implement routines that never do any of the following while holding a spin lock: ■ Cause hardware exceptions or raise software exceptions ■ Attempt to access pageable data ■ Make a recursive call that would cause a deadlock or could cause a spin lock to be

held for longer than 25 microseconds ■ Attempt to acquire another spin lock if doing so might cause a deadlock ■ Call an external routine that violates any of the preceding rules

The following subsections explain these guidelines in more detail.

16.2.1 Providing Storage for Spin Locks and Protected Data

When an NT driver is loaded, its DriverEntry routine must allocate storage for any spin-lock-protected data or resources and for corresponding spin locks in one of the following places: ■ The device extension of a device object that the driver sets up by calling IoCreateDevice ■ The controller extension of a controller object that the driver sets up by calling

IoCreateController ■ Nonpaged, system-space memory that the driver obtains by calling ExAllocatePool

Attempting to access pageable data while holding a spin lock causes a fatal page fault if thatpage is not present. Referencing a spin lock that is invalid (because it was stored in pageablememory and is currently paged-out) also causes a fatal page fault.

Page 238: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-6 Kernel-mode Driver Design Guide

An NT driver must provide the storage for each executive spin lock it uses, as follows: ■ Any spin lock used to synchronize access to driver-defined data by (nonISR) driver

routines that call KeAcquireSpinLock and KeReleaseSpinLock ■ Any spin lock used to synchronize access to a driver-allocated resource by calling a

resource-specific set of ExInterlockedXxx routines ■ Any spin lock the driver passes to the ExInterlockedIncrement/DecrementLong

routines (Such a spin lock must not be reused in calls to other support routines.)

While a driver can make calls to the ExInterlocked..List and/or ExInterlocked..Zoneroutines from its ISR or SynchCritSection routines, it cannot call KeAcquireSpinLock andKeReleaseSpinLock while running at IRQL > DISPATCH_LEVEL. Consequently, anydriver that reuses a spin lock in calls to the Ke..SpinLock and ExInterlockedXxx routinesmust make every call while running at IRQL <= DISPATCH_LEVEL.

Note that a driver can pass the same spin lock to ExInterlockedInsertHeadList as it does toExInterlockedAllocateFromZone. For more information about how spin lock usage affectsperformance, see Section 16.2.4. For more information about using zone buffers, see Section16.4.3.

Never pass the same executive spin lock to an ExInterlockedIncrement/DecrementLongroutine and to any other routine that uses an executive spin lock, such asKeAcquireSpinLock or ExInterlockedInsertHeadList. Reusing an executive spin lock inthis manner can cause fatal errors on some NT platforms.

In addition to the storage for its executive spin locks, an NT device driver must provide thestorage for another spin lock to be associated with its interrupt object(s) if it implements asingle ISR but calls IoConnectInterrupt more than once for devices that use differentvectors.

16.2.2 Initializing Spin Locks

A driver must call KeInitializeSpinLock to initialize each executive spin lock it uses, beforecalling any support routine that requires access to a caller-supplied executive spin lock, suchas the following routines: ■ KeAcquireSpinLock and, subsequently, KeReleaseSpinLock ■ An ExInterlockedXxx routine

Page 239: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-7

For a list of the Executive Support component's interlocked routines that drivers might use,see Appendix A.

A lowest-level driver must call KeInitializeSpinLock to initialize an interrupt spin lock forwhich it provides storage before it calls IoConnectInterrupt and, subsequently,KeSynchronizeExecution to synchronize access to data and resources shared between theISR and other driver routines.

16.2.3 Calling Support Routines That Use Spin Locks

Routines that hold an executive spin lock usually execute at IRQL DISPATCH_LEVEL untilthey release the spin lock. Calling KeAcquireSpinLock sets the IRQL on the currentprocessor to DISPATCH_LEVEL until a corresponding KeReleaseSpinLock call restores theprevious IRQL. Consequently, drivers must be executing at IRQL <= DISPATCH_LEVELwhen they call these support routines.

Each routine that holds an interrupt spin lock executes at the DIRQL of an associated set ofinterrupt objects. Therefore, a driver must not call KeAcquireSpinLock orKeReleaseSpinLock from its ISR or from any other code sequence executing at an IRQLhigher than DISPATCH_LEVEL. Such a call is an error that can cause a system deadlock,requiring the user to reboot his or her machine. Note that if a driver's ISR or SynchCritSectionroutine calls an ExInterlocked..List routine, the driver cannot reuse the spin lock it passes tothe ExInterlocked..List routines in calls to the Ke..SpinLock routines.

If a driver handles interrupts from more than one kind of device, its routines can callKeSynchronizeExecution while executing at any IRQL up to the higher of the interruptDIRQL or synchronization DIRQL specified for the associated interrupt objects when theywere connected. For more information about interrupt objects, see Chapter 3. For moreinformation about device drivers' SynchCritSection routines passed toKeSynchronizeExecution, see Chapters 4 and 10, and Section 16.2.4, next. For moreinformation about managing hardware priorities in drivers, see Section 16.1, and seeAppendix A for support-routine-specific IRQL requirements.

16.2.4 Releasing Spin Locks Promptly

A driver writer who minimizes the time that the driver holds spin locks can significantlyimprove both the performance of the driver and of the system overall.

For example, consider Figure 16.2, which shows how an interrupt spin lock protects device-specific data that must be shared between an ISR and the StartIo and DpcForIsr routines in amultiprocessor machine.

Page 240: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-8 Kernel-mode Driver Design Guide

Figure 16.2 Using an Interrupt Spin Lock

Page 241: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-9

1 While the driver's ISR runs at DIRQL on one processor, its StartIo routine runs atDISPATCH_LEVEL on a second processor. The Kernel interrupt handler holds theInterruptSpinLock for the driver's ISR, which accesses protected, device-specific data inthe driver's device extension. The StartIo routine, which needs to access the same data,calls KeSynchronizeExecution, passing a pointer to the associated interrupt object(s),the shared data (SynchronizeContext), and the driver's SynchCritSection routine calledAccessDevice.

Until the ISR returns, thereby releasing the driver's InterruptSpinLock,KeSynchronizeExecution spins on the second processor, preventing AccessDevice fromtouching the SynchronizeContext data. However, KeSynchronizeExecution also raisesIRQL on the second processor to the DIRQL of the interrupt object(s), therebypreventing another device interrupt from occurring on that processor so AccessDevicecan be run at DIRQL as soon as the ISR returns. (Higher DIRQL interrupts for otherdevices, clock interrupts, and power-fail interrupts can still occur on either processor, asalready shown in Figure 16.1.)

2 When the ISR queues the driver's DpcForIsr and returns, AccessDevice runs on thesecond processor at the DIRQL of the associated interrupt object(s) and accesses theSynchronizeContext data. Meanwhile, the DpcForIsr is run on another processor atDISPATCH_LEVEL IRQL. The DpcForIsr also needs to access the SynchronizeContextdata, so it calls KeSynchronizeExecution with the same arguments as the StartIo routinedid in Step 1.

Note that when KeSynchronizeExecution acquires the spin lock and runs AccessDeviceon behalf of the StartIo routine, the driver-supplied synchronization routine is givenexclusive access to the SynchronizeContext data. Because AccessDevice runs at DIRQL,the driver's ISR cannot acquire the spin lock and access the same data until the spin lockis released, even if the driver's device interrupts on another processor whileAccessDevice is executing.

3 AccessDevice returns and the spin lock is released, so the StartIo routine resumesrunning at DISPATCH_LEVEL on the second processor. KeSynchronizeExecution nowruns AccessDevice on the third processor, so it can access the SynchronizeContext dataon behalf of the DpcForIsr. However, if a device interrupt had occurred before theDpcForIsr called KeSynchronizeExecution in Step 2, the ISR might run on anotherprocessor before KeSynchronizeExecution could acquire the spin lock and runAccessDevice on the third processor.

As Figure 16.2 shows, while a routine running on one processor holds a spin lock, every otherroutine trying to acquire that spin lock gets no work done. Each routine trying to acquire analready-held spin lock simply spins on its current processor until the holder releases the spinlock. When a spin lock is released, one and only one routine can acquire it, so every otherroutine currently trying to acquire the same spin lock will continue to spin. Consequently,driver writers who minimize the time their drivers hold spin locks get significantly betterperformance from their drivers.

Page 242: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-10 Kernel-mode Driver Design Guide

As explained in Section 16.2.3, the holder of any spin lock runs at raised IRQL: either atDISPATCH_LEVEL for an executive spin lock, or at a DIRQL for an interrupt spin lock.Callers of KeAcquireSpinLock run at DISPATCH_LEVEL until they callKeReleaseSpinLock. Callers of KeSynchronizeExecution automatically raise IRQL on thecurrent processor to the DIRQL of the interrupt object until the caller-suppliedsynchronization routine exits and KeSynchronizeExecution returns control. All code thatruns at a lower IRQL cannot get any work done on the set of processors occupied by a spin-lock holder and by other routines trying to acquire the same spin lock. Consequently, driverwriters who minimize the time their drivers hold spin locks also contribute significantly tobetter overall system performance.

As Figure 16.2 shows, the Kernel interrupt handler executes routines running at the sameIRQL in a multiprocessor machine on a first-come, first-served basis, and the Kernel also doesthe following: ■ When a driver routine calls KeSynchronizeExecution, the Kernel causes the driver's

SynchCritSection routine to run on the same processor from which the call toKeSynchronizeExecution occurred (see Steps 1 and 3).

■ When a driver's ISR queues its DpcForIsr, the Kernel can cause the DPC to run on thefirst available processor whose IRQL falls below DISPATCH_LEVEL, which is notnecessarily the same processor from which the IoRequestDpc call occurred (see Step 2).

Note also that a given driver's interrupt-driven I/O operations would tend to be serialized in auniprocessor machine, but that the same operations can be truly asynchronous in amultiprocessor machine. As Figure 16.2 shows, a driver's ISR could run on CPU4 in amultiprocessor machine before its DpcForIsr begins processing an IRP for which the ISR hasalready handled a device interrupt on CPU1.

In other words, driver writers should not assume that an interrupt spin lock can protectoperation-specific data, saved by the ISR when it runs on one processor, from beingoverwritten by the ISR when a device interrupt occurs on another processor.

Although a driver writer could try to serialize all interrupt-driven I/O operations in order topreserve data collected by the ISR, such a driver would not run much faster in amultiprocessor machine than it did in a uniprocessor machine. To get the best possible driverperformance while remaining portable across uniprocessor and multiprocessor platforms,drivers should use some other technique to save operation-specific data obtained by the ISRfor subsequent processing by the DpcForIsr.

For example, an ISR can save operation-specific data in the IRP it passes to the DpcForIsr. Arefinement of this technique is to implement a DpcForIsr that consults an ISR-augmentedcount, processes the count's number of IRPs using ISR-supplied data, and resets the count tozero before returning. Of course, such a count must be protected by the driver's interrupt spinlock because its ISR and a SynchCritSection would maintain its value dynamically.

Page 243: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-11

16.2.5 Preventing Fatal Errors or Deadlocks While Using Spin Locks

When a driver routine holds a spin lock, it cannot cause a hardware exception or raise asoftware exception without bringing down the system. In other words, a driver's ISR and anySynchCritSection routine that the driver supplies in a call to KeSynchronizeExecution mustnot cause a fault or trap, such as a page fault or an arithmetic exception, and cannot raise asoftware exception. A routine that calls KeAcquireSpinLock also cannot cause a hardwareexception or raise a software exception until it has released its executive spin lock.

NT drivers must not call routines that access pageable data while holding a spin lock. All NTexecutive components, including drivers, are loaded into nonpaged system space, so kernel-mode support routines that drivers might call are not pageable. However, drivers can callcertain support routines that access pageable data, if and only if their calls occur whileexecuting at an IRQL strictly less than DISPATCH_LEVEL. Note that this IRQL restrictionprecludes calling such a support routine while holding a spin lock.

Attempting to acquire a spin lock recursively is guaranteed to cause a deadlock: the holdinginstantiation of a recursive routine cannot release the spin lock while a second instantiationspins, trying to acquire the same spin lock. In other words, if you implement a recursive driverroutine, follow these guidelines concerning spin lock usage: ■ The recursive routine must not call itself while holding a spin lock, or must not attempt to

acquire the same spin lock on subsequent calls. ■ While it holds a spin lock, another driver routine must not call the recursive routine if

recursion might cause a deadlock or could cause the caller to hold the spin lock for longerthan 25 microseconds.

For more information about recursive driver routines, see also Section 16.4.2.

Attempting to acquire a second spin lock while holding another spin lock also can causedeadlocks or poor driver performance. NT drivers should follow these guidelines whileholding a spin lock: ■ A driver must not call a support routine that uses a spin lock unless a deadlock cannot

occur. ■ Even if a deadlock cannot occur, a driver should not call a support routine that uses a spin

lock unless alternate coding techniques cannot provide comparable driver performanceand functionality.

Page 244: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-12 Kernel-mode Driver Design Guide

In general, drivers should avoid using nested spin locks to protect overlapping subsets ordiscrete sets of shared data and resources. Consider what can happen if a driver uses twoexecutive spin locks to protect discrete resources, such as a pair of timer objects that might beset individually and collectively by various driver routines. Such a driver would deadlockintermittently in a multiprocessor machine, whenever either of two routines, each holding onespin lock, tried to acquire the other spin lock. Even if a deadlock-proof driver could bedesigned to use nested spin locks, it would be very difficult to implement successfully. Nestedspin locks are almost impossible to debug and to test adequately in a multiprocessor machine.Moreover, using nested spin locks can degrade both driver and system performance severely.

16.3 Polling the Device

An NT device driver should avoid polling its device unless it is absolutely necessary andshould never use a whole timeslice polling. If necessary, a driver can callKeStallExecutionProcessor before it reads the device register(s). Such a driver shouldminimize the interval it stalls and should, in general, specify a stall interval no longer than 50microseconds. The granularity of a KeStallExecutionProcessor interval is 1 microsecond.

Polling a device is an expensive operation that makes any operating system compute boundwithin the polling driver. If a driver does a lot of polling, it interferes with I/O operations onother devices, making the system slow and unresponsive to users. Recently developed devices,which are as technologically advanced as the microprocessors NT is designed to run on,seldom require a driver that polls its device to be sure the device is ready to start an I/Ooperation or that an operation has completed.

Nevertheless, some devices still in use were designed to work with old microprocessors,which had narrow data buses, slow clock rates, and single-user, single-tasking operatingsystems that did synchronous I/O. Such devices might require polling or some other means ofwaiting for the device to update its registers, even for NT, which is designed to doasynchronous I/O on new microprocessors with wide data buses and fast clock rates.

An inexperienced programmer might think that a driver could solve such a slow-deviceproblem by coding a simple loop that increments a counter, thereby "wasting" a minimuminterval while its device updates registers. However, such a driver is unlikely to be portable.The loop counter maximum would require customization for each NT platform. Furthermore,when such a driver is recompiled with a good optimizing compiler, the compiler could removethe driver's counter variable and the loop(s) where it is incremented.

Page 245: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-13

The driver of a slow device or a device that is seldom used (like the floppy), can solve many"waiting" problems by creating a device-dedicated system thread or using system workerthreads (and supplying worker-thread callback routines, as most NT file systems do). A threadcan call KeDelayExecutionThread to wait for an interval that could be a full timeslice orlonger. The granularity of a KeDelayExecutionThread interval is around 10 milliseconds.(Because KeDelayExecutionThread is a timer-driven routine, the granularity of its interval isslightly faster or slower than 10 milliseconds, depending on the platform. However, the call isportable because the delta time specified is constant.)

If the driver has its own thread context or is running in a system-thread context, the device-dedicated thread or worker-thread callback routine can synchronize operations on a Kernel-defined dispatcher object such as an event, semaphore, or timer stored in a sharedcommunication region of the driver-defined device extension. When the device is not in use,the thread can wait on a shared dispatcher object, for example, by callingKeWaitForSingleObject with a semaphore. Until the device driver is called to carry out anI/O operation and sets the semaphore to the Signaled state, its waiting thread uses no CPUtime. Such a driver should set the priority of any device-dedicated thread to the lowest real-time priority level in order to give its thread(s) a relatively high priority while avoidingruntime priority inversions in multiprocessor machines.

16.4 Managing Memory Usage

Many NT drivers use only the memory allocated for their device objects' device extensions astheir global storage areas and their I/O stack locations in IRPs as operation-specific localstorage areas. However, an NT driver can allocate additional system-space memory for itsneeds and can use the kernel stack to pass small amounts of data in calls to internal driverroutines.

16.4.1 Using System Memory

Figure 16.3 illustrates the NT virtual memory spaces and their relationship to system physicalmemory.

Page 246: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-14 Kernel-mode Driver Design Guide

Figure 16.3 NT Virtual Memory Spaces and Physical Memory

As Figure 16.3 shows, virtual memory is backed by paged physical memory, and a virtualaddress range can be backed by discontiguous pages in the CPU. User-space virtual memoryand system-space memory allocated from paged pool are always pageable: that is, anynoncurrent process and its data can be paged out to secondary storage, usually on a disk. Thehyperspace area shown in Figure 16.3 is a dedicated range of system-space addresses, used bythe NT Memory Manager to map the current process's virtual address space to a set ofphysical pages in the CPU. Note that any noncurrent process's virtual addresses are not visible,so its memory space is inaccessible.

Page 247: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-15

NT drivers cannot allocate user-space virtual memory because they run in kernel mode. AnNT driver also cannot access memory via user-mode virtual addresses unless it is running inthe context of the user-mode thread that caused the driver's current I/O operation and it isusing that thread's virtual addresses.

Only highest-level drivers, such as NT file systems, can be sure their Dispatch routines will becalled in the context of such a user-mode thread. A highest-level driver can callMmProbeAndLockPages to lock down a user buffer before setting up an IRP for lowerdrivers. NT device and intermediate drivers that set up their device objects for buffered ordirect I/O (see the section on device objects in Chapter 3) can rely on the I/O Manager or ahighest-level driver to pass valid access to locked-down user buffers or to system-spacebuffers in IRPs.

The system-space virtual memory shown in Figure 16.3 consists of a limited amount of pagedpool and an even more limited amount of nonpaged pool. Nonpaged pool is guaranteed to beresident at all times. For NT drivers, paged pool can be accessed only under the followingcondition: ■ The routine using the corresponding paged-pool virtual addresses is running at IRQL <=

APC_LEVEL. As mentioned in Section 16.1, if a page fault occurs while running atIRQL > APC_LEVEL, it is a fatal error.

Except during their initialization, NT device and intermediate drivers seldom allocate memoryfrom paged pool because these types of drivers usually run at higher IRQL(s). Any pagablestorage that such a driver allocates cannot be accessed safely except by driver-created threadsor by the DriverEntry and Reinitialize (if any) routines, which can use paged pool allocationsto contain data, objects, and resources needed only during initialization.

Since several standard driver routines run at an IRQL higher than APC_LEVEL (atDISPATCH_LEVEL or DIRQL), memory allocated from paged pool is inaccessible to mostof an NT intermediate or device driver's routines. For example, higher-level drivers'IoCompletion routines execute in an arbitrary thread context, usually at DISPATCH_LEVELIRQL. Such a driver should never allocate pageable storage for data to be accessed from anIoCompletion routine. For more information about the IRQLs at which standard driverroutines execute, see Section 16.1.

Page 248: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-16 Kernel-mode Driver Design Guide

For an I/O buffer space, an NT driver can call one of the following support routines to allocatenonpaged pool memory: ■ MmAllocateNonCachedMemory ■ MmAllocateContiguousMemory ■ HalAllocateCommonBuffer if the driver's device uses busmaster DMA or a system

DMA controller's autoinitialize mode ■ ExAllocatePool

Note that nonpaged pool tends to become fragmented as the system runs, so an NT driver'sDriverEntry routine should call these routines to set up any long-term I/O buffers the driverneeds. Each of these routines, except possibly ExAllocatePool, allocates memory that isaligned on a processor-specific boundary (determined by the processor's data-cache-line size)to prevent cache and coherency problems.

NT drivers should allocate their buffers as economically as possible because nonpaged poolmemory is a limited system resource. In general, a DriverEntry routine should avoid callingthese support routines repeatedly to request allocations of less thanPAGE_SIZE(NumberOfBytes). In particular, driver writers should consider the following inorder to allocate I/O buffer memory economically: ■ Each call to MmAllocateNonCachedMemory ties up at least a full page of nonpaged

system-space memory, whatever the size of the requested allocation. For requests lessthan a page, any "remainder bytes" on the page are wasted: inaccessible by the driver thatcalled MmAllocateNonCachedMemory and unusable by other kernel-mode code.

■ Each call to MmAllocateContiguousMemory allocates up to a page if the specifiednumber of bytes is less than or equal to a page. For requests greater than a page, any"remainder bytes" on the last-allocated page are wasted: inaccessible to the driver thatcalled MmAllocateContiguousMemory and unusable by other kernel-mode code.

■ Each call to HalAllocateCommonBuffer uses at least one adapter object map register,which maps at least one byte and at most one page. (For more information about mapregisters and using common buffers, see the section on adapter objects in Chapter 3.)

Page 249: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-17

NT drivers can also call ExAllocatePool, specifying one of the following system-definedvalues for the PoolType parameter:NonPagedPoolCacheAligned for a permanent I/O buffer the driver uses, such as a SCSI

driver's buffer for request-sense data Note that the driver could call MmAllocateNonCachedMemory or

MmAllocateContiguousMemory instead.NonPagedPoolCacheAlignedMustS for a temporary, but critically important, I/O buffer,

such as a buffer containing initialization data for a physical device that must be presentfor the system to boot

NonPagedPool for any objects or resources not stored in a device extension or controllerextension that the driver might access while running at IRQL > APC_LEVEL

For this PoolType value, ExAllocatePool allocates exactly the amount of memoryrequested if the specified NumberOfBytes is less than or equal to a page. Otherwise, any"remainder bytes" on the last-allocated page are wasted: inaccessible to the caller andunusable by other kernel-mode code.

NonPagedPoolMustSucceed for a temporary, but critically important, storage area, that thedriver will release as soon as possible, such as memory that a driver needs to correct anerror condition (otherwise, it would corrupt the system)

PagedPoolCacheAligned for a file system driver's I/O buffer if the buffer will be accessedonly by driver-created threads

PagedPool for a temporary buffer, used by the DriverEntry routine to contain objects, data, orresources necessary for initialization, if the buffer will be released before the DriverEntryroutine returns, or for a storage area that will be accessed only by one or more driver-created threads

Because the must-succeed pool is a very limited system resource, allocations must be releasedby calling ExFreePool as soon as possible. Most NT drivers should not call ExAllocatePoolwith the PoolType values NonPagedPoolMustSucceed orNonPagedPoolCacheAlignedMustS, unless the system cannot continue to run if the driver'sallocation request does not succeed. For these PoolType specifications, ExAllocatePool bringsdown the system if it cannot allocate the requested memory.

For all other PoolType specifications, ExAllocatePool returns a NULL pointer if it cannotallocate the requested NumberOfBytes. NT drivers should check the pointer returned byExAllocatePool. If its value is NULL, the DriverEntry routine (or any other driver routine thatreturns NTSTATUS) should return STATUS_INSUFFICIENT_RESOURCES or handle theerror condition if possible. For more information about handling I/O errors, see Section 16.6,later in this chapter.

Page 250: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-18 Kernel-mode Driver Design Guide

For the CacheAligned PoolType specifications, ExAllocatePool allocates memory that isaligned on a processor-specific boundary (determined by the processor's data-cache-line size)to prevent cache and coherency problems.

To remap a memory-space physical address (such as that of the device registers) returned byHalTranslateBusAddress to a virtual address in paged pool, a DriverEntry routine can callMmMapIoSpace. For a summary of support routines that NT device and intermediate driverscan call, see Appendix A.

16.4.2 Using the Kernel Stack

While an NT driver can pass data parameters when calling its internal routines, the NT kernel-mode stack size is around two pages. Consequently, NT drivers cannot pass large amounts ofdata on the kernel stack. Driver writers should take care not to run out of kernel-mode stackspace by following these design guidelines: ■ Avoid making deeply nested calls from one internal driver routine to another if each

routine passes data on the kernel stack. ■ Take care to limit the number of recursive calls that can occur if you design a driver with

a recursive routine.

In other words, the call-tree structure of an NT driver should be relatively flat. Whilenonpaged pool is also a limited system resource, it is better for an NT driver to allocate asystem-space buffer, as described in the preceding section, than to run out of kernel stackspace.

The NT kernel-mode stack is in cached memory. Consequently, NT drivers cannot transferdata on the stack using DMA. Driver writers should avoid data alignment and/or data integrityproblems by following this design guideline: ■ Do not attempt to transfer data on the kernel stack using DMA.

Drivers of devices that use DMA can buffer data to be transferred, if necessary, either bycalling ExAllocatePool for a NonPagedPoolCacheAligned-type buffer or, for some drivers,by using common buffer DMA, as described in the section on adapter objects in Chapter 3.

16.4.3 Using Zone Buffers

NT drivers that set up zone buffers for fast allocation of fixed-size blocks (also called entries)should use that memory economically. Driver writers who use the Ex..Zone routines shouldfollow these guidelines: ■ Set up a modestly sized zone buffer. That is, make the zone buffer large enough for the

driver to handle an average I/O demand for its device in a typical NT machine, ratherthan setting up a fixed-size zone buffer large enough to handle a worst-case I/O-demandscenario on a high-end NT platform.

Page 251: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-19

When it initializes, the driver can call MmQuerySystemSize to get an estimate for sizinga zone buffer. This support routine returns a system-defined enumerated type, indicatingwhether the machine has a small, medium, or large amount of available memory.

■ If the driver allocates and frees zone buffer entries using the ExInterlocked..Zoneroutines, it must provide storage for an excutive spin lock and initialize the spin lock inits DriverEntry routine (see Section 16.2).

■ The driver should always call ExIsFullZone immediately preceding a call toExInterlockedAllocateFromZone. Otherwise, the driver might tie up a processor for anextended interval by trying to acquire the spin lock that protects a zone with no freeblocks.

■ If the driver's call to ExAllocateFromZone returns a NULL pointer (or its call toExIsFullZone returns TRUE), the driver should call ExAllocatePool to obtain additionalmemory to use as zone entries. The driver can use this memory temporarily until somezone entries have been released by calls to ExFreeToZone orExInterlockedFreeToZone. Then, the driver can call ExFreePool to release thememory since its zone buffer has free blocks again.

■ The driver should not call ExExtendZone, except possibly under the followingcircumstances:

■ Its originally allocated zone buffer has no free blocks. ■ It has already called ExAllocatePool more than once, but the driver is still using all

the additional memory it has allocated for zone entries. Such a condition indicates a very heavy I/O demand for the driver's device, so extending

its zone buffer might be worthwhile. That is, the performance gain from allocatingadditional zone blocks might allow the driver to process requests fast enough tocompensate for its additional "permanent" demand on nonpaged pool, especially ifextending the zone allows the driver to release some of the temporary memory itallocated with ExAllocatePool.

Note that a driver's successful calls to ExExtendZone allocate nonpaged pool memory thatremains allocated to the driver until the system is rebooted. Consequently, any driver that callsExExtendZone whenever its zone buffer becomes full (that is, all entries are currentlyallocated), can eventually run out of nonpaged pool and/or put the system into a low-memorystate such that all I/O throughput becomes very slow, including the "memory-hogging"driver's.

For a summary of the Ex..Zone routines, see Appendix A. For specifics, see the Kernel-modeDriver Reference.

Page 252: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-20 Kernel-mode Driver Design Guide

16.5 Maintaining Cache Coherency for DMA and PIO Operations

In NT platforms, data can be cached in one or more microprocessor caches and/or in thesystem DMA controller's cache when a driver is transferring data between system memoryand its device. NT drivers that use DMA or PIO to service read/write IRPs (or any devicecontrol request that requires a data transfer operation) should ensure the integrity of possiblycached data during transfer operations as explained in the following sections.

16.5.1 Flushing Cached Data during DMA Operations

On some platforms, the microprocessor and system DMA controller (or busmaster adapters)exhibit cache coherency anomalies. To maintain data integrity during DMA operations, NTdevice drivers should follow two rules: 1 Call KeFlushIoBuffers before beginning a transfer operation to maintain consistency

between data that might be cached in the microprocessor and the data in memory. If the driver calls HalAllocateCommonBuffer with the CacheEnabled parameter set to

TRUE, the driver must call KeFlushIoBuffers before beginning a transfer operationto/from its buffer.

2 Call IoFlushAdapterBuffers at the end of each transfer operation to be sure any"remainder bytes" in the system DMA controller's buffers have been written into memoryor to the slave device.

Or, call IoFlushAdapterBuffers at the end of each transfer operation for a given IRP tobe sure all data has been read into system memory or written out to a busmaster DMAdevice. For read operations, drivers of busmaster devices can callHalFlushCommonBuffer to be sure all data has been flushed from the device into thecommon buffer.

Figure 16.4 shows why it is important to flush the microprocessor cache before a read or writeoperation using DMA if the host processor and DMA controller do not automatically maintaincache coherency.

Page 253: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-21

Figure 16.4 Read and Write Operations Using DMA

When an asynchronous DMA read or write operation occurs, it accesses data in memory, notin the microprocessor cache. Unless this cache has been flushed by calling KeFlushIoBuffersjust before a read, the data written into memory by the DMA operation could be overwrittenwith stale data if the cache is flushed later. Unless the microprocessor cache has been flushedby calling KeFlushIoBuffers just before a write, the data in this cache might be more up-to-date than the copy in memory. Note that KeFlushIoBuffers does nothing if the host processorand DMA controller can be relied on to maintain cache coherency, so calls have almost nooverhead.

Certain DMA controllers, which are represented by NT adapter objects, have internal buffersand transfer cached data in fixed-size chunks, usually eight or more bytes at a time. Suchcontrollers can wait until their internal buffers are full before each transfer operation. Considerthe case of any driver that uses DMA to read data in variable-sized chunks or in fixed-sizechunks that are not an integral multiple of the controller's cache size. Unless the driver callsIoFlushAdapterBuffers at the end of each read or write operation, it cannot be sure whenevery byte the driver requested actually will be transferred.

Note that IoFlushAdapterBuffers returns a Boolean, indicating whether the requested flushoperation was successful. NT drivers can use this value to determine how to set the I/O statusblock when completing an IRP for any DMA read or write operation.

The driver of a busmaster DMA device also should call IoFlushAdapterBuffers at the end ofeach transfer operation for a given IRP to be sure that all data has been transferred into systemmemory or out to the device.

Page 254: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-22 Kernel-mode Driver Design Guide

16.5.2 Flushing Cached Data during PIO Operations

On some NT platforms, the instruction and data caches in the microprocessor exhibit cachecoherency anomalies if a page fault occurs during a PIO read operation.

To maintain data integrity during their read operations, NT drivers that use PIO should followthis rule: ■ Call KeFlushIoBuffers at the end of each read operation.

Note that KeFlushIoBuffers does nothing if the microprocessor can be relied on to maintaincache coherency, so calls have almost no overhead.

16.6 Error Logging and NTSTATUS Values

NT is designed to be both more robust and more "user-friendly" than some PC operatingsystems with respect to runtime errors. That is, NT is designed to do the following: ■ To continue running without allowing one component (or thread) to corrupt other

components' code or data when an error occurs ■ To continue running without sending a flood of cryptic error messages to end users

whenever errors occur

Granted that some I/O errors are user-induced. For example, a request to read data from a fileon removable media when the user has supplied the wrong diskette causes a user-induced I/Oerror. Such an error can be corrected readily when the user is prompted to supply the rightdiskette (see Section 16.7, next).

Other I/O errors cannot be readily corrected by the majority of end users. For these kinds ofI/O errors, NT continues to run without forcing the user to become aware of errors that theuser cannot fix immediately. Instead, NT provides an error-logging thread that formats andstores information about I/O errors as entries in a file.

The NT error log file allows users, system administrators, or support technicians to monitorthe condition of the hardware in a given NT machine and replace failing hardware asnecessary, to tune the configuration of an NT machine for better performance, and to debugproblems if they occur.

Page 255: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-23

When an NT driver discovers an I/O error in processing an IRP, it should callIoAllocateErrorLogEntry as follows:

errorLogEntry =(PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry( deviceExtension->DeviceObject, // target device for current operation sizeof(IO_ERROR_LOG_PACKET) + n * sizeof(ULONG)\ + sizeof(InsertionStrings) ); // where n depends on how much // DumpData the driver will supply

Note that an error log packet is limited in size, and the system-defined limit includes anyinsertion strings the driver supplies with the packet. NT drivers can callIoAllocateErrorLogEntry with a specified EntrySize value that is, at most,ERROR_LOG_MAXIMUM_SIZE. IoAllocateErrorLogEntry returns a pointer to an errorlog packet. If the returned pointer is NULL, the driver need not log an error. It should simplycontinue running and assume that, if the same error occurs again, it can be logged at that point.

The error log packet is defined as follows:

typedef struct _IO_ERROR_LOG_PACKET { UCHAR MajorFunctionCode; UCHAR RetryCount; USHORT DumpDataSize; USHORT NumberOfStrings; USHORT StringOffset; USHORT EventCategory; NTSTATUS ErrorCode; ULONG UniqueErrorValue; NTSTATUS FinalStatus; ULONG SequenceNumber; ULONG IoControlCode; LARGE_INTEGER DeviceOffset; ULONG DumpData[1];} IO_ERROR_LOG_PACKET, *PIO_ERROR_LOG_PACKET;

Page 256: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-24 Kernel-mode Driver Design Guide

The driver should fill in the error log packet with the following data:

MajorFunctionCodeIndicates the IRP_MJ_xxx from the driver's I/O stack location in the current IRP.

RetryCountIndicates how many times the driver has retried the operation and encountered thiserror. (RetryCount should be given a zero value when an error is firstencountered.)

DumpDataSizeIndicates the size in bytes required for any DumpData the driver will set in thepacket. The specified value should be an integral multiple of sizeof(ULONG).

NumberOfStringsIndicates how many insertion strings the driver will supply with this packet. Thesystem error-logging thread can use these driver-supplied, zero-terminatedUnicode strings to fill in messages sent to the user. For example, a network drivermight supply the name of a machine from which it received an erroneous packet.NT drivers set this value to zero for errors that need no insertion strings.

StringOffsetIndicates the offset, immediately following the DumpData, at which any driver-supplied insertion string data begins. If a driver supplies this data, each string mustbe a null-terminated Unicode string.

EventCategoryFor drivers that install themselves in the NT configuration registry as event-logging components, this is a driver-defined value that was specified in thedriver's message file for categories. Most NT device and intermediate drivers setthis value to zero. For more information, see Section 16.8.4, later in this chapter.

ErrorCodeIndicates the type of error (created with the IO_MAKE_ERROR_CODE macro,described later).

UniqueErrorValueIndicates where the error was detected in the driver.

FinalStatusIndicates the value set in the I/O status block of the IRP when it was completed orthe STATUS_xxx returned by a support routine the driver called.

SequenceNumberIndicates a driver-assigned sequence number for the current IRP (constant for thelife of a given request).

Page 257: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-25

IoControlCodeIndicates the I/O control code from the driver's I/O stack location in the currentIRP if the major function code was IRP_MJ_DEVICE_CONTROL orIRP_MJ_INTERNAL_DEVICE_CONTROL. Otherwise, this value should bezero. For more information about I/O control codes and device control requests,see Appendix B.

DeviceOffsetIndicates the offset into the device where the error occurred.

DumpDataIs used to store driver-specific data, such as register values or whatever else thedriver writer decides would be useful for identifying the cause of the error. Anydriver-supplied insertion strings must be supplied immediately following theDumpData, starting at StringOffset.

Both the ErrorCode and FinalStatus fields of the error log packet are of type NTSTATUS.Figure 16.5 shows the layout for NTSTATUS values.

Figure 16.5 NTSTATUS layout

NT drivers must set an NTSTATUS-type IO_ERR_xxx value for ErrorCode and anSTATUS_xxx value for FinalStatus in each error log packet. NT supplies a set of publicIO_ERR_xxx constants for setting the ErrorCode in an error log packet. For example, NTdrivers can use the following system-defined constants:

IO_ERR_RETRY_SUCCEEDEDIO_ERR_INSUFFICIENT_RESOURCESIO_ERR_CONFIGURATION_ERRORIO_ERR_INCORRECT_IRQLIO_ERR_INVALID_IOBASEIO_ERR_DRIVER_ERRORIO_ERR_PARITY : :IO_ERR_OVERRUN_ERRORIO_ERR_TIMEOUTIO_ERR_CONTROLLER_ERRORIO_ERR_INTERNAL_ERROR

Page 258: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-26 Kernel-mode Driver Design Guide

NT also supplies a set of public STATUS_xxx values that drivers can use to set FinalStatus inerror log packets and return from their standard driver routines for NTSTATUS. For example,NT drivers can return the following system-defined constants for NTSTATUS:

STATUS_SUCCESSSTATUS_PENDINGSTATUS_DEVICE_CONFIGURATION_ERRORSTATUS_DRIVER_INTERNAL_ERRORSTATUS_INVALID_DEVICE_STATESTATUS_IO_DEVICE_ERROR : :STATUS_DEVICE_BUSYSTATUS_DEVICE_DOES_NOT_EXISTSTATUS_ADAPTER_HARDWARE_ERROR

Public IO_ERR_xxx and STATUS_xxx constants are system resources, and each TM TMSTATUS_xxx is mapped to a corresponding Win32 error constant by the Windows NTsystem. Since they are built into NT, public constants can be added to the system only with thecooperation of Microsoft Corporation.

However, NT driver writers can create driver-specific private IO_ERR_xxx codes by using thesystem-defined macro IO_MAKE_ERROR_CODE. This macro sets the NTSTATUS Facilityfield, shown in Figure 16.5, to FACILITY_IO_ERROR_CODE. The driver writer must supplya Sev value and a unique Code value for each new IO_ERR_xxx defined withIO_MAKE_ERROR_CODE.

If the designer of such a driver decides to make the driver-defined errors visible to systemadministrators (or users) through the Windows NT event viewer, the driver must set itself upas an error-logging component in the configuration registry (see Section 16.8.4, later in thischapter). In most cases, NT device and intermediate drivers can simply log I/O errors withouthaving to supply insertion strings for a higher-level event-logging component and/or withouthaving to set up a driver-specific event-logging component. Of the system-supplied NTdrivers, only network device drivers currently supply insertion strings in error log packets.

The writer of a new pair (class/port or video display/miniport) of drivers also can definedriver-specific STATUS_xxx values to communicate information about privately definedIRP_MJ_INTERNAL_DEVICE_CONTROL requests from the lower to the higher driver ofthe pair. However, such a class driver should map any private STATUS_xxx value to asystem-defined NTSTATUS value when it completes an IRP if an existing higher-leveldriver's IoCompletion routine might be called for that IRP. (For paired video display/miniportdrivers, the NT-supplied video port driver does the mapping between STATUS_xxx valuesand Win32 status values.)

Page 259: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-27

Driver writers who create private STATUS_xxx values for a set ofIRP_MJ_INTERNAL_DEVICE_CONTROL requests should set the Facility field, shown inFigure 16.5, to an appropriate driver-defined constant, such as VIDEO_DEVICE for a set ofprivate video display/miniport STATUS_xxx values.

NT driver writers must set the customer code flag, labelled C in Figure 16.5, when they createnew NTSTATUS-type values for add-on drivers.

The Sev field shown in Figure 16.5 indicates the severity code, which must be one of thefollowing values:

STATUS_SEVERITY_SUCCESSIndicates STATUS_SUCCESS was set for FinalStatus, and a value such asIO_ERR_RETRY_SUCCEEDED for an ErrorCode value.

STATUS_SEVERITY_INFORMATIONALIndicates an informational NTSTATUS value, such asSTATUS_SERIAL_MORE_WRITES for a FinalStatus value.

STATUS_SEVERITY_WARNINGIndicates a warning NTSTATUS value, such asSTATUS_DEVICE_PAPER_EMPTY for a FinalStatus value.

STATUS_SEVERITY_ERRORIndicates an error NTSTATUS value, such asSTATUS_INSUFFICIENT_RESOURCES for a FinalStatus value orIO_ERR_CONFIGURATION_ERROR for an ErrorCode value.

Most I/O error codes belong to the STATUS_SEVERITY_ERROR category. WhileSTATUS_PENDING also belongs to the STATUS_SEVERITY_SUCCESS category, driverswould not log an error on a pending IRP.

When the driver has filled in the data for the packet returned by IoAllocateErrorLogEntry, itmust call IoWriteErrorLogEntry so the system error-logging thread will write its entry inthe error log file.

Whenever possible, an NT driver should log an error (and fail the IRP if necessary), butcontinue to run when the driver encounters unusual or unexpected I/O errors. An NT drivershould not call KeBugCheck to bring down the system.

Page 260: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-28 Kernel-mode Driver Design Guide

16.7 Handling Removable Media

NT file systems and removable-media device drivers share the responsibility for ensuring thatthe correct media is mounted when a file is opened on a removable-media device and that thecorrect media remains mounted during operations that access the media.

At its discretion, the file system can send an IRP to the device driver's Dispatch entry point forIRP_MJ_DEVICE_CONTROL requests with Parameters.DeviceIoControl.IoControlCodein the I/O stack location set to the following: IOCTL_xxx_CHECK_VERIFY

where xxx is the type of device, such as DISK, TAPE, or CDROM. The type DISK includes

both unpartitionable (floppy) and partitionable removable-media devices.

If the device driver determines that the media has not changed, the driver should complete theIRP, returning the IoStatus block with the following values:

Status set to STATUS_SUCCESSInformation set to zero

A removable-media driver must ensure that the media is not changed for the devicerepresented by the DeviceObject (input to every driver routine that is sent an IRP) wheneverthe driver processes an IRP that requests a transfer to/from the media or a device controloperation that affects the media. The best possible time to check for changed media is justafter the transition from a no-media-present state to a media-present state if the physicaldevice always notifies the driver about these state changes.

If the physical device indicates that the state of the media might have changed before thedriver begins an I/O operation or during an operation, the driver must do the following: 1 Set the Flags in the DeviceObject with DO_VERIFY_VOLUME. 2 Set the IoStatus block in the IRP to the following:

Status STATUS_VERIFY_REQUIREDInformation set to zero

Page 261: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-29

3 Before completing any IRP with an IoStatus block in which the Status field is not set toSTATUS_SUCCESS, the driver must call IoIsErrorUserInduced, which returns aBoolean TRUE for any of the following Status values:

STATUS_VERIFY_REQUIRED STATUS_NO_MEDIA_IN_DEVICE STATUS_WRONG_VOLUME STATUS_UNRECOGNIZED_MEDIA STATUS_MEDIA_WRITE_PROTECTED STATUS_IO_TIMEOUT STATUS_DEVICE_NOT_READY If IoIsErrorUserInduced returns TRUE, the driver must call

IoSetHardErrorOrVerifyDevice in order to send a popup to the user, who can thensupply the correct media, decide to retry the original request, or decide to cancel therequested operation.

For each IRP requesting an I/O operation to/from removable media, the device driver mustdetermine whether DO_VERIFY_VOLUME is already set in its DeviceObject Flags. If thisvalue is set, the driver must do the following: ■ For IRP_MJ_READ requests, check whether the I/O stack location Flags is set with

SL_OVERRIDE_VERIFY_VOLUME. If it is, continue the read operation. ■ Otherwise, the driver must refuse to carry out I/O requests for the corresponding drive,

device, or partition while DO_VERIFY_VOLUME is set in its DeviceObject Flags. Aremovable media driver must fail IRPs sent to the corresponding device as previouslydescribed, repeating both Steps 2 and 3 for each IRP until the file system clearsDO_VERIFY_VOLUME in the removable-media driver's DeviceObject Flags.

If a removable-media driver neglects to fail IRPs when DO_VERIFY_VOLUME is set (andSL_OVERRIDE_VERIFY_VOLUME is not set for reads), the file system can neithermaintain the integrity of cached file data nor cause the user to be prompted to remount themedia that holds an open file.

Any intermediate driver layered between an NT file system driver and a removable-mediadevice driver must set up the next-lower-level driver's I/O stack location in IRPs. Fromincoming IRP_MJ_READ requests, such an intermediate driver must copy its own I/O stacklocation Flags into the next-lower-level driver's I/O stack location when it sets up the I/Ostack location for the lower driver. If the intermediate driver creates new IRPs for lower-levelremovable-media drivers, it must copy the I/O stack location Flags field from the original IRPto any IRPs that it creates for IRP_MJ_READ requests.

Page 262: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-30 Kernel-mode Driver Design Guide

16.8 Using the Configuration Registry

Some of the information in the Windows NT configuration registry is persistent across systemboots after it has been set up, particularly information about things like an end user's graphicsdisplay preferences, application-specific environment settings, and so forth. However, NTreconstructs some of the configuration registry information every time the system is loaded sothat new drivers, hardware components, and peripheral devices can be added to machinesrunning NT.

In MIPS-based platforms, the ARC firmware provides an execution environment for thesystem bootstrap loader and gathers configuration information about the hardware componentsin the machine. In X86-based platforms, a "hardware recognizer" component called NtDetectprovides comparable functionality.

These bootstrap components "poke" the hardware and/or ROM BIOS in the machine to collectall hardware information that they can about the following: ■ The number and type of microprocessors (and coprocessors, if any) in the computer ■ The firmware caches ■ The I/O buses present and the type of interface (e.g., ISA, EISA, microchannel,

turbochannel, internal, etc.) for each bus ■ The devices detectable on each I/O bus ■ The disk, floppy, or CD-ROM device from which the operating system is to be loaded ■ The monitor on which strings are displayed early in the system boot process (and the

particular video adapter that controls it) ■ The keyboard and pointing devices ■ The parallel and serial ports and any printer and terminal devices connected to them ■ The network controller card(s)

The ARC or NtDetect component collects all the hardware information it can find into a treestructure, and the OS loader writes this information into the configuration registry for NTdevice drivers to use and to supplement when they, in turn, are loaded.

Higher-level NT drivers, which are loaded after device drivers, can consult the configurationregistry to get information about already loaded lower drivers and their devices. Still higher-level user-mode drivers (at the NT protected-subsystem level) can also use some of theregistry information supplied by NT device drivers.

Page 263: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-31

Naturally, NT drivers for different types of peripheral devices use the configuration registry toget and set somewhat device-type-specific information. Every NT driver, including higher-level drivers, can get any driver-specific (or user-supplied machine-specific) parameters thatare necessary for the driver to initialize itself. Each NT driver's parameters are stored in theconfiguration registry.

Some higher-level NT drivers, such as the NT SCSI class drivers, actually do not get hardwareinformation from the registry when they initialize themselves. Instead, SCSI class drivers getany HBA-specific configuration information from the NT SCSI port driver. As a "lowest-level" NT driver, the NT SCSI port driver does use the registry both to get and to set hardwareinformation about each SCSI HBA in the machine.

In other words, NT physical device drivers (or the NT-supplied video and SCSI port driverson behalf of OS-independent miniport device drivers) must both get system-supplied hardwareinformation from and set device-specific hardware information in the configuration registry.Higher-level NT drivers can use the registry information supplied by device drivers, by otherlower-level drivers, or by the user as deemed necessary (or useful) by each driver writer.

Because NT drivers vary in how they use the configuration registry, this section contains anoverview, rather than a device-type-specific or driver-level-specific discussion, of thefollowing topics: ■ What configuration registry information is made available to NT drivers when they

initialize themselves ■ How a typical NT device driver uses information supplied in the configuration registry

and supplements the hardware configuration information when the driver is loaded ■ How higher-level NT drivers can use system-supplied and device-driver-supplied

configuration information contained in the registry

16.8.1 Registry Paths Supplied to NT Drivers

When its DriverEntry routine is called, each NT driver is given a pointer to a Unicode string,representing a path to a driver-specific key in the configuration registry, along with a pointerto the driver object. Every driver object also contains a pointer to another key in theconfiguration registry. Under each key in the configuration registry is either a set of namedvalues with assigned value data or additional subkeys (eventually) with a set of values. (Thecombination of a named value and its corresponding data is called a value entry.) Figure 16.6illustrates the configuration registry paths given to NT drivers.

Page 264: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-32 Kernel-mode Driver Design Guide

Figure 16.6 Configuration Registry Paths for NT Drivers

As Figure 16.6 shows, the HardwareDatabase field of the driver object and theRegistryPath input to the DriverEntry routine each point to particular keys in theconfiguration registry. A DriverEntry routine is given read access to the Unicode strings atRegistryPath and HardwareDatabase, but must call system-supplied routines to manipulatethe value entries of keys in the configuration registry.

NT provides the following sets of routines that NT drivers call, depending on how a particulardriver uses the registry: ■ The I/O Manager supplies two routines that NT device drivers must use to initialize and

configure themselves when they are loaded. ■ The system supplies a number of kernel-mode runtime library routines (RtlXxx) that can

be used to query the value entries of keys with names known to the driver, to supplydefault value entries for "missing" keys, and to modify the value entries of keys. Driversthat get or set information under the registry keys\Registry\Machine\System\CurrentControlSet\Services\DriverName (as long as theDriverName value is known to the caller or created as a subkey under a key with a knownname) and/or \Registry\Machine\Hardware\DeviceMap can use these Rtl routines.

Page 265: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-33

■ The Configuration Manager supplies a set of Zw..Key routines that drivers can use toopen a registry key with a name known to the driver, to enumerate the immediatesubkeys of an open key and get their value entries (including the names of subkeys), toquery the value entries for a particular key, or to modify the value entries of keys in theregistry.

Some device drivers also use Zw..SymbolicLink routines, supplied by the ObjectManager, to create symbolic links between user- or administrator-supplied names for thedriver's device(s) and the corresponding NT device objects (see Section 16.8.1.1, next).For more information about how to use the Zw..Key routines in conjunction with theZw..SymbolicLink routines, see Section 16.5, later in this chapter.

16.8.1.1 RegistryPath, DriverName, and Device Object Names

As shown in Figure 16.6, RegistryPath points to the driver-specific key\Registry\Machine\System\CurrentControlSet\Services\DriverName. The value entry ofthe key DriverName is a Unicode string that identifies the driver, such as "Serial." Subkeysunder each DriverName key include the driver-specific Parameters (with value entries)needed to load the driver and can also include subkeys for device-specific Parameters neededto initialize each device the driver controls.

If an NT device driver needs hardware configuration information that the ARC or NtDetectcomponent cannot "find," that the driver cannot get from its device, or that the HAL cannotget from the bus, the information must be supplied under the registry key for the driver, so thatthe driver can get this hardware configuration information from Parameters when it is loaded.

An NT driver loses its RegistryPath pointer when the DriverEntry routine returns. If a driver-supplied Reinitialize routine uses this path, the driver should save the RegistryPath pointerthat is input to its DriverEntry routine.

Note that the DriverName string along the RegistryPath is not necessarily the generic namefor a type of physical device like "Serial." NT drivers generally cannot use this string to createnames for their device object(s).

As mentioned in Chapter 4, names for device objects are also Unicode strings but have theform \Device\GenericDeviceTypeDigit, such as "\Device\Harddisk1\Partition2." However, NTdrivers for certain kinds of devices can call IoGetConfigurationInformation to determinethe Digit value each driver should append to the name of each device object that it creates torepresent a physical device. Higher-level drivers also might callIoGetConfigurationInformation to determine how many device objects for a particular typeof physical device have been created by lowest-level drivers.

Page 266: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-34 Kernel-mode Driver Design Guide

IoGetConfigurationInformation returns a PCONFIGURATION_INFORMATION pointerto a structure in which NT drivers set the following about certain types of physical devices asthe drivers are being loaded:

ULONG DiskCount; // Harddisks already namedULONG FloppyCount; // Floppy drives already namedULONG CdRomCount; // CdRom drives already namedULONG TapeCount; // Tape drives already namedULONG ScsiPortCount; // ScsiPort (HBAs) already namedULONG SerialCount; // Serial ports already namedULONG ParallelCount; // Parallel ports already namedBOOLEAN AtDiskPrimaryAddressClaimed; // 0x1f0 - 0x1ffBOOLEAN AtDiskSecondaryAddressClaimed; // 0x170 - 0x17f

Drivers for these types of devices can use the current value in this structure to name theirdevice objects with the correct Digit suffix. For example, if the current value of DiskCount is1, a disk device driver would name its driver-created device objects for two physical disks"\Device\Harddisk1" and "\Device\Harddisk2." (Device object suffixes are zero-based.) Sucha driver must increment the DiskCount value for each device object that it creates to representa physical disk.

The last two fields in the CONFIGURATION_INFORMATION structure indicate whethereither of the I/O address spaces used by "AT" disk (WD1003-compatible) controllers is beingclaimed by the driver. A subsequently loaded disk driver for a device with an "AT-compatible" mode might use this information.

Note that an NT SCSI class driver can call IoGetConfigurationInformation to determinehow many HBAs are present in a particular machine, as well as how many Harddisk, Floppy,CdRom, Tape, Serial, or Parallel devices have already been named. Since SCSI class driversare loaded after the NT port and HBA-specific miniport drivers, ScsiPortCount wouldindicate the total number of HBAs in the machine. A SCSI class driver can use the value ofScsiPortCount as a loop-control variable to determine which HBAs drive buses with attachedperipheral devices of the class driver's type.

16.8.1.2 Load-control Sets for NT Drivers

As shown in Figure 16.6, RegistryPath points to a subkey along the path to the key\Registry\Machine\System\CurrentControlSet\Services.

CurrentControlSet is a symbolic link to a particular ControlSetnnn in the configurationregistry. Each ControlSetnnn is a named key with subkeys and value entries that specify theload order for the set of NT drivers.

Page 267: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-35

Every ControlSetnnn has two basic subkeys: 1 Control, which holds miscellaneous machine-specific value entries, such as the size of

nonpaged pool, the GroupOrderList that controls the load order within each set of NTdrivers that are classified as a group, and the ServiceGroupOrder that specifies the loadorder for each group of layered NT drivers. For example, a part of the load order mightbe

Control : : GroupOrderList : : ServiceGroupOrder List:REG_MULTI_SZ:SCSI miniport port Primary disk \ SCSI class SCSI CDROM class...

This ServiceGroupOrder example indicates that HBA-specific "SCSI miniport" drivers(each linked to the NT SCSI port driver, which is implemented as a dynamic-link library)should be loaded before other drivers in the "port" group (such as add-on bus adapterdrivers), that "Primary disk" ("AT" or "ABIOS") drivers should be loaded next, followedby the "SCSI class" drivers, and so on.

2 Services, which holds driver-specific information about how (and when during systeminitialization) a given driver is loaded and which, if any, lower-level drivers must beloaded first. For example, the Services subkeys pertaining to the "SCSI class" groupunder the Control\ServiceGroupOrder key might have

Services : : Scsidisk Type:REG_DWORD:0x1 // = SERVICE_KERNEL_DRIVER Start:REG_DWORD:0 // = SERVICE_BOOT_START Group:REG_SZ:SCSI class ErrorControl:REG_DWORD:0x1 // = SERVICE_ERROR_NORMAL Dependencies:REG_MULTI_SZ:SCSI miniport : : Scsitape Type:REG_DWORD:0x1 // = SERVICE_KERNEL_DRIVER Start:REG_DWORD:0x1 // = SERVICE_SYSTEM_START Group:REG_SZ:SCSI class ErrorControl:REG_DWORD:0x1 // = SERVICE_ERROR_NORMAL Dependencies:REG_MULTI_SZ:SCSI miniport

Page 268: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-36 Kernel-mode Driver Design Guide

This Services example indicates the following: ■ These drivers are NT drivers (Type = SERVICE_KERNEL_DRIVER), rather than

user-mode subsystem drivers. ■ Whether each driver must be loaded by the bootstrap loader because the system

itself might be loaded from the SCSI class driver's physical device (Start =SERVICE_BOOT_START) or during the next phase of system initialization (Start= SERVICE_SYSTEM_START). For more information about writing NT driversfor boot-from (disk, floppy, and CD-ROM) devices, see also Section 16.9.2.

■ These drivers belong to the "SCSI class" Group in the ServiceGroupOrder of theControl section for this ControlSetnnn.

■ The system will continue the boot process, possibly trying the LastKnownGoodControlSetmmm, if these drivers fail to initialize and cannot be loaded(ErrorControl = SERVICE_ERROR_NORMAL).

■ These drivers cannot be loaded (Dependencies) unless at least one driver from the"SCSI miniport" Group in the ServiceGroupOrder section has already beenloaded.

A new driver can be loaded in NT machines by adding appropriate subkeys with value entriesfor the driver to the CurrentControlSet in the registry. For example, a new SCSI miniportdriver that enables "WD1003 emulation" on its HBA should be loaded before any other SCSIminiport drivers so that it can claim an AtDisk..AddressSpace in the I/O Manager'sconfiguration information structure (see Section 16.8.1.1). On the other hand, a SCSI miniportdriver for an HBA that does not support a BIOS should be loaded following all miniportdrivers of HBAs that do.

When there are load-order dependencies within a particular service group, such as for the"SCSI miniport" group, each driver's Tag entry (not shown in the preceding SCSI classServices example because this group does not use Tag entries) under the Services subkey isused to specify its load order within its service group. That is, the position of a miniportdriver's Tag value in the CurrentControlSet\Control\GroupOrderList SCSI miniport listdetermines when that miniport driver loads relative to other miniport drivers. (A miniportdriver that has no Tag value in this list is loaded last.)

The configuration registry always contains additional load-control specifications, including abackup specification called LastKnownGood, in case modifications to theCurrentControlSet make it impossible to load the system.

Page 269: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-37

To create a driver-specific key in the configuration registry for an under-development driverand to make it loadable, the driver writer can do the following: ■ Use the Windows NT registry editor, regedit, to modify appropriate keys and their value

entries in the registry, such as the CurrentControlSet\Services key, and to add driver-specific keys, such as the DriverName\Parameters with appropriate value entries orsubkeys with value entries.

However, the driver writer must decide whether to rely on a system administrator (or end user)to "install" the retail driver in the configuration registry, to use a setup utility, to have thedriver set itself up in the registry, or to use some combination of these methods. For moreinformation, see Sections 16.8.5 and 16.9.1, later in this chapter.

16.8.1.3 HardwareDatabase and the System DeviceMap

Higher-level NT drivers can also use the HardwareDatabase, which points to the key\Registry\Machine\Hardware as shown in Figure 16.6, to get information from or to setinformation in a driver-created subkey under \Registry\Machine\Hardware\DeviceMap.

Driver-created subkeys of DeviceMap are volatile; that is, they are recreated every time thesystem boots. Any NT driver can supply driver-determined value entries in its DeviceMapsubkey. Thus, an NT driver can set up a subkey under the DeviceMap containing anyinformation that the driver writer chooses.

Usually, such a subkey supplies information to be consumed by a higher-level driver or that isuseful to a system administrator (or end user). For example, the NT video port driver sets upaliases between its device objects' names and the type of video adapter each represents; suchan alias might appear in the registry as"\Device\Video0:REG_SZ:\REGISTRY\Machine\System\ControlSet001\Services\Vga." Thesystem-supplied serial driver supplies a mapping between the generic NT names of its device TMobject names and the corresponding Win32 (or "DOS") logical name for the serial port;such a mapping might appear in the registry as "Serial0:REG_SZ:COM1."

Page 270: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-38 Kernel-mode Driver Design Guide

While the NT generic naming scheme for device objects (see Section 16.8.1.1 or Chapter 4) isdesigned to create a set of unique names for every device object that can be the target of anI/O request in any particular machine, end users and/or user-mode applications running withina protected subsystem might use subsystem-specific logical device names to request I/Ooperations. For more information about setting up symbolic links between device objectnames and user-mode names for the same devices, see Section 16.9.1, later in this chapter.

NT device drivers must both get system-supplied information from and set device-specificinformation in the configuration registry along the HardwareDatabase registry path. Devicedrivers get system-supplied hardware configuration information under the key\Registry\Machine\Hardware\Description and set hardware configuration information underthe key \Registry\Machine\Hardware\ResourceMap, as explained in the next two sections.

16.8.2 Getting System-supplied Device Description Data

When an NT device driver is loaded, the OS loader has already set up the registry with anymachine-specific information the ARC or NtDetect component could find, such as the bus-relative interrupt vector for a particular peripheral connected on a particular bus, the systemphysical address range or the bus-relative physical address range of I/O ports that a particulardevice can use, or the bus-relative DMA controller channel to which a particular device isconnected.

Each NT device driver must have this kind of information in order to initialize successfully.For example, a device driver must pass the bus-relative interrupt vector for its device toHalGetInterruptVector (see the section on interrupt objects in Chapter 3) to get a mappedsystem vector, system-assigned DIRQL, and processor affinity mask, required as arguments toIoConnectInterrupt. Otherwise, the driver cannot be loaded because it cannot register its ISRto handle interrupts from its device(s).

NT device drivers call IoQueryDeviceDescription to obtain the system-detected and system-supplied hardware information about their devices from the configuration registry. If thedriver needs hardware information that the ARC or NtDetect component cannot supply, itmight also get some of the required information from its device or by callingHalGetBusData, particularly for an EISA- or microchannel-type device. Otherwise, thedriver must must use the RegistryPath pointer to get its DriverName\Parameters, asmentioned in Section 16.8.1.1. For more information about using Parameters, see Section16.8.5 later in this chapter.

Page 271: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-39

A device driver that calls IoQueryDeviceDescription must provide a ConfigCallback routine,defined as follows:

NTSTATUS (*PIO_QUERY_DEVICE_ROUTINE) ( IN PVOID Context, //driver-determined IN PUNICODE_STRING PathName, IN INTERFACE_TYPE BusType, //system-defined enum IN ULONG BusNumber, //system-assigned count IN PKEY_VALUE_FULL_INFORMATION *BusInformation, IN CONFIGURATION_TYPE ControllerType, //system-defined enum IN ULONG ControllerNumber, IN PKEY_VALUE_FULL_INFORMATION \ *ControllerInformation, IN CONFIGURATION_TYPE PeripheralType, IN ULONG PeripheralNumber, IN PKEY_VALUE_FULL_INFORMATION \ *PeripheralInformation );

The Context passed to a ConfigCallback routine is driver-determined. However, the kernel-mode stack is limited in size: somewhat less than two pages are available for data storage bythe DriverEntry and ConfigCallback routines. Drivers that need a larger context area can callExAllocatePool to allocate a buffer for the configuration information passed between theirDriverEntry and ConfigCallback routines. The DriverEntry routine can free such a bufferwhen it has consumed (and processed as described in Section 16.8.3, next) the informationsupplied by a ConfigCallback routine.

Note the order of the bus, controller, and peripheral parameters to a ConfigCallback routine.The corresponding parameters to IoQueryDeviceDescription are declared to be optional, butthe DriverEntry routine must supply arguments for at least one of the following sets ofparameters when it calls IoQueryDeviceDescription: ■ BusType and BusNumber ■ ControllerType and ControllerNumber ■ PeripheralType and PeripheralNumber

Page 272: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-40 Kernel-mode Driver Design Guide

Depending on the information requested when the DriverEntry routine callsIoQueryDeviceDescription, the PathName passed to the ConfigCallback routine points to aregistry key that contains successively more device-specific and detailed information. Forexample, if a driver requests information only about a particular BusType in the machine, thePathName passed to the ConfigCallback routine would point to the key containing businformation. If the driver also requests information about both a ControllerType and aPeripheralType, the PathName parameter to the ConfigCallback routine would point to thekey containing peripheral information.

Figure 16.7 illustrates a device driver's call to IoQueryDeviceDescription.

Figure 16.7 Getting Information from the Registry for Device Drivers

Page 273: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-41

Pointers to the driver-supplied Context and ConfigCallback routine are required arguments toIoQueryDeviceDescription. All other parameters are optional, so the caller can specify oneor more of the bus, controller, and peripheral parameter sets, possibly in separate calls toIoQueryDeviceDescription with separate ConfigCallback routines. NT drivers usually needdevice-type-specific hardware information about a given ControllerType and/orPeripheralType.

System-defined enumerated values for the BusType parameter indicate the types of interfacesthat buses in NT machines provide. The driver for a device that must be connected on an ISAbus could set BusType to the system-defined INTERFACE_TYPE value Isa when it calledIoQueryDeviceDescription.

Such a driver could also search for its devices on every bus in the machine. Any driver can setBusType to successive values by using the system-defined value MaximumInterfaceType asa loop-control variable for calling IoQueryDeviceDescription, particularly if a driverconstrains its query by specifying values for the ControllerType and/or PeripheralTypeparameters. IoQueryDeviceDescription returns STATUS_OBJECT_NAME_NOT_FOUNDif a particular type of bus is not available in the machine.

NT device drivers can use the following system-defined CONFIGURATION_TYPE valuesfor the ControllerType and PeripheralType parameters to IoQueryDeviceDescription:

________________________________________________________________________ControllerType Values PeripheralType Values________________________________________________________________________

DiskController DiskPeripheralTapeController FloppyDiskPeripheralCdromController TapePeripheralWormController ModemPeripheralSerialController MonitorPeripheralNetworkController PrinterPeripheralDisplayController PointerPeripheralParallelController KeyboardPeripheralPointerController TerminalPeripheralKeyboardController OtherPeripheralAudioControllerOtherController________________________________________________________________________

Page 274: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-42 Kernel-mode Driver Design Guide

Most of these defined type names are self-explanatory. However, the PeripheralType valueMonitorPeripheral indicates a video monitor for which there is a corresponding videoadapter. The value TerminalPeripheral indicates a so-called "dumb terminal" that displaysdata usually sent through a serial port.

Depending on which optional arguments the DriverEntry routine passes toIoQueryDeviceDescription, its ConfigCallback routine is given values for the correspondingparameters when it is called. A ConfigCallback routine can save these values (such as system-assigned bus numbers) in a temporary buffer (usually allocated from nonpaged pool, asdescribed in Section 16.4.1, and released from the DriverEntry routine) or on the stack forsubsequent calls to HAL routines.

Depending on which optional arguments the DriverEntry routine passes toIoQueryDeviceDescription, a ConfigCallback routine is also given one or more nonNULLpointers to BusInformation, ControllerInformation, and PeripheralInformation, as shownin Figure 16.7. Each of these parameters is actually an array of pointers whose elements selectARC- or NtDetect-supplied information in the registry. Device drivers can use the followingsystem-defined enumerated type to get to the kind(s) of bus, controller, or peripheralinformation they need from the registry:

typedef enum _IO_QUERY_DEVICE_DATA_FORMAT { IoQueryDeviceIdentifier = 0, IoQueryDeviceConfigurationData, IoQueryDeviceComponentInformation, IoQueryDeviceMaxData} IO_QUERY_DEVICE_DATA_FORMAT,*PIO_QUERY_DEVICE_DATA_FORMAT;

An IoQueryDeviceIdentifier element points to any "hardware" name that the ARCcomponent found for the device (or that the x86-based OS loader created for the device),which the system translates into a Unicode string when this name is written into the registry.An IoQueryDeviceComponentInformation element points to any information that the ARCor NtDetect component found about a device's subcomponents.

As shown in Figure 16.7, NT device drivers usually get information from the configurationregistry of type CM_FULL_RESOURCE_DESCRIPTOR, for which they use theControllerInformation[IoQueryDeviceConfigurationData] and/orPeripheralInformation[IoQueryDeviceConfigurationData] pointers.

As Figure 16.7 also shows, the IoQueryDeviceXxx pointers are valid only within theConfigCallback routine. Moreover, NT drivers can read the value entries of subkeys under thekey \Registry\Machine\Hardware\Description but cannot modify them. It is aConfigCallback routine's responsibility to copy pertinent system-supplied data it finds to adriver-allocated buffer (or driver-structured area on the stack) so the DriverEntry routine canuse and, if necessary, supplement this information when the ConfigCallback routine returnscontrol.

Page 275: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-43

The CM_FULL_RESOURCE_DESCRIPTOR type information shown in Figure 16.7 includesall controller- or peripheral-specific information that can be detected about one or more of thefollowing: ■ An inclusive, but bus-relative, range of physical Port addresses - the DriverEntry routine

calls HalTranslateBusAddress to get a mapped system value that the driver uses (orremaps by calling MmMapIoSpace if HalTranslateBusAddress indicates that the Portrange is in memory, rather than I/O space) to access its device ports thereafter.

■ An inclusive range of physical Memory addresses used by the device - the DriverEntryroutine calls HalTranslateBusAddress to get a mapped range, which it passes toMmMapIoSpace to get a remapped system range that the driver uses to access itsdevice-dedicated memory thereafter.

■ The bus-relative Interrupt vector for its device and the IRQL for that vector - theDriverEntry routine calls HalGetInterruptVector to get a mapped system vector,DIRQL, and processor affinity mask that the DriverEntry routine can pass toIoConnectInterrupt when it registers the driver's ISR.

■ The bus-relative physical Dma channel or port (on microchannel-type buses) to which itsdevice is connected - the DriverEntry routine fills in a DEVICE_DESCRIPTIONstructure using this information, and calls HalGetAdapter so its device can do DMAtransfer operations thereafter. (For more information about devices that use DMA, see thesection on adapter objects in Chapter 3.)

Note that it is possible for a ConfigCallback routine to call HalTranslateBusAddress,HalGetInterruptVector, or HalGetAdapter, as long as it passes the values returned by theseHAL routines to the DriverEntry routine.

Page 276: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-44 Kernel-mode Driver Design Guide

The DeviceSpecificData, shown in Figure 16.7 at the tail of each partial resource descriptor ina partial resource list, is generally a device-type-specific structure. The system definesDeviceSpecificData structures for the most common kinds of peripheral devices. See thesystem-supplied header files for details.

Depending on the platform and/or type of bus, an NT driver might get only some of themachine-specific hardware information that it needs by calling IoQueryDeviceDescription.Depending on the device, some NT drivers also interrogate their devices to supplementsystem-supplied hardware configuration information. Others call HalGetBusData to getconfiguration information, particularly for EISA- or microchannel-type devices. An NT drivercan always supplement the system- or device-supplied hardware information with "user-supplied" Parameters under its registry key\Registry\Machine\System\CurrentControlSet\Services\DriverName (see Sections 16.8.1.1and 16.8.5).

While it supplies as much hardware configuration information as possible to each devicedriver, NT also requires each device driver to supply hardware configuration informationabout itself (and/or each of its devices), as a CM_FULL_RESOURCE_DESCRIPTOR, whichincludes DeviceSpecificData entries, in the configuration registry. In other words, every NTdevice driver must attempt to claim all the hardware resources in the machine that the driverand its device(s) need to carry out I/O operations.

Page 277: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-45

16.8.3 Claiming Hardware Resources

When a device driver's ConfigCallback routine returns, its DriverEntry routine can use theconfiguration information collected from its subkeys under\Registry\Machine\Hardware\Description (and/or from its Parameters subkey under\Registry\Machine\System\CurrentControlSet\Services\DriverName) in calls to one ormore of HalTranslateBusAddress, HalGetInterruptVector, and HalGetAdapter. Thedriver can use the values returned by the HAL as arguments to "higher-level" NT routinessuch as MmMapIoSpace, IoConnectInterrupt, and (after initialization)IoAllocateAdapterChannel or HalAllocateCommonBuffer.

When the driver has created its device objects and set up any other NT objects that it needs,but before the driver initializes its device(s), the DriverEntry routine must callIoReportResourceUsage one or more times to write configuration information about thehardware resources the driver and its device(s) will be using into the configuration registry.

Every NT device driver is required to transfer the same configuration information it got bycalling IoQueryDeviceDescription, plus its supplemental hardware configurationinformation, under the configuration registry key\Registry\Machine\Hardware\ResourceMap. Calling IoReportResourceUsage preventsdevice drivers that load later from trying to use the hardware resources already claimed by aloaded driver.

Figure 16.8 illustrates a device driver's call to IoReportResourceUsage.

Page 278: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-46 Kernel-mode Driver Design Guide

Figure 16.8 Setting Information in the Registry by Device Drivers

As Figure 16.8 shows, each resource list that an NT driver writes into the registry under\Registry\Machine\Hardware\ResourceMap is a variable-sized array ofCM_FULL_RESOURCE_DESCRIPTOR elements, containing the same information in thesame form as shown in Figure 16.7.

Page 279: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-47

In other words, values supplied for the driver (or for each of its devices) when it callsIoReportResourceUsage must be the same CM_PARTIAL_RESOURCE_DESCRIPTORPort, Interrupt, Memory, and Dma "hardware" values obtained from the driver's call(s) toIoQueryDeviceDescription. A device driver usually must supplement this information,particularly with DeviceSpecificData for each of its devices. For example, a floppy driverwould fill in DeviceSpecificData information about the maximum density and mount densityfor each of the floppy drives it controlled, while a serial device driver would fill ininformation about the baud rate for each of its serial ports. NT defines DeviceSpecificDatastructures for the most common kinds of peripheral devices, but an NT driver writer candefine a local structure for a new device driver.

When it calls IoReportResourceUsage, the driver must supply arguments for theOverrideConflict and ConflictDetected parameters. The driver also must supply arguments forat least one of the following sets of parameters: ■ DriverObject, DriverList, and DriverListSize ■ DeviceObject, DeviceList, and DeviceListSize

In general, the resource descriptors for a DriverList should specify those hardware resourcesthat the driver uses in common for all its devices. The resource descriptors for a DeviceListspecify those used for a particular device. An NT driver must call IoReportResourceUsagemore than once if the physical devices it controls each use device-dedicated hardwareresources or if these physical devices do not have identical values for DeviceSpecificData.

While the DriverClassName parameter to IoReportResourceUsage is optional, it determineswhether the driver's resource lists are written under a registry key that is named according tothe type of device, such as \Registry\Machine\Hardware\ResourceMap\Video, or under thedefault key (...\Other). If a named key already exists for a new driver's type of device, thedriver writer should supply the appropriate value for DriverClassName when the driver callsIoReportResourceUsage. In addition, the driver writer should have the driver write its\Registry\Machine\Hardware\DeviceMap information, if any, under the key with the samename.

When an NT driver for an entirely new kind of peripheral device supplies a unique value forDriverClassName, IoReportResourceUsage creates a new key in the registry. The designerof such a driver should create a generic device-type name, and should use the same name forthe driver's key, if it creates one, under \Registry\Machine\Hardware\DeviceMap.

The ConflictDetected value returned to the driver indicates whether it has attempted to claim ahardware resource that another device driver already claimed. If a hardware resource conflictoccurs, the driver that "found" the conflict should log an error, as described in Section 16.6.

Page 280: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-48 Kernel-mode Driver Design Guide

If its device can be reconfigured dynamically, the driver can rebuild the offending resource listand call IoReportResourceUsage again to overwrite its "conflicted" value entries under\Registry\Machine\Hardware\ResourceMap. If IoReportResourceUsage returns TRUE forConflictDetected and a driver cannot carry out I/O operations without the specified hardwareresources, the DriverEntry routine must release every system resource it has set up (freememory it has allocated, disconnect its interrupt objects, delete its device objects, and soforth), because that driver will be unloaded when its DriverEntry routine returns control,before the driver's Reinitialize routine, if any, is called.

The OverrideConflict parameter to IoReportResourceUsage is a value of type BOOLEAN.When it is set to TRUE, the driver's resource lists are written into the registry under the\Registry\Machine\Hardware\ResourceMap key even if another driver has already claimeda resource in this driver's resource lists. If such a conflict exists, the driver that loads later doesnot override the other driver's claim on the hardware resource itself.

Nevertheless, storing a device driver's unsuccessful attempt to claim a hardware resourcehelps the driver writer to debug configuration conflicts with other devices in NT machineswhile the driver is under development. Specifying TRUE for OverrideConflict also helpssystem administrators or users to correct configuration conflicts when they add the driver'sdevice (or add a new device later) in machines running NT.

When an NT driver's Unload routine is called, it must call IoReportResourceUsage one ormore times with the DriverListSize and/or DeviceListSize parameter set to zero. Such a callremoves the driver's resource list from the registry, releasing the resource(s) it claimed in itsDriverEntry routine. For more information about the Unload routine, see Chapters 4 and 15.

Page 281: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-49

16.8.4 Setting Up Driver-specific, User-visible Error Logging

Any NT driver that logs driver-defined I/O errors (see Section 16.6) can set itself up as anerror-logging component of the system (also called an event logging component within theWin32 subsystem programming environment). Such a driver must create a new subkey in theconfiguration registry under the key CurrentControlSet\Services\EventLog\System in orderto have its driver-supplied message file made available to the Windows NT event viewer or toany (subsystem-specific) application that reads the system error log file and displays Win32events to users or system administrators. (Note that a comparable application within anotherprotected subsystem might use another, subsystem-specific term for Win32 "events.")

Such a driver should name its new subkey with a unique DriverName that is identical to thename of the driver's executable. The driver-created subkey must include a value entry for thedriver-supplied message file, which can have a name chosen by the driver writer. Note that thevalue entry of a driver-supplied message file is a full pathname for the file.

Any NT driver writer who sets up the value entries for a new DriverName subkey also shouldweigh the tradeoffs of supplying information that is truly useful to the customer who buys thedevice against the resources that setting up a Win32 event-logging component consumes.

Such an NT driver should make its messages meaningful to the system administrator or enduser who will use the Windows NT event viewer (or some other application) to diagnoseproblems that have occurred while the system is running on a particular machine. The systemerror-logging mechanisms are not intended to be, nor should they be used for, debuggingunder-development device drivers. Instead, they should provide useful information to a driverwriter's retail customers.

For more information about the Windows NT event-logging mechanisms and how to set upSource (that is, DriverName) subkeys and message files, see the documentation provided withthe Windows NT System Design Kit (SDK).

Page 282: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-50 Kernel-mode Driver Design Guide

16.8.5 Using the RegistryPath Parameters

NT drivers for some types of devices must rely entirely on the value entries underDriverName\Parameters (see Figure 16.6) when they are loaded because their calls toIoQueryDeviceDescription (see Section 16.8.2) do not provide sufficient hardwareconfiguration information, because HalGetBusData cannot return sufficient information(particularly for ISA-type buses), or because the device itself does not supply everything thedriver needs to initialize itself and to claim hardware resources (see Section 16.8.3) in themachine.

Relying on a system administrator or end user to set up necessary subkeys and/or value entriesunder \DriverName\Parameters can present the driver writer with challenges, such as thefollowing: ■ A system administrator or user who sets up value entries for one or more subkeys under

the driver's RegistryPath Parameters key can supply an entirely arbitrary subkey namefrom the driver writer's point of view.

■ User-supplied value entries under Parameters subkeys can specify a user-assigned namefor the corresponding device, so the driver must create a symbolic link between thegeneric NT name of its device object and the corresponding user-assigned name (seeSection 16.9, later in this chapter).

■ Any driver that has no limit on the number of devices of a particular type it can support(such as the system-supplied parallel driver), must create an undefined number of nameddevice objects (and possibly symbolic links for user-assigned names) in any particularmachine.

Note that driver-supplied configuration information under the key\Registry\Machine\Hardware\DeviceMap (see Figure 16.6 and Section 16.8.1.3) about theNT names of driver-created device objects can be useful both to higher-level drivers and toanyone who uses regedit to modify (or create) Win32-visible names for add-on devices in aWindows NT machine.

Page 283: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-51

Such a driver can use the registry when it initializes to determine how many devices it mustsupport, find any user-supplied Parameters subkey value entries (possibly under user-chosennames for the subkeys) for particular devices, supply default value entries if necessary, and tofind any user-assigned Win32-visible name(s) for its device(s). To do this, an NT driver cancall the following general sequence of support routines: 1 Call ZwOpenKey to get a handle for the Parameters subkey, using the RegistryPath

pointer (see Figure 16.6). 2 Call ZwEnumerateKey with the handle to determine how many logical (such as parallel

ports) or physical devices the driver supports in the machine. This routine can be calledrepeatedly until it returns STATUS_NO_MORE_ENTRIES, thereby determining a countof subkeys and returning KEY_BASIC_INFORMATION about each subkey. Thereturned information includes the name of the subkey so the driver can supply therequired key name parameter to RtlQueryRegistryValues.

3 With each named subkey, call RtlQueryRegistryValues to find any user-suppliedhardware configuration values for the device and to find any user-assigned Win32 namefor the device. If a subkey has no value entry except its name, the driver can supplydefault value entries for that subkey.

4 Call ZwClose with the handle, obtained in Step 1, when the driver has consumed theinformation available in the Parameters subkeys.

5 After the driver has claimed hardware resources (or deleted one or more device objectsthat it created if it discovers resource conflicts) and initialized its physical device(s), thedriver must set up symbolic link(s) between any user-assigned Win32 name(s) or driver-determined default name(s) for the Win32-visible device(s) and the corresponding NTdevice object name(s), as described in Section 16.9, next.

6 After the driver releases the handle(s) for the symbolic link object(s) it created, callZwOpenKey to get a handle for the device-type specific DriverClassName subkey, ifany, under the DeviceMap (see Section 16.8.3) and call RtlWriteRegistryValue to setup aliased names under the driver's DeviceMap subkey(s).

7 Call ZwClose to release the handle(s) of the DeviceMap\DriverClassName subkey(s).

Any NT driver that has an Unload routine is responsible for releasing the symbolic linkobjects it has created (see Section 16.9), as well as for freeing the resources it claimed in theregistry (see Section 16.8.3). For more information about the Unload routine, see alsoChapters 4 and 15.

Note also that NT drivers of devices from which the system itself can be loaded must set up asymbolic link between the system-defined ARC name for the device and the NT device objectname. For more information about this requirement for fixed-disk, floppy, and CD-ROMdevice drivers, see also Section 16.9.2.

Page 284: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-52 Kernel-mode Driver Design Guide

16.9 Setting Up Symbolic Links

As mentioned in Section 16.8.1.3, the user-visible name for a particular device can be TMsubsystem-specific. For example, an MS-DOS or Win32 application's print job is likely tobe directed to a logical device named "LPT1," rather than to the NT device object name forthe parallel port to which a printer is attached. As mentioned in Chapter 4, a subkey ofCurrentControlSet\Control, \Session Manager\DOS Devices, contains subkeys with valueentries for the Win32 subsystem's logical device names. During system initialization, theSession Manager sets up symbolic link objects in the NT object namespace \DosDevices,using the registry's \Session Manager\DOS Devices subkeys. Each symbolic link objectcreates an alias between a Win32-visible name and the corresponding NT device object name,so that applications and/or end users can use the Win32-visible name to send I/O requests tothe underlying device.

Windows NT supplies a default set of Win32 logical names for standard MS-DOS and TMWindows devices like LPT1, but the default set cannot include every possible device thatmight be present in (or added to) a Windows NT machine. Consequently, NT driver writersmust set up symbolic link objects in the NT object namespace \DosDevices, in order to maketheir devices available to Win32 applications (and end users) for I/O requests. Section 16.9.1,next, explains how the writer of a new NT device driver can set up the necessary symboliclink(s).

As mentioned in Section 16.8.1.2, NT drivers of devices from which the system itself can beloaded must set up a symbolic link between the ARC- or NtDetect-created "hardware" namefor the device and the corresponding device object name. Section 16.9.2 explains how thewriter of a new disk, floppy, or CD-ROM device driver can set up the necessary symboliclink(s).

16.9.1 Making a Named Device Object Visible to User-mode Applications

To make a new device accessible from the Win32 subsystem, there must be a symbolic linkobject in the NT object namespace \DosDevices. Such a symbolic link object maps the NTgeneric name for a driver's device object to the corresponding Win32 logical name for thesame device. There are three basic techniques to set up such a symbolic link: 1 Use regedit (or a setup utility) to add a subkey under

CurrentControlSet\Control\Session Manager\DOS Devices (such as \MyFaxDevice)with a value entry equating the NT device object name to the Win32 logical name. Forexample, the value entry under \DOS Devices\MyFaxDevice might be"\Device\Fax0:REG_SZ:FaxDev."

2 Set up the symbolic link when the driver initializes by callingZwCreateSymbolicLinkObject.

3 Set up the symbolic link when the driver initializes by calling IoAssignArcName.

Page 285: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-53

When such a symbolic link object has been created in \DosDevices, a Win32 application (orsubsystem-level driver) can get a handle for the file object that represents the \Device\Fax0device object using "\\ .\FaxDev" as an argument.

Note that a system administrator or end user must be very knowledgable about using regeditand about the DOS Devices key in the configuration registry in order to use the firsttechnique of "installing" a new Win32 device in the registry.

Note also that using regedit creates a nonvolatile subkey under \Session Manager\DOSDevices in the registry. As mentioned in Section 16.9, the Session Manager creates a symboliclink object in the \DosDevices object namespace for each subkey. To the Session Manager, thecreation of these symbolic link objects requires only that it process the value entries of theDOS Devices subkeys. Consequently, it can create a symbolic link for a device that is notactually available for user-mode I/O requests. For example, if a device driver fails itsinitialization or is unloaded dynamically, the Session Manager does not remove thecorresponding DOS Devices subkey in the registry nor the corresponding symbolic link objectin the \Dos Devices namespace.

For developers of new NT drivers, relying on the first technique for setting up a new deviceand production-quality driver can be costly in terms of customer support. Consequently, anNT driver writer might prefer to use the second technique for making its device visible toWin32 applications and end users. For example, the driver of a new fax device might do thefollowing in order to make its device accessible to I/O requests from Win32 applications: 1 The driver forms a full path name string (including the \DosDevices as the "root" of

the path) for the user-visible name, such as "FaxDev," (as a driver-buffered, zero-terminated Unicode string) and another full path name string for the correspondingdevice object name, such as "\Device\Fax0," (also as a driver-buffered, zero-terminated Unicode string). (The driver must pass a pointer to the buffered, user-visible name when it calls ZwCreateSymbolicLinkObject.)

2 The driver fills in an OBJECT_ATTRIBUTES structure with a pointer to the buffercontaining the device object name string and with the permanent flag (and, possiblythe case-insensitive flag) set, and calls InitializeObjectAttributes. (The driver mustpass a pointer to the initialized object attributes data when it callsZwCreateSymbolicLinkObject.)

3 The driver calls ZwCreateSymbolicLinkObject with the parameters that it set upin Steps 1 and 2.

4 The driver calls ZwClose with the handle returned byZwCreateSymbolicLinkObject.

To use the third technique, the driver also must set up the same buffered, zero-terminatedUnicode strings for each name and call IoAssignArcName (see also Section 16.9.2).

Page 286: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

16-54 Kernel-mode Driver Design Guide

While using the first technique to make a driver's device visible within the Win32 subsystemenvironment is a simple way to get an under-development NT driver loaded so it can bedebugged and tested, production-quality drivers should create symbolic links between the NTnames for their device objects and the corresponding Win32 logical device names in the NTobject namespace \DosDevices.

Any NT driver that has an Unload routine is responsible for releasing the symbolic linkobjects it has created. Such a driver's Unload routine must do the following with each driver-created symbolic link object: 1 Call ZwOpenSymbolicLinkObject to get a handle for the symbolic link object. 2 Call ZwMakeTemporaryObject with the handle. 3 Call ZwClose to have the symbolic link object removed from the system.

16.9.2 Creating Symbolic Links between Boot Devices and ARC Names

As mentioned in Section 16.8, drivers of devices from which the system itself can be loadedmust be identified by the ARC or NtDetect components during the earliest phase of the systemboot process. For the boot-from disk, floppy, or CD-ROM device in a given machine, ahardware device name, representing a vector to the device, is passed to the bootstrap loader bythe ARC component.

To ensure driver portablity across NT platforms, the OS loader sets up the ARC names inX86-based NT machines.

When the driver of a boot-from device is loaded, it must call IoAssignArcName to create asymbolic link between the ARC name for the device and the name that the driver assignedwhen it created a device object to represent a boot-from device.

Page 287: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Common Driver Design Issues 16-55

For example, when a driver creates the device object, it should include code somewhat like thefollowing:

{ CCHAR ntNameBuffer[MAXIMUM_FILENAME_LENGTH]; CCHAR arcNameBuffer[MAXIMUM_FILENAME_LENGTH]; STRING ntNameString; STRING arcNameString; UNICODE_STRING ntUnicodeString; UNICODE_STRING arcUnicodeString; : : RtlInitString(&ntNameString, ntNameBuffer); (VOID)RtlAnsiStringToUnicodeString(&ntUnicodeString, &ntNameString, TRUE); : : // Call IoCreateDevice with ntUnicodeString : : // An ARC name looks something like // \ArcName\scsi(0)disk(0)rdisk(0) or // \ArcName\multi(0)disk(0)rdisk(digit) RtlInitString(&arcNameString, arcNameBuffer); status = \ RtlAnsiStringToUnicodeString(&arcUnicodeString, &arcNameString, TRUE); if (!NT_SUCCESS(status)) { RtlFreeUnicodeString(&ntUnicodeString); goto CreateDeviceObjectsExit; } IoAssignArcName(&arcUnicodeString, &ntUnicodeString); RtlFreeUnicodeString(&arcUnicodeString); : :

NT drivers for partitionable-media devices also must create a symbolic link between the nameof the driver-created device object that could represent the boot partition and thecorresponding ARC name with IoAssignArcName.

Page 288: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT
Page 289: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT
Page 290: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Appendix AVideo Miniport Drivers

TMWindows NT video miniport drivers are adapter-specific but OS-independent. Eachminiport driver links itself against the NT video port driver, which is a kernel-mode dynamic-link library. A video miniport driver responds to requests set up by the NT port driver as videorequest packets (VRPs) and calls only the port driver's VideoPortXxx routines tocommunicate with the system. Windows NT video miniport drivers can be recompiled to run TMon machines running a subsequent version of MS-DOS , which will provide an OS-dependent port driver that also exports the VideoPortXxx routines.

Each video miniport driver provides kernel-mode, hardware-level support for a corresponding TMuser-mode display driver written to the Win32 GDI/DDI interface. A display driver calls theWin32 DeviceIoControl function, which in turn calls an NT I/O system service to send therequest through the NT video port driver to the video miniport driver.

In most circumstances, the display driver carries out time-critical operations that are visible tothe user, while the underlying miniport driver provides support for infrequently requestedoperations or truly time-critical operations that cannot be preempted by an interrupt. A user-mode display driver cannot handle device interrupts, and only an underlying kernel-modeminiport driver can allocate device memory and map it into a user-mode display driver'saddress space. For more information about Windows NT display drivers, see the Win32Subsystem Driver Design Guide and Reference manuals.

Page 291: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-2 Kernel-mode Driver Design Guide

In x86-based Windows NT platforms, there are two kinds of video miniport drivers: 1 Self-declared "VGA-compatible" miniport drivers that are set up to configure themselves

in the registry with VgaCompatible set to TRUE (For more information about setting upvideo drivers, see the Programmer's Guide.)

■ Such a miniport driver both provides support for full-screen MS-DOS applicationsto do I/O directly to the adapter registers and also functions as a video validator toprevent such an application from issuing any sequence of instructions that wouldhang the machine. All miniport drivers of SVGA adapters fall in this category, butany new miniport driver for a nonSVGA adapter can provide this support at thediscretion of the driver designer.

Because miniport drivers for SVGA adapters provide this support, this appendixrefers to all such miniport drivers as "SVGA" miniport drivers.

■ A miniport driver for an SVGA adapter is based on the system-supplied VGAminiport driver, with code modified to support adapter-specific SVGA features. Thesystem-supplied VGA display drivers use the kernel-mode support provided byminiport drivers for SVGA adapters, so the developer of a new miniport for anSVGA adapter need not write a new user-mode display driver.

2 Miniport drivers that are set up to configure themselves in the registry withVgaCompatible set to FALSE

■ Such a miniport driver provides no special support for full-screen MS-DOSapplications. Instead, it is loaded along with a system-supplied VGA (or, possiblySVGA) miniport driver, which provide this support for MS-DOS applications.

For convenience, this appendix refers to all such miniport drivers as "nonSVGA"miniport drivers.

■ In most cases, such a miniport driver either is written for an adapter that has noVGA-compatibility mode or for an accelerator that works independently of theVGA.

■ If a new miniport driver cannot provide support for an existing display driver, suchas the system-supplied frame buffer or S3 display driver, a corresponding Win32display driver must be written together with the new miniport driver.

In all non-x86-based Windows NT platforms, video miniport drivers need not supply anyspecial support for full-screen MS-DOS applications. That is, video miniport drivers must beset up to configure themselves in the registry with VgaCompatible set to FALSE.

Page 292: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-3

Every video miniport driver must have at least the following system-defined routines: ■ DriverEntry to initialize the miniport driver ■ HwFindAdapter to determine how (or whether) driver-supported video adapter(s) are

configured in the machine ■ HwInitialize to "open" the adapter for the corresponding display driver ■ HwStartIo to start operations on the adapter for each incoming I/O request

Depending on a given adapter's features and on the driver designer, video miniport driversalso can have any or all of the following system-defined routines: ■ HwRegistryCallback(s) to process configuration information stored in the registry ■ HwInterrupt to handle adapter-generated interrupts ■ HwSynchronizeExecutionCallback for critical sections of code to be run at a higher than

normal hardware priority (IRQL) with some device and system interrupts masked off ■ SvgaHwIoPortXxx routines in SVGA miniport drivers to support direct access to I/O

ports by full-screen MS-DOS applications in x86-based machines

The following subsections describe the requirements for and functionality of each of theseminiport driver routines. See the Kernel-mode Driver Reference for their formal definitionsand descriptions of their parameters.

Page 293: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-4 Kernel-mode Driver Design Guide

A.1 DriverEntry Routine

A DriverEntry routine is the initial entry point for each NT driver, including any videominiport driver. A miniport's DriverEntry routine is called with two input arguments (of typePVOID) and must do the following: 1 Call VideoPortZeroMemory to initialize a VIDEO_HW_INITIALIZATION_DATA

structure on the stack. 2 Set driver-specific and adapter-specific values in the

VIDEO_HW_INITIALIZATION_DATA fields, including the miniport driver's entrypoints. The following entry points must be set:

■ HwFindAdapter ■ HwInitialize ■ HwStartIo The HwInterrupt entry point can be set to a driver-supplied routine or must be set to

NULL. 3 Initialize the StartingDeviceNumber to zero. (If the miniport driver supports more than

one adapter, the NT port driver sets up a key in the registry for each adapter and giveseach key a name with a unique number.)

4 Set up any driver-determined context data that the miniport's DriverEntry routine wantspassed to the miniport's HwFindAdapter routine.

5 Call VideoPortInitialize with the pointers that were input to the DriverEntry routine andwith pointers to the filled-in VIDEO_HW_INITIALIZATION_DATA and miniport-supplied context data, if any.

A miniport driver might have a set of bus-type-specific HwFindAdapter routines if its adaptercan be connected on various types of I/O bus, or it can call VideoPortInitialize with the sameHwFindAdapter routine for each type of bus and use driver-supplied HwRegistryCallbackroutines to get adapter configuration information. Such a miniport driver must modify theAdapterInterfaceType field in the VIDEO_HW_INITIALIZATION_DATA after each callto VideoPortInitialize, possibly modify the context data (and HwFindAdapter field, if thedriver implements separate HwFindAdapter routines) for the new bus type, and callVideoPortInitialize for each type of bus on which a supported adapter could be connected.

VideoPortInitialize checks the validity of the VIDEO_HW_INITIALIZATION_DATA,collects and stores pertinent information in the device extension of a device object that itcreates to represent the adapter, allocates memory for and zeros aVIDEO_PORT_CONFIG_INFO-type buffer, fills in as much configuration information as itcan, and calls the miniport driver's HwFindAdapter routine, described in Section A.1.1, next.

If the miniport driver sets an AdapterInterfaceType in theVIDEO_HW_INITIALIZATION_DATA for which there is no bus of that type in themachine, the NT port driver returns STATUS_NO_SUCH_DEVICE. VideoPortInitialize

Page 294: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-5

does not call the miniport-supplied HwFindAdapter routine for that bus type. The miniportdriver does not remain loaded if there are no buses of a type that support its adapter in themachine.

Note that VideoPortInitialize is responsible for the following before it returns control to theminiport's DriverEntry routine: ■ Setting up all necessary NT objects for the miniport driver ■ Getting requested information from and setting information in the configuration registry ■ Allocating system resources on behalf of the miniport driver, including memory for the

miniport-specified HwDeviceExtensionSize, and, possibly for the miniport-specifiedNumberOfAccessRanges and/or NumEmulatorAccessEntries

The miniport driver defines the internal structure and contents of its device extension. Apointer to the miniport driver's device extension is an input argument to every system-definedminiport driver routine except the DriverEntry, HwSynchronizeExecutionCallback, andSvgaHwIoPortXxx routines. Many VideoPortXxx routines require this pointer as an argument.

The DriverEntry routine might or might not fill in the NumberOfAccessRanges and/orNumEmulatorAccessEntries fields in the HARDWARE_INITIALIZATION_DATA. Avideo miniport driver can do either of the following: ■ Set nonzero values in either or both of these fields in the DriverEntry routine to request

that the NT video port driver allocate memory for an array of access range elementsand/or for an array of emulator access entries

An array of VIDEO_ACCESS_RANGE-type elements describes one or more ranges ofI/O ports and/or memory for direct access to (or by) the video adapter. The miniport canmark those ranges it allows the display driver to access as visible.

In x86-based machines, nonSVGA miniports always set theNumEmulatorAccessEntries to zero, because a system-supplied VGA or SVGAminiport driver provides this support for full-screen MS-DOS applications. In otherWindows NT platforms, video miniport drivers need not support emulator access entries.(See Section A.6.1, later in this appendix, for more information about how SVGAminiport drivers set these up.)

■ Define the driver's access ranges and/or emulator access entries as statically allocatedmemory (in the driver's header file or code), leave these fields set to zero, and reset thecorresponding fields in the VIDEO_PORT_CONFIG_INFO-type buffer from theHwFindAdapter routine

When VideoPortInitialize returns control, the DriverEntry routine propagates the return valueof VideoPortInitialize. If a miniport driver calls VideoPortInitialize more than once, itsDriverEntry routine should propagate the lowest return value. VideoPortInitialize can becalled only from a miniport driver's DriverEntry routine. For more information about theVIDEO_HW_INITIALIZATION_DATA structure and VideoPortInitialize, see the Kernel-mode Driver Reference.

Page 295: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-6 Kernel-mode Driver Design Guide

A.1.1 HwFindAdapter Routines

The NT video port driver fills in as much VIDEO_PORT_CONFIG_INFO as it can from theminiport's returned VIDEO_HW_INITIALIZATION_DATA before calling itsHwFindAdapter routine with a pointer to the configuration information buffer. In particular,VideoPortInitialize always sets the following: ■ Length to sizeof(VIDEO_PORT_CONFIG_INFO) ■ AdapterInterfaceType to the miniport's VIDEO_HW_INITIALIZATION_DATA

specification ■ SystemIoBusNumber to the system-assigned number of the I/O bus on which the

adapter might be attached (This value can be nonzero only in machines with more than one bus of the given

AdapterInterfaceType.) ■ NumberOfAccessRanges to the miniport's VIDEO_HW_INITIALIZATION_DATA

specification ■ NumEmulatorAccessEntries to the miniport's VIDEO_HW_INITIALIZATION_DATA

specification ■ StartingDeviceNumber to the miniport's VIDEO_HW_INITIALIZATION_DATA

specification

A HwFindAdapter routine first checks whether the Length of the input buffer has enoughroom for data that the miniport driver needs to find its video adapter. (In effect, this is a checkon the version of the VIDEO_PORT_CONFIG_INFO structure.)

If the miniport driver allocated static memory for its access ranges and/or emulator accessentries, the HwFindAdapter routine must do the following: 1 Set the VIDEO_PORT_CONFIG_INFO AccessRanges and/or EmulatorAccessEntries

field(s) to the start of the driver's static data structure. 2 Set the NumberOfAccessRanges and/or NumEmulatorAccessEntries field(s) to the

correct value.

Otherwise, the miniport driver should use the pointer(s) stored in the AccessRanges and/orEmulatorAccessEntries field(s) of the configuration information buffer to store its accessranges and/or emulator access ranges in the memory allocated by the port driver.

To get the miniport's access ranges (and, possibly, other configuration information), itsHwFindAdapter routine might call VideoPortGetDeviceData orVideoPortGetRegistryParameters with a miniport-supplied HwRegistryCallback routinethat will process information retrieved from the registry. For more information aboutHwRegistryCallback routines, see Section A.1.2, next.

Page 296: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-7

Certain VideoPortXxx routines should be called only from the miniport driver'sHwFindAdapter routine(s), in particular, the following: ■ VideoPortGetDeviceData to get VIDEO_DEVICE_DATA_TYPE-specific information

from the configuration registry (see Section A.1.2, next) ■ VideoPortGetDeviceBase to map each base physical address, as described in an element

of the VIDEO_PORT_CONFIG_INFO AccessRanges array, to a system (logical)address range

Each such (bus-relative) physical address range must be mapped withVideoPortGetDeviceBase before it is stored in the miniport's device extension. With themapped logical addresses, the driver can call the VideoPortReadXxx andVideoPortWriteXxx routines to read from or write to an adapter. For a mapped range inI/O space, the miniport driver calls the VideoPortReadPortXxx andVideoPortWritePortXxx routines. For a mapped range in memory, the miniport drivercalls the VideoPortReadRegisterXxx and VideoPortWriteRegisterXxx routines.

■ VideoPortFreeDeviceBase to release such a mapped range if the HwFindAdapter (orHwRegistryCallback) routine does not find an adapter it can support on a given I/O bus(as indicated by the VIDEO_PORT_CONFIG_INFO SystemIoBusNumber value)

Note that the HwFindAdapter (or HwRegistryCallback) routine cannot "poke" an adapter in anattempt to identify its device without calling a VideoPortReadXxx (or, possibly,VideoPortWriteXxx) routine. However, the HwFindAdapter routine should not change thestate of any video adapter unnecessarily and must not leave any adapter in a changed state;while the HwFindAdapter routine runs, the NT Hardware Abstraction Layer (HAL) hascontrol of the monitor so it can write information to the screen in the early stages of thesystem boot process. If a HwFindAdapter routine's attempt to identify its adapter might haveaffected an adapter's state, this routine should restore the original state immediately.

If the miniport driver does not use the configuration registry or cannot find sufficientinformation in the registry, it can have a set of default addresses for video memory. However,it must set up access to video memory with VideoPortGetDeviceBase so the driver can callthe VideoPortReadXxx and VideoPortWriteXxx to determine whether an adapter it supportsis present in the machine.

If the miniport's HwFindAdapter routine cannot find an adapter it supports, it should set Againto FALSE and return VP_RETURN_NOT_FOUND. If the HwFindAdapter routine finds asupported adapter but the driver cannot find valid configuration data for its adapter on aparticular I/O bus, it should set Again to FALSE and return VP_RETURN_BAD_CONFIG.

Note that setting Again to FALSE and returning control with VP_RETURN_NOT_FOUND orVP_RETURN_BAD_CONFIG indicates that a given I/O bus, identified by theSystemIoBusNumber in the input VIDEO_PORT_CONFIG_INFO buffer, has no videoadapters that the miniport driver can support. It does not prevent the NT video port driverfrom calling HwFindAdapter again with an updated SystemIoBusNumber so thatHwFindAdapter can look for its video adapter on another I/O bus of the sameAdapterInterfaceType.

Page 297: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-8 Kernel-mode Driver Design Guide

When the HwFindAdapter routine finds an adapter it can support, this routine must fill inpertinent fields, as appropriate for its adapter and the given AdapterInterfaceType, in theinput VIDEO_PORT_CONFIG_INFO buffer, and return VP_RETURN_FOUND. Everyvideo miniport driver that finds a supported adapter must fill in the AccessRanges informationfor each adapter that it supports. The HwFindAdapter routine can set Again to TRUE to becalled again with the same input VIDEO_PORT_CONFIG_INFO values in case there isanother of its adapters on the same I/O bus.

A HwFindAdapter routine also can call the VideoPortGetRegistryParameters andVideoPortSetRegistryParameters routines to get and set configuration information. TheHwInitialize routine, described in Section A.2, also might callVideoPortGetRegistryParameters or VideoPortSetRegistryParameters. For moreinformation about the VIDEO_PORT_CONFIG_INFO structure and these VideoPortXxxroutines, see also the Kernel-mode Driver Reference.

When a miniport driver's HwFindAdapter routine returns control, VideoPortInitialize returnsto the DriverEntry routine if the call(s) to HwFindAdapter indicated that the miniport drivercould not support an adapter. If a miniport's HwFindAdapter routine does not returnVP_RETURN_FOUND for at least one adapter, the driver does not remain loaded after itsDriverEntry routine returns control.

Otherwise, VideoPortInitialize claims resources in the configuration registry, sets upnecessary system objects (such as an interrupt object if the adapter generates interrupts) onbehalf of the miniport driver. VideoPortInitialize also sets up a symbolic link between thenamed device object that it created to represent the adapter and a Win32 alias so thecorresponding display driver can open the adapter using its Win32-visible name. (Theminiport's HwInitialize routine, described in Section A.2, is called in response to the Win32open request.)

Note that the mapped logical addresses passed to the VideoPortReadXxx andVideoPortWriteXxx routines are kernel-mode addresses. A Win32 display driver cannot useany kernel-mode addresses that the corresponding miniport driver has mapped withVideoPortGetDeviceBase. After a miniport driver is loaded and its HwInitialize routine isrun, the miniport's StartIo routine, described in Section A.3, is called to remap any accessrange of video memory that the miniport driver makes visible to its corresponding displaydriver.

Page 298: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-9

A.1.2 HwRegistryCallback Routines

As mentioned in Section A.1.1, a miniport driver's HwFindAdapter routine can request thatconfiguration information be retrieved from the registry by calling either (or both)VideoPortGetDeviceData or VideoPortGetRegistryParameters with a driver-suppliedHwRegistryCallback routine.

When the system bootstrap loader runs, as much information as possible is stored in theregistry about the I/O buses and peripherals that can be found in the machine. A videominiport driver can attempt to find an adapter it supports by callingVideoPortGetDeviceData with a driver-supplied HwQueryDeviceCallback routine, a pointerto the miniport's device extension, and a pointer to any driver-supplied context data (whichcan be the VIDEO_PORT_CONFIG_INFO), requesting one of the following types of data: ■ VpBusData for bus-type-specific configuration data if the AdapterInterfaceType in the

VIDEO_PORT_CONFIG_INFO was set to Eisa or MicroChannel ■ VpControllerData for bus-relative configuration information about a video adapter on

the I/O bus ■ VpMonitorData for configuration information about the video monitor in ARC-

compliant (MIPS-based) machines

VideoPortGetDeviceData calls the HwQueryDeviceCallback routine with pointers to theinput device extension and context data, and with a pointer to aCM_FULL_RESOURCE_DESCRIPTOR-type buffer containing the requested information ifany is available in the registry. For requested VpControllerData, this information can includethe following: ■ An inclusive but bus-relative range of physical Port addresses, which the driver can set

in the AccessRanges array of the VIDEO_PORT_CONFIG_INFO buffer ■ An inclusive but bus-relative range of physical Memory addresses, which the driver can

set in the AccessRanges array of the VIDEO_PORT_CONFIG_INFO buffer ■ A bus-relative Interrupt vector and Irql, which the driver can set in the

VIDEO_PORT_CONFIG_INFO BusInterruptVector and BusInterruptLevel fields ifits adapter generates interrupts

Note that the bus-relative addresses stored in the VIDEO_PORT_CONFIG_INFOAccessRanges array must be mapped to logical address ranges by callingVideoPortGetDeviceBase before they are stored in the miniport's device extension and beforethey are passed to a VideoPortReadXxx or VideoPortWriteXxx routine to communicate withthe device.

Page 299: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-10 Kernel-mode Driver Design Guide

As an alternative or in addition, the miniport driver might get configuration data it needs forits adapter from the registry key ..\CurrentControlSet\Services\DriverName\DeviceNumberby calling VideoPortGetRegistryParameters.

A HwFindAdapter routine calls VideoPortGetRegistryParameters with a driver-suppliedHwQueryNamedValueCallback routine, pointers to the device extension and any driver-supplied context data (which can be the VIDEO_PORT_CONFIG_INFO buffer), and apointer to a Unicode string naming the value to be retrieved from the registry.

A HwFindAdapter or HwInitialize routine can set the IsFileNameParameter argument toTRUE if the driver-supplied Unicode string is a named registry entry whose value is a filename.

When VideoPortGetRegistryParameters calls the HwQueryNamedValueCallback routine, itprocesses whatever configuration data is made available. For example, aHwQueryNamedValueCallback routine might use retrieved data to set up the miniport's accessranges, emulator access ranges (if any), interrupt vector or interrupt IRQL (if any), and soforth in the VIDEO_PORT_CONFIG_INFO buffer, as well as in the miniport's deviceextension, before returning control to the HwFindAdapter routine (see Section A.1.1).

A miniport's HwInitialize routine, described in Section A.2, next, also might callVideoPortGetRegistryParameters with IsFileNameParameter set to TRUE so that aHwQueryNamedValueCallback routine could use the buffered contents of a named file to setup microcode on the adapter.

Each of the preceding VideoPortGetXxx routines retrieves information from a different key inthe registry. Consequently, each requires the miniport driver to supply a system-definedHwRegistryCallback routine with different formal parameters. For more detailed informationabout parameters for the HwQueryDeviceCallback and HwQueryNamedValueCallbackroutines, see the Kernel-mode Driver Reference.

A miniport driver can call VideoPortSetRegistryParameters to store information, such asinformation about the monitor, in the registry under the key..\CurrentControlSet\Services\DriverName\DeviceNumber, although few miniport driversuse the configuration registry in this manner. To retrieve the information from the registrylater, the miniport calls VideoPortGetRegistryParameters.

Note that the registry information made available by calls to VideoPortGetDeviceData andVideoPortGetRegistryParameters is volatile: that is, it must be re-created every time thesystem is loaded. Note also that the NT port driver makes this information available only tovideo miniport drivers. A miniport driver cannot call VideoPortSetRegistryParameters topass configuration information to a corresponding Win32 display driver.

Page 300: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-11

A.2 HwInitialize Routine

For each adapter found by the miniport driver, its HwInitialize routine is called when thecorresponding Win32 display driver is loaded. The HwInitialize routine can initialize softwarestate information, but it should not set up visible state on the adapter.

However, a miniport's HwInitialize routine can carry out a one-time initialization operation onthe adapter that was postponed by its HwFindAdapter routine. For example, a miniport mightpostpone loading microcode on the adapter and have the HwInitialize routine callVideoPortGetRegistryParameters, as described in Section A.1.2.

When the HwInitialize routine returns control, the corresponding Win32 display driver has ahandle for the miniport's adapter. The display driver then can call the Win32 DeviceIoControlfunction to request access to video memory. When the miniport's StartIo routine (see SectionA.3, next) has returned the mapped user-mode address range(s), the display driver can outputgraphics directly to video memory. (Usually, a display driver controls the display the end usersees, except occasionally when a full-screen MS-DOS application is run in an x86-basedmachine.)

A.3 HwStartIo Routine

As its name suggests, a HwStartIo routine is the entry point for incoming video requests forthe miniport driver to process on the adapter. HwStartIo is called with pointers to a videorequest packet (VRP) and to the miniport's device extension for adapter state.

After the corresponding Win32 display driver has a handle for the miniport's adapter, all of theVRPs sent to the HwStartIo routine have the IoControlCode field set to anIOCTL_VIDEO_xxx. On receipt of such a request, the StartIo routine does the following: ■ Determines what operation is requested by switching on the IoControlCode value ■ Checks that the VRP InputBufferLength and/or OutputBufferLength indicates a

buffer that is large enough to satisfy the request and returns an error if either buffer is toosmall

■ Either calls an internal routine to satisfy the request or satisfies it in the HwStartIoroutine

■ Sets the Status and Information fields in the VRP and returns control

The NT video port driver sets up VRPs and serializes calls to the HwStartIo routine, which"owns" each input VRP until the miniport driver completes the requested operation andreturns control. Every HwStartIo routine must set status in each VRP and return a BooleanTRUE, indicating that each input request was processed.

Page 301: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-12 Kernel-mode Driver Design Guide

All video miniport drivers are required to support the following requests: ■ IOCTL_VIDEO_QUERY_NUM_AVAIL_MODES ■ IOCTL_VIDEO_QUERY_AVAIL_MODES ■ IOCTL_VIDEO_QUERY_CURRENT_MODE ■ IOCTL_VIDEO_SET_CURRENT_MODE ■ IOCTL_VIDEO_RESET_DEVICE ■ IOCTL_VIDEO_MAP_VIDEO_MEMORY ■ IOCTL_VIDEO_UNMAP_VIDEO_MEMORY

//Beta2 sample miniports don't support the next two, but will in Product1; theserequest mapping/unmapping of adapter registers to user-mode space for thedisplay driver.//

■ IOCTL_VIDEO_QUERY_PUBLIC_ACCESS_RANGES ■ IOCTL_VIDEO_FREE_PUBLIC_ACCESS_RANGES

Depending on the adapter's features, video miniport drivers can support the followingadditional requests: ■ IOCTL_QUERY_COLOR_CAPABILITIES ■ IOCTL_VIDEO_SET_COLOR_REGISTERS (required if the device has a palette) ■ IOCTL_VIDEO_DISABLE_POINTER ■ IOCTL_VIDEO_ENABLE_POINTER ■ IOCTL_VIDEO_QUERY_POINTER_CAPABILITIES ■ IOCTL_VIDEO_QUERY_POINTER_ATTR ■ IOCTL_VIDEO_SET_POINTER_ATTR ■ IOCTL_VIDEO_QUERY_POINTER_POSITION ■ IOCTL_VIDEO_SET_POINTER_POSITION

Page 302: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-13

SVGA miniport drivers are required to support the following additional requests: ■ IOCTL_VIDEO_SAVE_HW_STATE ■ IOCTL_VIDEO_RESTORE_HW_STATE ■ IOCTL_VIDEO_DISABLE_CURSOR ■ IOCTL_VIDEO_ENABLE_CURSOR ■ IOCTL_VIDEO_QUERY_CURSOR_ATTR ■ IOCTL_VIDEO_SET_CURSOR_ATTR ■ IOCTL_VIDEO_QUERY_CURSOR_POSITION ■ IOCTL_VIDEO_SET_CURSOR_POSITION ■ IOCTL_VIDEO_GET_BANK_SELECT_CODE ■ IOCTL_VIDEO_SET_PALATTE_REGISTERS ■ IOCTL_VIDEO_LOAD_AND_SET_FONT

Depending on the adapter's features, an SVGA miniport driver also can support the followingrequests: ■ IOCTL_VIDEO_QUERY_NUM_AVAIL_FONTS ■ IOCTL_VIDEO_QUERY_AVAIL_FONTS ■ IOCTL_VIDEO_QUERY_CURRENT_FONT ■ IOCTL_VIDEO_SET_CURRENT_FONT

Every HwStartIo routine also must handle the receipt of an unsupported IOCTL_VIDEO_xxx,as follows: ■ Set the input VRP's Status field to ERROR_INVALID_FUNCTION. ■ Set the input VRP's Information field to zero. ■ Return TRUE to indicate the request was processed.

A miniport driver also might define one or more private I/O control codes for itscorresponding display driver. For example, two of the system-supplied miniport drivers'private I/O control codes are IOCTL_VIDEO_XGA_MAP_COPROCESSOR andIOCTL_VIDEO_QUERY_JAGUAR.

However, only a written-together display-and-miniport driver pair can use privately definedI/O control codes. That is, a miniport driver designed to run under an existing display drivershould not define private I/O control codes because the existing display driver cannot makenew I/O control requests without being rewritten (and, possibly, without breaking existingminiport drivers it already uses). An existing or generic display driver layered over manydifferent models of adapters, such as SVGA adapters, also cannot rely on a privately definedI/O control code to have the same effects in every underlying miniport driver.

For more information about VRPs, the system-defined IOCTL_VIDEO_xxx values, anddefining private I/O control codes, see the Kernel-mode Driver Reference.

Page 303: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-14 Kernel-mode Driver Design Guide

A.4 HwInterrupt Routine

On entry, a HwInterrupt routine should determine if its adapter actually generated an interrupt.The HwInterrupt routine must return FALSE as soon as possible if it is called for a spuriousinterrupt. Then, the interrupt service routine for the device that generated the interrupt can becalled quickly.

Otherwise, a miniport's HwInterrupt routine is generally responsible for completing the I/Ooperation that caused the interrupt. Depending on the HBA and the design of the miniport, aHwInterrupt routine does the following: ■ Dismisses the interrupt on the adapter (required) ■ Possibly, completes the requested operation that caused the interrupt ■ Returns control as quickly as possible (required)

A HwInterrupt routine cannot call certain VideoPortXxx without bringing down the system.However, a HwInterrupt routine can call the following routines safely: ■ VideoPortReadXxx and VideoPortWriteXxx ■ VideoPortMoveMemory and VideoPortZeroMemory ■ VideoPortStallExecution (for no more than a few microseconds) ■ VideoPortDisableInterrupt and VideoPortEnableInterrupt

Note that the HwInterrupt routine might access the miniport's device extension. Depending onthe design of the miniport, it might be impossible for other driver routines to share the deviceextension with the HwInterrupt routine safely in SMP machines. For example, suppose theminiport's HwStartIo routine is accessing the device extension when the adapter interrupts, theHwInterrupt routine is run on another processor, and the HwInterrupt routine also accesses thedevice extension. If such a situation might occur, the miniport's HwStartIo routine should callVideoPortSynchronizeExecution with a driver-supplied HwSynchronizeExecutionCallbackroutine, described in Section A.5, next.

Page 304: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-15

A.5 HwSynchronizeExecutionCallback Routines

Miniport drivers for adapters that do not generate interrupts seldom callVideoPortSynchronizeExecution with a HwSynchronizeExecutionCallback routine. In fact,even miniport drivers that have a HwInterrupt routine do not necessarily have aHwSynchronizeExecutionCallback routine. Since the NT video port driver does not send arequest to a miniport's HwStartIo routine until it completes processing of the precedingrequest (see Section A.3), miniport drivers rarely call VideoPortSynchronizeExecution.

There are two possible uses for a miniport's HwSynchronizeExecutionCallback routine: 1 To access the adapter registers using the miniport's device extension for a driver routine

other than the HwInterrupt routine When the HwSynchronizeExecutionCallback routine is given control, interrupts from the

adapter are masked off so the miniport's HwInterrupt routine cannot change state in thedevice extension while the HwSynchronizeExecutionCallback routine is running in anSMP machine.

2 To write commands to the adapter registers or ports very quickly if the adapter requires it When the HwSynchronizeExecutionCallback routine is given control, almost all system

interrupts are masked off, so the HwSynchronizeExecutionCallback routine cannot bepreempted by a device (or even, a clock) interrupt.

Consequently, a HwSynchronizeExecutionCallback routine must return control as quickly aspossible.

With the first type of HwSynchronizeExecutionCallback routine, the miniport callsVideoPortSynchronizeExecution with the Priority set to VpMediumPriority. With thesecond type of HwSynchronizeExecutionCallback routine, the miniport also makes this callwith the Priority set to VpMediumPriority if the driver has no HwInterrupt routine.Otherwise, such a miniport makes this call with the Priority set to VpHighPriority.

In general, a miniport driver should not call VideoPortSynchronizeExecution with thesecond type of HwSynchronizeExecutionCallback routine unless the driver designer has noother alternative. That is, the adapter is such that it must be programmed with systeminterrupts masked off or the miniport can call VideoPortSynchronizeExecution with thePriority set to VpLowPriority.

A HwSynchronizeExecutionCallback routine, like a HwInterrupt routine, cannot call certainVideoPortXxx routines without bringing down the system. For a summary of VideoPortXxxthat the HwSynchronizeExectionCallback routine can call safely, see Section A.4.

Page 305: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-16 Kernel-mode Driver Design Guide

A.6 SvgaHwIoPortXxx Routines

Miniport drivers for SVGA adapters in x86-based machines must have a set ofSvgaHwIoPortXxx routines to support full-screen MS-DOS applications as the system-supplied VGA miniport driver does. The designer of a new SVGA miniport driver shouldadapt the VGA miniport's SvgaHwIoPortXxx routines to the SVGA adapter's features. Asmentioned in the introduction for this appendix, miniport drivers for other types of adapters inx86-based machines can have a set of SvgaHwIoPortXxx and provide the same support, butthey are not required to do so.

Each MS-DOS application (and any 16-bit Windows application) runs as a Windows NTvirtual dos machine (VDM), which in turn, runs as a Console Manager application in theWin32 protected subsystem. A kernel-mode component called the V86 emulator traps on I/Oassembly instructions, issued by MS-DOS applications, that attempt to access the videoadapter. As long as such an application runs within a window, its attempts to access videoports are trapped and reflected back to the system-supplied video virtual device driver (VDD),which emulates the behavior of the adapter for the application. In other words, the Win32display driver retains control of the the video adapter while a VDM runs in a window.

For performance reasons, when the user switches such an MS-DOS application to full-screenmode in an x86-based machine, the display driver yields control of the adapter. The SVGAminiport driver allows some of the application's IN and OUT instructions for video I/O portsto go directly through the V86 emulator to the SVGA (or VGA) SvgaHwIoPortXxx routines.In effect, the miniport's SvgaHwIoPortXxx routines hook certain I/O ports so the V86 emulatorwill forward application-issued instructions to the corresponding SvgaHwIoPortXxx routinefor validation.

To prevent such a full-screen application from issuing a sequence of instructions that mighthang the machine, the SvgaHwIoPortXxx routines must monitor the application instructionstream to a driver-determined set of adapter registers. An SVGA miniport driver must enabledirect access only to ports that are completely safe. For example, ports for the sequencer andmiscellaneous output registers should always be trapped by the V86 emulator and sent to theminiport-supplied SvgaHwIoPortXxx routines for validation.

Direct access to I/O ports for the application is determined by the IOPM (named for the x86I/O permission map) that an SVGA miniport sets by callingVideoPortSetTrappedEmulatorPorts. Note that the miniport can adjust the IOPM by callingthis routine to have access ranges, describing I/O ports, trapped to an SvgaHwIoPortXxx orreleased for direct access by the application. The current IOPM determines which ports can beaccessed directly by the application and which remain trapped by the V86 emulator and sentto an SvgaHwIoPortXxx routine for validation. The default IOPM granted to a full-screenVDM normally includes all video adapter registers except the VGA (or SVGA) sequencerregisters and the miscellaneous output register.

Page 306: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-17

A.6.1 SVGA Miniport HwFindAdapter Routine

An SVGA miniport's HwFindAdapter routine (or HwRegistryCallback) must set up thefollowing in the VIDEO_PORT_CONFIG_INFO buffer: ■ NumEmulatorAccessEntries, indicating the number of entries in the

EmulatorAccessEntries array ■ EmulatorAccessEntries, containing the given number of

EMULATOR_ACCESS_ENTRY-structured elements, each describing a range of I/Oports the miniport hooks from the V86 emulator

Each entry includes a starting I/O address, a range length, the size of access to be trapped(UCHAR, USHORT, or ULONG), whether the miniport supports input or output ofstring data through the I/O port(s), and the miniport-supplied SvgaHwIoPortXxx thatactually validates and, possibly, transfers the data. Each SvgaHwIoPortXxx routinehandles either read (IN) or write (OUT) transfers of UCHAR-, USHORT-, or ULONG-sized data.

■ HardwareStateSize, describing the minimum number of bytes required to store thehardware state for the adapter in response to anIOCTL_VIDEO_SAVE_HARDWARE_STATE request

When the user switches a full-screen MS-DOS application to run in a window, theminiport driver must save the adapter state before the Win32 display driver regainscontrol of the video adapter. Note that an SVGA miniport driver also must support thereciprocal IOCTL_VIDEO_RESTORE_HARDWARE_STATE request because the usermight switch the windowed application back to full-screen mode.

■ VdmPhysicalVideoMemoryAddress and VdmPhysicalVideoMemoryLength,describing a range of video memory that must be mapped into the VDM address space tosupport BIOS INT10 calls from MS-DOS applications

The miniport driver can call the VideoPortInt10 routine when such an applicationchanges the video mode to one that the miniport driver's adapter can support.

Note that a miniport's emulator access entries specify subsets of its access ranges (usually allI/O ports in its access ranges). Note also that the access ranges that define the current IOPM,which determines the I/O ports that are directly accessible to a full-screen MS-DOSapplication, also specify subsets of the miniport's emulator access entries.

For more detailed information about the VIDEO_ACCESS_RANGE andEMULATOR_ACCESS_ENTRY structures, the declaration of SvgaHwIoPortXxx routines,and VideoPortInt10, see the Kernel-mode Driver Reference.

Page 307: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

A-18 Kernel-mode Driver Design Guide

A.6.2 Validating Instructions in SvgaHwIoPortXxx Routines

As already mentioned in Section A.6, the default IOPM set for a range of I/O ports includesall SVGA registers except the sequencer registers and the miscellaneous output register, whichthe miniport hooks with its SvgaHwIoPortXxx routines. The sequencer registers controlinternal chip timing on VGA-compatible video adapters. If a full-screen MS-DOS applicationtouches other adapter registers during a synchronous reset, the machine can hang. Likewise, ifthe miscellaneous output register is set to select a nonexistent clock, the machine can hang.

Consequently, SVGA miniport drivers must ensure that full-screen MS-DOS applications donot issue instructions that cause the machine to hang. Each SVGA miniport driver must supplySvgaHwIoPortXxx routine that hook the I/O ports for the adapter sequencer registers andmiscellaneous output register. Each new SVGA miniport driver for an adapter with specialfeatures also must hook any I/O ports to which an application might send any instructionsequence that could hang the machine.

Whenever an application attempts to access the sequencer clock register, theSvgaHwIoPortXxx must change the IOPM in order to trap all instructions coming in during asynchronous reset. As soon as an application sends an instruction that affects the sequencer orattempts to write to the miscellaneous output register, the SvgaHwIoPortXxx should adjust theIOPM by calling VideoPortSetTrappedEmulatorPorts to disable direct access to all adapterregisters. The miniport-supplied SvgaHwIoPortXxx routines should buffer subsequent IN orOUT instructions until the synchronous reset is done, or until the application either restoresthe miscellaneous output register or resets it to a "safe" clock.

Then, the miniport driver is responsible for checking that the buffered instructions cannothang the machine. If not, the miniport should process the buffered instructions, usually bycalling VideoPortSynchronizeExecution with a driver-suppliedHwSynchronizeExectionCallback routine, described in Section A.5. Otherwise, the miniportdriver should discard the buffered instructions.

Page 308: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Video Miniport Drivers A-19

A.6.3 SVGA Miniport StartIo Routine

When the user switchs a full-screen MS-DOS application back to running in a window, theminiport's StartIo routine is sent a VRP with the I/O control codeIOCTL_VIDEO_SAVE_HW_STATE. The miniport must store the state of the adapter in casethe user switches the application to full-screen mode again.

Note that the miniport's SvgaHwIoPortXxx might have buffered a sequence of application INsor OUTs, as described in Section B.6.2, when its HwStartIo routine is called to save theadapter state. The miniport driver should save the current state, including the bufferedinstructions, so that its SvgaHwIoPortXxx can resume validation operations exactly where itleft off if the user switches the application to full-screen mode again.

When the miniport driver completes a save operation, the NT port driver automaticallydisables the current IOPM and the miniport's SvgaHwIoPortXxx routines. The port driverrestores them automatically if the application is switched to full-screen mode again.

Page 309: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Appendix BSCSI Drivers

This appendix provides guidelines for designing the following kinds of SCSI drivers: ■ A class driver for a new type of peripheral device ■ A filter driver for a peripheral device of a type that already has a class driver ■ A tape subclass driver for a vendor-specific tape device ■ A host bus adapter (HBA) miniport driver

Class and filter drivers for SCSI peripheral devices act as an interface between anyintermediate or highest-level drivers layered above the class or filter driver and the NT-supplied SCSI port driver. That is, SCSI class drivers translate the standard IRPs they get intoIRPs with system-defined SCSI request blocks (SRBs) before sending each IRP on to the NTSCSI port driver. Each SRB contains a SCSI-II command descriptor block (CDB), as well asfields that identify the target device for the requested operation.

In turn, the system-supplied, NT-dependent port driver passes SRBs with CDBs on to OS-independent, HBA-specific miniport drivers. The NT SCSI port driver acts as an interfacebetween all SCSI class drivers and all HBA-specific miniport drivers.

For more detailed information about SRBs, see the Kernel-mode Driver Reference. Fordevice-type-specific information about CDBs, consult the ANSI X3.131-199X SCSI-IIstandard. Note that the system-supplied SCSI drivers include the header file, scsi.h, whichcontains SCSI-II-compliant definitions for CDBs and other structures used by most SCSI-II- TMcompliant drivers. The Windows NT Device Driver Kit (DDK) supplies scsi.h to beincluded in or modified for new SCSI drivers.

Page 310: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-2 Kernel-mode Driver Design Guide

B.1 Designing a SCSI Class Driver

To the NT I/O Manager and all higher-level drivers, a SCSI class driver is a standard NTintermediate driver. It must supply a set of Dispatch routines to which the I/O Manager and/orhigher-level drivers can send IRPs for appropriate I/O operations. That is, a new class driver isrequired to support the same set of IRP_MJ_xxx as a nonSCSI driver for the same type ofdevice. For specific information about which requests drivers of the most common types ofdevices must handle, see the Kernel-mode Driver Reference.

The designer of a class driver for an entirely new type of SCSI device must determine anappropriate set of requests for the driver to support, depending on the nature of the device. Theset of requests to be supported generally includes at least IRP_MJ_CREATE (and, for somedevice types or for symmetry, IRP_MJ_CLOSE), IRP_MJ_DEVICE_CONTROL, andIRP_MJ_READ, IRP_MJ_WRITE, or both.

In addition, every class driver must have a DriverEntry routine and one or more IoCompletionroutines. A SCSI class driver also can have any other NT standard higher-level driver routine,such as an Unload routine, as necessary (see Chapter 4).

To the NT SCSI port driver, a SCSI class driver is a higher-level driver with built-in, SCSI-specific functionality. In general, every SCSI class driver is responsible for the following: ■ Connecting itself to the port driver so that requests can be routed to the correct target(s) ■ Creating device objects to represent its devices on each SCSI bus in the machine ■ Initializing the appropriate controllers (TIDs) and devices (LUs) on the SCSI bus(es) ■ Interpreting system I/O requests (IRPs) ■ Mapping IRPs to SCSI class/port interface requests (SRBs with SCSI-II CDBs) ■ Establishing time-out values for requests ■ Limiting the size of data transfers to suit the limits of the HBA ■ Handling error conditions that are not already handled by the port driver or the HBA,

such as check-condition status or bus resets

The following subsections describe how class drivers provide this SCSI-specific functionalityin the context of each standard routine that class drivers commonly have.

Note that the system-supplied class drivers include the header file class.h so they can call theScsiClassXxx routines provided by class.c. The Windows NT DDK provides class.h andclass.c as sample source code. However, class.c should not be considered a system-suppliedlibrary for all future SCSI class drivers; both class.h and class.c are subject to change withoutnotice.

Page 311: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-3

B.1.1 DriverEntry Routine

Like any other NT intermediate driver, the DriverEntry routine of a SCSI class driver mustdefine its Dispatch entry points in the input driver object. Then, the class driver must layeritself over the NT SCSI port driver by calling IoGetDeviceObjectPointer and must locate itsdevice(s), if any are present in the machine. The class driver's initial call toIoGetDeviceObjectPointer returns a pointer to a generic device object created by the NT portdriver, rather than a pointer to a device object that represents a particular HBA. The classdriver uses this generic device object pointer to find its device(s) on each bus driven by one ormore HBAs in the machine.

B.1.1.1 GetInquiryData Routines

To find its device(s), the class driver either calls a GetInquiryData routine or implements thesame functionality in its DriverEntry routine. A GetInquiryData routine allocates anINQUIRY_DATA_SIZE buffer for SCSI_ADAPTER_BUS_INFO-type data. Next, itallocates an IRP with IoBuildDeviceIoControlRequest, sets the port driver's I/O stacklocation up with the I/O control code IOCTL_SCSI_GET_INQUIRY_DATA, and passes theIRP on to the NT port driver with IoCallDriver.

The class driver uses the InquiryDataOffset in the SCSI_ADAPTER_BUS_INFO buffer toinspect the SCSI inquiry data and find device(s) of its type. For more information about theSCSI_ADAPTER_BUS_INFO structure and IOCTLs supported by the NT SCSI port driver,see the Kernel-mode Driver Reference.

Like any other NT driver, a SCSI class driver must set up a device object to represent eachphysical, logical, and/or virtual device for which it handles I/O requests. The class driver alsomust establish a claim on each of the physical devices for which it creates a device object.Subsequently loaded drivers for the same type of device can determine whether a particulardevice has already been claimed by inspecting the inquiry data for each HBA and SCSI bus inthe machine. A class driver must not attempt to claim an already claimed device.

Page 312: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-4 Kernel-mode Driver Design Guide

B.1.1.2 ClaimDevice Routines

To claim a device, a class driver either calls a ClaimDevice routine or implements the samefunctionality in its DriverEntry routine. A ClaimDevice routine sets up an SRB for the targetdevice (designated by PathId, TargetId, and Lun in the SRB) with the Function valueSRB_FUNCTION_CLAIM_DEVICE. (Such a ClaimDevice routine can serve double duty asa routine to be called from a class driver's Unload or Shutdown routine by also handling SRBswith the Function value SRB_FUNCTION_RELEASE_DEVICE.)

The ClaimDevice routine allocates an IRP with IoBuildDeviceIoControlRequest, setting upthe port driver's I/O stack location with the I/O control codeIOCTL_SCSI_EXECUTE_NONE and a pointer to the SRB at Parameters.Scsi.Srb.ClaimDevice sets up an event object so it can wait on the completion of the IRP and sends theIRP on to the NT port driver with IoCallDriver. On return from a successful call to the NTport driver, the ClaimDevice routine checks the SRB's DataBuffer for a nonNULL pointer tothe NT port driver's device object for the HBA.

The class driver must store such a returned pointer in the device extension of its own deviceobject that represents the newly claimed device. The class driver must use this pointer to theport driver's device object for the HBA in all subsequent requests the class driver sends downto the port-miniport driver pair. Consequently, the ClaimDevice routine must callObReferenceObjectByPointer with the pointer returned in the SRB's DataBuffer, therebybumping the reference count for this pointer so it cannot be deleted when ClaimDevice returnscontrol to the DriverEntry routine. If the driver can be unloaded, its DispatchUnload (orClaimDevice) routine must dereference this pointer by calling ObDereferenceObject.

Page 313: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-5

B.1.1.3 Setting up Device Extensions

In the device extension of each device object, the class driver provides storage for whateverdriver-determined data it uses to manage I/O requests for the device, such as the pointer to theNT port driver's HBA-specific device object, a back pointer to its own device object, and soforth. Most class drivers also provide storage for the following SCSI-specific information: ■ A device-type-specific time-out value The class driver can pass the time-out value in SRBs it sends to the port driver, which

optionally times requests on behalf of each class driver. The port driver returns an SRBwith its SrbStatus field set to SRB_STATUS_TIMEOUT if the interval between whenthe port driver sends the request to the miniport driver and when the request completesexceeds the specified time-out value.

■ A pointer to the class driver's error-handling routine See Section B.1.3 for more information about error-handling in class drivers. ■ A count that the driver maintains of SCSI protocol errors on the device ■ The PathId value This identifies the SCSI bus on which the device is attached and is a required value in

SRBs. ■ The TargetId value This identifies the TID and is a required value in SRBs. ■ The Lun value This identifies the logical unit for the device and is a required value in SRBs. ■ A pointer to a driver-allocated buffer, which must be allocated from nonpaged pool, for

SCSI request-sense data ■ A driver-determined default value for SrbFlags that the driver sets in SRBs ■ A pointer to a zone header if the driver sets up a zone buffer for the SRBs it allocates If the driver calls the ExInterlocked..Zone routines for its SRBs, it must also provide

storage for and initialize a spin lock that protects the zone's free list. ■ A spin lock used to guarantee multiprocessor-safe access to a count of driver-allocated

IRPs for partial transfer requests ■ A pointer to the IO_SCSI_CAPABILITIES-type data that the port driver collected from

the miniport driver of the HBA How class drivers get and use this data is described in Section B.1.1.4, next.

Page 314: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-6 Kernel-mode Driver Design Guide

Note that the class driver's original call to IoGetDeviceObjectPointer creates a reference tothe handle of the file object associated with the port driver's generic device object. The classdriver must call ObDereferenceObject to release this reference after the driver has created allits device objects. A class driver cannot send requests through the NT port driver to its devicewithout using the port driver's HBA-specific device object pointer that was stored in thedevice extension by the ClaimDevice routine.

B.1.1.4 GetCapabilities Routines

For data transfer operations, SCSI class drivers need configuration information about eachHBA driving a bus to which their devices are attached. Consequently, a class driver eithercalls a GetCapabilities routine or implements the same functionality in the DriverEntryroutine.

A GetCapabilities routine builds and sets up an IRP_MJ_DEVICE_CONTROL request withthe I/O control code IOCTL_SCSI_GET_CAPABILITIES for the port driver. The class driverinspects the returned IO_SCSI_CAPABILITIES-type information to determine the following: ■ The maximum number of bytes a particular HBA can transfer in a single operation ■ If the HBA can transfer buffered data backed by discontiguous physical pages, how many

discontiguities per buffer it can manage ■ The HBA's alignment requirements for transfers so the class driver can properly set the

AlignmentRequirement field in its device objects (see Chapter 3) Applications that send IOCTL_SCSI_PASS_THROUGH requests also might use this

field. ■ Whether the HBA uses programmed I/O (PIO), and whether it requires data buffers

backed by physical memory to have their physical addresses be mapped to system virtualaddresses

■ Whether the HBA supports SCSI-II tagged queueing and/or per-logical-unit internalqueues (MultipleRequestsPerLu)

■ Whether the HBA supports asynchronous event notifications ■ Whether the HBA scans down

The class driver should store information about the HBA's transfer limits in the deviceextension so the driver's DispatchRead and/or DispatchWrite routines can split large transferrequests to suit the transfer capabilities of the HBA. For more information about class driverDispatch routines, see Section B.1.2.

For more information about general requirements for DriverEntry routines, see Chapter 5.

Page 315: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-7

B.1.2 Dispatch Routines

Class driver Dispatch routines for IRP_MJ_CREATE and IRP_MJ_CLOSE requests usuallyhave no SCSI-specific requirements. Most SCSI class drivers are intermediate drivers; theirDispatch routines just return STATUS_SUCCESS to indicate that a given device object existsso that higher-level drivers (or, indirectly, user-mode applications) can open the device for I/Oand close the device afterward.

A class driver must have a Dispatch routine for IRP_MJ_SHUTDOWN requests and, possiblyfor IRP_MJ_FLUSH_BUFFERS requests, if its device caches data internally or might beattached to a bus driven by an HBA that caches data internally. To maintain data integrity,such a cache should be flushed to the device before the system is shut down.

For all requests that require the NT port driver to execute a request on the SCSI bus, the classdriver must set up an IRP with an SRB containing a CDB. Consequently, most class drivershave one or more internal routines to build SRBs. For more information about BuildSrbroutines, see Section B.1.2.1.

Note that a class driver does not necessarily set up an SRB when it sets up anIRP_MJ_DEVICE_CONTROL request for an I/O control code supported by the NT portdriver, such as SCSI_IOCTL_GET_CAPABILITIES or SCSI_IOCTL_PASS_THROUGH.The class driver's Dispatch routine for device I/O control requests is responsible for thefollowing on receipt of a SCSI_IOCTL_PASS_THROUGH request: ■ Checking that the length of the user buffer at

Parameters.DeviceIoControl.InputBufferLength is at leastsizeof(SCSI_PASS_THROUGH) and failing the IRP withSTATUS_INVALID_PARAMETER if is not

■ Ensuring that certain fields in the SCSI_PASS_THROUGH structure contain correctvalues by setting the pointer to the structure itself to AssociatedIrp.SystemBuffer, andsetting its PathId, TargetId, and Lun fields to the corresponding values from thedriver's device extension

■ Setting up the port driver's I/O stack location as usual and also setting theMinorFunction field to IRP_MN_SCSI_CLASS, which marks the request as havingbeen processed by a SCSI class driver

If the NT port driver's I/O stack location for an IOCTL_SCSI_PASS_THROUGH requestdoes not have its MinorFunction field set with IRP_MN_SCSI_CLASS, the port driverassumes the request came directly from an application and that no class driver exists for thetarget device type. It is an application error to send such a request directly to the port driverfor a device that has been claimed by a class driver.

Page 316: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-8 Kernel-mode Driver Design Guide

SCSI class drivers also pass on IRP_MJ_INTERNAL_DEVICE_CONTROL requests to theNT port driver. Such a request can originate from a SCSI filter driver, described in SectionB.2. As for IOCTL_SCSI_PASS_THROUGH requests, the class driver is responsible forsetting the PathId, TargetId, and Lun fields in the SRB, and setting the MinorFunctioncode to IRP_MN_SCSI_CLASS in the port driver's I/O stack location before passing the IRPon to the NT port driver.

Every SCSI class driver is responsible for splitting up transfer (IRP_MJ_READ and/orIRP_MJ_WRITE) requests with an MDL describing a buffer that exceeds the HBA'scapabilities. Consequently, most class drivers also call an internal SplitTransferRequestroutine, described in Section B.1.2.2, or implement the same functionality in their Dispatchroutine(s) for read and write requests.

While it is possible for a class driver to set up internal queues for IRPs, it is seldom necessaryto do so and likely to degrade the driver's performance as well. As mentioned in Chapter 3, theNT SCSI port driver sets up and maintains LU-specific device queues for IRPs. Whether aparticular HBA supports SCSI-II tagged queueing or not, SCSI class drivers can send everyrequest to their devices as each IRP comes in and rely on the NT-supplied port driver or theHBA to handle queued requests expeditiously.

If an HBA supports tagged queueing, as indicated in the IO_SCSI_CAPABILITIES data, theclass driver uses the QueueAction field of the SRBs it creates to control how its requests arequeued.

When certain SCSI errors occur, the queue for a particular logical unit is frozen. For moreinformation about handling errors and releasing frozen request queues, see Sections B.1.3.1and B.1.3.3. For more information about general requirements for Dispatch routines, seeChapter 6.

B.1.2.1 BuildSrb Routines

Like all higher-level NT drivers, a SCSI class driver must set up the IRP stack location for thenext-lower-level driver. In IRPs that the class driver sets up with SRBs for the NT port driver,the port driver's I/O stack location is set with the following: ■ IRP_MJ_SCSI is the major function code. ■ Parameters.Scsi.Srb contains a pointer to the SRB.

Each class driver is responsible for allocating memory for SRBs as well as setting them upwith CDBs for the paired NT port and HBA miniport drivers. The class driver can either callExAllocatePool for nonpaged memory or can set up a nonpaged zone buffer for its SRBs. Formore information about using nonpaged pool and zones, see Chapter 16. Note that the classdriver is also responsible for freeing the memory it allocates for SRBs.

Page 317: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-9

A class driver's BuildSrb routine must set appropriate values in the SRB fields, including thelength of the CDB it has set up to communicate with its device. For requests that returnrequest-sense information and/or that the driver might need to retry, it sets an IoCompletionroutine in the IRP. For read or write requests, it ORs the SrbFlags with the appropriatetransfer direction, SRB_FLAGS_DATA_IN or SRB_FLAGS_DATA_OUT, respectively.

After the class driver has loaded, it sets up most SRBs with the Function field set toSRB_FUNCTION_EXECUTE, indicating a device I/O request to be sent over the bus. A classdriver can request that the port driver perform a SCSI-II request-sense operation when thetarget controller returns a check condition. To do this, the class driver sets up theSenseInfoBuffer pointer and SenseInfoBufferLength in the SRB, so the port driver canreturn the request-sense information if a check condition occurs, which it indicates in theSrbStatus field with SRB_STATUS_AUTOSENSE_VALID when it returns the IRP. Formore information about InterpretRequestSense routines, see Section B.1.3.1.

SCSI class drivers are responsible for retrying requests that fail due to target controller errors,bus resets, or request time-outs, so many class drivers maintain a retry count in their own I/Ostack location of the IRP. Such a class driver's BuildSrb routine also might initialize the retrylimit for a given request before it sends the IRP to the port driver. For more information aboutRetryRequest routines, see Section B.1.3.2.

A BuildSrb routine might split the responsibility for setting up an SRB with a pair ofSendSrbSynchronous and SendSrbAsynchronous routines. That is, the BuildSrb routine couldset up the SRB fields that are commonly set up for all requests, while the SendSrbXxx routineseach set SRB values pertinent only to each type of SCSI request. When an IRP is passed downto the port driver from a SendSrbAsynchronous routine, the IRP must be set up with a driver-supplied IoCompletion routine.

For more information about the system-defined SRB fields and their values, see the Kernel-mode Driver Reference.

B.1.2.2 SplitTransferRequest Routine

The IO_SCSI_CAPABILITIES-type data returned to the DriverEntry routine indicates thetransfer capabilities of a given HBA to the class driver. In particular, this data indicates theHBA's MaximumTransferLength in bytes and the HBA's MaximumPhysicalPages: that is,how many discontiguites in the physical memory backing a system buffer the HBA canmanage.

Most class drivers store a pointer to this configuration data in the device extension of eachdevice object because SCSI class drivers are responsible for splitting up all transfer requeststhat exceed the HBA's capability to transfer data. In other words, a class driver'sDispatchReadWrite routine must determine whether each IRP requests a transfer that is morethan the HBA can handle in a single operation.

Page 318: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-10 Kernel-mode Driver Design Guide

For example, such a DispatchReadWrite routine could have code similar to the following:

maxTransferLength = deviceExtension->PortCapabilities-> MaximumTransferLength;maxPhysicalPages = deviceExtension->PortCapabilities-> MaximumPhysicalPages;currentIrpStack = IoGetCurrentIrpStackLocation(Irp);transferLengthRequested = currentIrpStack-> Parameters.Read.Length; : ://// Calculate number of pages in this transfer//transferPages = ADDRESS_AND_SIZE_TO_SPAN_PAGES( MmGetMdlVirtualAddress(Irp->MdlAddress), transferLengthRequested);//// Check whether requested length is greater than maximum// HBA can transfer in a single operation//if (transferLengthRequested > maxTransferLength || transferPages > maxPhysicalPages) { transferPages = maxPhysicalPages - 1; if (maxTransferLength > transferPages << PAGE_SHIFT) { maxTransferLength = transferPages << PAGE_SHIFT; } IoMarkIrpPending(Irp); SplitTransferRequest(DeviceObject, Irp, maxTransferLength); return STATUS_PENDING} : :

Page 319: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-11

To carry out the original transfer request, the driver's SplitTransferRequest routine creates oneor more IRPs to handle sub-buffers that are sized to suit the HBA's capabilities. For each suchIRP, the SplitTransferRequest routine sets up an SRB, usually by calling a BuildSrb routine(see Section B.1.2.1) before sending the IRP on to the port driver.

Note that the Dispatch routine returns STATUS_PENDING immediately after a call toSplitTransferRequest with the original IRP. To track each piece of the transfer,SplitTransferRequest registers an IoCompletion routine for each driver-allocated IRP it sendsdown to the port driver. The IoCompletion routine maintains a count of completed partialtransfer requests in the original IRP and protects the count with a spin lock (see SectionB.1.1.3).

Note that such an IoCompletion routine must free any IRPs and/or SRBs the driver hasallocated and must complete the original IRP when all requested data has been transferred (oran error has occurred during a transfer operation).

B.1.3 IoCompletion Routines

As mentioned previously in Section B.1.2.1, SCSI class drivers are responsible for releasingthe memory they allocate for SRBs. Like any other higher-level NT driver, they are alsoresponsible for releasing any IRPs that they allocate, such as an IRP to split up a transferrequest as described in the preceding section. A class driver's IoCompletion routine isultimately responsible for ensuring that the I/O status block is set and completing the originalIRP. Note that completing an IRP can include translating an error returned in the SRB'sScsiStatus field (or SrbSenseInfoBuffer) into an NTSTATUS-type value and/or logging anerror, as described in Chapter 16.

SCSI class drivers must have one or more IoCompletion routines, unless a particular driverwaits on the completion of every IRP it sends to the port driver, retries requests as necessary,and releases memory for SRBs from a Dispatch or BuildSrb routine. Note that handling everyIRP as a synchronous request would degrade the performance of any class driver.

Furthermore, SCSI class drivers for devices that might hold the system page file must handleall transfer requests asynchronously. Such a class driver must have an IoCompletion routinefor read/write requests.

When certain kinds of errors occur in processing a request, the NT port driver freezes therequest queue for the target LU. Consequently, class drivers usually have internal routines tochange the status of the queue for their device I/O requests. For more information about classdrivers' ReleaseQueue routines, see Section B.1.3.1.

If the driver's BuildSrb routine requested that the port driver return request-sense informationfor a request, its IoCompletion routine either calls an InterpretRequestSense routine orimplements the same functionality inline. For more information about InterpretRequestSenseroutines, see Section B.1.3.2.

Page 320: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-12 Kernel-mode Driver Design Guide

As mentioned in Section B.1.2.1, SCSI class drivers are responsible for retrying requests thatfail due to target controller errors, bus resets, or request time-outs. When the port driverreturns a particular request with its SrbStatus set to indicate such an error, the driver can calla RetryRequest routine from its IoCompletion routine or, possibly, from theInterpretRequestSense routine. For more information about RetryRequest routines, see SectionB.1.3.3.

For more information about general requirements for IoCompletion routines, see Chapter 13.

B.1.3.1 ReleaseQueue Routines

Unless the class driver ORs the SrbFlags for a given request withSRB_FLAGS_NO_QUEUE_FREEZE, the port (and/or miniport) driver freezes a queue for agiven logical unit after any of the following: ■ A bus reset occurred while the logical unit was executing a request. ■ The logical unit returned SCSISTAT_CHECK_CONDITION or

SCSISTAT_COMMAND_TERMINATED, which the class driver can find in the SRB'sScsiStatus field.

■ A request was timed out. ■ A request was terminated by a bus message command such as SCSIMESS_ABORT.

The NT port driver indicates that the queue has been frozen by returning a request withSRB_STATUS_QUEUE_FROZEN in the SrbStatus field. New requests from the class drivercan be inserted into the queue, but no requests are sent to the logical unit except autosenserequests. Freezing the queue under these conditions gives the class driver an opportunity toanalyze an error before other queued jobs are executed. For example, a class driver might needto reinitialize the TID after the controller has been reset, or queued jobs might need to becancelled if the media has changed. To reinitialize a TID, the driver can send a request withthe SrbFlags ORed with SRB_FLAGS_BYPASS_FROZEN_QUEUE.

A ReleaseQueue routine allocates and sets up an IRP and an SRB to flush or release a frozenqueue. The Function field of the SRB must be set to SRB_FUNCTION_RELEASE_QUEUEor SRB_FUNCTION_FLUSH_QUEUE, which also releases a frozen queue and cancels allcurrently queued requests. The NT port driver completes all requests in a flushed queue withtheir SrbStatus fields set to SRB_STATUS_FLUSHED.

Note that a ReleaseQueue routine is called asynchronously, generally from an IoCompletionroutine. A class driver's IoCompletion routine cannot call ReleaseQueue to flush a queue thatis not frozen. However, it can call ReleaseQueue to release an unfrozen queue, and the portdriver simply ignores such a request.

A ReleaseQueue routine should allocate memory for an SRB by calling ExAllocatePool withthe memory type NonPagedPoolMustSucceed since failing to release a frozen queue makesthe device inaccessible.

Page 321: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-13

B.1.3.2 InterpretRequestSense Routines

An InterpretRequestSense routine interprets the data returned to the SrbSenseInfoBuffer,maps the error to an NTSTATUS value for the IRP's I/O status block, and determines whetherthe request should be retried. The NT port driver indicates whether request-sense informationis available by setting the SrbStatus field with SRB_STATUS_AUTOSENSE_VALID (orSRB_STATUS_REQUEST_SENSE_FAILED).

If no request-sense information is available, this routine should check the SrbStatus value todetermine an appropriate mapping to an NTSTATUS value and whether to retry a givenrequest. Note that an InterpretRequestSense routine might call a driver-supplied error-loggingroutine, as well. When the class driver logs an I/O error, it should include the PathId,TargetId, Lun, and SrbStatus values from the SRB, and, if possible, pertinent request-senseinformation as part of the error log entry's DumpData. For more information about loggingI/O errors, see Chapter 16.

B.1.3.3 RetryRequest Routines

The port-miniport driver pair is responsible for retrying requests if certain SCSI errors occur,including bus parity errors, selection time-outs, and target controller busy errors. If their retryattempts fail, the NT port driver completes the request with an appropriate error and logs theI/O error, as well. Class drivers should never attempt to retry a request that the port driver hasalready failed due to such an error.

However, class drivers are required to retry appropriate requests that fail due to targetcontroller errors, SCSI bus resets, or time-outs. In general, a RetryRequest routine canresubmit the request to the NT port driver and direct that the request be placed at the head ofthe queue.

In particular, a RetryRequest routine should do the following: ■ Ensure that a partial transfer request has the correct values set for the starting address and

length. ■ Zero the SrbStatus and ScsiStatus fields of the SRB. ■ Set up the SrbFlags field, as necessary for the device. ■ Set up the I/O stack location for the port driver in the IRP, as already described in Section

B.1.2.1. ■ Call IoSetCompletionRoutine for the IRP, since the driver's IoCompletion routine might

need to retry the request more than once. ■ Pass the request on to the port driver with IoCallDriver.

Page 322: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-14 Kernel-mode Driver Design Guide

B.2 Designing a SCSI Filter Driver

If a system-supplied SCSI class driver already exists for a particular type of device, it mightbe unnecessary to write a driver for a new device of the same type. Each NT-supplied SCSIclass driver was designed to support SCSI-II-compliant peripheral devices of a given type andwas tested against a number of vendors' devices. Thus, any system-supplied SCSI class drivermight provide all the support another SCSI-II-compliant device of its type needs.

If an existing SCSI class driver does not fully support a new device of its type, a new drivermust be written as either of the following: ■ A new SCSI class driver for the device, which, in most cases, is also unnecessary ■ Or, a SCSI filter driver layered over an existing NT SCSI class driver

A SCSI filter driver supplies device-specific support for requests that require special handling,such as translating data sent to (or returned from) the device in a nonstandard format orprogramming the device in response to an IRP_MJ_DEVICE_CONTROL request, while itcan let the appropriate NT SCSI class driver process all requests that require no specialhandling. Unless a new device requires that every request be handled in a device-specificmanner, writing an NT SCSI filter driver takes far less development time than writing a newclass driver.

Like any SCSI class driver, such a filter driver is a standard NT higher-level driver with thefollowing requirements common to all higher-level drivers: ■ It must supply a set of Dispatch routines to which the I/O Manager and/or still higher-

level drivers can send IRPs for appropriate I/O operations. A SCSI filter driver mustsupport the same set of IRP_MJ_xxx as the SCSI class driver for its type of device.

■ For IRP_MJ_DEVICE_CONTROL requests, it must support as many of the class-driver-supported I/O control codes as its physical device can handle, and, if possible, emulatesupport for any remaining I/O control codes in the driver.

■ It must have a DriverEntry routine and can have any other NT standard higher-leveldriver routine, such as Unload or IoCompletion routines, as necessary.

If its device has special features, a filter driver can support a set of driver-defined I/O controlcodes in addition to the required set of device-type-specific I/O control codes forIRP_MJ_DEVICE_CONTROL requests. For more information about required device-type-specific requests, including the IRP_MJ_DEVICE_CONTROL request and I/O control codes,see the Kernel-mode Driver Reference.

Page 323: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-15

Depending on the nature of its device, a filter driver also might (or must) be responsible forthe following SCSI-specific functionality: ■ Translating data from or into a device-specific format before or after sending a transfer

request to lower driver(s) if the device processes data in a nonstandard format ■ Setting up IRPs with SRBs for internal device control or pass-through requests, as

necessary for its device and sending those IRPs through the class driver (see SectionB.1.2) to the NT port driver

■ Establishing time-out values for requests ■ Supplying one or more IoCompletion routines and, like the class driver, handling certain

SCSI error conditions and retries for device-specific requests that require specialhandling

In general, a SCSI filter driver has the same responsibilities as a class driver for those requeststhat require device-specific handling. For a discussion of the functionality required of SCSIclass drivers, see Section B.1.

The following subsections describe how filter drivers provide this SCSI-specific functionalityin the context of each standard routine that a SCSI filter driver commonly has.

B.2.1 DriverEntry Routine

Like any other NT driver, the DriverEntry routine of a SCSI filter driver must define itsDispatch (and Unload, if any) entry points in the input driver object. Then, the filter drivermust create one or more device objects with IoCreateDevice, layer itself over the appropriateSCSI class driver with IoAttachDevice, and set up one or more requests to find its device(s),if any, on a SCSI bus in the machine.

A filter driver for certain kinds of devices might call IoGetConfigurationInformation todetermine how many devices of its type are already represented by named device objects. Thecount of named device objects in the I/O Manager's CONFIGURATION_INFORMATION-type structure places an upper bound on the number of device objects the filter driver mightneed to create. If this system-defined structure does not have a count for the driver's type ofdevice, the filter driver can examine the NT configuration registry for devices of its type. Formore information about using the I/O Manager's configuration information and/or the NTconfiguration registry, see Chapter 16.

Note that a configuration information count or the registry keys for a given type of device alsocould include device objects for nonSCSI devices and/or other hardware vendors' SCSIdevices of the filter driver's type. For more information about GetInquiryData routines, seeSection B.1.1.1. A SCSI filter driver's DriverEntry routine must determine whether anyparticular device object(s) created by another driver actually represent a SCSI device that thefilter driver supports.

Page 324: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-16 Kernel-mode Driver Design Guide

After the filter driver has layered itself over the SCSI class driver (or over another driver forits type of device), the DriverEntry routine must allocate a buffer and set up a request forSCSI-II inquiry data about the underlying device(s). On a successful return from this request,the filter driver must inspect the buffered inquiry data for the vendor name (and, possibly,model designation) to determine whether the device is one the filter driver supports. If so, thefilter driver should set up the device extension in its device object.

Note that every driver must call IoAttachDevice with a driver-created device object.Therefore, any SCSI filter driver must create a device object before it can determine whetherthe underlying device is one that it supports. Its DriverEntry routine must call IoDeleteDeviceto release any device objects it creates for unsupported devices. The DriverEntry routine alsomust free the memory it allocated for SCSI-II inquiry data before it returns control.

Assuming it finds a device it supports, the filter driver must save the pointer (returned fromIoAttachDevice) to the SCSI class driver's device object in its own device extension so thefilter driver can send IRPs on to the class driver. If the filter driver sends device-specificrequests through the class driver, it might need to set up SCSI-specific data in the deviceextension of its device object(s). In other words, such a filter driver's device extension shouldinclude any SCSI-specific data it needs to manage its device-specific requests.

For more information about setting up a higher-level driver's device extension with SCSI-specific data, see Section B.1.1.3. For more information about the general requirements forDriverEntry routines, see Chapter 5.

B.2.2 Dispatch Routines

Like any other higher-level NT driver, a SCSI filter driver must have one or more Dispatchroutines to handle every IRP_MJ_xxx request for which the underlying SCSI class driversupplies a Dispatch entry point. Depending on the nature of its device, the filter driver'sDispatch entry point for any given request might do either of the following: ■ For a request that requires no special handling, set up the I/O stack location in the IRP for

the next-lower-level class driver, possibly call IoSetCompletionRoutine to set up itsIoCompletion routine for the IRP, and pass the IRP on for further processing by lowerdrivers with IoCallDriver.

■ Or, set up a new IRP with an SRB and CDB for its device, call IoSetCompletionRoutineso the SRB (and the IRP if the driver calls IoAllocateIrp orIoBuildAsynchronousFsdRequest) can be freed, and pass the IRP on withIoCallDriver.

A SCSI filter driver is most likely to set up new IRPs with the major function codeIRP_MJ_INTERNAL_DEVICE_CONTROL.

For requests that require no special handling, a SCSI filter driver's Dispatch routine callsIoGetNextIrpStackLocation with an input IRP, sets up the class driver's I/O stack locationand, then, calls IoCallDriver with pointers to the class driver's device object and the IRP.

Page 325: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-17

For requests that require special handling, the filter driver can do the following: ■ Create a new IRP with IoBuildDeviceIoControlRequest, IoAllocateIrp,

IoBuildSynchronousFsdRequest, or IoBuildAsynchronousFsdRequest. ■ Check the returned IRP pointer for NULL and return

STATUS_INSUFFICIENT_RESOURCES if an IRP could not be allocated. ■ Call IoSetNextIrpStackLocation to get a pointer to its own I/O stack location in the

driver-created IRP and, possibly, set up it up with state to be used by its ownIoCompletion routine.

■ Call IoGetNextIrpStackLocation to get a pointer to the class driver's I/O stack locationin the driver-created IRP and set it up with the major function code IRP_MJ_SCSI and anSRB (see Section B.1.2.1).

■ Translate data to be transferred to the device into a device-specific, nonstandard format ifnecessary.

■ Call IoSetCompletionRoutine if the driver allocated any memory, such as memory foran SRB, SCSI request-sense buffer, MDL, and/or with a call to IoAllocateIrp orIoBuildAsynchronousFsdRequest, or if the driver must translate data transferred fromthe device in a device-specific, nonstandard format.

■ Pass the driver-created IRP to (and through) the class driver with IoCallDriver.

Like a SCSI class driver, a SCSI filter driver might have SCSI-specific BuildSrb orSplitTransferRequest routines to be called from the filter driver's Dispatch routine(s), or mightimplement the same functionality inline. A filter driver also might have a TranslateDataOutroutine if its device requires data to be written to the device in a nonstandard format. Such aTranslateDataOut routine would reformat the data in the buffer at Irp Mdl.Address or IrpSystemBuffer before the filter driver called the class driver with an IRP for a transfer to itsdevice.

For more information about BuildSrb and SplitTransferRequest routines, see Sections B.1.2.1and B.1.2.2. For more information about general requirements for Dispatch routines, seeChapter 6.

B.2.3 IoCompletion Routines

A SCSI filter driver's IoCompletion routine is called when lower (class and port) drivers havecalled IoCompleteRequest. The filter driver's IoCompletion routine should returnSTATUS_MORE_PROCESSING_REQUIRED to prevent completion processing of a driver-allocated IRP or the original IRP if the filter driver will reuse it before it completes theoriginal request. Like any other higher-level NT driver, a SCSI filter driver's IoCompletionroutine is responsible for calling IoFreeIrp to release any IRP the driver's Dispatch routine(s)created with IoAllocateIrp or IoBuildAsynchronousFsdRequest.

Page 326: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-18 Kernel-mode Driver Design Guide

Depending on its device, a SCSI filter driver might supply an IoCompletion routine for IRPs itsends to the class driver from its Dispatch routines. In particular, a device that retrieves andprocesses data in a nonstandard format might require a SCSI filter driver to have aTranslateDataIn routine called from its IoCompletion routine for transfer requests from such adevice to system memory. Note that such a TranslateDataIn routine would be called at IRQLDISPATCH_LEVEL and in an arbitrary thread context. Therefore, the buffer into which thedriver returns data either must be located in nonpaged pool or must be locked down andaccessible using mapped, nonpaged system-space virtual addresses. For more informationabout accessing user-supplied buffers at raised IRQL safely, see Chapter 16.

In general, a SCSI filter driver should supply an IoCompletion routine with the samefunctionality as a class driver's IoCompletion routine for IRPs that the filter driver sets up withSRBs and CDBs. Consequently, a SCSI filter driver might have any or all of theReleaseQueue, InterpretRequestSense, or RetryRequest routines that can be called from aSCSI class driver's IoCompletion routine(s). For more information aboutInterpretRequestSense, RetryRequest, and ReleaseQueue routines, see Section B.1.3. Formore information about general requirements for IoCompletion routines, see Chapter 13.

B.3 Designing a SCSI Tape Subclass Driver

NT SCSI class drivers for tape devices have two pieces: 1 The system-supplied generic tape driver, tape.c, which provides functionality common to

all SCSI tape drivers 2 A device-specific tape subclass driver, which supports a particular type of tape device

and which is linked with the generic tape driver

Each of these pieces has specific responsibilities. The generic tape driver is responsible for thefollowing: ■ Providing a DriverEntry routine that does everything a SCSI class driver must to be

loaded (see Section B.1.1) ■ Providing a set of Dispatch routines to accept I/O requests bound for the tape device from

the system (see Section B.1.2) ■ Enforcing the HBA's limits on large transfer requests, as necessary (see Section B.1.2.2) ■ Calling the subclass driver's system-defined TapeXxx routines to process incoming I/O

requests

Consequently, only a new tape subclass driver must be written to support a SCSI tape devicefor which the system does not already provide a driver.

Page 327: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-19

A tape subclass driver is generally responsible for the following: ■ Determining if a given device is of a type it can support ■ Building SRBs with CDBs for its device(s) ■ Implementing the following system-required set of TapeXxx routines: ■ TapeVerifyInquiry to return a Boolean indicating whether the input

SCSI_INQUIRY_DATA-type buffer contains VendorId and/or ProductId valuesfor a device the subclass driver supports

■ TapeReadWrite to set up an SRB with a CDB for a transfer from its device to thesystem or vice versa, to set up the NT port driver's I/O stack location in the inputIRP (see Sections B.1.2 and B.1.2.1), and to return control withSTATUS_PENDING

Note that the IRP input to a TapeReadWrite routine never requests a transfer toolarge for the underlying HBA because the generic tape driver's DispatchReadWriteroutine pre-splits large tranfer requests before it calls TapeReadWrite. The generictape driver's DispatchReadWrite routine also passes the IRP on to the NT port driverwith IoCallDriver.

■ TapeError to interpret returned request-sense information or status for a request,set appropriate status in the input IRP's I/O status block, and determine whether toretry the request

Note that TapeError is called from the generic tape driver's InterpretRequestSenseroutine (see Section B.1.3.2).

■ A set of TapeXxx routines, each to set up an SRB with a CDB for a particular (andrequired) device I/O control request, and, then, to send the request to the NT portdriver and return status about the requested operation

Note that the return status should be STATUS_VERIFY_REQUIRED if thesubclass driver detects (or suspects) that the media in the device might havechanged. (For more information about handling removable media, see Chapter 16.)

The system-supplied generic tape driver calls the ScsiClassXxx routines implemented inclass.c (see Section B.1) and included with the Windows NT DDK. A new tape subclassdriver also can call these routines, or it can implement the same functionality as aScsiClassXxx inline.

For more information about the required TapeXxx routines for subclass drivers or about therequired tape-specific I/O control requests, see the Kernel-mode Driver Reference.

Page 328: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-20 Kernel-mode Driver Design Guide

B.4 Designing a SCSI Miniport Driver

Windows NT SCSI miniport drivers are HBA-specific but OS-independent. That is, eachminiport driver links itself against the NT SCSI port driver, which is a dynamic-link library(DLL), and calls only the port driver's ScsiPortXxx routines to communicate with the system. TMIn future, HBA miniport drivers can be recompiled to run on machines running MS-DOS ,which will provide an OS-dependent port driver that also exports the ScsiPortXxx routines.

Every SCSI miniport driver must have at least the following system-defined routines: ■ DriverEntry to initialize the miniport driver ■ HwFindAdapter to determine how (or whether) driver-supported HBA(s) are configured

in the machine ■ HwInitialize to initialize the HBA(s) ■ HwStartIo to start operations on the HBA for an incoming request ■ HwResetBus to handle bus-reset requests ■ HwInterrupt to handle HBA-generated interrupts

Depending on the HBA and the driver designer, SCSI miniport drivers also can have any or allof the following system-defined routines: ■ HwDmaStarted to set up an HBA transfer after the system DMA controller has been

programmed by the NT port driver ■ HwDisableInterruptsCallback and HwEnableInterruptsCallback to handle deferred

interrupt-driven I/O processing if a particular operation takes a long time ■ HwTimer to time operations on the HBA that require long delays ■ HwAdapterState, defined for compatibility with future MS-DOS versions

The following subsections describe the requirements for and functionality of each of theseminiport driver routines. See the Kernel-mode Driver Reference for their formal definitionsand descriptions of their parameters.

Section B.4.8 describes error-handling requirements for SCSI miniport drivers.

Page 329: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-21

B.4.1 DriverEntry Routine

A DriverEntry routine is the initial entry point for each NT driver, including each SCSIminiport driver. A miniport's DriverEntry routine is called with two input arguments (of typePVOID) and must do the following: 1 Initialize a HW_INITIALIZATION_DATA structure (on the stack) with zeros. 2 Set driver-specific and HBA-specific values in the HW_INITIALIZATION_DATA

fields, including the miniport driver's entry points. The following entry points must beset:

■ HwFindAdapter ■ HwInitialize ■ HwStartIo ■ HwResetBus ■ HwInterrupt The following entry points can be set to a driver-supplied routine or must be set to

NULL: ■ HwDmaStarted ■ HwAdapterState (NULL) 3 Set up any driver-determined context data that the miniport's HwFindAdapter routine will

need. 4 Call ScsiPortInitialize with the pointers that were input to the DriverEntry routine, the

address of the filled-in HW_INITIALIZATION_DATA, and the address of the contextdata, if any.

A miniport driver can have a set of bus-type-specific HwFindAdapter routines if its HBA canbe connected on various types of I/O bus. Such a miniport driver must modify theAdapterInterfaceType and HwFindAdapter fields in the HW_INITIALIZATION_DATAafter each call to ScsiPortInitialize, possibly modify the context data for the new bus type,and call ScsiPortInitialize for each type of bus on which a supported HBA might beconnected.

ScsiPortInitialize checks the validity of the HW_INITIALIZATION_DATA, collects andstores pertinent information in the device extension of a device object that it creates torepresent the HBA, allocates memory for a PORT_CONFIGURATION_INFORMATION-type buffer, fills in as much configuration information as it can, and calls the miniport driver'sHwFindAdapter routine, described in Section B.4.1.1, next.

If the miniport driver sets a particular AdapterInterfaceType value in theHW_INITIALIZATION_DATA but there is no bus of that type in the machine, the NT portdriver returns STATUS_DEVICE_DOES_NOT_EXIST. It does not call the driver-suppliedHwFindAdapter routine for that bus type. The miniport driver does not remain loaded if itsHBA can be connected only to one or more types of bus that are not present in the machine.

Page 330: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-22 Kernel-mode Driver Design Guide

Note that ScsiPortInitialize is responsible for the following before it returns control to theminiport's DriverEntry routine: ■ Setting up all necessary system objects ■ Getting information from and setting information in the configuration registry ■ Allocating system resources on behalf of the miniport driver, including memory for the

miniport-specified DeviceExtensionSize, SpecificLuExtensionSize, and/orSrbExtensionSize in which the miniport driver can maintain HBA-specific state, per-logical-unit state, and/or per-request state, respectively

The miniport driver defines the internal structure and contents of its device extension, logicalunit extensions (if any), and SRB extensions (if any). A pointer to the device extension is aninput argument to every system-defined miniport driver routine except DriverEntry. ManyScsiPortXxx routines require this pointer as an argument.

When ScsiPortInitialize returns control, the DriverEntry routine propagates the return valueof ScsiPortInitialize. If the miniport driver calls ScsiPortInitialize more than once, itsDriverEntry routine should propagate the lowest return value.

ScsiPortInitialize can be called only from a miniport driver's DriverEntry routine. For moreinformation about the HW_INITIALIZATION_DATA structure and ScsiPortInitialize, seethe Kernel-mode Driver Reference.

B.4.1.1 HwFindAdapter Routines

The NT port driver fills in as much PORT_CONFIGURATION_INFORMATION as it canfrom the miniport's returned HW_INITIALIZATION_DATA and from the configurationregistry before calling a HwFindAdapter routine with a pointer to the configurationinformation buffer. In particular, the port driver always sets the following: ■ Length to sizeof(PORT_CONFIGURATION_INFORMATION) ■ AdapterInterfaceType to the miniport's HW_INITIALIZATION_DATA specification ■ InterruptMode to LevelSensitive if the AdapterInterfaceType is set to

MicroChannel; otherwise, set to Latched ■ AtdiskPrimaryClaimed to TRUE if an already loaded driver is using the I/O port range

0x1F0 to 0x1FF ■ AtdiskSecondaryClaimed to TRUE if an already loaded driver is using the I/O port

range 0x170 to 0x17F ■ NumberOfAccessRanges to the miniport's HW_INITIALIZATION_DATA

specification

Page 331: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-23

The NT port driver also attempts to fill in the following fields with values from theconfiguration registry: ■ SystemIoBusNumber set to zero if there is only one bus of a particular type in the

machine Otherwise, the miniport's HwFindAdapter routine can be called for each bus of the given

AdapterInterfaceType with an updated value for SystemIoBusNumber, or this valuecan be set to a value from the configuration registry if the system detects an HBA on aparticular bus.

■ AccessRanges elements, set with the RangeStart and RangeLength of each element, aswell as whether each element is a RangeInMemory

FALSE indicates a range of ports in I/O space. ■ BusInterruptLevel or BusInterruptVector ■ DmaChannel or DmaPort if the HBA uses system DMA If the HBA is a busmaster or uses PIO, these fields in the

PORT_CONFIGURATION_INFORMATION are irrelevant. ■ InitiatorBusId

A HwFindAdapter routine calls ScsiGetBusData and examines the returned bus-type-specificconfiguration information, such as POS data or EISA configuration data, for a supportedHBA. If the input PORT_CONFIGURATION_INFORMATION AccessRanges elementshave been filled in from the registry, the HwFindAdapter routine should use these addresses.

If HwFindAdapter cannot find an HBA it supports, it should set Again to FALSE and returnSP_RETURN_NOT_FOUND. If the HwFindAdapter routine finds a supported HBA but theinput PORT_CONFIGURATION_INFORMATION has invalid data (such as an invalidDmaChannel or BusInterruptLevel on an EISA bus), it should set Again to FALSE andreturn SP_RETURN_BAD_CONFIG.

Note that setting Again to FALSE and returning control with SP_RETURN_NOT_FOUND orSP_RETURN_BAD_CONFIG indicates that a given I/O bus, identified by theSystemIoBusNumber in the input PORT_CONFIGURATION_INFORMATION buffer, hasno HBAs connected that the miniport driver can support. It does not prevent the NT portdriver from calling HwFindAdapter again with updatedPORT_CONFIGURATION_INFORMATION to scan another I/O bus for HBA(s) if themachine has additional buses of the same AdapterInterfaceType.

When the HwFindAdapter routine finds an HBA it can support, this routine must fill inpertinent fields, as appropriate for its HBA and the given AdapterInterfaceType, in the inputPORT_CONFIGURATION_INFORMATION buffer. Every miniport driver must fill in theAccessRanges information, possibly calling ScsiPortGetDeviceBase first to map the basephysical address for the HBA to a system address, and converting each AccessRangeselement's RangeStart value with ScsiPortConvertUlongToPhysicalAddresses.

Page 332: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-24 Kernel-mode Driver Design Guide

The HwFindAdapter routine can set Again to TRUE to indicate it should be called again withexactly the same configuration information if another of its HBAs might be connected on thesame I/O bus. For a supported HBA, the HwFindAdapter routine returnsSP_RETURN_FOUND.

If the PORT_CONFIGURATION_INFORMATION buffer input to the HwFindAdapterroutine has no configuration information from the registry, HwFindAdapter can set driver-defined default values for AccessRanges elements, InterruptLevel or BusInterruptVector,DmaChannel or DmaPort if the HBA uses system DMA, and InitiatorBusId, and, then, callScsiPortGetBusData and examine the returned configuration information for a supportedHBA on the I/O bus.

Certain ScsiPortXxx routines can be called only from the miniport driver's HwFindAdapterroutine(s), in particular, the following: ■ ScsiPortGetBusData to get BUS_DATA_TYPE-specific configuration information ■ ScsiPortGetDeviceBase to map the (bus-relative) base physical address range for an

HBA to a system address range that the driver can use to communicate with the HBA bycalling the ScsiPortReadXxx and ScsiPortWriteXxx routines

■ ScsiPortFreeDeviceBase to release such a mapped range if HwFindAdapter does notfind an HBA it can support on a given I/O bus (as indicated by thePORT_CONFIGURATION_INFORMATION SystemIoBusNumber value)

■ ScsiPortGetUncachedExtension to allocate a DMA buffer shared between the systemand a busmaster HBA

For more information about the PORT_CONFIGURATION_INFORMATION structure andthese ScsiPortXxx routines, see the Kernel-mode Driver Reference.

When a miniport driver's HwFindAdapter routine returns control, ScsiPortInitialize returns tothe DriverEntry routine if the call(s) to HwFindAdapter indicated that the miniport drivercould not support an HBA. Otherwise, ScsiPortInitialize claims resources in theconfiguration registry, sets up necessary system objects (such as interrupt and adapter objects)on behalf of the miniport driver, and calls the miniport's HwInitialize routine, described inSection B.4.1.2, next.

Page 333: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-25

In addition to the values it sets in the PORT_CONFIGURATION_INFORMATION buffer,VideoPortInitialize also checks the registry for user-set values that disable any or all of thefollowing: ■ Synchronous transfers on the HBA The port driver ORs the default SrbFlags that it maintains for the HBA with

SRB_FLAGS_DISABLE_SYNCH_TRANSFER. ■ Bus-disconnect operations on the HBA The port driver ORs the default SrbFlags with

SRB_FLAGS_DISABLE_DISCONNECT. ■ Tagged queueing The port driver sets the TaggedQueuing Boolean that it maintains for the HBA to

FALSE. ■ Internal queueing of requests on the HBA The port driver sets the MultipleRequestPerLu Boolean that it maintains about the

HBA to FALSE.

If any of the immediately preceding "disable" values are in the registry, it overrides whateverthe HwFindAdapter routine sets in the PORT_CONFIGURATION_INFORMATION buffer.Note that temporarily disabling synchronous transfers, bus-disconnect operations, taggedqueueing, and HBA internal request queueing can simplify using a debugger to trace requesthandling by an under-development miniport driver.

Note also that the NT port driver uses the PORT_CONFIGURATION_INFORMATIONvalues provided by a miniport's HwFindAdapter routine and/or in the registry to fill in theIO_SCSI_CAPABILITIES data for use by SCSI class drivers, as described in Section B.1.1.4.

B.4.1.2 HwInitialize Routine

For each HBA found by the miniport driver, its HwInitialize routine is called to set up theHBA's registers and initial protocol state, if any.

If the HwInitialize routine enables interrupts on the HBA, the miniport's HwInterrupt routinewill be called to handle any interrupts the device generates during the initialization.

If initializing the HBA causes a bus reset, the HwInitialize routine must callScsiPortNotification with the NotificationType value ResetDetected.

When the HwInitialize routine returns control, ScsiPortInitialize sends SCSIOP_INQUIRYrequests to the miniport driver, collecting the SCSI-II INQUIRYDATA about the devices oneach bus and storing it for subsequent examination by SCSI class drivers, as described inSection B.1.1.1. Each request to scan a SCSI bus for the INQUIRY data is sent to theminiport's HwStartIo routine, described in Section B.4.2, next.

Page 334: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-26 Kernel-mode Driver Design Guide

B.4.2 HwStartIo Routine

As its name suggests, a HwStartIo routine is the entry point for incoming requests toperipheral devices on the HBA-driven bus(es). HwStartIo is called with pointers to a SCSIrequest block (SRB) and to the miniport's device extension for per-HBA state.

If the miniport's DriverEntry routine also requested that memory be allocated for logical unitextensions (see Section B.4.1), the HwStartIo routine calls ScsiPortGetLogicalUnit with theinput device extension pointer and the PathId, TargetId, and Lun values from the input SRB.

If the DriverEntry routine requested that memory be allocated for SRB extensions, theSrbExtension field in each SRB contains a pointer to the miniport's per-request storage area.Note that a miniport driver must request that memory be allocated for SrbExtensions if itmaintains per-request state information. It cannot use an SRB for this purpose. In particular, aminiport driver can write values into SRBs only for the following purposes and only in thefollowing fields: ■ To return required status about each operation in the SrbStatus and ScsiStatus fields ■ If an underrun occurs, to update the DataTransferLength field ■ If it supports auto request sense and transfers request-sense information, to update the

SenseInfoBufferLength

After the higher-level SCSI class drivers have loaded, most of the SRBs sent to the HwStartIoroutine have the Function field set to SRB_FUNCTION_EXECUTE_SCSI. On receipt ofsuch a request, the StartIo routine does the following: ■ Gets and/or sets up whatever context the miniport driver maintains in its device, logical

unit, and/or SRB extensions For example, a miniport driver might set up a logical unit extension with pointers to the

SRB itself and the SRB DataBuffer pointer, the SRB DataTransferLength value, and adriver-defined value (or CDB SCSIOP_xxx value) indicating the operation to be carriedout on the HBA.

■ Calls an internal routine to program the HBA, as partially directed by the SRB Flags, forthe requested operation

For a device I/O operation, such an internal routine generally selects the target device andsends the CDB over the bus to the target logical unit.

If the miniport driver uses system DMA, it must call ScsiPortIoMapTransfer beforeprogramming the HBA to transfer data. ScsiPortIoMapTransfer sets up the system DMAcontroller and calls the miniport's HwDmaStarted routine, described in Section B.4.3, next.

Page 335: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-27

A HwStartIo routine is also responsible for handling incoming SRBs with the Function fieldset to SRB_FUNCTION_ABORT_COMMAND. For an abort request, the miniport drivermight need to verify that the given SRB has not been aborted already by callingScsiPortGetSrb for the target logical unit and comparing the returned pointer to the currentSRB's NextSrb value. If they are unequal, the current SRB has already been aborted, and theminiport driver should do the following: ■ Set the input SRB's ScsiStatus to SRB_STATUS_ABORT_FAILED. ■ Call ScsiPortNotification with the NotificationType RequestComplete and with the

input SRB. ■ Call ScsiPortNotification again with the NotificationType NextRequest, or with

NextLuRequest if the HBA supports tagged queueing or multiple requests per logicalunit.

Otherwise, the HwStartIo routine does the following: ■ Sets up context for the request in its device, logical unit, and/or SRB extensions ■ Programs the HBA to abort the given request

The NT port driver always sends its own reset-bus requests directly to the miniport'sHwResetBus routine, described in Section B.4.4. However, it is possible for the HwStartIoroutine to be called with an SRB in which the Function field is set toSRB_FUNCTION_RESET_BUS if a SCSI class driver requests this operation. The StartIoroutine can simply call the HwResetBus routine to satisfy an incoming reset request.

If the HBA caches data internally, as indicated when the HwFindAdapter routine (see SectionB.4.1.1) set up the PORT_CONFIGURATION_INFORMATION, the HwStartIo routine mustsupport the SRB_FUNCTION_FLUSH and SRB_FUNCTION_SHUTDOWN requests. AnSRB with its Function field set to SRB_FUNCTION_FLUSH tells the miniport driver totransfer data cached in the HBA, usually to a disk. (Such a flush request might originate whenan application closes a file or the application itself is terminated.)

An SRB with its Function field set to SRB_FUNCTION_SHUTDOWN tells the miniportdriver to complete transferring data, including flushing all cached data, out to the targetdevice. The miniport driver should hold on to the shutdown request until no data remains inthe HBA's internal cache for the target logical unit and, then, complete the shutdown request.Note that the miniport can be called with more than one shutdown request (possibly, for thesame LU), or with several shutdown requests for different LUs, before the system itself isactually shut down.

Page 336: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-28 Kernel-mode Driver Design Guide

Whether a miniport driver handles SRB_FUNCTION_IO_CONTROL requests depends onwhether the HBA is to provide dedicated support for an application. Supporting this requestallows a set of privately defined device I/O control requests to be sent directly to the miniportdriver. For SRBs with the Function field set to SRB_FUNCTION_IO_CONTROL, theDataBuffer field contains a pointer to a system-defined SRB_IO_CONTROL structurecontaining the driver-defined and application-specified ControlCode. Note that system-defined device I/O control requests sent to SCSI class drivers are mapped to SRBs with theFunction field set to SRB_FUNCTION_EXECUTE_SCSI.

Every HwStartIo routine must handle the receipt of an unsupported SRB_FUNCTION_xxx, asfollows: ■ Set the input SRB's SrbStatus to SRB_STATUS_INVALID_REQUEST. ■ Call ScsiPortNotification with the NotificationType RequestComplete and with the

input SRB. ■ Call ScsiPortNotification again with the NotificationType NextRequest, or with

NextLuRequest if the HBA supports tagged queueing or multiple requests per logicalunit.

Every HwStartIo routine must return a Boolean value, indicating whether the input requestwas processed or should be resubmitted later (FALSE). However, the miniport eventuallymust call ScsiPortNotification twice for each SRB input to its StartIo routine: first, tocomplete the request (NotificationType RequestComplete) and, second, to tell the NT portdriver to call the StartIo routine again with the next SRB (NotificationType NextRequest orNextLuRequest).

The following SRB Function values are defined for use in future versions of Windows NT: ■ SRB_FUNCTION_RECEIVE_EVENT ■ SRB_FUNCTION_RELEASE_RECOVERY ■ SRB_FUNCTION_RESET_DEVICE ■ SRB_FUNCTION_TERMINATE_IO

The NT port driver processes requests with the following SRB Function values withoutcalling the miniport driver: ■ SRB_FUNCTION_CLAIM_DEVICE (see Section B.1.1.2) ■ SRB_FUNCTION_RELEASE_QUEUE (see Section B.1.3.1) ■ SRB_FUNCTION_FLUSH_QUEUE (see Section B.1.3.1) ■ SRB_FUNCTION_ATTACH_DEVICE (see Section B.2.1) ■ SRB_FUNCTION_RELEASE_DEVICE (see Section B.2.1)

Page 337: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-29

Miniport driver designers can assume that their miniports will never be sent an SRB with anyof the immediately preceding Function values. The NT port driver handles these requestsfrom SCSI class and filter drivers to protect these higher-level SCSI drivers from having toaccess any HBA-specific (or miniport-specific) state information in order to find their devicesor cancel queued requests. This ensures that NT SCSI class and filter drivers have nodependencies on any particular model of HBA.

For more information about SRBs and the set of system-defined SRB_FUNCTION_xxxvalues, see the Kernel-mode Driver Reference.

B.4.3 HwDmaStarted Routine

A miniport driver whose HBA uses the system DMA controller must have a HwDmaStartedroutine.

For a data transfer operation, such a miniport driver must call ScsiPortIoMapTransfer,passing in the pointers to its device extension for per-HBA data and the SRB requesting thetransfer, along with a range of logical addresses for the buffer from which or into which thedata will be transferred.

Note that the logical address range passed to ScsiPortIoMapTransfer either must be themapped values for the input SRB's DataBuffer and DataTransferLength or a proper subsetof this range. For most transfer requests, a miniport driver designer can assume that all dataspecified in the input SRB can be transferred in a single DMA operation. That is, the miniportdriver might have to carry out more than one transfer operation to satisfy a given SRB only ifthe HBA provides application-dedicated support and the application sends large transferrequests directly to the miniport driver. Otherwise, it is the responsibility of NT SCSI classdrivers to split up large transfer requests into a set of partial transfer requests, each sized tosuit the capabilities of the HBA (see Section B.1.2.2).

ScsiPortIoMapTransfer calls the miniport's HwDmaStarted routine when the system DMAcontroller is ready to transfer data between system memory and the HBA. HwDmaStartedmust set up the HBA for the transfer operation.

When a transfer operation is complete, the miniport driver must call ScsiPortFlushDmabefore it calls ScsiPortNotification with the SRB and/or calls ScsiPortIoMapTransfer to setup the DMA controller again for a subrange in the buffer. ScsiPortFlushDma flushes anyremaining data cached in the DMA controller. Note that ScsiPortFlushDma also can becalled to cancel a system DMA transfer, even if the miniport's HwDmaStarted routine has notyet been called.

For more information about ScsiPortIoMapTransfer and ScsiPortFlushDma, see theKernel-mode Driver Reference.

Page 338: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-30 Kernel-mode Driver Design Guide

B.4.4 HwResetBus Routine

Every miniport driver must have a HwResetBus routine, which is called with a pointer to theminiport's device extension for HBA state and the PathId of the bus to be reset. If anattempted bus-reset operation fails (or times out), the miniport driver should callScsiPortLogError and, then, program the HBA for a hard reset.

A bus-reset operation might require the miniport driver to clean up state it maintains in thedevice extension and/or logical unit extensions for devices on the bus. HwResetBus mustcomplete any outstanding requests by calling ScsiPortCompleteRequest with the SrbStatusvalue SRB_STATUS_BUS_RESET or, for individual SRBs, ScsiPortNotification with thisstatus.

After completing the bus-reset request and any outstanding requests, the miniport must callScsiPortNotification (if it has not already done so) with the NotificationType NextRequest,or NextLuRequest if the HBA supports tagged queueing or multiple requests per logical unit.

B.4.5 HwInterrupt Routine

On entry, a HwInterrupt routine should determine if its HBA actually generated an interrupt.HwInterrupt must return FALSE as soon as possible if it detects a spurious interrupt so theinterrupt service routine for the device that generated the interrupt can be called quickly.

Otherwise, a miniport's HwInterrupt routine is generally responsible for completing the I/Ooperation that caused the interrupt. Depending on the HBA and the design of the miniport, aHwInterrupt routine does some or all of the following: ■ Dismisses the interrupt on the HBA ■ Notifies (by calling ScsiPortNotification or ScsiPortCompleteRequest) the port driver

if the HBA indicates that certain SCSI error conditions occurred during the operation andlogging the error (see Section B.4.8)

■ Completing the requested operation that caused the interrupt, such as callingScsiPortIoMapTransfer (see Section B.4.3) if the interrupt came in from a previouslyselected target TID and LU indicating a readiness to transfer data

When the HwInterrupt routine (or an internal miniport routine) completes an SRB, it callsScsiPortNotification twice: ■ First, with the NotificationType RequestComplete and the just satisfied request ■ Next, with the NotificationType NextRequest, or with NextLuRequest if the HBA

supports tagged queueing or multiple requests per logical unit

For better overall system performance, a miniport's HwInterrupt routine should do only theminimum necessary to process I/O requests. That is, the miniport should be designed to returncontrol from the HwInterrupt routine as quickly as possible, rather than tying up a processorand preventing other drivers from servicing device interrupts.

Page 339: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-31

If interrupt-driven I/O operations take a long time to complete, a miniport driver should have apair of HwEnableInterruptsCallback and HwDisableInterruptsCallback routines. For example,if a miniport must poll for longer than 50 microseconds doing PIO, its HwInterrupt routineshould not retain control of the CPU for the full polling interval in order to complete arequested operation. Instead, its HwInterrupt routine should do the following: 1 Disable interrupts from the HBA. 2 Set up the device extension with any context necessary to complete the operation. 3 Call ScsiPortNotification with a pointer to the device extension, the NotificationType

CallEnableInterrupts, and the miniport's HwEnableInterruptsCallback routine,described in Section B.4.5.1, next.

4 Return control.

ScsiPortNotification calls the HwEnableInterruptsCallback routine as an NT DPC (seeChapter 3).

B.4.5.1 HwEnableInterruptsCallback Routine

A HwEnableInterruptsCallback routine finishes processing a polled I/O operation withoutinhibiting I/O operations for other devices in the machine. When theHwEnableInterruptsCallback routine gets control, all system device interrupts are enabledexcept from the HBA. In other words, the miniport's HwInterrupt routine cannot be called andcannot disturb the context data it set up about the current operation while theHwEnableInterruptsCallback routine is running.

A HwEnableInterruptsCallback routine should do the following: 1 Use the context that was set up for the operation in the input device extension to

complete processing of the request that caused the interrupt. 2 Call ScsiPortNotification with a pointer to the device extension, the NotificationType

CallDisableInterrupts, and the miniport's HwDisableInterruptsCallback routine,described in Section B.4.5.2, next.

3 Return control.

ScsiPortNotification calls the HwDisableInterruptsCallback routine with a subset of thesystem device interrupts disabled: that is, no device interrupt with a system-assigned hardwarepriority (IRQL) less than or equal to the HBA's can interrupt.

Page 340: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-32 Kernel-mode Driver Design Guide

B.4.5.2 HwDisableInterruptsCallback Routine

A HwDisableInterruptsCallback routine should do, at most, the following: 1 Reenable interrupts from the HBA. 2 Return control.

Note that this routine must execute as quickly as possible to avoid tying up I/O operations forother devices in the machine.

B.4.6 HwTimer Routine

While a miniport driver can call ScsiPortStallExecution to wait for a state change on theHBA, miniport drivers should never call this routine to wait for longer than one millisecond(except, possibly, for an operation performed only when a miniport is loaded).ScsiPortStallExecution ties up the processor for the given interval, preventing other code inthe system from doing useful work.

Instead of calling ScsiPortStallExecution and wasting many CPU cycles, a miniport drivercan have a HwTimer routine. One or more HwTimer routines are particularly useful if theHBA does not generate a completion interrupt for every operation or if any commonlyrequested operation, such as a bus reset, takes longer than a millisecond.

After the HBA has been programmed for such an operation, the miniport driver callsScsiPortNotification with the NotificationType RequestTimerCall, a pointer to the deviceextension containing context about the operation, its HwTimer routine, and a driver-determined interval.

HwTimer is called once for each such call to ScsiPortNotification, which can be called fromthe HwTimer routine itself. However, any call to ScsiPortNotification with theNotificationType RequestTimerCall overrides a preceding call for which the specifiedinterval has not expired. That is, there is only one outstanding request to call a miniport'sHwTimer routine at any given moment.

The interval passed in to ScsiPortNotification is in microseconds but the minimum resolutionfor each call is approximately ten milliseconds. An input interval of zero cancels the precedingrequest to call the HwTimer routine, provided it has not been called (or dispatched forexecution on another processor in an SMP machine) already.

ScsiPortNotification calls the HwTimer routine as an NT DPC associated with a timer object(see Chapter 3).

Page 341: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

SCSI Drivers B-33

B.4.7 HwAdapterState Routine

Windows NT miniport drivers should set this entry point to NULL in theHW_INITIALIZATION_DATA (see Section B.4.1). The HwAdapterState prototype isdefined for compatibility with future versions of MS-DOS.

B.4.8 Error Handling

A SCSI miniport driver is responsible for handling and for notifying the NT port driver of thefollowing kinds of SCSI errors, which should be set in the SrbStatus field before the drivercompletes the SRB it was processing when the error occurred: ■ SRB_STATUS_ERROR (if the HBA returns a nonspecific bus error) ■ SRB_STATUS_PARITY_ERROR ■ SRB_STATUS_UNEXPECTED_BUS_FREE ■ SRB_STATUS_SELECTION_TIMEOUT ■ SRB_STATUS_COMMAND_TIMEOUT ■ SRB_STATUS_MESSAGE_REJECTED ■ SRB_STATUS_NO_DEVICE ■ SRB_STATUS_NO_HBA ■ SRB_STATUS_DATA_OVERRUN ■ SRB_STATUS_PHASE_SEQUENCE_FAILURE ■ SRB_STATUS_BUSY (TID busy)

Whenever the miniport driver encounters an HBA or bus error, it should callScsiPortLogError with one of the following system-defined error codes: ■ SP_BUS_PARITY_ERROR ■ SP_UNEXPECTED_DISCONNECT (by the target LU) ■ SP_INVALID_RESELECTION ■ SP_BUS_TIME_OUT ■ SP_REQUEST_TIMEOUT ■ SP_PROTOCOL_ERROR ■ SP_INTERNAL_ADAPTER_ERROR ■ SP_IRQ_NOT_RESPONDING ■ SP_BAD_FW_ERROR ("FW" stands for "firmware") ■ SP_BAD_FW_WARNING

Page 342: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

B-34 Kernel-mode Driver Design Guide

ScsiPortLogError allocates an error-log packet, sets it up, and logs the I/O error on behalf ofthe miniport driver, as described in Chapter 16, to help users or system administrators monitorthe condition of the HBA.

Page 343: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronymsaccess violation

An attempt to carry out a memory operation that is not allowed by the underlying pageprotection. See also probe and SEH.

There are four basic kinds of actions that can cause access violations: 1 Attempting an invalid operation, such as writing to a read-only buffer. 2 Attempting to access memory beyond the limit of the current program's address

space (a.k.a. "length violation"). 3 Attempting to access a page to which the system forbids access. For example,

code is not allowed to run in the low-order 64K of the NT user-mode addressspace in order to simplify the detection of NULL pointer references.

4 Attempting to access a page that is currently resident but dedicated to the use ofan executive component. For example, user-mode code is not allowed access apage that the Kernel is using.

Note that this term pertains to memory operations. It has nothing to do with theSecurity Manager's checking of user-mode access rights to objects. See securityviolation, ACE, and ACL.

ACE(1) Access control entry

An ACE is an individual entry in an ACL. An ACE contains an SID and describesthe access rights that a particular user or group of users has to a system resource.The set of all ACEs on the object are used to determine whether an access request tothe object is granted. See also security descriptor.

(2) Advanced computing environmentA consortium of hardware and software companies formed to promote an opencomputing environment. NT is one of the original ACE operating systems.

access rightA permission granted to a process to manipulate a particular object in a particular way(by calling a service). Different NT object types support different access rights, which arestored in an object's ACL.

Page 344: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-2 Kernel-mode Driver Design Guide

ACLAccess control list

An ordered list of ACEs.

adapter objectA kernel-mode-only object type, defined by the I/O Manager and supported by the HALcomponent. An adapter object represents a hardware bus adapter or DMA controllerchannel. Adapter objects "connect" different kinds of devices on the bus or DMAcontroller, each device (or kind of device) with its own driver.

affinityA programmer-defined attribute of a process and/or thread on a multiprocessor platform:

■ For a process, its affinity is the programmer-determined set of processors on whichits threads are permitted to run.

■ For a thread, its default affinity is identical to that of the process to which it belongs;otherwise, its affinity must be a proper subset of the process's.

alertA Boolean that provides a way to break into a thread's execution at a point where eitherof the following conditions is met:

■ The thread is in an alertable wait state (as specified when the wait service wascalled).

■ The thread polls the alerted flag.

APCAsynchronous procedure call

An APC is a Kernel-defined control object representing a procedure that is calledasynchronously. APCs are thread-context dependent; that is, they are queued to aparticular thread for execution.There are three different kinds of APCs in NT:

1 User APCs - These APCs are used by certain asynchronous NT system servicesto allow users to synchronize the execution of a thread with the completion ofan operation or the occurrence of an event such as a timer's expiration. UserAPCs are, by default, disabled. That is, they are queued to the user's thread, butthey are not executed except at well-defined points in the user's program.Specifically, they can only be executed when the user has called a wait serviceand has enabled alerts to occur, or if the user has called the test-alert service.

Page 345: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-3

2 Kernel APCs - These APCs are normal kernel-mode APCs. They are muchlike a normal user APC except that they are executable by default. That is, theyare enabled except when the thread is already executing a Kernel APC. (Notethat a special Kernel APC always preempts these.)

3 Special Kernel APCs - These APCs cannot be blocked except by running at araised IRQL. They are executed at APC_LEVEL IRQL (see IDT), in kernelmode. These types of APCs are used by the system to force a thread to executea procedure in the thread's context. An example of this is I/O completion: theI/O Manager needs to get back into the context of the original requestor of theI/O operation so that it can copy buffers, etc. In order to do this, the I/OManager must be able to access the virtual address space of the thread/process,and the most efficient way to complete the operation is to be in the callingthread's context.

ARC(1) The NT executive runs on top of ARC-compliant machines, including both RISC- andCISC-based platforms that supply the following components to the OS loader:

■ In MIPS-based platforms, a set of machine firmware that supports bootstrap loadingand execution as an "abstracted" set of ARC routines and ARC devices.

■ In x86-based platforms, a hardware recognizer (Ntdetect) that finds devicesnecessary to bootstrap the system by querying the ROM BIOS and builds ahardware database that emulates the RISC-based ARC firmware.

■ In both kinds of platforms, a HAL that interfaces between the hardware and NT.Later in the load process, the driver of the disk, CD-ROM, or floppy device from whichthe system booted creates an alias between the name of its device object and thecorresponding ARC device name by calling IoAssignArcName.(2) Advanced RISC computing

Refers to a RISC-based computer architecture standard, associated with the ACEconsortium.

associated IRPOne of a set of IRPs, created by the highest-level driver in a chain of layered drivers, thatspecifies some part of an I/O request to be sent on to a lower-level driver. When allassociated IRPs in the set have been processed, the highest-level driver completes theoriginal request. See also IRP.

Page 346: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-4 Kernel-mode Driver Design Guide

asynchronous I/OA model for I/O in which the operations carried out to satisfy I/O requests do notnecessarily occur in sequence. The application that originally made the request cancontinue executing (rather than waiting for its I/O to complete), the I/O Manager or ahigh-level driver can reorder I/O requests as they are received, and a low-level driver canstart an I/O operation on a device before it has completed the preceding request,particularly in a multiprocessor machine.

backing storeA mass storage medium, such as a disk, that serves as backup "memory" for paging whenphysical memory becomes full. See also paging file.

balance setThe set of processes currently in the system, in particular, processes whose threads areeligible for execution. See also dispatch state and working set.

At any given moment, the balance set depends on the availability of physicalmemory (pages) to back the virtual address space associated with each activeprocess. If a physical memory shortage occurs, the NT Memory Manager first trimsthe working set of each active process to its minimum, then (if necessary) removesprocesses from the balance set.

based sectionA section allocated at the same virtual address for each process that has a view of thesection. See also section and view.

BCBBuffer control block

An opaque Cache Manager structure, used to maintain state as a file system pins andreleases data (for example, its volume structure) in the cache.

big-endianRefers to a memory architecture in which the byte layout is as follows:

■ Byte N is the most significant (and, in conventional layout diagrams, the "leftmost")byte of:

■ A word composed of bytes N and (N + 1). ■ A double word composed of bytes N, (N + 1), (N + 2), and (N + 3). ■ A K-byte memory entity composed of bytes N, (N + 1),...,(N + K - 1). ■ The address of the preceding word, double word, or K-byte entity is its most

significant byte, N.

Page 347: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-5

A MIPS machine can be configured for either big-endian or little-endian byte addressing.For a big-endian configuration, the most significant bit of a 16-bit short value is the"leftmost" bit at byte N, while the least significant bit is the "rightmost" bit of byte (N +1). See also little-endian.

The terms "big-endian" and "little-endian" are derived from Jonathan Swift'sGulliver's Travels.

Cache ManagerAn executive component that provides file caching support for NT file system drivers.

CCBContext control block

An internal NT-defined file system structure, in which a file system maintains perfile object state for an open instance of a file. See also FCB and DCB.

CDBCommand descriptor block

A structure, defined by the SCSI-II standard, used to communicate requests to atarget device on the SCSI bus.

CDFSCD-ROM file system

CD-ROMCompact disk, read-only memory

chaseTo follow a linked list, queue, or other software-defined path. For example, when a filesystem encounters a symbolic link file within a path specification, it "chases the symboliclink" to redirect operations to the target file. See also symbolic link.

Page 348: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-6 Kernel-mode Driver Design Guide

child processWhen a new NT-native process is created by calling a system service, the caller mustspecify a "parent" process from which the new process inherits its token, quota, and basepriority. The new process can optionally inherit any or all of the following from thespecified parent process:

■ A copy of the parent's virtual address space. ■ All object handles that were opened with the inheritance attribute. ■ Debugging and exception handling ports.

However, an NT user-mode process becomes a wholly independent peer to its so-calledparent process as soon as it has been created. After process creation, any process-to-process dependencies become the responsibility of a protected subsystem (such as the TMWin32 or POSIX subsystem).A kernel-mode-only (a.k.a. "system") process has no "parent" when it is created.

CIDClient identifier

A unique value that identifies a thread.

class drivera.k.a. "type driver"

(1) An intermediate driver for a physical device, which is connected to a so-called"intelligent controller" such as a SCSI HBA, or to an adapter bus. Such a classdriver communicates with the corresponding port driver according to an establishedprotocol, such NT-defined SRBs. The system-supplied SCSI disk, CD-ROM, andtape class drivers are examples of this type of class driver.(2) An intermediate driver that provides system-required but hardware-independentsupport for a given class of physical devices. Such a class driver communicates witha corresponding hardware-dependent port driver, using a set of system-defineddevice I/O control requests, possibly with additional driver defined internal deviceI/O control requests. The system-supplied keyboard and mouse class drivers areexamples of this type of class driver.

client/server modelA model for structuring applications or operating systems such that the system is dividedinto processes (servers), each of which provides a set of specialized services to otherprocesses (clients).

collided page faultA page fault caused by one thread while an in-page I/O operation for another thread isbeing done on the same page.

Page 349: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-7

commitmentThe NT Memory Manager's current value for paging file usage.

contextThe execution state of a thread at any given moment:

■ For a user-mode thread, the platform-dependent register state, kernel stack, TEB,and user stack in the address space of the process to which the thread belongs.

■ For a kernel-mode thread, the platform-dependent register state and kernel stack.Kernel-mode threads have neither a TEB nor a user-mode context, but they musthave an associated process. See also process object.Most NT device drivers do not have a context in this sense. Unless a driver (such asan FSD) creates its own process and/or thread(s), it does not have its own stackspace or register state. For each driver, the set of objects it owns and the IRPs that itcan access via a device queue object associated with its device object can beconsidered all or part of its context.

context recordA system-defined structure, containing the current register state for an exception handler.See also SEH.

control objectsA class of Kernel-defined object types, used to manage all kernel-mode operations exceptdispatching and synchronization. See also dispatcher objects.

Control objects include: APC, DPC, device queue, interrupt and process objects.Note that all control object types except processes are kernel-mode-only objects,invisible to user-mode code. User-mode APC objects are also "invisible" in thesense that they look more like user-supplied routines to be called on return fromcertain system services (such as a request to read a file) than like objects.

controller objectA kernel-mode-only object type, defined by the I/O Manager. A controller objectrepresents a hardware controller or channel. The driver calls IoAllocateController tocarry out synchronized I/O on attached devices. Controller objects "connect" a set ofsimilar devices attached to a controller with a single driver.

critical sectionA piece of code that accesses a nonsharable resource, such as device-state data stored in adevice extension to which access must be synchronized among some number of driverroutines.

Page 350: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-8 Kernel-mode Driver Design Guide

DACLDiscretionary access control list

A discretionary ACL is part of the security descriptor for an object. It can be appliedto a newly created object in order to constrain access to the object. See also ACE,access right, ACL and security descriptor.

DCBDirectory control block

An internal FS structure in which a file system maintains state for an open instanceof a directory file.

deadlockA runtime error condition that occurs when two threads of execution are blocked, eachwaiting to acquire a resource that the other holds, and both unable to continue running.

device extensionA part of a device object whose size is determined when a driver creates the device objectand whose internal structure is driver-defined. For many NT drivers, a device extensionis the driver's major (and only) data storage area, used to maintain device state and tocontain any system-defined objects and other data that the driver writer decides to use.

device objectA kernel-mode-only, I/O Manager-defined object type, used to represent a physical,logical, or virtual device whose driver has been loaded into the system. Each driver callsIoCreateDevice to initialize a device object for each device that driver services. See alsodriver object.Devices are "visible" to end users as named file objects, stored in a directory protectedagainst careless or malicious user-mode access. User-mode code (protected subsystems)must access any device through the opaque handle returned by the system service thatopens the file object that represents the device to user-mode code.

Page 351: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-9

device queue objectA kernel-mode-only, Kernel-defined control object type, used to record the Boolean state(Busy or Not_Busy) of a device and to queue I/O requests for subsequent processing. Adevice queue object has an associated executive spin lock.

An I/O request, called a "queue entry," is not actually placed in the device queueunless the state is already Busy:

■ On the transition from Not_Busy to Busy, the driver is expected to service thequeue entry immediately.

■ Subsequent requests while the state is Busy are queued in FIFO or key-sortedorder.

■ An attempt to remove a queue entry from an empty queue causes the transitionfrom Busy to Not_Busy.

■ An attempt to remove a queue entry from an empty queue when the device isNot_Busy causes the system to "bugcheck" (i.e., crash).

directory fileA file that points to or lists a set of named file objects.

Note that on-disk directory files are represented in NT as file objects, not as objectdirectory objects.

directory objectSee object directory object.

DIRQLDevice interrupt request level

See also IRQL and IDT - This particular IRQL refers to the IRQL at which a givendevice interrupts.

dispatch stateFor a thread at any given moment, one of the following:

■ Initialized. ■ Ready - queued and eligible for dispatch to a processor. ■ Standby - ready and selected to execute, but a context switch to the thread has not

yet occurred. ■ Running. ■ Waiting - suspended until a particular dispatcher object is set to the Signaled state. ■ Terminated.

Page 352: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-10 Kernel-mode Driver Design Guide

dispatcher objectsA class of Kernel-defined object types, used to manage dispatching and synchronization.See also control objects.

Dispatcher objects have a Boolean state (Signaled or Not-Signaled), and arearguments to the wait services or Kernel wait routines. Dispatcher objects include:events, (kernel-mode-only) mutexes, semaphores, threads, and timers.Kernel-mode threads synchronize their operations by waiting on one or moredispatcher objects, or by waiting on other objects, such as user-mode file objects,that contain "hidden" kernel-mode dispatcher objects.Note that it is a fatal error to wait on a dispatcher object at IRQL > APC_LEVEL.

DMADirect memory access

(No different from any other OS.)

DPCDeferred procedure call

A DPC is a Kernel-defined control object type, representing a procedure that is to becalled later. DPCs are executed in kernel mode at IRQL DISPATCH_LEVEL (seeIDT and IRQL).A DPC is primarily used when an interrupt service routine (e.g., device driver orclock interrupt service routine) needs to perform more work but should do so at alower IRQL than the one at which an ISR executes. (Note that getting the IRQLlowered back down quickly is important for overall system response time.) The ISR,then, can request that a procedure be executed at a lower IRQL at a later time.

driver objectA kernel-mode-only object representing an NT driver's load image, used by the I/OManager to locate the driver's entry points.

EAExtended attribute

Files have basically four different parts: 1 Data 2 File system attributes (such as creation time, other times, FAT attributes, etc.) 3 Security descriptor 4 EAs

EAs are the set of extended information about a file. An EA is viewed as an untypedname-value pair that is defined by the user. Typical system uses are to store the iconfor an image, to indicate that the file is a symbolic link, etc.

Page 353: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-11

EFUError format utility

A utility, supplied by the system, that reads error entries out of an error log file anddisplays them. Drivers call IoAllocateErrorLogEntry andIoWriteErrorLogEntry to transfer errors to an error log file; they can supply errorbuffer translation routines to format entries for display by the EFU.

EISAExtended industry standard architecture

Also, a standard that defines the architecture of (and interface to) the extended PCbus. See also ISA.

event objectA user-mode object upon which one or more threads can wait until the event is set to theSignaled state. Every user-mode event object is implemented through the use of a kernel-mode event object.A kernel-mode event object is an instance of a Kernel-defined dispatcher object type.Each kernel-mode event can be classified as either of the following:

1 When a synchronization event (a.k.a. "autoclearing event") is set to the Signaledstate, a single thread that was waiting on the event is released (its dispatch statetransitions from waiting to ready, standby, or running), and an autoreset to Not-Signaled occurs.

2 When a notification event is set to the Signaled state, all threads that were waitingon the event are released, and the event remains in the Signaled state until anexplicit reset to Not-Signaled occurs.

exceptionA synchronous error condition, resulting from the execution of a particular machineinstruction. See also SEH.

Page 354: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-12 Kernel-mode Driver Design Guide

executiveThe collection of components that form the base NT operating system. Executivecomponents include the Executive Support, Kernel, Memory Manager, Cache Manager,Process Structure, Interprocess Communication (LPC and RPC), Object Manager, I/OManager, Configuration Manager, Hardware Abstraction Layer, and Security ReferenceMonitor. Each executive component except the Executive Support, Cache Manager, andHardware Abstraction Layer supplies a specialized set of native user-mode systemservices, and every executive component exports a set of kernel-mode functions for useby other executive components.

NT drivers also call kernel-mode functions supplied by executive components,including (but not limited to) functions beginning with the prefix "Ex" forEx(ecutive) Support. Other kernel-mode functions of interest to drivers begin withthe prefixes: Hal, Ke(rnel), Ps (for Process Structure), Ob(ject), Io, Mm (forMemory Manager), Cc (for Cache Manager), Se(curity) and FsRtl (for File SystemRun-time Library). See also Zw routines.

Note that NT drivers, including kernel-mode device, intermediate, and file systemdrivers, are considered part of the NT executive after they are loaded. The "operatingsystem" visible to end users is actually a protected subsystem that runs in user mode ontop of the NT executive; the executive is hidden from end users by a subsystem-suppliedinterface that emulates Windows, POSIX, or some other operating system.

FATFile allocation table

(adjective: an NT-installable file system that was native to DOS; also, a type of diskpartition)

fault toleranceAn optional feature of the Windows NT operating system environment, which includesthe end-user-visible Windows DiskMan utility supported by an NT-supplied intermediatedriver that handles disk partition mirroring, striping, and volume set management.

FCBFile control block

An internal FS structure in which a file system maintains state for an open file.

Page 355: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-13

file objectA user-mode object that represents an open instance of a file, device, directory, orvolume that is accessed through the file handle returned by a protected subsystem'sfunction that calls down to a system service that opens (or creates) a file object . Everyfile handle contains a "hidden" kernel-mode event object, so that callers of the I/O systemservices can wait on a file handle.At any given moment, several file objects can be associated with a single, shared datafile, but each such file object has a unique handle and maintains an object-specific valuefor the current file pointer.

A file object is sometimes called a "persistent object" because the (file, device,directory, or volume) entity that backs any run-time set of file objects is retainedacross system boots.

FSFile system

An FS is a file system driver that registers itself with the NT I/O Manager as anactive file system. Unlike many other operating systems, NT does not have aparticular must-be-resident file system, and several NT file systems can be active atthe same time. NT file systems are dynamically located when a volume is mountedor when a remote name is being resolved: the I/O Manager calls the registered FSsin turn until one file system "recognizes" the volume structure or remote name.Examples of NT-supplied file systems are FAT, HPFS, NTFS, CDFS, the LANManager redirector, NPFS (named pipe file system), and RAW.

FSDFile system driver

The FSD refers to the portion of a driver that executes in the context of the caller, towhich an I/O request is dispatched when the request is originally made. The FSDhas no process virtual address space and no particular thread context because itoperates in the context of whoever happens to call it.An FSD also executes in the context of the lower-level (intermediate or massstorage device) driver's DPC routine when the I/O operation is being completed ifthe FSD indicated that it should be called upon completion of the IRP.The term FSD normally refers to the DD (device driver) part of a file system driveronly when the driver also has an FSP associated with it; otherwise, it's called simplya device driver.

Page 356: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-14 Kernel-mode Driver Design Guide

FSPFile system process

The FSP usually refers to a set of device-dedicated or system worker threads that aida file system driver (FSD) in getting work done, but can also refer to a kernel-modeprocess set up by an FSD. FSP threads are generally used when a file system driverneeds a thread context in which to work. An example is when the driver needs to beable to wait without tying up the calling thread. An FSP kernel-mode process is usedonly when an FSD must have a driver-specific process address space in which itsdevice-dedicated threads execute. (Most NT file system drivers conserve systemmemory by not creating their own kernel-mode processes.)Communication between the FSD and the FSP threads is performed through the useof a communication region. This region is generally allocated in a device object'sdevice extension and consists of an interlocked queue and a synchronization event.See also device object, device extension, and event object.Note that any NT driver (not just file systems) can create one or more device-dedicated threads or use system worker threads. In particular, an NT driver shouldcreate a thread for I/O operations that use synchronization mechanisms (such asevents, timers, semaphores, mutexes, etc.) other than spin locks. Such a driver'sthread can wait for an indefinite, nonzero interval on a Kernel-defined dispatcherobject, but context switches to the thread slow down the driver's I/O operations.

GUIDGlobally unique identifier

See SID.

HALHardware abstraction layer

An NT executive component that provides platform-specific support for the NTKernel, I/O Manager, kernel-mode debuggers, and lowest-level device drivers. TheHAL exports routines that abstract platform-specific hardware details about caches,I/O buses, interrupts, etc. and provides an interface between the platform's hardwareand the system software. For example, the HAL implements a routine to map eachdevice driver's bus-relative device interrupt vector to a system-assigned vector witha corresponding platform-specific hardware priority (DIRQL), as well as severalroutines that provide DMA-transfer support.

hardware exceptionSee SEH.

Page 357: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-15

HBAHost bus adapter

A hardware device that "connects" NT and a SCSI bus. A SCSI HBA driver usuallymanages the lower layers of the SCSI-II protocol. See miniport driver; see also portdriver, class driver, and SCSI.

HPFSHigh performance file system

An NT-supplied installable file system

hyperspaceA block of virtual memory somewhere in kernel space, dedicated to the use of the NTMemory Manager. Every instance of a user-mode virtual address space has an associatedhyperspace area where the Memory Manager maintains internal data (such as page tablepages and WSLs) for the current process. See also kernel space and user space.

IDBInterrupt dispatch block

An internal structure used by the NT Kernel.

Page 358: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-16 Kernel-mode Driver Design Guide

IDTInterrupt dispatch table

A Kernel-defined call table with a platform-dependent number of first-level entries(some for DIRQL ISRs) and second-level entries for interrupt transfer routines(dependent on a first-level ISR). For symmetric multiprocessor platforms, theKernel sets up an IDT for each processor. See also multiprocessor machine.The NT Kernel reserves eight first-level entries per IDT for its own use; theremaining first-level entries can be connected to a platform-specific bus interrupt bythe HAL or to a device interrupt. The Kernel's reserved entries (in low-to-high IRQLpriority) are defined by the following constants:

■ PASSIVE_LEVEL - execute thread. ■ APC_LEVEL - execute special Kernel APC. ■ DISPATCH_LEVEL - dispatch (execute DPC). ■ WAKE_LEVEL - debugger execution. ■ CLOCK2_LEVEL - interval-timer execution. ■ REQUEST_LEVEL - interprocessor request. ■ POWER_LEVEL - power failure notification. ■ HIGH_LEVEL - machine checks or bus errors.

The set of software and hardware interrupt vectors mapped to these constants isplatform-dependent, but none can be connected to a driver's interrupt object. Notethat the constants PASSIVE_LEVEL through DISPATCH_LEVEL andWAKE_LEVEL correspond to software interrupt vectors. DIRQLs for devicedrivers usually have IRQL priority higher than DISPATCH_LEVEL but lower thanCLOCK2_LEVEL.

IFSInstallable file system

See FS.

integral subsystemA protected subsystem (server) that performs an essential operating system task. For NT,this group includes the Local Security Authority, the Security Accounts Manager, theSession Manager, and the network server. See also protected subsystem.

Page 359: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-17

intermediate driverAn intermediate driver processes I/O requests "between" the highest-level driver(frequently an FS) and the lowest-level driver that controls the hardware device for whichthe request is bound. See also layered driver.

Examples of intermediate drivers include the NT ftdisk driver, which supports faulttolerance, and many class drivers.

interruptAn asynchronous hardware signal, usually indicating that a peripheral device needsservice, that is detected by the processor. An interrupt causes the processor to save state,to branch to a fixed location, and to resume execution at that location. See also IDT.

interrupt objectA kernel-mode-only, Kernel-defined control object type, used to connect a hardwareinterrupt source (see DIRQL) and an ISR to an IDT entry, or to connect an ISR and aninterrupt transfer routine (dependent on the ISR at its IRQL) to a second-level IDT entry.Each interrupt object:

■ Can be associated with a single IDT entry (and, therefore, with a single processor ina multiprocessor machine).

■ Is either LevelSensitive or Latched, depending on the platform or device. ■ Can be one of several interrupt objects associated with a given IDT entry if and only

if every interrupt object for that entry is of a single type (LevelSensitive or Latched).For a second-level IDT entry, an ISR (e.g., for a bus adapter) associated with a first-levelIRQL routes the interrupt to the (device-specific) interrupt transfer routine for service viaits entry in the IDT.When more than one set of interrupt objects are connected to the same first-level IDTentry, the corresponding ISRs are called in the same order in which they were connected.

invalid pageA page-sized range of virtual addresses for which a page fault occurs when any addressin the range is referenced. For NT drivers, referencing an invalid page causes a fatal pagefault unless the driver is running at an IRQL < DISPATCH_LEVEL when the referenceoccurs.

Page 360: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-18 Kernel-mode Driver Design Guide

I/O stack locationa.k.a. "stack location"

An I/O stack location refers to the part of an IRP that is allocated for each driver in achain of layered drivers. Each driver owns one of the stack locations in the packetand obtains driver-specific information about what I/O operation to carry out on thetarget device, using the supplied arguments. It is also used to store context about thedriver during I/O completion so that the driver's I/O completion routine can performits cleanup operations.

I/O status blockAn I/O status block is a data structure that is part of each IRP. An I/O status block servestwo purposes:

1 For asynchronous system calls, it provides the user (or a higher-level driver'sIoCompletion routine) a way of determining whether or not the service workedwhen the IRP is completed.

2 It also provides more information about why the service either worked or did notwork.

The type definition of an I/O status block is as follows:

typedef struct _IO_STATUS_BLOCK { NTSTATUS Status; ULONG Information;} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

Upon completion of a system service, the Information member supplies the caller withmore information about what actually occurred. For example, this member contains thenumber of bytes actually read from a file after a read operation.

Page 361: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-19

IPCInterprocess communication

In addition to synchronization primitives, the NT system has two defined, message-passing mechanisms for IPC:

1 LPC, defining the port object type with a server process that supplies system(port) services to local (using the same physical memory on a single system)client processes.

2 RPC, with an NT-supplied runtime library that implements such RPCcapabilities as binding a client process in one address space with a serverprocess in another address space and sending necessary messages to make aremote procedure call possible.

The system-supplied named pipe file system supports LPC. The NT LAN Managerredirector also implements named pipes as a transport layer to connect clients toapplication servers (RPC).

IRPI/O request packet

An IRP is the basic I/O Manager structure used to communicate with drivers and toallow drivers to communicate with each other. A packet consists of two differentparts:

1 Header, or fixed portion of the packet - This is used by the I/O Manager tostore information about the original request, such as the caller's parameters, theaddress of the device object upon which the file is open, etc. It is also used bydrivers for storing such information as the final status of the request. See alsoI/O status block and device object.

2 I/O stack locations - Following the header is a set of I/O stack locations, oneper driver in the chain of layered drivers for which the request is bound. Eachstack location contains the parameters, function codes, and context used by thecorresponding driver to determine what it is supposed to be doing.

IRQA hardware line over which a peripheral device, bus controller, other processor, or theKernel signals a request for service to the microprocessor. (IRQ is a commonabbreviation for "interrupt request lines.") See interrupt.

Page 362: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-20 Kernel-mode Driver Design Guide

IRQLInterrupt request level

The hardware priority level at which a given kernel-mode routine runs, thereby"masking off" interrupts with equivalent and lower IRQL on the processor. Note thatsuch a routine can be preempted by any interrupt with a higher IRQL. Note also thatrunning at IRQL DISPATCH_LEVEL or higher prevents threads (even those withthe highest real-time priority level) from running on the same processor until thecurrent kernel-mode routine lowers IRQL. However, running at raised IRQL on agiven processor has no effect on the IRQL priority of any other processor in asymmetric multiprocessor machine. See also IDT, interrupt object, IRQ, ISR,multiprocessor machine, and priority.

ISAIndustry standard architecture

Also, a standard defining the architecture of the PC bus (a.k.a. "AT bus standard").

ISRInterrupt service routine

A routine whose function is to service a device when it generates an interrupt.An NT driver's ISR executes at raised IRQL, usually at the DIRQL of the interruptobject(s) set up for its device (i.e., drivers connected to second-level IDT entriesexecute at the IRQL of the first-level entry they connect to). Every NT driver's ISRshould execute as quickly as possible, doing only what is necessary to savesufficient state, to make the device stop generating interrupts, and to queue a DPCthat completes interrupt processing at a lower IRQL.

kernel modeThe privileged processor mode in which NT executive code runs. A driver or threadrunning in kernel mode has access to system memory and hardware. See also user mode.

kernel space(a.k.a. "system space")

A block of virtual memory, dedicated to the use of kernel-mode code. Generally, theNT kernel space is a range of high-order virtual addresses around one gigabyte insize. See also hyperspace, pool memory, and user space.

Page 363: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-21

key objectA NT object type defined by the Configuration Manager that represents an entry in theconfiguration registry database. See also Registry.

latched interrupt(a.k.a. "edge-triggered interrupt")

An interrupt that occurs at the transition from deasserted to asserted on the IRQ line.

layered driverOne of a collection of drivers that respond to the same IRPs. See also I/O stack location.

The term "layered driver" describes the highest-level and lowest-level drivers in achain of layered drivers that process the same IRPs, along with all intermediatedrivers in the chain.

LBNLogical block number

A logical block number identifies a physical block on a disk, using a logical addressrather than physical disk values (for cylinder, track, and sector). For a disk with Nblocks (a.k.a. "sectors"), the corresponding LBNs are 0 through (N - 1). See alsoMCB and VBN.

level-sensitive interruptAn interrupt that occurs when the signal is asserted on the IRQ line.

LPCLocal procedure call

See also IPC and port object.

Page 364: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-22 Kernel-mode Driver Design Guide

little-endianRefers to a memory architecture in which the byte layout is as follows:

■ Byte N is the least significant (and, in conventional layout diagrams, the"rightmost") byte of:

■ A word composed of bytes N and (N + 1). ■ A double word composed of bytes N, (N + 1), (N + 2), and (N + 3). ■ A K-byte memory entity composed of bytes N, (N + 1),...,(N + K - 1). ■ The address of the preceding word, double word, or K-byte entity is its least

significant byte, N.Intel microprocessors always support little-endian addressing. A MIPS-based machinecan be configured for either big-endian or little-endian addressing. For a little-endianconfiguration, the least significant bit of a 16-bit short value is the "rightmost" bit at byteN, while the most significant bit is the "leftmost" bit of byte (N + 1). See also big-endian.

LSALocal Security Authority

LULogical unit

From a SCSI-II HBA driver's point of view, a physical or virtual peripheral device,addressable through a TID, attached to a SCSI bus.

LUIDLocally unique identifier

See SID.

MACLMandatory access control list

A part of the security descriptor for each object in a system with a B1 or highersecurity rating from the U.S. National Computer Security Center.

map(1) To translate a virtual or logical address into a physical address. See also MDL.(2) To alias a bus-relative interrupt vector to a system-assigned interrupt vector. See alsoHAL.

Page 365: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-23

MCBMap control block

An MCB is a structure provided by the FsRtl (File System Run-time Library)package to aid NT file systems in mapping the VBNs for a file to the correspondingLBNs on the disk.

MDLMemory descriptor list

A memory descriptor list uses an array of physical page frame numbers (PFNs) todescribe the pages that back a virtual memory range.

methodA routine supplied with an NT object type that handles standard operations (such asopen, close, delete, parse, dump, and read or reset security attributes) on every instanceof the object type.

MIDIMusical instrument digital interface

miniport driver(1) An HBA-specific driver, linked against the NT-supplied SCSI port driver, that drivesa SCSI bus.(2) A video-adapter-specific driver, linked against the NT-supplied video port driver.

mirror drivera.k.a. "shadow driver"

An intermediate (layered somewhere between an FSD and a disk DD) device driverwhose responsibility is to keep a duplicate of a disk partition.

mountOperation that requests a file system to make a volume useable by the I/O Manager. Thisis done by filling in the information in a VPB which indicates the serial number and labelof the volume, as well as by filling in the pointer to the file system's device object thatrepresents the "volume" mounted on the real device.

Page 366: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-24 Kernel-mode Driver Design Guide

multiprocessor machineA platform with more than one CPU. NT is designed to run on multiprocessor machines,usually configured to be:

■ Homogenous - all CPUs are identical, and either all have identical coprocessors ornone has a coprocessor.

■ Closely coupled - all CPUs share memory and have uniform access to memory. ■ Symmetric - every CPU can access memory, handle any interrupt, and access I/O

control registers just like every other CPU in the system. Note that any version ofNT that runs on a symmetric multiprocessor machine also runs unchanged on auniprocessor machine based on the same CPU.While NT can run on asymmetric platforms, configured with a master CPU thathandles all IRQs and one or more slave CPUs as long as the platform is bothhomogenous and closely coupled, the system has been designed to run primarily onsymmetric multiprocessor platforms because SMP machines have betterperformance characteristics, particularly for I/O.

mutex objectA kernel-mode-only, Kernel-defined dispatcher object type, used to provide mutually-exclusive, deadlock-free access to a resource.

A mutex owner (thread) must run exclusively in kernel mode while it retainsownership; an attempt to switch to user mode while holding a mutex causes asystem crash.Ownership of a mutex by a given thread has the following effects:

■ Prevents the owning thread's process from leaving the balance set. ■ Raises the owning thread's priority to the the lowest real-time priority value if

the owning thread's priority is not already higher. ■ Prevents the delivery of normal kernel-mode APCs.

Each mutex object has an associated level value, and a given thread may acquireownership of a mutex if and only if:

■ The requested mutex is currently unowned. ■ The requesting thread does not already own a mutex of a (numerically) higher

level.These requirements, and the requirement that a mutex owner run exclusively inkernel mode, prevent deadlocks.Note that recursive ownership of a mutex is possible. However, any thread thatclaims a mutex N times must explicitly release that mutex N times in order to set themutex to the Signaled state.

Page 367: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-25

native servicesSee system services.

NLSNational language support

A set of routines that give applications access to locale-specific information. Seealso Unicode.

nonpaged poolSee pool memory.

NTFSNT file system

A file system, native to NT, that supports file system and file data recovery,extremely large storage media, and various other features.

objectA single, run-time instance of a system-defined object type. Objects visible in user modeinclude process, thread, section, file, event, semaphore, key, timer, port, object directory,symbolic link, and token objects.Many user-mode objects are implemented through the use of a corresponding kernel-mode object, including processes, threads, events, semaphores, and timers. Other user-mode objects contain "hidden" kernel-mode objects, such as the event associated witheach file object.

Kernel-mode-only objects include APC, DPC, device queue, interrupt, mutex,driver, device, adapter, controller, and stream file objects.

object directory objectA type defined by the Object Manager and used to implement hierarchical object names.

Note that file system directories are implemented as NT file objects, not as objectdirectory objects.

Page 368: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-26 Kernel-mode Driver Design Guide

object typeAn opaque data structure that defines a protected entity implemented and manipulated bythe operating system. See also opaque.

Every user-visible object has: ■ A type (whose name must be unique). ■ Two parts: 1 A header operated on by the NT Object Manager. 2 A body operated on by type-specific system services. For example,

the system service that reads a file operates on an open file object. ■ A set of standardized rules for object creation, deletion, protection, access,

management, and naming.User-visible objects can contain "hidden" kernel-mode objects. For example, a user-mode thread can wait on a file handle because it contains a hidden, kernel-modeevent object.Kernel-mode objects are visible to (and directly accessible only by) the executivecomponent that defined a given object type. They are are indirectly accessible toother kernel-mode code through the type-specific functions exported by the definingcomponent (e.g., KeInitializeDeviceQueue operates on device queue objects).

opaquePertains to any data structure (including NT objects) defined with a deliberately hiddeninternal structure that is known only to the executive component that defined the type.However, the defining component supplies system services and, usually, kernel-modefunctions to manipulate opaque objects. User-mode data types can also be opaque.

paging fileA system file containing the contents of virtual pages that have been paged out ofmemory. See also backing store.

page frameA block of contiguous physical addresses used to store the contents of a virtual page.Note that the virtual page size and page frame size are usually identical, but that a pageframe size is actually microprocessor-dependent.

paged poolSee pool memory.

Page 369: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-27

PCRProcessor control registers

An internal data structure in which the Kernel stores per-processor information.

PEBProcess environment block

PFNPage frame number

Also, the database in which the NT Memory Manager maintains information aboutevery physical page in the system. Each PFN is an index for a physical page in thearray of records that makes up the PFN database. See also PTE.

PIOProgrammed I/O

pollingA pernicious, but sometimes necessary, coding technique that wastes CPU cycles in adriver until the physical device updates its registers. NT device drivers should neverimplement polling loops and should minimize the interval they specify in calls toKeStallExecutionProcessor (no longer than 50 microseconds) if they must poll theirdevices.

Page 370: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-28 Kernel-mode Driver Design Guide

pool memoryRegions located in kernel space from which memory blocks can be allocated anddeallocated dynamically (some other systems call this "heap memory"). The NT MemoryManager creates two kinds of pools for system use and for use by drivers via Ex(ecutive)Support function calls:

1 Paged pool is a based region that can be paged in and out of a process's working set.Each process has its own set of PTEs that map paged pool into its address space.Touching a page in this pool can cause a page fault at any time, so drivers shouldallocate only those data structures accessed exclusively at IRQL PASSIVE_LEVELor APC_LEVEL (see IDT) from paged pool.

2 Nonpaged pool is a based region for which all processes share a set of PTEs. TheNT Memory Manager guarantees that nonpaged pool is resident in physical memoryat all times; therefore, this region can be accessed from any process's address spacewithout causing a page fault. However, nonpaged pool is a limited system resource.

Drivers should allocate from paged pool for entities that are accessed exclusively at orbelow IRQL APC_LEVEL. They should minimize their usage of nonpaged pool by"saving" it for data that must be accessed at or above IRQL DISPATCH_LEVEL.Allocations of contiguous or noncached memory come from nonpaged pool.The Memory Manager allocates entities from both pools using a buddy scheme. See alsokernel space.

port objectAn instance of an object type representing a conduit for messages between local(server/client) processes. It is used in the LPC (Local Procedure Call) model of NT as anobject accessible to user-mode processes through system services. The server (callee)process defines a named connection port object and sets up two (unnamed)communication port objects when a client (caller) process connects to the named port.

Page 371: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-29

port driver(1) An NT-supplied SCSI driver that supports a set of class drivers above it and one ormore HBA-specific miniport drivers, which link themselves to the NT-dependent portdriver (implemented as a dynamic-link library) and call its ScsiPortXxx routines.(2) An NT-supplied video adapter driver that supports a user-level display driver and oneor more adapter-specific miniport drivers, which link themselves to the OS-dependentport driver (implemented as a dynamic-link library) and call its VideoPortXxx routines.(3) A lowest-level device driver whose device is a so-called "intelligent controller" or abus adapter. A port driver communicates with one or more class drivers according to anestablished protocol and synchronizes access to the controller or bus.(4) A lowest-level physical device driver that responds to a set of system-defined deviceI/O control requests (and, possibly an additional set of driver-defined internal I/O controlrequests) sent down by a corresponding class driver.See also class driver and miniport driver.

POSIXPortable operating system for (UN)IX

PRCBProcessor control block

An extension of the PCR.

priorityAn attribute of a thread that determines when and how often it is scheduled to run. For arunning thread, its priority falls into either of two classes, each class with 16 levels:

1 Variable priority class has values in the range 0 to 15. This class is used by mostthreads.Threads with variable priority are always preemptible (scheduled to run round-robinwith other threads at the same level). In general, the Kernel manages a variable-priority thread as follows: when the thread is interactive with a user, its priority ishigh (given a boost); otherwise, its priority decays (by one level per quantum thethread runs) until it reaches its original (programmer-defined) base level.

2 Real-time priority class has values in the range 16 to 31. This class is used by time-critical threads, making such a thread preemptible only by a thread with higherpriority. Note that a thread running at the highest real-time priority level could bepreempted by a special Kernel APC if an APC_LEVEL interrupt occurs.

Page 372: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-30 Kernel-mode Driver Design Guide

priority boostsA set of system-defined constant values, supplied when certain device drivers completean IRP. In general, drivers for interactive devices call IoCompleteRequest with adevice-type-specific priority boost value. The boost is added to the priority of the user-mode thread that originally requested the I/O operation, in order to compensate for thatthread's assumed wait on the I/O.

probeTo check whether a memory access of a particular kind (e.g., read) to data (e.g., a handlefor an object or a buffer) is allowed without causing an access violation. Note that user-supplied argument values are always probed and captured (on the system stack) before anNT system service gets control in kernel mode. The system service is responsible forprobing whatever a captured pointer accesses.

process objectA Kernel-defined control object type, representing the virtual address space and controlinformation necessary for the execution of a set of thread objects. A user-mode processobject defines the base priority, default affinity, and directory table base value for itsthreads and for any child processes it creates. Every user-visible process object isimplemented through the use of an embedded kernel-mode process object.

A kernel-mode-only process object must be initialized before any thread objects thatbelong to the process.At any given moment, a process is either part of the balance set (Included) or not(Excluded). A kernel-mode process must remain in the balance set as long as any ofits threads owns a mutex object. Each thread owning one or more mutexes continuesto run until it releases its last mutex, when the thread is suspended. The process isnot removed from the balance set until every thread has released all mutexes.

protected subsystemA server that performs operating system functions or that emulates another operating TMsystem, such as Windows or POSIX, on top of the NT executive. See also integralsubsystem.

PTEPage table entry

The Memory Manager uses a PTE to represent the state of a virtual page.

Page 373: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-31

pulseTo set an event to the Signaled state, satisfy as many waiters on the event as possible, andto reset the event to the Not-Signaled state.

quantuma.k.a. "time slice"

A brief period of time during which a given thread executes in a multitaskingoperating system.

quotaFor each process, NT sets a limit on certain system resources the process's threads canuse, including quotas for paging-file, paged-pool, and nonpaged-pool usage, as well asfor CPU time. As the process's threads run, the Kernel subtracts the CPU time used fromthe quota. The Memory Manager "charges quota" against the process as its threads usepage-file, paged-pool, or nonpaged-pool memory; it also updates these values whenthreads release memory.

raise an exceptionA deliberate transfer of control to an exception handler when an exception occurs. Akernel-mode component, including any NT driver, cannot raise an exeception whilerunning at IRQL > APC_LEVEL without bringing down the system. See also SEH.

rangeA finite and discrete sequence of values. Note that a virtual address range can be backedby a set of discontiguous physical pages or by a file stored in discontiguous sectors ondisk.

RAWAn NT-supplied file system that is the "last resort" for all NT I/O requests requiring filesystem support. When the I/O Manager calls active NT file systems to mount a volume,RAW file system driver is always called last, because it recognizes all disk and tapemedia. However, RAW supplies very primitive file handling capabilities. For example, ittreats the whole disk as a single file and supplies physical-disk-level VBN access to thedisk.

RAW is not an acronym.

redirectorAn NT (network) file system driver that provides access to files on remote machines.

Page 374: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-32 Kernel-mode Driver Design Guide

regionA range of virtual addresses.

When a user-mode caller creates a section or maps a view, it must specify a region.The NT Memory Manager rounds the specified region's starting address down to thenearest host-alignment boundary and rounds its size in bytes up to the next host-page-size boundary.

registrya.k.a. "configuration registry"

A database containing configuration and control data for the system, includingwhich file systems and drivers to load at system boot. The NT ConfigurationManager exports system services to manipulate registry key objects contained in thedatabase.

resourceAn entity (such as a device object, file object, section object, variable, structure, orbuffer) visible to one or more processes.

In kernel mode, a shared resource is a multiprocessor-safe synchronizationmechanism, rather like a gating semaphore with a dynamic limit value. Drivers canuse system-supplied Ex(ecutive) Support and/or Rtl (Runtime Library) functions tocontrol access to a memory entity, such as a file or database, on a multiple-reader(shared access), single-writer (exclusive access) basis.

RPCRemote procedure call

A call from one process's thread (client) to another's (server) that exists in a differentaddress space, usually on another (networked) machine. See also IPC.

Rtl functionsThere are two general sets of (C) Runtime Library functions supplied with the NTsystem, one each for user mode and for kernel mode. All NT drivers can call the kernel-mode Rtl functions. An NT (highest-level) driver cannot call a user-mode Rtl functionunless it is executing in the context of the user-mode thread that requested the driver'scurrent I/O operation.

Page 375: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-33

runverb: to execute on a processor.noun: (1) within an MCB, a contiguous range of VBNs mapped to a contiguous range ofLBNs (a.k.a. "extent"); (2) slang for a quantum, as in "when the thread is given the run..."

runtime priority inversionA condition that can occur when threads with mismatched priority attributes shareresources or when a higher priority thread waits for a lower priority thread to completesome action. Such an inversion occurs whenever:

■ A high priority thread is blocked, waiting for a lower priority thread to release ashared resource or to complete an action (and probably to set a dispatcher object tothe Signaled state).

■ This lower priority thread is also blocked, because many other higher prioritythreads are ready for execution, so they will be run first.

Under these circumstances, the waiting high priority thread undergoes a runtime priorityinversion, because one or more lower priority threads will run before it does. Note thattwo threads with mismatched priorities must be very careful to avoid deadlocks if they"share" a resource in a mutually exclusive manner (that is, only one thread at a giventime can access the resource). Such a pair of threads should not attempt to use a spinlock. See also thread object, priority, resource, and deadlock.

SACLSystem access control list

A part of the NT security descriptor for an object, used to maintain per objectauditing information. See also security descriptor, ACE, and ACL.

SAMSecurity Account Manager

An integral subsystem that maintains a database of information on user accounts,including passwords, any account groups a given user belongs to, the access rightseach user is allowed, and any special privileges a given user has.

SCSISmall computer standard interface (pronunciation: "scuzzy")

NT SCSI drivers are assumed to comply with the ANSI SCSI-II standard. Inaddition, NT supplies an OS-dependent SCSI port driver, implemented as adynamic-link library, and several device-type-specific class drivers, together withsystem-defined interfaces to HBA-specific SCSI miniport drivers and additionalclass drivers.

Page 376: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-34 Kernel-mode Driver Design Guide

section objectA user-mode object type, representing a memory entity that can be mapped onto a rangeof virtual addresses in a process's address space. Note that creating or opening a sectionobject does not allocate physical memory. See also view.An opened section object can be either:

1 File-backed - The original entity resides as a file on a disk. 2 Page-backed - The original entity resides either in a paging file controlled by the

Memory Manager or (wholly or partially) in physical memory where storage ispage-granular and the page size is microprocessor-dependent.Unnamed sections are not sharable, but a view into an unnamed section can bemapped through the creating process's handle. Named sections are shareable. Allsections are inheritable by child processes.

security descriptorA data structure used to hold per-object security information, including the object'sowner, group, protection attributes, and audit information. See also ACE, ACL, SACLand SID.

security violationA user-mode attempt to access an object (by passing its handle to a system service)without having the correct, granted access rights for the requested operation. See alsoACE and ACL.

Note that the Security component does not check the access rights on object handlesused by kernel-mode code. However, the I/O Manager and network server drivercan force a security access check on object handles.

Page 377: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-35

SEHStructured exception handling

A feature of the NT system, which supports control transfers to exception handlerswhen certain runtime exceptions occur. The system traps the following generalkinds of exceptions:

1 Hardware-defined faults or traps, such as, ■ Access violations (see also access violation). ■ Data-type misalignments (such as a 16-bit entity aligned on an odd-

byte boundary). ■ Illegal and privileged instructions. ■ Invalid lock sequences (attempting to execute an invalid sequence

of instructions within an interlocked section of code). ■ Integer divides by zero and overflows. ■ Floating-point divides by zero, overflows, underflows, and

reserved operands. ■ Breakpoints and single steps (to support debuggers). 2 System software-defined exceptions, such as, ■ Guard-page violations (attempting to load or store data from or to a

location within a guard page). ■ Page read errors (attempting to read a page into memory and

encountering a concurrent I/O error). ■ Paging file quota exceeded (attempting to commit backing store for

a page currently being removed from a process's working set).NT supplies standard exception handlers for data-type alignment faults and forANSI/IEEE Std 754-1985 floating-point faults.Both user-mode and kernel-mode code can set up custom exception handlers usingthe except, try, and finally constructs currently supplied by NT-compatible Ccompilers and supported by NT-supplied Rtl (Run-time Library) functions. Anexception handler can be associated with each call frame in the function-callhierarchy of a program. A CONTEXT record contains the register state for eachexception handler.

Page 378: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-36 Kernel-mode Driver Design Guide

semaphore objectA user-mode semaphore gates access to resources, allowing some maximum number ofthreads (as specified when the semaphore object was created) to access the resourcesprotected by the semaphore.Each user-mode semaphore also has an associated current count, representing how manyadditional threads can acquire the semaphore. When the current count is zero, a threadattempting to acquire the semaphore waits (i.e., blocks) until the count is incremented(another thread has released that semaphore). Every user-mode semaphore object isimplemented through the use of a kernel-mode semaphore object.A kernel-mode semaphore is an instance of a Kernel-defined dispatcher object type. Ithas either of two uses:

1 A counting semaphore gates access to a resource by allowing some number ofthreads (up to a specified limit) to access the resource while the semaphore is set tothe Signaled state (semaphore count is nonzero).

2 A binary semaphore gates access to a single resource, if and only if the limit is set to1 and it is not possible for the semaphore to be over-Signaled (set to the Signaledstate when it is already in the Signaled state). A binary semaphore gates exclusiveaccess to a resource.However, using a semaphore with a limit of 1 to gate access is not the same thing asusing a mutex: semaphores have no ownership, deadlocks are possible, and theowner's priority is unaffected.

server(1) A process with one or more threads that accept requests from client processes. Seealso client/server.(2) An NT-supplied file system driver that provides remote access to files, named pipes,comm devices, and print devices. It implements the LAN Manager 2.1 SMB (servermessage block) protocol, along with NT-specific extensions.

Session ManagerAn integral subsystem that starts and keeps track of NT logon sessions and serves as anintermediary between the Windows NT and other protected subsystems.

Page 379: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-37

SFDSCSI filter driver

An add-on driver for a SCSI class device that is layered between the system-supplied class driver and the NT SCSI port driver. An SFD intercepts requests for aparticular SCSI peripheral device that has special features (not shared by other SCSIdevices of its class), or that does not accept standard SCSI-II CDBs, in order to setup device-specific requests. For other devices of the same class on the same bus, anSFD simply passes requests sent down from the system-supplied class driver on tothe NT SCSI port driver.

SIDSecurity identifier

A value, unique across time and space, that identifies a process in the Securitysystem. SIDs can either identify an individual process, usually containing a user'slogon identifier, or a group of processes.

SignaledAn attribute of Kernel-defined dispatcher objects that support synchronization. When theKernel sets such an object to the Signaled state, any threads waiting on the object arereleased from their wait and become elgible for execution.

single-byte coding schemeA character encoding scheme, such as ASCII, that uses a byte to represent each character.See also Unicode.

SMPSymmetric multiprocessor machine

See multiprocessor machine.

spin lockA spin lock is a low-level, synchronization mechanism defined by the NT Kernel. Adriver or any other kernel-mode component can use a spin lock to synchronize access to ashared resource, particularly in a multiprocessor machine.When one routine holds a particular spin lock, a second routine running on anotherprocessor cannot access the resource protected by that spin lock until it acquires the lock.While a routine attempts to acquire a spin lock, it cannot carry out other operations (itspins) until the lock has been acquired.Note that operations involving system-defined spin locks are IRQL-specific to helpprevent deadlocks: an interrupt spin lock can only be acquired while executing at aDIRQL of the associated interrupt object; an executive spin lock can only be acquiredwhile executing at DISPATCH_LEVEL IRQL.

Page 380: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-38 Kernel-mode Driver Design Guide

SRBSCSI request block

An NT-defined structure, used to communicate I/O requests from a SCSI classdriver to the NT-dependent port driver, which relays such request to appropriateHBA-specific miniport drivers.

stream file objectA virtual file representing on-disk data associated with a file, some of which mightnot be part of the physical file that backs a file object. For example, a stream fileobject makes it possible to cache the EAs or ACL for a file object together with thefile's data. See also volume file.

stripe driver(a.k.a. "striper")An intermediate driver whose job is to get better performance for file accesses thanthe underlying disk driver can. A stripe driver is layered between the FSD and a diskdevice driver (or between a mirror driver and the disk driver). It distributes pieces ofeach file over a set of partitions concurrently, thereby cutting down on synchronousread/write time for the file.

subjectThe combination of a (security) token and of an associated program that may use systemservices. See also token.

symbolic link(1) An instance of the symbolic link object type, representing a "soft alias" that equatesone name to another within the NT Object Manager's name space.(2) A file object with special properties. A symbolic link file is recognized as having thefollowing three attributes:

1 Its FILE_ATTRIBUTE_CONTROL bit is set. 2 It has an EA whose name is .FAMILY_IDs and whose 128-bit binary value is 42

(decimal). 3 It has an EA whose name is .SYMBOLIC_LINK_VALUE and whose value is the

fully qualified pathname of the target file.When a special file of this type is encountered as a component of a pathname, rather thanopening the file itself, the file system is redirected to the target file.

Note that the I/O Manager does not actually use a symbolic link object to implementsymbolic link files; it uses a file object.

Page 381: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-39

symbolic link objectAn instance of a type defined by the Object Manager, used to translate a reference to onenamed object into a different name.

system servicesThe set of native, user-mode routines exported by the NT executive. Each system servicehas a name of the form TwoLettersXxxYyy where:

■ TwoLetters is the prefix for all system services. ■ Xxx is usually a verb, describing the operation of a given service. ■ Yyy is generally the object type the service operates on.

system spaceSee kernel space.

system worker threadsA set of kernel-mode threads provided by the NT Executive Support component that NTdrivers, particularly FSDs, use to get work done within a nonarbitrary thread context.Such a driver sets up a work queue item with a driver-supplied callback routine that isqueued to be run when a system worker thread is dispatched for execution by the Kernel.See also FSP and thread object.

TBTranslation buffer

See TLB.

TEBThread environment block

Page 382: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-40 Kernel-mode Driver Design Guide

thread objectA user-mode thread object represents a path of execution within the current process.Every user-mode thread object is implemented through the use of an embedded kernel-mode thread object.A kernel-mode thread is an instance of a Kernel-defined dispatcher object type. It is thebasic schedulable entity in the NT system.A thread object:

■ Is dispatched for execution by the Kernel. ■ Has the following properties at any given moment: 1 Dispatch state 2 Priority 3 Context 4 Execution mode (kernel or user) 5 Affinity ■ Is "owned by" a process object but can attach itself to another process's address

space.Note that most NT drivers usually execute in the context of the currently running thread,that is, in an arbitrary thread context. While an NT file system driver can create anindependent process for its own device-dedicated threads, FSDs usually avoid setting upa driver-created process in order to conserve system memory. FSDs (and other NTdrivers) can set up device-dedicated (system-process) threads and/or FSDs can usesystem worker threads if they need a driver-specific thread context in which to execute.NT drivers use kernel-mode Ps (Process Structure) functions to create processes and/ordevice-dedicated threads, not the Ke(rnel) thread functions. FSDs call routines suppliedby the NT Executive Support component to use system worker threads.

TIDTarget identifier

One of up to eight target controllers on a SCSI-II bus through which peripheraldevices are addressable, either as numbered TIDs or as logical units (LUs)subordinate to a particular TID.

Page 383: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-41

timer objectA timer object is an instance of a Kernel-defined dispatcher object type. User-mode timerobjects can be used to synchronize the execution of specific actions, optionally an APC,with time. Kernel-mode timer objects are used to synchronize the execution of specificactions, in particular a DPC, with time.

When set to a specified interval: ■ The timer's state is reset to Not-Signaled. ■ The timer is placed in a queue that is ordered according to expiration time.

Any thread that waits on the timer is suspended until the timer is set to the Signaledstate when its interval expires.Expiration of the specified interval does not determine the end of a wait; theSignaled state does. Then, the Kernel attempts to satisfy as many waiters aspossible.

The I/O Manager also supplies timer functions for drivers to use.

TLBTranslation lookaside buffer

(Not directly accessible by system software for the i386; otherwise, an on-chipvirtual-to-page address translation cache for the i486 and MIPS microprocessors.)

toaster deviceslang: A SCSI peripheral device of an unknown class.

token objectA security object type, representing an authenticated user process. Every process has anassigned token, which becomes the default token for each of that process's threads.However, an individual thread can be assigned a token that overrides this default.

trap handlerA body of code in the Kernel to which the processor transfers control when an interruptor exception occurs. The trap handler determines the type of condition that caused theinterrupt or exception and transfers control to an ISR or exception handler.

UnicodeA fixed-width, 16-bit character encoding standard that NT uses to provide NLS supportfor locale-specific natural languages. See also NLS.

Page 384: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-42 Kernel-mode Driver Design Guide

user modeThe nonprivileged processor mode in which application code, including protectedsubsystem code, executes. User-mode threads can only gain access to system data bycalling system services. See also kernel mode.

valid pageA virtual page that is currently in physical memory. See also invalid page.

user spaceA block of virtual memory, dedicated to the use of the current user-mode process.Generally, the NT user space is a range of low-order virtual addresses around twogigabytes in size. See also hyperspace, kernel space, section object, and view.

VBNVirtual block number

A virtual block number identifies a block (a.k.a. "sector") relative to the start of afile. For a file with N blocks of data, the corresponding VBNs are 0 through (N - 1).

VCBVolume control block

An internal NT file system structure in which a file system maintains state about amounted volume.

VDMVirtual DOS machines

A protected subsystem that emulates DOS and Windows (earlier versions thanWindows NT) on top of NT.

viewA whole or partial mapping of a section object, always allocated on a 64K boundary, inthe virtual address space of a process. Note that mapping a view of a section that isbacked by an executable image file, in effect, "loads" the image. (Except during thesystem boot process, NT uses memory-mapped I/O, rather than a loader, to start programexecution.)

Page 385: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

Glossary of Terms and Acronyms G-43

virtual memoryA logical view of memory that does not necessarily correspond to the underlyingphysical memory structure. For example, a given range of virtual addresses might bemapped to (and backed by) some number of discontiguous physical pages, even thoughthe corresponding virtual pages could be accessed as a single, contiguous range.

VMCBVolume map control block

An opaque structure that stores VBN-to-LBN mappings for a volume file. FSDs canuse a set of FsRtl routines to maintain a VMCB for each mounted volume.

volume fileA virtual file, maintained by certain NT file systems, whose contents map ancillarystructures of the on-disk file system. A volume file is a type of stream file.

VPBVolume parameter block

A VPB is a structure that connects a file system's device object to the device uponwhich the volume is mounted. The file system's device object is actually used torepresent the volume (VPB) mounted on the actual device (physical device object).Device objects for physical disks, tapes, CD ROMs, and RAM disks have associatedVPBs.

VRPVideo request packet

A structure used to communicate device I/O control requests from a Windows NTdisplay driver to its corresponding adapter-specific miniport driver. The NT I/OManager sets up an IRP in response to a user-mode display driver's I/O controlrequest and calls the NT-supplied video port driver with the IRP. The video portdriver uses the IRP to set up a VRP and calls the miniport driver's StartIo entry pointwith the VRP.

window turnTurning a window refers to the process of filling an MCB with the appropriate retrievalpointers in order to map a VBN for a file to an LBN for a disk.

Page 386: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT

G-44 Kernel-mode Driver Design Guide

working setThe set of physical pages that are resident for a given process while it has one or morerunning threads. See also balance set and quota.

The size of each process's working set is bounded by a minimum number of pages(that the NT Memory Manager guarantees to be resident while the process has onerunning thread) and by a maximum. However, NT does not necessarily constrain aprocess's working set to its maximum if many free pages are available.

WORMWrite-once, read many

WSLWorking set list

The set of WSLEs for the current process.

WSLEWorking set list entry

zoneA block of dynamically allocatable and deallocatable storage of a specified and fixed sizewithin a block of equivalent zones. Each zone within such a block is aligned on aplatform-dependent boundary, determined by the processor's data cache line size. Callersof the Ex(ecutive) Support zone functions must synchronize their allocation anddeallocation of zones within the block.

For example, the I/O Manager uses zones to implement lookaside lists for fastallocation and deallocation of IRPs and MDLs.

Zw routinesA set of entry points parallel to the NT executive's system services. A call to a ZwXxxentry point from kernel-mode code (including calls from other system services or NTdrivers) supplies the corresponding system service, except the caller's access rights andthe arguments to the Zw "alias" are not checked for validity, and the call does not causethe previous mode to be set to user mode. It is the responsibility of a kernel-mode callerto check all user-supplied arguments for validity before such a caller passes them on to aZwXxx entry point. See also system services, probe, and SEH.

Page 387: Contents Chapter 1 - NT Drivers 1.1 Kinds of Drivers in Windows NT