Programming the Microsoft Windows Driver Model

Overall Architecture

In Chapter 2, Figure 2-6 illustrates the topology of device objects when a parent device, such as a bus driver, has children. Controller and multifunction devices use a similar topology. The parent device plugs in to a standard bus. The driver for the standard bus detects the parent, and the PnP Manager configures it just like any ordinary device up to a point. After it starts the parent device, the PnP Manager sends a Plug and Play request with the minor function code IRP_MN_QUERY_DEVICE_RELATIONS to learn the so-called bus relations of the parent device. This query occurs for all devices, actually, because the PnP Manager doesn t know yet whether the device has children.

In response to the bus relations query, the parent device s function driver locates or creates additional device objects. Each of these objects becomes the physical device object (PDO) at the bottom of the stack for one of the child devices. The PnP Manager will go on to load the function and filter drivers for the child devices, whereupon you end up with a picture like that in Figure 2-6.

The driver for the parent device has to play two roles. In one role, it s the function device object (FDO) driver for the controller or the multifunction device. In the other role, it s the PDO driver for its child devices. In its FDO role, it handles PnP and power requests in the way function drivers normally handle them. In its PDO role, however, it acts as the driver of last resort for PnP and power requests.

Child Device Objects

The parent device driver for a multifunction device is responsible for creating PDOs for its child devices. There are two basic ways to do this task:

Device Extension Structures

A minor complication for a multifunction driver is that both the FDO and all the PDOs belong to the same driver object. This means that I/O request packets (IRPs) directed to any of these device objects will come to one set of dispatch routines. The driver needs to handle PnP and power IRPs differently for FDOs and PDOs. Consequently, you need to provide a way for a dispatch function to easily distinguish between an FDO and one of the child PDOs. One way to deal with this complication is to define two device extension structures with a common beginning, as follows:

// The FDO extension: typedef struct _DEVICE_EXTENSION { ULONG flags; } DEVICE_EXTENSION, *PDEVICE_EXTENSION; // The PDO extension: typedef struct _PDO_EXTENSION { ULONG flags; } PDO_EXTENSION, *PPDO_EXTENSION; // The common part: typedef struct _COMMON_EXTENSION { ULONG flags; } COMMON_EXTENSION, *PCOMMON_EXTENSION; #define ISPDO 0x00000001

The dispatch routines in the driver then look like this:

NTSTATUS DispatchSomething(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PCOMMON_EXTENSION pcx = (PCOMMON_EXTENSION) DeviceObject->DeviceExtension; if (pcx->flags & ISPDO) return DispatchSomethingPdo(DeviceObject, Irp); else return DispatchSomethingFdo(DeviceObject, Irp); }

That is, you distinguish between FDO and PDO roles by examining the header that both types of device extension have in common, and then you call an FDO-specific or PDO-specific routine to handle the IRP.

Example of Creating Child Device Objects

MULFUNC, which is available in the companion content, is a very lame multifunction device: it has just two children, and we always know what they are. I just called them A and B. MULFUNC executes the following code with more error checking than I m showing you here at IRP_MN_START_DEVICE time to create PDOs for A and B:

NTSTATUS StartDevice(PDEVICE_OBJECT fdo, ...) { PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

CreateChild(pdx, CHILDTYPEA, &pdx->ChildA); CreateChild(pdx, CHILDTYPEB, &pdx->ChildB); return STATUS_SUCCESS; } NTSTATUS CreateChild(PDEVICE_EXTENSION pdx, ULONG flags, PDEVICE_OBJECT* ppdo) { PDEVICE_OBJECT child;

IoCreateDevice(pdx->DriverObject, sizeof(PDO_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &child); PPDO_EXTENSION px = (PPDO_EXTENSION) child->DeviceExtension; px->flags = ISPDO flags; px->DeviceObject = child; px->Fdo = pdx->DeviceObject; child->Flags &= ~DO_DEVICE_INITIALIZING; *ppdo = child; return STATUS_SUCCESS; }

  1. CHILDTYPEA and CHILDTYPEB are additional flag bits for the flags member that begins the common device extension. If you were writing a true bus driver, you wouldn t create the child PDOs here you d enumerate your actual hardware in response to an IRP_MN_QUERY_DEVICE_RELATIONS and create the PDOs then.

  2. We re creating a named device object here, but we re asking the system to automatically generate the name by supplying the FILE_AUTOGENERATED_DEVICE_NAME flag in the DeviceCharacteristics argument slot.

The end result of the creation process is two pointers to device objects (ChildA and ChildB) in the device extension for the parent device s FDO.

Категории