////////////////////////////////////////////////////////////////
// File - P9050_DIAG.C
//
// o A simple diagnostics program that lets you access the
//   PLX 9050 registers and local memory. 
// o This program is meant to be used as an example for using the P9050_LIB.H API,
//   you may use it as a skeleton for your driver, or 'cut & paste' parts
//   of it into your device driver code.
// o For a more advanced monitor program, use the standard PLXMON.EXE
//   from PLX.
// 
////////////////////////////////////////////////////////////////

#include "../lib/p9050_lib.h"
#include "../../../samples/shared/pci_diag_lib.h"
#include <stdio.h>

// input of command from user
static char line[256];

void PLX_EditReg(P9050_HANDLE hPlx)
{
    struct 
    {
        CHAR *name;
        DWORD dwOffset;
        DWORD dwVal;
    } fields[30];

    int cmd;
    int i;
    int field_count;

    i = 0;
    fields[i].name = "LAS0RR"; fields[i++].dwOffset = 0x00;
    fields[i].name = "LAS1RR"; fields[i++].dwOffset = 0x04;
    fields[i].name = "LAS2RR"; fields[i++].dwOffset = 0x08;
    fields[i].name = "LAS3RR"; fields[i++].dwOffset = 0x0c;
    fields[i].name = "EROMRR"; fields[i++].dwOffset = 0x10;
    fields[i].name = "LAS0BA"; fields[i++].dwOffset = 0x14;
    fields[i].name = "LAS1BA"; fields[i++].dwOffset = 0x18;
    fields[i].name = "LAS2BA"; fields[i++].dwOffset = 0x1c;
    fields[i].name = "LAS3BA"; fields[i++].dwOffset = 0x20;
    fields[i].name = "EROMBA"; fields[i++].dwOffset = 0x24;
    fields[i].name = "LAS0BRD"; fields[i++].dwOffset = 0x28;
    fields[i].name = "LAS1BRD"; fields[i++].dwOffset = 0x2c;
    fields[i].name = "LAS2BRD"; fields[i++].dwOffset = 0x30;
    fields[i].name = "LAS3BRD"; fields[i++].dwOffset = 0x34;
    fields[i].name = "EROMBRD"; fields[i++].dwOffset = 0x38;
    fields[i].name = "CS0BASE"; fields[i++].dwOffset = 0x3c;
    fields[i].name = "CS1BASE"; fields[i++].dwOffset = 0x40;
    fields[i].name = "CS2BASE"; fields[i++].dwOffset = 0x44;
    fields[i].name = "CS3BASE"; fields[i++].dwOffset = 0x48;
    fields[i].name = "INTCSR"; fields[i++].dwOffset = 0x4c;
    fields[i].name = "CNTRL"; fields[i++].dwOffset = 0x50;
    field_count = i;
    do
    {
        int row;
        int col;
        int row_count = field_count/2 + field_count%2;

        printf ("\n");
        printf ("Edit PLX 9050 registers\n");
        printf ("--------------------------------\n");
        for (row = 0; row<row_count; row++)
        {
            for (col = 0; col<=1; col++)
            {
                if (col==0) i = row;
                else i = row + row_count;

                if (i<field_count)
                {
                    char buf[10];
                    fields[i].dwVal = P9050_ReadReg(hPlx, fields[i].dwOffset);
                    sprintf(buf, "%08x",fields[i].dwVal);
                    printf ("%2d. %7s : %s     ",i+1, fields[i].name, buf);
                }
                if (col==1) printf ("\n");
            }
        }

        printf ("99. Back to main menu\n");
        printf ("Choose register to write to, or 99 to exit: ");
        cmd = 0;
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%d",&cmd);
        if (cmd>=1 && cmd <=21)
        {
            i = cmd-1;
            printf ("Enter value to write to %s register (or 'X' to cancel): ",fields[i].name);
            fgets(line, sizeof(line), stdin);
            if (toupper (line[0])!='X')
            {
                DWORD dwVal;
                dwVal = 0;
                sscanf (line,"%x",&dwVal);
                P9050_WriteReg(hPlx, fields[i].dwOffset, dwVal);
            }
        }
    } while (cmd!=99);
}

char *PLX_GetAddrRangeName(P9050_ADDR addrSpace)
{
    return 
        addrSpace==P9050_ADDR_SPACE0 ? "Addr Space 0 - (BAR2)" :
        addrSpace==P9050_ADDR_SPACE1 ? "Addr Space 1 - (BAR3)" :
        addrSpace==P9050_ADDR_SPACE2 ? "Addr Space 2 - (BAR4)" :
        addrSpace==P9050_ADDR_SPACE3 ? "Addr Space 3 - (BAR5)" :
        addrSpace==P9050_ADDR_EPROM ? "EEPROM Addr Space" : "Invalid";
}

void PLX_BoardAccess(P9050_HANDLE hPlx, BOOL fLocalAddr)
{
    int cmd, cmd2, i;
    DWORD addr, data;
    P9050_ADDR ad_sp = P9050_ADDR_SPACE0;
    P9050_MODE ad_mode = P9050_MODE_DWORD;
    char *pcMemoryType = fLocalAddr ? "local address" : "offset";

    for (; ad_sp<=P9050_ADDR_EPROM && !P9050_IsAddrSpaceActive(hPlx, ad_sp); ad_sp++)
    if (ad_sp>P9050_ADDR_EPROM)
    {
        printf ("No active memory spaces on board!\n");
        return;
    }

    do
    {
        printf ("Access the board's %s ranges\n",pcMemoryType);
        printf ("-------------------------------------------\n");
        printf ("(Access to invalid %s may hang the computer!)\n", pcMemoryType);
        printf ("1. Change active memory space: %s\n",PLX_GetAddrRangeName(ad_sp));
        printf ("2. Toggle active mode: %s\n", 
            ad_mode==P9050_MODE_BYTE ? "BYTE (8 bit)" :
            ad_mode==P9050_MODE_WORD ? "WORD (16 bit)" : "DWORD (32 bit)");
        printf ("3. Read from board\n");
        printf ("4. Write to board\n");
        printf ("99. Back to main menu\n");
        printf ("\n");
        printf ("Enter option: ");
        cmd = 0;
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%d",&cmd);
        switch (cmd)
        {
        case 1:
            printf ("Choose memory space:\n");
            printf ("--------------------\n");
            for (i=P9050_ADDR_SPACE0; i<=P9050_ADDR_EPROM; i++)
            {
                printf ("%d. %s", i, PLX_GetAddrRangeName(i));
                if (P9050_IsAddrSpaceActive(hPlx, i)) printf ("\n");
                else printf (" - space not active\n");
            }
            printf ("Enter option: ");
            cmd2 = 99;
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%d",&cmd2);
            if (cmd2>=P9050_ADDR_SPACE0 && cmd2<=P9050_ADDR_EPROM)
            {
                int new_ad_sp = cmd2;
                if (P9050_IsAddrSpaceActive(hPlx, new_ad_sp)) ad_sp = new_ad_sp;
                else printf ("Chosen space not active!\n");
            }
            break;
        case 2:
            ad_mode = (ad_mode + 1) % 3;
            break;
        case 3:
            printf ("Enter %s to read from: ", pcMemoryType);
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            switch (ad_mode)
            {
            case P9050_MODE_BYTE:
                if (fLocalAddr) data = P9050_ReadByte(hPlx, ad_sp, addr);
                else data = P9050_ReadSpaceByte(hPlx, ad_sp, addr);
                break;
            case P9050_MODE_WORD:
                if (fLocalAddr) data = P9050_ReadWord(hPlx, ad_sp, addr);
                else data = P9050_ReadSpaceWord(hPlx, ad_sp, addr);
                break;
            case P9050_MODE_DWORD:
                if (fLocalAddr) data = P9050_ReadDWord(hPlx, ad_sp, addr);
                else data = P9050_ReadSpaceDWord(hPlx, ad_sp, addr);
                break;
            }
            printf ("Value read: %x\n", data);
            break;
        case 4:
            printf ("Enter %s to write to: ", pcMemoryType);
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            printf ("Enter data to write %s: ",
                ad_mode==P9050_MODE_BYTE ? "BYTE (8 bit)" :
                ad_mode==P9050_MODE_WORD ? "WORD (16 bit)" : "DWORD (32 bit)");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x",&data);
            switch (ad_mode)
            {
            case P9050_MODE_BYTE:
                if (fLocalAddr) P9050_WriteByte(hPlx, ad_sp, addr, (BYTE) data);
                else P9050_WriteSpaceByte(hPlx, ad_sp, addr, (BYTE) data);
                break;
            case P9050_MODE_WORD:
                if (fLocalAddr) P9050_WriteWord(hPlx, ad_sp, addr, (WORD) data);
                else P9050_WriteSpaceWord(hPlx, ad_sp, addr, (WORD) data);
                break;
            case P9050_MODE_DWORD:
                if (fLocalAddr) P9050_WriteDWord(hPlx, ad_sp, addr, data);
                else P9050_WriteSpaceDWord(hPlx, ad_sp, addr, data);
                break;
            }
            break;
        }
    } while (cmd!=99);
}

void WINAPI PLX_IntHandlerRoutine(P9050_HANDLE hPlx, P9050_INT_RESULT *intResult)
{
    printf ("Got interrupt number %d\n", intResult->dwCounter);
}

void PLX_EnableDisableInterrupts(P9050_HANDLE hPlx)
{
    int cmd;

    printf ("WARNING!!!\n");
    printf ("----------\n");
    printf ("Your hardware has level sensitive interrupts.\n");
    printf ("You must modify the source code of P9050_IntEnable(), in the file p9050_lib.c,\n");
    printf ("to acknowledge the interrupt before enabling interrupts.\n");
    printf ("Without this modification, your PC will HANG upon interrupt!\n");

    do
    {
        printf ("Enable / Disable interrupts\n");
        printf ("---------------------------\n");
        printf ("1. %s interrupts\n", P9050_IntIsEnabled(hPlx) ? "Disable" : "Enable");
        printf ("99. Back to main menu\n");
        printf ("\n");
        printf ("Enter option: ");
        cmd = 0;
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%d",&cmd);
        switch (cmd)
        {
        case 1:
            if (P9050_IntIsEnabled(hPlx))
            {
                printf ("Disabling interrupt Int\n");
                P9050_IntDisable(hPlx);
            }
            else
            {
                printf ("Enabling interrupts\n");
                if (!P9050_IntEnable(hPlx, PLX_IntHandlerRoutine))
                    printf ("failed enabling interrupts\n");
            }
            break;
        }
    } while (cmd!=99);
}

void PLX_EEPROMAccess(P9050_HANDLE hPlx)
{
    int cmd;
    DWORD addr;
    DWORD dwData;

    do
    {
        printf ("Access the board's serial EERPOM\n");
        printf ("--------------------------------\n");
        if (!P9050_EEPROMValid(hPlx))
            printf ("Note: PLX EEPROM valid BIT is 0\n");
		printf ("1. Display EEPROM content\n");
        printf ("2. Read dword from serial EEPROM on the board\n");
        printf ("3. Write dword to the serial EEPROM on the board\n");
        printf ("99. Back to main menu\n");
        printf ("\n");
        printf ("Enter option: ");
        cmd = 0;
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%d",&cmd);
        switch (cmd)
        {
		case 1:
			for (addr=0; addr<0xff; addr += 4)
			{
				if (!(addr % 0x10))
				printf("\n %02x: ", addr);
				if (!P9050_EEPROMReadDWord(hPlx, addr, &dwData))
				{
					printf("\nError occured reading serial EEPROM - %s\n", P9050_ErrorString);
					break;
				}
				printf("%08x  ", dwData);
			}
			printf ("\n");
			break;
        case 2:
            printf ("Enter addr to read from (0-7f): ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            if (P9050_EEPROMReadDWord(hPlx, addr, &dwData))
                printf ("Value read: %08x\n", dwData);
             else
                printf("Error occured reading serial EEPROM - %s\n", P9050_ErrorString);
            break;

        case 3:
            printf ("Enter addr to write to (0-7f): ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            printf ("Enter data to write: ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x",&dwData);
            if (!P9050_EEPROMWriteDWord(hPlx, addr, dwData))
                printf("Error occured reading serial EEPROM - %s\n", P9050_ErrorString);

            break;

        default:
            break;
        }
    } while (cmd!=99);
}

P9050_HANDLE PLX_LocateAndOpenBoard(DWORD dwVendorID, DWORD dwDeviceID, BOOL fUseInt)
{
    DWORD cards, my_card;
    P9050_HANDLE hPlx = NULL;

    if (dwVendorID==0)
    {
        printf ("Enter VendorID: ");
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%x",&dwVendorID);
        if (dwVendorID==0) return NULL;

        printf ("Enter DeviceID: ");
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%x",&dwDeviceID);
    }
    cards = P9050_CountCards (dwVendorID, dwDeviceID);
    if (cards==0) 
    {
        printf("%s", P9050_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;
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%d",&i);
        if (i>=1 && i <=cards) my_card = i;
        else 
        {
            printf ("Choice out of range\n");
            return NULL;
        }
    }
    // to correct an errata with BIT7 of BAR0 in some 9050 chips, use P9050_OPEN_FIX_BIT7
    if (P9050_Open (&hPlx, dwVendorID, dwDeviceID, my_card - 1, fUseInt ? P9050_OPEN_USE_INT : 0 /* P9050_OPEN_FIX_BIT7 */ ))
        printf ("PLX 9050 PCI card found!\n");
    else printf ("%s", P9050_ErrorString);
    return hPlx;
}

int main(int argc, char *argv[])
{
    int cmd;
    P9050_HANDLE hPlx = NULL;
    HANDLE hWD;
    BOOL fUseInt = FALSE; // by default - do not install interrupts
    BOOL fOpenedWithInt = fUseInt;
    
    printf ("PLX 9050 diagnostic utility.\n");
    printf ("Application accesses hardware using " WD_PROD_NAME ".\n");

    // make sure WinDriver is loaded
    if (!PCI_Get_WD_handle(&hWD)) return 0;
    WD_Close (hWD);

    hPlx = PLX_LocateAndOpenBoard(0x10b5, 0x9050, fUseInt);

    do
    {
        printf ("\n");
        printf ("PLX 9050 main menu\n");
        printf ("-------------------\n");
        printf ("1. Scan PCI bus\n");
        printf ("2. Set opening board %s interrupts\n", fUseInt ? "without" : "with");
        printf ("3. Locate/Choose PLX 9050 board (%s interrupts)\n", fUseInt ? "with" : "without");
        if (hPlx)
        {
            printf ("4. PCI configuration registers\n");
            printf ("5. PLX 9050 local registers\n");
            printf ("6. Access address spaces on the board\n");
            printf ("7. Access local address ranges on the board\n");
            if (fOpenedWithInt)
                printf ("8. Enable / Disable interrupts\n");
            printf ("9. Access serial EEPROM on the board\n");
        }
        printf ("99. Exit\n");
        printf ("Enter option: ");
        cmd = 0;
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%d",&cmd);
        switch (cmd)
        {
        case 1: // Scan PCI bus
            PCI_Print_all_cards_info();
            break;
        case 2: // Set open board with / without interrupts
            fUseInt = !fUseInt;
            break;
        case 3: // Locate PLX 9050 board
            if (hPlx) P9050_Close(hPlx);
            hPlx = PLX_LocateAndOpenBoard(0, 0, fUseInt);
            if (!hPlx) printf ("PLX card open failed!\n");
            fOpenedWithInt = fUseInt;
            break;
        case 4: // PCI configuration registers
            if (hPlx) 
            {
                WD_PCI_SLOT pciSlot;
                P9050_GetPciSlot(hPlx, &pciSlot);
                PCI_EditConfigReg(pciSlot);
            }
            break;
        case 5: // PLX 9050 local registers
            if (hPlx) PLX_EditReg(hPlx);
            break;
        case 6: // Access address spaces on the board
            if (hPlx) PLX_BoardAccess(hPlx, FALSE);
            break;
        case 7: // Access local address ranges on the board
            if (hPlx) PLX_BoardAccess(hPlx, TRUE);
            break;
        case 8: // Enable / Disable interrupts
            if (hPlx && fOpenedWithInt) PLX_EnableDisableInterrupts(hPlx);
            break;
        case 9: // Access serial EEPROM on the board
            if (hPlx) PLX_EEPROMAccess(hPlx);
            break;
        }
    } while (cmd!=99);

    if (hPlx) P9050_Close(hPlx);

    return 0;
}