Sospensione selettiva USB

Nota

Questo articolo è destinato agli sviluppatori di driver di dispositivo. Se si riscontrano difficoltà con un dispositivo USB, vedere Risolvere i problemi comuni di USB

La funzionalità di sospensione selettiva USB consente al driver hub di sospendere una singola porta senza influire sul funzionamento delle altre porte nell'hub. La sospensione selettiva dei dispositivi USB è particolarmente utile nei computer portatili perché consente di risparmiare energia a batteria. Molti dispositivi, ad esempio lettori di impronte digitali e altri tipi di scanner biometrici, richiedono solo l'alimentazione intermittente. La sospensione di tali dispositivi, quando il dispositivo non è in uso, riduce il consumo energetico complessivo. Inoltre, qualsiasi dispositivo non sospeso in modo selettivo potrebbe impedire al controller host USB di disabilitare la pianificazione del trasferimento, che risiede nella memoria di sistema. I trasferimenti DMA (Direct Memory Access) dal controller host all'utilità di pianificazione possono impedire ai processori del sistema di entrare in stati di sospensione più profondi, ad esempio C3.

Esistono due meccanismi diversi per sospendere in modo selettivo un dispositivo USB: IP di richiesta inattiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) e i runtime di integrazione di alimentazione (IRP_MN_SET_POWER). Il meccanismo da usare dipende dal sistema operativo e dal tipo di dispositivo: composito o non composito.

Selezione di un meccanismo di sospensione selettiva

I driver client, per un'interfaccia in un dispositivo composito, che abilitano l'interfaccia per la riattivazione remota con un IRP di riattivazione di attesa (IRP_MN_WAIT_WAKE), devono usare il meccanismo IRP di richiesta inattiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) per sospendere in modo selettivo un dispositivo.

Per informazioni sulla riattivazione remota, vedere:

La versione del sistema operativo Windows determina il modo in cui i driver per i dispositivi non compositi abilitano la sospensione selettiva.

  • Windows XP: in Windows XP tutti i driver client devono usare i provider di integrazione delle richieste inattive (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) per spegnere i dispositivi. I driver client non devono usare i provider di integrazione di alimentazione WDM per sospendere in modo selettivo i dispositivi. In questo modo si impedisce la sospensione selettiva di altri dispositivi.
  • Windows Vista e versioni successive di Windows: i writer di driver hanno più opzioni per spegnere i dispositivi in Windows Vista e nelle versioni successive di Windows. Anche se Windows Vista supporta il meccanismo IRP delle richieste inattive di Windows, i driver non sono necessari per usarlo.

La tabella seguente illustra gli scenari che richiedono l'uso dell'IRP della richiesta inattiva e quelli che possono usare un IRP di alimentazione WDM per sospendere un dispositivo USB:

Versione di Windows Funzione su dispositivo composito, armato per la riattivazione Funzione su dispositivo composito, non armato per la riattivazione Dispositivo USB a interfaccia singola
Windows 7 Usare L'IRP della richiesta inattiva Usare WDM power IRP Usare WDM power IRP
Windows Server 2008 Usare L'IRP della richiesta inattiva Usare WDM power IRP Usare WDM power IRP
Windows Vista Usare L'IRP della richiesta inattiva Usare WDM power IRP Usare WDM power IRP
Windows Server 2003 Usare L'IRP della richiesta inattiva Usare L'IRP della richiesta inattiva Usare L'IRP della richiesta inattiva
Windows XP Usare L'IRP della richiesta inattiva Usare L'IRP della richiesta inattiva Usare L'IRP della richiesta inattiva

Questa sezione illustra il meccanismo di sospensione selettiva di Windows.

Invio di un IRP di richiesta inattiva USB

Quando un dispositivo diventa inattivo, il driver del client informa il driver del bus inviando un IRP di richiesta inattiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). Dopo che il driver del bus determina che è sicuro inserire il dispositivo in uno stato di bassa potenza, chiama la routine di callback che il driver del dispositivo client ha passato lo stack con l'IRP della 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 I/OP USB. Può quindi inviare una richiesta di IRP_MN_SET_POWER per modificare lo stato di alimentazione del dispositivo WDM in D2. La routine di callback deve attendere il completamento della richiesta D2 prima della restituzione. Per altre informazioni sulla routine di callback di notifica inattiva, vedere "Routine di callback delle notifiche di inattività USB".

Il driver del bus non completa l'IRP della richiesta inattiva dopo aver chiamato la routine di callback delle notifiche inattive. Al contrario, il driver del bus mantiene l'IRP di richiesta inattiva in sospeso fino a quando non viene soddisfatta una delle condizioni seguenti:

  • Viene ricevuto un IRP_MN_SUPRISE_REMOVAL o un IRP_MN_REMOVE_DEVICE IRP . Quando uno di questi provider di integrazione viene ricevuto, l'IRP della richiesta inattiva viene completato con STATUS_CANCELLED.
  • Il driver del bus riceve una richiesta di inserimento del dispositivo in uno stato di alimentazione funzionante (D0). Al momento della ricezione di questo driver del bus di richiesta, l'IRP della richiesta inattiva in sospeso viene completato con STATUS_SUCCESS.

Le restrizioni seguenti si applicano all'uso dei provider di integrazione delle richieste inattive:

  • I driver devono trovarsi nello stato di alimentazione del dispositivo D0 quando si invia un IRP di richiesta inattiva.
  • I driver devono inviare un solo IRP di richiesta inattiva per ogni stack di dispositivi.

Il codice di esempio WDM seguente illustra i passaggi impiegato da un driver di dispositivo per inviare un IRP di richiesta inattiva USB. Il controllo degli errori è stato omesso nell'esempio di codice seguente.

  1. 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);
    
  2. Allocare e inizializzare la struttura delle 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;
    
  3. Impostare 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 "Usb Idle Request IRP Completion Routine".

    IoSetCompletionRoutine (irp,
        IdleNotificationRequestComplete,
        DeviceContext,
        TRUE,
        TRUE,
        TRUE);
    
  4. Archiviare la richiesta inattiva nell'estensione del dispositivo.

    deviceExtension->PendingIdleIrp = irp;
    
    
  5. Inviare la richiesta inattiva 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 un IRP di richiesta inattiva che è stato inviato al driver del bus. Questo problema può verificarsi se il dispositivo viene rimosso, diventa attivo dopo l'inattività e l'invio della richiesta inattiva o se l'intero sistema passa a uno stato di alimentazione del sistema inferiore.

Il driver client annulla l'IRP inattivo chiamando IoCancelIrp. La tabella seguente descrive tre scenari per l'annullamento di un IRP inattivo e specifica l'azione che il driver deve eseguire:

Scenario Meccanismo di annullamento delle richieste inattive
Il driver client ha annullato l'IRP inattivo e lo stack di driver USB non ha chiamato la "routine di callback delle notifiche di inattività USB". Lo stack di driver USB completa l'IRP inattivo. Poiché il dispositivo non ha mai lasciato D0, il driver non modifica lo stato del dispositivo.
Il driver client ha annullato l'IRP inattivo, lo stack di driver USB ha chiamato la routine di callback delle notifiche di inattività USB e non è ancora stato restituito. È 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 il completamento dello stack di driver USB per completare l'IRP inattivo e quindi inviare il D0 IRP.

Se la routine di callback non è in grado di inserire il dispositivo in uno stato di bassa potenza a causa di memoria insufficiente per allocare un IRP di alimentazione, l'IRP inattivo deve essere annullato immediatamente. L'IRP inattivo non verrà 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 di alimentazione insufficiente. Se il dispositivo è già in stato di alimentazione ridotta, 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 della richiesta di inattività USB

In molti casi, un autista del bus potrebbe chiamare la routine di completamento IRP di richiesta inattiva di un conducente. In questo caso, un driver client deve rilevare il motivo per cui l'autista 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'altra richiesta inattiva.

Nota

La routine di completamento di 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 dell'hub e il blocco su un'altra 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
STATUS_SUCCESS Indica che il dispositivo non deve più essere sospeso. Tuttavia, i driver devono verificare che i dispositivi siano alimentati e li inseriscano in D0 se non sono già in D0.
STATUS_CANCELLED Il driver del bus completa l'IRP di richiesta inattiva con STATUS_CANCELLED in una delle circostanze seguenti:
  • Il driver del dispositivo ha annullato l'IRP.
  • È necessaria una modifica dello stato di alimentazione del sistema.
  • In Windows XP il driver del dispositivo per uno dei dispositivi USB connessi non è riuscito a inserire il dispositivo in D2 durante l'esecuzione della routine di callback delle richieste inattive. Di conseguenza, il driver del bus ha completato tutti gli IRP di richiesta inattiva in sospeso.
STATUS_POWER_STATE_INVALID Indica che il driver del dispositivo ha richiesto uno stato di alimentazione D3 per il dispositivo. Quando si verifica questo problema, il driver del bus completa tutti gli IRP in sospeso con STATUS_POWER_STATE_INVALID.
STATUS_DEVICE_BUSY Indica che il driver del bus contiene già un'IRP richiesta inattiva in sospeso per il dispositivo. Un solo IRP inattivo può essere in sospeso alla volta per un determinato dispositivo. L'invio di più IRP di richiesta inattiva è un errore nella parte del proprietario di power policy e deve essere risolto dal writer del driver.

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 di notifica inattiva USB

Il driver 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 inattiva fornita dal driver client di ogni 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 di notifica inattiva:

  • Richiedere un IRP_MN_WAIT_WAKE IRP per il dispositivo se il dispositivo deve essere armato 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). In Windows XP un driver non deve inserire il dispositivo in PowerDeviceD3, anche se il dispositivo non è armato per la riattivazione remota.

In Windows XP, un driver deve basarsi su una routine di callback di notifica inattiva per sospendere in modo selettivo un dispositivo. Se un driver in esecuzione in Windows XP inserisce un dispositivo in uno stato di alimentazione inferiore direttamente senza usare una routine di callback di notifica inattiva, questo potrebbe impedire ad altri dispositivi nell'albero dei dispositivi USB di sospendere.

Sia il driver dell'hub che il driver padre generico USB (Usbccgp.sys) chiamano la routine di callback di notifica inattiva in IRQL = PASSIVE_LEVEL. Ciò consente alla routine di callback di bloccare 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 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 di notifica inattiva, 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 di notifica inattiva.

Arming devices for wakeup nella routine di callback di notifica inattiva

La routine di callback di notifica inattiva deve determinare se il dispositivo ha una richiesta IRP_MN_WAIT_WAKE in sospeso. Se non è in sospeso alcuna richiesta di IRP_MN_WAIT_WAKE, la routine di callback deve inviare una richiesta di IRP_MN_WAIT_WAKE prima di sospendere il dispositivo. Per altre informazioni sul meccanismo di riattivazione di attesa, vedere Supporto dei dispositivi con funzionalità di riattivazione.

Sospensione globale USB

La specifica USB 2.0 definisce la sospensione globale come sospensione dell'intero bus dietro un controller host USB cesing tutto il traffico USB sul bus, inclusi i pacchetti start-of-frame. I dispositivi downstream che non sono già sospesi rilevano lo stato inattivo sulla porta upstream e immettono lo stato di sospensione in modo autonomo. Windows non implementa la sospensione globale in questo modo. Windows sospende sempre in modo selettivo ogni dispositivo USB dietro un controller host USB prima di terminare tutto il traffico USB sul bus.

Condizioni per la sospensione globale in Windows 7

Windows 7 è più aggressivo sulla sospensione selettiva degli hub USB rispetto a Windows Vista. Il driver dell'hub USB di Windows 7 sospende in modo selettivo 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 dopo la sospensione selettiva di tutti gli hub USB. Lo stack di driver USB di Windows 7 considera un dispositivo inattiva ogni volta che il dispositivo si trova in uno stato del dispositivo WDM D1, D2 o D3.

Condizioni per la sospensione globale in Windows Vista

I requisiti per eseguire una sospensione globale sono più flessibili in Windows Vista rispetto a Windows XP.

In particolare, lo stack USB considera un dispositivo inattiva in Windows Vista ogni volta che il dispositivo si trova in uno stato del dispositivo WDM di D1, D2 o D3.

Il diagramma seguente illustra uno scenario che potrebbe verificarsi in Windows Vista.

Diagramma che illustra una sospensione globale in Windows Vista.

Questo diagramma illustra una situazione simile a quella illustrata nella sezione "Condizioni per la sospensione globale in Windows XP". Tuttavia, in questo caso Device 3 viene qualificato come dispositivo inattiva. Poiché tutti i dispositivi sono inattive, il driver del bus è in grado di chiamare le routine di callback di notifica inattive associate agli IRP di richiesta inattiva in sospeso. Ogni driver sospende il dispositivo e il driver del bus sospende il controller host USB non appena è sicuro farlo.

In Windows Vista tutti i dispositivi USB non hub devono trovarsi in D1, D2 o D3 prima che venga avviata la sospensione globale, in cui tutti gli hub USB, incluso l'hub radice, vengono sospesi. Ciò significa che qualsiasi driver client USB che non supporta la sospensione selettiva, impedisce all'autobus di entrare in sospensione globale.

Condizioni per la sospensione globale in Windows XP

Per massimizzare il risparmio di energia in Windows XP, è importante che ogni driver di dispositivo usi irP di richiesta inattiva per sospendere il dispositivo. Se un driver sospende il dispositivo con una richiesta IRP_MN_SET_POWER anziché un'IRP richiesta inattiva, potrebbe impedire ad altri dispositivi di sospendere.

Il diagramma seguente illustra uno scenario che potrebbe verificarsi in Windows XP.

Diagramma che illustra una sospensione globale in Windows XP.

In questa figura il dispositivo 3 è in stato di alimentazione D3 e non ha una richiesta inattiva in sospeso. Il dispositivo 3 non è qualificato come dispositivo inattivo a scopo di una sospensione globale in Windows XP, perché non ha una richiesta inattiva in sospeso con il relativo padre. Ciò impedisce al driver del bus di chiamare le routine di callback delle richieste inattive associate ai driver di altri dispositivi nell'albero.

Abilitazione della sospensione selettiva

La sospensione selettiva è disabilitata per le versioni di aggiornamento di Microsoft Windows XP. È abilitato per le installazioni pulite di Windows XP, Windows Vista e versioni successive di Windows.

Per abilitare il supporto selettivo per un determinato hub radice e i relativi dispositivi figlio, selezionare la casella di controllo nella scheda Power Management per l'hub radice USB in Gestione dispositivi.

In alternativa, è possibile abilitare o disabilitare la sospensione selettiva impostando il valore di HcDisableSelectiveSuspend sotto la chiave software del driver della porta USB. Il valore 1 disabilita la sospensione selettiva. Un valore pari a 0 abilita la sospensione selettiva.

Ad esempio, le righe seguenti in Usbport.inf disabilitano la sospensione selettiva per un controller Hydra OHCI:

[OHCI_NOSS.AddReg.NT]
HKR,,"HcDisableSelectiveSuspend",0x00010001,1

I driver client non devono provare a determinare se la sospensione selettiva è abilitata prima di inviare richieste inattive. Devono inviare richieste inattive ogni volta che il dispositivo è inattiva. Se la richiesta inattiva ha esito negativo, il driver client deve reimpostare il timer inattiva e riprovare.