Condividi tramite


Come selezionare una configurazione per un dispositivo USB

Per selezionare una configurazione per un dispositivo USB, il driver client per il dispositivo deve scegliere almeno una delle configurazioni supportate e specificare le impostazioni alternative di ogni interfaccia da usare. Il driver client crea un pacchetto di queste opzioni in una richiesta di configurazione selezionata e invia la richiesta allo stack di driver USB fornito da Microsoft, in particolare il driver bus USB (PDO dell'hub USB). Il driver del bus USB seleziona ogni interfaccia nella configurazione specificata e configura un canale di comunicazione, o pipe, a ogni endpoint all'interno dell'interfaccia. Al termine della richiesta, il driver client riceve un handle per la configurazione selezionata e gli handle di pipe per gli endpoint definiti nell'impostazione alternativa attiva per ogni interfaccia. Il driver client può quindi usare gli handle ricevuti per modificare le impostazioni di configurazione e inviare richieste di lettura e scrittura di I/O a un endpoint specifico.

Un driver client invia una richiesta di configurazione select in un blocco di richieste USB del tipo URB_FUNCTION_edizione Standard LECT_CONFIGURATION. La procedura descritta in questo argomento descrive come utilizzare la routine USBD_SelectConfigUrbAllocateAndBuild per compilare tale ELEMENTO. La routine alloca la memoria per un ELEMENTO CSV, formatta l'OPZIONE PER una richiesta di configurazione selezionata e restituisce l'indirizzo dell'elemento CSV al driver client.

In alternativa, è possibile allocare unastrutturaae e quindi formattare manualmente o chiamando la macro UsbBuildSelectConfigurationRequest.

Prerequisiti

Passaggio 1: Creare una matrice di strutture USBD_INTERFACE_LIST_ENTRY

  1. Ottenere il numero di interfacce nella configurazione. Queste informazioni sono contenute nel membro bNumInterfaces della struttura USB_CONFIGURATION_DESCRIPTOR .

  2. Creare una matrice di strutture USBD_INTERFACE_LIST_ENTRY. Il numero di elementi nella matrice deve essere maggiore del numero di interfacce. Inizializzare la matrice chiamando RtlZeroMemory.

    Il driver client specifica impostazioni alternative in ogni interfaccia da abilitare, nella matrice di strutture USBD_INTERFACE_LIST_ENTRY.

    • Il membro InterfaceDescriptor di ogni struttura punta al descrittore di interfaccia che contiene l'impostazione alternativa.
    • Il membro Interface di ogni struttura punta a una struttura USBD_INTERFACE_INFORMATION che contiene informazioni sulla pipe nel relativo membro Pipes . Le pipe archivia le informazioni su ogni endpoint definito nell'impostazione alternativa.
  3. Ottenere un descrittore di interfaccia per ogni interfaccia (o la relativa impostazione alternativa) nella configurazione. È possibile ottenere questi descrittori di interfaccia chiamando USBD_ParseConfigurationDescriptorEx.

    Informazioni sui driver di funzione per un dispositivo composito USB:

    Se il dispositivo USB è un dispositivo composito, la configurazione viene selezionata dal driver padre generico USB fornito da Microsoft (Usbccgp.sys). Un driver client, uno dei driver di funzione del dispositivo composito, non può modificare la configurazione, ma il driver può comunque inviare una richiesta di configurazione di selezione tramite Usbccgp.sys.

    Prima di inviare tale richiesta, il driver client deve inviare una richiesta di URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE. In risposta, Usbccgp.sys recupera un descrittore di configurazione parziale che contiene solo descrittori di interfaccia e altri descrittori relativi alla funzione specifica per cui viene caricato il driver client. Il numero di interfacce segnalate nel campo bNumInterfaces di un descrittore di configurazione parziale è minore del numero totale di interfacce definite per l'intero dispositivo composito USB. Inoltre, in un descrittore di configurazione parziale, un descrittore di interfaccia bInterfaceNumber indica il numero di interfaccia effettivo relativo all'intero dispositivo. Ad esempio, Usbccgp.sys potrebbe segnalare un descrittore di configurazione parziale con valore bNumInterfaces pari a 2 e bInterfaceNumber pari a 4 per la prima interfaccia. Si noti che il numero di interfaccia è maggiore del numero di interfacce segnalate.

    Durante l'enumerazione delle interfacce in una configurazione parziale, evitare di cercare interfacce calcolando i numeri di interfaccia in base al numero di interfacce. Nell'esempio precedente, se USBD_ParseConfigurationDescriptorEx viene chiamato in un ciclo che inizia da zero, termina con (bNumInterfaces - 1)e incrementa l'indice dell'interfaccia (specificato nel parametro InterfaceNumber ) in ogni iterazione, la routine non riesce a ottenere l'interfaccia corretta. Assicurarsi invece di cercare tutte le interfacce nel descrittore di configurazione passando -1 in InterfaceNumber. Per informazioni dettagliate sull'implementazione, vedere l'esempio di codice in questa sezione.

    Per informazioni su come Usbccgp.sys gestisce una richiesta di configurazione selezionata inviata da un driver client, vedere Configurazione di Usbccgp.sys per selezionare una configurazione USB non predefinita.

  4. Per ogni elemento (ad eccezione dell'ultimo elemento) nella matrice, impostare il membro InterfaceDescriptor sull'indirizzo di un descrittore di interfaccia. Per il primo elemento della matrice, impostare il membro InterfaceDescriptor sull'indirizzo del descrittore di interfaccia che rappresenta la prima interfaccia nella configurazione. Analogamente per l'n elemento nella matrice, impostare il membro InterfaceDescriptor sull'indirizzo del descrittore di interfaccia che rappresenta l'n interfaccia nella configurazione.

  5. Il membro InterfaceDescriptor dell'ultimo elemento deve essere impostato su NULL.

Passaggio 2: Ottenere un puntatore a un ELEMENTO ODBC allocato dallo stack di driver USB

Chiamare quindi USBD_SelectConfigUrbAllocateAndBuild specificando la configurazione per selezionare e la matrice popolata di strutture USBD_INTERFACE_LIST_ENTRY. La routine esegue le attività seguenti:

  • Crea un ELEMENTO EXPLORER e lo riempie di informazioni sulla configurazione specificata, sulle relative interfacce ed endpoint e imposta il tipo di richiesta su URB_FUNCTION_edizione Standard LECT_CONFIGURATION.

  • All'interno di TALE ELEMENTO, alloca una struttura USBD_INTERFACE_INFORMATION per ogni descrittore di interfaccia specificato dal driver client.

  • Imposta il membro Interface dell'n elemento della matrice di USBD_INTERFACE_LIST_ENTRY fornita dal chiamante sull'indirizzo della struttura di USBD_INTERFACE_INFORMATION corrispondente in JSON.

  • Inizializza InterfaceNumber, AlternateSetting, NumberOfPipes, Pipes[i]. MaximumTransferSize e Pipe[i]. Membri PipeFlags .

    Nota

    In Windows 7 e versioni precedenti, il driver client ha creato un VALORE DI CLUSTER per una richiesta di configurazione selezionata chiamando USBD_CreateConfigurationRequestEx. In Windows 2000 USBD_CreateConfigurationRequestEx inizializza Pipe[i]. MaximumTransferSize per le dimensioni massime di trasferimento predefinite per una singola richiesta di lettura/scrittura DALLS. Il driver client può specificare una dimensione di trasferimento massima diversa in Pipe[i]. MaximumTransferSize. Lo stack USB ignora questo valore in Windows XP, Windows Server 2003 e versioni successive del sistema operativo. Per altre informazioni su MaximumTransferSize, vedere "Impostazione del trasferimento USB e delle dimensioni dei pacchetti" in Allocazione della larghezza di banda USB.

Passaggio 3: Inviare l'istruzione ODBC allo stack di driver USB

Per inviare l'elemento ODBC allo stack di driver USB, il driver client deve inviare una richiesta di controllo I/O IOCTL_INTERNAL_USB_SUBMIT_URB . Per informazioni sull'invio di un'istanza DI FRAMEWORK, vedere How to Submit an ASSEMBLY.

Dopo aver ricevuto l'OPZIONE, lo stack di driver USB riempie il resto dei membri di ogni struttura USBD_INTERFACE_INFORMATION. In particolare, il membro della matrice Pipes viene riempito con informazioni sulle pipe associate agli endpoint dell'interfaccia.

Passaggio 4: Al completamento della richiesta, controllare le strutture USBD_INTERFACE_INFORMATION e l'ISTRUZIONE

Dopo che lo stack di driver USB ha completato l'IRP per la richiesta, lo stack restituisce l'elenco delle impostazioni alternative e le interfacce correlate nella matrice di USBD_INTERFACE_LIST_ENTRY .

  1. Il membro Pipes di ogni struttura USBD_INTERFACE_INFORMATION punta a una matrice di strutture USBD_PIPE_INFORMATION che contiene informazioni sulle pipe associate a ogni endpoint di tale interfaccia specifica. Il driver client può ottenere handle pipe da Pipe[i]. PipeHandle e usarli per inviare richieste di I/O a pipe specifiche. Pipe [i]. Il membro PipeType specifica il tipo di endpoint e il trasferimento supportati da tale pipe.

  2. All'interno del membro Di TipoSelectConfiguration dell'UTILITÀ, lo stack di driver USB restituisce un handle che è possibile usare per selezionare un'impostazione di interfaccia alternativa inviando un'altra impostazione di INTERFACCIA DEL TIPO URB_FUNCTION_EDIZIONE STANDARD LECT_INTERFACE (richiesta di interfaccia select). Per allocare e compilare la struttura DELL'ISTRUZIONE PER tale richiesta, chiamare USBD_SelectInterfaceUrbAllocateAndBuild.

    La richiesta di configurazione di selezione e la richiesta di interfaccia select potrebbero non riuscire se la larghezza di banda non è sufficiente per supportare gli endpoint isocroni, di controllo e di interrupt all'interno delle interfacce abilitate. In tal caso, il driver del bus USB imposta il membro Status dell'intestazione ODBC su USBD_STATUS_NO_BANDWIDTH.

Il codice di esempio seguente illustra come creare una matrice di strutture USBD_INTERFACE_LIST_ENTRY e chiamare USBD_SelectConfigUrbAllocateAndBuild. L'esempio invia la richiesta in modo sincrono chiamando SubmitUrbSync. Per visualizzare l'esempio di codice per SubmitUrbSync, vedere How to Submit an ASSEMBLY (Come inviare un FILE).

/*++

Routine Description:
This helper routine selects the specified configuration.

Arguments:
USBDHandle - USBD handle that is retrieved by the 
client driver in a previous call to the USBD_CreateHandle routine.

ConfigurationDescriptor - Pointer to the configuration
descriptor for the device. The caller receives this pointer
from the URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE request.

Return Value: NT status value
--*/

NTSTATUS SelectConfiguration (PDEVICE_OBJECT DeviceObject,
                              PUSB_CONFIGURATION_DESCRIPTOR ConfigurationDescriptor)
{
    PDEVICE_EXTENSION deviceExtension;
    PIO_STACK_LOCATION nextStack;
    PIRP irp;
    PURB urb = NULL;

    KEVENT    kEvent;
    NTSTATUS ntStatus;    

    PUSBD_INTERFACE_LIST_ENTRY   interfaceList = NULL; 
    PUSB_INTERFACE_DESCRIPTOR    interfaceDescriptor = NULL;
    PUSBD_INTERFACE_INFORMATION  Interface = NULL;
    USBD_PIPE_HANDLE             pipeHandle;

    ULONG                        interfaceIndex;

    PUCHAR StartPosition = (PUCHAR)ConfigurationDescriptor;

    deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;

    // Allocate an array for the list of interfaces
    // The number of elements must be one more than number of interfaces.
    interfaceList = (PUSBD_INTERFACE_LIST_ENTRY)ExAllocatePool (
        NonPagedPool, 
        sizeof(USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    if(!interfaceList)
    {
        //Failed to allocate memory
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    // Initialize the array by setting all members to NULL.
    RtlZeroMemory (interfaceList, sizeof (
        USBD_INTERFACE_LIST_ENTRY) *
        (deviceExtension->NumInterfaces + 1));

    // Enumerate interfaces in the configuration.
    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        interfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
            ConfigurationDescriptor, 
            StartPosition, // StartPosition 
            -1,            // InterfaceNumber
            0,             // AlternateSetting
            -1,            // InterfaceClass
            -1,            // InterfaceSubClass
            -1);           // InterfaceProtocol

        if (!interfaceDescriptor) 
        {
            ntStatus = STATUS_INSUFFICIENT_RESOURCES;
            goto Exit;
        }

        // Set the interface entry
        interfaceList[interfaceIndex].InterfaceDescriptor = interfaceDescriptor;
        interfaceList[interfaceIndex].Interface = NULL;

        // Move the position to the next interface descriptor
        StartPosition = (PUCHAR)interfaceDescriptor + interfaceDescriptor->bLength;

    }

    // Make sure that the InterfaceDescriptor member of the last element to NULL.
    interfaceList[deviceExtension->NumInterfaces].InterfaceDescriptor = NULL;

    // Allocate and build an URB for the select-configuration request.
    ntStatus = USBD_SelectConfigUrbAllocateAndBuild(
        deviceExtension->UsbdHandle, 
        ConfigurationDescriptor, 
        interfaceList,
        &urb);

    if(!NT_SUCCESS(ntStatus)) 
    {
        goto Exit;
    }

    // Allocate the IRP to send the buffer down the USB stack.
    // The IRP will be freed by IO manager.
    irp = IoAllocateIrp((deviceExtension->NextDeviceObject->StackSize)+1, TRUE);  

    if (!irp)
    {
        //Irp could not be allocated.
        ntStatus = STATUS_INSUFFICIENT_RESOURCES;
        goto Exit;
    }

    ntStatus = SubmitUrbSync( 
        deviceExtension->NextDeviceObject, 
        irp, 
        urb, 
        CompletionRoutine);

    // Enumerate the pipes in the interface information array, which is now filled with pipe
    // information.

    for ( interfaceIndex = 0; 
        interfaceIndex < deviceExtension->NumInterfaces; 
        interfaceIndex++) 
    {
        ULONG i;

        Interface = interfaceList[interfaceIndex].Interface;

        for(i=0; i < Interface->NumberOfPipes; i++) 
        {
            pipeHandle = Interface->Pipes[i].PipeHandle;

            if (Interface->Pipes[i].PipeType == UsbdPipeTypeInterrupt)
            {
                deviceExtension->InterruptPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_IN (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkInPipe = pipeHandle;
            }
            if (Interface->Pipes[i].PipeType == UsbdPipeTypeBulk && USB_ENDPOINT_DIRECTION_OUT (Interface->Pipes[i].EndpointAddress))
            {
                deviceExtension->BulkOutPipe = pipeHandle;
            }
        }
    }

Exit:

    if(interfaceList) 
    {
        ExFreePool(interfaceList);
        interfaceList = NULL;
    }

    if (urb)
    {
        USBD_UrbFree( deviceExtension->UsbdHandle, urb); 
    }

    return ntStatus;
}

NTSTATUS CompletionRoutine ( PDEVICE_OBJECT DeviceObject,
                            PIRP           Irp,
                            PVOID          Context)
{
    PKEVENT kevent;

    kevent = (PKEVENT) Context;

    if (Irp->PendingReturned == TRUE)
    {
        KeSetEvent(kevent, IO_NO_INCREMENT, FALSE);
    }

    KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "Select-configuration request completed. \n" ));

    return STATUS_MORE_PROCESSING_REQUIRED;
}

Disabilitazione di una configurazione per un dispositivo USB

Per disabilitare un dispositivo USB, creare e inviare una richiesta di configurazione select con un descrittore di configurazione NULL. Per quel tipo di richiesta, è possibile riutilizzare l'ELEMENTO EXPLORER creato per la richiesta che ha selezionato una configurazione nel dispositivo. In alternativa, è possibile allocare un nuovo ELEMENTO PST chiamando USBD_UrbAllocate. Prima di inviare la richiesta, è necessario formattare l'OPZIONE USANDO la macro UsbBuildSelectConfigurationRequest , come illustrato nel codice di esempio seguente.

URB Urb;
UsbBuildSelectConfigurationRequest(
  &Urb,
  sizeof(_URB_SELECT_CONFIGURATION),
  NULL
);