Condividi tramite


Come enumerare le pipe USB

Questo articolo fornisce una panoramica delle pipe USB e descrive i passaggi richiesti da un driver client USB per ottenere handle di pipe dallo stack di driver USB.

Un endpoint USB è un buffer nel dispositivo da cui il driver client invia o riceve i dati. Per inviare o ricevere dati, il driver client invia una richiesta di trasferimento di I/O allo stack di driver USB, che presenta i dati al controller host. Il controller host segue quindi determinati protocolli (a seconda del tipo di endpoint: bulk, interrupt o isochronous) per compilare richieste che trasferiscono dati da o verso il dispositivo. Tutti i dettagli del trasferimento dei dati vengono astratti dal driver client. Finché il driver client invia una richiesta ben formata, lo stack di driver USB elabora la richiesta e trasferisce i dati al dispositivo.

Durante la configurazione del dispositivo, lo stack di driver USB crea una pipe USB (sul lato host) per ognuno degli endpoint del dispositivo definiti nell'interfaccia USB e la relativa impostazione alternativa attiva. Una pipe USB è un canale di comunicazione tra il controller host e l'endpoint. Per il driver client, una pipe è un'astrazione logica dell'endpoint. Per inviare trasferimenti di dati, il driver deve ottenere l'handle pipe associato all'endpoint che rappresenta la destinazione per il trasferimento. Gli handle di pipe sono necessari anche quando il driver vuole interrompere i trasferimenti o reimpostare la pipe, in caso di condizioni di errore.

Tutti gli attributi di una pipe sono derivati dal descrittore di endpoint associato. Ad esempio, a seconda del tipo di endpoint, lo stack di driver USB assegna un tipo per la pipe. Per un endpoint in blocco, lo stack di driver USB crea una pipe bulk; per un endpoint isochronous, viene creata una pipe isocrona e così via. Un altro attributo importante è la quantità di dati che il controller host può inviare al punto endpoint in una richiesta. A seconda di tale valore, il driver client deve determinare il layout del buffer di trasferimento.

Windows Driver Foundation (WDF) fornisce oggetti di destinazione I/O specializzati in Kernel-Mode Driver Framework (KMDF) e User-Mode Driver Framework (UMDF) che semplificano molte delle attività di configurazione per il driver client. Usando questi oggetti, il driver client può recuperare informazioni sulla configurazione corrente, ad esempio il numero di interfacce, l'impostazione alternativa all'interno di ogni interfaccia e i relativi endpoint. Uno di questi oggetti, denominato oggetto pipe di destinazione, esegue attività correlate all'endpoint. Questo articolo descrive come ottenere informazioni sulla pipe usando l'oggetto pipe di destinazione.

Per i driver client WINDOWS Driver Model (WDM), lo stack di driver USB restituisce una matrice di strutture USBD_PIPE_INFORMATION . Il numero di elementi nella matrice dipende dal numero di endpoint definiti per l'impostazione alternativa attiva di un'interfaccia nella configurazione selezionata. Ogni elemento contiene informazioni sulla pipe creata per un determinato endpoint. Per informazioni sulla selezione di una configurazione e sul recupero della matrice di informazioni sulla pipe, vedere Come selezionare una configurazione per un dispositivo USB.

Informazioni importanti

Prima che il driver client possa enumerare le pipe, assicurarsi che siano soddisfatti questi requisiti:

Recupero di handle di pipe USB in un driver client KMDF

Il framework rappresenta ogni pipe, aperta dallo stack di driver USB, come oggetto pipe di destinazione USB. Un driver client KMDF può accedere ai metodi dell'oggetto pipe di destinazione per ottenere informazioni sulla pipe. Per eseguire trasferimenti di dati, il driver client deve disporre di handle di pipe WDFUSBPIPE. Per ottenere gli handle della pipe, il driver deve enumerare le interfacce e le impostazioni alternative della configurazione attiva e quindi enumerare gli endpoint definiti in ogni impostazione. L'esecuzione di operazioni di enumerazione, per ogni trasferimento di dati, può essere costosa. Di conseguenza, un approccio consiste nel ottenere handle pipe dopo la configurazione del dispositivo e archiviarli nel contesto di dispositivo definito dal driver. Quando il driver riceve le richieste di trasferimento dei dati, il driver può recuperare gli handle di pipe necessari dal contesto di dispositivo e usarli per inviare la richiesta. Se il driver client modifica la configurazione del dispositivo, ad esempio, seleziona un'impostazione alternativa, il driver deve anche aggiornare il contesto di dispositivo con i nuovi handle di pipe. In caso contrario, il driver può inviare erroneamente richieste di trasferimento su handle di pipe non aggiornati.

Gli handle pipe non sono necessari per i trasferimenti di controllo. Per inviare richieste di trasferimento dei controlli, un driver client WDF chiama i metodi WdfUsbDevicexxxx esposti dall'oggetto dispositivo framework. Questi metodi richiedono un handle WDFUSBDEVICE per avviare i trasferimenti di controllo destinati all'endpoint predefinito. Per tali trasferimenti, la destinazione di I/O per la richiesta è l'endpoint predefinito ed è rappresentata dall'handle WDFIOTARGET, astratta dall'handle WDFUSBPIPE. A livello di dispositivo, l'handle WDFUSBDEVICE è un'astrazione dell'handle WDFUSBPIPE all'endpoint predefinito.

Per informazioni sull'invio di trasferimenti di controllo e sui metodi KMDF, vedere Come inviare un trasferimento di controllo USB.

  1. Estendere la struttura del contesto di dispositivo per archiviare gli handle della pipe.

    Se si conoscono gli endpoint nel dispositivo, estendere la struttura del contesto di dispositivo aggiungendo membri WDFUSBPIPE per archiviare gli handle di pipe USB associati. Ad esempio, è possibile estendere la struttura del contesto di dispositivo, come illustrato di seguito:

    typedef struct _DEVICE_CONTEXT {
        WDFUSBDEVICE    UsbDevice;
        WDFUSBINTERFACE UsbInterface;
        WDFUSBPIPE      BulkReadPipe;   // Pipe opened for the bulk IN endpoint.
        WDFUSBPIPE      BulkWritePipe;  // Pipe opened for the bulk IN endpoint.
        WDFUSBPIPE      InterruptPipe;  // Pipe opened for the interrupt IN endpoint.
        WDFUSBPIPE      StreamInPipe;   // Pipe opened for stream IN endpoint.
        WDFUSBPIPE      StreamOutPipe;  // Pipe opened for stream OUT endpoint.
        UCHAR           NumberConfiguredPipes;  // Number of pipes opened.
        ...
        ...                                     // Other members. Not shown.
    
    } DEVICE_CONTEXT, *PDEVICE_CONTEXT;
    
  2. Dichiarare una struttura del contesto della pipe.

    Ogni pipe può archiviare le caratteristiche correlate all'endpoint in un'altra struttura denominata contesto di pipe. Analogamente a un contesto di dispositivo, un contesto di pipe è una struttura di dati (definita dal driver client) per l'archiviazione di informazioni sulle pipe associate agli endpoint. Durante la configurazione del dispositivo, il driver client passa un puntatore al contesto della pipe al framework. Il framework alloca un blocco di memoria in base alle dimensioni della struttura e archivia un puntatore a tale posizione di memoria con l'oggetto pipe di destinazione USB del framework. Il driver client può usare il puntatore per accedere e archiviare le informazioni sulla pipe nei membri del contesto della pipe.

    typedef struct _PIPE_CONTEXT {
    
        ULONG MaxPacketSize;
        ULONG MaxStreamsSupported;
        PUSBD_STREAM_INFORMATION StreamInfo;
    } PIPE_CONTEXT, *PPIPE_CONTEXT;
    
    WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PIPE_CONTEXT, GetPipeContext)
    
    

    In questo esempio il contesto della pipe archivia il numero massimo di byte che possono essere inviati in un trasferimento. Il driver client può usare tale valore per determinare le dimensioni del buffer di trasferimento. La dichiarazione include anche la macro WDF_DECLARE_CONTEXT_TYPE_WITH_NAME , che genera una funzione inline, GetPipeContext. Il driver client può chiamare tale funzione per recuperare un puntatore al blocco di memoria che archivia il contesto della pipe.

    Per altre informazioni sui contesti, vedere Framework Object Context Space.

    Per passare un puntatore al framework, il driver client inizializza prima il contesto della pipe chiamando WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE. Passa quindi un puntatore al contesto della pipe durante la chiamata a WdfUsbTargetDeviceSelectConfig (per la selezione di una configurazione) o WdfUsbInterfaceSelectSetting (per la selezione di un'impostazione alternativa).

  3. Al termine della richiesta di configurazione del dispositivo, enumerare l'interfaccia e ottenere gli handle della pipe per le pipe configurate. Questo set di informazioni sarà necessario:

    • Handle WDFUSBINTERFACE per l'interfaccia che contiene l'impostazione corrente. È possibile ottenere tale handle enumerando le interfacce nella configurazione attiva. In alternativa, se è stato fornito un puntatore a una struttura WDF_USB_DEVICE_SELECT_CONFIG_PARAMS in WdfUsbTargetDeviceSelectConfig, è possibile ottenere l'handle dal membro Type.SingleInterface.ConfiguredUsbInterface (per dispositivi a interfaccia singola) o dal membro Type.MultiInterface.Pairs.UsbInterface (per un dispositivo a più interfacce).
    • Numero di pipe aperte per gli endpoint nell'impostazione corrente. È possibile ottenere tale numero in una determinata interfaccia chiamando il metodo WdfUsbInterfaceGetNumConfiguredPipes .
    • Handle WDFUSBPIPE per tutte le pipe configurate. È possibile ottenere l'handle chiamando il metodo WdfUsbInterfaceGetConfiguredPipe .

    Dopo aver ottenuto l'handle della pipe, il driver client può chiamare i metodi per determinare il tipo e la direzione della pipe. Il driver può ottenere informazioni sull'endpoint, in una struttura WDF_USB_PIPE_INFORMATION . Il driver può ottenere la struttura popolata chiamando il metodo WdfUsbTargetPipeGetInformation . In alternativa, il driver può fornire un puntatore alla struttura nella chiamata WdfUsbInterfaceGetConfiguredPipe .

Nell'esempio di codice seguente vengono enumerate le pipe nell'impostazione corrente. Ottiene gli handle pipe per gli endpoint bulk e interrupt del dispositivo e li archivia nella struttura del contesto di dispositivo del driver. Archivia le dimensioni massime dei pacchetti di ogni endpoint nel contesto della pipe associata. Se l'endpoint supporta i flussi, apre flussi statici chiamando la routine OpenStreams. L'implementazione di OpenStreams è illustrata in Come aprire e chiudere flussi statici in un endpoint bulk USB.

Per determinare se un determinato endpoint bulk supporta flussi statici, il driver client esamina il descrittore dell'endpoint. Tale codice viene implementato in una routine helper denominata RetrieveStreamInfoFromEndpointDesc, illustrata nel blocco di codice successivo.

NTSTATUS
    FX3EnumeratePipes(
    _In_ WDFDEVICE Device)

{
    NTSTATUS                    status;
    PDEVICE_CONTEXT             pDeviceContext;
    UCHAR                       i;
    PPIPE_CONTEXT               pipeContext;
    WDFUSBPIPE                  pipe;
    WDF_USB_PIPE_INFORMATION    pipeInfo;

    PAGED_CODE();

    pDeviceContext = GetDeviceContext(Device);

    // Get the number of pipes in the current alternate setting.
    pDeviceContext->NumberConfiguredPipes = WdfUsbInterfaceGetNumConfiguredPipes(
        pDeviceContext->UsbInterface);

    if (pDeviceContext->NumberConfiguredPipes == 0)
    {
        status = USBD_STATUS_BAD_NUMBER_OF_ENDPOINTS;
        goto Exit;
    }
    else
    {
        status = STATUS_SUCCESS;
    }

    // Enumerate the pipes and get pipe information for each pipe.
    for (i = 0; i < pDeviceContext->NumberConfiguredPipes; i++)
    {
        WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);

        pipe =  WdfUsbInterfaceGetConfiguredPipe(
            pDeviceContext->UsbInterface,
            i,
            &pipeInfo);

        if (pipe == NULL)
        {
            continue;
        }

        pipeContext = GetPipeContext (pipe);

        // If the pipe is a bulk endpoint that supports streams,
        // If the host controller supports streams, open streams.
        // Use the endpoint as an IN bulk endpoint.
        // Store the maximum packet size.

        if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) &&
            WdfUsbTargetPipeIsInEndpoint (pipe))
        {

            // Check if this is a streams IN endpoint. If it is,
            // Get the maximum number of streams and store
            // the value in the pipe context.
            RetrieveStreamInfoFromEndpointDesc (
                Device,
                pipe);

            if ((pipeContext->IsStreamsCapable) &&
                (pipeContext->MaxStreamsSupported > 0))
            {
                status = OpenStreams (
                    Device,
                    pipe);

                if (status != STATUS_SUCCESS)
                {
                    TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
                        "%!FUNC! Could not open streams.");

                    pDeviceContext->StreamInPipe = NULL;
                }
                else
                {
                    pDeviceContext->StreamInPipe = pipe;
                    pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
                }
            }
            else
            {
                pDeviceContext->BulkReadPipe = pipe;
                pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
            }

            continue;
        }

        if ((WdfUsbPipeTypeBulk == pipeInfo.PipeType) &&
            WdfUsbTargetPipeIsOutEndpoint (pipe))
        {
            // Check if this is a streams IN endpoint. If it is,
            // Get the maximum number of streams and store
            // the value in the pipe context.
            RetrieveStreamInfoFromEndpointDesc (
                Device,
                pipe);

            if ((pipeContext->IsStreamsCapable) &&
                (pipeContext->MaxStreamsSupported > 0))
            {
                status = OpenStreams (
                    Device,
                    pipe);

                if (status != STATUS_SUCCESS)
                {
                    TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
                        "%!FUNC! Could not open streams.");

                    pDeviceContext->StreamOutPipe = NULL;
                }
                else
                {
                    pDeviceContext->StreamOutPipe = pipe;
                    pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
                }
            }
            else
            {
                pDeviceContext->BulkWritePipe = pipe;
                pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
            }

            continue;
        }

        if ((WdfUsbPipeTypeInterrupt == pipeInfo.PipeType) &&
            WdfUsbTargetPipeIsInEndpoint (pipe))
        {
            pDeviceContext->InterruptPipe = pipe;
            pipeContext->MaxPacketSize = pipeInfo.MaximumPacketSize;
            continue;
        }

    }

Exit:
    return status;
}

Nell'esempio di codice seguente viene illustrata una routine helper denominata RetrieveStreamInfoFromEndpointDesc, che il driver client chiama durante l'enumerazione delle pipe.

Nell'esempio di codice seguente il driver client chiama la routine helper precedente, RetrieveStreamInfoFromEndpointDesc, durante l'enumerazione delle pipe. La routine esamina prima di tutto il descrittore di configurazione e la analizza per recuperare i descrittori dell'endpoint. Se il descrittore dell'endpoint per la pipe contiene un descrittore USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE, il driver recupera il numero massimo di flussi supportati dall'endpoint.

/*++
Routine Description:

This routine parses the configuration descriptor and finds the endpoint
with which the specified pipe is associated.
It then retrieves the maximum number of streams supported by the endpoint.
It stores maximum number of streams in the pipe context.

Arguments:

Device - WDFUSBDEVICE handle to the target device object.
The driver obtained that handle in a previous call to
WdfUsbTargetDeviceCreateWithParameters.

Pipe - WDFUSBPIPE handle to the target pipe object.

Return Value:

NTSTATUS
++*/

VOID RetrieveStreamInfoFromEndpointDesc (
    WDFDEVICE Device,
    WDFUSBPIPE Pipe)
{
    PDEVICE_CONTEXT                                 deviceContext                = NULL;
    PUSB_CONFIGURATION_DESCRIPTOR                   configDescriptor             = NULL;
    WDF_USB_PIPE_INFORMATION                        pipeInfo;
    PUSB_COMMON_DESCRIPTOR                          pCommonDescriptorHeader      = NULL;
    PUSB_INTERFACE_DESCRIPTOR                       pInterfaceDescriptor         = NULL;
    PUSB_ENDPOINT_DESCRIPTOR                        pEndpointDescriptor          = NULL;
    PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR   pEndpointCompanionDescriptor = NULL;
    ULONG                                           maxStreams;
    ULONG                                           index;
    BOOLEAN                                         found                        = FALSE;
    UCHAR                                           interfaceNumber = 0;
    UCHAR                                           alternateSetting = 1;
    PPIPE_CONTEXT                                   pipeContext = NULL;
    NTSTATUS                                        status;

    PAGED_CODE();

    deviceContext = GetDeviceContext (Device);
    pipeContext = GetPipeContext (Pipe);

    // Get the configuration descriptor of the currently selected configuration
    status = FX3RetrieveConfigurationDescriptor (
        deviceContext->UsbDevice,
        &deviceContext->ConfigurationNumber,
        &configDescriptor);

    if (!NT_SUCCESS (status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Could not retrieve the configuration descriptor.");

        status = USBD_STATUS_INAVLID_CONFIGURATION_DESCRIPTOR;

        goto Exit;
    }

    if (deviceContext->ConfigurationNumber == 1)
    {
        alternateSetting = 1;
    }
    else
    {
        alternateSetting = 0;
    }

    // Get the Endpoint Address of the pipe
    WDF_USB_PIPE_INFORMATION_INIT(&pipeInfo);
    WdfUsbTargetPipeGetInformation (Pipe, &pipeInfo);

    // Parse the ConfigurationDescriptor (including all Interface and
    // Endpoint Descriptors) and locate a Interface Descriptor which
    // matches the InterfaceNumber, AlternateSetting, InterfaceClass,
    // InterfaceSubClass, and InterfaceProtocol parameters.
    pInterfaceDescriptor = USBD_ParseConfigurationDescriptorEx(
        configDescriptor,
        configDescriptor,
        interfaceNumber,  //Interface number is 0.
        alternateSetting,  // Alternate Setting is 1
        -1, // InterfaceClass, ignore
        -1, // InterfaceSubClass, ignore
        -1  // InterfaceProtocol, ignore
        );

    if (pInterfaceDescriptor == NULL )
    {
        // USBD_ParseConfigurationDescriptorEx failed to retrieve Interface Descriptor.
        goto Exit;
    }

    pCommonDescriptorHeader = (PUSB_COMMON_DESCRIPTOR) pInterfaceDescriptor;

    for(index = 0; index < pInterfaceDescriptor->bNumEndpoints; index++)
    {

        pCommonDescriptorHeader = USBD_ParseDescriptors(
            configDescriptor,
            configDescriptor->wTotalLength,
            pCommonDescriptorHeader,
            USB_ENDPOINT_DESCRIPTOR_TYPE);

        if (pCommonDescriptorHeader == NULL)
        {
            // USBD_ParseDescriptors failed to retrieve Endpoint Descriptor unexpectedly.
            goto Exit;
        }

        pEndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR) pCommonDescriptorHeader;

        // Search an Endpoint Descriptor that matches the EndpointAddress
        if (pEndpointDescriptor->bEndpointAddress == pipeInfo.EndpointAddress)
        {

            found = TRUE;
            break;
        }

        // Skip the current Endpoint Descriptor and search for the next.
        pCommonDescriptorHeader = (PUSB_COMMON_DESCRIPTOR)(((PUCHAR)pCommonDescriptorHeader)
            + pCommonDescriptorHeader->bLength);
    }

    if (found)
    {
        // Locate the SuperSpeed Endpoint Companion Descriptor
        // associated with the endpoint descriptor
        pCommonDescriptorHeader = USBD_ParseDescriptors (
            configDescriptor,
            configDescriptor->wTotalLength,
            pEndpointDescriptor,
            USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE);

        if (pCommonDescriptorHeader != NULL)
        {
            pEndpointCompanionDescriptor =
                (PUSB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR) pCommonDescriptorHeader;

            maxStreams = pEndpointCompanionDescriptor->bmAttributes.Bulk.MaxStreams;

            if (maxStreams == 0)
            {
                pipeContext->MaxStreamsSupported = 0;
                pipeContext->IsStreamsCapable = FALSE;
            }
            else
            {
                pipeContext->IsStreamsCapable = TRUE;
                pipeContext->MaxStreamsSupported = 1 << maxStreams;
            }
        }
        else
        {
            KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
                "USBD_ParseDescriptors failed to retrieve SuperSpeed Endpoint Companion Descriptor unexpectedly.\n" ));
        }
    }
    else
    {
        pipeContext->MaxStreamsSupported = 0;
        pipeContext->IsStreamsCapable = FALSE;
    }

Exit:
    if (configDescriptor)
    {
        ExFreePoolWithTag (configDescriptor, USBCLIENT_TAG);
    }

    return;
}

Recupero di handle pipe in un driver client UMDF

Un driver client UMDF usa l'infrastruttura COM e implementa classi di callback COM che si associano a oggetti dispositivo framework. Analogamente a un driver KMDF, un driver client UMDF può ottenere informazioni sulla pipe solo dopo la configurazione del dispositivo. Per ottenere informazioni sulla pipe, il driver client deve ottenere un puntatore all'interfaccia IWDFUsbTargetPipe dell'oggetto interfaccia framework che contiene l'impostazione attiva. Usando il puntatore di interfaccia, il driver può enumerare le pipe in tale impostazione per ottenere i puntatori di interfaccia IWDFUsbTargetPipe esposti dagli oggetti della pipe di destinazione del framework.

Prima che il driver inizi a enumerare le pipe, il driver deve conoscere la configurazione del dispositivo e gli endpoint supportati. In base a tali informazioni, il driver può archiviare oggetti pipe come variabili membro della classe.

L'esempio di codice seguente estende il modello UMDF USB fornito con Visual Studio Professional 2012. Per una spiegazione del codice iniziale, vedere "Implementazione IPnpCallbackHardware e attività specifiche di USB" in Informazioni sulla struttura del codice del driver client USB (UMDF).

Estendere la dichiarazione della classe CDevice, come illustrato di seguito. Questo codice di esempio presuppone che il dispositivo sia la scheda OSR FX2. Per informazioni sul layout del descrittore, vedi Layout dispositivo USB.

class CMyDevice :
    public CComObjectRootEx<CComMultiThreadModel>,
    public IPnpCallbackHardware
{

public:
    DECLARE_NOT_AGGREGATABLE(CMyDevice)

    BEGIN_COM_MAP(CMyDevice)
        COM_INTERFACE_ENTRY(IPnpCallbackHardware)
    END_COM_MAP()

    CMyDevice() :
        m_FxDevice(NULL),
        m_IoQueue(NULL),
        m_FxUsbDevice(NULL)
    {
    }

    ~CMyDevice()
    {
    }

private:
    IWDFDevice *            m_FxDevice;
    CMyIoQueue *            m_IoQueue;
    IWDFUsbTargetDevice *   m_FxUsbDevice;
    IWDFUsbInterface *      m_pIUsbInterface;  //Pointer to the target interface object.
    IWDFUsbTargetPipe *     m_pIUsbInputPipe;  // Pointer to the target pipe object for the bulk IN endpoint.
    IWDFUsbTargetPipe *     m_pIUsbOutputPipe; // Pointer to the target pipe object for the bulk OUT endpoint.
    IWDFUsbTargetPipe *     m_pIUsbInterruptPipe; // Pointer to the target pipe object for the interrupt endpoint.

private:
    HRESULT
    Initialize(
        __in IWDFDriver *FxDriver,
        __in IWDFDeviceInitialize *FxDeviceInit
        );

public:
    static
    HRESULT
    CreateInstanceAndInitialize(
        __in IWDFDriver *FxDriver,
        __in IWDFDeviceInitialize *FxDeviceInit,
        __out CMyDevice **Device
        );

    HRESULT
    Configure(
        VOID
        );

    HRESULT                     // Declare a helper function to enumerate pipes.
    ConfigureUsbPipes(
        );

public:
    // IPnpCallbackHardware methods
    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnPrepareHardware(
            __in IWDFDevice *FxDevice
            );

    virtual
    HRESULT
    STDMETHODCALLTYPE
    OnReleaseHardware(
        __in IWDFDevice *FxDevice
        );

};

Nella definizione della classe CDevice implementare un metodo helper denominato CreateUsbIoTargets. Questo metodo viene chiamato dall'implementazione IPnpCallbackHardware::OnPrepareHardware dopo che il driver ha ottenuto un puntatore all'oggetto dispositivo di destinazione.

HRESULT  CMyDevice::CreateUsbIoTargets()
{
    HRESULT                 hr;
    UCHAR                   NumEndPoints = 0;
    IWDFUsbInterface *      pIUsbInterface = NULL;
    IWDFUsbTargetPipe *     pIUsbPipe = NULL;

    if (SUCCEEDED(hr))
    {
        UCHAR NumInterfaces = pIUsbTargetDevice->GetNumInterfaces();

        WUDF_TEST_DRIVER_ASSERT(1 == NumInterfaces);

        hr = pIUsbTargetDevice->RetrieveUsbInterface(0, &pIUsbInterface);
        if (FAILED(hr))
        {
            TraceEvents(TRACE_LEVEL_ERROR,
                        TEST_TRACE_DEVICE,
                        "%!FUNC! Unable to retrieve USB interface from USB Device I/O Target %!HRESULT!",
                        hr
                        );
        }
        else
        {
            m_pIUsbInterface = pIUsbInterface;

            DriverSafeRelease (pIUsbInterface); //release creation reference
        }
     }

    if (SUCCEEDED(hr))
    {
        NumEndPoints = pIUsbInterface->GetNumEndPoints();

        if (NumEndPoints != NUM_OSRUSB_ENDPOINTS)
        {
            hr = E_UNEXPECTED;
            TraceEvents(TRACE_LEVEL_ERROR,
                        TEST_TRACE_DEVICE,
                        "%!FUNC! Has %d endpoints, expected %d, returning %!HRESULT! ",
                        NumEndPoints,
                        NUM_OSRUSB_ENDPOINTS,
                        hr
                        );
        }
    }

    if (SUCCEEDED(hr))
    {
        for (UCHAR PipeIndex = 0; PipeIndex < NumEndPoints; PipeIndex++)
        {
            hr = pIUsbInterface->RetrieveUsbPipeObject(PipeIndex,
                                                  &pIUsbPipe);

            if (FAILED(hr))
            {
                TraceEvents(TRACE_LEVEL_ERROR,
                            TEST_TRACE_DEVICE,
                            "%!FUNC! Unable to retrieve USB Pipe for PipeIndex %d, %!HRESULT!",
                            PipeIndex,
                            hr
                            );
            }
            else
            {
                if ( pIUsbPipe->IsInEndPoint() )
                {
                    if ( UsbdPipeTypeInterrupt == pIUsbPipe->GetType() )
                    {
                        m_pIUsbInterruptPipe = pIUsbPipe;
                    }
                    else if ( UsbdPipeTypeBulk == pIUsbPipe->GetType() )
                    {
                        m_pIUsbInputPipe = pIUsbPipe;
                    }
                    else
                    {
                        pIUsbPipe->DeleteWdfObject();
                    }
                }
                else if ( pIUsbPipe->IsOutEndPoint() && (UsbdPipeTypeBulk == pIUsbPipe->GetType()) )
                {
                    m_pIUsbOutputPipe = pIUsbPipe;
                }
                else
                {
                    pIUsbPipe->DeleteWdfObject();
                }

                DriverSafeRelease(pIUsbPipe);  //release creation reference
            }
        }

        if (NULL == m_pIUsbInputPipe || NULL == m_pIUsbOutputPipe)
        {
            hr = E_UNEXPECTED;
            TraceEvents(TRACE_LEVEL_ERROR,
                        TEST_TRACE_DEVICE,
                        "%!FUNC! Input or output pipe not found, returning %!HRESULT!",
                        hr
                        );
        }
    }

    return hr;
}

In UMDF il driver client usa un indice pipe per inviare richieste di trasferimento dei dati. Un indice pipe è un numero assegnato dallo stack di driver USB quando apre pipe per gli endpoint in un'impostazione. Per ottenere l'indice della pipe, chiamare il metodo**IWDFUsbTargetPipe::GetInformation**. Il metodo popola una struttura WINUSB_PIPE_INFORMATION . Il valore PipeId indica l'indice della pipe.

Un modo per eseguire operazioni di lettura e scrittura nella pipe di destinazione consiste nel chiamare IWDFUsbInterface::GetWinUsbHandle per ottenere un handle WinUSB e quindi chiamare le funzioni WinUSB. Ad esempio, il driver può chiamare la funzione WinUsb_ReadPipe o WinUsb_WritePipe . In queste chiamate di funzione, il driver deve specificare l'indice della pipe. Per altre informazioni, vedere Come accedere a un dispositivo USB tramite funzioni WinUSB.

Handle di pipe per i driver client basati su WDM

Dopo aver selezionato una configurazione, lo stack di driver USB configura una pipe a ognuno degli endpoint del dispositivo. Lo stack di driver USB restituisce una matrice di strutture USBD_PIPE_INFORMATION . Il numero di elementi nella matrice dipende dal numero di endpoint definiti per l'impostazione alternativa attiva di un'interfaccia nella configurazione selezionata. Ogni elemento contiene informazioni sulla pipe creata per un determinato endpoint. Per altre informazioni su come ottenere handle di pipe, vedere How to Select a Configuration for a USB Device (Come selezionare una configurazione per un dispositivo USB).

Per compilare una richiesta di trasferimento di I/O, il driver client deve avere un handle per la pipe associata a tale endpoint. Il driver client può ottenere l'handle pipe dal membro PipeHandle di USBD_PIPE_INFORMATION nella matrice.

Oltre all'handle pipe, il driver client richiede anche il tipo di pipe. Il driver client può determinare il tipo di pipe esaminando il membro PipeType .

In base al tipo di endpoint, lo stack di driver USB supporta diversi tipi di pipe. Il driver client può determinare il tipo di pipe esaminando il membro PipeType di USBD_PIPE_INFORMATION. I diversi tipi di pipe richiedono diversi tipi di blocchi di richieste USB (URB) per eseguire transazioni di I/O.

Il driver client invia quindi l'OGGETTO ALL'stack di driver USB. Lo stack di driver USB elabora la richiesta e invia i dati specificati alla pipe di destinazione richiesta.

L'OGGETTO WSUS contiene informazioni sulla richiesta, ad esempio l'handle della pipe di destinazione, il buffer di trasferimento e la relativa lunghezza. Ogni struttura all'interno dell'unione DEL NODO condivide determinati membri: TransferFlags, TransferBuffer, TransferBufferLength e TransferBufferMDL. Nel membro TransferFlags sono presenti flag specifici del tipo che corrispondono a ogni tipo DI CODIFICA. Per tutti gli URL di trasferimento dei dati, il flag USBD_TRANSFER_DIRECTION_IN in TransferFlags specifica la direzione del trasferimento. I driver client impostano il flag USBD_TRANSFER_DIRECTION_IN per leggere i dati dal dispositivo. I driver cancellano questo flag per inviare dati al dispositivo. I dati possono essere letti o scritti in un buffer residente in memoria o in un file MDL. In entrambi i casi, il driver specifica le dimensioni del buffer nel membro TransferBufferLength . Il driver fornisce un buffer residente nel membro TransferBuffer e un MDL nel membro TransferBufferMDL . Indipendentemente da uno dei driver forniti, l'altro deve essere NULL.