The Two Basic Data Structures

The Two Basic Data Structures

This section describes the two most basic data structures that concern a WDM driver: the driver object and the device object. The driver object represents the driver itself and contains pointers to all the driver subroutines that the system will ever call on its own motion. (For the sake of completeness, you should know that you often provide pointers to other routines within your driver as arguments in various kernel-mode service calls.) The device object represents an instance of hardware and contains data to help you manage that instance.

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 supposed to directly access or change only 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.

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 the type name 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 (such as CSHORT) to describe the atomic data types used in kernel mode. CSHORT, for example, means signed short integer used as a cardinal number. Table 2-1 lists some of these names.

figure 2-13 the driver_object data structure.

Figure 2-13. The DRIVER_OBJECT data structure.

Table 2-1. Common Type Names for Kernel-Mode Drivers

Type Name

Description

PVOID, PVOID64

Generic pointers (default precision and 64-bit precision)

NTAPI

Used with service function declarations to force use of __stdcall calling convention on i86 architectures

VOID

Equivalent to void

CHAR, PCHAR

8-bit character, pointer to same (signed or not according to compiler default)

UCHAR, PUCHAR

Unsigned 8-bit character, pointer to same

SCHAR, PSCHAR

Signed 8-bit character, pointer to same

SHORT, PSHORT

Signed 16-bit integer, pointer to same

CSHORT

Signed short integer, used as a cardinal number

USHORT, PUSHORT

Unsigned 16-bit integer, pointer to same

LONG, PLONG

Signed 32-bit integer, pointer to same

ULONG, PULONG

Unsigned 32-bit integer, pointer to same

WCHAR, PWSTR, PWCHAR

Wide (Unicode) character or string

PCWSTR

Pointer to constant Unicode string

NTSTATUS

Status code (typed as signed long integer)

LARGE_INTEGER

Signed 64-bit integer

ULARGE_INTEGER

Unsigned 64-bit integer

PSZ, PCSZ

Pointer to ASCIIZ (single-byte) string or constant string

BOOLEAN, PBOOLEAN

TRUE or FALSE (equivalent to UCHAR)

NOTE
Note on 64-bit Types: The DDK headers contain type names that will make it relatively painless for driver authors to compile the same source code for either 32-bit or 64-bit Intel platforms. For example, instead of blithely assuming that a long integer and a pointer are the same size, you should declare variables that might be either a LONG_PTR or a ULONG_PTR. Such a variable can hold either a long (or unsigned long) or a pointer to something. Also, for example, use the type SIZE_T to declare an integer that can count as high as a pointer might span you ll get a 64-bit integer on a 64-bit platform. These and other 32/64 typedefs are in the DDK header file named BASETSD.H.

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 in the section The AddDevice Routine later in this chapter.

figure 2-14 the driver_extension data structure.

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.

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 requests. 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 ll create some of these objects by calling IoCreateDevice.

DriverObject (PDRIVER_OBJECT) points to the object describing the driver associated with this device object, usually the one that called IoCreateDevice to create it.

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. That s just as well because proper use of this pointer requires synchronization using an internal system lock that s not exposed for access by device drivers.

figure 2-15 the device_object data structure.

Figure 2-15. The DEVICE_OBJECT data structure.

CurrentIrp (PIRP) is used by the Microsoft IRP queuing routines StartPacket and StartNextPacket to record the IRP most recently sent to your StartIo routine. WDM drivers should implement their own IRP queues (see Chapter 5) and may have no use for this field.

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

Table 2-2. 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 is 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 isn t initialized yet.

DO_POWER_PAGABLE

IRP_MJ_PNP must be handled at PASSIVE_LEVEL.

DO_POWER_INRUSH

Device requires large inrush of current during power-on.

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 some of them upward in the device stack. (See the detailed discussion of filter drivers in Chapter 16 for more information about flag propagation.)

Table 2-3. Characteristics Flags in a DEVICE_OBJECT Data Structure

Flag

Description

FILE_REMOVABLE_MEDIA

Media can be removed from device.

FILE_READ_ONLY_DEVICE

Media can only be read, not written.

FILE_FLOPPY_DISKETTE

Device is a floppy disk drive.

FILE_WRITE_ONCE_MEDIA

Media can be written once.

FILE_REMOTE_DEVICE

Device accessible through network connection.

FILE_DEVICE_IS_MOUNTED

Physical media is present in device.

FILE_VIRTUAL_VOLUME

This is a virtual volume.

FILE_AUTOGENERATED_DEVICE_NAME

I/O Manager should automatically generate a name for this device.

FILE_DEVICE_SECURE_OPEN

Force security check during open.

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 over 50 possible values for this member. Consult the DDK documentation entry Specifying Device Types in the MSDN Library for a list.

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 regarding 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 routine they use for building the device stack (IoAttachDeviceToDeviceStack) does so automatically.

AlignmentRequirement (ULONG) specifies the required alignment for data buffers used in read or write requests to this device. WDM.H contains a set of manifest constants ranging from FILE_BYTE_ALIGNMENT and FILE_WORD_ALIGNMENT up to FILE_512_BYTE_ALIGNMENT for these values. The values are just powers of 2 minus 1. For example, the value 0x3F is FILE_64_BYTE_ALIGNMENT.



Programming the Microsoft Windows Driver Model
Programming the Microsoft Windows Driver Model
ISBN: 0735618038
EAN: 2147483647
Year: 2003
Pages: 119
Authors: Walter Oney

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net