/*++ 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; } } FxRequestContext::ReleaseAndRestore(Request); // __super call } private: virtual VOID StoreAndReferenceMemory( __in FxRequestBuffer* Buffer ) { FxRequestContext::StoreAndReferenceMemory(Buffer); // __super call } 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" #elif ((FX_CORE_MODE)==(FX_CORE_USER_MODE)) #include "fxiotargetum.hpp" #endif #endif //_FXIOTARGET_H_