EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE funzione di callback (wdfdmatransaction.h)

[Si applica solo a KMDF]

La funzione di callback dell'evento EvtDmaTransactionDmaTransferComplete di un driver viene chiamata quando il controller in modalità sistema ha completato il trasferimento DMA corrente.

Sintassi

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
)
{...}

Parametri

[in] Transaction

Handle per un oggetto transazione DMA che rappresenta il trasferimento DMA appena completato.

[in] Device

Handle per l'oggetto dispositivo framework specificato dal driver quando viene chiamato WdfDmaTransactionCreate.

[in] Context

Puntatore di contesto specificato dal driver in una chiamata precedente a WdfDmaTransactionSetTransferCompleteCallback.

[in] Direction

Valore tipizzato WDF_DMA_DIRECTION che specifica la direzione del completamento dell'operazione di trasferimento DMA.

[in] Status

Valore tipizzato DMA_COMPLETION_STATUS che specifica lo stato del trasferimento.

Valore restituito

nessuno

Osservazioni

L'hardware per un dispositivo DMA master del bus genera in genere un interrupt al termine del trasferimento DMA. Il driver completa quindi il trasferimento DMA nella relativa funzione di callback EvtInterruptDpc .

Tuttavia, l'hardware per un dispositivo DMA in modalità sistema non segnala sempre il completamento del trasferimento DMA emettendo un interrupt. Per ricevere la notifica del completamento del trasferimento DMA, un driver per un dispositivo DMA in modalità sistema può invece registrare una funzione di callback dell'evento EvtDmaTransactionDmaTransferComplete chiamando WdfDmaTransactionSetTransferCompleteCallback.

Il framework chiama EvtDmaTransactionDmaTransferComplete dopo che il controller DMA di sistema ha completato il trasferimento, una volta per ogni trasferimento in una transazione.

Dall'interno del callback EvtDmaTransactionDmaTransferComplete , il driver può chiamare i metodi seguenti per notificare al framework che il trasferimento è stato completato:

WdfDmaTransactionDmaCompletedWdfDmaTransactionDmaCompletedFinalWdfDmaTransactionDmaCompletedWithLength Il driver potrebbe non chiamare uno dei metodi precedenti da EvtDmaTransactionDmaTransferComplete, optando invece di creare un oggetto timer o pianificare un DPC per completare il trasferimento in un secondo momento, in base alle esigenze. Dopo che WdfDmaTransactionDmaCompletedXxx restituisce TRUE, a indicare che non sono necessari altri trasferimenti per completare la transazione DMA, il driver può facoltativamente chiamare WdfDmaTransactionExecute per avviare una transazione successiva.

Se il driver chiama WdfDmaTransactionStopSystemTransfer, il framework chiama EvtDmaTransactionDmaTransferComplete con un valore Status di DmaCancelled. In questo caso, il driver deve chiamare WdfDmaTransactionDmaCompletedFinal dall'interno di EvtDmaTransactionDmaTransferComplete e quindi può continuare con l'elaborazione delle richieste.

Il driver non deve modificare i buffer di dati associati alla transazione fino a quando WdfDmaTransactionDmaCompletedXxx ha restituito TRUE.

Il driver può chiamare WdfDmaTransactionRelease dall'interno di EvtDmaTransactionDmaTransferComplete se deve terminare la transazione DMA.

Per altre informazioni su DMA in modalità sistema, vedere Supporto System-Mode DMA.

Esempio

Per definire una funzione di callback EvtDmaTransactionDmaTransferComplete , è prima necessario fornire una dichiarazione di funzione che identifica il tipo di funzione di callback che si sta definendo. Windows fornisce un set di tipi di funzione di callback per i driver. La dichiarazione di una funzione tramite i tipi di funzione di callback consente di analizzare il codice per i driver, l'SDV ( Static Driver Verifier ) e altri strumenti di verifica di trovare errori ed è un requisito per la scrittura di driver per il sistema operativo Windows.

Ad esempio, per definire una funzione di callback EvtDmaTransactionDmaTransferCompletedenominata MyDmaTransactionDmaTransferComplete, usare il tipo di EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE , come illustrato in questo esempio di codice:

EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE  MyDmaTransactionDmaTransferComplete;

Implementare quindi la funzione di callback come indicato di seguito.


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

Il tipo di funzione EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE è definito nel file di intestazione WdfDmaTransaction.h. Per identificare in modo più accurato gli errori quando si eseguono gli strumenti di analisi del codice, assicurarsi di aggiungere l'annotazione Use_decl_annotations alla definizione della funzione. L'annotazione Use_decl_annotations assicura che vengano utilizzate le annotazioni applicate al tipo di funzione EVT_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE nel file di intestazione. Per altre informazioni sui requisiti per le dichiarazioni di funzione, vedere Dichiarazione di funzioni tramite tipi di ruolo di funzione per i driver KMDF. Per informazioni sulle Use_decl_annotations, vedere Annotazione del comportamento della funzione.

Requisiti

Requisito Valore
Client minimo supportato Windows 8
Piattaforma di destinazione Universale
Versione KMDF minima 1.11
Intestazione wdfdmatransaction.h (include Wdf.h)
IRQL DISPATCH_LEVEL

Vedi anche

WdfDmaTransactionSetTransferCompleteCallback