[Previous] [Next]

IRP_MJ_PNP Dispatch Function

In Chapter 5, "The I/O Request Packet," I explained the mechanics of passing IRPs down the driver stack in two situations: one in which you care about the result and therefore need a completion routine, and the other in which you don't care about the result and therefore don't install a completion routine. Many of the PnP requests fit into the second of these categories—you're receiving the IRP and passing it down, but you don't care what happens to it afterward. To begin with, then, I suggest writing a helper function that you can use to pass a request down in the "don't care" scenario—see the code below.

NTSTATUS DefaultPnpHandler(PDEVICE_OBJECT fdo, PIRP Irp)
  {
  IoSkipCurrentIrpStackLocation(Irp);
  PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
  return IoCallDriver(pdx->LowerDeviceObject, Irp);
  }

A simplified version of the dispatch function for IRP_MJ_PNP might look like the following:



1
2

3





4

5
NTSTATUS DispatchPnp(PDEVICE_OBJECT fdo, PIRP Irp)
  {
  PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
  ULONG fcn = stack->MinorFunction;

  static NTSTATUS (*fcntab[])(PDEVICE_OBJECT, PIRP) = {
    HandleStartDevice,         // IRP_MN_START_DEVICE
    HandleQueryRemove,         // IRP_MN_QUERY_REMOVE_DEVICE
    <etc.>,
    };

  if (fcn >= arraysize(fcntab))
    return DefaultPnpHandler(fdo, Irp);
  return (*fcntab[fcn])(fdo, Irp);
  }

  1. All the parameters for the IRP, including the all-important minor function code, are in the stack location. Hence, we obtain a pointer to it by calling IoGetCurrentIrpStackLocation.
  2. We expect the IRP's minor function code to be one of those listed in Table 6-1.
  3. A method of handling the two dozen possible minor function codes is to write a subdispatch function for each one we're going to handle and then to define a table of pointers to those subdispatch functions. Many of the entries in the table will be DefaultPnpHandler. Subdispatch functions like HandleStartDevice will take pointers to a device object and an IRP as parameters and will return an NTSTATUS code.
  4. If we get a minor function code we don't recognize, it's probably because Microsoft defined a new one in a release of the DDK after the DDK with which we built our driver. The right thing to do is to pass the minor function code down the stack by calling the default handler. By the way, arraysize is a macro that returns the number of elements in an array. It's defined as #define arraysize(p) (sizeof(p)/sizeof((p)[0])).
  5. This is the operative statement in the dispatch routine, with which we index the table of subdispatch functions and call the right one.