[WDF] Add Windows Driver Framework files
[reactos.git] / sdk / lib / drivers / wdf / kmdf / src / dma / base / fxdmatransaction.cpp
diff --git a/sdk/lib/drivers/wdf/kmdf/src/dma/base/fxdmatransaction.cpp b/sdk/lib/drivers/wdf/kmdf/src/dma/base/fxdmatransaction.cpp
new file mode 100644 (file)
index 0000000..38ee50b
--- /dev/null
@@ -0,0 +1,2533 @@
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+    FxDmaTransaction.cpp
+
+Abstract:
+
+    WDF DMA Transaction Object
+
+Environment:
+
+    Kernel mode only.
+
+Notes:
+
+
+Revision History:
+
+--*/
+
+#include "FxDmaPCH.hpp"
+
+extern "C" {
+#include "FxDmaTransaction.tmh"
+}
+
+FxDmaTransactionBase::FxDmaTransactionBase(
+    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+    __in USHORT ObjectSize,
+    __in USHORT ExtraSize,
+    __in FxDmaEnabler *DmaEnabler
+    ) :
+    FxNonPagedObject(
+        FX_TYPE_DMA_TRANSACTION,
+        ExtraSize == 0 ? ObjectSize : COMPUTE_OBJECT_SIZE(ObjectSize, ExtraSize),
+        FxDriverGlobals)
+{
+    m_DmaEnabler            = DmaEnabler;
+    m_EncodedRequest        = NULL;
+    m_MaxFragmentLength     = 0;
+    m_DmaDirection          = WdfDmaDirectionReadFromDevice;
+    m_DmaAcquiredContext    = NULL;
+    m_CurrentFragmentMdl    = NULL;
+    m_CurrentFragmentOffset = 0;
+    m_StartOffset           = NULL;
+    m_StartMdl              = NULL;
+    m_Remaining             = 0;
+    m_CurrentFragmentLength = 0;
+    m_TransactionLength     = 0;
+    m_Transferred           = 0;
+    m_Flags                 = 0;
+
+    m_DmaAcquiredFunction.Method.ProgramDma = NULL;
+
+    m_State = FxDmaTransactionStateCreated;
+
+    if (ExtraSize == 0) {
+        m_TransferContext = NULL;
+    } else {
+        m_TransferContext = WDF_PTR_ADD_OFFSET_TYPE(
+                                this,
+                                COMPUTE_RAW_OBJECT_SIZE(ObjectSize),
+                                PVOID
+                                );
+    }
+
+    MarkDisposeOverride(ObjectDoNotLock);
+}
+
+BOOLEAN
+FxDmaTransactionBase::Dispose(
+    VOID
+    )
+{
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+    //
+    // Must not be in transfer state.
+    //
+    if (m_State == FxDmaTransactionStateTransfer) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                    "is invalid", GetHandle(), m_State);
+
+        if (pFxDriverGlobals->IsVerificationEnabled(1, 9, OkForDownLevel)) {
+            FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                               WDF_DMA_FATAL_ERROR,            // type
+                               (ULONG_PTR) GetObjectHandle(),  // parm 2
+                               (ULONG_PTR) m_State);           // parm 3
+        }
+    }
+
+    m_State = FxDmaTransactionStateDeleted;
+
+    //
+    // Release resources for this Dma Transaction.
+    //
+    ReleaseResources(TRUE);
+
+    if (m_EncodedRequest != NULL) {
+        ClearRequest();
+    }
+
+    return TRUE;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaTransactionBase::Initialize(
+    __in PFN_WDF_PROGRAM_DMA     ProgramDmaFunction,
+    __in WDF_DMA_DIRECTION       DmaDirection,
+    __in PMDL                    Mdl,
+    __in size_t                  Offset,
+    __in ULONG                   Length
+    )
+{
+    NTSTATUS status;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Enter WDFDMATRANSACTION %p", GetHandle());
+    //
+    // Must be in Reserve, Created or Released state.
+    //
+    if (m_State != FxDmaTransactionStateCreated &&
+        m_State != FxDmaTransactionStateReserved &&
+        m_State != FxDmaTransactionStateReleased) {
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                    "is invalid", GetHandle(), m_State);
+
+        FxVerifierBugCheck(pFxDriverGlobals,                // globals
+                           WDF_DMA_FATAL_ERROR, // specific type
+                           (ULONG_PTR) GetObjectHandle(),  // parm 2
+                           (ULONG_PTR) m_State);           // parm 3
+    }
+
+    if (DmaDirection == WdfDmaDirectionReadFromDevice) {
+        m_AdapterInfo = m_DmaEnabler->GetReadDmaDescription();
+    } else {
+        m_AdapterInfo = m_DmaEnabler->GetWriteDmaDescription();
+    }
+
+    //
+    // Initialize the DmaTransaction object
+    //
+
+    m_MaxFragmentLength  = m_AdapterInfo->MaximumFragmentLength;
+    m_DmaDirection       = DmaDirection;
+    m_StartMdl           = Mdl;
+    m_StartOffset        = Offset;
+    m_CurrentFragmentMdl = Mdl;
+    m_CurrentFragmentOffset = Offset;
+    m_Remaining          = Length;
+    m_TransactionLength  = Length;
+    m_DmaAcquiredFunction.Method.ProgramDma = ProgramDmaFunction;
+
+    //
+    // If needed, initialize the transfer context.
+    //
+
+    if (m_DmaEnabler->UsesDmaV3()) {
+        m_DmaEnabler->InitializeTransferContext(GetTransferContext(),
+                                                m_DmaDirection);
+    }
+
+    status = InitializeResources();
+    if (NT_SUCCESS(status)) {
+        m_State = FxDmaTransactionStateInitialized;
+    } else {
+        ReleaseForReuse(FALSE);
+    }
+
+    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Exit WDFDMATRANSACTION %p, %!STATUS!",
+                        GetHandle(), status);
+
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaTransactionBase::Execute(
+    __in PVOID          Context
+    )
+{
+    NTSTATUS status;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+    //
+    // Must be in Initialized state.
+    //
+    if (m_State != FxDmaTransactionStateInitialized) {
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                    "is invalid", GetHandle(), m_State);
+
+        FxVerifierBugCheck(pFxDriverGlobals,                // globals
+                           WDF_DMA_FATAL_ERROR, // specific type
+                           (ULONG_PTR) GetObjectHandle(),  // parm 2
+                           (ULONG_PTR) m_State);           // parm 3
+    }
+
+    //
+    // If this was initialized with a request, then reference the
+    // request now.
+    //
+    if (m_EncodedRequest != NULL) {
+        ReferenceRequest();
+    }
+
+    //
+    // Set state to Transfer.
+    // This is necessary because the Execute path complete
+    // all the way to DmaCompleted before returning to this point.
+    //
+    m_State = FxDmaTransactionStateTransfer;
+
+    //
+    // Save the caller's context
+    //
+    m_DmaAcquiredContext = Context;
+
+    ASSERT(m_Transferred == 0);
+    ASSERT(m_CurrentFragmentLength == 0);
+
+    status = StartTransfer();
+    if (!NT_SUCCESS(status)) {
+        m_State = FxDmaTransactionStateTransferFailed;
+        m_DmaAcquiredContext = NULL;
+
+        if (m_EncodedRequest != NULL) {
+            ReleaseButRetainRequest();
+        }
+    }
+
+    //
+    // StartTransfer results in a call to the EvtProgramDma routine
+    // where driver could complete and delete the object.  So
+    // don't touch the object beyond this point.
+    //
+
+    return status;
+}
+
+BOOLEAN
+FxDmaTransactionBase::DmaCompleted(
+    __in  size_t      TransferredLength,
+    __out NTSTATUS  * ReturnStatus,
+    __in  FxDmaCompletionType CompletionType
+    )
+{
+    BOOLEAN         hasTransitioned;
+    NTSTATUS        status;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+    WDFDMATRANSACTION dmaTransaction;
+
+    //
+    // In the case of partial completion, we will start a new transfer
+    // from with in this function by calling StageTransfer. After that
+    // call, we lose ownership of the object.  Since we need the handle
+    // for tracing purposes, we will save the value in a local variable and
+    // use that.
+    //
+    dmaTransaction = GetHandle();
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Enter WDFDMATRANSACTION %p, length %d",
+                        dmaTransaction, (ULONG)TransferredLength);
+    }
+
+    //
+    // Must be in Transfer state.
+    //
+    if (m_State != FxDmaTransactionStateTransfer) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                    "is invalid", dmaTransaction, m_State);
+
+        FxVerifierBugCheck(pFxDriverGlobals,                // globals
+                            WDF_DMA_FATAL_ERROR, // specific type
+                            (ULONG_PTR) dmaTransaction,  // parm 2
+                            (ULONG_PTR) m_State);           // parm 3
+    }
+
+    if (TransferredLength > m_CurrentFragmentLength) {
+        status = STATUS_INVALID_PARAMETER;
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p Transfered Length %I64d can't be more "
+                    "than the length asked to transfer %I64d "
+                    "%!STATUS!", dmaTransaction, TransferredLength,
+                    m_CurrentFragmentLength, status);
+        FxVerifierDbgBreakPoint(pFxDriverGlobals);
+        goto End;
+    }
+
+
+    if (CompletionType == FxDmaCompletionTypePartial ||
+        CompletionType == FxDmaCompletionTypeAbort) {
+        //
+        // Tally this DMA tranferred byte count into the accumulator.
+        //
+        m_Transferred += TransferredLength;
+
+        //
+        // Adjust the remaining length to account for the partial transfer.
+        //
+        m_Remaining += (m_CurrentFragmentLength - TransferredLength);
+
+        //
+        // Update CurrentDmaLength to reflect actual transfer because
+        // we need to FlushAdapterBuffers based on this value in
+        // TransferCompleted for packet based transfer.
+        //
+        m_CurrentFragmentLength = TransferredLength;
+
+    } else {
+        //
+        // Tally this DMA tranferred byte count into the accumulator.
+        //
+        m_Transferred += m_CurrentFragmentLength;
+    }
+
+    ASSERT(m_Transferred <= m_TransactionLength);
+
+    //
+    // Inform the derived object that transfer is completed so it
+    // can release resources specific to last transfer.
+    //
+    status = TransferCompleted();
+    if (!NT_SUCCESS(status)) {
+        goto End;
+    }
+
+    //
+    // If remaining DmaTransaction length is zero or if the driver wants
+    // this to be the last transfer then free the map registers and
+    // change the state to completed.
+    //
+    if (m_Remaining == 0 || CompletionType == FxDmaCompletionTypeAbort) {
+        status = STATUS_SUCCESS;
+        goto End;
+    }
+
+    //
+    // Stage the next packet for this DmaTransaction...
+    //
+    status = StageTransfer();
+
+    if (NT_SUCCESS(status)) {
+        //
+        // StageTransfer results in a call to the EvtProgramDma routine
+        // where driver could complete and delete the object.  So
+        // don't touch the object beyond this point.
+        //
+        status = STATUS_MORE_PROCESSING_REQUIRED;
+    }
+    else {
+        //
+        // The error will be returned to the caller of
+        // WdfDmaTransactionDmaComplete*()
+        //
+    }
+
+End:
+
+    if (status != STATUS_MORE_PROCESSING_REQUIRED) {
+        //
+        // Failed or succeeded. Either way free
+        // map registers and release the device.
+        //
+        if (NT_SUCCESS(status)) {
+            m_State = FxDmaTransactionStateTransferCompleted;
+        } else {
+            m_State = FxDmaTransactionStateTransferFailed;
+        }
+
+        if (pFxDriverGlobals->FxVerifierOn) {
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                "WDFDMATRANSACTION %p completed with status %!STATUS! - "
+                                "releasing DMA resources",
+                                GetHandle(),
+                                status);
+        }
+
+        ReleaseResources(FALSE);
+
+        if (m_EncodedRequest != NULL) {
+            ReleaseButRetainRequest();
+        }
+
+        m_CurrentFragmentLength = 0;
+
+        hasTransitioned = TRUE;
+    } else {
+        hasTransitioned = FALSE;
+    }
+
+    *ReturnStatus = status;
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Exit WDFDMATRANSACTION %p "
+                        "Transitioned(%!BOOLEAN!)",
+                        dmaTransaction, hasTransitioned);
+    }
+
+    return hasTransitioned;
+}
+
+VOID
+FxDmaTransactionBase::ReleaseForReuse(
+    __in BOOLEAN ForceRelease
+    )
+{
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+    if (ForceRelease == FALSE)
+    {
+        if (m_State == FxDmaTransactionStateReleased) {
+
+            //
+            // Double release is probably due to cancel during early in transaction
+            // initialization.  DC2 on very slow machines shows this behavior.
+            // The double release case is rare and benign.
+            //
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGDMA,
+                                "WDFDMATRANSACTION %p is already released, "
+                                "%!STATUS!", GetHandle(), STATUS_SUCCESS);
+
+            return;  // already released.
+        }
+
+        //
+        // Must not be in transfer state.
+        //
+        if (m_State == FxDmaTransactionStateTransfer) {
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                        "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                        "is invalid (release transaction)", GetHandle(), m_State);
+
+            if (pFxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
+                FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                                   WDF_DMA_FATAL_ERROR,            // type
+                                   (ULONG_PTR) GetObjectHandle(),  // parm 2
+                                   (ULONG_PTR) m_State);           // parm 3
+            }
+        }
+    }
+
+    m_State = FxDmaTransactionStateReleased;
+
+    ReleaseResources(ForceRelease);
+
+    //
+    // Except DMA enabler field and adapter info everything else should be
+    // cleared.  Adapter info is cleared by ReleaseResources above.
+    //
+    m_DmaAcquiredContext = NULL;
+
+    if (m_EncodedRequest != NULL) {
+        ClearRequest();
+    }
+
+    m_StartMdl = NULL;
+    m_CurrentFragmentMdl = NULL;
+    m_StartOffset = 0;
+    m_CurrentFragmentOffset = 0;
+    m_CurrentFragmentLength = 0;
+    m_Transferred = 0;
+    m_Remaining = 0;
+    m_MaxFragmentLength = 0;
+    m_TransactionLength = 0;
+    m_Flags = 0;
+
+    m_DmaAcquiredFunction.Method.ProgramDma = NULL;
+
+}
+
+VOID
+FxDmaTransactionBase::SetImmediateExecution(
+    __in BOOLEAN Value
+    )
+{
+    if (m_State != FxDmaTransactionStateCreated &&
+        m_State != FxDmaTransactionStateInitialized &&
+        m_State != FxDmaTransactionStateReleased) {
+        DoTraceLevelMessage(
+            GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA,
+            "Must set immediate execution flag for WDFDMATRANSACTION "
+            "%p before calling AllocateResources or Execute (current "
+            "state is %!FxDmaTransactionState!)",
+            GetHandle(),
+            m_State
+            );
+        FxVerifierDbgBreakPoint(GetDriverGlobals());
+    }
+
+    if (Value) {
+        m_Flags |= DMA_SYNCHRONOUS_CALLBACK;
+    }
+    else {
+        m_Flags &= ~DMA_SYNCHRONOUS_CALLBACK;
+    }
+}
+
+BOOLEAN
+FxDmaTransactionBase::CancelResourceAllocation(
+    VOID
+    )
+{
+    if ((m_State == FxDmaTransactionStateCreated) ||
+        (m_State == FxDmaTransactionStateReleased) ||
+        (m_State == FxDmaTransactionStateDeleted)) {
+
+        DoTraceLevelMessage(
+            GetDriverGlobals(),
+            TRACE_LEVEL_ERROR,
+            TRACINGDMA,
+            "WDFDMATRANSACTION %p cannot be cancelled in state "
+            "%!FxDmaTransactionState!",
+            GetHandle(),
+            m_State
+            );
+
+        FxVerifierBugCheck(GetDriverGlobals(),
+                           WDF_DMA_FATAL_ERROR,
+                           (ULONG_PTR) GetObjectHandle(),
+                           (ULONG_PTR) m_State);
+        // unreachable code
+    }
+
+    PDMA_OPERATIONS dmaOperations =
+        m_AdapterInfo->AdapterObject->DmaOperations;
+
+    BOOLEAN result;
+
+    result = dmaOperations->CancelAdapterChannel(
+                            m_AdapterInfo->AdapterObject,
+                            m_DmaEnabler->m_FDO,
+                            GetTransferContext()
+                            );
+
+    if (result) {
+        m_State = FxDmaTransactionStateTransferFailed;
+
+        if (m_EncodedRequest != NULL) {
+            ReleaseButRetainRequest();
+        }
+    }
+
+    return result;
+}
+
+VOID
+FxDmaTransactionBase::_ComputeNextTransferAddress(
+    __in PMDL CurrentMdl,
+    __in size_t CurrentOffset,
+    __in ULONG Transferred,
+    __deref_out PMDL  *NextMdl,
+    __out size_t *NextOffset
+    )
+/*++
+
+Routine Description:
+
+    This function computes the next mdl and offset given the current MDL,
+    offset and bytes transfered.
+
+Arguments:
+
+    CurrentMdl - Mdl where the transfer currently took place.
+
+    CurrentVa - Current virtual address in the buffer
+
+    Transfered - Bytes transfered or to be transfered
+
+    NextMdl - Mdl where the next transfer will take place
+
+    NextVA - Offset within NextMdl where the transfer will start
+
+--*/
+{
+    size_t transfered, mdlSize;
+    PMDL mdl;
+
+    mdlSize = MmGetMdlByteCount(CurrentMdl) - CurrentOffset;
+
+    if (Transferred < mdlSize) {
+        //
+        // We are still in the first MDL
+        //
+        *NextMdl = CurrentMdl;
+        *NextOffset = CurrentOffset + Transferred;
+        return;
+    }
+
+    //
+    // We have transfered the content of the first MDL.
+    // Move to the next one.
+    //
+    transfered = Transferred - mdlSize;
+    mdl = CurrentMdl->Next;
+    ASSERT(mdl != NULL);
+
+    while (transfered >= MmGetMdlByteCount(mdl)) {
+        //
+        // We have transfered the content of this MDL.
+        // Move to the next one.
+        //
+        transfered -= MmGetMdlByteCount(mdl);
+        mdl = mdl->Next;
+        ASSERT(mdl != NULL);
+    }
+
+    //
+    // This is the mdl where the last transfer occured.
+    //
+    *NextMdl = mdl;
+    *NextOffset = transfered;
+
+    return;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaTransactionBase::_CalculateRequiredMapRegisters(
+    __in PMDL Mdl,
+    __in size_t CurrentOffset,
+    __in ULONG Length,
+    __in ULONG AvailableMapRegisters,
+    __out_opt PULONG PossibleTransferLength,
+    __out PULONG MapRegistersRequired
+    )
+/*++
+
+Routine Description:
+
+    Used on Windows 2000 to compute number of map registered required
+    for this transfer. This is derived from HalCalculateScatterGatherListSize.
+
+Arguments:
+
+    Mdl - Pointer to the MDL that describes the pages of memory that are being
+        read or written.
+
+    CurrentVa - Current virtual address in the buffer described by the MDL
+        that the transfer is being done to or from.
+
+    Length - Supplies the length of the transfer.
+
+    AvailableMapRegisters - Map registers available to do the transfer
+
+    PossibleTransferLength - Length that can transfered for the
+
+    MapRegistersRequired - Map registers required to the entire transfer
+
+Return Value:
+
+    NTSTATUS
+
+Notes:
+
+--*/
+{
+    PMDL tempMdl;
+    ULONG requiredMapRegisters;
+    ULONG transferLength;
+    ULONG mdlLength;
+    ULONG pageOffset;
+    ULONG possTransferLength;
+
+    //
+    // Calculate the number of required map registers.
+    //
+    tempMdl = Mdl;
+    transferLength = (ULONG) MmGetMdlByteCount(tempMdl) - (ULONG) CurrentOffset;
+    mdlLength = transferLength;
+
+    pageOffset = BYTE_OFFSET(GetStartVaFromOffset(tempMdl, CurrentOffset));
+    requiredMapRegisters = 0;
+    possTransferLength = 0;
+
+    //
+    // The virtual address should fit in the first MDL.
+    //
+
+    ASSERT(CurrentOffset <= tempMdl->ByteCount);
+
+    //
+    // Loop through chained MDLs, accumulating the required
+    // number of map registers.
+    //
+
+    while (transferLength < Length && tempMdl->Next != NULL) {
+
+        //
+        // With pageOffset and length, calculate number of pages spanned by
+        // the buffer.
+        //
+        requiredMapRegisters += (pageOffset + mdlLength + PAGE_SIZE - 1) >>
+                                    PAGE_SHIFT;
+
+        if (requiredMapRegisters <= AvailableMapRegisters) {
+            possTransferLength = transferLength;
+        }
+
+        tempMdl = tempMdl->Next;
+        pageOffset = tempMdl->ByteOffset;
+        mdlLength = tempMdl->ByteCount;
+        transferLength += mdlLength;
+    }
+
+    if ((transferLength + PAGE_SIZE) < (Length + pageOffset )) {
+        ASSERT(transferLength >= Length);
+        return STATUS_BUFFER_TOO_SMALL;
+    }
+
+    //
+    // Calculate the last number of map registers based on the requested
+    // length not the length of the last MDL.
+    //
+
+    ASSERT( transferLength <= mdlLength + Length );
+
+    requiredMapRegisters += (pageOffset + Length + mdlLength - transferLength +
+                             PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+    if (requiredMapRegisters <= AvailableMapRegisters) {
+        possTransferLength += (Length + mdlLength - transferLength);
+    }
+
+    if (PossibleTransferLength != NULL) {
+        *PossibleTransferLength = possTransferLength;
+    }
+
+    ASSERT(*PossibleTransferLength);
+
+    *MapRegistersRequired = requiredMapRegisters;
+
+    return STATUS_SUCCESS;
+}
+
+// ----------------------------------------------------------------------------
+// ------------------- Scatter/Gather DMA Section -----------------------------
+// ----------------------------------------------------------------------------
+
+FxDmaScatterGatherTransaction::FxDmaScatterGatherTransaction(
+    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+    __in USHORT ExtraSize,
+    __in FxDmaEnabler *DmaEnabler
+    ) :
+    FxDmaTransactionBase(FxDriverGlobals,
+                         sizeof(FxDmaScatterGatherTransaction),
+                         ExtraSize,
+                         DmaEnabler)
+{
+    m_LookasideBuffer       = NULL;
+    m_SGList                = NULL;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaScatterGatherTransaction::_Create(
+    __in  PFX_DRIVER_GLOBALS      FxDriverGlobals,
+    __in  PWDF_OBJECT_ATTRIBUTES  Attributes,
+    __in  FxDmaEnabler*           DmaEnabler,
+    __out WDFDMATRANSACTION*      Transaction
+    )
+{
+    FxDmaScatterGatherTransaction* pTransaction;
+    WDFOBJECT hTransaction;
+    NTSTATUS status;
+
+    pTransaction = new (FxDriverGlobals, Attributes, DmaEnabler->GetTransferContextSize())
+                FxDmaScatterGatherTransaction(FxDriverGlobals,
+                                              DmaEnabler->GetTransferContextSize(),
+                                              DmaEnabler);
+
+    if (pTransaction == NULL) {
+        status =  STATUS_INSUFFICIENT_RESOURCES;
+        DoTraceLevelMessage(
+            FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+            "Could not allocate memory for WDFTRANSACTION, %!STATUS!", status);
+        return status;
+    }
+
+    //
+    // Commit and apply the attributes
+    //
+    status = pTransaction->Commit(Attributes, &hTransaction, DmaEnabler);
+
+    if (NT_SUCCESS(status) && DmaEnabler->m_IsSGListAllocated) {
+
+        //
+        // Allocate buffer for SGList from lookaside list.
+        //
+        pTransaction->m_LookasideBuffer = (SCATTER_GATHER_LIST *)
+            FxAllocateFromNPagedLookasideList(
+                &DmaEnabler->m_SGList.ScatterGatherProfile.Lookaside
+                );
+
+        if (pTransaction->m_LookasideBuffer == NULL) {
+            status = STATUS_INSUFFICIENT_RESOURCES;
+            DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                                "Unable to allocate memory for SG List, "
+                                "WDFDMATRANSACTION %p, %!STATUS! ",
+                                pTransaction->GetHandle(), status);
+        }
+        else {
+            //
+            // Take a reference on the enabler to ensure that it remains valid
+            // if the transaction's disposal is deferred.
+            //
+            DmaEnabler->ADDREF(pTransaction);
+        }
+    }
+
+    if (NT_SUCCESS(status)) {
+        *Transaction = (WDFDMATRANSACTION)hTransaction;
+    }
+    else {
+        //
+        // This will properly clean up the target's state and free it
+        //
+        pTransaction->DeleteFromFailedCreate();
+    }
+
+    return status;
+}
+
+BOOLEAN
+FxDmaScatterGatherTransaction::Dispose(
+    VOID
+    )
+{
+    BOOLEAN ret;
+
+    ret = __super::Dispose();
+
+    //
+    // Free Lookaside Buffer which held SGList
+    //
+    if (m_LookasideBuffer != NULL) {
+
+        FxFreeToNPagedLookasideList(
+            &m_DmaEnabler->m_SGList.ScatterGatherProfile.Lookaside,
+            m_LookasideBuffer
+            );
+        m_LookasideBuffer = NULL;
+        m_DmaEnabler->RELEASE(this);
+    }
+
+    return ret;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaScatterGatherTransaction::InitializeResources(
+    VOID
+    )
+{
+    NTSTATUS status;
+    PMDL  nextMdl;
+    size_t nextOffset;
+    ULONG mapRegistersRequired;
+    size_t remLength, transferLength, transferred, possibleLength=0;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+    status = STATUS_SUCCESS;
+
+    //
+    // If the caller has specified a limit on the number of scatter-gather
+    // elements each transfer can support then make sure it's within the
+    // limit by breaking up the whole transfer into m_MaxFragmentLength and
+    // computing the number of map-registers required for each fragment.
+    // This check may not be valid if the driver starts to do partial
+    // transfers. So driver that do partial transfer with sg-element limit
+    // should be capable of handling STATUS_WDF_TOO_FRAGMENTED failures during
+    // dma execution.
+    //
+    remLength = m_TransactionLength;
+    transferred = 0;
+    nextMdl = m_StartMdl;
+    nextOffset = m_StartOffset;
+    transferLength = 0;
+
+    while (remLength != 0) {
+
+        _ComputeNextTransferAddress(nextMdl,
+                                    nextOffset,
+                                    (ULONG) transferLength,
+                                    &nextMdl,
+                                    &nextOffset);
+
+        transferLength = FxSizeTMin(remLength, m_MaxFragmentLength);
+
+        status = _CalculateRequiredMapRegisters(nextMdl,
+                                     nextOffset,
+                                     (ULONG) transferLength,
+                                     m_AdapterInfo->NumberOfMapRegisters,
+                                     (PULONG) &possibleLength,
+                                     &mapRegistersRequired
+                                     );
+
+        if (!NT_SUCCESS(status)) {
+            DoTraceLevelMessage(
+                pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                "CalculateScatterGatherList failed for "
+                "WDFDMATRANSACTION %p, %!STATUS!", GetHandle(), status);
+            FxVerifierDbgBreakPoint(pFxDriverGlobals);
+            return status;
+        }
+
+        if (mapRegistersRequired > m_DmaEnabler->m_MaxSGElements) {
+            status = STATUS_WDF_TOO_FRAGMENTED;
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                        "WDFDMATRANSACTION %p for MDL %p is more fragmented (%d) "
+                        "than the limit (%I64d) specified by the driver, %!STATUS! ",
+                        GetHandle(), nextMdl, mapRegistersRequired,
+                        m_DmaEnabler->m_MaxSGElements, status);
+            return status;
+        }
+
+        transferred += transferLength;
+        remLength -= transferLength;
+    }
+
+    return status;
+}
+
+VOID
+FxDmaScatterGatherTransaction::ReleaseResources(
+    __in BOOLEAN /* ForceRelease */
+    )
+{
+    if (m_SGList != NULL) {
+        PutScatterGatherList(m_SGList);
+        m_SGList = NULL;
+    }
+    m_AdapterInfo = NULL;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaScatterGatherTransaction::StartTransfer(
+    VOID
+    )
+{
+    ASSERT(m_CurrentFragmentMdl == m_StartMdl);
+    ASSERT(m_CurrentFragmentOffset == m_StartOffset);
+    ASSERT(m_CurrentFragmentLength == 0);
+    ASSERT(m_Transferred == 0);
+
+    return StageTransfer();
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaScatterGatherTransaction::StageTransfer(
+    VOID
+    )
+{
+    NTSTATUS           status;
+    ULONG   mapRegistersRequired;
+    WDFDMATRANSACTION dmaTransaction;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+    //
+    // Use an invalid value to make the function fail if the var is not
+    // updated correctly below.
+    //
+    mapRegistersRequired = 0xFFFFFFFF;
+
+    //
+    // Client driver could complete and delete the object in
+    // EvtProgramDmaFunction. So, save the handle because we need it
+    // for tracing after we invoke the callback.
+    //
+    dmaTransaction = GetHandle();
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Enter WDFDMATRANSACTION %p ", GetHandle());
+    }
+
+    //
+    // Given the first MDL and the bytes transfered, find the next MDL
+    // and byteoffset within that MDL.
+    //
+    _ComputeNextTransferAddress(m_CurrentFragmentMdl,
+                                m_CurrentFragmentOffset,
+                                (ULONG) m_CurrentFragmentLength,
+                                &m_CurrentFragmentMdl,
+                                &m_CurrentFragmentOffset);
+
+    //
+    // Get the next possible transfer size.
+    //
+    m_CurrentFragmentLength = FxSizeTMin(m_Remaining, m_MaxFragmentLength);
+
+    //
+    // Fix m_CurrentFragmentLength to meet the map registers limit. This is done
+    // in case the MDL is a chained MDL for an highly fragmented buffer.
+    //
+    status = _CalculateRequiredMapRegisters(m_CurrentFragmentMdl,
+                                   m_CurrentFragmentOffset,
+                                   (ULONG) m_CurrentFragmentLength,
+                                   m_AdapterInfo->NumberOfMapRegisters,
+                                   (PULONG)&m_CurrentFragmentLength,
+                                   &mapRegistersRequired);
+    //
+    // We have already validated the entire transfer during initialize
+    // to see each transfer meets the sglimit. So this call shouldn't fail.
+    // But, if the driver does partial transfer and changes the fragment
+    // boundaries then it's possible for the sg-elements to vary. So, check
+    // one more time to see if we are within the bounds before building
+    // the sglist and calling into the driver.
+    //
+    ASSERT(NT_SUCCESS(status));
+
+    if (mapRegistersRequired > m_DmaEnabler->m_MaxSGElements) {
+        status = STATUS_WDF_TOO_FRAGMENTED;
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p for MDL %p is more fragmented (%d) "
+                    "than the limit (%I64d) specified by the driver, %!STATUS! ",
+                    dmaTransaction, m_CurrentFragmentMdl, mapRegistersRequired,
+                    m_DmaEnabler->m_MaxSGElements, status);
+        return status;
+    }
+
+
+    m_Remaining -= m_CurrentFragmentLength;
+
+    if (m_DmaEnabler->m_IsSGListAllocated) {
+
+        ASSERT(m_LookasideBuffer != NULL);
+        status = BuildScatterGatherList(m_CurrentFragmentMdl,
+                                        m_CurrentFragmentOffset,
+                                        (ULONG) m_CurrentFragmentLength,
+#pragma prefast(suppress: __WARNING_CLASS_MISMATCH_NONE, "This warning requires a wrapper class for the DRIVER_LIST_CONTROL type.")
+                                        _AdapterListControl,
+                                        this,
+                                        m_LookasideBuffer,
+                                        (ULONG) m_AdapterInfo->PreallocatedSGListSize);
+
+    } else {
+
+        status = GetScatterGatherList(m_CurrentFragmentMdl,
+                                      m_CurrentFragmentOffset,
+                                      (ULONG) m_CurrentFragmentLength,
+#pragma prefast(suppress: __WARNING_CLASS_MISMATCH_NONE, "This warning requires a wrapper class for the DRIVER_LIST_CONTROL type.")
+                                      _AdapterListControl,
+                                      this);
+    }
+
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                            "Build or GetScatterGatherList failed for "
+                            "WDFDMATRANSACTION %p, %!STATUS!",
+                            dmaTransaction, status);
+        //
+        // Readjust remaining bytes transfered.
+        //
+        m_Remaining += m_CurrentFragmentLength;
+        return status;
+    }
+
+    //
+    // Before GetScatterGatherList returns, _AdapterListControl can get called
+    // on another thread and the driver could delete the transaction object.
+    // So don't touch the object after this point.
+    //
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Exit WDFDMATRANSACTION %p, "
+                        "%!STATUS!", dmaTransaction, status);
+    }
+
+    return status;
+}
+
+
+VOID
+FxDmaScatterGatherTransaction::_AdapterListControl(
+    __in  PDEVICE_OBJECT         DeviceObject,
+    __in  PIRP                   Irp,            // UNUSED
+    __in  PSCATTER_GATHER_LIST   SgList,
+    __in  PVOID                  Context
+    )
+{
+    PFX_DRIVER_GLOBALS pFxDriverGlobals;
+    WDFDMATRANSACTION dmaTransaction;
+    FxDmaScatterGatherTransaction * pDmaTransaction;
+
+    UNREFERENCED_PARAMETER(Irp);
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    pDmaTransaction = (FxDmaScatterGatherTransaction*) Context;
+    pFxDriverGlobals = pDmaTransaction->GetDriverGlobals();
+    dmaTransaction = pDmaTransaction->GetHandle();
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Enter WDFDMATRANSACTION %p",
+                        dmaTransaction);
+    }
+
+    ASSERT(pDmaTransaction != NULL);
+    ASSERT(pDmaTransaction->m_DmaAcquiredFunction.Method.ProgramDma != NULL);
+
+    ASSERT(SgList->NumberOfElements <= pDmaTransaction->m_DmaEnabler->GetMaxSGElements());
+
+    pDmaTransaction->m_SGList = SgList;
+
+    //
+    // We ignore the return value. The pattern we want the driver to follow is
+    // that if it fails to program DMA transfer, it should call DmaCompletedFinal
+    // to abort the transfer.
+    //
+    (VOID) pDmaTransaction->m_DmaAcquiredFunction.InvokeProgramDma(
+                dmaTransaction,
+                pDmaTransaction->m_DmaEnabler->m_DeviceBase->GetHandle(),
+                pDmaTransaction->m_DmaAcquiredContext,
+                pDmaTransaction->m_DmaDirection,
+                SgList);
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Exit WDFDMATRANSACTION %p",
+                        dmaTransaction);
+    }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaScatterGatherTransaction::TransferCompleted(
+    VOID
+    )
+{
+    //
+    // All we have to do is release the scatter-gather list.
+    //
+    if (m_SGList != NULL) {
+
+        PutScatterGatherList(m_SGList);
+        m_SGList = NULL;
+    }
+
+    return STATUS_SUCCESS;
+}
+
+
+// ----------------------------------------------------------------------------
+// ------------------- PACKET DMA SECTION -------------------------------------
+// ----------------------------------------------------------------------------
+
+FxDmaPacketTransaction::FxDmaPacketTransaction(
+    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+    __in USHORT ObjectSize,
+    __in USHORT ExtraSize,
+    __in FxDmaEnabler *DmaEnabler
+    ) :
+    FxDmaTransactionBase(FxDriverGlobals, ObjectSize, ExtraSize, DmaEnabler)
+{
+    m_MapRegistersNeeded  = 0;
+    m_MapRegisterBase     = NULL;
+    m_MapRegisterBaseSet = FALSE;
+    m_DeviceAddressOffset = 0;
+    m_MapRegistersReserved = 0;
+
+    m_IsCancelled = FALSE;
+
+    m_TransferState.CurrentStagingThread = NULL;
+    m_TransferState.RerunStaging = FALSE;
+    m_TransferState.RerunCompletion = FALSE;
+    m_TransferState.CompletionStatus = UNDEFINED_DMA_COMPLETION_STATUS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaPacketTransaction::_Create(
+    __in  PFX_DRIVER_GLOBALS      FxDriverGlobals,
+    __in  PWDF_OBJECT_ATTRIBUTES  Attributes,
+    __in  FxDmaEnabler*           DmaEnabler,
+    __out WDFDMATRANSACTION*      Transaction
+    )
+{
+    FxDmaPacketTransaction* pTransaction;
+    WDFOBJECT hTransaction;
+    NTSTATUS status;
+
+    pTransaction = new (FxDriverGlobals, Attributes, DmaEnabler->GetTransferContextSize())
+                FxDmaPacketTransaction(FxDriverGlobals,
+                                       sizeof(FxDmaPacketTransaction),
+                                       DmaEnabler->GetTransferContextSize(),
+                                       DmaEnabler);
+
+    if (pTransaction == NULL) {
+        status =  STATUS_INSUFFICIENT_RESOURCES;
+        DoTraceLevelMessage(
+            FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+            "Could not allocate memory for WDFTRANSACTION, %!STATUS!", status);
+        return status;
+    }
+
+    //
+    // Commit and apply the attributes
+    //
+    status = pTransaction->Commit(Attributes, &hTransaction, DmaEnabler);
+    if (NT_SUCCESS(status)) {
+        *Transaction = (WDFDMATRANSACTION)hTransaction;
+    }
+    else {
+        //
+        // This will properly clean up the target's state and free it
+        //
+        pTransaction->DeleteFromFailedCreate();
+    }
+
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaPacketTransaction::InitializeResources(
+    VOID
+    )
+{
+    KIRQL oldIrql;
+    m_DeviceAddressOffset = 0;
+    LockTransferState(&oldIrql);
+    m_IsCancelled = FALSE;
+    UnlockTransferState(oldIrql);
+    return STATUS_SUCCESS;
+}
+
+VOID
+FxDmaPacketTransaction::ReleaseResources(
+    __in BOOLEAN ForceRelease
+    )
+{
+    //
+    // If the map register base hasn't been assigned, then just
+    // skip this.
+    //
+
+    if (IsMapRegisterBaseSet() == FALSE) {
+        return;
+    }
+
+    //
+    // Map registers are reserved.  Unless the caller is forcing
+    // us to free them, just return.  Otherwise updated the
+    // number of map registers that FreeMapRegistersAndAdapter
+    // is going to look at.
+    //
+    if ((m_MapRegistersReserved > 0) && (ForceRelease == FALSE))
+    {
+        return;
+    }
+
+    //
+    // Free the map registers and release the device.
+    //
+    FreeMapRegistersAndAdapter();
+
+    ClearMapRegisterBase();
+
+    ReleaseDevice();
+
+    m_AdapterInfo = NULL;
+    m_MapRegistersReserved = 0;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaPacketTransaction::ReserveAdapter(
+    __in     ULONG               NumberOfMapRegisters,
+    __in     WDF_DMA_DIRECTION   DmaDirection,
+    __in     PFN_WDF_RESERVE_DMA Callback,
+    __in_opt PVOID               Context
+    )
+{
+    NTSTATUS status;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+    WDFDMATRANSACTION dmaTransaction = GetHandle();
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Enter WDFDMATRANSACTION %p", dmaTransaction);
+    }
+
+    //
+    // If caller doesn't supply a map register count then we get the count
+    // out of the transaction.  So the transaction must be initialized.
+    //
+    // Otherwise the transaction can't be executing.
+    //
+    if (NumberOfMapRegisters == 0) {
+        if (m_State != FxDmaTransactionStateInitialized) {
+            status = STATUS_INVALID_PARAMETER;
+
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                                "RequiredMapRegisters cannot be 0 because "
+                                "WDFDMATRANSACTION %p is not initialized ("
+                                "state is %!FxDmaTransactionState!) - %!STATUS!",
+                                GetHandle(),
+                                m_State,
+                                status);
+            FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                               WDF_DMA_FATAL_ERROR,            // specific type
+                               (ULONG_PTR) GetObjectHandle(),  // parm 2
+                               (ULONG_PTR) m_State);           // parm 3
+        }
+    }
+    else if (m_State != FxDmaTransactionStateCreated &&
+             m_State != FxDmaTransactionStateInitialized &&
+             m_State != FxDmaTransactionStateReleased) {
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                    "is invalid", dmaTransaction, m_State);
+
+        FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                           WDF_DMA_FATAL_ERROR,            // specific type
+                           (ULONG_PTR) GetObjectHandle(),  // parm 2
+                           (ULONG_PTR) m_State);           // parm 3
+    }
+
+    //
+    // Must not already have reserved map registers
+    //
+    if (m_MapRegistersReserved != 0)
+    {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p already has allocated map registers.",
+                    dmaTransaction);
+
+        FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                           WDF_DMA_FATAL_ERROR,            // specific type
+                           (ULONG_PTR) GetObjectHandle(),  // parm 2
+                           (ULONG_PTR) m_State);           // parm 3
+    }
+
+    //
+    // Get the adapter
+    //
+    if (DmaDirection == WdfDmaDirectionReadFromDevice) {
+        m_AdapterInfo = m_DmaEnabler->GetReadDmaDescription();
+    } else {
+        m_AdapterInfo = m_DmaEnabler->GetWriteDmaDescription();
+    }
+
+    //
+    // Save the number of map registers being reserved.
+    //
+    if (NumberOfMapRegisters != 0) {
+
+        //
+        // Use the number the caller passed us
+        //
+        m_MapRegistersReserved = NumberOfMapRegisters;
+    }
+    else if (m_DmaEnabler->IsBusMaster() == FALSE) {
+
+        //
+        // For system DMA use all the map registers we have
+        //
+        m_MapRegistersReserved = m_AdapterInfo->NumberOfMapRegisters;
+
+    } else {
+
+        //
+        // Compute the number of map registers required based on
+        // the MDL and length passed in
+        //
+        status = _CalculateRequiredMapRegisters(
+                    m_StartMdl,
+                    m_StartOffset,
+                    (ULONG) m_TransactionLength,
+                    m_AdapterInfo->NumberOfMapRegisters,
+                    NULL,
+                    &m_MapRegistersReserved
+                    );
+
+        if (!NT_SUCCESS(status)) {
+            ReleaseForReuse(TRUE);
+            goto End;
+        }
+    }
+
+    //
+    // Initialize the DmaTransaction object with enough data to
+    // trick StartTransfer into allocating the adapter channel for us.
+    //
+    m_DmaDirection       = DmaDirection;
+    m_StartMdl           = NULL;
+    m_StartOffset        = 0;
+    m_CurrentFragmentMdl = NULL;
+    m_CurrentFragmentOffset = 0;
+    m_Remaining          = 0;
+    m_TransactionLength  = 0;
+
+    //
+    // Save the callback and context
+    //
+    m_DmaAcquiredFunction.Method.ReserveDma = Callback;
+    m_DmaAcquiredContext = Context;
+
+    //
+    // If needed, initialize the transfer context.
+    //
+    if (m_DmaEnabler->UsesDmaV3()) {
+        m_DmaEnabler->InitializeTransferContext(GetTransferContext(),
+                                                m_DmaDirection);
+    }
+
+    status = InitializeResources();
+    if (NT_SUCCESS(status)) {
+        //
+        // Set the state to reserved so _AdapterControl knows which
+        // callback to invoke.
+        //
+        m_State = FxDmaTransactionStateReserved;
+    } else {
+        ReleaseForReuse(TRUE);
+        goto End;
+    }
+
+    //
+    // Start the adapter channel allocation through StartTransfer
+    //
+    status = StartTransfer();
+
+End:
+    if (!NT_SUCCESS(status)) {
+        m_State = FxDmaTransactionStateTransferFailed;
+        m_DmaAcquiredFunction.Method.ReserveDma = NULL;
+        m_DmaAcquiredContext = NULL;
+        m_MapRegistersReserved = 0;
+
+        if (m_EncodedRequest != NULL) {
+            ReleaseButRetainRequest();
+        }
+    }
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Exit WDFDMATRANSACTION %p, %!STATUS!",
+                            dmaTransaction, status);
+    }
+
+    return status;
+}
+
+VOID
+FxDmaPacketTransaction::ReleaseAdapter(
+    VOID
+    )
+{
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+    WDFDMATRANSACTION dmaTransaction = GetHandle();
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Enter WDFDMATRANSACTION %p", dmaTransaction);
+    }
+
+    //
+    // Must not be in invalid, created, transfer or deleted state
+    //
+    if (m_State == FxDmaTransactionStateInvalid ||
+        m_State == FxDmaTransactionStateCreated ||
+        m_State == FxDmaTransactionStateTransfer ||
+        m_State == FxDmaTransactionStateDeleted) {
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                    "is invalid", dmaTransaction, m_State);
+
+        FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                           WDF_DMA_FATAL_ERROR,            // specific type
+                           (ULONG_PTR) GetObjectHandle(),  // parm 2
+                           (ULONG_PTR) m_State);           // parm 3
+    }
+
+    //
+    // The caller wants to free the reserved map registers, so force their
+    // release.
+    //
+    ReleaseForReuse(TRUE);
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Exit WDFDMATRANSACTION %p",
+                            dmaTransaction);
+    }
+
+    return;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaPacketTransaction::StartTransfer(
+    VOID
+    )
+{
+    NTSTATUS           status;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+    WDFDMATRANSACTION  dmaTransaction;
+
+    //
+    // Client driver could complete and delete the object in
+    // EvtProgramDmaFunction. So, save the handle because we need it
+    // for tracing after we invoke the callback.
+    //
+    dmaTransaction = GetHandle();
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Enter WDFDMATRANSACTION %p",
+                            dmaTransaction);
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Starting WDFDMATRANSACTION %p - MDL %#p, "
+                            "offset %I64x, length %I64x",
+                            dmaTransaction,
+                            m_StartMdl,
+                            m_StartOffset,
+                            m_TransactionLength);
+    }
+
+    //
+    // Reference the device when using DMA v2.  For DMA v3 we can support
+    // concurrent attempts to allocate the channel.
+    //
+    status = AcquireDevice();
+    if (!NT_SUCCESS(status)) {
+
+        NT_ASSERTMSG("AcquireDevice should never fail when DMAv3 is in use",
+                     m_DmaEnabler->UsesDmaV3());
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                            "Only one transaction can be queued "
+                            "at one time on a packet based WDFDMAENABLER %p "
+                            "%!STATUS!", m_DmaEnabler->GetHandle(),
+                            status);
+        FxVerifierDbgBreakPoint(pFxDriverGlobals);
+        return status;
+    }
+
+    //
+    // Calculate the initial DMA transfer length.
+    //
+    m_CurrentFragmentLength = FxSizeTMin(m_Remaining, m_MaxFragmentLength);
+
+    m_CurrentFragmentOffset = m_StartOffset;
+
+    if (m_State == FxDmaTransactionStateReserved) {
+        //
+        // Caller is simply reserving the DMA adapter for later use.  Ask for
+        // as many map registers as the driver requested.
+        //
+        m_MapRegistersNeeded = m_MapRegistersReserved;
+
+        ASSERT(m_MapRegistersNeeded <= m_AdapterInfo->NumberOfMapRegisters);
+
+        status = AllocateAdapterChannel(FALSE);
+
+    }
+    else {
+
+        if (m_DmaEnabler->IsBusMaster() == FALSE) {
+
+            //
+            // Use as many map registers as we were granted.
+            //
+            m_MapRegistersNeeded = m_AdapterInfo->NumberOfMapRegisters;
+        } else {
+
+            //
+            // If the transfer is the size of the transaction then use the offset
+            // to determine the number of map registers needed.  If it's smaller
+            // then use the worst-case offset to make sure we ask for enough MR's
+            // to account for a bigger offset in one of the later transfers.
+            //
+            // Example:
+            //  Transaction is 8 KB and is page aligned
+            //      if max transfer is >= 8KB then this will be one transfer and only
+            //      requires two map registers.  Even if the driver completes a partial
+            //      transfer and we have to do the rest in a second transfer it will
+            //      fit within two map registers becuase the overall transaction does
+            //      (and a partial transfer can't take more map registers than the
+            //       whole transaction would).
+            //
+            //      If max transfer is 2KB then this nominally requires 4 2KB transfers.
+            //      In this case however, a partial completion of one of those transfers
+            //      would leave us attempting a second 2KB transfer starting on an
+            //      unaligned address.  For example, we might transfer 2KB, then 1KB
+            //      then 2KB.  Even though the first transfer was page aligned, the
+            //      3rd transfer isn't and could cross a page boundary, requiring two
+            //      map registers rather than one.
+            //
+            // To account for this second case, ignore the actual MDL offset and
+            // instead compute the maximum number of map registers than an N byte
+            // transfer could take (with worst-case alignment).
+            //
+            //
+            m_MapRegistersNeeded =
+                (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES(
+                    ((m_CurrentFragmentLength == m_Remaining) ?
+                        GetStartVaFromOffset(m_CurrentFragmentMdl,
+                                             m_CurrentFragmentOffset) :
+                        (PVOID)(ULONG_PTR) (PAGE_SIZE -1)),
+                    m_CurrentFragmentLength
+                    );
+
+
+            ASSERT(m_MapRegistersNeeded <= m_AdapterInfo->NumberOfMapRegisters);
+        }
+
+        //
+        // NOTE: the number of map registers needed for this transfer may
+        //       exceed the number that we've reserved.  StageTransfer will
+        //       take care of fragmenting the transaction accordingly.
+        //
+        status = AllocateAdapterChannel(m_MapRegistersReserved > 0);
+    }
+
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                            "AllocateAdapterChannel failed for "
+                            "WDFDMATRANSACTION %p, %!STATUS!",
+                            dmaTransaction, status);
+        ReleaseDevice();
+    }
+
+    //
+    // Before AllocateAdapterChannel returns, _AdapterControl can get called
+    // on another thread and the driver could delete the transaction object.
+    // So don't touch the object after this point.
+    //
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Exit WDFDMATRANSACTION %p, "
+                        "%!STATUS!", dmaTransaction, status);
+    }
+
+    return status;
+}
+
+IO_ALLOCATION_ACTION
+FxDmaPacketTransaction::_AdapterControl(
+    __in PDEVICE_OBJECT  DeviceObject,
+    __in PIRP            Irp,
+    __in PVOID           MapRegisterBase,
+    __in PVOID           Context
+    )
+{
+    FxDmaPacketTransaction * pDmaTransaction;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals;
+    IO_ALLOCATION_ACTION action;
+    NTSTATUS status;
+
+    UNREFERENCED_PARAMETER(Irp);
+    UNREFERENCED_PARAMETER(DeviceObject);
+
+    pDmaTransaction = (FxDmaPacketTransaction*) Context;
+    ASSERT(pDmaTransaction);
+
+    pFxDriverGlobals = pDmaTransaction->GetDriverGlobals();
+
+    //
+    // Cache the return value while we can still touch the transaction
+    //
+    action = pDmaTransaction->GetAdapterControlReturnValue();
+
+    //
+    // Save the MapRegister base, unless it was previously set
+    // during a reserve.
+    //
+    if (pDmaTransaction->IsMapRegisterBaseSet() == FALSE) {
+        pDmaTransaction->SetMapRegisterBase(MapRegisterBase);
+    }
+    else {
+        NT_ASSERTMSG("Caller was expected to use existing map register base",
+                     MapRegisterBase == pDmaTransaction->m_MapRegisterBase);
+    }
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Map registers for WDFDMATRANSACTION %p allocated "
+                            "(base %p)",
+                            pDmaTransaction->GetHandle(),
+                            MapRegisterBase);
+    }
+
+    //
+    // NOTE: KMDF used to call KeFlushIoBuffers() here to "ensure the
+    //       data buffers were flushed."  However KeFlushIoBuffers did
+    //       nothing on x86 & amd64 (which are cache coherent WRT DMA)
+    //       and calling FlushAdapterBuffers() does any necessary
+    //       flushing anyway.  Plus on non-cache-coherent architectures
+    //       (such as ARM) the flush operation has to be cache-line aligned
+    //       to avoid cache line tearing.  So the flush is not necessary
+    //       and has been removed.
+
+    //
+    // Check the state of the transaction.  If it's reserve then call the
+    // reserve callback and return.  Otherwise stage the first fragment.
+    //
+    if (pDmaTransaction->m_State == FxDmaTransactionStateReserved)
+    {
+        FxDmaTransactionProgramOrReserveDma callback;
+
+        //
+        // Save off and clear the callback before calling it.
+        //
+        callback = pDmaTransaction->m_DmaAcquiredFunction;
+        pDmaTransaction->m_DmaAcquiredFunction.Clear();
+
+        ASSERTMSG("Mismatch between map register counts",
+                  (pDmaTransaction->m_MapRegistersReserved ==
+                   pDmaTransaction->m_MapRegistersNeeded));
+
+        //
+        // Invoke the callback.  Note that from here the driver may initialize
+        // and execute the transaction.
+        //
+        callback.InvokeReserveDma(
+            pDmaTransaction->GetHandle(),
+            pDmaTransaction->m_DmaAcquiredContext
+            );
+    }
+    else {
+
+        //
+        // Stage next fragment
+        //
+        status = pDmaTransaction->StageTransfer();
+
+        if (!NT_SUCCESS(status)) {
+
+            DMA_COMPLETION_STATUS dmaStatus =
+                (status == STATUS_CANCELLED ? DmaCancelled : DmaError);
+            FxDmaSystemTransaction* systemTransaction = (FxDmaSystemTransaction*) pDmaTransaction;
+
+            //
+            // Map transfer failed.  There will be no DMA completion callback
+            // and no call to EvtProgramDma.  And we have no way to hand this
+            // status back directly to the driver.  Fake a DMA completion with
+            // the appropriate status.
+            //
+            // This should only happen for system DMA (and there most likely
+            // only during cancelation, though we leave the possibility that
+            // the DMA extension may fail the transfer)
+            //
+            ASSERTMSG("Unexpected failure of StageTransfer for packet based "
+                      "DMA",
+                       (pDmaTransaction->GetDmaEnabler()->IsBusMaster() == false));
+
+            if (pFxDriverGlobals->FxVerifierOn) {
+                DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                    "Invoking DmaCompleted callback %p (context %p) "
+                                    "for WDFDMATRANSACTION %p (status %x) "
+                                    "due to staging failure (%!STATUS!)",
+                                    systemTransaction->m_TransferCompleteFunction.Method,
+                                    systemTransaction->m_TransferCompleteContext,
+                                    pDmaTransaction->GetHandle(),
+                                    dmaStatus,
+                                    status);
+            }
+
+            pDmaTransaction->CallEvtDmaCompleted(
+                status == STATUS_CANCELLED ? DmaCancelled : DmaError
+                );
+        }
+    }
+
+    //
+    // Indicate that MapRegs are to be kept
+    //
+    return action;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaPacketTransaction::StageTransfer(
+    VOID
+    )
+{
+    PSCATTER_GATHER_LIST sgList;
+
+    PFX_DRIVER_GLOBALS pFxDriverGlobals;
+    UCHAR_MEMORY_ALIGNED sgListBuffer[sizeof(SCATTER_GATHER_LIST)
+                            + sizeof(SCATTER_GATHER_ELEMENT)];
+    WDFDMATRANSACTION dmaTransaction;
+
+    KIRQL oldIrql;
+    BOOLEAN stagingNeeded;
+
+    NTSTATUS status = STATUS_SUCCESS;
+
+    //
+    // Client driver could complete and delete the object in
+    // EvtProgramDmaFunction. So, save the handle because we need it
+    // for tracing after we invoke the callback.
+    //
+    pFxDriverGlobals = GetDriverGlobals();
+    dmaTransaction = GetHandle();
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                        "Enter WDFDMATRANSACTION %p ", dmaTransaction);
+    }
+
+    //
+    // For packet base DMA, current and startMDL will always be
+    // same.  For V2 DMA we don't support MDL chains.  For V3 DMA
+    // we use the HAL's support for MDL chains and don't walk through
+    // the MDL chain on our own.
+    //
+    ASSERT(m_CurrentFragmentMdl == m_StartMdl);
+
+    LockTransferState(&oldIrql);
+
+    if (m_TransferState.CurrentStagingThread != NULL) {
+
+        //
+        // Staging in progress.  Indicate that another staging will
+        // be needed.
+        //
+        m_TransferState.RerunStaging = TRUE;
+
+        stagingNeeded = FALSE;
+
+        if (pFxDriverGlobals->FxVerifierOn) {
+            DoTraceLevelMessage(
+                pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                "Staging next fragment of WDFDMATRANSACTION %p "
+                "deferred",
+                dmaTransaction
+                );
+        }
+    }
+    else {
+        //
+        // Staging isn't in progress anyplace else.  Indicate that it's
+        // running now so that any parallel attempt is blocked.
+        //
+        m_TransferState.CurrentStagingThread = KeGetCurrentThread();
+
+        ASSERTMSG("The thread which was staging didn't clear "
+                  "RerunStaging",
+                  (m_TransferState.RerunStaging == FALSE));
+
+        stagingNeeded = TRUE;
+    }
+
+    UnlockTransferState(oldIrql);
+
+    //
+    // Take a reference on the transaction so that we can safely
+    // manipulate the transfer state even after it's destroyed.
+    //
+    AddRef(&pFxDriverGlobals);
+
+    //
+    // Loop for as long as staging is required
+    //
+    while (stagingNeeded) {
+
+        //
+        // Calculate length for this packet.
+        //
+        m_CurrentFragmentLength = FxSizeTMin(m_Remaining, m_MaxFragmentLength);
+
+        //
+        // Calculate address for this packet.
+        //
+        m_CurrentFragmentOffset = m_StartOffset + m_Transferred;
+
+        //
+        // Adjust the fragment length for the number of reserved map registers.
+        //
+        if ((m_MapRegistersReserved > 0) &&
+            (m_MapRegistersNeeded > m_MapRegistersReserved))
+        {
+            size_t currentOffset = m_CurrentFragmentOffset;
+            size_t currentPageOffset;
+            PMDL mdl;
+
+            for (mdl = m_CurrentFragmentMdl; mdl != NULL; mdl = mdl->Next)
+            {
+                //
+                // For packet/system transfers of chained MDLs, m_CurrentFragmentMdl
+                // is never adjusted, and m_CurrentFragmentOFfset is the offset
+                // into the entire chain.
+                //
+                // Locate the MDL which contains the current fragment.
+                //
+                ULONG mdlBytes = MmGetMdlByteCount(mdl);
+                if (mdlBytes >= currentOffset)
+                {
+                    //
+                    // This MDL is larger than the remaining offset, so it
+                    // contains the start address.
+                    //
+                    break;
+                }
+
+                currentOffset -= mdlBytes;
+            }
+
+            ASSERT(mdl != NULL);
+
+            //
+            // Compute page offset from current MDL's initial page offset
+            // and the offset into that MDL
+            //
+
+            currentPageOffset = BYTE_OFFSET(MmGetMdlByteOffset(mdl) +
+                                            currentOffset);
+
+            //
+            // Compute the maximum number of bytes we can transfer with
+            // the number of map registers we have reserved, taking into
+            // account the offset of the first page.
+            //
+            size_t l =  ((PAGE_SIZE * (m_MapRegistersReserved - 1)) +
+                         (PAGE_SIZE - currentPageOffset));
+
+            m_CurrentFragmentLength = FxSizeTMin(m_CurrentFragmentLength, l);
+        }
+
+        m_Remaining -= m_CurrentFragmentLength;
+
+        if (pFxDriverGlobals->FxVerifierOn) {
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                "Initiating %s transfer for WDFDMATRANSACTION %p.  "
+                                "Mdl %p, Offset %I64x, Length %I64x",
+                                m_Transferred == 0 ? "first" : "next",
+                                GetHandle(),
+                                m_CurrentFragmentMdl,
+                                m_Transferred,
+                                m_CurrentFragmentLength);
+        }
+
+        //
+        // Check for a pending cancellation.  This can happen if the cancel
+        // occurred between DMA completion and FlushAdapterBuffers -
+        // FlushAdapterBuffers will clear the canceled bit in the transfer
+        // context (TC), which would allow MapTransfer to succeed.
+        //
+        // An unprotected check of IsCancelled here is safe.  A concurrent
+        // cancel at this point would mark the TC cancelled such that
+        // MapTransfer will fail.
+        //
+        if (m_IsCancelled == TRUE) {
+            status = STATUS_CANCELLED;
+            goto End;
+        }
+
+        //
+        // Profile specific work before mapping the transfer.  if this
+        // fails consider 'this' invalid.
+        //
+        if (PreMapTransfer() == FALSE) {
+            status = STATUS_SUCCESS;
+            goto End;
+        }
+
+        //
+        // Map this packet for transfer.
+        //
+
+        //
+        // For packet based DMA we use a single entry on-stack SGL.  This
+        // allows us to map multiple packet-based requests concurrently and
+        // we know packet base DMA only requires a single SGL
+        //
+        // NOTE: It turns out the HAL doesn't handle chained MDLs in packet
+        //       mode correctly.  It makes each MDL continguous, but returns
+        //       a list of SG elements - one for each MDL.  That's scatter
+        //       gather DMA, not packet DMA.
+        //
+        //       So it's actually very important in Win8 that we only use a
+        //       single entry SGL when calling MapTransferEx.  This ensures
+        //       we only map the first MDL in the chain and thus get a
+        //       contiguous buffer
+        //
+        // For system DMA we use the SystemSGList stored in the DMA enabler
+        // We can use a shared one in that case because we won't be mapping
+        // multiple system DMA requests concurrently (HAL doesn't allow it)
+        //
+        FxDmaEnabler* enabler = GetDmaEnabler();
+        size_t sgListSize;
+
+        if (enabler->IsBusMaster()) {
+            sgList = (PSCATTER_GATHER_LIST)sgListBuffer;
+            sgListSize = sizeof(sgListBuffer);
+        } else {
+            sgList = enabler->m_SGList.SystemProfile.List;
+            sgListSize = enabler->m_SGListSize;
+        }
+
+        ULONG mappedBytes;
+
+        status = MapTransfer(sgList,
+                             (ULONG) sgListSize,
+                             GetTransferCompletionRoutine(),
+                             this,
+                             &mappedBytes);
+
+        NT_ASSERTMSG("Unexpected failure of MapTransfer",
+                     ((NT_SUCCESS(status) == TRUE) ||
+                      (status == STATUS_CANCELLED)));
+
+        if (NT_SUCCESS(status)) {
+
+            NT_ASSERTMSG("unexpected number of mapped bytes",
+                         ((mappedBytes > 0) &&
+                          (mappedBytes <= m_CurrentFragmentLength)));
+
+            //
+            // Adjust the remaining byte count if the HAL mapped less data than we
+            // requested.
+            //
+            if (mappedBytes < m_CurrentFragmentLength) {
+                m_Remaining += m_CurrentFragmentLength - mappedBytes;
+                m_CurrentFragmentLength = mappedBytes;
+            }
+
+            //
+            // Do client PFN_WDF_PROGRAM_DMA callback.
+            //
+            if (m_DmaAcquiredFunction.Method.ProgramDma != NULL) {
+
+                if (pFxDriverGlobals->FxVerifierOn) {
+                    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                        "Invoking ProgramDma callback %p (context %p) for "
+                                        "WDFDMATRANSACTION %p.",
+                                        m_DmaAcquiredFunction.Method.ProgramDma,
+                                        m_DmaAcquiredContext,
+                                        GetHandle()
+                                        );
+                }
+
+                //
+                // Call program DMA
+                //
+                (VOID) m_DmaAcquiredFunction.InvokeProgramDma(
+                                                GetHandle(),
+                                                m_DmaEnabler->m_DeviceBase->GetHandle(),
+                                                m_DmaAcquiredContext,
+                                                m_DmaDirection,
+                                                sgList
+                                                );
+            }
+        }
+
+End:
+        //
+        // Process any pending completion or nested staging.
+        //
+        {
+            LockTransferState(&oldIrql);
+
+            //
+            // While staging we could either have deferred a call to the
+            // completion routine or deferred another call to stage the
+            // next fragment.  We should not ever have to do both - this
+            // would imply that the driver didn't wait for its DMA completion
+            // routine to run when calling TransferComplete*.
+            //
+            ASSERTMSG("driver called TransferComplete with pending DMA "
+                      "completion callback",
+                      !((m_TransferState.RerunCompletion == TRUE) &&
+                        (m_TransferState.RerunStaging == TRUE)));
+
+            //
+            // Check for pending completion.  save the status away and clear it
+            // before dropping the lock.
+            //
+            if (m_TransferState.RerunCompletion == TRUE) {
+                DMA_COMPLETION_STATUS completionStatus;
+                FxDmaSystemTransaction* systemTransaction = (FxDmaSystemTransaction*) this;
+
+                //
+                // Save the completion status for when we drop the lock.
+                //
+                completionStatus = m_TransferState.CompletionStatus;
+
+                ASSERTMSG("completion needed, but status was not set or was "
+                          "already cleared",
+                          completionStatus != UNDEFINED_DMA_COMPLETION_STATUS);
+
+                ASSERTMSG("completion needed, but mapping failed so there shouldn't "
+                          "be any parallel work going on",
+                          NT_SUCCESS(status));
+
+                //
+                // Clear the completion needed state.
+                //
+                m_TransferState.RerunCompletion = FALSE;
+                m_TransferState.CompletionStatus = UNDEFINED_DMA_COMPLETION_STATUS;
+
+                //
+                // Drop the lock, call the completion routine, then take the
+                // lock again.
+                //
+                UnlockTransferState(oldIrql);
+                if (pFxDriverGlobals->FxVerifierOn) {
+                    DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                        "Invoking DmaCompleted callback %p (context %p) "
+                                        "for WDFDMATRANSACTION %p (status %x) "
+                                        "after deferral",
+                                        systemTransaction->m_TransferCompleteFunction.Method,
+                                        systemTransaction->m_TransferCompleteContext,
+                                        GetHandle(),
+                                        completionStatus);
+                }
+                CallEvtDmaCompleted(completionStatus);
+                LockTransferState(&oldIrql);
+
+                //
+                // Staging is blocked, which means we aren't starting up the
+                // next transfer.  Therefore we cannot have a queued completion.
+                //
+                ASSERTMSG("RerunCompletion should not be set on an unstaged "
+                          "transaction",
+                          m_TransferState.RerunCompletion == FALSE);
+            }
+
+            //
+            // Capture whether another staging is needed.  If none is needed
+            // then we can clear staging in progress.
+            //
+            if (m_TransferState.RerunStaging == TRUE) {
+                stagingNeeded = TRUE;
+                m_TransferState.RerunStaging = FALSE;
+            }
+            else {
+                m_TransferState.CurrentStagingThread = NULL;
+                stagingNeeded = FALSE;
+            }
+
+            UnlockTransferState(oldIrql);
+        }
+
+#if DBG
+        if (!NT_SUCCESS(status)) {
+            ASSERTMSG("MapTransfer returned an error - there should not be any "
+                      "deferred work.",
+                      (stagingNeeded == FALSE));
+        }
+#endif
+
+    } // while(stagingNeeded)
+
+    Release(&pFxDriverGlobals);
+
+    if (pFxDriverGlobals->FxVerifierOn) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                            "Exit WDFDMATRANSACTION %p, "
+                            "%!STATUS!", dmaTransaction,
+                            status);
+    }
+
+    return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaPacketTransaction::TransferCompleted(
+    VOID
+    )
+{
+    NTSTATUS status;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+    //
+    // Flush the buffers
+    //
+    status = FlushAdapterBuffers();
+
+    if (!NT_SUCCESS(status)) {
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                            "FlushAdapterBuffers on WDFDMATRANSACTION %p "
+                            "failed, %!STATUS!",
+                            GetHandle(), status);
+        FxVerifierDbgBreakPoint(pFxDriverGlobals);
+    }
+
+    return status;
+}
+
+// ----------------------------------------------------------------------------
+// ------------------- SYSTEM DMA SECTION -------------------------------------
+// ----------------------------------------------------------------------------
+
+FxDmaSystemTransaction::FxDmaSystemTransaction(
+    __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+    __in USHORT ExtraSize,
+    __in FxDmaEnabler *DmaEnabler
+    ) :
+    FxDmaPacketTransaction(FxDriverGlobals, sizeof(FxDmaSystemTransaction), ExtraSize, DmaEnabler)
+{
+    return;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDmaSystemTransaction::_Create(
+    __in  PFX_DRIVER_GLOBALS      FxDriverGlobals,
+    __in  PWDF_OBJECT_ATTRIBUTES  Attributes,
+    __in  FxDmaEnabler*           DmaEnabler,
+    __out WDFDMATRANSACTION*      Transaction
+    )
+{
+    FxDmaPacketTransaction* pTransaction;
+    WDFOBJECT hTransaction;
+    NTSTATUS status;
+
+    pTransaction = new (FxDriverGlobals, Attributes, DmaEnabler->GetTransferContextSize())
+                FxDmaSystemTransaction(FxDriverGlobals, DmaEnabler->GetTransferContextSize(), DmaEnabler);
+
+    if (pTransaction == NULL) {
+        status =  STATUS_INSUFFICIENT_RESOURCES;
+        DoTraceLevelMessage(
+            FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+            "Could not allocate memory for WDFTRANSACTION, %!STATUS!", status);
+        return status;
+    }
+
+    //
+    // Commit and apply the attributes
+    //
+    status = pTransaction->Commit(Attributes, &hTransaction, DmaEnabler);
+    if (NT_SUCCESS(status)) {
+        *Transaction = (WDFDMATRANSACTION)hTransaction;
+    }
+    else {
+        //
+        // This will properly clean up the target's state and free it
+        //
+        pTransaction->DeleteFromFailedCreate();
+    }
+
+    return status;
+}
+
+BOOLEAN
+FxDmaSystemTransaction::PreMapTransfer(
+    VOID
+    )
+{
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+    BOOLEAN result = TRUE;
+
+    if (m_ConfigureChannelFunction.Method != NULL) {
+        //
+        // Invoke the callback.  If it returns false then the driver has
+        // completed the transaction in the callback and we must abort
+        // processing.
+        //
+
+        if (pFxDriverGlobals->FxVerifierOn) {
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                "Invoking ConfigureChannel callback %p (context "
+                                "%p) for WDFDMATRANSACTION %p.",
+                                m_ConfigureChannelFunction.Method,
+                                m_ConfigureChannelContext,
+                                GetHandle());
+        }
+
+        result = m_ConfigureChannelFunction.Invoke(
+                    GetHandle(),
+                    m_DmaEnabler->m_DeviceBase->GetHandle(),
+                    m_ConfigureChannelContext,
+                    m_CurrentFragmentMdl,
+                    m_CurrentFragmentOffset,
+                    m_CurrentFragmentLength
+                    );
+    }
+
+    return result;
+}
+
+
+PDMA_COMPLETION_ROUTINE
+FxDmaSystemTransaction::GetTransferCompletionRoutine(
+    VOID
+    )
+{
+    if (m_TransferCompleteFunction.Method == NULL) {
+        return NULL;
+    }
+    else {
+        return _SystemDmaCompletion;
+    }
+}
+
+VOID
+FxDmaSystemTransaction::CallEvtDmaCompleted(
+    __in DMA_COMPLETION_STATUS Status
+    )
+{
+    //
+    // Call the TransferComplete callback to indicate that the
+    // transfer was aborted.
+    //
+    m_TransferCompleteFunction.Invoke(
+        GetHandle(),
+        m_DmaEnabler->m_Device->GetHandle(),
+        m_TransferCompleteContext,
+        m_DmaDirection,
+        Status
+        );
+}
+
+VOID
+FxDmaTransactionBase::GetTransferInfo(
+    __out_opt ULONG *MapRegisterCount,
+    __out_opt ULONG *ScatterGatherElementCount
+    )
+{
+    if (m_State != FxDmaTransactionStateInitialized) {
+        PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                    "WDFDMATRANSACTION %p state %!FxDmaTransactionState! "
+                    "is invalid", GetHandle(), m_State);
+
+        FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                           WDF_DMA_FATAL_ERROR,            // specific type
+                           (ULONG_PTR) GetObjectHandle(),  // parm 2
+                           (ULONG_PTR) m_State);           // parm 3
+    }
+
+    DMA_TRANSFER_INFO info = {0};
+
+
+
+
+
+
+    if (m_DmaEnabler->UsesDmaV3()) {
+
+        //
+        // Ask the HAL for information about the MDL and how many resources
+        // it will require to transfer.
+        //
+        m_AdapterInfo->AdapterObject->DmaOperations->GetDmaTransferInfo(
+            m_AdapterInfo->AdapterObject,
+            m_StartMdl,
+            m_StartOffset,
+            (ULONG) this->m_TransactionLength,
+            this->m_DmaDirection == WDF_DMA_DIRECTION::WdfDmaDirectionWriteToDevice,
+            &info
+            );
+    } else {
+        size_t offset = m_StartOffset;
+        size_t length = m_TransactionLength;
+
+        //
+        // Walk through the MDL chain and make a worst-case computation of
+        // the number of scatter gather entries and map registers the
+        // transaction would require.
+        //
+        for(PMDL mdl = m_StartMdl;
+            mdl != NULL && length != 0;
+            mdl = mdl->Next) {
+
+            size_t byteCount = MmGetMdlByteCount(mdl);
+            if (byteCount <= offset) {
+                offset -= byteCount;
+            } else {
+                ULONG_PTR startVa = (ULONG_PTR) MmGetMdlVirtualAddress(mdl);
+
+                startVa += offset;
+                byteCount -= offset;
+
+                info.V1.MapRegisterCount +=
+                    (ULONG) ADDRESS_AND_SIZE_TO_SPAN_PAGES(
+                        startVa,
+                        min(byteCount, length)
+                        );
+
+                length -= min(byteCount, length);
+            }
+        }
+
+        info.V1.ScatterGatherElementCount = info.V1.MapRegisterCount;
+    }
+
+    if (ARGUMENT_PRESENT(MapRegisterCount)) {
+        *MapRegisterCount = info.V1.MapRegisterCount;
+    }
+
+    if (ARGUMENT_PRESENT(ScatterGatherElementCount)) {
+        *ScatterGatherElementCount = info.V1.ScatterGatherElementCount;
+    }
+
+    return;
+}
+
+VOID
+FxDmaSystemTransaction::_SystemDmaCompletion(
+    __in PDMA_ADAPTER          /* DmaAdapter */,
+    __in PDEVICE_OBJECT        /* DeviceObject */,
+    __in PVOID                 CompletionContext,
+    __in DMA_COMPLETION_STATUS Status
+    )
+{
+    FxDmaSystemTransaction* transaction = (FxDmaSystemTransaction*) CompletionContext;
+    PFX_DRIVER_GLOBALS pFxDriverGlobals = transaction->GetDriverGlobals();
+    KIRQL oldIrql;
+    BOOLEAN completionDeferred;
+
+    //
+    // Lock the transfer state so that a staging or cancelling thread
+    // cannot change it.
+    //
+    transaction->LockTransferState(&oldIrql);
+
+    ASSERTMSG("Completion state was already set",
+              (transaction->m_TransferState.CompletionStatus ==
+               UNDEFINED_DMA_COMPLETION_STATUS));
+    ASSERTMSG("Deferred completion is already pending",
+              (transaction->m_TransferState.RerunCompletion == FALSE));
+
+    //
+    // If a staging is in progress then defer the completion.
+    //
+    if (transaction->m_TransferState.CurrentStagingThread != NULL) {
+        transaction->m_TransferState.CompletionStatus = Status;
+        transaction->m_TransferState.RerunCompletion = TRUE;
+        completionDeferred = TRUE;
+    }
+    else {
+        completionDeferred = FALSE;
+    }
+
+    transaction->UnlockTransferState(oldIrql);
+
+    //
+    // Process the old state.
+    //
+    if (completionDeferred == TRUE) {
+        //
+        // The staging thread has not moved past EvtProgramDma.  The staging thread
+        // will detect the state change and call the completion routine.
+        //
+        // Nothing to do in this case.
+        //
+        if (pFxDriverGlobals->FxVerifierOn) {
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                "Deferring DmaCompleted callback for WDFDMATRANSACTION %p"
+                                "(status %x)",
+                                transaction->GetHandle(),
+                                Status);
+        }
+    }
+    else {
+        //
+        // Completion occurred while the transfer was running or
+        // being cancelled.  Call the completion routine.
+        //
+        // Note: a cancel when in programming state leaves the
+        //       state as programming.  that we're not in programming
+        //       means we don't need to worry about racing with
+        //       EvtProgramDma.
+        //
+
+        if (pFxDriverGlobals->FxVerifierOn) {
+            DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
+                                "Invoking DmaCompleted callback %p (context %p) "
+                                "for WDFDMATRANSACTION %p (status %x)",
+                                transaction->m_TransferCompleteFunction.Method,
+                                transaction->m_TransferCompleteContext,
+                                transaction->GetHandle(),
+                                Status);
+        }
+        transaction->CallEvtDmaCompleted(Status);
+    }
+}
+
+VOID
+FxDmaSystemTransaction::StopTransfer(
+    VOID
+    )
+{
+    //
+    // Mark the transfer cancelled so we have a record of it even if
+    // a racing call to FlushAdapterBuffers clears the TC.
+    //
+    m_IsCancelled = TRUE;
+
+    //
+    // Cancel the system DMA transfer.  This arranges for one of two things
+    // to happen:
+    //  * the next call to MapTransfer will fail
+    //  * the DMA completion routine will run
+    //
+    if (CancelMappedTransfer() == FALSE) {
+
+        //
+        // The cancel failed.  Someone has already stopped this transfer.
+        // That's illegal.
+        //
+        PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
+
+        DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA,
+                            "WDFDMATRANSACTION %p has already been stopped",
+                            GetHandle());
+
+        if (pFxDriverGlobals->IsVerificationEnabled(1, 11, OkForDownLevel)) {
+            FxVerifierBugCheck(pFxDriverGlobals,               // globals
+                               WDF_DMA_FATAL_ERROR,            // type
+                               (ULONG_PTR) GetObjectHandle(),  // parm 2
+                               (ULONG_PTR) m_State);           // parm 3
+        }
+    }
+}