連続リーダーを使用して USB パイプからデータを読み取る方法

このトピックでは、WDF で提供される連続リーダー オブジェクトについて説明します。 このトピックの手順では、オブジェクトを構成し、それを使用して USB パイプからデータを読み取る方法についてステップ バイ ステップで説明します。

Windows Driver Framework (WDF) は、連続リーダーと呼ばれる特殊なオブジェクトを提供します。 このオブジェクトを使用すると、USB クライアント ドライバーは、使用可能なデータがある限り、一括エンドポイントおよび割り込みエンドポイントから連続的にデータを読み取ります。 リーダーを使用するには、クライアント ドライバーには、ドライバーがデータを読み取るエンドポイントに関連付けられている USB ターゲット パイプ オブジェクトへのハンドルが必要です。 エンドポイントはアクティブな構成である必要があります。 構成をアクティブにするには、USB 構成を選択するか、現在の構成の代替設定を変更します。 これらの操作の詳細については、「USB デバイスの構成を選択する方法」および「USB インターフェイスで代替設定を選択する方法」を参照してください。

連続リーダーを作成した後、クライアント ドライバーは、必要に応じて、リーダーを開始および停止できます。 ターゲット パイプ オブジェクト上で読み取り要求が常に利用可能であり、クライアント ドライバーが常にエンドポイントからデータを受信できる状態にあることを保証する連続リーダー。

連続リーダーは、フレームワークによって自動的に電源管理されません。 これは、クライアント ドライバーは、デバイスが低電力状態になったときにリーダーを停止し、デバイスが動作状態になったときにリーダーを再起動する必要があることを意味します。

この記事では、以下を利用します。

開始する前に

クライアント ドライバーが連続リーダーを使用する前に、次の要件が満たされていることを確認します。

  • USB デバイスには IN エンドポイントが必要です。 USBView でデバイス構成を確認します。 Usbview.exe は、すべての USB コントローラーと接続されている USB デバイスを参照できるアプリケーションです。 通常、USBView が、Windows ドライバー キット (WDK) の Debuggers フォルダーにインストールされることもあります。

  • クライアント ドライバーによって、フレームワーク USB ターゲット デバイス オブジェクトが作成されている必要があります。

    Microsoft Visual Studio Professional 2012 に付属する USB テンプレートを使用している場合、テンプレート コードでこれらのタスクが実行されます。 テンプレート コードによりターゲット デバイス オブジェクトのハンドルが取得され、デバイス コンテキストに格納されます。

    KMDF クライアント ドライバー:

    KMDF クライアント ドライバーでは、WdfUsbTargetDeviceCreateWithParameters メソッドを呼び出すことで WDFUSBDEVICE ハンドルを取得する必要があります。 詳細については、「USB クライアント ドライバー コード構造について (KMDF)」の「デバイスのソース コード」を参照してください。

    UMDF クライアント ドライバー:

    UMDF クライアント ドライバーは、フレームワーク ターゲット デバイス オブジェクトを問い合わせることで、IWDFUsbTargetDevice ポインターを取得する必要があります。 詳細については、「USB クライアント ドライバー コード構造について (UMDF)」の「IPnpCallbackHardware 実装と USB 固有のタスク」を参照してください。

  • デバイスにはアクティブな構成が必要です。

    USB テンプレートを使用している場合、コードは各インターフェイスの最初の構成と既定の代替設定を選択します。 代替設定を変更する方法については、「USBインターフェイスで代替設定を選択する方法」を参照してください。

    KMDF クライアント ドライバー:

    KMDF クライアント ドライバーは、WdfUsbTargetDeviceSelectConfig メソッドを呼び出す必要があります。

    UMDF クライアント ドライバー:

    UMDF クライアント ドライバーの場合、フレームワークは最初の構成と、その構成内の各インターフェイスの既定の代替設定を選択します。

  • クライアント ドライバーには、IN エンドポイントのフレームワーク ターゲット パイプ オブジェクトへのハンドルが必要です。 詳細については、「USB パイプを列挙する方法」をご参照ください。

KMDF クライアント ドライバーで連続リーダーを使用する

連続リーダーの使用を開始する前に、WDF_USB_CONTINUOUS_READER_CONFIG 構造体を初期化して構成する必要があります。

KMDF クライアント ドライバーで連続リーダーを構成する

  1. WDF_USB_CONTINUOUS_READER_CONFIG_INIT マクロを呼び出して、WDF_USB_CONTINUOUS_READER_CONFIG 構造体を初期化します。

  2. WDF_USB_CONTINUOUS_READER_CONFIG 構造体でその構成オプションを指定します。

  3. WdfUsbTargetPipeConfigContinuousReader メソッドを呼び出します。

    次のコード例では、指定したターゲット パイプ オブジェクトの連続リーダーを構成します。

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

通常、クライアント ドライバーは、アクティブな設定でターゲット パイプ オブジェクトを列挙した後、EvtDevicePrepareHardware コールバック関数で連続リーダーを構成します。

前の例では、クライアント ドライバーは 2 つの方法で構成オプションを指定します。 まず、WDF_USB_CONTINUOUS_READER_CONFIG_INIT を呼び出し、次に WDF_USB_CONTINUOUS_READER_CONFIG メンバーを設定します。 WDF_USB_CONTINUOUS_READER_CONFIG_INIT のパラメーターに注目してください。 これらの値は必須です。 この例では、クライアント ドライバーは次を指定します。

  • ドライバーが実装する完了ルーチンへのポインター。 フレームワークは、読み取り要求が完了したときにこのルーチンを呼び出します。 完了ルーチンでは、ドライバーは、読み取られたデータを含むメモリの場所にアクセスできます。 完了ルーチンの実装については、手順 2 で説明します。
  • ドライバー定義コンテキストへのポインター。
  • 1 回の転送でデバイスから読み取ることができるバイト数。 クライアント ドライバーは、WdfUsbInterfaceGetConfiguredPipe メソッドまたは WdfUsbTargetPipeGetInformation メソッドを呼び出すことで、WDF_USB_PIPE_INFORMATION 構造体でその情報を取得することができます。 詳細については、「USB パイプを列挙する方法」をご参照ください。

WDF_USB_CONTINUOUS_READER_CONFIG_INIT は、NumPendingReads の既定値を使用するように連続リーダーを構成します。 この値は、フレームワークが保留中のキューに追加する読み取り要求の数を決定します。 既定の値は、多くのプロセッサ構成上の多くのデバイスに適切なパフォーマンスを提供するために決定されています。

この例では、WDF_USB_CONTINUOUS_READER_CONFIG_INIT で指定された構成パラメーターに加えて、WDF_USB_CONTINUOUS_READER_CONFIG でエラー ルーチンも設定します。 このエラー ルーチンは省略可能です。

エラー ルーチンに加えて、WDF_USB_CONTINUOUS_READER_CONFIG にはクライアント ドライバーが転送バッファーのレイアウトを指定するために使用できる他のメンバーがあります。 たとえば、連続リーダーを使用してネットワーク パケットを受信するネットワーク ドライバーについて考えてみます。 各パケットには、ヘッダー、ペイロード、フッターのデータが含まれています。 パケットを記述するには、ドライバーはまず、WDF_USB_CONTINUOUS_READER_CONFIG_INIT への呼び出しでパケットのサイズを指定する必要があります。 次に、ドライバーは、WDF_USB_CONTINUOUS_READER_CONFIGHeaderLength メンバーと TrailerLength メンバーを設定して、ヘッダーとフッターの長さを指定する必要があります。 フレームワークでは、これらの値を使用して、ペイロードの両側のバイト オフセットを計算します。 ペイロード データがエンドポイントから読み取られると、フレームワークはそのデータをオフセット間のバッファーの一部に保存します。

完了ルーチンの実装

フレームワークは、要求が完了するたびにクライアント ドライバー実装完了ルーチンを呼び出します。 フレームワークは、読み取られたバイト数と、パイプから読み取られたデータをバッファーに含む WDFMEMORY オブジェクトを渡します。

次のコード例は、完了ルーチンの実装を示しています。

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

フレームワークは、要求が完了するたびにクライアント ドライバー実装完了ルーチンを呼び出します。 フレームワークは、読み取り操作ごとにメモリ オブジェクトを割り当てます。 完了ルーチンでは、フレームワークは読み取られたバイト数と WDFMEMORY ハンドルをメモリ オブジェクトに渡します。 メモリ オブジェクト バッファーには、パイプから読み取られたデータが含まれています。 クライアント ドライバーは、メモリ オブジェクトを解放できません。 フレームワークは、各完了ルーチンが戻った後にオブジェクトを解放します。 クライアントドライバーが受信したデータを保存したい場合、ドライバーは完了ルーチンでバッファーの内容をコピーする必要があります。

エラー ルーチンを実装する

フレームワークは、クライアント ドライバー実装エラー ルーチンを呼び出して、読み取り要求の処理中に連続リーダーがエラーを報告したことをドライバーに通知します。 フレームワークは、要求が失敗したターゲット パイプ オブジェクトとエラー コード値へのポインターを渡します。 これらのエラー コード値に基づいて、ドライバーはエラー回復メカニズムを実装できます。 ドライバーは、フレームワークが連続リーダーを再起動する必要があるかどうかをフレームワークに示す適切な値も返す必要があります。

次のコード例は、エラー ルーチンの実装を示しています。

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

前の例では、ドライバーは TRUE を返します。 この値は、パイプをリセットしてから連続リーダーを再起動する必要があることをフレームワークに示します。

または、クライアント ドライバーは ALSE を返し、パイプでストール状態が発生した場合にエラー回復メカニズムを提供できます。 たとえば、ドライバーは USBD 状態をチェックし、ストール状態をクリアするリセット パイプ要求を発行できます。

パイプ内のエラー回復の詳細については、「USB パイプ エラーから回復する方法」を参照してください。

連続リーダーを開始および停止する

デバイスが動作状態になったときに連続リーダーを開始するようにフレームワークに指示します。デバイスが動作状態を離れるとリーダーを停止します。 これらのメソッドを呼び出し、ターゲット パイプ オブジェクトを I/O ターゲット オブジェクトとして指定します。

連続リーダーは、フレームワークによって自動的に電源管理されません。 したがって、クライアント ドライバーは、デバイスの電源状態が変化したときに、ターゲット パイプ オブジェクトを明示的に開始または停止する必要があります。 ドライバーは、ドライバーの EvtDeviceD0Entry 実装で WdfIoTargetStart を呼び出します。 この呼び出しにより、デバイスが動作状態の場合にのみ、キューが要求を配信できるようになります。 逆に、ドライバーはドライバー EvtDeviceD0Exit 実装で WdfIoTargetStop を呼び出して、デバイスが低い電源状態になったときにキューが要求の配信を停止するようにします。

次のコード例では、指定したターゲット パイプ オブジェクトの連続リーダーを構成します。

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

前の例は、EvtDeviceD0Entry およびEvtDeviceD0Exit コールバック ルーチンの実装を示しています。 WdfIoTargetStop の Action パラメーターを使用すると、クライアント ドライバーは、デバイスが動作状態を離れたときにキュー内の保留中の要求のアクションを決定することができます。 この例では、ドライバーは WdfIoTargetCancelSentIo を指定します。 このオプションは、キュー内のすべての保留中の要求を取り消すようにフレームワークに指示します。 または、ドライバーは、I/O ターゲットを停止する前に保留中のリクエストが完了するまで待機するか、保留中のリクエストを保持して I/O ターゲットの再起動時に再開するようにフレームワークに指示できます。

UMDF クライアント ドライバーで連続リーダーを使用する

連続リーダーの使用を開始する前に、IPnpCallbackHardware::OnPrepareHardware メソッドの実装でリーダーを構成する必要があります。 IN エンドポイントに関連付けられているターゲット パイプ オブジェクトの IWDFUsbTargetPipe インターフェイスへのポインターを取得したら、次の手順を実行します。

UMDF クライアント ドライバーで連続リーダーを構成する

  1. ターゲット パイプ オブジェクト (IWDFUsbTargetPipe) でQueryInterface を呼び出し、IWDFUsbTargetPipe2 インターフェイスのクエリを実行します。

  2. デバイス コールバック オブジェクトで QueryInterface を呼び出し、IUsbTargetPipeContinuousReaderCallbackReadComplete インターフェイスのクエリを実行します。 連続リーダーを使用するには、IUsbTargetPipeContinuousReaderCallbackReadComplete を実装する必要があります。 実装については、このトピックの後半で説明します。

  3. デバイス コールバック オブジェクトで QueryInterface を呼び出し、エラー コールバックを実装している場合は、IUsbTargetPipeContinuousReaderCallbackReadersFailed インターフェイスのクエリを実行します。 実装については、このトピックの後半で説明します。

  4. IWDFUsbTargetPipe2::ConfigureContinuousReader メソッドを呼び出し、ヘッダー、トレーラー、保留中の要求の数、完了コールバック メソッドと失敗コールバック メソッドへの参照などの構成パラメーターを指定します。

    このメソッドは、ターゲット パイプ オブジェクトの連続リーダーを構成します。 連続リーダーは、ターゲット パイプ オブジェクトから送受信される一連の読み取り要求を管理するキューを作成します。

次のコード例では、指定したターゲット パイプ オブジェクトの連続リーダーを構成します。 この例では、呼び出し元によって指定されたターゲット パイプ オブジェクトが IN エンドポイントに関連付けられていることを前提としています。 連続リーダーは USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE バイトを読み取るように構成されています。フレームワークによって使用される既定の保留中の要求数を使用します。 クライアント ドライバーが提供する完了コールバック メソッドとエラー コールバック メソッドを呼び出します。 受信したバッファーには、ヘッダーまたはトレーラー データは含まれません。

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

次に、デバイスが作業状態 (D0) に入って終了するときのターゲット パイプ オブジェクトの状態を指定します。

クライアント ドライバーが電源管理キューを使用して要求をパイプに送信する場合、キューはデバイスが D0 状態の場合にのみ要求を配信します。 デバイスの電源状態が D0 から低い電源状態 (D0 終了時) に変わると、ターゲット パイプ オブジェクトは保留中の要求を完了し、キューはターゲット パイプ オブジェクトへの要求の送信を停止します。 したがって、クライアント ドライバーはターゲット パイプ オブジェクトを開始および停止する必要がありません。

連続リーダーは、Power マネージド キューを使用して要求を送信しません。 したがって、デバイスの電源状態が変化したときに、ターゲット パイプ オブジェクトを明示的に開始または停止する必要があります。 ターゲット パイプ オブジェクトの状態を変更するには、フレームワークによって実装される IWDFIoTargetStateManagement インターフェイスを使用できます。 IN エンドポイントに関連付けられているターゲット パイプ オブジェクトの IWDFUsbTargetPipe インターフェイスへのポインターを取得したら、次の手順を実行します。

状態管理の実装

  1. IPnpCallbackHardware::OnPrepareHardware の実装では、ターゲット パイプ オブジェクト (IWDFUsbTargetPipe) で QueryInterface を呼び出し、IWDFIoTargetStateManagement インターフェイスのクエリを実行します。 デバイス コールバック クラスのメンバー変数に参照を格納します。

  2. デバイス コールバック オブジェクトに IPnpCallback インターフェイスを実装します。

  3. IPnpCallback::OnD0Entry メソッドの実装では、IWDFIoTargetStateManagement::Start を呼び出して連続リーダーを開始します。

  4. IPnpCallback::OnD0Entry メソッドの実装では、IWDFIoTargetStateManagement::Stop を呼び出して連続リーダーを停止します。

デバイスが動作状態 (D0) に入った後、フレームワークは、ターゲット パイプ オブジェクトを開始するクライアント ドライバー指定の D0 エントリ コールバック メソッドを呼び出します。 デバイスが D0 状態を終了すると、フレームワークは D0 終了コールバック メソッドを呼び出します。 ターゲット パイプ オブジェクトは、クライアント ドライバーによって構成された保留中の読み取り要求の数を完了し、新しい要求の受け入れを停止します。 次のコード例では、デバイス コールバック オブジェクトに IPnpCallback インターフェイスを実装します。

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

次のコード例は、IPnpCallback::OnPrepareHardware メソッドで、ターゲット パイプ オブジェクトの IWDFIoTargetStateManagement インターフェイスへのポインターを取得する方法を示しています。

   //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.
        }
    }

次のコード例は、IPnpCallbackHardware::OnPrepareHardware メソッドで、ターゲット パイプ オブジェクトの IWDFIoTargetStateManagement インターフェイスへのポインターを取得する方法を示しています。

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

連続リーダーが読み取り要求を完了した後、クライアント ドライバーは、読み取り要求が正常に完了したときに通知を受け取る方法を提供する必要があります。 クライアント ドライバーは、デバイス コールバック オブジェクトにこのコードを追加する必要があります。

IUsbTargetPipeContinuousReaderCallbackReadComplete を実装して完了コールバックを提供する

  1. デバイス コールバック オブジェクトに IUsbTargetPipeContinuousReaderCallbackReadComplete インターフェイスを実装します。

  2. デバイス コールバック オブジェクトの QueryInterface 実装がコールバック オブジェクトの参照カウントをインクリメントし、IUsbTargetPipeContinuousReaderCallbackReadComplete インターフェイス ポインターを返していることを確認します。

  3. IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion メソッドの実装では、パイプから読み取られたデータにアクセスします。 pMemory パラメーターは、データを含むフレームワークによって割り当てられたメモリを指します。 IWDFMemory::GetDataBuffer を呼び出して、データを含むバッファーを取得できます。 バッファーにはヘッダーが含まれますが、OnReaderCompletionNumBytesTransferred パラメーターで示されるデータの長さにはヘッダーの長さは含まれません。 ヘッダーの長さは、IWDFUsbTargetPipe2::ConfigureContinuousReader へのドライバーの呼び出しで連続リーダーを構成するときに、クライアント ドライバーによって指定されます。

  4. IWDFUsbTargetPipe2::ConfigureContinuousReader メソッドの pOnCompletion パラメーターで完了コールバックへのポインターを指定します。

デバイス上のエンドポイントでデータが使用可能になるたびに、ターゲット パイプ オブジェクトは読み取り要求を完了します。 読み取り要求が正常に完了した場合、フレームワークは IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion を呼び出してクライアント ドライバーに通知します。 それ以外の場合、フレームワークは、ターゲット パイプ オブジェクトが読み取り要求でエラーを報告するときに、クライアント ドライバーが指定したエラー コールバックを呼び出します。

次のコード例では、デバイス コールバック オブジェクトに IUsbTargetPipeContinuousReaderCallbackReadComplete インターフェイスを実装します。

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

次のコード例は、デバイス コールバック オブジェクトの QueryInterface 実装を示しています。

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

次のコード例は、IUsbTargetPipeContinuousReaderCallbackReadComplete::OnReaderCompletion によって返されるバッファーからデータを取得する方法を示しています。 ターゲット パイプ オブジェクトが読み取り要求を正常に完了するたびに、フレームワークは OnReaderCompletion を呼び出します。 この例では、データを含むバッファーを取得し、デバッガーの出力に内容を出力します。

 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."));
    }
}

クライアント ドライバーは、読み取り要求の完了中にターゲット パイプ オブジェクトでエラーが発生したときに、フレームワークから通知を受け取ることができます。 通知を取得するには、クライアント ドライバーがエラー コールバックを実装し、連続リーダーの構成中にコールバックへのポインターを指定する必要があります。 次の手順では、エラー コールバックを実装する方法について説明します。

IUsbTargetPipeContinuousReaderCallbackReadersFailed を実装してエラー コールバックを提供する

  1. デバイス コールバック オブジェクトに IUsbTargetPipeContinuousReaderCallbackReadersFailed インターフェイスを実装します。

  2. デバイス コールバック オブジェクトの QueryInterface 実装がコールバック オブジェクトの参照カウントをインクリメントし、IUsbTargetPipeContinuousReaderCallbackReadersFailed インターフェイス ポインターを返していることを確認します。

  3. IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure メソッドの実装で、失敗した読み取り要求のエラー処理を指定します。

    連続リーダーが読み取り要求を完了できず、クライアント ドライバーがエラー コールバックを提供した場合、フレームワークは IUsbTargetPipeContinuousReaderCallbackReadersFailed::OnReaderFailure メソッドを呼び出します。 フレームワークは hrStatus パラメーターに HRESULT 値を提供し、ターゲット パイプ オブジェクトで発生したエラー コードを示します。 そのエラー コードに基づいて、特定のエラー処理を提供できます。 たとえば、フレームワークでパイプをリセットし、連続リーダーを再起動する場合は、コールバックが TRUE を返していることを確認します。

    注:IWDFIoTargetStateManagement::StartIWDFIoTargetStateManagement::Stop をエラー コールバック内で呼び出さないでください。

  4. IWDFUsbTargetPipe2::ConfigureContinuousReader メソッドの pOnFailure パラメーターで、エラー コールバックへのポインターを指定します。

次のコード例では、デバイス コールバック オブジェクトに IUsbTargetPipeContinuousReaderCallbackReadersFailed インターフェイスを実装します。

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

次のコード例は、デバイス コールバック オブジェクトの QueryInterface 実装を示しています。

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

次のコード例は、エラー コールバックの実装を示しています。 読み取り要求が失敗した場合、メソッドはフレームワークによって報告されたエラー コードをデバッガーに出力し、パイプをリセットして連続リーダーを再起動するようにフレームワークに指示します。

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

クライアント ドライバーがエラー コールバックを提供せず、エラーが発生した場合、フレームワークは USB パイプをリセットし、連続リーダーを再起動します。