//////////////////////////////////////////////////////////////////////
// File - SCREEN_LIB.C
//
// Library for accessing the SCREEN card.
// Code was generated by Driver Wizard.
// It accesses the hardware via WinDriver functions.
//
// Copyrights (c) Jungo Ltd. 2000 
//////////////////////////////////////////////////////////////////////

#include "screen_lib.h"
#include <stdio.h>

// This string is set to an error message, if one occurs
CHAR SCREEN_ErrorString[1024];

// Internal data structures
typedef struct
{
    DWORD index;
    DWORD dwMask;
    BOOL  fIsMemory;
    BOOL  fActive;
} SCREEN_ADDR_DESC;

typedef struct SCREEN_STRUCT
{
    HANDLE hWD;
    BOOL   fUseInt;
    WD_PCI_SLOT pciSlot;
    SCREEN_ADDR_DESC addrDesc[SCREEN_ITEMS];
    WD_CARD_REGISTER cardReg;
} SCREEN_STRUCT;

// Internal function used by SCREEN_Open()
BOOL SCREEN_DetectCardElements(SCREEN_HANDLE hSCREEN);

DWORD SCREEN_CountCards (DWORD dwVendorID, DWORD dwDeviceID)
{
    WD_VERSION ver;
    WD_PCI_SCAN_CARDS pciScan;
    HANDLE hWD;

    SCREEN_ErrorString[0] = '\0';
    hWD = WD_Open();

    // Check if handle valid & version OK
    if (hWD==INVALID_HANDLE_VALUE) 
    {
        sprintf( SCREEN_ErrorString, "Failed opening WinDriver device\n");
        return 0;
    }

    BZERO(ver);
    WD_Version(hWD,&ver);
    if (ver.dwVer<WD_VER) 
    {
        sprintf( SCREEN_ErrorString, "Incorrect WinDriver version\n");
        WD_Close (hWD);
        return 0;
    }

    BZERO(pciScan);
    pciScan.searchId.dwVendorId = dwVendorID;
    pciScan.searchId.dwDeviceId = dwDeviceID;
    WD_PciScanCards (hWD, &pciScan);
    WD_Close (hWD);
    if (pciScan.dwCards==0)
        sprintf( SCREEN_ErrorString, "no cards found\n");
    return pciScan.dwCards;
}

BOOL SCREEN_Open (SCREEN_HANDLE *phSCREEN, DWORD dwVendorID, DWORD dwDeviceID, DWORD nCardNum, DWORD dwOptions)
{
    SCREEN_HANDLE hSCREEN = (SCREEN_HANDLE) malloc (sizeof (SCREEN_STRUCT));

    WD_VERSION ver;
    WD_PCI_SCAN_CARDS pciScan;
    WD_PCI_CARD_INFO pciCardInfo;

    *phSCREEN = NULL;
    SCREEN_ErrorString[0] = '\0';
    BZERO(*hSCREEN);

    hSCREEN->cardReg.hCard = 0;
    hSCREEN->hWD = WD_Open();

    // Check if handle valid & version OK
    if (hSCREEN->hWD==INVALID_HANDLE_VALUE)
    {
        sprintf( SCREEN_ErrorString, "Failed opening WinDriver device\n");
        goto Exit;
    }

    BZERO(ver);
    WD_Version(hSCREEN->hWD,&ver);
    if (ver.dwVer<WD_VER)
    {
        sprintf( SCREEN_ErrorString, "Incorrect WinDriver version\n");
        goto Exit;
    }

    BZERO(pciScan);
    pciScan.searchId.dwVendorId = dwVendorID;
    pciScan.searchId.dwDeviceId = dwDeviceID;
    WD_PciScanCards (hSCREEN->hWD, &pciScan);
    if (pciScan.dwCards==0) // Found at least one card
    {
        sprintf( SCREEN_ErrorString, "Could not find PCI card\n");
        goto Exit;
    }
    if (pciScan.dwCards<=nCardNum)
    {
        sprintf( SCREEN_ErrorString, "Card out of range of available cards\n");
        goto Exit;
    }

    BZERO(pciCardInfo);
    pciCardInfo.pciSlot = pciScan.cardSlot[nCardNum];
    WD_PciGetCardInfo (hSCREEN->hWD, &pciCardInfo);
    hSCREEN->pciSlot = pciCardInfo.pciSlot;
    hSCREEN->cardReg.Card = pciCardInfo.Card;

    hSCREEN->fUseInt = (dwOptions & SCREEN_OPEN_USE_INT) ? TRUE : FALSE;
    if (!hSCREEN->fUseInt)
    {
        DWORD i;
        // Remove interrupt item if not needed
        for (i=0; i<hSCREEN->cardReg.Card.dwItems; i++)
        {
            WD_ITEMS *pItem = &hSCREEN->cardReg.Card.Item[i];
            if (pItem->item==ITEM_INTERRUPT)
                pItem->item = ITEM_NONE;
        }
    }
    //else
    {
        DWORD i;
        // Make interrupt resource sharable
        for (i=0; i<hSCREEN->cardReg.Card.dwItems; i++)
        {
            WD_ITEMS *pItem = &hSCREEN->cardReg.Card.Item[i];
            //if (pItem->item==ITEM_INTERRUPT)
                pItem->fNotSharable = FALSE;
        }
    }

    hSCREEN->cardReg.fCheckLockOnly = FALSE;
    WD_CardRegister (hSCREEN->hWD, &hSCREEN->cardReg);
    if (hSCREEN->cardReg.hCard==0)
    {
        sprintf ( SCREEN_ErrorString, "Failed locking device.\n");
        goto Exit;
    }

    if (!SCREEN_DetectCardElements(hSCREEN))
    {
        sprintf ( SCREEN_ErrorString, "Card does not have all items expected for SCREEN\n");
        goto Exit;
    }

    // Open finished OK
    *phSCREEN = hSCREEN;
    return TRUE;

Exit:
    // Error during Open
    if (hSCREEN->cardReg.hCard) 
        WD_CardUnregister(hSCREEN->hWD, &hSCREEN->cardReg);
    if (hSCREEN->hWD!=INVALID_HANDLE_VALUE)
        WD_Close(hSCREEN->hWD);
    free (hSCREEN);
    return FALSE;
}

void SCREEN_Close(SCREEN_HANDLE hSCREEN)
{
    // Unregister card
    if (hSCREEN->cardReg.hCard) 
        WD_CardUnregister(hSCREEN->hWD, &hSCREEN->cardReg);

    // Close WinDriver
    WD_Close(hSCREEN->hWD);

    free (hSCREEN);
}

DWORD SCREEN_ReadPCIReg(SCREEN_HANDLE hSCREEN, DWORD dwReg)
{
    WD_PCI_CONFIG_DUMP pciCnf;
    DWORD dwVal;

    BZERO(pciCnf);
    pciCnf.pciSlot = hSCREEN->pciSlot;
    pciCnf.pBuffer = &dwVal;
    pciCnf.dwOffset = dwReg;
    pciCnf.dwBytes = 4;
    pciCnf.fIsRead = TRUE;
    WD_PciConfigDump(hSCREEN->hWD,&pciCnf);
    return dwVal;
}

BOOL SCREEN_DetectCardElements(SCREEN_HANDLE hSCREEN)
{
    DWORD i;
    DWORD ad_sp;

    BZERO(hSCREEN->addrDesc);

    for (i=0; i<hSCREEN->cardReg.Card.dwItems; i++)
    {
        WD_ITEMS *pItem = &hSCREEN->cardReg.Card.Item[i];

        switch (pItem->item)
        {
        case ITEM_MEMORY:
        case ITEM_IO:
            {
                DWORD dwBytes;
                DWORD dwPhysAddr;
                BOOL fIsMemory;
                if (pItem->item==ITEM_MEMORY)
                {
                    dwBytes = pItem->I.Mem.dwBytes;
                    dwPhysAddr = pItem->I.Mem.dwPhysicalAddr;
                    fIsMemory = TRUE;
                }
                else 
                {
                    dwBytes = pItem->I.IO.dwBytes;
                    dwPhysAddr = pItem->I.IO.dwAddr;
                    fIsMemory = FALSE;
                }

                for (ad_sp=0; ad_sp<SCREEN_ITEMS; ad_sp++)
                {
                    DWORD dwPCIAddr;
                    DWORD dwPCIReg;

                    if (SCREEN_IsAddrSpaceActive(hSCREEN, (SCREEN_ADDR) ad_sp)) continue;
                    if (ad_sp<SCREEN_AD_EPROM) dwPCIReg = PCI_BAR0 + 4*ad_sp;
                    else dwPCIReg = PCI_ERBAR;
                    dwPCIAddr = SCREEN_ReadPCIReg(hSCREEN, dwPCIReg);
                    if (dwPCIAddr & 1)
                    {
                        if (fIsMemory) continue;
                        dwPCIAddr &= ~0x3;
                    }
                    else
                    {
                        if (!fIsMemory) continue;
                        dwPCIAddr &= ~0xf;
                    }
                    if (dwPCIAddr==dwPhysAddr)
                        break;
                }
                if (ad_sp<SCREEN_ITEMS)
                {
                    DWORD j;
                    hSCREEN->addrDesc[ad_sp].fActive = TRUE;
                    hSCREEN->addrDesc[ad_sp].index = i;
                    hSCREEN->addrDesc[ad_sp].fIsMemory = fIsMemory;
                    hSCREEN->addrDesc[ad_sp].dwMask = 0;
                    for (j=1; j<dwBytes && j!=0x80000000; j *= 2)
                    {
                        hSCREEN->addrDesc[ad_sp].dwMask = 
                            (hSCREEN->addrDesc[ad_sp].dwMask << 1) | 1;
                    }
                }
            }
            break;
        }
    }

    // Check that all the items needed were found
    // Check that at least one memory space was found
    for (i = 0; i<SCREEN_ITEMS; i++)
        if (SCREEN_IsAddrSpaceActive(hSCREEN, (SCREEN_ADDR) i)) break;
    if (i==SCREEN_ITEMS) return FALSE;

    return TRUE;
}

BOOL SCREEN_IsAddrSpaceActive(SCREEN_HANDLE hSCREEN, SCREEN_ADDR addrSpace)
{
    return hSCREEN->addrDesc[addrSpace].fActive;
}

// General read/write function
void SCREEN_ReadWriteBlock(SCREEN_HANDLE hSCREEN, SCREEN_ADDR addrSpace, DWORD dwOffset, BOOL fRead, PVOID buf, DWORD dwBytes, SCREEN_MODE mode)
{
    WD_TRANSFER trans;
    BOOL fMem = hSCREEN->addrDesc[addrSpace].fIsMemory;
    // Safty check: is the address range active
    if (!SCREEN_IsAddrSpaceActive(hSCREEN, addrSpace)) return;
    BZERO(trans);
    if (fRead)
    {
        if (mode==SCREEN_MODE_BYTE) trans.cmdTrans = fMem ? RM_SBYTE : RP_SBYTE;
        else if (mode==SCREEN_MODE_WORD) trans.cmdTrans = fMem ? RM_SWORD : RP_SWORD;
        else if (mode==SCREEN_MODE_DWORD) trans.cmdTrans = fMem ? RM_SDWORD : RP_SDWORD;
    }
    else
    {
        if (mode==SCREEN_MODE_BYTE) trans.cmdTrans = fMem ? WM_SBYTE : WP_SBYTE;
        else if (mode==SCREEN_MODE_WORD) trans.cmdTrans = fMem ? WM_SWORD : WP_SWORD;
        else if (mode==SCREEN_MODE_DWORD) trans.cmdTrans = fMem ? WM_SDWORD : WP_SDWORD;
    }
    if (fMem)
        trans.dwPort = hSCREEN->cardReg.Card.Item[hSCREEN->addrDesc[addrSpace].index].I.Mem.dwTransAddr;
    else trans.dwPort = hSCREEN->cardReg.Card.Item[hSCREEN->addrDesc[addrSpace].index].I.IO.dwAddr;
    trans.dwPort += dwOffset;

    trans.fAutoinc = TRUE;
    trans.dwBytes = dwBytes;
    trans.dwOptions = 0;
    trans.Data.pBuffer = buf;
    WD_Transfer (hSCREEN->hWD, &trans);
}

void SCREEN_WriteDword (SCREEN_HANDLE hSCREEN, SCREEN_ADDR addrSpace, DWORD dwOffset, DWORD data)
{
    if (hSCREEN->addrDesc[addrSpace].fIsMemory)
    {
        PDWORD pData = (PDWORD) (hSCREEN->cardReg.Card.Item[hSCREEN->addrDesc[addrSpace].index].I.Mem.dwUserDirectAddr + dwOffset);
        *pData = data; // Write to the memory mapped range directly
    }
    else SCREEN_ReadWriteBlock( hSCREEN, addrSpace, dwOffset, FALSE, &data, sizeof (DWORD), SCREEN_MODE_DWORD);
}

BOOL PCI_Get_WD_handle(HANDLE *phWD)
{
    WD_VERSION ver;

    *phWD = WD_Open();

    // Check whether handle is valid and version OK
    if (*phWD==INVALID_HANDLE_VALUE) 
    {
        printf("Failed opening WinDriver device\n");
        return FALSE;
    }

    BZERO(ver);
    WD_Version(*phWD,&ver);
    if (ver.dwVer<WD_VER) 
    {
        printf("Incorrect WinDriver version\n");
        WD_Close (*phWD);
        *phWD = INVALID_HANDLE_VALUE;
        return FALSE;
    }

    return TRUE;
}

SCREEN_HANDLE SCREEN_LocateAndOpenBoard (DWORD dwVendorID, DWORD dwDeviceID, BOOL fUseInt, PVOID *pScreen)
{
    DWORD cards, my_card;
    SCREEN_HANDLE hSCREEN = NULL;
    char line[256];

    cards = SCREEN_CountCards (dwVendorID, dwDeviceID);
    if (cards==0)
    {
        printf("%s", SCREEN_ErrorString);
        return NULL;
    }
    else if (cards==1)
        my_card = 1;
    else
    {
        DWORD i;

        printf("Found %d matching PCI cards\n", cards);
        printf("Select card (1-%d): ", cards);
        i = 0;
        gets(line);
        sscanf (line, "%d",&i);
        if (i>=1 && i <=cards) my_card = i;
        else 
        {
            printf ("Choice out of range\n");
            return NULL;
        }
    }
    if (!SCREEN_Open (&hSCREEN, dwVendorID, dwDeviceID, my_card - 1, fUseInt ? SCREEN_OPEN_USE_INT : 0 ))
        return NULL;
    *pScreen = (PVOID) hSCREEN->cardReg.Card.Item[hSCREEN->addrDesc[0].index].I.Mem.dwUserDirectAddr;
    return hSCREEN;
}

BOOL DrawPicture(PVOID pBuffer, FRAME_INFO frameInfo, BOOL first_time, BOOL last_time)
{
    int i;
    BYTE *pBGR_Buffer = (BYTE *)pBuffer;
    static SCREEN_HANDLE hSCREEN = NULL;
    static PVOID pScreen = 0;
    PBYTE pb;

    if (first_time)
    {
        if (SCREEN_DEFAULT_VENDOR_ID)
            hSCREEN = SCREEN_LocateAndOpenBoard(SCREEN_DEFAULT_VENDOR_ID, SCREEN_DEFAULT_DEVICE_ID, FALSE, &pScreen);
        else
            return (FALSE);

        if (!hSCREEN)
            return (FALSE);
    }
    if (!pScreen)
        return FALSE;

    pb = (PBYTE) pScreen;
    for (i=0; i<frameInfo.frameRowNum; i++)
        memcpy(pb + STRID*3*i, pBGR_Buffer + frameInfo.frameColNum*3*i, frameInfo.frameColNum*3);

    if (last_time)
    {
        if (hSCREEN)
            SCREEN_Close(hSCREEN);
        pScreen = NULL;
        hSCREEN = NULL;
    }
}