轉載自: http://blog.csdn.net/xxxluozhen/article/details/5023703
https://blog.csdn.net/chenyujing1234/article/details/7846845
在Windows 2000和Windows 98中,操作系統(tǒng)接管了大部分電源管理工作。當然,這是因為只有操作系統(tǒng)才能真正了解電源管理的內(nèi)部過程。例如,系統(tǒng)BIOS負責的電源管理不能區(qū)分應用程序使用的屏幕和屏幕保護程序使用的屏幕之間的區(qū)別。但操作系統(tǒng)可以區(qū)分開這種不同,從而確定是否可以關閉顯示器。
作為計算機全局電源策略,操作系統(tǒng)支持一些用戶接口元素,用戶可以通過這些接口元素控制最終的電源管理策略。這些用戶接口元素包括控制面板、開始菜單上的命令、控制設備喚醒特征的API。通過向設備發(fā)送IRP,內(nèi)核的電源管理部件實現(xiàn)了操作系統(tǒng)的電源策略。WDM驅動程序主要是作為響應這些IRP的被動角色。
(1)設備的某個驅動程序需要充當設備電源策略的管理者。通常都是由功能驅動程序充當這個角色。電源管理器可以改變整個系統(tǒng)的電源狀態(tài)。功能驅動程序接收電源管理器發(fā)來的IRP(系統(tǒng)IRP),作為設備電源策略的管理者,功能驅動程序用設備理解的術語翻譯這些IRP并引發(fā)新的IRP(設備IRP)。
(2)當響應設備IRP時,功能驅動程序需要關心設備專有的細節(jié)。設備硬件會有自己的上下文信息,不應該在設備處于低電源期間丟失這些信息。例如,鍵盤驅動程序會保存鎖定鍵(如CAPS-LOCK、NUM-LOCK、SCROLL-LOCK)、LED等信息。功能驅動程序有責任保存并恢復這些上下文信息。
(3)某些設備帶有喚醒特征,當外部事件發(fā)生時,這些設備可以喚醒系統(tǒng);功能驅動程序應與用戶協(xié)同工作以確保喚醒特征在需要時有效。
(4)許多功能驅動程序還管理含有大量IRP的隊列(設備讀寫IRP),因此當設備電源狀態(tài)轉變時需要停止或釋放這些隊列。
處于設備堆棧底端的總線驅動程序有責任控制設備的電流和執(zhí)行任何與設備喚醒特征相關的電氣步驟。過濾器驅動程序通常作為電源管理IRP通過的管道,它們用專用的協(xié)議向下層驅動程序傳遞電源管理請求。
(1)WDM模型使用與ACPI(Advanced Configuration and Power Interface)規(guī)范(見http://www.teleport.com/~acpi/spec.htm)相同的術語來描述電源狀態(tài)。在D0狀態(tài)中,設備處于全供電狀態(tài)。在D3狀態(tài)中,設備處于無供電(或最小限度的電流)狀態(tài)。中間的D1和D2狀態(tài)指出設備的兩個不同睡眠狀態(tài)。隨著設備從D0狀態(tài)變化到D3狀態(tài),設備將消耗越來越少的電力,同時需要保留的當前狀態(tài)上下文信息也越來越少。而設備再轉變回D0狀態(tài)的延遲期則相應增加。
(2)Microsoft規(guī)定了不同類型設備的類專用電源需求。這個需求規(guī)范可以在http://www.microsoft.com/hwdev/specs/PMref/找到。例如,這個規(guī)范要求每個設備至少要支持D0和D3兩個狀態(tài)。輸入設備(鍵盤、鼠標等)還應該支持D1狀態(tài)。Modem設備需要另外支持D2狀態(tài)。設備類上的這些不同規(guī)定可能來源于設備的用途和工業(yè)上的實踐。
操作系統(tǒng)不直接處理設備的電源狀態(tài),由設備驅動程序專門處理。
(3)系統(tǒng)使用一組與ACPI設備狀態(tài)類似的系統(tǒng)電源狀態(tài)來控制電源。Working狀態(tài)是全供電狀態(tài),計算機可以實現(xiàn)全部功能。程序僅能在Working狀態(tài)下執(zhí)行。其它系統(tǒng)電源狀態(tài)對應更小的電力需求配置,在這些系統(tǒng)電源狀態(tài)中,計算機不能執(zhí)行任何指令。Shutdown狀態(tài)就是電源關閉狀態(tài)。Hibernate狀態(tài)是另一種Shutdown狀態(tài),它把計算機的整個狀態(tài)都記錄到硬盤上,因此在電源恢復供電時可以使計算機快速恢復到記錄前的狀態(tài)。在Hibernate和Working狀態(tài)之間是三個有不同電力消耗級別的中間狀態(tài)。
(1)系統(tǒng)初始化后即進入Working狀態(tài)。大部分設備也以D0狀態(tài)啟動,但某些設備的驅動程序會在設備啟動時使設備進入低電源消耗狀態(tài)。在系統(tǒng)啟動并正常運行后,這些設備的驅動程序才使設備進入一個穩(wěn)定的狀態(tài),在這個狀態(tài)中,系統(tǒng)電源處于Working狀態(tài),而設備處于的狀態(tài)取決于具體活動和設備自身的能力。
(2)用戶的活動或外部事件會導致電源狀態(tài)的改變。一個常見的電源狀態(tài)轉換情景是用戶在開始菜單上選擇“關閉系統(tǒng)”中的“standby”選項,使計算機進入等待狀態(tài)。在響應這個命令過程中,電源管理器首先向每個驅動程序發(fā)送帶有IRP_MN_QUERY_POWER副功能碼的IRP_MJ_POWER請求以詢問設備能否接受即將到來的電源關閉請求。如果所有驅動程序都同意,電源管理器將發(fā)送第二個帶有IRP_MN_SET_POWER副功能碼的電源管理IRP,然后驅動程序把其設備置入低電源狀態(tài)以響應這個IRP。如果有任何一個驅動程序否決了這個查詢,電源管理器仍舊發(fā)出這個IRP_MN_SET_POWER請求,但它用原來的電源級別換成了請求的電源級別。
所以在我們的功能驅動中的IRP_MJ_POWER的派遣函數(shù)中要去case這幾個電源的請求。eg:
- NTSTATUS USB2COM_ProcessPowerIrp(
- IN PDEVICE_OBJECT DeviceObject,
- IN PIRP Irp
- )
- {
- PIO_STACK_LOCATION irpStack;
- NTSTATUS ntStatus = STATUS_SUCCESS;
- PDEVICE_EXTENSION deviceExtension;
- BOOLEAN fGoingToD0 = FALSE;
- POWER_STATE sysPowerState, desiredDevicePowerState;
- KEVENT event;
- USB2COM_KdPrint( DBGLVL_MEDIUM,(" USB2COM_ProcessPowerIrp() IRP_MJ_POWER\n"));
- deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;
- irpStack = IoGetCurrentIrpStackLocation (Irp);
- USB2COM_IncrementIoCount(DeviceObject);
- switch (irpStack->MinorFunction)
- {
- case IRP_MN_WAIT_WAKE:
- break;
- case IRP_MN_SET_POWER:
- {
- switch (irpStack->Parameters.Power.Type)
- {
- case SystemPowerState:
- break;
- case DevicePowerState:
- break;
- }
- }
- break;
- case IRP_MN_QUERY_POWER:
- break;
- default:
- break;
- }
- }
(3)系統(tǒng)并不總是發(fā)送IRP_MN_QUERY_POWER請求。某些事件(如電池電力將要耗盡)必須被無條件接受,并且操作系統(tǒng)也不再發(fā)出查詢請求。如果查詢發(fā)出后,并且驅動程序也接受了請求的電源狀態(tài),那么驅動程序將不再啟動任何會妨礙未來電源狀態(tài)設置請求的操作。例如,磁帶機驅動程序在使一個進入低電源狀態(tài)的查詢請求成功返回前先確保當前沒有執(zhí)行備份操作。另外,該驅動程序還拒絕任何后來的備份命令,除非是另一個電源狀態(tài)設置請求。
該結構體在電源管理中占據(jù)很重要的地位,其主要進行細微的描述電源的狀態(tài)和剩余時間等方面的信息,該結構體描述如下;
- typedef struct _POWER_STATUS
- {
- BYTE PS_AC_Line_Status;
- BYTE PS_Battery_Status;
- BYTE PS_Battery_Flag;
- BYTE PS_Battery_Life_Percentage;
- WORD PS_Battery_Life_Time;
- } POWER_STATUS;
? PS_AC_Line_Status描述電源AC輸入線的狀態(tài)。
? PS_Battery_Status描述電池的狀態(tài)。
? PS_Battery_Status描述電池先前的狀態(tài)或者是改變時的狀態(tài)。
? PS_Battery_Life_Percentage描述電池還剩余多少電,以百分計。
? PS_Battery_Life_Time描述電池還能支持多少時間,以秒鐘計。
通常,我們總是把設備設置成與設備的當前活動、設備的喚醒特征、設備的能力、以及迫近的系統(tǒng)狀態(tài),相一致的最低的電源狀態(tài)。PnP管理器在啟動設備后(也可能是其它時間)的很短時間內(nèi)向設備發(fā)出查詢設備能力請求——IRP_MN_QUERY_CAPABILITIES請求,該請求的參數(shù)是一個DEVICE_CAPABILITIES結構,該結構體將描述系統(tǒng)的電源狀態(tài)信息,DEVICE_CAPABILITIES結構體描述:
- typedef struct _DEVICE_CAPABILITIES {
- USHORT Size;
- USHORT Version; // the version documented here is version 1
- ULONG DeviceD1:1;
- ULONG DeviceD2:1;
- ULONG LockSupported:1;
- ULONG EjectSupported:1; // Ejectable in S0
- ULONG Removable:1;
- ULONG DockDevice:1;
- ULONG UniqueID:1;
- ULONG SilentInstall:1;
- ULONG RawDeviceOK:1;
- ULONG SurpriseRemovalOK:1;
- ULONG WakeFromD0:1;
- ULONG WakeFromD1:1;
- ULONG WakeFromD2:1;
- ULONG WakeFromD3:1;
- ULONG HardwareDisabled:1;
- ULONG NonDynamic:1;
- ULONG WarmEjectSupported:1;
- ULONG NoDisplayInUI:1;
- ULONG Reserved:14;
- ULONG Address;
- ULONG UINumber;
- DEVICE_POWER_STATE DeviceState[POWER_SYSTEM_MAXIMUM];
- SYSTEM_POWER_STATE SystemWake;
- DEVICE_POWER_STATE DeviceWake;
- ULONG D1Latency;
- ULONG D2Latency;
- ULONG D3Latency;
- } DEVICE_CAPABILITIES, *PDEVICE_CAPABILITIES;
? DeviceState域為與每個系統(tǒng)狀態(tài)對應的可能存在的設備最高級狀態(tài)數(shù)組。
? SystemWake域為最低級的系統(tǒng)電源狀態(tài),在該狀態(tài)中設備可以生成喚醒信號。PowerSystemUnspecified域指出設備不能喚醒系統(tǒng)。
? DeviceWake域為最低級的設備電源狀態(tài),在該狀態(tài)中設備可以生成喚醒信號。PowerDeviceUnspecified指出設備不能生成喚醒信號
? D1Latency域為設備從D1切換到D0狀態(tài)所需的最長時間(100微秒單位)。
? D2Latency域為設備從D2切換到D0狀態(tài)所需的最長時間(100微秒單位)。
? D3Latency域為設備從D3切換到D0狀態(tài)所需的最長時間(100微秒單位)。
? WakeFromD0、WakeFromD1、WakeFromD2和WakeFromD3標志,指出當設備處于指定的狀態(tài)中時,設備的系統(tǒng)喚醒特征是否是可操作的
在WDM驅動當中,驅動程序響應IRP_MJ_POWER例程表示支持電源管理。電源狀態(tài)描述保存在IO_STACK_LOCATION的Parameters聯(lián)合中。
eg:
- case IRP_MN_SET_POWER:
- {
- USB2COM_KdPrint( DBGLVL_MEDIUM,("USB2COM_ProcessPowerIrp() Enter IRP_MN_SET_POWER\n"));
- switch (irpStack->Parameters.Power.Type) {
- case SystemPowerState:
該聯(lián)合提供了響應的四種電源狀態(tài),如下表3-3-2所示。表3-3-2 IO_STACK_LOCATION的Parameters.Power子結構中的各個域
在管理器和驅動溝通使用了IRP_MJ_POWER類型的I/O請求包,該功能附帶了四個輔助碼:
(1)IRP_MN_QUERY_POWER確定預期的電源狀態(tài)改變是否安全;
(2)IRP_MN_SET_POWER命令驅動程序改變電源狀態(tài);
(3)IRP_MN_WAIT_WAKE 命令總線驅動程序使用喚醒特征、使功能驅動程序能了解喚醒信號何時發(fā)生;
(4)IRP_MN_POWER_SEQUENCE 為上下文保存和恢復提供優(yōu)化。
對于整個操作系統(tǒng)而言,所有驅動程序,包括過濾器驅動程序和功能驅動程序通常都向其下面的驅動程序傳遞電源管理請求,除IRP_MN_QUERY_POWER請求外。
在功能驅動(例如、USB驅動),系統(tǒng)控制程序如何把電源管理請求傳遞到低級驅動程序有特殊的規(guī)則。三種可能的處理過程:
第一,在釋放一個電源管理請求的控制之前,你必須調(diào)用PoStartNextPowerIrp,即使你以錯誤狀態(tài)完成該IRP,也要這樣做。做這個調(diào)用的原因是,電源管理器自己需要維持一個電源管理請求隊列,所以必須通知它確實可以出隊一個請求,以及向設備發(fā)送下一個請求。除了調(diào)用PoStartNextPowerIrp,你還必須調(diào)用專用例程PoCallDriver(代替IoCallDriver)來向下層驅動程序發(fā)送請求。
當PNP管理器發(fā)出IRP_MN_SET_POWER后,例程函數(shù)可以設置系統(tǒng)和設備電源狀態(tài)。在DDK中SystemPowerState和DevicePowerState為系統(tǒng)和設備電源狀態(tài)。在PNP中是如何區(qū)分那種類型的電源狀態(tài)呢?DDK中I/O參數(shù)Parameters.Power.Type標識電源狀態(tài)類型。如果Parameters.Power.Type為SystemPowerState則重新設置的是系統(tǒng)電源狀態(tài),反之亦然。
IRP_MN_QUERY_POWER請求發(fā)生在IRP_MN_SET_POWER之前,主要用于確定是否具備修改電源狀態(tài)的能力。
當PNP管理器發(fā)出IRP_MN_POWER_SEQUENCE請求,主要用于指出設備是否已經(jīng)進入制定設電源狀態(tài)。
IRP_MN_POWER_SEQUENCE請求制定三種電源狀態(tài),SequenceD1、SequenceD2、SequenceD2。該請求后電源狀態(tài)保存在Parameters.PowerSequence當中。
當PNP管理器發(fā)出IRP_MN_WAIT_WAKE請求,指定設備是否具有硬件喚醒特征,這允許它們在外部事件發(fā)生時可以喚醒沉睡中的系統(tǒng)。當系統(tǒng)產(chǎn)生喚醒請求時,會調(diào)用PoRequestPowerIrp函數(shù)發(fā)出IRP_MN_WAIT_WAKE請求,其實其他IRP_MN_SET_POWER、IRP_MN_QUERY_POWER等請求也都是由該函數(shù)發(fā)起,但IRP_MN_POWER_SEQUENCE除外,請求后由USB總線處理。PoRequestPowerIrp函數(shù)描述如下;
- NTKERNELAPI
- NTSTATUS
- PoRequestPowerIrp (
- IN PDEVICE_OBJECT DeviceObject,
- IN UCHAR MinorFunction,
- IN POWER_STATE PowerState,
- IN PREQUEST_POWER_COMPLETE CompletionFunction,
- IN PVOID Context,
- OUT PIRP *Irp OPTIONAL
- );
? MinorFunction 于指定為IRP_MN_QUERY_POWER,IRP_MN_SET_POWER和IRP_MN_WAIT_WAKE之一。
? PowerState域特地為IRP_MN_SET_POWER和IRP_MN_QUERY_POWER準備,指出需要請求的電源狀態(tài)。
? CompletionFunction域為完成用例函數(shù)。
? Context域為需要向用例函數(shù)傳遞的參數(shù)。
設備對象中的兩個標志位,控制電源管理的各個方面。當你在AddDevice函數(shù)中調(diào)用了IoCreateDevice例程之后,這兩個位都將被置0,你可以根據(jù)具體環(huán)境設置這兩個位。
表3-3-3. DEVICE_OBJECT中的電源管理標志
eg:
deviceObject->Flags |= DO_POWER_PAGABLE;
(1)如果IRP_MJ_POWER請求的派遣函數(shù)必須運行在PASSIVE_LEVEL級別,你需要設置DO_POWER_PAGABLE標志。該標志的名稱含有相關的含義,因為只有在PASSIVE_LEVEL級上才允許分頁操作。如果你把該標志設置為0,則電源管理器可以在DISPATCH_LEVEL級上向你發(fā)送電源管理請求。實際上,在當前發(fā)行的Windows 2000中總應該這樣做。
(2)如果你的設備在上電時需要大電流,設置DO_POWER_INRUSH標志,這可以使多個有這樣要求的設備不同時上電。另外,電源管理器會在DISPATCH_LEVEL級上向inrush設備發(fā)送電源管理請求,這意味著你不能同時設置DO_POWER_PAGABLE標志。
如果設備的ASL描述中這樣指定,那么系統(tǒng)的ACPI過濾器驅動程序將在PDO中自動設置INRUSH標志,這樣系統(tǒng)會正確地順序化有INRUSH標志設備的電源供應,因此你并不需要在自己的設備對象中設置這個標志。但是,如果系統(tǒng)不能自動確定你的設備是否需要inrush對待,你需要自己設置這個標志。
設備的所有設備對象中的PAGABLE和INRUSH標志設置都要一致。如果PDO設置了PAGABLE標志,則其它設備對象也應該設置PAGABLE標志。否則將發(fā)生代碼為DRIVER_POWER_STATE_FAILURE的bug check(一個PAGABLE設備在一個非PAGABLE設備之上是合法的,但相反則不行)。
如果設備對象設置了INRUSH標志,那么它自己和任何它下面的低級設備對象都不應該設置PAGABLE標志,否則將發(fā)生代碼為INTERNAL_POWER_ERROR的bug check。
如果你正寫一個磁盤驅動程序,不要忘了為響應設備分頁文件的PnP通知,你可以隨時在分頁和非分頁狀態(tài)間改變。
設備電源管理代碼編寫相當復雜,如果處理不當將會帶來很嚴重的錯誤。在DDK環(huán)境下,支持NT和WDM兩種驅動模式,電源管理代碼需要自己編寫進行處理。在微軟新推出的WDK環(huán)境下,新推出的WDF驅動模式很好的解決了這個問題。微軟認為電源管理應該模塊化,處理方式基本一致,而且技術不過關的工程師很難將其處理完美,因為微軟提供了WDK選擇支持電源管理的功能,幫助工程師完成電源管理。但是、我們的USB 驅動采用的是WDM模式。
https://blog.csdn.net/chenyujing1234/article/details/7846845