[Previous] [Next]

Device and Driver Layering

The Windows Driver Model formalizes a layering of drivers, as illustrated in Figure 2-1. A stack of device objects appears at the left of the figure. The device objects are data structures that the system creates to help software manage hardware. Many of these data structures can exist for a single piece of physical hardware. The lowest-level device object in a stack is called the physical device object, or PDO for short. Somewhere in the middle of a device object stack is an object called the functional device object, or FDO. Above and below the FDO there might be a collection of filter device objects. Filter device objects above the FDO are called upper filters, whereas filter device objects below the FDO (but still above the PDO) are called lower filters.

Click to view at full size.

Figure 2-1. Layering of device objects and drivers in the WDM.

The Plug and Play (PnP) Manager component of the operating system constructs the stack of device objects at the behest of device drivers. For our purposes in this book, we can use the generic term bus to describe a piece of hardware to which devices connect electronically. This is a pretty broad definition. Not only does it include things like the PCI (Peripheral Component Interconnect) bus, but it also includes a SCSI (Small Computer System Interface) adapter, a parallel port, a serial port, a USB (universal serial bus) hub, and so on—anything, in fact, that can have multiple devices plugged into it. One responsibility of the driver for a bus is to enumerate the devices attached to the bus and to create PDOs for each of them. The PnP Manager begins painting the picture in Figure 2-1, then, by creating a PDO because some bus driver has detected some actual hardware.

Having created a PDO, the PnP Manager consults the registry database to find the filter and function drivers that occupy the middle of the figure. The setup program is responsible for many of these registry entries, and the INF files that control hardware installation are responsible for others. The registry entries define the order in which the drivers will appear in the stack, so the PnP Manager begins by loading the lowest-level filter driver and calling its AddDevice function. This function creates a FiDO, thus establishing the horizontal link between a FiDO and a driver. AddDevice then connects the PDO to the FiDO; that's where the line connecting the two device objects comes from. The PnP Manager proceeds upward, loading and calling each lower filter, the function driver, and each upper filter, until the stack is complete.

The purpose for the layering becomes apparent when you consider the flow of I/O requests diagrammed on the right-hand side of Figure 2-1. Each request for an operation affecting a device uses an I/O request packet (IRP). IRPs are normally sent to the topmost driver for the device and can percolate down the stack to the other drivers. At each level, the driver decides what to do with the IRP. Sometimes, a driver will do nothing except pass the IRP down. Other times, a driver might completely handle the IRP without passing it down. Still other times, a driver might process the IRP and pass it down, or vice versa. It all depends on the device and the exact semantics of the IRP. I'll explain in a later sidebar how it comes to pass that drivers can send IRPs down even though device objects are linked upward from the PDO.

The various drivers that occupy the stack for a single piece of hardware perform different roles. The function driver manages the device, represented by the FDO. The bus driver manages the connection between the device and the computer, represented by the PDO. Because of the close relationship between driver software and device object, I'll sometimes use the term FDO driver to mean the function driver and the term PDO driver to refer to the bus driver. The filter drivers, if they even exist, monitor or modify the stream of IRPs.

One of my seminar students, on seeing a diagram similar to Figure 2-1, was misled (I won't say by which teacher, who also wrote this book) into thinking of C++ and class inheritance. A perfectly reasonable way of designing an architecture for device drivers would be to define base classes from which programmers could derive progressively more specialized classes. In such a scheme, you could have a set of abstract classes that manage different sorts of PDOs, and you could derive FDO drivers from them. The system would send IRPs to virtual functions, some of which would be handled by the base class in the PDO driver and some of which would be handled by the derived class in the FDO driver. WDM doesn't work this way, though. The PDO driver performs completely different jobs from the FDO driver. The FDO driver "delegates" certain work to the PDO driver by passing IRPs down to it, but the relationship is more like being peers in a bucket brigade (and we won't discuss the contents of the metaphorical buckets!) than like being hierarchically related.

How the System Loads Drivers

Having presented this much description of device layering in the WDM, it's time for me to be a bit more precise. To begin with, there's an obvious chicken-and-egg problem with what I've described. I said that the bus driver creates the PDO, but I also said that the PnP Manager loads drivers based on registry entries for a PDO that already exists. So where does the bus driver come from? I'll explain that in the next section. The registry database plays a crucial role in the process of loading drivers and configuring devices, so I'll explain which registry keys are relevant and what they contain.

Recursive Enumeration

In the first instance, the PnP Manager has a built-in "driver" for a "root" bus that doesn't actually exist. The root bus conceptually connects the computer to all hardware that can't electronically announce its presence—including the primary hardware bus (such as PCI). The root bus driver gets information about the computer from the registry, which was initialized by the Windows 2000 Setup program. Setup got the information by running an elaborate hardware detection program and by asking the end user suitable questions. Consequently, the root bus driver knows enough to create a PDO for the primary bus.

The function driver for the primary bus can then enumerate its own hardware electronically. The PCI bus, for example, provides a way of accessing a special configuration space for each attached device, and the configuration space contains a description of the device and its resource requirements. When a bus driver enumerates hardware, it acts in the guise of an ordinary function driver. Having detected a piece of hardware, however, the driver switches roles: it becomes a bus driver and creates a new PDO for the detected hardware. The PnP Manager then loads drivers for this device PDO, as previously discussed. It might happen that the function driver for the device enumerates still more hardware, in which case the whole process repeats recursively. The end result will be a tree like that shown in Figure 2-2, wherein a bus device stack branches into other device stacks for the hardware attached to that bus.

Click to view at full size.

Figure 2-2. Layering of recursively enumerated devices.

The Role of the Registry

Three different registry keys bear on configuration. These are called the hardware key, the class key, and the service key. To be clear, these are not the proper names of specific subkeys: they are generic names of three keys whose pathnames depend on the device to which they belong. Broadly speaking, the hardware key contains information about a single device, the class key concerns all devices of the same type, and the service keys contains information about drivers. People sometimes use the name "instance key" to refer to the hardware key and "software key" to refer to the service key. The multiplicity of names derives from the fact that Windows 95/98 and Windows 2000 were written (mostly) by different people.

The Hardware (Instance) Keys Device hardware keys appear in the \System\CurrentControlSet\Enum subkey of the local machine branch of the registry. You normally can't look inside this key because the system grants access to the System account only. In other words, kernel-mode programs and user-mode services running in the System account can read from and write to the Enum key and its subkeys, but not even an administrator can do so. To see what's inside Enum, you can run REGEDT32.EXE from an administrator-privilege account and change the security settings. Figure 2-3 illustrates the hardware key for one of the sample devices that accompanies this book (namely, the USB42 sample I'll discuss in Chapter 11, "The Universal Serial Bus").

Click to view at full size.

Figure 2-3. A hardware key in the registry.

The subkeys on the first level below the Enum key correspond to the different bus enumerators in the system. The description of all past or present USB devices is in the …\Enum\USB subkey. I've expanded the key for the USB42 sample to show you how the device's hardware ID (vendor 0574, product 102A) has turned into the name of a key (Vid_0547&Pid_102A) and how a particular instance of the device that has that ID appears as a further subkey named 7&2. The 7&2 key is the hardware, or instance, key for this device.

Some of the values in the hardware key provide descriptive information that user-mode components such as the Device Manager can use. (You reach the Device Manager from the Management Console or, more easily, from the Hardware tab of the property sheet you get when you right-click the My Computer desktop icon and select Properties.) Figure 2-4 shows how the Device Manager portrays the properties of USB42. Refer to the sidebar "Accessing Device Keys from User Mode" for an indication of how the Device Manager can gather this information even though it can't, by itself, get past the normal security block to the Enum key.

Click to view at full size.

Figure 2-4. The Device Manager properties display for a device.

The hardware key also contains several values that identify the class of device to which the device belongs and the drivers for the device. ClassGUID is the ASCII representation of a globally unique identifier (GUID) that uniquely identifies a device class; in effect, it's a pointer to the class key for this device. Service is a pointer to the service key. Optional values (which USB42 doesn't have) named LowerFilters and UpperFilters, if present, would identify the service names for any lower or upper filter drivers, respectively.

Finally, a hardware key might have overriding values named Security, Exclusive, DeviceType, and DeviceCharacteristics that force the device object the driver will create to have certain attributes. I'll discuss the importance of these overrides later on when I tell you how to create a device object.

Most of the values in the hardware key get there automatically as part of the setup process or because the system recognizes new hardware (or gets told it about via the Hardware Wizard) sometime after initial setup. Some of the values get there because the INF file that's used to install the hardware directs that they be put there. I'll discuss INF files when I talk about how to plan for installation in Chapter 12, "Installing Device Drivers."

The Class Keys The class keys for all classes of device appear in the HKLM\System\CurrentControlSet\Control\Class key. Their key names are GUIDs assigned by Microsoft. Figure 2-5 illustrates the class key for SAMPLE devices, which is the class to which the USB42 sample and all the other sample drivers in this book belong.

Click to view at full size.

Figure 2-5. A class key in the registry.

The USB class isn't particularly interesting as it lacks some of the optional values that might be there, such as these:

Each device also has its own subkey below the class key. The name of this key is the Driver value in the device's hardware key. Refer to Figure 2-6 for an illustration of the contents of this subkey, the purpose of which is to correlate all these registry entries with the INF file used to install the device.

Click to view at full size.

Figure 2-6. A device-specific subkey of the device's class key in the registry.

The Service (Software) Keys The last key that's important for a device driver is the service key. It indicates where the driver's executable file is on disk and contains some other parameters that govern the way the driver is loaded. Service keys appear in the HKLM\System\CurrentControlSet\Services key. Refer to Figure 2-7 for USB42's service key.

Click to view at full size.

Figure 2-7. A service key in the registry.

It's not my purpose to rehash all the possible settings in the service key, which is splendidly documented in several places, including under the heading "Service Install" in the Platform Software Development Kit (SDK). In this particular case, the values have the following significance:

Order of Driver Loading

When the PnP Manager encounters a new device, it opens the hardware and class keys and proceeds to load drivers in the following order:

  1. Any lower filter drivers specified in the hardware key for the device. Since the LowerFilters value is of type REG_MULTI_SZ, it can specify more than one driver. They're loaded in the order in which they appear in the value's data string.
  2. Any lower filter drivers specified in the class key. Again, these are loaded in the order in which they appear in the LowerFilters value's data string.
  3. The driver specified by the Service value in the hardware key.
  4. Any upper filter drivers specified in the hardware key, in the order in which they appear in the UpperFilters data string.
  5. Any upper filter drivers specified in the class key, in the order in which they appear in the UpperFilters data string.

When I say the system "loads" a driver, I mean that it maps the driver's image into virtual memory, fixes up relocatable references, and calls the driver's main entry point. The main entry point is usually named DriverEntry. I'll describe the DriverEntry function a bit further on in this chapter. It might turn out that a particular driver is already present in memory, in which case nothing happens at the load stage except incrementing a reference count that will preserve the image in memory for however long some device needs it.

You might have noticed that the loading of upper and lower filters belonging to the class and to the device instance isn't neatly nested as you might have expected. Before I knew the facts, I guessed that device-level filters would be closer to the function driver than class-level filters. As we'll see later on, it's not very important in what order the loading occurs. However, the system calls the drivers' AddDevice functions (another topic I'll discuss in considerable detail shortly) in the same order in which the PnP Manager loads the drivers. Consequently, the device object stack will mirror this order, with possibly unexpected results.

How Device Objects Interrelate

The tree of device object stacks shown in Figure 2-2 doesn't imply that IRPs necessarily flow from a PDO to the top FiDO for the next lower branch of the tree. In fact, the driver for one stack's PDO is the FDO driver for the next lower branch, as illustrated by the shading in the figure. When the driver receives an IRP in its PDO role, it will do something to perform the IRP, but that might not involve sending the same, or even any other, IRP to the devices in the stack it occupies while performing its FDO role. Conversely, when a bus driver receives an IRP in its FDO role, it might or might not need to send some IRPs to one or more of the devices for which it acts as PDO.

A few examples should clarify the relationship between FiDOs, FDOs, and PDOs. The first example concerns a read operation directed to a device that happens to be on a secondary PCI bus that itself attaches to the main bus through a PCI-to-PCI bridge chip. To keep things simple, let's suppose that there's one FiDO for this device, as illustrated in Figure 2-8. You'll learn in later chapters that a read request turns into an IRP with a major function code of IRP_MJ_READ. Such a request would flow first to the upper FiDO and then to the function driver for the device. (That driver is the one for the device object marked FDOdev in the figure.) The function driver calls the hardware abstraction layer (HAL) directly to perform its work, so none of the other drivers in the figure will see the IRP.

Click to view at full size.

Figure 2-8. The flow of a read request for a device on a secondary bus.

A variation on the first example is shown in Figure 2-9. Here we have a read request for a device plugged into a USB hub that itself is plugged into the host controller. The complete device tree therefore contains stacks for the device, for the hub, and for the host controller. The IRP_MJ_READ flows through the FiDO to the function driver, which then sends one or more IRPs of a different kind downward to its own PDO. The PDO driver for a USB device is USBHUB.SYS, and it forwards the IRPs to the topmost driver in the host controller device stack, skipping the two-driver stack for the USB hub in the middle of the figure.

Click to view at full size.

Figure 2-9. The flow of a read request for a USB device.

The third example is similar to the first, except that the IRP in question is a notification concerning whether a disk drive on a PCI bus will or will not be used as the repository for a system paging file. You'll learn in Chapter 6, "Plug and Play," that this notification takes the form of an IRP_MJ_PNP request with the minor function code IRP_MN_DEVICE_USAGE_NOTIFICATION. In this case, the FiDO driver will pass the request to the FDOdev driver, which will take note of it and pass it further down the stack to the PDOdev driver. This particular notification has implications about how other I/O requests that concern the PnP system or power management will be handled, so the PDOdev driver sends an identical notification to the stack within which it's the FDObus, as illustrated in Figure 2-10. (Not all bus drivers work this way, but the PCI bus does.)

Click to view at full size.

Figure 2-10. The flow of a device usage notification.

Examining the Device Stack

To better visualize the way device objects and drivers are layered, it helps to have a tool. I wrote the DEVVIEW utility, which you'll find on the companion disc, for this purpose. I'll be describing other uses for DEVVIEW in this chapter, but the feature that concerns us now is its ability to display the device objects that are used to manage hardware devices. With the so-called Answer device plugged into my USB hub, I ran DEVVIEW and generated the two screen shots shown in Figure 2-11 and Figure 2-12.

This particular device uses only two device objects. The PDO is managed by USBHUB.SYS, whereas the FDO is managed by USB42.SYS (the image for the Answer device). In the first of these screen shots, you can see other information about the PDO. Based on our exploration of the registry keys associated with USB42, it should now be clear where that information came from.

It's worth experimenting with DEVVIEW on your own system to see how various drivers are layered for the hardware you own.

Click to view at full size.

Figure 2-11. DEVVIEW information about USB42's PDO.

Click to view at full size.

Figure 2-12. DEVVIEW information about USB42's FDO.

Driver Objects

The I/O Manager uses a driver object data structure to represent each device driver. See Figure 2-13. Like many of the data structures we'll be discussing, the driver object is partially opaque. This means that you and I are only supposed to directly access or change certain fields in the structure, even though the DDK headers declare the entire structure. I've shown the opaque fields of the driver object in the figure with a gray background. These opaque fields are analogous to the private and protected members of a C++ class, and the accessible fields are analogous to public members.

Figure 2-13. The DRIVER_OBJECT data structure.

The DDK headers declare the driver object, and all other kernel-mode data structures for that matter, in a stylized way, as this excerpt from WDM.H illustrates:

typedef struct _DRIVER_OBJECT {
  CSHORT Type;
  CSHORT Size;
  ...
  } DRIVER_OBJECT, *PDRIVER_OBJECT;

That is, the header declares a structure with a type name of DRIVER_OBJECT. It also declares a pointer type (PDRIVER_OBJECT) and assigns a structure tag (_DRIVER_OBJECT). This declaration pattern appears many places in the DDK, and I won't mention it again. The headers also declare a small set of type names (like CSHORT) to describe the atomic data types used in kernel mode. Table 2-1 lists some of these names. CSHORT, for example, means "signed short integer used as a cardinal number."

Table 2-1. Common type names for kernel-mode drivers.

Type NameDescription
PVOID, PVOID64Generic pointers (default precision and 64-bit precision)
NTAPIUsed with service function declarations to force use of _ _stdcall calling convention on x86 architectures
VOIDEquivalent to "void"
CHAR, PCHAR8-bit character, pointer to same (signed or not according to compiler default)
UCHAR, PUCHARUnsigned 8-bit character, pointer to same
SCHAR, PSCHARSigned 8-bit character, pointer to same
SHORT, PSHORTSigned 16-bit integer, pointer to same
USHORT, PUSHORTUnsigned 16-bit integer, pointer to same
LONG, PLONGSigned 32-bit integer, pointer to same
ULONG, PULONGUnsigned 32-bit integer, pointer to same
WCHAR, PWSTRWide (Unicode) character or string
PCWSTRPointer to constant Unicode string
NTSTATUSStatus code (typed as signed long integer)
LARGE_INTEGERSigned 64-bit integer
ULARGE_INTEGERUnsigned 64-bit integer
PSZ, PCSZPointer to ASCIIZ (single-byte) string or constant string
BOOLEAN, PBOOLEANTRUE or FALSE (equivalent to UCHAR)

I'll briefly discuss the accessible fields of the driver object structure now.

DeviceObject (PDEVICE_OBJECT) anchors a list of device object data structures, one for each of the devices managed by the driver. The I/O Manager links the device objects together and maintains this field. The DriverUnload function of a non-WDM driver would use this field to traverse the list of device objects in order to delete them. A WDM driver probably doesn't have any particular need to use this field.

DriverExtension (PDRIVER_EXTENSION) points to a small substructure within which only the AddDevice (PDRIVER_ADD_DEVICE) member is accessible to the likes of us. (See Figure 2-14.) AddDevice is a pointer to a function within the driver that creates device objects; this function is rather a big deal, and I'll discuss it at length later in this chapter.

Figure 2-14. The DRIVER_EXTENSION data structure.

HardwareDatabase (PUNICODE_STRING) describes a string that names a hardware database registry key for the device. This is a name like "\Registry\Machine\Hardware\Description\System" and names the registry key within which resource allocation information resides. WDM drivers have no need to access the information below this key because the PnP Manager performs resource allocation automatically. The name is stored in Unicode. (In fact, all kernel-mode string data uses Unicode.) I'll discuss the format and the use of the UNICODE_STRING data structure in the next chapter.

FastIoDispatch (PFAST_IO_DISPATCH) points to a table of function pointers that file system and network drivers export. How these functions are used is beyond the scope of this book. If you're interested in learning more about file system drivers, consult Rajeev Nagar's Windows NT File System Internals: A Developer's Guide (O'Reilly & Associates, 1997).

DriverStartIo (PDRIVER_STARTIO) points to a function in your driver that processes I/O requests that the I/O Manager has serialized for you. I'll discuss request queuing in general and the use of this routine in particular in Chapter 5, "The I/O RequestPacket."

DriverUnload (PDRIVER_UNLOAD) points to a cleanup function in your driver. I'll discuss this function a bit further on in connection with DriverEntry, but you might as well know now that a WDM driver probably doesn't have any significant cleanup to do anyway.

MajorFunction (array of PDRIVER_DISPATCH) is a table of pointers to functions in your driver that handle each of the roughly two dozen types of I/O request. This table is also something of a big deal, as you might guess, because it defines how I/O requests make it into your code.

Device Objects

Figure 2-15 illustrates the format of a device object and uses the same shading convention for opaque fields that I used in the preceding discussion of driver objects. As the author of a WDM driver, you will create some of these objects by calling IoCreateDevice, but the I/O Manager will be responsible for managing them.

DriverObject (PDRIVER_OBJECT) points to the object describing the driver associated with this device object, usually the one that called IoCreateDevice to create it. Filter drivers sometimes need to use this pointer to find the driver object for a device they're filtering so that they can inspect entries in the MajorFunction table.

NextDevice (PDEVICE_OBJECT) points to the next device object that belongs to the same driver as this one. This field is the one that links device objects together starting from the driver object's DeviceObject member. There's probably no reason for a WDM driver to use this field.

Figure 2-15. The DEVICE_OBJECT data structure.

CurrentIrp (PIRP) points to the I/O request packet most recently sent to the corresponding driver's StartIo function. I'll have more to say about the CurrentIrp field in Chapter 5 when I discuss cancel routines.

Flags (ULONG) contains a collection of flag bits. Table 2-2 lists the bits that are accessible to driver writers.

Table 2-2. Accessible flags in a DEVICE_OBJECT data structure.

Flag Description
DO_BUFFERED_IO Reads and writes use the buffered method (system copy buffer) for accessing user-mode data
DO_EXCLUSIVE Only one thread at a time allowed to open a handle
DO_DIRECT_IO Reads and writes use the direct method (memory descriptor list) for accessing user-mode data
DO_DEVICE_INITIALIZING Device object not initialized yet
DO_POWER_PAGABLE IRP_MJ_PNP must be handled at PASSIVE_LEVEL
DO_POWER_INRUSH Device requires large in-rush of current during power-on
DO_POWER_NOOP Device doesn't participate in power management

Characteristics (ULONG) is another collection of flag bits describing various optional characteristics of the device. (See Table 2-3.) The I/O Manager initializes these flags based on an argument to IoCreateDevice. Filter drivers propagate them upward in the device stack.

Table 2-3. Characteristics flags in a DEVICE_OBJECT data structure.

FlagDescription
FILE_REMOVABLE_MEDIAMedia can be removed from device
FILE_READ_ONLY_DEVICEMedia can only be read, not written
FILE_FLOPPY_DISKETTEDevice is a floppy disk drive
FILE_WRITE_ONCE_MEDIAMedia can be written once
FILE_REMOTE_DEVICEDevice accessible through network connection
FILE_DEVICE_IS_MOUNTEDPhysical media is present in device
FILE_DEVICE_SECURE_OPENCheck security on device object during open operations

DeviceExtension (PVOID) points to a data structure you define that will hold per-instance information about the device. The I/O Manager allocates space for the structure, but its name and contents are entirely up to you. A common convention is to declare a structure with the type name DEVICE_EXTENSION. To access it given a pointer (for example, fdo ) to the device object, use a statement like this one:

PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

It happens to be true (now, anyway) that the device extension immediately follows the device object in memory. It would be a bad idea to rely on this always being true, though, especially when the documented method of following the DeviceExtension pointer will always work.

DeviceType (DEVICE_TYPE) is an enumeration constant describing what type of device this is. The I/O Manager initializes this member based on an argument to IoCreateDevice. Filter drivers might conceivably need to inspect it. At the date of this writing, there are roughly 50 possible values for this member. (See Table 2-4.)

Table 2-4. Device type codes and default security.

Device TypeDefault Security
FILE_DEVICE_BEEPPublic Open Unrestricted
FILE_DEVICE_CD_ROMModified Public Default Unrestricted
FILE_DEVICE_CD_ROM_FILE_SYSTEMPublic Default Unrestricted
FILE_DEVICE_CONTROLLERPublic Open Unrestricted
FILE_DEVICE_DATALINKPublic Open Unrestricted
FILE_DEVICE_DFSPublic Open Unrestricted
FILE_DEVICE_DISK Modified Public Default Unrestricted
FILE_DEVICE_DISK_FILE_SYSTEMPublic Default Unrestricted
FILE_DEVICE_FILE_SYSTEMPublic Default Unrestricted
FILE_DEVICE_INPORT_PORTPublic Open Unrestricted
FILE_DEVICE_KEYBOARDPublic Open Unrestricted
FILE_DEVICE_MAILSLOTPublic Open Unrestricted
FILE_DEVICE_MIDI_INPublic Open Unrestricted
FILE_DEVICE_MIDI_OUTPublic Open Unrestricted
FILE_DEVICE_MOUSEPublic Open Unrestricted
FILE_DEVICE_MULTI_UNC_PROVIDERPublic Open Unrestricted
FILE_DEVICE_NAMED_PIPEPublic Open Unrestricted
FILE_DEVICE_NETWORK Modified Public Default Unrestricted
FILE_DEVICE_NETWORK_BROWSERPublic Open Unrestricted
FILE_DEVICE_NETWORK_FILE_SYSTEM Modified Public Default Unrestricted
FILE_DEVICE_NULLPublic Open Unrestricted
FILE_DEVICE_PARALLEL_PORTPublic Open Unrestricted
FILE_DEVICE_PHYSICAL_NETCARDPublic Open Unrestricted
FILE_DEVICE_PRINTERPublic Open Unrestricted
FILE_DEVICE_SCANNERPublic Open Unrestricted
FILE_DEVICE_SERIAL_MOUSE_PORTPublic Open Unrestricted
FILE_DEVICE_SERIAL_PORTPublic Open Unrestricted
FILE_DEVICE_SCREENPublic Open Unrestricted
FILE_DEVICE_SOUNDPublic Open Unrestricted
FILE_DEVICE_STREAMSPublic Open Unrestricted
FILE_DEVICE_TAPEPublic Open Unrestricted
FILE_DEVICE_TAPE_FILE_SYSTEMPublic Default Unrestricted
FILE_DEVICE_TRANSPORTPublic Open Unrestricted
FILE_DEVICE_UNKNOWNPublic Open Unrestricted
FILE_DEVICE_VIDEOPublic Open Unrestricted
FILE_DEVICE_VIRTUAL_DISK Modified Public Default Unrestricted
FILE_DEVICE_WAVE_INPublic Open Unrestricted
FILE_DEVICE_WAVE_OUTPublic Open Unrestricted
FILE_DEVICE_8042_PORTPublic Open Unrestricted
FILE_DEVICE_NETWORK_REDIRECTORPublic Open Unrestricted
FILE_DEVICE_BATTERYPublic Open Unrestricted
FILE_DEVICE_BUS_EXTENDERPublic Open Unrestricted
FILE_DEVICE_MODEMPublic Open Unrestricted
FILE_DEVICE_VDMPublic Open Unrestricted
FILE_DEVICE_MASS_STORAGE Modified Public Default Unrestricted
FILE_DEVICE_SMBPublic Open Unrestricted
FILE_DEVICE_KSPublic Open Unrestricted
FILE_DEVICE_CHANGERPublic Open Unrestricted
FILE_DEVICE_SMARTCARDPublic Open Unrestricted
FILE_DEVICE_ACPIPublic Open Unrestricted
FILE_DEVICE_DVDPublic Open Unrestricted
FILE_DEVICE_FULLSCREEN_VIDEOPublic Open Unrestricted
FILE_DEVICE_DFS_FILE_SYSTEMPublic Open Unrestricted
FILE_DEVICE_DFS_VOLUMEPublic Open Unrestricted
FILE_DEVICE_SERENUMPublic Open Unrestricted
FILE_DEVICE_TERMSRVPublic Open Unrestricted
FILE_DEVICE_KSECPublic Open Unrestricted

StackSize (CCHAR) counts the number of device objects starting from this one and descending all the way to the PDO. The purpose of this field is to inform interested parties about how many stack locations should be created for an IRP that will be sent first to this device's driver. WDM drivers don't normally need to modify this value, however, because the support routines they use for building the device stack do so automatically.