IClass与电源管理

2019-07-13 23:44发布

  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   显然,系统中应当维护着4DEVICE_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.exe6.0下应该是kernel.exe/GWES加载的设备以及网络和通用设备都对应一个该结构体的变量。 每一类GUID设备会组成一个DEVICE_STATE链表,也就是说系统中最大只有四个这种链表,通过结构体DEVICE_LISTpList成员进行指向。        好了,现在你可以知道要找到一个设备的DEVICE_STATE的方法了,就是先找到它对应的GUID类的DEVICE_LIST结构体,然后从该类结构体的pList里面遍历特定的设备名找到你的DEVICE_STATE结点。        系统中已经实现了该函数,即GetDeviceListFromClassDeviceStateFindListGetDeviceListFromClass代码如下: // 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;        系统中共维护两个该结构体的变量,即PMMDDDriver中定义的gStreamInterfacegDisplayInterface        其中,gDisplayInterface对应的GWES加载的设备,而gStreamInterface对应的是网络设备,块驱动设备和通用设备。        其各个成员的意思就不再赘述。

二.IClass在电源管理中的用途

1PM的相关初始化

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)) {          &