/*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.                                */
/*                                                                           */
/*****************************************************************************/
/**********************************************************************/
/*                                                                    */
/* edd0code.c: Ring0 C code for kernal XGA PM driver                  */
/*                                                                    */
/**********************************************************************/

#define INCL_GPIBITMAPS

#define INCL_DOS
#include <os2.h>
#undef INCL_DOS

#include <xgaadapt.h>
#include <edd0type.h>
#include <edderre.h>
#include <edd0extf.h>

/**********************************************************************/
/*                                                                    */
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
/* !!                                                              !! */
/* !! Currently the ring0 is built such that you can not have any  !! */
/* !! global data declared within this C module.  This is because  !! */
/* !! the device driver header must come first in the data segment !! */
/* !! and data declared here currently would come first instead.   !! */
/* !!                                                              !! */
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
/*                                                                    */
/**********************************************************************/

/**********************************************************************/
/* Globals                                                            */
/**********************************************************************/
extern USHORT       deviceNum;
extern XGAINSTANCE  instances[];
extern USHORT       IDOrder[16];

int                 __acrtused = 1;

/**********************************************************************/
/* These globals are used to pass data to and from DoPhysToUVirt.     */
/**********************************************************************/
VOID    DoPhysToUVirt(VOID);
VOID    DoGetDOSVar(VOID);
extern  ULONG   ulPhysicalAddress;
extern  USHORT  usSize;
extern  PVOID   pResult;
extern  ULONG   fInt10DMQSData;

/**********************************************************************/
/* return_adapterinfo                                                 */
/*  Pass back ADAPTERINFO to calling function (BVH, FillPDB?)         */
/*                                                                    */
/* This replaces, and matches, original asm code, with addition of    */
/* slot parameter, to enable query of any adapter.                    */
/*                                                                    */
/**********************************************************************/
USHORT PASCAL return_adapterinfo( PADAPTERINFO param )
{
    USHORT  flags;

    param->usDisplayType  = instances[0].XGA_monitor_id;
    param->lMemorySize    = instances[0].XGA_vram_size;
    param->usIORegBase    = instances[0].XGA_io_base;
    param->pMemRegBase    = (pMMReg)instances[0].XGA_mem_map_virt;
    param->ulVRAMBase     = instances[0].XGA_base_vram;

    /******************************************************************/
    /* Now work out the flags value.                                  */
    /******************************************************************/
    flags = 0;
    if (instances[0].primary_adapter)
    {
        flags |= XGA_PRIMARY_ADAPTER;
    }
#ifdef MATROX
    if (instances[0].pos_id == MATROX_ID)
    {
        /******************************************************************/
        /* All Matrox boards support 16bpp.                               */
        /******************************************************************/
        flags |= XGA_16BPP_SUPPORTED;
    }
    else
#endif /* MATROX */
    /******************************************************************/
    /* This comment is in the middle of an else if statement !!!!!    */
    /* (if MATROX is defined).                                        */
    /******************************************************************/

    /******************************************************************/
    /* Defect 64715 - The functional and resolution level IDs are     */
    /* swapped since a USHORT is being used in a structure instead    */
    /* of two consecutive BYTES.  The was causing the driver to treat */
    /* the XGA/2 card as an XGA/1 using a polling delay.  This is a   */
    /* big NO-NO.                                                     */
    /******************************************************************/
    if ( (instances[0].XGA_level << 8)  >= 0x400 )
    {
        /**************************************************************/
        /* XGA_level holds 2 fields each of 4 bits                    */
        /*          XGA chip level  (bits 8 - 15)                     */
        /*          DAC level       (bits 0 - 7)                      */
        /* We assume that any chip level greater than 3 will support  */
        /* both 16bpp operation, and have the external polling bit.   */
        /**************************************************************/
        flags |= (XGA_EXTERNAL_POLLING | XGA_16BPP_SUPPORTED);
    }

    param->usFlags = flags;
    param->AdapterID = instances[0].pos_id;

    return( OK );
}



/**********************************************************************/
/* This routine will try to obtain the DMQS primary data via the      */
/* kernel (DCR 1601). The kernel issues an INT 10H, on our behalf     */
/* to obtain the DMQS information. We simply issue devhlp_getdosvar   */
/* to obtain the information. The INT 10H does not work on all        */
/* adapters. The XGA NI Adapter and beyond will have this support.    */
/*                                                                    */
/* If the devhlp fails we will go to the EBDA for the DMQS            */
/* information. The EBDA is a PS/2 only solution.                     */
/*                                                                    */
/**********************************************************************/
VOID GetDMQSPrimaryData(VOID)
{

    /******************************************************************/
    /* First try to obtain the information directly from the kernel   */
    /* If the kernel already has the DMQS information then pResult    */
    /* will contain a pointer to that data.                           */
    /******************************************************************/
    GetDMQSPrimaryDataViaINT10();

    /******************************************************************/
    /* Set a flag to indicate whether the DMQS data was found via     */
    /* the INT10.                                                     */
    /******************************************************************/
    fInt10DMQSData = (pResult != NULL);

    /******************************************************************/
    /* If no INT 10H support then try to read the EBDA.               */
    /******************************************************************/
    if (pResult == NULL)
        GetDMQSPrimaryDataViaEBDA();
}

/**********************************************************************/
/* This routine will try to obtain the DMQS information via the       */
/* kernel (DevHlp_GetDosVar - DCR 1601).                              */
/**********************************************************************/
VOID GetDMQSPrimaryDataViaINT10(VOID)
{

    PINT10DMQSPDI pDMQSDataInstance;

    /**************************************************************/
    /* Now call GetDosVar. If successful this call will return    */
    /* a pointer to the DMQS primary data.                        */
    /**************************************************************/
    DoGetDOSVar();

    /**************************************************************/
    /* If we got back a null pointer then something has gone      */
    /*       so we can not get hold of the DMQS primary data.     */
    /**************************************************************/
    if (pResult == NULL)
    {
        instances[0].pos_id = 0;
        return;
    }


    /**************************************************************/
    /* Initialise our pointer to the start of the dynamic         */
    /* extensions chain.                                          */
    /**************************************************************/
    pDMQSDataInstance = MAKEP( SELECTOROF(pResult),
                              OFFSETOF(pResult) );


    /**************************************************************/
    /* Loop thru each DMQS instance until we find a match for the */
    /* slot specified. If we run out of instances return with     */
    /* error indicated.                                           */
    /**************************************************************/

    while (instances[0].slot_number != pDMQSDataInstance->Int10DMQSPDI.bSlot)
    {

        if (pDMQSDataInstance->usNextDMQSPDI)

           /************************************************************/
           /* Defect 65065 - The usNextDMQSPDI field is not a pointer  */
           /* to the next instance, it is the number of bytes to the   */
           /* next instance.  Changed accordingly.                     */
           /************************************************************/
           (PBYTE)pDMQSDataInstance += pDMQSDataInstance->usNextDMQSPDI;

        else {
           instances[0].pos_id = 0;
           return;
        }

    }

    /**********************************************************/
    /* We have found the DMQS primary data for the slot we    */
    /* wanted.  Fill in the instance data using the DMQS      */
    /* primary data.                                          */
    /**********************************************************/
    instances[0].pos_id = XGA_DMQS_ID;
    instances[0].XGA_level = pDMQSDataInstance->Int10DMQSPDI.usXGALevel;
    instances[0].XGA_io_base = pDMQSDataInstance->Int10DMQSPDI.usIOBase;
    instances[0].XGA_base_vram = pDMQSDataInstance->Int10DMQSPDI.usVRAMBase
                                                    * (LONG)0x100000;
    instances[0].XGA_monitor_id = pDMQSDataInstance->Int10DMQSPDI.usMonitorID;
    instances[0].XGA_vram_size = pDMQSDataInstance->Int10DMQSPDI.bVRAMSize
                                                     * (LONG)0x40000;
    instances[0].XGA_1M_aperture_phys =
                                 pDMQSDataInstance->Int10DMQSPDI.us1MPhysAperture
                                                    * (LONG)0x100000;
    instances[0].XGA_4M_aperture_phys =
                                 pDMQSDataInstance->Int10DMQSPDI.us4MPhysAperture
                                                    * (LONG)0x100000;
    instances[0].XGA_mem_map_phys =
                          ((ULONG)pDMQSDataInstance->Int10DMQSPDI.usRegSeg) << 4;

    /**********************************************************/
    /* These are the related fields that are not supplied by  */
    /* the DMQS primary data.  The primary_adapter flag is    */
    /* the currently the only one of these that we actually   */
    /* need. This will be set up later by the ring0 code in   */
    /*                                                        */
    /**********************************************************/
    instances[0].pos_data[0] = 0;
    instances[0].pos_data[1] = 0;
    instances[0].pos_data[2] = 0;
    instances[0].pos_data[3] = 0;
    instances[0].XGA_mem_map_virt = 0;
    instances[0].XGA_mem_map_flat = 0;
    instances[0].primary_adapter = 0;
    instances[0].phys_addr_instance = 0;
    instances[0].XGA_1M_aperture_flat = 0;
    instances[0].XGA_4M_aperture_flat = 0;

}

/**********************************************************************/
/* This routine searches the extended bios data area for the DMQS     */
/* primary data that corresponds to the slot requested.               */
/**********************************************************************/
VOID GetDMQSPrimaryDataViaEBDA(VOID)
{
    static PDMQSPD pDMQSPrimaryData = NULL;

    PDMQSPDI pDMQSPDataInstance;
    PUSHORT  pEBDASegment;
    BYTE     bNumTableEntries;
    BYTE     i;

/**********************************************************************/
/* !! Put this in here (takes out optimisation) because more than one */
/* process currently seems to call this function. !!                  */
/**********************************************************************/
pDMQSPrimaryData = NULL;

    if (pDMQSPrimaryData == NULL)
    {
        /**************************************************************/
        /* This is the first time we have been called so we must find */
        /* the start of the DMQS primary data.                        */
        /**************************************************************/

        /**************************************************************/
        /* The extended BIOS data area segment is always in 40:E.     */
        /**************************************************************/
        pEBDASegment = MAKEP(0x40, 0xE);

        /**************************************************************/
        /* The physical address is the segment shifted left 4 places. */
        /* (Remember segments from DOS ??!!  - Ay when I were a       */
        /* lad...)                                                    */
        /**************************************************************/
        ulPhysicalAddress = ((ULONG)(*pEBDASegment)) << 4;

        /**************************************************************/
        /* The EBDA is carved out from beneath 640K so we can work    */
        /* out its length from it start address.                      */
        /**************************************************************/
        usSize = (USHORT)((ULONG)0xA0000 - ulPhysicalAddress);

        /**************************************************************/
        /* Now call DoPhysToUVirt to do the call to set up the        */
        /* registers and call PhysToUVirt.                            */
        /**************************************************************/
        DoPhysToUVirt();

        /**************************************************************/
        /* If we got back a null pointer then something has gone      */
        /*       so we can not get hold of the DMQS primary data.     */
        /**************************************************************/
        if (pResult == NULL)
        {
            instances[0].pos_id = 0;
            return;
        }


        /**************************************************************/
        /* Initialise our pointer to the start of the dynamic         */
        /* extensions chain.                                          */
        /**************************************************************/
        pDMQSPrimaryData = MAKEP( SELECTOROF(pResult),
                                  OFFSETOF(pResult) + 0x0180 );

        /**************************************************************/
        /* Follow the dynamic extensions chain until we find the DMQS */
        /* primary data area.                                         */
        /**************************************************************/
        while (pDMQSPrimaryData->usAdapterID != 0x8FDA)
        {
            if (pDMQSPrimaryData->usNextBlockOffset == 0)
            {
                /******************************************************/
                /* We have reached the end of the chain and did not   */
                /* find the DMQS primary data.  Set the POS ID to be  */
                /* zero to indicate this to the calling function.     */
                /******************************************************/
                instances[0].pos_id = 0;
                return;
            }
            pDMQSPrimaryData = MAKEP(SELECTOROF(pDMQSPrimaryData),
                    OFFSETOF(pResult) + pDMQSPrimaryData->usNextBlockOffset);
        }
    }

    /******************************************************************/
    /* We have found the block of DMQS primary data. Now we must look */
    /* through each instance                                          */
    /******************************************************************/
    bNumTableEntries = pDMQSPrimaryData->bNumberOfXGATableEntries;
    pDMQSPDataInstance = &(pDMQSPrimaryData->DMQSPrimaryDataInstance[0]);

    /******************************************************************/
    /* If the number of entries is more than 9 (8 slots + 1 planar)   */
    /* then something must have gone badly wrong.                     */
    /******************************************************************/
    if (bNumTableEntries > 9)
    {
        instances[0].pos_id = 0;
        return;
    }

    for ( i=0;
          i<bNumTableEntries;
          i++, ((PBYTE)pDMQSPDataInstance) += pDMQSPrimaryData->bTableSize )
    {
        if (instances[0].slot_number ==
                     pDMQSPDataInstance->bSlot)
        {
            /**********************************************************/
            /* We have found the DMQS primary data for the slot we    */
            /* wanted.  Fill in the instance data using the DMQS      */
            /* primary data.                                          */
            /**********************************************************/
            instances[0].pos_id = XGA_DMQS_ID;
            instances[0].XGA_level = pDMQSPDataInstance->usXGALevel;
            instances[0].XGA_io_base = pDMQSPDataInstance->usIOBase;
            instances[0].XGA_base_vram = pDMQSPDataInstance->usVRAMBase
                                                            * (LONG)0x100000;
            instances[0].XGA_monitor_id = pDMQSPDataInstance->usMonitorID;
            instances[0].XGA_vram_size = pDMQSPDataInstance->bVRAMSize
                                                             * (LONG)0x40000;
            instances[0].XGA_1M_aperture_phys =
                                         pDMQSPDataInstance->us1MPhysAperture
                                                            * (LONG)0x100000;
            instances[0].XGA_4M_aperture_phys =
                                         pDMQSPDataInstance->us4MPhysAperture
                                                            * (LONG)0x100000;
            instances[0].XGA_mem_map_phys =
                                  ((ULONG)pDMQSPDataInstance->usRegSeg) << 4;

            /**********************************************************/
            /* These are the related fields that are not supplied by  */
            /* the DMQS primary data.  The primary_adapter flag is    */
            /* the currently the only one of these that we actually   */
            /* need. This will be set up later by the ring0 code in   */
            /*                                                        */
            /**********************************************************/
            instances[0].pos_data[0] = 0;
            instances[0].pos_data[1] = 0;
            instances[0].pos_data[2] = 0;
            instances[0].pos_data[3] = 0;
            instances[0].XGA_mem_map_virt = 0;
            instances[0].XGA_mem_map_flat = 0;
            instances[0].primary_adapter = 0;
            instances[0].phys_addr_instance = 0;
            instances[0].XGA_1M_aperture_flat = 0;
            instances[0].XGA_4M_aperture_flat = 0;

            return;
        }
    }
    /******************************************************************/
    /* We could not find any DMQS data for the slot we wanted so zero */
    /* out the POS ID to indicate this to the calling function.       */
    /******************************************************************/
    instances[0].pos_id = 0;
}
