The DriverShell source code can be downloaded from: 

Windows CE runs on many battery powered devices and therefore needs to be able to minimize the power consumption to maximize battery life. To do this Windows CE has a power manager that can be used to notify device drivers when the system power state changes. Drivers can then respond to the notification by powering down the hardware that they manage.

Basic Power Management
The most basic power handling that a driver can support are the XXX_PowerUp() and XXX_PowerDown() functions.   XXX_PowerDown() is called after the system has switched to single threaded mode and the system is ready to suspend. XXX_PowerUo() is called when the system resumes and before the system resumes multithreaded operation.
These functions give the driver developer a simple way to manage the power of the hardware, but there are some restrictions. These functions can only call a few system functions; DEBUGMSG, RETAILMSG, CeSetPowerOnEvent and SetInterruptEvent.
These power functions can be used to save and restore driver state information, power off the hardware and power on the hardware and reinitialize it. CeSetPowerOnEvent() and SetInterruptEvent() can be used to signal threads to do something after the system resumes multithreaded operation.
Power Manageable Device Driver
A driver can be developed to respond to power manager requests to change state when the system power state changes. The system supports five power states by default. A driver must support full on, but the other power states are optional.
The following example builds on Windows CE: A Stream Interface Driver Shell by adding power management support. This driver doesn’t really support any actual hardware, and in fact doesn’t really do anything at all but provides a driver shell that can be used as the basis for developing a new device driver.
I started by removing XXX_PowerDown() and XXX_PowerUp() because they aren’t needed if the driver supports the power manager. This involved removing the functions from the C code and from the DEF file.
Then I added an IClass to the registry entry for the driver:
IF BSP_DRIVERSHELL
[HKEY_LOCAL_MACHINE\Drivers\BuiltIn\DriverShell]
    "Dll"="DriverShell.dll"
   "Order"=dword:4
    "Prefix"="XXX"
    "DeviceArrayIndex"=dword:1
    "IClass"="{A32942B7-920C-486b-B0E6-92A702A99B35}"
ENDIF
It is not necessary to add the IClass to the registry, the driver could call AdvertiseInterface() from the driver. The IClass identifies the capabilities of the driver to the system and is set to a GUID value. I chose this IClass value by looking at the power manager register settings in [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Power\Interfaces]. This GUID defines Generic power-manageable devices, and nothing could be more generic than this DriverShell driver so it seems like a good fit.
Adding this IClass means that the driver should now handle the power manger IOCTLS in the XXX_IoControl() function. The IOCTLS are IOCTL_POWER_CAPABILITIES, IOCTL_POWER_GET, IOCTL_POWER_GET and IOCTL_REGISTER_POWER_RELATIONSHIP.
In general, I will show the source code for the driver with power management at the end of this post, but I think that it is worth looking at the code for the IOCTLS here. To simplify this, the error checking is left out, but it is included later.
IOCTL_POWER_CAPABILITIES
The IOCTL_POWER_CAPABILITIES is used by the power manager to ask the driver what power states it supports and the power levels that can be achieved at the different power states. It does this by copying a POWER_CAPABILITIES structure into the output buffer and setting the size of the return structure. 
                                case IOCTL_POWER_CAPABILITIES:
                                                {
                                                                PPOWER_CAPABILITIES ppc = (PPOWER_CAPABILITIES)pBufOut;       
                                                                memcpy(ppc, &g_PowerCaps, sizeof(POWER_CAPABILITIES));
                                                                *pdwActualOut = sizeof(POWER_CAPABILITIES);
                                                                RetVal = TRUE;
                                                }
                                                break;
Since this driver is a shell that could be used for any development of a stream interface driver, the following is the POWER_CAPABILITIES structure that the driver defines. This sets the driver up to handle all of the power states, but it is not necessary to do so and most driver will probably only handle power on and power off.
static const POWER_CAPABILITIES g_PowerCaps =
{
    DX_MASK(D0) | DX_MASK(D1) | DX_MASK(D2) | DX_MASK(D3) | DX_MASK(D4),
    0,              // WakeFromDx:
    0,              // InrushDx:    No inrush of power
    {               // Power: Maximum milliwatts in each state
        0x00000001, //        D0 = 0
        0x00000001, //        D1 = 0
        0x00000001, //        D2 = 0
        0x00000001, //        D3 = 0
        0x00000001 //        D4 = 0 (off)
    },
    {               // Latency
        0x00000000, //        D0 = 0
        0x00000000, //        D1 = 0
        0x00000000, //        D2 = 0
        0x00000000, //        D3 = 0
        0x00000000 //        D4 = 0
    },
    0,                    // Flags: None
};
IOCTL_POWER_GET
The IOCTL_POWER_GET isn’t used by the power manager because it maintains the current state of the drivers. But the power manager can be asked by another process to request the state from a driver and then the IOCTL will be called. This IOCTL will just return the current power state that the driver is in, so the driver does need to keep track of its current state. For that, I added a driver context structure that includes a member to track the state.
                                case IOCTL_POWER_GET:
                                                {
                                                                *(PCEDEVICE_POWER_STATE) pBufOut = pDriverContext->CurrentPowerState;
                                                                *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);     
                                                                RetVal = TRUE;
                                                }
                                                break;
IOCTL_POWER_GET
IOCTL_POWER_GET is the main power state processor for the driver. When the power manager requests that the driver change state, it calls this IOCTL passing in the state that the driver should change to. To help handle the power state change and support multithreaded scenarios the CEDDK includes some functions that this code uses. These APIs do not appear to be documented, but the source code is available in Public\Common\Oak\Drivers\CEDDK. The CEDDK functions need to be initialized before using them, and you will see later how that is done in XXX_Init() and a HANDLE that they use is stored in the driver context structure.
                    case IOCTL_POWER_SET:
                                                {
                                                                CEDEVICE_POWER_STATE newDx = *(PCEDEVICE_POWER_STATE) pBufOut;
                                                                RetVal = DDKPwr_SetDeviceLevel( pDriverContext->hDDKPower, newDx, NULL );
                                                                if(RetVal == TRUE)
                                                                {
                                                                                //set to an adjusted power state if the driver does not support the requested power state.                                                     
                                                                                *(PCEDEVICE_POWER_STATE)pBufOut = DDKPwr_GetDeviceLevel(pDriverContext->hDDKPower );
                                                                                *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
                                                                }
                                                                // Save the state in the driver context so that we can use it later
                                                                pDriverContext->CurrentPowerState = *(PCEDEVICE_POWER_STATE)pBufOut;
                                                }
                                                break;
DDKPwr_SetDeviceLevel calls a function in the driver to handle changing the power state. This function can be named anything that you want, in this case it is XXX_SetPowerState where the XXX isn’t necessary, but it clearly identifies that the function is in the DriverShell driver. The code in DriverShell doesn’t do anything but call RETAILMSG() to identify the state that it is transitioning into. To initialize the CEDDK power functions call DDKPwr_Initialize() and pass a function pointer to the power state handler function, a pointer to the device context, a flag to tell the functions to fail if blocked by another thread and a timeout value to prevent deadlock. This is how DriverShell initializes the functions in XXX_Init():
                pDriverContext->hDDKPower = DDKPwr_Initialize(XXX_SetPowerState, (DWORD)pDriverContext , TRUE, 1000 );
 
 IOCTL_REGISTER_POWER_RELATIONSHIP
The IOCTL_REGISTER_POWER_RELATIONSHIP doesn’t do anything in DriverShell because the driver doesn’t have any relationship to other drivers.
                                case IOCTL_REGISTER_POWER_RELATIONSHIP:
                                                // This is a simple driver that doesn't have any relationship to other drivers
                                                // so just return TRUE
                                                RetVal = TRUE;
                                                break;
The DriverShell Source Code
The following is the entire DriverShell source code with power management enabled. Note that along with adding a driver context this also adds a better open context than the original TRUE. This allows the power manager to hold an open handle to the driver that it uses to manage the power states.
#include <windows.h>
#include <Devload.h>
#include <pm.h>
#include <ceddk.h>
 
typedef struct _DRIVERSHELL_CONTEXT
{
                DWORD Instance;
                CEDEVICE_POWER_STATE CurrentPowerState;
                HANDLE hDDKPower;
} DRIVERSHELL_CONTEXT;
 
typedef struct _OPEN_CONTEXT
{
                DRIVERSHELL_CONTEXT *pHWContext;
} OPEN_CONTEXT;
 
static const POWER_CAPABILITIES g_PowerCaps =
{
    // DeviceDx:    Supported power states
    DX_MASK(D0) | DX_MASK(D1) | DX_MASK(D2) | DX_MASK(D3) | DX_MASK(D4),
 
    0,              // WakeFromDx:
    0,              // InrushDx:    No inrush of power
 
    {               // Power: Maximum milliwatts in each state
        0x00000001, //        D0 = 0
        0x00000001, //        D1 = 0
        0x00000001, //        D2 = 0
        0x00000001, //        D3 = 0
        0x00000001 //        D4 = 0 (off)
    },
 
    {               // Latency
        0x00000000, //        D0 = 0
        0x00000000, //        D1 = 0
        0x00000000, //        D2 = 0
        0x00000000, //        D3 = 0
        0x00000000 //        D4 = 0
    },
 
    0,                    // Flags: None
};
 
 
CEDEVICE_POWER_STATE XXX_SetPowerState(DWORD dwContext, CEDEVICE_POWER_STATE PowerState)
{
                DRIVERSHELL_CONTEXT *pDriverContext = (DRIVERSHELL_CONTEXT *)dwContext;
    CEDEVICE_POWER_STATE bReturn = pDriverContext->CurrentPowerState;
 
                RETAILMSG( 1, (TEXT("XXX_SetPowerState to D%d\n"), PowerState ));
               
    if (pDriverContext->CurrentPowerState != PowerState) {
        if (pDriverContext->CurrentPowerState != D4 && PowerState==D4)
                                {
                                                // D4 - System Idle with Display Off
            RETAILMSG( 1, (TEXT("XXX_SetPowerState D4 - System Idle with Display Off\n")));
 
                                                //Add code here to handle D4
 
                                                bReturn = PowerState;
        }
        if (pDriverContext->CurrentPowerState != D3 && PowerState== D3)
                                {
                                                // D3 - Suspend
            RETAILMSG( 1, (TEXT("XXX_SetPowerState Powering down hardware\n")));
 
                                                //Add code here to power down the hardware and save state if needed
 
                                                bReturn = PowerState;
        }
        if (pDriverContext->CurrentPowerState != D2 && PowerState==D2)
                                {
                                                // D2 - System Idle
            RETAILMSG( 1, (TEXT("XXX_SetPowerState D2 - System Idle\n")));
 
                                                //Add code here to handle D2
 
                                                bReturn = PowerState;
        }
        if (pDriverContext->CurrentPowerState != D1 && PowerState==D1)
                                {
                                                // D1 - User Idle
            RETAILMSG( 1, (TEXT("XXX_SetPowerState D1 - User Idle\n")));
 
                                                //Add code here to handle D1
 
                                                bReturn = PowerState;
        }
 
        if (pDriverContext->CurrentPowerState != D0 && PowerState==D0)
                                {
                                                // D0 - System On Full Power
            RETAILMSG( 1, (TEXT("XXX_SetPowerState Powering up hardware\n")));
 
                                                //Add code here to power up the hardware and restore state if needed
 
                                                bReturn = PowerState;
        }
    }
 
                return bReturn;
}
 
 
BOOL XXX_Deinit( DWORD hDeviceContext )
{
                DRIVERSHELL_CONTEXT *pDriverContext = (DRIVERSHELL_CONTEXT *)hDeviceContext;
                if( hDeviceContext )
                {
                    if (pDriverContext->hDDKPower != INVALID_HANDLE_VALUE )
                DDKPwr_Deinitialize(pDriverContext->hDDKPower);
                                LocalFree( pDriverContext );
                }
    return TRUE;
}
 
DWORD XXX_Init(ULONG   RegistryPath)
{
    HKEY hKey;
                DRIVERSHELL_CONTEXT *pDriverContext;
 
                RETAILMSG( 1, (TEXT("XXX_Init\n")));
 
                pDriverContext = LocalAlloc( LMEM_FIXED, sizeof( DRIVERSHELL_CONTEXT ));
                if( pDriverContext == NULL )
                {
                                RETAILMSG( 1, (TEXT("XXX_Init failed, unable to allocate driver context\n")));
                                return FALSE;
                }
               
                hKey = OpenDeviceKey((LPCTSTR)RegistryPath);
    if ( !hKey ) {
        RETAILMSG(1, (TEXT("Failed to open devkeypath,\r\n")));
    }
                else
                {
                                DWORD Type = REG_DWORD;
                                DWORD Data;
                                DWORD DataSize = sizeof( DWORD );
 
                                // Read values from registry if needed
                                if( ERROR_SUCCESS == RegQueryValueEx( hKey, TEXT("DeviceArrayIndex"), NULL, &Type, (LPBYTE)&Data, &DataSize ) )
                                                pDriverContext->Instance = Data;
 
                                RETAILMSG( 1, (TEXT("pDriverContext->Instance %d\n"), pDriverContext->Instance));
                                RegCloseKey (hKey);
                }
 
                // Init Power management
                pDriverContext->CurrentPowerState = D0;
                pDriverContext->hDDKPower = DDKPwr_Initialize(XXX_SetPowerState, (DWORD)pDriverContext , TRUE, 1000 );
 
    return (DWORD)pDriverContext;
}
 
BOOL WINAPI DllEntry(HINSTANCE DllInstance, ULONG Reason, LPVOID Reserved)
{
                RETAILMSG( 1, (TEXT("DriverShell: DllEntry\n")));
    return TRUE;
}
 
 
DWORD XXX_Open(DWORD hDeviceContext, DWORD AccessCode, DWORD ShareMode)
{
                OPEN_CONTEXT *pOpenContext;
               
                RETAILMSG( 1, (TEXT("XXX_Open\n")));
               
                pOpenContext = LocalAlloc( LMEM_FIXED, sizeof( OPEN_CONTEXT ));
                if( pOpenContext == NULL )
                {
                                RETAILMSG( 1, (TEXT("XXX_Open failed, unable to allocate open context\n")));
                                return FALSE;
                }
                pOpenContext->pHWContext = (DRIVERSHELL_CONTEXT *)hDeviceContext;
               
    return (DWORD)pOpenContext;
}
 
BOOL XXX_Close(DWORD hOpenContext)
{
                OPEN_CONTEXT *pDriverContext = (OPEN_CONTEXT *)hOpenContext;
               
                RETAILMSG( 1, (TEXT("XXX_Close\n")));
                if( pDriverContext )
                {
                                LocalFree( pDriverContext );
                }
               
    return TRUE;
}
 
DWORD XXX_Read(DWORD hOpenContext, LPVOID pBuffer, DWORD Count)
{
    return 0;
}
 
DWORD XXX_Write(DWORD hOpenContext, LPCVOID pSourceBytes, DWORD NumberOfBytes)
{
    return 0;
}
 
DWORD XXX_Seek(DWORD hOpenContext, long Amount, DWORD Type)
{
    return 0;
}
 
BOOL XXX_IOControl(DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut)
{
                OPEN_CONTEXT *pOpenContext = (OPEN_CONTEXT *)hOpenContext;
                DRIVERSHELL_CONTEXT *pDriverContext = pOpenContext->pHWContext;
                BOOL RetVal = TRUE;
 
                switch(dwCode)
                {
                                case IOCTL_POWER_CAPABILITIES:
                                                RETAILMSG(1,(TEXT("XXX_IoControl IOCTL_POWER_CAPABILITIES\n")));
               
                                                if (!pBufOut ||
                                                                dwLenOut < sizeof(POWER_CAPABILITIES) ||
                                                                !pdwActualOut)
                                                {
                                                                SetLastError(ERROR_INVALID_PARAMETER);
                                                                RetVal = FALSE;
                                                }
                                                else
                                                {
                                                                PPOWER_CAPABILITIES ppc = (PPOWER_CAPABILITIES)pBufOut;       
 
                                                                memcpy(ppc, &g_PowerCaps, sizeof(POWER_CAPABILITIES));
                                                                *pdwActualOut = sizeof(POWER_CAPABILITIES);
                                                                RetVal = TRUE;
                                                }
                                                break;
 
                    case IOCTL_POWER_SET:
                                                RETAILMSG(1,(TEXT("XXX_IoControl IOCTL_POWER_SET\n")));
 
                                                if (!pBufOut ||
                                                                dwLenOut < sizeof(CEDEVICE_POWER_STATE) ||
                                                                !pdwActualOut )
                                                {
                                                                SetLastError(ERROR_INVALID_PARAMETER);
                                                                RetVal = FALSE;
                                                }
                                                else
                                                {
                                                                CEDEVICE_POWER_STATE newDx = *(PCEDEVICE_POWER_STATE) pBufOut;
                                                                DEBUGMSG(1, (TEXT("XXX: IOCTL_POWER_SET request: D%d\r\n"), newDx));
                                                                RetVal = DDKPwr_SetDeviceLevel( pDriverContext->hDDKPower, newDx, NULL );
                                                                if(RetVal == TRUE)
                                                                {
                                                                                //set to an adjusted power state if the driver does not support the requested power state.                                                     
                                                                                *(PCEDEVICE_POWER_STATE)pBufOut = DDKPwr_GetDeviceLevel(pDriverContext->hDDKPower );
                                                                                *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);
                                                                }
                                                                else
                                                                                SetLastError(ERROR_INVALID_PARAMETER);
                                                                pDriverContext->CurrentPowerState = newDx;
                                                                RETAILMSG( 1, (TEXT("XXX_IoControl power set to D%d\n"), newDx));
                                                }
                                                break;
 
                                case IOCTL_POWER_GET:
 
                                                RETAILMSG(1,(TEXT("XXX_IoControl IOCTL_POWER_GET\n")));
                                                if (!pBufOut || dwLenOut < sizeof(CEDEVICE_POWER_STATE) || !pdwActualOut)
                                                {
                                                                SetLastError(ERROR_INVALID_PARAMETER);
                                                                RetVal = FALSE;
                                                }
                                                else
                                                {
                                                                *(PCEDEVICE_POWER_STATE) pBufOut = pDriverContext->CurrentPowerState;
                                                                *pdwActualOut = sizeof(CEDEVICE_POWER_STATE);     
                                                                RetVal = TRUE;
                                                }
                                                break;
                                               
                                case IOCTL_REGISTER_POWER_RELATIONSHIP:
                                                RETAILMSG(1,(TEXT("XXX_IoControl IOCTL_REGISTER_POWER_RELATIONSHIP\n")));
                                                // This is a simple driver that doesn't have any relationship to other drivers
                                                // so just return TRUE
                                                RetVal = TRUE;
                                                break;
 
               
                                default:
                                                RETAILMSG(1,(TEXT("XXX_IoControl default\n")));
                                                SetLastError(ERROR_INVALID_PARAMETER);
                                                RetVal = FALSE;
                                                break;
                }
    return RetVal;
}
 
Tags: Drivers
Copyright © 2008 – Bruce Eitman
All Rights Reserved