/******************************Module*Header*******************************\
* Module Name: os.cxx                                                      *
*                                                                          *
* Convenient functions to access the OS interface.                         *
*                                                                          *
* Created: 29-Aug-1989 19:59:05                                            *
* Author: Charles Whitmer [chuckwh]                                        *
*                                                                          *
* Copyright (c) 1989,1990,1991 Microsoft Corporation                       *
\**************************************************************************/

#include "precomp.hxx"

/******************************Public*Routine******************************\
* EngGetProcessHandle
*
* Returns the current thread of the application.
*
* History:
*  24-Jan-1996 -by- Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

HANDLE APIENTRY EngGetProcessHandle()
{
    return (HANDLE) NULL;
}


/******************************Public*Routine******************************\
* hsemCreate
*
* Create critical section.
*
* History:
*  Wed 21-Oct-1992 -by- Patrick Haluptzok [patrickh]
* remove call to Pos wrapper, deallocate on failure.
*
*  Mon 25-Sep-1989 22:37:24 -by- Charles Whitmer [chuckwh]
* Simplified the name.
*
*  09-Sep-1989 -by- Donald Sidoroff [donalds]
* Wrote it.
*
\**************************************************************************/

PERESOURCE hsemCreate()
{
    PERESOURCE resource;

    resource = (PERESOURCE) ExAllocatePoolWithTag(NonPagedPool,
                                                  sizeof(ERESOURCE),
                                                  'mesG');

    if (resource)
    {
        if (!NT_SUCCESS(ExInitializeResourceLite(resource)))
        {
            ExFreePool(resource);
            resource = NULL;
        }
    }

    return resource;
}

/******************************Public*Routine******************************\
* hsemDestroy
*
* Delete Critical section.
*
* History:
*  Wed 21-Oct-1992 -by- Patrick Haluptzok [patrickh]
* Remove wrapper.
*
*  29-Jul-1990 -by- Donald Sidoroff [donalds]
* Wrote it.
\**************************************************************************/

VOID hsemDestroy(PERESOURCE hsem)
{
    ExDeleteResourceLite(hsem);
    ExFreePool(hsem);
}

/******************************Public*Routine******************************\
* EngSetLastError
*
* Saves Error code passed in.
*
* History:
*  Sat 31-Oct-1992 -by- Patrick Haluptzok [patrickh]
* Remove wrapper.
*
*  28-Oct-1992 -by- Bodin Dresevic [BodinD]
* Wrote it.
\**************************************************************************/

VOID  EngSetLastError(ULONG iError)
{
    PTEB pteb = NtCurrentTeb();

    if (pteb)
        pteb->LastErrorValue = iError;

#if DBG
    PSZ psz;

    switch (iError)
    {
    case ERROR_INVALID_HANDLE:
        psz = "ERROR_INVALID_HANDLE";
        break;

    case ERROR_NOT_ENOUGH_MEMORY:
        psz = "ERROR_NOT_ENOUGH_MEMORY";
        break;

    case ERROR_INVALID_PARAMETER:
        psz = "ERROR_INVALID_PARAMETER";
        break;

    case ERROR_BUSY:
        psz = "ERROR_BUSY";
        break;

    case ERROR_ARITHMETIC_OVERFLOW:
        psz = "ERROR_ARITHMETIC_OVERFLOW";
        break;

    case ERROR_INVALID_FLAGS:
        psz = "ERROR_INVALID_FLAGS";
        break;

    case ERROR_CAN_NOT_COMPLETE:
        psz = "ERROR_CAN_NOT_COMPLETE";
        break;

    default:
        psz = "unknown error code";
        break;
    }

    // DbgPrint("GRE Err: %s = 0x%04X\n", psz, (USHORT) iError);
#endif
}

/******************************Public*Routine******************************\
*
* History:
*  27-Jun-1995 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

ULONG APIENTRY EngGetLastError()
{
    ULONG ulError = 0;
    PTEB  pteb    = NtCurrentTeb();

    if (pteb)
        ulError = pteb->LastErrorValue;

    return(ulError);
}

/******************************Public*Routine******************************\
* GreLockDisplay()
*
* History:
*  01-Nov-1994 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

VOID
APIENTRY
GreLockDisplay(
    PDEVICE_LOCK devlock
    )
{
    VACQUIREDEVLOCK(devlock);
}

/******************************Public*Routine******************************\
* GreUnlockDisplay()
*
* History:
*  01-Nov-1994 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

VOID
APIENTRY
GreUnlockDisplay(
    PDEVICE_LOCK devlock
    )
{
    VRELEASEDEVLOCK(devlock);
}

/***************************************************************************\
* EngDebugPrint
*
* History:
*  02-Feb-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\***************************************************************************/

VOID
EngDebugPrint(
    PCHAR StandardPrefix,
    PCHAR DebugMessage,
    va_list ap
    )
{
    char buffer[256];
    int  len;

    //
    // We prepend the STANDARD_DEBUG_PREFIX to each string, and
    // append a new-line character to the end:
    //

    DbgPrint(StandardPrefix);

    vsprintf(buffer, DebugMessage, ap);
    DbgPrint(buffer);

}


/******************************Public*Routine******************************\
* EngDebugBreak()
*
* History:
*  16-Feb-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

VOID
APIENTRY
EngDebugBreak(
    VOID
    )
{
    DbgBreakPoint();
}


/******************************Public*Routine******************************\
* EngAllocMem()
*
* History:
*  27-May-1995 -by-  Tom Zakrajsek [tomzak]
* Added a flags parameter to allow zeroing of memory.
*
*  02-Feb-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

PVOID
EngAllocMem(
    ULONG fl,
    ULONG cj,
    ULONG tag
    )
{
    if (cj >= (PAGE_SIZE * 10000))
    {
        WARNING("EngAllocMem: temp buffer >= 10000 pages");
        return(NULL);
    }

    if (fl & FL_ZERO_MEMORY) {
        return PALLOCMEM(cj,tag);
    } else {
        return PALLOCNOZ(cj,tag);
    }
}


/******************************Public*Routine******************************\
* EngFreeMem()
*
* History:
*  02-Feb-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

VOID
EngFreeMem(
    PVOID pv
    )
{
    if (pv)
    {
        VFREEMEM(pv);
    }

    return;
}

/******************************Public*Routine******************************\
* EngProbeForRead()
*
* History:
*  02-Oct-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

VOID
EngProbeForRead(
    PVOID Address,
    ULONG Length,
    ULONG Alignment
    )
{
    ProbeForRead(Address, Length, Alignment);
}

/******************************Public*Routine******************************\
* EngAllocUserMem()
*
*   This routine allocates a piece of memory for USER mode and locks it
*   down.  A driver must be very careful with this memory as it is only
*   valid for this process.
*
* History:
*  10-Sep-1995 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

PVOID
EngAllocUserMem(
    ULONG cj,
    ULONG tag
    )
{
    NTSTATUS status;
    PVOID    pv = NULL;
    HANDLE   hSecure;

    // add two dwords so we have space to store the hSecure and tag.

    cj += sizeof(ULONG) * 4;



    status = ZwAllocateVirtualMemory(
                    NtCurrentProcess(),
                    &pv,
                    0,
                    &cj,
                    MEM_COMMIT | MEM_RESERVE,
                    PAGE_READWRITE);

    if (NT_SUCCESS(status))
    {
        hSecure = MmSecureVirtualMemory(pv,cj,PAGE_READWRITE);

        if (hSecure)
        {
            ((PULONG)pv)[0] = 'xIDG';
            ((PULONG)pv)[1] = cj;
            ((PULONG)pv)[2] = tag;
            ((PULONG)pv)[3] = (ULONG)hSecure;
            pv = (PBYTE)pv + sizeof(DWORD)*4;
        }
        else
        {
            ZwFreeVirtualMemory(
                    NtCurrentProcess(),
                    &pv,
                    &cj,
                    MEM_RELEASE);
        }
    }

    return(pv);
}

/******************************Public*Routize******************************\
* EngSecureMem()
*
* History:
*  02-Oct-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

HANDLE
APIENTRY
EngSecureMem(
    PVOID Address,
    ULONG Length
    )
{
    return (MmSecureVirtualMemory(Address, Length, PAGE_READWRITE));
}

/******************************Public*Routine******************************\
* EngUnsecureMem()
*
* History:
*  02-Oct-1995 -by-  Andre Vachon [andreva]
* Wrote it.
*
* Note:  Forwarder only - no code needed
\**************************************************************************/


/******************************Public*Routine******************************\
* EngFreeUserMem()
*
* History:
*  10-Sep-1995 -by-  Eric Kutter [erick]
* Wrote it.
\**************************************************************************/

VOID
EngFreeUserMem(
    PVOID pv
    )
{
    if (pv)
    {
        pv = (PBYTE)pv - sizeof(DWORD)*4;

        if (((PULONG)pv)[0] == 'xIDG') // GDIx
        {
            ULONG cj       = ((PULONG)pv)[1];
            HANDLE hSecure = (HANDLE)((PULONG)pv)[3];
            NTSTATUS status;

            MmUnsecureVirtualMemory(hSecure);

            ZwFreeVirtualMemory(
                    NtCurrentProcess(),
                    &pv,
                    &cj,
                    MEM_RELEASE);
        }
        else
        {
            RIP("EngFreeUserMem passed bad pointer\n");
        }
    }

    return;
}



/******************************Public*Routine******************************\
* EngCreateSemaphore()
*
* History:
*  22-Feb-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

HSEMAPHORE
EngCreateSemaphore(
    VOID
    )
{
    return ((HSEMAPHORE)hsemCreate());
}


VOID
EngAcquireSemaphore(
    HSEMAPHORE hsem
    )
{
    VACQUIRESEM((PERESOURCE)hsem);
}


VOID
EngReleaseSemaphore(
    HSEMAPHORE hsem
    )
{
    VRELEASESEM((PERESOURCE)hsem);
}


VOID
EngDeleteSemaphore(
    HSEMAPHORE hsem
    )
{
    hsemDestroy((PERESOURCE)hsem);
}

/******************************Public*Routine******************************\
* EngDeviceIoControl()
*
* History:
*  04-Feb-1995 -by-  Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

NTSTATUS
GreDeviceIoControl(
    HANDLE hDevice,
    DWORD dwIoControlCode,
    LPVOID lpInBuffer,
    DWORD nInBufferSize,
    LPVOID lpOutBuffer,
    DWORD nOutBufferSize,
    LPDWORD lpBytesReturned
    )

{

    NTSTATUS Status;
    IO_STATUS_BLOCK Iosb;
    PIRP pIrp;
    NTSTATUS retStatus = STATUS_NOT_IMPLEMENTED;

    // KEVENT event;
    //
    // KeInitializeEvent(&event,
    //                   SynchronizationEvent,
    //                   FALSE);

    pIrp = IoBuildDeviceIoControlRequest(
               dwIoControlCode,
               (PDEVICE_OBJECT) hDevice,
               lpInBuffer,
               nInBufferSize,
               lpOutBuffer,
               nOutBufferSize,
               FALSE,
               NULL,
               &Iosb);

    if (pIrp)
    {
        Status = IoCallDriver((PDEVICE_OBJECT) hDevice,
                              pIrp);

        // No need to do this since all of our calls are
        // synchronous.
        //
        // Status = KeWaitForSingleObject(&event,
        //                                UserRequest,
        //                                KernelMode,
        //                                TRUE,
        //                                NULL);

        //
        // Since the call is synchronous, the IO is always completed
        // and the Status is the same as the Iosb.Status.
        //

        retStatus = Status;

        *lpBytesReturned = Iosb.Information;
    }

    return (retStatus);

}


DWORD
EngDeviceIoControl(
    HANDLE hDevice,
    DWORD dwIoControlCode,
    LPVOID lpInBuffer,
    DWORD nInBufferSize,
    LPVOID lpOutBuffer,
    DWORD nOutBufferSize,
    LPDWORD lpBytesReturned
    )

{
    DWORD retStatus;
    NTSTATUS Status = GreDeviceIoControl(hDevice,
                                         dwIoControlCode,
                                         lpInBuffer,
                                         nInBufferSize,
                                         lpOutBuffer,
                                         nOutBufferSize,
                                         lpBytesReturned);

    //
    // Do the inverse translation to what the video port does
    // so that we can have the original win32 status codes.
    //
    // Maybe, somehow, we can completely eliminate this double
    // translation - but I don't care for now.  It's just a bit
    // longer on the very odd failiure case
    //

    switch (Status) {

    case STATUS_SUCCESS:
        retStatus = NO_ERROR;
        break;

    case STATUS_NOT_IMPLEMENTED:
        retStatus = ERROR_INVALID_FUNCTION;
        break;

    case STATUS_INSUFFICIENT_RESOURCES:
        retStatus = ERROR_NOT_ENOUGH_MEMORY;
        break;

    case STATUS_INVALID_PARAMETER:
        retStatus = ERROR_INVALID_PARAMETER;
        break;

    case STATUS_BUFFER_TOO_SMALL:
        retStatus = ERROR_INSUFFICIENT_BUFFER;
        break;

    case STATUS_BUFFER_OVERFLOW:
        retStatus = ERROR_MORE_DATA;
        break;

    case STATUS_PENDING:
        retStatus = ERROR_IO_PENDING;
        break;

    case STATUS_DEVICE_DOES_NOT_EXIST:
        retStatus = ERROR_DEV_NOT_EXIST;
        break;

    default:
        retStatus = Status;
        break;

    }


    return retStatus;
}

//
// BUGBUG only temporary.
//

VOID
EngMultiByteToUnicodeN(
    PWSTR UnicodeString,
    ULONG MaxBytesInUnicodeString,
    PULONG BytesInUnicodeString,
    PCHAR MultiByteString,
    ULONG BytesInMultiByteString
    )

{

    RtlMultiByteToUnicodeN(UnicodeString,
                           MaxBytesInUnicodeString,
                           BytesInUnicodeString,
                           MultiByteString,
                           BytesInMultiByteString);
}

VOID
EngUnicodeToMultiByteN(
    PCHAR MultiByteString,
    ULONG MaxBytesInMultiByteString,
    PULONG BytesInMultiByteString,
    PWSTR UnicodeString,
    ULONG BytesInUnicodeString
    )
{
    RtlUnicodeToMultiByteN( MultiByteString,
                            MaxBytesInMultiByteString,
                            BytesInMultiByteString,
                            UnicodeString,
                            BytesInUnicodeString );
}




/******************************Public*Routine******************************\
* EngLoadImage
*
* Loads an image that a display of printers driver can then call to execute
* code
*
* History:
*  20-Sep-1995 -by- Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

HANDLE
APIENTRY
EngLoadImage(
    LPWSTR pwszDriver
    )
{
    BOOL bLoaded;

    return (HANDLE) ldevLoadImage(pwszDriver, TRUE, &bLoaded);
}

/******************************Public*Routine******************************\
* EngFindImageProcAddress
*
* Returns the address of the specified functions in the module.
* Special DrvEnableDriver since it is the entry point.
*
* History:
*  20-Sep-1995 -by- Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

PVOID
APIENTRY
EngFindImageProcAddress(
    HANDLE hModule,
    LPSTR lpProcName
    )
{
    PSYSTEM_GDI_DRIVER_INFORMATION pGdiDriverInfo = ((PLDEV)hModule)->pGdiDriverInfo;

    PULONG NameTableBase;
    ULONG NumberOfNames;
    PULONG AddressTableBase;
    ULONG i;

    NameTableBase = (PULONG)((ULONG)pGdiDriverInfo->ImageAddress +
                    (ULONG)pGdiDriverInfo->ExportSectionPointer->AddressOfNames);
    NumberOfNames = pGdiDriverInfo->ExportSectionPointer->NumberOfNames;

    AddressTableBase = (PULONG)((ULONG)pGdiDriverInfo->ImageAddress +
                       (ULONG)pGdiDriverInfo->ExportSectionPointer->AddressOfFunctions);

    if (!strncmp(lpProcName,
                 "DrvEnableDriver",
                 strlen(lpProcName)))
    {
        return (pGdiDriverInfo->EntryPoint);
    }

    for (i=0; i < NumberOfNames; i++)
    {
        if (!strncmp(lpProcName,
                      (PCHAR) (NameTableBase[i] + (ULONG)pGdiDriverInfo->ImageAddress),
                      strlen(lpProcName)))
        {
            return ((PVOID) ((ULONG)pGdiDriverInfo->ImageAddress +
                              AddressTableBase[i]));
        }
    }

    return NULL;
}

/******************************Public*Routine******************************\
* EngUnoadImage
*
* Unloads an image loaded using EngLoadModule.
*
* History:
*  20-Sep-1995 -by- Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

VOID
APIENTRY
EngUnloadImage(
    HANDLE hModule
    )
{
    ldevUnloadImage((PLDEV)hModule);
}

/******************************Public*Routine******************************\
* EngQueryPerformanceCounter
*
* Queries the performance counter.
*
* It would have been preferable to use 'KeQueryTickCount,' but has a
* resolution of about 10ms on an x86, which is not sufficient for
* getting an accurate measure of the time between vertical blanks, which
* is typically between 8ms and 17ms.
*
* NOTE: Use this routine sparingly, calling it as infrequently as possible!
*       Calling this routine too frequently can degrade I/O performance
*       for the calling driver and for the system as a whole.
*
* History:
*  3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/

VOID
APIENTRY
EngQueryPerformanceCounter(
    LONGLONG    *pPerformanceCount
    )
{
    LARGE_INTEGER li;

    li = KeQueryPerformanceCounter(NULL);

    *pPerformanceCount = *((LONGLONG*) &li);
}


/******************************Public*Routine******************************\
* EngQueryPerformanceFrequency
*
* Queries the resolution of the performance counter.
*
* History:
*  3-Dec-1995 -by- J. Andrew Goossen [andrewgo]
* Wrote it.
\**************************************************************************/

VOID
APIENTRY
EngQueryPerformanceFrequency(
    LONGLONG    *pFrequency
    )
{
    KeQueryPerformanceCounter((LARGE_INTEGER*) pFrequency);
}

/******************************Public*Routine******************************\
* EngCopyMemory
*
* Stub to support broken 64 bit writes on a few machines
*
* History:
*  20-Oct-1995 -by- Andre Vachon [andreva]
* Wrote it.
\**************************************************************************/

#if defined(_MIPS_)


extern "C" {

__cdecl
void *
EngCopyMemory(void * dest, const void * src, size_t length)
{
    return memcpy(dest, src, length);
}


__cdecl
void *
EngFillMemory(void * dest, int fill, size_t length)
{
    return memset(dest, fill, length);
}


__cdecl
void
EngFillMemoryUlong(PVOID dest, ULONG length, ULONG fill)
{
    RtlFillMemoryUlong(dest, length, fill);
}

}



#endif