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

一个WDF_DMA_DIRECTION类型的值,该值指定完成 DMA 传输操作的方向。

[in] Status

一个DMA_COMPLETION_STATUS类型的值,该值指定传输的状态。

返回值

备注

总线主 DMA 设备的硬件通常在 DMA 传输完成时发出中断。 然后,驱动程序在其 EvtInterruptDpc 回调函数中完成 DMA 传输。

但是,系统模式 DMA 设备的硬件并不总是通过发出中断来发出 DMA 传输完成信号。 若要接收 DMA 传输完成通知,系统模式 DMA 设备的驱动程序可以通过调用 WdfDmaTransmaTransactionSetTransferCompleteCallback 来改为注册 EvtDmaTransmaTransferComplete 事件回调函数。

在系统 DMA 控制器完成传输后,框架调用 EvtDmaTransactionDmaTransferComplete ,对于事务中的每个传输一次。

在其 EvtDmaTransactionDmaTransferComplete 回调中,驱动程序可以调用以下方法来通知框架传输已完成:

WdfDmaTransactionDmaCompletedWdfDmaTransactionDmaCompletedFinalWdfDmaTransactionDmaCompletedWithLength 驱动程序可能不会从 EvtDmaTransactionDmaTransferComplete 调用以前的方法之一,而是选择 创建计时器对象 或计划 DPC 以根据需要稍后完成传输。 WdfDmaTransactionDmaCompletedXxx 返回 TRUE 后,指示完成 DMA 事务不需要更多传输,驱动程序可以选择调用 WdfDmaTransactionExecute 来启动后续事务。

如果驱动程序调用 WdfDmaTransactionStopSystemTransfer,框架将调用 Status 值为 DmaCancelled 的 EvtDmaTransactionDmaTransferComplete 在这种情况下,驱动程序应从 EvtDmaTransactionDmaTransactionDmaTransferComplete 中调用 WdfDmaTransactionDmaCompletedFinal,然后才能继续处理请求。

WdfDmaTransactionDmaCompletedXxx 返回 TRUE 之前,驱动程序不得操作与事务关联的数据缓冲区。

如果需要终止 DMA 事务,驱动程序可以从 EvtDmaTransactionDmaTransferComplete 内部调用 WdfDmaTransactionRelease

有关系统模式 DMA 的详细信息,请参阅 支持 System-Mode DMA

示例

若要定义 EvtDmaTransactionDmaTransferComplete 回调函数,必须先提供一个函数声明,用于标识要定义的回调函数的类型。 Windows 为驱动程序提供了一组回调函数类型。 使用回调函数类型声明函数可帮助 驱动程序的代码分析静态驱动程序验证程序 (SDV) 和其他验证工具查找错误,这是为 Windows 操作系统编写驱动程序的要求。

例如,若要定义名为 MyDmaTransactionDmaTransferComplete 的 EvtDmaTransmaTransferComplete 回调函数,请使用 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