Handling PnP Requests

Handling PnP Requests

A controller or a multifunction parent driver has two subdispatch routines for IRP_MJ_PNP requests one to handle requests it receives while wearing its FDO hat and another to handle requests it receives while wearing its PDO hat. Table 11-1 indicates the actions that the parent driver takes for each type of PnP request in its two roles. I ll explain the Parent Vote column later on.

Table 11-1. Parent Driver Handling of PnP Requests

PnP Request

FDO Hat

PDO Hat

Parent Vote?

IRP_MN_START_DEVICE

Normal

Succeed

N/A

IRP_MN_QUERY_REMOVE_ DEVICE

Normal

Succeed

N/A

IRP_MN_REMOVE_DEVICE

Normal

Succeed

N/A

IRP_MN_CANCEL_REMOVE_ DEVICE

Normal

Succeed

N/A

IRP_MN_STOP_DEVICE

Normal

Succeed

N/A

IRP_MN_QUERY_STOP_ DEVICE

Normal

Succeed

N/A

IRP_MN_CANCEL_STOP_ DEVICE

Normal

Succeed

N/A

IRP_MN_QUERY_DEVICE_ RELATIONS

Special pro- cessing for BusRelations query; other- wise normal

Special process- ing for Target - Relations query; otherwise ignore

No

IRP_MN_QUERY_INTERFACE

Normal

Special process - ing

No

IRP_MN_QUERY_CAPA BILITIES

Normal

Delegate

No

IRP_MN_QUERY_RESOURCES

Normal

Succeed

N/A

IRP_MN_QUERY_RESOURCE_ REQUIREMENTS

Normal

Succeed

N/A

IRP_MN_QUERY_DEVICE_ TEXT

Normal

Succeed

N/A

IRP_MN_FILTER_RESOURCE_ REQUIREMENTS

Normal

Succeed

N/A

IRP_MN_READ_CONFIG

Normal

Delegate

Yes

IRP_MN_WRITE_CONFIG

Normal

Delegate

Yes

IRP_MN_EJECT

Normal

Delegate

Yes

IRP_MN_SET_LOCK

Normal

Delegate

Yes

IRP_MN_QUERY_ID

Normal

Special processing

N/A

IRP_MN_QUERY_PNP_DEVICE _STATE

Normal

Special processing

No

IRP_MN_QUERY_BUS_INFOR- MATION

Normal

Delegate

Yes

IRP_MN_DEVICE_USAGE_ NOTIFICATION

Normal

Delegate

No

IRP_MN_SURPRISE_REMOVAL

Normal

Succeed

N/A

IRP_MN_QUERY_LEGACY_ BUS_INFORMATION

Normal

Delegate

Yes

Any other

Normal

Ignore

Yes

I used a shorthand notation in this table to indicate the action, as follows:

  • Normal means normal processing for a function driver. In other words, when wearing its FDO hat, the parent driver handles nearly every PnP request the same way a function driver would. Chapter 6 discusses a function driver s responsibilities in detail. For example, you pass all requests down the parent device stack except for a query you are causing to fail. You configure your device in response to IRP_MN_START_DEVICE. And so on.

  • Succeed means to complete the IRP with STATUS_SUCCESS.

  • Ignore means to complete the IRP with whatever status is already in the IRP s IoStatus field.

  • Delegate means to repeat the IRP on the parent device s FDO stack and return the same results back on the PDO stack.

I ll discuss the mechanics of these actions in the next few sections of this chapter.

Telling the PnP Manager About Our Children

The PnP Manager inquires about the children of every device by sending an IRP_MN_QUERY_DEVICE_RELATIONS request with the type code BusRelations. Wearing its FDO hat, the parent driver responds to this request with code like the following:

NTSTATUS HandleQueryRelations(PDEVICE_OBJECT fdo, PIRP Irp) { PDEVICE_EXTENSION pdx = ...; PIO_STACK_LOCATION stack = ...; 

if (stack->Parameters.QueryDeviceRelations.Type != BusRelations) return DefaultPnpHandler(fdo, Irp);

PDEVICE_RELATIONS newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS) + sizeof(PDEVICE_OBJECT)); newrel->Count = 2; newrel->Objects[0] = pdx->ChildA; newrel->Objects[1] = pdx->ChildB;

ObReferenceObject(pdx->ChildA); ObReferenceObject(pdx->ChildB);

Irp->IoStatus.Information = (ULONG_PTR) newrel; Irp->IoStatus.Status = STATUS_SUCCESS; return DefaultPnpHandler(fdo, Irp); }

  1. This IRP can concern several types of relations besides the bus relations we re interested in here. We simply delegate these other queries to the bus driver for the underlying hardware bus.

  2. Here we allocate a structure that will contain two device object pointers. The DEVICE_RELATIONS structure ends in an array with a dimension of 1, so we need only add on the size of an additional pointer when we calculate the amount of memory to allocate.

  3. We call ObReferenceObject to increment the reference counts associated with each of the device objects we put into the DEVICE_RELATIONS array. The PnP Manager will dereference the objects at an appropriate time.

  4. We need to pass this request down to the real bus driver in case it or a lower filter knows additional facts that we didn t know. This IRP uses an unusual protocol for pass down and completion. You set the IoStatus as shown here if you actually handle the IRP; otherwise, you leave the IoStatus alone. Note the use of the Information field to contain a pointer to the DEVICE_RELATIONS structure. In other situations we ve encountered in this book, the Information field has always held a number.

I glossed over an additional complication in the preceding code fragment that you ll notice in the code sample. An upper filter might have already installed a list of device objects in the IoStatus.Information field of the IRP. We must not lose that list. Rather, we must extend it by adding our own two device object pointers.

The PnP Manager automatically sends a query for bus relations at start time. You can force the query to be sent by calling this service function:

IoInvalidateDeviceRelations(pdx->Pdo, BusRelations);

A bus driver with hot-plug capability uses this function when it detects the insertion or removal of a child device. A controller or a multifunction driver with a fixed population of child devices wouldn t need to make this call.

PDO Handling of PnP Requests

In this section, I ll illustrate the mechanics of the PDO Hat column of Table 11-1. You already know from Chapter 6 and from the preceding section how to handle IRPs in the FDO role.

The Succeed Action

The parent should simply have many PnP IRPs succeed without doing any particular processing:

NTSTATUS SucceedRequest(PDEVICE_OBJECT pdo, PIRP Irp) { Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; }

The only remarkable feature of this short subroutine is that it doesn t change the IoStatus.Information field of the IRP. The PnP Manager always initializes this field in some way before launching an IRP. In some cases, the field might be altered by a filter driver or the function driver to point to some data structure or another. It would be incorrect for the PDO driver to alter the field.

The Ignore Action

The parent driver can ignore certain IRPs. Ignoring an IRP is similar to causing it to fail with an error code except that the driver doesn t change the IRP s status fields:

NTSTATUS IgnoreRequest(PDEVICE_OBJECT pdo, PIRP Irp) { NTSTATUS status = Irp->IoStatus.Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; }

Evidently, you re completing the IRP with whatever values already happen to be in IoStatus.Status and IoStatus.Information. The reason this strategy makes sense is that whoever originates a PnP request initializes these values to STATUS_NOT_SUPPORTED and 0, respectively. The PnP Manager gleans information from the fact that an IRP completes with these same values still in place, namely, that none of the drivers in the stack actually did anything with the IRP. The DDK instructions for function and filter drivers indicate that a driver that processes certain types of IRP is supposed to change the status to STATUS_SUCCESS before passing the IRP down the stack. Those instructions dovetail with ignore handling in some of Microsoft s bus drivers and in a controller or a multifunction driver built according to the pattern I m describing in this chapter.

The Delegate Action

The parent driver can simply delegate some PnP requests to the real bus driver that lies underneath the parent device s FDO. Delegation in this case is not quite as simple as just calling IoCallDriver because by the time we receive an IRP as a PDO driver, the I/O stack is generally exhausted. We must therefore create what I call a repeater IRP that we can send to the driver stack we occupy as FDO driver:

NTSTATUS RepeatRequest(PDEVICE_OBJECT pdo, PIRP Irp) { PPDO_EXTENSION pdx = (PPDO_EXTENSION) pdo->DeviceExtension; PDEVICE_OBJECT fdo = pdx->Fdo; PDEVICE_EXTENSION pfx = (PDEVICE_EXTENSION) fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); 

PDEVICE_OBJECT tdo = IoGetAttachedDeviceReference(fdo);

PIRP subirp = IoAllocateIrp(tdo->StackSize + 1, FALSE); PIO_STACK_LOCATION substack = IoGetNextIrpStackLocation(subirp); substack->DeviceObject = tdo; substack->Parameters.Others.Argument1 = (PVOID) Irp;

IoSetNextIrpStackLocation(subirp); substack = IoGetNextIrpStackLocation(subirp); RtlCopyMemory(substack, stack, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); substack->Control = 0;

BOOLEAN needsvote = <I'll explain later>; IoSetCompletionRoutine(subirp, OnRepeaterComplete, (PVOID) needsvote, TRUE, TRUE, TRUE);

subirp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoMarkIrpPending(Irp); IoCallDriver(tdo, subirp); return STATUS_PENDING } NTSTATUS OnRepeaterComplete(PDEVICE_OBJECT tdo, PIRP subirp, PVOID needsvote) {

ObDereferenceObject(tdo); PIO_STACK_LOCATION substack = IoGetCurrentIrpStackLocation(subirp);

PIRP Irp = (PIRP) substack->Parameters.Others.Argument1;

if (subirp->IoStatus.Status == STATUS_NOT_SUPPORTED) { if (needsvote) Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; } else Irp->IoStatus = subirp->IoStatus;

IoFreeIrp(subirp);

IoCompleteRequest(Irp, IO_NO_INCREMENT);

return STATUS_MORE_PROCESSING_REQUIRED; }

  1. We re going to send the repeater IRP to the topmost filter driver in the stack to which our FDO belongs. This service routine returns the address of the topmost device object, and it also adds a reference to the object to prevent the Object Manager from deleting the object for the time being.

  2. When we allocate the IRP, we create an extra stack location in which we can record some context information for the completion routine we re going to install. The DeviceObject pointer we place in this extra location becomes the first argument to the completion routine.

  3. Here we initialize the first real stack location, which is the one that the topmost driver in the FDO stack will receive. Then we install our completion routine. This is an instance in which we can t use the standard IoCopyCurrentIrpStackLocationToNext macro to copy a stack location: we re dealing with two separate I/O stacks.

  4. We need to plan ahead for how we re going to deal with the possibility that the parent device stack doesn t actually handle this repeater IRP. Our later treatment will depend on exactly which minor function of IRP we re repeating in a way I ll describe later on. Mechanically, what we do is calculate a Boolean value I called it needsvote and pass it as the context argument to our completion routine.

  5. You always initialize the status field of a new PnP IRP to hold the special value STATUS_NOT_SUPPORTED. The Driver Verifier will bugcheck if you don t.

  6. This statement is how we release our reference to the topmost device object in the FDO stack.

  7. We saved the address of the original IRP here.

  8. This short section sets the completion status for the original IRP. Refer to the following main text for an explanation of what s going on here.

  9. We allocated the repeater IRP, so we need to delete it.

  10. We can complete the original IRP now that the FDO driver stack has serviced its clone.

  11. We must return STATUS_MORE_PROCESSING_REQUIRED because the IRP whose completion we dealt with the repeater IRP has now been deleted.

The preceding code deals with a rather complex problem that afflicts the various PnP IRPs that a parent driver repeats on its FDO stack. The PnP Manager initializes PnP IRPs to contain STATUS_NOT_SUPPORTED. It can tell whether any driver actually handled one of these IRPs by examining the ending status. If the IRP completes with STATUS_NOT_SUPPORTED, the PnP Manager can deduce that no driver did anything with the IRP. If the IRP completes with any other status, the PnP Manager knows that some driver deliberately caused the IRP either to fail or to succeed but didn t simply ignore it.

A driver that creates a PnP IRP must follow the same convention by initializing IoStatus.Status to STATUS_NOT_SUPPORTED. As I remarked, the Driver Verifier will bugcheck if you forget to do this. But this initialization gives rise to the following problem: suppose one of the devices in the child stack (that is, above the PDO for the child device) changes IoStatus.Status to another value before passing a particular IRP down to us in our role as PDO driver. We will create a repeater IRP, preinitialized with STATUS_NOT_SUPPORTED, and pass it down the parent stack (that is, the stack to which we belong in our role as FDO driver). If the repeater IRP completes with STATUS_NOT_SUPPORTED, what status should we use in completing the original IRP? It shouldn t be STATUS_NOT_SUPPORTED because that would imply that none of the child-stack drivers processed the IRP (but one did, and it changed the main IRP s status). That s where the needsvote flag comes in.

For some of the IRPs we repeat, we don t care whether a parent driver actually processes the IRP. We say (actually, the Microsoft developers say) that the parent drivers don t need to vote on the IRP. If you look carefully at On RepeaterComplete, you ll see that we don t change the main IRP s ending status in this case. For other of the IRPs we repeat, we can t provide a real answer if the parent-stack drivers ignore the IRP. For these IRPs, on which the parent must vote, we have the main IRP fail with STATUS_UNSUCCESSFUL. To see which IRPs belong to the needs vote class and which IRPs don t, take a look at the last column in Table 11-1. The minor functions for which the table indicates N/A are ones that the parent driver never repeats on the parent stack in the first place, by the way.

If one of the parent drivers actually does process the repeater IRP, however, we copy the entire IoStatus field, which includes both the Status and Information values, into the main IRP. The Information field might contain the answer to a query, and this copy step is how we pass the answer upward.

I did one other slightly subtle thing in RepeatRequest I marked the IRP pending and returned STATUS_PENDING. Most PnP IRPs complete synchronously so that the call to IoCallDriver will most likely cause immediate completion of the IRP. So why mark the IRP pending and cause the I/O Manager unnecessary pain in the form of needing to schedule an asynchronous procedure call (APC) as part of completing the main IRP? The reason is that if we don t return STATUS_PENDING from our dispatch function recall that RepeatRequest is running as a subroutine below the dispatch function for IRP_MJ_PNP we must return exactly the same value that we use when we complete the IRP. Only our completion routine knows which value this will actually be after checking for STATUS_NOT_SUPPORTED and checking the needsvote flag, and there s no good way for our completion routine to communicate its decision back to the dispatch routine.

Handling Device Removal

The PnP Manager is aware of the parent-child relationship between a parent s FDO and its child PDOs. Consequently, when the user removes the parent device, the PnP Manager automatically removes all the children. Oddly enough, though, the parent driver should not normally delete a child PDO when it receives an IRP_MN_REMOVE_DEVICE. The PnP Manager expects PDOs to persist until the underlying hardware is gone. A multifunction driver will therefore not delete the children PDOs until it s told to delete the parent FDO. A bus driver for a hot-pluggable device, however, will delete a child PDO when it receives IRP_MN_REMOVE_DEVICE after failing to report the device during an enumeration.

Handling IRP_MN_QUERY_ID

The most important of the PnP requests that a parent driver handles is IRP_MN_QUERY_ID. The PnP Manager issues this request in several forms to determine which device identifiers it will use to locate the INF file for a child device. You respond by returning (in IoStatus.Information) a MULTI_SZ value containing the requisite device identifiers. The MULFUNC device has two children with the (bogus) device identifiers *WCO1104 and *WCO1105. It handles the query in the following way:

NTSTATUS HandleQueryId(PDEVICE_OBJECT pdo, PIRP Irp) { PPDO_EXTENSION pdx = (PPDO_EXTENSION) pdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); PWCHAR idstring; switch (stack->Parameters.QueryId.IdType) { 

case BusQueryInstanceID: idstring = L"0000"; break;

case BusQueryDeviceID: if (pdx->flags & CHILDTYPEA) idstring = LDRIVERNAME L"\\*WCO1104"; else idstring = LDRIVERNAME L"\\*WCO1105"; break;

case BusQueryHardwareIDs: if (pdx->flags & CHILDTYPEA) idstring = L"*WCO1104"; else idstring = L"*WCO1105"; break; default: return CompleteRequest(Irp, STATUS_NOT_SUPPORTED, 0); } ULONG nchars = wcslen(idstring); ULONG size = (nchars + 2) * sizeof(WCHAR); PWCHAR id = (PWCHAR) ExAllocatePool(PagedPool, size); wcscpy(id, idstring); id[nchars + 1] = 0; return CompleteRequest(Irp, STATUS_SUCCESS, (ULONG_PTR) id); }

  1. The instance identifier is a single string value that uniquely identifies a device of a particular type on a bus. Using a constant such as 0000 won t work if more than one device of the parent type can appear in the computer.

  2. The device identifier is a single string of the form enumerator\type and basically supplies two components in the name of the hardware registry key. Our ChildA device s hardware key will be in \Enum\Mulfunc\*WCO1104\0000, for example.

  3. The hardware identifiers are strings that uniquely identify a type of device. In this case, I just made up the pseudo-EISA (Extended Industry Standard Architecture) identifiers *WCO1104 and *WCO1105.

NOTE
Be sure to use your own name in place of MULFUNC if you construct a device identifier in the manner I showed you here. To emphasize that you shouldn t just copy my sample program s name in a hard-coded constant, I wrote the code to use the manifest constant LDRIVERNAME, which is defined in the DRIVER.H file in the MULFUNC project.

The Windows 98/Me PnP Manager will tolerate your supplying the same string for a device identifier that you supply for a hardware identifier, but the Windows XP PnP Manager won t. I learned the hard way to supply a made-up enumerator name in the device ID. Calling IoGetDeviceProperty to get the PDO s enumerator name leads to a bug check because the PnP Manager ends up working with a NULL string pointer. Using the parent s enumerator name ROOT in the case of the MULFUNC sample leads to the bizarre result that the PnP Manager brings the child devices back after you delete the parent!

Handling IRP_MN_QUERY_DEVICE_RELATIONS

The last PnP request to consider is IRP_MN_QUERY_DEVICE_RELATIONS. Recall that the FDO driver answers this request by providing a list of child PDOs for a bus relations query. Wearing its PDO hat, however, the parent driver need only answer a request for the so-called target device relation by providing the address of the PDO:

NTSTATUS HandleQueryRelations(PDEVICE_OBJECT pdo, PIRP Irp) { PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = Irp->IoStatus.Status; if (stack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation) { PDEVICE_RELATIONS newrel = (PDEVICE_RELATIONS) ExAllocatePool(PagedPool, sizeof(DEVICE_RELATIONS)); newrel->Count = 1; newrel->Objects[0] = pdo; ObReferenceObject(pdo); status = STATUS_SUCCESS; Irp->IoStatus.Information = (ULONG_PTR) newrel; } Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return

Handling IRP_MN_QUERY_INTERFACE

IRP_MN_QUERY_INTERFACE allows any driver in a PnP device stack to locate a direct-call interface in any lower driver in the same stack. A direct-call interface allows a driver to call directly one or more functions in another driver without first constructing an IRP. The basic concepts involved in the direct call interface mechanism are these:

  • A GUID identifies each unique direct-call interface. The interface itself is embodied in a structure that contains pointers to the functions that implement the interface s methods.

  • A driver that wants to use a particular interface originates a QUERY_INTERFACE request that includes the identifying GUID and an instance of the interface structure. Thereafter, such a driver can directly call functions pointed to by members of the interface structure. When the driver is done using the direct-call interface, it calls the InterfaceDereference function to release its reference to the exporting driver.

  • The driver that exports a particular interface looks for QUERY_INTERFACE requests that specify that interface s GUID. To handle such a request, the exporting driver fills in fields in an interface structure provided by the caller with pointers to functions inside that driver. That driver also undertakes not to unload from memory until the caller releases its reference to the interface.

I ll explain these concepts in more detail now. Refer to the MULFUNC sample in the companion content for a fully worked out example that uses these concepts in a real driver.

Identifying an Interface

You identify a direct-call interface by creating and publishing a GUID and a structure. Conventionally, the symbolic name of the GUID will be of the form GUID_XXX_STANDARD, to match the pattern established by the DDK header WDMGUID.H. For example, MULFUNC exports a direct-call interface with the following GUID:

DEFINE_GUID(GUID_RESOURCE_SUBALLOCATE_STANDARD, 0xaa04540, 0x6fd1, 0x11d3, 0x81, 0xb5, 0x0, 0xc0, 0x4f, 0xa3, 0x30, 0xa6);

The purpose of the RESOURCE_SUBALLOCATE interface is to permit child devices to divvy up I/O resources that technically belong to the parent device; I ll discuss how this works at the end of this chapter.

The structure associated with the RESOURCE_SUBALLOCATE interface is as follows (note that INTERFACE is declared in a DDK header because it s the base class of every direct-call interface):

typedef struct _INTERFACE { USHORT Size; USHORT Version; PVOID Context; PINTERFACE_REFERENCE InterfaceReference; PINTERFACE_DEREFERENCE InterfaceDereference; // interface specific entries go here } INTERFACE, *PINTERFACE; struct _RESOURCE_SUBALLOCATE_STANDARD : public INTERFACE { PGETRESOURCES GetResources; }; typedef struct _RESOURCE_SUBALLOCATE_STANDARD RESOURCE_SUBALLOCATE_STANDARD, *PRESOURCE_SUBALLOCATE_STANDARD;

In other words, the RESOURCE_SUBALLOCATE interface includes a GetResources function as well as the InterfaceReference and InterfaceDereference functions from the base class.

Locating and Using a Direct-Call Interface

A driver that wants to use a direct-call interface exported by a driver below it in the PnP stack constructs and sends a QUERY_INTERFACE request. Table 11-2 indicates the parameters in Parameters.QueryInterface for this request.

Table 11-2. Parameters for IRP_MN_QUERY_INTERFACE

Parameter

Meaning

InterfaceType

Pointer to GUID that identifies the interface

Size

Size of the interface structure pointed to by the Interface parameter

Version

Version of the interface structure

Interface

Address of interface structure to be filled in by the exporting driver

InterfaceSpecificData

Additional data expected by the driver that exports the interface depends on the interface

Here s an example of one way to issue the QUERY_INTERFACE request:

RESOURCE_SUBALLOCATE_STANDARD suballoc; // <== the eventual result KEVENT event; KeInitializeEvent(&event, NotificationEvent, FALSE); IO_STATUS_BLOCK iosb; PIRP Irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP, pdx->LowerDeviceObject, NULL, 0, NULL, &event, &iosb); PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp); stack->MinorFunction = IRP_MN_QUERY_INTERFACE; stack->Parameters.QueryInterface.InterfaceType = &GUID_RESOURCE_SUBALLOCATE_STANDARD; stack->Parameters.QueryInterface.Size = sizeof(suballoc); stack->Parameters.QueryInterface.Version = RESOURCE_SUBALLOCATE_STANDARD_VERSION; stack->Parameters.QueryInterface.Interface = &suballoc; stack->Parameters.QueryInterface.InterfaceSpecificData = NULL; NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; }

In this example, we use a synchronous IRP to communicate down the stack. We expect somebody underneath to fill in the suballoc structure and complete the IRP with a success status.

If the interface query succeeds, we can then directly call the functions to which members of the interface structure point. Ordinarily, each interface function requires a context argument taken from the returned interface structure, as in this example (see the SUBALLOC filter that s part of the MULFUNC sample):

PCM_RESOURCE_LIST raw, translated; status = suballoc.GetResources(suballoc.Context, pdx->Pdo, &raw, &translated);

The other arguments to an interface function, and the meaning of the return value, are matters to be decided by the designer of the interface.

When you re done using a direct-call interface, make the following call:

suballoc.InterfaceDereference(suballoc.Context);

Exporting a Direct-Call Interface

To export a direct-call interface, you need to handle the IRP_MN_QUERY_INTER FACE request. The first step will be to examine the interface GUID in the stack parameters to see whether the caller is trying to locate an interface you support. For example:

if (*stack->Parameters.QueryInterface.InterfaceType != GUID_RESOURCE_SUBALLOCATE_STANDARD) <default handling>

NOTE
The DDK headers contain operator statements to define C++ comparison operators for GUIDs. If you re writing your driver exclusively in C, use the IsEqualGuid function instead.

The DDK implies that a bus driver should fail a query for an unknown interface that it receives in its PDO role: [A] driver that handles this IRP should avoid passing the IRP to another device stack to get the requested interface. Such a design would create dependencies between the device stacks that are difficult to manage. For example, the device represented by the second device stack cannot be removed until the appropriate driver in the first stack dereferences the interface. I ll have to disagree and advise you to do the contrary in a controller or a multifunction parent driver. There is no other way for a child device driver to get access to functionality exported by the real bus. For the record, the parent device stack can t be removed until all child device stacks have been removed anyway, and child drivers should be clever enough to release their references to direct-call interfaces as part of their shutdown processing.

If the interface has evolved through more than one version, the next step in handling the interface query will be to decide which version of the interface to provide. A convenient convention is to begin numbering interface versions with 1 and increment the version number by 1 each time something important in the interface changes. Provide a manifest constant for the current version in the same header file that defines the interface GUID and structure. Callers will specify their desired version number in the IRP parameters by using this manifest constant, which effectively pinpoints the version of the structure with which they were compiled. You can then negotiate down to the oldest of the requested version and the newest version supported by your driver. For example:

USHORT version = RESOURCE_SUBALLOCATE_STANDARD_VERSION; if (version > stack->Parameters.QueryInterface.Version) version = stack->Parameters.QueryInterface.Version; if (version == 0) return CompleteRequest(Irp, Irp->IoStatus.Status);

If you start your version numbering with 1, the version number 0 can occur only if the caller has asked for version number 0. The correct response in that case is to complete the IRP with whatever initial status is in the IRP this value will usually be STATUS_NOT_SUPPORTED.

The third step is to initialize the interface structure provided by the caller. For example:

if (stack->Parameters.QueryInterface.Size < sizeof(RESOURCE_SUBALLOCATE_STANDARD)) return CompleteRequest(Irp, STATUS_INVALID_PARAMETER); PRESOURCE_SUBALLOCATE_STANDARD ifp = (PRESOURCE_SUBALLOCATE_STANDARD) stack->Parameters.QueryInterface.Interface; ifp->Size = sizeof(RESOURCE_SUBALLOCATE_STANDARD); ifp->Version = 1; ifp->Context = (PVOID) fdx; ifp->InterfaceReference = (PINTERFACE_REFERENCE) SuballocInterfaceReference; ifp->InterfaceDereference = (PINTERFACE_DEREFERENCE) SuballocInterfaceDereference; ifp->GetResources = (PGETRESOURCES) GetChildResources;

Finally you should reference the interface in such a way that your driver will stay loaded until the caller calls the InterfaceDereference function.

Handling IRP_MN_QUERY_PNP_DEVICE_STATE

In some situations, you ll want to suppress the Device Manager display of some or all child devices. To suppress the display, add the flag PNP_DEVICE_DONT_DISPLAY_IN_UI to the device flags reported in response to IRP_MN_QUERY_PNP_DEVICE_STATE. Apart from this optional step, you should delegate the IRP to the parent stack, as described earlier.



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