/*++

Copyright (c) Microsoft Corporation

Module Name:

    FxDeviceUM.hpp

Abstract:

    This is the definition of the FxDevice object UM specific

Author:



Environment:

    User mode only

Revision History:

--*/

#ifndef _FXDEVICEUM_H_
#define _FXDEVICEUM_H_

#define WDF_PATH_SEPARATOR              L"\\"
#define WUDF_SUB_KEY                    L"WUDF"
#define WUDF_ADDITIONAL_SUB_KEY         L"WDF"

#define FX_DIRECT_HARDWARE_ACCESS           L"DirectHardwareAccess"
#define FX_DIRECT_HARDWARE_ACCESS_DEFAULT   (WdfRejectDirectHardwareAccess)

#define FX_REGISTER_ACCESS_MODE             L"RegisterAccessMode"
#define FX_REGISTER_ACCESS_MODE_DEFAULT     (WdfRegisterAccessUsingSystemCall)

#define FX_FILE_OBJECT_POLICY               L"FileObjectPolicy"
#define FX_FILE_OBJECT_POLICY_DEFAULT       (WdfRejectNullAndUnknownFileObjects)

#define FX_FS_CONTEXT_USE_POLICY            L"FsContextUsePolicy"
#define FX_FS_CONTEXT_USE_POLICY_DEFAULT    (WdfDefaultFsContextUsePolicy)

#define FX_KERNEL_MODE_CLIENT_POLICY        L"KernelModeClientPolicy"
#define FX_METHOD_NEITHER_ACTION            L"MethodNeitherAction"
#define FX_PROCESS_SHARING_ENABLED          L"HostProcessSharingEnabled"
#define FX_DEVICE_GROUP_ID                  L"DeviceGroupId"
#define FX_FILE_OBJECT_POLICY               L"FileObjectPolicy"

//
// READ/WRITE_REGISTER_Xxx macros need compiler and memory barrier. Each
// platform has a different set of compiler intrinsics to support that.
// In kernel, x86 READ/WRITE macros are implemented in assembly and use
// lock prefix to force real access. For amd64, _ReadWriteBarrier
// (compiler barrier) is used for reads and __faststorefence (CPU barrier) for
// writes. For ARM, _ReadWriteBarrier (compiler barrier) is used for reads and
// both _ReadWriteBarrier and __emit(Value) for writes.
//
// Because of this variation, UMDF will use the macros directly from wdm.h
// by autogenerating those macros from wdm.h into a private UMDF header.
// For x86, there are no macros so either UMDF could directly link to the
// ntosrtl.lib (that has these macros implemented in assembly), or implement
// its own macros that use MemoryBarrier() macro from winnt.h.
//
// Below is UMDF's implementation for x86. The macros for other platforms are
// in WudfWdm_private.h.
//
#if defined(_X86_)

#define READ_REGISTER_UCHAR(x) \
    (MemoryBarrier(), *(volatile UCHAR * const)(x))

#define READ_REGISTER_USHORT(x) \
    (MemoryBarrier(), *(volatile USHORT * const)(x))

#define READ_REGISTER_ULONG(x) \
    (MemoryBarrier(), *(volatile ULONG * const)(x))

#define READ_REGISTER_ULONG64(x) \
    (MemoryBarrier(), *(volatile ULONG64 * const)(x))

#define READ_REGISTER_BUFFER_UCHAR(x, y, z) {                           \
    PUCHAR registerBuffer = x;                                          \
    PUCHAR readBuffer = y;                                              \
    ULONG readCount;                                                    \
    MemoryBarrier();                                                    \
    for (readCount = z; readCount--; readBuffer++, registerBuffer++) {  \
        *readBuffer = *(volatile UCHAR * const)(registerBuffer);        \
    }                                                                   \
}

#define READ_REGISTER_BUFFER_USHORT(x, y, z) {                          \
    PUSHORT registerBuffer = x;                                         \
    PUSHORT readBuffer = y;                                             \
    ULONG readCount;                                                    \
    MemoryBarrier();                                                    \
    for (readCount = z; readCount--; readBuffer++, registerBuffer++) {  \
        *readBuffer = *(volatile USHORT * const)(registerBuffer);       \
    }                                                                   \
}

#define READ_REGISTER_BUFFER_ULONG(x, y, z) {                           \
    PULONG registerBuffer = x;                                          \
    PULONG readBuffer = y;                                              \
    ULONG readCount;                                                    \
    MemoryBarrier();                                                    \
    for (readCount = z; readCount--; readBuffer++, registerBuffer++) {  \
        *readBuffer = *(volatile ULONG * const)(registerBuffer);        \
    }                                                                   \
}

#define READ_REGISTER_BUFFER_ULONG64(x, y, z) {                         \
    PULONG64 registerBuffer = x;                                        \
    PULONG64 readBuffer = y;                                            \
    ULONG readCount;                                                    \
    MemoryBarrier();                                                    \
    for (readCount = z; readCount--; readBuffer++, registerBuffer++) {  \
        *readBuffer = *(volatile ULONG64 * const)(registerBuffer);      \
    }                                                                   \
}

#define WRITE_REGISTER_UCHAR(x, y) {    \
    *(volatile UCHAR * const)(x) = y;   \
    MemoryBarrier();                    \
}

#define WRITE_REGISTER_USHORT(x, y) {   \
    *(volatile USHORT * const)(x) = y;  \
    MemoryBarrier();               \
}

#define WRITE_REGISTER_ULONG(x, y) {    \
    *(volatile ULONG * const)(x) = y;   \
    MemoryBarrier();                    \
}

#define WRITE_REGISTER_ULONG64(x, y) {  \
    *(volatile ULONG64 * const)(x) = y; \
    MemoryBarrier();                    \
}

#define WRITE_REGISTER_BUFFER_UCHAR(x, y, z) {                            \
    PUCHAR registerBuffer = x;                                            \
    PUCHAR writeBuffer = y;                                               \
    ULONG writeCount;                                                     \
    for (writeCount = z; writeCount--; writeBuffer++, registerBuffer++) { \
        *(volatile UCHAR * const)(registerBuffer) = *writeBuffer;         \
    }                                                                     \
    MemoryBarrier();                                                      \
}

#define WRITE_REGISTER_BUFFER_USHORT(x, y, z) {                           \
    PUSHORT registerBuffer = x;                                           \
    PUSHORT writeBuffer = y;                                              \
    ULONG writeCount;                                                     \
    for (writeCount = z; writeCount--; writeBuffer++, registerBuffer++) { \
        *(volatile USHORT * const)(registerBuffer) = *writeBuffer;        \
    }                                                                     \
    MemoryBarrier();                                                      \
}

#define WRITE_REGISTER_BUFFER_ULONG(x, y, z) {                            \
    PULONG registerBuffer = x;                                            \
    PULONG writeBuffer = y;                                               \
    ULONG writeCount;                                                     \
    for (writeCount = z; writeCount--; writeBuffer++, registerBuffer++) { \
        *(volatile ULONG * const)(registerBuffer) = *writeBuffer;         \
    }                                                                     \
    MemoryBarrier();                                                      \
}

#define WRITE_REGISTER_BUFFER_ULONG64(x, y, z) {                          \
    PULONG64 registerBuffer = x;                                          \
    PULONG64 writeBuffer = y;                                             \
    ULONG writeCount;                                                     \
    for (writeCount = z; writeCount--; writeBuffer++, registerBuffer++) { \
        *(volatile ULONG64 * const)(registerBuffer) = *writeBuffer;       \
    }                                                                     \
    MemoryBarrier();                                                      \
}

#endif // _X86_

__inline
SIZE_T
FxDevice::ReadRegister(
    __in WDF_DEVICE_HWACCESS_TARGET_SIZE Size,
    __in PVOID Register
    )
{
    SIZE_T value = 0;

    //
    // ETW start event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_READ_FROM_HARDWARE_START(
        WdfDeviceHwAccessTargetTypeRegister, Size, 0);

    switch(Size) {
    case WdfDeviceHwAccessTargetSizeUchar:
        value = READ_REGISTER_UCHAR((PUCHAR)Register);
        break;
    case WdfDeviceHwAccessTargetSizeUshort:
        value = READ_REGISTER_USHORT((PUSHORT)Register);
        break;
    case WdfDeviceHwAccessTargetSizeUlong:
        value = READ_REGISTER_ULONG((PULONG)Register);
        break;
    case WdfDeviceHwAccessTargetSizeUlong64:
#if defined(_WIN64)
        value = READ_REGISTER_ULONG64((PULONG64)Register);
#else
        FX_VERIFY(DRIVER(BadArgument, TODO), CHECK("Invalid call to ULONG64 "
            "hardware access function", FALSE));
#endif
        break;
    default:
        FX_VERIFY(INTERNAL, TRAPMSG("Unexpected"));
        break;
    }

    //
    // ETW end event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_READ_FROM_HARDWARE_END(
        WdfDeviceHwAccessTargetTypeRegister, Size, 0);

    return value;
}

__inline
VOID
FxDevice::ReadRegisterBuffer(
    __in WDF_DEVICE_HWACCESS_TARGET_SIZE Size,
    __in PVOID Register,
    __out_ecount_full(Count) PVOID Buffer,
    __in ULONG Count
    )
{
    //
    // ETW start event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_READ_FROM_HARDWARE_START(
        WdfDeviceHwAccessTargetTypeRegisterBuffer, Size, Count);

    switch(Size) {
    case WdfDeviceHwAccessTargetSizeUchar:
        READ_REGISTER_BUFFER_UCHAR(((PUCHAR)Register), (PUCHAR)Buffer, Count );
        break;
    case WdfDeviceHwAccessTargetSizeUshort:
#pragma prefast(suppress:26000, "The Size parameter dictates the buffer size")
        READ_REGISTER_BUFFER_USHORT(((PUSHORT)Register), (PUSHORT)Buffer, Count );
        break;
    case WdfDeviceHwAccessTargetSizeUlong:
        READ_REGISTER_BUFFER_ULONG(((PULONG)Register), (PULONG)Buffer, Count );
        break;
    case WdfDeviceHwAccessTargetSizeUlong64:
#if defined(_WIN64)
        READ_REGISTER_BUFFER_ULONG64(((PULONG64)Register), (PULONG64)Buffer, Count );
#else
        FX_VERIFY(DRIVER(BadArgument, TODO), CHECK("Invalid call to ULONG64 "
            "hardware access function", FALSE));
#endif
        break;
    default:
        FX_VERIFY(INTERNAL, TRAPMSG("Unexpected"));
        break;
    }

    //
    // ETW start event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_READ_FROM_HARDWARE_END(
        WdfDeviceHwAccessTargetTypeRegisterBuffer, Size, Count);
}

__inline
VOID
FxDevice::WriteRegister(
    __in WDF_DEVICE_HWACCESS_TARGET_SIZE Size,
    __in PVOID Register,
    __in SIZE_T Value
    )
{
    //
    // ETW start event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_WRITE_TO_HARDWARE_START(
        WdfDeviceHwAccessTargetTypeRegister, Size, 0);

    switch(Size) {
    case WdfDeviceHwAccessTargetSizeUchar:
        WRITE_REGISTER_UCHAR((PUCHAR)Register, (UCHAR)Value);
        break;
    case WdfDeviceHwAccessTargetSizeUshort:
        WRITE_REGISTER_USHORT((PUSHORT)Register, (USHORT)Value);
        break;
    case WdfDeviceHwAccessTargetSizeUlong:
        WRITE_REGISTER_ULONG((PULONG)Register, (ULONG)Value);
        break;
    case WdfDeviceHwAccessTargetSizeUlong64:
#if defined(_WIN64)
        WRITE_REGISTER_ULONG64((PULONG64)Register, (ULONG64)Value);
#else
        FX_VERIFY(DRIVER(BadArgument, TODO), CHECK("Invalid call to ULONG64 "
            "hardware access function", FALSE));
#endif
        break;
    default:
        FX_VERIFY(INTERNAL, TRAPMSG("Unexpected"));
        break;
    }

    //
    // ETW start event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_WRITE_TO_HARDWARE_END(
        WdfDeviceHwAccessTargetTypeRegister, Size, 0);
}

__inline
VOID
FxDevice::WriteRegisterBuffer(
    __in WDF_DEVICE_HWACCESS_TARGET_SIZE Size,
    __in PVOID Register,
    __in_ecount(Count) PVOID Buffer,
    __in ULONG Count
    )
{
    //
    // ETW start event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_WRITE_TO_HARDWARE_START(
        WdfDeviceHwAccessTargetTypeRegisterBuffer, Size, Count);

    switch(Size) {
    case WdfDeviceHwAccessTargetSizeUchar:
        WRITE_REGISTER_BUFFER_UCHAR(((PUCHAR)Register), (PUCHAR)Buffer, Count);
        break;
    case WdfDeviceHwAccessTargetSizeUshort:
#pragma prefast(suppress:26000, "The Size parameter dictates the buffer size")
        WRITE_REGISTER_BUFFER_USHORT(((PUSHORT)Register), (PUSHORT)Buffer, Count);
        break;
    case WdfDeviceHwAccessTargetSizeUlong:
        WRITE_REGISTER_BUFFER_ULONG(((PULONG)Register), (PULONG)Buffer, Count);
        break;
    case WdfDeviceHwAccessTargetSizeUlong64:
#if defined(_WIN64)
        WRITE_REGISTER_BUFFER_ULONG64(((PULONG64)Register), (PULONG64)Buffer, Count);
#else
        FX_VERIFY(DRIVER(BadArgument, TODO), CHECK("Invalid call to ULONG64 "
            "hardware access function", FALSE));
#endif
        break;
    default:
        FX_VERIFY(INTERNAL, TRAPMSG("Unexpected"));
        break;
    }

    //
    // ETW start event for perf measurement
    //
    EventWriteEVENT_UMDF_FX_WRITE_TO_HARDWARE_END(
        WdfDeviceHwAccessTargetTypeRegisterBuffer, Size, Count);
}

__inline
FxWdmDeviceExtension*
FxDevice::_GetFxWdmExtension(
    __in MdDeviceObject DeviceObject
    )
{
    return (FxWdmDeviceExtension*)
        ((static_cast<IWudfDevice2*> (DeviceObject))->GetDeviceExtension());
}

__inline
BOOLEAN
FxDevice::IsRemoveLockEnabledForIo(
    VOID
    )
{
   return FALSE;
}

__inline
MdRemoveLock
FxDevice::GetRemoveLock(
    VOID
    )
{
    return &FxDevice::_GetFxWdmExtension(
        GetDeviceObject())->IoRemoveLock;
}

__inline
NTSTATUS
FxDevice::WmiPkgRegister(
    VOID
    )
{
    //
    // WMI doesn't apply for UMDF
    //
    DO_NOTHING();
    return STATUS_SUCCESS;
}

__inline
VOID
FxDevice::WmiPkgDeregister(
    VOID
    )
{
    //
    // WMI doesn't apply for UMDF
    //
    DO_NOTHING();
}

__inline
VOID
FxDevice::WmiPkgCleanup(
    VOID
    )
{
    //
    // WMI doesn't apply for UMDF
    //
    DO_NOTHING();
}

__inline
IWudfDeviceStack*
FxDevice::GetDeviceStack(
    VOID
    )
{
    return m_DevStack;
}

__inline
IWudfDeviceStack2 *
FxDevice::GetDeviceStack2(
    )
{
    IWudfDeviceStack2 *pDeviceStack2;
    HRESULT hrQI;

    hrQI = m_DevStack->QueryInterface(IID_IWudfDeviceStack2,
                                      (PVOID*)&pDeviceStack2);
    FX_VERIFY(INTERNAL, CHECK_QI(hrQI, pDeviceStack2));

    m_DevStack->Release();

    return pDeviceStack2;
}

#endif //_FXDEVICEUM_H_