/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**************************************************************************
 *
 * SOURCE FILE NAME = VVSYSRQ.C
 *
 * DESCRIPTIVE NAME = Virtual Video System Request Processing
 *
 *
 * VERSION = V2.0
 *
 * DATE      11/10/88
 *
 * DESCRIPTION  This module contains the VVD's DosRequestVDD support.
 *
 *
 * FUNCTIONS    VVSysReqProc()            DosRequestVDD router
 *              vvSysSetAccess()          Request/release VDM event access
 *              vvSysSetFocus()           Set/clear focus
 *              vvSysSetLock()            Freeze/thaw VDM
 *              vvSysQueryMode()          Query mode data
 *              vvSysQueryCursor()        Query cursor data
 *              vvSysQueryPalette()       Query color data
 *              vvSysCopyLVB()            Copy rectangle from VDM LVB
 *              vvSysCopyBitmap()         Copy rectangle from VDM bitmap
 *              vvSysWaitEvent()          Wait for next VDM video event
 *              vvSysControlEvent()       Abort event thread
 *              vvSysSetDisplayReq()      Set PM display requirements
 *              vvSysRequestMemory()      Request off-screen memory for use by PM
 *              vvSysFreeMemory()         Release off-screen memory for use by PM
 *              vvSysRequestController()  Request video controller access for PM
 *              vvSysFreeController()     Release video controller access for PM
 *              vvSysQueryVRAMStatus()    Determine VRAM status
 *              vvSysSetOEMFlag()         Set OEM Flag
 *
 * NOTES
 *
 *
 * STRUCTURES
 *
 * EXTERNAL REFERENCES
 *
 * EXTERNAL FUNCTIONS
 *
*/


#include <mvdm.h>
#include <vvd.h>
#include "vvdp.h"

#ifdef   VDDSTRICT
MODNAME = __FILE__;
#endif
#define  INCL_DOSERRORS
#include <bseerr.h>
extern ULONG flVVD;
extern PBOOL pflVDMBusy;               /* offset to WakeIdle flag           */
extern PBYTE pfOEMFlags;                         /*                         */
#pragma  BEGIN_SWAP_DATA
extern HVDM hvdmFocus;
extern HVDM hvdmUpdate;
extern PSYSFUN apfnSysReq[];
extern HMXSEM hmxWindowedEvent;
extern HEVSEM hevWindowedEvent;
extern ULONG nWindowedVDMs;
extern ULONG nPMWindowedVDMs;
extern ULONG nMinWindowedVDMs;
extern HHOOK hhookEventTimer;
#pragma  END_SWAP_DATA
#pragma  BEGIN_SWAP_CODE

/***************************************************************************
 *
 * FUNCTION NAME = VVSysReqProc()
 *
 * DESCRIPTION   = DosRequestVDD router
 *
 *                 This subroutine is registered during VDD initialization
 *                 via VDHRegisterVDD, and receives requests from OS/2
 *                 applications.
 *
 * INPUT         = sgid   == screen group
 *                 ulFunc == function code
 *                 nbIn   -> input buffer size (0 if none)
 *                 pIn    -> input buffer
 *                 nbOut  -> output buffer size (0 if none)
 *                 pOut   -> output buffer
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid function, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 * PSEUDO-CODE
 *                 based on function code
 *                   route to appropriate worker function
 *
 ****************************************************************************/

LONG EXPENTRY VVSysReqProc(SGID sgid,ULONG ulFunc,ULONG nbIn,PVOID pIn,ULONG
                            nbOut,PVOID pOut)
{
  HVDM hvdm = INVALID_HVDM;


  if (sgid)
  {
    hvdm = VDHHandleFromSGID(sgid);

    if (hvdm)
    {

      /*
      ** If the mode has changed, force the caller to get new mode data
      */

      if (pVDMData(hvdm)->flVDMVideo&VDM_MODECHANGED && ulFunc >
         VVDSYSREQ_QUERYMODE && ulFunc <= VVDSYSREQ_COPYBITMAP)
        return  ERROR_INVALID_DATA;

      /*
      ** If we don't own the device OR the display for this VDM
      ** when we get certain requests, then pass the requests on
      */

      if (!(pVDMData(hvdm)->flVDMXVideo&VDMX_DEVOWNER) || !(pVDMData(hvdm)->
         flVDMXVideo&VDMX_DSPOWNER) && ulFunc >= VVDSYSREQ_QUERYMODE && ulFunc
         <= VVDSYSREQ_COPYBITMAP)
      {
        return  VDDREQ_PASS;                     /* pass control to
                                                    cooperating VDD         */
      }
    }

    else

      hvdm = INVALID_HVDM;
  }

  if (ulFunc > VVDSYSREQ_MAX)
    return  ERROR_INVALID_FUNCTION;

  else

    return (apfnSysReq[--ulFunc])(hvdm,
                                  nbIn,
                                  pIn,
                                  nbOut,
                                  pOut);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysSetAccess()
 *
 * DESCRIPTION   = Request/release VDM event access
 *
 *                 This subroutine processes the VVDSYSREQ_SETACCESS
 *                 function, and establishes (or relinquishes) exclusive
 *                 access for the caller to any and all video events posted
 *                 for the specified VDM.
 *
 * INPUT         = hvdm -> VDM
 *                 fSet == TRUE to request access, FALSE to release
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, access denied, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysSetAccess(HVDM hvdm,ULONG fSet,PVOID p1,ULONG ul2,PVOID p2
)
{
  LONG rc = NO_ERROR;
  register PVDMDATA pvd = pVDMData(hvdm);

  /*
  ** If the handle is invalid, we let it slide, because this
  ** call could well have been issued for a VDM that is now dead
  */

  if (hvdm != INVALID_HVDM)
  {

    /*
    ** The WindowedState semaphore is a subset of the VDMState, and
    ** allows other sections of code to prevent the windowed state
    ** from changing without taking locking the entire VDMState; locking
    ** the entire state is a problem for some sections of code
    ** (eg, vvint10.asm) because it prevents us from adding and flushing
    ** window events .
    */

    RequestMutexSem(pvd->hmxWindowedState);
    RequestMutexSem(pvd->hmxVideoState);
    RequestMutexSem(hmxWindowedEvent);

    if (fSet)
    {

      /*
      ** Making VDM windowed...
      */

      if (pvd->pidVideo)
        rc = ERROR_ACCESS_DENIED;

      else

        if (pvd->flVDMVideo&VDM_WINDOWED)

          rc = ERROR_INVALID_FUNCTION;

        else
        {
          pvd->pidVideo = VDHQuerySysValue(NULL,
                                           VDHLSV_PID);
          pvd->flVDMVideo |= VDM_WINDOWED;
          pvd->flVDMVideo &= ~VDM_PMWINDOWED;

          /*
          ** Set special flag indicating we are being windowed
          ** by PM, and so we do not need to post events in bgnd
          */

          if (fSet == ACCESS_PMREQUEST)
          {
            pvd->flVDMVideo |= VDM_PMWINDOWED;
            nPMWindowedVDMs++;
          }

          if (!(pvd->flVDMXVideo&VDMX_SEAMLESS)) /*                         */

            if (!nWindowedVDMs++ || nWindowedVDMs != nMinWindowedVDMs)
            {
              VDHDisarmTimerHook(hhookEventTimer);
              VDHArmTimerHook(hhookEventTimer,
                              TIMER_CHECK,
                              VDH_TIMER_GLOBAL_CONTEXT);/*                  */
              flVVD |= VVD_TIMERARMED;           /*                         */
            }

          /*
          ** Kick some current events into the queue now, so that
          ** when the Shield next asks for new mode and cursor data,
          ** the Event structures have something useful in them
          */

          if (pvd->flVDMVideo&VDM_STATEINIT)
            vvUpdateScreenState(pvd->hvdmVideo,
                                TRUE);
        }
    }

    else
    {

      /*
      ** Making VDM non-windowed...
      */

      if (!(pvd->flVDMVideo&VDM_WINDOWED))
        rc = ERROR_INVALID_FUNCTION;

      else

        if (pvd->pidVideo != VDHQuerySysValue(NULL,
                                              VDHLSV_PID))

          rc = ERROR_ACCESS_DENIED;

        else
        {
          pvd->pidVideo = 0;

          if (!(pvd->flVDMXVideo&VDMX_SEAMLESS)) /*                         */
            nWindowedVDMs--;

          if (pvd->flVDMVideo&VDM_PMWINDOWED)
            nPMWindowedVDMs--;

          if (pvd->flVDMVideo&VDM_MINIMIZED)
            nMinWindowedVDMs--;
          pvd->flVDMVideo &= ~(VDM_WINDOWED|VDM_PMWINDOWED|VDM_MINIMIZED);

          /*
          ** If any threads were waiting for event(s) to be flushed,
          ** they may not get flushed now, since the VDM is no longer
          ** being windowed, so detect and eliminate this condition
          */

          vvFlushEventNotify(hvdm,
                             0);
        }
    }

    /*
    ** See if we should allocate/free a shadow buffer
    */

    vvAllocShadowBuffer(hvdm);
    ReleaseMutexSem(hmxWindowedEvent);
    ReleaseMutexSem(pvd->hmxVideoState);
    ReleaseMutexSem(pvd->hmxWindowedState);
  }
  return  rc;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysSetFocus()
 *
 * DESCRIPTION   = Set/clear focus
 *
 *                 This subroutine processes the VVDSYSREQ_SETFOCUS
 *                 function, and sets or clears focus flag for the
 *                 specified VDM.
 *
 * INPUT         = hvdm -> VDM
 *                 fSet == TRUE to set focus, FALSE to clear
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, lock overflow, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysSetFocus(HVDM hvdm,ULONG fSet,PVOID p1,ULONG ul2,PVOID p2)
{

  /*
  ** If the handle is invalid, we let it slide, because this
  ** call could well have been issued for a VDM that is now dead
  */

  if (hvdm != INVALID_HVDM)
  {

    if (pVDMData(hvdm)->pidVideo != VDHQuerySysValue(NULL,
                                                     VDHLSV_PID))
      return  ERROR_ACCESS_DENIED;


    if (fSet)
      hvdmFocus = hvdm;

    else
      hvdmFocus = NULL;
    SETBIT(pVDMData(hvdm)->flVDMVideo,
           VDM_FOCUS,
           fSet);
  }
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysSetLock()
 *
 * DESCRIPTION   = Freeze/thaw VDM
 *
 *                 This subroutine processes the VVDSYSREQ_SETLOCK function,
 *                 and freezes or thaws the VDM as specified.
 *
 * INPUT         = hvdm -> VDM
 *                 fSet == TRUE to freeze, FALSE to thaw
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, lock overflow, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysSetLock(HVDM hvdm,ULONG fSet,PVOID p1,ULONG ul2,PVOID p2)
{


  if( hvdm == INVALID_HVDM )                              //          
    return  ERROR_INVALID_PARAMETER;                      //          

  if (!fSet)
    VDHThawVDM(hvdm);

  else
  {

    if (!VDHFreezeVDM(hvdm))
      return  VDHGetError();
  }
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysQueryMode()
 *
 * DESCRIPTION   = Query mode data
 *
 *                 This subroutine processes the VVDSYSREQ_QUERYMODE
 *                 function, filling in the given VVMODE structure.
 *
 * INPUT         = hvdm -> VDM
 *                 pvvm -> video mode packet
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysQueryMode(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,PVVMODE
                               pvvmDst)
{
  HLOCK hlvvm;
  LONG rc = NO_ERROR;
  PVVMODE pvvmSrc;
  register PVDMDATA pvd = pVDMData(hvdm);


  if (ul2 < sizeof(VVMODE) || !pvvmDst || hvdm == INVALID_HVDM)
    return  ERROR_INVALID_PARAMETER;

  RequestMutexSem(pvd->hmxVideoState);

  if (!(pvd->flVDMVideo&VDM_STATEINIT))
    rc = ERROR_NOT_READY;

  else
  {
    hlvvm = VDHLockMem(pvvmDst,
                       sizeof(VVMODE),
                       VDHLM_WRITE,              /*                         */
                       (struct pagelist_s *)-1,  /*                         */
                       (ULONG *)-1);             /*                         */

    if (!hlvvm)
      rc = VDHGetError();

    else
    {
      pvvmSrc = &pvd->vvMode;

      if ((pvd->flVDMVideo & (VDM_WINDOWED | VDM_MODECHANGED)) ==
                             (VDM_WINDOWED | VDM_MODECHANGED)) /*           */
        pvvmSrc = &pvd->vvModeEvent;
      *pvvmDst = *pvvmSrc;
      VDHUnlockMem(hlvvm);
      pvd->flVDMVideo &= ~VDM_MODECHANGED;
      vvDeleteEvent(hvdm,
                    VVDEVENT_MODE,
                    0);
    }
  }
  ReleaseMutexSem(pvd->hmxVideoState);
  return  rc;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysQueryCursor()
 *
 * DESCRIPTION   = Query cursor data
 *
 *                 This subroutine processes the VVDSYSREQ_QUERYCURSOR
 *                 function, filling in the given VVCURSOR structure.
 *
 * INPUT         = hvdm -> VDM
 *                 pvvc -> query cursor packet
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysQueryCursor(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,
                                 PVVCURSOR pvvcDst)
{
  HLOCK hlvvc;
  LONG rc = NO_ERROR;
  PVVCURSOR pvvcSrc;
  register PVDMDATA pvd = pVDMData(hvdm);


  if (ul2 < sizeof(VVCURSOR) || !pvvcDst || hvdm == INVALID_HVDM)
    return  ERROR_INVALID_PARAMETER;

  RequestMutexSem(pvd->hmxVideoState);

  if (!(pvd->flVDMVideo&VDM_STATEINIT))
    rc = ERROR_NOT_READY;

  else
  {
    hlvvc = VDHLockMem(pvvcDst,
                       sizeof(VVCURSOR),
                       VDHLM_WRITE,              /*                         */
                       (struct pagelist_s *)-1,  /*                         */
                       (ULONG *)-1);             /*                         */

    if (!hlvvc)
      rc = VDHGetError();

    else
    {
      pvvcSrc = &pvd->vvCursor;

      if (pvd->flVDMVideo&VDM_WINDOWED)
        pvvcSrc = &pvd->vvCursorEvent;
      *pvvcDst = *pvvcSrc;
      VDHUnlockMem(hlvvc);
      vvDeleteEvent(hvdm,
                    VVDEVENT_CURSOR,
                    0);
    }
  }
  ReleaseMutexSem(pvd->hmxVideoState);
  return  rc;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysQueryPalette()
 *
 * DESCRIPTION   = Query color data
 *
 *                 This subroutine processes the VVDSYSREQ_QUERYPALETTE
 *                 function, filling in the given RGB array.
 *
 * INPUT         = hvdm -> VDM
 *                 prgb -> RGB array
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysQueryPalette(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,PRGB
                                  prgb)
{
  HLOCK hlrgb;
  LONG rc = NO_ERROR;
  ULONG nColors;
  register PVDMDATA pvd = pVDMData(hvdm);


  if (hvdm == INVALID_HVDM)
    return  ERROR_INVALID_PARAMETER;

  RequestMutexSem(pvd->hmxVideoState);

  /*
  ** Make sure the VDM has a valid state (ie, initial INT 10h complete)
  */

  if (!(pvd->flVDMVideo&VDM_STATEINIT) || (pvd->flVDMVideo&VDM_MODECHANGING))
    rc = ERROR_NOT_READY;

  else
  {
    nColors = 16;

    if (pvd->vvMode.vvm_ulFormat != FORMAT_CGA)
      nColors = 1 << pvd->vvMode.vvm_nBitCount;

    if (!prgb || ul2 < nColors *sizeof(RGB))
      rc = ERROR_INVALID_PARAMETER;

    else
    {
      hlrgb = VDHLockMem(prgb,
                         ul2,
                         VDHLM_WRITE,             /*                        */
                         (struct pagelist_s *)-1, /*                        */
                         (ULONG *)-1);            /*                        */

      if (!hlrgb)
        rc = VDHGetError();

      else
      {
        vvConvertPalette(hvdm,
                         nColors,
                         prgb);
        VDHUnlockMem(hlrgb);
        vvDeleteEvent(hvdm,
                      VVDEVENT_PALETTE,
                      0);
      }
    }
  }
  ReleaseMutexSem(pvd->hmxVideoState);
  return  rc;
}
/***************************************************************************
 *
 * FUNCTION NAME = vvValidateBufferForMode()
 *
 * DESCRIPTION   = It validates that allocated buffers do fit the mode.
 *
 * INPUT         = hvdm   -> VDM
 *
 * OUTPUT        = FALSE if buffer does not validate
 *                 TRUE if buffer validates.
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/
BOOL PRIVENTRY vvValidateBufferForMode(HVDM hvdm)
{
  register PVDMDATA pvd = pVDMData(hvdm);
  ULONG ulBufferBytes = 0;
  INT iBank= 0, iPlane, iMaxBanks = MAX_VGA_BANK;
  if (pvd->mstateVideo <= MEMORY_FONT)
    return(!!pvd->apPlane[BANK0][PLANE0]);    //must have plane 0
#ifdef SVGA
  vvSVGAMaxBanks(hvdm); /* make sure the mode is evaluated before we do this            */
  iMaxBanks = MAX_BANKS;
  if (pvd->flVDMXVideo & VDMX_ENHANCEDMODE)
    ulBufferBytes = pvd->ulLinearPages*PAGESIZE;
  else
#endif
  if (pvd->mstateVideo >= MEMORY_FONT)
  {
    for (iBank = 0;iBank < iMaxBanks;iBank++)
     for (iPlane = 0;iPlane < MAX_PLANES;iPlane++)
       ulBufferBytes += (pvd->anpgPlane[iBank][iPlane]*PAGESIZE);
  }
  return(ulBufferBytes >= pvd->nModeBytes);
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysCopyLVB()
 *
 * DESCRIPTION   = Copy rectangle from VDM LVB
 *
 *                 This subroutine processes the VVDSYSREQ_COPYLVB function.
 *
 *                  This routine no longer relies on page faults being
 *                  serviced for a VDM, since we copy the LVB directly from
 *                  the our virtual buffer(s) and zero/blank-fill any region
 *                  that hasn't been allocated yet.
 *
 *
 * INPUT         = hvdm -> VDM
 *                 ul1  == size of rect. desc.
 *                 prcl -> rectangle description
 *                 ul2  == size of shadow LVB buffer
 *                 pb   -> shadow LVB buffer
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysCopyLVB(HVDM hvdm,ULONG ul1,PRECTL prcl,ULONG ul2,PBYTE
                             pblvb)
{
  HLOCK hlrcl,hllvb;
  LONG rc = NO_ERROR;
  LONG yValid,yInvalid,xValid,nbValid;
  RECTL rclUpper,rclLeft,rclRight,rclLower;
  register PVDMDATA pvd = pVDMData(hvdm);


  if (ul1 < sizeof(RECTL) || !prcl || !ul2 || hvdm == INVALID_HVDM)/*          */
    return  ERROR_INVALID_PARAMETER;


  if (pvd->mstateVideo > MEMORY_FONT)
    return  ERROR_BAD_FORMAT;

  /*
  ** Don't let mode data change underneath us
  */

  RequestMutexSem(pvd->hmxVideoState);

  /*
  ** Make sure the VDM has a valid state (ie, initial INT 10h complete)
  */

  if (!(pvd->flVDMVideo&VDM_STATEINIT) || (pvd->flVDMVideo&VDM_MODECHANGING) ||
      (pvd->flVDMVideo & VDM_DYING))    /*            */
    rc = ERROR_NOT_READY;

  else
  {

    /*
    ** Copy input rect (without segment loads, too!)
    */

    hlrcl = VDHLockMem(prcl,
                       sizeof(RECTL),
                       VDHLM_READ,               /*                         */
                       (struct pagelist_s *)-1,  /*                         */
                       (ULONG *)-1);             /*                         */

    if (!hlrcl)
      rc = VDHGetError();

    else
    {
      *(PRECTL)SSToDS(&rclUpper) = *prcl;
      VDHUnlockMem(hlrcl);

      /*
      ** Full-size COPYLVB should reset LVB, SCROLL and STRING events
      */

      if (rclUpper.yTop == 0 && rclUpper.xLeft == 0 && rclUpper.yBottom ==
         pvd->vvMode.vvm_nRows-1 && rclUpper.xRight == pvd->vvMode.vvm_nCols-1
         )
      {
        vvFlushPages(hvdm);

        /* vvDeleteEvent(hvdm, VVDEVENT_SCROLL, 0);                   */
        /* vvDeleteEvent(hvdm, VVDEVENT_STRING, 0);                   */

      }

      /*
      ** Now we have to break the request up into 1-4 rectangles:  the
      ** upper rectangle must straddle only valid pages, the left rectangle
      ** may straddle a partial line up to the end of the last valid page, the
      ** right rectangle may straddle a partial blank line past the end of
      ** the last valid page, and the lower rectangle may describe the
      ** rest of the requested area past the last valid page -- all this
      ** is because page breaks don't fall on nice rectangle boundaries!
      */

      rclLeft.yTop = rclRight.yTop = rclLower.yTop = -1;
      nbValid = pvd->anpgPlane[BANK0][PLANE0]*PAGESIZE-pvd->offPhysVRAMVisible
         ;                                       /*                         */

      /*
      ** Compute yValid;  if -1, then there are no valid lines;
      ** otherwise, yValid is the last completely valid line
      */

      yValid = nbValid/(LONG)(pvd->vvMode.vvm_nCols *2)-1;

      if (yValid < 0)
        yValid = -1;

      /*
      ** Compute xValid;  if -1, then there is no partial line;
      ** otherwise, it's last valid col on last partially valid line
      */

      xValid = (nbValid-(yValid+1)*(LONG)(pvd->vvMode.vvm_nCols)*2)/2-1;

      /*
      ** See if the rectangle dips down into the invalid region
      */

      if (rclUpper.yBottom > yValid)
      {

        /*
        ** Compute yInvalid (the first completely invalid line)
        */

        yInvalid = yValid+(xValid >= 0)+1;

        /*
        ** See if the rectangle is completely invalid
        */

        if (rclUpper.yTop >= yInvalid)
        {

          /*
          ** Copy upper rect to lower (without segment loads, too!)
          */

          *(PRECTL)SSToDS(&rclLower) = *(PRECTL)SSToDS(&rclUpper);
          rclUpper.yTop = -1;
        }

        else
        {

          /*
          ** See if rectangle requires left subcomponent
          */

          if (rclUpper.xLeft <= xValid)
          {
            rclLeft.xLeft = rclUpper.xLeft;
            rclLeft.xRight = xValid;
            rclLeft.yTop = rclLeft.yBottom = yValid+1;
          }

          /*
          ** See if rectangle requires right subcomponent
          */

          if (rclUpper.xRight > xValid)
          {
            rclRight.xLeft = xValid+1;
            rclRight.xRight = rclUpper.xRight;
            rclRight.yTop = rclRight.yBottom = yValid+1;
          }

          /*
          ** See if rectangle requires lower subcomponent
          */

          if (rclUpper.yBottom >= yInvalid)
          {
            rclLower.xLeft = rclUpper.xLeft;
            rclLower.xRight = rclUpper.xRight;
            rclLower.yTop = yInvalid;
            rclLower.yBottom = rclUpper.yBottom;
          }

          /*
          ** Note that this may now invalidate the upper rectangle if
          ** its top was > yValid, but the copy routines handle that
          */

          rclUpper.yBottom = yValid;
        }
      }
      hllvb = VDHLockMem(pblvb,
                         ul2,
                         VDHLM_WRITE,            /*                         */
                         (struct pagelist_s *)-1, /*                        */
                         (ULONG *)-1);           /*                         */

      if (!hllvb)
        rc = VDHGetError();

      else
      {

        /*
        ** FLAG:  -- These routines also need ul2, so that
        **           they can verify the buffer is large enough
        */

        if (rclUpper.yTop >= 0)
          vvCopyLVBToLVB(hvdm,
                         SSToDS(&rclUpper),
                         pblvb,
                         ul2);                   /*                         */

        if (rclLeft.yTop >= 0)
          vvCopyLVBToLVB(hvdm,
                         SSToDS(&rclLeft),
                         pblvb,
                         ul2);                   /*                         */

        if (rclRight.yTop >= 0)
          vvCopyBlankToLVB(hvdm,
                           SSToDS(&rclRight),
                           pblvb,
                           ul2);                 /*                         */

        if (rclLower.yTop >= 0)
          vvCopyBlankToLVB(hvdm,
                           SSToDS(&rclLower),
                           pblvb,
                           ul2);                 /*                         */
        VDHUnlockMem(hllvb);
      }
    }
  }
  ReleaseMutexSem(pvd->hmxVideoState);
  return  rc;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysCopyBitmap()
 *
 * DESCRIPTION   = Copy rectangle from VDM bitmap
 *
 *                 This subroutine processes the VVDSYSREQ_COPYBITMAP
 *                 function.
 *
 * INPUT         = hvdm   -> VDM
 *                 ul1    == size of rect. desc.
 *                 pvvr   -> DD format and rectangle
 *                 ul2    == size of shadow LVB buffer
 *                 pb     -> shadow LVB buffer
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysCopyBitmap(HVDM hvdm,ULONG ul1,register PVVRECT pvvr,ULONG
                                ul2,register PBYTE pblvb)
{
  HLOCK hlvvr,hllvb;
  LONG rc = NO_ERROR;
  BOOL fSuccess;
  register PVDMDATA pvd = pVDMData(hvdm);


  if (ul1 < sizeof(VVRECT) || !pvvr || hvdm == INVALID_HVDM)
    return  ERROR_INVALID_PARAMETER;

  hlvvr = VDHLockMem(pvvr,
                     sizeof(VVRECT),
                     VDHLM_READ,                 /*                         */
                     (struct pagelist_s *)-1,    /*                         */
                     (ULONG *)-1);               /*                         */

  if (!hlvvr)
    return  VDHGetError();

#ifdef   MONOCGA

  if (pvvr->vvr_ulDDFormat || pvd->mstateVideo <= MEMORY_FONT)
    rc = ERROR_BAD_FORMAT;
#endif

#ifdef   EGAVGA

  if (pvvr->vvr_ulDDFormat > DDFORMAT_4PLANE || pvd->mstateVideo <=
     MEMORY_FONT)
    rc = ERROR_BAD_FORMAT;
#endif

  if (!rc)
  {

    /*
    ** Don't let mode data change underneath us
    */

    RequestMutexSem(pvd->hmxVideoState);

    /*
    ** Make sure the VDM has a valid state (initial mode set complete)
    */

    if (pvd->flVDMVideo&VDM_MODECHANGING || !(pvd->flVDMVideo&VDM_STATEINIT) ||
         !vvValidateBufferForMode(hvdm))                /*            */
      rc = ERROR_NOT_READY;

    else
    {

      /*
      ** Full-size COPYBITMAP should reset LVB events
      */

      if (pvvr->vvr_rcl.yTop == 0 && pvvr->vvr_rcl.xLeft == 0 &&
         pvvr->vvr_rcl.yBottom == pvd->vvMode.vvm_nRows-1 &&
         pvvr->vvr_rcl.xRight == pvd->vvMode.vvm_nCols-1)
      {
        vvFlushPages(hvdm);
      }

#ifdef   VDDDEBUGALL
      PRINTDEBUG("Copy requested: %04x,%04x down to %04x,%04x\n",
                 pvvr->vvr_rcl.xLeft,
                 pvvr->vvr_rcl.yTop,
                 pvvr->vvr_rcl.xRight,
                 pvvr->vvr_rcl.yBottom);
#endif

      /*
      ** 32-bit DISPLAY Driver passes actual buffer size
      */

      if (pvvr->vvr_ulDDFormat)

        if (ul2)
        {                                        /*                         */
          pvd->flVDMXVideo |= VDMX_32DSP;        /*                         */
        }

        else
        {                                        /*                         */
          pvd->flVDMXVideo &= ~VDMX_32DSP;       /*                         */
          ul2 = PAGESIZE;                        /*                         */
        }                                        /*                         */
      hllvb = VDHLockMem(pblvb,
                         ul2,
                         VDHLM_WRITE,             /*                        */
                         (struct pagelist_s *)-1, /*                        */
                         (ULONG *)-1);            /*                        */

      if (!hllvb)
        rc = VDHGetError();

      else
      {
         /*
         ** FLAG:  -- These routines also need ul2, so that
         **           they can verify the buffer is large enough
         */

#ifdef   SVGA              /* don't do the copy for high-res modes (yet)    */
        if(pvd->flVDMXVideo & VDMX_ENHANCEDMODE )              /*           */
          vvCopyGraphicsToBitmap256( hvdm,
                                     pvvr,
                                     pblvb );
        else
          if (!pvvr->vvr_ulDDFormat)
            vvCopyGraphicsToBitmap( hvdm,
                                    pvvr,
                                    pblvb );
          else
            vvCopyGraphicsToDDBitmap( hvdm,
                                      pvvr,
                                      pblvb );
#else

   #ifdef   CGA
        vvCopyGraphicsToBitmap(hvdm,
                               pvvr,
                               pblvb);
   #endif

   #ifdef   EGAVGA
        if( !pvvr->vvr_ulDDFormat )
          vvCopyGraphicsToBitmap( hvdm,
                                  pvvr,
                                  pblvb );

        else
          vvCopyGraphicsToDDBitmap( hvdm,
                                    pvvr,
                                    pblvb );
   #endif

#endif /* SVGA */
VDHUnlockMem(hllvb);
      }
    }
    ReleaseMutexSem(pvd->hmxVideoState);
  }
  VDHUnlockMem(hlvvr);
  return  rc;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysWaitEvent()
 *
 * DESCRIPTION   = Wait for next VDM video event
 *
 *                 This subroutine processes the VVDSYSREQ_WAITEVENT
 *                 function.
 *
 *                 Note that the EventUpdate semaphore is a global semaphore,
 *                 simply for simplicity:  it relieves the timer-driven
 *                 processing from having to grab and release a semaphore for
 *                 every VDM.  And besides, there is only one thread in the
 *                 system for handling VDM window updates.
 *
 * INPUT         = hvdm     == undefined
 *                 ul1      == size of data buffer
 *                 pData    -> pointer to data buffer
 *                 ul2      == size of vvo
 *                 pvvEvent -> structure describing event
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (ie, invalid parameter, etc)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysWaitEvent(HVDM hvdm,ULONG ul1,register PVOID pData,ULONG
                               ul2,register PVVEVENT pvvEvent)
{
  FLAGS fl;
  BOOL fNoHold = FALSE;
  INT iEvent = 0;
  HLOCK hlData,hlEvent;
  LONG rc = NO_ERROR;
  register PVDMDATA pvd;

#ifdef   VDDSTRICT
  INT nLoops = 0;
#endif


  if (ul2 < sizeof(VVEVENT) || !pvvEvent)
    return  ERROR_INVALID_PARAMETER;

  hlData = VDHLockMem(pData,
                      ul1,
                      VDHLM_WRITE,               /*                         */
                      (struct pagelist_s *)-1,   /*                         */
                      (ULONG *)-1);              /*                         */

  if (!hlData)
    return  VDHGetError();

  hlEvent = VDHLockMem(pvvEvent,
                       sizeof(VVEVENT),
                       VDHLM_WRITE,              /*                         */
                       (struct pagelist_s *)-1,  /*                         */
                       (ULONG *)-1);             /*                         */

  if (!hlEvent)
    rc = VDHGetError();

  else
  {

    while (!iEvent)
    {

      /*
      ** See if VDM is waiting for us to process last event;
      ** it's essential that the caller leave the event data from
      ** the previous call untouched
      */

      if (pvvEvent->vve_sgID)
      {

        if (hvdm = VDHHandleFromSGID((SGID)(pvvEvent->vve_sgID)))
        {
          vvFlushEventNotify(hvdm,
                             pvvEvent->vve_iEvent);
          pVDMData(hvdm)->flVDMVideo &= ~(VDM_UPDATING|VDM_PHYSPAGECHK);
        }

        /*
        ** fNoHold is generally not a good idea, because once we've
        ** decided to wake up the Shield thread, it should process all
        ** posted events in priority order;  honoring the holding bits
        ** could       that order up;  also, now that thread's awake,
        ** holding events back now could force more context switches.
        ** However, at least one case where it *is* useful is removing
        ** the side-effect of frequent paste notifications causing
        ** window updates
        */

        if (pvvEvent->vve_iEvent == VVDEVENT_PASTE)
          fNoHold++;
        pvvEvent->vve_sgID = 0;
      }

      /*
      ** Wait for the next video event (if one was already
      ** posted for immediate pickup, then the semaphore will
      ** already be clear)
      */

      WaitEventSem(hevWindowedEvent);

      if (vdhBTR(&flVVD,
                 LOG2(VVD_THREADEXIT)))
      {

        /*
        ** We're being shutdown,
        ** so don't bother returning an event
        */

        pvvEvent->vve_iEvent = VVDEVENT_NONE;
        break;
      }

      /*
      ** Note that GetEvent is designed to NOT return anything for
      ** a VDM with pending events if the caller's PID doesn't match
      ** the PID of the process that made us windowed (this is to
      ** support test code and any random apps that want/need to tie
      ** into our event support without interfering with the Shield)
      */

      if (iEvent = vvGetEvent((PHVDM)SSToDS(&hvdm),
                              fNoHold))
      {
        pvd = pVDMData(hvdm);

        /*
        ** This insures the consistency of per-VDM data
        */

        RequestMutexSem(pvd->hmxVideoState);

        /*
        ** Setting the UPDATING bit holds off further TIMER
        ** checks until caller has processed the current event; this
        ** is a non-essential performance tweak; remove if desired
        */

        pvd->flVDMVideo |= VDM_UPDATING;
        pvvEvent->vve_iEvent = iEvent;
        pvvEvent->vve_sgID = pvd->sgIDVideo;
        pvvEvent->vve_nData = 0;
        REFHVDM(hvdm,
                BOOL,
                *pflVDMBusy) = TRUE;             /*                         */

        switch (iEvent)
        {
          case  VVDEVENT_MODE :
            memcpy(pData,
                   &pvd->vvModeEvent,
                   min(ul1,
                       sizeof(VVMODE)));
            pvd->flVDMVideo &= ~VDM_MODECHANGED;
            pvvEvent->vve_nData++;
            break;
          case  VVDEVENT_LVB :

            /*
            ** FLAG: -- This won't work for co-existing VDDs
            */

            pvvEvent->vve_nData = vvRectsFromPageMap(hvdm,
                                                     pvd->flDirtyPageEvent,
                                                     ul1/sizeof(VVLVB),
                                                     pData);
            pvd->flDirtyPageEvent = 0;

            /*
            ** If no rectangles were returned, then the LVB event
            ** was either bogus or unnecessary, so let's stay in the
            ** while-loop
            */

            if (!pvvEvent->vve_nData)
            {
              iEvent = 0;

#ifdef   VDDSTRICT
              nLoops = 0;
#endif
            }
            break;
          case  VVDEVENT_SCROLL :
            memcpy(pData,
                   &pvd->vvScrollEvent,
                   min(ul1,
                       sizeof(VVSCROLL)));
            pvvEvent->vve_nData++;
            break;
          case  VVDEVENT_STRING :
            memcpy(pData,
                   &pvd->vvStringEvent,
                   min(ul1,
                       sizeof(VVSTRING)));
            pvvEvent->vve_nData++;
            break;
          case  VVDEVENT_CURSOR :
            memcpy(pData,
                   &pvd->vvCursorEvent,
                   min(ul1,
                       sizeof(VVCURSOR)));
            pvvEvent->vve_nData++;
            break;
          case  VVDEVENT_INPUT :
            memcpy(pData,
                   &pvd->vvInputEvent,
                   min(ul1,
                       sizeof(VVCURSOR)));
            pvvEvent->vve_nData++;
            break;
          case  VVDEVENT_TITLECHANGE :
            AssertTRUE(strlen(pvd->szVDMTitle) != 1);
            memcpy(pData,
                   pvd->szVDMTitle,
                   min(ul1,
                       MAX_TITLESIZE));
            pvvEvent->vve_nData++;
            break;
          case  VVDEVENT_DDE :                   /*                         */
            memcpy(pData,
                   &pvd->fDDEflag,
                   min(ul1,
                       2));                      /*                         */
            pvvEvent->vve_nData++;               /*                         */
            break;                               /*                         */
        }
        ReleaseMutexSem(pvd->hmxVideoState);
      }

      /*
      ** If no more events, re-arm the WindowedEvent semaphore
      */

      else
      {

#ifdef   VDDSTRICT

        if (++nLoops > 1)
        {
          PRINTDEBUG("Spurious event wakeup???\n");
        }
#endif
        ResetEventSem(hevWindowedEvent);
      }
    }
    VDHUnlockMem(hlEvent);
  }
  VDHUnlockMem(hlData);
  return  rc;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysControlEvent()
 *
 * DESCRIPTION   = Abort event thread
 *
 *                 This subroutine processes the VVDSYSREQ_EXITTHREAD
 *                 function.
 *
 * INPUT         = hvdm -> VDM
 *                 ul1  == One of the CONTROL_* constants
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysControlEvent(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,PVOID
                                  p2)
{
  register PVDMDATA pvd;


  if (ul1 == CONTROL_RELEASE)
  {
    flVVD |= VVD_THREADEXIT;
    PostEventSem(hevWindowedEvent);
  }

  else
  {

    if (hvdm == INVALID_HVDM)
      return  ERROR_INVALID_PARAMETER;

    pvd = pVDMData(hvdm);

    if (!(pvd->flVDMVideo&VDM_WINDOWED))
      return  ERROR_INVALID_FUNCTION;

    RequestMutexSem(hmxWindowedEvent);

    if (ul1 == CONTROL_VDMMINIMIZED && !(pvd->flVDMVideo&VDM_MINIMIZED))
    {
      pvd->flVDMVideo |= VDM_MINIMIZED;
      nMinWindowedVDMs++;
    }

    else

      if (ul1 == CONTROL_VDMUNMINIMIZED && (pvd->flVDMVideo&VDM_MINIMIZED))
      {
        pvd->flVDMVideo &= ~VDM_MINIMIZED;

        if (pvd->flVDMVideo&VDM_STATEINIT)
          vvUpdateScreenState(pvd->hvdmVideo,
                              TRUE);

        if (nWindowedVDMs == nMinWindowedVDMs--)
        {
          VDHDisarmTimerHook(hhookEventTimer);
          VDHArmTimerHook(hhookEventTimer,
                          TIMER_CHECK,
                          VDH_TIMER_GLOBAL_CONTEXT);/*                      */
          flVVD |= VVD_TIMERARMED;               /*                         */
        }
      }
    ReleaseMutexSem(hmxWindowedEvent);
  }
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysSetDisplayReq()
 *
 * DESCRIPTION   = Set PM display requirements
 *
 *                 This subroutine processes the VVDSYSREQ_SETDRQ function.
 *
 * INPUT         = hvdm -> VDM
 *                 ul1  == size of input structure
 *                 pdrq -> pointer to input structure
 *                 ul2  is not used
 *                 p2   is not used
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysSetDisplayReq(HVDM hvdm,ULONG ul1,PVVDRQ pdrq,ULONG ul2,
                                   PVOID p2)
{

#ifdef   EGAVGA
  HLOCK hldrq;


  if (ul1 < sizeof(VVDRQ) || !pdrq)
    return  ERROR_INVALID_PARAMETER;

  hldrq = VDHLockMem(pdrq,
                     sizeof(VVDRQ),
                     VDHLM_READ,                 /*                         */
                     (struct pagelist_s *)-1,    /*                         */
                     (ULONG *)-1);               /*                         */

  if (!hldrq)
    return  VDHGetError();

  /*
  ** Update the physical page usage table
  */

  vvUpdatePhysPageMap(pdrq);
  VDHUnlockMem(hldrq);
#endif
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysRequestMemory()
 *
 * DESCRIPTION   = Request off-screen memory for use by PM
 *
 *                 This subroutine processes the VVDSYSREQ_REQMEM function.
 *
 * INPUT         = hvdm  -> VDM
 *                 ul1   is 0 for NOWAIT request, 1 for WAIT (ignored at this time)
 *                 p1    is not used
 *                 nb    == # of bytes required
 *                 pAddr -> address (returned)
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysRequestMemory(HVDM hvdm,ULONG ul1,PVOID p1,ULONG nb,PULONG
                                   pAddr)
{

#ifdef   EGAVGA
  ULONG p;
  HLOCK hl;
  LONG rc = NO_ERROR;

  hl = VDHLockMem(pAddr,
                  sizeof(ULONG),
                  VDHLM_WRITE,                   /*                         */
                  (struct pagelist_s *)-1,       /*                         */
                  (ULONG *)-1);                  /*                         */

  if (!hl)
    return  VDHGetError();


  if (p = vvGetPhysBytes(nb))
    *pAddr = p;

  else
    rc = ERROR_NOT_ENOUGH_MEMORY;
  VDHUnlockMem(hl);
  return  rc;

#else
  return  ERROR_NOT_SUPPORTED;
#endif
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysFreeMemory()
 *
 * DESCRIPTION   = Release off-screen memory for use by PM
 *
 *                 This subroutine processes the VVDSYSREQ_FREEMEM function.
 *
 * INPUT         = hvdm  -> VDM
 *                 ul1   is not used
 *                 p1    is not used
 *                 nb    == # of bytes releasing
 *                 pAddr -> address
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysFreeMemory(HVDM hvdm,ULONG ul1,PVOID p1,ULONG nb,PULONG
                                pAddr)
{

#ifdef   EGAVGA
  HLOCK hl;

  hl = VDHLockMem(pAddr,
                  sizeof(ULONG),
                  VDHLM_READ,                    /*                         */
                  (struct pagelist_s *)-1,       /*                         */
                  (ULONG *)-1);                  /*                         */

  if (!hl)
    return  VDHGetError();

  vvFreePhysBytes(nb,
                  *pAddr);
  VDHUnlockMem(hl);
#endif
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysRequestController()
 *
 * DESCRIPTION   = Request video controller access for PM
 *
 *                 This subroutine processes the VVDSYSREQ_REQCTRL function.
 *                 It is issued by the DISPLAY driver if it finds the shared
 *                 OWNED flag set upon driver entry; otherwise, the DISPLAY
 *                 driver will simply set and clear the shared OWNED flag
 *                 for as long as it can get away with it.  This is to avoid
 *                 making VDD calls until it is strictly necessary.
 *
 * INPUT         = hvdm  -> VDM
 *                 ul1   is not used
 *                 p1    is not used
 *                 ul2   is not used
 *                 p2    is not used
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (none)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysRequestController(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,
                                       PVOID p2)
{

#ifdef   EGAVGA
  vvRequestController(DISPLAY_OWNER,
                      FALSE);
#endif
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysFreeController()
 *
 * DESCRIPTION   = Release video controller access for PM
 *
 *                 This subroutine processes the VVDSYSREQ_FREECTRL function.
 *                 Note that it is only issued if the VDD has set the shared
 *                 NOTIFY flag; otherwise, the DISPLAY driver will simply set
 *                 and clear the shared OWNED flag for as long as it can get
 *                 away with it.  This is to avoid making VDD calls until it
 *                 is strictly necessary.
 *
 * INPUT         = hvdm  -> VDM
 *                 ul1   is not used
 *                 p1    is not used
 *                 ul2   is not used
 *                 p2    is not used
 *
 * OUTPUT        = SUCCESS
 *                     Zero
 *                 FAILURE
 *                     Error code (none)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysFreeController(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,
                                    PVOID p2)
{

#ifdef   EGAVGA
  vvFreeController(DISPLAY_OWNER);
#endif
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysQueryVRAMStatus()
 *
 * DESCRIPTION   = Determine VRAM status
 *
 *                 This subroutine processes the VVDSYSREQ_QUERYVRAMSTATUS
 *                 function.  It enables the Shell to determine whether or
 *                 not its VRAM is still intact or not.
 *
 * INPUT         = hvdm  -> VDM
 *                 ul1   is not used
 *                 p1    is not used
 *                 ul2   is not used
 *                 p2    is not used
 *
 * OUTPUT        = SUCCESS
 *                     Zero (intact)
 *                 FAILURE
 *                     Error code (trashed)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysQueryVRAMStatus(HVDM hvdm,ULONG ul1,PVOID p1,ULONG ul2,
                                     PVOID p2)
{

  /*
  ** FLAG: -- Doesn't take co-existing VDDs into account
  */

  if (flVVD&(VVD_VRAMISTOAST|VVD_REPAINT))
  {                                              /*                         */

    if (!(flVVD&VVD_REPAINT))
    {                                            /*                         */

      /*
      ** only reset this bit if Save/Restore is currently                   
      ** BEING DONE FOR THE VGA ONLY                                        
      */

      flVVD &= ~VVD_VRAMISTOAST;
    }
    return  ERROR_INVALID_DATA;
  }
  return  NO_ERROR;
}

/***************************************************************************
 *
 * FUNCTION NAME = vvSysSetOEMFlag()
 *
 * DESCRIPTION   = Set OEM Flag
 *
 *                 This subroutine processes the VVDSYSREQ_SETOEMFLAG
 *                 function.
 *
 * INPUT         = hvdm  -> VDM
 *                 ul1   is not used
 *                 pflag -> OEM Status Flag
 *                 ul2   is not used
 *                 p2    is not used
 *
 * OUTPUT        = SUCCESS
 *                     Zero (intact)
 *                 FAILURE
 *                     Error code (trashed)
 *
 * RETURN-NORMAL =
 * RETURN-ERROR  =
 *
 ****************************************************************************/

LONG PRIVENTRY vvSysSetOEMFlag(HVDM hvdm,        /*                         */
                                ULONG pflag,PVOID p1,ULONG ul2,PVOID p2)
{                                                /*                         */

#ifdef   VGA                                     /*                         */
pfOEMFlags = ( PBYTE ) pflag;                    /*                         */
#endif                                           /*                         */
return  NO_ERROR;                                /*                         */
}                                                /*                         */
#pragma  END_SWAP_CODE

