Every Windows 2000 kernel-mode or WDM driver, regardless of its purpose, has to expose a routine whose name is DriverEntry. This routine initializes various driver data structures and prepares the environment for all the other driver components. Execution ContextThe 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 path to the driver's service key in the Registry. WDM drivers have little use for this registry name. Kernel-mode drivers rely on the string to extract any driver-specific parameters stored in the system registry. The Registry String takes the form HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\ DriverName. What a DriverEntry Routine DoesAlthough 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.
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 PointsThe 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 mandates that this entry point be called DriverEntry.) Other driver routines don't have fixed names, so the I/O Manager needs some other way to locate them. The linkage mechanism is the driver object, which contains pointers to other driver functions. A DriverEntry routine is responsible for setting up these function pointers. 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 ObjectsOnce 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 IoCreateDevice is simply a 16-bit value describing the class of device being added. Microsoft reserves the first half of this range for predefined device types. Above 32767, private device types can be defined. Beware, though, that conflict with another vendor's device is always possible. Currently, Microsoft predefines about 30 device types. The predefined device type values are given symbolic names of the form FILE_DEVICE_XXX (e.g., FILE_DEVICE_DVD). 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 requests.
Choosing a Buffering StrategyIf 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 neither bit is set in the Flags field, the I/O Manager assumes that the driver needs no further help from the I/O Manager when accessing the user's buffer. Device NamesDevices 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 parts of the Object Manager's namespace. The Object Manager maintains a directory of names for all resources managed by the operating system. Internal device names are stored beneath the \Device section of the directory tree. Symbolic link names appear beneath the \?? tree. Figure 6.1 illustrates this relationship. When using IoCreateDevice, the entire \Device name must be supplied. For example, "\\Device\\Minimal0" is a suitable device name string. (The double backslash (\\) is necessary in C so that the character following the first backslash \ will not be considered a special character sequence, like \t.) 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 zero-based number (e.g., FloppyDisk0 or FloppyDisk1). Symbolic link names follow the usual pattern of A through Z for file-system devices, and names ending in a one-based number for other devices (e.g., LPT1 or LPT2). 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 necessarily the option of the driver. In the WDM world, bus drivers, class drivers, and many Plug and Play devices define their own names. Chapter 9 describes this process more fully. In the meantime, non-WDM drivers must supply their own unique names for each device added.
|