Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Uwaga / Notatka
Ten artykuł jest przeznaczony dla deweloperów sterowników urządzeń. Jeśli występują problemy z urządzeniem USB, zobacz Rozwiązywanie problemów z USB-C w systemie Windows
Funkcja selektywnego wstrzymania USB umożliwia sterownikowi koncentratora zawieszenie pojedynczego portu bez wpływu na działanie innych portów w koncentratorze. Ta funkcja jest przydatna w komputerach przenośnych, ponieważ pomaga oszczędzać energię baterii. Wiele urządzeń, takich jak skanery biometryczne, wymaga tylko sporadycznie zasilania. Wstrzymanie takich urządzeń, gdy nie są używane, zmniejsza całkowite zużycie energii. Co ważniejsze, każde urządzenie, które nie jest selektywnie zawieszone, może uniemożliwić kontrolerowi hosta USB wyłączenie harmonogramu transferu, który znajduje się w pamięci systemowej. Transfery bezpośredniego dostępu do pamięci (DMA) wykonywane przez kontroler hosta na scheduler mogą uniemożliwić procesorom systemu wprowadzanie głębszych stanów uśpienia, takich jak C3.
Selektywne wstrzymanie jest domyślnie włączone. Firma Microsoft zdecydowanie zaleca nie wyłączać selektywnego zawieszenia.
Sterowniki klienta nie powinny próbować określić, czy selektywne wstrzymanie jest włączone przed wysłaniem bezczynnych żądań. Należy przesyłać żądania w stanie bezczynności za każdym razem, gdy urządzenie jest nieaktywne. Jeśli żądanie bezczynności zakończy się niepowodzeniem, sterownik klienta powinien zresetować czasomierz bezczynności i ponowić próbę.
Aby selektywnie wstrzymać urządzenie USB, istnieją dwa różne mechanizmy: żądania bezczynności IRPs (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) i ustawienia zasilania IRPs (IRP_MN_SET_POWER). Mechanizm do użycia zależy od typu urządzenia: złożonego lub niezgodnego.
Wybieranie mechanizmu selektywnego wstrzymania
Sterowniki klienta dla interfejsu na urządzeniu złożonym, które umożliwiają interfejs do zdalnego wybudzania z żądaniem czekania IRP (IRP_MN_WAIT_WAKE), muszą używać mechanizmu żądania bezczynności IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION), aby selektywnie wstrzymać urządzenie.
Aby uzyskać informacje na temat zdalnego budzenia, zobacz:
W tej sekcji opisano mechanizm selektywnego wstrzymania systemu Windows.
Wysyłanie żądania bezczynności IRP USB
Gdy urządzenie przejdzie w stan bezczynności, sterownik klienta informuje sterownik magistrali, wysyłając żądanie bezczynności IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Po określeniu przez sterownik magistrali, że można bezpiecznie przełączyć urządzenie w stan niskiego zużycia energii, wywołuje funkcję zwrotną, którą sterownik urządzenia klienckiego przekazał stosowi z żądaniem bezczynności IRP.
W procedurze wywołania zwrotnego sterownik klienta musi anulować wszystkie oczekujące operacje wejścia/wyjścia i poczekać na zakończenie wszystkich żądań IRP dla USB. Następnie może wysłać żądanie IRP_MN_SET_POWER , aby zmienić stan zasilania urządzenia WDM na D2. Trzeba poczekać na ukończenie żądania D2 przed zakończeniem procedury wywołania zwrotnego. Aby uzyskać więcej informacji na temat procedury wywołania zwrotnego powiadomienia o bezczynności, zobacz Implementację wywołania zwrotnego żądania bezczynności IRP USB.
Sterownik magistrali nie kończy żądania bezczynności IRP po wywołaniu rutyny powiadomień o bezczynności. Zamiast tego sterownik magistrali utrzymuje oczekujące bezczynne żądanie IRP do momentu spełnienia jednego z następujących warunków:
- Odebrano protokół IRPIRP_MN_SURPRISE_REMOVAL lub IRP_MN_REMOVE_DEVICE. Po odebraniu jednego z tych IRP, IRP żądanie bezczynności kończy się STATUS_CANCELLED.
- Kierowca autobusu otrzymuje żądanie wprowadzenia urządzenia do działającego stanu zasilania (D0). Po otrzymaniu tego żądania kierowca autobusu kończy oczekujące żądanie bezczynności IRP ze statusem STATUS_SUCCESS.
Następujące ograniczenia dotyczą korzystania z bezczynnych żądań IP:
- Sterowniki muszą być w stanie zasilania urządzenia D0 podczas wysyłania bezczynnego żądania IRP.
- Sterowniki muszą wysyłać tylko jedno bezczynne żądanie IRP na stos urządzenia.
Poniższy przykładowy kod WDM ilustruje kroki, które wykonuje sterownik urządzenia, w celu wysłania żądania bezczynności USB IRP. Sprawdzanie błędów zostanie pominięte w poniższym przykładzie kodu.
Przydzielanie i inicjowanie protokołu IRP IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION
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);Przydzielenie i inicjalizacja struktury informacji o bezczynności żądania (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;Ustaw procedurę uzupełniania.
Sterownik klienta musi skojarzyć procedurę uzupełniania z bezczynnym żądaniem IRP. Aby uzyskać więcej informacji na temat procedury kończenia powiadomień o bezczynności i przykładowego kodu, zobacz Implementowanie procedury uzupełniania żądań bezczynności USB IRP.
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);Zapisz żądanie bezczynności w rozszerzeniu urządzenia.
deviceExtension->PendingIdleIrp = irp;Wyślij żądanie bezczynności do sterownika nadrzędnego.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
Anulowanie żądania w stanie bezczynności USB
W pewnych okolicznościach sterownik urządzenia może wymagać anulowania żądania bezczynności IRP przesłanego do sterownika magistrali. Taka sytuacja może wystąpić, jeśli urządzenie zostanie usunięte, stanie się aktywne po bezczynności i wysłaniu żądania bezczynności lub jeśli cały system przechodzi do niższego stanu zasilania systemu.
Sterownik klienta anuluje bezczynny protokół IRP przez wywołanie IoCancelIrp. W poniższej tabeli opisano trzy scenariusze anulowania bezczynnego IRP i określono działania, które musi podjąć sterownik.
| Scenariusz | Mechanizm anulowania żądań bezczynności |
|---|---|
| Sterownik klienta anuluje bezczynny IRP, a procedura wywołania zwrotnego powiadomienia o bezczynności USB nie została jeszcze uruchomiona. | Stos sterowników USB zakończa bezczynny IRP. Ponieważ urządzenie nigdy nie opuściło D0, sterownik nie zmienia stanu urządzenia. |
| Sterownik klienta anuluje bezczynne IRP, stos sterowników USB wywołuje procedurę zwrotną powiadomienia o bezczynności USB, która nie została jeszcze zakończona. | Istnieje możliwość, że procedura wywołania zwrotnego powiadomienia o bezczynności USB jest wywoływana, mimo że sterownik klienta wywołał anulowanie IRP. W takim przypadku procedura wywołania zwrotnego sterownika klienta musi nadal wyłączać urządzenie, wysyłając urządzenie do niższego stanu zasilania synchronicznie. Gdy urządzenie jest w stanie niższego zasilania, sterownik klienta może wysłać żądanie D0 . Alternatywnie sterownik może poczekać, aż stos sterowników USB ukończy bezczynne IRP, a następnie wysłać IRP D0. Jeśli procedura wywołania zwrotnego nie może przełączyć urządzenia w stan niskiego zasilania z powodu niewystarczającej ilości pamięci do przydzielenia żądania IRP zasilania, powinna natychmiast anulować bezczynne żądanie IRP i zakończyć działanie. Bezczynny protokół IRP nie zostanie ukończony, dopóki nie zostanie zwrócona rutyna wywołania zwrotnego. W związku z tym rutyna wywołania zwrotnego nie powinna blokować się, czekając na zakończenie anulowanego oczekiwania bezczynnego pakietu żądania I/O (IRP). |
| Urządzenie jest już w stanie niskiego poboru mocy. | Jeśli urządzenie jest już w stanie niskiego zasilania, sterownik klienta może wysłać protokół IRP D0 . Stos sterowników USB kończy bezczynne żądanie IRP za pomocą STATUS_SUCCESS. Alternatywnie sterownik może anulować bezczynny protokół IRP, poczekać na ukończenie bezczynnego protokołu IRP przez stos sterowników USB, a następnie wysłać protokół IRP D0 . |
Procedura obsługi zakończenia żądania bezczynności IRP USB
W wielu przypadkach kierowca autobusu może wywołać procedurę końcową żądania bezczynności IRP kierowcy. Jeśli taka sytuacja wystąpi, sterownik klienta musi ustalić, dlaczego kierowca magistrali zakończył IRP. Zwrócony kod stanu może podać te informacje. Jeśli kod stanu nie jest STATUS_POWER_STATE_INVALID, sterownik powinien umieścić swoje urządzenie w D0 , jeśli urządzenie nie jest jeszcze w D0. Jeśli urządzenie jest nadal bezczynne, sterownik może przesłać kolejne żądanie IRP dotyczące bezczynności.
Uwaga / Notatka
Bezczynna rutyna ukończenia żądania IRP nie powinna blokować oczekiwania na ukończenie żądania zasilania D0 . Procedura zakończenia może być wywoływana w kontekście IRP zasilania przez sterownik koncentratora, a blokowanie innego IRP zasilania w procedurze zakończenia może prowadzić do zakleszczenia.
Poniższa lista wskazuje, jak procedury uzupełniania dla żądania bezczynności powinny interpretować niektóre typowe kody stanu:
| Kod stanu | Opis |
|---|---|
| STATUS_SUKCES | Wskazuje, że urządzenie nie powinno już być zawieszone. Jednak kierowcy powinni sprawdzić, czy ich urządzenia są zasilane, i umieścić je w D0, jeśli nie są jeszcze w D0. |
| STATUS_ANULOWANO | Kierowca autobusu kończy bezczynne żądanie IRP z STATUS_CANCELLED w dowolnej z następujących sytuacji:
|
| STATUS_NIEPRAWIDŁOWY_STAN_ENERGII | Wskazuje, że sterownik urządzenia zażądał stanu zasilania D3 dla urządzenia. Po wystąpieniu tego żądania kierowca magistrali kończy wszystkie oczekujące IRP-y w stanie bezczynności z kodem STATUS_POWER_STATE_INVALID. |
| STATUS_URZĄDZENIE_ZAJĘTE | Wskazuje, że kierowca autobusu ma już oczekujące żądanie IRP w stanie bezczynności dla urządzenia. Tylko jeden bezczynny protokół IRP może być oczekujący na danym urządzeniu w danym momencie. Przesyłanie wielu bezczynnych żądań IR jest błędem ze strony właściciela zasad zasilania. Autor sterownika rozwiązuje ten błąd. |
Poniższy przykładowy kod przedstawia implementację procedury kończenia rutyny bezczynności żądania.
/*
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;
}
Funkcja zwrotna bezczynności USB
Kierowca autobusu (wystąpienie sterownika koncentratora lub ogólny sterownik nadrzędny) określa, kiedy można bezpiecznie zawiesić swoje urządzenia podrzędne. Jeśli tak jest, wywołuje procedurę wywołania zwrotnego powiadomienia o bezczynności dostarczoną przez sterownik klienta każdego dziecka.
Prototyp funkcji dla USB_IDLE_CALLBACK jest następujący:
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
Sterownik urządzenia musi wykonać następujące czynności w swojej rutynie wywołania zwrotnego po otrzymaniu powiadomienia o bezczynności:
- Zażądaj IRP_MN_WAIT_WAKE IRP dla urządzenia, jeśli urządzenie musi być przygotowane do zdalnego wybudzania.
- Anuluj wszystkie operacje we/wy i przygotuj urządzenie, aby przejść do niższego stanu zasilania.
- Umieść urządzenie w stanie uśpienia WDM, wywołując polecenie PoRequestPowerIrp z parametrem PowerState ustawionym na wartość modułu wyliczającego PowerDeviceD2 (zdefiniowaną w wdm.h; ntddk.h).
Zarówno sterownik koncentratora, jak i ogólny sterownik nadrzędny USB (Usbccgp.sys) wywołają procedurę wywołania zwrotnego powiadomień o bezczynności na poziomie IRQL = PASSIVE_LEVEL. Procedury wywołania zwrotnego mogą następnie blokować się podczas oczekiwania na ukończenie żądania zmiany stanu zasilania.
Procedury wywołania zwrotnego są wywoływane tylko wtedy, gdy system znajduje się w S0 , a urządzenie znajduje się w D0.
Następujące ograniczenia dotyczą procedur wywołania zwrotnego powiadomień dotyczące bezczynnych żądań.
- Sterowniki urządzeń mogą inicjować przejście stanu zasilania urządzenia z D0 do D2 w procedurze wywołania zwrotnego powiadomień bezczynnych, ale nie jest dozwolone żadne inne przejście stanu zasilania. W szczególności sterownik nie może podjąć próby zmiany urządzenia na D0 podczas wykonywania procedury wywołania zwrotnego.
- Sterowniki urządzeń nie mogą żądać więcej niż jednego żądania IRP zasilania z poziomu procedury wywołania zwrotnego powiadomienia o bezczynności.
Uzbrajanie urządzeń do wybudzenia w procedurze powiadomienia zwrotnego trybu bezczynności.
Procedura wywołania zwrotnego powiadomienia bezczynności powinna określić, czy jej urządzenie ma oczekujące żądanie IRP_MN_WAIT_WAKE. Jeśli żądanie IRP_MN_WAIT_WAKE nie jest oczekujące, procedura wywołania zwrotnego powinna przesłać żądanie IRP_MN_WAIT_WAKE przed wstrzymaniem urządzenia. Aby uzyskać więcej informacji na temat mechanizmu budzenia z oczekiwania, zobacz Obsługa urządzeń z funkcjami budzenia.
Globalne wstrzymanie USB
Specyfikacja USB 2.0 definiuje globalne zawieszenie jako zawieszenie całej magistrali za kontrolerem hosta USB przez zaprzestanie całego ruchu USB w magistrali, w tym pakietów start-of-frame. Urządzenia podrzędne, które nie zostały jeszcze zawieszone, wykryją stan bezczynności na ich porcie nadrzędnym i wprowadzają stan wstrzymania samodzielnie. System Windows nie implementuje globalnego zawieszenia w ten sposób. System Windows zawsze selektywnie wstrzymuje każde urządzenie USB przez kontroler hosta USB, zanim zatrzyma całą transmisję USB na magistrali.
Warunki zawieszenia globalnego
Sterownik koncentratora USB selektywnie zawiesza każdy koncentrator, w którym wszystkie dołączone urządzenia znajdują się w stanie zasilania urządzenia D1, D2 lub D3. Cała magistrala przechodzi do globalnego zawieszenia, gdy wszystkie koncentratory USB są selektywnie zawieszone. Stos sterowników USB traktuje urządzenie jako bezczynne za każdym razem, gdy urządzenie znajduje się w stanie urządzenia WDM D1, D2 lub D3.