#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;
}
