KeInitializeEvent(&event, NotificationEvent, FALSE);

 IoCopyCurrentIrpStackLocationToNext(Irp);

 IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE)ForwardedIrpCompletionRoutine, (PVOID)&event, TRUE, TRUE, TRUE);

 NTSTATUS status = IoCallDriver(dx->NextStackDevice, Irp);

 if (status==STATUS_PENDING) {

  DebugPrintMsg('ForwardIrpAndWait: waiting for completion');

  KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);

  status = Irp->IoStatus.Status;

 }

#if DBG

 if (status!=STATUS_SUCCESS) DebugPrint('ForwardIrpAndWait: completed %x',status);

#endif

 return status;

}

NTSTATUS ForwardedIrpCompletionRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN PKEVENT ev) {

 KeSetEvent(ev, 0, FALSE);

 return STATUS_MORE_PROCESSING_REQUIRED;

}

Most completion routines should include the following code to remember if any lower driver has pended an IRP. If any driver in a stack marks an IRP as pending then the I/O Manager has to take special steps to ensure that the IRP results are returned to user space in the correct thread context.

Calling IoMarkIrpPending sets an internal pending flag in the current IRP stack location. When a lower driver completes an IRP, IoCompleteRequest sets the Irp->PendingReturned flag equal to the IRP stack internal flag. To remember that the IRP was pended, a completion routine must therefore call IoMarkIrpPending if Irp->PendingReturned is set.

if (Irp->PendingReturned) IoMarklrpPending(Irp);

PnP Start Device Handler

The Wdm2 driver handles the PnP Start Device message in its PnpStartDeviceHandler routine shown in Listing 9.8. The state diagram in Figure 9.1 shows that the Start Device message is received either from the Awaiting resources state or the Stopped state.

PnpStartDeviceHandler can then do whatever it needs to do to start its device. Wdm2 delegates this to the routine StartDevice, which will be covered later. StartDevice is passed a pointer to the allocated resources, in the IRP stack Parameters.StartDevice.AllocatedResourcesTranslated field.

If StartDevice succeeds, it will have set the GotResources flag. PnpStartDeviceHandler also then clears the Paused and IODisabled flags.

The drivers above Wdm2 in the device stack could fail the Start Device IRP. If this happens, Wdm2 and the other lower drivers will be sent a Remove Device request.

Listing 9.8 Wdm2 PnP start device handler

NTSTATUS PnpStartDeviceHandle(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {

 DebugPrintMsg('PnpStartDeviceHandler');

 PWDM2_DEVICE_EXTENSION dx=(PWDM2_DEVICE_EXTENSION)fdo->DeviceExtension;

 PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);

 NTSTATUS status = ForwardIrpAndWait(fdo, Irp);

 if (!NT_SUCCESS(status)) return CompleteIrp(Irp, status, Irp->IoStatus.Information);

 DebugPrint('PnpStartDeviceHandler: post-processing');

 status = StartDevice(dx, IrpStack->Parameters.StartDevice.AllocatedResourcesTranslated);

 if (NT_SUCCESS(status)) {

  dx->Paused = false;

  dx->IODisabled = false;

 }

 return Complete Irp(Irp, status, 0);

}

Device Locking

When a user wants to remove a Wdm2 device, the PnP Manager always asks if it is all right to remove it using a Query Remove request. As described previously, Wdm2 agrees to a removal request only if there are no open handles to the device. Therefore, for the main RemoveDevice request, the Wdm2 driver can be certain that there is no I/O in progress on the device.

However, some devices are hot-pluggable (i.e., a user can ruthlessly pull out the plug, or it could be bashed out by mistake). In this case, a PnP driver does not receive a Query Remove message. Instead, in Windows 98, it simply gets a Remove Device message. Windows 2000 sends a Surprise Removal message first and then sends a Remove Device request when all the open handles are closed.

A driver needs to cope with surprise removals in the best way possible. A SurpriseRemoval IRP must succeed. Obviously, the driver needs to stop any further I/O as soon as possible. However, one or more I/O requests may be in progress or queued up. It is fairly straightforward to cancel all the IRPs in a device queue; see Chapter 16 for details. However, the best method to handle I/O IRPs that are in progress is more complicated.

If you cannot somehow fail any I/O requests in progress, the recommended solution is to wait for them to complete. The main IRP processing routines are more than likely to be using the device or device extension, so these structures cannot be deleted until it is certain that they will not be used again. The main IRP processing routines almost certainly encounter some sort of problem. Make sure that they can handle a device being removed. A routine might be expecting an interrupt. Chapter 17 later shows how two different types of timers can be used to provide a time-out for I/O requests.

What is best way to keep track of how many I/O requests are in progress? The answer is to laboriously keep track of the number of open I/O requests in a UsageCount field in the device extension. A call to LockDevice is made at the beginning of each IRP request to increment UsageCount. UnlockDevice is called when each IRP is completed to decrement UsageCount.

UsageCount is set to one when the device is created. To remove or stop a device, an extra call to UnlockDevice is made. If there are no other IRPs in progress, this call should have decremented UsageCount to zero. If there are IRPs in progress, UsageCount will at this stage have a value greater than zero. However, when these IRPs finish, UnlockDevice will be called and so UsageCount will in due course become zero.

The Remove Device or Stop Device PnP IRP needs to know when UnlockDevice decrements UsageCount to zero. Another kernel event, StoppingEvent, is used for this purpose. UnlockDevice sets StoppingEvent into the signalled state when UsageCount drops to zero.

Listing 9.9 shows the PnpStopDevice routine that the Wdm2 driver uses to stop a device. It is called by the Remove Device, Surprise Removal, and Stop Device PnP IRP handlers. PnpStopDevice sets the IODisabled flag straightaway to stop any new requests from starting. The device is already stopped if GotResources is false, so no more processing is required.

PnpStopDevice resets StoppingEvent and then calls UnlockDevice twice. The first undoes the call to LockDevice at the start of the main PnP IRP handler. The second will reduce UsageCount to zero, either straightaway or when all the IRPs in progress complete. PnpStopDevice then waits for StoppingEvent to be set by UnlockDevice.

PnpStopDevice then calls the StopDevice routine, described later. StopDevice resets the GotResources flag. PnpStopDevice's last task is to call LockDevice again to increment UsageCount again. The main PnP IRP handler calls UnlockDevice in due course to get UsageCount down to its correct value of zero.

Listing 9.9 PnpStopDevice routine

void PnpStopDevice(IN PWDM2_DEVICE_EXTENSION dx) {

 // Stop I/O ASAP

 dx->IODisabled = true;

 // Do nothing if we're already stopped

 if (!dx->GotResources) return;

 // Wait for any pending I/O operations to complete

 dx->Stopping = true;

 KeResetEvent(&dx->StoppingEvent);

 UnlockDevice(dx);

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

0

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

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