Supporting Plug and Play primarily means implementing an
• IRP_MN_START_DEVICE
• IRP_MN_QUERY_REMOVE_DEVICE
• IRP_MN_REMOVE_DEVICE
• IRP_MN_CANCEL_REMOVE_DEVICE
• IRP_MN_STOP_DEVICE
• IRP_MN_QUERY_STOP_DEVICE
• IRP_MN_CANCEL_STOP_DEVICE
• IRP_MN_SURPRISE_REMOVAL
Looking at this list, it might not seem too complicated to handle Plug and Play in a driver. In fact, there are many things to get right. At a basic level this means:
• Coping with adding and removing devices
• Getting resource assignments
• Handling
• Handling
• Handling
However, as will be shown, it soon becomes apparent that you must also do the following tasks:
• Allow only I/O requests while the device is started
• Not allow a device to be removed while there are any open handles
• Queue I/O requests while the device is not started
• Wait until any I/O requests have completed before handling remove requests
• Process
• Pass unsupported IRPs down the stack
The rest of this chapter will look at the theory behind all the important PnP messages. The Wdm2 example driver shows how to implement most of the related tasks listed above. However, the most complicated — queuing I/O requests — is left until Chapter 16.
Refer to the previous chapter if you need to remind yourself of the difference between function and filter drivers, and the different types of device objects that the Wdm2 driver deals with.
When a PnP driver handles an
The PnP Manager will have worked out which drivers are going to be in the stack. The Physical Device Object (PDO) is created by the bus driver first. Then, going up the stack, each driver's
If a driver's
The job of an AddDevice routine is to create and initialize a device object for the current driver to use, and to connect it to the device stack. For function drivers, the device object is called a Functional Device Object (FDO). Filter drivers make a Filter Device Object (Filter DO). As mentioned in the last chapter, PDOs, FDOs, and Filter DOs all use the same DEVICE_OBJECT structure. Microsoft recommends that we use different names to help us remember what sort of driver owns the device object.
When a Wdm2 device is added, the Unknown bus driver makes a PDO for it. The PnP Manager passes the PDO to Wdm2. The
The eventual job of the
The Wdm1 driver handles adding a device in its
The
AddDevice
In the Wdm2 driver, the code for adding and removing devices is substantially the same as Wdm1. The
Most of the PnP handling code is in the Wdm2 Pnp.cpp module. Some changes have been made to the Dispatch.cpp code from the Wdm1 version. A new module DeviceIo.cpp handles device starting and stopping.
Listing 9.1 Wdm2 Wdm2AddDevice
NTSTATUS Wdm2AddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT pdo) {
DebugPrint('AddDevice');
NTSTATUS status;
PDEVICE_OBJECT fdo;
// Create our Functional Device Object in fdo
status = IoCreateDevice(DriverObject, sizeof(WDM2_DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNQWN, 0, FALSE, &fdo);
if (NT_ERROR(status)) return status;
// Initialise device extension
PWDM2_DEVICE_EXTENSION dx = (PWDM2_DEVICE_EXTENSION)fdo->DeviceExtension;
dx->fdo = fdo;
dx->pdo = pdo;
dx->UsageCount = 1;
KeInitializeEvent(&dx->StoppingEvent, NotificationEvent, FALSE);
dx->OpenHandleCount = 0;
dx->GotResources = false;
dx->Paused = false;
dx->IODisabled = true;
dx->Stopping = false;
dx->PowerState = PowerDeviceD3;
dx->PowerIdleCounter = NULL;
DebugPrint('FDO is %x',fdo);
// Initialise device power state
POWER_STATE NewState;
NewState.DeviceState = dx->PowerState;
PoSetPowerState(fdo, DevicePowerState, NewState);
// Register and enable our device interface
status = IoRegisterDevicelnterface(pdo, &WDM2_GUID, NULL, &dx->ifSymLinkName);
if (NT_ERROR(status)) {
IoDeleteDevice(fdo);
return status;
}
IoSetDeviceInterfaceState(&dx->ifSymLinkName, TRUE);
DebugPrint('Symbolic Link Name is %T',&dx->ifSymLinkName);
// Attach to the driver stack below us
dx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo.pdo);
// Set fdo flags appropriately
fdo->Flags &= ~DO_DEVICE_INITIALIZING;