Supporting Plug and Play primarily means implementing an AddDevice routine and an IRP_MJ_PNP handler. This PnP IRP has eight minor function codes that most WDM drivers need to support.

• IRP_MN_START_DEVICE (Start Device)

• IRP_MN_QUERY_REMOVE_DEVICE (Query Remove)

• IRP_MN_REMOVE_DEVICE (Remove Device)

• IRP_MN_CANCEL_REMOVE_DEVICE (Cancel Remove)

• IRP_MN_STOP_DEVICE (Stop Device)

• IRP_MN_QUERY_STOP_DEVICE (Query Stop)

• IRP_MN_CANCEL_STOP_DEVICE (Stop Device)

• IRP_MN_SURPRISE_REMOVAL (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 Query Stop and Query Remove messages

• Handling Stop messages

• Handling Surprise Removal messages

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 Start Device messages after lower devices have started

• 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.

Adding and Removing Devices

When a PnP driver handles an AddDevice message, it means that a new device has be found. Either a bus driver has found the device at power on or when it was inserted, or the user has added it by hand from the Control Panel. As Wdm2 devices are virtual, these have to be added by hand.

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 AddDevice routine is called in turn. Chapter 11 describes how the PnP Manager works out which drivers to put in the stack.

If a driver's AddDevice routine fails, any drivers below it (whose AddDevice succeeded) will be sent a Remove Device message. Be prepared to accept a Remove Device message straightaway after your AddDevice routine has completed.

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 Wdm2AddDevice routine, shown later, calls IoCreateDevice to create the Wdm2 Functional Device Object and eventually calls IoAttachDeviceToDeviceStack to attach it to the device stack. In between, the FDO and its device extension are initialized and a device interface for it is set up.

The eventual job of the Remove Device message handler is to stop the device and delete the FDO. In Wdm2, PnpRemoveDeviceHandler, shown later, eventually calls IoDetachDevice to detach the device object from the stack and calls IoDeleteDevice to delete the device object and its device extension memory. Processing Remove Device PnP messages safely is, in fact, a bit more complicated than this, as will be shown. Note that a Remove Device request will be the last IRP a driver receives before it is unloaded.

Basic PnP Handlers

The Wdm1 driver handles adding a device in its AddDevice routine and its Wdm1Pnp routine handles the remove device minor code IRP_MN_REMOVE_DEVICE.

The AddDevice and IRP_MJ_PNP handlers are called at PASSIVE_LEVEL IRQL in the context of a system thread. Calls to these routines may be issued while other IRPs are running in the same driver. Even in a uniprocessor computer, processing of a Read IRP could have stalled for some reason. A PnP call could then be issued.

AddDevice

In the Wdm2 driver, the code for adding and removing devices is substantially the same as Wdm1. The Wdm2AddDevice routine in Listing 9.1 is exactly the same, apart from new lines initializing some extra fields in the device extension. These all relate to handling PnP and Power IRPs correctly and are explained in due course.

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;

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату
×