PWDM2_DEVICE_EXTENSI0N dx = (PWDM2_DEVICE_EXTENSION)fdo->DeviceExtension;
if (Irp->PendingReturned) IoMarkIrpPending(Irp);
NTSTATUS status = Irp->IoStatus.Status;
DebugPrint('OnCompleteIncreaseSystemPower %x', status);
if(!NT_SUCCESS(status)) return status;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
POWER_STATE PowerState = IrpStack->Parameters.Power.State;
DEVICE_POWER_STATE DesiredDevicePowerState = (PowerState.SystemState<=PowerSystemWorking ? PowerDeviceD0 : PowerDeviceD3);
if (DesiredDevicePowerState<dx->PowerState) status = SendDeviceSetPower(dx, DesiredDevicePowerState);
PoStartNextPowerIrp(Irp);
return status;
}
Sending a Set Device Power IRP
The Set System Power IRP has to send a Set Device Power IRP to itself to change its own device power state.
The completion routine should not free the IRP memory. The Power Manager frees the IRP after the completion routine exits. You cannot use the allocated IRP pointer to inspect the results of the IRP.
The completion routine
When you send a Power IRP using
As usual,
I found a twist in this tale when running this routine in Windows 98. On my computer, the Set device Power state was sent and seemed to complete successfully. However, it was not actually received by the Wdm2 driver. The last section of code calls
Finally, on this topic, the DDK documentation says that you cannot wait using events for your own Power IRP to complete.
Listing 10.5 SendDeviceSetPower routine
typedef struct _SDSP {
KEVENT event;
NTSTATUS Status;
} SDSP, *PSDSP;
NTSTATUS SendDeviceSetPower(IN PWDM2_DEVICE_EXTENSION dx, IN DEVICE_POWER_STATE NewDevicePowerState) {
DebugPrint('SendDeviceSetPower to %d', NewDevicePowerState);
POWER_STATE NewState;
NewState.DeviceState = NewDevicePowerState;
SDSP sdsp;
KeInitializeEvent(&sdsp.event, NotificationEvent, FALSE);
sdsp.Status = STATUS_SUCCESS;
NTSTATUS status = PoRequestPowerIrp(dx->pdo, IRP_MN_SET_POWER, NewState, OnCompleteDeviceSetPower, &sdsp, NULL);
if (status==STATUS_PENDING) {
KeWaitForSingleObject(&sdsp.event, Executive, KernelMode, FALSE, NULL);
status = sdsp.Status;
}
// Cope with W98 not passing power irp to us
if (NT_SUCCESS(status) && dx->PowerState!=NewDevicePowerState) {
DebugPrintMsg('SendDeviceSetPower: Device state not set properly by us. Setting again');
SetPowerState(dx,NewDevicePowerState);
}
return status;
}
VOID OnCompleteDeviceSetPower(IN PDEVICE_OBJECT fdo, IN UCHAR MinorFunction, IN POWER_STATE PowerState, IN PVOID Context, IN PIO_STATUS_BLOCK IoStatus) {
DebugPrintMsg('OnCompleteDeviceSetPower');
PSDSP psdsp = (PSDSP)Context;
psdsp->Status = IoStatus->Status;
KeSetEvent(&psdsp->event, 0, FALSE);
}
PowerSetPower in Listing 10.3 handles Set device Power IRPs.
If the device must be powered up, the power state must be changed after all the lower drivers have processed the Set device Power IRP. As before, a completion routine,
If the device must be powered down,
Listing 10.6 OnCompleteIncreaseDevicePower routine
NTSTATUS OnCompleteIncreaseDevicePower(IN PDEVICE_OBJECT fdo, IN PIRP Irp, IN PVOID context) {
PWDM2_DEVICE_EXTENSION dx = (PWDM2_DEVICE_EXTENSION)fdo->DeviceExtension;
if (Irp->PendingReturned) IoMarkIrpPending(Irp);
NTSTATUS status = Irp->IoStatus.Status;
DebugPrint('OnCompleteIncreaseDevicePower %x',status);
if( !NT_SUCCESS(status)) return status;
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
POWER_STATE PowerState = IrpStack->Parameters.Power.State; SetPowerState(dx.PowerState.DeviceState);
PoStartNextPowerIrp(Irp);
return status;
}
Listing 10.7 shows the SetPowerState routine in DeviceIo.cpp that actually changes the device power state.
A
If powering a device down, you may need to store some context information that the device normally holds. For example, you could store 'the current volume settings' for a set of speakers. When power is restored, pass the stored volume settings to the device.
As stated before, you may want to queue up I/O request IRPs while a device is powered down.
Listing 10.7 SetPowerState routine
void SetPowerState(IN PWDM2_DEVICE_EXTENSION dx, IN DEVICE_POWER_STATE NewDevicePowerState) {
DebugPrint('SetPowerState %d', NewDevicePowerState);
// Use KeSynchronizeExecution if necessary
// to actually change power in device