System Powering Up
In contrast, Figure 10.3 shows what might happen when your device has to power up to get to the required new system power state. First, a Query Power IRP is sent at (1) asking if a new system state is OK. Then a Set Power IRP is issued for the system power state. The driver determines that it must power up its device. It sets a completion routine and passes the IRP to the lower drivers at (2).
The completion routine for the Set System State sends itself a Set Device State Power IRP at (3), setting a completion routine. When this IRP gets to your driver, it again works out that it must power up. It passes the IRP to the lower drivers at (4) and sets another completion routine. When this completion routine runs at (5), the driver can finally power its device up in whatever way is appropriate at (6).
The completion routine at (7) runs. The original Set System State Power IRPcompletion routine at (3) eventually continues its run. It completes the first IRP.
This example appears complicated, as there are three different IRP completion routines involved. However, the full example given later should make the process clear. The original Set System State IRP needs a completion routine at (3) so that the driver knows when the IRP has been processed by the rest of the stack. This completion routine must send a Set Device State IRP to itself. It needs a completion routine at (7) so that
Figure 10.3 Power up system processing
Some drivers like Wdm1 (and in fact, Wdm2) can happily ignore all Power IRPs. All they have to do is pass all Power IRPs down the stack. Wdm1's Wdm1 Power dispatch routine shown in Listing 10.1 does just this. Note the use of
Drivers for removable devices should check to see if its media is present; if not, it should call PoStartNextPowerIrp and complete the Power IRP straightaway with a status of STATUS_DELETE_PENDING.
Listing 10.1 Passing on all Power IRPs
NTSTATUS Wdm1Power(IN PDEVICE_OBJECT fdo, IN PIRP Irp) {
DebugPrint('Power %I', Irp);
PWDM1_DEVICE_EXTENSION dx = (PWDM1_DEVICE_EXTENSION)fdo->DeviceExtension;
// Just pass to lower driver
PoStartNextPowerIrp(Irp);
IoSkipCurrentIrpStackLocation(Irp);
return PoCallDriver(dx->NextStackDevice, Irp);
}
Noting Device Power State Changes
Suppose a function driver does not handle Power IRPs, but its bus driver does. In this case, the function driver might want to know when its device is powered down and take some appropriate action. Unfortunately, there is no simple call to discover the current device power state. Instead, it must remember the last device power state from a Set Power IRP.
The Power IRP handler can easily be enhanced to note any changes of power state if the minor function code is IRP_MN_SET_POWER. The IRP stack
The following code snippet shows how to check for a Set Device Power IRP and store the new device power state in a DEVICE_POWER_STATE
PIO_STACK_LOCATION IrpStack = IoGetCurrentIrpStackLocation(Irp);
POWER_STATE_TYPE PowerType = IrpStack->Parameters.Power.Type;
POWER_STATE PowerState = IrpStack->Parameters.Power.State;
if (IrpStack->MinorFunction==IRP_MN_SET_POWER && PowerType==DevicePowerState) dx->PowerState = PowerState.DeviceState;
Device Power Policy Owner
In most device stacks, there is one driver that knows if the device is being used and its power state. This driver is deemed the device power policy owner. There is nothing to stop more than one driver trying to set power policy. In most cases, the first function driver for a device is the power policy owner. However, the bus driver may well play an important part in power management, or even be the power policy owner.
The device power policy owner should call PoSetPowerState to set the current device power state. This device power state is stored for a device by the Power Manager. However, there is no call to retrieve the current device power state, so you must keep a copy of this value.
The Wdm2 driver defines the following two extra fields in its device extension to support Power Management.
DEVICE_POWER_STATE PowerState; // Our device power state
PULONG PowerIdleCounter; // Device idle counter
The
dx->PowerState = PowerDeviceD3;
dx->PowerIdleCounter = NULL;
POWER_STATE NewState;
NewState.DeviceState = dx->PowerState;
PoSetPowerState(fdo, DevicePowerState, NewState);
An AddDevice routine can also set some extra bits in the FDO
The device power policy owner must process Query and Set Power IRPs using the technique described previously. The following Wdm2 example shows how to do this in detail.
It is recommended that a device is fully powered up by the time the Plug and Play
You may decide that the device can be powered off when it has been idle for a while. The easiest way to do this is to call the
The following line at the end of
dx->PowerIdleCounter = PoRegisterDeviceForIdleDetection(pdo, 30, 60, PowerDeviceD3);
The following code snippet shows how
if (dx->PowerIdleCounter) PoSetDeviceBusy(dx->PowerIdleCounter);
At the beginning of every I/O operation, you must check to see if the device is powered up. If it is not, most drivers will simply power up the device before starting the request. An alternative strategy is to queue the IRP until the device powers up at some other time.
Make sure that you do not accidentally power down a device during a lengthy I/O operation.
Handling Set Power IRPs
The parameters on the stack of a Set Power IRP indicate which system or device power state is being set.
If
Listing 10.2 shows the main Wdm2 handler for all Power IRPs, Wdm2Power. If this is a Set Power IRP,