Cara menghitung pipa USB

Artikel ini memberikan gambaran umum tentang pipa USB dan menjelaskan langkah-langkah yang diperlukan oleh driver klien USB untuk mendapatkan handel pipa dari tumpukan driver USB.

Titik akhir USB adalah buffer di perangkat tempat driver klien mengirim data ke atau menerima data. Untuk mengirim atau menerima data, driver klien mengirimkan permintaan transfer I/O ke tumpukan driver USB, yang menyajikan data ke pengontrol host. Pengontrol host kemudian mengikuti protokol tertentu (tergantung pada jenis titik akhir: massal, interupsi, atau isochronous) untuk membangun permintaan yang mentransfer data ke atau dari perangkat. Semua detail transfer data diabstraksi dari driver klien. Selama driver klien mengirimkan permintaan yang terbentuk dengan baik, tumpukan driver USB memproses permintaan dan mentransfer data ke perangkat.

Selama konfigurasi perangkat, tumpukan driver USB membuat pipa USB (di sisi host) untuk setiap titik akhir perangkat yang ditentukan dalam antarmuka USB dan pengaturan alternatif aktifnya. Pipa USB adalah saluran komunikasi antara pengontrol host dan titik akhir. Untuk driver klien, pipa adalah abstraksi logis dari titik akhir. Untuk mengirim transfer data, driver harus mendapatkan handel pipa yang terkait dengan titik akhir yang merupakan target untuk transfer. Handel pipa juga diperlukan ketika driver ingin membatalkan transfer atau mengatur ulang pipa, jika terjadi kondisi kesalahan.

Semua atribut pipa berasal dari deskriptor titik akhir terkait. Misalnya, tergantung pada jenis titik akhir, tumpukan driver USB menetapkan jenis untuk pipa. Untuk titik akhir massal, tumpukan driver USB membuat pipa massal; untuk titik akhir isochronous, pipa isochronous dibuat, dan sebagainya. Atribut penting lainnya adalah jumlah data yang dapat dikirim pengontrol host ke titik akhir dalam permintaan. Bergantung pada nilai tersebut, driver klien harus menentukan tata letak buffer transfer.

Windows Driver Foundation (WDF) menyediakan objek target I/O khusus dalam Kerangka Kerja Driver Mode Kernel (KMDF) dan Kerangka Kerja Driver Mode Pengguna (UMDF) yang menyederhanakan banyak tugas konfigurasi untuk driver klien. Dengan menggunakan objek tersebut, driver klien dapat mengambil informasi tentang konfigurasi saat ini, seperti jumlah antarmuka, pengaturan alternatif dalam setiap antarmuka, dan titik akhirnya. Salah satu objek tersebut, yang disebut objek pipa target, melakukan tugas terkait titik akhir. Artikel ini menjelaskan cara mendapatkan informasi pipa dengan menggunakan objek pipa target.

Untuk driver klien Windows Driver Model (WDM), tumpukan driver USB mengembalikan array struktur USBD_PIPE_INFORMATION . Jumlah elemen dalam array bergantung pada jumlah titik akhir yang ditentukan untuk pengaturan alternatif aktif antarmuka dalam konfigurasi yang dipilih. Setiap elemen berisi informasi tentang pipa yang dibuat untuk titik akhir tertentu. Untuk informasi tentang memilih konfigurasi dan mendapatkan array informasi pipa, lihat Cara Memilih Konfigurasi untuk Perangkat USB.

Apa yang perlu Anda ketahui

Sebelum driver klien dapat menghitung pipa, pastikan persyaratan ini terpenuhi:

  • Driver klien harus membuat objek perangkat target USB kerangka kerja.

    Jika Anda menggunakan templat USB yang disediakan dengan Microsoft Visual Studio Professional 2012, kode templat melakukan tugas tersebut. Kode templat mendapatkan handel ke objek perangkat target dan disimpan dalam konteks perangkat.

    Driver klien KMDF:

    Driver klien KMDF harus mendapatkan handel WDFUSBDEVICE dengan memanggil metode WdfUsbTargetDeviceCreateWithParameters . Untuk informasi selengkapnya, lihat "Kode sumber perangkat" di Memahami struktur kode driver klien USB (KMDF).

    Driver klien UMDF:

    Driver klien UMDF harus mendapatkan penunjuk IWDFUsbTargetDevice dengan mengkueri objek perangkat target kerangka kerja. Untuk informasi selengkapnya, lihat "Implementasi IPnpCallbackHardware dan tugas khusus USB" dalam Memahami struktur kode driver klien USB (UMDF).

  • Perangkat harus memiliki konfigurasi aktif.

    Jika Anda menggunakan templat USB, kode memilih konfigurasi pertama dan pengaturan alternatif default di setiap antarmuka. Untuk informasi tentang cara mengubah pengaturan default tersebut, lihat Cara memilih pengaturan alternatif di antarmuka USB.

    Driver klien KMDF:

    Driver klien KMDF harus memanggil metode WdfUsbTargetDeviceSelectConfig .

    Driver klien UMDF:

    Untuk driver klien UMDF, kerangka kerja memilih konfigurasi pertama dan pengaturan alternatif default untuk setiap antarmuka dalam konfigurasi tersebut.

Mendapatkan handel pipa USB di driver klien KMDF

Kerangka kerja mewakili setiap pipa, yang dibuka oleh tumpukan driver USB, sebagai objek pipa target USB. Driver klien KMDF dapat mengakses metode objek pipa target untuk mendapatkan informasi tentang pipa. Untuk melakukan transfer data, driver klien harus memiliki handel pipa WDFUSBPIPE. Untuk mendapatkan handel pipa, driver harus menghitung antarmuka konfigurasi aktif dan pengaturan alternatif, lalu menghitung titik akhir yang ditentukan di setiap pengaturan. Melakukan operasi enumerasi, untuk setiap transfer data, bisa mahal. Oleh karena itu, salah satu pendekatannya adalah mendapatkan handel pipa setelah perangkat dikonfigurasi dan menyimpannya dalam konteks perangkat yang ditentukan driver. Ketika driver menerima permintaan transfer data, driver dapat mengambil handel pipa yang diperlukan dari konteks perangkat, dan menggunakannya untuk mengirim permintaan. Jika driver klien mengubah konfigurasi perangkat, misalnya, memilih pengaturan alternatif, driver juga harus merefresh konteks perangkat dengan handel pipa baru. Jika tidak, driver dapat secara keliru mengirim permintaan transfer pada handel pipa kedaluarsa.

Handel pipa tidak diperlukan untuk transfer kontrol. Untuk mengirim permintaan transfer kontrol, driver klien WDF memanggil metode WdfUsbDevicexxxx yang diekspos oleh objek perangkat kerangka kerja. Metode tersebut memerlukan handel WDFUSBDEVICE untuk memulai transfer kontrol yang menargetkan titik akhir default. Untuk transfer tersebut, target I/O untuk permintaan adalah titik akhir default dan diwakili oleh handel WDFIOTARGET, yang diabstraksi oleh handel WDFUSBPIPE. Pada tingkat perangkat, handel WDFUSBDEVICE adalah abstraksi dari handel WDFUSBPIPE ke titik akhir default.

Untuk informasi tentang mengirim transfer kontrol dan metode KMDF, lihat Cara mengirim transfer kontrol USB.

  1. Perluas struktur konteks perangkat Anda untuk menyimpan handel pipa.

    Jika Anda mengetahui titik akhir di perangkat Anda, perluas struktur konteks perangkat Anda dengan menambahkan anggota WDFUSBPIPE untuk menyimpan handel pipa USB terkait. Misalnya, Anda dapat memperluas struktur konteks perangkat seperti yang ditunjukkan di sini:

    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. Mendeklarasikan struktur konteks pipa.

    Setiap pipa dapat menyimpan karakteristik terkait titik akhir dalam struktur lain yang disebut konteks pipa. Mirip dengan konteks perangkat, konteks pipa adalah struktur data (ditentukan oleh driver klien) untuk menyimpan informasi tentang pipa yang terkait dengan titik akhir. Selama konfigurasi perangkat, driver klien meneruskan pointer ke konteks pipanya ke kerangka kerja. Kerangka kerja mengalokasikan blok memori berdasarkan ukuran struktur, dan menyimpan pointer ke lokasi memori tersebut dengan objek pipa target USB kerangka kerja. Driver klien dapat menggunakan pointer untuk mengakses dan menyimpan informasi pipa dalam anggota konteks pipa.

    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)
    
    

    Dalam contoh ini, konteks pipa menyimpan jumlah maksimum byte yang dapat dikirim dalam satu transfer. Driver klien dapat menggunakan nilai tersebut untuk menentukan ukuran buffer transfer. Deklarasi juga menyertakan makro WDF_DECLARE_CONTEXT_TYPE_WITH_NAME , yang menghasilkan fungsi sebaris, GetPipeContext. Driver klien dapat memanggil fungsi tersebut untuk mengambil pointer ke blok memori yang menyimpan konteks pipa.

    Untuk informasi selengkapnya tentang konteks, lihat Ruang Konteks Objek Kerangka Kerja.

    Untuk meneruskan penunjuk ke kerangka kerja, driver klien terlebih dahulu menginisialisasi konteks pipanya dengan memanggil WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE. Kemudian, meneruskan penunjuk ke konteks pipa saat memanggil WdfUsbTargetDeviceSelectConfig (untuk memilih konfigurasi) atau WdfUsbInterfaceSelectSetting (untuk memilih pengaturan alternatif).

  3. Setelah permintaan konfigurasi perangkat selesai, hitung antarmuka dan dapatkan handel pipa untuk pipa yang dikonfigurasi. Anda akan memerlukan sekumpulan informasi ini:

    • WDFUSBINTERFACE menangani ke antarmuka yang berisi pengaturan saat ini. Anda bisa mendapatkan handel tersebut dengan menghitung antarmuka dalam konfigurasi aktif. Secara bergantian, jika Anda menyediakan penunjuk ke struktur WDF_USB_DEVICE_SELECT_CONFIG_PARAMS di WdfUsbTargetDeviceSelectConfig, Anda bisa mendapatkan handel dari anggota Type.SingleInterface.ConfiguredUsbInterface (untuk perangkat antarmuka tunggal) atau anggota Type.MultiInterface.Pairs.UsbInterface (untuk perangkat multi-antarmuka).
    • Jumlah pipa yang dibuka untuk titik akhir dalam pengaturan saat ini. Anda bisa mendapatkan nomor tersebut pada antarmuka tertentu dengan memanggil metode WdfUsbInterfaceGetNumConfiguredPipes .
    • WDFUSBPIPE menangani semua pipa yang dikonfigurasi. Anda bisa mendapatkan handel dengan memanggil metode WdfUsbInterfaceGetConfiguredPipe .

    Setelah mendapatkan handel pipa, driver klien dapat memanggil metode untuk menentukan jenis dan arah pipa. Driver dapat memperoleh informasi tentang titik akhir, dalam struktur WDF_USB_PIPE_INFORMATION . Driver dapat memperoleh struktur yang diisi dengan memanggil metode WdfUsbTargetPipeGetInformation . Atau, driver dapat menyediakan pointer ke struktur dalam panggilan WdfUsbInterfaceGetConfiguredPipe .

Contoh kode berikut menghitung pipa dalam pengaturan saat ini. Ini mendapatkan handel pipa untuk titik akhir massal dan interupsi perangkat dan menyimpannya dalam struktur konteks perangkat driver. Ini menyimpan ukuran paket maksimum dari setiap titik akhir dalam konteks pipa terkait. Jika titik akhir mendukung aliran, titik akhir akan membuka aliran statis dengan memanggil rutinitas OpenStreams. Implementasi OpenStreams ditampilkan dalam Cara membuka dan menutup aliran statis di titik akhir massal USB.

Untuk menentukan apakah titik akhir massal tertentu mendukung aliran statis, driver klien memeriksa deskriptor titik akhir. Kode tersebut diimplementasikan dalam rutinitas pembantu bernama, RetrieveStreamInfoFromEndpointDesc, yang ditunjukkan di blok kode berikutnya.

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

Contoh kode berikut menunjukkan rutinitas pembantu bernama, RetrieveStreamInfoFromEndpointDesc, yang dipanggil driver klien saat menghitung pipa.

Dalam contoh kode berikut, driver klien memanggil rutinitas pembantu sebelumnya, RetrieveStreamInfoFromEndpointDesc, sambil menghitung pipa. Pemeriksaan rutin pertama-tama mendapatkan deskriptor konfigurasi dan mengurainya untuk mengambil deskriptor titik akhir. Jika deskriptor titik akhir untuk pipa berisi deskriptor USB_SUPERSPEED_ENDPOINT_COMPANION_DESCRIPTOR_TYPE, driver mengambil jumlah maksimum aliran yang didukung oleh titik akhir.

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

Mendapatkan handel pipa di driver klien UMDF

Driver klien UMDF menggunakan infrastruktur COM dan mengimplementasikan kelas panggilan balik COM yang dipasangkan dengan objek perangkat kerangka kerja. Mirip dengan driver KMDF, driver klien UMDF hanya bisa mendapatkan informasi pipa setelah perangkat dikonfigurasi. Untuk mendapatkan informasi pipa, driver klien harus mendapatkan penunjuk ke antarmuka IWDFUsbTargetPipe dari objek antarmuka kerangka kerja yang berisi pengaturan aktif. Dengan menggunakan penunjuk antarmuka, driver dapat menghitung pipa dalam pengaturan tersebut untuk mendapatkan pointer antarmuka IWDFUsbTargetPipe yang diekspos oleh objek pipa target kerangka kerja.

Sebelum driver mulai menghitung pipa, driver harus tahu tentang konfigurasi perangkat dan titik akhir yang didukung. Berdasarkan informasi itu, driver dapat menyimpan objek pipa sebagai variabel anggota kelas.

Contoh kode berikut memperluas templat USB UMDF yang disediakan dengan Visual Studio Professional 2012. Untuk penjelasan tentang kode starter, lihat "Implementasi IPnpCallbackHardware dan tugas khusus USB" dalam Memahami struktur kode driver klien USB (UMDF).

Perluas deklarasi kelas CDevice seperti yang ditunjukkan di sini. Contoh kode ini mengasumsikan bahwa perangkat adalah papan OSR FX2. Untuk informasi tentang tata letak deskriptornya, lihat Tata Letak Perangkat 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
        );

};

Dalam definisi kelas CDevice, terapkan metode pembantu yang disebut CreateUsbIoTargets. Metode ini dipanggil dari implementasi IPnpCallbackHardware::OnPrepareHardware setelah driver memperoleh pointer ke objek perangkat target.

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

Di UMDF, driver klien menggunakan indeks pipa untuk mengirim permintaan transfer data. Indeks pipa adalah angka yang ditetapkan oleh tumpukan driver USB saat membuka pipa untuk titik akhir dalam pengaturan. Untuk mendapatkan indeks pipa, panggil metode**IWDFUsbTargetPipe::GetInformation**. Metode ini mengisi struktur WINUSB_PIPE_INFORMATION . Nilai PipeId menunjukkan indeks pipa.

Salah satu cara melakukan operasi baca dan tulis pada pipa target adalah dengan memanggil IWDFUsbInterface::GetWinUsbHandle untuk mendapatkan handel WinUSB dan kemudian memanggil fungsi WinUSB. Misalnya, driver dapat memanggil fungsi WinUsb_ReadPipe atau WinUsb_WritePipe . Dalam panggilan fungsi tersebut, driver harus menentukan indeks pipa. Untuk informasi selengkapnya, lihat Cara Mengakses Perangkat USB dengan Menggunakan Fungsi WinUSB.

Handel pipa untuk driver klien berbasis WDM

Setelah konfigurasi dipilih, tumpukan driver USB menyiapkan pipa ke setiap titik akhir perangkat. Tumpukan driver USB mengembalikan array struktur USBD_PIPE_INFORMATION . Jumlah elemen dalam array bergantung pada jumlah titik akhir yang ditentukan untuk pengaturan alternatif aktif antarmuka dalam konfigurasi yang dipilih. Setiap elemen berisi informasi tentang pipa yang dibuat untuk titik akhir tertentu. Untuk informasi selengkapnya tentang mendapatkan handel pipa, lihat Cara Memilih Konfigurasi untuk Perangkat USB.

Untuk membangun permintaan transfer I/O, driver klien harus memiliki handel ke pipa yang terkait dengan titik akhir tersebut. Driver klien dapat memperoleh handel pipa dari anggota PipeHandledari USBD_PIPE_INFORMATION dalam array.

Selain handel pipa, driver klien juga memerlukan jenis pipa. Driver klien dapat menentukan jenis pipa dengan memeriksa anggota PipeType .

Berdasarkan jenis titik akhir, tumpukan driver USB mendukung berbagai jenis pipa. Driver klien dapat menentukan jenis pipa dengan memeriksa anggota PipeTypedari USBD_PIPE_INFORMATION. Berbagai jenis pipa memerlukan berbagai jenis blok permintaan USB (URB) untuk melakukan transaksi I/O.

Driver klien kemudian mengirimkan URB ke tumpukan driver USB. Tumpukan driver USB memproses permintaan dan mengirim data yang ditentukan ke pipa target yang diminta.

URB berisi informasi tentang permintaan seperti handel pipa target, buffer transfer, dan panjangnya. Setiap struktur dalam serikat URB berbagi anggota tertentu: TransferFlags, TransferBuffer, TransferBufferLength, dan TransferBufferMDL. Ada bendera khusus jenis di anggota TransferFlags yang sesuai dengan setiap jenis URB. Untuk semua URL transfer data, bendera USBD_TRANSFER_DIRECTION_IN di TransferFlags menentukan arah transfer. Driver klien mengatur bendera USBD_TRANSFER_DIRECTION_IN untuk membaca data dari perangkat. Driver menghapus bendera ini untuk mengirim data ke perangkat. Data dapat dibaca dari atau ditulis ke residen buffer dalam memori atau MDL. Dalam kedua kasus, driver menentukan ukuran buffer di anggota TransferBufferLength . Driver menyediakan buffer penduduk di anggota TransferBuffer dan MDL di anggota TransferBufferMDL . Mana pun yang disediakan driver, yang lain harus NULL.