The New WDM IRP Dispatch Functions

< BACK  NEXT >
[oR]

The AddDevice function, called by the PnP Manager, merely initializes the device (and its extension) object. It is apparent from the AddDevice code that hardware is not yet touched. In fact, two general responsibilities remain for a driver.

  • Reserve and configure hardware resource requirements for the device

  • Initialize the hardware to prepare it for use

Both tasks are performed by a driver upon receipt of a special IRP function (and subfunction) code that is new for WDM drivers: IRP_MJ_PNP. PnP IRP codes are sent by the PnP Manager when a variety of events occur, including

  • Device initialization (perhaps due to insertion)

  • Device shutdown (perhaps due to removal)

  • Configuration queries

As described in chapter 6, IRP major function codes, such as Read and Write requests, are handled by an indexed table of Dispatch routines. Since an entire category of IRP_MJ_PNP messages are routed through this single Dispatch routine, it is the responsibility of this handler to perform a secondary dispatch using the minor subcode contained within the IRP. For PnP, the minor subcodes take the symbolic form IRP_MN_XXX, where XXX is a specific Plug and Play action requested by the PnP Manager.

An example of the initialization of the major function dispatch is shown below.

 ... pDriverObject->MajorFunction[IRP_MJ_PNP] =                DispPnP; 

The code to perform the secondary dispatch based upon the minor subcode of the IRP is shown below.

 NTSTATUS DispPnp(       IN PDEVICE_OBJECT pDO,                              IN PIRP pIrp ) {      // Obtain current IRP stack location      PIO_STACK_LOCATION pIrpStack;      pIrpStack = IoGetCurrentIrpStackLocation( pIrp );      switch (pIrpStack->MinorFunction) {      case IRP_MN_START_DEVICE:           ...      case IRP_MN_STOP_DEVICE:           ...      default:           // if not supported here, just pass it down           return PassDownPnP(pDO, pIrp);      }      // all paths from the switch statement will "return"      // the results of the handler invoked } 

Required Plug and Play IRPs

In order to be WDM compliant, drivers must support specific PnP IRPs, depending upon the type of device object nonbus FDO, bus FDO, and PDO. Table 9.3 lists the subcodes that must be supported for all device object types.

Table 9.3. PnP IRP Minor Codes Supported by All WDM Drivers
PnP IRP Minor Code Meaning
IRP_MN_START_DEVICE (Re)Initialize device with specified resources
IRP_MN_QUERY_STOP_DEVICE May device be stopped now for possible resource reassignment?
IRP_MN_STOP_DEVICE Stop device, await potential restart or removal
IRP_MN_CANCEL_STOP_DEVICE Notifies that previous QUERY_STOP will not be enacted
IRP_MN_QUERY_REMOVE_DEVICE May device be safely removed now?
IRP_MN_REMOVE_DEVICE Undoes work of AddDevice
IRP_MN_CANCEL_REMOVE_DEVICE Notifies that previous QUERY_REMOVE will not be enacted
IRP_MN_SURPRISE_REMOVAL Notifies that device has been removed without prior warning

From an examination of Table 9.3, it should be apparent that PnP devices exist in one of many states and, indeed, the DDK provides a state diagram depicting transitions based on PnP IRPs processed by a driver. The states can be divided into two categories: the states traversed by a device while it is being inserted (i.e., the prestart states) and the states encountered after the device is started. A state diagram for the prestart states is shown in Figure 9. 4. The only responsibility of a driver during these state transitions is to correctly implement DriverEntry and AddDevice.

Figure 9.4. Prestart device states.
graphics/09fig04.gif

Once a device has entered the "Started" state, PnP IRPs direct all subsequent transitions. The possibilities are depicted in Figure 9.5. Drivers maintain post-start state within the device extension.

Figure 9.5. Post-start device states.
graphics/09fig05.gif

PDO Plug and Play IRPs

In addition to the PnP IRPs that must be handled by all WDM drivers, PDOs typically implement handlers for other minor subcodes of IRPs, as shown in Table 9.4. These IRP requests permit a driver to implement additional features such as device eject and reassignment of hardware resources. The closer a driver is to the hardware (i.e., the physical device or PDO), the more likely it is that a driver should support one or more of these codes.

Table 9.4. PnP IRP Minor Codes Supported by PDO Drivers
PnP IRP Minor Code Meaning
IRP_MN_QUERY_CAPABILITIES What features does device support? (e.g., lock, eject, surprise removal)
IRP_MN_QUERY_DEVICE_RELATIONS Request for information about related device objects (FDOs, PDOs, or filter DOs)
IRP_MN_ QUERY_INTERFACE Request for support of a specific interface
IRP_MN_ EJECT Request to eject device from slot
IRP_MN_ SET_LOCK Request to lock device

Passing Down Plug and Play Requests

All PnP requests are initiated by the PnP Manager. The PnP Manager always routes these requests to the highest driver in a device stack.

Regardless of which PnP minor codes are handled by a driver, those that are not must be passed down the device stack to lower drivers, which may implement their own handler. Indeed, it is typical for a functional driver (controlling an FDO) to rely upon the physical driver (controlling a PDO) to implement many PnP requests. In turn, the physical driver relies upon the bus driver (i.e., a bus FDO) to implement many PnP requests.

Passing a PnP request down the device stack is necessary for several reasons. Some drivers within the stack must "add value" to the request, and no one driver may assume that the complete response can be compiled from that level. In other cases, many levels of the stack benefit from a PnP notification. For example, a stopped device notice is critical to all layers.

To pass down a PnP request, a driver taking action on the request must mark the IRP as complete (described in chapter 7) by setting the IoStatus.Status and IoStatus.Information fields of the IRP as appropriate. It then invokes IoCopyCurrentStackLocationToNext and IoCallDriver on the lower device. The lower device is known from the AddDevice call to IoAttachDeviceToDeviceStack, the result of which was saved in the device extension. An example of this technique is shown below.

 ... IoCopyCurrentStackLocationToNext(pIrp); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)       pDO->DeviceExtension; IoCallDriver(pDevExt->pLowerDevice, pIrp); ... 

If a driver has no need to await the completion of lower drivers handling the passed down request, a more efficient mechanism for skipping the current IRP stack location can be utilized. The function IoSkipCurrentIrpStackLocation simply removes the current IRP stack location from participation in the IRP processing. Indeed, this is the suggested mechanism for handling PnP requests that are not handled by a driver and merely passed to the next lower driver:

 NTSTATUS PassDownPnP( IN PDEVICE_OBJECT pDO,                          IN PIRP pIrp ) {      IoSkipCurrentIrpStackLocation( pIrp );      PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)           pDO->DeviceExtension;      return IoCallDriver(pDevExt->pLowerDevice, pIrp); } 

Sometimes a driver passes down a PnP request before it can complete its own work on the request. For example, in response to a start request, IRP_MN_START_DEVICE, a driver typically needs to wait until lower-level drivers have started before starting its own hardware. The bus and any lower-level hardware initializes before individual devices start. Thus, a higher-level driver must first pass down the request and then await lower-level processing before continuing. This is best handled with a completion routine tied to the IRP by the higher-level driver.

I/O Completion Routines

An I/O Completion routine is an I/O Manager callback that lets a driver layer recapture an IRP after a lower-level driver has completed it. I/O Completion routines are registered by a higher-level driver with IoSetCompletionRoutine described in Table 9.5. When a lower-level driver ultimately calls IoCompleteRequest, the I/O Completion routine executes as the IRP bubbles its way back to the top of the driver hierarchy.

Except for the driver on the bottom, each driver in the hierarchy can attach its own I/O Completion routine to an IRP. The I/O Completion routines execute in the driver-stacking order, from bottom to top.

Table 9.5. Function Prototype for IoSetCompletionRoutine
VOID IoSetCompletionRoutine IRQL <=DISPATCH_LEVEL
Parameter Description
IN PIRP pIrp Pointer to IRP being tracked
IN PIO_COMPLETION_ROUTINE CompletionRoutine Function to receive control when IRP completes
IN PVOID pContext Argument ultimately passed to completion routine
IN BOOLEAN bInvokeOnSuccess Call completion routine if IRP succeeds
IN BOOLEAN bInvokeOnError Call completion routine if IRP fails
IN BOOLEAN bInvokeOnCancel Call completion routine if IRP cancels
Return value - void -

The three Boolean arguments passed to IoSetCompletionRoutine determine when and if the Completion routine ultimately runs. As an IRP returns "up the device stack," the field IoStatus.Status is used in conjunction with the three arguments to determine whether or not to invoke the registered routine.

The prototype for an I/O Completion routine is described in Table 9.6. An example of the use of an I/O Completion routine to regain control of a PnP IRP after handling by a lower-level driver follows:

 ... IoCopyCurrentStackLocationToNext(pIrp); // Register the presence of a completion routine. // The completion routine is called when the IRP //   is "completed" by the lower level. IoSetCompletionRoutine( pIrp, OnIoComplete, NULL,                               TRUE, TRUE, TRUE); PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)      pDO->DeviceExtension; IoCallDriver(pDevExt->pLowerDevice, pIrp); ... NTSTATUS OnIoComplete( PDEVICE_OBJECT pDO, PIRP pIrp,                          PVOID pContext) {      // Perform post processing for IRP request here      // At what IRQL level does this code run?      // (see text below for explanation)      ...      return pIrp->IoStatus.Status; } 

Unfortunately, it is difficult to predict at what IRQL level a completion routine executes. If the lower-level driver calls IoCompleteRequest from PASSIVE_LEVEL IRQL, then higher-level I/O Completion routines execute at PASSIVE_LEVEL. If the lower-level driver completes the IRP request from DISPATCH_LEVEL (for example, from a DPC routine), then the higher-level Completion routines execute at DISPATCH_LEVEL.

Table 9.6. Function Prototype for an I/O Completion Routine
NTSTATUS OnIoCompletion IRQL == ??? (see text)
Parameter Description
IN PDEVICE_OBJECT pDevObj Pointer to Device object
IN PIRP pIrp Pointer to IRP just completed
IN PVOID pContext Argument passed from IoSetCompletionRoutine
Return value STATUS_MORE_PROCESSING_REQUIRED
STATUS_SUCCESS

Since PnP IRP requests sent by the PnP Manager execute at PASSIVE_LEVEL, no special design is necessary to ensure that a returned (completed from a lower level) PnP IRP continues to execute at PASSIVE_LEVEL. Code executing at DISPATCH_LEVEL is restricted in the kernel calls it may use and, as described in chapter 3, must ensure it does not reference paged memory. To ensure that PnP handlers execute at PASSIVE_LEVEL, a kernel Event object is used.

A kernel Event object is a synchronization mechanism that is analogous to a flag. A thread of execution patiently waits for the Event flag to be raised (set) without consuming CPU resource. Once the Event flag is raised, the blocked (waiting) thread is scheduled for resumed operation. A full description of the use of kernel Events is contained in chapter 14.

For now, however, it is sufficient to note that the Event flag could be used as a signal between an I/O Completion routine and a PASSIVE_LEVEL thread within a higher-level driver. To use a kernel event, storage must be reserved by the programmer in nonpaged memory. The full technique is shown below.

 ... IoCopyCurrentStackLocationToNext(pIrp); // Reserve space for a kernel Event object KEVENT event; // And initialize it, flag DOWN KeInitializeEvent( &event, NotificationEvent, FALSE); // Register the presence of a completion routine. // Pass the Event object to the Completion routine. IoSetCompletionRoutine( pIrp, OnIoComplete,                          (PVOID)&event,                          TRUE, TRUE, TRUE); // Call the lower level(s) PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)      pDO->DeviceExtension; IoCallDriver(pDevExt->pLowerDevice, pIrp); // Wait for lower level(s) to complete KeWaitForSingleObject( &event, Executive, KernelMode,                              FALSE, NULL); // Perform post-processing functions here... // On return from Wait, PASSIVE_LEVEL IRQL guaranteed ... NTSTATUS OnIoComplete( PDEVICE_OBJECT pDO, PIRP pIrp,                          PVOID pContext) {      // cast the pContext arg into what it really is:      //  an Event pointer.      PEVENT pEvent = (PEVENT) pContext;      // Raise the Event flag to signal waiting thread      KeSetEvent(pEvent, 0, FALSE);      // Hold off further higher level processing      // until this level completes:      return STATUS_MORE_PROCESSING_REQUIRED; } 

Table 9.7. PnP IRP Minor Codes Supported by Bus Drivers
PnP IRP Minor Code Meaning
IRP_MN_QUERY_RESOURCES Requests boot configuration resources
IRP_MN_ QUERY_RESOURCE_REQUIREMENTS Requests resource information for a device
IRP_MN_QUERY_ID Request for device instance ID
IRP_MN_ QUERY_DEVICE_TEXT Request for device description and/or location
IRP_MN_QUERY_BUS_INFORMATION Request parent bus instance and ID
IRP_MN_ READ_CONFIG Request to read configuration space of bus slot occupied by device
IRP_MN_ WRITE_CONFIG Request to write configuration space

Bus Driver Plug and Play Requests

In the unusual circumstance where a bus driver or filter must be written, it should be noted that some PnP IRP requests must be handled by such a driver. These minor code handlers are additional requirements above and beyond those already listed and are described in Table 9.7.

< BACK  NEXT >


The Windows 2000 Device Driver Book(c) A Guide for Programmers
The Windows 2000 Device Driver Book: A Guide for Programmers (2nd Edition)
ISBN: 0130204315
EAN: 2147483647
Year: 2000
Pages: 156

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