IClass与电源管理
前段时间为J9项目上添加电源管理,中间走了一些弯路。之前错误的认为,IClass只是与电源状态的改变方法有关,也就是说IClass的正确与否只会影响到设备电源状态的正确与否,而不会造成设备是否可以支持设备电源状态的转换。
结果后来整USB的时候,发现完全不是这么回事,郁闷了两天。
担心忘记了,电源管理中与IClass相关知识赶紧写下来。
一.PM中的相关内容说明
1.结构体DEVICE_LIST
首先看一下结构体DEVICE_LIST的定义:
// this structure describes a set of power manageable devices
typedef struct _DeviceList_tag {
LPCGUID pGuid; // class of device
PDEVICE_STATE pList; // pointer to devices
HANDLE hMsgQ; // device notification queue
HANDLE hnClass; // handle from RequestDeviceNotifications()
PDEVICE_INTERFACE pInterface; // interface to the device class power management routines
struct _DeviceList_tag *pNext; // singly linked list pointer
} DEVICE_LIST, *PDEVICE_LIST;
可以看到其第一个成员pGuid指向了GUID的名字,大家都知道CE中的PM相关GUID有四个,分别是通用设备,网络设备,块设备和GWES设备。默认在注册表[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Power/Interfaces]下进行声明,它是通过PM.dll中的相关宏定义进行指定,可以将PM Driver移植到BSP下后进行修改。
典型的定义如下:
; Power Manager interfaces. These list the interface classes that the Power
; Manager will monitor for new devices.
;
[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Power/Interfaces]
"{A32942B7-920C-486b-B0E6-92A702A99B35}"="Generic power-manageable devices"
"{8DD679CE-8AB4-43c8-A14A-EA4963FAA715}"="Power-manageable block devices"
; @CESYSGEN IF CE_MODULES_NDIS
"{98C5250D-C29A-4985-AE5F-AFE5367E5006}"="Power-manageable NDIS miniports"
; @CESYSGEN ENDIF CE_MODULES_NDIS
; @CESYSGEN IF CE_MODULES_GWES
"{EB91C7C9-8BF6-4a2d-9AB8-69724EED97D1}"="Power-manageable display"
; @CESYSGEN ENDIF CE_MODULES_GWES
显然,系统中应当维护着4个DEVICE_LIST结构体变量,它们最终组成一个单向链表,通过全局变量gpDeviceLists指向其表头,该链表在函数DeviceListsInit()中进行初始化,而DeviceListsInit()由PmInit()进行调用。
2.结构体DEVICE_STATE
首先看一下结构体DEVICE_STATE的定义:
// this structure describes a power-manageable device
typedef struct _DeviceState_tag {
LPCTSTR pszName; // device's name
CEDEVICE_POWER_STATE curDx; // current official power state (not necessarily supported by the device)
CEDEVICE_POWER_STATE floorDx; // minimum device power state, or PwrDeviceUnspecified
CEDEVICE_POWER_STATE ceilingDx; // maximum device power state, or PwrDeviceUnspecified
CEDEVICE_POWER_STATE setDx; // power state if explicitly set, or PwrDeviceUnspecified
CEDEVICE_POWER_STATE lastReqDx; // last state requested by the device
CEDEVICE_POWER_STATE actualDx; // current actual device power state
CEDEVICE_POWER_STATE pendingDx; // Pending DX for updating
DWORD dwNumPending; // Number of Pending for updating.
struct _DeviceState_tag *pParent; // parent device, or NULL
POWER_CAPABILITIES caps; // as reported by the device
DWORD dwRefCount; // structure can be deallocated when this is 0
HANDLE hDevice; // handle to the device from OpenDevice(), or NULL
PDEVICE_INTERFACE pInterface; // interface to the device class power management routines
struct _DeviceList_tag *pListHead; // pointer to the containing list
struct _DeviceState_tag *pNext; // linked list pointers
struct _DeviceState_tag *pPrev;
} DEVICE_STATE, *PDEVICE_STATE;
可以看到,该链表是一个双向链表,可以通过其前向和后驱双向遍历。
每一个由Device.exe(6.0下应该是kernel.exe)/GWES加载的设备以及网络和通用设备都对应一个该结构体的变量。
每一类GUID设备会组成一个DEVICE_STATE链表,也就是说系统中最大只有四个这种链表,通过结构体DEVICE_LIST的pList成员进行指向。
好了,现在你可以知道要找到一个设备的DEVICE_STATE的方法了,就是先找到它对应的GUID类的DEVICE_LIST结构体,然后从该类结构体的pList里面遍历特定的设备名找到你的DEVICE_STATE结点。
系统中已经实现了该函数,即GetDeviceListFromClass和DeviceStateFindList。GetDeviceListFromClass代码如下:
// this routine determines to which device list a particular device class
// corresponds
PDEVICE_LIST
GetDeviceListFromClass(LPCGUID guidDevClass)
{
PDEVICE_LIST pdl;
SETFNAME(_T("GetDeviceListFromClass"));
PREFAST_DEBUGCHK(guidDevClass != NULL);
// look for a match
__try {
for(pdl = gpDeviceLists; pdl != NULL; pdl = pdl->pNext) {
if(*pdl->pGuid == *guidDevClass) {
break;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
PMLOGMSG(TRUE, (_T("%s: exception accessing guidDevClass 0x%08x/r/n"),
pszFname, guidDevClass));
pdl = NULL;
}
return pdl;
}
DeviceStateFindList代码如下:
// This routine looks for a device on a list. If it finds the device, it
// increments its reference counter and returns a pointer to it. The caller
// should decrement the reference counter when it is done with the pointer.
// Note that the search is case sensitive.
PDEVICE_STATE
DeviceStateFindList(PDEVICE_LIST pdl, LPCTSTR pszName)
{
PDEVICE_STATE pds;
SETFNAME(_T("DeviceStateFindList"));
PMLOCK();
__try {
// look for a match
for(pds = pdl->pList; pds != NULL; pds = pds->pNext) {
if(_tcscmp(pds->pszName, pszName) == 0) {
// increment the reference count and exit
DeviceStateAddRef(pds);
break;
}
}
}
__except(EXCEPTION_EXECUTE_HANDLER) {
PMLOGMSG(ZONE_WARN, (_T("%s: exception searching list/r/n"), pszFname));
pds = NULL;
}
PMUNLOCK();
return pds;
}
3.指针函数结构体DEVICE_INTERFACE
首先看一下结构体DEVICE_INTERFACE的定义:
typedef struct _DeviceInterface_tag {
BOOL (WINAPI * pfnInitInterface) (VOID);
HANDLE (WINAPI *pfnOpenDevice) (struct _DeviceState_tag *);
BOOL (WINAPI * pfnCloseDevice) (HANDLE);
BOOL (WINAPI * pfnRequestDevice) (HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPDWORD);
} DEVICE_INTERFACE, *PDEVICE_INTERFACE;
系统中共维护两个该结构体的变量,即PM的MDD层Driver中定义的gStreamInterface和gDisplayInterface。
其中,gDisplayInterface对应的GWES加载的设备,而gStreamInterface对应的是网络设备,块驱动设备和通用设备。
其各个成员的意思就不再赘述。
二.IClass在电源管理中的用途
1.PM的相关初始化
1> 谁加载了PM.dll
在CE5.0中,设备管理器是Device.exe,到6.0里面由于单个Process空间上了GB的级别,就将其改为Device.dll,挂在了Kernel.exe里面。惭愧的是对于6.0,小弟始终没有找到在哪里LoadLibrary(“Device.dll”)。
小弟猜测应该是Filesys加载的PM.dll,帮助文档中讲述Filesys的启动过程的时候也没有提到这点,但是由于Filesys并没有源码,不知道各位有没有比较好的方法可以验证这一点。
2> PM的初始化
Filesys.exe/Filesys.dll调用了Device.exe/Device.dll的导出函数StartDeviceManager(),而在函数StartDeviceManager()中调用了PM.dll导出的初始化函数PmInit()。
PmInit()首先调用DeviceListsInit()去查询注册表的配置并初始化DEVICE_LIST链表。函数DeviceListsInit()的代码如下:
// This routine reads the registry to determine what type of device interfaces
// we will be monitoring. The default PM GUID is ignored if present in the
// registry and is always added last (so it's first in the list).
BOOL
DeviceListsInit(VOID)
{
BOOL fOk = TRUE;
PDEVICE_LIST pdl;
DWORD dwStatus;
HKEY hk;
TCHAR szBuf[MAX_PATH];
SETFNAME(_T("DeviceListsInit"));
// enumerate all the device classes
// 到注册表[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Power/Interfaces]下
// 获取GUID的值,默认情况下,ICLASS共有类别,此处直接通过枚举的方式查询到每一个GUID
wsprintf(szBuf, _T("%s//Interfaces"), PWRMGR_REG_KEY);
dwStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, szBuf, 0, 0, &hk);
if(dwStatus == ERROR_SUCCESS) {
DWORD dwIndex = 0;
do {
DWORD cbValueName = dim(szBuf), dwType;
GUID idInterface;
dwStatus = RegEnumValue(hk, dwIndex, szBuf, &cbValueName, NULL,
&dwType, NULL, NULL);
if(dwStatus == ERROR_SUCCESS) {
if(dwType != REG_SZ) {
PMLOGMSG(ZONE_WARN, (_T("%s: invalid type for value '%s'/r/n"),
pszFname, szBuf));
}
// 将GUID进行转换
else if(!ConvertStringToGuid(szBuf, &idInterface)) {
&