IClass与电源管理

 

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

                    PMLOGMSG(ZONE_WARN, (_T("%s: can't convert '%s' to GUID/r/n"),

                        pszFname, szBuf));

                }

                   // 如果是通用设备的GUID,则跳过,留到最后进行初始化其DEVICE_LIST

                   else if(idInterface == idGenericPMDeviceClass) {

                    PMLOGMSG(ZONE_INIT, (_T("%s: default GUID found in registry as expected/r/n"),

                        pszFname));

                }

                   // 为特定GUID类分配并初始化一个DEVICE_LIST结构体变量

                   else if((pdl = DeviceListCreate(&idInterface)) == NULL) {

                    PMLOGMSG(ZONE_WARN, (_T("%s: DeviceListCreate() failed/r/n"),

                        pszFname));

                }

                   // 初始化上面分配的DEVICE_LIST结构体变量的成员pInterface,使其指向有效的函数指针结构体

                   // 函数PlatformDeviceListInit这里非常重要,它初始化了DEVICE_LIST结构体变量的成员pInterface,而pInterface的具体实现包括gDisplayInterfacegStreamInterface,在pmstream.cpppmdisplay.cpp可以找到其具体的实现,代码写的蛮经典的

                   // 这些函数指针结构体通过PM DriverMDD层获得

                   else if(PlatformDeviceListInit(pdl) == FALSE) {

                    PMLOGMSG(ZONE_WARN, (_T("%s: PlatformDeviceListInit() failed/r/n"),

                        pszFname));

                    DeviceListDestroy(pdl);

                }

                   // 将当前GUIDDEVICE_LIST结构体变量添加链表gpDeviceLists

                   else {

                    // add the new entry to the list

                    pdl->pNext = gpDeviceLists;

                    gpDeviceLists = pdl;

                }

 

                // update the index

                dwIndex++;

            }

        } while(dwStatus == ERROR_SUCCESS);

 

        // check for abnormal termination of the loop

        if(dwStatus != ERROR_NO_MORE_ITEMS) {

            fOk = FALSE;

        }

 

        // close the registry handle

        RegCloseKey(hk);

    }

 

// add the default list last

// 通用设备类留在最后进行初始化,为什么?

// 主要是为了让通用设备类保持在gpDeviceLists的最前面,利于后面的处理

    if(fOk) {

        fOk = FALSE;

        pdl = DeviceListCreate(&idGenericPMDeviceClass);

        if(pdl != NULL) {

            if(PlatformDeviceListInit(pdl) == FALSE) {

                PMLOGMSG(ZONE_INIT || ZONE_WARN,

                    (_T("%s: PlatformDeviceListInit() failed for default class/r/n"),

                    pszFname));

                DeviceListDestroy(pdl);

            } else {

                pdl->pNext = gpDeviceLists;

                gpDeviceLists = pdl;

                fOk = TRUE;

            }

        }

    }

 

    // clean up if necessary

    if(!fOk) {

        PMLOGMSG(ZONE_WARN, (_T("%s: error during list initialization/r/n"),

            pszFname));

        while(gpDeviceLists != NULL) {

            pdl = gpDeviceLists;

            gpDeviceLists = pdl->pNext;

            pdl->pNext = NULL;

            DeviceListDestroy(pdl);

        }

    }

 

    return fOk;

}

       接下来PmInit会去创建线程PnpThreadProc,该线程非常重要,它主要来检测系统中地新加载的设备。虽然其名字是PNP,但是其服务对象不仅仅是PNP设备,迷惑了我十几分钟。

PnpThreadProc优先级可以通过注册表项PnPPriority256去进行配置,如果没有配置,将采用默认的249

// this thread waits for power manageable devices to be announced.  When

// they arrive they are added to the PM's list of devices and initialized

// appropriately.  If a device goes away, its entry will be removed

// from the list.

DWORD WINAPI

PnpThreadProc(LPVOID lpvParam)

{

    DWORD dwStatus;

    HANDLE hnGeneric = NULL;

    HANDLE hevReady = (HANDLE) lpvParam;

    HANDLE hEvents[MAXIMUM_WAIT_OBJECTS];

    DWORD dwNumEvents = 0;

    BOOL fDone = FALSE;

    BOOL fOk;

    INT iPriority;

    PDEVICE_LIST pdl;

    SETFNAME(_T("PnpThreadProc"));

 

    PMLOGMSG(ZONE_INIT, (_T("+%s: thread 0x%08x/r/n"), pszFname, GetCurrentThreadId()));

 

    // set the thread priority

    if(!GetPMThreadPriority(_T("PnPPriority256"), &iPriority)) {

        iPriority = DEF_PNP_THREAD_PRIORITY;

    }

    CeSetThreadPriority(GetCurrentThread(), iPriority);

 

    // first list entry is the exit event

    hEvents[dwNumEvents++] = ghevPmShutdown;

 

    // set up device notifications

    for(pdl = gpDeviceLists; pdl != NULL && dwNumEvents < dim(hEvents); pdl = pdl->pNext) {

        hEvents[dwNumEvents++] = pdl->hMsgQ;

         // 有几个GUID类别,就申请几个通知

        pdl->hnClass = RequestDeviceNotifications(pdl->pGuid, pdl->hMsgQ, TRUE);

        if(pdl->hnClass == NULL) {

            PMLOGMSG(ZONE_WARN, (_T("%s: RequestDeviceNotifications() failed %d/r/n"),

                pszFname, GetLastError()));

        }

    }

    DEBUGCHK(dwNumEvents > 1);

 

    // we're up and running

    SetEvent(hevReady);

 

    // wait for new devices to arrive

    while(!fDone) {

        dwStatus = WaitForMultipleObjects(dwNumEvents, hEvents, FALSE, INFINITE);

        if(dwStatus == (WAIT_OBJECT_0 + 0)) {

            PMLOGMSG(ZONE_WARN, (_T("%s: shutdown event set/r/n"), pszFname));

            fDone = TRUE;

        } else if(dwStatus > WAIT_OBJECT_0 && dwStatus <= (WAIT_OBJECT_0 + MAXIMUM_WAIT_OBJECTS)) {

            dwStatus -= WAIT_OBJECT_0;

              // 收到通知就就对特定GUID设备的电源状态进行获取

            fOk = ProcessPnPMsgQueue(hEvents[dwStatus]);

            if(!fOk) {

                PMLOGMSG(ZONE_WARN, (_T("%s: ProcessPnPMsgQueue(0x%08x) failed/r/n"), pszFname,

                    hEvents[dwStatus]));

            }

        } else {

            PMLOGMSG(ZONE_WARN, (_T("%s: WaitForMultipleObjects() returned %d, status is %d/r/n"),

                pszFname, dwStatus, GetLastError()));

            fDone = TRUE;

            break;

        }

    }

 

    // release resources

    for(pdl = gpDeviceLists; pdl != NULL; pdl = pdl->pNext) {

        if(pdl->hnClass != NULL) StopDeviceNotifications(pdl->hnClass);

    }

 

    // all done

    PMLOGMSG(ZONE_INIT | ZONE_WARN, (_T("-%s: exiting/r/n"), pszFname));

    return 0;

}

       可以上面可以看到,线程PnpThreadProc只会侦测链表gpDeviceLists中创建的GUID类设备的通知,而gpDeviceLists是由函数DeviceListsInit()中进行初始化的。也就是说,PM最终所能够识别的设备是那些在注册表[HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet

/Control/Power/Interfaces]下定义的GUID类,这也是为什么设备支持电源管理的话必须定义IClass

       当然,理论上来讲,你可以修改注册表,添加一种新的GUID类,并ReWrite PM的代码,使其支持你所添加的GUID类设备。

2PM获取新加载设备所支持电源状态过程

       从上面函数PnpThreadProc()中可以看到,当侦测到有新设备通知发出来后,将会调用函数ProcessPnPMsgQueue()

       函数ProcessPnPMsgQueue()完成的功能是获取设备所支持的电源状态,方式是调用MDD层提供的函数RequestStreamDevice或者RequestDisplayDevice。再说的具体一点就是,分别调用到设备驱动中的DeviceIoControlExtEscape

       请看函数ProcessPnPMsgQueue()的代码:

// This routine adds a device to the list associated with its device class.

// This routine does not return a value; it will either create a new

// device state structure and add it to a list or it will not.  If the new

// device duplicates an existing one this routine won't create a new node.

// This routine executes in the context of the PnP thread, which handles

// device interface additions and removals.

VOID

AddDevice(LPCGUID guidDevClass, LPCTSTR pszName, PDEVICE_STATE pdsParent,

          PPOWER_CAPABILITIES pCaps)

{

    SETFNAME(_T("AddDevice"));

 

    PMLOGMSG(ZONE_DEVICE,

        (_T("%s: adding '%s', pdsParent 0x%08x, pCaps 0x%08x to class %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x/r/n"),

        pszFname, pszName, pdsParent, pCaps,

        guidDevClass->Data1, guidDevClass->Data2, guidDevClass->Data3,

        (guidDevClass->Data4[0] << 8) + guidDevClass->Data4[1], guidDevClass->Data4[2], guidDevClass->Data4[3],

        guidDevClass->Data4[4], guidDevClass->Data4[5], guidDevClass->Data4[6], guidDevClass->Data4[7]));

 

    // figure out onto which list this device should be added

     // DEVICE_LIST链表中查询当前设备所在的GUID类对应的DEVICE_LIST指针

    PDEVICE_LIST pdl = GetDeviceListFromClass(guidDevClass);

   

    // did we find the list?

    if(pdl != NULL) {

        // check for duplicates

         // 到当前设备所对应的DEVICE_LIST链表中查询是否在名字为pszName的设备

        PDEVICE_STATE pds = DeviceStateFindList(pdl, pszName);

       

        // create the device if it doesn't already exist

         // 如果不存在,则创建一个

        if(pds == NULL) {

            BOOL fOk = FALSE;

              // 为当前设备创建一个DEVICE_STATE节点

            pds = DeviceStateCreate(pszName);

            if(pds != NULL) {

                // if we are passed the device's capabilities, just copy them

                // into the structure

                if(pCaps != NULL) {

                    __try {

                        pds->caps = *pCaps;

                    }

                    __except(EXCEPTION_EXECUTE_HANDLER) {

                        PMLOGMSG(ZONE_WARN,

                            (_T("%s: exception during capabilities copy from 0x%08x/r/n"),

                            pszFname, pCaps));

                        pCaps = NULL;

                    }

                }

               

                // update the device's parent pointer

                if(pdsParent != NULL) {

                    DeviceStateAddRef(pdsParent);

                }

                pds->pParent = pdsParent;

               

                // add the new device to its class list

                   // 将当前设备的DEVICE_STATE加入到guidDevClass对应的DEVICE_LIST链表中

                if(!DeviceStateAddList(pdl, pds)) {

                    // deallocate the node, reference count isn't incremented

                    DeviceStateDecRef(pds);

                    pds = NULL;

                } else {

                    PREFAST_DEBUGCHK(pds->pInterface != NULL);

                    PREFAST_DEBUGCHK(pds->pInterface->pfnOpenDevice != NULL);

                    PREFAST_DEBUGCHK(pds->pInterface->pfnRequestDevice != NULL);

                    PREFAST_DEBUGCHK(pds->pInterface->pfnCloseDevice != NULL);

                       // 获取设备的操作Handle,这里比较重要,调用的其实是MDD层中首先的函数OpenStreamDevice或者OpenDisplayDevice

                    pds->hDevice = pds->pInterface->pfnOpenDevice(pds);

                    if(pds->hDevice == INVALID_HANDLE_VALUE) {

                        PMLOGMSG(ZONE_WARN, (_T("%s: couldn't open device '%s'/r/n"),

                            pszFname, pszName != NULL ? _T("<NULL>") : pszName));

                    } else {

                        // do we need to request capabilities?

                        fOk = TRUE;             // assume success

                        if(pCaps == NULL) {

                            DWORD dwBytesReturned;

                            POWER_RELATIONSHIP pr;

                            PPOWER_RELATIONSHIP ppr = NULL;

                            memset(&pr, 0, sizeof(pr));

                            if(pds->pParent != NULL) {

                                PMLOGMSG(ZONE_DEVICE, (_T("%s: parent of '%s' is '%s'/r/n"),

                                    pszFname, pds->pszName, pds->pParent->pszName));

                                pr.hParent = (HANDLE) pds->pParent;

                                pr.pwsParent = pds->pParent->pszName;

                                pr.hChild = (HANDLE) pds;

                                pr.pwsChild = pds->pszName;

                                ppr = &pr;

                            }                       

                           

                            // get the device's capabilities structure

                                 // 有点意思了,呵呵,调用IOCTL_POWER_CAPABILITIES获取设备所支持的电源状态这里比较重要,调用的其实是MDD层中首先的函数RequestStreamDevice或者RequestDisplayDevice,对于这两类设备来说,分别调用到设备驱动中的DeviceIoControlExtEscape

                            fOk = pds->pInterface->pfnRequestDevice(pds->hDevice, IOCTL_POWER_CAPABILITIES,

                                ppr, ppr == NULL ? 0 : sizeof(*ppr),

                                &pds->caps, sizeof(pds->caps), &dwBytesReturned);

                           

                            // sanity check the size in case a device is just returning

                            // a good status on all ioctls for some reason

                            if(fOk && dwBytesReturned != sizeof(pds->caps)) {

                                PMLOGMSG(ZONE_WARN,

                                    (_T("%s: invalid size returned from IOCTL_POWER_CAPABILITIES/r/n"),

                                    pszFname));

                                fOk = FALSE;

                            }

                        }

 

                        // any problems so far?

                        if(fOk) {

                            // determine whether we should request power relationships from a parent device

                                 // 帮助文档中提到,这里的意思是:

                                 // Set to POWER_CAP_PARENT bit if the device would like to receive an IOCTL_REGISTER_POWER_RELATIONSHIP call after initialization

                                 // BSP中并没有使用,应该不用关注

                            if((pds->caps.Flags & POWER_CAP_PARENT) != 0) {

                                pds->pInterface->pfnRequestDevice(pds->hDevice, IOCTL_REGISTER_POWER_RELATIONSHIP,

                                    NULL, 0, NULL, 0, NULL);

                            }

                        }

                    }

                }

               

                // have we read all the configuration information we need from

                // the new device

                if(!fOk) {

                    // no, delete the device

                    DeviceStateRemList(pds);

                } else {

                    // See if the device supports multiple handles.  Power manageable devices

                    // should allow multiple open handles, but if they don't we will have to open

                    // one before each access.

                    HANDLE hDevice = pds->pInterface->pfnOpenDevice(pds);

                    if(hDevice == INVALID_HANDLE_VALUE) {

                        PMLOGMSG(ZONE_WARN, (_T("%s: WARNING: '%s' does not support multiple handles/r/n"),

                            pszFname));

                        pds->pInterface->pfnCloseDevice(pds->hDevice);

                        pds->hDevice = INVALID_HANDLE_VALUE;

                    } else {

                        // close the second handle, since we don't need it

                        pds->pInterface->pfnCloseDevice(hDevice);

                    }

                   

                    // initialize the new device's power state variables

                       // 这里就不用说了,更新电源状态,呵呵

                    UpdateDeviceState(pds);

                }

            }

        }

       

        // we are done with the device pointer

        if(pds != NULL) {

            DeviceStateDecRef(pds);

        }

    } else {

        PMLOGMSG(ZONE_WARN, (_T("%s: class for device '%s' not supported/r/n"),

            pszFname, pszName));

    }

}

3IClassAP层面的影响

       这个比较简单,可以参照Help文档,如下:

Beginning with Windows CE .NET 4.10, power-manageable devices can belong to varying device classes. These device classes consist both of predefined classes as well as custom device classes. The Power Manager APIs that accept device names can also accept class-qualified device names. For example, each of the following names is a valid device name:

·                 COM1:

·                 {A32942B7-920C-486b-B0E6-92A702A99B35}/COM1:

·                 {98C5250D-C29A-4985-AE5F-AFE5367E5006}/CISCO1

·                 {8DD679CE-8AB4-43c8-A14A-EA4963FAA715}/DSK1:

If a class does not qualify a device name, the device is assumed to belong to the default device class. For example, the names COM1: and {A32942B7-920C-486b-B0E6-92A702A99B35}/COM1: are equivalent.

       可以看到,AP调用PMAPI尝试去获取或者改变设备的电源状态的时候,需要传入争取地Device name,如果传入的Device name不包含IClass的话,系统将其认为是默认的IClass类,也就是{A32942B7-920C-486b-B0E6-92A702A99B35}

       由于SDK中会有GUID类的定义,所以AP中调用PM API的时候最好能够传入完整的Device name

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/243306.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

状态机在VHDL中的实现

状态机在VHDL中的实现 1、Moore状态机的VHDL描述 输出仅取决于所处的状态 LIBRARY IEEE; --库、程序包的说明调用 USE IEEE.STD_LOGIC_1164.ALL;ENTITY Moore IS PORT (RESET,CLOCK,DIN : IN STD_LOGIC;DOUT : OUT STD_LOGIC_VECTOR(2 DOWNTO 0) ); END;ARCHITECTURE Mo…

Linux : find big file in the all directories

1. Juniper Firewall find . -type f -size 10000 -exec ls -lh {} ; Sample output: [email protected]% find . -type f -size 10000 -exec ls -lh {} ; -rw-r–r– 1 930 929 134M Jan 5 17:34 ./cf/packages/junos-11.4R6.6-domestic-rw-r–r– 1 root wheel 1…

VC++静态文本框/PICTURE控件的notify属性

RT&#xff0c;该属性对应的是SS_NOTIFY&#xff0c;但是很多人误以为是WM_NOTIFY 。该属性可以用ModifyStyle函数修改。

VHDL仿真

VHDL仿真 仿真(Simulation也称模拟),不接触具体的硬件系统利用计算机对电路设计的逻辑行为和运行功能进行模拟检测,较大规模的VHDL系统设计的最后完成必须经历多层次的仿真测试过程,包括针对系统的VHDL行为仿真、分模块的时序仿真和硬件仿真,直至最后系统级的硬件仿真测…

从Var Tick角度来对CE电源管理

从Var Tick角度来对CE电源管理 一&#xff0e;相关的基础知识如下 1&#xff0e;OAL中Timer相关函数说明 1> OALTimerInit 参数&#xff1a; msecPerSysTick: 每个系统调度Tick对应多少ms&#xff1b; countsPerMSec: Timer的Counter变化多少为1ms&#xff0c;其值为T…

变量初始化的确定性

变量初始化的确定性 SystemVerilog初始化顺序 SystemVerilog标准增强了变量的内嵌初始化。SystemVerilog规定所有内嵌初始化先于仿真时刻0执行的事件。这就保证了如果Initial或者always过程块读取具有内嵌初始值的变量时取得正确的初始值&#xff0c;这个确定行为消除了Verilo…

很好的Android论坛

需要的兄弟可以看一下 http://www.eoeandroid.com/?fromuid9379

用户自定义和枚举数据类型

用户自定义和枚举数据类型 用户自定义 1、typedef定义用户自定义类型 SystemVerilog同C一样&#xff0c;使用typedef关键字来建立用户自定义类型。用户自定义类型允许使用现有的数据类型建立新的数据类型。新的数据类型定义后&#xff0c;可以声明这个类型的变量 typedef int…

Keyboard驱动介绍

Keyboard驱动介绍 最近手里面没啥事&#xff0c;就想看看一些Driver的MDD层。 以前改过Keyboard Driver的PDD层&#xff0c;但是对它的MDD层还真是一片空白&#xff0c;这两天随便看了看Keyboard的MDD层&#xff0c;赶紧把东西记录下来&#xff0c;以防以过段时间忘记了。 很多…

GDI+不同的地方

研究了GDI处理图像的地方&#xff0c;发现它一些与众不同的地方&#xff0c;被它坑了一天。。。。。1、GDI的像素的原点默认你在左下角的&#xff0c;所以读取像素的顺序是从最低一行开始的(bottom-left)&#xff0c;其他一般的图像处理软件&#xff0c;像Photoshop&#xff0c…

关于结构体的内容

关于结构体的内容 结构体使用类似于C语言的语法来定义 结构体使用struct关键字声明。结构体内的成员可以是任何数据类型&#xff0c;包括用户自定义类型和其他的结构体类型。 struct{int a,b; //32位变量opcode_t opcode;//用户定义类型logic [23:0] adress;//24位变量bit er…

Pushing Policy Failed because Checkpoint Firewall “Load on module failed – no memory”

One day when pushing firewall policy from Checkpoint management server to UTM 272 cluster gateways, it failed and I got error message “Load on module failed – no memory” on one of cluster members. “Network Security Policy ‘Montreal_DMZ’ was prepared …

电池驱动介绍

电池驱动介绍 一&#xff0e;整体框架 电池驱动代码量很小&#xff0c;可是麻雀虽小&#xff0c;五脏俱全。与其他的很多Driver一样&#xff0c;分为PDDMDD层&#xff0c;双层之间通过PDD的如下导出接口相联系。 Programming element Description BatteryDrvrGetLevels…

将1bpp的bmp图像存储为1bpp或者2bpp的tiff格式

// 将1bpp的位图转换为 1bit/2bit tiff /** 参数&#xff1a;BYTE *src 二值图像的像素数据&#xff0c;不包含头部信息&#xff0c; 1bpp, int src_width 原图的宽度, in pixles,int src_height 原图的高度, in pixlesint bpp 转换tiff指定的bpp */ static BYTE *BW2Tif(…

关于联合体的内容

关于联合体的内容 联合体只储存一个值 联合体只存储一个元素&#xff0c;但这个元素可以有多种表示方法&#xff0c;每种表示可以是不同的数据类型。 联合体的声明语法类似于结构体&#xff0c;联合体的成员的引用也跟结构体一样。 union{int i;int unsigned u; }data; ... d…

Point-BERT:一种基于Transformer架构的点云深度网络

目录 1. 前言 2. Point Tokenization 3. Transformer Backbone 4. Masked Point Modeling 5. Experiments Reference 1. 前言 从PointNet [1] 开始&#xff0c;点云深度网络逐渐成为解决点云特征提取与语义分析的主要研究方向。尤其在OpenAI的GPT模型获得了突破性成果后&#…

GNS3 VoIP Lab (Cisco 3725 and CME 4.3)

Here is a simple VoIP Lab in GNS3 environment. It is only used for my lab test and recorded here for future reference. 1. Topology: GNS3 Topology:Logic Topology:xp(192.168.2.60)——–C3725 Router(192.168.2.10) 2. Enviroment: ESXi 5.5 (or Vmware Workstation…

谁知道这个代码片段干嘛的

int value 0xAAAA;for (int i0; i<8; i){int tmp value & 0x3; // 取出第两个比特位置if (tmp 0x0){//}else if (tmp 0x1){//}else if (tmp 0x2){//}else if(tmp 0x3){TRACE0("???");}TRACE1("tmp0x%x\n\n", tmp);value >> 2;}

关于数组的内容

关于数组的内容 Verilog数组声明的基本语法 <data_type><vector_size><array_name><array_dimension> 例如&#xff1a; reg[15:0] RAM [0:4095];//储存器数组SystemVerilog允许任何数据类型的非压缩数组 SystemVerilog将非压缩数组的声明进行了扩展…

Touch Driver介绍

Touch Driver介绍 一&#xff0e;相关知识介绍 1&#xff0e;Touch Driver的加载过程 GWES到[HKEY_LOCAL_MACHINE/HARDWARE/DEVICEMAP/TOUCH]的“Driver name”获取Driver DLL的名字&#xff0c;如果没有找到该键值&#xff0c;则使用默认名字Touch.dll。 Touch Driver的加…