Selektives Anhalten in USB KMDF-Funktionstreibern
In diesem Artikel wird beschrieben, wie KMDF-Funktionstreiber das selektive Anhalten von USB unterstützen.
Wenn der USB-Treiber Features oder Ressourcen erfordert, die im Benutzermodus nicht verfügbar sind, sollten Sie einen KMDF-Funktionstreiber bereitstellen. KMDF-Treiber implementieren selektives Anhalten, indem sie relevante Werte in einer KMDF-Initialisierungsstruktur festlegen und dann die entsprechenden Rückruffunktionen bereitstellen. KMDF verarbeitet die Details der Kommunikation mit niedrigeren Treibern, um das Gerät an- und fortzusetzen.
Richtlinien für selektives Anhalten in KMDF-Treibern
KMDF-Treiber, die selektives Anhalten unterstützen, müssen die folgenden Richtlinien befolgen:
- Ein KMDF-Funktionstreiber muss das PPO für den Gerätestapel sein. Standardmäßig sind KMDF-Funktionstreiber das PPO.
- Ein KMDF-Funktionstreiber, der selektives Anhalten unterstützt, kann Warteschlangen verwenden, die energieverwaltet sind, oder Warteschlangen, die nicht energieverwaltet sind. Standardmäßig werden Warteschlangenobjekte für PPOs energieverwaltet.
Energierichtlinienbesitz und KMDF-USB-Treiber
Standardmäßig ist der KMDF-Funktionstreiber für ein USB-Gerät das PPO für den Gerätestapel. KMDF verwaltet das selektive Anhalten und Fortsetzen im Namen dieses Treibers.
E/A-Warteschlangenkonfiguration in KMDF-Treibern
Ein KMDF-Funktionstreiber, der selektives Anhalten unterstützt, kann Warteschlangen verwenden, die energieverwaltet sind, oder Warteschlangen, die nicht energieverwaltet sind. In der Regel konfiguriert ein Treiber eine Warteschlange, die nicht energiegemanagt ist, um eingehende E/A-Steuerungsanforderungen von Geräten zu empfangen, und konfiguriert eine oder mehrere energieverwaltete Warteschlangen zum Empfangen von Lese-, Schreib- und anderen stromabhängigen Anforderungen. Wenn eine Anforderung bei einer energieverwalteten Warteschlange eingeht, stellt KMDF sicher, dass sich das Gerät in D0 befindet, bevor es die Anforderung an den Treiber sendet.
Wenn Sie einen KMDF-Filtertreiber schreiben, der sich über dem PPO im Gerätestapel befindet, dürfen Sie keine stromverwalteten Warteschlangen verwenden. Der Grund ist der gleiche wie bei UMDF-Treibern. Das Framework stellt keine Anforderungen von stromverwalteten Warteschlangen bereit, während das Gerät angehalten wird, sodass die Verwendung solcher Warteschlangen den Gerätestapel zum Stillstand führen kann.
Mechanismus zum selektiven Anhalten für KMDF-Funktionstreiber
KMDF übernimmt die meisten Aufgaben, die zur Unterstützung des selektiven Anhaltens von USB erforderlich sind. Es verfolgt die E/A-Aktivitäten, verwaltet den Leerlauftimer und sendet die E/A-Steuerungsanforderungen des Geräts, die dazu führen, dass der übergeordnete Treiber (Usbhub.sys oder Usbccgp.sys) das Gerät angehalten und fortgesetzt wird.
Wenn ein KMDF-Funktionstreiber selektives Anhalten unterstützt, verfolgt KMDF die E/A-Aktivität in allen energieverwalteten Warteschlangen nach, die jedes Geräteobjekt besitzt. Das Framework startet einen Leerlauftimer, wenn die E/A-Anzahl 0 erreicht. Der Standardtimeoutwert beträgt 5 Sekunden.
Wenn eine E/A-Anforderung bei einer energieverwalteten Warteschlange eingeht, die zum Geräteobjekt gehört, bevor der Leerlaufzeitpunkt abläuft, bricht das Framework den Leerlauftimer ab und hält das Gerät nicht an.
Wenn der Leerlaufzeitgeber abläuft, gibt KMDF die Anforderungen aus, die erforderlich sind, um das USB-Gerät in den angehaltenen Zustand zu versetzen. Wenn ein Funktionstreiber einen fortlaufenden Reader auf einem USB-Endpunkt verwendet, zählt die wiederholte Abfrage des Lesers nicht als Aktivität für den KMDF-Leerlauftimer. In der Rückruffunktion EvtDeviceD0Exit muss der USB-Treiber jedoch den fortlaufenden Reader und alle anderen E/A-Ziele manuell beenden, die von Warteschlangen gespeist werden, die nicht mit Strom versorgt werden, um sicherzustellen, dass der Treiber keine E/A-Anforderungen sendet, während sich das Gerät nicht im Betriebszustand befindet. Um die Ziele zu beenden, ruft der Treiber WdfIoTargetStop auf und gibt WdfIoTargetWaitForSentIoToComplete als Zielaktion an. Als Reaktion beendet das Framework das E/A-Ziel erst, nachdem alle E/A-Anforderungen, die sich in der E/A-Warteschlange des Ziels befinden, abgeschlossen wurden und alle zugehörigen E/A-Vervollständigungsrückrufe ausgeführt wurden.
Standardmäßig übergibt KMDF das Gerät aus D0 in den Gerätestromzustand, den der Treiber in den Leerlaufeinstellungen angegeben hat. Im Rahmen des Übergangs ruft KMDF die Stromrückruffunktionen des Treibers auf die gleiche Weise auf wie bei jeder anderen Powerdownsequenz.
Nachdem das Gerät angehalten wurde, setzt das Framework das Gerät automatisch fort, wenn eines der folgenden Ereignisse auftritt:
- Eine E/A-Anforderung wird für jede der energieverwalteten Warteschlangen des Treibers eingetroffen.
- Der Benutzer deaktiviert das selektive Anhalten von USB mithilfe von Geräte-Manager.
- Der Treiber ruft WdfDeviceStopIdle auf, wie unter Verhindern der Aussetzung von USB-Geräten beschrieben.
Um das Gerät fortzusetzen, sendet KMDF eine Einschaltanforderung auf den Gerätestapel und ruft dann die Rückruffunktionen des Treibers auf die gleiche Weise auf wie bei jeder anderen Einschaltsequenz.
Ausführliche Informationen zu den Rückrufen, die an den Power-Down- und Power-Up-Sequenzen beteiligt sind, finden Sie im Whitepaper Plug & Play und Energieverwaltung in WDF-Treibern.
Unterstützung des selektiven Anhaltens von USB in einem KMDF-Funktionstreiber
So implementieren Sie das selektive Anhalten von USB in einem KMDF-Funktionstreiber:
- Initialisieren Sie Energierichtlinieneinstellungen, die sich auf den Leerlauf beziehen, einschließlich Leerlauftimeouts.
- Fügen Sie optional Logik ein, um vorübergehend das Anhalten oder Fortsetzen des Betriebs zu verhindern, wenn der Treiber feststellt, dass das Gerät aufgrund eines geöffneten Handles oder eines anderen Grundes, der nicht mit den E/A-Warteschlangen des Geräts zusammenhängt, nicht angehalten werden soll.
- Geben Sie in einem USB-Treiber für ein Human Interface Device (HID) im INF an, dass selektives Anhalten unterstützt wird.
Initialisieren von Power Policy-Einstellungen in einem KMDF-Funktionstreiber
Um die Unterstützung für das selektive Anhalten von USB zu konfigurieren, verwendet ein KMDF-Treiber die WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS-Struktur . Der Treiber muss zuerst die Struktur initialisieren und kann dann Felder festlegen, die Details zu den Funktionen des Treibers und seines Geräts enthalten. In der Regel füllt der Treiber diese Struktur in seiner Funktion EvtDriverDeviceAdd oder EvtDevicePrepareHardware aus.
So initialisieren Sie die WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS-Struktur
Nachdem der Treiber das Geräteobjekt erstellt hat, verwendet der Treiber die WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT-Funktion , um die Struktur zu initialisieren. Diese Funktion akzeptiert zwei Argumente:
- Ein Zeiger auf die zu initialisierende WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS Struktur.
- Ein Enumerationswert, der die Unterstützung für selektives Anhalten angibt. Der Treiber sollte IdleUsbSelectiveSuspend angeben.
Wenn der Treiber IdleUsbSelectiveSuspend angibt, initialisiert die Funktion die Member der Struktur wie folgt:
- IdleTimeout ist auf IdleTimeoutDefaultValue (derzeit 5000 Millisekunden oder 5 Sekunden) festgelegt.
- UserControlOfIdleSettings ist auf IdleAllowUserControl festgelegt.
- Enabled ist auf WdfUseDefault festgelegt, was angibt, dass selektives Anhalten aktiviert ist, aber ein Benutzer kann sie deaktivieren, wenn das UserControlOfIdleSettings-Element dies zulässt.
- DxState ist auf PowerDeviceMaximum festgelegt, das die gemeldeten Energiefunktionen für das Gerät verwendet, um den Zustand zu bestimmen, in den das Leerlaufgerät übergehen soll.
So konfigurieren Sie das selektive Anhalten von USB
Nachdem der Treiber die WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS-Struktur initialisiert hat, kann der Treiber andere Felder in der Struktur festlegen und dann WdfDeviceAssignS0IdleSettings aufrufen, um diese Einstellungen an das Framework zu übergeben. Die folgenden Felder gelten für USB-Funktionstreiber:
IdleTimeout: Das Intervall in Millisekunden, das ohne Empfang einer E/A-Anforderung verstreichen muss, bevor das Framework den Geräte-Leerlauf berücksichtigt. Der Treiber kann einen ULONG-Wert angeben oder den Standardwert akzeptieren.
UserControlOfIdleSettings– Gibt an, ob der Benutzer die Einstellungen im Leerlauf des Geräts ändern kann. Mögliche Werte sind IdleDoNotAllowUserControl und IdleAllowUserControl.
DxState: Der Geräteleistungszustand, für den das Framework das Gerät ansetzt. Mögliche Werte sind PowerDeviceD1, PowerDeviceD2 und PowerDeviceD3.
USB-Treiber sollten die anfängliche Einstellung dieses Werts nicht ändern. Die WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT-Funktion legt diesen Wert auf PowerDeviceMaximum fest, wodurch sichergestellt wird, dass das Framework den richtigen Wert basierend auf den Gerätefunktionen wählt.
Der folgende Codeausschnitt stammt aus der Datei Device.c des Osrusbfx2-Beispieltreibers:
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;
}
Im Beispiel ruft der Treiber WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT auf und gibt IdleUsbSelectiveSuspend an. Der Treiber legt IdleTimeout auf 10.000 Millisekunden (10 Sekunden) fest und akzeptiert die Frameworkstandardeinstellungen für DxState und UserControlOfIdleSettings. Daher wechselt das Framework das Gerät im Leerlauf in den D3-Zustand und erstellt eine Geräte-Manager Eigenschaftenseite, die Benutzern mit Administratorrechten das Aktivieren oder Deaktivieren der Unterstützung im Leerlauf des Geräts ermöglicht. Der Treiber ruft dann WdfDeviceAssignS0IdleSettings auf, um die Unterstützung im Leerlauf zu aktivieren und diese Einstellungen beim Framework zu registrieren.
Ein Treiber kann WdfDeviceAssignS0IdleSettings jederzeit aufrufen, nachdem er das Geräteobjekt erstellt hat. Obwohl die meisten Treiber diese Methode zunächst über den EvtDriverDeviceAdd-Rückruf aufrufen, ist dies möglicherweise nicht immer möglich oder sogar wünschenswert. Wenn ein Treiber mehrere Geräte oder Geräteversionen unterstützt, kennt der Treiber möglicherweise nicht alle Gerätefunktionen, bis er die Hardware abfragt. Solche Treiber können den Aufruf von WdfDeviceAssignS0IdleSettings bis zum Rückruf von EvtDevicePrepareHardware verschieben.
Nach dem anfänglichen Aufruf von WdfDeviceAssignS0IdleSettings kann der Treiber jederzeit den Wert des Leerlauftimeouts und den Gerätestatus, in dem das Gerät im Leerlauf ist, ändern. Um eine oder mehrere Einstellungen zu ändern, initialisiert der Treiber einfach eine weitere WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS-Struktur , wie zuvor beschrieben, und ruft WdfDeviceAssignS0IdleSettings erneut auf.
Verhindern der Usb-Geräteaufhängung
Manchmal sollte ein USB-Gerät nicht heruntergefahren werden, auch wenn innerhalb des Timeoutzeitraums keine E/A-Anforderungen vorhanden sind – in der Regel, wenn ein Griff für das Gerät geöffnet ist oder das Gerät aufgeladen wird. Ein USB-Treiber kann verhindern, dass das Framework in solchen Situationen ein gerät im Leerlauf ansetzt, indem er WdfDeviceStopIdle aufruft und WdfDeviceResumeIdle aufruft, wenn es wieder akzeptabel ist, dass das Gerät angehalten wird.
WdfDeviceStopIdle beendet den Leerlauftimer. Wenn die Leerlaufzeit nicht abgelaufen ist und das Gerät noch nicht angehalten wurde, bricht das Framework den Leerlauftimer ab und hält das Gerät nicht an. Wenn das Gerät bereits angehalten wurde, versetzt das Framework das Gerät in den Arbeitszustand zurück. WdfDeviceStopIdlehindert das Framework nicht daran, das Gerät anzuhalten, wenn das System in einen Sx-Ruhezustand wechselt. Der einzige Effekt besteht darin, das Anhalten des Geräts zu verhindern, während sich das System im Arbeitszustand S0 befindet. WdfDeviceResumeIdle startet den Leerlauftimer neu. Diese beiden Methoden verwalten eine Verweisanzahl auf dem Gerät. Wenn der Treiber WdfDeviceStopIdle also mehrmals aufruft, hält das Framework das Gerät erst an, wenn der Treiber WdfDeviceResumeIdle mit derselben Anzahl aufgerufen hat. Ein Treiber darf WdfDeviceResumeIdlenicht aufrufen, ohne zuerst WdfDeviceStopIdle aufzurufen.
Einschließen eines Registrierungsschlüssels (nur HID-Treiber)
KMDF-Oberfiltertreiber für USB HID-Geräte müssen in der INF angeben, dass sie selektives Anhalten unterstützen, damit der von Microsoft bereitgestellte HIDClass.sys-Porttreiber selektives Anhalten für den HID-Stapel aktivieren kann. Die INF sollte eine AddReg-Anweisung enthalten, die den SelectiveSuspendEnabled-Schlüssel hinzufügt und seinen Wert auf 1 festlegen, wie die folgende Zeichenfolge zeigt:
HKR,,"SelectiveSuspendEnabled",0x00000001,0x1
Ein Beispiel finden Sie unter Hidusbfx2.inx im WDK unter %WinDDK%\BuildNumber\Src\Hid\ Hidusbfx2\sys.
Remotereaktivierungsunterstützung für KMDF-Treiber
Wie beim selektiven Anhalten enthält KMDF Unterstützung für das Reaktivieren, sodass ein USB-Gerät ein Aktivierungssignal auslösen kann, während sich das Gerät im Leerlauf befindet und sich das System im Arbeitszustand (S0) oder im Ruhezustand (S1–S4) befindet. In KMDF-Begriffen werden diese beiden Features als "Aus S0 aufwachen" bzw. "aus Sx" bezeichnet.
Bei USB-Geräten gibt die Aktivierung lediglich an, dass das Gerät selbst den Übergang von einem Zustand mit geringerer Leistung in den Betriebszustand initiieren kann. Daher sind in USB-Begriffen Wake from S0 und Wake from Sx identisch und werden als "Remote Wake" bezeichnet.
KMDF-USB-Funktionstreiber benötigen keinen Code, um das Reaktivieren von S0 zu unterstützen, da KMDF diese Funktion als Teil des mechanismus für selektives Anhalten bereitstellt. Um jedoch die Remoteaktivierung zu unterstützen, wenn sich das System in Sx befindet, muss ein Funktionstreiber:
- Überprüfen Sie, ob das Gerät die Remotereaktivierung unterstützt, indem Sie WdfUsbTargetDeviceRetrieveInformation aufrufen.
- Aktivieren Sie die Remotereaktivierung, indem Sie die Aktivierungseinstellungen initialisieren und WdfDeviceAssignSxWakeSettings aufrufen.
KMDF-Treiber konfigurieren die Aktivierungsunterstützung in der Regel gleichzeitig mit der Konfiguration der Unterstützung für das selektive Anhalten von USB in der EvtDriverDeviceAdd - oder EvtDevicePrepareHardware-Funktion .
Überprüfen der Gerätefunktionen
Bevor ein KMDF-USB-Funktionstreiber seine Energierichtlinieneinstellungen für Leerlauf und Reaktivierung initialisiert, sollte überprüft werden, ob das Gerät die Remotereaktivierung unterstützt. Um Informationen zu Gerätehardwarefeatures abzurufen, initialisiert der Treiber eine WDF_USB_DEVICE_INFORMATION-Struktur und ruft WdfUsbTargetDeviceRetrieveInformation auf, in der Regel in seinem EvtDriverDeviceAdd oder EvtDevicePrepareHardware-Rückruf .
Beim Aufruf von WdfUsbTargetDeviceRetrieveInformation übergibt der Treiber ein Handle an das Geräteobjekt und einen Zeiger auf die initialisierte WDF_USB_DEVICE_INFORMATION-Struktur . Nach erfolgreicher Rückgabe von der Funktion enthält das Feld Merkmale der Struktur Flags, die angeben, ob das Gerät selbstbetrieben ist, mit hoher Geschwindigkeit arbeiten kann und remote wake unterstützt.
Das folgende Beispiel aus dem Osrusbfx2 KMDF-Beispiel zeigt, wie Diese Methode aufgerufen wird, um zu bestimmen, ob ein Gerät die Remotereaktivierung unterstützt. Nachdem diese Codezeilen ausgeführt wurden, enthält die Variable waitWakeEnable TRUE, wenn das Gerät remote Wake unterstützt, und FALSE, wenn dies nicht der Fall ist:
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;
Aktivieren der Remotereaktivierung
In der USB-Terminologie ist ein USB-Gerät für die Remotereaktivierung aktiviert, wenn das DEVICE_REMOTE_WAKEUP-Feature festgelegt ist. Gemäß der USB-Spezifikation muss die Hostsoftware die Remotereaktivierungsfunktion auf einem Gerät "nur kurz vor" festlegen, um das Gerät in den Ruhezustand zu versetzen. Der KMDF-Funktionstreiber ist nur erforderlich, um die Aktivierungseinstellungen zu initialisieren. KMDF und die von Microsoft bereitgestellten USB-Bustreiber geben die E/A-Anforderungen aus und behandeln die Hardwarebearbeitung, die zum Aktivieren der Remotereaktivierung erforderlich ist.
So initialisieren Sie Die Aktivierungseinstellungen
- Rufen Sie WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS_INIT auf, um eine WDF_DEVICE_POWER_POLICY_WAKE_SETTINGS-Struktur zu initialisieren. Diese Funktion legt den Enabled-Member der Struktur auf WdfUseDefault fest, legt das DxState-Element auf PowerDeviceMaximum fest und legt den UserControlOfWakeSettings-Member auf WakeAllowUserControl fest.
- Rufen Sie WdfDeviceAssignSxWakeSettings mit der initialisierten Struktur auf. Daher wird das Gerät aktiviert, um aus dem D3-Zustand zu reaktivieren, und der Benutzer kann das Aktivierungssignal auf der Geräteeigenschaftenseite in Geräte-Manager aktivieren oder deaktivieren.
Der folgende Codeausschnitt aus dem Osrusbfx2-Beispiel zeigt, wie Die Aktivierungseinstellungen mit ihren Standardwerten initialisiert werden:
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;
}
Bei USB-Geräten, die selektives Anhalten unterstützen, bereitet der zugrunde liegende Bustreiber die Gerätehardware auf das Reaktivieren vor. Usb-Funktionstreiber erfordern daher selten einen EvtDeviceArmWakeFromS0-Rückruf . Das Framework sendet eine selektive Anhaltenanforderung an den USB-Bustreiber, wenn das Leerlaufzeitlimit abläuft.
Aus dem gleichen Grund erfordern USB-Funktionstreiber selten einen EvtDeviceWakeFromS0Triggered - oder EvtDeviceWakeFromSxTriggered-Rückruf . Stattdessen verarbeiten das Framework und der zugrunde liegende Bustreiber alle Anforderungen für die Rückkehr des Geräts in den Betriebszustand.