Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542/*++
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);}
BOOLEANFxDmaTransactionBase::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_NTSTATUSFxDmaTransactionBase::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_NTSTATUSFxDmaTransactionBase::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;}
BOOLEANFxDmaTransactionBase::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;}
VOIDFxDmaTransactionBase::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;
}
VOIDFxDmaTransactionBase::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; }}
BOOLEANFxDmaTransactionBase::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;}
VOIDFxDmaTransactionBase::_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_NTSTATUSFxDmaTransactionBase::_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_NTSTATUSFxDmaScatterGatherTransaction::_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;}
BOOLEANFxDmaScatterGatherTransaction::Dispose( VOID ){ BOOLEAN ret;
ret = FxDmaTransactionBase::Dispose(); // __super call
// // 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_NTSTATUSFxDmaScatterGatherTransaction::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;}
VOIDFxDmaScatterGatherTransaction::ReleaseResources( __in BOOLEAN /* ForceRelease */ ){ if (m_SGList != NULL) { PutScatterGatherList(m_SGList); m_SGList = NULL; } m_AdapterInfo = NULL;}
_Must_inspect_result_NTSTATUSFxDmaScatterGatherTransaction::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_NTSTATUSFxDmaScatterGatherTransaction::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.") (PDRIVER_LIST_CONTROL)_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.") (PDRIVER_LIST_CONTROL)_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;}
VOIDFxDmaScatterGatherTransaction::_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_NTSTATUSFxDmaScatterGatherTransaction::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_NTSTATUSFxDmaPacketTransaction::_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_NTSTATUSFxDmaPacketTransaction::InitializeResources( VOID ){ KIRQL oldIrql; m_DeviceAddressOffset = 0; LockTransferState(&oldIrql); m_IsCancelled = FALSE; UnlockTransferState(oldIrql); return STATUS_SUCCESS;}
VOIDFxDmaPacketTransaction::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_NTSTATUSFxDmaPacketTransaction::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;}
VOIDFxDmaPacketTransaction::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_NTSTATUSFxDmaPacketTransaction::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_ACTIONFxDmaPacketTransaction::_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_NTSTATUSFxDmaPacketTransaction::StageTransfer( VOID ){ PSCATTER_GATHER_LIST sgList;
PFX_DRIVER_GLOBALS pFxDriverGlobals;#ifndef __REACTOS__ UCHAR_MEMORY_ALIGNED sgListBuffer[sizeof(SCATTER_GATHER_LIST) + sizeof(SCATTER_GATHER_ELEMENT)];#else UCHAR sgListBuffer[sizeof(SCATTER_GATHER_LIST) + sizeof(SCATTER_GATHER_ELEMENT)];#endif
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; size_t sgListSize;
enabler = GetDmaEnabler();
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_NTSTATUSFxDmaPacketTransaction::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_NTSTATUSFxDmaSystemTransaction::_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;}
BOOLEANFxDmaSystemTransaction::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_ROUTINEFxDmaSystemTransaction::GetTransferCompletionRoutine( VOID ){ if (m_TransferCompleteFunction.Method == NULL) { return NULL; } else { return _SystemDmaCompletion; }}
VOIDFxDmaSystemTransaction::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 );}
VOIDFxDmaTransactionBase::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;}
VOIDFxDmaSystemTransaction::_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); }}
VOIDFxDmaSystemTransaction::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 } }}