EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE コールバック関数 (wdfdmatransaction.h)

[KMDF にのみ適用]

システム モード コントローラーが現在の DMA 転送を完了すると、ドライバーの EvtDmaTransactionDmaTransferComplete イベント コールバック関数が呼び出されます。

構文

EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE EvtWdfDmaTransactionDmaTransferComplete;

void EvtWdfDmaTransactionDmaTransferComplete(
  [in] WDFDMATRANSACTION Transaction,
  [in] WDFDEVICE Device,
  [in] WDFCONTEXT Context,
  [in] WDF_DMA_DIRECTION Direction,
  [in] DMA_COMPLETION_STATUS Status
)
{...}

パラメーター

[in] Transaction

完了した DMA 転送を表す DMA トランザクション オブジェクトへのハンドル。

[in] Device

WdfDmaTransactionCreate を呼び出したときにドライバーが指定したフレームワーク デバイス オブジェクトへのハンドル。

[in] Context

WdfDmaTransactionSetTransferCompleteCallback の以前の呼び出しでドライバーが指定したコンテキスト ポインター。

[in] Direction

DMA 転送操作を完了する方向を指定するWDF_DMA_DIRECTION型指定された値。

[in] Status

転送 状態を指定するDMA_COMPLETION_STATUS型指定された値。

戻り値

なし

解説

バス マスター DMA デバイスのハードウェアは、通常、DMA 転送が完了すると割り込みを発行します。 その後、ドライバーは EvtInterruptDpc コールバック関数で DMA 転送を完了します。

ただし、システム モード DMA デバイスのハードウェアは、割り込みを発行することによって DMA 転送の完了を常に通知するとは限りません。 DMA 転送完了の通知を受け取るために、システム モード DMA デバイスのドライバーは、代わりに WdfDmaTransactionSetTransferCompleteCallback を呼び出して EvtDmaTransactionDmaTransferComplete イベント コールバック関数を登録できます。

フレームワークは、システム DMA コントローラーが転送を完了した後、トランザクション内の転送ごとに 1 回、 EvtDmaTransactionDmaTransferComplete を呼び出します。

EvtDmaTransactionDmaTransferComplete コールバック内から、ドライバーは次のメソッドを呼び出して、転送が完了したことをフレームワークに通知できます。

WdfDmaTransactionDmaCompletedWdfDmaTransactionDmaCompletedFinalWdfDmaTransactionDmaCompletedWithLength ドライバーは 、EvtDmaTransactionDmaTransferComplete から以前のメソッドの 1 つを呼び出さない場合があり、代わりに タイマー オブジェクトを作成 するか、後で転送を完了するように DPC をスケジュールすることを選択します。 WdfDmaTransactionDmaCompletedXxx が TRUE を返した後、DMA トランザクションを完了するためにこれ以上転送が必要ないことを示すドライバーは、必要に応じて WdfDmaTransactionExecute を呼び出して後続のトランザクションを開始できます。

ドライバーが WdfDmaTransactionStopSystemTransfer を呼び出す場合、フレームワークは StatusDmaCancelled を使用して EvtDmaTransactionDmaTransferComplete を呼び出します。 この場合、ドライバーは EvtDmaTransactionDmaTransferComplete 内から WdfDmaTransactionDmaCompletedFinal を呼び出す必要があり、要求処理を続行できます。

WdfDmaTransactionDmaCompletedXxx が TRUE を返すまで、ドライバーはトランザクションに関連付けられているデータ バッファーを操作しないでください。

ドライバーは、DMA トランザクションを終了する必要がある場合は、EvtDmaTransactionDmaTransferComplete 内から WdfDmaTransactionRelease を呼び出すことができます。

システム モード DMA の詳細については、「 System-Mode DMA のサポート」を参照してください。

EvtDmaTransactionDmaTransferComplete コールバック関数を定義するには、まず、定義するコールバック関数の型を識別する関数宣言を指定する必要があります。 Windows には、ドライバーのコールバック関数の種類のセットが用意されています。 コールバック関数の種類を使用して関数を宣言すると、ドライバー、静的ドライバー検証ツール (SDV)、およびその他の検証ツールのコード分析でエラーが検出され、Windows オペレーティング システム用のドライバーを記述するための要件になります。

たとえば、MyDmaTransactionDmaTransferComplete という名前の EvtDmaTransactionDmaTransferComplete コールバック関数を定義するには、次のコード例に示すように、EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE型を使用します。

EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE  MyDmaTransactionDmaTransferComplete;

次に、コールバック関数を次のように実装します。


_Use_decl_annotations_
VOID
MyDmaTransactionDmaTransferComplete(
    WDFDMATRANSACTION Transaction,
    WDFDEVICE /* Device */,
    WDFCONTEXT Context,
    WDF_DMA_DIRECTION /* Direction */,
    DMA_COMPLETION_STATUS DmaStatus
    )
{
    PREQUEST_CONTEXT requestContext = (PREQUEST_CONTEXT) Context;
    NTSTATUS requestStatus;
    bool overrideStatus = true;
    size_t bytesTransferred;

    if (DmaStatus == DmaComplete) {
        //
        // Normal transfer completion.  Indicate this to the framework and see 
        // if there's more work to do.
        //
        if (WdfDmaTransactionDmaCompleted(Transaction, &requestStatus) == FALSE) {
            //
            // There are more DMA transfers to come.  The transaction 
            // may already have been completed on another processor.  
            // Return without touching it again.
            //
            goto exit;
        }

        requestStatus = STATUS_SUCCESS;
    }
    else {

        //
        // Complete the entire transaction.  But throw out the status and 
        // use one derived from the DmaStatus.
        //
        WdfDmaTransactionDmaCompletedFinal(Transaction, 0, &requestStatus);        
        
        //
        // Error or cancellation.  Indicate that this was the final transfer to 
        // the framework.
        //
        if (DmaStatus == DmaError) {
            requestStatus = STATUS_DEVICE_DATA_ERROR;
        }
        else {

            //
            // Cancel status should only be triggered by timeout or cancel.  Rely on 
            // someone having already set the status, which means we should lose
            // the race for BeginCompletion below.
            //
            requestStatus = STATUS_PENDING;
            overrideStatus = false;
        }
    }

    //
    // Begin completion.  There's nothing special to do here if cancel or
    // timeout got there first.
    //
    BeginCompletion(requestContext, requestStatus, overrideStatus);

    //
    // Record the number of bytes we transferred.
    //
    bytesTransferred = WdfDmaTransactionGetBytesTransferred(
                        requestContext->DmaTransaction
                        );

    WdfRequestSetInformation(requestContext->Request, bytesTransferred);

    //
    // Success, error or cancel, this was the last transfer in the 
    // transaction.  Attempt to complete the request.
    //
    AttemptRequestCompletion(requestContext, true);

exit: 
    return;
}

bool
BeginCompletion(
    __in PREQUEST_CONTEXT  RequestContext,
    __in NTSTATUS          CompletionStatus,
    __in bool              ForceStatusUpdate
    )
{
    bool completionStarted;

    //
    // Grab the object lock and mark the beginning of 
    // completion.
    //
    WdfSpinLockAcquire(RequestContext->Lock);

    completionStarted = RequestContext->CompletionStarted;
    RequestContext->CompletionStarted = true;

    if ((completionStarted == false) || 
        (ForceStatusUpdate == true)) {
        RequestContext->CompletionStatus = CompletionStatus;
    }

    WdfSpinLockRelease(RequestContext->Lock);

    return !completionStarted;
}

VOID
AttemptRequestCompletion(
    __in PREQUEST_CONTEXT RequestContext,
    __in bool TransferComplete
    )
{
    LONG refCount;

    NT_ASSERTMSG("No thread has begun completion", 
                 RequestContext->CompletionStarted == true);

    if (TransferComplete) {
        //
        // Unmark the request cancelable.  If that succeeds then drop the cancel reference
        //
        if (WdfRequestUnmarkCancelable(RequestContext->Request) == STATUS_SUCCESS) {
            refCount = InterlockedDecrement(&(RequestContext->CompletionRefCount));
            NT_ASSERTMSGW(L"Reference count should not have gone to zero yet",
                          refCount != 0);
        }
                
        //
        // Stop the timer if it's been started.
        //
        if (RequestContext->TimerStarted == true) {
            if (WdfTimerStop(RequestContext->Timer, FALSE) == TRUE) {
                //
                // The timer was queued but will never run.  Drop its 
                // reference count.
                //
                refCount = InterlockedDecrement(&RequestContext->CompletionRefCount);
                NT_ASSERTMSG("Completion reference count should not reach zero until "
                             L"this routine calls AttemptRequestCompletion",
                             refCount > 0);
            }
        }
    }

    //
    // Drop this caller's reference.  If that was the last one then 
    // complete the request.
    //
    refCount = InterlockedDecrement(&(RequestContext->CompletionRefCount));

    if (refCount == 0) {
        NT_ASSERTMSGW(L"Execution reference was released, but execution "
                      L"path did not set a completion status for the "
                      L"request",
                      RequestContext->CompletionStatus != STATUS_PENDING);
        
        
        //
        // Timers are disposed of at passive level.  If we leave it attached to 
        // the request then we can hit a verifier issue, since the request 
        // needs to be immediately disposable at dispatch-level.
        //
        // Delete the timer now so that we can complete the request safely.
        // At this point the timer has either expired or been successfully 
        // cancelled so there's no race with the timer routine.
        //
        if (RequestContext->Timer != NULL) {
            WdfObjectDelete(RequestContext->Timer);
            RequestContext->Timer = NULL;
        }

        WdfRequestComplete(RequestContext->Request, 
                           RequestContext->CompletionStatus);
    }
}

EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE関数型は、WdfDmaTransaction.h ヘッダー ファイルで定義されています。 コード分析ツールを実行するときにエラーをより正確に識別するには、 Use_decl_annotations 注釈を関数定義に追加してください。 Use_decl_annotations注釈を使用すると、ヘッダー ファイル内のEVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE関数型に適用される注釈が確実に使用されます。 関数宣言の要件の詳細については、「 KMDF ドライバーの関数ロール型を使用した関数の宣言」を参照してください。 Use_decl_annotationsの詳細については、「関数の動作に注釈を付ける」を参照してください。

要件

要件
サポートされている最小のクライアント Windows 8
対象プラットフォーム ユニバーサル
最小 KMDF バージョン 1.11
Header wdfdmatransaction.h (Wdf.h を含む)
IRQL DISPATCH_LEVEL

こちらもご覧ください

WdfDmaTransactionSetTransferCompleteCallback