Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Hinweis
Dieser Artikel richtet sich an Gerätetreiberentwickler. Wenn Probleme mit einem USB-Gerät auftreten, lesen Sie "Beheben USB-C Probleme in Windows"
Die selektive USB-Anhaltefunktion ermöglicht es dem Hubtreiber, einen einzelnen Port zu deaktivieren, ohne den Betrieb der anderen Ports des Hubs zu beeinträchtigen. Diese Funktionalität ist bei tragbaren Computern nützlich, da sie den Akkustrom sparen kann. Viele Geräte, z. B. biometrische Scanner, erfordern nur zeitweise Energie. Durch das Anhalten solcher Geräte wird der Gesamtenergieverbrauch reduziert, wenn sie nicht verwendet werden. Wichtiger ist, dass jedes Gerät, das nicht selektiv angehalten wird, verhindern kann, dass der USB-Hostcontroller seinen Übertragungszeitplan deaktiviert, der sich im Systemspeicher befindet. Direkte Speicherzugriffsübertragungen vom Hostcontroller an den Planer können verhindern, dass die Prozessoren des Systems tiefer in den Ruhezustand gelangen, z. B. C3.
Das selektive Anhalten ist standardmäßig aktiviert. Microsoft empfiehlt dringend, das selektive Anhalten nicht zu deaktivieren .
Clienttreiber sollten nicht versuchen, festzustellen, ob das selektive Anhalten aktiviert ist, bevor Leerlaufanforderungen gesendet werden. Sie sollten Leerlaufanforderungen senden, wenn das Gerät im Leerlauf ist. Wenn die Leerlaufanforderung fehlschlägt, sollte der Clienttreiber den Leerlaufzeitgeber zurücksetzen und den Vorgang wiederholen.
Um ein USB-Gerät selektiv auszusetzen, gibt es zwei unterschiedliche Mechanismen: Leerlaufanfrage-IRPs (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) und Energieeinstellungs-IRPs (IRP_MN_SET_POWER). Der zu verwendende Mechanismus hängt vom Gerätetyp ab: zusammengesetzt oder nicht komposit.
Auswählen eines Mechanismus zur selektiven Aussetzung
Client-Treiber für eine Schnittstelle eines zusammengesetzten Geräts, die die Schnittstelle für die Remote-Weckfunktion mit einem Wait Wake IRP (IRP_MN_WAIT_WAKE) aktivieren, müssen den Leerlaufanforderungs-IRP-Mechanismus (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) verwenden, um ein Gerät selektiv in den Ruhezustand zu versetzen.
Informationen zum Remote-Wake-up finden Sie unter:
In diesem Abschnitt wird der selektive Anhaltemechanismus von Windows erläutert.
Senden eines USB-Idle-Anforderungs-IRP
Wenn ein Gerät in den Leerlaufzustand übergeht, informiert der Clienttreiber den Bustreiber durch das Senden einer Idle-Anforderungs-IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Nachdem der Bus-Treiber festgestellt hat, dass es sicher ist, das Gerät in einen niedrigen Leistungszustand zu versetzen, ruft er die Rückrufroutine auf, die der Clientgerätetreiber mit dem Idle-Anforderungs-IRP übergeben hat.
In der Rückrufroutine muss der Clienttreiber alle ausstehenden E/A-Vorgänge abbrechen und auf den Abschluss aller USB-E/A-IRPs warten. Anschließend kann eine IRP_MN_SET_POWER Anforderung zum Ändern des WdM-Gerätestromzustands in D2 gestellt werden. Die Rückrufroutine muss warten, bis die D2-Anforderung abgeschlossen ist, bevor sie zurückgegeben wird. Weitere Informationen zur Idle-Benachrichtigungsrückrufroutine finden Sie unter Implementieren einer IRP-Rückrufroutine für USB-Idle-Anforderungen.
Der Busfahrer schließt das Idle-Anforderungs-IRP nicht ab, nachdem die Rückrufroutine zur Leerlaufbenachrichtigung aufgerufen wurde. Stattdessen hält der Bustreiber den Leerlaufanforderungs-IRP aus, bis eine der folgenden Bedingungen zutrifft:
- Ein IRP_MN_SURPRISE_REMOVAL oder IRP_MN_REMOVE_DEVICE IRP wird empfangen. Wenn einer dieser IRPs erhalten wird, wird der Leerlaufanforderungs-IRP mit dem Status "Abgebrochen" beendet.
- Der Bustreiber erhält eine Anforderung, das Gerät in einen Betriebsleistungszustand (D0) zu versetzen. Nach Erhalt dieser Anfrage schließt der Busfahrer die ausstehende Leerlaufanforderung IRP mit STATUS_SUCCESS ab.
Die folgenden Einschränkungen gelten für die Verwendung von IRPs für Leerlaufanforderungen:
- Treiber müssen sich im Stromzustand des Geräts D0 befinden, wenn ein Leerlaufanforderungs-IRP gesendet wird.
- Treiber müssen nur eine Idle-Anforderungs-IRP pro Gerätestapel senden.
Der folgende WDM-Beispielcode veranschaulicht die Schritte, die ein Gerätetreiber zum Senden einer USB-Idle-Anforderungs-IRP ausführt. Fehlerüberprüfung wird im folgenden Codebeispiel nicht angegeben.
Zuordnen und Initialisieren des IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION IRP
irp = IoAllocateIrp (DeviceContext->TopOfStackDeviceObject->StackSize, FALSE); nextStack = IoGetNextIrpStackLocation (irp); nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL; nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION; nextStack->Parameters.DeviceIoControl.InputBufferLength = sizeof(struct _USB_IDLE_CALLBACK_INFO);
Zuordnen und Initialisieren der Informationsstruktur der Leerlaufanforderung (USB_IDLE_CALLBACK_INFO).
idleCallbackInfo = ExAllocatePool (NonPagedPool, sizeof(struct _USB_IDLE_CALLBACK_INFO)); idleCallbackInfo->IdleCallback = IdleNotificationCallback; // Put a pointer to the device extension in member IdleContext idleCallbackInfo->IdleContext = (PVOID) DeviceExtension; nextStack->Parameters.DeviceIoControl.Type3InputBuffer = idleCallbackInfo;
Legen Sie eine Abschlussroutine fest.
Der Clienttreiber muss eine Abschlussroutine dem Idle-Anforderungs-IRP zuordnen. Weitere Informationen zur Abschlussroutine für Leerlaufbenachrichtigungen und Beispielcode finden Sie unter Implementieren einer IRP-Abschlussroutine für USB-Idle-Anforderungen.
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);
Speichern Sie die Leerlaufanforderung in der Geräteerweiterung.
deviceExtension->PendingIdleIrp = irp;
Senden Sie die Idle-Anforderung an den übergeordneten Treiber.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
Abbrechen einer USB-Leerlaufanforderung
Unter bestimmten Umständen muss ein Gerätetreiber möglicherweise eine Idle-Anforderung IRP abbrechen, die an den Bustreiber übermittelt wurde. Diese Situation kann auftreten, wenn das Gerät entfernt wird, nach dem Leerlauf und Senden der Leerlaufanforderung aktiv wird, oder wenn das gesamte System in einen niedrigeren Systemstromzustand wechselt.
Der Clienttreiber bricht das IRP im Leerlauf ab, indem IoCancelIrp aufgerufen wird. Die folgende Tabelle beschreibt drei Szenarien zum Abbrechen eines IRP im Leerlauf und gibt die Aktion an, die der Treiber ausführen muss:
Szenario | Abbruchmechanismus für Leerlaufanforderungen |
---|---|
Der Clienttreiber bricht das Leerlauf-IRP ab, und die Rückrufroutine für die USB-Leerlaufbenachrichtigung wurde nicht aufgerufen. | Der USB-Treiberstapel schließt den Leerlauf-IRP ab. Da das Gerät den D0 nie verlassen hat, ändert der Treiber den Gerätezustand nicht. |
Der Clienttreiber bricht das IRP im Leerlauf ab, der USB-Treiberstapel ruft die USB-Idle-Benachrichtigungsrückrufroutine auf und wird noch nicht zurückgegeben. | Es ist möglich, dass die Rückrufroutine für usb-Leerlaufbenachrichtigungen aufgerufen wird, obwohl der Clienttreiber den Abbruch auf dem IRP aufgerufen hat. In diesem Fall muss die Rückrufroutine des Clienttreibers das Gerät trotzdem herunterschalten, indem das Gerät synchron an einen niedrigeren Leistungszustand gesendet wird. Wenn sich das Gerät im unteren Leistungszustand befindet, kann der Clienttreiber dann eine D0-Anforderung senden. Alternativ kann der Treiber warten, bis der USB-Treiberstapel das Leerlauf-IRP abgeschlossen hat und dann das D0-IRP senden. Wenn die Rückrufroutine das Gerät aufgrund unzureichendem Arbeitsspeicher nicht in einen Energiesparzustand versetzen kann, um einen Strom-IRP zuzuweisen, sollte er den Leerlauf-IRP abbrechen und sofort beenden. Der Leerlauf-IRP wird erst abgeschlossen, wenn die Rückrufroutine zurückkehrt. Daher sollte die Rückrufroutine nicht blockieren, bis das abgebrochene Leerlauf-IRP abgeschlossen ist. |
Das Gerät befindet sich bereits im Energiesparmodus. | Wenn sich das Gerät bereits im Energiesparmodus befindet, kann der Clienttreiber einen D0 IRP senden. Der USB-Treiberstapel schließt das Leerlauf-Anforderungs-IRP mit STATUS_SUCCESS ab. Alternativ kann der Treiber das IRP im Leerlauf abbrechen, warten, bis der USB-Treiberstapel das IRP im Leerlauf abgeschlossen hat, und dann ein D0 IRP senden. |
IRP-Abschlussroutine für USB-Idle-Anforderung
In vielen Fällen kann ein Bustreiber die IRP-Abschlussroutine eines Fahrers im Leerlauf aufrufen. Wenn diese Situation eintritt, muss ein Client-Treiber herausfinden, warum der Bus-Treiber das IRP abgeschlossen hat. Der zurückgegebene Statuscode kann diese Informationen bereitstellen. Wenn der Statuscode nicht STATUS_POWER_STATE_INVALID ist, sollte der Treiber sein Gerät in D0 platzieren, wenn das Gerät noch nicht in D0 enthalten ist. Wenn das Gerät noch im Leerlauf ist, kann der Treiber eine weitere IDRP-Anforderung senden.
Hinweis
Die IRP-Abschlussroutine für Leerlaufanforderungen sollte nicht blockieren, während sie auf den Abschluss einer D0-Energieanforderung wartet. Die Abschlussroutine kann im Kontext eines Strom-IRP vom Hubtreiber aufgerufen werden, und das Blockieren eines anderen Strom-IRPs in der Abschlussroutine kann zu einem Deadlock führen.
Die folgende Liste gibt an, wie eine Abschlussroutine für eine Leerlaufanforderung einige gängige Statuscodes interpretieren sollte:
Statuscode | BESCHREIBUNG |
---|---|
STATUS_ERFOLGREICH | Gibt an, dass das Gerät nicht mehr angehalten werden soll. Treiber sollten jedoch überprüfen, ob ihre Geräte eingeschaltet sind, und sie in D0 platzieren, wenn sie noch nicht in D0 sind. |
STATUS_STORNIERT | Der Bus-Treiber schließt die Leerlaufanforderung IRP mit Status_Cancelled unter den folgenden Umständen ab:
|
STATUS_UNGÜLTIGER_ENERGIEZUSTAND | Gibt an, dass der Gerätetreiber einen D3-Energiezustand für sein Gerät angefordert hat. Wenn diese Anforderung auftritt, schließt der Bustreiber alle ausstehenden IRPs im Leerlauf mit dem Status STATUS_POWER_STATE_INVALID ab. |
GERÄT_AUSGELASTET | Gibt an, dass der Bus-Treiber bereits eine Leerlaufanforderung IRP hat, die aussteht. Für ein bestimmtes Gerät kann jeweils nur ein Leerlauf-IRP ausstehen. Das Senden mehrerer Leerlaufanforderungs-IRPs ist ein Fehler auf Seiten des Energieverwaltungspolitikers. Der Treiberentwickler behebt den Fehler. |
Das folgende Codebeispiel zeigt eine Beispielimplementierung für die Leerlaufanforderungsabschlussroutine.
/*
Routine Description:
Completion routine for idle notification IRP
Arguments:
DeviceObject - pointer to device object
Irp - I/O request packet
DeviceExtension - pointer to device extension
Return Value:
NT status value
--*/
NTSTATUS
IdleNotificationRequestComplete(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PDEVICE_EXTENSION DeviceExtension
)
{
NTSTATUS ntStatus;
POWER_STATE powerState;
PUSB_IDLE_CALLBACK_INFO idleCallbackInfo;
ntStatus = Irp->IoStatus.Status;
if(!NT_SUCCESS(ntStatus) && ntStatus != STATUS_NOT_SUPPORTED)
{
//Idle IRP completes with error.
switch(ntStatus)
{
case STATUS_INVALID_DEVICE_REQUEST:
//Invalid request.
break;
case STATUS_CANCELLED:
//1. The device driver canceled the IRP.
//2. A system power state change is required.
break;
case STATUS_POWER_STATE_INVALID:
// Device driver requested a D3 power state for its device
// Release the allocated resources.
goto IdleNotificationRequestComplete_Exit;
case STATUS_DEVICE_BUSY:
//The bus driver already holds an idle IRP pending for the device.
break;
default:
break;
}
// If IRP completes with error, issue a SetD0
//Increment the I/O count because
//a new IRP is dispatched for the driver.
//This call is not shown.
powerState.DeviceState = PowerDeviceD0;
// Issue a new IRP
PoRequestPowerIrp (
DeviceExtension->PhysicalDeviceObject,
IRP_MN_SET_POWER,
powerState,
(PREQUEST_POWER_COMPLETE) PoIrpCompletionFunc,
DeviceExtension,
NULL);
}
IdleNotificationRequestComplete_Exit:
idleCallbackInfo = DeviceExtension->IdleCallbackInfo;
DeviceExtension->IdleCallbackInfo = NULL;
DeviceExtension->PendingIdleIrp = NULL;
InterlockedExchange(&DeviceExtension->IdleReqPend, 0);
if(idleCallbackInfo)
{
ExFreePool(idleCallbackInfo);
}
DeviceExtension->IdleState = IdleComplete;
// Because the IRP was created using IoAllocateIrp,
// the IRP needs to be released by calling IoFreeIrp.
// Also return STATUS_MORE_PROCESSING_REQUIRED so that
// the kernel does not reference this.
IoFreeIrp(Irp);
KeSetEvent(&DeviceExtension->IdleIrpCompleteEvent, IO_NO_INCREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
Rückrufroutine für USB-Leerlaufbenachrichtigungen
Der Bustreiber (entweder eine Instanz des Hubtreibers oder der generische übergeordnete Treiber) bestimmt, wann es sicher ist, die untergeordneten Geräte auszusetzen. Wenn dies der Fall ist, ruft es die vom Client-Treiber jedes Kindes bereitgestellte Rückrufroutine für die Leerlaufbenachrichtigung auf.
Der Funktionsprototyp für USB_IDLE_CALLBACK lautet wie folgt:
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
Ein Gerätetreiber muss die folgenden Aktionen in der Idle-Benachrichtigungsrückrufroutine ausführen:
- Fordern Sie eine IRP_MN_WAIT_WAKE IRP für das Gerät an, wenn das Gerät für die Remotereaktivierung bewaffnet sein muss.
- Brechen Sie alle E/A-Vorgänge ab, und bereiten Sie das Gerät auf einen niedrigeren Leistungszustand vor.
- Setzen Sie das Gerät in einen WDM-Energiesparzustand, indem Sie PoRequestPowerIrp aufrufen, wobei der PowerState-Parameter auf den Enumerationswert "PowerDeviceD2" festgelegt ist (definiert in wdm.h; ntddk.h).
Sowohl der Hubtreiber als auch der generische übergeordnete USB-Treiber (Usbccgp.sys) rufen die Rückrufroutine für leerlaufbenachrichtigungen bei IRQL = PASSIVE_LEVEL auf. Die Rückrufroutine kann dann blockieren, während sie auf den Abschluss der Änderungsanforderung für den Energiezustand wartet.
Die Rückrufroutine wird nur aufgerufen, während sich das System in S0 befindet und sich das Gerät in D0 befindet.
Die folgenden Einschränkungen gelten für Rückrufroutinen für Idle-Anforderungsbenachrichtigungen:
- Gerätetreiber können einen Gerätestromzustandsübergang von D0 zu D2 in der Idle-Benachrichtigungsrückrufroutine initiieren, aber es ist kein anderer Energiezustandsübergang zulässig. Insbesondere darf ein Treiber nicht versuchen, sein Gerät bei der Ausführung der Rückrufroutine in D0 zu ändern.
- Gerätetreiber dürfen nicht mehr als ein Strom-IRP innerhalb der Rückrufroutine für Leerlaufbenachrichtigungen anfordern.
Aktivierung von Geräten für Aufweckvorgänge in der Leerlaufbenachrichtigungs-Callback-Routine
Die Idle-Benachrichtigungsrückrufroutine sollte bestimmen, ob auf dem Gerät eine IRP_MN_WAIT_WAKE Anforderung aussteht. Wenn keine IRP_MN_WAIT_WAKE Anforderung aussteht, sollte die Rückrufroutine vor dem Anhalten des Geräts eine IRP_MN_WAIT_WAKE Anforderung senden. Weitere Informationen zum Warten-Weck-Mechanismus finden Sie unter Unterstützen von Geräten, die über Weck-Fähigkeiten verfügen.
Globaler USB-Betriebsstopp
Die USB 2.0-Spezifikation definiert das globale Anhalten als Anhalten des gesamten Bus hinter einem USB-Hostcontroller, indem alle USB-Datenverkehr auf dem Bus, einschließlich Start-of-Frame-Paketen, abgesenkt werden. Nachgeschaltete Geräte, die noch nicht angehalten wurden, erkennen den Leerlaufstatus auf ihrem upstream-Port und geben den Anhaltezustand selbst ein. Windows implementiert das globale Anhalten auf diese Weise nicht. Windows versetzt jedes USB-Gerät immer selektiv hinter einem USB-Hostcontroller in den Ruhezustand, bevor der gesamte USB-Verkehr auf dem Bus beendet wird.
Bedingungen für das globale Anhalten
Der USB-Hubtreiber suspendiert selektiv jeden Hub, bei dem alle angeschlossenen Geräte im Energiezustand D1, D2 oder D3 sind. Der gesamte Bus wechselt in den globalen Ruhezustand, sobald alle USB-Hubs selektiv angehalten werden. Der USB-Treiberstapel behandelt ein Gerät als Leerlauf, wenn sich das Gerät in einem WDM-Gerätezustand von D1, D2 oder D3 befindet.