Anteckning
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Anmärkning
Den här artikeln gäller för utvecklare av enhetsdrivrutiner. Om du har problem med en USB-enhet kan du läsa Åtgärda USB-C problem i Windows
Med funktionen selektiv paus för USB kan hubbdrivrutinen pausa en enskild port utan att påverka driften av de andra portarna på hubben. Den här funktionen är användbar på bärbara datorer eftersom den bidrar till att spara batterikraft. Många enheter, till exempel biometriska skannrar, kräver bara ström tillfälligt. Om du pausar sådana enheter, när de inte används, minskar den totala energiförbrukningen. Ännu viktigare är att alla enheter som inte är selektivt avstängda kan förhindra att USB-värdstyrenheten inaktiverar sitt överföringsschema, som finns i systemminnet. DMA-överföring (Direct Memory Access) från värdstyrenheten till schemaläggaren kan hindra systemets processorer från att komma in i djupare vilolägen, till exempel C3.
Selektivt uppehåll är aktiverat som standard. Microsoft rekommenderar starkt att du inte ska inaktivera selektivt uppehåll.
Klientdrivrutiner bör inte försöka avgöra om selektivt uppehåll är aktiverat innan inaktiva begäranden skickas. De bör skicka inaktiva begäranden när enheten är inaktiv. Om begäran i viloläge misslyckas ska klientdrivrutinen återställa vilotimern och försöka igen.
För att selektivt pausa en USB-enhet finns det två olika mekanismer: begäranden för inaktivitet (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) och ställ in power-IRP:er (IRP_MN_SET_POWER). Vilken mekanism som ska användas beror på typen av enhet: sammansatt eller icke-kompatibel.
Välja en selektiv suspenderingsmekanism
Klientdrivrutiner för ett gränssnitt på en sammansatt enhet som aktiverar gränssnittet för väckning på distans med ett väntväcknings-IRP (IRP_MN_WAIT_WAKE) måste använda mekanismen för inaktiv begäran IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) för att selektivt suspendera en enhet.
Information om fjärruppvakning finns i:
I det här avsnittet beskrivs Windows mekanismen för selektiv avstängning.
Skicka en vilolägesbegäran för USB IRP
När en enhet blir inaktiv informerar klientdrivrutinen busschauffören genom att skicka en IRP för inaktiv begäran (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). När bussdrivrutinen har fastställt att det är säkert att försätta enheten i ett lågeffektstillstånd, anropas återanropsrutinen som klientenhetsdrivrutin överförde nedåt i stacken med inaktiv begäran IRP.
I återanropsrutinen måste klientdrivrutinen avbryta alla väntande I/O-åtgärder och vänta tills alla USB I/O-IRP:er har slutförts. Sedan kan den utfärda en IRP_MN_SET_POWER begäran om att ändra WDM-enhetens energitillstånd till D2. Återanropsrutinen måste vänta tills D2-begäran har slutförts innan den returneras. Mer information om återanropsrutinen för inaktiva meddelanden finns i Implementera en IRP-återanropsrutin för USB-inaktiv begäran.
Busschauffören fullföljer inte IRP för begäran om inaktivitet efter att ha anropat återanropsrutinen för inaktivitetsmeddelande. I stället håller bussföraren den väntande inaktivitetsbegäran IRP tills något av följande villkor är sant:
- En IRP_MN_SURPRISE_REMOVAL eller IRP_MN_REMOVE_DEVICE IRP tas emot. När en av dessa IRP:er tas emot slutförs viloläge begärande-IRP med STATUS_CANCELLED.
- Busschauffören tar emot en begäran om att placera enheten i ett fungerande energitillstånd (D0). När den här begäran tas emot slutför bussföraren den väntande, inaktiva IRP-begäran med STATUS_SUCCESS.
Följande begränsningar gäller för användning av ip-adresser för inaktiva begäranden:
- Drivrutiner måste vara i enhetens energitillstånd D0 när du skickar en IRP för inaktiv begäran.
- Drivrutiner måste bara skicka en IRP för inaktiv begäran per enhetsstack.
Följande WDM-exempelkod illustrerar de steg som en enhetsdrivrutin vidtar för att skicka en IRP för USB-vilobegäran. Felkontroll utelämnas i följande kodexempel.
Allokera och initiera 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);
Allokera och initiera strukturen för information om inaktiva förfrågningar (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;
Ange en slutföranderutin.
Klientdrivrutinen måste associera en slutföranderutin med IRP för inaktiv begäran. Mer information om rutinen för slutförande av inaktiva meddelanden och exempelkod finns i Implementering av en IRP-slutföranderutin för USB-inaktiv begäran.
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);
Lagra den inaktiva begäran i enhetsutvidgningen.
deviceExtension->PendingIdleIrp = irp;
Skicka begäran om inaktivitet till den överordnade drivrutinen.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
Avbrytande av en begäran om inaktivitet för USB
Under vissa omständigheter kan en enhetsdrivrutin behöva avbryta en inaktiv begäran som IRP har skickat till busschauffören. Den här situationen kan inträffa om enheten tas bort, blir aktiv när den har varit inaktiv och skickat inaktivitetsbegäran, eller om hela systemet övergår till ett lägre systemkrafttillstånd.
Klientdrivrutinen avbryter den inaktiva IRP:en genom att anropa IoCancelIrp. I följande tabell beskrivs tre scenarier för att avbryta en inaktiv IRP och anger vilken åtgärd drivrutinen måste vidta:
Scenarium | Mekanism för annullering av inaktiv begäran |
---|---|
Klientdrivrutinen avbryter den inaktiva IRP och återkopplingsrutinen för USB-inaktivitetsmeddelanden har inte anropats. | USB-drivrutinsstacken slutför den inaktiva IRP:n. Eftersom enheten aldrig lämnade D0 ändrar drivrutinen inte enhetens tillstånd. |
Klientdrivrutinen avbryter den lediga IRP:n, USB-drivrutinsstacken anropar återanropsrutinen för USB-meddelelsen om inaktivitet, och återkomsten har inte skett än. | Det är möjligt att återanropsrutinen för USB-inaktiva meddelanden anropas trots att klientdrivrutinen har anropat annullering på IRP. I det här fallet måste klientdrivrutinens återanropsrutin fortfarande stänga av enheten genom att skicka enheten till ett lägre strömtillstånd synkront. När enheten är i lägre strömläge kan klientdrivrutinen sedan skicka en D0-begäran . Alternativt kan drivrutinen vänta tills USB-drivrutinsstacken har slutfört den inaktiva IRP:en och sedan skicka D0 IRP. Om återanropsrutinen inte kan försätta enheten i ett lågeffekttillstånd på grund av otillräckligt minne för att allokera en ström-IRP, bör den avbryta den inaktiva IRP:n och avsluta omedelbart. Den inaktiva IRP:en har inte slutförts förrän återanropsrutinen returneras. Därför bör återanropsrutinen inte blockera väntan på att den avbrutna inaktiva IRP:en ska slutföras. |
Enheten är redan i ett tillstånd med låg ström. | Om enheten redan är i ett lågeffekttillstånd kan klientdrivrutinen skicka en D0 IRP. USB-drivrutinsstacken slutför IRP för inaktiv begäran med STATUS_SUCCESS. Alternativt kan drivrutinen avbryta den inaktiva IRP:en, vänta tills USB-drivrutinsstacken har slutfört den inaktiva IRP:en och sedan skicka en D0-IRP . |
USB vilolägesförfrågan IRP slutföranderutin
I många fall kan en busschaufför anropa en förares inaktiva IRP-slutföranderutin. Om den här situationen inträffar måste en klientdrivrutin identifiera varför busschauffören slutförde IRP:t. Den returnerade statuskoden kan ge den här informationen. Om statuskoden inte är STATUS_POWER_STATE_INVALID ska drivrutinen placera sin enhet i D0 om enheten inte redan finns i D0. Om enheten fortfarande är inaktiv kan drivrutinen skicka en annan avaktivitetsförfrågan IRP.
Anmärkning
IRP-slutföranderutinen för inaktiv begäran bör inte blockera väntan på att en D0-energibegäran ska slutföras. Slutföranderutinen kan anropas i samband med en power IRP av hubbdrivrutinen, och blockering på en annan power IRP i slutföranderutinen kan leda till ett dödläge.
I följande lista visas hur en slutföranderutin för en inaktiv begäran ska tolka några vanliga statuskoder:
Statuskod | Beskrivning |
---|---|
STATUS_FRAMGÅNG | Anger att enheten inte längre ska suspenderas. Förare bör dock kontrollera att deras enheter är påslagna, och återställ dem till D0 om de inte redan finns i D0. |
STATUS_AVBRUTEN | Busschauffören slutför inaktiv förfrågan IRP med STATUS_CANCELLED under någon av följande omständigheter:
|
STATUS_POWER_STATE_OGILTIG | Anger att enhetsdrivrutinen begärde ett D3-energitillstånd för enheten. När den här begäran inträffar slutför busschauffören alla väntande inaktivitets-IRPs med STATUS_POWER_STATE_INVALID. |
STATUS_DEVICE_BUSY | Anger att bussföraren redan har en IRP-begäran som väntar för enheten. Endast en vilande IRP kan vara i avvaktan åt gången för en viss enhet. Att skicka flera inaktiva begäranden IRP är ett fel från kraftpolicyägarens sida. Drivrutinsförfattaren åtgärdar felet. |
I följande kodexempel visas en exempelimplementering för rutinen för slutförande av inaktiva begäranden.
/*
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;
}
Rutin för USB-avisering vid inaktivitet
Busschauffören (antingen en instans av hubbdrivrutinen eller den allmänna överordnade drivrutinen) avgör när det är säkert att pausa enhetens underordnade enheter. I så fall anropas den inaktiva återanropsrutinen som tillhandahålls av varje barns klientdrivrutin.
Funktionsprototypen för USB_IDLE_CALLBACK är följande:
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
En enhetsdrivrutin måste vidta följande åtgärder i sin återanropsrutin för inaktiva meddelanden:
- Begär en IRP_MN_WAIT_WAKE IRP för enheten om enheten måste vara aktiverad för fjärruppvakning.
- Avbryt all I/O och förbered enheten för att gå till ett lägre energisparläge.
- Placera enheten i ett WDM-viloläge genom att anropa PoRequestPowerIrp med PowerState-parametern inställd på uppräkningsvärdet PowerDeviceD2 (definierat i wdm.h; ntddk.h).
Både hubbdrivrutinen och USB Generic Parent Driver (Usbccgp.sys) anropar vilolägesåteranropsrutinen på IRQL = PASSIVE_LEVEL. Återanropsrutinen kan sedan blockera medan den väntar på att begäran om strömtillståndsförändring ska slutföras.
Återanropsrutinen anropas endast när systemet är i S0 och enheten är i D0.
Följande begränsningar gäller för rutiner för återanrop av inaktiva begäranden:
- Enhetsdrivrutiner kan initiera en övergång till enhetskrafttillstånd från D0 till D2 i återanropsrutinen för inaktiva meddelanden, men ingen annan övergång till energitillstånd tillåts. I synnerhet får en drivrutin inte försöka ändra enheten till D0 när den kör återanropsrutinen.
- Enhetsdrivrutiner får inte begära mer än en power IRP inifrån återanropsrutinen för inaktiva meddelanden.
Förbered enheter för väckning i en återanropsrutin under inaktiva tillstånd
Återanropsrutinen för inaktiva meddelanden bör avgöra om enheten har en väntande IRP_MN_WAIT_WAKE begäran. Om ingen IRP_MN_WAIT_WAKE-begäran väntar bör återanropsrutinen skicka in en IRP_MN_WAIT_WAKE-begäran innan enheten sätts i viloläge. Mer information om vänteväckningsmekanismen finns i Stödenheter som har wakeup-funktioner.
GLOBALT USB-uppehåll
USB 2.0-specifikationen definierar global avstängning som en suspendering av hela bussen bakom en USB-värdkontroller genom att stoppa all USB-trafik på bussen, inklusive paket vid start av bildruta. Underordnade enheter som inte redan är suspenderade identifierar viloläge på sin överordnade port och går in i suspenderat tillstånd självständigt. Windows implementerar inte global suspendering på det här sättet. Windows stoppar alltid selektivt varje USB-enhet ansluten till en USB-värdstyrenhet innan den stoppar all USB-trafik på bussen.
Villkor för global avstängning
USB-hubbens drivrutin pausar selektivt alla hubbar där alla dess anslutna enheter är i D1-, D2- eller D3-enhetskrafttillstånd . Hela bussen går in i global avstängning när alla USB-hubbar är selektivt avstängda. USB-drivrutinsstacken behandlar en enhet som inaktiv när enheten är i WDM-enhetstillståndet D1, D2 eller D3.