Dela via


Hur man räknar upp USB-kanaler

Den här artikeln innehåller en översikt över USB-rör och beskriver de steg som krävs av en USB-klientdrivrutin för att hämta rörhandtag från USB-drivrutinsstacken.

En USB-slutpunkt är en buffert på enheten som klientdrivrutinen skickar data till eller tar emot data från. För att skicka eller ta emot data skickar klientdrivrutinen en I/O-överföringsbegäran till USB-drivrutinsstacken, som visar data för värdstyrenheten. Värdstyrenheten följer sedan vissa protokoll (beroende på typen av slutpunkt: bulk, avbrott eller isochron) för att skapa begäranden som överför data till eller från enheten. All information om dataöverföringen abstraheras från klientdrivrutinen. Så länge klientdrivrutinen skickar en välformulerad begäran bearbetar USB-drivrutinsstacken begäran och överför data till enheten.

Under enhetskonfigurationen skapar USB-drivrutinsstacken en USB-kanal (på värdsidan) för var och en av enhetens slutpunkter som definierats i USB-gränssnittet och dess aktiva alternativinställning. Ett USB-rör är en kommunikationskanal mellan värdstyrenheten och slutpunkten. För klientdrivrutinen är en pipe en logisk abstraktion av slutpunkten. För att kunna skicka dataöverföringar måste drivrutinen få rörhandtaget associerat med slutpunkten som är målet för överföringen. Rörhandtag krävs också när drivrutinen vill avbryta överföringar eller återställa röret, i händelse av feltillstånd.

Alla attribut för en rörledning utgår från beskrivningen av den associerade slutpunkten. Beroende på typen av slutpunkt tilldelar USB-drivrutinsstacken till exempel en typ för röret. För en bulk-slutpunkt skapar USB-drivrutinsstacken ett bulkrör; För en isokron slutpunkt skapas ett isokront rör och så vidare. Ett annat viktigt attribut är mängden data som värdkontrollanten kan skicka till slutpunktspunkten i en begäran. Beroende på det värdet måste klientdrivrutinen fastställa layouten för överföringsbufferten.

Windows Driver Foundation (WDF) tillhandahåller specialiserade I/O-målobjekt i Kernel-Mode Driver Framework (KMDF) och User-Mode Driver Framework (UMDF) som förenklar många av konfigurationsuppgifterna för klientdrivrutinen. Med hjälp av dessa objekt kan klientdrivrutinen hämta information om den aktuella konfigurationen, till exempel antalet gränssnitt, alternativa inställningar i varje gränssnitt och deras slutpunkter. Ett av dessa objekt, som kallas målpipe-objektet, utför slutpunktsrelaterade uppgifter. Den här artikeln beskriver hur du hämtar rörinformation med hjälp av målpipeobjektet.

För WDM-klientdrivrutiner (Windows Driver Model) returnerar USB-drivrutinsstacken en matris med USBD_PIPE_INFORMATION strukturer. Antalet element i matrisen beror på antalet slutpunkter som definierats för den aktiva alternativa inställningen för ett gränssnitt i den valda konfigurationen. Varje element innehåller information om röret som skapats för en viss slutpunkt. Information om hur du väljer en konfiguration och hämtar matrisen med rörinformation finns i Så här väljer du en konfiguration för en USB-enhet.

Vad du behöver veta

Innan klientdrivrutinen kan räkna upp rör kontrollerar du att dessa krav är uppfyllda:

Hämta USB-rörhandtag i en KMDF-klientdrivrutin

Ramverket representerar varje rör som öppnas av USB-drivrutinsstacken som ett USB-målpipeobjekt. En KMDF-klientdrivrutin kan komma åt metoderna för målpipeobjektet för att få information om röret. För att kunna utföra dataöverföringar måste klientdrivrutinen ha WDFUSBPIPE-rörhandtag. För att hämta rörhandtagen måste drivrutinen räkna upp den aktiva konfigurationens gränssnitt och alternativa inställningar och sedan räkna upp slutpunkterna som definierats i varje inställning. Det kan vara dyrt att utföra uppräkningsåtgärder för varje dataöverföring. Därför är en metod att hämta rörhandtag när enheten har konfigurerats och lagra dem i den drivrutinsdefinierade enhetskontexten. När drivrutinen tar emot begäranden om dataöverföring kan drivrutinen hämta de nödvändiga rörhandtagen från enhetskontexten och använda dem för att skicka begäran. Om klientdrivrutinen ändrar enhetens konfiguration, till exempel väljer en alternativ inställning, måste drivrutinen även uppdatera enhetskontexten med de nya rörhandtagen. Annars kan drivrutin felaktigt skicka överföringsbegäranden på inaktuella rörhandtag.

Rörhandtag krävs inte för kontrollöverföringar. För att skicka begäranden om kontrollöverföring anropar en WDF-klientdrivrutin WdfUsbDevicexxxx metoder som exponeras av ramverkets enhetsobjekt. Dessa metoder kräver WDFUSBDEVICE-handtag för att initiera kontrollöverföringar som riktar sig mot den standardmässiga slutpunkten. För sådana överföringar är I/O-målet för begäran standardslutpunkten och representeras av WDFIOTARGET-referensen, som abstraheras av WDFUSBPIPE-handtaget. På enhetsnivå är WDFUSBDEVICE-handtaget en abstraktion av WDFUSBPIPE-handtaget till standardslutpunkten.

Information om hur du skickar kontrollöverföringar och KMDF-metoderna finns i Så här skickar du en USB-kontrollöverföring.

  1. Utöka enhetens kontextstruktur för att lagra rörhandtag.

    Om du känner till slutpunkterna på enheten utökar du enhetens kontextstruktur genom att lägga till WDFUSBPIPE-medlemmar för att lagra de associerade USB-rörhandtagen. Du kan till exempel utöka enhetens kontextstruktur enligt följande:

    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. Deklarera en rörkontextstruktur.

    Varje rör kan lagra slutpunktsrelaterade egenskaper i en annan struktur som kallas rörkontext. Precis som i en enhetskontext är en rörkontext en datastruktur (definierad av klientdrivrutinen) för lagring av information om rör som är associerade med slutpunkter. Vid enhetskonfiguration skickar klientdrivrutinen en pekare till sin pipe-kontext till ramverket. Ramverket allokerar ett minnesblock baserat på strukturens storlek och lagrar en pekare till den minnesplatsen med ramverkets USB-målrörsobjekt. Klientdrivrutinen kan använda pekaren för att komma åt och lagra rörinformation i medlemmar i rörkontexten.

    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)
    
    

    I det här exemplet lagrar rörkontexten det maximala antalet byte som kan skickas i en överföring. Klientdrivrutinen kan använda det värdet för att fastställa storleken på överföringsbufferten. Deklarationen innehåller även makrot WDF_DECLARE_CONTEXT_TYPE_WITH_NAME, som genererar en infogad funktion, GetPipeContext. Klientdrivrutinen kan anropa den funktionen för att hämta en pekare till det minnesblock som lagrar rörkontexten.

    Mer information om kontexter finns i Framework Object Context Space.

    Om du vill skicka en pekare till ramverket initierar klientdrivrutinen först sin pipe-kontext genom att anropa WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE. Skickar sedan en pekare till rörkontexten när du anropar WdfUsbTargetDeviceSelectConfig (för att välja en konfiguration) eller WdfUsbInterfaceSelectSetting (för att välja en alternativ inställning).

  3. När enhetskonfigurationsbegäran har slutförts räknar du upp gränssnittet och hämtar rörhandtagen för de konfigurerade rören. Du behöver den här uppsättningen med information:

    • WDFUSBINTERFACE hanterar gränssnittet som innehåller den aktuella inställningen. Du kan få detta handtag genom att lista gränssnitten i den aktiva konfigurationen. Om du har angett en pekare till en WDF_USB_DEVICE_SELECT_CONFIG_PARAMS struktur i WdfUsbTargetDeviceSelectConfigkan du hämta handtaget från Type.SingleInterface.ConfiguredUsbInterface medlem (för enheter med ett enda gränssnitt) eller Type.MultiInterface.Pairs.UsbInterface medlem (för enheter med flera gränssnitt).
    • Antal rör som öppnats för slutpunkterna i den aktuella inställningen. Du kan hämta det numret i ett visst gränssnitt genom att anropa metoden WdfUsbInterfaceGetNumConfiguredPipes.
    • WDFUSBPIPE-handtag för alla konfigurerade rör. Du kan hämta handtaget genom att anropa metoden WdfUsbInterfaceGetConfiguredPipe.

    När du har fått rörhandtaget kan klientdrivrutinen anropa metoder för att fastställa rörtypen och riktningen. Drivrutinen kan hämta information om slutpunkten i en struktur WDF_USB_PIPE_INFORMATION. Drivrutinen kan hämta den ifyllda strukturen genom att anropa metoden WdfUsbTargetPipeGetInformation. Alternativt kan drivrutinen ange en pekare till strukturen i WdfUsbInterfaceGetConfiguredPipe-anrop.

I följande kodexempel räknas rören upp i den aktuella inställningen. Den hämtar rörhandtag för enhetens mass- och avbrottsslutpunkter och lagrar dem i förarens enhetskontextstruktur. Den lagrar den maximala paketstorleken för varje slutpunkt i den associerade rörkontexten. Om slutpunkten stöder strömmar öppnar den statiska strömmar genom att anropa OpenStreams-rutinen. Implementeringen av OpenStreams visas i Så här öppnar och stänger du statiska strömmar i en USB-slutpunkt.

För att avgöra om en viss massslutpunkt stöder statiska strömmar undersöker klientdrivrutinen slutpunktsbeskrivningen. Koden implementeras i en hjälprutin med namnet RetrieveStreamInfoFromEndpointDesc som visas i nästa kodblock.

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;
}

I följande kodexempel visas en hjälprutin med namnet RetrieveStreamInfoFromEndpointDesc som klientdrivrutinen anropar vid uppräkning av rör.

I följande kodexempel anropar klientdrivrutinen den föregående hjälprutinen, RetrieveStreamInfoFromEndpointDesc, medan rör räknas upp. Rutinen hämtar först konfigurationsdeskriptorn och parsar den för att hämta slutpunktsdeskriptorer. Om slutpunktsbeskrivningen för röret innehåller en USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE-beskrivning hämtar drivrutinen det maximala antalet strömmar som stöds av slutpunkten.

/*++
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;
}

Hämta rörhandtag i en UMDF-klientdrivrutin

En UMDF-klientdrivrutin använder COM-infrastruktur och implementerar COM-motringningsklasser som parkopplas med ramverksenhetsobjekt. Precis som en KMDF-drivrutin kan en UMDF-klientdrivrutin bara hämta rörinformation när enheten har konfigurerats. För att hämta rörinformation måste klientdrivrutinen hämta en pekare till IWDFUsbTargetPipe gränssnitt för ramverksgränssnittsobjektet som innehåller den aktiva inställningen. Med hjälp av gränssnittspekaren kan drivrutinen räkna upp kanalerna i den inställningen för att hämta IWDFUsbTargetPipe gränssnittspekare som exponeras av ramverkets målpipeobjekt.

Innan drivrutinen börjar räkna upp rören måste drivrutinen känna till enhetskonfigurationen och de slutpunkter som stöds. Baserat på den informationen kan drivrutinen lagra rörobjekt som klassmedlemsvariabler.

Följande kodexempel utökar den USB UMDF-mall som tillhandahålls med Visual Studio Professional 2012. För en förklaring till startkoden, se "IPnpCallbackHardware implementering och USB-specifika uppgifter" i Understanding the USB client driver code structure (UMDF).

Utöka CDevice-klassdeklarationen enligt nedan. Den här exempelkoden förutsätter att enheten är OSR FX2-kortet. Information om dess beskrivningslayout finns i USB-enhetslayout.

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
        );

};

I CDevice-klassdefinitionen implementerar du en hjälpmetod med namnet CreateUsbIoTargets. Den här metoden anropas från IPnpCallbackHardware::OnPrepareHardware-implementeringen när drivrutinen har hämtat en pekare till målenhetsobjektet.

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;
}

I UMDF använder klientdrivrutinen ett pipe-index för att skicka dataöverföringsbegäranden. Ett rörindex är ett nummer som tilldelas av USB-drivrutinsstacken när det öppnar rör för slutpunkterna i en inställning. Hämta rörindexet genom att anropa metoden **IWDFUsbTargetPipe::GetInformation** . Metoden fyller i en WINUSB_PIPE_INFORMATION struktur. Värdet PipeId anger rörindexet.

Ett sätt att utföra läs- och skrivåtgärder på målpipan är att anropa IWDFUsbInterface::GetWinUsbHandle för att hämta ett WinUSB-handtag och sedan anropa WinUSB-funktionerna. Drivrutinen kan till exempel anropa funktionen WinUsb_ReadPipe eller WinUsb_WritePipe. I dessa funktionsanrop måste drivrutinen ange pipe-indexet. För mer information, se Hur man får åtkomst till en USB-enhet genom att använda WinUSB-funktioner.

Rörhandtag för WDM-baserade klientdrivrutiner

När en konfiguration har valts konfigurerar USB-drivrutinsstacken ett rör till var och en av enhetens slutpunkter. USB-drivrutinsstacken returnerar en matris med USBD_PIPE_INFORMATION strukturer. Antalet element i matrisen beror på antalet slutpunkter som definierats för den aktiva alternativa inställningen för ett gränssnitt i den valda konfigurationen. Varje element innehåller information om röret som skapats för en viss slutpunkt. Mer information om hur du hämtar rörhandtag finns i Så här väljer du en konfiguration för en USB-enhet.

För att skapa en I/O-överföringsbegäran måste klientdrivrutinen ha tillgång till det rör som är kopplat till den slutpunkten. Klientdrivrutinen kan hämta rörhandtaget från PipeHandle medlem i USBD_PIPE_INFORMATION i matrisen.

Förutom rörhandtaget kräver klientdrivrutinen även rörtypen. Klientdrivrutinen kan fastställa rörtypen genom att undersöka PipeType--medlemsvariabeln.

Baserat på slutpunktstypen stöder USB-drivrutinsstacken olika typer av rör. Klientdrivrutinen kan avgöra rörtypen genom att undersöka PipeType medlem av USBD_PIPE_INFORMATION. De olika rörtyperna kräver olika typer av USB-begärandeblock (URL:er) för att utföra I/O-transaktioner.

Klientdrivrutinen skickar sedan URB till USB-drivrutinsstacken. USB-drivrutinsstacken bearbetar begäran och skickar angivna data till den begärda målpipan.

URB innehåller information om begäran, till exempel målpipehandtaget, överföringsbufferten och dess längd. Varje struktur i URB union delar vissa medlemmar: TransferFlags, TransferBuffer, TransferBufferLengthoch TransferBufferMDL. Det finns typspecifika flaggor i TransferFlags medlem som motsvarar varje URB-typ. För alla url:er för dataöverföring anger flaggan USBD_TRANSFER_DIRECTION_IN i TransferFlags överföringens riktning. Klientdrivrutiner anger flaggan USBD_TRANSFER_DIRECTION_IN för att läsa data från enheten. Drivrutiner avmarkerar den här flaggan för att skicka data till enheten. Data kan läsas från eller skrivas till antingen en buffert bosatt i minnet eller en MDL. I båda fallen anger drivrutinen storleken på bufferten i TransferBufferLength medlem. Drivrutinen tillhandahåller en reserverad buffert i TransferBuffer och en MDL i TransferBufferMDL. Oavsett vilken parameter som ges måste den andra vara NULL.