/*
* Copyright c                  Realtek Semiconductor Corporation, 2009  
* All rights reserved.
* 
* Program : Switch table Layer3 route driver,following features are included:
*	Route/Multicast
* Abstract :
* Author : hyking (hyking_liu@realsil.com.cn)  
*/
#include <net/rtl/rtl_types.h>
#include <net/ipv6.h>
#include "asicRegs.h"
#include "rtl865x_asicCom.h"
#include "rtl865x_asicBasic.h"
#include "rtl865x_asicL3.h"
//#include "rtl_utils.h"
#include "rtl865x_hwPatch.h"
#include <net/rtl/rtl865x_route_api.h>
#include "../l3Driver/rtl865x_route.h"

#if defined(CONFIG_RTL_8685S_HWNAT)
#include "../testModel/testModel.h"
#include "../common/rtl_utils.h"
#include <net/rtl/rtl_types.h>
#endif /* CONFIG_RTL_8685S_HWNAT */

int32 rtl8651_setAsicExtIntIpTable(uint32 index, rtl865x_tblAsicDrv_extIntIpParam_t *extIntIpp) 
{
	rtl8651_tblAsic_extIpTable_t   entry;
	
	if((index >= RTL8651_IPTABLE_SIZE) || (extIntIpp == NULL) || 
	((extIntIpp->localPublic == TRUE) && (extIntIpp->nat == TRUE))) //Local public IP and NAT property cannot co-exist
		return FAILED;

	memset(&entry,0,sizeof(entry));
	entry.externalIP = extIntIpp->extIpAddr;
	entry.internalIP = extIntIpp->intIpAddr;
	entry.isLocalPublic = extIntIpp->localPublic==TRUE? 1: 0;
	entry.isOne2One = extIntIpp->nat==TRUE? 1: 0;
    	entry.nextHop = extIntIpp->nhIndex;
	entry.valid = 1;
	return _rtl8651_forceAddAsicEntry(TYPE_EXT_INT_IP_TABLE, index, &entry);

}

int32 rtl8651_delAsicExtIntIpTable(uint32 index) 
{
    	rtl8651_tblAsic_extIpTable_t   entry;

	if(index >= RTL8651_IPTABLE_SIZE)
		return FAILED;
        
	memset(&entry,0,sizeof(entry));
	entry.valid = 0;
	return _rtl8651_forceAddAsicEntry(TYPE_EXT_INT_IP_TABLE, index, &entry);
}

int32 rtl8651_getAsicExtIntIpTable(uint32 index, rtl865x_tblAsicDrv_extIntIpParam_t *extIntIpp)
{
    	rtl8651_tblAsic_extIpTable_t   entry;
    
	if((index>=RTL8651_IPTABLE_SIZE) || (extIntIpp == NULL))
		return FAILED;
	_rtl8651_readAsicEntry(TYPE_EXT_INT_IP_TABLE, index, &entry);
	if(entry.valid == 0)
		return FAILED;//Entry not found
	extIntIpp->extIpAddr = entry.externalIP;
	extIntIpp->intIpAddr = entry.internalIP;
	extIntIpp->localPublic = entry.isLocalPublic==1? TRUE: FALSE;
	extIntIpp->nat = entry.isOne2One==1? TRUE: FALSE;
    	extIntIpp->nhIndex = entry.nextHop;
    	return SUCCESS;
}

int32 rtl8651_setAsicPppoe(uint32 index, rtl865x_tblAsicDrv_pppoeParam_t *pppoep) 
{
	rtl8651_tblAsic_pppoeTable_t entry;

	if((index >= RTL8651_PPPOETBL_SIZE) || (pppoep == NULL) || (pppoep->sessionId == 0xffff))
		return FAILED;

	memset(&entry,0,sizeof(entry));
	entry.sessionID = pppoep->sessionId;
#if 1 //chhuang: #ifdef CONFIG_RTL865XB
	//entry.ageTime = pppoep->age;
	entry.ageTime = (pppoep->age>0)?0x07:0;
#endif /*CONFIG_RTL865XB*/
	
	return _rtl8651_forceAddAsicEntry(TYPE_PPPOE_TABLE, index, &entry);
}

int32 rtl8651_getAsicPppoe(uint32 index, rtl865x_tblAsicDrv_pppoeParam_t *pppoep) {
	rtl8651_tblAsic_pppoeTable_t entry;

	if((index >= RTL8651_PPPOETBL_SIZE) || (pppoep == NULL))
		return FAILED;

	_rtl8651_readAsicEntry(TYPE_PPPOE_TABLE, index, &entry);
	pppoep->sessionId = entry.sessionID;
#if 1 //chhuang: #ifdef CONFIG_RTL865XB
	pppoep->age = entry.ageTime;
#endif /*CONFIG_RTL865XB*/

	return SUCCESS;
}



int32 rtl8651_setAsicNextHopTable(uint32 index, rtl865x_tblAsicDrv_nextHopParam_t *nextHopp)
{
    rtl8651_tblAsic_nextHopTable_t  entry;
    if((index >= RTL8651_NEXTHOPTBL_SIZE ) || (nextHopp == NULL))
        return FAILED;

	/* for debug
	rtlglue_printf("[%s:%d] rtl8651_setAsicNextHopTable(idx=%d,Row=%d,Col=%d,PPPIdx=%d,dvid=%d,IPIdx=%d,type=%d)\n",
		__FILE__,__LINE__,index, nextHopp->nextHopRow,nextHopp->nextHopColumn,nextHopp->pppoeIdx,
		nextHopp->dvid,nextHopp->extIntIpIdx,nextHopp->isPppoe);
	*/
    memset(&entry,0,sizeof(entry));
    entry.nextHop = (nextHopp->nextHopRow <<2) | nextHopp->nextHopColumn;
    entry.PPPoEIndex = nextHopp->pppoeIdx;
    entry.dstVid = nextHopp->dvid;
    entry.IPIndex = nextHopp->extIntIpIdx;
    entry.type = nextHopp->isPppoe==TRUE? 1: 0;
    return _rtl8651_forceAddAsicEntry(TYPE_NEXT_HOP_TABLE, index, &entry);
}

int32 rtl8651_getAsicNextHopTable(uint32 index, rtl865x_tblAsicDrv_nextHopParam_t *nextHopp) 
{
    rtl8651_tblAsic_nextHopTable_t  entry;
    if((index>=RTL8651_NEXTHOPTBL_SIZE) || (nextHopp == NULL))
        return FAILED;

    _rtl8651_readAsicEntry(TYPE_NEXT_HOP_TABLE, index, &entry);
    nextHopp->nextHopRow = entry.nextHop>>2;
    nextHopp->nextHopColumn = entry.nextHop&0x3;
    nextHopp->pppoeIdx = entry.PPPoEIndex;
    nextHopp->dvid = entry.dstVid;
    nextHopp->extIntIpIdx = entry.IPIndex;
    nextHopp->isPppoe = entry.type==1? TRUE: FALSE;
    return SUCCESS;
}


#if defined(CONFIG_RTL_8685S_HWNAT)
int32 rtl8198C_setAsicIpv6Routing(uint32 index, rtl8198C_tblAsicDrv_ipv6routingParam_t *routingp) 
{

	rtl8198C_tblAsic_IPv6_l3RouteTable_t entry;

	if((index >= RTL8198C_IPv6ROUTINGTBL_SIZE) || (routingp == NULL))
		return FAILED;
    
	memset(&entry,0,sizeof(entry));
	entry.IPAddr0 = routingp->ipAddr.s6_addr32[3];
	entry.IPAddr1 = routingp->ipAddr.s6_addr32[2];
	entry.IPAddr2 = routingp->ipAddr.s6_addr32[1];
	entry.IPAddr3 = routingp->ipAddr.s6_addr32[0];
	entry.v6rdEg	=routingp->v6rdeg ;
	entry.v6rdIdx   =routingp->v6rdIdx ;
	
	switch(routingp->process) {
	case 0://PPPoE

		entry.linkTo.PPPoEEntry.ipMaskNum = routingp->ipMaskNum;
		entry.linkTo.PPPoEEntry.valid = 1;
		entry.linkTo.PPPoEEntry.process = routingp->process;
		entry.linkTo.PPPoEEntry.netif = routingp->netif;
		if(IS_AFTER_RL6405)
		{
			entry.linkTo.PPPoEEntry.nextHop = ((routingp->nextHopRow <<2) | routingp->nextHopColumn)&0x3ff;
			entry.linkTo.PPPoEEntry.nextHop10_10 = (routingp->nextHopRow >>8)&0x1 ;
		}
		else
		{
			entry.linkTo.PPPoEEntry.nextHop = (routingp->nextHopRow <<2) | routingp->nextHopColumn;
		}
		entry.linkTo.PPPoEEntry.pppIdx = routingp->pppoeIdx;



		break;
	case 1://Direct
		entry.linkTo.L2Entry.ipMaskNum = routingp->ipMaskNum;	
		entry.linkTo.L2Entry.valid = 1;			
		entry.linkTo.L2Entry.process = routingp->process;
		entry.linkTo.L2Entry.netif = routingp->netif;	
		if(IS_AFTER_RL6405)
		{
			entry.linkTo.L2Entry.nextHop = ((routingp->nextHopRow <<2) | routingp->nextHopColumn)&0x3ff;
			entry.linkTo.L2Entry.nextHop10_10 = (routingp->nextHopRow >>8)&0x1 ;
		}
		else
		{
			entry.linkTo.L2Entry.nextHop = (routingp->nextHopRow <<2) | routingp->nextHopColumn;
		}

		break;
	case 2://arp
		entry.linkTo.ARPEntry.ipMaskNum = routingp->ipMaskNum;
		entry.linkTo.ARPEntry.valid = 1;
		entry.linkTo.ARPEntry.process = routingp->process;
		entry.linkTo.ARPEntry.netif = routingp->netif;
		entry.linkTo.ARPEntry.subnetIdx=routingp->subnetIdx;
		break;
	case 3://CPU
	case 4://DROP
		/*
		  *   Note:  although the process of this routing entry is CPU/DROP,
		  *             we still have to assign "vid" field for packet decision process use.
		  *                                                                                          - 2005.3.23 -
		  */

		entry.linkTo.ARPEntry.netif = routingp->netif;
		entry.linkTo.ARPEntry.ipMaskNum = routingp->ipMaskNum;
		entry.linkTo.ARPEntry.process = routingp->process;
		entry.linkTo.ARPEntry.valid = 1;


		break;
	case 5://NAPT NextHop

		entry.linkTo.NxtHopEntry.nhStart = routingp->nhStart ;
		entry.linkTo.NxtHopEntry.nhNum = routingp->nhNum;
//		switch (routingp->nhNum)
//		{
//		    case 2: entry.linkTo.NxtHopEntry.nhNum = 0; break;
//		    case 4: entry.linkTo.NxtHopEntry.nhNum = 1; break;
//		    case 8: entry.linkTo.NxtHopEntry.nhNum = 2; break;
//		    case 16: entry.linkTo.NxtHopEntry.nhNum = 3; break;
//		    case 32: entry.linkTo.NxtHopEntry.nhNum = 4; break;
//		    default: return FAILED;
//		}
		entry.linkTo.NxtHopEntry.ipMaskNum = routingp->ipMaskNum;
		entry.linkTo.NxtHopEntry.valid = 1;
		entry.linkTo.NxtHopEntry.process = routingp->process;
		entry.linkTo.NxtHopEntry.nhNum = (routingp->nhNum>>1)-1;
		entry.linkTo.NxtHopEntry.nhStart = routingp->nhStart>>1;
		entry.linkTo.NxtHopEntry.nhNxt = routingp->nhNxt;
		entry.linkTo.NxtHopEntry.nhAlgo = routingp->nhAlgo;

		break;
		
	default: 
		return FAILED;
	}


    return _rtl8651_forceAddAsicEntry(TYPE_IPv6_ROUTING_TABLE, index, &entry);
}



int32 rtl8198C_getAsicIpv6Routing(uint32 index, rtl8198C_tblAsicDrv_ipv6routingParam_t *routingp) 
{
	rtl8198C_tblAsic_IPv6_l3RouteTable_t entry;

	if((index >= RTL8198C_IPv6ROUTINGTBL_SIZE) || (routingp == NULL))
		return FAILED;

	_rtl8651_readAsicEntry(TYPE_IPv6_ROUTING_TABLE, index, &entry);

	if(entry.linkTo.ARPEntry.valid == 0)
		return FAILED;


	routingp->ipAddr.s6_addr32[3] = entry.IPAddr0;
	routingp->ipAddr.s6_addr32[2] = entry.IPAddr1;
	routingp->ipAddr.s6_addr32[1] = entry.IPAddr2;
	routingp->ipAddr.s6_addr32[0] = entry.IPAddr3;
	routingp->process = entry.linkTo.ARPEntry.process;
	routingp->ipMaskNum= entry.linkTo.PPPoEEntry.ipMaskNum;

	routingp->v6rdeg =entry.v6rdEg;
	routingp->v6rdIdx =entry.v6rdIdx;
	
	switch(routingp->process) {
	case 0://PPPoE

		routingp->netif =entry.linkTo.PPPoEEntry.netif;
		routingp->valid =1;
		if(IS_AFTER_RL6405)
		{
			routingp->nextHopRow = (entry.linkTo.PPPoEEntry.nextHop>>2) | (entry.linkTo.PPPoEEntry.nextHop10_10 <<8);
		}
		else
		{
			routingp->nextHopRow = entry.linkTo.PPPoEEntry.nextHop>>2;
		}
		routingp->nextHopColumn = entry.linkTo.PPPoEEntry.nextHop & 0x3;
		routingp->pppoeIdx = entry.linkTo.PPPoEEntry.pppIdx;

		break;
	case 1://Direct

		routingp->pppoeIdx = 0;
		routingp->valid = 1;
		routingp ->netif =entry.linkTo.L2Entry.netif;
		if(IS_AFTER_RL6405)
		{
			routingp->nextHopRow = (entry.linkTo.L2Entry.nextHop>>2) | (entry.linkTo.L2Entry.nextHop10_10 <<8);
		}
		else
		{
			routingp->nextHopRow = entry.linkTo.L2Entry.nextHop>>2;
		}
		routingp->nextHopColumn = entry.linkTo.L2Entry.nextHop&0x3;

		break;
	case 2://Indirect

		routingp->pppoeIdx = 0;
		routingp->nextHopRow = 0;
		routingp->nextHopColumn = 0;
		routingp->valid =1 ;
		routingp ->netif =entry.linkTo.L2Entry.netif;
		routingp->subnetIdx = entry.linkTo.ARPEntry.subnetIdx;

		break;
	case 3: /* CPU */
		routingp->valid = 1;
	case 4: /* Drop */

		routingp->valid = 1;
		routingp->pppoeIdx = 0;
		routingp->nextHopRow = 0;
		routingp->nextHopColumn = 0;

		break;

	case 5:

		routingp->nhStart = (entry.linkTo.NxtHopEntry.nhStart) << 1;
		switch (entry.linkTo.NxtHopEntry.nhNum)
		{
		case 0: routingp->nhNum = 2; break;
		case 1: routingp->nhNum = 4; break;
		case 2: routingp->nhNum = 8; break;
		case 3: routingp->nhNum = 16; break;
		case 4: routingp->nhNum = 32; break;
				break;
			default: 
				return FAILED;
		}

		routingp->valid =1 ;
//		routingp->nhNum =entry.linkTo.NxtHopEntry.nhNum ;
//		routingp->nhStart =entry.linkTo.NxtHopEntry.nhStart;
		routingp->nhNxt = entry.linkTo.NxtHopEntry.nhNxt;	
		routingp->nhAlgo = entry.linkTo.NxtHopEntry.nhAlgo;
		break;

	default: 
		printk("Unknown Process! @ %s %d\n",__func__,__LINE__);
		return FAILED;
	}
    return SUCCESS;
}

int32 rtl8198C_delAsicIpv6Routing(uint32 index)
{
	rtl8198C_tblAsic_IPv6_l3RouteTable_t entry;
	if(index >= RTL8198C_IPv6ROUTINGTBL_SIZE )
		return FAILED;
	memset(&entry,0,sizeof(entry));
    return _rtl8651_forceAddAsicEntry(TYPE_IPv6_ROUTING_TABLE, index, &entry);
}


int32 rtl8198C_setAsicArp6(rtl8198C_tblAsicDrv_v6NeighParam_t *arpp)
{
	rtl8198C_tblAsic_v6NeighTable_t   entry;
	uint32 index;
	uint32 i;
		
	index = rtl8198C_Arp6HashFunction(arpp);

	if((index >= RTL8198C_V6NEIGHTBL_SIZE) || (arpp == NULL))
		return FAILED;

	for(i=0;i<4;i++){
		
		_rtl8651_readAsicEntry(TYPE_IPV6_NEIGH_TABLE, (index*4)+i, &entry);

		if(entry.valid == 0)
		{
			memset(&entry,0,sizeof(entry));
			entry.valid = 1;
			if(IS_AFTER_RL6405)
			{
				entry.nexthop = ((arpp->nextHopRow<<2) | (arpp->nextHopColumn&0x3)) &0x3ff;
				entry.nexthop10_10 = (arpp->nextHopRow >>8 )&0x1; 
			}
			else
			{
				entry.nexthop = (arpp->nextHopRow<<2) | (arpp->nextHopColumn&0x3);
			}
			entry.subnetIdx = arpp->subnetIdx;
			entry.hostID17_0 = (arpp->hostID);
			entry.hostID49_18 = (arpp->hostID) >>18 ;
			entry.hostID63_50 = (arpp->hostID) >>50 ;
			entry.age=arpp->age;
			return _rtl8651_forceAddAsicEntry(TYPE_IPV6_NEIGH_TABLE, (index*4)+i, &entry);
			
		}

	}
	printk("rtl8198C_setAsicArp6 hash entry filled \n");
	return FAILED;

}

int32 rtl8198C_setAsicArp6_idx(uint32 index,rtl8198C_tblAsicDrv_v6NeighParam_t *arpp)
{
	rtl8198C_tblAsic_v6NeighTable_t   entry;

	if((index >= RTL8198C_V6NEIGHTBL_SIZE) || (arpp == NULL))
		return FAILED;

	memset(&entry,0,sizeof(entry));
	entry.valid = 1;	
	if(IS_AFTER_RL6405)
	{
		entry.nexthop = ((arpp->nextHopRow<<2) | (arpp->nextHopColumn&0x3))&0x3ff ;
		entry.nexthop10_10 = (arpp->nextHopRow >>8)&0x1;
	}
	else
	{
		entry.nexthop = (arpp->nextHopRow<<2) | (arpp->nextHopColumn&0x3);
	}
	entry.subnetIdx = arpp->subnetIdx;
	entry.hostID17_0 = (arpp->hostID);
	entry.hostID49_18 = (arpp->hostID) >>18 ;
	entry.hostID63_50 = (arpp->hostID) >>50 ;
	entry.age=arpp->age;

	return _rtl8651_forceAddAsicEntry(TYPE_IPV6_NEIGH_TABLE, index, &entry);
}


int32 rtl8198C_getAsicArp6(uint32 index ,rtl8198C_tblAsicDrv_v6NeighParam_t *arpp)
{
	rtl8198C_tblAsic_v6NeighTable_t   entry;

	if((index >= RTL8198C_V6NEIGHTBL_SIZE) || (arpp == NULL))
		return FAILED;
	_rtl8651_readAsicEntry(TYPE_IPV6_NEIGH_TABLE, index, &entry);

	if(entry.valid == 0)
		return FAILED;
	arpp->valid=1;
	if(IS_AFTER_RL6405)
	{
		arpp->nextHopRow = entry.nexthop>>2 | entry.nexthop10_10 <<8;
	}
	else
	{
		arpp->nextHopRow = entry.nexthop>>2;
	}
	arpp->nextHopColumn = entry.nexthop&0x3;
	arpp ->subnetIdx = entry.subnetIdx;
	arpp ->hostID = ( ((uint64)entry.hostID63_50) << 50) |  (((uint64)entry.hostID49_18)<<18) | ((uint64)entry.hostID17_0) ;
	arpp->age=entry.age&0x1f;


	return SUCCESS;

}


int32 rtl8198C_delAsicArp6(uint32 index)
{
	rtl8198C_tblAsic_v6NeighTable_t   entry;
	if((index >= RTL8198C_V6NEIGHTBL_SIZE) )
		return FAILED;

	memset(&entry,0,sizeof(entry));
	return _rtl8651_forceAddAsicEntry(TYPE_IPV6_NEIGH_TABLE, index, &entry);

}


int32 rtl8198C_setAsicDSLite(uint32 index, rtl8198C_tblAsicDrv_DSLiteParam_t *dslp)
{
	rtl8198C_tblAsic_DSLiteTable_t   entry;
	

	if((index >= RTL8198C_DSLITETBL_SIZE) || (dslp == NULL))
		return FAILED;


	memset(&entry,0,sizeof(entry));

	entry.hostIP0 =dslp ->hostIP.s6_addr32[3];
	entry.hostIP1 =dslp ->hostIP.s6_addr32[2];
	entry.hostIP2 =dslp ->hostIP.s6_addr32[1];
	entry.hostIP3 =dslp ->hostIP.s6_addr32[0];
	entry.hostIPMaskNum =dslp ->hostIPMaskNum;
	
	entry.ipAFTR24_0 =dslp->ipAFTR.s6_addr32[3];
	entry.ipAFTR56_25 =   (dslp->ipAFTR.s6_addr32[3] >>25) | (dslp->ipAFTR.s6_addr32[2] << 7);
	entry.ipAFTR88_57 =   (dslp->ipAFTR.s6_addr32[2] >>25) | (dslp->ipAFTR.s6_addr32[1] << 7);
	entry.ipAFTR120_89 =  (dslp->ipAFTR.s6_addr32[1] >>25) | (dslp->ipAFTR.s6_addr32[0] << 7);
	entry.ipAFTR127_121 = (dslp->ipAFTR.s6_addr32[0] >>25) ;

	entry.ipMaskAFTRNum = dslp ->ipMaskAFTRNum;
	entry.dslMtu = dslp->dslMtu;
	entry.valid =dslp->valid;


	return _rtl8651_forceAddAsicEntry(TYPE_DS_LITE_TABLE, index, &entry);

}



int32 rtl8198C_getAsicDSLite(uint32 index, rtl8198C_tblAsicDrv_DSLiteParam_t *dslp)
{
	rtl8198C_tblAsic_DSLiteTable_t   entry;
	if((index >= RTL8198C_DSLITETBL_SIZE) || (dslp == NULL))
		return FAILED;
	_rtl8651_readAsicEntry(TYPE_DS_LITE_TABLE, index, &entry);

	if(entry.valid == 0)
		return FAILED;


	dslp ->hostIP.s6_addr32[3]=entry.hostIP0 ;
	dslp ->hostIP.s6_addr32[2]=entry.hostIP1 ;
	dslp ->hostIP.s6_addr32[1]=entry.hostIP2 ;
	dslp ->hostIP.s6_addr32[0]=entry.hostIP3 ;
	dslp ->hostIPMaskNum=entry.hostIPMaskNum ;

	dslp->ipAFTR.s6_addr32[3] = (entry.ipAFTR24_0)		 | (entry.ipAFTR56_25  <<25);
	dslp->ipAFTR.s6_addr32[2] = (entry.ipAFTR56_25 >> 7) | entry.ipAFTR88_57   <<25 ;
	dslp->ipAFTR.s6_addr32[1] = (entry.ipAFTR88_57 >> 7) | entry.ipAFTR120_89  <<25 ;
	dslp->ipAFTR.s6_addr32[0] = (entry.ipAFTR120_89 >>7) | entry.ipAFTR127_121 <<25 ;

	dslp ->ipMaskAFTRNum=entry.ipMaskAFTRNum  ;
	dslp->dslMtu=entry.dslMtu ;
	dslp ->valid =1;
	return SUCCESS;
}


int32 rtl8198C_delAsicDSLite(uint32 index)
{
	rtl8198C_tblAsic_DSLiteTable_t   entry;
	if((index >= RTL8198C_DSLITETBL_SIZE) )
		return FAILED;

	memset(&entry,0,sizeof(entry));
	return _rtl8651_forceAddAsicEntry(TYPE_DS_LITE_TABLE, index, &entry);


}



int32 rtl8198C_setAsicNexthop6(uint32 index, rtl8198C_tblAsicDrv_v6NexthopParam_t *nxt6p)
{
	rtl8198C_tblAsic_v6NexthopTable_t   entry;
	if((index >= RTL8198C_V6NEXTHOPTBL_SIZE) || (nxt6p == NULL))
		return FAILED;


	memset(&entry,0,sizeof(entry));

	entry.isPppoe =nxt6p->isPppoe;
	entry.destVlanIdx=nxt6p->destVlanIdx;
	entry.pppTbIdx =nxt6p ->pppTbIdx ;
	entry.nexthop = (nxt6p->nextHopRow<<2) | (nxt6p->nextHopColumn&0x3);

	return _rtl8651_forceAddAsicEntry(TYPE_IPv6_NEXTHOP_TABLE, index, &entry);


}
int32 rtl8198C_getAsicNexthop6(uint32 index, rtl8198C_tblAsicDrv_v6NexthopParam_t *nxt6p)
{
	rtl8198C_tblAsic_v6NexthopTable_t   entry;
	if((index >= RTL8198C_V6NEXTHOPTBL_SIZE) || (nxt6p == NULL))
		return FAILED;
	_rtl8651_readAsicEntry(TYPE_IPv6_NEXTHOP_TABLE, index, &entry);
	
	nxt6p->isPppoe = entry.isPppoe;
	nxt6p->destVlanIdx =entry.destVlanIdx;
	nxt6p->pppTbIdx = entry.pppTbIdx;
	nxt6p->nextHopRow = entry.nexthop>>2;
	nxt6p->nextHopColumn = entry.nexthop&0x3;

	return SUCCESS;


}

int32 rtl8198C_delAsicNexthop6(uint32 index)
{
	rtl8198C_tblAsic_v6NexthopTable_t   entry;
	if((index >= RTL8198C_V6NEXTHOPTBL_SIZE))
		return FAILED;
	memset(&entry,0,sizeof(entry));
	return _rtl8651_forceAddAsicEntry(TYPE_IPv6_NEXTHOP_TABLE, index, &entry);

}


int32 rtl8198C_setAsicV6Rd(uint32 index, rtl8198C_tblAsicDrv_v6RDParam_t *v6rdp)
{

	rtl8198C_tblAsic_v6RDTable_t   entry;

	if((index >= RTL8198C_IPv6RDTBL_SIZE) || (v6rdp == NULL))
		return FAILED;

	memset(&entry,0,sizeof(entry));
	entry.ipCE =v6rdp->ipCE;
	entry.ipMaskCENum =v6rdp->ipMaskCENum;
	entry.v6rdPrefix25_0 =v6rdp->v6rdPrefix;
	entry.v6rdPrefix57_26=v6rdp->v6rdPrefix >>26;
	entry.v6rdPrefix63_58=v6rdp->v6rdPrefix>>58;
	entry.maskPrefixNum=v6rdp->maskPrefixNum;
	entry.ipBR19_0=v6rdp->ipBR ;
	entry.ipBR31_20=v6rdp->ipBR >>20 ;
	entry.maskBRNum = v6rdp->maskBRNum;
	entry.v6RDmtu = v6rdp->v6RDmtu;
	entry.valid = v6rdp->valid;

	return _rtl8651_forceAddAsicEntry(TYPE_6RD_TABLE, index, &entry);


}



int32 rtl8198C_getAsicV6Rd(uint32 index, rtl8198C_tblAsicDrv_v6RDParam_t *v6rdp)
{

	rtl8198C_tblAsic_v6RDTable_t	entry;
	if((index >= RTL8198C_IPv6RDTBL_SIZE) || (v6rdp == NULL))
		return FAILED;

	_rtl8651_readAsicEntry(TYPE_6RD_TABLE, index, &entry);
	if(entry.valid == 0)
		return FAILED;
	v6rdp->ipCE =entry.ipCE ;
	v6rdp->ipMaskCENum = entry.ipMaskCENum;
	v6rdp->v6rdPrefix= entry.v6rdPrefix25_0 | (((uint64)entry.v6rdPrefix57_26) <<26) | (((uint64)entry.v6rdPrefix63_58) <<58) ;
	v6rdp->maskPrefixNum =entry.maskPrefixNum;
	v6rdp->ipBR = entry.ipBR19_0 | (entry.ipBR31_20 <<20);
	v6rdp->maskBRNum=entry.maskBRNum;
	v6rdp->v6RDmtu = entry.v6RDmtu;
	v6rdp->valid =1 ;

	return SUCCESS;

}

int32 rtl8198C_delAsicV6Rd(uint32 index)
{
	rtl8198C_tblAsic_v6RDTable_t   entry;
	if((index >= RTL8198C_IPv6RDTBL_SIZE))
		return FAILED;
	memset(&entry,0,sizeof(entry));
	return _rtl8651_forceAddAsicEntry(TYPE_6RD_TABLE, index, &entry);

}

/*type =2 idx = srcAddr.s6_addr32[3]&0xFF  ,
  type =3 idx = dstAddr.s6_addr32[3]&0xFF  
  other IPM hash function */
uint32 rtl8198C_ipMulticastv6TableIndex(uint32 hash_type,struct in6_addr srcAddr,struct in6_addr dstAddr)
{
	uint32 idx=0,hash_idx_sip,hash_idx_dip;
	static uint32 sip[128],dip[128];
	uint32 sip_hash[8],dip_hash[8];
	uint32 i,j,offset,src,dst;

    if(hash_type==2)
    {
        idx = srcAddr.s6_addr32[3]&0xFF;
        return idx;
    }
    else if(hash_type==3)
    {
        idx = dstAddr.s6_addr32[3]&0xFF;
        return idx;
    }

	for(i=0; i<8; i++) {
		sip_hash[i]=0;
		dip_hash[i]=0;        
	}

    for(j=0;j<4;j++)
    {
        offset = j*32;
        src = srcAddr.s6_addr32[j];
        dst = dstAddr.s6_addr32[j];
    	for(i=0; i<32; i++)	{
    		if((src& (1<<i))!=0) {
    			sip[i+offset]=1;
    		}
    		else 	{
    			sip[i+offset]=0;
    		}

    		if((dst & (1<<i))!=0) {
    			dip[i+offset]=1;
    		}
    		else {
    			dip[i+offset]=0;
    		}			
    	}
    }

	sip_hash[0] = sip[7] ^ sip[15] ^ sip[23] ^ sip[31] ^ sip[39] ^ sip[47] ^ sip[55] ^ sip[63] ^ sip[71] ^ sip[79] ^ sip[87] ^ sip[95] ^ sip[103]^ sip[111] ^ sip[119] ^ sip[127];
	sip_hash[1] = sip[6] ^ sip[14] ^ sip[22] ^ sip[30] ^ sip[38] ^ sip[46] ^ sip[54] ^ sip[62] ^ sip[70] ^ sip[78] ^ sip[86] ^ sip[94] ^ sip[102]^ sip[110] ^ sip[118] ^ sip[126];
	sip_hash[2] = sip[5] ^ sip[13] ^ sip[21] ^ sip[29] ^ sip[37] ^ sip[45] ^ sip[53] ^ sip[61] ^ sip[69] ^ sip[77] ^ sip[85] ^ sip[93] ^ sip[101]^ sip[109] ^ sip[117] ^ sip[125];
	sip_hash[3] = sip[4] ^ sip[12] ^ sip[20] ^ sip[28] ^ sip[36] ^ sip[44] ^ sip[52] ^ sip[60] ^ sip[68] ^ sip[76] ^ sip[84] ^ sip[92] ^ sip[100]^ sip[108] ^ sip[116] ^ sip[124];
	sip_hash[4] = sip[3] ^ sip[11] ^ sip[19] ^ sip[27] ^ sip[35] ^ sip[43] ^ sip[51] ^ sip[59] ^ sip[67] ^ sip[75] ^ sip[83] ^ sip[91] ^ sip[99] ^ sip[107] ^ sip[115] ^ sip[123];
	sip_hash[5] = sip[2] ^ sip[10] ^ sip[18] ^ sip[26] ^ sip[34] ^ sip[42] ^ sip[50] ^ sip[58] ^ sip[66] ^ sip[74] ^ sip[82] ^ sip[90] ^ sip[98] ^ sip[106] ^ sip[114] ^ sip[122];
	sip_hash[6] = sip[1] ^ sip[9]  ^ sip[17] ^ sip[25] ^ sip[33] ^ sip[41] ^ sip[49] ^ sip[57] ^ sip[65] ^ sip[73] ^ sip[81] ^ sip[89] ^ sip[97] ^ sip[105] ^ sip[113] ^ sip[121];
	sip_hash[7] = sip[0] ^ sip[8]  ^ sip[16] ^ sip[24] ^ sip[32] ^ sip[40] ^ sip[48] ^ sip[56] ^ sip[64] ^ sip[72] ^ sip[80] ^ sip[88] ^ sip[96] ^ sip[104] ^ sip[112] ^ sip[120];

	dip_hash[7] = dip[7] ^ dip[15] ^ dip[23] ^ dip[31] ^ dip[39] ^ dip[47] ^ dip[55] ^ dip[63] ^ dip[71] ^ dip[79] ^ dip[87] ^ dip[95] ^ dip[103]^ dip[111] ^ dip[119] ^ dip[127];
	dip_hash[6] = dip[6] ^ dip[14] ^ dip[22] ^ dip[30] ^ dip[38] ^ dip[46] ^ dip[54] ^ dip[62] ^ dip[70] ^ dip[78] ^ dip[86] ^ dip[94] ^ dip[102]^ dip[110] ^ dip[118] ^ dip[126];
	dip_hash[5] = dip[5] ^ dip[13] ^ dip[21] ^ dip[29] ^ dip[37] ^ dip[45] ^ dip[53] ^ dip[61] ^ dip[69] ^ dip[77] ^ dip[85] ^ dip[93] ^ dip[101]^ dip[109] ^ dip[117] ^ dip[125];
	dip_hash[4] = dip[4] ^ dip[12] ^ dip[20] ^ dip[28] ^ dip[36] ^ dip[44] ^ dip[52] ^ dip[60] ^ dip[68] ^ dip[76] ^ dip[84] ^ dip[92] ^ dip[100]^ dip[108] ^ dip[116] ^ dip[124];
	dip_hash[3] = dip[3] ^ dip[11] ^ dip[19] ^ dip[27] ^ dip[35] ^ dip[43] ^ dip[51] ^ dip[59] ^ dip[67] ^ dip[75] ^ dip[83] ^ dip[91] ^ dip[99] ^ dip[107] ^ dip[115] ^ dip[123];
	dip_hash[2] = dip[2] ^ dip[10] ^ dip[18] ^ dip[26] ^ dip[34] ^ dip[42] ^ dip[50] ^ dip[58] ^ dip[66] ^ dip[74] ^ dip[82] ^ dip[90] ^ dip[98] ^ dip[106] ^ dip[114] ^ dip[122];
	dip_hash[1] = dip[1] ^ dip[9]  ^ dip[17] ^ dip[25] ^ dip[33] ^ dip[41] ^ dip[49] ^ dip[57] ^ dip[65] ^ dip[73] ^ dip[81] ^ dip[89] ^ dip[97] ^ dip[105] ^ dip[113] ^ dip[121];
	dip_hash[0] = dip[0] ^ dip[8]  ^ dip[16] ^ dip[24] ^ dip[32] ^ dip[40] ^ dip[48] ^ dip[56] ^ dip[64] ^ dip[72] ^ dip[80] ^ dip[88] ^ dip[96] ^ dip[104] ^ dip[112] ^ dip[120];

	for(i=0; i<8; i++) {
		sip_hash[i]=sip_hash[i] & (0x01);
		dip_hash[i]=dip_hash[i] & (0x01);
	}

	hash_idx_sip=0;
	for(i=0; i<8; i++) {
		hash_idx_sip=hash_idx_sip+(sip_hash[i]<<i);
	}
	hash_idx_dip=0;
	for(i=0; i<8; i++) {
		hash_idx_dip=hash_idx_dip+(dip_hash[i]<<i);
	}

 	idx=0;
    idx = hash_idx_dip ^ hash_idx_sip;
    
	return idx;
}


int32 rtl8198C_setAsicIpv6MulticastTable(uint32 index,rtl8198C_tblAsicDrv_IPv6multiCastParam_t *v6mcastp)
{
	
	rtl8198C_tblAsic_IPv6ipMulticastTable_t   entry;
	if((index >= RTL8198C_IPv6MCASTTBL_SIZE) || (v6mcastp == NULL))
		return FAILED;

	memset(&entry,0,sizeof(entry));
	entry.srcIPAddr0 = v6mcastp->srcIPAddr.s6_addr32[3];
	entry.srcIPAddr1 = v6mcastp->srcIPAddr.s6_addr32[2];
	entry.srcIPAddr2 = v6mcastp->srcIPAddr.s6_addr32[1];
	entry.srcIPAddr3 = v6mcastp->srcIPAddr.s6_addr32[0];

	entry.destIPAddr0 = v6mcastp->destIPAddr.s6_addr32[3];
	entry.destIPAddr1 = v6mcastp->destIPAddr.s6_addr32[2];
	entry.destIPAddr2 = v6mcastp->destIPAddr.s6_addr32[1];
	entry.destIPAddr3 = (v6mcastp->destIPAddr.s6_addr32[0] & 0xfffffff);

	entry.srcPort = v6mcastp->srcPort;
	entry.portMember = v6mcastp->portMember;
	entry.extPortMember = v6mcastp->extPortMember;
	entry.v6rdEngress = v6mcastp->v6rdEngress;
	entry.v6rdTbIdx = v6mcastp->v6rdTbIdx;
	entry.valid=1;
	entry.toCPU = v6mcastp->toCPU;
	entry.ageTime = v6mcastp->ageTime;
	if(IS_AFTER_RL6405)
	{
		entry.difidx = v6mcastp->difidx;
	}

	return _rtl8651_forceAddAsicEntry(TYPE_IPv6_MULTICAST_TABLE, index, &entry);

}
int32 rtl8198C_getAsicIpv6MulticastTable(uint32 index, rtl8198C_tblAsicDrv_IPv6multiCastParam_t *v6mcastp)
{

	rtl8198C_tblAsic_IPv6ipMulticastTable_t	entry;
	if((index >= RTL8198C_IPv6MCASTTBL_SIZE) || (v6mcastp == NULL))
		return FAILED;

	_rtl8651_readAsicEntry(TYPE_IPv6_MULTICAST_TABLE, index, &entry);
	if(entry.valid == 0)
		return FAILED;
	
	v6mcastp->srcIPAddr.s6_addr32[3]= entry.srcIPAddr0 ;
	v6mcastp->srcIPAddr.s6_addr32[2]= entry.srcIPAddr1 ;
	v6mcastp->srcIPAddr.s6_addr32[1]= entry.srcIPAddr2 ;
	v6mcastp->srcIPAddr.s6_addr32[0]= entry.srcIPAddr3 ; 

	v6mcastp->destIPAddr.s6_addr32[3]=entry.destIPAddr0 ;
	v6mcastp->destIPAddr.s6_addr32[2]=entry.destIPAddr1 ; 
	v6mcastp->destIPAddr.s6_addr32[1]=entry.destIPAddr2 ; 
	v6mcastp->destIPAddr.s6_addr32[0]=entry.destIPAddr3 ;
	v6mcastp->destIPAddr.s6_addr32[0] |= 0xf0000000;  // [1111,entry.destIPAddr3]
/*spec error !?*/
//v6mcastp->destIPAddr.s6_addr32[0] |= 0xe0000000;  // [1110,entry.destIPAddr3]

	v6mcastp->srcPort=entry.srcPort ;
	v6mcastp->portMember=entry.portMember ; 
	v6mcastp->extPortMember=entry.extPortMember ;
	v6mcastp->v6rdEngress=entry.v6rdEngress ;
	v6mcastp->v6rdTbIdx=entry.v6rdTbIdx ; 
	v6mcastp->valid=1;
	v6mcastp->toCPU=entry.toCPU ;
	v6mcastp->ageTime=entry.ageTime ;
	if(IS_AFTER_RL6405)
	{
		v6mcastp->difidx = entry.difidx;
	}
	return SUCCESS;

}

int32 rtl8198C_delAsicIpv6MulticastTable(uint32 index) {
	rtl8198C_tblAsic_IPv6ipMulticastTable_t entry;
	memset(&entry,0,sizeof(entry));
	return _rtl8651_forceAddAsicEntry(TYPE_IPv6_MULTICAST_TABLE, index, &entry);
}

int32 rtl8198C_delAsicIpMulticastTable(uint32 index)
{
	rtl8198C_tblAsic_IPv6ipMulticastTable_t   entry;
	if((index >= RTL8198C_IPv6MCASTTBL_SIZE))
		return FAILED;
	memset(&entry,0,sizeof(entry));
	return _rtl8651_forceAddAsicEntry(TYPE_IPv6_MULTICAST_TABLE, index, &entry);
}

/*=========================================
  * ASIC DRIVER API: IPv6 ARP Table
  *=========================================*/

int32 rtl8198C_Arp6HashFunction(rtl8198C_tblAsicDrv_v6NeighParam_t *arpp)
{
	uint32 idx;
	uint8 hID[64];
	uint8 subIdx[3];
	uint8 hash[8];
	uint32 i;

	for(i=0; i<8; i++) {
		hash[i]=0;
	}

	for(i=0; i<64; i++)	{
		if(((arpp->hostID)>>i)&1) {
			hID[i]=1;
		}
		else 	{
			hID[i]=0;
		}
		
	}
	
	for(i=0; i<3; i++)	{
		if(((arpp->subnetIdx) & (1<<i))!=0) {
			subIdx[i]=1;
		}
		else {
			subIdx[i]=0;
		}
	}

/*
ARPV6 4-way hash algorithm:
ID[66:0]={SUBNET_IDX, HOSTID}
ID[66]=subIdx[2] ID[65]=subIdx[1] ID[64]=subIdx[0] 

ARPV6_IDX[0] = ID[0]   ID[6]   ID[12]   ID[18]    D[24]    D[30]  ?n  D[36]  
               ID[42]   ID[48]   ID[54]   ID[60]    D[66]
               
ARPV6_IDX[1] = ID[1]   ID[7]   ID[13]   ID[19]    D[25]    D[31]  ?n  D[37]  
               ID[43]   ID[49]   ID[55]   ID[61]
               
ARPV6_IDX[2] = ID[2]   ID[8]   ID[14]   ID[20]    D[26]    D[32]  ?n  D[38]  
               ID[44]   ID[50]   ID[56]   ID[62]
               
ARPV6_IDX[3] = ID[3]   ID[9]   ID[15]   ID[21]    D[27]    D[33]  ?n  D[39]  
               ID[45]   ID[51]   ID[57]   ID[63]
               
ARPV6_IDX[4] = ID[4]   ID[10]   ID[16]   ID[22]    D[28]   D[34]  ?n  D[40]   
                              ID[46]   ID[52]   ID[58]   ID[64]
                              
ARPV6_IDX[5] = ID[5]   ID[11]   ID[17]   ID[23]    D[29]   D[35]  ?n  D[41]   
                              ID[47]   ID[53]   ID[59]   ID[65]

*/
	hash[0] = 	hID[0] ^ hID[6] ^ hID[12] ^ hID[18] ^ hID[24] ^ hID[30] ^ \
				hID[36] ^ hID[42] ^hID[48] ^ hID[54] ^ hID[60] ^ subIdx[2];
	
	hash[1] = 	hID[1] ^ hID[7] ^ hID[13] ^ hID[19] ^ hID[25] ^ hID[31] ^ \
				hID[37] ^ hID[43] ^hID[49] ^ hID[55] ^ hID[61];
	
	hash[2] = 	hID[2] ^ hID[8] ^ hID[14] ^ hID[20] ^ hID[26] ^ hID[32] ^ \
				hID[38] ^ hID[44] ^hID[50] ^ hID[56] ^ hID[62];
	
	hash[3] = 	hID[3] ^ hID[9] ^ hID[15] ^ hID[21] ^ hID[27] ^ hID[33] ^ \
				hID[39] ^ hID[45] ^hID[51] ^ hID[57] ^ hID[63];
	
	hash[4] = 	hID[4] ^ hID[10] ^ hID[16] ^ hID[22] ^ hID[28] ^ hID[34] ^ \
				hID[40] ^ hID[46] ^hID[52] ^ hID[58] ^ subIdx[0];
	
	hash[5] = 	hID[5] ^ hID[11] ^ hID[17] ^ hID[23] ^ hID[29] ^ hID[35] ^ \
				hID[41] ^ hID[47] ^hID[53] ^ hID[59] ^ subIdx[1];

	for(i=0; i<8; i++) {
		hash[i]=hash[i] & (0x01);
	}

	idx=0;
	for(i=0; i<8; i++) {
		idx=idx+(hash[i]<<i);
	}
	
	return idx;

}


int32 rtl8198C_setAsicV6MulticastEnable(uint32 enable)
{

	if (enable == TRUE)
	{
		//0xBB804428, Frame Forwarding Configuration Register
		WRITE_MEM32(V6CTR0, READ_MEM32(V6CTR0)|CF_IPMSTCTL_V6_EN);
	} else
	{
		WRITE_MEM32(V6CTR0, READ_MEM32(V6CTR0) & ~CF_IPMSTCTL_V6_EN);
	}

	return SUCCESS;
}

int rtl8198C_getAsicV6MulticastAge(struct in6_addr *sip,struct in6_addr *gip)
{
	rtl8198C_tblAsicDrv_IPv6multiCastParam_t v6mCastEntry;
	uint32 i;
	int age=0;

	for(i=0; i<RTL8198C_v6MULTICASTTBL_SIZE; i++) 
	{
		rtl8198C_getAsicIpv6MulticastTable(i,&v6mCastEntry);
		if ( v6mCastEntry.valid && ipv6_addr_equal(&v6mCastEntry.destIPAddr,gip) && ipv6_addr_equal(&v6mCastEntry.srcIPAddr,sip))
		{
			age = v6mCastEntry.ageTime;
			break;
		}
	}
	return age;
}

int32 rtl8198C_getAsicV6MulticastEnable(uint32 *enable)
{
	if (enable == NULL)
	{
		return FAILED;
	}

	*enable = (READ_MEM32(V6CTR0) & CF_IPMSTCTL_V6_EN) ? TRUE : FALSE;

	return SUCCESS;
}


#endif  //END defined(CONFIG_RTL_8685S_HWNAT)
int32 rtl8651_setAsicRouting(uint32 index, rtl865x_tblAsicDrv_routingParam_t *routingp) 
{
	uint32 i, asicMask;
	rtl865xc_tblAsic_l3RouteTable_t entry;
#ifdef FPGA
	if (index==2) index=6;
	if (index==3) index=7;
	if (index>=4 && index <=5) 
		rtlglue_printf("Out of range\n");
#endif	
	if((index >= RTL8651_ROUTINGTBL_SIZE) || (routingp == NULL))
		return FAILED;

	if (routingp->ipMask) {
		for(i=0; i<32; i++)
			if(routingp->ipMask & (1<<i)) break;
		asicMask = 31 - i;
	} else asicMask = 0;
    
	memset(&entry,0,sizeof(entry));
	entry.IPAddr = routingp->ipAddr;
#if defined(CONFIG_RTL_8685S_HWNAT)
	entry.linkTo.ARPEntry.DSLiteEgress = routingp->DSLiteEgress;
	entry.linkTo.ARPEntry.DSLiteIdx1_0 = routingp->DSLiteIdx;
	entry.DSLiteIdx2_2 = (routingp->DSLiteIdx) >>2;
#endif
	switch(routingp->process) {
	case 0://PPPoE
		entry.linkTo.PPPoEEntry.PPPoEIndex = routingp->pppoeIdx;
#if defined(CONFIG_RTL_8685S_HWNAT)
		if(IS_AFTER_RL6405)
		{
			entry.linkTo.PPPoEEntry.nextHop = ((routingp->nextHopRow <<2) | routingp->nextHopColumn)&0x3ff;
			entry.linkTo.PPPoEEntry.nextHop10_10= ((routingp->nextHopRow) >>8 )&0x1;
		}
		else
#endif
		{
			entry.linkTo.PPPoEEntry.nextHop = (routingp->nextHopRow <<2) | routingp->nextHopColumn;
		}

		entry.linkTo.PPPoEEntry.IPMask = asicMask;
		entry.linkTo.PPPoEEntry.netif = routingp->vidx;
		entry.linkTo.PPPoEEntry.internal=routingp->internal;
		entry.linkTo.PPPoEEntry.isDMZ=routingp->DMZFlag;
		entry.linkTo.PPPoEEntry.process = routingp->process;
		entry.linkTo.PPPoEEntry.valid = 1;

		break;
	case 1://Direct
#if defined(CONFIG_RTL_8685S_HWNAT)
		if(IS_AFTER_RL6405)
		{
			entry.linkTo.L2Entry.nextHop = ((routingp->nextHopRow <<2) | routingp->nextHopColumn)&0x3ff;
			entry.linkTo.L2Entry.nextHop10_10= ((routingp->nextHopRow) >>8 )&0x1;
		}
		else
#endif
		{
			entry.linkTo.L2Entry.nextHop = (routingp->nextHopRow <<2) | routingp->nextHopColumn;
		}
		entry.linkTo.L2Entry.IPMask = asicMask;
		entry.linkTo.L2Entry.netif = routingp->vidx;
		entry.linkTo.L2Entry.internal=routingp->internal;
		entry.linkTo.L2Entry.isDMZ=routingp->DMZFlag;
		entry.linkTo.L2Entry.process = routingp->process;
		entry.linkTo.L2Entry.valid = 1;		
		break;
	case 2://arp
		entry.linkTo.ARPEntry.ARPEnd = routingp->arpEnd >> 3;
		entry.linkTo.ARPEntry.ARPStart = routingp->arpStart >> 3;
		entry.linkTo.ARPEntry.IPMask = asicMask;
		entry.linkTo.ARPEntry.netif = routingp->vidx;
		entry.linkTo.ARPEntry.internal=routingp->internal;
		entry.linkTo.ARPEntry.isDMZ = routingp->DMZFlag;
		entry.linkTo.ARPEntry.process = routingp->process;
		entry.linkTo.ARPEntry.valid = 1;

		entry.linkTo.ARPEntry.ARPIpIdx = routingp->arpIpIdx; /* for RTL8650B C Version Only */
		break;
	case 4://CPU
	case 6://DROP
		/*
		  *   Note:  although the process of this routing entry is CPU/DROP,
		  *             we still have to assign "vid" field for packet decision process use.
		  *                                                                                          - 2005.3.23 -
		  */
		entry.linkTo.ARPEntry.netif = routingp->vidx;
		entry.linkTo.ARPEntry.IPMask = asicMask;
		entry.linkTo.ARPEntry.process = routingp->process;
		entry.linkTo.ARPEntry.valid = 1;
		entry.linkTo.ARPEntry.internal=routingp->internal;
		break;
	case 5://NAPT NextHop
		entry.linkTo.NxtHopEntry.nhStart = routingp->nhStart >> 1;
		switch (routingp->nhNum)
		{
		    case 2: entry.linkTo.NxtHopEntry.nhNum = 0; break;
		    case 4: entry.linkTo.NxtHopEntry.nhNum = 1; break;
		    case 8: entry.linkTo.NxtHopEntry.nhNum = 2; break;
		    case 16: entry.linkTo.NxtHopEntry.nhNum = 3; break;
		    case 32: entry.linkTo.NxtHopEntry.nhNum = 4; break;
		    default: return FAILED;
		}
		entry.linkTo.NxtHopEntry.nhNxt = routingp->nhNxt;
		entry.linkTo.NxtHopEntry.nhAlgo = routingp->nhAlgo;
		entry.linkTo.NxtHopEntry.IPMask = asicMask;
		entry.linkTo.NxtHopEntry.process = routingp->process;
		entry.linkTo.NxtHopEntry.valid = 1;
		entry.linkTo.NxtHopEntry.IPDomain = routingp->ipDomain;
		entry.linkTo.NxtHopEntry.internal = routingp->internal;
		entry.linkTo.NxtHopEntry.isDMZ = routingp->DMZFlag;
		break;
		
	default: 
		return FAILED;
	}
    return _rtl8651_forceAddAsicEntry(TYPE_L3_ROUTING_TABLE, index, &entry);
}

int32 rtl8651_delAsicRouting(uint32 index) 
{
	rtl865xc_tblAsic_l3RouteTable_t entry;

	if(index >= RTL8651_ROUTINGTBL_SIZE)
		return FAILED;
	memset(&entry,0,sizeof(entry));
	entry.linkTo.ARPEntry.valid = 0;
	return _rtl8651_forceAddAsicEntry(TYPE_L3_ROUTING_TABLE, index, &entry);
}

int32 rtl8651_getAsicRouting(uint32 index, rtl865x_tblAsicDrv_routingParam_t *routingp) 
{
	uint32 i;
	rtl865xc_tblAsic_l3RouteTable_t entry;
    
	if((index >= RTL8651_ROUTINGTBL_SIZE) || (routingp == NULL))
		return FAILED;

	_rtl8651_readAsicEntry(TYPE_L3_ROUTING_TABLE, index, &entry);
	if(entry.linkTo.ARPEntry.valid == 0)
		return FAILED;

	routingp->ipAddr = entry.IPAddr;
	routingp->process = entry.linkTo.ARPEntry.process;
#if defined(CONFIG_RTL_8685S_HWNAT)
	routingp->DSLiteEgress=entry.linkTo.ARPEntry.DSLiteEgress;
	routingp->DSLiteIdx = entry.linkTo.ARPEntry.DSLiteIdx1_0 | ((entry.DSLiteIdx2_2)<<2) ;
#endif
	for(i=0, routingp->ipMask = 0; i<=entry.linkTo.ARPEntry.IPMask; i++)
		routingp->ipMask |= 1<<(31-i);
    
    	routingp->vidx = entry.linkTo.ARPEntry.netif;
	routingp->internal= entry.linkTo.PPPoEEntry.internal;
	switch(routingp->process) {
	case 0://PPPoE
		routingp->arpStart = 0;
		routingp->arpEnd = 0;
		routingp->pppoeIdx = entry.linkTo.PPPoEEntry.PPPoEIndex;
		routingp->nextHopRow = entry.linkTo.PPPoEEntry.nextHop>>2;
#if defined(CONFIG_RTL_8685S_HWNAT)
		if(IS_AFTER_RL6405)
		{
			routingp->nextHopRow |= ((entry.linkTo.PPPoEEntry.nextHop10_10&0x1)<<8);
		}
#endif
		routingp->nextHopColumn = entry.linkTo.PPPoEEntry.nextHop & 0x3;
		routingp->DMZFlag= entry.linkTo.NxtHopEntry.isDMZ;
		break;
	case 1://Direct
		routingp->arpStart = 0;
		routingp->arpEnd = 0;
		routingp->pppoeIdx = 0;
		routingp->nextHopRow = entry.linkTo.L2Entry.nextHop>>2;
		routingp->nextHopColumn = entry.linkTo.L2Entry.nextHop&0x3;
		routingp->DMZFlag= entry.linkTo.NxtHopEntry.isDMZ;
		break;
	case 2://Indirect
		routingp->arpEnd = entry.linkTo.ARPEntry.ARPEnd;
		routingp->arpStart = entry.linkTo.ARPEntry.ARPStart;
		routingp->pppoeIdx = 0;
		routingp->nextHopRow = 0;
		routingp->nextHopColumn = 0;
		routingp->arpIpIdx = entry.linkTo.ARPEntry.ARPIpIdx;
		routingp->DMZFlag= entry.linkTo.NxtHopEntry.isDMZ;
		break;
	case 4: /* CPU */
	case 6: /* Drop */
		routingp->arpStart = 0;
		routingp->arpEnd = 0;
		routingp->pppoeIdx = 0;
		routingp->nextHopRow = 0;
		routingp->nextHopColumn = 0;
		routingp->DMZFlag= entry.linkTo.NxtHopEntry.isDMZ;
		break;
#if 1 //chhuang: #ifdef CONFIG_RTL865XB
	case 5:
		routingp->nhStart = (entry.linkTo.NxtHopEntry.nhStart) << 1;
		switch (entry.linkTo.NxtHopEntry.nhNum)
		{
		case 0: routingp->nhNum = 2; break;
		case 1: routingp->nhNum = 4; break;
		case 2: routingp->nhNum = 8; break;
		case 3: routingp->nhNum = 16; break;
		case 4: routingp->nhNum = 32; break;
		default: return FAILED;
		}
		routingp->nhNxt = entry.linkTo.NxtHopEntry.nhNxt;
		routingp->nhAlgo = entry.linkTo.NxtHopEntry.nhAlgo;
		routingp->ipDomain = entry.linkTo.NxtHopEntry.IPDomain;
		routingp->internal= entry.linkTo.NxtHopEntry.internal;
		routingp->DMZFlag= entry.linkTo.NxtHopEntry.isDMZ;
		 
		break;
#endif
	default: return FAILED;
	}
    return SUCCESS;
}


int32 rtl8651_setAsicArp(uint32 index, rtl865x_tblAsicDrv_arpParam_t *arpp) 
{
	rtl865xc_tblAsic_arpTable_t   entry;
	if((index >= RTL8651_ARPTBL_SIZE) || (arpp == NULL))
		return FAILED;

	memset(&entry,0,sizeof(entry));
#if defined(CONFIG_RTL_8685S_HWNAT)
	if(IS_AFTER_RL6405)
	{
		entry.nextHop = (arpp->nextHopRow<<2) | (arpp->nextHopColumn&0x3);
		entry.nextHop10_10 = (arpp->nextHopRow >>8);
	}
	else
#endif
	{
		entry.nextHop = (arpp->nextHopRow<<2) | (arpp->nextHopColumn&0x3);
	}
	entry.valid = 1;
	entry.aging=arpp->aging;
	return _rtl8651_forceAddAsicEntry(TYPE_ARP_TABLE, index, &entry);
}

int32 rtl8651_delAsicArp(uint32 index) 
{
	rtl865xc_tblAsic_arpTable_t   entry;
	if(index >= RTL8651_ARPTBL_SIZE)
		return FAILED;

	memset(&entry,0,sizeof(entry));
	entry.valid = 0;
	return _rtl8651_forceAddAsicEntry(TYPE_ARP_TABLE, index, &entry);
}

int32 rtl8651_getAsicArp(uint32 index, rtl865x_tblAsicDrv_arpParam_t *arpp) 
{
	rtl865xc_tblAsic_arpTable_t   entry;
	if((index >= RTL8651_ARPTBL_SIZE) || (arpp == NULL))
		return FAILED;
	_rtl8651_readAsicEntry(TYPE_ARP_TABLE, index, &entry);
	/*for 8196B patch,read arp table and ip multicast table should stop TLU*/
	//_rtl8651_readAsicEntryStopTLU(TYPE_ARP_TABLE, index, &entry); //No need to Stop_Table_Lookup process 

	if(entry.valid == 0)
		return FAILED;
#if defined(CONFIG_RTL_8685S_HWNAT)
	if(IS_AFTER_RL6405)
	{
		arpp->nextHopRow = (entry.nextHop>>2) | (entry.nextHop10_10 <<8);
	}
	else
#endif
	{
		arpp->nextHopRow = entry.nextHop>>2;
	}

	arpp->nextHopColumn = entry.nextHop&0x3;
	arpp->aging=entry.aging&0x1f;
	return SUCCESS;
}

/*=========================================
  * ASIC DRIVER API: Multicast Table
  *=========================================*/

uint32 rtl8651_ipMulticastTableIndex(ipaddr_t srcAddr, ipaddr_t dstAddr)
{
	uint32 idx;
	#if defined(CONFIG_RTL_819X) || defined(CONFIG_RTL_8676HWNAT)
#if defined (CONFIG_RTL8196C_REVISION_B) || defined (CONFIG_RTL8198_REVISION_B)
	uint32 sip[32],dip[32];
	uint32 hash[8];
	uint32 i;

	for(i=0; i<8; i++) {
		hash[i]=0;
	}

	for(i=0; i<32; i++)	{
		if((srcAddr & (1<<i))!=0) {
			sip[i]=1;
		}
		else 	{
			sip[i]=0;
		}

		if((dstAddr & (1<<i))!=0) {
			dip[i]=1;
		}
		else {
			dip[i]=0;
		}			
	}

	hash[7] = sip[0] ^ sip[8]   ^ sip[16] ^ sip[24] ^ dip[7] ^ dip[15] ^ dip[23] ^ dip[31];
	hash[6] = sip[1] ^ sip[9]   ^ sip[17] ^ sip[25] ^ dip[6] ^ dip[14] ^ dip[22] ^ dip[30];
	hash[5] = sip[2] ^ sip[10] ^ sip[18] ^ sip[26] ^ dip[5] ^ dip[13] ^ dip[21] ^ dip[29];
	hash[4] = sip[3] ^ sip[11] ^ sip[19] ^ sip[27] ^ dip[4] ^ dip[12] ^ dip[20] ^ dip[28];
	hash[3] = sip[4] ^ sip[12] ^ sip[20] ^ sip[28] ^ dip[3] ^ dip[11] ^ dip[19] ^ dip[27];
	hash[2] = sip[5] ^ sip[13] ^ sip[21] ^ sip[29] ^ dip[2] ^ dip[10] ^ dip[18] ^ dip[26];
	hash[1] = sip[6] ^ sip[14] ^ sip[22] ^ sip[30] ^ dip[1] ^ dip[9]  ^ dip[17] ^ dip[25];
	hash[0] = sip[7] ^ sip[15] ^ sip[23] ^ sip[31] ^ dip[0] ^ dip[8]  ^ dip[16] ^ dip[24];

	for(i=0; i<8; i++) {
		hash[i]=hash[i] & (0x01);
	}

	idx=0;
	for(i=0; i<8; i++) {
		idx=idx+(hash[i]<<i);
	}
	
	return idx;
#else
	{
		#if 0
		uint32 sip[32],dip[32];
		uint32 hash[6];
		uint32 i;
		uint32 bitmask;

		for(i=0; i<32; i++)
		{
			bitmask=1<<i;
			if((srcAddr & bitmask)!=0)
			{
				sip[i]=1;
			}
			else
			{
				sip[i]=0;
			}

			if((dstAddr & bitmask)!=0)
			{
				dip[i]=1;
			}
			else
			{
				dip[i]=0;
			}
			
		}

		hash[0] = dip[0]^dip[6]^dip[12]^dip[18]^dip[24]^dip[26]^dip[28]^dip[30]^
	         		sip[23]^sip[5]^sip[11]^sip[17]^sip[31]^sip[25]^sip[27]^sip[29];
		hash[1] = dip[1]^dip[7]^dip[13]^dip[19]^dip[25]^dip[27]^dip[29]^dip[31]^
		         	sip[0]^sip[6]^sip[12]^sip[18]^sip[24]^sip[26]^sip[28]^sip[30];
		hash[2] = dip[2]^dip[8]^dip[14]^dip[20]^sip[1]^sip[7]^sip[13]^sip[19];
		hash[3] = dip[3]^dip[9]^dip[15]^dip[21]^sip[2]^sip[8]^sip[14]^sip[20];
		hash[4] = dip[4]^dip[10]^dip[16]^dip[22]^sip[3]^sip[9]^sip[15]^sip[21];
		hash[5] = dip[5]^dip[11]^dip[17]^dip[23]^sip[4]^sip[10]^sip[16]^sip[22];

		idx=0;
		for(i=0; i<6; i++)
		{
			hash[i]=hash[i] & (0x01);
			idx=idx+(hash[i]<<i);
		}
		#else
		uint32 hashSrcAddr;
		uint32 hashDstAddr;		
		hashDstAddr=((dstAddr ^ (dstAddr>>6) ^ (dstAddr>>12) ^ (dstAddr>>18)) ) ^ 
					(((dstAddr>>24) ^ (dstAddr>>26) ^ (dstAddr>>28) ^ (dstAddr>>30) ) & 0x03);
		
		hashSrcAddr=(	( 	(((srcAddr>>23) ^(srcAddr>>31)) & 0x01) 	^ 
							((srcAddr ^ (srcAddr >>24))<<1) 			^
							(srcAddr>>5) 	^
							(srcAddr>>11) 	^ 
							(srcAddr>>17) 	^ 
							(srcAddr>>25) 	^ 
							(srcAddr>>27) 	^ 
							(srcAddr>>29)	)	& 0x03	
					) 	|
					 (	(	(srcAddr>>1) 	^
					 		(srcAddr>>7) 	^
					 		(srcAddr>>13) 	^
					 		(srcAddr>>19)	)	<<2
					 );
					
					
		idx= (hashDstAddr ^ hashSrcAddr) & 0x3F; 
		#endif
	}
#endif
	#else
	#error "wrong compile flag, not supported IC reversion"
	{
		idx = srcAddr ^ (srcAddr>>8) ^ (srcAddr>>16) ^ (srcAddr>>24) ^ dstAddr ^ (dstAddr>>8) ^ (dstAddr>>16) ^ (dstAddr>>24);
		idx = ((idx >> 2) ^ (idx & 0x3)) & (RTL8651_IPMULTICASTTBL_SIZE-1);
	}
	#endif
	return idx;
}

int32 rtl8651_setAsicIpMulticastTable(rtl865x_tblAsicDrv_multiCastParam_t *mCast_t) {
	uint32 idx;
 	rtl865xc_tblAsic_ipMulticastTable_t entry;
	int16 age;

	if(mCast_t->dip >>28 != 0xe || mCast_t->port >= RTL8651_PORT_NUMBER+rtl8651_totalExtPortNum)
		return FAILED;//Non-IP multicast destination address
	memset(&entry,0,sizeof(entry));
	entry.srcIPAddr 		= mCast_t->sip;
	entry.destIPAddrLsbs 	= mCast_t->dip & 0xfffffff;

	idx = rtl8651_ipMulticastTableIndex(mCast_t->sip, mCast_t->dip);

#if (defined (CONFIG_RTL8196C_REVISION_B) || defined (CONFIG_RTL8198_REVISION_B)) && !defined(CONFIG_RTL_8685S_HWNAT)
	entry.srcPort 			= mCast_t->port;
	entry.portList 			= mCast_t->mbr;
#elif defined(CONFIG_RTL_8685S_HWNAT)
	entry.srcPort			= mCast_t->port;
	entry.portList			= mCast_t->mbr&0x3f;
	entry.extPortList		= (mCast_t->mbr>>6)&0x7;
	if(IS_AFTER_RL6405)
	{
		entry.difidx  			= mCast_t->difidx &0x7;
	}
#else

	if (mCast_t->port >= RTL8651_PORT_NUMBER) {
		/* extension port */
		entry.srcPortExt = 1;
		entry.srcPort 			= (mCast_t->port-RTL8651_PORT_NUMBER);

	} else {
		entry.srcPortExt = 0;
		entry.srcPort 			= mCast_t->port;

	}

	entry.extPortList 		= mCast_t->mbr >> RTL8651_PORT_NUMBER;
	entry.srcVidH 			= ((mCast_t->svid)>>4) &0xff;
	entry.srcVidL 			= (mCast_t->svid)&0xf;
	entry.portList 			= mCast_t->mbr & (RTL8651_PHYSICALPORTMASK);
#endif
	entry.toCPU 			= 0;
	entry.valid 			= 1;
	entry.extIPIndex 		= mCast_t->extIdx;
	
	entry.ageTime			= 0;
	age = (int16)mCast_t->age;
	while ( age > 0 ) {
		if ( (++entry.ageTime) == 7)
			break;
		age -= 5;
	}
	#ifdef CONFIG_RTL_HARDWARE_MULTICAST
	entry.ageTime = 7;
	#endif
	
	return _rtl8651_forceAddAsicEntry(TYPE_MULTICAST_TABLE, idx, &entry);
}

int32 rtl8651_delAsicIpMulticastTable(uint32 index) {
	rtl865xc_tblAsic_ipMulticastTable_t entry;
	memset(&entry,0,sizeof(entry));
	return _rtl8651_forceAddAsicEntry(TYPE_MULTICAST_TABLE, index, &entry);
}

int32 rtl8651_getAsicIpMulticastTable(uint32 index, rtl865x_tblAsicDrv_multiCastParam_t *mCast_t) 
{	
	rtl865xc_tblAsic_ipMulticastTable_t entry;
	
	if (mCast_t == NULL)
		return FAILED;
   	_rtl8651_readAsicEntry(TYPE_MULTICAST_TABLE, index, &entry);
	//_rtl8651_readAsicEntryStopTLU(TYPE_MULTICAST_TABLE, index, &entry); //No need to Stop_Table_Lookup process 

 	mCast_t->sip	= entry.srcIPAddr;
	if(entry.valid)
	{
 		mCast_t->dip	= entry.destIPAddrLsbs | 0xe0000000;
	}
	else
	{
		if(entry.destIPAddrLsbs==0)
		{
			mCast_t->dip	= entry.destIPAddrLsbs;
		}
		else
		{
			mCast_t->dip	= entry.destIPAddrLsbs | 0xe0000000;
		}
	}

#if (defined (CONFIG_RTL8196C_REVISION_B) || defined (CONFIG_RTL8198_REVISION_B)) && !defined(CONFIG_RTL_8685S_HWNAT)
	mCast_t->svid = 0;
	mCast_t->port = entry.srcPort;
	mCast_t->mbr = entry.portList;
#elif defined(CONFIG_RTL_8685S_HWNAT)
	mCast_t->svid = 0;
	mCast_t->port = entry.srcPort;
	mCast_t->mbr = entry.portList | (entry.extPortList<<6);
	if(IS_AFTER_RL6405)
	{
		mCast_t->difidx = entry.difidx ;
	}
	
#else
	mCast_t->svid = (entry.srcVidH<<4) | entry.srcVidL;
#if 1
//#ifdef CONFIG_RTL8650BBASIC
#if 1
	if (entry.srcPortExt) {
		mCast_t->port = entry.srcPort + RTL8651_PORT_NUMBER;
	} else {
		mCast_t->port = entry.srcPort;
	}
#else
	mCast_t->port = entry.srcPortExt<<3 | entry.srcPortH<<1 | entry.srcPortL;
#endif
	mCast_t->mbr = entry.extPortList<<RTL8651_PORT_NUMBER | entry.portList;
#else
	mCast_t->port = entry.srcPortH<<1 | entry.srcPortL;
	mCast_t->mbr = entry.portList;
#endif
#endif
	mCast_t->extIdx = entry.extIPIndex ;
	mCast_t->age	= entry.ageTime * 5;
	mCast_t->cpu = entry.toCPU;
	
	if(entry.valid == 0)
	{
		return FAILED;

	}

	return SUCCESS;
	
}


/*
@func int32		|	rtl8651_setAsicMulticastEnable 	| Enable / disable ASIC IP multicast support.
@parm uint32		|	enable	| TRUE to indicate ASIC IP multicast process is enabled; FALSE to indicate ASIC IP multicast process is disabled.
@rvalue FAILED	|	Failed
@rvalue SUCCESS	|	Success
@comm
We would use this API to enable/disable ASIC IP multicast process.
If it's disabled here, Hardware IP multicast table would be ignored.
If it's enabled here, IP multicast table is used to forwarding IP multicast packets.
 */
int32 rtl8651_setAsicMulticastEnable(uint32 enable)
{
	if (enable == TRUE)
	{
		//0xBB804428, Frame Forwarding Configuration Register
		WRITE_MEM32(FFCR, READ_MEM32(FFCR)|EN_MCAST);
	} else
	{
		WRITE_MEM32(FFCR, READ_MEM32(FFCR) & ~EN_MCAST);
		WRITE_MEM32(FFCR, READ_MEM32(FFCR) |= IPMltCstCtrl_TrapToCpu);
	}

	return SUCCESS;
}

/*
@func int32		|	rtl8651_getAsicMulticastEnable 	| Get the state about ASIC IP multicast support.
@parm uint32*		|	enable	| Pointer to store the state about ASIC IP multicast support.
@rvalue FAILED	|	Failed
@rvalue SUCCESS	|	Success
@comm
We would use this API to get the status of ASIC IP multicast process.
TRUE to indicate ASIC IP multicast process is enabled; FALSE to indicate ASIC IP multicast process is disabled.
*/
int32 rtl8651_getAsicMulticastEnable(uint32 *enable)
{
	if (enable == NULL)
	{
		return FAILED;
	}

	*enable = (READ_MEM32(FFCR) & EN_MCAST) ? TRUE : FALSE;

	return SUCCESS;
}

/*
@func int32		|	rtl8651_setAsicMulticastPortInternal 	| Configure internal/external state for each port
@parm uint32		|	port		| Port to set its state.
@parm int8		|	isInternal	| set TRUE to indicate <p port> is internal port; set FALSE to indicate <p port> is external port
@rvalue FAILED	|	Failed
@rvalue SUCCESS	|	Success
@comm
In RTL865x platform,
user would need to config the Internal/External state for each port to support HW multicast NAPT.
If packet is sent from internal port to external port, and source VLAN member port checking indicates that L34 is needed.
Source IP modification would be applied.
 */
int32 rtl8651_setAsicMulticastPortInternal(uint32 port, int8 isInternal)
{
	if (port >= RTL8651_PORT_NUMBER + rtl8651_totalExtPortNum)
	{	/* Invalid port */
		return FAILED;
	}

	/*
		RTL865XC : All multicast mode are stored in [SWTCR0 / Switch Table Control Register 0]
	*/
	if (isInternal == FALSE)
	{//external
		//0xBB804418, Switch Table Control Register 0
		WRITE_MEM32(SWTCR0, READ_MEM32(SWTCR0) | (((1 << port) & MCAST_PORT_EXT_MODE_MASK) << MCAST_PORT_EXT_MODE_OFFSET));
	} else
	{//internal
		WRITE_MEM32(SWTCR0, READ_MEM32(SWTCR0) & ~(((1 << port) & MCAST_PORT_EXT_MODE_MASK) << MCAST_PORT_EXT_MODE_OFFSET));
	}

	return SUCCESS;
}

/*
@func int32		|	rtl8651_getAsicMulticastPortInternal 	| Get internal/external state for each port
@parm uint32		|	port		| Port to set its state.
@parm int8*		|	isInternal	| Pointer to get port state of <p port>.
@rvalue FAILED	|	Failed
@rvalue SUCCESS	|	Success
@comm
To get the port internal / external state for <p port>:
TRUE to indicate <p port> is internal port; FALSE to indicate <p port> is external port
 */
int32 rtl8651_getAsicMulticastPortInternal(uint32 port, int8 *isInternal)
{
	if (isInternal == NULL)
	{
		return FAILED;
	}

	if (port >= RTL8651_PORT_NUMBER + rtl8651_totalExtPortNum)
	{	/* Invalid port */
		return FAILED;
	}

	if (READ_MEM32(SWTCR0) & (((1 << port) & MCAST_PORT_EXT_MODE_MASK) << MCAST_PORT_EXT_MODE_OFFSET))
	{
		*isInternal = TRUE;
	} else
	{
		*isInternal = FALSE;
	}

	return SUCCESS;
}

/*
@func int32		|	rtl8651_setAsicMulticastMTU 	| Set MTU for ASIC IP multicast forwarding
@parm uint32		|	mcastMTU	| MTU used by HW IP multicast forwarding.
@rvalue FAILED	|	Failed
@rvalue SUCCESS	|	Success
@comm
To set the MTU for ASIC IP multicast forwarding.
Its independent from other packet forwarding because IP multicast would include L2/L3/L4 at one time.
*/
int32 rtl8651_setAsicMulticastMTU(uint32 mcastMTU)
{
	if (mcastMTU & ~(MultiCastMTU_MASK) )
	{	/* multicast MTU overflow */
		return FAILED;
	}

	//0xBB80440C, ALE Control Register
	UPDATE_MEM32(ALECR, mcastMTU, MultiCastMTU_MASK, MultiCastMTU_OFFSET);

	return SUCCESS;
}

/*
@func int32		|	rtl8651_setAsicMulticastMTU 	| Get MTU for ASIC IP multicast forwarding
@parm uint32*	|	mcastMTU	| Pointer to get MTU used by HW IP multicast forwarding.
@rvalue FAILED	|	Failed
@rvalue SUCCESS	|	Success
@comm
To get the MTU value for ASIC IP multicast forwarding.
Its independent from other packet forwarding because IP multicast would include L2/L3/L4 at one time.
*/
int32 rtl8651_getAsicMulticastMTU(uint32 *mcastMTU)
{
	if (mcastMTU == NULL)
	{
		return FAILED;
	}

	*mcastMTU = GET_MEM32_VAL(ALECR, MultiCastMTU_MASK, MultiCastMTU_OFFSET);

	return SUCCESS;
}

int32 rtl865x_setAsicMulticastAging(uint32 enable)
{
	if (enable == TRUE)
	{
		//0xBB804400, multicast entry aging.
		WRITE_MEM32(TEACR, READ_MEM32(TEACR) & ~IPMcastAgingDisable);
		
	} else
	{
		WRITE_MEM32(TEACR, READ_MEM32(TEACR)|IPMcastAgingDisable);
	}

	return SUCCESS;
}

int32 rtl865x_initAsicL3(void)
{
	int32 index;
	rtl865x_tblAsicDrv_routingParam_t rth;
	
	/*clear asic table*/
	rtl8651_clearSpecifiedAsicTable(TYPE_EXT_INT_IP_TABLE, RTL8651_IPTABLE_SIZE);
	rtl8651_clearSpecifiedAsicTable(TYPE_ARP_TABLE, RTL8651_ARPTBL_SIZE);
	rtl8651_clearSpecifiedAsicTable(TYPE_PPPOE_TABLE, RTL8651_PPPOETBL_SIZE);
	rtl8651_clearSpecifiedAsicTable(TYPE_NEXT_HOP_TABLE, RTL8651_NEXTHOPTBL_SIZE);
	rtl8651_clearSpecifiedAsicTable(TYPE_L3_ROUTING_TABLE, RTL8651_ROUTINGTBL_SIZE);
	rtl8651_clearSpecifiedAsicTable(TYPE_MULTICAST_TABLE, RTL8651_IPMULTICASTTBL_SIZE);

	rtl8651_setAsicMulticastEnable(TRUE); /* Enable multicast table */
	//0xBB80440C, Enable PPPoE auto encap.
	WRITE_MEM32(ALECR, READ_MEM32(ALECR)|(uint32)EN_PPPOE);//enable PPPoE auto encapsulation
	rtl8651_setAsicMulticastMTU(1522);

	//0xBB80440C, Enable TTL-1
	WRITE_MEM32(TTLCR,READ_MEM32(TTLCR)|(uint32)EN_TTL1);//Don't hide this router. enable TTL-1 when routing on this gateway.

	for (index=0; index<RTL8651_PORT_NUMBER+rtl8651_totalExtPortNum; index++) 
	{		
		if( rtl8651_setAsicMulticastPortInternal(index, TRUE) )
			return FAILED;		
	}

	if(0)
	{
		memset(&rth, 0, sizeof(rtl865x_tblAsicDrv_routingParam_t));
		rth.process = 0x4; /* CPU */
		rth.ipAddr = 0;
		rth.ipMask = 0;
		rth.vidx = 0;
		rth.internal = 0;
		rtl8651_setAsicRouting(7, &rth);
	}
#if defined(CONFIG_RTL_HWNAT_IPv6_SUPPORT)
	//Enable IPv6 multicasting
	rtl8198C_setAsicV6MulticastEnable(TRUE);
	// Configure IPv6 default routing 
	{
		rtl8198C_tblAsicDrv_ipv6routingParam_t asic_t;
		memset(&asic_t, 0, sizeof(rtl8198C_tblAsicDrv_ipv6routingParam_t));
		asic_t.process = RT_CPU;
		rtl8198C_setAsicIpv6Routing(V6RT_ASIC_ENTRY_NUM-1, &asic_t);
	}
	//Disable Neigh & MC table aging
	WRITE_MEM32(TEACR, READ_MEM32(TEACR) | EnIPv6ARPAgingDisable);
	WRITE_MEM32(TEACR, READ_MEM32(TEACR) | EnIPv6MulticastAgingDisable);
#endif

	#if defined(CONFIG_RTL_HWNAT_DUAL_STACK_LITE)
	WRITE_MEM32(DSLITECTR0,CF_UNK_DSLDIP_CPU | CF_DSLITE_EN);
	#endif
	return SUCCESS;
	
}