Sunday, September 7, 2008

Making a device "Power-Managed"

Power manager uses IOCTLs to interact with Power-Managed devices. So inorder to make a device power managed implement certain set of IOCTLs in the driver with registry changes.

For the purpose of explaining, i am taking the sample of the Power Button driver implemented in Mainstone platform available in windows ce 6.0. Please bear in mind that this driver is already having a function which monitors system power state transitions using Notification Queues. But I am not going to make use of them.

Here I will briefly describe the steps involved in doing the same. We can divide the changes in to two parts:
  1. Change in registry settings for driver
  2. Implementing IOCTLs

Change in registry settings for driver

Now as the documentation for power manager, each power managed device needs to be classified under a certain category i.e Generic power-manageable devices, Power-manageable block devices and Power-manageable NDIS miniports. Each class is identified by a certain GUID(defined in pm.h). Associating a device to a power manager class is really easy, just by adding a registry entry to the device driver entry, as follows.

I will make this power button driver as a generic device
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\PWB]
"Prefix"="PWB"
"Dll"="mainstoneii_pwrbutton.dll"
"Order"=dword:3
"IClass"="{A32942B7-920C-486b-B0E6-92A702A99B35}" ;; we are making it as generic device over here

When device manager loads the driver, it will associate the device to the class.

Now power manager will start issuing the IOCTLs to the driver and query the capabilities of the driver. The details I will explain in the next section.
Note : We should atleast implement IOCTL_POWER_CAPABILITIES in a driver to make it power managed

Implementing IOCTLs

The driver needs to implement the following IOCTLs for interacting with power manager:
  • IOCTL_POWER_CAPABILITIES **
  • IOCTL_POWER_GET
  • IOCTL_POWER_QUERY *
  • IOCTL_POWER_SET
  • IOCTL_REGISTER_POWER_RELATIONSHIP *
** - This IOCTL is mandatory for the entire thing to work
* - rather optional


When power manager receives a request to register a device driver under a particular device class, it first passes IOCTL_POWER_CAPABILITIES to the driver. If the call returns false, the driver wont be registered otherwise it will be. This IOCTL should return the device capabilities.

BOOL IOControl( ............. )
{

................
case IOCTL_POWER_CAPABILITIES :
if (NULL != pOutBuf)
{
memcpy(pOutBuf, &PWBDrvPowerCaps, sizeof(PWBDrvPowerCaps));
*pdwBytesTransferred = sizeof(PWBDrvPowerCaps);
OutBufLen = sizeof(PWBDrvPowerCaps);
dwErr = ERROR_SUCCESS;
}
else
{
dwErr = ERROR_INVALID_PARAMETER;
}
break; // IOCTL_POWER_CAPABILITIES
...............
if(dwErr != ERROR_SUCCESS) {
bRc = FALSE;
} else {
bRc = TRUE;
}

return bRc;

}


The variable PWBDrvPowerCaps contains the device power capabilities of the driver in a POWER_CAPABILITIES structure.

POWER_CAPABILITIES PWBDrvPowerCaps =
{
DX_MASK(D0) | DX_MASK(D2) | DX_MASK(D3) | DX_MASK(D4),
0,
0,
{
0x00000000, // D0
0x00000000, // D1
0x00000000, // D2
0x00000000, // D3
0x00000000 // D4
},
{
0x00000000, // D0
0x00000000, // D1
0x00000000, // D2
0x00000000, // D3
0x00000000 // D4
},
0
};


For information on the meaning of each parameter please refer MSDN. After returning true from the IOCTL, power manager will register this device under the requested class in registry.

From now on wards, when ever a state change occurs (or is requested) for the device or system, power manager will send the IOCTL_POWER_SET to the driver and the driver can take care of the same.

case IOCTL_POWER_SET:
if ( pOutBuf != NULL )
{
SetPwbPowerState(*(PCEDEVICE_POWER_STATE) pOutBuf);
dwErr = ERROR_SUCCESS;
}
else
dwErr = ERROR_INVALID_PARAMETER;
break; // IOCTL_POWER_SET


In SetPwbPowerState(), you can define the power depending on the paramater.

1 comments:

siva said...

Hi,

I would like to test NDISUIO in my X86 based target. I have compiled the ndisuio.dll successfully.

But I am not sure about the registry settings that is needed for loading the driver.

I am aware of registry settings for stream interface driver.

Can you give me some inputs to load ndisuio.dll?