In this chapter some general guidelines for designing and coding a Windows 2000 device driver were covered. The prerequisites to coding the initial Windows 2000 device driver are now in place. In the next chapter, an actual device driver is implemented.
Chapter 6. Initialization and Cleanup Routines
Everything has to start somewhere. In the case of a Windows 2000 kernel-mode driver, the starting point is a function called DriverEntry. This chapter shows how to write a DriverEntry routine along with various other pieces of initialization and cleanup code. At the conclusion of this chapter, a minimal driver is produced that can actually load into the system.
Writing a DriverEntry Routine
Every Windows 2000 kernel-mode or WDM driver, regardless of its purpose, has to expose a routine whose
The I/O Manager calls a DriverEntry routine once it loads the driver. As Table 6.1 shows, the DriverEntry routine runs at PASSIVE_LEVEL IRQL, which means it has access to page system resources.
The DriverEntry routine receives a pointer to its own driver object, which it must initialize. It also receives a UNICODE_STRING containing the
What a DriverEntry Routine Does
Although the exact details vary based on whether a driver is WDM or kernel-mode, in general the following steps are taken in a DriverEntry routine.
Table 6.1. Function Prototype for DriverEntry
It is important to understand that steps 1 and 3 to 6 are not performed by a WDM driver's DriverEntry routine. They are deferred to another routine, AddDevice.
If DriverEntry should fail during initialization for any reason, it should release any system resources it may have allocated up to the failure, and return an appropriate NTSTATUS failure code to the I/O Manager.
The following sections describe some of the steps in more detail. The process of finding and allocating hardware is complex and heavily impacted by the device's ability to be autorecognized. Chapter 9 describes this step in more detail. The use of an interrupt object and DPCs is deferred until chapter 8.
Announcing DriverEntry Points
The I/O Manager is able to locate the DriverEntry routine because it has a well-known name. (Actually, the linker announces the address of DriverEntry using a command line switch. Nevertheless, the DDK documentation
These function pointers fall into two categories.
The following code fragment shows how a DriverEntry routine initializes both kinds of function pointers.
pDO->DriverStartIo = StartIo; pDO->DriverUnload = Unload; // // Initialize the MajorFunction Dispatch table // pDO->MajorFunction[ IRP_ MJ_CREATE ] = DispatchCreate; pDO->MajorFunction[ IRP_MJ_CLOSE ] = DispatchClose; :
Creating Device Objects
Once hardware is identified and allocated, the next step is to create a device object for each physical or virtual device that is to be exposed to the rest of the system. Most of the work is done by the IoCreateDevice function, which takes a description of the device and returns a device object, complete with an attached device extension. IoCreateDevice also links the new device object into the list of devices managed by this driver object. Table 6. 2 contains a description of this function.
The DeviceType parameter of
is simply a 16-bit value describing the class of device being added. Microsoft
One final point about creating device objects: Although this chapter describes the use of IoCreateDevice from DriverEntry, it is usually called from AddDevice (WDM) and occasionally from a Dispatch routine. In such a case, it is imperative that the driver reset a bit in the Flags field of the device object once it is created. This bit is called DO_DEVICE_INITIALIZING and is normally reset upon return from DriverEntry. The bit is reset with code similar to the following:
pDevObject->Flags &= ~DO_DEVICE_INITIAILIZING;
Do not clear this bit until the device object is actually initialized and ready to process
Table 6.2. Function Prototype for IoCreateDevice
Choosing a Buffering Strategy
If the IoCreateDevice call succeeds, the I/O Manager must be notified of whether buffered I/O or direct I/O will be used with the device. The choice is designated by selecting appropriate bits into the Flags field of the new device object.
The next chapter explains how user buffers are accessed using both techniques. If
Devices in Windows 2000 can have more than one name. Internally, the name specified by IoCreateDevice is the name by which the device is known to the Windows 2000 Executive itself. This internal name is (almost) completely hidden from Win32 user applications. In order to expose the device to the Win32 subsystem, the Win16 subsystem, or the virtual DOS environment, the new device must be given a symbolic link name in addition to its internal name.
These two types of names live in different
Figure 6.1. Internal and Symbolic Link names in the Object Manager's Namespace.
Internal and symbolic link names follow different device naming conventions. Internal device names tend to be longer, and they always end in a
To create a symbolic link name, use IoCreateSymbolicLink . This function takes an existing device name and a new symbolic name (both passed as UNICODE_STRING data types).
Finally, it must be noted that the selection of the device name is not