Summary: General guidelines and tips for implementing a power-managed driver in Windows CE. Discusses the required handlers for a power-managed driver: D-states, XXXPowerUp/XXXPowerDown, and IOCTLPOWERCAPABILITIES/IOCTLPOWERSET, as well as usage of DevicePowerNotify.




Making a windows CE driver "power efficient" has different meanings based on the type of driver that is under consideration.

* A 'leaf' driver is one that services a particular piece of hardware. An example of this type of driver is a backlight driver.
* A 'bus' driver is a driver which enables other 'leaf' drivers to function. An example of this type of driver is a PCI driver.
* A 'class' driver is a driver which provides an abstraction from the 'leaf' drivers to a generic interface. An example of this type is a USB RNDIS driver which abstracts a 'leaf' USB client driver into an ethernet interface.

For power efficiency, we are mostly concerned with drivers which touch the things that actually use power - hardware. A driver which does not manipulate hardware is just taking up RAM and/or FLASH space -- the only way to optimize it is to reduce its footprint in memory.

Even though bus drivers can use hardware, they occur far less frequently in BSP implementation. Usually a default bus driver that can already be used is in place. The same power management principles apply to bus drivers, but usually their implementation is more straightforward. For the most useful and relevant discussion of power efficiency, we will consider the 'leaf' drivers only. This section references numerous callouts to the MSDN documentation.

Basics

Regardless of anything you do with higher-level power management, all stream device drivers will continue to receive notifications of system suspend and resume states through their XXXPowerDown() and XXXPowerUp(). These notifications are sent in an interrupt context (when the system is single-threaded) just before the kernel calls the OAL OEMPowerOff() function. The PowerDown/PowerUp callback mechanism is independent of the Power Manager and allows legacy device drivers to function under Microsoft® Windows® CE .NET 4.0 and later.

The state model

To efficiently manage power in a leaf device, you must understand the device power state model. There are five 'device power states' that a device can be told to go into by the power manager:

* D0 - The device is on and running. It is receiving full power from the system and is delivering full functionality to the user.
* D1 - The device is fully functional at a lower power or performance state than D0. D1 is applicable when the device is being used, but where peak performance is unnecessary and power is at a premium. A device driver can query the power level of the system using the function GetSystemPowerStatusEx2 .
* D2 - The device is standing by, partially powered with automatic wakeup on request/activity.
* D3 - The device is sleeping, partially powered with device-initiated wakeup if available on request/activity.
* D4 - The device has no power.

It's pretty simple - D0 or "on" takes the most power, and D4 or "off" takes the least. Any device driver which can be power managed must implement at least D0. Any other states are optional, but devices that only support the D0 power state will never receive IOCTLPOWERSET. If a device does not support a power state, it should go into the next higher number power state (state which uses less power). For example, if your driver is going to be asked to go into D1 power state, but it only implements D0 and D4, the driver should go into the D4 state.

Starting point

The power manager communicates with device drivers through the DeviceIoControl() mechanism. This results in a call to the XXX_IoControl() function in your driver.
The first command that is issued to a driver when it starts is the IOCTLCAPABILITIES command. The driver is passed a buffer containing a POWER_CAPABILITIES structure. The driver fills in the structure with simple information about the device power states it supports, then returns. This tells the power manager what the device managed by the driver can do.
The Power Manager may request that the device go into any device power state, not just the ones the device claims to support in its IOCTLPOWERCAPABILITIES handler. As mentioned above, when requested to go into an unsupported power state, the device should go into the next higher number power state (state which uses less power).
If a driver does not respond to the IOCTLPOWERCAPABILITIES command, the power manager will not manage it and assume it is a legacy device that only uses the XXXPowerUp() and XXXPowerDown() function calls.
Any stream device driver that wants to be power managed (almost all should be) must:
  1. Initialize the device and put it into the D0 power state.
  2. Synchronously respond to commands from the power manager to change power state
  3. Respond to normal functionality requests

Item 1 puts the device into a specific state known to the driver and to the power manager. Item 3 is just the ordinary workings of the driver. Item 2 is the interesting part.

Being told what to do

A driver can request from the power manager that it change state. However, the design of the CE power manager is such that a driver cannot change state independently and then inform the power manager that it has done so. (If a device is going to change its own power state automatically, then it should not use the PM mechanisms -- doing this is specifically discouraged.)

The power manager tells a device driver what to do by sending the IOCTLSET command. When a driver receives this command it should:
  1. Stop the thread that is doing the IOCTLPOWERSET until any concurrent driver request is complete (serialize access to the driver), then block any driver function calls on other threads.
  2. Perform the power state change
  3. Unblock any driver function calls that may come in on other user threads.

If a driver is multithreaded (for example, has an interrupt thread running), it should '''not'' let the thread receiving the IOCTLPOWERSET call return until the power state change has completed. If it needs to change the state inside the interrupt thread (likely), then it should set a global variable to tell the interrupt thread to change power, and signal the driver's interrupt event (via SetInterruptEvent ) to have the interrupt thread wake up to process the command. The thread receiving the IOCTLPOWERSET should block until the interrupt thread has completed the power state change, then return.

The Power Manager may issue an IOCTLPOWERSET that essentially puts the device into its already-current state. In this case, the device driver can simply return success and do nothing.

On startup or on resume from suspend, be sure that your driver does not power up your target device in the call to XXXPowerUp(). Your driver must instead restore the device power state to the value set by the Power Manager, which is most likely the D3 or D4 state. Also, be sure that your driver does not power down your target device in the call to XXXPowerDown(). Usually the XXX_PowerUp/Down() calls are good for indicating that a suspend has happened, but not very good for doing device actions.

Power control range

The power manager controls what a device's actual power state should be by knowing two parameters - the 'ceiling' and 'floor' of how much power can be consumed. The 'ceiling' (maximum power usage) for a device when the system is in one particular mode may be different from when the system is in another particular mode.
For example, the 'ceiling' when a device is in active use is probably D0 (full on). You cannot have a device state higher in power usage than D0. The 'ceiling' when a device is idle is probably D2, D3, or even D4. The power manager lowers the ceiling depending on the system power state.
The 'floor' of how much power can be consumed is determined by application requirements. An application can specify that it requires a particular device to be 'at least' in a device power state. If it successfully sets the requirement, then the power manager will not permit the device to go into a lower (higher number) power state.
For example, the 'floor' when a device is idle is probably D4. You cannot have a device state lower in power usage than D4. The 'floor' for a backlight when the system is in active use or is doing a display-intensive task is probably set to D0 by the application doing the task.
The power manager only permits a device to go into power states between its 'ceiling' and 'floor'. The 'ceilings' are determined by the definition of system power states . The 'floors' are determined dynamically at runtime by applications putting requirements on the system devices.

Requesting a device power state

Some devices can be sophisticated in how they manage their own power. Device driver developers for these devices will typically want to reduce their power consumption during periods of inactivity. Reducing power consumption will generally also reduce the performance of the device, so these devices also typically want to raise their performance levels when they are being actively used. Increasing performance level generally increases the power consumed.

These devices will tend to request to dynamically raise and lower their device power states according to how often a device is used. The actual algorithm for this self-management of power is device specific.

The Power Manager's DevicePowerNotify API allows driver developers to request that the Power Manager adjust their device's power state. The Power Manager will permit power to be adjusted, provided that the requested device power state falls between the maximum and minimum values. The maximum value is mandated by the system power state and the minimum value is determined by application calls to SetPowerRequirement .

When the Power Manager decides to accept a device's requested power state adjustment, it will update the device's power state using IOCTLPOWERSET. Driver implementers should observe the following guidelines when calling DevicePowerNotify():

* The device should not update its power state if the DevicePowerNotify() call succeeds, only when it receives an IOCTLPOWERSET. The success of the call to DevicePowerNotify() is an indication that the request got to the power manager correctly, not an indication that the device changed power state.
* The driver developer should not assume that a successful return code from DevicePowerNotify() implies that the Power Manager will issue an IOCTLPOWERSET. A request may not necessarily be granted.
* The driver developer should not assume that an IOCTLPOWERSET immediately following a DevicePowerNotify() was issued as a result of the call.
* Devices that are capable of waking the system when they are put into D3 should not request D3 using DevicePowerNotify().

Some devices are capable of supporting more levels of power than are represented by the D0 through D4 device power states. If desired, driver developers for such devices can map multiple power levels actually supported by the device into each of the device power states understood by the Power Manager. The device is free to self-manage power within each device power state group independently of the Power Manager. However, it should still use the DevicePowerNotify() to transition between groups and to request changes in device power state.

Power state configuration

Usually the configuration of what the power 'ceiling' is for a device in a particular system state is left to the shell and experience people to sort out. You can imagine that the ceiling for a backlight when the system is in idle mode would be D1 or higher in number. What the 'ceilings' are for devices are managed by values in the system registry. There are two ways a device is managed:
  1. By device class. A device typically belongs to a specific 'class' of devices. In this case if there is no specific override for a device it is managed as a member of a class. Examples of classes are 'display','network','general', etc. The power manager's registry settings configure it to manage a device class, and all devices in a class are affected.
  2. By specific device override. You can override a device even if it belongs to a class, to specify a device power state that is different from the class power state for a particular system state. For example, you may want to put the backlight driver into the 'general' device class. In the 'idle' system state the 'general' devices can be configured to have a ceiling of 'D1'. However, you can override the driver specifically by putting an entry in the registry for it's device moniker and specifying the device power state it should be overridden to use. For the backlight example if the backlight has D0,D1,and D4 states, you could override the backlight to be in D4 when the system is idle instead of D1, but still keep the backlight in the 'general' power class for all other system power states.
A device claims what class it belongs to in its registry definitions (that specify its DLL, load order, and moniker). A REGMULTISZ value called '"IClass"' designates the class(es) that the device belongs to. The power manager will use the first class mentioned that it recognizes as the device's power class. Other values will be ignored, but could be used by other system components to determine the interfaces of the driver. Class names are represented as GUID values.
Power-manageable stream devices can automatically notify the Power Manager of their presence when they are loaded by ActivateDeviceEx(). If the GUID "{A32942B7-920C-486b-B0E6-92A702A99B35}" is part of their "IClass" value, which is a REGMULTISZ, the Power Manager will receive a device notification when the device is loaded. The Power Manager requests notifications for all classes listed in the HKEYLOCALMACHINE\System\CurrentControlSet\Control\Power\Interfaces registry key.
The Power Manager APIs that accept device names can also accept class-qualified device names. For example, each of the following names is a valid device name:

* "COM1:"
* "{A32942B7-920C-486b-B0E6-92A702A99B35}\COM1:" (general device class guid)
* {98C5250D-C29A-4985-AE5F-AFE5367E5006}\CISCO1 (network adapter device class guid)
* {8DD679CE-8AB4-43c8-A14A-EA4963FAA715}\DSK1: (storage device class guid)

If a class does not qualify a device name, the device is assumed to belong to the default device class. For example, the names "COM1:" and "{A32942B7-920C-486b-B0E6-92A702A99B35}\COM1:" are equivalent.

Suggested implementation steps

Steps 1 to 6 happen before you do any work to actually modify the power state of the device being driven
  1. Add the general device class definition to the "IClass" value in the device driver instance's registry settings.
  2. Add a default handler for IOCTLPOWERCAPABILITIES to your driver. Report that you support D0,D1,and D4.
  3. Add a default handler for IOCTLPOWERSET to your driver. Just track what state is being specified and output a debug message accordingly.
  4. Recompile the driver and run the platform, watching what is output when the power manager changes the device state.
  5. Rework the device power class definition and any overrides you need to get the device to transition at the right points during system use.
  6. Use debug messages to indicate what you should do at a particular point, and develop the heuristics accordingly.
  7. Implement power state handling for D0 (on) and D4 (off)
  8. Implement any processing needed for XXXPowerUp() and XXXPowerDown() for suspend/resume (if applicable)
  9. Implement any remaining power states (D1 through D3)
  10. Investigate if additional actions need to be taked for the D3 power state, to enable automatic device wakeup.

Additional references


Power Management Architecture
Device Power Management Guidelines
How to Add Power Management to a Device Driver
Best Practices for Power Manageable Device Drivers





Go up to Features of a BSP
Go up to Big Book of BSP


Thank you for contributing to this BSP Wiki. To ensure your comments and concerns receive proper exposure, include bspwiki@microsoft.com when providing feedback or topical suggestions.




Microsoft Communities