■ Implement power management in a device driver.
Estimated lesson time: 30 minutes.
Power Manager Device Drivers Interface
Power Manager interacts with power management-enabled drivers through the XXX_PowerUp, XXX_PowerDown, and XXX_IOControl functions. For example, the device driver itself can request a change of the device power level from Power Manager by calling the DevicePowerNotify function. In response, Power Manager calls XXX_IOControl with an IO control code of IOCTL_POWER_SET passing in the requested device power state. It might seem overcomplicated for a device driver to change the power state of its own device through Power Manager, yet this procedure ensures a consistent behavior and positive end-user experience. If an application requested a specific power level for the device, Power Manager might not call the IOCTL_POWER_SET handler in response to DevicePowerNotify. Accordingly, device drivers should not assume that successful calls to DevicePowerNotify result in a call to the IOCTL_POWER_SET handler or that any calls to IOCTL_POWER_SET are the result of a DevicePowerNotify call. Power Manager can send notifications to a device driver in many situations, such as during a system power state transition. To receive power management notifications, device drivers must advertise that they are power management enabled, either statically through the IClass registry entry in the driver's registry subkey or dynamically by using the AdvertiseInterface function.
XXX_PowerUp and XXX_PowerDown
You can use the XXX_PowerUp and XXX_PowerDown stream interface functions to implement suspend and resume functionality. The kernel calls XXX_PowerDown right before powering down the CPU and XXX_PowerUp right after powering it up. It is important to note that the system operates in single-threaded mode during these stages with most system calls disabled. For this reason, Microsoft recommends using the XXX_IOControl function instead of XXX_PowerUp and XXX_PowerDown to implement power management features, including suspend and resume functionality.
If you implement suspend and resume functionality based on the XXX_PowerUp and XXX_PowerDown functions, avoid calling system APIs, particularly thread-blocking APIs, such as WaitForSingleObject. Blocking the active thread in single-threaded mode causes an unrecoverable system lockup.
IOControl
The best way to implement power manager in a stream driver is to add support for power management I/O control codes to the driver's IOControl function. When you notify Power Manager about your driver's power management capabilities through an IClass registry entry or the AdvertiseInterface function, your driver receives corresponding notification messages.
Table 6-8 lists the IOCTLs that Power Manager can send to a device driver to perform power management-related tasks.
Table 6-8 Power management IOCTLs
Function | Description |
---|---|
IOCTL_POWER_CAPABILITIES | Requests information on what power states the driver supports. Note that the driver can still be set to other power states (D0 through D4). |
IOCTL_POWER_GET | Requests the current power state of the driver. |
IOCTL_POWER_SET | Sets the power state of the driver. The driver maps the received power state number to actual settings and changes the device state. The new device driver power state should be returned to Power Manager in the output buffer. |
IOCTL_POWER_QUERY | Power Manger checks to see if the driver is able to change the state of the device. This function is deprecated. |
IOCTL_REGISTER_POWER_RELATIONSHIP | Enables a device driver to register as the proxy for another device driver, so that Power Manager passes all power requests to this device driver. |
IClass Power Management Interfaces
Power Manager supports an IClass registry entry that you can configure in the driver's registry subkey to associate your driver with one or more device class values. An IClass value is a globally unique identifier (GUID) referring to an interface, defined under the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Power\Interfaces registry key. The most important interface for driver developers is the interface for generic power management-enabled devices, associated with the GUID {A32942B7-920C-486b-B0E6-92A702A99B35}. By adding this GUID to the IClass registry entry of your device driver, you can inform Power Manager to send your driver IOCTLs for power management notifications, as illustrated in Figure 6-7.
Figure 6-7 Configuring the IClass registry entry to receive power management notifications
You can also use registry settings and device classes to configure the default power states for a device, as explained in Lesson 5, "Implementing Power Management," of Chapter 3.
Lesson Summary
To ensure reliable power management on Windows Embedded CE, device drivers should not change their own internal power state without the involvement of Power Manager. Operating system components, drivers, and applications can call the DevicePowerNotify function to request a power state change. Accordingly, Power Manager sends a power state change request to the driver if the power state change is consistent with the current state of the system. The recommended way to add power management capabilities to a stream driver is to add support for power management IOCTLs to the XXX_IOControl function. The XXX_PowerUp and XXX_PowerDown functions only provide limited capability because Power Manager calls these functions at a time when the system operates in single-thread mode. If the device advertises a power management interface through an IClass registry entry or calls the AdvertiseInterface to announce supported IOCTL interfaces dynamically, Power Manager will send IOCTLs to the device driver in response to power-related events.
Lesson 6: Marshaling Data across Boundaries
In Windows Embedded CE 6.0, each process has its own separate virtual memory space and memory context. Accordingly, marshaling data from one process to another requires either a copying process or a mapping of physical memory sections. Windows Embedded CE 6.0 handles most of the details and provides system functions, such as OALPAtoVA and MmMapIoSpace, to map physical memory addresses to virtual memory addresses in a relatively straightforward way. However, driver developers must understand the details of data marshaling to ensure a reliable and secure system. It is imperative to validate embedded pointers and properly handle asynchronous buffer access so that a user application cannot exploit a kernel- mode driver to manipulate memory regions that the application should not be able to access. Poorly implemented kernel-mode drivers can open a back door for malicious applications to take over the entire system.