USB KMDF 函式驅動程式中的選擇性暫停
本文說明 KMDF 函式驅動程式如何支援 USB 選擇性暫停。
如果 USB 驅動程式需要使用者模式中無法使用的功能或資源,您應該提供 KMDF 函式驅動程式。 KMDF 驅動程式會在 KMDF 初始化結構中設定相關值,然後提供適當的回呼函式,以實作選擇性暫停。 KMDF 會處理與較低驅動程式通訊的詳細數據,以暫停和繼續裝置。
KMDF 驅動程式中選擇性暫停的指導方針
支援選擇性暫停的 KMDF 驅動程式必須遵循下列指導方針:
- KMDF 函式驅動程式必須是其裝置堆疊的 PPO。 根據預設,KMDF 函式驅動程式是 PPO。
- 支援選擇性暫停的 KMDF 函式驅動程式可以使用受電源管理的佇列或未受電源管理的佇列。 根據預設,PDO 的佇列物件是受電源管理的。
電源原則擁有權和 KMDF USB 驅動程式
根據預設,USB 裝置的 KMDF 函式驅動程式是裝置堆疊的 PPO。 KMDF 會代表此驅動程式管理選擇性暫停和繼續。
KMDF 驅動程式中的 I/O 佇列設定
支援選擇性暫停的 KMDF 函式驅動程式可以使用受電源管理的佇列或未受電源管理的佇列。 一般而言,驅動程式會設定未受電源管理的佇列來接收傳入裝置 I/O 控制要求,並設定一或多個受電源管理的佇列來接收讀取、寫入和其他電源相依要求。 當要求抵達電源管理的佇列時,KMDF 可確保裝置在向驅動程序呈現要求之前,位於 D0 中。
如果您要撰寫分層在裝置堆疊中 PPO 上方的 KMDF 篩選驅動程式,則不得使用受電源管理的佇列。 原因與 UMDF 驅動程式相同。 當裝置暫停時,架構不會顯示來自受電源管理佇列的要求,因此使用這類佇列可能會停止裝置堆疊。
KMDF 函式驅動程式的選擇性暫停機制
KMDF 會處理支援 USB 選擇性暫停所需的大部分工作。 它會持續追蹤 I/O 活動、管理閑置定時器,以及傳送造成父驅動程式 (Usbhub.sys 或 Usbccgp.sys) 裝置暫停和繼續裝置的裝置 I/O 控制要求。
如果 KMDF 函式驅動程式支援選擇性暫停,KMDF 會追蹤每個裝置對象擁有之所有電源受控佇列上的 I/O 活動。 每當 I/O 計數達到零時,架構就會啟動閑置定時器。 默認逾時值為5秒。
如果在閑置逾時期限到期之前,I/O 要求抵達屬於裝置對象的電源受控佇列,架構就會取消閑置定時器,且不會暫停裝置。
閑置定時器到期時,KMDF 會發出將USB裝置置於暫停狀態所需的要求。 如果函式驅動程式在 USB 端點上使用連續讀取器,讀取器的重複輪詢不會計入 KMDF 閑置定時器的活動。 不過,在 EvtDeviceD0Exit 回呼函式中,USB 驅動程式必須手動停止連續讀取器,以及由未受電源管理之佇列提供的任何其他 I/O 目標,以確保驅動程式不會在裝置處於工作狀態時傳送 I/O 要求。 若要停止目標,驅動程式會呼叫 WdfIoTargetStop ,並將 WdfIoTargetWaitForSentIoToComplete 指定為目標動作。 為了回應,架構只會在目標 I/O 佇列中的所有 I/O 要求都已完成且任何相關聯的 I/O 完成回呼都已執行之後,才會停止 I/O 目標。
根據預設,KMDF 會將裝置從 D0 轉換,並轉換成閑置設定中指定的驅動程式電源狀態。 在轉換過程中,KMDF 會呼叫驅動程式的電源回呼函式,就像任何其他關機序列一樣。
在裝置暫停之後,當發生下列任何事件時,架構會自動繼續裝置:
- I/O 要求會針對任何驅動程式的電源管理佇列抵達。
- 使用者會使用 裝置管理員 停用USB選擇性暫停。
- 驅動程式會呼叫 WdfDeviceStopIdle,如 防止 USB 裝置暫停中所述。
若要繼續裝置,KMDF 會將電源啟動要求向下傳送到裝置堆疊,然後以任何其他電源順序的相同方式叫用驅動程式的回呼函式。
如需有關關機和電源啟動序列相關回呼的詳細資訊,請參閱 WDF 驅動程式中的 隨插即用 和電源管理白皮書。
支援 KMDF 函式驅動程式中的 USB 選擇性暫停
若要在 KMDF 函式驅動程式中實作 USB 選擇性暫停:
- 初始化與閑置相關的電源原則設定,包括閑置逾時。
- 選擇性地包含邏輯,以在驅動程序判斷裝置不應因為開啟句柄或其他與裝置 I/O 佇列無關的原因而暫停或繼續作業時,暫時防止暫停或繼續作業。
- 在人類介面裝置的 USB 驅動程式中, (HID) ,在 INF 中指出它支援選擇性暫停。
初始化 KMDF 函式驅動程式中的電源原則設定
若要設定 USB 選擇性暫停的支援,KMDF 驅動程式會使用 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 結構。 驅動程式必須先初始化 結構,然後可以設定欄位,以提供驅動程式及其裝置功能的詳細數據。 驅動程式通常會在其 EvtDriverDeviceAdd 或 EvtDevicePrepareHardware 函式中填入此結構。
初始化WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS結構
驅動程式建立裝置物件之後,驅動程式會使用 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT 函式來初始化 結構。 此函式採用兩個自變數:
- 要初始化之WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS結構的指標。
- 列舉值,表示支援選擇性暫停。 驅動程式應該指定 IdleUsbSelectiveSuspend。
如果驅動程式指定 IdleUsbSelectiveSuspend,此函式會初始化結構的成員,如下所示:
- IdleTimeout 設定為 IdleTimeoutDefaultValue (目前為 5000 毫秒或 5 秒) 。
- UserControlOfIdleSettings 設定為 IdleAllowUserControl 。
- Enabled 設定為 WdfUseDefault,這表示已啟用選擇性暫停,但如果 UserControlOfIdleSettings 成員允許,則使用者可以停用它。
- DxState 會設定為 PowerDeviceMaximum,這會使用裝置的報告電源功能來判斷要轉換閒置裝置的狀態。
若要設定 USB 選擇性暫停
在驅動程式初始化 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 結構之後,驅動程式可以設定結構中的其他欄位,然後呼叫 WdfDeviceAssignS0IdleSettings 將這些設定傳遞至架構。 下列欄位適用於 USB 函式驅動程式:
IdleTimeout —在架構將裝置視為閑置之前,必須經過的間隔,以毫秒為單位。不需要收到 I/O 要求。 驅動程式可以指定 ULONG 值,也可以接受預設值。
UserControlOfIdleSettings — 使用者是否可以修改裝置的閑置設定。 可能的值為 IdleDoNotAllowUserControl 和 IdleAllowUserControl。
DxState— 架構暫停裝置的裝置電源狀態。 可能的值為 PowerDeviceD1、PowerDeviceD2 和 PowerDeviceD3。
USB 驅動程式不應該變更此值的初始設定。 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT函式會將此值設定為 PowerDeviceMaximum,以確保架構會根據裝置功能選擇正確的值。
下列代碼段來自 Osrusbfx2 範例驅動程式的 Device.c 檔案:
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS idleSettings;
NTSTATUS status = STATUS_SUCCESS;
//
// Initialize the idle policy structure.
//
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&idleSettings,
IdleUsbSelectiveSuspend);
idleSettings.IdleTimeout = 10000; // 10 sec
status = WdfDeviceAssignS0IdleSettings(Device, &idleSettings);
if ( !NT_SUCCESS(status)) {
TraceEvents(TRACE_LEVEL_ERROR, DBG_PNP,
"WdfDeviceSetPowerPolicyS0IdlePolicy failed %x\n",
status);
return status;
}
在此範例中,驅動程式會 呼叫 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT,並指定 IdleUsbSelectiveSuspend。 驅動程式會將 IdleTimeout 設定為10,000毫秒, (10秒) ,並接受 DxState 和 UserControlOfIdleSettings 的架構預設值。 因此,架構會在裝置閑置時將裝置轉換為 D3 狀態,並建立 裝置管理員 屬性頁,讓使用者具有系統管理員許可權來啟用或停用裝置閑置支援。 驅動程式接著會呼叫 WdfDeviceAssignS0IdleSettings 來啟用閑置支援,並在架構中註冊這些設定。
驅動程式可以在建立裝置對象之後,隨時呼叫 WdfDeviceAssignS0IdleSettings 。 雖然大部分的驅動程式一開始都是從 EvtDriverDeviceAdd 回 呼呼叫此方法,但這不一定是可能或甚至想要的。 如果驅動程式支援多個裝置或裝置版本,在查詢硬體之前,驅動程式可能不知道所有裝置功能。 這類驅動程式可以延後呼叫 WdfDeviceAssignS0IdleSettings ,直到 EvtDevicePrepareHardware 回呼為止。
在初始呼叫 WdfDeviceAssignS0IdleSettings 之後,驅動程式可以隨時變更閑置逾時值,以及裝置閑置所在的裝置狀態。 若要變更一或多個設定,驅動程式只會如先前所述,初始化另一個 WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS 結構,並再次呼叫 WdfDeviceAssignS0IdleSettings 。
防止USB裝置暫停
有時候,即使沒有 I/O 要求出現在逾時期間內,USB 裝置也不應該關閉電源,通常是當裝置開啟句柄或裝置充電時。 USB 驅動程式可以藉由呼叫 WdfDeviceStopIdle 並再次允許裝置暫停時呼叫 WdfDeviceResumeIdle ,以防止架構暫停閑置裝置。
WdfDeviceStopIdle 會停止閑置定時器。 如果 IdleTimeout 期間尚未過期,且裝置尚未暫停,架構會取消閑置定時器,且不會暫停裝置。 如果裝置已暫停,架構會將裝置傳回工作狀態。 WdfDeviceStopIdle不會防止架構在系統變更為 Sx 睡眠狀態時暫停裝置。 其唯一的效果是在系統處於 S0 運作狀態時防止裝置暫停。 WdfDeviceResumeIdle 會重新啟動閑置定時器。 這兩種方法會管理裝置上的參考計數,因此,如果驅動程式呼叫 WdfDeviceStopIdle 多次,架構就不會暫停裝置,直到驅動程式呼叫 WdfDeviceResumeIdle 相同次數為止。 驅動程式不需先呼叫 WdfDeviceStopIdle,就不能呼叫 WdfDeviceResumeIdle。
僅) 包含登錄機碼 (HID 驅動程式
USB HID 裝置的 KMDF 上層篩選驅動程式必須在 INF 中指出它們支援選擇性暫停,讓 Microsoft 提供的 HIDClass.sys 埠驅動程式可以啟用 HID 堆疊的選擇性暫停。 INF 應該包含 AddReg 指示詞,以新增 SelectiveSuspendEnabled 機碼並將其值設定為 1,如下列字串所示:
HKR,,"SelectiveSuspendEnabled",0x00000001,0x1
如需範例,請參閱 %WinDDK%\BuildNumber\Src\Hid\ Hidusbfx2\sys 的 WDK 中的 Hidusbfx2.inx。
KMDF 驅動程式的遠端喚醒支援
如同選擇性暫停,KMDF 包含喚醒的支援,讓 USB 裝置可以在裝置閒置時觸發喚醒訊號,而系統處於運作狀態 (S0) 或處於睡眠狀態, (S1-S4) 。 在 KMDF 詞彙中,這兩項功能分別稱為「從 S0 喚醒」和「從 Sx 喚醒」。
對於USB裝置,喚醒只會指出裝置本身可以起始從較低電源狀態到工作狀態的轉換。 因此,在USB詞彙中,從S0喚醒和從Sx喚醒相同,稱為「遠端喚醒」。
KMDF USB 函式驅動程式不需要任何程式代碼支援從 S0 喚醒,因為 KMDF 會提供這項功能作為選擇性暫停機制的一部分。 不過,若要在系統位於 Sx 時支援遠端喚醒,函式驅動程式必須:
- 呼叫 WdfUsbTargetDeviceRetrieveInformation 來檢查裝置是否支援遠端喚醒。
- 藉由初始化喚醒設定並呼叫 WdfDeviceAssignSxWakeSettings 來啟用遠端喚醒。
KMDF 驅動程式通常會在 EvtDriverDeviceAdd 或 EvtDevicePrepareHardware 函式中設定 USB 選擇性暫停支援時設定喚醒支援。
檢查裝置功能
在 KMDF USB 函式驅動程式初始化其閒置和喚醒的電源原則設定之前,它應該確認裝置支援遠端喚醒。 若要取得裝置硬體功能的相關信息,驅動程式會初始化 WDF_USB_DEVICE_INFORMATION 結構,並呼叫 WdfUsbTargetDeviceRetrieveInformation,通常是在其 EvtDriverDeviceAdd 或 EvtDevicePrepareHardware 回呼中。
在 對 WdfUsbTargetDeviceRetrieveInformation 的呼叫中,驅動程式會將句柄傳遞給裝置物件,以及初始化 WDF_USB_DEVICE_INFORMATION 結構的指標。 從函式成功傳回時,結構的 Traits 欄位會包含旗標,指出裝置是否為自我電源、可以高速運作,並支援遠端喚醒。
下列來自 Osrusbfx2 KMDF 範例的範例示範如何呼叫這個方法,以判斷裝置是否支援遠端喚醒。 執行這幾行程式代碼之後,如果裝置支援遠端喚醒,waitWakeEnable 變數就會包含 TRUE,如果裝置不支援則為 FALSE:
WDF_USB_DEVICE_INFORMATION deviceInfo;
// Retrieve USBD version information, port driver capabilities and device
// capabilities such as speed, power, etc.
//
WDF_USB_DEVICE_INFORMATION_INIT(&deviceInfo);
status = WdfUsbTargetDeviceRetrieveInformation(
pDeviceContext->UsbDevice,
&deviceInfo);
waitWakeEnable = deviceInfo.Traits & WDF_USB_DEVICE_TRAIT_REMOTE_WAKE_CAPABLE;
啟用遠端喚醒
在USB術語中,當設定USB裝置DEVICE_REMOTE_WAKEUP功能時,會啟用遠端喚醒。 根據USB規格,主機軟體必須在裝置上「只在之前」設定遠端喚醒功能,才能讓裝置進入睡眠狀態。 KMDF 函式驅動程式只需要初始化喚醒設定。 KMDF 和 Microsoft 提供的 USB 總線驅動程式會發出 I/O 要求,並處理啟用遠端喚醒所需的硬體操作。
初始化喚醒設定
- 呼叫 WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT 以初始化 WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS 結構。 此函式會將結構的 Enabled 成員設定為 WdfUseDefault、將 DxState 成員設定為 PowerDeviceMaximum,並將 UserControlOfWakeSettings 成員設定為 WakeAllowUserControl。
- 使用初始化的結構呼叫 WdfDeviceAssignSxWakeSettings 。 因此,裝置會啟用從 D3 狀態喚醒,而且用戶可以在 裝置管理員 中的裝置屬性頁啟用或停用喚醒訊號。
Osrusbfx2 範例中的下列代碼段示範如何將喚醒設定初始化為其預設值:
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS wakeSettings;
WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT(&wakeSettings);
status = WdfDeviceAssignSxWakeSettings(Device, &wakeSettings);
if (!NT_SUCCESS(status)) {
return status;
}
對於支援選擇性暫停的 USB 裝置,基礎總線驅動程式會準備要喚醒的裝置硬體。 因此,USB 函式驅動程式很少需要 EvtDeviceArmWakeFromS0 回呼。 當閑置逾時到期時,架構會將選擇性暫停要求傳送至USB總線驅動程式。
基於相同的原因,USB 函式驅動程式很少需要 EvtDeviceWakeFromS0Triggered 或 EvtDeviceWakeFromSxTriggered 回 呼。 相反地,架構和基礎總線驅動程式會處理將裝置傳回工作狀態的所有需求。