#include <Copyright.h>
/********************************************************************************
* qdSim.c
*
* DESCRIPTION:
*       Simulate QuaterDeck Device(88E6052)'s register map. When QuareterDeck API 
*        try to read/write a bit or bits into QuaterDeck, the simulator will redirect to
*         its own memory place and performing the function very close to QuaterDeck.
*        For example, 
*        1) user can set/reset a certain bit of QuarterDeck registers(Phy,Port,and General registers).
*        2) user can access ATU (flush, load, purge, etc. with max MAC addresses of 32)
*        3) user can manually generate an Interrupt and test the Interrupt routine.
*        4) when user read a register, it will clear a certain register if it's a Self Clear register.
*        5) when user write a register, it will return ERROR if it's read only register.
*         
*
* DEPENDENCIES:   QuaterDeck (88E6052) Register MAP.
*
* FILE REVISION NUMBER:
*
*******************************************************************************/

#include <msApi.h>
#include <qdSimRegs.h>

#define IS_BROADCAST_ADDR(_addr)                                \
            (((_addr)[0] == 0xFF) && ((_addr)[1] == 0xFF) &&    \
             ((_addr)[2] == 0xFF) && ((_addr)[3] == 0xFF) &&    \
             ((_addr)[4] == 0xFF) && ((_addr)[5] == 0xFF))

#define IS_GLOBAL_REG(_port)    ((int)(_port) == qdSimDev.qdSimGlobalRegBase)
#define IS_PORT_REG(_port) (((int)(_port) >= qdSimDev.qdSimPortBase) && ((int)(_port) < qdSimDev.qdSimPortBase + qdSimDev.qdSimNumOfPorts))
#define IS_PHY_REG(_port) (((int)(_port) >= qdSimDev.qdSimPhyBase) && ((int)(_port) < qdSimDev.qdSimPhyBase + qdSimDev.qdSimNumOfPhys))

typedef struct _QD_SIM_DEV
{
    int qdSimUsed;
    unsigned int qdSimDevId;
    int qdSimNumOfPorts;
    int qdSimPortBase;
    int qdSimNumOfPhys;
    int qdSimPhyBase;
    int qdSimGlobalRegBase;
    int qdSimPortStatsClear[10];
    int qdSimStatsCapturedPort;
    int vtuSize;
    int atuSize;
} QD_SIM_DEV;

static QD_SIM_DEV qdSimDev = {0};

void qdSimRegsInit();
GT_BOOL qdSimRead (GT_QD_DEV *dev, unsigned int portNumber , unsigned int miiReg, unsigned int* value);
GT_BOOL qdSimWrite(GT_QD_DEV *dev, unsigned int portNumber , unsigned int miiReg, unsigned int value);

/*
 *    This Array will simulate the QuarterDeck Registers.
 *    To use it, qdSimRegs has to be initialized with its default values and
 *    Call qdSimRead and qdSimWrite functions.
*/
#define MAX_SMI_ADDRESS        0x20
#define MAX_REG_ADDRESS        0x20
#define MAX_ATU_ADDRESS        0x800
#define MAX_QD_VTU_ENTRIES    0x40

GT_U16 qdSimRegs[MAX_SMI_ADDRESS][MAX_REG_ADDRESS];

typedef struct _QDSIM_ATU_ENTRY
{
    GT_U16 atuData;
    GT_U16 DBNum;
    GT_U8 atuMac[6];
} QDSIM_ATU_ENTRY;

/* 
    Since QuarterDeck Simulator supports only fixed size of atu entry,
    we are going with array list not dynamic linked list.
*/
typedef struct _QDSIM_ATU_NODE
{
    QDSIM_ATU_ENTRY atuEntry;
    GT_U32 nextEntry;
} QDSIM_ATU_NODE;

typedef struct _QDSIM_ATU_LIST
{
    int atuSize;
    GT_U32 head;
} QDSIM_ATU_LIST;

QDSIM_ATU_NODE ATUNode[MAX_ATU_ADDRESS];
QDSIM_ATU_LIST ATUList;

typedef struct _QDSIM_VTU_ENTRY
{
    GT_U16 DBNum;
    GT_U16 memberTag[10];
    GT_U16 vid;
} QDSIM_VTU_ENTRY;

/* 
    Since QuarterDeck Simulator supports only fixed size of atu entry,
    we are going with array list not dynamic linked list.
*/
typedef struct _QDSIM_VTU_NODE
{
    QDSIM_VTU_ENTRY vtuEntry;
    GT_U32 nextEntry;
} QDSIM_VTU_NODE;

typedef struct _QDSIM_VTU_LIST
{
    int vtuSize;
    GT_U32 head;
} QDSIM_VTU_LIST;

QDSIM_VTU_NODE VTUNode[MAX_QD_VTU_ENTRIES];
QDSIM_VTU_LIST VTUList;

/*******************************************************************************
* qdMemSet
*
* DESCRIPTION:
*       Set a block of memory
*
* INPUTS:
*       start  - start address of memory block for setting
*       simbol - character to store, converted to an unsigned char
*       size   - size of block to be set
*
* OUTPUTS:
*       None
*
* RETURNS:
*       Pointer to set memory block
*
* COMMENTS:
*       None
*
*******************************************************************************/
void * qdMemSet
(
    IN void * start,
    IN int    symbol,
    IN GT_U32 size
)
{
    GT_U32 i;
    char* buf;
    
    buf = (char*)start;
        
    for(i=0; i<size; i++)
    {
        *buf++ = (char)symbol;
    }

    return start;
}

/*******************************************************************************
* qdMemCpy
*
* DESCRIPTION:
*       Copies 'size' characters from the object pointed to by 'source' into
*       the object pointed to by 'destination'. If copying takes place between
*       objects that overlap, the behavior is undefined.
*
* INPUTS:
*       destination - destination of copy
*       source      - source of copy
*       size        - size of memory to copy
*
* OUTPUTS:
*       None
*
* RETURNS:
*       Pointer to destination
*
* COMMENTS:
*       None
*
*******************************************************************************/
void * qdMemCpy
(
    IN void *       destination,
    IN const void * source,
    IN GT_U32       size
)
{
    GT_U32 i;
    char* buf;
    char* src;
    
    buf = (char*)destination;
    src = (char*)source;
        
    for(i=0; i<size; i++)
    {
        *buf++ = *src++;
    }

    return destination;
}

/*******************************************************************************
* qdMemCmp
*
* DESCRIPTION:
*       Compares given memories.
*
* INPUTS:
*       src1 - source 1
*       src2 - source 2
*       size - size of memory to copy
*
* OUTPUTS:
*       None
*
* RETURNS:
*       0, if equal.
*        negative number, if src1 < src2.
*        positive number, if src1 > src2.
*
* COMMENTS:
*       None
*
*******************************************************************************/
int qdMemCmp
(
    IN char src1[],
    IN char src2[],
    IN GT_U32 size
)
{
    GT_U32 i;
    int value;

    for(i=0; i<size; i++)
    {
        if((value = (int)(src1[i] - src2[i])) != 0)
            return value; 
    }

    return 0;
}

/*
    Compare the given ethernet addresses.
    0, if they are equal.
    Negative int, if mac2 is bigger than mac1.
    Positive int, if mac1 is bigger than mac2.
*/
int cmpEtherMac(unsigned char* mac1, unsigned char* mac2)
{
    int i, tmp;

    for(i=0; i<6; i++)
    {
        if((tmp = mac1[i] - mac2[i]) != 0)
            return tmp;
    }
    return 0;
}

/*
    entry index, if found.
    MAX_ATU_ADDRESS, otherwise.
*/
int qdSimATUFindNext(QDSIM_ATU_ENTRY* entry)
{
    int i;
    int node = ATUList.head;

    if (IS_BROADCAST_ADDR(entry->atuMac))
    {
        if(ATUList.atuSize != 0)
        {
            if (ATUNode[node].atuEntry.DBNum == entry->DBNum)
                return node;
            else
            {
                for(i=0; i<ATUList.atuSize; i++)
                {
                    if(ATUNode[node].atuEntry.DBNum == entry->DBNum)
                        return node;
                    node = ATUNode[node].nextEntry;
                }
            }
                
        }
        return MAX_ATU_ADDRESS;
    }

    for(i=0; i<ATUList.atuSize; i++)
    {
        if(cmpEtherMac(ATUNode[node].atuEntry.atuMac,entry->atuMac) > 0)
        {
            if(ATUNode[node].atuEntry.DBNum == entry->DBNum)
                break;
        }
        node = ATUNode[node].nextEntry;
    }

    if (i == ATUList.atuSize)
        return MAX_ATU_ADDRESS;

    return node;
}

/*
    Return 1, if added successfully.
    Return 0, otherwise.
*/
GT_BOOL qdSimATUAdd(QDSIM_ATU_ENTRY* entry)
{
    int i, freeNode, preNode, node;

    preNode = node = ATUList.head;

    if (ATUList.atuSize >= MAX_ATU_ADDRESS)
        return GT_FALSE;

    /* find a free entry from our global memory. */
    for(i=0; i<MAX_ATU_ADDRESS; i++)
    {
        if(ATUNode[i].nextEntry == MAX_ATU_ADDRESS)
            break;
    }
    
    if (i==MAX_ATU_ADDRESS)
    {
        return GT_FALSE;
    }

    freeNode = i;

    /* find the smallest entry which is bigger than the given entry */
    for(i=0; i<ATUList.atuSize; i++)
    {
        if(cmpEtherMac(ATUNode[node].atuEntry.atuMac,entry->atuMac) >= 0)
            break;
        preNode = node;
        node = ATUNode[node].nextEntry;
    }    

    /* if the same Mac address is in the list and dbnum is identical, then just update and return. */
    if (i != ATUList.atuSize)
        if(cmpEtherMac(ATUNode[node].atuEntry.atuMac,entry->atuMac) == 0)
        {
            if(ATUNode[node].atuEntry.DBNum == entry->DBNum)
            {
                ATUNode[node].atuEntry.atuData = entry->atuData;
                return GT_TRUE;
            }
        }

    qdMemCpy(ATUNode[freeNode].atuEntry.atuMac, entry->atuMac, 6);
    ATUNode[freeNode].atuEntry.atuData = entry->atuData;
    ATUNode[freeNode].atuEntry.DBNum = entry->DBNum;

    /* Add it to head */
    if (i == 0)
    {
        ATUNode[freeNode].nextEntry = ATUList.head;
        ATUList.head = freeNode;
    }
    /* Add it to tail */
    else if (i == ATUList.atuSize)
    {
        ATUNode[preNode].nextEntry = freeNode;
        ATUNode[freeNode].nextEntry = ATUList.head;
    }
    /* Add it in the middle of the list */
    else
    {
        ATUNode[freeNode].nextEntry = ATUNode[preNode].nextEntry;
        ATUNode[preNode].nextEntry = freeNode;
    }
    ATUList.atuSize++;
    return GT_TRUE;
}


/*
    Return 1, if added successfully.
    Return 0, otherwise.
*/
GT_BOOL qdSimATUDel(QDSIM_ATU_ENTRY* entry)
{
    int i, preNode, node;

    preNode = node = ATUList.head;

    /* find the entry */
    for(i=0; i<ATUList.atuSize; i++)
    {
        if(cmpEtherMac(ATUNode[node].atuEntry.atuMac,entry->atuMac) == 0)
        {
            if(ATUNode[node].atuEntry.DBNum == entry->DBNum)
                break;
        }
        preNode = node;
        node = ATUNode[node].nextEntry;
    }    

    if (i == ATUList.atuSize)
    {
        /* cannot find the given entry to be deleted. */
        return GT_FALSE;
    }

    /* Delete it from head */
    if (i == 0)
    {
        ATUList.head = ATUNode[node].nextEntry;
    }
    /* Delete it in the middle of the list */
    else if (i != ATUList.atuSize-1)
    {
        ATUNode[preNode].nextEntry = ATUNode[node].nextEntry;
    }
    ATUList.atuSize--;
    ATUNode[node].nextEntry = MAX_ATU_ADDRESS;

    return GT_TRUE;
}


GT_BOOL qdSimATUFlushUnlockedEntry()
{
    int i;

    for (i=0; i<MAX_ATU_ADDRESS; i++)
    {
        if(((ATUNode[i].atuEntry.atuData & 0xF) != 0xF)    &&
            (!(ATUNode[i].atuEntry.atuMac[0] & 1))         &&
            (ATUNode[i].nextEntry != MAX_ATU_ADDRESS))
        {
            qdSimATUDel(&ATUNode[i].atuEntry);
        }            
    }
    return GT_TRUE;
}

GT_BOOL qdSimATUFlushInDB(int dbNum)
{
    int i;

    for (i=0; i<MAX_ATU_ADDRESS; i++)
    {
        if(ATUNode[i].atuEntry.DBNum != dbNum)
            continue;
        qdSimATUDel(&ATUNode[i].atuEntry);
    }
    return GT_TRUE;
}

GT_BOOL qdSimATUFlushUnlockedInDB(int dbNum)
{
    int i;

    for (i=0; i<MAX_ATU_ADDRESS; i++)
    {
        if(ATUNode[i].atuEntry.DBNum != dbNum)
            continue;
        
        if(((ATUNode[i].atuEntry.atuData & 0xF) != 0xF)    &&
            (!(ATUNode[i].atuEntry.atuMac[0] & 1))         &&
            (ATUNode[i].nextEntry != MAX_ATU_ADDRESS))
        {
            qdSimATUDel(&ATUNode[i].atuEntry);
        }            
    }
    return GT_TRUE;
}


void qdSimATUInit()
{
    int i;

    qdMemSet((char*)ATUNode, 0, sizeof(ATUNode));

    /* MAX_ATU_ADDRESS means entry i is free, otherwise, it's not free */
    for (i=0; i<MAX_ATU_ADDRESS; i++)
        ATUNode[i].nextEntry = MAX_ATU_ADDRESS;

    ATUList.atuSize = 0;    
    ATUList.head = 0;    
}

void qdSimGetATUInfo(QDSIM_ATU_ENTRY* entry)
{
    entry->atuData = qdSimRegs[qdSimDev.qdSimGlobalRegBase][12];
    entry->atuMac[0] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][13] >> 8) & 0xFF;
    entry->atuMac[1] = qdSimRegs[qdSimDev.qdSimGlobalRegBase][13] & 0xFF;
    entry->atuMac[2] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][14] >> 8) & 0xFF;
    entry->atuMac[3] = qdSimRegs[qdSimDev.qdSimGlobalRegBase][14] & 0xFF;
    entry->atuMac[4] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][15] >> 8) & 0xFF;
    entry->atuMac[5] = qdSimRegs[qdSimDev.qdSimGlobalRegBase][15] & 0xFF;
    entry->DBNum = qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] & 0xF;
    return;
}

void qdSimSetATUInfo(QDSIM_ATU_ENTRY* entry)
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][12] = entry->atuData;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][13] = (entry->atuMac[0]<<8) | entry->atuMac[1];
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][14] = (entry->atuMac[2]<<8) | entry->atuMac[3];
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][15] = (entry->atuMac[4]<<8) | entry->atuMac[5];
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] &= ~0xF;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] |= (entry->DBNum & 0xF);

    return;
}

void qdSimReSetATUInfo()
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] &= ~0xF;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][12] = 0;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][13] = 0xFFFF;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][14] = 0xFFFF;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][15] = 0xFFFF;

    return;
}

GT_BOOL qdSimATUOperation(unsigned int value)
{
    QDSIM_ATU_ENTRY entry;
    int    index;

    switch((value & 0x7000) >> 12)
    {
        case 1:
            /* Flush ALL */
            qdSimATUInit();
            break;
        case 2:
            /* Flush all unlocked entries */
            return qdSimATUFlushUnlockedEntry();
        case 3:
            /* Load or Purge entry */
            qdSimGetATUInfo(&entry);
            if(entry.atuData & 0xF)
                return qdSimATUAdd(&entry);
            else
                return qdSimATUDel(&entry);
            break;
        case 4:
            /* Get Next Entry */
            qdSimGetATUInfo(&entry);
            index = qdSimATUFindNext(&entry);
            if (index == MAX_ATU_ADDRESS)
            {
                qdSimReSetATUInfo();
                return GT_TRUE;
            }
            else
            {
                qdSimSetATUInfo(&ATUNode[index].atuEntry);
                return GT_TRUE;
            }
            break;
        case 5:
            /* Flush ALL in a DBNum */
            return qdSimATUFlushInDB(value & 0xF);
            break;
        case 6:
            /* Flush all unlocked entries */
            return qdSimATUFlushUnlockedInDB(value & 0xF);
        default:
            break;
    }
    return GT_TRUE;
}

/*
    VTU Related Routines
*/

/*
    entry index, if found.
    MAX_QD_VTU_ENTRIES, otherwise.
*/
int qdSimVTUFindNext(QDSIM_VTU_ENTRY* entry)
{
    int i;
    int node = VTUList.head;

    if (entry->vid == 0xFFF)
    {
        if(VTUList.vtuSize != 0)
            return node;
        else
            return MAX_QD_VTU_ENTRIES;
    }
        
    for(i=0; i<VTUList.vtuSize; i++)
    {
        if(VTUNode[node].vtuEntry.vid > entry->vid)
            break;
        node = VTUNode[node].nextEntry;
    }

    if (i == VTUList.vtuSize)
        return MAX_QD_VTU_ENTRIES;

    return node;
}

/*
    Return 1, if added successfully.
    Return 0, otherwise.
*/
GT_BOOL qdSimVTUAdd(QDSIM_VTU_ENTRY* entry)
{
    int i, freeNode, preNode, node;

    preNode = node = VTUList.head;

    if (VTUList.vtuSize >= qdSimDev.vtuSize)
        return GT_FALSE;

    /* find a free entry from our global memory. */
    for(i=0; i<MAX_QD_VTU_ENTRIES; i++)
    {
        if(VTUNode[i].nextEntry == MAX_QD_VTU_ENTRIES)
            break;
    }
    
    if (i==MAX_QD_VTU_ENTRIES)
    {
        return GT_FALSE;
    }

    freeNode = i;

    /* find the smallest entry which is bigger than the given entry */
    for(i=0; i<VTUList.vtuSize; i++)
    {
        if(VTUNode[node].vtuEntry.vid >= entry->vid)
            break;
        preNode = node;
        node = VTUNode[node].nextEntry;
    }    

    /* if the same vid is in the list, then just update and return. */
    if (i != VTUList.vtuSize)
        if(VTUNode[node].vtuEntry.vid == entry->vid)
        {
            qdMemCpy(&VTUNode[node].vtuEntry, entry, sizeof(QDSIM_VTU_ENTRY));
            return GT_TRUE;
        }

    qdMemCpy(&VTUNode[freeNode].vtuEntry, entry, sizeof(QDSIM_VTU_ENTRY));

    /* Add it to head */
    if (i == 0)
    {
        VTUNode[freeNode].nextEntry = VTUList.head;
        VTUList.head = freeNode;
    }
    /* Add it to tail */
    else if (i == VTUList.vtuSize)
    {
        VTUNode[preNode].nextEntry = freeNode;
        VTUNode[freeNode].nextEntry = VTUList.head;
    }
    /* Add it in the middle of the list */
    else
    {
        VTUNode[freeNode].nextEntry = VTUNode[preNode].nextEntry;
        VTUNode[preNode].nextEntry = freeNode;
    }
    VTUList.vtuSize++;
    return GT_TRUE;
}


/*
    Return 1, if added successfully.
    Return 0, otherwise.
*/
GT_BOOL qdSimVTUDel(QDSIM_VTU_ENTRY* entry)
{
    int i, preNode, node;

    preNode = node = VTUList.head;

    /* find the entry */
    for(i=0; i<VTUList.vtuSize; i++)
    {
        if(VTUNode[node].vtuEntry.vid == entry->vid)
            break;
        preNode = node;
        node = VTUNode[node].nextEntry;
    }    

    if (i == VTUList.vtuSize)
    {
        /* cannot find the given entry to be deleted. */
        return GT_FALSE;
    }

    /* Delete it from head */
    if (i == 0)
    {
        VTUList.head = VTUNode[node].nextEntry;
    }
    /* Delete it in the middle of the list */
    else if (i != VTUList.vtuSize-1)
    {
        VTUNode[preNode].nextEntry = VTUNode[node].nextEntry;
    }
    VTUList.vtuSize--;
    VTUNode[node].nextEntry = MAX_QD_VTU_ENTRIES;

    return GT_TRUE;
}


/*
    Return 1, if added successfully.
    Return 0, otherwise.
*/
GT_BOOL qdSimVTUUpdate(QDSIM_VTU_ENTRY* entry)
{
    int i;
    int node = VTUList.head;

    /* find the entry */
    for(i=0; i<VTUList.vtuSize; i++)
    {
        if(VTUNode[node].vtuEntry.vid == entry->vid)
            break;
        node = VTUNode[node].nextEntry;
    }    

    if (i == VTUList.vtuSize)
    {
        /* cannot find the given entry to be deleted. */
        return GT_FALSE;
    }

    /* Update the found entry */
    qdMemCpy(&VTUNode[node].vtuEntry, entry, sizeof(QDSIM_VTU_ENTRY));

    return GT_TRUE;
}

void qdSimVTUInit()
{
    int i;

    qdMemSet((char*)VTUNode, 0, sizeof(VTUNode));

    /* MAX_ATU_ADDRESS means entry i is free, otherwise, it's not free */
    for (i=0; i<MAX_QD_VTU_ENTRIES; i++)
        VTUNode[i].nextEntry = MAX_QD_VTU_ENTRIES;

    VTUList.vtuSize = 0;    
    VTUList.head = 0;    
}

void qdSimGetVTUInfo(QDSIM_VTU_ENTRY* entry)
{
    entry->DBNum = qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] & 0xF;
    entry->vid = qdSimRegs[qdSimDev.qdSimGlobalRegBase][6] & 0x1FFF;
    entry->memberTag[0] = qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] & 0x3;
    entry->memberTag[1] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] >> 4) & 0x3;
    entry->memberTag[2] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] >> 8) & 0x3;
    entry->memberTag[3] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] >> 12) & 0x3;
    entry->memberTag[4] = qdSimRegs[qdSimDev.qdSimGlobalRegBase][8] & 0x3;
    entry->memberTag[5] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][8] >> 4) & 0x3;
    entry->memberTag[6] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][8] >> 8) & 0x3;
    entry->memberTag[7] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][8] >> 12) & 0x3;
    entry->memberTag[8] = qdSimRegs[qdSimDev.qdSimGlobalRegBase][9] & 0x3;
    entry->memberTag[9] = (qdSimRegs[qdSimDev.qdSimGlobalRegBase][9] >> 4) & 0x3;

    return;
}

void qdSimSetVTUInfo(QDSIM_VTU_ENTRY* entry)
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] |= entry->DBNum;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][6] = (entry->vid & 0xFFF) | 0x1000;

    qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] =     entry->memberTag[0] |
                        (entry->memberTag[1] << 4) |
                        (entry->memberTag[2] << 8) |
                        (entry->memberTag[3] << 12);

    qdSimRegs[qdSimDev.qdSimGlobalRegBase][8] =     entry->memberTag[4] |
                        (entry->memberTag[5] << 4) |
                        (entry->memberTag[6] << 8) |
                        (entry->memberTag[7] << 12);

    qdSimRegs[qdSimDev.qdSimGlobalRegBase][9] =     entry->memberTag[8] |
                        (entry->memberTag[9] << 4);

    return;
}

void qdSimReSetVTUInfo()
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][6] = 0xFFF;

    return;
}

void qdSimVTUGetViolation()
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] &= ~0xFFF;
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] |= 1;    /* assume port 1 causes the violation */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][6] = 1;    /* assume vid 1 causes the violation */
}

void qdSimVTUResetBusy()
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] &= ~0x8000;

    return;
}

GT_BOOL qdSimVTUOperation(unsigned int value)
{
    QDSIM_VTU_ENTRY entry;
    int    index;

    if(!(value & 0x8000))
        return GT_FALSE;

    qdSimVTUResetBusy();

    switch((value & 0x7000) >> 12)
    {
        case 1:
            /* Flush ALL */
            qdSimVTUInit();
            break;
        case 3:
            /* Load or Purge entry */
            qdSimGetVTUInfo(&entry);
            if(entry.vid & 0x1000)
            {
                entry.vid &= ~0x1000;
                return qdSimVTUAdd(&entry);
            }
            else
                return qdSimVTUDel(&entry);
            break;
        case 4:
            /* Get Next Entry */
            qdSimGetVTUInfo(&entry);
            entry.vid &= ~0x1000;
            index = qdSimVTUFindNext(&entry);
            if (index == MAX_QD_VTU_ENTRIES)
            {
                qdSimReSetVTUInfo();
                return GT_TRUE;
            }
            else
            {
                qdSimSetVTUInfo(&VTUNode[index].vtuEntry);
                return GT_TRUE;
            }
            break;
        case 7:
            qdSimVTUGetViolation();
            break;
        default:
            break;
    }
    return GT_TRUE;
}

void qdSimStatsInit()
{
    int i;

    for(i=0; i<qdSimDev.qdSimNumOfPorts; i++)
        qdSimDev.qdSimPortStatsClear[i] = 0;

}

GT_BOOL qdSimStatsOperation(unsigned int value)
{
    int    i;

    if(!(value & 0x8000))
        return GT_FALSE;

    qdSimRegs[qdSimDev.qdSimGlobalRegBase][29] &= ~0x8000;

    switch((value & 0x7000) >> 12)
    {
        case 1:
            /* Flush ALL */
            for(i=0; i<qdSimDev.qdSimNumOfPorts; i++)
                qdSimDev.qdSimPortStatsClear[i] = 1;
            break;
        case 2:
            /* Flush a port */
            if ((value & 0x3F) >= (unsigned int)qdSimDev.qdSimNumOfPorts)
                return GT_FALSE;
            qdSimDev.qdSimPortStatsClear[value & 0x3F] = 1;
            break;
        case 4:
            /* Read a counter */
            if(qdSimDev.qdSimPortStatsClear[qdSimDev.qdSimStatsCapturedPort] == 1)
            {
                qdSimRegs[qdSimDev.qdSimGlobalRegBase][30] = 0;
                qdSimRegs[qdSimDev.qdSimGlobalRegBase][31] = 0;
            }
            else
            {
                qdSimRegs[qdSimDev.qdSimGlobalRegBase][30] = qdSimDev.qdSimStatsCapturedPort;
                qdSimRegs[qdSimDev.qdSimGlobalRegBase][31] = value & 0x3F;
            }
            break;
        case 5:
            if ((value & 0x3F) >= (unsigned int)qdSimDev.qdSimNumOfPorts)
                return GT_FALSE;
            qdSimDev.qdSimStatsCapturedPort = value & 0x3F;
            break;
        default:
            return GT_FALSE;
    }
    return GT_TRUE;
}

#define QD_PHY_CONTROL_RW (QD_PHY_RESET|QD_PHY_LOOPBACK|QD_PHY_SPEED|QD_PHY_AUTONEGO|QD_PHY_POWER|QD_PHY_RESTART_AUTONEGO|QD_PHY_DUPLEX)
#define QD_PHY_CONTROL_RO (~QD_PHY_CONTROL_RW)

GT_BOOL qdSimPhyControl(unsigned int portNumber , unsigned int miiReg, unsigned int value)
{

    /* reset all the Read Only bits. */
    value &= QD_PHY_CONTROL_RW;

    /* If powerDown is set, add Reset and Restart Auto bits. */
    if(value & QD_PHY_POWER)
    {
        value |= (QD_PHY_RESET|QD_PHY_RESTART_AUTONEGO);
        qdSimRegs[portNumber][miiReg] = (GT_U16)value;
        return GT_TRUE;
    }

    /* If Power Down was set, clear Reset and Restart Auto bits. */
    if(qdSimRegs[portNumber][miiReg] & QD_PHY_POWER)
    {
        value &= ~(QD_PHY_RESET|QD_PHY_RESTART_AUTONEGO);
        qdSimRegs[portNumber][miiReg] = (GT_U16)value;
        return GT_TRUE;
    }

    /* If Reset or Restart Auto set, replace with current value and clear Reset/Restart Auto. */
    if (value & (QD_PHY_RESET|QD_PHY_RESTART_AUTONEGO))
    {
        value &= ~(QD_PHY_RESET|QD_PHY_RESTART_AUTONEGO);
        qdSimRegs[portNumber][miiReg] = (GT_U16)value;
        return GT_TRUE;
    }
    else
    {
        value &= ~(QD_PHY_SPEED|QD_PHY_AUTONEGO|QD_PHY_DUPLEX);
        qdSimRegs[portNumber][miiReg] &= (QD_PHY_SPEED|QD_PHY_AUTONEGO|QD_PHY_DUPLEX);
        qdSimRegs[portNumber][miiReg] |= (GT_U16)value;
        return GT_TRUE;
    }

    return GT_TRUE;
}

void qdSimRegsInit_6021()
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] = 0;    /* VTU Operation Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][6] = 0;    /* VTU VID Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] = 0;    /* VTU Data Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][29] = 0;    /* Stats Operation Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][30] = 0;    /* Stats Counter Register Bytes 3,2 */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][31] = 0;    /* Stats Counter Register Bytes 1,0 */
}

void qdSimRegsInit_6063()
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] = 0;    /* VTU Operation Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][6] = 0;    /* VTU VID Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] = 0;    /* VTU Data Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][8] = 0;    /* VTU Data Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][29] = 0;    /* Stats Operation Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][30] = 0;    /* Stats Counter Register Bytes 3,2 */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][31] = 0;    /* Stats Counter Register Bytes 1,0 */
}

void qdSimRegsInit_6083()
{
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] = 0;    /* VTU Operation Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][6] = 0;    /* VTU VID Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][7] = 0;    /* VTU Data Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][8] = 0;    /* VTU Data Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][9] = 0;    /* VTU Data Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][29] = 0;    /* Stats Operation Register */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][30] = 0;    /* Stats Counter Register Bytes 3,2 */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][31] = 0;    /* Stats Counter Register Bytes 1,0 */
}

void qdSimRegsInit()
{
    int i;

    qdMemSet(qdSimRegs, 0xff, sizeof(qdSimRegs));
    
    /* 
        PHY Registers Setup
    */
    for(i=0; i<qdSimDev.qdSimNumOfPhys; i++)
    {
        qdSimRegs[i][0] = 0x3100;    /* PHY Control */
        qdSimRegs[i][1] = 0x7849;    /* PHY Status */
        qdSimRegs[i][2] = 0x0141;    /* PHY Id 1 */
        qdSimRegs[i][3] = 0x0c1f;    /* PHY Id 2 */
        qdSimRegs[i][4] = 0x01e1;    /* AutoNego Ad */
        qdSimRegs[i][5] = 0;        /* Partner Ability */
        qdSimRegs[i][6] = 4;        /* AutoNego Expansion */
        qdSimRegs[i][7] = 0x2001;    /* Next Page Transmit */
        qdSimRegs[i][8] = 0;        /* Link Partner Next Page */
        qdSimRegs[i][16] = 0x130;    /* Phy Specific Control */
        qdSimRegs[i][17] = 0x40;    /* Phy Specific Status */
        qdSimRegs[i][18] = 0;        /* Phy Interrupt Enable */
        qdSimRegs[i][19] = 0x40;    /* Phy Interrupt Status */
        qdSimRegs[i][20] = 0;        /* Interrupt Port Summary */
        qdSimRegs[i][21] = 0;        /* Receive Error Counter */
        qdSimRegs[i][22] = 0xa34;    /* LED Parallel Select */
        qdSimRegs[i][23] = 0x3fc;    /* LED Stream Select */
        qdSimRegs[i][24] = 0x42bf;    /* LED Control */
    }

    /*
        Port Registers Setup
    */
    for(i=qdSimDev.qdSimPortBase; i<qdSimDev.qdSimNumOfPorts+qdSimDev.qdSimPortBase; i++)
    {
        qdSimRegs[i][0] = 0x800;    /* Port Status */
        qdSimRegs[i][3] = (GT_U16)qdSimDev.qdSimDevId << 4;    /* Switch ID */
        qdSimRegs[i][4] = 0x7f;    /* Port Control */
        qdSimRegs[i][6] = 0x7f & (~(1 << (i-8)));    /* Port Based Vlan Map */
        qdSimRegs[i][7] = 1;        /* Default Port Vlan ID & Priority */
        qdSimRegs[i][16] = 0;        /* Rx Frame Counter */
        qdSimRegs[i][17] = 0;        /* Tx Frame Counter */
    }

    /*
        Global Registers Setup
    */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][0] = 0x3c01;    /* Global Status */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][1] = 0;        /* Switch Mac Addr 0 ~ 1 byte */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][2] = 0;        /* Switch Mac Addr 2 ~ 3 byte */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][3] = 0;        /* Switch Mac Addr 4 ~ 5 byte */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][4] = 0x81;    /* Global Control */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][10] = 0x1130;        /* ATU Control */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] = 0;                /* ATU Operation */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][12] = 0;                /* ATU Data */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][13] = 0;                /* ATU Mac Addr 0 ~ 1 byte */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][14] = 0;                /* ATU Mac Addr 2 ~ 3 byte */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][15] = 0;                /* ATU Mac Addr 4 ~ 5 byte */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][16] = 0;            /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][17] = 0;            /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][18] = 0x5555;    /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][19] = 0x5555;    /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][20] = 0xaaaa;    /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][21] = 0xaaaa;    /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][22] = 0xffff;    /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][23] = 0xffff;    /* IP-PRI Mapping */
    qdSimRegs[qdSimDev.qdSimGlobalRegBase][24] = 0xfa41;    /* IEEE-PRI Mapping */
              
    switch(qdSimDev.qdSimDevId)
    {
        case GT_88E6021:
            qdSimRegsInit_6021();
            break;
        case GT_88E6063:
        case GT_FF_HG:
        case GT_FF_EG:
        case GT_FH_VPN:
            qdSimRegsInit_6063();
            break;
        case GT_88E6083:
            qdSimRegsInit_6083();
            break;
        default:
            break;
    }
}

GT_BOOL qdSimRead_6052(unsigned int portNumber , unsigned int miiReg, unsigned int* value)
{
    *value = (unsigned int) qdSimRegs[portNumber][miiReg];

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    qdSimRegs[portNumber][miiReg] &= ~0xF;
                    if(qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG])
                        qdSimRegs[portNumber][miiReg] |= 0x2;

                    break;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
            case QD_REG_GLOBAL_CONTROL:
            case QD_REG_ATU_CONTROL:
            case QD_REG_ATU_OPERATION:
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
                    break;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
            case QD_REG_SWITCH_ID:
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
                    break;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    break;
            case QD_PHY_INT_ENABLE_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
                    qdSimRegs[portNumber][miiReg] = 0;
                    qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG] &= ~(1<<portNumber);
                    break;
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    *value = (unsigned int) qdSimRegs[0][miiReg];
                    break;
        }
    }

    return GT_TRUE;
}

GT_BOOL qdSimRead_6021(unsigned int portNumber , unsigned int miiReg, unsigned int* value)
{
    *value = (unsigned int) qdSimRegs[portNumber][miiReg];

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    qdSimRegs[portNumber][miiReg] &= ~0x7F;
                    if(qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG])
                        qdSimRegs[portNumber][miiReg] |= 0x2;

                    break;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
            case QD_REG_VTU_OPERATION:
            case QD_REG_VTU_VID_REG:
            case QD_REG_VTU_DATA1_REG:
            case QD_REG_VTU_DATA2_REG:
            case QD_REG_GLOBAL_CONTROL:
            case QD_REG_ATU_CONTROL:
            case QD_REG_ATU_OPERATION:
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
            case QD_REG_STATS_OPERATION:
            case QD_REG_STATS_COUNTER3_2:
            case QD_REG_STATS_COUNTER1_0:
                    break;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
            case QD_REG_SWITCH_ID:
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
            case QD_REG_RATE_CTRL:
            case QD_REG_PAV:
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
            case QD_REG_Q_COUNTER:
                    break;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    break;
            case QD_PHY_INT_ENABLE_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
                    qdSimRegs[portNumber][miiReg] = 0;
                    qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG] &= ~(1<<portNumber);
                    break;
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    *value = (unsigned int) qdSimRegs[0][miiReg];
                    break;
        }
    }

    return GT_TRUE;
}

GT_BOOL qdSimRead_6063(unsigned int portNumber , unsigned int miiReg, unsigned int* value)
{
    *value = (unsigned int) qdSimRegs[portNumber][miiReg];

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    qdSimRegs[portNumber][miiReg] &= ~0x7F;
                    if(qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG])
                        qdSimRegs[portNumber][miiReg] |= 0x2;

                    break;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
            case QD_REG_VTU_OPERATION:
            case QD_REG_VTU_VID_REG:
            case QD_REG_VTU_DATA1_REG:
            case QD_REG_VTU_DATA2_REG:
            case QD_REG_GLOBAL_CONTROL:
            case QD_REG_ATU_CONTROL:
            case QD_REG_ATU_OPERATION:
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
            case QD_REG_STATS_OPERATION:
            case QD_REG_STATS_COUNTER3_2:
            case QD_REG_STATS_COUNTER1_0:
                    break;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
            case QD_REG_SWITCH_ID:
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
            case QD_REG_RATE_CTRL:
            case QD_REG_PAV:
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
            case QD_REG_Q_COUNTER:
                    break;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    break;
            case QD_PHY_INT_ENABLE_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
                    qdSimRegs[portNumber][miiReg] = 0;
                    qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG] &= ~(1<<portNumber);
                    break;
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    *value = (unsigned int) qdSimRegs[0][miiReg];
                    break;
        }
    }

    return GT_TRUE;
}


GT_BOOL qdSimRead_6083(unsigned int portNumber , unsigned int miiReg, unsigned int* value)
{
    *value = (unsigned int) qdSimRegs[portNumber][miiReg];

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    qdSimRegs[portNumber][miiReg] &= ~0x7F;
                    if(qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG])
                        qdSimRegs[portNumber][miiReg] |= 0x2;

                    break;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
            case QD_REG_VTU_OPERATION:
            case QD_REG_VTU_VID_REG:
            case QD_REG_VTU_DATA1_REG:
            case QD_REG_VTU_DATA2_REG:
            case QD_REG_GLOBAL_CONTROL:
            case QD_REG_ATU_CONTROL:
            case QD_REG_ATU_OPERATION:
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
            case QD_REG_STATS_OPERATION:
            case QD_REG_STATS_COUNTER3_2:
            case QD_REG_STATS_COUNTER1_0:
                    break;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
            case QD_REG_SWITCH_ID:
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
            case QD_REG_RATE_CTRL:
            case QD_REG_PAV:
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
            case QD_REG_Q_COUNTER:
                    break;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    break;
            case QD_PHY_INT_ENABLE_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
                    qdSimRegs[portNumber][miiReg] = 0;
                    qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG] &= ~(1<<portNumber);
                    break;
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    *value = (unsigned int) qdSimRegs[0][miiReg];
                    break;
        }
    }

    return GT_TRUE;
}

GT_BOOL qdSimRead (GT_QD_DEV *dev,unsigned int portNumber , unsigned int miiReg, unsigned int* value)
{
    if (portNumber >= MAX_SMI_ADDRESS)
        portNumber -= MAX_SMI_ADDRESS;

    if ((portNumber >= MAX_SMI_ADDRESS) || (miiReg >= MAX_REG_ADDRESS))
        return GT_FALSE;

    switch(qdSimDev.qdSimDevId)
    {
        case GT_88E6051:
        case GT_88E6052:
            return qdSimRead_6052(portNumber, miiReg, value);
        case GT_88E6021:
            return qdSimRead_6021(portNumber, miiReg, value);
        case GT_88E6063:
        case GT_FF_HG:
        case GT_FF_EG:
        case GT_FH_VPN:
            return qdSimRead_6063(portNumber, miiReg, value);
        case GT_88E6083:
            return qdSimRead_6083(portNumber, miiReg, value);
        default:
            break;
    }

    return GT_TRUE;
}

GT_BOOL qdSimWrite_6052 (unsigned int portNumber , unsigned int miiReg, unsigned int value)
{
    GT_BOOL status;

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    /* readonly register */
                    return GT_FALSE;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
                    break;
            case QD_REG_GLOBAL_CONTROL:
                    if(value & 0x200)
                    {
                        /* Reload EEPROM values */
                        qdSimRegsInit();
                        qdSimRegs[portNumber][QD_REG_GLOBAL_STATUS] |= 0x1;
                        return GT_TRUE;
                    }
                    break;
            case QD_REG_ATU_CONTROL:
                    value &= ~0x8000;
                    break;
            case QD_REG_ATU_OPERATION:
                    status = qdSimATUOperation(value);
                    return status;
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
                    break;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
            case QD_REG_SWITCH_ID:
                    /* readonly registers */
                    return GT_FALSE;
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
                    break;
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
                    /* readonly registers */
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    return qdSimPhyControl(portNumber,miiReg,value);
            case QD_PHY_INT_ENABLE_REG:
            case QD_PHY_AUTONEGO_AD_REG:
            case QD_PHY_NEXTPAGE_TX_REG:
            case QD_PHY_SPEC_CONTROL_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else
        return GT_FALSE;

    qdSimRegs[portNumber][miiReg] = (GT_U16)value;
    return GT_TRUE;
}

GT_BOOL qdSimWrite_6021 (unsigned int portNumber , unsigned int miiReg, unsigned int value)
{
    GT_BOOL status;

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    /* readonly register */
                    return GT_FALSE;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
                    break;
            case QD_REG_VTU_OPERATION:
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] &= ~0xF;
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] |= (value & 0xF);
                    status = qdSimVTUOperation(value);
                    return status;
            case QD_REG_VTU_VID_REG:
            case QD_REG_VTU_DATA1_REG:
            case QD_REG_VTU_DATA2_REG:
                    break;
            case QD_REG_GLOBAL_CONTROL:
                    if(value & 0x200)
                    {
                        /* Reload EEPROM values */
                        qdSimRegsInit();
                        qdSimRegs[portNumber][QD_REG_GLOBAL_STATUS] |= 0x1;
                        return GT_TRUE;
                    }
                    break;
            case QD_REG_ATU_CONTROL:
                    value &= ~0x8000;
                    break;
            case QD_REG_ATU_OPERATION:
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] &= ~0xF;
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] |= (value & 0xF);
                    status = qdSimATUOperation(value);
                    return status;
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
                    break;
            case QD_REG_STATS_OPERATION:
                    status = qdSimStatsOperation(value);
                    return status;
            case QD_REG_STATS_COUNTER3_2:
            case QD_REG_STATS_COUNTER1_0:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
                    if(portNumber > 9)
                    {
                        qdSimRegs[portNumber][miiReg] &= ~QD_PORT_STATUS_DUPLEX;
                        qdSimRegs[portNumber][miiReg] |= (value & QD_PORT_STATUS_DUPLEX);
                        return GT_TRUE;
                    }
            case QD_REG_SWITCH_ID:
                    /* readonly registers */
                    return GT_FALSE;
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
                    break;
            case QD_REG_RATE_CTRL:
            case QD_REG_PAV:
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
                    /* readonly registers */
                    return GT_FALSE;
            case QD_REG_Q_COUNTER:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    return qdSimPhyControl(portNumber,miiReg,value);
            case QD_PHY_INT_ENABLE_REG:
            case QD_PHY_AUTONEGO_AD_REG:
            case QD_PHY_NEXTPAGE_TX_REG:
            case QD_PHY_SPEC_CONTROL_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else
        return GT_FALSE;

    qdSimRegs[portNumber][miiReg] = (GT_U16)value;
    return GT_TRUE;
}

GT_BOOL qdSimWrite_6063 (unsigned int portNumber , unsigned int miiReg, unsigned int value)
{
    GT_BOOL status;

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    /* readonly register */
                    return GT_FALSE;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
                    break;
            case QD_REG_VTU_OPERATION:
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] &= ~0xF;
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] |= (value & 0xF);
                    status = qdSimVTUOperation(value);
                    return status;
            case QD_REG_VTU_VID_REG:
            case QD_REG_VTU_DATA1_REG:
            case QD_REG_VTU_DATA2_REG:
                    break;
            case QD_REG_GLOBAL_CONTROL:
                    if(value & 0x200)
                    {
                        /* Reload EEPROM values */
                        qdSimRegsInit();
                        qdSimRegs[portNumber][QD_REG_GLOBAL_STATUS] |= 0x1;
                        return GT_TRUE;
                    }
                    break;
            case QD_REG_ATU_CONTROL:
                    value &= ~0x8000;
                    break;
            case QD_REG_ATU_OPERATION:
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] &= ~0xF;
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] |= (value & 0xF);
                    status = qdSimATUOperation(value);
                    return status;
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
                    break;
            case QD_REG_STATS_OPERATION:
                    status = qdSimStatsOperation(value);
                    return status;
            case QD_REG_STATS_COUNTER3_2:
            case QD_REG_STATS_COUNTER1_0:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
                    if(portNumber > 12)
                    {
                        qdSimRegs[portNumber][miiReg] &= ~QD_PORT_STATUS_DUPLEX;
                        qdSimRegs[portNumber][miiReg] |= (value & QD_PORT_STATUS_DUPLEX);
                        return GT_TRUE;
                    }
            case QD_REG_SWITCH_ID:
                    /* readonly registers */
                    return GT_FALSE;
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
            case QD_REG_RATE_CTRL:
            case QD_REG_PAV:
                    break;
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
                    /* readonly registers */
                    return GT_FALSE;
            case QD_REG_Q_COUNTER:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    return qdSimPhyControl(portNumber,miiReg,value);
            case QD_PHY_INT_ENABLE_REG:
            case QD_PHY_AUTONEGO_AD_REG:
            case QD_PHY_NEXTPAGE_TX_REG:
            case QD_PHY_SPEC_CONTROL_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else
        return GT_FALSE;

    qdSimRegs[portNumber][miiReg] = (GT_U16)value;
    return GT_TRUE;
}

GT_BOOL qdSimWrite_6083 (unsigned int portNumber , unsigned int miiReg, unsigned int value)
{
    GT_BOOL status;

    if (IS_GLOBAL_REG(portNumber))    /* Global register */
    {
        switch(miiReg)
        {
            case QD_REG_GLOBAL_STATUS:
                    /* readonly register */
                    return GT_FALSE;
            case QD_REG_MACADDR_01:
            case QD_REG_MACADDR_23:
            case QD_REG_MACADDR_45:
                    break;
            case QD_REG_VTU_OPERATION:
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] &= ~0xF;
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][5] |= (value & 0xF);
                    status = qdSimVTUOperation(value);
                    return status;
            case QD_REG_VTU_VID_REG:
            case QD_REG_VTU_DATA1_REG:
            case QD_REG_VTU_DATA2_REG:
            case QD_REG_VTU_DATA3_REG:
                    break;
            case QD_REG_GLOBAL_CONTROL:
                    if(value & 0x200)
                    {
                        /* Reload EEPROM values */
                        qdSimRegsInit();
                        qdSimRegs[portNumber][QD_REG_GLOBAL_STATUS] |= 0x1;
                        return GT_TRUE;
                    }
                    break;
            case QD_REG_ATU_CONTROL:
                    value &= ~0x8000;
                    break;
            case QD_REG_ATU_OPERATION:
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] &= ~0xF;
                    qdSimRegs[qdSimDev.qdSimGlobalRegBase][11] |= (value & 0xF);
                    status = qdSimATUOperation(value);
                    return status;
            case QD_REG_ATU_DATA_REG:
            case QD_REG_ATU_MAC_01:
            case QD_REG_ATU_MAC_23:
            case QD_REG_ATU_MAC_45:
            case QD_REG_IP_PRI_REG0:
            case QD_REG_IP_PRI_REG1:
            case QD_REG_IP_PRI_REG2:
            case QD_REG_IP_PRI_REG3:
            case QD_REG_IP_PRI_REG4:
            case QD_REG_IP_PRI_REG5:
            case QD_REG_IP_PRI_REG6:
            case QD_REG_IP_PRI_REG7:
            case QD_REG_IEEE_PRI:
                    break;
            case QD_REG_STATS_OPERATION:
                    status = qdSimStatsOperation(value);
                    return status;
            case QD_REG_STATS_COUNTER3_2:
            case QD_REG_STATS_COUNTER1_0:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PORT_REG(portNumber))    /* Port registers */
    {
        switch(miiReg)
        {
            case QD_REG_PORT_STATUS:
                    if(portNumber > 12)
                    {
                        qdSimRegs[portNumber][miiReg] &= ~QD_PORT_STATUS_DUPLEX;
                        qdSimRegs[portNumber][miiReg] |= (value & QD_PORT_STATUS_DUPLEX);
                        return GT_TRUE;
                    }
            case QD_REG_SWITCH_ID:
                    /* readonly registers */
                    return GT_FALSE;
            case QD_REG_PORT_CONTROL:
            case QD_REG_PORT_VLAN_MAP:
            case QD_REG_PVID:
            case QD_REG_RATE_CTRL:
            case QD_REG_PAV:
                    break;
            case QD_REG_RXCOUNTER:
            case QD_REG_TXCOUNTER:
                    /* readonly registers */
                    return GT_FALSE;
            case QD_REG_Q_COUNTER:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else if(IS_PHY_REG(portNumber))    /* phy registers */
    {
        switch(miiReg)
        {
            case QD_PHY_CONTROL_REG:
                    return qdSimPhyControl(portNumber,miiReg,value);
            case QD_PHY_INT_ENABLE_REG:
            case QD_PHY_AUTONEGO_AD_REG:
            case QD_PHY_NEXTPAGE_TX_REG:
            case QD_PHY_SPEC_CONTROL_REG:
                    break;
            case QD_PHY_INT_STATUS_REG:
            case QD_PHY_INT_PORT_SUMMARY_REG:
                    return GT_FALSE;
            default:
                    return GT_FALSE;
        }
    }
    else
        return GT_FALSE;

    qdSimRegs[portNumber][miiReg] = (GT_U16)value;
    return GT_TRUE;
}


GT_BOOL qdSimWrite (GT_QD_DEV *dev,unsigned int portNumber , unsigned int miiReg, unsigned int value)
{
    if (portNumber >= MAX_SMI_ADDRESS)
        portNumber -= MAX_SMI_ADDRESS;

    if ((portNumber >= MAX_SMI_ADDRESS) || (miiReg >= MAX_REG_ADDRESS))
        return GT_FALSE;

    switch(qdSimDev.qdSimDevId)
    {
        case GT_88E6051:
        case GT_88E6052:
            return qdSimWrite_6052(portNumber, miiReg, value);
        case GT_88E6021:
            return qdSimWrite_6021(portNumber, miiReg, value);
        case GT_88E6063:
        case GT_FF_HG:
        case GT_FF_EG:
        case GT_FH_VPN:
            return qdSimWrite_6063(portNumber, miiReg, value);
        case GT_88E6083:
            return qdSimWrite_6083(portNumber, miiReg, value);

        default:
            break;
    }

    return GT_TRUE;
}

GT_STATUS qdSimSetPhyInt(unsigned int portNumber, unsigned short u16Data)
{
    if(!qdSimDev.qdSimUsed)
        return GT_FAIL;

    qdSimRegs[portNumber][QD_PHY_INT_STATUS_REG] = u16Data;
    if(u16Data)
        qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG] |= (1<<portNumber);
    else
        qdSimRegs[0][QD_PHY_INT_PORT_SUMMARY_REG] &= ~(1<<portNumber);
    
    qdSimRegs[MAX_SMI_ADDRESS-1][QD_REG_GLOBAL_STATUS] |= 0x2;
    return GT_OK;
}

GT_STATUS qdSimSetGlobalInt(unsigned short u16Data)
{
    if(!qdSimDev.qdSimUsed)
        return GT_FAIL;

    qdSimRegs[MAX_SMI_ADDRESS-1][QD_REG_GLOBAL_STATUS] |= (u16Data & 0xF);
    return GT_OK;
}


void qdSimInit(GT_DEVICE devId, int baseAddr)
{
    qdSimDev.qdSimUsed = 1;

    qdSimDev.qdSimDevId = devId;
    qdSimDev.vtuSize = 0;

    qdSimDev.qdSimPhyBase = baseAddr;
    qdSimDev.qdSimPortBase = baseAddr + 0x8;
    qdSimDev.qdSimGlobalRegBase = baseAddr + 0xF;

    switch(devId)
    {
        case GT_88E6021:
            qdSimDev.vtuSize = 16;
            qdSimDev.qdSimNumOfPhys = 2;
            qdSimDev.qdSimNumOfPorts = 3;
            break;
        case GT_88E6051:
            qdSimDev.qdSimNumOfPhys = 5;
            qdSimDev.qdSimNumOfPorts = 6;
            break;
        case GT_88E6063:
        case GT_FH_VPN:
            qdSimDev.vtuSize = 64;
        case GT_88E6052:
        case GT_FF_HG:
        case GT_FF_EG:
            qdSimDev.qdSimNumOfPhys = 5;
            qdSimDev.qdSimNumOfPorts = 7;
            break;
        case GT_88E6083:
            qdSimDev.vtuSize = 64;
            qdSimDev.qdSimNumOfPhys = 8;
            qdSimDev.qdSimNumOfPorts = 10;
            qdSimDev.qdSimPhyBase = 0;
            qdSimDev.qdSimPortBase = 0x10;
            qdSimDev.qdSimGlobalRegBase = 0x1b;
            break;
        default:
            qdSimDev.vtuSize = 64;
            qdSimDev.qdSimDevId = GT_88E6063;
            qdSimDev.qdSimNumOfPhys = 5;
            qdSimDev.qdSimNumOfPorts = 7;
            break;
    }

    qdSimATUInit();
    qdSimVTUInit();
    qdSimRegsInit();

    return;
}
