S3/S4/S5 Long Run Test

本文介绍了在NB工程中进行S3/S4/S5 Long Run Test的重要性,以及作者在实现S5 Wakeup过程中遇到的问题和解决方案。通过查阅资料和尝试API,作者发现常规方法无法实现S5 Wakeup,最后通过模拟BIOS的RTC唤醒功能,利用EC_BIOS在S5状态下完成唤醒。文章详细讲解了S3/S4的实现代码,并展示了S5 Wakeup的关键代码,涉及WMI ACPI的使用。

S3/S4/S5 Long Run Test

 

  1. Why need it

  NB研发的工程中,需要跑很多的测试项目,其中long run S3/S4/S5就是非常重要的测试项目。而且对于测试结果非常看重,一般long run测试fail就没法出货。常规的测试方法就是1020台机器测试S3/S4/S5 1000 cycle,如果fail率在万分之几就有可能要挂了。于是我就有了写一个long run S3/S4/S5测试程序的想法了。

 

  1. How to implement?

 

      心动不如行动,Let’s go!经过几番查阅MSDNS3/S4的功能已经有些眉目了,可是如何实现S5 wakeup呢?猛翻SDK&DDK,狂试API结果发现不行,好像没有相关的API能做到这件事。最后联系微软的FAE,他们的结论也是如此。既然常规做法不行,那么我就另辟蹊径。我知道BIOS Setup menu 有个选项可以设置RTC唤醒,只要机器还有电S5的状态下也可以唤醒机器。那么BIOS是怎么做的呢?我请教了BIOS 得知需要做两个动作:1.设置CMOS中的alarm time 2:将chipset RTC_EN bit置起然后进入S5。系统在alarm time到达时会产生wakeup eventChipset会送power sequence系统就会开机。通过一个IO port driver,完成上述过程后我调用API ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE,0);关机,我等的头发都白了系统也没有唤醒L,到底什么地方出了问题呀?后来我用SE.exe模拟上述过程,结果发现RTC_EN被我修改过之后,过了一会居然又被改回原来的值了,看起来windows在幕后做了不少小动作哦J,这样一来这个方法就行不通了,那么该怎么办呢?答案就是使用EC_BIOS去做,只要有电EC就会运行,那么S5EC仍然在工作,所以只需要EC在特定的条件下模拟一个开机动作即可。思路已经有了那么该如何实现呢?方法有两种:a.通过IO port driver 81 cmd66 port,然后将EC ram indexvalue送给62 port,这样就可以修改EC ram了,然后ECS5时检查该EC ram值如果非0就倒数计时,时间到了就模拟一个开机动作,从而完成S5 wake upb.同样是写EC ram不过使用不同的方法,我们可以在BIOS asl code里定制一个WMI ACPI device,并且在该device scope提供query/set EC ram的方法,并且提供一个WMI ACPIdriver,这样应用程序就可以方便的操纵EC ram了,而且一旦完成这只driver就可以完成非常多的增值部分,比如我们可以写一个程序读写EC ram中的battery info;或者我们可以写一个读取thermal info程序等等。  

如下图1是程序的运行画面,该测试程序实现了S3/S4/S5的功S3/S4xp下面可以正常工作,而vista下无法唤醒L。下S3/S4是通下述代码实现的:

 

                        1

         

            //hibernate and standby

            int        CAutoPowerOnShutdownDlg::SetPower(BOOL   bSuspend,BOOL   bForce)

            {            

                        TOKEN_PRIVILEGES   tp;          

                        HANDLE   hToken;  

                        LUID   luid;  

                        LPTSTR   MachineName=NULL;    

 

                        if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken   ))  

                        {  

                                    return   RTN_ERROR;  

                        }  

                        if(!LookupPrivilegeValue(MachineName,   SE_SHUTDOWN_NAME,   &luid))  

                        {  

                                    return   RTN_ERROR;   

                        }  

                        tp.PrivilegeCount   =   1;  

                        tp.Privileges[0].Luid   =   luid;  

                        tp.Privileges[0].Attributes   =   SE_PRIVILEGE_ENABLED;  

                        AdjustTokenPrivileges(hToken,   FALSE,   &tp,   sizeof(TOKEN_PRIVILEGES),NULL,                          NULL   );  

                        SetSystemPowerState(bSuspend,bForce);  

 

                        return   0;  

  }

  

S3/S4 wake up function是通过在下去的时候设置一个WaitableTimer现唤醒的功能代码如下所示:

        case   PBT_APMSUSPEND: 

        {

                HANDLE hTimer=::CreateWaitableTimer(NULL,TRUE,CString(_T("WaitForResume")));  

                if(!hTimer)  

                {  

                        MessageBox(CString(_T("Fail   to   create   waitable   timer!")));   

                }  

                                                       

                hTimer=OpenWaitableTimer(TIMER_ALL_ACCESS,TRUE,CString(_T("WaitForResume")));  

                LARGE_INTEGER   liDueTime;  

                liDueTime.QuadPart=m_DlInf.m_TimeSnd *1000*1000*(-10);  

                if(!::SetWaitableTimer(hTimer,&liDueTime,0,NULL,NULL,TRUE))  

                {  

                        MessageBox(CString(_T("Fail   to   set   waitable   timer!")));  

                        break;  

                }  

        }

break;

 

最关键的部分就是S5 wake up了我们来看看它的实现代码吧,代码中最

心的部分就是通过连接到WMI namespace,查询到我们定制的WMI

class  MSI_System,然后通过

 HRESULT PutInstance(
  [in]   IWbemClassObject* pInst,
  [in]   LONG lFlags,
  [in]   IWbemContext* pCtx,
  [out]  IWbemCallResult** ppCallResult
);

修改该classSystem变量,而该变量在BIOSasl中被定义在EC ram

的特定位置,这样就会更新EC ram中的值。 

            void CAutoPowerOnShutdownDlg::SetS4WakeTimer(unsigned char seconds)

            {

                        HRESULT hres;

 

              // Step 1: --------------------------------------------------

              // Initialize COM. ------------------------------------------

 

             hres =  CoInitializeEx(0, COINIT_MULTITHREADED);

              if (FAILED(hres))

             {

              cout << "Failed to initialize COM library. Error code = 0x"

            << hex << hres << endl;

                                    return;

              }

 

              // Step 2: --------------------------------------------------

            // Set general COM security levels --------------------------

             // Note: If you are using Windows 2000, you need to specify -

             // the default authentication credentials for a user by using

             // a SOLE_AUTHENTICATION_LIST structure in the pAuthList ----

             // parameter of CoInitializeSecurity ------------------------

 

              hres =  CoInitializeSecurity(

                        NULL,

                        -1,                          // COM authentication

                        NULL,                        // Authentication services

                         NULL,                        // Reserved

            RPC_C_AUTHN_LEVEL_DEFAULT,   // Default authentication

            RPC_C_IMP_LEVEL_IMPERSONATE, // Default Impersonation 

            NULL,                        // Authentication info

            EOAC_NONE,                   // Additional capabilities

                        NULL                         // Reserved

                        );

 

                     

            if (FAILED(hres))

            {

            cout << "Failed to initialize security. Error code = 0x"

                        << hex << hres << endl;

            CoUninitialize();

                        return;

            }

   

            // Step 3: ---------------------------------------------------

            // Obtain the initial locator to WMI -------------------------

 

            IWbemLocator *pLoc = NULL;

 

            hres = CoCreateInstance(

            CLSID_WbemLocator,            

            0,

            CLSCTX_INPROC_SERVER,

            IID_IWbemLocator, (LPVOID *) &pLoc);

 

            if (FAILED(hres))

            {

            cout << "Failed to create IWbemLocator object."

                        << " Err code = 0x"

                        << hex << hres << endl;

            CoUninitialize();

            return ;                 // Program has failed.

            }

 

            // Step 4: -----------------------------------------------------

             // Connect to WMI through the IWbemLocator::ConnectServer method

 

            IWbemServices *pSvc = NULL;

           

            // Connect to the root/cimv2 namespace with

            // the current user and obtain pointer pSvc

            // to make IWbemServices calls.

            hres = pLoc->ConnectServer(

            _bstr_t(L"ROOT//WMI"), // Object path of WMI namespace

            NULL,                    // User name. NULL = current user

            NULL,                    // User password. NULL = current

            0,                       // Locale. NULL indicates current

            NULL,                    // Security flags.

            0,                       // Authority (e.g. Kerberos)

            0,                       // Context object

            &pSvc                    // pointer to IWbemServices proxy

            );

   

            if (FAILED(hres))

            {

            cout << "Could not connect. Error code = 0x"

                        << hex << hres << endl;

            pLoc->Release();    

            CoUninitialize();

                        return ;                // Program has failed.

            }

 

            cout << "Connected to ROOT//CIMV2 WMI namespace" << endl;

 

 

            // Step 5: --------------------------------------------------

            // Set security levels on the proxy -------------------------

 

            hres = CoSetProxyBlanket(

            pSvc,                        // Indicates the proxy to set

            RPC_C_AUTHN_WINNT,           // RPC_C_AUTHN_xxx

            RPC_C_AUTHZ_NONE,            // RPC_C_AUTHZ_xxx

            NULL,                        // Server principal name

            RPC_C_AUTHN_LEVEL_CALL,      // RPC_C_AUTHN_LEVEL_xxx

            RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx

            NULL,                        // client identity

            EOAC_NONE                    // proxy capabilities

            );

 

            if (FAILED(hres))

            {

            cout << "Could not set proxy blanket. Error code = 0x"

                        << hex << hres << endl;

            pSvc->Release();

            pLoc->Release();    

            CoUninitialize();

            return ;               // Program has failed.

            }

 

            // Step 6: --------------------------------------------------

            // Use the IWbemServices pointer to make requests of WMI ----

 

            // For example, get the name of the operating system

            IEnumWbemClassObject* pEnumerator = NULL;

             hres = pSvc->ExecQuery(

            bstr_t("WQL"),

            bstr_t("SELECT * FROM MSI_System"),

            WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,

            NULL,

            &pEnumerator);

   

            if (FAILED(hres))

            {

            cout << "Query for operating system name failed."

                        << " Error code = 0x"

                        << hex << hres << endl;

            pSvc->Release();

            pLoc->Release();

            CoUninitialize();

            return;               // Program has failed.

            }

 

            // Step 7: -------------------------------------------------

            // Get the data from the query in step 6 -------------------

 

            IWbemClassObject *pclsObj;

            ULONG uReturn = 0;

            int count = 0;

            while (pEnumerator)

            {

                        HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1,

                                    &pclsObj, &uReturn);

 

                        if(0 == uReturn)

                        {

                                    break;

                        }

 

                                    if(++count == 3)

                                    {

                                                CString tmp;

                                                tmp.Format(_T("%d"),seconds);

                                                // Set up the property value.

                                                VARIANT v;

                                                VariantInit(&v);

                                                V_VT(&v) = VT_BSTR;

                                                V_BSTR(&v) = tmp.AllocSysString(); // - decimal format, not hex

 

                                                hr = pclsObj->Put(L"System",0,&v,CIM_UINT8);

                                                // Clean up.

                                                VariantClear(&v);

                                   

                                                if (hr == WBEM_E_ACCESS_DENIED)

                                                {

                                                            printf("WBEM_E_ACCESS_DENIED/n");

 

                                                            // Processing to handle specific error code

                                                }

                                                else if (hr == WBEM_S_DUPLICATE_OBJECTS)

                                                {

                                                            printf("WBEM_S_DUPLICATE_OBJECTS/n");

                                                            // All other cases, including errors specific to COM

                                                }

                                                else if (hr == WBEM_E_INVALID_OBJECT)

                                                {

                                                            printf("WBEM_E_INVALID_OBJECT/n");

                                                }

                                                else if(hr == WBEM_E_INVALID_PARAMETER)

                                                {

                                                            printf("WBEM_E_INVALID_PARAMETER/n");

                                                }

                                                else if(hr == WBEM_S_NO_ERROR)

                                                {

                                                            printf("WBEM_S_NO_ERROR/n");

                                                }

                                                else

                                                            printf("ERROR:%x",hr);

 

                                    HRESULT hRes = pSvc->PutInstance(pclsObj,WBEM_FLAG_CREATE_OR_UPDATE,0,0);

                                   

                                                // Check for specific error and status codes.

                                                if (hRes == WBEM_E_ACCESS_DENIED)

                                                {

                                                            printf("WBEM_E_ACCESS_DENIED/n");

 

                                                            // Processing to handle specific error code

                                                }

                                                else if (hRes == WBEM_S_DUPLICATE_OBJECTS)

                                                {

                                                            printf("WBEM_S_DUPLICATE_OBJECTS/n");

                                                            // All other cases, including errors specific to COM

                                                }

                                                else if (hRes == WBEM_E_INVALID_OBJECT)

                                                {

                                                            printf("WBEM_E_INVALID_OBJECT/n");

                                                }

                                                else if(hRes == WBEM_E_INVALID_PARAMETER)

                                                {

                                                            printf("WBEM_E_INVALID_PARAMETER/n");

                                                }

                                                else if(hRes == WBEM_S_NO_ERROR)

                                                {

                                                            printf("WBEM_S_NO_ERROR/n");

                                                }

                                    }

 

            }

 

            // Cleanup

            // ========

   

            pSvc->Release();

            pLoc->Release();

            pEnumerator->Release();

            pclsObj->Release();

            CoUninitialize(); 

      }

以上就是该程序实现的全部过程,完整的source code可以在这里下载S5 wake up使用了WMI ACPI,该部分比较复杂,我在后续会发一个WMI ACPI的系列,完整的描述WMI ACPI实现过程中BIOS,EC,OS,Driver分别扮演的角色。

 

Peter

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值