Nota
L'accesso a questa pagina richiede l'autorizzazione. Puoi provare ad accedere o a cambiare directory.
L'accesso a questa pagina richiede l'autorizzazione. Puoi provare a cambiare directory.
Annotazioni
Questo articolo è destinato agli sviluppatori di driver di dispositivo. Se si riscontrano problemi con un dispositivo USB, vedere Risolvere i problemi di USB-C in Windows
La funzionalità di sospensione selettiva USB consente al driver hub di sospendere una singola porta senza influire sul funzionamento delle altre porte nell'hub. Questa funzionalità è utile nei computer portatili perché consente di risparmiare energia a batteria. Molti dispositivi, ad esempio scanner biometrici, richiedono solo l'alimentazione intermittente. La sospensione di tali dispositivi, quando non sono in uso, riduce il consumo energetico complessivo. Ancora più importante, qualsiasi dispositivo che non sia sospeso in modo selettivo potrebbe impedire al controller host USB di disabilitare la programmazione del trasferimento, che risiede nella memoria di sistema. I trasferimenti DMA (accesso diretto alla memoria) dal controller host al pianificatore possono impedire ai processori del sistema di entrare in stati di sospensione più profondi, come C3.
La sospensione selettiva è abilitata per impostazione predefinita. Microsoft consiglia di non disabilitare la sospensione selettiva.
I driver client non devono provare a determinare se la sospensione selettiva è abilitata prima di inviare richieste inattive. Devono inviare richieste in sospeso ogni volta che il dispositivo è inattivo. Se la richiesta inattiva ha esito negativo, il driver client deve resettare il timer di inattività e riprovare.
Per sospendere in modo selettivo un dispositivo USB, esistono due meccanismi diversi: IRP di richiesta inattiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) e IRP per impostazione di potenza (IRP_MN_SET_POWER). Il meccanismo da usare dipende dal tipo di dispositivo: composito o noncomposito.
Selezione di un meccanismo di sospensione selettiva
I driver client per un'interfaccia in un dispositivo composito che abilitano l'interfaccia per il risveglio remoto con un IRP di attesa del risveglio (IRP_MN_WAIT_WAKE) devono impiegare il meccanismo di richiesta di inattività IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) per sospendere selettivamente un dispositivo.
Per informazioni sulla riattivazione remota, vedere:
Questa sezione illustra il meccanismo di sospensione selettiva di Windows.
Invio di una richiesta USB inattiva IRP
Quando un dispositivo diventa inattivo, il driver client informa il driver del bus inviando una richiesta di inattività IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Dopo che il driver del bus determina che è sicuro inserire il dispositivo in uno stato di basso consumo, invoca la routine di callback che il driver del dispositivo client ha trasmesso lungo lo stack con l'IRP di richiesta inattiva.
Nella routine di callback, il driver client deve annullare tutte le operazioni di I/O in sospeso e attendere il completamento di tutti gli IRP di I/O USB. Può quindi inviare una richiesta di IRP_MN_SET_POWER per modificare lo stato di alimentazione del dispositivo WDM su D2. La routine di callback deve attendere il completamento della richiesta D2 prima della restituzione. Per ulteriori informazioni sulla routine di callback di una richiesta inattiva USB, vedere Implementare una routine di callback per una richiesta inattiva USB IRP.
Il conducente del bus non completa l'IRP per la richiesta di inattività dopo aver chiamato la routine di callback di notifica di inattività. Al contrario, l'autista del bus mantiene in sospeso la richiesta IRP inattiva fino a quando non viene soddisfatta una delle seguenti condizioni:
- È stato ricevuto un IRP_MN_SURPRISE_REMOVAL o un IRP_MN_REMOVE_DEVICE IRP. Quando viene ricevuto uno di questi pacchetti di richiesta di I/O, l'IRP di richiesta di inattività viene completato con STATUS_CANCELLED.
- Il conducente del bus riceve una richiesta di inserire il dispositivo in uno stato di alimentazione funzionante (D0). Alla ricezione di questa richiesta, il driver del bus completa la richiesta inattiva IRP con STATUS_SUCCESS.
Le seguenti restrizioni si applicano all'uso degli Idle Request IRPs:
- I driver devono trovarsi nello stato di alimentazione del dispositivo D0 quando si invia un IRP di richiesta inattiva.
- I driver devono inviare una sola richiesta IRP inattiva per ogni stack di dispositivi.
Il codice di esempio WDM seguente illustra i passaggi impiegati da un driver di dispositivo per inviare un IRP di richiesta di inattività USB. Il controllo degli errori viene omesso nell'esempio di codice seguente.
Allocare e inizializzare il 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);Allocare e inizializzare la struttura di informazioni sulle richieste inattive (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;Imposta una routine di completamento.
Il driver client deve associare una routine di completamento all'IRP della richiesta inattiva. Per altre informazioni sulla routine di completamento delle notifiche inattive e sul codice di esempio, vedere Implementazione di una routine di completamento IRP della richiesta inattiva USB.
IoSetCompletionRoutine (irp, IdleNotificationRequestComplete, DeviceContext, TRUE, TRUE, TRUE);Archiviare la richiesta inattiva nell'estensione del dispositivo.
deviceExtension->PendingIdleIrp = irp;Invia la richiesta di inattività al driver padre.
ntStatus = IoCallDriver (DeviceContext->TopOfStackDeviceObject, irp);
Annullamento di una richiesta di inattività USB
In determinate circostanze, un driver di dispositivo potrebbe dover annullare una richiesta di inattività IRP inviata al conducente del bus. Questa situazione può verificarsi se il dispositivo viene rimosso, diventa attivo dopo l'inattività e l'invio della richiesta inattiva o se l'intero sistema sta passando a uno stato di alimentazione del sistema inferiore.
Il driver client annulla l'IRP inattivo chiamando IoCancelIrp. La tabella seguente descrive tre scenari per annullare un IRP inattivo e specifica l'azione che il driver deve eseguire:
| Sceneggiatura | Meccanismo di annullamento delle richieste inattive |
|---|---|
| Il driver client annulla l'IRP inattivo e la routine di callback di notifica inattiva USB non è stata chiamata. | Lo stack di driver USB completa l'IRP inattivo. Poiché il dispositivo non ha mai lasciato il D0, il driver non modifica lo stato del dispositivo. |
| Il driver client annulla l'IRP in stato di inattività, lo stack di driver USB chiama la routine di callback di notifica inattiva USB, che non è ancora terminata. | È possibile che la routine di callback di notifica di inattività USB venga richiamata anche se il driver client ha richiamato l'annullamento in IRP. In questo caso, la routine di callback del driver client deve comunque spegnere il dispositivo inviando il dispositivo a uno stato di alimentazione inferiore in modo sincrono. Quando il dispositivo si trova nello stato di alimentazione inferiore, il driver client può quindi inviare una richiesta D0 . In alternativa, il driver può attendere che lo stack di driver USB completi l'IRP in fase di inattività e quindi invii IRP D0. Se la routine di callback non è in grado di inserire il dispositivo in uno stato di basso consumo a causa di memoria insufficiente per allocare un IRP di alimentazione, deve annullare immediatamente l'IRP inattivo e uscire immediatamente. L'IRP inattivo non viene completato fino a quando non viene restituita la routine di callback. Pertanto, la routine di callback non deve bloccare l'attesa del completamento dell'IRP inattivo annullato. |
| Il dispositivo è già in stato basso consumo. | Se il dispositivo è già in stato di bassa potenza, il driver client può inviare un IRP D0 . Lo stack di driver USB completa l'IRP della richiesta inattiva con STATUS_SUCCESS. In alternativa, il driver può annullare l'IRP inattivo, attendere che lo stack di driver USB completi l'IRP inattivo e quindi inviare un IRP D0 . |
Routine di completamento IRP per richieste USB inattive
In molti casi, un autista di autobus potrebbe invocare la routine di completamento IRP per una richiesta inattiva del conducente. In questo caso, un driver client deve rilevare il motivo per cui il conducente dell'autobus ha completato l'IRP. Il codice di stato restituito può fornire queste informazioni. Se il codice di stato non è STATUS_POWER_STATE_INVALID, il driver deve inserire il dispositivo in D0 se il dispositivo non è già in D0. Se il dispositivo è ancora inattivo, il driver può inviare un altro IRP di richiesta idle.
Annotazioni
La routine di completamento IRP della richiesta inattiva non deve bloccare l'attesa del completamento di una richiesta di alimentazione D0 . La routine di completamento può essere chiamata nel contesto di un IRP di alimentazione dal driver hub e il blocco su un altro IRP di alimentazione nella routine di completamento può causare un deadlock.
L'elenco seguente indica come una routine di completamento per una richiesta inattiva deve interpretare alcuni codici di stato comuni:
| Codice di stato | Descrizione |
|---|---|
| STATO_RIUSCITO | Indica che il dispositivo non deve più essere sospeso. Tuttavia, i driver devono verificare che i dispositivi siano accesi e inserirli in D0 se non sono già in D0. |
| STATO_ANNULLATO | Il conducente del bus completa l'IRP di richiesta inattiva con STATUS_CANCELLED in una delle circostanze seguenti:
|
| STATO_ALIMENTAZIONE_NON_VALIDO | Indica che il driver di dispositivo ha richiesto uno stato di alimentazione D3 per il dispositivo. Quando si verifica questa richiesta, il driver del bus completa tutti gli IRP in sospeso con STATUS_POWER_STATE_INVALID. |
| STATO_DISPOSITIVO_OCCUPATO | Indica che il driver del bus possiede già una richiesta IRP inattiva in sospeso per il dispositivo. Solo un IRP inattivo può essere in sospeso in un dato momento per un determinato dispositivo. L'invio di più richieste IRP inattive per il tempo di inattività è un errore del responsabile della gestione dell'energia. Il programmatore del driver risolve l'errore. |
Nell'esempio di codice seguente viene illustrata un'implementazione di esempio per la routine di completamento della richiesta inattiva.
/*
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;
}
Routine di callback per notifiche di inattività USB
Il conducente del bus (un'istanza del driver hub o il driver padre generico) determina quando è sicuro sospendere i figli del dispositivo. In caso affermativo, chiama la routine di callback di notifica di inattività fornita dal driver client di ciascun figlio.
Il prototipo di funzione per USB_IDLE_CALLBACK è il seguente:
typedef VOID (*USB_IDLE_CALLBACK)(__in PVOID Context);
Un driver di dispositivo deve eseguire le azioni seguenti nella routine di callback delle notifiche inattive:
- Richiedere un IRP_MN_WAIT_WAKE IRP per il dispositivo se il dispositivo deve essere predisposto per la riattivazione remota.
- Annullare tutte le operazioni di I/O e preparare il dispositivo per passare a uno stato di alimentazione inferiore.
- Inserire il dispositivo in uno stato di sospensione WDM chiamando PoRequestPowerIrp con il parametro PowerState impostato sul valore dell'enumeratore PowerDeviceD2 (definito in wdm.h; ntddk.h).
Sia il driver dell'hub, sia il driver padre generico USB (Usbccgp.sys) invocano la routine di callback di notifica inattiva a IRQL = PASSIVE_LEVEL. La routine di callback può quindi bloccarsi mentre attende il completamento della richiesta di modifica dello stato di alimentazione.
La routine di callback viene richiamata solo mentre il sistema è in S0 e il dispositivo è in D0.
Le restrizioni seguenti si applicano alle routine di callback di notifica delle richieste inattive:
- I driver di dispositivo possono avviare una transizione dello stato di alimentazione del dispositivo da D0 a D2 nella routine di callback delle notifiche inattive, ma non è consentita alcuna altra transizione dello stato di alimentazione. In particolare, un driver non deve tentare di modificare il dispositivo in D0 durante l'esecuzione della routine di callback.
- I driver di dispositivo non devono richiedere più di un IRP di alimentazione all'interno della routine di callback delle notifiche inattive.
Preparare i dispositivi per il risveglio nella routine di callback della notifica di inattività.
La routine di callback della notifica di inattività deve determinare se il dispositivo ha ricevuto una richiesta IRP_MN_WAIT_WAKE in sospeso. Se non è in corso alcuna richiesta di IRP_MN_WAIT_WAKE, la routine di callback dovrebbe inviare una richiesta di IRP_MN_WAIT_WAKE prima di sospendere il dispositivo. Per ulteriori informazioni sul meccanismo di attesa e riattivazione, vedere Supporto ai dispositivi con capacità di riattivazione.
Sospensione globale USB
La specifica USB 2.0 definisce la sospensione globale come la sospensione dell'intero bus gestito da un controller host USB cessando tutto il traffico USB sul bus, inclusi i pacchetti di inizio frame. I dispositivi downstream che non sono già sospesi rilevano lo stato inattivo sulla porta upstream e immettono lo stato di sospensione autonomamente. Windows non implementa la sospensione globale in questo modo. Windows sospende sempre in modo selettivo ogni dispositivo USB dietro un controller host USB prima che interrompa tutto il traffico USB sul bus.
Condizioni per la sospensione globale
Il driver dell'hub USB sospende selettivamente qualsiasi hub in cui tutti i dispositivi collegati si trovano nello stato di alimentazione del dispositivo D1, D2 o D3 . L'intero bus entra in sospensione globale una volta sospesi selettivamente tutti gli hub USB. Lo stack di driver USB considera un dispositivo inattivo quando il dispositivo si trova in uno stato di dispositivo WDM D1, D2 o D3.