////////////////////////////////////////////////////////////////
// File - PBC_DIAG.C
//
// o A simple diagnostics program that lets you access the
//   V3 PBC registers and local memory. 
// o This program is meant to be used as an example for using the
//   PBCLIB.H API,LIB you may use it as a skeleton for your driver,
//   or 'cut & paste' parts of it into your device driver code.
// 
////////////////////////////////////////////////////////////////

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

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

void V3_EditReg(V3PBCHANDLE hV3)
{
    struct 
    {
        CHAR *name;
        DWORD dwOffset;
        DWORD dwVal;
    } fields[40];

    int cmd;
    int i;
    int field_count;

    i = 0;
    fields[i].name = "PCI_DEVICE_VENDOR  "; fields[i++].dwOffset = 0x00;
    fields[i].name = "PCI_STAT_CMD       "; fields[i++].dwOffset = 0x04;
    fields[i].name = "PCI_CC_REV         "; fields[i++].dwOffset = 0x08;
    fields[i].name = "PCI_HDR_CFG        "; fields[i++].dwOffset = 0x0c;
    fields[i].name = "PCI_IO_BASE        "; fields[i++].dwOffset = 0x10;
    fields[i].name = "PCI_BASE0          "; fields[i++].dwOffset = 0x14;
    fields[i].name = "PCI_BASE1          "; fields[i++].dwOffset = 0x18;
    fields[i].name = "PCI_SUB_ID_VENDOR  "; fields[i++].dwOffset = 0x2c;
    fields[i].name = "PCI_ROM            "; fields[i++].dwOffset = 0x30;
    fields[i].name = "PCI_BPARM          "; fields[i++].dwOffset = 0x3c;
    fields[i].name = "PCI_MAP0           "; fields[i++].dwOffset = 0x40;
    fields[i].name = "PCI_MAP1           "; fields[i++].dwOffset = 0x44;
    fields[i].name = "PCI_INT_STAT       "; fields[i++].dwOffset = 0x48;
    fields[i].name = "PCI_INT_CFG        "; fields[i++].dwOffset = 0x4c;
    fields[i].name = "LB_BASE0           "; fields[i++].dwOffset = 0x54;
    fields[i].name = "LB_BASE1           "; fields[i++].dwOffset = 0x58;
    fields[i].name = "LB_MAP0            "; fields[i++].dwOffset = 0x5c;
    fields[i].name = "LB_MAP1            "; fields[i++].dwOffset = 0x60;
    fields[i].name = "LB_IO_BASE         "; fields[i++].dwOffset = 0x6c;
    fields[i].name = "FIFO_PRIORITY_CFG  "; fields[i++].dwOffset = 0x70;
    fields[i].name = "LB_IMASK_ISTAT_STAT"; fields[i++].dwOffset = 0x74;
    fields[i].name = "LB_CFG_SYSTEM      "; fields[i++].dwOffset = 0x78;
    fields[i].name = "DMA_PCI_ADDR0      "; fields[i++].dwOffset = 0x80;
    fields[i].name = "DMA_LOCAL_ADDR0    "; fields[i++].dwOffset = 0x84;
    fields[i].name = "DMA_CRS0_LENGTH0   "; fields[i++].dwOffset = 0x88;
    fields[i].name = "DMA_CTLB_ADDR0     "; fields[i++].dwOffset = 0x8c;
    fields[i].name = "DMA_PCI_ADDR1      "; fields[i++].dwOffset = 0x90;
    fields[i].name = "DMA_LOCAL_ADDR1    "; fields[i++].dwOffset = 0x94;
    fields[i].name = "DMA_CRS0_LENGTH1   "; fields[i++].dwOffset = 0x98;
    fields[i].name = "DMA_CTLB_ADDR1     "; fields[i++].dwOffset = 0x9c;
    fields[i].name = "DMA_CTLB_ADDR1     "; fields[i++].dwOffset = 0x9c;
    fields[i].name = "PCI_MAIL_IERD_IEWR "; fields[i++].dwOffset = 0xd0;
    fields[i].name = "LB_MAIL_IERD_IEWR  "; fields[i++].dwOffset = 0xd4;
    fields[i].name = "MAIL_RD_WR_STAT    "; fields[i++].dwOffset = 0xd8;
    field_count = i;
    do
    {
        int row;
        int col;
        int row_count = field_count/2 + field_count%2;

        printf ("\n");
        printf ("Edit V3 PBC Rev%d registers\n", V3PBC_GetRevision(hV3));
        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 = V3PBC_ReadRegDWord(hV3, 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 <=34)
        {
            i = cmd-1;
            printf ("Enter value to write to %s register ('X' to cancel): ",fields[i].name);
            fgets(line, sizeof(line), stdin);
            if (toupper (line[0])!='X')
            {
                DWORD dwVal;
                dwVal = 0;
                sscanf (line,"%x",&dwVal);
                V3PBC_WriteRegDWord(hV3, fields[i].dwOffset, dwVal);
            }
        }
    } while (cmd!=99);
}

char *V3_GetAddrRangeName(V3PBC_ADDR addrSpace)
{
    return 
        addrSpace==V3PBC_ADDR_IO_BASE ? "Configuration Register Space - (BAR0)" :
        addrSpace==V3PBC_ADDR_BASE0 ? "Aperture 0 Space - (BAR1)" :
        addrSpace==V3PBC_ADDR_BASE1 ? "Aperture 1 Space - (BAR2)" :
        addrSpace==V3PBC_ADDR_ROM ? "ROM Address Space" : "Invalid";
}

void V3_MailboxAccess(V3PBCHANDLE hV3)
{
    int cmd;
    BYTE addr;
    BYTE data;

    do
    {
        printf ("Access the board's mailbox data registers\n");
        printf ("--------------------------------\n");
        printf ("1. Read byte from mailbox data register on the board\n");
        printf ("2. Write byte to the mailbox data register 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:
            printf ("Enter mailbox data register to read from (0-15): ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%d", &addr);
            if (addr <= 15)
            {
                data = V3PBC_ReadRegByte(hV3, V3PBC_MAIL_DATA0 + addr); 
                printf ("Value read: %x\n", data);
            }
            else
                printf("Invalid range for mailbox data register\n");
            
            break;

        case 2:
            printf ("Enter mailbox data register to write to (0-15): ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%d", &addr);
            if (addr <= 15)
            {
                printf ("Enter data to write (0-ff): ");
                fgets(line, sizeof(line), stdin);
                sscanf (line, "%x",&data);
                V3PBC_WriteRegByte(hV3, V3PBC_MAIL_DATA0 + addr, data); 
            }
            else
                printf("Invalid range for mailbox data register\n");

            break;

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

void V3_BoardAccess(V3PBCHANDLE hV3)
{
    int cmd;
    DWORD addr, data, ad_mode;
    ad_mode = V3PBC_MODE_DWORD;
    if (!V3PBC_IsAddrSpaceActive(hV3, V3PBC_ADDR_BASE0))
    {
        printf ("BASE0 memory space not active!\n");
        return;
    }

    do
    {
        printf ("Access the board's local address ranges\n");
        printf ("---------------------------------------\n");
        printf ("(Access to invalid local address may hang the computer!)\n");
        printf ("2. Toggle active mode: %s\n", 
            ad_mode==V3PBC_MODE_BYTE ? "BYTE (8 bit)" :
            ad_mode==V3PBC_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 2:
            ad_mode = (ad_mode + 1) % 3;
            break;
        case 3:
            printf ("Enter local address to read from: ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            switch (ad_mode)
            {
            case V3PBC_MODE_BYTE:
                data = V3PBC_ReadByte(hV3, addr);
                break;
            case V3PBC_MODE_WORD:
                data = V3PBC_ReadWord(hV3, addr);
                break;
            case V3PBC_MODE_DWORD:
                data = V3PBC_ReadDWord(hV3, addr);
                break;
            }
            printf ("Value read: %x\n", data);
            break;
        case 4:
            printf ("Enter local address to write to: ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            printf ("Enter data to write %s: ",
                ad_mode==V3PBC_MODE_BYTE ? "BYTE (8 bit)" :
                ad_mode==V3PBC_MODE_WORD ? "WORD (16 bit)" : "DWORD (32 bit)");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x",&data);
            switch (ad_mode)
            {
            case V3PBC_MODE_BYTE:
                V3PBC_WriteByte(hV3, addr, (BYTE) data);
                break;
            case V3PBC_MODE_WORD:
                V3PBC_WriteWord(hV3, addr, (WORD) data);
                break;
            case V3PBC_MODE_DWORD:
                V3PBC_WriteDWord(hV3, addr, data);
                break;
            }
            break;
        }
    } while (cmd!=99);
}

void V3_BoardAccessSpaces(V3PBCHANDLE hV3)
{
    int cmd, cmd2, i;
    DWORD addr, data;
    V3PBC_ADDR ad_sp = V3PBC_ADDR_IO_BASE;
    V3PBC_MODE ad_mode = V3PBC_MODE_DWORD;

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

    do
    {
        printf ("Access the board's memory ranges\n");
        printf ("--------------------------------\n");
        printf ("(Access to invalid local address may hang the computer!)\n");
        printf ("1. Change active memory space: %s\n",V3_GetAddrRangeName(ad_sp));
        printf ("2. Toggle active mode: %s\n", 
            ad_mode==V3PBC_MODE_BYTE ? "BYTE (8 bit)" :
            ad_mode==V3PBC_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=V3PBC_ADDR_IO_BASE; i<=V3PBC_ADDR_ROM; i++)
            {
                printf ("%d. %s", i, V3_GetAddrRangeName(i));
                if (V3PBC_IsAddrSpaceActive(hV3, 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>=V3PBC_ADDR_IO_BASE && cmd2<=V3PBC_ADDR_ROM)
            {
                int new_ad_sp = cmd2;
                if (V3PBC_IsAddrSpaceActive(hV3, 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 offset to read from: ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            switch (ad_mode)
            {
            case V3PBC_MODE_BYTE:
                data = V3PBC_ReadSpaceByte(hV3, ad_sp, addr);
                break;
            case V3PBC_MODE_WORD:
                data = V3PBC_ReadSpaceWord(hV3, ad_sp, addr);
                break;
            case V3PBC_MODE_DWORD:
                data = V3PBC_ReadSpaceDWord(hV3, ad_sp, addr);
                break;
            }
            printf ("Value read: %x\n", data);
            break;
        case 4:
            printf ("Enter offset to write to: ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            printf ("Enter data to write %s: ",
                ad_mode==V3PBC_MODE_BYTE ? "BYTE (8 bit)" :
                ad_mode==V3PBC_MODE_WORD ? "WORD (16 bit)" : "DWORD (32 bit)");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x",&data);
            switch (ad_mode)
            {
            case V3PBC_MODE_BYTE:
                V3PBC_WriteSpaceByte(hV3, ad_sp, addr, (BYTE) data);
                break;
            case V3PBC_MODE_WORD:
                V3PBC_WriteSpaceWord(hV3, ad_sp, addr, (WORD) data);
                break;
            case V3PBC_MODE_DWORD:
                V3PBC_WriteSpaceDWord(hV3, ad_sp, addr, data);
                break;
            }
            break;
        }
    } while (cmd!=99);
}

void WINAPI V3_IntHandlerRoutine(V3PBCHANDLE hV3, V3PBC_INT_RESULT *intResult)
{
    printf ("Got interrupt number %d\n", intResult->dwCounter);
}

void V3_EnableDisableInterrupts(V3PBCHANDLE hV3)
{
    int cmd;

    printf ("WARNING!!!\n");
    printf ("----------\n");
    printf ("Your hardware has level sensitive interrupts.\n");
    printf ("You must modify the source code of V3PBC_IntEnable(), in the file pbclib.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", V3PBC_IntIsEnabled(hV3) ? "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 (V3PBC_IntIsEnabled(hV3))
            {
                printf ("Disabling interrupt Int\n");
                V3PBC_IntDisable(hV3);
            }
            else
            {
                printf ("Enabling interrupts\n");
                if (!V3PBC_IntEnable(hV3, V3_IntHandlerRoutine))
                    printf ("failed enabling interrupts\n");
            }
            break;
        }
    } while (cmd!=99);
}

void V3_EEPROMAccess(V3PBCHANDLE hV3)
{
    int cmd;
    DWORD addr;
    BYTE device, data, readback;
    DWORD dwData;

    do
    {
        printf ("Access the board's serial EERPOM\n");
        printf ("--------------------------------\n");
        printf ("1. Read byte from serial EEPROM on the board\n");
        printf ("2. Write byte to the serial EEPROM on the board\n");
        printf ("99. Back to main menu\n");
        printf ("\n");
        printf ("Enter option: ");
        cmd = 0;
        // force the serial boot device address to zero
        device = 0;
        fgets(line, sizeof(line), stdin);
        sscanf (line, "%d",&cmd);
        switch (cmd)
        {
        case 1:
            printf ("Enter addr to read from (0-ff): ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            if (V3PBC_EEPROMRead(hV3, device, (BYTE) addr, &data))
                printf ("Value read: %x\n", data);
             else
                printf("Error occured reading serial EEPROM\n");
            break;

        case 2:
            printf ("Enter addr to write to (0-ff): ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x", &addr);
            printf ("Enter data to write: ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%x",&dwData);
            data = (BYTE) dwData;
            if (!V3PBC_EEPROMWrite(hV3, device, (BYTE) addr, data))
                printf("Error occured reading serial EEPROM\n");

            if (V3PBC_EEPROMRead(hV3, device, (BYTE) addr, &readback))
            {
                if (data != readback)
                    printf ("Value readback did not match value written :  %x\n", readback);
            }
            else
                printf("Error occured during serial EEPROM readback\n");

            break;

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

void V3_PulseLocalReset(V3PBCHANDLE hV3)
{
    int cmd;
    WORD delay;

    do
    {
        printf ("Pulse the local bus reset\n");
        printf ("--------------------------------\n");
        printf ("1. Enter reset duration in milliseconds\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 ("Enter delay in milliseconds: (0-65535) ");
            fgets(line, sizeof(line), stdin);
            sscanf (line, "%d", &delay);
            V3PBC_PulseLocalReset(hV3, delay);
            break;

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

V3PBCHANDLE V3_LocateAndOpenBoard(DWORD dwVendorID, DWORD dwDeviceID, BOOL fUseInt)
{
    DWORD cards, my_card;
    V3PBCHANDLE hV3 = 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 = V3PBC_CountCards (dwVendorID, dwDeviceID);
    if (cards==0) 
    {
        printf("%s", V3PBC_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;
        }
    }
    if (V3PBC_Open (&hV3, dwVendorID, dwDeviceID, my_card - 1, fUseInt ? V3PBC_OPEN_USE_INT : 0))
        printf ("V3 PBC PCI card found!\n");
    else printf ("%s", V3PBC_ErrorString);
    return hV3;
}

int main(int argc, char *argv[])
{
    int cmd;
    V3PBCHANDLE hV3 = NULL;
    HANDLE hWD;
    BOOL fUseInt = FALSE; // by default - do not install interrupts

    printf ("V3 Semiconductor PBC diagnostic utility. Version 1.0\n");
    printf ("Using PBCLIB Version %d.%02d\n", V3PBCLIB_VER/100, V3PBCLIB_VER%100);
    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);

    hV3 = V3_LocateAndOpenBoard(0x11b0, 0x0004, fUseInt);

    /* If board found initialize I2C bus */
    if (hV3)
        V3PBC_EEPROMInit(hV3);

    do
    {
        printf ("\n");
        printf ("V3 PBC 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 V3 PBC board (%s interrupts)\n", fUseInt ? "with" : "without");
        if (hV3)
        {
            printf ("4. PCI configuration registers\n");
            printf ("5. V3 PBC local registers\n");
            printf ("6. Access mailbox data registers on the board\n");
            printf ("7. Access local address ranges on the board\n");
            printf ("8. Access memory ranges on the board\n");
            if (hV3->fUseInt)
                printf ("9. Enable / Disable interrupts\n");
            printf ("10. Access serial EEPROM on the board\n");
            printf ("11. Pulse local reset 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 V3 PBC board
            if (hV3) V3PBC_Close(hV3);
            hV3 = V3_LocateAndOpenBoard(0, 0, fUseInt);
            if (!hV3) printf ("V3 PBC card open failed!\n");
            break;
        case 4: // PCI configuration registers
            if (hV3) PCI_EditConfigReg(hV3->pciSlot);
            break;
        case 5: // V3 PBC local registers
            if (hV3) V3_EditReg(hV3);
            break;
        case 6: // Access mailbox data registers on the board
            if (hV3) V3_MailboxAccess(hV3);
            break;
        case 7: // Access local address ranges on the board
            if (hV3) V3_BoardAccess(hV3);
            break;
        case 8: // Access memory ranges on the board
            if (hV3) V3_BoardAccessSpaces(hV3);
            break;
        case 9: // Enable / Disable interrupts
            if (hV3 && hV3->fUseInt) V3_EnableDisableInterrupts(hV3);
            break;
        case 10: // Access serial EEPROM on the board
            if (hV3) V3_EEPROMAccess(hV3);
            break;
        case 11: // Reset the local bus processor
            if (hV3) V3_PulseLocalReset(hV3);
            break;
        }
    } while (cmd!=99);

    if (hV3) V3PBC_Close(hV3);

    return 0;
}