Condividi tramite


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 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. 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 alimentazione intermittente. La sospensione di tali dispositivi, quando il dispositivo non è in uso, riduce il consumo energetico complessivo. Più importante, 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 immettere stati di sospensione più profondi, ad esempio C3.

Esistono due diversi meccanismi per sospendere in modo selettivo un dispositivo USB: i runtime di integrazione delle richieste inattive (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 consentono l'interfaccia per la riattivazione remota con un IRP di riattivazione dell'attesa (IRP_MN_WAIT_WAKE), devono usare il meccanismo IRP (idle request IRP) (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 irP di richiesta inattiva (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) per spegnere i dispositivi. I driver client non devono usare i runtime 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 di richiesta inattiva 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 Windows Funzione sul dispositivo composito, armato per la riattivazione Funzione sul 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 client informa l'autista del bus inviando un IRP (IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION) di richiesta inattiva. Dopo che il driver del bus determina che è sicuro inserire il dispositivo in uno stato di basso consumo, 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 IRP di I/O 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 delle notifiche inattive, vedere "Routine di callback delle notifiche inattive USB".

Il driver del bus non completa l'IRP della richiesta inattiva dopo aver chiamato la routine di callback di notifica inattiva. Al contrario, il driver del bus contiene 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 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 conducente del bus riceve una richiesta di inserire il 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 runtime 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 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;
    
  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 conducente del bus. Ciò 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:

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 il 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 di notifica inattiva 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 che lo stack di driver USB completi l'IRP inattivo e quindi invii L'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 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 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 della richiesta inattiva 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 altro IRP di richiesta inattiva.

Nota

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
STATUS_SUCCESS 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.
STATUS_CANCELLED Il conducente del bus completa l'IRP di richiesta inattiva con STATUS_CANCELLED in una delle circostanze seguenti:
  • Il driver di dispositivo ha annullato l'IRP.
  • È necessaria una modifica dello stato di alimentazione del sistema.
  • In Windows XP, il driver di 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 inattive in sospeso.
STATUS_POWER_STATE_INVALID Indica che il driver di dispositivo ha richiesto uno stato di alimentazione D3 per il dispositivo. In questo caso, 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 di 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 da parte del proprietario dei criteri di alimentazione 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 delle notifiche inattive 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 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 delle notifiche inattive:

  • 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 utilizzare una routine di callback di notifica inattiva, ciò potrebbe impedire la sospensione di altri dispositivi nell'albero dei dispositivi USB.

Sia il driver hub che il driver padre generico USB (Usbccgp.sys) chiamano la routine di callback di notifica inattiva in IRQL = PASSIVE_LEVEL. In questo modo la routine di callback viene bloccata 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.

Arming devices for wakeup in the idle notification callback routine

La routine di callback di notifica inattiva deve determinare se il dispositivo ha una richiesta di 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 dell'attesa, vedere Supporto di 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 cessing 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 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 in Windows 7

Windows 7 è più aggressivo per sospendere in modo selettivo gli 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 una volta sospesi selettivi 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 D1, D2 o D3.

Il diagramma seguente illustra uno scenario che può 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 il dispositivo 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 ai runtime di integrazione delle richieste inattive 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 dell'avvio della 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 al bus di entrare in sospensione globale.

Condizioni per la sospensione globale in Windows XP

Per ottimizzare 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 di IRP_MN_SET_POWER anziché un IRP di richiesta inattiva, potrebbe impedire la sospensione di altri dispositivi.

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

Diagramma che illustra una sospensione globale in Windows XP.

In questa figura, il dispositivo 3 è nello stato di alimentazione D3 e non ha una richiesta inattiva IRP in sospeso. Il dispositivo 3 non è idoneo come dispositivo inattivo ai fini di una sospensione globale in Windows XP, perché non ha un IRP di richiesta inattiva in sospeso con il relativo elemento padre. Ciò impedisce al conducente 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 installazioni pulite di Windows XP, Windows Vista e versioni successive di Windows.

Per abilitare il supporto della sospensione selettiva per un determinato hub radice e i relativi dispositivi figlio, selezionare la casella di controllo nella scheda Risparmio energia 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. Il valore 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 non riesce, il driver client deve reimpostare il timer di inattività e riprovare.