/*DDK*************************************************************************/
/*                                                                           */
/* COPYRIGHT    Copyright (C) 1995 IBM Corporation                           */
/*                                                                           */
/*    The following IBM OS/2 WARP source code is provided to you solely for  */
/*    the purpose of assisting you in your development of OS/2 WARP device   */
/*    drivers. You may use this code in accordance with the IBM License      */
/*    Agreement provided in the IBM Device Driver Source Kit for OS/2. This  */
/*    Copyright statement may not be removed.                                */
/*                                                                           */
/*****************************************************************************/
#define XGADEBUG
/**********************************************************************/
/*                                                                    */
/*   File            = EDDGPROC                                       */
/*                                                                    */
/*   Description     = Display Device Driver subroutines              */
/*                     EnterDriver                                    */
/*                     ExitDriver                                     */
/*                                                                    */
/*   Function        = Perform entry checks and control driver        */
/*                     semaphore                                      */
/*                                                                    */
/*   Reference       = Device Driver Interface Specification          */
/*                                                                    */
/*                                                                    */
/*   History                                                          */
/*                                                                    */
/*   73175 930820   Torgny Stadler changed dekko major code f7 -> df  */
/*                                                                    */
/**********************************************************************/
#define INCL_DDICOMFLAGS                                    
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DDIMISC
#define INCL_GRE_DEVMISC3
#include <eddinclt.h>

#include <eddfcone.h>

#include <edddtypt.h>
#include <eddftype.h>

#include <eddcextf.h>
#include <eddgextf.h>
#include <eddsextf.h>

#include <semaphor.h>
#include <seamless.h>


/**********************************************************************/
/* The performance test macros / prototypes                           */
/**********************************************************************/
#include <perftest.h>

#ifdef DEKKO
/**********************************************************************/
/* Dekko hooks for IBM's performance analysis                         */
/**********************************************************************/
VOID FAR cdecl Dekko32Trace( ULONG, ULONG, ULONG, PSZ );
#endif /* DEKKO */

#ifdef FIREWALLS
/**********************************************************************/
/* Use these in a firewall to check the softDrawInUse is in a         */
/* consistent state.                                                  */
/**********************************************************************/
extern SHORT                    softDrawInUse;
extern drawFunctionsTable       softDrawTable;
extern drawFunctionsTable       hardDrawTable;
extern pDrawFunctionsTable      pDrawFunctions;
#endif /* FIREWALLS */


#ifdef FIREWALLS
/**********************************************************************/
/* We use these global variables to enable us to check that           */
/* ExitDriver is called with the same options as EnterDriver is for   */
/* up to ten levels of nesting.                                       */
/**********************************************************************/
ULONG fLastOptions[10];
ULONG lLastFunN[10];
ULONG lCallLevel = 0;

/**********************************************************************/
/* The following defines/types/variables help track what functions    */
/* have been recently called when something goes wrong.               */
/**********************************************************************/
#define CALL_HISTORY_LENGTH     100
typedef struct
{
    ULONG   FunN;
    ULONG   Options;
    ULONG   CallLevel;
}   FUNCTION_CALL_DETAILS;
FUNCTION_CALL_DETAILS CallHistory[CALL_HISTORY_LENGTH];
ULONG NextHistoryIndex = 0;
ULONG WrappedThroughTime = 0;

/**********************************************************************/
/* For reference at run/debug-time the size of the CallHistory buffer */
/* is made available.                                                 */
/**********************************************************************/
ULONG CallHistoryLength = CALL_HISTORY_LENGTH;
#endif /* FIREWALLS */

extern USHORT       CurrentSysColState;
extern PRGB2        DirectDeviceDefaultPalette;
extern PCOLORTABLE  DefaultDirectLogicalColorTable;
#ifdef TMP                                                  
extern USHORT       foregroundSession; 
#endif 

ULONG   EnterCount = 0;
// This Became Global For the 8514 call back to reuse DMS32CallBACK
HDC     savedHDC;
// This is neccessary to find the function for DMS32CallBACK
VOID VisRegionNotify32(VOID);

VOID CheckChipState(VOID);

/******************************************************************/
/* EnterDriver is called whenever the driver is entered. Its      */
/* action depends on the flags set in fEnterOptions:              */
/*                                                                */
/*      EDF_EXCLUSIVE   -   Exclusive access required, so get the */
/*                          driver semaphore.                     */
/*      EDF_DC_NULL     -   The DC is invalid and should not be   */
/*                          used.                                 */
/*      EDF_DONT_CLEAN  -   Don't clean the DC if it is dirty.    */
/*                                                                */
/*  The actual processing performed is:                           */
/*                                                                */
/* 1) If exclusive access required, get the driver semaphore.     */
/* 2) If the DC is valid and                                      */
/*       cleaning DC not disabled and                             */
/*       the DC is dirty (ie the clip regions are invalid) THEN   */
/*    a call is made back to the engine via VisRegionCallBack     */
/*    which should then call NotifyClipChanges with the correct   */
/*    clip regions.                                               */
/* 3) If DC is a direct (screen) DC using an LCT and the device   */
/*    default palette has changed then the LCT must be updated    */
/*    so that its physical indexes are correct.                   */
/* 4) If dc is valid and the system colors have changed since     */
/*    this DC last entered the driver then the changes are        */
/*    propagated through the DC's attribute bundles.              */
/*                                                                */
/******************************************************************/
VOID DRIVERCALL XGA_EnterDriver(PDC    pdcEntered,
                                ULONG  FunN,
                                ULONG  fEnterOptions)
{

#ifdef DEKKO
    /******************************************************************/
    /* Dekko hook to start timer for this entry                       */
    /*                                                                */
    /* Note: The LockDevice and UnlockDevice calls are not started    */
    /* here as they do not both call EnterDriver and ExitDriver.  The */
    /* Dekko hooks for these functions are started explicitly in the  */
    /* functions code.                                                */
    /******************************************************************/
    if (((FunN & 0xFF) != (NGreLockDevice   & 0xFF))  &&
        ((FunN & 0xFF) != (NGreUnlockDevice & 0xFF)))
    {
        Dekko32Trace( 0xdf00, (FunN & 0xff), 0, NULL );     /* @73175 */
    }
#endif /* DEKKO */

    /******************************************************************/
    /* Label for a GOTO that appears later.                           */
    /******************************************************************/
START:

    CheckChipState();

    /******************************************************************/
    /* Get the driver semaphore if required.                          */
    /******************************************************************/
    if (fEnterOptions & EDF_EXCLUSIVE)
    {
#ifdef SEAMLESS
        while (RequestDriverSemaphore() != NULL)
        {
            /**********************************************************/
            /* The request for the driver semaphore has timed out.    */
            /* Call the heart beat function.  This will free the      */
            /* semaphore if it is detected that a         VDM has run */
            /* away with it.                                          */
            /**********************************************************/
            SeamlessHeartBeat();
        }
#else  /* ifdef SEAMLESS else */
        /**************************************************************/
        /* RequestDriverSamaphore returns zero if successfull.        */
        /**************************************************************/
        if (RequestDriverSemaphore() != NULL)
        {
#ifdef FIREWALLS
            haltproc();
#endif /* FIREWALLS */
        }
#endif /* ifdef SEAMLESS else */

    }


#ifdef FIREWALLS
    /******************************************************************/
    /* Knowing how many times we have been called helps debugging.     */
    /******************************************************************/
    EnterCount++;

    /******************************************************************/
    /* Keep a history of the last CALL_HISTORY_LENGTH calls so that   */
    /* we can determine what has happened recently when a problem is  */
    /* found.                                                         */
    /******************************************************************/
    CallHistory[NextHistoryIndex].FunN      = FunN;
    CallHistory[NextHistoryIndex].Options   = fEnterOptions;
    CallHistory[NextHistoryIndex].CallLevel = lCallLevel;
    NextHistoryIndex++;
    if (NextHistoryIndex == CALL_HISTORY_LENGTH)
    {
        WrappedThroughTime = 1;
        NextHistoryIndex = 0;
    }

    /******************************************************************/
    /* Record the options so that ExitDriver can check it gets the    */
    /* same options. (lCallLevel is used to access a stack of upto    */
    /* 10 recorded options to allow for nested calls).                */
    /*                                                                */
    /* Note: EDF_IGNORE_TIME is only used on some ExitDriver calls so */
    /*       is ignored for this check.                               */
    /******************************************************************/
    if (lCallLevel < 10)
    {
        fLastOptions[lCallLevel] = fEnterOptions;
        lLastFunN[lCallLevel] = FunN;
    }
    lCallLevel++;

#endif /* FIREWALLS */

#ifdef FIREWALLS
#ifdef DCAF                                                              //@DCAF
    /******************************************************************/ //@DCAF
    /* This is a     firewall - hangs the system when you go into a   */ //@DCAF
    /* full screen. I just remove it!                                 */ //@DCAF
    /******************************************************************/ //@DCAF
#else                                                                    //@DCAF
    /******************************************************************/
    /* Check to see that softDrawInUse has not been left in an        */
    /* inconsistent state. We will just check one of the other vars   */
    /* to see if it is ok. (Other vars may be inconsistent though!)   */
    /******************************************************************/
    if (softDrawInUse)
    {
        if (pDrawFunctions != (pDrawFunctionsTable)softDrawTable)
        {
            haltproc();
        }
    }
    else if (pDrawFunctions != (pDrawFunctionsTable)hardDrawTable)
    {
        haltproc();
    }
#endif /* DCAF */                                                        //@DCAF
#endif /* FIREWALLS */

    /******************************************************************/
    /* set the global PDC to the PDC on which we entered              */
    /******************************************************************/
    if (fEnterOptions & EDF_DC_NULL)
    {
        pdc = NULL;
    }
    else
    {
        pdc = pdcEntered;
    }

    /******************************************************************/
    /* Check for a dirty DC                                           */
    /******************************************************************/
    if (   !(fEnterOptions & EDF_DC_NULL)
        &&  (pdc->DCIIsDirty)
        && !(fEnterOptions & EDF_DONT_CLEAN) )
    {
        /**************************************************************/
        /* Exit the driver and call the window manager.               */
        /**************************************************************/

        savedHDC = pdc->DCIhdc;
        XGA_ExitDriver(pdcEntered, FunN, fEnterOptions);

        #ifndef   _8514
        WinVisRegionNotify(savedHDC);
        #else
        // compiler optimization was not saving savedHDC for the 8514 version !!!
        // compiler     !!!
        if ( savedHDC == NULL ) {
           haltproc();
        }
        else
           DMS32CallBack( (PFN)VisRegionNotify32 );
        #endif

        /**************************************************************/
        /* Go back and try again.                                     */
        /**************************************************************/
        goto START;
    }

    /******************************************************************/
    /* If we are an OD_DIRECT DC using a LCT then we must check that  */
    /* the indexes in the color table are upto date.  (ie the device  */
    /* default palette for the screen may have changed since the      */
    /* indexes were calculated). Note that we have to check to see    */
    /* if we actually have a DC because we often get delete bitmap    */
    /* calls without a DC.                                            */
    /******************************************************************/
    if (   (pdc != NULL)
        && (pdc->DCIDCType == OD_DIRECT)
        && (pdc->DCIColFormat != LCOLF_PALETTE)
        && (pdc->DCIDeviceDefaultPalette != DirectDeviceDefaultPalette) )
    {
        /**************************************************************/
        /* If we are using the default logical color table then we    */
        /* must point to the one that is now being used.  (It will    */
        /* contain the same RGBs but different physical indexes       */
        /* because the device default palette has changed).           */
        /* Otherwise we must recalculate our physical indexes.        */
        /**************************************************************/
        if (pdc->DCIColFormat == LCOLF_DEFAULT)
        {
            pdc->DCIColorTable = DefaultDirectLogicalColorTable;
            pdc->DCIDeviceDefaultPalette = DirectDeviceDefaultPalette;
        }
        else if (pdc->DCIColFormat == LCOLF_RGB)
        {
            pdc->DCIDeviceDefaultPalette = DirectDeviceDefaultPalette;
        }
        else
        {
            /**********************************************************/
            /* This function will recalculte the physical indexes     */
            /* into the current device default palette and set        */
            /* DCIDeviceDefaultPalette.                               */
            /**********************************************************/
            CalculateColorTablePhysicalIndices();
        }
    }

    /******************************************************************/
    /* Check whether the system colors have changed since this DC     */
    /* last entered the driver.                                       */
    /******************************************************************/
    if (   !(fEnterOptions & EDF_DC_NULL)
        && (pdc->DCISysColState != CurrentSysColState) )
    {
        /**************************************************************/
        /* System colors have changed, so we need to propagate the    */
        /* changes through the LCT and the attribute bundles.         */
        /**************************************************************/
        eddc_PropagateSysColorChanges();
    }

#ifdef INCL_PERFTEST
    /******************************************************************/
    /* Start timer for this call. (INCL_PERFTEST needs to be defined  */
    /* for this macro to call the timing routines in perftest.obj)    */
    /*                                                                */
    /* The bottom byte of the function number is used as it is        */
    /* guarenteed to be unique.                                       */
    /*                                                                */
    /* We dont want timings for LockDevice or UnlockDevice.           */
    /******************************************************************/
    /******************************************************************/
    /* Can't handle calls that weren't exclusive...                   */
    /******************************************************************/
    if (fEnterOptions & EDF_EXCLUSIVE)
    {
        if (((FunN & 0xFF) != (NGreLockDevice   & 0xFF))  &&
            ((FunN & 0xFF) != (NGreUnlockDevice & 0xFF)))
        {
            PERFSTART((USHORT)(FunN & 0xff));
        }
    }
#endif /* INCL_PERFTEST */

#ifdef TMP                                                  
    /******************************************************************/
    /* Temp     fix for PMGRE. Currently, PMGRE requires display      */
    /* driver to draw something even if a process makes the request   */
    /* is in background session and not visible. It causes system     */
    /* TRAP, so we have to check it and make walk around for it.      */
    /*                                                                */
    /*                                   PTR JD20/JS04818  92/Dec/07  */
    /******************************************************************/
    if (!foregroundSession &&
        (pdc != NULL) &&
        (pdc->DCIDCType == OD_DIRECT) &&
        ((pdc->DCICommandMask) & ((USHORT)(COM_DRAW >> 16))) &&
        ((pdc->DCIClipNum != 0) || (pdc->DCIEngineClips != 0)))
    {
        pdc->DCICommandMask &= ~((USHORT)(COM_DRAW >> 16));
        pdc->DCIClipNum = 0;
        pdc->DCIEngineClips = 0;
    }
#endif 
}

/**********************************************************************/
/* ExitDriver is called when the driver is exited.  The options       */
/* passed should be the same as the options passed to EnterDriver.    */
/*                                                                    */
/* Processing performed:                                              */
/*                                                                    */
/* 1) If EDF_EXCLUSIVE set, release the driver semaphore.             */
/*                                                                    */
/**********************************************************************/
VOID DRIVERCALL XGA_ExitDriver(PDC     pdcExit,
                               ULONG   FunN,
                               ULONG   fExitOptions)
{
    /******************************************************************/
    /* Avoid compiler warnings.                                       */
    /******************************************************************/
    IgnoreParam(pdcExit);


#ifdef INCL_PERFTEST
    /******************************************************************/
    /* Stop timings for this call.  Functions LockDevice and          */
    /* UnLockDevice are not timed.                                    */
    /******************************************************************/
    if (fExitOptions & EDF_EXCLUSIVE)
    {
        if (((FunN & 0xFF) != (NGreLockDevice   & 0xFF))  &&
            ((FunN & 0xFF) != (NGreUnlockDevice & 0xFF)))
        {
            if (!(fExitOptions & EDF_IGNORE_TIME))
            {
                /******************************************************/
                /* We want to record this call (if EDF_IGNORE_TIME is */
                /* set we are probably passing the call back to the   */
                /* engine and don't want to record the time           */
                /* information)                                       */
                /******************************************************/
                PERFEND((USHORT)(FunN & 0xff));
            }
        }
    }
#endif /* INCL_PERFTEST */

#ifdef FIREWALLS
    /******************************************************************/
    /* If this was not a nested call, check that we have been passed  */
    /* the same options as were passed to EnterDriver.                */
    /* Note : The performance flag EDF_IGNORE_TIME is not included in */
    /*        this test as this can be different between Enter / Exit.*/
    /******************************************************************/
    lCallLevel--;
    if (lCallLevel < 10)
    {
        if (   ((fExitOptions & ~EDF_IGNORE_TIME) !=
                (fLastOptions[lCallLevel] & ~EDF_IGNORE_TIME))
            || ((FunN& 0xFFFF) != (lLastFunN[lCallLevel] & 0xFFFF)) )
        {
            /**********************************************************/
            /* The options or function number have changed between    */
            /* the EnterDriver and ExitDriver calls.  This should not */
            /* be allowed except in the case of lock and unlock       */
            /* device (where the lock calls EnterDriver and the unlock*/
            /* call ExitDriver).                                      */
            /**********************************************************/
            if (   ((FunN & 0xFFFF) != NGreUnlockDevice)
                || ((lLastFunN[lCallLevel] & 0xFFFF) != NGreLockDevice))
            {
                haltproc();
            }
        }
    }
#endif /* FIREWALLS */

    if (fExitOptions & EDF_EXCLUSIVE)
    {
        ReleaseDriverSemaphore();
    }

#ifdef DEKKO
    /******************************************************************/
    /* Dekko hook to stop timer for this entry                        */
    /*                                                                */
    /* Note: The LockDevice and UnlockDevice calls are not processed  */
    /* here as they do not both call EnterDriver and ExitDriver.  The */
    /* Dekko hooks for these functions are started and stopped        */
    /* explicitly in the functions code.                              */
    /******************************************************************/
    if (((FunN & 0xFF) != (NGreLockDevice   & 0xFF))  &&
        ((FunN & 0xFF) != (NGreUnlockDevice & 0xFF)))
    {
        Dekko32Trace( 0xdf80, (FunN & 0xff), 0, NULL );     /* @73175 */
    }
#endif /* DEKKO */

}

#ifdef   _8514
VOID VisRegionNotify32(VOID)
{
    WinVisRegionNotify(savedHDC);
}
#endif
