Condividi tramite


Come implementare la sospensione della funzione in un driver composito

Questo articolo offre una panoramica delle funzionalità di sospensione delle funzioni e delle funzionalità di riattivazione remota per i dispositivi usb (Universal Serial Bus) 3.0 (dispositivi compositi). In questo articolo verranno fornite informazioni sull'implementazione di tali funzionalità in un driver che controlla un dispositivo composito. L'articolo si applica ai driver compositi che sostituiscono Usbccgp.sys.

La specifica Universal Serial Bus (USB) 3.0 definisce una nuova funzionalità denominata sospensione della funzione. La funzionalità consente a una singola funzione di un dispositivo composito di immettere uno stato di bassa potenza, indipendentemente da altre funzioni. Prendere in considerazione un dispositivo composito che definisce una funzione per la tastiera e un'altra funzione per il mouse. L'utente mantiene la funzione della tastiera nello stato di lavoro, ma non sposta il mouse per un periodo di tempo. Il driver client per il mouse può rilevare lo stato inattiva della funzione e inviare la funzione per sospendere lo stato mentre la funzione della tastiera rimane in stato di lavoro.

L'intero dispositivo può passare allo stato di sospensione indipendentemente dallo stato di alimentazione di qualsiasi funzione all'interno del dispositivo. Se una determinata funzione e l'intero dispositivo entrano nello stato di sospensione, lo stato di sospensione della funzione viene mantenuto mentre il dispositivo è in stato di sospensione e durante i processi di sospensione e uscita del dispositivo.

Analogamente a una funzionalità di riattivazione remota del dispositivo USB 2.0 (vedere Riattivazione remota dei dispositivi USB), una singola funzione in un dispositivo composito USB 3.0 può riattivarsi da uno stato di bassa potenza senza influire sugli stati di alimentazione di altre funzioni. Questa funzionalità viene chiamata riattivazione remota della funzione. La funzionalità è abilitata in modo esplicito dall'host inviando una richiesta di protocollo che imposta i bit di riattivazione remota nel firmware del dispositivo. Questo processo viene chiamato arming della funzione per la riattivazione remota. Per informazioni sui bit correlati alla riattivazione remota, vedere la figura 9-6 nella specifica USB ufficiale.

Se una funzione è armata per la riattivazione remota, la funzione (quando in stato di sospensione) mantiene una potenza sufficiente per generare un segnale di riattivazione quando si verifica un evento utente nel dispositivo fisico. A seguito di tale ripresa del segnale, il driver client può quindi uscire dallo stato di sospensione della funzione associata. Nell'esempio per la funzione del mouse nel dispositivo composito, quando l'utente attiva lo stato inattiva del mouse, la funzione mouse invia un segnale di ripresa all'host. Nell'host lo stack di driver USB rileva la funzione che si sveglia e propaga la notifica al driver client della funzione corrispondente. Il driver client può quindi riattivare la funzione e immettere lo stato di lavoro.

Per il driver client, i passaggi per l'invio di una funzione allo stato di sospensione e riattivazione della funzione sono simili a un driver di dispositivo a funzione singola che invia l'intero dispositivo allo stato di sospensione. La procedura seguente riepiloga questi passaggi.

  1. Rilevare quando la funzione associata è inattiva.
  2. Inviare un pacchetto di richiesta I/O inattivo (IRP).
  3. Inviare una richiesta per armerne la funzione per la riattivazione remota inviando un pacchetto di richiesta di I/O di attesa (IRP).
  4. Eseguire la transizione della funzione a uno stato di bassa potenza inviando IRP di alimentazione Dx (D2 o D3).

Per altre informazioni sui passaggi precedenti, vedere "Invio di un'IRP richiesta di inattività USB" in Sospensione selettiva USB. Un driver composito crea un oggetto dispositivo fisico (PDO) per ogni funzione nel dispositivo composito e gestisce le richieste di alimentazione inviate dal driver client (fdO dello stack di dispositivi funzione). Per consentire a un driver client di immettere e uscire correttamente lo stato di sospensione per la sua funzione, il driver composito deve supportare le funzionalità di sospensione della funzione e riattivazione remota e elaborare le richieste di alimentazione ricevute.

In Windows 8 lo stack di driver USB per i dispositivi USB 3.0 supporta queste funzionalità. Inoltre, l'implementazione della sospensione della funzione e della funzione remota è stata aggiunta al driver padre generico (Usbccgp.sys) fornito da Microsoft, ovvero il driver composito predefinito di Windows. Se si scrive un driver composito personalizzato, il driver deve gestire le richieste correlate alle richieste di sospensione e riattivazione remota, in base alla procedura seguente.

Passaggio 1: Determinare se lo stack di driver USB supporta la sospensione della funzione

Nella routine start-device (IRP_MN_START_DEVICE) del driver composito seguire questa procedura:

  1. Chiamare la routine USBD_QueryUsbCapability per determinare se lo stack di driver USB sottostante supporta la funzionalità di sospensione della funzione. La chiamata richiede un handle USBD valido ottenuto nella chiamata precedente alla routine di USBD_CreateHandle .

Una chiamata riuscita a USBD_QueryUsbCapability determina se lo stack di driver USB sottostante supporta la sospensione della funzione. La chiamata può restituire un codice di errore che indica che lo stack di driver USB non supporta la sospensione della funzione o il dispositivo collegato non è un dispositivo USB 3.0 multi-funzione.

  1. Se la chiamata USBD_QueryUsbCapability indica che la sospensione della funzione è supportata, registrare il dispositivo composito con lo stack di driver USB sottostante. Per registrare il dispositivo composito, è necessario inviare una richiesta di controllo I /O IOCTL_INTERNAL_USB_REGISTER_COMPOSITE_DEVICE. Per altre informazioni su questa richiesta, vedere Come registrare un dispositivo composito.

La richiesta di registrazione usa la struttura REGISTER_COMPOSITE_DEVICE per specificare le informazioni sul driver composito. Assicurarsi di impostare CapabilityFunctionSuspend su 1 per indicare che il driver composito supporta la sospensione della funzione.

Per esempio di codice che illustra come determinare se lo stack di driver USB supporta la sospensione della funzione, vedere USBD_QueryUsbCapability.

Passaggio 2: Gestire l'IRP inattivo

Il driver client può inviare un'IRP inattiva (vedere IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION). La richiesta viene inviata dopo che il driver client ha rilevato uno stato inattiva per la funzione. L'IRP contiene un puntatore alla routine di completamento del callback (denominato callback inattivo) implementata dal driver client. All'interno del callback inattivo, il client esegue attività, ad esempio annullando i trasferimenti di I/O in sospeso, prima di inviare la funzione per sospendere lo stato.

Nota

Il meccanismo IRP inattivo è facoltativo per i driver client di dispositivi USB 3.0. Tuttavia, la maggior parte dei driver client viene scritta per supportare entrambi i dispositivi USB 2.0 e USB 3.0. Per supportare i dispositivi USB 2.0, il driver deve inviare l'IRP inattivo, perché il driver composito si basa su tale IRP per tenere traccia dello stato di alimentazione di ogni funzione. Se tutte le funzioni sono inattive, il driver composito invia l'intero dispositivo allo stato di sospensione.

Dopo aver ricevuto l'IRP inattivo dal driver client, il driver composito deve richiamare immediatamente il callback inattivo per notificare al driver client che il driver client può inviare la funzione per sospendere lo stato.

Passaggio 3: Inviare una richiesta per la notifica di riattivazione remota

Il driver client può inviare una richiesta per armerne la funzione per la riattivazione remota inviando un IRP_MJ_POWER IRP con codice di funzione secondario impostato su IRP_MN_WAIT_WAKE (IRP di attesa). Il driver client invia questa richiesta solo se il driver vuole immettere lo stato di lavoro come risultato di un evento utente.

Al momento della ricezione dell'IRP di attesa, il driver composito deve inviare la richiesta di controllo I/O IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION allo stack di driver USB. La richiesta consente allo stack di driver USB di notificare al driver composito quando lo stack riceve la notifica sul segnale di ripresa. Il IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION usa la struttura REQUEST_REMOTE_WAKE_NOTIFICATION per specificare i parametri della richiesta. Uno dei valori che il driver composito deve specificare è l'handle di funzione per la funzione armata per la riattivazione remota. Il driver composito ottenuto che gestisce in una richiesta precedente per registrare il dispositivo composito con lo stack di driver USB. Per altre informazioni sulle richieste di registrazione dei driver compositi, vedere Come registrare un dispositivo composito.

Nell'IRP per la richiesta, il driver composito fornisce un puntatore a una routine di completamento (riattivazione remota) implementata dal driver composito.

Il codice di esempio seguente illustra come inviare una richiesta di riattivazione remota.

/*++

Description:
    This routine sends a IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION request
    to the USB driver stack. The IOCTL is completed by the USB driver stack
    when the function wakes up from sleep.

    Parameters:
    parentFdoExt: The device context associated with the FDO for the
    composite driver.

    functionPdoExt: The device context associated with the PDO (created by
    the composite driver) for the client driver.
--*/

VOID
SendRequestForRemoteWakeNotification(
    __inout PPARENT_FDO_EXT parentFdoExt,
    __inout PFUNCTION_PDO_EXT functionPdoExt
)

{
    PIRP                                irp;
    REQUEST_REMOTE_WAKE_NOTIFICATION    remoteWake;
    PIO_STACK_LOCATION                  nextStack;
    NTSTATUS                            status;

    // Allocate an IRP
    irp =  IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (irp)
    {

        //Initialize the USBDEVICE_REMOTE_WAKE_NOTIFICATION structure
        remoteWake.Version = 0;
        remoteWake.Size = sizeof(REQUEST_REMOTE_WAKE_NOTIFICATION);
        remoteWake.UsbdFunctionHandle = functionPdoExt->functionHandle;
        remoteWake.Interface = functionPdoExt->baseInterfaceNumber;

        nextStack = IoGetNextIrpStackLocation(irp);

        nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
        nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION;

        nextStack->Parameters.Others.Argument1 = &remoteWake;

        // Caller's completion routine will free the IRP when it completes.

        SetCompletionRoutine(functionPdoExt->debugLog,
                             parentFdoExt->fdo,
                             irp,
                             CompletionRemoteWakeNotication,
                             (PVOID)functionPdoExt,
                             TRUE, TRUE, TRUE);

        // Pass the IRP
        IoCallDriver(parentFdoExt->topDevObj, irp);

    }

    return;
}

La richiesta di IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION viene completata dallo stack di driver USB durante il processo di riattivazione quando riceve una notifica sul segnale di ripresa. Durante questo periodo, lo stack di driver USB richiama anche la routine di completamento della riattivazione remota.

Il driver composito deve mantenere l'IRP di attesa in sospeso e accodarlo per l'elaborazione successiva. Il driver composito deve completare l'IRP quando la routine di completamento della riattivazione remota del driver viene richiamata dallo stack di driver USB.

Passaggio 4: Inviare una richiesta a arme la funzione per la riattivazione remota

Per inviare la funzione a uno stato a bassa potenza, il driver client invia un IRP_MN_SET_POWER IRP con la richiesta di modificare lo stato di alimentazione del dispositivo Windows Driver Model (WDM) su D2 o D3. In genere, il driver client invia IRP D2 se il driver ha inviato un'IRP di attesa in precedenza per richiedere la riattivazione remota. In caso contrario, il driver client invia IRP D3 .

Al momento della ricezione dell'IRP D2 , il driver composito deve prima determinare se un'IRP di attesa è in sospeso da una richiesta precedente inviata dal driver client. Se l'IRP è in sospeso, il driver composito deve armere la funzione per la riattivazione remota. A tale scopo, il driver composito deve inviare una richiesta di controllo SET_FEATURE alla prima interfaccia della funzione, per consentire al dispositivo di inviare un segnale di ripresa. Per inviare la richiesta di controllo, allocare una struttura URB chiamando la routine di USBD_UrbAllocate e chiamare la macroUsbBuildFeatureRequest per formattare l'ISTANZA di UN SET_FEATURE richiesta. Nella chiamata specificare URB_FUNCTION_SET_FEATURE_TO_INTERFACE come codice dell'operazione e il USB_FEATURE_FUNCTION_SUSPEND come selettore di funzionalità. Nel parametro Index impostare Bit 1 del byte più significativo. Tale valore viene copiato nel campo wIndex nel pacchetto di installazione del trasferimento.

Nell'esempio seguente viene illustrato come inviare una richiesta di controllo SET_FEATURE.

/*++

Routine Description:

Sends a SET_FEATURE for REMOTE_WAKEUP to the device using a standard control request.

Parameters:
parentFdoExt: The device context associated with the FDO for the
composite driver.

functionPdoExt: The device context associated with the PDO (created by
the composite driver) for the client driver.

Returns:

NTSTATUS code.

--*/
VOID
    NTSTATUS SendSetFeatureControlRequestToSuspend(
    __inout PPARENT_FDO_EXT parentFdoExt,
    __inout PFUNCTION_PDO_EXT functionPdoExt,
    )

{
    PURB                            urb
    PIRP                            irp;
    PIO_STACK_LOCATION              nextStack;
    NTSTATUS                        status;

    status = USBD_UrbAllocate(parentFdoExt->usbdHandle, &urb);

    if (!NT_SUCCESS(status))
    {
        //USBD_UrbAllocate failed.
        goto Exit;
    }

    //Format the URB structure.
    UsbBuildFeatureRequest (
        urb,
        URB_FUNCTION_SET_FEATURE_TO_INTERFACE, // Operation code
        USB_FEATURE_FUNCTION_SUSPEND,          // feature selector
        functionPdoExt->firstInterface,           // first interface of the function
        NULL);

    irp =  IoAllocateIrp(parentFdoExt->topDevObj->StackSize, FALSE);

    if (!irp)
    {
        // IoAllocateIrp failed.
        status = STATUS_INSUFFICIENT_RESOURCES;

        goto Exit;
    }

    nextStack = IoGetNextIrpStackLocation(irp);

    nextStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

    nextStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;

    //  Attach the URB to the IRP.
    USBD_AssignUrbToIoStackLocation(nextStack, (PURB)urb);

    // Caller's completion routine will free the IRP when it completes.
    SetCompletionRoutine(functionPdoExt->debugLog,
        parentFdoExt->fdo,
        irp,
        CompletionForSuspendControlRequest,
        (PVOID)functionPdoExt,
        TRUE, TRUE, TRUE);


    // Pass the IRP
    IoCallDriver(parentFdoExt->topDevObj, irp);


Exit:
    if (urb)
    {
        USBD_UrbFree( parentFdoExt->usbdHandle, urb);
    }

    return status;

}

Il driver composito invia quindi l'IRP D2 allo stack di driver USB. Se tutte le altre funzioni sono in stato sospeso, lo stack di driver USB sospende la porta modificando determinati registri di porta nel controller.

Commenti

Nell'esempio di funzione del mouse, poiché la funzionalità di riattivazione remota è abilitata (vedere il passaggio 4), la funzione del mouse genera un segnale di ripresa sul cavo upstream del controller host quando l'utente attiva il mouse. Il controller invia quindi una notifica allo stack di driver USB inviando un pacchetto di notifica che contiene informazioni sulla funzione che si è svegliata. Per informazioni sulla notifica di riattivazione delle funzioni, vedere La figura 8-17 nella specifica USB 3.0.

Al momento della ricezione del pacchetto di notifica, lo stack di driver USB completa la richiesta di IOCTL_INTERNAL_USB_REQUEST_REMOTE_WAKE_NOTIFICATION in sospeso (vedere il passaggio 3) e richiama la routine di callback di completamento (riattivazione remota) specificata nella richiesta e implementata dal driver composito. Quando la notifica raggiunge il driver composito, notifica al driver client corrispondente che la funzione ha immesso lo stato di lavoro completando l'IRP di attesa inviato in precedenza dal driver client.

Nella routine di completamento (riattivazione remota) il driver composito deve accodare un elemento di lavoro per completare l'IRP di attesa in sospeso. Per i dispositivi USB 3.0, il driver composito riattiva solo la funzione che invia il segnale di ripresa e lascia altre funzioni in stato sospeso. L'accodamento dell'elemento di lavoro garantisce la compatibilità con l'implementazione esistente per i driver di funzione dei dispositivi USB 2.0. Per informazioni sull'accodamento di un elemento di lavoro, vedere IoQueueWorkItem.

Il thread di lavoro completa l'IRP di attesa e richiama la routine di completamento del driver client. La routine di completamento invia quindi un'IRP D0 per immettere la funzione nello stato di lavoro. Prima di completare l'IRP di attesa, il driver composito deve chiamare PoSetSystemWake per contrassegnare l'IRP di attesa come quella che ha contribuito a svegliare il sistema dallo stato di sospensione. Il power manager registra un evento Di traccia eventi per Windows (ETW) (visualizzabile nel canale di sistema globale) che include informazioni sui dispositivi che hanno svegliato il sistema.