/*
* Copyright c                  Realsil Semiconductor Corporation, 2006
* All rights reserved.
* 
* Program :  igmp snooping function
* Abstract : 
* Author :qinjunjie 
* Email:qinjunjie1980@hotmail.com
*
*/
/*	@doc Realtek_Igmp_Snooping_API

	@module rtl865x_igmpsnooping.c - Realtek Igmp Snooping API documentation	|
	This document explains the API interface of the igmp snooping  module.
	@normal Jun-Jie Qin (qjj_qin@realsil.com.cn) <date>

	Copyright <cp>2009 Realtek<tm> Semiconductor Cooperation, All Rights Reserved.

 	@head3 List of Symbols |
 	Here is a list of all functions and variables in this module.

 	@index | Realtek_Igmp_Snooping_API
*/
#ifdef __linux__
#include <linux/config.h>
#include <linux/jiffies.h>
#include <linux/timer.h>
#include <linux/proc_fs.h>
#ifdef CONFIG_PROC_FS
#include <linux/seq_file.h>
#endif
#endif

#include "rtl865x_igmpsnooping_glue.h"
#include "rtl865x_igmpsnooping.h"
#include "rtl865x_igmpsnooping_local.h"

#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
#include "../common/rtl865x_eventMgr.h"
#endif

static struct rtl_multicastModule rtl_mCastModuleArray[MAX_MCAST_MODULE_NUM];    
#if defined(__linux__) && defined(__KERNEL__)
static struct timer_list igmpSysTimer;	/*igmp timer*/
#endif
/*global system resources declaration*/
static uint32 rtl_totalMaxGroupCnt;    /*maximum total group entry count,  default is 100*/
static uint32 rtl_totalMaxSourceCnt;   /*maximum total group entry count,  default is 3000*/

void *rtl_groupMemory=NULL;
void *rtl_sourceMemory=NULL;

static struct rtl_groupEntry *rtl_groupEntryPool=NULL;
static struct rtl_sourceEntry *rtl_sourceEntryPool=NULL;

static struct rtl_mCastTimerParameters rtl_mCastTimerParas;  /*IGMP snooping parameters */

static uint32 rtl_hashTableSize=0;
static uint32 rtl_hashMask=0;

/*the system up time*/
static uint32 rtl_startTime;
static uint32 rtl_sysUpSeconds;       
     
#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
static rtl_multicastEventContext_t reportEventContext;
static rtl_multicastEventContext_t timerEventContext;
static rtl_multicastEventContext_t linkEventContext;
#endif

/*******************************internal function declaration*****************************/



/**************************
	resource managment
**************************/
static  struct rtl_groupEntry* rtl_initGroupEntryPool(uint32 poolSize);
static  struct rtl_groupEntry* rtl_allocateGroupEntry(void);
static  void rtl_freeGroupEntry(struct rtl_groupEntry* groupEntryPtr) ;


static  struct rtl_sourceEntry* rtl_initSourceEntryPool(uint32 poolSize);
static  struct rtl_sourceEntry* rtl_allocateSourceEntry(void);
static  void rtl_freeSourceEntry(struct rtl_sourceEntry* sourceEntryPtr) ;


/**********************************Structure Maintenance*************************/

static struct rtl_groupEntry* rtl_searchGroupEntry(uint32 moduleIndex, uint32 ipVersion,uint32 *multicastAddr);
static void rtl_linkGroupEntry(struct rtl_groupEntry* entryNode ,  struct rtl_groupEntry ** hashTable, uint32 hashIndex);
static void rtl_unlinkGroupEntry(struct rtl_groupEntry* entryNode,  struct rtl_groupEntry ** hashTable, uint32 hashIndex);
static void rtl_clearGroupEntry(struct rtl_groupEntry* groupEntryPtr);

static struct rtl_sourceEntry* rtl_searchSourceEntry(uint32 ipVersion, uint32 *sourceAddr, struct rtl_groupEntry *groupEntry);
static void rtl_linkSourceEntry(struct rtl_groupEntry *groupEntry,  struct rtl_sourceEntry* entryNode);
static void rtl_unlinkSourceEntry(struct rtl_groupEntry *groupEntry, struct rtl_sourceEntry* entryNode);
static void rtl_clearSourceEntry(struct rtl_sourceEntry* sourceEntryPtr);
static void rtl_deleteSourceEntry(struct rtl_groupEntry *groupEntry, struct rtl_sourceEntry* sourceEntry);
#if 0
static int32 rtl_checkPortMask(uint8 pktPortMask);
static uint32 rtl_mapPortMaskToPortNum(uint8 pormask);
#endif


//static int32 rtl_mapMCastIPToMAC(uint32 ipVersion, uint32 *ipAddr, uint8 *macAddr );
static int32 rtl_checkMCastAddrMapping(uint32 ipVersion, uint32 *ipAddr, uint8* macAddr);

#ifdef CONFIG_RTL_MLD_SNOOPING
static int32 rtl_compareIpv6Addr(uint32* ipv6Addr1, uint32* ipv6Addr2);
static uint16 rtl_ipv6L3Checksum(uint8 *pktBuf, uint32 pktLen, union pseudoHeader *ipv6PseudoHdr);
#endif
static int32 rtl_compareMacAddr(uint8* macAddr1, uint8* macAddr2);
static uint16 rtl_checksum(uint8 *packetBuf, uint32 packetLen);

static uint32 rtl_getGroupFwdPortMask(struct rtl_groupEntry * groupEntry, uint32 enableSourceList, uint32 sysTime);
static void rtl_checkSourceTimer(struct rtl_groupEntry * groupEntry , struct rtl_sourceEntry * sourceEntry);
static uint32 rtl_getSourceFwdPortMask(struct rtl_groupEntry * groupEntry,uint32 *sourceAddr, uint32 sysTime);
//static void rtl_checkGroupFilterTimer(struct rtl_groupEntry *groupEntry);             
static void rtl_checkGroupEntryTimer(struct rtl_groupEntry * groupEntry,uint32 enableSourceList, struct rtl_groupEntry ** hashTable);

static uint32  rtl_getMulticastRouterPortMask(uint32 moduleIndex, uint32 ipVersion, uint32 sysTime);


/*hash table operation*/
static int32 rtl_initHashTable(uint32 moduleIndex, uint32 hashTableSize);


/************************************Pkt Process**********************************/
/*MAC frame analyze function*/
static void  rtl_parseMacFrame(uint32 moduleIndex, uint8* MacFrame, uint32 verifyCheckSum, struct rtl_macFrameInfo* macInfo);

/*Process Query Packet*/
static void rtl_snoopQuerier(uint32 moduleIndex, uint32 ipVersion, uint32 portNum);
static uint32 rtl_processQueries(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8* pktBuf, uint32 pktLen);
/*Process Report Packet*/
static  uint32 rtl_processJoin(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf); // process join report packet 
static  uint32 rtl_processLeave(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf); //process leave/done report packet
static  int32 rtl_processIsInclude(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf); //process MODE_IS_INCLUDE report packet 
static  int32 rtl_processIsExclude(uint32 moduleIndex, uint32 ipVersion,uint32 portNum, uint8 *pktBuf); //process MODE_IS_EXCLUDE report packet
static  int32 rtl_processToInclude(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf); //process CHANGE_TO_INCLUDE_MODE report packet
static  int32 rtl_processToExclude(uint32 moduleIndex, uint32 ipVersion,uint32 portNum, uint8 *pktBuf); //process CHANGE_TO_EXCLUDE_MODE report packet
static  int32 rtl_processAllow(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf); //process ALLOW_NEW_SOURCES report packet 
static  int32 rtl_processBlock(uint32 moduleIndex, uint32 ipVersion,uint32 portNum, uint8 *pktBuf); //process BLOCK_OLD_SOURCES report packet
static  uint32 rtl_processIgmpv3Mldv2Reports(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf);

/*******************different protocol process function**********************************/
static uint32 rtl_processIgmpMld(uint32 moduleIndex, uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum);
static uint32 rtl_processDvmrp(uint32 moduleIndex, uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum);
static uint32 rtl_processMospf(uint32 moduleIndex, uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum);
static uint32 rtl_processPim(uint32 moduleIndex, uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum);

#if  defined(__linux__) && defined(__KERNEL__)
static void rtl_multicastSysTimerExpired(uint32 expireDada);
static void rtl_multicastSysTimerInit(void);
static void rtl_multicastSysTimerDestroy(void);
#endif


/************************************************
			Implementation
  ************************************************/
  
/**************************
	Initialize
**************************/
/*
@func int32		| rtl_initMulticastSnooping	|Initialize  igmp snooping system.
@parm 		struct rtl_mCastSnoopingGlobalConfig  | mCastSnoopingGlobalConfig | Global parameters to config igmp snooping system.
@rvalue SUCCESS	| 	Initialize successfully.
@rvalue FAILED	| 	Initialize failed.
*/
int32 rtl_initMulticastSnooping(struct rtl_mCastSnoopingGlobalConfig mCastSnoopingGlobalConfig)
{
	int i,j;
	uint32 maxHashTableSize=MAX_HASH_TABLE_SIZE;
	for(i=0; i<MAX_MCAST_MODULE_NUM; i++)
	{
		memset(&(rtl_mCastModuleArray[i]), 0,sizeof(struct rtl_multicastModule));	
		
		for(j=0; j<6; j++)
		{
			rtl_mCastModuleArray[i].rtl_gatewayMac[j]=0;
		}
		
		rtl_mCastModuleArray[i].rtl_gatewayIpv4Addr=0;
		rtl_mCastModuleArray[i].rtl_ipv4HashTable=NULL;	
		
		#ifdef CONFIG_RTL_MLD_SNOOPING	
		for(j=0; j<4; j++)
		{
			rtl_mCastModuleArray[i].rtl_gatewayIpv6Addr[j]=0;
		}
		rtl_mCastModuleArray[i].rtl_ipv6HashTable=NULL;
		#endif

		rtl_mCastModuleArray[i].enableSourceList=TRUE;
		rtl_mCastModuleArray[i].enableSnooping=FALSE;
		rtl_mCastModuleArray[i].enableFastLeave=TRUE;
	}


       /*set multicast snooping parameters, use default value*/
      if(mCastSnoopingGlobalConfig.groupMemberAgingTime==0)
      {
	      rtl_mCastTimerParas.groupMemberAgingTime= DEFAULT_GROUP_MEMBER_INTERVAL;
      }
      else
      {
	      rtl_mCastTimerParas.groupMemberAgingTime= mCastSnoopingGlobalConfig.groupMemberAgingTime;
      }

      if(mCastSnoopingGlobalConfig.lastMemberAgingTime==0)
      {
             rtl_mCastTimerParas.lastMemberAgingTime= 0;
      }
      else
      {
	      rtl_mCastTimerParas.lastMemberAgingTime= mCastSnoopingGlobalConfig.lastMemberAgingTime;
      }

      if(mCastSnoopingGlobalConfig.querierPresentInterval==0)
      {
	      rtl_mCastTimerParas.querierPresentInterval= DEFAULT_QUERIER_PRESENT_TIMEOUT;
      }
	else
      {
	      rtl_mCastTimerParas.querierPresentInterval=mCastSnoopingGlobalConfig.querierPresentInterval;
      }


      if(mCastSnoopingGlobalConfig.dvmrpRouterAgingTime==0)
      {
	      rtl_mCastTimerParas.dvmrpRouterAgingTime=DEFAULT_DVMRP_AGING_TIME;
      }
      else
      {
	      rtl_mCastTimerParas.dvmrpRouterAgingTime=mCastSnoopingGlobalConfig.dvmrpRouterAgingTime;
      }

      if(mCastSnoopingGlobalConfig.mospfRouterAgingTime==0)
      {
	      rtl_mCastTimerParas.mospfRouterAgingTime=DEFAULT_MOSPF_AGING_TIME;
      }
  	else
      {
	      rtl_mCastTimerParas.mospfRouterAgingTime=mCastSnoopingGlobalConfig.mospfRouterAgingTime;
      }

      if(mCastSnoopingGlobalConfig.pimRouterAgingTime==0)
      {
	      rtl_mCastTimerParas.pimRouterAgingTime=DEFAULT_PIM_AGING_TIME;
      }
      else
      {
	      rtl_mCastTimerParas.pimRouterAgingTime=mCastSnoopingGlobalConfig.pimRouterAgingTime;
      }

	 /* set hash table size and hash mask*/
       if(mCastSnoopingGlobalConfig.hashTableSize==0)
        {
	      rtl_hashTableSize=DEFAULT_HASH_TABLE_SIZE;   /*default hash table size*/
        }
        else
        {
		for(i=0;i<11;i++)
	        {
		      if(mCastSnoopingGlobalConfig.hashTableSize>=maxHashTableSize)
		      {
			      rtl_hashTableSize=maxHashTableSize;
		
			      break;
		      }
	 	      maxHashTableSize=maxHashTableSize>>1;
		
	        }
        }

      rtl_hashMask=rtl_hashTableSize-1;
	  
	  
      if(mCastSnoopingGlobalConfig.maxGroupNum==0)
      {
	      rtl_totalMaxGroupCnt=DEFAULT_MAX_GROUP_COUNT;
      }	
      else
      {
		rtl_totalMaxGroupCnt=mCastSnoopingGlobalConfig.maxGroupNum;
      }

	/*initialize group entry pool*/
      rtl_groupMemory=NULL;
      rtl_sourceMemory=NULL;

      rtl_groupEntryPool=rtl_initGroupEntryPool(rtl_totalMaxGroupCnt); 
      if(rtl_groupEntryPool==NULL)
      {
	      return FAILED;
      }
	  

	if(mCastSnoopingGlobalConfig.maxSourceNum==0)
      {
	      rtl_totalMaxSourceCnt=DEFAULT_MAX_SOURCE_COUNT;
      }	
      else
      {
            	rtl_totalMaxSourceCnt=mCastSnoopingGlobalConfig.maxSourceNum;
      }
	  
	rtl_sourceEntryPool=rtl_initSourceEntryPool(rtl_totalMaxSourceCnt); 
      if(rtl_sourceEntryPool==NULL)
      {
	      rtl_totalMaxSourceCnt=0;
	      return FAILED;
      }
	  
#if defined(__linux__) && defined(__KERNEL__)
	rtl_multicastSysTimerInit();
#endif

	return SUCCESS;

}

/*
@func int32	| rtl_exitMulticastSnooping	| Exit igmp snooping system.
@rvalue SUCCESS	| 	Exit successfully.
@rvalue FAILED	| 	Exit failed.
*/
int32 rtl_exitMulticastSnooping(void)
{

	uint32 moduleIndex;
	for(moduleIndex=0; moduleIndex<MAX_MCAST_MODULE_NUM; moduleIndex++)
	{
		rtl_unregisterIgmpSnoopingModule(moduleIndex);
	}

	rtl_hashTableSize=0;
	rtl_hashMask=0;
	memset(&rtl_mCastTimerParas,0,sizeof(struct rtl_mCastTimerParameters));
	
	if(rtl_groupMemory!=NULL)
	{
		rtl_glueFree(rtl_groupMemory);	
	}
	
	rtl_totalMaxGroupCnt=0;
	rtl_groupMemory=NULL;
	rtl_groupEntryPool=NULL;
	
	
	if(rtl_sourceMemory!=NULL)
	{
		rtl_glueFree(rtl_sourceMemory);
	}	  

	rtl_totalMaxSourceCnt=0;
	rtl_sourceMemory=NULL;
	rtl_sourceEntryPool=NULL;

#if defined(__linux__) && defined(__KERNEL__)
	rtl_multicastSysTimerDestroy();
#endif

	 return SUCCESS;
	
}

static  struct rtl_groupEntry* rtl_initGroupEntryPool(uint32 poolSize)
{
	
	uint32 idx=0;
	struct rtl_groupEntry *poolHead=NULL;
	struct rtl_groupEntry *entryPtr=NULL;
	rtl_glueMutexLock();	/* Lock resource */
	if (poolSize == 0)
	{
		goto out;
	}

	/* Allocate memory */
	poolHead = (struct rtl_groupEntry *)rtl_glueMalloc(sizeof(struct rtl_groupEntry) * poolSize);
	rtl_groupMemory=(void *)poolHead;
	
	if (poolHead != NULL)
	{
		memset(poolHead, 0,  (poolSize  * sizeof(struct rtl_groupEntry)));
		entryPtr = poolHead;

		/* link the whole group entry pool */
		for (idx = 0 ; idx < poolSize ; idx++, entryPtr++)
		{	
			if(idx==0)
			{
				entryPtr->previous=NULL;
				if(idx == (poolSize - 1))
				{
					entryPtr->next=NULL;
				}
				else
				{
					entryPtr->next = entryPtr + 1;
				}
			}
			else
			{
				entryPtr->previous=entryPtr-1;
				if (idx == (poolSize - 1))
				{
					entryPtr->next = NULL;
				}
				else
				{
					entryPtr->next = entryPtr + 1;
				}
			}
		}
	}
	
out:

	rtl_glueMutexUnlock();	/* UnLock resource */
	return poolHead;
	
}



/**************************
	Resource Managment
**************************/

// allocate a group entry pool from the group entry pool
static  struct rtl_groupEntry* rtl_allocateGroupEntry(void)
{
	struct rtl_groupEntry *ret = NULL;

	rtl_glueMutexLock();	
		if (rtl_groupEntryPool!=NULL)
		{
			ret = rtl_groupEntryPool;
			if(rtl_groupEntryPool->next!=NULL)
			{
				rtl_groupEntryPool->next->previous=NULL;
			}
			rtl_groupEntryPool = rtl_groupEntryPool->next;
			memset(ret, 0, sizeof(struct rtl_groupEntry));
		}
		
	rtl_glueMutexUnlock();	
	
	return ret;
}

// free a group entry and link it back to the group entry pool, default is link to the pool head
static  void rtl_freeGroupEntry(struct rtl_groupEntry* groupEntryPtr) 
{
	if (!groupEntryPtr)
	{
		return;
	}
		
	rtl_glueMutexLock();	
		groupEntryPtr->next = rtl_groupEntryPool;
		if(rtl_groupEntryPool!=NULL)
		{
			rtl_groupEntryPool->previous=groupEntryPtr;
		}
		rtl_groupEntryPool=groupEntryPtr;	
	rtl_glueMutexUnlock();	
}



static  struct rtl_sourceEntry* rtl_initSourceEntryPool(uint32 poolSize)
{

	uint32 idx=0;
	struct rtl_sourceEntry *poolHead=NULL;
	struct rtl_sourceEntry *entryPtr=NULL;
	rtl_glueMutexLock();	/* Lock resource */
	if (poolSize == 0)
	{
		goto out;
	}

	/* Allocate memory */
	poolHead = (struct rtl_sourceEntry *)rtl_glueMalloc(sizeof(struct rtl_sourceEntry) * rtl_totalMaxSourceCnt);
	rtl_sourceMemory=(void *)poolHead;
	if (poolHead != NULL)
	{
		memset(poolHead, 0,  (poolSize  * sizeof(struct rtl_sourceEntry)));
		entryPtr = poolHead;

		/* link the whole source entry pool */
		for (idx = 0 ; idx < poolSize ; idx++, entryPtr++)
		{	
			if(idx==0)
			{
				entryPtr->previous=NULL;
				if(idx == (poolSize - 1))
				{
					entryPtr->next=NULL;
				}
				else
				{
					entryPtr->next = entryPtr + 1;
				}
			}
			else
			{
				entryPtr->previous=entryPtr-1;
				if (idx == (poolSize - 1))
				{
					entryPtr->next = NULL;
				}
				else
				{
					entryPtr->next = entryPtr + 1;
				}
			}
			
		}
	}
	
out:
	rtl_glueMutexUnlock();	/* UnLock resource */
	return poolHead;

}



/**************************
	Resource Managment
**************************/

// allocate a group entry pool from the group entry pool
static  struct rtl_sourceEntry* rtl_allocateSourceEntry(void)
{
	struct rtl_sourceEntry *ret = NULL;

	rtl_glueMutexLock();	
		if (rtl_sourceEntryPool!=NULL)
		{	
			ret = rtl_sourceEntryPool;
			if(rtl_sourceEntryPool->next!=NULL)
			{
				rtl_sourceEntryPool->next->previous=NULL;
			}
			rtl_sourceEntryPool = rtl_sourceEntryPool->next;
			memset(ret, 0, sizeof(struct rtl_sourceEntry));
		}
		
	rtl_glueMutexUnlock();	
	
	return ret;
}

// free a group entry and link it back to the group entry pool, default is link to the pool head
static  void rtl_freeSourceEntry(struct rtl_sourceEntry* sourceEntryPtr) 
{
	if (!sourceEntryPtr)
	{
		return;
	}
		
	rtl_glueMutexLock();	
		sourceEntryPtr->next = rtl_sourceEntryPool;
		if(rtl_sourceEntryPool!=NULL)
		{
			rtl_sourceEntryPool->previous=sourceEntryPtr;
		}

		rtl_sourceEntryPool=sourceEntryPtr;	

	rtl_glueMutexUnlock();	
}



/*********************************************
			Group list operation
 *********************************************/

/*       find a group address in a group list    */

struct rtl_groupEntry* rtl_searchGroupEntry(uint32 moduleIndex, uint32 ipVersion,uint32 *multicastAddr)
{
	struct rtl_groupEntry* groupPtr = NULL;
	int32 hashIndex;
	if(ipVersion==IP_VERSION4)
	{
		hashIndex=rtl_hashMask&multicastAddr[0];
		groupPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[hashIndex];

	}
	
#ifdef CONFIG_RTL_MLD_SNOOPING	
	if(ipVersion==IP_VERSION6)
	{
		hashIndex=rtl_hashMask&multicastAddr[3];
		groupPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable[hashIndex];
	}
#endif	

	while (groupPtr!=NULL)
	{	

#ifdef CONFIG_RTL_MLD_SNOOPING
		if(ipVersion==IP_VERSION4)
		{
			if((multicastAddr[0]==groupPtr->groupAddr[0]) && (groupPtr->ipVersion==IP_VERSION4))
			{
				return groupPtr;
			}
		}
		
		if(ipVersion==IP_VERSION6)
		{
			if(	(multicastAddr[0]==groupPtr->groupAddr[0])&&
				(multicastAddr[1]==groupPtr->groupAddr[1])&&
				(multicastAddr[2]==groupPtr->groupAddr[2])&&
				(multicastAddr[3]==groupPtr->groupAddr[3])
			)	
			{
				return groupPtr;
				
			}
		}
#else
	if(ipVersion==IP_VERSION4)
	{
		if(multicastAddr[0]==groupPtr->groupAddr[0])
		{
			return groupPtr;
		}
	}
#endif
		groupPtr = groupPtr->next;

	}

	return NULL;
}


/* link group Entry in the front of a group list */
static void  rtl_linkGroupEntry(struct rtl_groupEntry* groupEntry ,  struct rtl_groupEntry ** hashTable, uint32 hashIndex)
{
	rtl_glueMutexLock();//Lock resource
	if(NULL==groupEntry)
	{
		return;
	}
	else
	{
		if(hashTable[hashIndex]!=NULL)
		{
			hashTable[hashIndex]->previous=groupEntry;
		}
		groupEntry->next = hashTable[hashIndex];
		hashTable[hashIndex]=groupEntry;
		hashTable[hashIndex]->previous=NULL;
		
	}
	rtl_glueMutexUnlock();//UnLock resource

}


/* unlink a group entry from group list */
static void rtl_unlinkGroupEntry(struct rtl_groupEntry* groupEntry,  struct rtl_groupEntry ** hashTable, uint32 hashIndex)
{	
	if(NULL==groupEntry)
	{
		return;
	}
	else
	{
		rtl_glueMutexLock();  /* lock resource*/	
		/* unlink entry node*/
		if(groupEntry==hashTable[hashIndex]) /*unlink group list head*/
		{
			hashTable[hashIndex]=groupEntry->next;
			if(hashTable[hashIndex]!=NULL)
			{
				hashTable[hashIndex]->previous=NULL;
			}

			groupEntry->previous=NULL;
			groupEntry->next=NULL;
		}
		else
		{
			if(groupEntry->previous!=NULL)
			{
				groupEntry->previous->next=groupEntry->next;
			}
			 
			if(groupEntry->next!=NULL)
			{
				groupEntry->next->previous=groupEntry->previous;
			}
			groupEntry->previous=NULL;
			groupEntry->next=NULL;
		}
		
		rtl_glueMutexUnlock();//UnLock resource
	}
}


/* clear the content of group entry */
static void rtl_clearGroupEntry(struct rtl_groupEntry* groupEntry)
{
	rtl_glueMutexLock();
	if (NULL!=groupEntry)
	{
		memset(groupEntry, 0, sizeof(struct rtl_groupEntry));
	}
	rtl_glueMutexUnlock();
}

static void rtl_deleteSourceList(struct rtl_groupEntry* groupEntry)
{
	struct rtl_sourceEntry *sourceEntry=groupEntry->sourceList;
	struct rtl_sourceEntry *nextSourceEntry=NULL;
	while(sourceEntry!=NULL)
	{
		nextSourceEntry=sourceEntry->next;
		rtl_deleteSourceEntry(groupEntry,sourceEntry);
		sourceEntry=nextSourceEntry;
	}
}

static void rtl_deleteGroupEntry( struct rtl_groupEntry* groupEntry,struct rtl_groupEntry ** hashTable)
{	
	if(groupEntry!=NULL)
	{
		
		if(groupEntry->ipVersion==IP_VERSION4)
		{	
			rtl_deleteSourceList(groupEntry);
			rtl_unlinkGroupEntry(groupEntry, hashTable,(groupEntry->groupAddr[0]&rtl_hashMask));
			rtl_clearGroupEntry(groupEntry);
			rtl_freeGroupEntry(groupEntry);
		}
		else
		{
			rtl_deleteSourceList(groupEntry);
			rtl_unlinkGroupEntry(groupEntry, hashTable,(groupEntry->groupAddr[3]&rtl_hashMask));
			rtl_clearGroupEntry(groupEntry);
			rtl_freeGroupEntry(groupEntry);
		}
	}
		
}

static struct rtl_sourceEntry* rtl_searchSourceEntry(uint32 ipVersion, uint32 *sourceAddr, struct rtl_groupEntry *groupEntry)
{
	struct rtl_sourceEntry *sourcePtr=groupEntry->sourceList;
	while(sourcePtr!=NULL)
	{
		if(ipVersion==IP_VERSION4)
		{
			if(sourceAddr[0]==sourcePtr->sourceAddr[0])
			{
				return sourcePtr;
			}
		}
		
#ifdef CONFIG_RTL_MLD_SNOOPING		
		if(ipVersion==IP_VERSION6)
		{
			if(	(sourceAddr[0]==sourcePtr->sourceAddr[0]) &&
				(sourceAddr[1]==sourcePtr->sourceAddr[1])&&
				(sourceAddr[2]==sourcePtr->sourceAddr[2])&&
				(sourceAddr[3]==sourcePtr->sourceAddr[3])
			)
			{
				return sourcePtr;
					
			}
		}
#endif
		sourcePtr=sourcePtr->next;
	}

	return NULL;
}

static int32 rtl_searchSourceAddr(uint32 ipVersion, uint32 *sourceAddr, uint32 *sourceArray, uint32 elementCount)
{
	uint32 i=0;
	uint32 *srcPtr=sourceArray;
	
	for(i=0; i<elementCount; i++)
	{
		if(ipVersion==IP_VERSION4)
		{
			if(sourceAddr[0]==srcPtr[0])
			{
				return TRUE;
			}
			srcPtr++;
		}
#ifdef CONFIG_RTL_MLD_SNOOPING
		if(ipVersion==IP_VERSION6)
		{
			if(	(sourceAddr[0]==srcPtr[0])&&\
				(sourceAddr[1]==srcPtr[1])&&\
				(sourceAddr[2]==srcPtr[2])&&\
				(sourceAddr[3]==srcPtr[3]))
			{
			
				return TRUE;
			}
			
			srcPtr=srcPtr+4;
		}
#endif
	}
	
	return FALSE;
}

static void rtl_linkSourceEntry(struct rtl_groupEntry *groupEntry,  struct rtl_sourceEntry* entryNode)
{
	if((NULL==entryNode) || (NULL==groupEntry))
	{
		return;
	}
	else
	{
		rtl_glueMutexLock();  /* lock resource*/	

		if(groupEntry->sourceList!=NULL)
		{
			groupEntry->sourceList->previous=entryNode;
		}
		entryNode->next=groupEntry->sourceList;
		groupEntry->sourceList=entryNode;
		groupEntry->sourceList->previous=NULL;
		
		rtl_glueMutexUnlock();  /* lock resource*/	
	}
}

static void rtl_unlinkSourceEntry(struct rtl_groupEntry *groupEntry, struct rtl_sourceEntry* sourceEntry)
{

	if((NULL==sourceEntry) ||(NULL==groupEntry))
	{
		return;
	}
	else
	{
		rtl_glueMutexLock();  /* lock resource*/	
		/* unlink entry node*/ 
		if(sourceEntry==groupEntry->sourceList) /*unlink group list head*/
		{
	
			groupEntry->sourceList=sourceEntry->next;
			if(groupEntry->sourceList!=NULL)
			{
				groupEntry->sourceList ->previous=NULL;
			}
			
			sourceEntry->previous=NULL;
			sourceEntry->next=NULL;
		}
		else
		{	
			if(sourceEntry->previous!=NULL)
			{
				sourceEntry->previous->next=sourceEntry->next;
			}

			if(sourceEntry->next!=NULL)
			{
				sourceEntry->next->previous=sourceEntry->previous;
			}
			sourceEntry->previous=NULL;
			sourceEntry->next=NULL;
		}
		
		rtl_glueMutexUnlock();//UnLock resource
	}
	

}

static void rtl_clearSourceEntry(struct rtl_sourceEntry* sourceEntryPtr)
{
	rtl_glueMutexLock();
	if (NULL!=sourceEntryPtr)
	{
		memset(sourceEntryPtr, 0, sizeof(struct rtl_sourceEntry));
	}
	rtl_glueMutexUnlock();
}


static void rtl_deleteSourceEntry(struct rtl_groupEntry *groupEntry, struct rtl_sourceEntry* sourceEntry)
{
	if(sourceEntry!=NULL)
	{
		rtl_unlinkSourceEntry(groupEntry,sourceEntry);
		rtl_clearSourceEntry(sourceEntry);
		rtl_freeSourceEntry(sourceEntry);
	}
}
	
#if 0	
static int32 rtl_checkPortMask(uint8 pktPortMask)
{
	int32 i=0;
	uint8 portMaskn=PORT0_MASK;
	uint8 count=0;
	for(i=0; i<MAX_SUPPORT_PORT_NUMBER; i++)
	{
		if(portMaskn&pktPortMask)
		{
			count++;
		}
		portMaskn=portMaskn<<1;
	}
	
	if(count==1)
	{
		return SUCCESS;
	}
	else
	{
		return FAILED;
	}
}

static uint32 rtl_mapPortMaskToPortNum(uint8 portMask)
{	
	int i;
	for(i=0; i<MAX_SUPPORT_PORT_NUMBER; i++)
	{
		if((portMask&(1<<i))!=0)
		{
			return i;
		}
	}

	return 0xFFFFFFFF;
}


static int32 rtl_mapMCastIPToMAC(uint32 ipVersion, uint32 *ipAddr, uint8 *macAddr )
{
	if(ipVersion==IP_VERSION6)
	{
		if(IS_IPV6_MULTICAST_ADDRESS(ipAddr))
		{
			macAddr[0]=0x33;
			macAddr[1]=0x33;
			macAddr[2]=(ipAddr[3]&0xff000000)>>24;
			macAddr[3]=(ipAddr[3]&0x00ff0000)>>16;
			macAddr[4]=(ipAddr[3]&0x0000ff00)>>8;
			macAddr[5]= ipAddr[3]&0x000000ff;
			return SUCCESS;
		}
		else
		{
			return FAILED;
		}
	}

	if(ipVersion==IP_VERSION4)
	{
		if(IS_IPV4_MULTICAST_ADDRESS(ipAddr))
		{
			macAddr[0]=0x01;
			macAddr[1]=0x00;
			macAddr[2]=0x5e;
			macAddr[3]=(ipAddr[0]&0x007f0000)>>16;
			macAddr[4]=(ipAddr[0]&0x0000ff00)>>8;
			macAddr[5]= ipAddr[0]&0x000000ff;
			return SUCCESS;
		}
		else
		{
			return FAILED;
		}
	}

	return FAILED;
}
#endif

static int32 rtl_checkMCastAddrMapping(uint32 ipVersion, uint32 *ipAddr, uint8* macAddr)
{
	if(ipVersion==IP_VERSION4)
	{
		if(macAddr[0]!=0x01)
		{
			return FALSE;
		}

		if((macAddr[3]&0x7f)!=(uint8)((ipAddr[0]&0x007f0000)>>16))
		{
			return FALSE;
		}
		
		if(macAddr[4]!=(uint8)((ipAddr[0]&0x0000ff00)>>8))
		{
			return FALSE;
		}

		if(macAddr[5]!=(uint8)(ipAddr[0]&0x000000ff))
		{
			return FALSE;
		}

		return TRUE;
	}
#ifdef CONFIG_RTL_MLD_SNOOPING
	if(ipVersion==IP_VERSION6)
	{
		if(macAddr[0]!=0x33)
		{
			return FALSE;
		}

		if(macAddr[1]!=0x33)
		{
			return FALSE;
		}

		if(macAddr[2]!=(uint8)((ipAddr[3]&0xff000000)>>24))
		{
			return FALSE;
		}
		
		if(macAddr[3]!=(uint8)((ipAddr[3]&0x00ff0000)>>16))
		{
			return FALSE;
		}

		if(macAddr[4]!=(uint8)((ipAddr[3]&0x0000ff00)>>8))
		{
			return FALSE;
		}
		
		if(macAddr[5]!=(uint8)(ipAddr[3]&0x000000ff))
		{
			return FALSE;
		}
		
		return TRUE;
	}
#endif
	return FALSE;
}

#ifdef CONFIG_RTL_MLD_SNOOPING	
static int32 rtl_compareIpv6Addr(uint32* ipv6Addr1, uint32* ipv6Addr2)
{
	int i;
	for(i=0; i<4; i++)
	{
		if(ipv6Addr1[i]!=ipv6Addr2[i])
		{
			return FALSE;
		}
	}

	return TRUE;
}
#endif

static int32 rtl_compareMacAddr(uint8* macAddr1, uint8* macAddr2)
{
	int i;
	for(i=0; i<6; i++)
	{
		if(macAddr1[i]!=macAddr2[i])
		{
			return FALSE;
		}
	}
	return TRUE;
}

static uint16 rtl_checksum(uint8 *packetBuf, uint32 packetLen)
{
	/*note: the first bytes of  packetBuf should be two bytes aligned*/
	uint32  checksum=0;
	uint32 count=packetLen;
	uint16   *ptr= (uint16 *) (packetBuf);	
	
	 while(count>1)
	 {
		  checksum+= ntohs(*ptr);
		  ptr++;
		  count -= 2;
	 }
	 
	if(count>0)
	{
		checksum+= *(packetBuf+packetLen-1)<<8; /*the last odd byte is treated as bit 15~8 of unsigned short*/
	}

	/* Roll over carry bits */
	checksum = (checksum >> 16) + (checksum & 0xffff);
	checksum += (checksum >> 16);

	/* Return checksum */
	return ((uint16) ~ checksum);

}

#ifdef CONFIG_RTL_MLD_SNOOPING
static uint16 rtl_ipv6L3Checksum(uint8 *pktBuf, uint32 pktLen, union pseudoHeader *ipv6PseudoHdr)
{
	uint32  checksum=0;
	uint32 count=pktLen;
	uint16   *ptr;

	/*compute ipv6 pseudo-header checksum*/
	ptr= (uint16 *) (ipv6PseudoHdr);	
	for(count=0; count<20; count++) /*the pseudo header is 40 bytes long*/
	{
		  checksum+= ntohs(*ptr);
		  ptr++;
	}
	
	/*compute the checksum of mld buffer*/
	 count=pktLen;
	 ptr=(uint16 *) (pktBuf);	
	 while(count>1)
	 {
		  checksum+= ntohs(*ptr);
		  ptr++;
		  count -= 2;
	 }
	 
	if(count>0)
	{
		checksum+= *(pktBuf+pktLen-1)<<8; /*the last odd byte is treated as bit 15~8 of unsigned short*/
	}

	/* Roll over carry bits */
	checksum = (checksum >> 16) + (checksum & 0xffff);
	checksum += (checksum >> 16);

	/* Return checksum */
	return ((uint16) ~ checksum);
	
}
#endif

static uint32 rtl_getGroupFwdPortMask(struct rtl_groupEntry * groupEntry, uint32 enableSourceList, uint32 sysTime)
{
	int i;
	uint32 portMaskn=PORT0_MASK;
	uint32 fwdPortMask=0;
	struct rtl_sourceEntry * sourcePtr=NULL;;
	
	for(i=0; i<MAX_SUPPORT_PORT_NUMBER; i++)
	{
		if(enableSourceList==TRUE)
		{
			if(groupEntry->groupFilterTimer[i]>sysTime) /*exclude mode never expired*/
			{
				fwdPortMask|=portMaskn;
			}
			else/*include mode*/
			{
				sourcePtr=groupEntry->sourceList;
				while(sourcePtr!=NULL)
				{
					if(sourcePtr->portTimer[i]>sysTime)
					{
						fwdPortMask|=portMaskn;
						break;
					}
				 	else
					{
						sourcePtr=sourcePtr->next;
				 	}
				
				}
				
			}
		}
		else
		{
			if(groupEntry->groupFilterTimer[i]>sysTime) /*exclude mode never expired*/
			{
				fwdPortMask|=portMaskn;
			}
		}
		portMaskn=portMaskn<<1;
	}

	
	return fwdPortMask;
}



static void rtl_checkSourceTimer(struct rtl_groupEntry * groupEntry , struct rtl_sourceEntry * sourceEntry)
{
	int i=0;

	uint32 portMaskn=PORT0_MASK;
	uint32 deletePortMask=0;
	for(i=0; i<MAX_SUPPORT_PORT_NUMBER; i++)
	{	
	
		if(sourceEntry->portTimer[i]==0)/*means not exist*/
		{
			deletePortMask|=portMaskn;
		}
		else
		{
			if(sourceEntry->portTimer[i]<=rtl_sysUpSeconds) /*means time out*/
			{
				if(groupEntry->groupFilterTimer[i]<=rtl_sysUpSeconds) /*means include mode*/
				{
					sourceEntry->portTimer[i]=0;
					deletePortMask|=portMaskn;
				}
				
			}
		}
		
		portMaskn=portMaskn<<1;
	}

	if(deletePortMask==((1<<MAX_SUPPORT_PORT_NUMBER)-1)) /*means all port  are INCLUDE mode and expired*/
	{
		rtl_deleteSourceEntry(groupEntry,sourceEntry);
	}

	
}

static uint32 rtl_getSourceFwdPortMask(struct rtl_groupEntry * groupEntry,uint32 *sourceAddr, uint32 sysTime)
{
	int i;
	uint32 portMaskn=PORT0_MASK;
	uint32 fwdPortMask=0;
	struct rtl_sourceEntry * sourceEntry=NULL;
	if(groupEntry==NULL)
	{
		return 0xFFFFFFFF; /*broadcast*/
	}
	else
	{
		sourceEntry=rtl_searchSourceEntry((uint32)(groupEntry->ipVersion),sourceAddr, groupEntry);
		for(i=0; i<MAX_SUPPORT_PORT_NUMBER; i++)
		{
			
			if(groupEntry->groupFilterTimer[i]<=sysTime)	/*include mode*/
			{	
				if(sourceEntry!=NULL)
				{
					if( sourceEntry->portTimer[i]>sysTime)
					{
						fwdPortMask|=portMaskn;
					}
				}
			}
			else/*exclude mode*/
			{	
				if(sourceEntry==NULL)
				{
					fwdPortMask|=portMaskn;
				}
				else
				{
					if((sourceEntry->portTimer[i]>sysTime) || (sourceEntry->portTimer[i]==0))
					{
						fwdPortMask|=portMaskn;
					}
				}
			}
			
			portMaskn=portMaskn<<1;
		}
		return fwdPortMask;
		
	}
}




static void rtl_checkGroupEntryTimer(struct rtl_groupEntry * groupEntry, uint32 enableSourceList, struct rtl_groupEntry ** hashTable)
{
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	uint32 oldFwdPortMask=0;
	uint32 tmpFwdPortMask=0;
	#endif
	
	uint32 newFwdPortMask=0;
	struct rtl_sourceEntry *sourceEntry=groupEntry->sourceList;
	struct rtl_sourceEntry *nextSourceEntry=NULL;
	
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	oldFwdPortMask=rtl_getGroupFwdPortMask(groupEntry, enableSourceList, rtl_sysUpSeconds?(rtl_sysUpSeconds-1):0);
	#endif
	
	while(sourceEntry!=NULL)
	{
		nextSourceEntry=sourceEntry->next;
		#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		timerEventContext.sourceAddr[0]=sourceEntry->sourceAddr[0];
		#endif
		rtl_checkSourceTimer(groupEntry, sourceEntry);
		sourceEntry=nextSourceEntry;
	}
	
	newFwdPortMask=rtl_getGroupFwdPortMask(groupEntry, enableSourceList, rtl_sysUpSeconds);


	if(newFwdPortMask==0) /*none active port*/
	{
		rtl_deleteGroupEntry(groupEntry,hashTable);	
	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if((oldFwdPortMask!=newFwdPortMask) ||(newFwdPortMask==0))
	{	
		timerEventContext.sourceAddr[0]=0;

		tmpFwdPortMask=oldFwdPortMask&(~newFwdPortMask);
		if(tmpFwdPortMask==0)
		{
			tmpFwdPortMask=newFwdPortMask & (~oldFwdPortMask);
		}
		
#ifdef CONFIG_PROC_FS
		rtl_mCastModuleArray[timerEventContext.moduleIndex].expireEventCnt++;
#endif
		timerEventContext.portMask=(uint32)tmpFwdPortMask;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &timerEventContext);
		
	}
	#endif

}



static int32 rtl_initHashTable(uint32 moduleIndex, uint32 hashTableSize)
{
	uint32 i=0;
	
	/* Allocate memory */
	rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable=NULL;
	rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable = (struct rtl_groupEntry **)rtl_glueMalloc(4 * hashTableSize);
	if (rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable!= NULL)
	{
		for (i = 0 ; i < hashTableSize ; i++)
		{	
			rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[i]=NULL;
		}

#ifndef CONFIG_RTL_MLD_SNOOPING	
		return SUCCESS;
#endif

	}
	else
	{
		if(rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable!=NULL)
		{
			rtl_glueFree(rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable);
		}
		
#ifndef CONFIG_RTL_MLD_SNOOPING	
		return FAILED;
#endif

	}
	
#ifdef CONFIG_RTL_MLD_SNOOPING	
	rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable=NULL;
	rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable=  (struct rtl_groupEntry **)rtl_glueMalloc(4 * hashTableSize);
	if (rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable!=NULL)
	{
		for (i = 0 ; i < hashTableSize ; i++)
		{	

			rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable[i]=NULL;
		}
		return SUCCESS;
	}
	else
	{
		if(rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable!=NULL)
		{
			rtl_glueFree(rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable);
		}

		if(rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable!=NULL)
		{
			rtl_glueFree(rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable);
		}
		
		return FAILED;

	}
#endif
	

}



/**************************
	Utility
**************************/
static void  rtl_parseMacFrame(uint32 moduleIndex, uint8* macFrame, uint32 verifyCheckSum, struct rtl_macFrameInfo* macInfo) 
{
	
//MAC Frame :DA(6 bytes)+SA(6 bytes)+ CPU tag(4 bytes) + VlAN tag(Optional, 4 bytes)
//                   +Type(IPv4:0x0800, IPV6:0x86DD, PPPOE:0x8864, 2 bytes )+Data(46~1500 bytes)+CRC(4 bytes)
//CPU tag: Realtek Ethertype==0x8899(2 bytes)+protocol==0x9(4 MSB)+priority(2 bits)+reserved(4 bits)+portmask(6 LSB)
	uint8 *ptr=macFrame;

#ifdef CONFIG_RTL_MLD_SNOOPING	
	int i=0;
	uint8 nextHeader=0;
	uint16 extensionHdrLen=0;
	uint8 routerhead=FALSE;
	uint8 needchecksum=FALSE;
	
	uint8 optionDataLen=0;
	uint8 optionType=0;
	uint32 ipv6RAO=0;
#endif

	uint32 ipAddr[4]={0,0,0,0};
	union pseudoHeader pHeader;
	
	memset(macInfo,0,sizeof(struct rtl_macFrameInfo));
	memset(&pHeader, 0, sizeof(union pseudoHeader));

	ptr=ptr+12;


	/*check the presence of VLAN tag*/	
	if(*(int16 *)(ptr)==(int16)htons(VLAN_PROTOCOL_ID))
	{
		ptr=ptr+4;
	}

	/*ignore packet with PPPOE header*/	
	if(*(int16 *)(ptr)==(int16)htons(PPPOE_ETHER_TYPE))
	{
		return;	
	}

	
	/*check the presence of ipv4 type*/
	if(*(int16 *)(ptr)==(int16)htons(IPV4_ETHER_TYPE))
	{
		ptr=ptr+2;
		macInfo->ipBuf=ptr;
		macInfo->ipVersion=IP_VERSION4;
	}
	else
	{
		/*check the presence of ipv4 type*/
		if(*(int16 *)(ptr)==(int16)htons(IPV6_ETHER_TYPE))
		{
			ptr=ptr+2;
			macInfo->ipBuf=ptr;
			macInfo->ipVersion=IP_VERSION6;
		}
	}

	if((macInfo->ipVersion!=IP_VERSION4) && (macInfo->ipVersion!=IP_VERSION6))
	{
		return;
	}
	macInfo->checksumFlag=FAILED;
	
	if(macInfo->ipVersion==IP_VERSION4)
	{
		macInfo->ipHdrLen=(uint16)((((struct ipv4Pkt *)(macInfo->ipBuf))->vhl&0x0f)<<2);
		macInfo->l3PktLen=ntohs(((struct ipv4Pkt *)(macInfo->ipBuf))->length)-macInfo->ipHdrLen;
		ptr=ptr+macInfo->ipHdrLen;
		macInfo->l3PktBuf=ptr;
		macInfo->macFrameLen=(uint16)((ptr-macFrame)+macInfo->l3PktLen);
	
/*distinguish different IGMP packet:
                                                    ip_header_length      destination_ip      igmp_packet_length   igmp_type   group_address         	
IGMPv1_general_query:                            20                   224.0.0.1                       8                    0x11                 0
IGMPv2_general_query:                            24                   224.0.0.1                       8                    0x11                 0                     
IGMPv2_group_specific_query:			24                   224.0.0.1                       8                    0x11               !=0  
IGMPv3 _query:                                        24                   224.0.0.1                   >=12                  0x11        according_to_different_situation 

IGMPv1_join:                                            20          actual_multicast_address         8                    0x12           actual_multicast_address
IGMPv2_join:                                            24          actual_multicast_address         8                    0x16           actual_multicast_address
IGMPv2_leave:                                          24          actual_multicast_address         8                    0x17           actual_multicast_address
IGMPv3_report:                                         24          actual_multicast_address       >=12                0x22           actual_multicast_address*/

	/* parse IGMP type and version*/	
		if(((struct ipv4Pkt *)(macInfo->ipBuf))->protocol==IGMP_PROTOCOL)
		{	
			/*check DVMRP*/
			if((macInfo->l3PktBuf[0]==DVMRP_TYPE) && (((struct ipv4Pkt *)(macInfo->ipBuf))->destinationIp==htonl(DVMRP_ADDR)) )
			{
				macInfo->l3Protocol=DVMRP_PROTOCOL;
			}
			else
			{
				/*means unicast*/
				if((macFrame[0]&0x01)==0)
				{	
						if(rtl_compareMacAddr(macFrame, rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac)==TRUE) 
				       	{
								if(((struct ipv4Pkt *)(macInfo->ipBuf))->destinationIp==htonl(rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv4Addr))
								{
									macInfo->l3Protocol=IGMP_PROTOCOL;
               							goto otherpro;
								}
						}
			      									
				}
				else /*means multicast*/
				{	
					ipAddr[0]=ntohl(((struct ipv4Pkt *)(macInfo->ipBuf))->destinationIp);
					if(rtl_checkMCastAddrMapping(IP_VERSION4,ipAddr,macFrame)==TRUE)
					{
						macInfo->l3Protocol=IGMP_PROTOCOL;
					}
					else
					{
						return;
					}
				}
			}
			
		}

otherpro:	
			if(((struct ipv4Pkt *)(macInfo->ipBuf))->protocol==MOSPF_PROTOCOL &&\
			((((struct ipv4Pkt *)(macInfo->ipBuf))->destinationIp==htonl(IPV4_MOSPF_ADDR1)) ||\
			(((struct ipv4Pkt *)(macInfo->ipBuf))->destinationIp==htonl(IPV4_MOSPF_ADDR2))))
		{
			macInfo->l3Protocol=MOSPF_PROTOCOL;
		}

		if(((struct ipv4Pkt *)(macInfo->ipBuf))->protocol==PIM_PROTOCOL && (((struct ipv4Pkt *)(macInfo->ipBuf))->destinationIp==htonl(IPV4_PIM_ADDR)))
		{
			macInfo->l3Protocol=PIM_PROTOCOL;
		}

		if(verifyCheckSum==TRUE)
		{
			if(rtl_checksum(macInfo->l3PktBuf, macInfo->l3PktLen)!=0)
			{
				macInfo->checksumFlag=FAILED;
			}
			else
			{
				macInfo->checksumFlag=SUCCESS;
			}
		}
		else
		{
			macInfo->checksumFlag=SUCCESS;
		}
	}

#ifdef CONFIG_RTL_MLD_SNOOPING
	if(macInfo->ipVersion==IP_VERSION6)
	{
		macInfo->macFrameLen=(uint16)(ptr-macFrame+IPV6_HEADER_LENGTH+ntohs(((struct ipv6Pkt *)(macInfo->ipBuf))->payloadLenth));
		macInfo->ipHdrLen=IPV6_HEADER_LENGTH;
		
		nextHeader=((struct ipv6Pkt *)(macInfo->ipBuf))->nextHeader;
		ptr=ptr+IPV6_HEADER_LENGTH;
		while((ptr-macInfo->ipBuf)<(ntohs(((struct ipv6Pkt *)(macInfo->ipBuf))->payloadLenth)+IPV6_HEADER_LENGTH))
		{
			switch(nextHeader) 
			{
				case HOP_BY_HOP_OPTIONS_HEADER:
					/*parse hop-by-hop option*/
					nextHeader=ptr[0];
					extensionHdrLen=((uint16)(ptr[1])+1)*8;
					ptr=ptr+2;
					
					while((ptr-macInfo->ipBuf-40)<extensionHdrLen)
					{
						optionType=ptr[0];
						/*pad1 option*/
						if(optionType==0)
						{
							ptr=ptr+1;
							continue;
						}

						/*padN option*/
						if(optionType==1)
						{
							optionDataLen=ptr[1];
							ptr=ptr+optionDataLen+2;
							continue;
						}

						/*router alter option*/
						if(ntohl(*(uint32 *)(ptr))==IPV6_ROUTER_ALTER_OPTION)
						{
							ipv6RAO=IPV6_ROUTER_ALTER_OPTION;
							ptr=ptr+4;	
							continue;
						}

						/*other TLV option*/
						if((optionType!=0) && (optionType!=1))
						{
							optionDataLen=ptr[1];
							ptr=ptr+optionDataLen+2;
							continue;
						}
					

					}
					/*
					if((ptr-macInfo->ipBuf-40)!=extensionHdrLen)
					{
						rtl_gluePrintf("ipv6 packet parse error\n");
					}*/
					
				break;
				
				case ROUTING_HEADER:
					nextHeader=ptr[0];
					extensionHdrLen=((uint16)(ptr[1])+1)*8;
					
                                  
					 if (ptr[3]>0)
				   	{
                                          ptr=ptr+extensionHdrLen;
						for(i=0; i<4; i++)
						{
						      pHeader.ipv6_pHdr.destinationAddr[i]=*((uint32 *)(ptr)-4+i);

						}
					     routerhead=TRUE;
					     
					     
					}
					else
					{
                                          ptr=ptr+extensionHdrLen;
					}
					
					
					
				break;
				
				case FRAGMENT_HEADER:
					nextHeader=ptr[0];
					ptr=ptr+8;
				break;
				
				case DESTINATION_OPTION_HEADER:
					nextHeader=ptr[0];
					extensionHdrLen=((uint16)(ptr[1])+1)*8;
					ptr=ptr+extensionHdrLen;
				break;
				
				case ICMP_PROTOCOL:
					nextHeader=NO_NEXT_HEADER;
					macInfo->l3PktLen=ntohs(((struct ipv6Pkt *)(macInfo->ipBuf))->payloadLenth)-(uint16)(ptr-macInfo->ipBuf-IPV6_HEADER_LENGTH);
					macInfo->l3PktBuf=ptr;
					if((ptr[0]==MLD_QUERY) ||(ptr[0]==MLDV1_REPORT) ||(ptr[0]==MLDV1_DONE) ||(ptr[0]==MLDV2_REPORT))
					{
						/*means multicast*/
						if(	(macFrame[0]==0x33)&&\
							(macFrame[1]==0x33))
						{
							ipAddr[0]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[0]);
							ipAddr[1]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[1]);
							ipAddr[2]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[2]);
							ipAddr[3]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[3]);
							
							if(rtl_checkMCastAddrMapping(IP_VERSION6, ipAddr, macFrame)==TRUE)
							{
								macInfo->l3Protocol=ICMP_PROTOCOL;
							}
							
						}
						else /*means multicast*/
						{	
							
								ipAddr[0]=htonl(rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[0]);
								ipAddr[1]=htonl(rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[1]);
								ipAddr[2]=htonl(rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[2]);
								ipAddr[3]=htonl(rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[3]);
								if(	(rtl_compareMacAddr(macFrame, rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac)==TRUE) &&\
								(rtl_compareIpv6Addr(((struct ipv6Pkt *)macInfo->ipBuf)->destinationAddr, ipAddr) == TRUE))
								{
									macInfo->l3Protocol=ICMP_PROTOCOL;
								}		

							
						}

						needchecksum=TRUE;
						/*
						if(ipv6RAO!=IPV6_ROUTER_ALTER_OPTION)
						{
							rtl_gluePrintf("router alter option error\n");
						}*/
					}
				
					
				break;
				
				case PIM_PROTOCOL:
					nextHeader=NO_NEXT_HEADER;
					macInfo->l3PktLen=ntohs(((struct ipv6Pkt *)(macInfo->ipBuf))->payloadLenth)-(uint16)(ptr-macInfo->ipBuf-IPV6_HEADER_LENGTH);
					macInfo->l3PktBuf=ptr;
					
					ipAddr[0]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[0]);
					ipAddr[1]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[1]);
					ipAddr[2]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[2]);
					ipAddr[3]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[3]);
					if(IS_IPV6_PIM_ADDR(ipAddr))
					{
						macInfo->l3Protocol=PIM_PROTOCOL;
					}
					needchecksum=TRUE;
				
				break;
				
				case MOSPF_PROTOCOL:
					nextHeader=NO_NEXT_HEADER;
					macInfo->l3PktLen=ntohs(((struct ipv6Pkt *)(macInfo->ipBuf))->payloadLenth)-(uint16)(ptr-macInfo->ipBuf-IPV6_HEADER_LENGTH);
					macInfo->l3PktBuf=ptr;
					
					ipAddr[0]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[0]);
					ipAddr[1]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[1]);
					ipAddr[2]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[2]);
					ipAddr[3]=ntohl(((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[3]);
					
					if(IS_IPV6_MOSPF_ADDR1(ipAddr) || IS_IPV6_MOSPF_ADDR2(ipAddr))
					{
						macInfo->l3Protocol=MOSPF_PROTOCOL;
					}
					needchecksum=TRUE;
				break;
				
				default:		
					goto out;
				break;
			}
		
		}

out:
		/*compute pseudo header*/
             if(needchecksum==TRUE)
             	{
		       for(i=0; i<4; i++)
		      {
			     pHeader.ipv6_pHdr.sourceAddr[i]=((struct ipv6Pkt *)(macInfo->ipBuf))->sourceAddr[i];
			     
		       }
           
		      if(routerhead==FALSE)
		      {
		             for(i=0;i<4;i++)
		      	      {
			            pHeader.ipv6_pHdr.destinationAddr[i]=((struct ipv6Pkt *)(macInfo->ipBuf))->destinationAddr[i];
		      	      }
		       }
	      
		
		       pHeader.ipv6_pHdr.nextHeader=macInfo->l3Protocol;
		       pHeader.ipv6_pHdr.upperLayerPacketLength=htonl((uint32)(macInfo->l3PktLen));
		       pHeader.ipv6_pHdr.zeroData[0]=0;
		       pHeader.ipv6_pHdr.zeroData[1]=0;
		       pHeader.ipv6_pHdr.zeroData[2]=0;
		
		       if(macInfo->l3PktBuf!=NULL)
		       {
		       	if(verifyCheckSum==TRUE)
		       	{
				      if(rtl_ipv6L3Checksum(macInfo->l3PktBuf, macInfo->l3PktLen,&pHeader)!=0)
				      {
					      macInfo->checksumFlag=FAILED;
				       }
				       else
				       {
					      macInfo->checksumFlag=SUCCESS;
				       }
		       	}
				else
				{
					macInfo->checksumFlag=FAILED;
				}
		       }
             	}
	}
#endif
	return;
}


static uint32  rtl_getMulticastRouterPortMask(uint32 moduleIndex, uint32 ipVersion, uint32 sysTime)
{
	uint32 portIndex=0;
	uint32 portMaskn=PORT0_MASK;
	uint32 routerPortmask=0;
	
	if(ipVersion==IP_VERSION4)
	{
		for(portIndex=0; portIndex<MAX_SUPPORT_PORT_NUMBER; portIndex++)
		{
			if(rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.querier.portTimer[portIndex]>sysTime)
			{
				routerPortmask=routerPortmask|portMaskn;
			}
			
			if(rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.dvmrpRouter.portTimer[portIndex]>sysTime)
			{	
				routerPortmask=routerPortmask|portMaskn;
			}	

			
			if(rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.mospfRouter.portTimer[portIndex]>sysTime)
			{	
				routerPortmask=routerPortmask|portMaskn;
			}		


			if(rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.pimRouter.portTimer[portIndex]>sysTime)
			{	
				routerPortmask=routerPortmask|portMaskn;
			}	
		
			portMaskn=portMaskn<<1;  /*shift to next port mask*/
			
		}
	
	}

#ifdef CONFIG_RTL_MLD_SNOOPING
	if(ipVersion==IP_VERSION6)
	{
		for(portIndex=0; portIndex<MAX_SUPPORT_PORT_NUMBER; portIndex++)
		{
			if(rtl_mCastModuleArray[moduleIndex].rtl_ipv6MulticastRouters.querier.portTimer[portIndex]>sysTime)
			{	

				routerPortmask=routerPortmask|portMaskn;
			}		

			if(rtl_mCastModuleArray[moduleIndex].rtl_ipv6MulticastRouters.mospfRouter.portTimer[portIndex]>sysTime)
			{	
				routerPortmask=routerPortmask|portMaskn;
			}	
			
			if(rtl_mCastModuleArray[moduleIndex].rtl_ipv6MulticastRouters.pimRouter.portTimer[portIndex]>sysTime)
			{	
				routerPortmask=routerPortmask|portMaskn;
			}	
			
			portMaskn=portMaskn<<1;  /*shift to next port mask*/
			
		}

	}
#endif	

	routerPortmask= routerPortmask |rtl_mCastModuleArray[moduleIndex].staticRouterPortMask;

	return routerPortmask;
}

static uint32 rtl_processQueries(uint32 moduleIndex,uint32 ipVersion, uint32 portNum, uint8* pktBuf, uint32 pktLen)
{
	struct rtl_groupEntry *groupEntry=NULL;
	struct rtl_sourceEntry*sourceEntry=NULL;
	uint32 timerIndex=0;
	uint32 hashIndex=0; 
	uint32 groupAddress[4]={0,0,0,0};
	uint32 suppressFlag=0;
	uint32 *sourceAddr=NULL;
	uint32 numOfSrc=0;
	uint32 i=0;
	
	/*querier timer update and election process*/
	rtl_snoopQuerier(moduleIndex, ipVersion, portNum);

	if(ipVersion==IP_VERSION4)
	{	
		if(pktLen>=12) /*means igmpv3 query*/
		{
			groupAddress[0]=ntohl(((struct igmpv3Query*)pktBuf)->groupAddr);
			suppressFlag=((struct igmpv3Query*)pktBuf)->rsq & S_FLAG_MASK;
			sourceAddr=&(((struct igmpv3Query*)pktBuf)->srcList);
			numOfSrc=(uint32)ntohs(((struct igmpv3Query*)pktBuf)->numOfSrc);

		}
		else
		{
			groupAddress[0]=ntohl(((struct igmpv2Pkt *)pktBuf)->groupAddr);
		}
		
		if(groupAddress[0]==0) /*means general query*/
		{
			goto out;
		}
		else
		{
			hashIndex=groupAddress[0]&rtl_hashMask;
		}
		
	}
#ifdef CONFIG_RTL_MLD_SNOOPING
	if(ipVersion==IP_VERSION6)
	{
		if(pktLen>=28) /*means mldv2 query*/
		{
			groupAddress[0]=ntohl(((struct mldv2Query*)pktBuf)->mCastAddr[0]);
			groupAddress[1]=ntohl(((struct mldv2Query*)pktBuf)->mCastAddr[1]);
			groupAddress[2]=ntohl(((struct mldv2Query*)pktBuf)->mCastAddr[2]);
			groupAddress[3]=ntohl(((struct mldv2Query*)pktBuf)->mCastAddr[3]);

			suppressFlag=((struct mldv2Query*)pktBuf)->rsq & S_FLAG_MASK;
			sourceAddr=&(((struct mldv2Query*)pktBuf)->srcList);
			numOfSrc=(uint32)ntohs(((struct mldv2Query*)pktBuf)->numOfSrc);

		}
		else /*means mldv1 query*/
		{
			groupAddress[0]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[0]);
			groupAddress[1]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[1]);
			groupAddress[2]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[2]);
			groupAddress[3]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[3]);
			
		}
		
		if(	(groupAddress[0]==0) &&
			(groupAddress[1]==0) &&
			(groupAddress[2]==0) &&
			(groupAddress[3]==0) )/*means general query*/
		{
			goto out;
		}
		else
		{
			hashIndex=groupAddress[3]&rtl_hashMask;
		}
	}
#endif	
	if(suppressFlag==0)
	{
	
		groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
		if((groupEntry!=NULL))
		{	
			if(numOfSrc==0) /*means group specific query*/
			{
				for(timerIndex=0; timerIndex<MAX_SUPPORT_PORT_NUMBER; timerIndex++)
				{
					if(groupEntry->groupFilterTimer[timerIndex]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime))
					{
						groupEntry->groupFilterTimer[timerIndex]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
					}	
			
				}
			}
			else /*means group and source specific query*/
			{
				if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
				{
					for(i=0; i<numOfSrc; i++)
					{	
						
						sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr, groupEntry);
						
						if(sourceEntry!=NULL)
						{
							for(timerIndex=0; timerIndex<MAX_SUPPORT_PORT_NUMBER; timerIndex++)
							{
								if(sourceEntry->portTimer[timerIndex]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime))
								{
									sourceEntry->portTimer[timerIndex]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
								}
							}
						}

						if(ipVersion==IP_VERSION4)
						{
							sourceAddr++;
						}
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{
							sourceAddr=sourceAddr+4;
						}
#endif
					}
				}
			}
		}
		
		
	}
	
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

out:	
	return (~(1<<portNum) & ((1<<MAX_SUPPORT_PORT_NUMBER)-1));
}


static void rtl_snoopQuerier(uint32 moduleIndex, uint32 ipVersion, uint32 portNum)
{
	
	if(ipVersion==IP_VERSION4)
	{
		rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.querier.portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.querierPresentInterval;/*update timer value*/
	}
	
#ifdef CONFIG_RTL_MLD_SNOOPING	
	if(ipVersion==IP_VERSION6)
	{
		rtl_mCastModuleArray[moduleIndex].rtl_ipv6MulticastRouters.querier.portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.querierPresentInterval;/*update timer value*/
	}
#endif	
	return;
}


/*Process Report Packet*/
static  uint32 rtl_processJoin(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf)
{
	
	
	uint32 groupAddress[4]={0, 0, 0, 0};
	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_groupEntry* newGroupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;

	uint32 hashIndex=0;

	uint32 multicastRouterPortMask=rtl_getMulticastRouterPortMask(moduleIndex, ipVersion, rtl_sysUpSeconds);

	if(ipVersion==IP_VERSION4)
	{
		if(pktBuf[0]==0x12)
		{ 
			groupAddress[0]=ntohl(((struct igmpv1Pkt *)pktBuf)->groupAddr);
		}

		if(pktBuf[0]==0x16)
		{
			groupAddress[0]=ntohl(((struct igmpv2Pkt *)pktBuf)->groupAddr);
		}
		
		hashIndex=groupAddress[0]&rtl_hashMask;

	}


#ifdef CONFIG_RTL_MLD_SNOOPING
	if(ipVersion==IP_VERSION6)
	{
		
		groupAddress[0]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[3]);

		hashIndex=groupAddress[3]&rtl_hashMask;
	}
#endif	

	groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	if(groupEntry==NULL)   /*means new group address, create new group entry*/
	{
		newGroupEntry=rtl_allocateGroupEntry();
		if(newGroupEntry==NULL)
		{
			rtl_gluePrintf("run out of group entry!\n");
			goto out;
		}
		else
		{
			/*set new multicast entry*/	
			#ifdef CONFIG_RTL_MLD_SNOOPING
			newGroupEntry->groupAddr[0]=groupAddress[0];
			newGroupEntry->groupAddr[1]=groupAddress[1];
			newGroupEntry->groupAddr[2]=groupAddress[2];
			newGroupEntry->groupAddr[3]=groupAddress[3];
			#else
			newGroupEntry->groupAddr[0]=groupAddress[0];
			#endif
			
			newGroupEntry->sourceList=NULL;

			newGroupEntry->ipVersion=ipVersion;

			newGroupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
			
			/*must first link into hash table, then call the function of set rtl8306sdm, because of aggregator checking*/
			if(ipVersion==IP_VERSION4)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable, hashIndex);
			}
#ifdef CONFIG_RTL_MLD_SNOOPING
			if(ipVersion==IP_VERSION6)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable, hashIndex);
			}
#endif			

		}
		
	}
	else
	{  

		sourceEntry=groupEntry->sourceList;
		/*delete all the source list*/
		while(sourceEntry!=NULL)
		{
			sourceEntry->portTimer[portNum]=0;
			sourceEntry=sourceEntry->next;
		}
		
		groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
	
	}

#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
#endif

out:
	return (multicastRouterPortMask&(~(1<<portNum))&((1<<MAX_SUPPORT_PORT_NUMBER)-1));
}

static  uint32 rtl_processLeave(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf)
{
	uint32 groupAddress[4]={0, 0, 0, 0};
	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;

	uint32 hashIndex=0;
	uint32 multicastRouterPortMask=rtl_getMulticastRouterPortMask(moduleIndex, ipVersion, rtl_sysUpSeconds);
	
	if(ipVersion==IP_VERSION4)
	{
		groupAddress[0]=ntohl(((struct igmpv2Pkt *)pktBuf)->groupAddr);
		hashIndex=groupAddress[0]&rtl_hashMask;
	}
	
#ifdef CONFIG_RTL_MLD_SNOOPING
	if(ipVersion==IP_VERSION6)
	{
		groupAddress[0]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mldv1Pkt *)pktBuf)->mCastAddr[3]);

		hashIndex=groupAddress[3]&rtl_hashMask;
	}
#endif	

	      groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	      /*lower the timer of the group*/
	      if(groupEntry!=NULL)
	      {   

		     if(groupEntry->groupFilterTimer[portNum]>rtl_sysUpSeconds)
		     {
                  
				if(rtl_mCastModuleArray[moduleIndex].enableFastLeave==TRUE)
				{
					groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds;
				}
				else
				{
					if(groupEntry->groupFilterTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime))
					{
						groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
					}
				}
				
                   }

		     sourceEntry=groupEntry->sourceList;
		     while(sourceEntry)
		     {
				if(sourceEntry->portTimer[portNum]>rtl_sysUpSeconds)
				{
					if(rtl_mCastModuleArray[moduleIndex].enableFastLeave==TRUE)
			        	{
						sourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
				  	}
					else
					{
						if(sourceEntry->portTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime))
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
						}
					}
					
				}

				sourceEntry=sourceEntry->next;
		      }

		
	      }	
	
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif
	
	return (multicastRouterPortMask&(~(1<<portNum))&0x3f);
}

static  int32 rtl_processIsInclude(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf)
{

	uint32 j=0;
	uint32 groupAddress[4]={0, 0, 0, 0};
	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_groupEntry* newGroupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;
	struct rtl_sourceEntry *newSourceEntry=NULL;
	
	uint32 hashIndex=0;

	uint16 numOfSrc=0;
	uint32 *sourceAddr=NULL;


	if(ipVersion==IP_VERSION4)
	{
		groupAddress[0]=ntohl(((struct groupRecord *)pktBuf)->groupAddr);
		numOfSrc=ntohs(((struct groupRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct groupRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[0]&rtl_hashMask;
	}

#ifdef CONFIG_RTL_MLD_SNOOPING
	if(ipVersion==IP_VERSION6)
	{
		groupAddress[0]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[3]);
		
		numOfSrc=ntohs(((struct mCastAddrRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[3]&rtl_hashMask;
	
	}
#endif	

	groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	if(groupEntry==NULL)   /*means new group address, create new group entry*/
	{
		newGroupEntry=rtl_allocateGroupEntry();
		if(newGroupEntry==NULL)
		{
			rtl_gluePrintf("run out of group entry!\n");
			return FAILED;
		}
		else
		{	
			/*set new multicast entry*/
#ifdef CONFIG_RTL_MLD_SNOOPING
			newGroupEntry->groupAddr[0]=groupAddress[0];
			newGroupEntry->groupAddr[1]=groupAddress[1];
			newGroupEntry->groupAddr[2]=groupAddress[2];
			newGroupEntry->groupAddr[3]=groupAddress[3];
#else
			newGroupEntry->groupAddr[0]=groupAddress[0];
#endif

			newGroupEntry->ipVersion=ipVersion;

			newGroupEntry->sourceList=NULL;
			
			/*end of set group entry*/
			
			/*must first link into hash table, then call the function of set rtl8306sdm, because of aggregator checking*/
			if(ipVersion==IP_VERSION4)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable, hashIndex);
			}
			
#ifdef CONFIG_RTL_MLD_SNOOPING
			if(ipVersion==IP_VERSION6)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable, hashIndex);
			}
#endif
			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				newGroupEntry->groupFilterTimer[portNum]=0;
				/*link the new source list*/
				for(j=0; j<numOfSrc; j++)
				{

					
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
						}
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
						rtl_linkSourceEntry(newGroupEntry,newSourceEntry);
					}

					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
				}
			
			}
			else
			{	
				newGroupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
			}
		
			
		}

		
	}
	else /*means it can be found in the forward hash table*/
	{  

		if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
		{
			/*here to handle the source list*/
			for(j=0; j<numOfSrc; j++)
			{
				
				sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
				if(sourceEntry==NULL)
				{
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
						
						}
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif						
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
						rtl_linkSourceEntry(groupEntry,newSourceEntry);
					}

				}
				else
				{		
					/*just update source timer*/
					sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
				}
					
				if(ipVersion==IP_VERSION4)
				{	
					sourceAddr++;
				}
#ifdef CONFIG_RTL_MLD_SNOOPING
				if(ipVersion==IP_VERSION6)
				{
					sourceAddr=sourceAddr+4;
				}
#endif				
			}

		}
		else
		{
			groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
		}
		
	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return SUCCESS;


}

static  int32 rtl_processIsExclude(uint32 moduleIndex, uint32 ipVersion,uint32 portNum, uint8 *pktBuf)
{
	uint32 j=0;
	uint32 groupAddress[4]={0, 0, 0, 0};
	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_groupEntry* newGroupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;
	struct rtl_sourceEntry *newSourceEntry=NULL;
	
	uint32 hashIndex=0;
	uint16 numOfSrc=0;
	uint32 *sourceArray=NULL;
	uint32 *sourceAddr=NULL;

	if(ipVersion==IP_VERSION4)
	{
		groupAddress[0]=ntohl(((struct groupRecord *)pktBuf)->groupAddr);
		numOfSrc=ntohs(((struct groupRecord *)pktBuf)->numOfSrc);
		sourceArray=&(((struct groupRecord *)pktBuf)->srcList);
		sourceAddr=&(((struct groupRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[0]&rtl_hashMask;
	}
#ifdef CONFIG_RTL_MLD_SNOOPING	
	if(ipVersion==IP_VERSION6)
	{
		
		groupAddress[0]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[3]);
		
		numOfSrc=ntohs(((struct mCastAddrRecord *)pktBuf)->numOfSrc);
		sourceArray=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		sourceAddr=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[3]&rtl_hashMask;
	
	}
#endif
	groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	if(groupEntry==NULL)   /*means new group address, create new group entry*/
	{
		newGroupEntry=rtl_allocateGroupEntry();
		if(newGroupEntry==NULL)
		{
			rtl_gluePrintf("run out of group entry!\n");
			return FAILED;
		}
		else
		{	
			/*set new multicast entry*/		
#ifdef CONFIG_RTL_MLD_SNOOPING	
			newGroupEntry->groupAddr[0]=groupAddress[0];
			newGroupEntry->groupAddr[1]=groupAddress[1];
			newGroupEntry->groupAddr[2]=groupAddress[2];
			newGroupEntry->groupAddr[3]=groupAddress[3];
#else
			newGroupEntry->groupAddr[0]=groupAddress[0];
#endif


			newGroupEntry->ipVersion=ipVersion;

			newGroupEntry->sourceList=NULL;
			/*means the filter mode is exclude*/
			newGroupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;

			/*end of set group entry*/
			
			/*must first link into hash table, then call the function of set rtl8306sdm, because of aggregator checking*/
			if(ipVersion==IP_VERSION4)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable, hashIndex);
			}
#ifdef CONFIG_RTL_MLD_SNOOPING
			if(ipVersion==IP_VERSION6)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable, hashIndex);
			}
#endif
			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				/*link the new source list*/
				for(j=0; j<numOfSrc; j++)
				{
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
						}
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif						
						/*time out the sources included in the MODE_IS_EXCLUDE report*/
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
						rtl_linkSourceEntry(newGroupEntry,newSourceEntry);
					}

					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif								
				}

			}
				
		}

	}
	else /*means group address can be found in the forward hash table*/
	{  

		if(groupEntry->groupFilterTimer[portNum]<=rtl_sysUpSeconds) /*means include mode*/
		{
			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				/*here to handle the source list*/

				/*delete (A-B)*/
				sourceEntry=groupEntry->sourceList;
				while(sourceEntry)
				{
					if(rtl_searchSourceAddr(ipVersion,sourceEntry->sourceAddr,sourceArray, numOfSrc)== FALSE)
					{
						sourceEntry->portTimer[portNum]=0;
					}
					sourceEntry=sourceEntry->next;
				}

				/*(B-A) time out*/
				for(j=0; j<numOfSrc; j++)
				{
					
					sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
					
					if(sourceEntry==NULL)
					{
						newSourceEntry=rtl_allocateSourceEntry();
						if(newSourceEntry==NULL)
						{
							rtl_gluePrintf("run out of source entry!\n");
							return FAILED;
						}
						else
						{	
						
							if(ipVersion==IP_VERSION4)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
							
							}
#ifdef CONFIG_RTL_MLD_SNOOPING
							if(ipVersion==IP_VERSION6)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
								newSourceEntry->sourceAddr[1]=sourceAddr[1];
								newSourceEntry->sourceAddr[2]=sourceAddr[2];
								newSourceEntry->sourceAddr[3]=sourceAddr[3];
							}
#endif							
							newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
							rtl_linkSourceEntry(groupEntry,newSourceEntry);
						}

					}
					else
					{
						if(sourceEntry->portTimer[portNum]==0)
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
						}
					}

				
					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
				}
			}
			
		}
		else/*means exclude mode*/
		{
			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				/*here to handle the source list*/
				/*delete (X-A) and delete (Y-A)*/
				sourceEntry=groupEntry->sourceList;
				while(sourceEntry)
				{
					if(rtl_searchSourceAddr(ipVersion,sourceEntry->sourceAddr,sourceArray, numOfSrc)== FALSE)
					{
						sourceEntry->portTimer[portNum]=0;
					}
					sourceEntry=sourceEntry->next;
				}

				/*A-X-Y=GMI*/
				for(j=0; j<numOfSrc; j++)
				{
					sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
					
					if(sourceEntry==NULL)
					{
						newSourceEntry=rtl_allocateSourceEntry();
						if(newSourceEntry==NULL)
						{
							rtl_gluePrintf("run out of source entry!\n");
							return FAILED;
						}
						else
						{	
						
							if(ipVersion==IP_VERSION4)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
							
							}
#ifdef CONFIG_RTL_MLD_SNOOPING
							if(ipVersion==IP_VERSION6)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
								newSourceEntry->sourceAddr[1]=sourceAddr[1];
								newSourceEntry->sourceAddr[2]=sourceAddr[2];
								newSourceEntry->sourceAddr[3]=sourceAddr[3];
							}
#endif							
						
							newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
							rtl_linkSourceEntry(groupEntry,newSourceEntry);
						}

					}
					else
					{
						if(sourceEntry->portTimer[portNum]==0)
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
						}
					}
					
					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
				}
			}
		
			
		}

		groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return SUCCESS;

}

static int32 rtl_processToInclude(uint32 moduleIndex, uint32 ipVersion,  uint32 portNum, uint8 *pktBuf)
{
	uint32 j=0;
	uint32 groupAddress[4]={0, 0, 0, 0};
	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_groupEntry* newGroupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;
	struct rtl_sourceEntry *newSourceEntry=NULL;
	
	uint32 hashIndex=0;
	uint16 numOfSrc=0;
	uint32 *sourceAddr=NULL;

	if(ipVersion==IP_VERSION4)
	{
		groupAddress[0]=ntohl(((struct groupRecord *)pktBuf)->groupAddr);
		numOfSrc=ntohs(((struct groupRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct groupRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[0]&rtl_hashMask;
	}
	
#ifdef CONFIG_RTL_MLD_SNOOPING		
	if(ipVersion==IP_VERSION6)
	{
		
		groupAddress[0]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[3]);
		
		numOfSrc=ntohs(((struct mCastAddrRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[3]&rtl_hashMask;
	
	}
#endif

	groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	if(groupEntry==NULL)   /*means new group address, create new group entry*/
	{
		newGroupEntry=rtl_allocateGroupEntry();
		if(newGroupEntry==NULL)
		{
			rtl_gluePrintf("run out of group entry!\n");
			return FAILED;
		}
		else
		{	
			/*set new multicast entry*/
	#ifdef CONFIG_RTL_MLD_SNOOPING	
			newGroupEntry->groupAddr[0]=groupAddress[0];
			newGroupEntry->groupAddr[1]=groupAddress[1];
			newGroupEntry->groupAddr[2]=groupAddress[2];
			newGroupEntry->groupAddr[3]=groupAddress[3];
	#else
			newGroupEntry->groupAddr[0]=groupAddress[0];
	#endif


			newGroupEntry->ipVersion=ipVersion;

			newGroupEntry->sourceList=NULL;
			
			/*end of set group entry*/
			
			/*must first link into hash table, then call the function of set rtl8306sdm, because of aggregator checking*/
			if(ipVersion==IP_VERSION4)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable, hashIndex);
			}
			
#ifdef CONFIG_RTL_MLD_SNOOPING
			if(ipVersion==IP_VERSION6)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable, hashIndex);
			}
#endif			

			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				newGroupEntry->groupFilterTimer[portNum]=0;
				/*link the new source list*/
				for(j=0; j<numOfSrc; j++)
				{

					
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							
						}
						
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif						
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
						rtl_linkSourceEntry(newGroupEntry,newSourceEntry);
					}
						
					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
					
				}
			}
			else
			{	
				newGroupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
			}
			
			
		}

	}
	else /*means it can be found in the forward hash table*/
	{  

		if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
		{
			/*here to handle the source list*/
			
			for(j=0; j<numOfSrc; j++)
			{

				sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
		
				if(sourceEntry==NULL)
				{
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
						
						}
						
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif						
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
						rtl_linkSourceEntry(groupEntry,newSourceEntry);
					}

				}
				else
				{		
					/*just update source timer*/
					sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;		
				}
					
				if(ipVersion==IP_VERSION4)
				{	
					sourceAddr++;
				}
				
#ifdef CONFIG_RTL_MLD_SNOOPING
				if(ipVersion==IP_VERSION6)
				{
					sourceAddr=sourceAddr+4;
				}
#endif				
				
			}
                      
	//		if(rtl_mCastModuleArray[moduleIndex].enableFastLeave==TRUE)         /*lower the timer of (A-B) or (X-A)*/
		//	{
			    sourceEntry=groupEntry->sourceList;
				  
	                  if(ipVersion==IP_VERSION4)
	                  {
	                             sourceAddr=&(((struct groupRecord *)pktBuf)->srcList);		
	                  }
					  
#ifdef CONFIG_RTL_MLD_SNOOPING		
	                  if(ipVersion==IP_VERSION6)
	                  {			
		                      sourceAddr=&(((struct mCastAddrRecord *)pktBuf)->srcList);
	                  }
#endif	  
				while(sourceEntry)
				{				  
				         if((rtl_searchSourceAddr(ipVersion, sourceEntry->sourceAddr, sourceAddr, numOfSrc)==FALSE)&&(sourceEntry->portTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime)))
					 {      
						if(sourceEntry->portTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime))
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;						
						}

					 }
				         sourceEntry=sourceEntry->next;
				}
				
                            if(groupEntry->groupFilterTimer[portNum]>rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime)   /*lower the group timer if in exclude mode*/
                            {
					groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
			
				}

				if((numOfSrc==0)&&(rtl_mCastModuleArray[moduleIndex].enableFastLeave==TRUE))
				{
		     			if(groupEntry->groupFilterTimer[portNum]>rtl_sysUpSeconds)
		     			{
						groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds;
			 		}
						
					sourceEntry=groupEntry->sourceList;
					
					while(sourceEntry)
					{
						if(sourceEntry->portTimer[portNum]>rtl_sysUpSeconds)
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
						}
		 				sourceEntry=sourceEntry->next;
					}
					
				}
		}
		else
		{	
			if((numOfSrc==0)&&(groupEntry->groupFilterTimer[portNum]>rtl_sysUpSeconds))
			{
		     	 		if(rtl_mCastModuleArray[moduleIndex].enableFastLeave==TRUE)
		     			{
						groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds;
					}
					else
					{
						groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
					}			
			}
			else
			{
				groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
			}

		}
	

	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

return SUCCESS;
	
}

static  int32 rtl_processToExclude(uint32 moduleIndex, uint32 ipVersion,uint32 portNum , uint8 *pktBuf)
{
	uint32 j=0;
	uint32 groupAddress[4]={0, 0, 0, 0};
	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_groupEntry* newGroupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;
	struct rtl_sourceEntry *newSourceEntry=NULL;
	
	uint32 hashIndex=0;
	uint16 numOfSrc=0;
	uint32 *sourceArray=NULL;
	uint32 *sourceAddr=NULL;

	if(ipVersion==IP_VERSION4)
	{
		groupAddress[0]=ntohl(((struct groupRecord *)pktBuf)->groupAddr);
		numOfSrc=ntohs(((struct groupRecord *)pktBuf)->numOfSrc);
		sourceArray=&(((struct groupRecord *)pktBuf)->srcList);
		sourceAddr=&(((struct groupRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[0]&rtl_hashMask;
	}

#ifdef CONFIG_RTL_MLD_SNOOPING		
	if(ipVersion==IP_VERSION6)
	{
		
		groupAddress[0]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[3]);
		
		numOfSrc=ntohs(((struct mCastAddrRecord *)pktBuf)->numOfSrc);
		sourceArray=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		sourceAddr=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[3]&rtl_hashMask;
	
	}
#endif

	groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	if(groupEntry==NULL)   /*means new group address, create new group entry*/
	{
		newGroupEntry=rtl_allocateGroupEntry();
		if(newGroupEntry==NULL)
		{
			rtl_gluePrintf("run out of group entry!\n");
			return FAILED;
		}
		else
		{	
			/*set new multicast entry*/
#ifdef CONFIG_RTL_MLD_SNOOPING	
			newGroupEntry->groupAddr[0]=groupAddress[0];
			newGroupEntry->groupAddr[1]=groupAddress[1];
			newGroupEntry->groupAddr[2]=groupAddress[2];
			newGroupEntry->groupAddr[3]=groupAddress[3];
#else
			newGroupEntry->groupAddr[0]=groupAddress[0];
#endif

			newGroupEntry->ipVersion=ipVersion;

			newGroupEntry->sourceList=NULL;
			
			/*means the filter mode is exclude*/
			newGroupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
			
			/*end of set group entry*/
			
			/*must first link into hash table, then call the function of set rtl8306sdm, because of aggregator checking*/
			if(ipVersion==IP_VERSION4)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable, hashIndex);
			}
			
#ifdef CONFIG_RTL_MLD_SNOOPING
			if(ipVersion==IP_VERSION6)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable, hashIndex);
			}
#endif
			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				/*link the new source list*/
				for(j=0; j<numOfSrc; j++)
				{
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
						}
						
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif						
						/*time out the sources included  in the TO_EXCLUDE report */
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
						rtl_linkSourceEntry(newGroupEntry,newSourceEntry);
					}
						
					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}

#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
					
				}

			}
			
			
		}
		
	}
	else /*means group address can be found in the forward hash table*/
	{  
	
		if(groupEntry->groupFilterTimer[portNum]<=rtl_sysUpSeconds) /*means include mode*/
		{

			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				/*here to handle the source list*/
				/*delete (A-B)*/
				sourceEntry=groupEntry->sourceList;
				while(sourceEntry)
				{
					if(rtl_searchSourceAddr(ipVersion,sourceEntry->sourceAddr,sourceArray, numOfSrc)== FALSE)
					{
						sourceEntry->portTimer[portNum]=0;
					}
					sourceEntry=sourceEntry->next;
				}

				/*(B-A) time out*/
				for(j=0; j<numOfSrc; j++)
				{
					sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
					
					if(sourceEntry==NULL)
					{
						newSourceEntry=rtl_allocateSourceEntry();
						if(newSourceEntry==NULL)
						{
							rtl_gluePrintf("run out of source entry!\n");
							return FAILED;
						}
						else
						{	
						
							if(ipVersion==IP_VERSION4)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
							
							}
							
#ifdef CONFIG_RTL_MLD_SNOOPING
							if(ipVersion==IP_VERSION6)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
								newSourceEntry->sourceAddr[1]=sourceAddr[1];
								newSourceEntry->sourceAddr[2]=sourceAddr[2];
								newSourceEntry->sourceAddr[3]=sourceAddr[3];
							}
#endif							
						
							/*B-A time out*/
							newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
							rtl_linkSourceEntry(groupEntry,newSourceEntry);
						}

					}
					else/*maybe include redundant sources*/
					{
						/*B-A time out*/
						if(sourceEntry->portTimer[portNum]==0)
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
						}

						if(sourceEntry->portTimer[portNum]>rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime)            /*lower the A*B timer if the cpu is router*/
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
						}
					}

					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
				}

			}	
			
		}
		else/*means exclude mode*/
		{

			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				/*here to handle the source list*/
				/*delete (X-A) and delete (Y-A)*/
				sourceEntry=groupEntry->sourceList;
				while(sourceEntry)
				{
					if(rtl_searchSourceAddr(ipVersion,sourceEntry->sourceAddr,sourceArray, numOfSrc)== FALSE)
					{
						sourceEntry->portTimer[portNum]=0;
					}
					sourceEntry=sourceEntry->next;
				}

				/*A-X-Y=filter timer*/
				for(j=0; j<numOfSrc; j++)
				{
					sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
					
					if(sourceEntry==NULL)
					{
						newSourceEntry=rtl_allocateSourceEntry();
						if(newSourceEntry==NULL)
						{
							rtl_gluePrintf("run out of source entry!\n");
							return FAILED;
						}
						else
						{	
						
							if(ipVersion==IP_VERSION4)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
							}
							
#ifdef CONFIG_RTL_MLD_SNOOPING
							if(ipVersion==IP_VERSION6)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
								newSourceEntry->sourceAddr[1]=sourceAddr[1];
								newSourceEntry->sourceAddr[2]=sourceAddr[2];
								newSourceEntry->sourceAddr[3]=sourceAddr[3];	
							}
#endif							
                                                 newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
                                                 rtl_linkSourceEntry(groupEntry,newSourceEntry);													
							
						}

					}
					else
					{	
						if((sourceEntry->portTimer[portNum]==0)||(sourceEntry->portTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime)))                                                          
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
						}
					}
				
					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif
					
				}
			}
		
			
		}

		/*switch to exclude mode or update exclude mode filter timer*/
		groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
		
	

	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return SUCCESS;
}

static  int32 rtl_processAllow(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf)
{
	uint32 j=0;
	uint32 groupAddress[4]={0, 0, 0, 0};
	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_groupEntry* newGroupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;
	struct rtl_sourceEntry *newSourceEntry=NULL;
	
	uint32 hashIndex=0;
	uint16 numOfSrc=0;
	uint32 *sourceAddr=NULL;

	

	if(ipVersion==IP_VERSION4)
	{
		groupAddress[0]=ntohl(((struct groupRecord *)pktBuf)->groupAddr);
		numOfSrc=ntohs(((struct groupRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct groupRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[0]&rtl_hashMask;
	}

#ifdef CONFIG_RTL_MLD_SNOOPING		
	if(ipVersion==IP_VERSION6)
	{
		
		groupAddress[0]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[3]);
		
		numOfSrc=ntohs(((struct mCastAddrRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[3]&rtl_hashMask;
	
	}
#endif	

	groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	if(groupEntry==NULL)   /*means new group address, create new group entry*/
	{
		newGroupEntry=rtl_allocateGroupEntry();
		if(newGroupEntry==NULL)
		{
			rtl_gluePrintf("run out of group entry!\n");
			return FAILED;
		}
		else
		{	
			/*set new multicast entry*/
#ifdef CONFIG_RTL_MLD_SNOOPING	
			newGroupEntry->groupAddr[0]=groupAddress[0];
			newGroupEntry->groupAddr[1]=groupAddress[1];
			newGroupEntry->groupAddr[2]=groupAddress[2];
			newGroupEntry->groupAddr[3]=groupAddress[3];
#else
			newGroupEntry->groupAddr[0]=groupAddress[0];
#endif

		
			newGroupEntry->ipVersion=ipVersion;

			newGroupEntry->sourceList=NULL;
			
			/*end of set group entry*/
			
			/*must first link into hash table, then call the function of set rtl8306sdm, because of aggregator checking*/
			if(ipVersion==IP_VERSION4)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable, hashIndex);
			}

#ifdef CONFIG_RTL_MLD_SNOOPING
			if(ipVersion==IP_VERSION6)
			{
				rtl_linkGroupEntry(newGroupEntry, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable, hashIndex);
			}
#endif
			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				newGroupEntry->groupFilterTimer[portNum]=0;
				/*link the new source list*/
				for(j=0; j<numOfSrc; j++)
				{

					
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
						}
						
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif						
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
						rtl_linkSourceEntry(newGroupEntry,newSourceEntry);
					}
					
					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
				}
			}
			else
			{
				newGroupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;;
			}
	
		}

	
		
	}
	else /*means it can be found in the forward hash table*/
	{  
		if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
		{
			/*here to handle the source list*/
			for(j=0; j<numOfSrc; j++)
			{
			
				sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
			
				if(sourceEntry==NULL)
				{
					newSourceEntry=rtl_allocateSourceEntry();
					if(newSourceEntry==NULL)
					{
						rtl_gluePrintf("run out of source entry!\n");
						return FAILED;
					}
					else
					{	
					
						if(ipVersion==IP_VERSION4)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
						}
						
#ifdef CONFIG_RTL_MLD_SNOOPING
						if(ipVersion==IP_VERSION6)
						{	
							newSourceEntry->sourceAddr[0]=sourceAddr[0];
							newSourceEntry->sourceAddr[1]=sourceAddr[1];
							newSourceEntry->sourceAddr[2]=sourceAddr[2];
							newSourceEntry->sourceAddr[3]=sourceAddr[3];
						}
#endif						
						newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;
						rtl_linkSourceEntry(groupEntry,newSourceEntry);
					}

				}
				else
				{		
					/*just update source timer*/
					sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;		
				}
					
				if(ipVersion==IP_VERSION4)
				{	
					sourceAddr++;
				}
				
#ifdef CONFIG_RTL_MLD_SNOOPING
				if(ipVersion==IP_VERSION6)
				{
					sourceAddr=sourceAddr+4;
				}
#endif				
			}
		}
		else
		{
			groupEntry->groupFilterTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;;
		}

	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return SUCCESS;
}

static int32 rtl_processBlock(uint32 moduleIndex, uint32 ipVersion,uint32 portNum, uint8 *pktBuf)
{
	uint32 j=0;
	uint32 groupAddress[4]={0, 0, 0, 0};

	struct rtl_groupEntry* groupEntry=NULL;
	struct rtl_sourceEntry *sourceEntry=NULL;
	struct rtl_sourceEntry *newSourceEntry=NULL;
//	struct rtl_sourceEntry *nextSourceEntry=NULL;
	
	uint32 hashIndex=0;
	uint16 numOfSrc=0;
	uint32 *sourceAddr=NULL;

	if(ipVersion==IP_VERSION4)
	{
		groupAddress[0]=ntohl(((struct groupRecord *)pktBuf)->groupAddr);
		numOfSrc=ntohs(((struct groupRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct groupRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[0]&rtl_hashMask;
	}

#ifdef CONFIG_RTL_MLD_SNOOPING		
	if(ipVersion==IP_VERSION6)
	{
		
		groupAddress[0]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[0]);
		groupAddress[1]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[1]);
		groupAddress[2]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[2]);
		groupAddress[3]=ntohl(((struct mCastAddrRecord *)pktBuf)->mCastAddr[3]);
		
		numOfSrc=ntohs(((struct mCastAddrRecord *)pktBuf)->numOfSrc);
		sourceAddr=&(((struct mCastAddrRecord *)pktBuf)->srcList);
		hashIndex=groupAddress[3]&rtl_hashMask;
	}
#endif

	groupEntry=rtl_searchGroupEntry(moduleIndex, ipVersion, groupAddress);
	if(groupEntry!=NULL)
	{  
				
		if(groupEntry->groupFilterTimer[portNum]>rtl_sysUpSeconds) /*means exclude mode*/
		{
			if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
			{
				/*here to handle the source list*/
				/*A-X-Y=filter timer*/
				for(j=0; j<numOfSrc; j++)
				{
					
					sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
				
					if(sourceEntry==NULL)
					{
						newSourceEntry=rtl_allocateSourceEntry();
						if(newSourceEntry==NULL)
						{
							rtl_gluePrintf("run out of source entry!\n");
							return FAILED;
						}
						else
						{	
						
							if(ipVersion==IP_VERSION4)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
							
							}
							
#ifdef CONFIG_RTL_MLD_SNOOPING
							if(ipVersion==IP_VERSION6)
							{	
								newSourceEntry->sourceAddr[0]=sourceAddr[0];
								newSourceEntry->sourceAddr[1]=sourceAddr[1];
								newSourceEntry->sourceAddr[2]=sourceAddr[2];
								newSourceEntry->sourceAddr[3]=sourceAddr[3];
							}
#endif                           
                                                
                                                 if(groupEntry->groupFilterTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime))
                                                 {
                                                 	newSourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
							}
							else
							{
								newSourceEntry->portTimer[portNum]=groupEntry->groupFilterTimer[portNum];
							}

                                                 rtl_linkSourceEntry(groupEntry,newSourceEntry);
							
						}

					}
					else
					{
						    
                                       	if(rtl_mCastModuleArray[moduleIndex].enableFastLeave==TRUE)
						{
						   	sourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
						}
						else
						{
							if((sourceEntry->portTimer[portNum]==0)||(sourceEntry->portTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime)))
							{
								sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
							}
						}

					}

				
					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
					
				}
                            
			}
			#if 0
			else
			{
					groupEntry->groupFilterTimer[portIndex]=rtl_sysUpSeconds+rtl_mCastTimerParas.groupMemberAgingTime;;
			}
			#endif
		}
              else           /*means include mode*/
              {
                     if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)             /*process the special-group query if the cpu is router*/
                     {
                      	for(j=0; j<numOfSrc; j++)
                          	{

				        sourceEntry=rtl_searchSourceEntry(ipVersion, sourceAddr,groupEntry);
					if(sourceEntry!=NULL)
					{
						
						if(rtl_mCastModuleArray[moduleIndex].enableFastLeave==TRUE)
						{
							sourceEntry->portTimer[portNum]=rtl_sysUpSeconds;
						}
						else
						{
							if((sourceEntry->portTimer[portNum]==0)||(sourceEntry->portTimer[portNum]>(rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime)))
							{
								sourceEntry->portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.lastMemberAgingTime;
							}
						}
					}	

					if(ipVersion==IP_VERSION4)
					{	
						sourceAddr++;
					}
					
#ifdef CONFIG_RTL_MLD_SNOOPING
					if(ipVersion==IP_VERSION6)
					{
						sourceAddr=sourceAddr+4;
					}
#endif					
			     	}

			}

	        }
	
	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=groupAddress[0];
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return SUCCESS;

}


static uint32 rtl_processIgmpv3Mldv2Reports(uint32 moduleIndex, uint32 ipVersion, uint32 portNum, uint8 *pktBuf)
{
	uint32 i=0;
	uint16 numOfRecords=0;
	uint8 *groupRecords=NULL;
	uint8 recordType=0xff;
	uint16 numOfSrc=0;
	int32 returnVal=0;
	uint32 multicastRouterPortMask=rtl_getMulticastRouterPortMask(moduleIndex, ipVersion, rtl_sysUpSeconds);
	
	if(ipVersion==IP_VERSION4)
	{
		numOfRecords=ntohs(((struct igmpv3Report *)pktBuf)->numOfRecords);
		if(numOfRecords!=0)
		{
			groupRecords=(uint8 *)(&(((struct igmpv3Report *)pktBuf)->recordList));
		}
	}

#ifdef CONFIG_RTL_MLD_SNOOPING
	if(ipVersion==IP_VERSION6)
	{	
		numOfRecords=ntohs(((struct mldv2Report *)pktBuf)->numOfRecords);
		if(numOfRecords!=0)
		{
			groupRecords=(uint8 *)(&(((struct mldv2Report *)pktBuf)->recordList));
		}
	}
#endif
	
	for(i=0; i<numOfRecords; i++)
	{
		if(ipVersion==IP_VERSION4)
		{
			recordType=((struct groupRecord *)groupRecords)->type;
		}
#ifdef CONFIG_RTL_MLD_SNOOPING		
		if(ipVersion==IP_VERSION6)
		{
			recordType=((struct mCastAddrRecord *)groupRecords)->type;
		}
#endif		
	
		switch(recordType)
		{
			case MODE_IS_INCLUDE:
				returnVal=rtl_processIsInclude(moduleIndex, ipVersion, portNum, groupRecords);
			break;
			
			case MODE_IS_EXCLUDE:
				returnVal=rtl_processIsExclude(moduleIndex, ipVersion, portNum, groupRecords);
			break;
			
			case CHANGE_TO_INCLUDE_MODE:
				returnVal=rtl_processToInclude(moduleIndex, ipVersion, portNum, groupRecords);
			break;
			
			case CHANGE_TO_EXCLUDE_MODE:
				returnVal=rtl_processToExclude(moduleIndex, ipVersion, portNum, groupRecords);
			break;
			
			case ALLOW_NEW_SOURCES:
				returnVal=rtl_processAllow(moduleIndex, ipVersion, portNum, groupRecords);
			break;
			
			case BLOCK_OLD_SOURCES:
				returnVal=rtl_processBlock(moduleIndex, ipVersion, portNum, groupRecords);
			break;
			
			default:break;
			
		}

		if(ipVersion==IP_VERSION4)
		{
			numOfSrc=ntohs(((struct groupRecord *)groupRecords)->numOfSrc);
			/*shift pointer to another group record*/
			groupRecords=groupRecords+8+numOfSrc*4+(((struct groupRecord *)(groupRecords))->auxLen)*4;
		}
#ifdef CONFIG_RTL_MLD_SNOOPING		
		if(ipVersion==IP_VERSION6)
		{
			numOfSrc=ntohs(((struct mCastAddrRecord *)groupRecords)->numOfSrc);
			/*shift pointer to another group record*/
			groupRecords=groupRecords+20+numOfSrc*16+(((struct mCastAddrRecord *)(groupRecords))->auxLen)*4;
		}
#endif		
	}

	return (multicastRouterPortMask&(~(1<<portNum))&((1<<MAX_SUPPORT_PORT_NUMBER)-1));
	
}

static uint32 rtl_processIgmpMld(uint32 moduleIndex, uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum)
{	
	uint32 fwdPortMask=0;

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	reportEventContext.moduleIndex=moduleIndex;
	#endif
	
	switch(pktBuf[0])
	{
		case IGMP_QUERY:
			fwdPortMask=rtl_processQueries(moduleIndex, ipVersion, portNum, pktBuf, pktLen);
		break;
			
		case IGMPV1_REPORT:
			 fwdPortMask=rtl_processJoin(moduleIndex, ipVersion, portNum, pktBuf);
		break;
			
		case IGMPV2_REPORT:	
			 fwdPortMask=rtl_processJoin(moduleIndex, ipVersion, portNum, pktBuf);
		break;
			
		case IGMPV2_LEAVE:
			 fwdPortMask=rtl_processLeave(moduleIndex, ipVersion, portNum, pktBuf);
		break;

		case IGMPV3_REPORT:
			 fwdPortMask=rtl_processIgmpv3Mldv2Reports(moduleIndex, ipVersion, portNum, pktBuf);
		break;

		case MLD_QUERY:
			fwdPortMask=rtl_processQueries(moduleIndex, ipVersion, portNum, pktBuf, pktLen);
		break;
			
		case MLDV1_REPORT:
			 fwdPortMask=rtl_processJoin(moduleIndex, ipVersion, portNum, pktBuf);
		break;
			
		case MLDV1_DONE:	
			 fwdPortMask=rtl_processLeave(moduleIndex, ipVersion, portNum, pktBuf);
		break;
			
		case MLDV2_REPORT:
			 fwdPortMask=rtl_processIgmpv3Mldv2Reports(moduleIndex, ipVersion, portNum, pktBuf);
		break;

		default:
			fwdPortMask=((~(1<<portNum))&((1<<MAX_SUPPORT_PORT_NUMBER)-1));
		break;
	}						
	
	return fwdPortMask;
			
}



static uint32 rtl_processDvmrp(uint32 moduleIndex, uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum)
{
	
	if(ipVersion==IP_VERSION4)
	{
		rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.dvmrpRouter.portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.dvmrpRouterAgingTime; /*update timer*/
	}

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=0;
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return ((~(1<<portNum))&((1<<MAX_SUPPORT_PORT_NUMBER)-1));

}

static uint32 rtl_processMospf(uint32 moduleIndex,uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum)
{ 
	struct ipv4MospfHdr *ipv4MospfHeader=(struct ipv4MospfHdr*)pktBuf;
	struct ipv4MospfHello *ipv4HelloPkt=(struct ipv4MospfHello*)pktBuf;
	
#ifdef CONFIG_RTL_MLD_SNOOPING		
	struct ipv6MospfHdr *ipv6MospfHeader=(struct ipv6MospfHdr*)pktBuf;
	struct ipv6MospfHello *ipv6HelloPkt=(struct ipv6MospfHello*)pktBuf;
#endif


	if(ipVersion==IP_VERSION4)
	{	
		/*mospf is built based on ospfv2*/
		if((ipv4MospfHeader->version==2) && (ipv4MospfHeader->type==MOSPF_HELLO_TYPE))
		{
			if((ipv4HelloPkt->options & 0x04)!=0)
			{
				rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.mospfRouter.portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.mospfRouterAgingTime; /*update timer*/
			}
		}
	}

#ifdef CONFIG_RTL_MLD_SNOOPING	
	if(ipVersion==IP_VERSION6)
	{	
		if((ipv6MospfHeader->version==3) && (ipv6MospfHeader->type==MOSPF_HELLO_TYPE))
		{
			if((ipv6HelloPkt->options[2] & 0x04)!=0)
			{
				rtl_mCastModuleArray[moduleIndex].rtl_ipv6MulticastRouters.mospfRouter.portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.mospfRouterAgingTime; /*update timer*/
			
			}
		}
	}
#endif

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=0;
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return ((~(1<<portNum))&((1<<MAX_SUPPORT_PORT_NUMBER)-1));
	
}

static uint32 rtl_processPim(uint32 moduleIndex, uint32 ipVersion, uint8* pktBuf, uint32 pktLen, uint32 portNum)
{
	if(ipVersion==IP_VERSION4)
	{	
		rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters.pimRouter.portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.pimRouterAgingTime; /*update timer*/
		
	}
	
#ifdef CONFIG_RTL_MLD_SNOOPING	
	if(ipVersion==IP_VERSION6)
	{
		rtl_mCastModuleArray[moduleIndex].rtl_ipv6MulticastRouters.pimRouter.portTimer[portNum]=rtl_sysUpSeconds+rtl_mCastTimerParas.pimRouterAgingTime; /*update timer*/
	}
#endif

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(ipVersion==IP_VERSION4)
	{
		strcpy(reportEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
		reportEventContext.groupAddr[0]=0;
		reportEventContext.sourceAddr[0]=0;
		rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &reportEventContext);
	}
	#endif

	return ((~(1<<portNum))&((1<<MAX_SUPPORT_PORT_NUMBER)-1));
}



/*********************************************
				External Function
  *********************************************/


//External called function by high level program
/*
@func int32	| rtl_registerIgmpSnoopingModule	|   API to register an igmp snooping module.
@parm  uint32 *	| moduleIndex	| Output parameter to return the igmp snooping module index.
@rvalue SUCCESS	| Register igmp snooping module successfully.
@rvalue FAILED	| Register igmp snooping module failed.
*/
int32 rtl_registerIgmpSnoopingModule(uint32 *moduleIndex)
{
	int32 i=0;
	uint32 index=0xFFFFFFFF;
	
	*moduleIndex=0xFFFFFFFF;

	for(i=0; i<MAX_MCAST_MODULE_NUM; i++)
	{
		if(rtl_mCastModuleArray[i].enableSnooping==FALSE)
		{
			index=i;
			break;
		}
	}

	if(i>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
	

	if(rtl_mCastModuleArray[index].enableSnooping==FALSE)
	{
	      /*initialize multicast Routers information*/
	      for(i=0; i<MAX_SUPPORT_PORT_NUMBER; i++)
	      {
			rtl_mCastModuleArray[index].rtl_ipv4MulticastRouters.querier.portTimer[i]=0;
			rtl_mCastModuleArray[index].rtl_ipv4MulticastRouters.dvmrpRouter.portTimer[i]=0;
			rtl_mCastModuleArray[index].rtl_ipv4MulticastRouters.pimRouter.portTimer[i]=0;
			rtl_mCastModuleArray[index].rtl_ipv4MulticastRouters.mospfRouter.portTimer[i]=0;
			
#ifdef CONFIG_RTL_MLD_SNOOPING		
			rtl_mCastModuleArray[index].rtl_ipv6MulticastRouters.querier.portTimer[i]=0;
			rtl_mCastModuleArray[index].rtl_ipv6MulticastRouters.dvmrpRouter.portTimer[i]=0;
			rtl_mCastModuleArray[index].rtl_ipv6MulticastRouters.pimRouter.portTimer[i]=0;
			rtl_mCastModuleArray[index].rtl_ipv6MulticastRouters.mospfRouter.portTimer[i]=0;
#endif			
	      }
	
	      /*initialize hash table*/
	      rtl_initHashTable(index, rtl_hashTableSize);
	
	      if((rtl_mCastModuleArray[index].rtl_ipv4HashTable==NULL) )
	      {
		      return FAILED;
	      }
		  
#ifdef CONFIG_RTL_MLD_SNOOPING
		if(rtl_mCastModuleArray[index].rtl_ipv6HashTable==NULL)
		{
			return FAILED;
		}
#endif

#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		memset(&rtl_mCastModuleArray[index].deviceInfo,0, sizeof(rtl_multicastDeviceInfo_t));
#endif
      		for(i=0; i<6; i++)
      		{
	      		rtl_mCastModuleArray[index].rtl_gatewayMac[i]=0;
      		}
	
		rtl_mCastModuleArray[index]. rtl_gatewayIpv4Addr=0;

#ifdef CONFIG_RTL_MLD_SNOOPING		
		 for(i=0; i<4; i++)
      		{
	      		rtl_mCastModuleArray[index].rtl_gatewayIpv6Addr[i]=0;
     		 }
#endif
		rtl_mCastModuleArray[index].enableSourceList=TRUE;
	       rtl_mCastModuleArray[index].enableFastLeave=TRUE;
	       rtl_mCastModuleArray[index].enableSnooping=TRUE;
		rtl_mCastModuleArray[index].unknownMCastFloodMap=0;
		rtl_mCastModuleArray[index].staticRouterPortMask=0;

#ifdef CONFIG_PROC_FS
		rtl_mCastModuleArray[index].expireEventCnt=0;
#endif
		*moduleIndex=index;
		return SUCCESS;
	}
	else
	{
	       return FAILED;
	}

	*moduleIndex=index;
	*moduleIndex=index;
	return SUCCESS;
}


/*
@func int32	| rtl_unregisterIgmpSnoopingModule	|  Unregister an igmp snooping module.
@parm  uint32	| moduleIndex	| Igmp snooping module index. 
@rvalue SUCCESS	|Unregister igmp snooping module successfully.
@rvalue FAILED	|Unregister igmp snooping module failed.
@comm 
 This function should be called once a second to maintain multicast timer list.
*/
int32 rtl_unregisterIgmpSnoopingModule(uint32 moduleIndex)
{
	uint32 i=0;
	struct rtl_groupEntry *groupEntryPtr=NULL;
	
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
	
       if(rtl_mCastModuleArray[moduleIndex].enableSnooping==TRUE)
       {
	
		 rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv4Addr=0;
		 
          	 for(i=0; i<6; i++)
     		{
	   		 rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac[i]=0;
      		}
			
#ifdef CONFIG_RTL_MLD_SNOOPING					 
		for(i=0;i<4;i++)
		{
			rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[i]=0;
		}
#endif	

	 	 /*delete ipv4 multicast entry*/
        	for(i=0;i<rtl_hashTableSize;i++)
	     	{
			groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[i];
				
			while(groupEntryPtr!=NULL)
			{
				rtl_deleteGroupEntry(groupEntryPtr, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable);
				groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[i];
			}
	       }
		rtl_glueFree(rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable);
		rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable=NULL;
		memset(&(rtl_mCastModuleArray[moduleIndex].rtl_ipv4MulticastRouters), 0, sizeof(struct rtl_multicastRouters));
		
#ifdef CONFIG_RTL_MLD_SNOOPING		
		/*delete ipv6 multicast entry*/
		for(i=0; i<rtl_hashTableSize; i++)
		{
		
			groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable[i];
			while(groupEntryPtr!=NULL)
			{
				rtl_deleteGroupEntry(groupEntryPtr, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable);
				groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable[i];
			}
		}
		rtl_glueFree(rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable);
		rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable=NULL;
		memset(&(rtl_mCastModuleArray[moduleIndex].rtl_ipv6MulticastRouters), 0, sizeof(struct rtl_multicastRouters));
#endif

		rtl_mCastModuleArray[moduleIndex].enableSnooping=FALSE;
		rtl_mCastModuleArray[moduleIndex].enableSourceList=TRUE;
		rtl_mCastModuleArray[moduleIndex].enableFastLeave=TRUE;

		rtl_mCastModuleArray[moduleIndex].unknownMCastFloodMap=0;
		rtl_mCastModuleArray[moduleIndex].staticRouterPortMask=0;
#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		memset(&rtl_mCastModuleArray[moduleIndex].deviceInfo,0,sizeof(rtl_multicastDeviceInfo_t));
#endif
	       return SUCCESS;
       }
	   
	 return SUCCESS;
	
}

static void _rtl865x_configIgmpSnoopingExpire(int32 disableExpire)
{
	uint32 maxTime=0xffffffff;
	
	if((rtl_mCastTimerParas.disableExpire==FALSE) && (disableExpire==TRUE))
	{
		rtl_mCastTimerParas.disableExpire=TRUE;
	}
		
	if((rtl_mCastTimerParas.disableExpire==TRUE) && (disableExpire==FALSE) )
	{
#if defined(__linux__) && defined(__KERNEL__)
		struct timeval currentTimeVector; 
		do_gettimeofday(&currentTimeVector);
		/*reset start time*/
		if(currentTimeVector.tv_sec>=rtl_sysUpSeconds)
		{
			rtl_startTime=(uint32)(currentTimeVector.tv_sec)-rtl_sysUpSeconds;
		}
		else
		{
			/*avoid timer wrap back*/
			rtl_startTime=maxTime-rtl_sysUpSeconds+(uint32)(currentTimeVector.tv_sec)+1;
		}
#endif
		rtl_mCastTimerParas.disableExpire=FALSE;
	}
	

	return;
}

//External called function by high level program
/*
@func  void	| rtl_setMulticastParameters	|   API to config igmp snooping time parameters.
@parm  struct rtl_mCastTimerParameters	| mCastTimerParameters	|IGMP snooping time parameters to be set.
*/
void rtl_setMulticastParameters(struct rtl_mCastTimerParameters mCastTimerParameters)
{
	_rtl865x_configIgmpSnoopingExpire(mCastTimerParameters.disableExpire);

	if(mCastTimerParameters.groupMemberAgingTime!=0)
	{
		rtl_mCastTimerParas.groupMemberAgingTime= mCastTimerParameters.groupMemberAgingTime;
	}
	
	if(mCastTimerParameters.lastMemberAgingTime!=0)
	{
		rtl_mCastTimerParas.lastMemberAgingTime= mCastTimerParameters.lastMemberAgingTime;
	}

	if(mCastTimerParameters.querierPresentInterval!=0)
	{
	
		rtl_mCastTimerParas.querierPresentInterval=mCastTimerParameters.querierPresentInterval;
	}


	if(mCastTimerParameters.dvmrpRouterAgingTime!=0)
	{
	
		rtl_mCastTimerParas.dvmrpRouterAgingTime=mCastTimerParameters.dvmrpRouterAgingTime;
	}

	if(mCastTimerParameters.mospfRouterAgingTime!=0)
	{
	
		rtl_mCastTimerParas.mospfRouterAgingTime=mCastTimerParameters.mospfRouterAgingTime;
	}

	if(mCastTimerParameters.pimRouterAgingTime!=0)
	{
	
		rtl_mCastTimerParas.pimRouterAgingTime=mCastTimerParameters.pimRouterAgingTime;
	}
	
	return;
}

/*
@func int32	| rtl_configIgmpSnoopingModule	|   API to config local parameters of an igmp snooping module.
@parm  uint32	| moduleIndex	| Input parameter to specify an igmp snooping module.
@parm  struct rtl_mCastSnoopingLocalConfig *	| mCastSnoopingLocalConfig	| Local parameters to be set to an igmp snooping module
@rvalue SUCCESS	| Configuration  success.
@rvalue FAILED	| Configuration failed.
*/
int32 rtl_configIgmpSnoopingModule(uint32 moduleIndex, struct rtl_mCastSnoopingLocalConfig *mCastSnoopingLocalConfig)
{

	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
	
	if(mCastSnoopingLocalConfig==NULL)
	{
		return FAILED;
	}
	
	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==FALSE)
	{
		return FAILED;
	}
	
	rtl_mCastModuleArray[moduleIndex].enableSourceList=mCastSnoopingLocalConfig->enableSourceList;
	rtl_mCastModuleArray[moduleIndex].enableFastLeave=mCastSnoopingLocalConfig->enableFastLeave;
	rtl_mCastModuleArray[moduleIndex].unknownMCastFloodMap=mCastSnoopingLocalConfig->unknownMcastFloodMap;
	rtl_mCastModuleArray[moduleIndex].staticRouterPortMask=mCastSnoopingLocalConfig->staticRouterPortMask;
		
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac[0]=mCastSnoopingLocalConfig->gatewayMac[0];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac[1]=mCastSnoopingLocalConfig->gatewayMac[1];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac[2]=mCastSnoopingLocalConfig->gatewayMac[2];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac[3]=mCastSnoopingLocalConfig->gatewayMac[3];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac[4]=mCastSnoopingLocalConfig->gatewayMac[4];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayMac[5]=mCastSnoopingLocalConfig->gatewayMac[5];



	rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv4Addr=mCastSnoopingLocalConfig->gatewayIpv4Addr;
	
#ifdef CONFIG_RTL_MLD_SNOOPING		
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[0]=mCastSnoopingLocalConfig->gatewayIpv6Addr[0];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[1]=mCastSnoopingLocalConfig->gatewayIpv6Addr[1];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[2]=mCastSnoopingLocalConfig->gatewayIpv6Addr[2];
	rtl_mCastModuleArray[moduleIndex].rtl_gatewayIpv6Addr[3]=mCastSnoopingLocalConfig->gatewayIpv6Addr[3];
#endif
	return SUCCESS;
}

/*
@func int32	| rtl_maintainMulticastSnoopingTimerList	|   Maintain igmp snooping internal timer list.
@parm  uint32	| currentSystemTime	|The current system time (unit: seconds).
@rvalue SUCCESS	|Always return SUCCESS.
@comm 
 This function should be called every second
*/
int32 rtl_maintainMulticastSnoopingTimerList(uint32 currentSystemTime)
{
	/* maintain current time */
	uint32 i=0;
	uint32 maxTime=0xffffffff;

	struct rtl_groupEntry* groupEntryPtr=NULL;
	struct rtl_groupEntry* nextEntry=NULL;

	uint32 moduleIndex;

	if(rtl_mCastTimerParas.disableExpire==TRUE)
	{
		return SUCCESS;
	}
	
	/*handle timer conter overflow*/
	if(currentSystemTime>rtl_startTime)
	{
		rtl_sysUpSeconds=currentSystemTime-rtl_startTime;
	}
	else
	{
		rtl_sysUpSeconds=(maxTime-rtl_startTime)+currentSystemTime+1;
	}

	for(moduleIndex=0; moduleIndex<MAX_MCAST_MODULE_NUM; moduleIndex++)
	{
		if(rtl_mCastModuleArray[moduleIndex].enableSnooping==TRUE)
		{
			#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
			strcpy(timerEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
			timerEventContext.moduleIndex=moduleIndex;
			#endif

			/*maintain ipv4 group entry  timer */
			for(i=0; i<rtl_hashTableSize; i++)
			{
				  /*scan the hash table*/
				 if(rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable!=NULL)
				 {
					groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[i];
					while(groupEntryPtr)              /*traverse each group list*/
					{	
						nextEntry=groupEntryPtr->next; 
						#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
						timerEventContext.groupAddr[0]=groupEntryPtr->groupAddr[0];
						#endif
						rtl_checkGroupEntryTimer(groupEntryPtr, rtl_mCastModuleArray[moduleIndex].enableSourceList, rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable);
						groupEntryPtr=nextEntry;/*because expired group entry  will be cleared*/
					}
				 }
			}
			
#ifdef CONFIG_RTL_MLD_SNOOPING		
			/*maintain ipv6 group entry  timer */
			for(i=0; i<rtl_hashTableSize; i++)
			{
				  /*scan the hash table*/
				if(rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable!=NULL)
				{
					groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable[i];
					while(groupEntryPtr)              /*traverse each group list*/
					{	
						nextEntry=groupEntryPtr->next; 
						rtl_checkGroupEntryTimer(groupEntryPtr, rtl_mCastModuleArray[moduleIndex].enableSourceList, rtl_mCastModuleArray[moduleIndex].rtl_ipv6HashTable);
						groupEntryPtr=nextEntry;/*because expired group entry  will be cleared*/
					}
				}
			}
#endif

		}
	}
	return SUCCESS;
}


/*
@func int32	| rtl_igmpMldProcess	|  API to process an incoming igmp packet.
@parm  uint32	| moduleIndex	| Igmp snooping module index.
@parm  uint8 *	| macFrame	| Data pointer of igmp mac frame.
@parm  uint32	| portNum	| Input parameter to specify at which port this igmp packet was recieved 
@parm  uint32 *	| fwdPortMask	| Output parameter to tell where to relay this igmp packet.
@rvalue SUCCESS	|Doing igmp snooping successfully.
@rvalue FAILED	|Doing igmp snooping failed.
*/
int32 rtl_igmpMldProcess(uint32 moduleIndex, uint8 * macFrame,  uint32 portNum, uint32 *fwdPortMask)
{

	struct rtl_macFrameInfo macFrameInfo;

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	reportEventContext.portMask=1<<portNum;
	#endif

	*fwdPortMask=(~(1<<portNum)) & 0xFFFFFFFF;

	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
	
	//rtl_parseMacFrame(moduleIndex, macFrame, TRUE, &macFrameInfo);
	rtl_parseMacFrame(moduleIndex, macFrame, FALSE, &macFrameInfo);
	if(  rtl_mCastModuleArray[moduleIndex].enableSnooping==TRUE)
	{
		if(macFrameInfo.ipBuf==NULL)
		{
			return FAILED;
		}
		
		if((macFrameInfo.ipVersion!=IP_VERSION4) && (macFrameInfo.ipVersion!=IP_VERSION6))
		{
		
			return FAILED;
		}
		
#ifndef CONFIG_RTL_MLD_SNOOPING	
		if (macFrameInfo.ipVersion==IP_VERSION6)
		{
			return FAILED;
		}
#endif
		/*port num starts from 0*/
		if(portNum>=MAX_SUPPORT_PORT_NUMBER)
		{
			return FAILED;
		}
		#if 0
		if(rtl_checkPortMask(pktPortMask)==FAILED)
		{
			return FAILED;
		}
		#endif
		if(macFrameInfo.checksumFlag!=SUCCESS)
		{
			return FAILED;
		}

		switch(macFrameInfo.l3Protocol)
		{

			case IGMP_PROTOCOL:
				*fwdPortMask=rtl_processIgmpMld(moduleIndex, (uint32)(macFrameInfo.ipVersion), macFrameInfo.l3PktBuf, macFrameInfo.l3PktLen, portNum);
			break;

			case ICMP_PROTOCOL:
				*fwdPortMask=rtl_processIgmpMld(moduleIndex, (uint32)(macFrameInfo.ipVersion), macFrameInfo.l3PktBuf, macFrameInfo.l3PktLen, portNum);
			break;


			case DVMRP_PROTOCOL:
				*fwdPortMask=rtl_processDvmrp(moduleIndex, (uint32)(macFrameInfo.ipVersion), macFrameInfo.l3PktBuf, macFrameInfo.l3PktLen, portNum);
			break;

			case MOSPF_PROTOCOL:
				*fwdPortMask=rtl_processMospf(moduleIndex, (uint32)(macFrameInfo.ipVersion), macFrameInfo.l3PktBuf, macFrameInfo.l3PktLen,portNum);
			break;
				
			case PIM_PROTOCOL:
				*fwdPortMask=rtl_processPim(moduleIndex, (uint32)(macFrameInfo.ipVersion), macFrameInfo.l3PktBuf, macFrameInfo.l3PktLen, portNum);
			break;

			default: break;
		}
		
	}
	
	return SUCCESS;
}


/*
@func int32	| rtl_getMulticastDataFwdPortMask	| API to get multicast data forward port mask.
@parm  uint32	| moduleIndex	| Igmp snooping module index.
@parm  struct rtl_multicastDataInfo *	| multicastDataInfo	| Multicast data related information.
@parm  uint32 *	| fwdPortMask	|Output parameter to tell where to forward this multicast data.
@rvalue SUCCESS	| Get multicast data forward port mask successfully.
@rvalue FAILED	| Get multicast data forward port mask failed.
*/
int32 rtl_getMulticastDataFwdPortMask(uint32 moduleIndex, struct rtl_multicastDataInfo *multicastDataInfo, uint32 *fwdPortMask)
{
	int32 retVal=FAILED;
	struct rtl_multicastFwdInfo multicastFwdInfo;
	
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
		
	if(multicastDataInfo==NULL)
	{
		return FAILED;
	}

	if(fwdPortMask==NULL)
	{
		return FAILED;
	}
	
	retVal=rtl_getMulticastDataFwdInfo( moduleIndex, multicastDataInfo, &multicastFwdInfo);

	*fwdPortMask=multicastFwdInfo.fwdPortMask;

	if(retVal==SUCCESS)
	{
		if(multicastFwdInfo.unknownMCast==TRUE)
		{
			return FAILED;
		}
		else
		{
			return SUCCESS;
		}
	}
	
	return FAILED;
	
}



int32 rtl_getMulticastDataFwdInfo(uint32 moduleIndex, struct rtl_multicastDataInfo *multicastDataInfo, struct rtl_multicastFwdInfo *multicastFwdInfo)
{

	struct rtl_groupEntry * groupEntry=NULL;
	uint32 multicastRouterPortMask=0;

	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
		
	if(multicastDataInfo==NULL)
	{
		return FAILED;
	}

	if(multicastFwdInfo==NULL)
	{
		return FAILED;
	}

	memset(multicastFwdInfo, 0, sizeof(struct rtl_multicastFwdInfo));

	if(multicastDataInfo->groupAddr[0]==RESERVE_MULTICAST_ADDR1)
	{
		multicastFwdInfo->reservedMCast=TRUE;
		multicastFwdInfo->fwdPortMask=0xFFFFFFFF;
		multicastFwdInfo->cpuFlag=TRUE;
		
		return FAILED;
	}
	
	if(IN_MULTICAST_RESV1(multicastDataInfo->groupAddr[0]) )
	{
		multicastFwdInfo->reservedMCast=TRUE;
		multicastFwdInfo->fwdPortMask=0xFFFFFFFF;
		multicastFwdInfo->cpuFlag=TRUE;
		
		return FAILED;
	}
	
	groupEntry=rtl_searchGroupEntry(moduleIndex,multicastDataInfo->ipVersion, multicastDataInfo->groupAddr); 
	
	if(groupEntry==NULL)
	{
		multicastFwdInfo->unknownMCast=TRUE;
		multicastFwdInfo->fwdPortMask= rtl_mCastModuleArray[moduleIndex].unknownMCastFloodMap;

		#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		if((multicastFwdInfo->fwdPortMask & rtl_mCastModuleArray[moduleIndex].deviceInfo.swPortMask)!=0)
		{
			multicastFwdInfo->cpuFlag=TRUE;
		}
		#endif

		return SUCCESS;
	}
	else
	{
		/*here to get multicast router port mask and forward port mask*/
		//multicastRouterPortMask=rtl_getMulticastRouterPortMask(moduleIndex, multicastDataInfo->ipVersion, rtl_sysUpSeconds);
		if(rtl_mCastModuleArray[moduleIndex].enableSourceList==TRUE)
		{
			multicastFwdInfo->fwdPortMask=rtl_getSourceFwdPortMask(groupEntry, multicastDataInfo->sourceIp, rtl_sysUpSeconds);
		}
		else
		{
			if(groupEntry!=NULL)
			{
				multicastFwdInfo->fwdPortMask=(uint32)rtl_getGroupFwdPortMask(groupEntry,0, rtl_sysUpSeconds);	
			}

		}
		
		multicastFwdInfo->fwdPortMask=(multicastFwdInfo->fwdPortMask|multicastRouterPortMask);

		#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		if((multicastFwdInfo->fwdPortMask & rtl_mCastModuleArray[moduleIndex].deviceInfo.swPortMask)!=0)
		{
			multicastFwdInfo->cpuFlag=TRUE;
		}
		#endif

		return SUCCESS;
	
	}
	return FAILED;
	
}

#if defined(__linux__) && defined(__KERNEL__)

static void rtl_multicastSysTimerExpired(uint32 expireDada)
{
	struct timeval currentTimeVector; 
	
	do_gettimeofday(&currentTimeVector);
	rtl_maintainMulticastSnoopingTimerList((uint32)(currentTimeVector.tv_sec));
	mod_timer(&igmpSysTimer, jiffies+HZ);
	
}

static void rtl_multicastSysTimerInit(void)
{
	struct timeval startTimeVector; 
	do_gettimeofday(&startTimeVector);
	rtl_startTime=(uint32)(startTimeVector.tv_sec);
	rtl_sysUpSeconds=0;  
	
	init_timer(&igmpSysTimer);
	igmpSysTimer.data=igmpSysTimer.expires;
	igmpSysTimer.expires=jiffies+HZ;
	igmpSysTimer.function=(void*)rtl_multicastSysTimerExpired;
	add_timer(&igmpSysTimer);
}

static void rtl_multicastSysTimerDestroy(void)
{
	del_timer(&igmpSysTimer);
}

#endif

int32 rtl_getDeviceIgmpSnoopingModuleIndex(rtl_multicastDeviceInfo_t *devInfo,uint32 *moduleIndex)
{
	int i;
	*moduleIndex=0xFFFFFFFF;
	if(devInfo==NULL)
	{
		return FAILED;
	}
	
	for(i=0; i<MAX_MCAST_MODULE_NUM; i++)
	{
		if(rtl_mCastModuleArray[i].enableSnooping==TRUE)
		{
			if(strcmp(rtl_mCastModuleArray[i].deviceInfo.devName, devInfo->devName)==0)
			{
				*moduleIndex=i;
				return SUCCESS;
			}
		}
	}
	
	return FAILED;
}

int32 rtl865x_getDeviceIgmpSnoopingModuleIndex(rtl_multicastDeviceInfo_t *devInfo,uint32 *moduleIndex)
{
	return rtl_getDeviceIgmpSnoopingModuleIndex(devInfo,moduleIndex);
}


/*
@func int32	| rtl_setIgmpSnoopingModuleDevInfo	|   API to set igmp snooping module related device information.
@parm  uint32	| moduleIndex	| Input parameter to specify igmp snooping module.
@parm  rtl_multicastDeviceInfo_t	*| devInfo	| Device information to be set.
@rvalue SUCCESS	|Setting success.
@rvalue FAILED	|Setting failed.
*/
int32 rtl_setIgmpSnoopingModuleDevInfo(uint32 moduleIndex,rtl_multicastDeviceInfo_t *devInfo)
{
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}

	if(devInfo==NULL)
	{
		return FAILED;
	}

	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==FALSE)
	{
		return FAILED;
	}

	memcpy(&rtl_mCastModuleArray[moduleIndex].deviceInfo,devInfo, sizeof(rtl_multicastDeviceInfo_t));
	
	return SUCCESS;
}

int32 rtl_setIgmpSnoopingModuleStaticRouterPortMask(uint32 moduleIndex,uint32 staticRouterPortMask)
{
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}

	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==FALSE)
	{
		return FAILED;
	}

	
	rtl_mCastModuleArray[moduleIndex].staticRouterPortMask=staticRouterPortMask;
	
	return SUCCESS;
}

int32 rtl_getgmpSnoopingModuleStaticRouterPortMask(uint32 moduleIndex,uint32 *staticRouterPortMask)
{
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
	
	if(staticRouterPortMask==NULL)
	{
		return FAILED;
	}
	
	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==FALSE)
	{
		return FAILED;
	}

	*staticRouterPortMask=rtl_mCastModuleArray[moduleIndex].staticRouterPortMask;
	
	return SUCCESS;
}


int32 rtl_setIgmpSnoopingModuleUnknownMCastFloodMap(uint32 moduleIndex,uint32 unknownMCastFloodMap)
{
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}

	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==FALSE)
	{
		return FAILED;
	}

	rtl_mCastModuleArray[moduleIndex].unknownMCastFloodMap=unknownMCastFloodMap;
	
	return SUCCESS;
}

int32 rtl_getIgmpSnoopingModuleUnknownMCastFloodMap(uint32 moduleIndex,uint32 *unknownMCastFloodMap)
{
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}
	
	if(unknownMCastFloodMap==NULL)
	{
		return FAILED;
	}
	
	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==FALSE)
	{
		return FAILED;
	}

	*unknownMCastFloodMap=rtl_mCastModuleArray[moduleIndex].unknownMCastFloodMap;
	
	return SUCCESS;
}

/*
@func int32	| rtl_setIgmpSnoopingModuleDevInfo	| API to get igmp snooping module related device information.
@parm  uint32	| moduleIndex	| Input parameter to specify igmp snooping module
@parm  rtl_multicastDeviceInfo_t	*| devInfo	| Output parameter to store the device information of specified igmp snooping module.
@rvalue SUCCESS	|Geting success.
@rvalue FAILED	|Getting failed.
*/
int32 rtl_getIgmpSnoopingModuleDevInfo(uint32 moduleIndex,rtl_multicastDeviceInfo_t *devInfo)
{
	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return FAILED;
	}

	if(devInfo==NULL)
	{
		return FAILED;
	}
	memset(devInfo,0,sizeof(rtl_multicastDeviceInfo_t));
	
	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==FALSE)
	{
		return FAILED;
	}
	
	memcpy(devInfo,&rtl_mCastModuleArray[moduleIndex].deviceInfo, sizeof(rtl_multicastDeviceInfo_t));
	return SUCCESS;
}



#ifdef CONFIG_PROC_FS
int igmp_show(struct seq_file *s, void *v)
{
	int32 moduleIndex;
	int i=0,j=0;
	struct rtl_groupEntry *groupEntryPtr;
	struct rtl_sourceEntry *sourceEntryPtr;
	int groupCnt=0;
	for(moduleIndex=0; moduleIndex<MAX_MCAST_MODULE_NUM ;moduleIndex++)
	{
		if(rtl_mCastModuleArray[moduleIndex].enableSnooping==TRUE)
		{	
			groupCnt=0;
			seq_printf(s, "---------------------------------------------\n");
			seq_printf(s, "module index:%d,",moduleIndex);
			#ifdef CONFIG_RTL_HARDWARE_MULTICAST
			seq_printf(s, "device name:%s\n",rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
			#endif
			for(i=0;i<rtl_hashTableSize;i++)
		     	{
				groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[i];
					
				while(groupEntryPtr!=NULL)
				{
					groupCnt++;
					seq_printf(s, "[%d]group address:%d.%d.%d.%d\n",groupCnt,
						groupEntryPtr->groupAddr[0]>>24, (groupEntryPtr->groupAddr[0]&0x00ff0000)>>16,
						(groupEntryPtr->groupAddr[0]&0x0000ff00)>>8, (groupEntryPtr->groupAddr[0]&0xff));
				
					for(j=0;j<MAX_SUPPORT_PORT_NUMBER;j++)
					{
						if(groupEntryPtr->groupFilterTimer[j]>=rtl_sysUpSeconds)
						{
							seq_printf(s, "\tgroup timer[port%d]:%d seconds\n",j,groupEntryPtr->groupFilterTimer[j]-rtl_sysUpSeconds);
						}
						else
						{
							//printk("\tgroup timer[port%d]:0 seconds\n",j);

						}
					}
					
					sourceEntryPtr=groupEntryPtr->sourceList;
					while(sourceEntryPtr!=NULL)
					{
						seq_printf(s, "\t\tsource address:%d.%d.%d.%d\n",
						sourceEntryPtr->sourceAddr[0]>>24, (sourceEntryPtr->sourceAddr[0]&0x00ff0000)>>16,
						(sourceEntryPtr->sourceAddr[0]&0x0000ff00)>>8, (sourceEntryPtr->sourceAddr[0]&0xff));
						for(j=0;j<MAX_SUPPORT_PORT_NUMBER;j++)
						{
							if(sourceEntryPtr->portTimer[j]>=rtl_sysUpSeconds)
							{
								seq_printf(s, "\t\tsource  timer[port%d]:%d seconds\n",j,sourceEntryPtr->portTimer[j]-rtl_sysUpSeconds);
							}
							else
							{
								//printk("\t\tsource  timer[port%d]:0 seconds\n",j);
							}
						}
						sourceEntryPtr=sourceEntryPtr->next;
					}
					seq_printf(s, "\n");
					groupEntryPtr=groupEntryPtr->next;
				}
		       }
			seq_printf(s, "expire event cnt is %d\n",rtl_mCastModuleArray[moduleIndex].expireEventCnt);
			seq_printf(s, "\n\n");
		}
	}

	return SUCCESS;
}
#endif



void rtl865x_igmpLinkStatusChangeCallback(uint32 moduleIndex, rtl_igmpPortInfo_t * portInfo)
{
	int i=0,j=0;
	int32 clearFlag=FALSE;
	struct rtl_groupEntry *groupEntryPtr;
	struct rtl_sourceEntry *sourceEntryPtr;
	
	
	if(portInfo==NULL)
	{
		return ;
	}

	if(moduleIndex>=MAX_MCAST_MODULE_NUM)
	{
		return ;
	}
	

	if(rtl_mCastModuleArray[moduleIndex].enableSnooping==TRUE)
	{
		
		for(i=0;i<rtl_hashTableSize;i++)
	     	{
			groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[i];
				
			while(groupEntryPtr!=NULL)
			{
				clearFlag=FALSE;
					
				for(j=0;j<MAX_SUPPORT_PORT_NUMBER;j++)
				{
					if(( (1<<j) & (portInfo->linkPortMask)) ==0)
					{
						if(groupEntryPtr->groupFilterTimer[j]!=0)
						{
							groupEntryPtr->groupFilterTimer[j]=0;
							clearFlag=TRUE;
						}
					}
				
				}
				
				sourceEntryPtr=groupEntryPtr->sourceList;
				while(sourceEntryPtr!=NULL)
				{
					

					for(j=0;j<MAX_SUPPORT_PORT_NUMBER;j++)
					{
						if(( (1<<j) & (portInfo->linkPortMask)) ==0)
						{
							if(sourceEntryPtr->portTimer[j]!=0)
							{
								sourceEntryPtr->portTimer[j]=0;
								clearFlag=TRUE;
							}
						}
						
					}
					sourceEntryPtr=sourceEntryPtr->next;
				}
#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
				if(clearFlag==TRUE)
				{
					strcpy(linkEventContext.devName,rtl_mCastModuleArray[moduleIndex].deviceInfo.devName);
					linkEventContext.moduleIndex=moduleIndex;
					
					linkEventContext.groupAddr[0]=groupEntryPtr->groupAddr[0];
					linkEventContext.groupAddr[1]=groupEntryPtr->groupAddr[1];
					linkEventContext.groupAddr[2]=groupEntryPtr->groupAddr[2];
					linkEventContext.groupAddr[3]=groupEntryPtr->groupAddr[3];
					
					linkEventContext.sourceAddr[0]=0;
					linkEventContext.sourceAddr[1]=0;
					linkEventContext.sourceAddr[2]=0;
					linkEventContext.sourceAddr[3]=0;
					
					rtl865x_raiseEvent(EVENT_UPDATE_MCAST, &linkEventContext);
				}
#endif
				
				groupEntryPtr=groupEntryPtr->next;
			}
	       }
	
	}

	return ;
}

int32 rtl_getGroupInfo(uint32 groupAddr, struct rtl_groupInfo * groupInfo)
{
	int32 moduleIndex;
	int32 hashIndex;
	struct rtl_groupEntry *groupEntryPtr;
	
	if(groupInfo==NULL)
	{
		return FAILED;
	}

	memset(groupInfo, 0 , sizeof(struct rtl_groupInfo));
	
	for(moduleIndex=0; moduleIndex<MAX_MCAST_MODULE_NUM ;moduleIndex++)
	{
		if(rtl_mCastModuleArray[moduleIndex].enableSnooping==TRUE)
		{
			hashIndex=rtl_hashMask&groupAddr;
			groupEntryPtr=rtl_mCastModuleArray[moduleIndex].rtl_ipv4HashTable[hashIndex];
				
			while(groupEntryPtr!=NULL)
			{
				if(groupEntryPtr->groupAddr[0]==groupAddr)
				{
					groupInfo->ownerMask |= (1<<moduleIndex);
					break;
				}
				groupEntryPtr=groupEntryPtr->next;
			}
		      
		}
	}

	return SUCCESS;
}