reactos/sdk/lib/drivers/wdf/kmdf/inc/private/fxdmatransaction.hpp
Victor Perevertkin 1f377076d7
[WDF] Fix KMDF so it can compile with ReactOS SDK
Not all files are included, but these are necessary to compile cdrom driver.
So far it can only be statically linked with drivers, a proper
implementation requires wdfldr helper driver
2020-11-03 00:06:27 +03:00

1564 lines
44 KiB
C++

//
// Copyright (C) Microsoft. All rights reserved.
//
#ifndef _FXDMATRANSACTION_HPP_
#define _FXDMATRANSACTION_HPP_
extern "C" {
// #include "FxDmaTransaction.hpp.tmh"
}
#include "fxdmatransactioncallbacks.hpp"
//
// This type is used to allocate scatter-gather list of 1 element on the stack.
//
typedef DECLSPEC_ALIGN(MEMORY_ALLOCATION_ALIGNMENT) UCHAR UCHAR_MEMORY_ALIGNED;
// begin_wpp enum
//
// FxDmaTransactionStateCreated when the object is created.
// FxDmaTransactionStateInitialized when object is initialized using with
// Mdl/VA/Length.
// FxDmaTransactionStateReserved when driver calls AllocateResources until
// the adapter control routine returns
// FxDmaTransactionStateTransfer is called when the driver call Execute
// to start the DMA transfer.
// FxDmaTransactionStateTransferCompleted is when transfer is completed or
// aborted
// FxDmaTransactionStateTransferFailed is set if the framework is not able
// to start the transfer due to error.
// FxDmaTransactionStateReleased is set when the object is reinitailized for reuse
// FxDmaTransactionStateDeleted is set in the Dipose due to WdfObjectDelete
//
enum FxDmaTransactionState {
FxDmaTransactionStateInvalid = 0,
FxDmaTransactionStateCreated,
FxDmaTransactionStateReserved,
FxDmaTransactionStateInitialized,
FxDmaTransactionStateTransfer,
FxDmaTransactionStateTransferCompleted,
FxDmaTransactionStateTransferFailed,
FxDmaTransactionStateReleased,
FxDmaTransactionStateDeleted,
};
//
// FxDmaCompletionTypeFull is used when the driver calls WdfDmaTransactionDmaComplete
// to indicate that last framgement has been transmitted fully and to initiate
// the transfer of next fragment.
// FxDmaCompletionTypePartial is used when the driver completes the transfer and
// specifies a amount of bytes it has transfered, and to initiate the next transfer
// from the untransmitted portion of the buffer.
// FxDmaCompletionTypeAbort i used when the driver calls DmaCompleteFinal to indicate
// that's the final transfer and not initiate anymore transfers for the remaining
// data.
//
enum FxDmaCompletionType {
FxDmaCompletionTypeFull = 1,
FxDmaCompletionTypePartial,
FxDmaCompletionTypeAbort,
};
// end_wpp
//
// This tag is used to track whether the request pointer in the transaction
// has a reference taken on it. Since the pointer is guaranteed to be
// 4-byte aligned this can be set and cleared without destroying the pointer.
//
#define FX_STRONG_REF_TAG 0x1
//
// Simple set of macros to encode and decode tagged pointers.
//
#define FX_ENCODE_POINTER(T,p,tag) ((T*) ((ULONG_PTR) p | (ULONG_PTR) tag))
#define FX_DECODE_POINTER(T,p,tag) ((T*) ((ULONG_PTR) p & ~(ULONG_PTR) tag))
#define FX_IS_POINTER_ENCODED(p,tag) ((((ULONG_PTR) p & (ULONG_PTR) tag) == tag) ? TRUE : FALSE)
//
// An uninitialized value for Dma completion status
//
#define UNDEFINED_DMA_COMPLETION_STATUS ((DMA_COMPLETION_STATUS) -1)
class FxDmaTransactionBase : public FxNonPagedObject {
friend class FxDmaEnabler;
public:
FxDmaTransactionBase(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in USHORT ObjectSize,
__in USHORT ExtraSize,
__in FxDmaEnabler *DmaEnabler
);
static
VOID
_ComputeNextTransferAddress(
__in PMDL CurrentMdl,
__in size_t CurrentOffset,
__in ULONG Transferred,
__deref_out PMDL *NextMdl,
__out size_t *NextOffset
);
_Must_inspect_result_
static
NTSTATUS
_CalculateRequiredMapRegisters(
__in PMDL Mdl,
__in size_t CurrentOffset,
__in ULONG Length,
__in ULONG AvailableMapRegisters,
__out_opt PULONG PossibleTransferLength,
__out PULONG MapRegistersRequired
);
virtual
BOOLEAN
Dispose(
VOID
);
_Must_inspect_result_
NTSTATUS
Initialize(
__in PFN_WDF_PROGRAM_DMA ProgramDmaFunction,
__in WDF_DMA_DIRECTION DmaDirection,
__in PMDL Mdl,
__in size_t Offset,
__in ULONG Length
);
_Must_inspect_result_
virtual
NTSTATUS
InitializeResources(
VOID
)=0;
_Must_inspect_result_
NTSTATUS
Execute(
__in PVOID Context
);
_Must_inspect_result_
virtual
NTSTATUS
StartTransfer(
VOID
)=0;
_Must_inspect_result_
virtual
NTSTATUS
StageTransfer(
VOID
)=0;
BOOLEAN
DmaCompleted(
__in size_t TransferredLength,
__out NTSTATUS * ReturnStatus,
__in FxDmaCompletionType CompletionType
);
_Must_inspect_result_
virtual
NTSTATUS
TransferCompleted(
VOID
)=0;
VOID
ReleaseForReuse(
__in BOOLEAN ForceRelease
);
virtual
VOID
ReleaseResources(
__in BOOLEAN ForceRelease
)=0;
VOID
GetTransferInfo(
__out_opt ULONG *MapRegisterCount,
__out_opt ULONG *ScatterGatherElementCount
);
__forceinline
size_t
GetBytesTransferred(
VOID
)
{
return m_Transferred;
}
__forceinline
FxDmaEnabler *
GetDmaEnabler(
VOID
)
{
return m_DmaEnabler;
}
__forceinline
FxRequest *
GetRequest(
VOID
)
{
//
// Strip out the strong reference tag if it's set
//
return FX_DECODE_POINTER(FxRequest,
m_EncodedRequest,
FX_STRONG_REF_TAG);
}
__forceinline
BOOLEAN
IsRequestReferenced(
VOID
)
{
return FX_IS_POINTER_ENCODED(m_EncodedRequest, FX_STRONG_REF_TAG);
}
__forceinline
VOID
SetRequest(
__in FxRequest* Request
)
{
ASSERT(m_EncodedRequest == NULL);
//
// Make sure the pointer doesn't have the strong ref flag set already
//
ASSERT(FX_IS_POINTER_ENCODED(Request, FX_STRONG_REF_TAG) == FALSE);
m_EncodedRequest = Request;
}
__forceinline
VOID
ReferenceRequest(
VOID
)
{
ASSERT(m_EncodedRequest != NULL);
ASSERT(IsRequestReferenced() == false);
//
// Take a reference on the irp to catch completion of request
// when there is a pending DMA transaction.
// While there is no need to take a reference on request itself,
// I'm keeping it to avoid regression as we are so close to
// shipping this.
//
m_EncodedRequest->AddIrpReference();
//
// Increment reference to this Request.
// See complementary section in WdfDmaTransactionDelete
// and WdfDmaTransactionRelease.
//
m_EncodedRequest->ADDREF( this );
m_EncodedRequest = FX_ENCODE_POINTER(FxRequest,
m_EncodedRequest,
FX_STRONG_REF_TAG);
}
__forceinline
VOID
ReleaseButRetainRequest(
VOID
)
{
ASSERT(m_EncodedRequest != NULL);
ASSERT(IsRequestReferenced());
//
// Clear the referenced bit on the encoded request.
//
m_EncodedRequest = FX_DECODE_POINTER(FxRequest,
m_EncodedRequest,
FX_STRONG_REF_TAG);
//
// Release this reference to the Irp and FxRequest.
//
m_EncodedRequest->ReleaseIrpReference();
m_EncodedRequest->RELEASE( this );
}
__forceinline
VOID
ClearRequest(
VOID
)
{
if (IsRequestReferenced()) {
ReleaseButRetainRequest();
}
m_EncodedRequest = NULL;
}
__forceinline
size_t
GetMaximumFragmentLength(
VOID
)
{
return m_MaxFragmentLength;
}
__forceinline
VOID
SetMaximumFragmentLength(
size_t MaximumFragmentLength
)
{
if (MaximumFragmentLength < m_AdapterInfo->MaximumFragmentLength) {
m_MaxFragmentLength = MaximumFragmentLength;
}
}
__forceinline
size_t
GetCurrentFragmentLength(
VOID
)
{
return m_CurrentFragmentLength;
}
__forceinline
WDFDMATRANSACTION
GetHandle(
VOID
)
{
return (WDFDMATRANSACTION) GetObjectHandle();
}
PVOID
GetTransferContext(
VOID
)
{
return m_TransferContext;
}
VOID
SetImmediateExecution(
__in BOOLEAN Value
);
BOOLEAN
CancelResourceAllocation(
VOID
);
FxDmaTransactionState
GetTransactionState(
VOID
)
{
return m_State;
}
protected:
FxDmaTransactionState m_State;
WDF_DMA_DIRECTION m_DmaDirection;
FxDmaEnabler* m_DmaEnabler;
//
// Depending on the direction of the transfer, this one
// points to either m_ReadAdapterInfo or m_WriteAdapterInfo
// structure of the DMA enabler.
//
FxDmaDescription* m_AdapterInfo;
//
// Request associated with this transfer. Encoding uses the
// FX_[EN|DE]CODE_POINTER macros with the FX_STRONG_REF_TAG
// to indicate whether the reference has been taken or not
//
FxRequest* m_EncodedRequest;
//
// Callback and context for ProgramDma function
//
// The callback is overloaded to also hold the callback for
// Packet & System transfer's Reserve callback (to save space.)
// This is possible because the driver may not call execute
// and reserve in parallel on the same transaction. Disambiguate
// using the state of the transaction.
//
FxDmaTransactionProgramOrReserveDma m_DmaAcquiredFunction;
PVOID m_DmaAcquiredContext;
//
// The DMA transfer context (when using V3 DMA)
//
PVOID m_TransferContext;
//
// This is the first MDL of the transaction.
//
PMDL m_StartMdl;
//
// This is the MDL where the current transfer is being executed.
// If the data spans multiple MDL then this would be different
// from the startMDL when we stage large transfers and also
// if the driver performs partial transfers.
//
PMDL m_CurrentFragmentMdl;
//
// Starting offset in the first m_StartMdl. This might be same as
// m_StartMdl->StartVA.
//
size_t m_StartOffset;
//
// Points to address where the next transfer will begin.
//
size_t m_CurrentFragmentOffset;
//
// This is maximum length of transfer that can be made. This is
// computed based on the available map registers and driver
// configuration.
//
size_t m_MaxFragmentLength;
//
// Length of the whole transaction.
//
size_t m_TransactionLength;
//
// Number of bytes pending to be transfered.
//
size_t m_Remaining;
//
// Total number of bytes transfered.
//
size_t m_Transferred;
//
// Number of bytes transfered in the last transaction.
//
size_t m_CurrentFragmentLength;
//
// DMA flags for passing to GetScatterGatherListEx &
// AllocateAdapterChannelEx
//
ULONG m_Flags;
static
PVOID
GetStartVaFromOffset(
__in PMDL Mdl,
__in size_t Offset
)
{
return ((PUCHAR) MmGetMdlVirtualAddress(Mdl)) + Offset;
}
virtual
VOID
Reuse(
VOID
)
{
return;
}
};
class FxDmaScatterGatherTransaction : public FxDmaTransactionBase {
public:
FxDmaScatterGatherTransaction(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in USHORT ExtraSize,
__in FxDmaEnabler *DmaEnabler
);
virtual
BOOLEAN
Dispose(
VOID
);
_Must_inspect_result_
virtual
NTSTATUS
InitializeResources(
VOID
);
_Must_inspect_result_
virtual
NTSTATUS
StartTransfer(
VOID
);
_Must_inspect_result_
virtual
NTSTATUS
TransferCompleted(
VOID
);
_Must_inspect_result_
virtual
NTSTATUS
StageTransfer(
VOID
);
virtual
VOID
ReleaseResources(
__in BOOLEAN ForceRelease
);
_Must_inspect_result_
static
NTSTATUS
_Create(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PWDF_OBJECT_ATTRIBUTES Attributes,
__in FxDmaEnabler* DmaEnabler,
__out WDFDMATRANSACTION* Transaction
);
protected:
//
// Scatter-Gather list provided by the system.
//
PSCATTER_GATHER_LIST m_SGList;
//
// Preallocated memory from lookaside buffer for the
// scatter-gather list when running on XP and later OSes.
//
PVOID m_LookasideBuffer;
private:
static
VOID
_AdapterListControl(
__in DEVICE_OBJECT * DeviceObject,
__in IRP * Irp,
__in SCATTER_GATHER_LIST * SgList,
__in VOID * Context
);
_Must_inspect_result_
NTSTATUS
GetScatterGatherList (
__in PMDL Mdl,
__in size_t CurrentOffset,
__in ULONG Length,
__in PDRIVER_LIST_CONTROL ExecutionRoutine,
__in PVOID Context
)
{
NTSTATUS status;
KIRQL irql;
KeRaiseIrql(DISPATCH_LEVEL, &irql);
if (m_DmaEnabler->UsesDmaV3())
{
PDMA_OPERATIONS dmaOperations =
m_AdapterInfo->AdapterObject->DmaOperations;
status = dmaOperations->GetScatterGatherListEx(
m_AdapterInfo->AdapterObject,
m_DmaEnabler->m_FDO,
GetTransferContext(),
Mdl,
CurrentOffset,
Length,
m_Flags,
ExecutionRoutine,
Context,
(BOOLEAN) m_DmaDirection,
NULL,
NULL,
NULL
);
}
else
{
status = m_AdapterInfo->AdapterObject->DmaOperations->
GetScatterGatherList(m_AdapterInfo->AdapterObject,
m_DmaEnabler->m_FDO,
Mdl,
GetStartVaFromOffset(Mdl, CurrentOffset),
Length,
ExecutionRoutine,
Context,
(BOOLEAN) m_DmaDirection);
}
KeLowerIrql(irql);
return status;
}
VOID
PutScatterGatherList(
__in PSCATTER_GATHER_LIST ScatterGather
)
{
KIRQL irql;
KeRaiseIrql(DISPATCH_LEVEL, &irql);
m_AdapterInfo->AdapterObject->DmaOperations->
PutScatterGatherList(m_AdapterInfo->AdapterObject,
ScatterGather,
(BOOLEAN) m_DmaDirection);
KeLowerIrql(irql);
return;
}
_Must_inspect_result_
NTSTATUS
BuildScatterGatherList(
__in PMDL Mdl,
__in size_t CurrentOffset,
__in ULONG Length,
__in PDRIVER_LIST_CONTROL ExecutionRoutine,
__in PVOID Context,
__in PVOID ScatterGatherBuffer,
__in ULONG ScatterGatherBufferLength
)
{
NTSTATUS status;
KIRQL irql;
KeRaiseIrql(DISPATCH_LEVEL, &irql);
if (m_DmaEnabler->UsesDmaV3()) {
PDMA_OPERATIONS dmaOperations =
m_AdapterInfo->AdapterObject->DmaOperations;
ULONG flags = 0;
if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1,15)) {
//
// Though the correct behavior is to pass the m_Flags to the
// BuildScatterGatherListEx function, the code was not doing it
// for versions <= 1.13. To reduce any chance of regression,
// the m_Flags is honored for drivers that are 1.15
// or newer.
//
flags = m_Flags;
}
status = dmaOperations->BuildScatterGatherListEx(
m_AdapterInfo->AdapterObject,
m_DmaEnabler->m_FDO,
GetTransferContext(),
Mdl,
CurrentOffset,
Length,
flags,
ExecutionRoutine,
Context,
(BOOLEAN) m_DmaDirection,
ScatterGatherBuffer,
ScatterGatherBufferLength,
NULL,
NULL,
NULL
);
}
else {
status = m_AdapterInfo->AdapterObject->DmaOperations->
BuildScatterGatherList(m_AdapterInfo->AdapterObject,
m_DmaEnabler->m_FDO,
Mdl,
GetStartVaFromOffset(Mdl, CurrentOffset),
Length,
ExecutionRoutine,
Context,
(BOOLEAN) m_DmaDirection,
ScatterGatherBuffer,
ScatterGatherBufferLength);
}
KeLowerIrql(irql);
return status;
}
};
class FxDmaPacketTransaction : public FxDmaTransactionBase {
protected:
FxDmaPacketTransaction(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in USHORT ObjectSize,
__in USHORT ExtraSize,
__in FxDmaEnabler *DmaEnabler
);
public:
_Must_inspect_result_
virtual
NTSTATUS
InitializeResources(
VOID
);
_Must_inspect_result_
NTSTATUS
ReserveAdapter(
__in ULONG NumberOfMapRegisters,
__in WDF_DMA_DIRECTION Direction,
__in PFN_WDF_RESERVE_DMA Callback,
__in_opt PVOID Context
);
VOID
ReleaseAdapter(
VOID
);
_Must_inspect_result_
virtual
NTSTATUS
StartTransfer(
VOID
);
_Must_inspect_result_
virtual
NTSTATUS
TransferCompleted(
VOID
);
_Must_inspect_result_
virtual
NTSTATUS
StageTransfer(
VOID
);
virtual
VOID
ReleaseResources(
__in BOOLEAN ForceRelease
);
_Must_inspect_result_
static
NTSTATUS
_Create(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PWDF_OBJECT_ATTRIBUTES Attributes,
__in FxDmaEnabler* DmaEnabler,
__out WDFDMATRANSACTION* Transaction
);
VOID
SetDeviceAddressOffset(
__in ULONG Offset
)
{
m_DeviceAddressOffset = Offset;
}
protected:
//
// Number of map registers to be used in this transfer.
// This value is the least of the number of map registers
// needed to satisfy the current transfer request, and the
// number of available map registers returned by IoGetDmaAdapter.
//
ULONG m_MapRegistersNeeded;
//
// Opaque-value represents the map registers that the system has
// assigned for this transfer operation. We pass this value in
// FlushAdapterBuffers, FreeMapRegisters, and MapTransfer.
//
PVOID m_MapRegisterBase;
//
// TRUE when the map register base above is valid. The HAL can give
// us a NULL map register base when double buffering isn't required,
// so we can't just do a NULL test on m_MapRegisterBase to know if
// the DMA channel is allocated.
//
BOOLEAN m_MapRegisterBaseSet;
//
// For system-DMA this provides the offset from the original
// DeviceAddress used to compute the device register to or from which
// DMA should occur.
//
ULONG m_DeviceAddressOffset;
//
// 0 if the transaction has not reserved the enabler. Otherwise
// this is the number of map registers requested for the reservation.
// This value persists across a reuse and reinitialization of the
// transaction, and is only cleared when the enabler is released.
//
ULONG m_MapRegistersReserved;
//
// These fields are used to defer completion or staging while another
// thread/CPU is already staging. They are protected by the
// transfer state lock.
//
// These values are cleared when checked, not in InitializeResources.
// It's possible for a transaction being staged to complete on another
// CPU, get reused and reinitialized. Clearing these values in
// InitializeResources would destroy the state the prior call to
// StageTransfer depends on.
//
struct {
//
// Non-null when a staging operation is in progress on some CPU.
// When set any attempt to call the DMA completion routine or
// stage the transfer again (due to a call to TransferComplete)
// will be deferred to this thread.
//
PKTHREAD CurrentStagingThread;
//
// Indicates that a nested or concurrent attempt to stage
// the transaction was deferred. The CurrentStagingThread
// will restage the transaction.
//
BOOLEAN RerunStaging;
//
// Indicates that a nested or concurrent attempt to call the
// DMA completion routine occurred. The CurrentStagingThread
// will call the DMA completion routine when it unwinds providing
// the saved CompletionStatus
//
BOOLEAN RerunCompletion;
DMA_COMPLETION_STATUS CompletionStatus;
} m_TransferState;
//
// Indicates that the DMA transfer has been cancelled.
// Set during StopTransfer and cleared during InitializeResources
// Checked during StageTransfer. Stop and Initialize should never
// race (it's not valid for the driver to stop an uninitialized
// transaction). Stop and Stage can race but in that case Stop
// will mark the transaction context such that MapTransfer fails
// (and that's protected by a HAL lock).
//
// So this field does not need to be volatile or interlocked, but
// it needs to be sized to allow atomic writes.
//
ULONG m_IsCancelled;
virtual
VOID
Reuse(
VOID
)
{
return;
}
protected:
inline
void
SetMapRegisterBase(
__in PVOID Value
)
{
NT_ASSERTMSG("Map register base is already set",
m_MapRegisterBaseSet == FALSE);
m_MapRegisterBase = Value;
m_MapRegisterBaseSet = TRUE;
}
inline
void
ClearMapRegisterBase(
VOID
)
{
NT_ASSERTMSG("Map register base was not set",
m_MapRegisterBaseSet == TRUE);
m_MapRegisterBaseSet = FALSE;
}
inline
BOOLEAN
IsMapRegisterBaseSet(
VOID
)
{
return m_MapRegisterBaseSet;
}
inline
PVOID
GetMapRegisterBase(
VOID
)
{
NT_ASSERTMSG("Map register base is not set",
m_MapRegisterBaseSet == TRUE);
return m_MapRegisterBase;
}
virtual
IO_ALLOCATION_ACTION
GetAdapterControlReturnValue(
VOID
)
{
return DeallocateObjectKeepRegisters;
}
virtual
BOOLEAN
PreMapTransfer(
VOID
)
{
return TRUE;
}
_Acquires_lock_(this)
VOID
__drv_raisesIRQL(DISPATCH_LEVEL)
#pragma prefast(suppress:__WARNING_FAILING_TO_ACQUIRE_MEDIUM_CONFIDENCE, "transferring lock name to 'this->TransferStateLock'")
#pragma prefast(suppress:__WARNING_FAILING_TO_RELEASE_MEDIUM_CONFIDENCE, "transferring lock name to 'this->TransferStateLock'")
LockTransferState(
__out __drv_deref(__drv_savesIRQL) KIRQL *OldIrql
)
{
Lock(OldIrql);
}
_Requires_lock_held_(this)
_Releases_lock_(this)
VOID
#pragma prefast(suppress:__WARNING_FAILING_TO_RELEASE_MEDIUM_CONFIDENCE, "transferring lock name from 'this->TransferStateLock'")
UnlockTransferState(
__in __drv_restoresIRQL KIRQL OldIrql
)
{
#pragma prefast(suppress:__WARNING_CALLER_FAILING_TO_HOLD, "transferring lock name from 'this->TransferStateLock'")
Unlock(OldIrql);
}
virtual
PDMA_COMPLETION_ROUTINE
GetTransferCompletionRoutine(
VOID
)
{
return NULL;
}
static
IO_ALLOCATION_ACTION
STDCALL
_AdapterControl(
__in PDEVICE_OBJECT DeviceObject,
__in PIRP Irp,
__in PVOID MapRegisterBase,
__in PVOID Context
);
_Must_inspect_result_
NTSTATUS
AcquireDevice(
VOID
)
{
if (m_DmaEnabler->UsesDmaV3() == FALSE)
{
return m_DmaEnabler->GetDevice()->AcquireDmaPacketTransaction();
}
else
{
return STATUS_SUCCESS;
}
}
FORCEINLINE
VOID
ReleaseDevice(
VOID
)
{
if (m_DmaEnabler->UsesDmaV3() == FALSE)
{
m_DmaEnabler->GetDevice()->ReleaseDmaPacketTransaction();
}
}
_Must_inspect_result_
NTSTATUS
AllocateAdapterChannel(
__in BOOLEAN MapRegistersReserved
)
{
NTSTATUS status;
KIRQL irql;
KeRaiseIrql(DISPATCH_LEVEL, &irql);
if (GetDriverGlobals()->FxVerifierOn) {
if (MapRegistersReserved == FALSE) {
DoTraceLevelMessage(
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Allocating %d map registers for "
"WDFDMATRANSACTION %p",
m_MapRegistersNeeded,
GetHandle()
);
}
else {
DoTraceLevelMessage(
GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Using %d reserved map registers for "
"WDFDMATRANSACTION %p",
m_MapRegistersNeeded,
GetHandle()
);
}
}
if (m_DmaEnabler->UsesDmaV3()) {
PDMA_OPERATIONS dmaOperations =
m_AdapterInfo->AdapterObject->DmaOperations;
if (MapRegistersReserved == FALSE)
{
status = dmaOperations->AllocateAdapterChannelEx(
m_AdapterInfo->AdapterObject,
m_DmaEnabler->m_FDO,
GetTransferContext(),
m_MapRegistersNeeded,
m_Flags,
#pragma prefast(suppress: __WARNING_CLASS_MISMATCH_NONE, "This warning requires a wrapper class for the DRIVER_CONTROL type.")
_AdapterControl,
this,
NULL
);
}
else {
#pragma prefast(suppress:__WARNING_PASSING_FUNCTION_UNEXPECTED_NULL, "_AdapterControl does not actually use the IRP parameter.");
_AdapterControl(m_DmaEnabler->m_FDO,
NULL,
GetMapRegisterBase(),
this);
status = STATUS_SUCCESS;
}
}
else {
ASSERTMSG("Prereserved map registers are not compatible with DMA V2",
MapRegistersReserved == FALSE);
status = m_AdapterInfo->AdapterObject->DmaOperations->
AllocateAdapterChannel(m_AdapterInfo->AdapterObject,
m_DmaEnabler->m_FDO,
m_MapRegistersNeeded,
#pragma prefast(suppress: __WARNING_CLASS_MISMATCH_NONE, "This warning requires a wrapper class for the DRIVER_CONTROL type.")
_AdapterControl,
this);
}
KeLowerIrql(irql);
if (!NT_SUCCESS(status))
{
DoTraceLevelMessage(
GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA,
"Allocating DMA resources (%d map registers) for WDFDMATRANSACTION %p "
"returned %!STATUS!",
m_MapRegistersNeeded,
GetHandle(),
status
);
}
return status;
}
FORCEINLINE
NTSTATUS
MapTransfer(
__out_bcount_opt(ScatterGatherListCb)
PSCATTER_GATHER_LIST ScatterGatherList,
__in ULONG ScatterGatherListCb,
__in_opt PDMA_COMPLETION_ROUTINE CompletionRoutine,
__in_opt PVOID CompletionContext,
__out ULONG *TransferLength
)
{
//
// Cache globals & object handle since call to MapTransferEx could
// result in a DmaComplete callback before returning.
//
PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
WDFDMATRANSACTION handle = GetHandle();
#if DBG
ULONG_PTR mapRegistersRequired;
mapRegistersRequired = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
GetStartVaFromOffset(m_CurrentFragmentMdl,
m_CurrentFragmentOffset),
m_CurrentFragmentLength
);
NT_ASSERTMSG("Mapping requires too many map registers",
mapRegistersRequired <= m_MapRegistersNeeded);
#endif
NTSTATUS status;
//
// Assume we're going to transfer the entire current fragment.
// MapTransfer may say otherwise.
//
*TransferLength = (ULONG) m_CurrentFragmentLength;
//
// Map the transfer.
//
if (pFxDriverGlobals->FxVerifierOn) {
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Mapping transfer for WDFDMATRANSACTION %p. "
"MDL %p, Offset %I64x, Length %x, MapRegisterBase %p",
handle,
m_CurrentFragmentMdl,
m_CurrentFragmentOffset,
*TransferLength,
GetMapRegisterBase());
}
if (m_DmaEnabler->UsesDmaV3()) {
PDMA_OPERATIONS dmaOperations =
m_AdapterInfo->AdapterObject->DmaOperations;
status = dmaOperations->MapTransferEx(
m_AdapterInfo->AdapterObject,
m_CurrentFragmentMdl,
GetMapRegisterBase(),
m_CurrentFragmentOffset,
m_DeviceAddressOffset,
TransferLength,
(BOOLEAN) m_DmaDirection,
ScatterGatherList,
ScatterGatherListCb,
CompletionRoutine,
CompletionContext
);
NT_ASSERTMSG(
"With these parameters, MapTransferEx should never fail",
NT_SUCCESS(status) || status == STATUS_CANCELLED
);
}
else {
NT_ASSERTMSG("cannot use DMA completion routine with DMAv2",
CompletionRoutine == NULL);
NT_ASSERTMSG(
"scatter gather list length must be large enough for at least one element",
(ScatterGatherListCb >= (sizeof(SCATTER_GATHER_LIST) +
sizeof(SCATTER_GATHER_ELEMENT)))
);
//
// This matches the assertion above. There's no way to explain to
// prefast that this code path requires the caller to provide a buffer
// of sufficient size to store the SGL. The only case which doesn't
// provide any buffer is system-mode DMA and that uses DMA v3 and so
// won't go through this path.
//
__assume((ScatterGatherListCb >= (sizeof(SCATTER_GATHER_LIST) +
sizeof(SCATTER_GATHER_ELEMENT))));
ScatterGatherList->NumberOfElements = 1;
ScatterGatherList->Reserved = 0;
ScatterGatherList->Elements[0].Address =
m_AdapterInfo->AdapterObject->DmaOperations->
MapTransfer(m_AdapterInfo->AdapterObject,
m_CurrentFragmentMdl,
GetMapRegisterBase(),
GetStartVaFromOffset(m_CurrentFragmentMdl,
m_CurrentFragmentOffset),
TransferLength,
(BOOLEAN) m_DmaDirection);
ScatterGatherList->Elements[0].Length = *TransferLength;
status = STATUS_SUCCESS;
}
if (pFxDriverGlobals->FxVerifierOn) {
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
"MapTransfer mapped next %d bytes of "
"WDFDMATRANSACTION %p - status %!STATUS!",
*TransferLength,
handle,
status);
}
return status;
}
FORCEINLINE
NTSTATUS
FlushAdapterBuffers(
VOID
)
{
PDMA_OPERATIONS dmaOperations =
m_AdapterInfo->AdapterObject->DmaOperations;
NTSTATUS status;
#if DBG
ULONG_PTR mapRegistersRequired;
mapRegistersRequired = ADDRESS_AND_SIZE_TO_SPAN_PAGES(
GetStartVaFromOffset(m_CurrentFragmentMdl,
m_CurrentFragmentOffset),
m_CurrentFragmentLength
);
NT_ASSERTMSG("Mapping requires too many map registers",
mapRegistersRequired <= m_MapRegistersNeeded);
#endif
if (GetDriverGlobals()->FxVerifierOn) {
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Flushing DMA buffers for WDFDMATRANSACTION %p. "
"MDL %p, Offset %I64x, Length %I64x",
GetHandle(),
m_CurrentFragmentMdl,
m_CurrentFragmentOffset,
m_CurrentFragmentLength);
}
if (m_DmaEnabler->UsesDmaV3()) {
status = dmaOperations->FlushAdapterBuffersEx(
m_AdapterInfo->AdapterObject,
m_CurrentFragmentMdl,
GetMapRegisterBase(),
m_CurrentFragmentOffset,
(ULONG) m_CurrentFragmentLength,
(BOOLEAN) m_DmaDirection
);
}
else if (dmaOperations->FlushAdapterBuffers(
m_AdapterInfo->AdapterObject,
m_CurrentFragmentMdl,
GetMapRegisterBase(),
GetStartVaFromOffset(m_CurrentFragmentMdl,
m_CurrentFragmentOffset),
(ULONG) m_CurrentFragmentLength,
(BOOLEAN) m_DmaDirection) == FALSE) {
status = STATUS_UNSUCCESSFUL;
}
else {
status = STATUS_SUCCESS;
}
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA,
"Flushing DMA buffers for WDFDMATRANSACTION %p ("
"MDL %p, Offset %I64x, Length %I64x)"
"completed with %!STATUS!",
GetHandle(),
m_CurrentFragmentMdl,
m_CurrentFragmentOffset,
m_CurrentFragmentLength,
status);
}
return status;
}
virtual
VOID
FreeMapRegistersAndAdapter(
VOID
)
{
KIRQL irql;
PVOID mapRegisterBase = GetMapRegisterBase();
//
// It's illegal to free a NULL map register base, even if the HAL gave it
// to us.
//
if (mapRegisterBase == NULL) {
if (GetDriverGlobals()->FxVerifierOn) {
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Skipping free of %d map registers for WDFDMATRANSACTION %p "
"because base was NULL",
m_MapRegistersNeeded,
GetHandle());
}
return;
}
//
// Free the map registers
//
KeRaiseIrql(DISPATCH_LEVEL, &irql);
if (GetDriverGlobals()->FxVerifierOn) {
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Freeing %d map registers for WDFDMATRANSACTION %p "
"(base %p)",
m_MapRegistersNeeded,
GetHandle(),
mapRegisterBase);
}
//
// If we pre-reserved map registers then Reserved contains
// the number to free. Otherwise Needed is the number allocated
// for the last transaction, which is the number to free.
//
m_AdapterInfo->AdapterObject->DmaOperations->
FreeMapRegisters(m_AdapterInfo->AdapterObject,
mapRegisterBase,
(m_MapRegistersReserved > 0 ?
m_MapRegistersReserved :
m_MapRegistersNeeded));
KeLowerIrql(irql);
return;
}
virtual
VOID
CallEvtDmaCompleted(
__in DMA_COMPLETION_STATUS /* Status */
)
{
//
// Packet mode DMA doesn't support cancellation or
// completion routines. So this should never run.
//
ASSERTMSG("EvtDmaCompleted is not a valid callback for "
"a packet-mode transaction",
FALSE);
return;
}
};
class FxDmaSystemTransaction: public FxDmaPacketTransaction {
friend FxDmaPacketTransaction;
public:
FxDmaSystemTransaction(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in USHORT ExtraSize,
__in FxDmaEnabler *DmaEnabler
);
_Must_inspect_result_
static
NTSTATUS
_Create(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PWDF_OBJECT_ATTRIBUTES Attributes,
__in FxDmaEnabler* DmaEnabler,
__out WDFDMATRANSACTION* Transaction
);
VOID
SetConfigureChannelCallback(
__in_opt PFN_WDF_DMA_TRANSACTION_CONFIGURE_DMA_CHANNEL Callback,
__in_opt PVOID Context
)
{
m_ConfigureChannelFunction.Method = Callback;
m_ConfigureChannelContext = Context;
}
VOID
SetTransferCompleteCallback(
__in_opt PFN_WDF_DMA_TRANSACTION_DMA_TRANSFER_COMPLETE Callback,
__in_opt PVOID Context
)
{
m_TransferCompleteFunction.Method = Callback;
m_TransferCompleteContext = Context;
}
VOID
StopTransfer(
VOID
);
protected:
//
// Callback and context for configure channel callback
//
FxDmaTransactionConfigureChannel m_ConfigureChannelFunction;
PVOID m_ConfigureChannelContext;
//
// Callback and context for DMA completion callback
//
FxDmaTransactionTransferComplete m_TransferCompleteFunction;
PVOID m_TransferCompleteContext;
IO_ALLOCATION_ACTION
GetAdapterControlReturnValue(
VOID
)
{
return KeepObject;
}
VOID
FreeMapRegistersAndAdapter(
VOID
)
{
PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals();
KIRQL irql;
KeRaiseIrql(DISPATCH_LEVEL, &irql);
if (pFxDriverGlobals->FxVerifierOn) {
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Freeing adapter channel for WDFDMATRANSACTION %p",
GetHandle());
}
m_AdapterInfo->AdapterObject->DmaOperations->
FreeAdapterChannel(m_AdapterInfo->AdapterObject);
KeLowerIrql(irql);
return;
}
BOOLEAN
CancelMappedTransfer(
VOID
)
{
NTSTATUS status;
ASSERT(m_DmaEnabler->UsesDmaV3());
//
// Cancel the transfer. if it's not yet mapped this will mark the
// TC so that mapping will fail. If it's running this will invoke the
// DMA completion routine.
//
status =
m_AdapterInfo->AdapterObject->DmaOperations->CancelMappedTransfer(
m_AdapterInfo->AdapterObject,
GetTransferContext()
);
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGDMA,
"Stopping WDFDMATRANSACTION %p returned status %!STATUS!",
GetHandle(),
status);
return NT_SUCCESS(status);
}
VOID
Reuse(
VOID
)
{
FxDmaPacketTransaction::Reuse(); // __super call
m_ConfigureChannelFunction.Method = NULL;
m_ConfigureChannelContext = NULL;
m_TransferCompleteFunction.Method = NULL;
m_TransferCompleteContext = NULL;
}
VOID
CallEvtDmaCompleted(
__in DMA_COMPLETION_STATUS Status
);
virtual
BOOLEAN
PreMapTransfer(
VOID
);
virtual
PDMA_COMPLETION_ROUTINE
GetTransferCompletionRoutine(
VOID
);
static DMA_COMPLETION_ROUTINE _SystemDmaCompletion;
};
#endif // _FXDMATRANSACTION_HPP_