/*++

Copyright (c) Microsoft Corporation.  All rights reserved.

Module Name:

    FxIoTargetRemote.hpp

Abstract:

    Derivation of FxIoTarget specializing in targets remote to this device
    stack.

Author:



Environment:

    Both kernel and user mode

Revision History:

--*/

#ifndef _FXIOTARGETREMOTE_H_
#define _FXIOTARGETREMOTE_H_

enum FxIoTargetRemoteCloseReason {
    FxIoTargetRemoteCloseReasonQueryRemove = 1,
    FxIoTargetRemoteCloseReasonPlainClose,
    FxIoTargetRemoteCloseReasonDelete
};

#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
typedef PVOID               MdTargetNotifyHandle;
#else
typedef WUDF_TARGET_CONTEXT MdTargetNotifyHandle;
#endif

class FxIoTargetRemoteNotificationCallback;

struct FxIoTargetQueryRemove : public FxCallback {

    FxIoTargetQueryRemove(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals
        ) :
        FxCallback(FxDriverGlobals),
        m_Method(NULL)
    {
    }

    _Must_inspect_result_
    NTSTATUS
    Invoke(
        __in WDFIOTARGET IoTarget
        )
    {
        NTSTATUS status;

        CallbackStart();
        status = m_Method(IoTarget);
        CallbackEnd();

        return status;
    }

    PFN_WDF_IO_TARGET_QUERY_REMOVE  m_Method;
};

struct FxIoTargetRemoveCanceled : public FxCallback {

    FxIoTargetRemoveCanceled(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals
        ) :
        FxCallback(FxDriverGlobals),
        m_Method(NULL)
    {
    }

    VOID
    Invoke(
        __in WDFIOTARGET Target
        )
    {
        CallbackStart();
        m_Method(Target);
        CallbackEnd();
    }

    PFN_WDF_IO_TARGET_REMOVE_CANCELED  m_Method;
};

struct FxIoTargetRemoveComplete : public FxCallback {

    FxIoTargetRemoveComplete(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals
        ) :
        FxCallback(FxDriverGlobals),
        m_Method(NULL)
    {
    }

    VOID
    Invoke(
        __in WDFIOTARGET Target
        )
    {
        CallbackStart();
        m_Method(Target);
        CallbackEnd();
    }

    PFN_WDF_IO_TARGET_REMOVE_COMPLETE m_Method;
};

enum FxIoTargetRemoteOpenState {
    FxIoTargetRemoteOpenStateClosed = 1,
    FxIoTargetRemoteOpenStateOpening,
    FxIoTargetRemoteOpenStateOpen,
};

struct FxIoTargetRemoveOpenParams {

    FxIoTargetRemoveOpenParams()
    {
        RtlZeroMemory(this, sizeof(FxIoTargetRemoveOpenParams));
    }

    VOID
    Set(
        __in PWDF_IO_TARGET_OPEN_PARAMS OpenParams,
        __in PUNICODE_STRING Name,
        __in PVOID Ea,
        __in ULONG EaLength
        );

    VOID
    Clear(
        VOID
        );

    UNICODE_STRING TargetDeviceName;

    WDF_IO_TARGET_OPEN_TYPE OpenType;

    ACCESS_MASK DesiredAccess;

    ULONG ShareAccess;

    ULONG FileAttributes;

    ULONG CreateDisposition;

    ULONG CreateOptions;

    __field_bcount(EaBufferLength) PVOID EaBuffer;

    ULONG EaBufferLength;

    LARGE_INTEGER AllocationSize;

    PLARGE_INTEGER AllocationSizePointer;

};

struct FxIoTargetClearedPointers {
    MdDeviceObject TargetPdo;
    MdFileObject TargetFileObject;
    HANDLE TargetHandle;
};

class FxIoTargetRemote : public FxIoTarget {

public:

    static
    _Must_inspect_result_
    NTSTATUS
    _Create(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals,
        __in PWDF_OBJECT_ATTRIBUTES Attributes,
        __in FxDeviceBase* Device,
        __out FxIoTargetRemote** Target
        );

    ~FxIoTargetRemote();

    NTSTATUS
    InitRemote(
        __in FxDeviceBase* Device
        );

    NTSTATUS
    InitRemoteModeSpecific(
        __in FxDeviceBase* Device
        );

    _Must_inspect_result_
    NTSTATUS
    Open(
        __in PWDF_IO_TARGET_OPEN_PARAMS OpenParams
        );

    VOID
    Close(
        __in FxIoTargetRemoteCloseReason Reason
        );

    NTSTATUS
    GetTargetDeviceRelations(
        _Out_ BOOLEAN* Close
        );

    BOOLEAN
    CanRegisterForPnpNotification(
        VOID
        )
    {
        BOOLEAN canRegister = FALSE;

#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
        if (m_TargetFileObject != NULL) {
            canRegister = TRUE;
        }
#else // FX_CORE_USER_MODE
        if (m_TargetHandle != NULL) {
            canRegister = TRUE;
        }
#endif
        return canRegister;
    }

    VOID
    ResetTargetNotifyHandle(
        VOID
        )
    {
#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
    m_TargetNotifyHandle = NULL;
#else // FX_CORE_USER_MODE
    m_TargetNotifyHandle = WUDF_TARGET_CONTEXT_INVALID;
#endif
    }

    NTSTATUS
    OpenTargetHandle(
        _In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams,
        _Inout_ FxIoTargetRemoveOpenParams* pParams
        );

    VOID
    CloseTargetHandle(
        VOID
        );

    HANDLE
    GetTargetHandle(
        VOID
        );

    NTSTATUS
    RegisterForPnpNotification(
        VOID
        );

    VOID
    UnregisterForPnpNotification(
        _In_ MdTargetNotifyHandle Handle
        );

    __inline
    WDFIOTARGET
    GetHandle(
        VOID
        )
    {
        return (WDFIOTARGET) GetObjectHandle();
    }

    virtual
    VOID
    Remove(
        VOID
        );

    VOID
    RemoveModeSpecific(
        VOID
        );

protected:
    FxIoTargetRemote(
        __in PFX_DRIVER_GLOBALS FxDriverGlobals
        );

    virtual
    VOID
    ClearTargetPointers(
        VOID
        );

    _Must_inspect_result_
    NTSTATUS
    QueryInterface(
        __in FxQueryInterfaceParams* Params
        )
    {
        if (Params->Type == FX_TYPE_IO_TARGET_REMOTE) {
            *Params->Object = (FxIoTargetRemote*) this;
            return STATUS_SUCCESS;
        }
        else {
            return FxIoTarget::QueryInterface(Params); // __super call
        }
    }

    _Must_inspect_result_
    NTSTATUS
    OpenLocalTargetByFile(
        _In_ PWDF_IO_TARGET_OPEN_PARAMS OpenParams
        );

#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
    static
    DRIVER_NOTIFICATION_CALLBACK_ROUTINE
    _PlugPlayNotification;

#else // FX_CORE_USER_MODE
    //
    // I/O dispatcher to be used for IRPs forwarded to this remote target. It is
    // created when the CWdfRemoteTarget is created. The win32 handle is
    // associated with it via a call to m_pRemoteDispatcher->BindToHandle()
    // right after we call CreateFile(...). We must call
    // m_pRemoteDispatcher->CloseHandle() to close the handle.
    //
    // Because of the plug-in pattern of the IoDispatcher, we need two
    // interface pointers (one to the outer object, and one to the plug-in.
    //
    IWudfIoDispatcher     * m_pIoDispatcher;
    IWudfRemoteDispatcher * m_pRemoteDispatcher;

    //
    // Implements host's callback interface for pnp notification
    //
    FxIoTargetRemoteNotificationCallback* m_NotificationCallback;

    VOID
    Forward(
        _In_ MdIrp Irp
        )
    {
        if (m_OpenParams.OpenType == WdfIoTargetOpenLocalTargetByFile) {
            //
            // 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();
        }
        else {
            IWudfIoIrp* pSubmitIrp = FxIrp(Irp).GetIoIrp();

            //
            // Move the stack location to the next location
            //
            pSubmitIrp->SetNextIrpStackLocation();

            //
            // Route it using Remote dispatcher
            //
            m_pIoDispatcher->Dispatch(pSubmitIrp, NULL);
        }
    }

private:

    NTSTATUS
    BindToHandle(
        VOID
        );

    VOID
    UnbindHandle(
        _In_ FxIoTargetClearedPointers* TargetPointers
        );

    NTSTATUS
    CreateWdfFileObject(
        _In_opt_ PUNICODE_STRING  FileName,
        _Out_ MdFileObject* FileObject
        );

    VOID
    CloseWdfFileObject(
        _In_ MdFileObject FileObject
        );

#endif // FX_CORE_USER-MODE)

public:
    //
    // File handle for m_TargetHandle
    //
    HANDLE m_TargetHandle;

    //
    // Notification handle returned by IoRegisterPlugPlayNotification for KMDF,
    // or host's notification registartion interface for UMDf. Note that host
    // uses the term RegistrationId for the same (with WUDF_CONTEXT_TYPE which
    // is UINT64).
    //
    MdTargetNotifyHandle m_TargetNotifyHandle;

    //
    // Driver writer callbacks to indicate state changes
    //
    FxIoTargetQueryRemove m_EvtQueryRemove;
    FxIoTargetRemoveCanceled m_EvtRemoveCanceled;
    FxIoTargetRemoveComplete m_EvtRemoveComplete;

    FxCREvent m_OpenedEvent;

    FxIoTargetClearedPointers* m_ClearedPointers;

    //
    // Value from FxIoTargetRemoteOpenState
    //
    UCHAR m_OpenState;

protected:
    FxIoTargetRemoveOpenParams m_OpenParams;
};

#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
#include "fxiotargetremotekm.hpp"
#else
#include "fxiotargetremoteum.hpp"
#endif

#endif // _FXIOTARGETREMOTE_H_