/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/*   File            = VVGAC                                          */
/*                                                                    */
/*   Description     = Virtual VGA code                               */
/*                                                                    */
/*   Function        = Enables VGA Virtualisation by using any spare  */
/*                     VGA hardware that is available.                */
/*                                                                    */
/*   Reference       = Winthorn Functional Specification              */
/*                     Device Driver Interface Specification          */
/*                     Display Device Driver Design                   */
/*                                                                    */
/*                                                                    */
/**********************************************************************/
#ifdef VVGA

#define     INCL_DOSMVDM
#define     INCL_GPIBITMAPS
#define     INCL_DOSDEVICES
#include    <os2.h>

#define     INCL_NONE
#define     INCL_VIDEOHW
#define     INCL_VIDEOSYSREQ
#include    <vvd.h>
#include    "xgaadapt.h"
#include    "vvga.h"

#define     DOS_OK 0L

HVDD    hVideoVDD = 0L;
ULONG   pVGAVRAM = 0L;
ULONG   fPlanarVGAIsActive = FALSE;
BOOL    fVVGA = FALSE;
BOOL    fFreePlanarXGA = FALSE;
USHORT  usPlanarXGAIOAddr = 0;

extern  HFILE ring0_handle;

#ifdef RETAIL
#define haltproc()
#else
VOID haltproc(VOID);
#endif /* RETAIL */



HVDD    OpenVDD(VOID)
{
    return DosOpenVDD( VVD_NAME1, &hVideoVDD );
}

BOOL    CloseVDD(HVDD hVDD)
{
    return DosCloseVDD( hVDD );
}


VOID  LookForFreePlanarXGA()
{
    ULONG        fn_id;
    XGAINSTANCE  XGAInstanceData;
    ULONG        rc;

    /******************************************************************/
    /* We'll start off being pessimistic!                             */
    /******************************************************************/
    fFreePlanarXGA = FALSE;

    /*****************************************************************/
    /* Call the ring0 with the blank instance data for it to fill    */
    /* in.                                                           */
    /*****************************************************************/
    XGAInstanceData.slot_number = 0; /* Query Planar slot  */
    XGAInstanceData.XGA_level = 0;
    fn_id = IO_SETINSTANCEDATA;
    rc = DosDevIOCtl( ring0_handle,
                      XGA_CATEGORY,
                      GEN_FUNCTION,
                      &fn_id,
                      sizeof(fn_id),
                      NULL,
                      &XGAInstanceData,
                      sizeof(XGAInstanceData),
                      NULL );

    /*****************************************************************/
    /* Get the instance data back from the ring0.                    */
    /*****************************************************************/
    fn_id = IO_GETINSTANCEDATA;
    rc = DosDevIOCtl( ring0_handle,
                      XGA_CATEGORY,
                      GEN_FUNCTION,
                      &fn_id,
                      sizeof(fn_id),
                      NULL,
                      &XGAInstanceData,
                      sizeof(XGAInstanceData),
                      NULL );

    /*****************************************************************/
    /* See if the ring0 found an XGA instance at this slot.          */
    /*****************************************************************/
    if (XGAInstanceData.XGA_level != 0)
    {
        /*************************************************************/
        /* The ring0 did find an instance.                           */
        /* If there is no monitor attached then we will assume        */
        /* that we can use it for VGA Virtualisation.                 */
        /*************************************************************/
        if (XGAInstanceData.XGA_monitor_id == D_NONE)
        {
            fFreePlanarXGA = TRUE;
            usPlanarXGAIOAddr = XGAInstanceData.XGA_io_base;
        }
    }

}


#ifdef PLOPPY
    !! Extract from VVD.H !!
typedef struct vvdrq_s {        /* vvdrq (main structure for VVDSYSREQ_SETDRQ) */
    PBYTE  vvd_pPhysVRAM;       /* physical address of VRAM */
    ULONG  vvd_nbReserved;      /* # of reserved bytes */
    ULONG  vvd_offLatchByte;    /* offset of available latch storage */
    PBYTE  vvd_pfbDRQFlags;     /* pointer to flags (see DRQ_* constants) */
    PBYTE  vvd_pfCtrlOwned;     /* address of display.dll's fCtrlOwned flag */
    PBYTE  vvd_pfCtrlNotify;    /* address of display.dll's fCtrlNotify flag */
    ULONG  vvd_nShadowRegs;     /* # of registers to shadow */
    PVVREG vvd_pShadowData;     /* address of first entry in shadow list */
} VVDRQ;
#endif

/**********************************************************************/
/* StartVirtualVGA() tells the VDD that it can start using the        */
/* VGA hardware.                                                      */
/**********************************************************************/
VOID    StartVirtualVGA()
{
    VVDRQ   vvdrq;

    vvdrq.vvd_pPhysVRAM = (PBYTE)EGAVGAMEM_START;  /* Start of VRAM     */
    vvdrq.vvd_nbReserved = 0L;              /* No. of VRAM bytes we use */
    vvdrq.vvd_offLatchByte = 0L;  /* offset of available latch storage */
    vvdrq.vvd_pfbDRQFlags = 0L;   /* pointer to flags (see DRQ_* constants) */
    vvdrq.vvd_pfCtrlOwned = 0L;   /* address of display.dll's fCtrlOwned flag */
    vvdrq.vvd_pfCtrlNotify = 0L;  /* address of display.dll's fCtrlNotify flag */
    vvdrq.vvd_nShadowRegs = 0L;   /* # of registers to shadow */
    vvdrq.vvd_pShadowData = 0L;   /* address of first entry in shadow list */

    if (hVideoVDD)
    {
        DosRequestVDD( hVideoVDD,
                       0,                   /* sgId     */
                       VVDSYSREQ_SETDRQ,
                       sizeof(VVDRQ),       /* cbInput  */
                       &vvdrq,              /* pInput   */
                       0L,                  /* cbOutput */
                       0L );                /* pOutput  */
    }
}


/**********************************************************************/
/* StopVirtualVGA() tells the VDD to stop using the VGA hardware.     */
/**********************************************************************/
VOID    StopVirtualVGA()
{
    VVDRQ   vvdrq;

    vvdrq.vvd_pPhysVRAM = 0x0L;         /* Set VRAM address to zero  */
    vvdrq.vvd_nbReserved = 0xFFFFFFFFL; /* Max reserved value        */
    vvdrq.vvd_offLatchByte = 0L;  /* offset of available latch storage */
    vvdrq.vvd_pfbDRQFlags = 0L;   /* pointer to flags (see DRQ_* constants) */
    vvdrq.vvd_pfCtrlOwned = 0L;   /* address of display.dll's fCtrlOwned flag */
    vvdrq.vvd_pfCtrlNotify = 0L;  /* address of display.dll's fCtrlNotify flag */
    vvdrq.vvd_nShadowRegs = 0L;   /* # of registers to shadow */
    vvdrq.vvd_pShadowData = 0L;   /* address of first entry in shadow list */

    if (hVideoVDD)
    {
        DosRequestVDD( hVideoVDD,
                       0,
                       VVDSYSREQ_SETDRQ,
                       sizeof(VVDRQ),       /* cbInput  */
                       &vvdrq,              /* pInput   */
                       0L,                  /* cbOutput */
                       0L );                /* pOutput  */
    }
}


/**********************************************************************/
/* VOID  CheckForVirtualVGA()                                         */
/* This function determines whether there is a spare VGA adapter      */
/* available for us to use for VGA Virtualisation.                    */
/*                                                                    */
/* This routine must be called only after the XGA adapter has been    */
/* put into Native Mode (otherwise the routine will fail in step 1).  */
/*                                                                    */
/* The algorithm is:                                                  */
/*  1) If there is an active VGA adapter, then we can't use it for    */
/*     VVGA (because we don't know what it is being used for).        */
/*     We cannot enable another VGA (even if there is one) because    */
/*     it would clash with the currently active one.                  */
/*     Therefore we cannot support VGA Virtualisation in this case.   */
/*  2) If there is a planar VGA that has been disabled, then activate */
/*     it and use it for VGA Virtualisation.                          */
/*                                                                    */
/* If (2) fails we (currently) do NOT scan the machine for further    */
/* VGA/XGA adapters because even if there are any present, we must    */
/* assume that they are there for a purpose, and it would be          */
/* downright dangerous (not to say rude!) for us to leap in and start */
/* using this hardware for our own purposes.                          */
/*                                                                    */
/**********************************************************************/
VOID    CheckForVirtualVGA()
{
    /******************************************************************/
    /* If there is a currently active VGA adapter then we won't       */
    /* support VGA Virtualisation.                                    */
    /******************************************************************/
    /******************************************************************/
    /* Attempt to activate the planar VGA. If we succeed then         */
    /* VGA Virtualisation is possible.                                */
    /* This function will first check for an active VGA, and if one   */
    /* is found it fails.                                             */
    /* The result of the function is stored in the global variable    */
    /* fPlanarVGAIsActive.                                            */
    /******************************************************************/
    ActivatePlanarVGA();

    if (fPlanarVGAIsActive)
    {
        if (DOS_OK == OpenVDD())
        {
            fVVGA = TRUE;
            StartVirtualVGA();
            return;
        }
    }

    /******************************************************************/
    /* We have failed to find a suitable piece of VGA hardware,       */
    /* so we cannot support VVGA.                                     */
    /******************************************************************/
    fVVGA = FALSE;
    return;
}


#ifdef WE_REQUEST_VGA_BITS

#ifdef PLOPPY
    !!! Extract from VVD.H !!!
typedef struct vvr_s {          /* vvr (input for COPYBITMAP request) */
    ULONG  vvr_ulDDFormat;      /* display driver format (0 if DIB used) */
    ULONG  vvr_cx;              /* target bitmap width */
    ULONG  vvr_cy;              /* target bitmap height */
    RECTL  vvr_rcl;             /* rectangle being requested */
    PBYTE  vvr_pbColorXlate;    /* display driver color translation table */
} VVRECT;
#endif

/**********************************************************************/
/* BOOL  RequestVGABitmapBits(ULONG ulBitmapWidth,                    */
/*                            ULONG ulBitmapHeight)                   */
/*                                                                    */
/* This function is called from SetBitmapBits when it receives a      */
/* request to set VGA screen data into a bitmap.                      */
/*                                                                    */
/* The function calls the Video VDD with a request to pass this data  */
/* into the Display Driver as a Device Independent Bitmap (DIB).      */
/*                                                                    */
/**********************************************************************/
BOOL    RequestVGABitmapBits( SGID          sgid,
                              LONG          lScanStart,
                              LONG          clScanCount,
                              PBYTE         pBitmapBits,
                              PBITMAPINFO2  pbmi2 )
{
    VVRECT  vvrect;
    ULONG   ulRetCode;
    ULONG   ulBytesPerLine;
    ULONG   ulMemSize;

    /******************************************************************/
    /* Return immediately if we have no handle to the Video VDD.      */
    /******************************************************************/
    if (hVideoVDD == 0)
    {
        return(FALSE);
    }

    /******************************************************************/
    /* Request the data in a Device Independent Bitmap Format.        */
    /* The specified rectangle coords must be INCLUSIVE and FLIPPED.  */
    /* i.e. setting a full bitmap would have:                         */
    /*    xLeft = 0;                                                  */
    /*    yTop = 0;                                                   */
    /*    xRight = pbmi2->cx-1;                                       */
    /*    yBottom = pbmi2->cy-1;                                      */
    /******************************************************************/
    vvrect.vvr_ulDDFormat = 0L;          /* Ask for DIB bitmap format */
    vvrect.vvr_cx = pbmi2->cx;
    vvrect.vvr_cy = pbmi2->cy;
    vvrect.vvr_rcl.xLeft = 0L;
    vvrect.vvr_rcl.yBottom = (pbmi2->cy-1) - lScanStart;
    vvrect.vvr_rcl.xRight = pbmi2->cx-1;
    vvrect.vvr_rcl.yTop = vvrect.vvr_rcl.yBottom - clScanCount + 1;
    vvrect.vvr_pbColorXlate = 0L;

    /******************************************************************/
    /* Calculate bitmap memory size.                                  */
    /* Rows are padded to dword boundaries.                           */
    /******************************************************************/
    ulBytesPerLine = (((pbmi2->cx * pbmi2->cBitCount / 8) + 3) & ~3L);
    ulMemSize = ulBytesPerLine * pbmi2->cy;

    /******************************************************************/
    /* Make the call.                                                 */
    /******************************************************************/
    ulRetCode = DosRequestVDD( hVideoVDD,
                               sgid,
                               VVDSYSREQ_COPYBITMAP,
                               sizeof(vvrect),
                               &vvrect,
                               ulMemSize,             /* cbOutput */
                               pBitmapBits );         /* pOutput  */

    /******************************************************************/
    /* A zero return code from DosRequestVDD indicates success.       */
    /******************************************************************/
    return(ulRetCode == 0 ? TRUE : FALSE);

}
#endif  // WE_REQUEST_VGA_BITS



#ifdef TEST_VGA_MEM
/**********************************************************************/
/* GetVRAMPointer returns a flat pointer for accessing VGA VRAM.      */
/* This must be called by each process that needs to access VRAM.     */
/**********************************************************************/
typedef struct _ALLOC_VRAM
{
    ULONG    ulVRAMPhysicalAddress;
    ULONG    ulVRAMLength;
    ULONG    ulFlags;
} ALLOC_VRAM;

typedef struct _ACCESS_VRAM
{
    ULONG    ulVRAMFlatAddress;
    ULONG    ulVRAMLength;
} ACCESS_VRAM;


#define FN_ALLOC_GLOBAL_VRAM  0x7E
#define FN_ACCESS_GLOBAL_VRAM 0x7F


PBYTE  GetVGAVRAMPointer()
{
    ULONG        hPMDD;
    ULONG        ulAction;
    ALLOC_VRAM   AllocVRAM;
    ACCESS_VRAM  AccessVRAM;
    ULONG        ulRetCode;
    static BOOL  fFirstTime = TRUE;

    if (DOS_OK != DosOpen( "\DEV\SINGLEQ$",
                           &hPMDD,
                           &ulAction,
                           0L,          /* Filesize */
                           0L,          /* File attribute */
                           1L,          /* Open flag - open existing file */
                           0xC0,        /* Open mode */
                           0L ))        /* Reserved */
    {
        goto vram_err_exit;
    }


    if (fFirstTime)
    {
        fFirstTime = FALSE;
        AllocVRAM.ulVRAMPhysicalAddress = EGAVGAMEM_START;
        AllocVRAM.ulVRAMLength = EGAVGAMEM_LEN;
        AllocVRAM.ulFlags = 0L;

        ulRetCode = DosDevIOCtl( hPMDD,                /* hDevice    */
                                 0x03,                 /* ulCat      */
                                 FN_ALLOC_GLOBAL_VRAM, /* ulFunction */
                                 &AllocVRAM,           /* pParmList  */
                                 sizeof(AllocVRAM),    /* cbParmList */
                                 NULL,                 /* plReserved */
                                 &pVGAVRAM,            /* pData      */
                                 sizeof(pVGAVRAM),     /* cbData     */
                           );                  /* plReserved */
    }
    else
    {
        AccessVRAM.ulVRAMFlatAddress = pVGAVRAM;
        AccessVRAM.ulVRAMLength = EGAVGAMEM_LEN;

        ulRetCode = DosDevIOCtl( hPMDD,                 /* hDevice    */
                                 0x03,                  /* ulCat      */
                                 FN_ACCESS_GLOBAL_VRAM, /* ulFunction */
                                 &AccessVRAM,           /* pParmList  */
                                 sizeof(AccessVRAM),    /* cbParmList */
                                 NULL,                  /* plReserved */
                                 NULL,                  /* pData      */
                                 0L,                    /* cbData     */
                                 NULL );                /* plReserved */
    }


    DosClose(hPMDD);

    if (ulRetCode != 0)
    {
        goto vram_err_exit;
    }

    return((PBYTE)pVGAVRAM);

vram_err_exit:
    return(NULL);

}
#endif /* TEST_VGA_MEM */

#endif /* VVGA */
