#include <Copyright.h>

/*******************************************************************************
* gtBrgStu.c
*
* DESCRIPTION:
*       API definitions for SID (VTU 802.1s Port State Information Database) 
*        Translation Unit.
*
* DEPENDENCIES:
*
* FILE REVISION NUMBER:
*       $Revision: $
*******************************************************************************/

#include <msApi.h>
#include <gtSem.h>
#include <gtHwCntl.h>
#include <gtDrvSwRegs.h>

/****************************************************************************/
/* Forward function declaration.                                            */
/****************************************************************************/

static GT_STATUS stuOperationPerform
(
    IN        GT_QD_DEV           *dev,
    IN      GT_STU_OPERATION    stuOp,
    INOUT   GT_U8               *valid,
    INOUT     GT_STU_ENTRY        *stuEntry
);

/*******************************************************************************
* gstuGetEntryCount
*
* DESCRIPTION:
*       Gets the current number of valid entries in the STU table
*
* INPUTS:
*       None.
*
* OUTPUTS:
*       numEntries - number of STU entries.
*
* RETURNS:
*       GT_OK      - on success
*       GT_FAIL    - on error
*        GT_NOT_SUPPORTED - if current device does not support this feature.
*
* COMMENTS:
*       None
*
*******************************************************************************/
GT_STATUS gstuGetEntryCount
(
    IN  GT_QD_DEV *dev,
    OUT GT_U32    *numEntries
)
{
    GT_U8               valid;
    GT_U32                numOfEntries;
    GT_STATUS           retVal;
    GT_STU_ENTRY        entry;

    DBG_INFO(("gstuGetEntryCount Called.\n"));

    /* check if device supports this feature */
    if (!IS_IN_DEV_GROUP(dev,DEV_802_1S_STU))
    {
        DBG_INFO(("GT_NOT_SUPPORTED\n"));
        return GT_NOT_SUPPORTED;
    }

    entry.sid = 0;
    numOfEntries = 0;

    while(1)
    {
        retVal = stuOperationPerform(dev,GET_NEXT_STU_ENTRY,&valid,&entry);
        if(retVal != GT_OK)
        {
            DBG_INFO(("Failed (stuOperationPerform returned GT_FAIL).\n"));
            return retVal;
        }

        if( entry.sid==0x3F )
        {
            if (valid==1) numOfEntries++;
            break;
        }

        numOfEntries++;
    }

    *numEntries = numOfEntries;

    DBG_INFO(("OK.\n"));
    return GT_OK;

}


/*******************************************************************************
* gstuGetEntryFirst
*
* DESCRIPTION:
*       Gets first lexicographic entry from the STU.
*
* INPUTS:
*       None.
*
* OUTPUTS:
*       stuEntry - find the first valid STU entry.
*
* RETURNS:
*       GT_OK      - on success
*       GT_FAIL    - on error
*       GT_NO_SUCH - table is empty.
*        GT_NOT_SUPPORTED - if current device does not support this feature.
*
* COMMENTS:
*
*******************************************************************************/
GT_STATUS gstuGetEntryFirst
(
    IN  GT_QD_DEV       *dev,
    OUT GT_STU_ENTRY    *stuEntry
)
{
    GT_U8               valid;
    GT_STATUS           retVal;
    GT_U8               port; 
    GT_LPORT               lport; 
    GT_STU_ENTRY        entry;

    DBG_INFO(("gstuGetEntryFirst Called.\n"));

    /* check if device supports this feature */
    if (!IS_IN_DEV_GROUP(dev,DEV_802_1S_STU))
    {
        DBG_INFO(("GT_NOT_SUPPORTED\n"));
        return GT_NOT_SUPPORTED;
    }

    entry.sid = 0;
    valid = 0;

    retVal = stuOperationPerform(dev,GET_NEXT_STU_ENTRY,&valid, &entry);
    if(retVal != GT_OK)
    {
        DBG_INFO(("Failed (stuOperationPerform returned GT_FAIL).\n"));
        return retVal;
    }

    /* retrieve the value from the operation */

    if((entry.sid == 0x3F) && (valid == 0))
        return GT_NO_SUCH;

    stuEntry->sid = entry.sid;

    for(lport=0; lport<dev->numOfPorts; lport++)
    {
        port = GT_LPORT_2_PORT(lport);
        stuEntry->portState[lport]=entry.portState[port];
    }

    DBG_INFO(("OK.\n"));
    return GT_OK;
}


/*******************************************************************************
* gstuGetEntryNext
*
* DESCRIPTION:
*       Gets next lexicographic STU entry from the specified SID.
*
* INPUTS:
*       stuEntry - the SID to start the search.
*
* OUTPUTS:
*       stuEntry - next STU entry.
*
* RETURNS:
*       GT_OK      - on success.
*       GT_FAIL    - on error or entry does not exist.
*       GT_NO_SUCH - no more entries.
*        GT_NOT_SUPPORTED - if current device does not support this feature.
*
* COMMENTS:
*
*******************************************************************************/
GT_STATUS gstuGetEntryNext
(
    IN  GT_QD_DEV       *dev,
    INOUT GT_STU_ENTRY  *stuEntry
)
{
    GT_U8               valid;
    GT_STATUS           retVal;
    GT_U8               port; 
    GT_LPORT               lport; 
    GT_STU_ENTRY        entry;

    DBG_INFO(("gstuGetEntryNext Called.\n"));
    
    /* check if device supports this feature */

    if (!IS_IN_DEV_GROUP(dev,DEV_802_1S_STU))
    {
        DBG_INFO(("GT_NOT_SUPPORTED\n"));
        return GT_NOT_SUPPORTED;
    }

    if(stuEntry->sid >= 0x3F)
    {
        return GT_NO_SUCH;
    }
    else
    {
        entry.sid = stuEntry->sid;
    }
    valid = 0;

    retVal = stuOperationPerform(dev,GET_NEXT_STU_ENTRY,&valid, &entry);
    if(retVal != GT_OK)
    {
        DBG_INFO(("Failed (stuOperationPerform returned GT_FAIL).\n"));
        return retVal;
    }

    /* retrieve the value from the operation */ 

    if((entry.sid == 0x3F) && (valid == 0))
        return GT_NO_SUCH;

    stuEntry->sid = entry.sid;

    for(lport=0; lport<dev->numOfPorts; lport++)
    {
        port = GT_LPORT_2_PORT(lport);
        stuEntry->portState[lport]=entry.portState[port];
    }

    DBG_INFO(("OK.\n"));
    return GT_OK;
}



/*******************************************************************************
* gstuFindSidEntry
*
* DESCRIPTION:
*       Find STU entry for a specific SID, it will return the entry, if found, 
*       along with its associated data 
*
* INPUTS:
*       stuEntry - contains the SID to searche for 
*
* OUTPUTS:
*       found    - GT_TRUE, if the appropriate entry exists.
*       stuEntry - the entry parameters.
*
* RETURNS:
*       GT_OK      - on success.
*       GT_FAIL    - on error or entry does not exist.
*       GT_NO_SUCH - no such entry.
*        GT_NOT_SUPPORTED - if current device does not support this feature.
*
* COMMENTS:
*        Valid SID is 1 ~ 63.
*
*******************************************************************************/
GT_STATUS gstuFindSidEntry
(
    IN  GT_QD_DEV       *dev,
    INOUT GT_STU_ENTRY  *stuEntry,
    OUT GT_BOOL         *found
)
{
    GT_U8               valid;
    GT_STATUS           retVal;
    GT_U8               port;
    GT_LPORT            lport;
    GT_STU_ENTRY        entry;

    DBG_INFO(("gstuFindSidEntry Called.\n"));

    /* check if device supports this feature */
    if (!IS_IN_DEV_GROUP(dev,DEV_802_1S_STU))
    {
        DBG_INFO(("GT_NOT_SUPPORTED\n"));
        return GT_NOT_SUPPORTED;
    }

    if((stuEntry->sid == 0) || (stuEntry->sid > 0x3F))
    {
        DBG_INFO(("GT_BAD_PARAM\n"));
        return GT_BAD_PARAM;
    }

    *found = GT_FALSE;

    /* Decrement 1 from sid */
    entry.sid   = stuEntry->sid-1;
    valid = 0; /* valid is not used as input in this operation */

    retVal = stuOperationPerform(dev,GET_NEXT_STU_ENTRY,&valid, &entry);
    if(retVal != GT_OK)
    {
        DBG_INFO(("Failed (stuOperationPerform returned GT_FAIL).\n"));
        return retVal;
    }

    /* retrive the value from the operation */ 
    if ((entry.sid != stuEntry->sid) | (valid == 0))
        return GT_NO_SUCH;

    for(lport=0; lport<dev->numOfPorts; lport++)
    {
        port = GT_LPORT_2_PORT(lport);
        stuEntry->portState[lport]=entry.portState[port];
    }

    *found = GT_TRUE;

    DBG_INFO(("OK.\n"));
    return GT_OK;
}


/*******************************************************************************
* gstuAddEntry
*
* DESCRIPTION:
*       Creates or update the entry in STU table based on user input.
*
* INPUTS:
*       stuEntry    - stu entry to insert to the STU.
*
* OUTPUTS:
*       None
*
* RETURNS:
*       GT_OK             - on success
*       GT_FAIL           - on error
*        GT_NOT_SUPPORTED - if current device does not support this feature.
*
* COMMENTS:
*        Valid SID is 1 ~ 63.
*
*******************************************************************************/
GT_STATUS gstuAddEntry
(
    IN  GT_QD_DEV       *dev,
    IN  GT_STU_ENTRY    *stuEntry
)
{
    GT_U8               valid;
    GT_STATUS           retVal;
    GT_U8           port; 
    GT_LPORT           lport; 
    GT_STU_ENTRY     tmpStuEntry;
    GT_BOOL             found;
    int                count = 50000;
    GT_STU_ENTRY        entry;

    DBG_INFO(("gstuAddEntry Called.\n"));

    /* check if device supports this feature */
    if (!IS_IN_DEV_GROUP(dev,DEV_802_1S_STU))
    {
        DBG_INFO(("GT_NOT_SUPPORTED\n"));
        return GT_NOT_SUPPORTED;
    }

    if((stuEntry->sid == 0) || (stuEntry->sid > 0x3F))
    {
        DBG_INFO(("GT_BAD_PARAM\n"));
        return GT_BAD_PARAM;
    }

    entry.sid = stuEntry->sid;

    valid = 1; /* for load operation */

    for(port=0; port<dev->maxPorts; port++)
    {
        lport = GT_PORT_2_LPORT(port);
        if (lport == GT_INVALID_PORT)
            entry.portState[port] = 0;
        else
            entry.portState[port] = stuEntry->portState[lport];
    }

    retVal = stuOperationPerform(dev,LOAD_PURGE_STU_ENTRY,&valid, &entry);
    if(retVal != GT_OK)
    {
        DBG_INFO(("Failed (stuOperationPerform returned GT_FAIL).\n"));
        return retVal;
    }

    /* verify that the given entry has been added */
    tmpStuEntry.sid = stuEntry->sid;

    if((retVal = gstuFindSidEntry(dev,&tmpStuEntry,&found)) != GT_OK)
    {
        while(count--);
        if((retVal = gstuFindSidEntry(dev,&tmpStuEntry,&found)) != GT_OK)
        {
            DBG_INFO(("Added entry cannot be found\n"));
            return retVal;
        }
    }
    if(found == GT_FALSE)
    {
        DBG_INFO(("Added entry cannot be found\n"));
        return GT_FAIL;
    }

    DBG_INFO(("OK.\n"));
    return GT_OK;
}

/*******************************************************************************
* gstuDelEntry
*
* DESCRIPTION:
*       Deletes STU entry specified by user.
*
* INPUTS:
*       stuEntry - the STU entry to be deleted 
*
* OUTPUTS:
*       None.
*
* RETURNS:
*       GT_OK           - on success
*       GT_FAIL         - on error
*        GT_NOT_SUPPORTED - if current device does not support this feature.
*
* COMMENTS:
*        Valid SID is 1 ~ 63.
*
*******************************************************************************/
GT_STATUS gstuDelEntry
(
    IN  GT_QD_DEV       *dev,
    IN  GT_STU_ENTRY     *stuEntry
)
{
    GT_U8               valid;
    GT_STATUS           retVal;
    GT_STU_ENTRY        entry;

    DBG_INFO(("gstuDelEntry Called.\n"));

    /* check if device supports this feature */
    if (!IS_IN_DEV_GROUP(dev,DEV_802_1S_STU))
    {
        DBG_INFO(("GT_NOT_SUPPORTED\n"));
        return GT_NOT_SUPPORTED;
    }
    
    if((stuEntry->sid == 0) || (stuEntry->sid > 0x3F))
    {
        DBG_INFO(("GT_BAD_PARAM\n"));
        return GT_BAD_PARAM;
    }

    entry.sid = stuEntry->sid;
    valid = 0; /* for delete operation */

    retVal = stuOperationPerform(dev,LOAD_PURGE_STU_ENTRY,&valid, &entry);
    if(retVal != GT_OK)
    {
        DBG_INFO(("Failed (stuOperationPerform returned GT_FAIL).\n"));
        return retVal;
    }
    DBG_INFO(("OK.\n"));
    return GT_OK;
}

/****************************************************************************/
/* Internal use functions.                                                  */
/****************************************************************************/

static GT_STATUS stuSetSTUData
(
    IN    GT_QD_DEV           *dev,
    IN    GT_STU_ENTRY        *entry
)
{
    GT_STATUS       retVal;         /* Functions return value.      */
    GT_U16          data1,data2,data3;           /* Data to be set into the      */
    GT_U16            nStuData = 0;

    data1 = data2 = data3 = 0;

    switch (dev->maxPorts)
    {
        case 11:
            data3 |= (entry->portState[10] & 3) << 10;
            /* pass through */
        case 10:
            data3 |= (entry->portState[9] & 3) << 6;
            /* pass through */
        case 9:
            data3 |= (entry->portState[8] & 3) << 2;
            nStuData++;

            /* pass through */
        case 8:
            data2 |= (entry->portState[7] & 3) << 14;
            /* pass through */
        case 7:
            data2 |= (entry->portState[6] & 3) << 10;
            /* pass through */
        case 6:
            data2 |= (entry->portState[5] & 3) << 6;
            /* pass through */
        case 5:
            data2 |= (entry->portState[4] & 3) << 2;
            nStuData++;

            /* pass through */
        case 4:
            data1 |= (entry->portState[3] & 3) << 14;
            /* pass through */
        case 3:
            data1 |= (entry->portState[2] & 3) << 10;
            /* pass through */
        case 2:
            data1 |= (entry->portState[1] & 3) << 6;
            /* pass through */
        case 1:
            data1 |= (entry->portState[0] & 3) << 2;
            nStuData++;
            break;

        default:
            return GT_FAIL;
    }

    switch(nStuData)
    {
        case 3:
            retVal = hwWriteGlobalReg(dev,QD_REG_VTU_DATA3_REG,data3);
            if(retVal != GT_OK)
            {
                return retVal;
            }
            /* pass through */
        case 2:
            retVal = hwWriteGlobalReg(dev,QD_REG_VTU_DATA2_REG,data2);
            if(retVal != GT_OK)
            {
                return retVal;
            }
            /* pass through */
        case 1:
            retVal = hwWriteGlobalReg(dev,QD_REG_VTU_DATA1_REG,data1);
            if(retVal != GT_OK)
            {
                return retVal;
            }
            break;
        default:
            return GT_FAIL;
    }
    
    return retVal;        
}

static GT_STATUS stuGetSTUData
(
    IN    GT_QD_DEV           *dev,
    OUT    GT_STU_ENTRY        *entry
)
{
    GT_STATUS       retVal;         /* Functions return value.      */
    GT_U16          data1,data2,data3;           /* Data to be set into the      */
    GT_U16            nStuData = 0;

    data1 = data2 = data3 = 0;

    gtMemSet((void*)entry->portState,0,sizeof(entry->portState));

    switch (dev->maxPorts)
    {
        case 11:
        case 10:
        case 9:
            nStuData = 3;
            break;

        case 8:
        case 7:
        case 6:
        case 5:
            nStuData = 2;
            break;

        case 4:
        case 3:
        case 2:
        case 1:
            nStuData = 1;
            break;

        default:
            return GT_FAIL;
    }

    switch(nStuData)
    {
        case 3:
            retVal = hwReadGlobalReg(dev,QD_REG_VTU_DATA3_REG,&data3);
            if(retVal != GT_OK)
            {
                return retVal;
            }
            /* pass through */
        case 2:
            retVal = hwReadGlobalReg(dev,QD_REG_VTU_DATA2_REG,&data2);
            if(retVal != GT_OK)
            {
                return retVal;
            }
            /* pass through */
        case 1:
            retVal = hwReadGlobalReg(dev,QD_REG_VTU_DATA1_REG,&data1);
            if(retVal != GT_OK)
            {
                return retVal;
            }
            break;
        default:
            return GT_FAIL;
    }
    
    switch (dev->maxPorts)
    {
        case 11:
            entry->portState[10]  = (data3 >> 10) & 3 ;
            /* pass through */
        case 10:
            entry->portState[9]  = (data3 >> 6) & 3 ;
            /* pass through */
        case 9:
            entry->portState[8]  = (data3 >> 2) & 3 ;
            /* pass through */
        case 8:
            entry->portState[7]  = (data2 >> 14) & 3 ;
            /* pass through */
        case 7:
            entry->portState[6]  = (data2 >> 10) & 3 ;
            /* pass through */
        case 6:
            entry->portState[5]  = (data2 >> 6) & 3 ;
            /* pass through */
        case 5:
            entry->portState[4]  = (data2 >> 2) & 3 ;
            /* pass through */
        case 4:
            entry->portState[3]  = (data1 >> 14) & 3 ;
            /* pass through */
        case 3:
            entry->portState[2]  = (data1 >> 10) & 3 ;
            /* pass through */
        case 2:
            entry->portState[1]  = (data1 >> 6) & 3 ;
            /* pass through */
        case 1:
            entry->portState[0]  = (data1 >> 2) & 3 ;
            break;

        default:
            return GT_FAIL;
    }

    return GT_OK;
}


/*******************************************************************************
* stuOperationPerform
*
* DESCRIPTION:
*       This function is used by all STU control functions, and is responsible
*       to write the required operation into the STU registers.
*
* INPUTS:
*       stuOp       - The STU operation bits to be written into the STU
*                     operation register.
*       sid         - sid
*       valid       - valid bit
*       stuData     - STU Data with port state information
*
* OUTPUTS:
*       sid         - sid
*       valid       - valid bit
*       stuData     - STU Data with port state information
*
* RETURNS:
*       GT_OK on success,
*       GT_FAIL otherwise.
*
* COMMENTS:
*
*******************************************************************************/

static GT_STATUS stuOperationPerform
(
    IN        GT_QD_DEV           *dev,
    IN      GT_STU_OPERATION    stuOp,
    INOUT   GT_U8               *valid,
    INOUT    GT_STU_ENTRY        *entry
)
{
    GT_STATUS       retVal;         /* Functions return value.      */
    GT_U16          data;           /* Data to be set into the      */
                                /* register.                    */

    gtSemTake(dev,dev->vtuRegsSem,OS_WAIT_FOREVER);

    /* Wait until the VTU in ready. */
    data = 1;
    while(data == 1)
    {
        retVal = hwGetGlobalRegField(dev,QD_REG_VTU_OPERATION,15,1,&data);
        if(retVal != GT_OK)
        {
            gtSemGive(dev,dev->vtuRegsSem);
            return retVal;
        }
    }

    /* Set the VTU data register if Load operation is required. */
    if (stuOp == LOAD_PURGE_STU_ENTRY)
    {
        if (*valid == 1)
        {
            /* set the Port State for all the ports */
            retVal = stuSetSTUData(dev,entry);
            if(retVal != GT_OK)
            {
                gtSemGive(dev,dev->vtuRegsSem);
                return retVal;
            }

            /* Set the valid bit (QD_REG_VTU_VID_REG) */
               data= *valid << 12 ;
            retVal = hwWriteGlobalReg(dev,(GT_U8)(QD_REG_VTU_VID_REG),data);
               if(retVal != GT_OK)
            {
                   gtSemGive(dev,dev->vtuRegsSem);
                return retVal;
                  }        
        }
        else
        {
            /* Clear the valid bit (QD_REG_VTU_VID_REG) */
               data= 0 ;
            retVal = hwWriteGlobalReg(dev,(GT_U8)(QD_REG_VTU_VID_REG),data);
               if(retVal != GT_OK)
            {
                   gtSemGive(dev,dev->vtuRegsSem);
                return retVal;
                  }        
        }
    }

    /* Set the SID register (QD_REG_STU_SID_REG) */
       data= (entry->sid) & 0x3F;
    retVal = hwWriteGlobalReg(dev,(GT_U8)(QD_REG_STU_SID_REG),data);
       if(retVal != GT_OK)
    {
           gtSemGive(dev,dev->vtuRegsSem);
        return retVal;
       }        

    /* Start the STU Operation by defining the stuOp and VTUBusy */
    data = (1 << 15) | (stuOp << 12);

    retVal = hwWriteGlobalReg(dev,QD_REG_VTU_OPERATION,data);
    if(retVal != GT_OK)
    {
        gtSemGive(dev,dev->vtuRegsSem);
        return retVal;
    }

    /* If the operation is a get next operation wait for the response   */
    if(stuOp == GET_NEXT_STU_ENTRY)
    {
        /* Wait until the STU in ready. */
        data = 1;
        while(data == 1)
        {
            retVal = hwGetGlobalRegField(dev,QD_REG_VTU_OPERATION,15,1,&data);
            if(retVal != GT_OK)
            {
                gtSemGive(dev,dev->vtuRegsSem);
                return retVal;
            }
        }

        /****************** get the valid bit *******************/
        retVal = hwGetGlobalRegField(dev,QD_REG_VTU_VID_REG,12,1,&data);
        if(retVal != GT_OK)
        {
            gtSemGive(dev,dev->vtuRegsSem);
            return retVal;
        }

        *valid = (GT_U8)data;

        /****************** get the sid *******************/

        retVal = hwReadGlobalReg(dev,QD_REG_STU_SID_REG,&data);
        if(retVal != GT_OK)
        {
            gtSemGive(dev,dev->vtuRegsSem);
            return retVal;
        }

        /* the sid is bits 0-5 */
        entry->sid   = data & 0x3F;

        if (*valid)
        {
            /* get the Port State for all the ports */
            retVal = stuGetSTUData(dev,entry);
            if(retVal != GT_OK)
            {
                gtSemGive(dev,dev->vtuRegsSem);
                return retVal;
            }

        } /* entry is valid */

    } /* end of get next entry */

    gtSemGive(dev,dev->vtuRegsSem);
    return GT_OK;
}
