Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Opmerking
Dit artikel is bedoeld voor ontwikkelaars van apparaatstuurprogramma's. Als u problemen ondervindt met een USB-apparaat, raadpleegt u Problemen met USB-C oplossen in Windows
Met de functie voor selectief onderbreken van USB kan het hubstuurprogramma een afzonderlijke poort onderbreken zonder dat dit van invloed is op de werking van de andere poorten op de hub. Deze functionaliteit is handig in draagbare computers omdat het helpt batterijvermogen te besparen. Veel apparaten, zoals biometrische scanners, vereisen slechts af en toe stroom. Het energieverbruik wordt verminderd wanneer dergelijke apparaten worden onderbroken als ze niet in gebruik zijn. Belangrijker nog is dat elk apparaat dat niet selectief is geschorst, kan voorkomen dat de USB-hostcontroller zijn overdrachtsschema uitschakelt, dat zich in het systeemgeheugen bevindt. Directe geheugentoegang (DMA) overdrachten door de hostcontroller naar de scheduler kunnen voorkomen dat de processors van het systeem in diepere slaaptoestanden terechtkomen, zoals C3.
Selectief onderbreken is standaard ingeschakeld. Microsoft raadt ten zeerste aan om selectief onderbreken niet uit te schakelen.
Clientstuurprogramma's moeten niet proberen te bepalen of selectief onderbreken is ingeschakeld voordat niet-actieve aanvragen worden verzonden. Ze moeten niet-actieve aanvragen indienen wanneer het apparaat niet actief is. Als het inactieve verzoek mislukt, moet het clientstuurprogramma de timer voor inactiviteit opnieuw instellen en opnieuw proberen.
Als u een USB-apparaat selectief wilt opschorten, bestaan er twee verschillende mechanismen: idle-aanvraag IRPs (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) en instellen van stroom-IRPs (IRP_MN_SET_POWER). Het te gebruiken mechanisme is afhankelijk van het type apparaat: samengesteld of niet-composite.
Een selectief onderbrekingsmechanisme selecteren
Clientsstuurprogramma's voor een interface op een samengesteld apparaat die de interface voor externe activering met een wacht-wake-IRP (IRP_MN_WAIT_WAKE) mogelijk maken, moeten het IRP-mechanisme voor inactiviteit (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) gebruiken om selectief een apparaat op te schorten.
Voor informatie over op afstand ontwaak, zie:
In deze sectie wordt het windows-mechanisme voor selectief onderbreken uitgelegd.
Een IRP voor inactiviteit van een USB-aanvraag verzenden
Wanneer een apparaat inactief wordt, informeert het clientstuurprogramma het busstuurprogramma door een idle-aanvraag-IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) te verzenden. Nadat het busstuurprogramma heeft vastgesteld dat het veilig is om het apparaat in een lage energiemodus te plaatsen, roept het de callback-routine aan die het stuurprogramma van het clientapparaat in de stack heeft geplaatst met de IRP van de niet-actieve aanvraag.
In de callback-routine moet de client-driver alle in behandeling zijnde I/O-bewerkingen annuleren en wachten tot alle USB I/O-IRP's zijn voltooid. Vervolgens kan er een IRP_MN_SET_POWER aanvraag worden ingediend om de energiestatus van het WDM-apparaat te wijzigen in D2. De callback-routine moet wachten totdat de D2-aanvraag is voltooid voordat deze wordt geretourneerd. Zie Implementeren van een IRP-callbackroutine voor niet-actieve meldingen voor meer informatie over de niet-actieve meldingen terugroeproutine.
De busdriver voltooit de IRP-verzoek tot inactiviteit niet nadat de callbackroutine voor melding van inactiviteit is aangeroepen. In plaats daarvan houdt de buschauffeur de inactieve aanvraag-IRP in behandeling totdat een van de volgende voorwaarden van toepassing is.
- Er wordt een IRP_MN_SURPRISE_REMOVAL of een IRP_MN_REMOVE_DEVICE IRP ontvangen. Wanneer een van deze IRP's wordt ontvangen, wordt de inactieve aanvraag-IRP voltooid met STATUS_CANCELLED.
- De buschauffeur ontvangt een verzoek om het apparaat in een werkende energiestatus (D0) te plaatsen. Na ontvangst van deze aanvraag voltooit de buschauffeur de in behandeling zijnde inactieve aanvraag-IRP met STATUS_SUCCESS.
De volgende beperkingen gelden voor het gebruik van inactieve IRP-verzoeken:
- Stuurprogramma's moeten de energiestatus D0 van het apparaat hebben wanneer ze een idle-verzoek IRP verzenden.
- Besturingsprogramma's moeten slechts één niet-actieve aanvraag-IRP per de apparaatstack versturen.
De volgende WDM-voorbeeldcode illustreert de stappen die een apparaatstuurprogramma nodig heeft om een IRP voor inactiviteit van een USB-aanvraag te verzenden. Foutcontrole wordt weggelaten in het volgende codevoorbeeld.
De IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION IRP toewijzen en initialiseren
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);Wijs de informatiestructuur voor de inactieve aanroep toe en initialiseer deze (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;Stel een voltooiingsroutine in.
Het clientstuurprogramma moet een voltooiingsroutine koppelen aan de IRP van de niet-actieve aanvraag. Zie Heimplementatie van een voltooiingsroutine voor USB-inactiviteitmeldingen IRP voor meer informatie over de routine voor het voltooien van meldingen en voorbeeldcode.
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);Sla de niet-actieve aanvraag op in de apparaatextensie.
deviceExtension->PendingIdleIrp = irp;Verstuur het idle-verzoek naar de bovenliggende driver.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
Een niet-actieve USB-aanvraag annuleren
Onder bepaalde omstandigheden moet een apparaatstuurprogramma mogelijk een idle-verzoek IRP annuleren die is ingediend bij het busstuurprogramma. Deze situatie kan optreden als het apparaat wordt verwijderd, actief wordt na inactiviteit en het verzenden van de niet-actieve aanvraag, of als het hele systeem overgaat naar een lagere energiestatus van het systeem.
Het clientstuurprogramma annuleert de inactieve IRP door IoCancelIrp aan te roepen. In de volgende tabel worden drie scenario's beschreven voor het annuleren van een niet-actieve IRP en geeft u de actie op die het stuurprogramma moet uitvoeren:
| Scenariobeschrijving | Annuleringsmechanisme voor niet-actieve aanvragen |
|---|---|
| Het clientstuurprogramma annuleert de inactieve IRP en de callbackroutine voor de USB-inactiviteitsmelding is niet aangeroepen. | De USB-stuurprogrammastack voltooit de IRP wanneer deze inactief is. Omdat het apparaat de D0 nooit heeft verlaten, verandert het stuurprogramma de apparaatstatus niet. |
| Het clientstuurprogramma annuleert de inactieve IRP, de USB-stuurprogrammastack roept de callbackroutine voor USB-idle meldingen aan, en deze heeft nog geen antwoord gegeven. | Het is mogelijk dat de callbackroutine voor usb-inactiviteit wordt aangeroepen, ook al heeft het clientstuurprogramma annulering op de IRP aangeroepen. In dit geval moet de callbackroutine van het clientstuurprogramma het apparaat nog steeds uitschakelen door het apparaat synchroon naar een lagere energiestatus te verzenden. Wanneer het apparaat in de laag-vermogentoestand is, kan het clientstuurprogramma vervolgens een D0-aanvraag verzenden. Het stuurprogramma kan ook wachten totdat de USB-stuurprogrammastack de inactieve IRP voltooit en vervolgens de D0 IRP verzendt. Als de callback-routine het apparaat niet in een lage energiestatus kan plaatsen vanwege onvoldoende geheugen om een energie-IRP toe te wijzen, moet het de niet-actieve IRP annuleren en onmiddellijk afsluiten. De niet-actieve IRP wordt pas voltooid als de callback-routine is voltooid. Daarom mag de callback-routine niet blokkeren totdat het geannuleerde niet-actieve IRP is voltooid. |
| Het apparaat heeft al een lage energiestatus. | Als het apparaat al een lage energiestatus heeft, kan het clientstuurprogramma een D0 IRP verzenden. De USB-stuurprogrammastack voltooit de inactieve aanvraag-IRP met STATUS_SUCCESS. Het stuurprogramma kan ook de niet-actieve IRP annuleren, wachten totdat de USB-stuurprogrammastack de inactieve IRP voltooit en vervolgens een D0 IRP verzenden. |
IRP-voltooiingsroutine voor niet-actieve USB-aanvragen
In veel gevallen kan een buschauffeur een inactief verzoek van een bestuurder aanroepen via een IRP-voltooiingsprocedure. Als deze situatie zich voordoet, moet een clientstuurprogramma detecteren waarom het busstuurprogramma de IRP heeft voltooid. De geretourneerde statuscode kan deze informatie verstrekken. Als de statuscode niet STATUS_POWER_STATE_INVALID is, moet het besturingsprogramma het apparaat in D0 zetten als het apparaat nog niet al in D0 staat. Als het apparaat nog steeds inactief is, kan het stuurprogramma een andere inactieve aanvraag-IRP indienen.
Opmerking
De IRP-voltooiingsroutine voor niet-actieve aanvragen mag niet blokkeren totdat een D0-stroomaanvraag is voltooid. De voltooiingsroutine kan door het hubstuurprogramma worden aangeroepen in de context van een stroom-IRP, en het blokkeren van een andere stroom-IRP in de voltooiingsroutine kan tot een impasse leiden.
De volgende lijst geeft aan hoe een voltooiingsroutine voor een niet-actieve aanvraag enkele algemene statuscodes moet interpreteren:
| Statuscode | Beschrijving |
|---|---|
| STATUS_SUKSES | Geeft aan dat het apparaat niet langer gepauzeerd mag worden. Echter, gebruikers moeten controleren of hun apparaten aan staan en ze in D0 zetten als ze nog niet in D0 zijn. |
| STATUS_GEANNULEERD | De buschauffeur voltooit de inactieve aanvraag-IRP met STATUS_CANCELLED in een van de volgende omstandigheden:
|
| STATUS_STROOMTOESTAND_ONGELDIG | Geeft aan dat het apparaatstuurprogramma een D3-energiestatus voor het apparaat heeft aangevraagd. Wanneer dit verzoek plaatsvindt, maakt het busstuurprogramma alle in afwachting zijnde inactieve IRP's af met STATUS_POWER_STATE_INVALID. |
| STATUS_APPARAAT_BEZET | Geeft aan dat het busstuurprogramma al een inactieve aanvraag-IRP bevat die in behandeling is voor het apparaat. Slechts één niet-actieve IRP kan tegelijk in behandeling zijn voor een bepaald apparaat. Het indienen van meerdere inactieve aanvraag-IR's is een fout van de kant van de eigenaar van het energiebeleid. De schrijver van het stuurprogramma lost de fout op. |
In het volgende codevoorbeeld ziet u een voorbeeld van een implementatie voor de voltooiingsroutine voor niet-actieve aanvragen.
/*
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;
}
Terugbelroutine voor niet-actieve USB-meldingen
Het busstuurprogramma (een exemplaar van het hubstuurprogramma of het generieke bovenliggende stuurprogramma) bepaalt wanneer het veilig is om de kindapparaten van het apparaat te schorsen. Als dat zo is, wordt de terugbelroutine voor niet-actieve meldingen aangeroepen die wordt geleverd door het clientstuurprogramma van elk kind.
Het prototype van de functie voor USB_IDLE_CALLBACK is als volgt:
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
Een apparaatstuurprogramma moet de volgende acties uitvoeren in de terugbelroutine voor niet-actieve meldingen:
- Vraag een IRP_MN_WAIT_WAKE IRP voor het apparaat aan als het apparaat moet worden ingesteld voor externe wekfunctie.
- Annuleer alle I/O en bereid het apparaat voor om naar een lagere energiestatus te gaan.
- Plaats het apparaat in een WDM-slaapstand door PoRequestPowerIrp aan te roepen met de Parameter PowerState ingesteld op de enumerator-waarde PowerDeviceD2 (gedefinieerd in wdm.h; ntddk.h).
Zowel de hub-driver als de USB Generic Parent Driver (Usbccgp.sys) roepen de callbackroutine voor niet-actieve meldingen aan bij IRQL = PASSIVE_LEVEL. De callback-routine kan vervolgens worden geblokkeerd terwijl wordt gewacht totdat de aanvraag voor energiestatuswijziging is voltooid.
De callback-routine wordt alleen aangeroepen terwijl het systeem zich in S0 bevindt en het apparaat zich in D0 bevindt.
De volgende beperkingen zijn van toepassing op terugbelroutines voor niet-actieve aanvragen:
- Apparaatstuurprogramma's kunnen een overgang van de energiestatus van het apparaat van D0 naar D2 initiëren in de terugbelroutine voor niet-actieve meldingen, maar er is geen andere overgang van energiestatus toegestaan. Een stuurprogramma mag met name niet proberen het apparaat te wijzigen in D0 tijdens het uitvoeren van de callback-routine.
- Apparaatstuurprogramma's mogen vanuit de terugbelroutine voor idlemeldingen niet meer dan één energie-IRP aanvragen.
Apparaten activeren voor ontwaken in de terugroeproutine bij inactieve meldingen
De terugbelroutine voor niet-actieve meldingen moet bepalen of het apparaat een IRP_MN_WAIT_WAKE aanvraag in behandeling heeft. Als er geen IRP_MN_WAIT_WAKE-aanvraag in behandeling is, moet de callback-routine een IRP_MN_WAIT_WAKE-aanvraag indienen voordat het apparaat wordt gepauzeerd. Voor meer informatie over het "wachten en activeren"-mechanisme, zie Apparaten die wekmogelijkheden ondersteunen.
USB globale onderbreking
De USB 2.0-specificatie definieert globale uitschakeling als de onderbreking van de hele bus achter een USB-hostcontroller door al het USB-verkeer op de bus te stoppen, inclusief start-of-frame pakketten. Downstreamapparaten die nog niet zijn onderbroken, detecteren de niet-actieve status op hun upstreampoort en voeren de onderbrekingsstatus zelf in. Windows implementeert op deze manier geen globale onderbreking. Windows onderbreekt altijd selectief elk USB-apparaat achter een USB-hostcontroller voordat het alle USB-verkeer op de bus stopt.
Voorwaarden voor globale schorsing
Het USB-hubstuurprogramma onderbreekt selectief alle hubs waar alle aangesloten apparaten zich in D1-, D2- of D3-apparaatstroomstatus bevinden. De hele bus wordt globaal in slaapstand gezet zodra alle USB-hubs selectief worden geschorst. De USB-stuurprogrammastack behandelt een apparaat als niet-actief wanneer het apparaat zich in een WDM-apparaatstatus van D1, D2 of D3 bevindt.