Cara menggunakan pembaca berkelanjutan untuk membaca data dari pipa USB

Topik ini menjelaskan objek pembaca berkelanjutan yang disediakan WDF. Prosedur dalam topik ini memberikan instruksi langkah demi langkah tentang cara mengonfigurasi objek dan menggunakannya untuk membaca data dari pipa USB.

Windows Driver Framework (WDF) menyediakan objek khusus yang disebut pembaca berkelanjutan. Objek ini memungkinkan driver klien USB untuk membaca data dari titik akhir massal dan mengganggu terus menerus, selama ada data yang tersedia. Untuk menggunakan pembaca, driver klien harus memiliki handel ke objek pipa target USB yang terkait dengan titik akhir tempat driver membaca data. Titik akhir harus dalam konfigurasi aktif. Anda dapat membuat konfigurasi aktif dengan salah satu dari dua cara: dengan memilih konfigurasi USB atau dengan mengubah pengaturan alternatif dalam konfigurasi saat ini. Untuk informasi selengkapnya tentang operasi tersebut, lihat Cara Memilih Konfigurasi untuk Perangkat USB dan Cara memilih pengaturan alternatif di antarmuka USB.

Setelah membuat pembaca berkelanjutan, driver klien dapat memulai dan menghentikan pembaca sebagai dan bila perlu. Pembaca berkelanjutan yang memastikan bahwa permintaan baca selalu tersedia pada objek pipa target dan driver klien selalu siap untuk menerima data dari titik akhir.

Pembaca berkelanjutan tidak secara otomatis didukung oleh kerangka kerja. Ini berarti bahwa driver klien harus menghentikan pembaca ketika perangkat memasuki status daya yang lebih rendah dan menghidupkan ulang pembaca ketika perangkat memasuki status kerja.

Artikel ini menggunakan:

Sebelum memulai

Sebelum driver klien dapat menggunakan pembaca berkelanjutan, pastikan bahwa persyaratan ini terpenuhi:

  • Perangkat USB Anda harus memiliki titik akhir IN. Periksa konfigurasi perangkat di USBView. Usbview.exe adalah aplikasi yang memungkinkan Anda menelusuri semua pengontrol USB dan perangkat USB yang terhubung dengannya. Biasanya, USBView diinstal di folder Debuggers di Windows Driver Kit (WDK).

  • 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 pointer 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 alternatif, 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.

  • Driver klien harus memiliki handel ke objek pipa target kerangka kerja untuk titik akhir IN. Untuk informasi selengkapnya, lihat Cara menghitung pipa USB.

Menggunakan pembaca berkelanjutan dalam driver klien KMDF

Sebelum mulai menggunakan pembaca berkelanjutan, Anda harus mengonfigurasinya dengan menginisialisasi struktur WDF_USB_CONTINUOUS_READER_CONFIG .

Mengonfigurasi pembaca berkelanjutan dalam driver klien KMDF

  1. Inisialisasi struktur WDF_USB_CONTINUOUS_READER_CONFIG dengan memanggil makro WDF_USB_CONTINUOUS_READER_CONFIG_INIT.

  2. Tentukan opsi konfigurasinya dalam struktur WDF_USB_CONTINUOUS_READER_CONFIG.

  3. Panggil metode WdfUsbTargetPipeConfigContinuousReader.

    Contoh kode berikut mengonfigurasi pembaca berkelanjutan untuk objek pipa target yang ditentukan.

    NTSTATUS FX3ConfigureContinuousReader(
        _In_ WDFDEVICE Device,
        _In_ WDFUSBPIPE Pipe)
    {
        NTSTATUS status;
        PDEVICE_CONTEXT                     pDeviceContext;
        WDF_USB_CONTINUOUS_READER_CONFIG    readerConfig;
        PPIPE_CONTEXT                       pipeContext;
    
        PAGED_CODE();
    
        pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
        pipeContext = GetPipeContext (Pipe);
    
        WDF_USB_CONTINUOUS_READER_CONFIG_INIT(
            &readerConfig,
            FX3EvtReadComplete,
            pDeviceContext,
            pipeContext->MaxPacketSize);
    
        readerConfig.EvtUsbTargetPipeReadersFailed=FX3EvtReadFailed;
    
        status = WdfUsbTargetPipeConfigContinuousReader(
            Pipe,
            &readerConfig);
    
        if (!NT_SUCCESS (status))
        {
            TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
                "%!FUNC! WdfUsbTargetPipeConfigContinuousReader failed 0x%x", status);
    
            goto Exit;
        }
    
    Exit:
        return status;
    }
    

Biasanya driver klien mengonfigurasi pembaca berkelanjutan dalam fungsi panggilan balik EvtDevicePrepareHardware setelah menghitung objek pipa target dalam pengaturan aktif.

Dalam contoh sebelumnya, driver klien menentukan opsi konfigurasinya dengan dua cara. Pertama dengan memanggil WDF_USB_CONTINUOUS_READER_CONFIG_INIT lalu dengan mengatur anggota WDF_USB_CONTINUOUS_READER_CONFIG. Perhatikan parameter untuk WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Nilai-nilai ini wajib. Dalam contoh ini, driver klien menentukan:

  • Penunjuk ke rutinitas penyelesaian yang diterapkan driver. Kerangka kerja memanggil rutinitas ini ketika menyelesaikan permintaan baca. Dalam rutinitas penyelesaian, driver dapat mengakses lokasi memori yang berisi data yang dibaca. Implementasi rutinitas penyelesaian dibahas pada langkah 2.
  • Penunjuk ke konteks yang ditentukan driver.
  • Jumlah byte yang dapat dibaca dari perangkat dalam satu transfer. Driver klien dapat memperoleh informasi tersebut dalam struktur WDF_USB_PIPE_INFORMATION dengan memanggil metode WdfUsbInterfaceGetConfiguredPipe atau WdfUsbTargetPipeGetInformation. Untuk informasi selengkapnya, lihat Cara menghitung pipa USB.

WDF_USB_CONTINUOUS_READER_CONFIG_INIT mengonfigurasi pembaca berkelanjutan untuk menggunakan nilai default untuk NumPendingReads. Nilai tersebut menentukan jumlah permintaan baca yang ditambahkan kerangka kerja ke antrean yang tertunda. Nilai default telah ditentukan untuk memberikan performa yang cukup baik untuk banyak perangkat pada banyak konfigurasi prosesor.

Selain parameter konfigurasi yang ditentukan dalam WDF_USB_CONTINUOUS_READER_CONFIG_INIT, contohnya juga menetapkan rutinitas kegagalan dalam WDF_USB_CONTINUOUS_READER_CONFIG. Rutinitas kegagalan ini bersifat opsional.

Selain rutinitas kegagalan, ada anggota lain di WDF_USB_CONTINUOUS_READER_CONFIG yang dapat digunakan driver klien untuk menentukan tata letak buffer transfer. Misalnya, pertimbangkan driver jaringan yang menggunakan pembaca berkelanjutan untuk menerima paket jaringan. Setiap paket berisi data header, payload, dan footer. Untuk menjelaskan paket, driver harus terlebih dahulu menentukan ukuran paket dalam panggilannya ke WDF_USB_CONTINUOUS_READER_CONFIG_INIT. Kemudian, driver harus menentukan panjang header dan footer dengan mengatur anggota HeaderLength dan TrailerLength dari WDF_USB_CONTINUOUS_READER_CONFIG. Kerangka kerja menggunakan nilai-nilai tersebut untuk menghitung offset byte di salah satu sisi payload. Saat data payload dibaca dari titik akhir, kerangka kerja menyimpan data tersebut di bagian buffer antara offset.

Menerapkan rutinitas penyelesaian

Kerangka kerja memanggil rutinitas penyelesaian yang diterapkan driver klien setiap kali permintaan selesai. Kerangka kerja meneruskan jumlah byte yang dibaca dan objek WDFMEMORY yang buffernya berisi data yang dibaca dari pipa.

Contoh kode berikut menunjukkan implementasi rutin penyelesaian.

EVT_WDF_USB_READER_COMPLETION_ROUTINE FX3EvtReadComplete;

VOID FX3EvtReadComplete(
    __in  WDFUSBPIPE Pipe,
    __in  WDFMEMORY Buffer,
    __in  size_t NumBytesTransferred,
    __in  WDFCONTEXT Context
    )
{
    PDEVICE_CONTEXT  pDeviceContext;
    PVOID  requestBuffer;

    pDeviceContext = (PDEVICE_CONTEXT)Context;

    if (NumBytesTransferred == 0)
    {
        return;
    }

    requestBuffer = WdfMemoryGetBuffer(Buffer, NULL);

    if (Pipe == pDeviceContext->InterruptPipe)
    {
        KdPrintEx(( DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL,
                                "Interrupt endpoint: %s.\n",
                                requestBuffer ));
    }

    return;
}

Kerangka kerja memanggil rutinitas penyelesaian yang diterapkan driver klien setiap kali permintaan selesai. Kerangka kerja mengalokasikan objek memori untuk setiap operasi baca. Dalam rutinitas penyelesaian, kerangka kerja meneruskan jumlah byte yang dibaca dan handel WDFMEMORY ke objek memori. Buffer objek memori berisi data yang dibaca dari pipa. Driver klien tidak boleh membebaskan objek memori. Kerangka kerja merilis objek setelah setiap rutinitas penyelesaian kembali. Jika driver klien ingin menyimpan data yang diterima, driver harus menyalin konten buffer dalam rutinitas penyelesaian.

Menerapkan rutinitas kegagalan

Kerangka kerja memanggil rutinitas kegagalan yang diterapkan driver klien untuk memberi tahu driver bahwa pembaca berkelanjutan telah melaporkan kesalahan saat memproses permintaan baca. Kerangka kerja meneruskan penunjuk ke objek pipa target tempat permintaan gagal dan nilai kode kesalahan. Berdasarkan nilai kode kesalahan tersebut, driver dapat menerapkan mekanisme pemulihan kesalahannya. Driver juga harus mengembalikan nilai yang sesuai yang menunjukkan kerangka kerja apakah kerangka kerja harus menghidupkan ulang pembaca berkelanjutan.

Contoh kode berikut menunjukkan implementasi rutin kegagalan.

EVT_WDF_USB_READERS_FAILED FX3EvtReadFailed;

BOOLEAN
FX3EvtReadFailed(
    WDFUSBPIPE      Pipe,
    NTSTATUS        Status,
    USBD_STATUS     UsbdStatus
    )
{
    UNREFERENCED_PARAMETER(Status);

    TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! ReadersFailedCallback failed NTSTATUS 0x%x, UsbdStatus 0x%x\n",
                    status,
                    UsbdStatus);

    return TRUE;
}

Dalam contoh sebelumnya, driver mengembalikan TRUE. Nilai ini menunjukkan kepada kerangka kerja bahwa ia harus mengatur ulang pipa dan kemudian menghidupkan ulang pembaca berkelanjutan.

Atau, driver klien dapat mengembalikan FALSE dan memberikan mekanisme pemulihan kesalahan jika kondisi stall terjadi pada pipa. Misalnya, driver dapat memeriksa status USBD dan mengeluarkan permintaan reset-pipe untuk menghapus kondisi kios.

Untuk informasi tentang pemulihan kesalahan dalam pipa, lihat Cara memulihkan dari kesalahan pipa USB.

Memulai dan menghentikan pembaca berkelanjutan

Instruksikan kerangka kerja untuk memulai pembaca berkelanjutan saat perangkat memasuki status kerja; hentikan pembaca saat perangkat meninggalkan status kerja. Panggil metode ini dan tentukan objek pipa target sebagai objek target I/O.

Pembaca berkelanjutan tidak secara otomatis didukung oleh kerangka kerja. Oleh karena itu, driver klien harus secara eksplisit memulai atau menghentikan objek pipa target ketika status daya perangkat berubah. Driver memanggil WdfIoTargetStart dalam implementasi EvtDeviceD0Entry driver. Panggilan ini memastikan bahwa antrean mengirimkan permintaan hanya ketika perangkat dalam status kerja. Sebaliknya, driver memanggil WdfIoTargetStop dalam implementasi driver EvtDeviceD0Exit sehingga antrean berhenti mengirimkan permintaan ketika perangkat memasuki status daya yang lebih rendah.

Contoh kode berikut mengonfigurasi pembaca berkelanjutan untuk objek pipa target yang ditentukan.

EVT_WDF_DEVICE_D0_ENTRY FX3EvtDeviceD0Entry;

NTSTATUS FX3EvtDeviceD0Entry(
    __in  WDFDEVICE Device,
    __in  WDF_POWER_DEVICE_STATE PreviousState
    )
{
    PDEVICE_CONTEXT  pDeviceContext;
    NTSTATUS status;

    PAGED_CODE();

    pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
    status = WdfIoTargetStart (WdfUsbTargetPipeGetIoTarget (pDeviceContext->InterruptPipe));

    if (!NT_SUCCESS (status))
    {
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE,
            "%!FUNC! Could not start interrupt pipe failed 0x%x", status);
    }
}

EVT_WDF_DEVICE_D0_EXIT FX3EvtDeviceD0Exit;

NTSTATUS FX3EvtDeviceD0Exit(
    __in  WDFDEVICE Device,
    __in  WDF_POWER_DEVICE_STATE TargetState
    )
{
    PDEVICE_CONTEXT  pDeviceContext;
    NTSTATUS status;
    PAGED_CODE();
    pDeviceContext = WdfObjectGet_DEVICE_CONTEXT(Device);
    WdfIoTargetStop (WdfUsbTargetPipeGetIoTarget (pDeviceContext->InterruptPipe), WdfIoTargetCancelSentIo));
}

Contoh sebelumnya menunjukkan implementasi untuk rutinitas panggilan balik EvtDeviceD0Entry dan EvtDeviceD0Exit. Parameter Tindakan WdfIoTargetStop memungkinkan driver klien untuk memutuskan tindakan untuk permintaan yang tertunda dalam antrean ketika perangkat meninggalkan status kerja. Dalam contoh, driver menentukan WdfIoTargetCancelSentIo. Opsi itu menginstruksikan kerangka kerja untuk membatalkan semua permintaan yang tertunda dalam antrean. Atau, driver dapat menginstruksikan kerangka kerja untuk menunggu permintaan tertunda selesai sebelum menghentikan target I/O atau menyimpan permintaan yang tertunda dan melanjutkan saat target I/O dimulai ulang.

Menggunakan pembaca berkelanjutan dalam driver klien UMDF

Sebelum mulai menggunakan pembaca berkelanjutan, Anda harus mengonfigurasi pembaca dalam implementasi metode IPnpCallbackHardware::OnPrepareHardware Anda. Setelah Anda mendapatkan penunjuk ke antarmuka IWDFUsbTargetPipe dari objek pipa target yang terkait dengan titik akhir IN, lakukan langkah-langkah berikut:

Mengonfigurasi pembaca berkelanjutan dalam driver klien UMDF

  1. Panggil QueryInterface pada objek pipa target (IWDFUsbTargetPipe) dan kueri untuk antarmuka IWDFUsbTargetPipe2 .

  2. Panggil QueryInterface pada objek panggilan balik perangkat dan kueri untuk antarmuka IUsbTargetPipeContinuousReaderCallbackReadComplete . Untuk menggunakan pembaca berkelanjutan, Anda harus menerapkan IUsbTargetPipeContinuousReaderCallbackReadComplete. Implementasinya dijelaskan nanti dalam topik ini.

  3. Panggil QueryInterface pada objek panggilan balik perangkat dan kueri untuk antarmuka IUsbTargetPipeContinuousReaderCallbackReadersFailed jika Anda telah menerapkan panggilan balik kegagalan. Implementasinya dijelaskan nanti dalam topik ini.

  4. Panggil metode IWDFUsbTargetPipe2::ConfigureContinuousReader dan tentukan parameter konfigurasi, seperti header, trailer, jumlah permintaan yang tertunda, dan referensi ke metode panggilan balik penyelesaian dan kegagalan.

    Metode ini mengonfigurasi pembaca berkelanjutan untuk objek pipa target. Pembaca berkelanjutan membuat antrean yang mengelola serangkaian permintaan baca saat dikirim dan diterima dari objek pipa target.

Contoh kode berikut mengonfigurasi pembaca berkelanjutan untuk objek pipa target yang ditentukan. Contoh mengasumsikan bahwa objek pipa target yang ditentukan oleh pemanggil dikaitkan dengan titik akhir IN. Pembaca berkelanjutan dikonfigurasi untuk membaca byte USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; untuk menggunakan jumlah default permintaan yang tertunda menggunakan oleh kerangka kerja; untuk memanggil metode penyelesaian dan panggilan balik yang disediakan driver klien. Buffer yang diterima tidak akan berisi data header atau trailer apa pun.

HRESULT CDeviceCallback::ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe)
{
    if (!pFxPipe)
    {
        return E_INVALIDARG;
    }

    IUsbTargetPipeContinuousReaderCallbackReadComplete *pOnCompletionCallback = NULL;
    IUsbTargetPipeContinuousReaderCallbackReadersFailed *pOnFailureCallback = NULL;
    IWDFUsbTargetPipe2* pFxUsbPipe2 = NULL;

    HRESULT hr = S_OK;

    // Set up the continuous reader to read from the target pipe object.

    //Get a pointer to the target pipe2 object.
    hr = pFxPipe->QueryInterface(IID_PPV_ARGS(&pFxUsbPipe2));
    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

    //Get a pointer to the completion callback.
    hr = QueryInterface(IID_PPV_ARGS(&pOnCompletionCallback));
    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

    //Get a pointer to the failure callback.
    hr = QueryInterface(IID_PPV_ARGS(&pOnFailureCallback));
    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

    //Get a pointer to the target pipe2 object.
    hr = pFxUsbPipe2->ConfigureContinuousReader (
        USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE, //size of data to be read
        0, //Header
        0, //Trailer
        0, // Number of pending requests queued by WDF
        NULL, // Cleanup callback. Not provided.
        pOnCompletionCallback, //Completion routine.
        NULL, //Completion routine context. Not provided.
        pOnFailureCallback); //Failure routine. Not provided

    if (FAILED(hr))
    {
        goto ConfigureContinuousReaderExit;
    }

ConfigureContinuousReaderExit:

    if (pOnFailureCallback)
    {
        pOnFailureCallback->Release();
        pOnFailureCallback = NULL;
    }

    if (pOnCompletionCallback)
    {
        pOnCompletionCallback->Release();
        pOnCompletionCallback = NULL;
    }

    if (pFxUsbPipe2)
    {
        pFxUsbPipe2->Release();
        pFxUsbPipe2 = NULL;
    }

    return hr;
}

Selanjutnya, tentukan status objek pipa target, saat perangkat masuk dan keluar dari status kerja (D0).

Jika driver klien menggunakan antrean yang dikelola daya untuk mengirim permintaan ke pipa, antrean mengirimkan permintaan hanya ketika perangkat berada dalam status D0 . Jika status daya perangkat berubah dari D0 ke status daya yang lebih rendah (pada keluar D0 ), objek pipa target menyelesaikan permintaan yang tertunda dan antrean berhenti mengirimkan permintaan ke objek pipa target. Oleh karena itu, driver klien tidak diperlukan untuk memulai dan menghentikan objek pipa target.

Pembaca berkelanjutan tidak menggunakan antrean yang dikelola daya untuk mengirimkan permintaan. Oleh karena itu, Anda harus secara eksplisit memulai atau menghentikan objek pipa target saat status daya perangkat berubah. Untuk mengubah status objek pipa target, Anda dapat menggunakan antarmuka IWDFIoTargetStateManagement yang diterapkan oleh kerangka kerja. Setelah Anda mendapatkan penunjuk ke antarmuka IWDFUsbTargetPipe dari objek pipa target yang terkait dengan titik akhir IN, lakukan langkah-langkah berikut:

Menerapkan manajemen status

  1. Dalam implementasi IPnpCallbackHardware::OnPrepareHardware, panggil QueryInterface pada objek pipa target (IWDFUsbTargetPipe) dan kueri untuk antarmuka IWDFIoTargetStateManagement. Simpan referensi dalam variabel anggota kelas panggilan balik perangkat Anda.

  2. Terapkan antarmuka IPnpCallback pada objek panggilan balik perangkat.

  3. Dalam implementasi metode IPnpCallback::OnD0Entry, panggil IWDFIoTargetStateManagement::Start untuk memulai pembaca berkelanjutan.

  4. Dalam implementasi metode IPnpCallback::OnD0Exit, panggil IWDFIoTargetStateManagement::Stop untuk menghentikan pembaca berkelanjutan.

Setelah perangkat memasuki status kerja (D0), kerangka kerja memanggil metode panggilan balik D0-entry yang disediakan driver klien yang memulai objek pipa target. Ketika perangkat meninggalkan status D0 , kerangka kerja memanggil metode panggilan balik D0-exit. Objek pipa target menyelesaikan jumlah permintaan baca yang tertunda, dikonfigurasi oleh driver klien, dan berhenti menerima permintaan baru. Contoh kode berikut mengimplementasikan antarmuka IPnpCallback pada objek panggilan balik perangkat.

class CDeviceCallback :
    public IPnpCallbackHardware,
    public IPnpCallback,
{
public:
    CDeviceCallback();
    ~CDeviceCallback();
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
    virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);

    virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice*  pWdfDevice);

private:
    LONG m_cRefs;
    IWDFUsbTargetPipe* m_pFxUsbPipe;
    IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;

    HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
    HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};

Contoh kode berikut menunjukkan cara mendapatkan penunjuk ke antarmuka IWDFIoTargetStateManagement dari objek pipa target dalam metode IPnpCallback::OnPrepareHardware

   //Enumerate the endpoints and get the interrupt pipe.
    for (UCHAR index = 0; index < NumEndpoints; index++)
    {
        hr = pFxInterface->RetrieveUsbPipeObject(index, &pFxPipe);

        if (SUCCEEDED (hr) && pFxPipe)
        {
            if ((pFxPipe->IsInEndPoint()) && (pFxPipe->GetType()==UsbdPipeTypeInterrupt))
            {
                //Pipe is for an interrupt IN endpoint.
                hr = pFxPipe->QueryInterface(IID_PPV_ARGS(&m_pFxIoTargetInterruptPipeStateMgmt));

                if (m_pFxIoTargetInterruptPipeStateMgmt)
                {
                    m_pFxUsbPipe = pFxPipe;
                    break;
                }

            }
            else
            {
                //Pipe is NOT for an interrupt IN endpoint.
                pFxPipe->Release();
                pFxPipe = NULL;
            }
        }
        else
        {
             //Pipe not found.
        }
    }

Contoh kode berikut menunjukkan cara mendapatkan pointer ke antarmuka IWDFIoTargetStateManagement dari objek pipa target di metode IPnpCallbackHardware::OnPrepareHardware.

 HRESULT CDeviceCallback::OnD0Entry(
    IWDFDevice*  pWdfDevice,
    WDF_POWER_DEVICE_STATE  previousState
    )
{

    if (!m_pFxIoTargetInterruptPipeStateMgmt)
    {
        return E_FAIL;
    }

    HRESULT hr = m_pFxIoTargetInterruptPipeStateMgmt->Start();

    if (FAILED (hr))
    {
        goto OnD0EntryExit;
    }

OnD0EntryExit:
    return hr;
}

HRESULT CDeviceCallback::OnD0Exit(
    IWDFDevice*  pWdfDevice,
    WDF_POWER_DEVICE_STATE  previousState
    )
{
    if (!m_pFxIoTargetInterruptPipeStateMgmt)
    {
        return E_FAIL;
    }

    // Stop the I/O target always succeeds.
    (void)m_pFxIoTargetInterruptPipeStateMgmt->Stop(WdfIoTargetCancelSentIo);

    return S_OK;
}

Setelah pembaca berkelanjutan menyelesaikan permintaan baca, driver klien harus menyediakan cara untuk mendapatkan pemberitahuan ketika permintaan berhasil menyelesaikan permintaan baca. Driver klien harus menambahkan kode ini ke objek panggilan balik perangkat.

Berikan panggilan balik penyelesaian dengan menerapkan IUsbTargetPipeContinuousReaderCallbackReadComplete

  1. Terapkan antarmuka IUsbTargetPipeContinuousReaderCallbackReadComplete pada objek panggilan balik perangkat.

  2. Pastikan implementasi QueryInterface dari objek panggilan balik perangkat bertambah jumlah referensi objek panggilan balik lalu mengembalikan penunjuk antarmuka IUsbTargetPipeContinuousReaderCallbackReadComplete.

  3. Dalam implementasi metode IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion , akses baca data yang dibaca dari pipa. Parameter pMemory menunjuk ke memori yang dialokasikan oleh kerangka kerja yang berisi data. Anda dapat memanggil IWDFMemory::GetDataBuffer untuk mendapatkan buffer yang berisi data. Buffer menyertakan header namun panjang data yang ditunjukkan oleh parameter NumBytesTransferred onReaderCompletion tidak menyertakan panjang header. Panjang header ditentukan oleh driver klien saat mengonfigurasi pembaca berkelanjutan dalam panggilan driver ke IWDFUsbTargetPipe2::ConfigureContinuousReader.

  4. Berikan penunjuk ke panggilan balik penyelesaian dalam parameter pOnCompletion dari metode IWDFUsbTargetPipe2::ConfigureContinuousReader .

Setiap kali data tersedia di titik akhir pada perangkat, objek pipa target menyelesaikan permintaan baca. Jika permintaan baca berhasil diselesaikan, kerangka kerja memberi tahu driver klien dengan memanggil IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. Jika tidak, kerangka kerja memanggil panggilan balik kegagalan yang disediakan driver klien ketika objek pipa target melaporkan kesalahan pada permintaan baca.

Contoh kode berikut mengimplementasikan antarmuka IUsbTargetPipeContinuousReaderCallbackReadComplete pada objek panggilan balik perangkat.

class CDeviceCallback :
    public IPnpCallbackHardware,
    public IPnpCallback,
    public IUsbTargetPipeContinuousReaderCallbackReadComplete

{
public:
    CDeviceCallback();
    ~CDeviceCallback();
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
    virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);

    virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice*  pWdfDevice);

    virtual VOID STDMETHODCALLTYPE OnReaderCompletion(IWDFUsbTargetPipe* pPipe, IWDFMemory* pMemory, SIZE_T NumBytesTransferred, PVOID Context);

private:
    LONG m_cRefs;
    IWDFUsbTargetPipe* m_pFxUsbPipe;
    IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;

    HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
    HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};

Contoh kode berikut menunjukkan implementasi QueryInterface dari objek panggilan balik perangkat.

HRESULT CDeviceCallback::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
    if (ppvObject == NULL)
    {
        return E_INVALIDARG;
    }

    *ppvObject = NULL;

    HRESULT hr = E_NOINTERFACE;

    if(  IsEqualIID(riid, __uuidof(IPnpCallbackHardware))   ||  IsEqualIID(riid, __uuidof(IUnknown))  )
    {
        *ppvObject = static_cast<IPnpCallbackHardware*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IPnpCallback)))
    {
        *ppvObject = static_cast<IPnpCallback*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadComplete)))
    {
        *ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadComplete*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    return hr;
}

Contoh kode berikut menunjukkan cara mendapatkan data dari buffer yang dikembalikan oleh IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion. Setiap kali objek pipa target berhasil menyelesaikan permintaan baca, kerangka kerja memanggil OnReaderCompletion. Contoh mendapatkan buffer yang berisi data dan mencetak konten pada output debugger.

 VOID CDeviceCallback::OnReaderCompletion(
    IWDFUsbTargetPipe* pPipe,
    IWDFMemory* pMemory,
    SIZE_T NumBytesTransferred,
    PVOID Context)
{
    if (pPipe != m_pFxUsbInterruptPipe)
    {
        return;
    }

    if (NumBytesTransferred == 0)
    {
        // NumBytesTransferred is zero.
        return;
    }

    PVOID pBuff = NULL;
    LONG CurrentData = 0;
    char data[20];

    pBuff = pMemory->GetDataBuffer(NULL);

    if (pBuff)
    {
        CopyMemory(&CurrentData, pBuff, sizeof(CurrentData));
        sprintf_s(data, 20, "%d\n", CurrentData);
        OutputDebugString(data);
        pBuff = NULL;
    }
    else
    {
        OutputDebugString(TEXT("Unable to get data buffer."));
    }
}

Driver klien bisa mendapatkan pemberitahuan dari kerangka kerja ketika kegagalan terjadi di objek pipa target saat menyelesaikan permintaan baca. Untuk mendapatkan pemberitahuan, driver klien harus menerapkan panggilan balik kegagalan dan menyediakan penunjuk ke panggilan balik saat mengonfigurasi pembaca berkelanjutan. Prosedur berikut menjelaskan cara menerapkan panggilan balik kegagalan.

Berikan panggilan balik kegagalan dengan menerapkan IUsbTargetPipeContinuousReaderCallbackReadersFailed

  1. Terapkan antarmuka IUsbTargetPipeContinuousReaderCallbackReadersFailed pada objek panggilan balik perangkat.

  2. Pastikan implementasi QueryInterface dari objek panggilan balik perangkat bertambah jumlah referensi objek panggilan balik lalu mengembalikan penunjuk antarmuka IUsbTargetPipeContinuousReaderCallbackReadersFailed.

  3. Dalam implementasi metode IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure , berikan penanganan kesalahan permintaan baca yang gagal.

    Jika pembaca berkelanjutan gagal menyelesaikan permintaan baca dan driver klien menyediakan panggilan balik kegagalan, kerangka kerja memanggil metode IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure. Kerangka kerja menyediakan nilai HRESULT dalam parameter hrStatus yang menunjukkan kode kesalahan yang terjadi di objek pipa target. Berdasarkan kode kesalahan tersebut, Anda mungkin memberikan penanganan kesalahan tertentu. Misalnya, jika Anda ingin kerangka kerja mengatur ulang pipa lalu memulai ulang pembaca berkelanjutan, pastikan bahwa panggilan balik mengembalikan TRUE.

    Catatan Jangan panggil IWDFIoTargetStateManagement::Start dan IWDFIoTargetStateManagement::Stop dalam panggilan balik kegagalan.

  4. Berikan penunjuk ke panggilan balik kegagalan dalam parameter pOnFailure dari metode IWDFUsbTargetPipe2::ConfigureContinuousReader.

Contoh kode berikut mengimplementasikan antarmuka IUsbTargetPipeContinuousReaderCallbackReadersFailed pada objek panggilan balik perangkat.

class CDeviceCallback :
    public IPnpCallbackHardware,
    public IPnpCallback,
    public IUsbTargetPipeContinuousReaderCallbackReadComplete,
    public IUsbTargetPipeContinuousReaderCallbackReadersFailed
{
public:
    CDeviceCallback();
    ~CDeviceCallback();
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID** ppvObject);
    virtual ULONG STDMETHODCALLTYPE AddRef();
    virtual ULONG STDMETHODCALLTYPE Release();

    virtual HRESULT STDMETHODCALLTYPE OnPrepareHardware(IWDFDevice* pDevice);
    virtual HRESULT STDMETHODCALLTYPE OnReleaseHardware(IWDFDevice* pDevice);

    virtual HRESULT STDMETHODCALLTYPE OnD0Entry(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual HRESULT STDMETHODCALLTYPE OnD0Exit(IWDFDevice*  pWdfDevice, WDF_POWER_DEVICE_STATE  previousState);
    virtual void STDMETHODCALLTYPE OnSurpriseRemoval(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryRemove(IWDFDevice*  pWdfDevice);
    virtual HRESULT STDMETHODCALLTYPE OnQueryStop(IWDFDevice*  pWdfDevice);

    virtual VOID STDMETHODCALLTYPE OnReaderCompletion(IWDFUsbTargetPipe* pPipe, IWDFMemory* pMemory, SIZE_T NumBytesTransferred, PVOID Context);
    virtual BOOL STDMETHODCALLTYPE OnReaderFailure(IWDFUsbTargetPipe * pPipe, HRESULT hrCompletion);

private:
    LONG m_cRefs;
    IWDFUsbTargetPipe* m_pFxUsbInterruptPipe;
    IWDFIoTargetStateManagement* m_pFxIoTargetInterruptPipeStateMgmt;

    HRESULT CreateUSBTargetDeviceObject (IWDFDevice* pFxDevice, IWDFUsbTargetDevice** ppUSBTargetDevice);
    HRESULT RetrieveUSBDeviceDescriptor (IWDFUsbTargetDevice* pUSBTargetDevice, PUSB_DEVICE_DESCRIPTOR DescriptorHeader, PULONG cbDescriptor);
    HRESULT ConfigureContinuousReader (IWDFUsbTargetPipe* pFxPipe);
};

Contoh kode berikut menunjukkan implementasi QueryInterface dari objek panggilan balik perangkat.

HRESULT CDeviceCallback::QueryInterface(REFIID riid, LPVOID* ppvObject)
{
    if (ppvObject == NULL)
    {
        return E_INVALIDARG;
    }

    *ppvObject = NULL;

    HRESULT hr = E_NOINTERFACE;

    if(  IsEqualIID(riid, __uuidof(IPnpCallbackHardware))   ||  IsEqualIID(riid, __uuidof(IUnknown))  )
    {
        *ppvObject = static_cast<IPnpCallbackHardware*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;

    }

    if(  IsEqualIID(riid, __uuidof(IPnpCallback)))
    {
        *ppvObject = static_cast<IPnpCallback*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadComplete)))
    {
        *ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadComplete*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    if(  IsEqualIID(riid, __uuidof(IUsbTargetPipeContinuousReaderCallbackReadersFailed)))
    {
        *ppvObject = static_cast<IUsbTargetPipeContinuousReaderCallbackReadersFailed*>(this);
        reinterpret_cast<IUnknown*>(*ppvObject)->AddRef();
        hr = S_OK;
    }

    return hr;
}

Contoh kode berikut menunjukkan implementasi panggilan balik kegagalan. Jika permintaan baca gagal, metode mencetak kode kesalahan yang dilaporkan oleh kerangka kerja dalam debugger dan menginstruksikan kerangka kerja untuk mengatur ulang pipa lalu memulai ulang pembaca berkelanjutan.

 BOOL CDeviceCallback::OnReaderFailure(
    IWDFUsbTargetPipe * pPipe,
    HRESULT hrCompletion
    )
{
    UNREFERENCED_PARAMETER(pPipe);
    UNREFERENCED_PARAMETER(hrCompletion);
    return TRUE;
}

Jika driver klien tidak memberikan panggilan balik kegagalan dan terjadi kesalahan, kerangka kerja mengatur ulang pipa USB dan memulai ulang pembaca berkelanjutan.