次の方法で共有


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の前のメソッドの代わりに、タイマー オブジェクト を作成するか、DPC をスケジュールして後で必要に応じて転送を完了することを選択します。 WdfDmaTransactionDmaCompleted Xxx が TRUE を返、DMA トランザクションを完了するためにこれ以上転送が必要ないことを示した後、ドライバーは必要に応じて WdfDmaTransactionExecute 呼び出して後続のトランザクションを開始できます。

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

ドライバーは、WdfDmaTransactionDmaCompleted Xxx が TRUE を返するまで、トランザクションに関連付けられているデータ バッファーを操作できません。

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

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

例示

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

たとえば、MyDmaTransactionDmaTransferComplete myDmaTransactionDmaTransferComplete という名前のコールバック関数定義するには、次のコード例に示すように 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
ヘッダー wdfdmatransaction.h (Wdf.h を含む)
IRQL DISPATCH_LEVEL

こちらもご覧ください

WdfDmaTransactionSetTransferCompleteCallback