mirror of
https://github.com/reactos/reactos.git
synced 2025-08-06 15:03:00 +00:00
2533 lines
81 KiB
C++
2533 lines
81 KiB
C++
/*++
|
|
|
|
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
|
|
}
|
|
}
|
|
}
|