--- /dev/null
+/*++
+
+Copyright (c) Microsoft Corporation. All rights reserved.
+
+Module Name:
+
+ FxIoTarget.hpp
+
+Abstract:
+
+ Encapsulation of the target to which FxRequest are sent to. For example,
+ an FxTarget could represent the next device object in the pnp stack.
+ Derivations from this class could include bus specific formatters or device
+ objects outside of the pnp stack of the device.
+
+Author:
+
+
+
+Environment:
+
+ Both kernel and user mode
+
+Revision History:
+
+--*/
+
+#ifndef _FXIOTARGET_H_
+#define _FXIOTARGET_H_
+
+
+struct FxIoContext : public FxRequestContext {
+
+ FxIoContext(
+ VOID
+ );
+
+ virtual
+ ~FxIoContext(
+ VOID
+ );
+
+ VOID
+ StoreAndReferenceOtherMemory(
+ __in FxRequestBuffer* Buffer
+ )
+ {
+ _StoreAndReferenceMemoryWorker(this, &m_OtherMemory, Buffer);
+ }
+
+ virtual
+ VOID
+ ReleaseAndRestore(
+ __in FxRequestBase* Request
+ );
+
+ VOID
+ ClearBuffer(
+ VOID
+ );
+
+ VOID
+ SetBufferAndLength(
+ __in PVOID Buffer,
+ __in size_t BufferLength,
+ __in BOOLEAN CopyBackToBuffer
+ );
+
+ VOID
+ CopyParameters(
+ __in FxRequestBase* Request
+ );
+
+ VOID
+ CaptureState(
+ __in FxIrp* Irp
+ );
+
+ VOID
+ SwapIrpBuffer(
+ _In_ FxRequestBase* Request,
+ _In_ ULONG NewInputBufferCb,
+ _In_reads_bytes_opt_(NewInputBufferCb) PVOID NewInputBuffer,
+ _In_ ULONG NewOutputBufferCb,
+ _In_reads_bytes_opt_(NewOutputBufferCb) PVOID NewOutputBuffer
+ );
+
+public:
+
+#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+ PVOID m_BufferToFree;
+ PVOID m_OriginalSystemBuffer;
+ PVOID m_OriginalUserBuffer;
+ PMDL m_MdlToFree;
+ union {
+ PMDL m_OriginalMdl;
+ PFX_DRIVER_GLOBALS m_DriverGlobals;
+ };
+
+ ULONG m_OriginalFlags;
+
+ size_t m_BufferToFreeLength;
+ size_t m_MdlToFreeSize;
+ BOOLEAN m_CopyBackToBuffer;
+ BOOLEAN m_UnlockPages;
+#else
+ //
+ // Captured state of the IRP before buffers are modified by Format
+ //
+ WUDFX_IRP_BUFFER_INFO m_OriginalBufferInfo;
+#endif
+
+ BOOLEAN m_RestoreState;
+ UCHAR m_MajorFunction;
+ IFxMemory* m_OtherMemory;
+};
+
+struct FxInternalIoctlOthersContext : public FxRequestContext {
+
+ FxInternalIoctlOthersContext(
+ VOID
+ ) :
+ FxRequestContext(FX_RCT_INTERNAL_IOCTL_OTHERS)
+ {
+ RtlZeroMemory(&m_MemoryObjects[0], sizeof(m_MemoryObjects));
+ }
+
+ VOID
+ StoreAndReferenceOtherMemories(
+ __in FxRequestBuffer* Buffer1,
+ __in FxRequestBuffer* Buffer2,
+ __in FxRequestBuffer* Buffer4
+ )
+ {
+ StoreAndReferenceMemory(Buffer1);
+ _StoreAndReferenceMemoryWorker(this, &m_MemoryObjects[0], Buffer2);
+ _StoreAndReferenceMemoryWorker(this, &m_MemoryObjects[1], Buffer4);
+ }
+
+ virtual
+ VOID
+ ReleaseAndRestore(
+ __in FxRequestBase* Request
+ )
+ {
+ ULONG i;
+
+ for (i = 0;
+ i < sizeof(m_MemoryObjects)/sizeof(m_MemoryObjects[0]);
+ i++) {
+
+ if (m_MemoryObjects[i] != NULL) {
+ m_MemoryObjects[i]->RELEASE(this);
+ m_MemoryObjects[i] = NULL;
+ }
+ }
+
+ __super::ReleaseAndRestore(Request);
+ }
+
+private:
+ virtual
+ VOID
+ StoreAndReferenceMemory(
+ __in FxRequestBuffer* Buffer
+ )
+ {
+ __super::StoreAndReferenceMemory(Buffer);
+ }
+
+public:
+ //
+ // __super has a field for one IFxMemory, so we don't need to store all
+ // 3 in the derivative, reuse the __super's field for one of them.
+ //
+ IFxMemory* m_MemoryObjects[FX_REQUEST_NUM_OTHER_PARAMS-1];
+};
+
+struct FxTargetSubmitSyncParams {
+ //
+ // Event to set if the request is synchronous after the request has completed
+ //
+ FxCREvent SynchEvent;
+
+ //
+ // Status of the request if it was synchronous
+ //
+ NTSTATUS Status;
+
+ //
+ // Original completion routine to be called in the synchronous case
+ //
+ PFN_WDF_REQUEST_COMPLETION_ROUTINE OrigTargetCompletionRoutine;
+
+ //
+ // Original completion context to be passed in the synchronous case
+ //
+ WDFCONTEXT OrigTargetCompletionContext;
+};
+
+enum SubmitActionFlags {
+ SubmitSend = 0x00000001,
+ SubmitQueued = 0x00000002,
+ SubmitSent = 0x00000004,
+ SubmitWait = 0x00000008,
+ SubmitTimeout = 0x00000010,
+ SubmitSyncCallCompletion = 0x00000020,
+};
+
+class FxIoTarget : public FxNonPagedObject {
+
+ friend FxRequestBase;
+
+public:
+
+ FxIoTarget(
+ __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+ __in USHORT ObjectSize
+ );
+
+ FxIoTarget(
+ __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+ __in USHORT ObjectSize,
+ __in WDFTYPE WdfType
+ );
+
+ virtual
+ _Must_inspect_result_
+ NTSTATUS
+ Start(
+ VOID
+ );
+
+ virtual
+ VOID
+ Stop(
+ __in WDF_IO_TARGET_SENT_IO_ACTION Action
+ );
+
+ virtual
+ VOID
+ Purge(
+ __in WDF_IO_TARGET_PURGE_IO_ACTION Action
+ );
+
+ virtual
+ VOID
+ Remove(
+ VOID
+ );
+
+ //
+ // IFxObject override
+ //
+ NTSTATUS
+ _Must_inspect_result_
+ QueryInterface(
+ __inout FxQueryInterfaceParams* Params
+ );
+
+ __inline
+ WDF_IO_TARGET_STATE
+ GetState(
+ VOID
+ )
+ {
+ return m_State;
+ }
+
+ __inline
+ MdDeviceObject
+ GetTargetDevice(
+ VOID
+ )
+ {
+ return m_TargetDevice;
+ }
+
+ __inline
+ MdDeviceObject
+ GetTargetPDO(
+ VOID
+ )
+ {
+ return m_TargetPdo;
+ }
+
+ __inline
+ MdFileObject
+ GetTargetFileObject(
+ VOID
+ )
+ {
+ return m_TargetFileObject;
+ }
+
+ __inline
+ WDFDEVICE
+ GetDeviceHandle(
+ VOID
+ )
+ {
+ return m_Device->GetHandle();
+ }
+
+ WDFIOTARGET
+ GetHandle(
+ VOID
+ )
+ {
+ return (WDFIOTARGET) GetObjectHandle();
+ }
+
+ __inline
+ FxDriver*
+ GetDriver(
+ VOID
+ )
+ {
+ return m_Driver;
+ }
+
+ virtual
+ _Must_inspect_result_
+ MdDeviceObject
+ GetTargetDeviceObject(
+ _In_ CfxDeviceBase* Device
+ )
+ {
+ return Device->GetAttachedDevice();
+ }
+
+ _Must_inspect_result_
+ NTSTATUS
+ Init(
+ __in CfxDeviceBase* Device
+ );
+
+ ULONG
+ Submit(
+ __in FxRequestBase* Request,
+ __in_opt PWDF_REQUEST_SEND_OPTIONS Options,
+ __in_opt ULONG Flags
+ );
+
+ // Do not specify argument names
+ FX_DECLARE_VF_FUNCTION_P1(
+ NTSTATUS,
+ VerifySubmitLocked,
+ _In_ FxRequestBase*
+ );
+
+ ULONG
+ SubmitLocked(
+ __in FxRequestBase* Request,
+ __in_opt PWDF_REQUEST_SEND_OPTIONS Options,
+ __in ULONG Flags
+ );
+
+ _Must_inspect_result_
+ NTSTATUS
+ SubmitSync(
+ __in FxRequestBase* Request,
+ __in_opt PWDF_REQUEST_SEND_OPTIONS Options = NULL,
+ __out_opt PULONG Action = NULL
+ );
+
+ VOID
+ TimerCallback(
+ __in FxRequestBase* Request
+ );
+
+ VOID
+ CompleteCanceledRequest(
+ __in FxRequestBase* Request
+ );
+
+ VOID
+ SubmitPendedRequest(
+ __in FxRequestBase* Request
+ );
+
+ VOID
+ CompletePendedRequest(
+ __in FxRequestBase* Request
+ );
+
+ static
+ VOID
+ _CancelSentRequest(
+ __in FxRequestBase* Request
+ );
+
+ BOOLEAN
+ __inline
+ HasEnoughStackLocations(
+ __in FxIrp* Irp
+ )
+ {
+#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+ //
+ // Check to make sure there are enough current stack locations available.
+ // When a IRP is initially created, Irp->CurrentLocation is set to
+ // StackSize + 1. When comparing against the target device, subtract
+ // off the extra space to see how many locations are left.
+ //
+ // Say Target->m_TargetStackSize == 1, then:
+ // irp = IoAllocateIrp(Target->m_TargetStackSize, FALSE);
+ // ASSERT(irp->CurrentLocation == 2);
+ //
+ return (Irp->GetCurrentIrpStackLocationIndex() - 1 >= m_TargetStackSize) ? TRUE : FALSE;
+#else // FX_CORE_USER_MODE
+ //
+ // For UMDF, host does the necessary checks to ensure there are enough
+ // stack locations. In addition, UMDF drivers can't create WDM IRPs
+ // so they don't get to dictate the number of stack locations in the irp
+ // so this kind of check in framework for UMDF is redundant. Return TRUE
+ // always.
+ //
+ return TRUE;
+#endif
+ }
+
+ _Must_inspect_result_
+ NTSTATUS
+ FormatIoRequest(
+ __inout FxRequestBase* Request,
+ __in UCHAR MajorCode,
+ __in FxRequestBuffer* IoBuffer,
+ __in_opt PLONGLONG StartingOffset,
+ __in_opt FxFileObject* FileObject = NULL
+ );
+
+ _Must_inspect_result_
+ NTSTATUS
+ FormatIoctlRequest(
+ __in FxRequestBase* Request,
+ __in ULONG Ioctl,
+ __in BOOLEAN Internal,
+ __in FxRequestBuffer* InputBuffer,
+ __in FxRequestBuffer* OutputBuffer,
+ __in_opt FxFileObject* FileObject = NULL
+ );
+
+ _Must_inspect_result_
+ NTSTATUS
+ FormatInternalIoctlOthersRequest(
+ __in FxRequestBase* Request,
+ __in ULONG Ioctl,
+ __in FxRequestBuffer* Buffers
+ );
+
+ static
+ FxIoTarget*
+ _FromEntry(
+ __in FxTransactionedEntry* Entry
+ )
+ {
+ return CONTAINING_RECORD(Entry, FxIoTarget, m_TransactionedEntry);
+ }
+
+ VOID
+ CancelSentIo(
+ VOID
+ );
+
+ _Must_inspect_result_
+ NTSTATUS
+ SubmitSyncRequestIgnoreTargetState(
+ __in FxRequestBase* Request,
+ __in_opt PWDF_REQUEST_SEND_OPTIONS RequestOptions
+ );
+
+ VOID
+ UpdateTargetIoType(
+ VOID
+ );
+
+ BOOLEAN
+ HasValidStackSize(
+ VOID
+ );
+
+ virtual
+ VOID
+ Send(
+ _In_ MdIrp Irp
+ );
+
+protected:
+ //
+ // Hide destructor since we are reference counted object
+ //
+ ~FxIoTarget();
+
+ _Must_inspect_result_
+ NTSTATUS
+ InitModeSpecific(
+ __in CfxDeviceBase* Device
+ );
+
+ // FxObject overrides
+ virtual
+ BOOLEAN
+ Dispose(
+ VOID
+ );
+ // FxObject overrides
+
+ VOID
+ FailPendedRequest(
+ __in FxRequestBase* Request,
+ __in NTSTATUS Status
+ );
+
+ VOID
+ DrainPendedRequestsLocked(
+ __in PLIST_ENTRY RequestListHead,
+ __in BOOLEAN RequestWillBeResent
+ );
+
+ VOID
+ CompletePendedRequestList(
+ __in PLIST_ENTRY RequestListHead
+ );
+
+ VOID
+ SubmitPendedRequests(
+ __in PLIST_ENTRY RequestListHeadHead
+ );
+
+ VOID
+ GetSentRequestsListLocked(
+ __in PSINGLE_LIST_ENTRY RequestListHead,
+ __in PLIST_ENTRY SendList,
+ __out PBOOLEAN AddedToList
+ );
+
+ static
+ VOID
+ _CancelSentRequests(
+ __in PSINGLE_LIST_ENTRY RequestListHead
+ );
+
+ virtual
+ _Must_inspect_result_
+ NTSTATUS
+ GotoStartState(
+ __in PLIST_ENTRY RequestListHead,
+ __in BOOLEAN Lock = TRUE
+ );
+
+ virtual
+ VOID
+ GotoStopState(
+ __in WDF_IO_TARGET_SENT_IO_ACTION Action,
+ __in PSINGLE_LIST_ENTRY SentRequestListHead,
+ __out PBOOLEAN Wait,
+ __in BOOLEAN LockSelf
+ );
+
+ virtual
+ VOID
+ GotoPurgeState(
+ __in WDF_IO_TARGET_PURGE_IO_ACTION Action,
+ __in PLIST_ENTRY PendedRequestListHead,
+ __in PSINGLE_LIST_ENTRY SentRequestListHead,
+ __out PBOOLEAN Wait,
+ __in BOOLEAN LockSelf
+ );
+
+ _Must_inspect_result_
+ NTSTATUS
+ PendRequestLocked(
+ __in FxRequestBase* Request
+ );
+
+ __inline
+ VOID
+ CompleteRequest(
+ __in FxRequestBase* Request
+ )
+ {
+ //
+ // This will remove the reference taken by this object on the request
+ //
+ Request->CompleteSubmitted();
+ }
+
+ //
+ // Completion routine to handle the case when re-submitting a pended
+ // request fails.
+ //
+ VOID
+ HandleFailedResubmit(
+ __in FxRequestBase* Request
+ );
+
+ //
+ // Generic I/O completion routine and its static caller.
+ //
+ VOID
+ RequestCompletionRoutine(
+ __in FxRequestBase* Request
+ );
+
+ static
+ MdCompletionRoutineType
+ _RequestCompletionRoutine;
+
+ BOOLEAN
+ RemoveCompletedRequestLocked(
+ __in FxRequestBase* Request
+ );
+
+ virtual
+ VOID
+ ClearTargetPointers(
+ VOID
+ )
+ {
+ m_TargetDevice = NULL;
+ m_TargetPdo = NULL;
+ m_TargetFileObject = NULL;
+
+ m_TargetStackSize = 0;
+ m_TargetIoType = WdfDeviceIoUndefined;
+ }
+
+ UCHAR
+ GetTargetIoType(
+ VOID
+ )
+ {
+ ULONG flags;
+ MxDeviceObject deviceObject(m_TargetDevice);
+
+ flags = deviceObject.GetFlags();
+
+ if (flags & DO_BUFFERED_IO) {
+ return WdfDeviceIoBuffered;
+ }
+ else if (flags & DO_DIRECT_IO) {
+ return WdfDeviceIoDirect;
+ }
+ else {
+ return WdfDeviceIoNeither;
+ }
+ }
+
+ static
+ VOID
+ _RequestCancelled(
+ __in FxIrpQueue* Queue,
+ __in MdIrp Irp,
+ __in PMdIoCsqIrpContext pCsqContext,
+ __in KIRQL CallerIrql
+ );
+
+ static
+ EVT_WDF_REQUEST_COMPLETION_ROUTINE
+ _SyncCompletionRoutine;
+
+ virtual
+ VOID
+ GotoRemoveState(
+ __in WDF_IO_TARGET_STATE NewState,
+ __in PLIST_ENTRY PendedRequestListHead,
+ __in PSINGLE_LIST_ENTRY SentRequestListHead,
+ __in BOOLEAN Lock,
+ __out PBOOLEAN Wait
+ );
+
+ virtual
+ VOID
+ WaitForSentIoToComplete(
+ VOID
+ )
+ {
+ ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
+
+ m_SentIoEvent.EnterCRAndWaitAndLeave();
+ }
+
+ virtual
+ VOID
+ WaitForDisposeEvent(
+ VOID
+ );
+
+#if (FX_CORE_MODE == FX_CORE_USER_MODE)
+ //
+ //Making it a virtual function so that derived classes can override it
+ //For example, CWdfIoTargetLocal overrides it to set the file object
+ //before forwarding the request
+ //
+ virtual
+ VOID
+ Forward(
+ __in MdIrp Irp
+ )
+ {
+ //
+ // Ignore the return value because once we have sent the request, we
+ // want all processing to be done in the completion routine.
+ //
+ (void) Irp->Forward();
+ }
+#endif
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ __inline
+ VOID
+ CopyFileObjectAndFlags(
+ __in FxRequestBase* Request
+ )
+ {
+ FxIrp* irp = Request->GetSubmitFxIrp();
+
+ if (Request->IsAllocatedFromIo()) {
+ irp->SetNextStackFlags(irp->GetCurrentStackFlags());
+ irp->SetNextStackFileObject(irp->GetCurrentStackFileObject());
+ }
+
+ //
+ // Use the target's fileobject if present, otherwise use the current
+ // stack location's fileobject (if there is a current stack location).
+ //
+ if (m_InStack == FALSE) {
+ irp->SetNextStackFileObject(m_TargetFileObject);
+ }
+ }
+
+
+
+ __inline
+ VOID
+ IncrementIoCount(
+ VOID
+ )
+ {
+ LONG ret;
+
+ ret = InterlockedIncrement(&m_IoCount);
+
+#if DBG
+ ASSERT(ret > 1);
+#else
+ UNREFERENCED_PARAMETER(ret);
+#endif
+ }
+
+
+ __inline
+ VOID
+ DecrementIoCount(
+ VOID
+ )
+ {
+ LONG ret;
+
+ ret = InterlockedDecrement(&m_IoCount);
+ ASSERT(ret >= 0);
+
+ if (ret == 0) {
+ PrintDisposeMessage();
+ ASSERT(m_DisposeEvent != NULL);
+ m_DisposeEvent->Set();
+ }
+ }
+
+ VOID
+ PrintDisposeMessage(
+ VOID
+ );
+
+private:
+
+ VOID
+ Construct(
+ VOID
+ );
+
+ VOID
+ ClearCompletedRequestVerifierFlags(
+ __in FxRequestBase* Request
+ )
+ {
+ if (GetDriverGlobals()->FxVerifierOn &&
+ GetDriverGlobals()->FxVerifierIO) {
+ KIRQL irql;
+
+ Request->Lock(&irql);
+ //
+ // IF we are completing a request that was pended in the target,
+ // this flag was not set.
+ //
+ // ASSERT(Request->GetVerifierFlagsLocked() & FXREQUEST_FLAG_SENT_TO_TARGET);
+ Request->ClearVerifierFlagsLocked(FXREQUEST_FLAG_SENT_TO_TARGET);
+ Request->Unlock(irql);
+ }
+ }
+
+ VOID
+ SetCompletionRoutine(
+ __in FxRequestBase* Request
+ )
+ {
+ FxIrp* irp = Request->GetSubmitFxIrp();
+
+ irp->SetCompletionRoutineEx(
+ m_InStackDevice,
+ _RequestCompletionRoutine,
+ Request,
+ TRUE,
+ TRUE,
+ TRUE);
+ }
+
+public:
+ //
+ // Transaction entry for FxDevice to queue this target on
+ //
+ FxTransactionedEntry m_TransactionedEntry;
+
+ BOOLEAN m_InStack;
+
+ //
+ // TRUE when FxDevice::AddIoTarget has been called
+ //
+ BOOLEAN m_AddedToDeviceList;
+
+ static const PVOID m_SentRequestTag;
+
+protected:
+ //
+ // List of requests that have been sent to the target
+ //
+ LIST_ENTRY m_SentIoListHead;
+
+ //
+ // List of requests which were sent ignoring the state of the target
+ //
+ LIST_ENTRY m_IgnoredIoListHead;
+
+ //
+ // Event used to wait for sent I/O to complete
+ //
+ FxCREvent m_SentIoEvent;
+
+ //
+ // Event used to wait by Dispose to make sure all I/O's are completed.
+ // This is required to make sure that all the I/O are completed before
+ // disposing the target. This acts like remlock.
+ //
+ FxCREvent *m_DisposeEvent;
+
+#if (FX_CORE_MODE == FX_CORE_USER_MODE)
+ //
+ // Eventy initialization can fail in user-mode so we define one as
+ // part of object.
+ //
+ FxCREvent m_DisposeEventUm;
+#endif
+
+ FxIrpQueue m_PendedQueue;
+
+ //
+ // Back link to the object that represents our devobj
+ //
+ FxDriver* m_Driver;
+
+ //
+ // The PDEVICE_OBJECT that is owned by m_Device
+ //
+ MdDeviceObject m_InStackDevice;
+
+ //
+ // The device object which is our "target"
+ //
+ MdDeviceObject m_TargetDevice;
+
+ //
+ // The PDO for m_TargetDevice. For this class, it would be the same PDO
+ // as the owning WDFDEVICE. In a derived class (like FxIoTargetRemote),
+ // this would not be the PDO of the owning WDFDEVICE, rather the PDO for
+ // the other stack.
+ //
+ MdDeviceObject m_TargetPdo;
+
+ //
+ // File object that is attached to all I/O sent to m_TargetDevice
+ //
+ MdFileObject m_TargetFileObject;
+
+ //
+ // Current state
+ //
+ WDF_IO_TARGET_STATE m_State;
+
+ //
+ // This is used to track the I/O's sent to the lower driver
+ // and is used to make sure all I/Os are completed before disposing the
+ // Iotarget.
+ //
+ LONG m_IoCount;
+
+ //
+ // Cached value of m_TargetDevice->StackSize. The value is cached so that
+ // we can still format to the target during query remove transitions.
+ //
+ CCHAR m_TargetStackSize;
+
+ //
+ // Cached value of m_TargetDevice->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO)
+ // which uses WDF_DEVICE_IO_TYPE to indicate state.
+ //
+ UCHAR m_TargetIoType;
+
+ //
+ // TRUE if we are in the processing of stopping/purging and there are
+ // requests that have been sent and must be waited upon for completion.
+ //
+ BOOLEAN m_WaitingForSentIo;
+
+ BOOLEAN m_Removing;
+
+};
+
+
+#if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
+#include "FxIoTargetKm.hpp"
+#else if ((FX_CORE_MODE)==(FX_CORE_USER_MODE))
+#include "FxIoTargetUm.hpp"
+#endif
+
+#endif //_FXIOTARGET_H_