/*
 * Copyright c                Realtek Semiconductor Corporation, 2003
 * All rights reserved.                                                    
 * 
 * $Header: /usr/local/dslrepos/linux-2.6.30/drivers/net/rtl819x/rtl_nic.c,v 1.156 2012/12/19 09:46:53 ikevin362 Exp $
 *
 * $Author: ikevin362 $
 *
 * Abstract: Pure L2 NIC driver, without RTL865X's advanced L3/4 features.
 *
 *   re865x_nic.c: NIC driver for the RealTek 865* 
 *
 */

#define DRV_RELDATE		"Mar 25, 2004"
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
#include <linux/kconfig.h>
#else
#include <linux/config.h>
#include <linux/autoconf.h>
#endif
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/compiler.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/if_vlan.h>
#include <linux/crc32.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/signal.h>
#include <linux/proc_fs.h>
#include <linux/time.h>
#include <linux/rtc.h>
#include <linux/sockios.h>
#include <bspchip.h>
#include <linux/timer.h>
#include <net/rtl/rtl_types.h>
#if defined(CONFIG_RTK_VLAN_SUPPORT)
#include <net/rtl/rtk_vlan.h>
#endif

#if	defined(CONFIG_RTL8196_RTL8366) && defined(CONFIG_RTL_IGMP_SNOOPING)
#undef	CONFIG_RTL_IGMP_SNOOPING
#endif

/*romedriver modify Boyce 2014-07-10*/
#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER) &&  defined(CONFIG_XDSL_ROMEDRIVER)
#define STOP_PRINTK  printk("Stop at %s  LINE:%d \n",__func__,__LINE__); while(1){}
#define FUNC_PRINTK  printk("Trace at %s  LINE:%d \n",__func__,__LINE__)

char watch_tmp[512];

#ifndef DEBUG
#define DEBUG( comment ,arg...) COMMON_DUMP(0x1,"DEBUG",33,40,comment,##arg)
#endif
#ifndef COMMON_DUMP
#define COMMON_DUMP( bitmask, string, color,bgcolor,comment ,arg...) \
do {\
			int mt_trace_i;\
			sprintf( watch_tmp, comment,## arg);\
			for(mt_trace_i=1;mt_trace_i<512;mt_trace_i++) \
			{ \
				if(watch_tmp[mt_trace_i]==0) \
				{ \
					if(watch_tmp[mt_trace_i-1]=='\n') watch_tmp[mt_trace_i-1]=' '; \
					else break; \
				} \
			} \
			rtlglue_printf("\033[1;%d;%dm[%s] %s \033[1;30;40m@%s(%d)\033[0m\n",color,bgcolor,string,watch_tmp,__FUNCTION__,__LINE__); \
} while(0);
#endif

//#include "romeDriver/rtk_rg_struct.h"
//#include "romeDriver/rtk_rg_fwdEngine.h"
//#include "romeDriver/re8686_sim.h"
#endif

#include "version.h"
#include <net/rtl/rtl_types.h>
#include <net/rtl/rtl_glue.h>

#include "AsicDriver/asicRegs.h"
#include "AsicDriver/rtl865x_asicCom.h"
#include "AsicDriver/rtl865x_asicL2.h"
#ifdef CONFIG_RTL_LAYERED_ASIC_DRIVER_L3
#include "AsicDriver/rtl865x_asicL3.h"
#endif

#include "common/mbuf.h"
#include <net/rtl/rtl_queue.h>
#include "common/rtl_errno.h"
#include "rtl865xc_swNic.h"

/*common*/
#include "common/rtl865x_vlan.h"
#include <net/rtl/rtl865x_netif.h>
#include "common/rtl865x_netif_local.h"

/*l2*/
#if defined(CONFIG_RTL_LAYERED_DRIVER_L2) || defined(CONFIG_RTL_MULTI_LAN_DEV)
#include "l2Driver/rtl865x_fdb.h"
#else
#include <net/rtl/rtl865x_fdb_api.h>
#endif

/*l3*/
#ifdef CONFIG_RTL_LAYERED_DRIVER_L3
#include "l3Driver/rtl865x_ip.h"
#include "l3Driver/rtl865x_nexthop.h"
#include <net/rtl/rtl865x_ppp.h>
#include "l3Driver/rtl865x_ppp_local.h"
#include "l3Driver/rtl865x_route.h"
#include "l3Driver/rtl865x_arp.h"
#include <net/rtl/rtl865x_nat.h>
#endif

/*l4*/
#ifdef	CONFIG_RTL865X_ROMEPERF
#include "romeperf.h"
#endif
#include <net/rtl/rtl_nic.h>

#if defined(CONFIG_PROC_FS) && defined(CONFIG_NET_SCHED) && defined(CONFIG_RTL_LAYERED_DRIVER)
#if defined(CONFIG_RTL_HW_QOS_SUPPORT)
#include <net/rtl/rtl865x_outputQueue.h>
#endif
#endif

#ifdef CONFIG_RTL_STP
#include "l2Driver/rtl865x_stp.h"
#endif

#if defined(CONFIG_RTL_HW_STP)
#include <net/rtl/rtk_stp.h>
#endif
#include <linux/if_smux.h>
#ifdef CONFIG_RTL_MULTI_ETH_WAN

//#if defined(CONFIG_RTL8676_Dynamic_ACL)
//#include <linux/netfilter_ipv4/ip_tables.h>	
//#endif
#endif

#ifdef CONFIG_RTL867X_VLAN_MAPPING
#include "../re_vlan.h"
#endif

extern int DumpSwNicTxRx_debug;	
extern int DumpSwNicTxRx_debug_LIMIT;
extern int vlan_passthru_enable;

#ifdef CONFIG_RTL_XDSL_WIFI_HWACCELERATION
int rtk_xdsl_wlan_hwnat_enable = 1;
int RTK_XDSL_SMP_WIFI_IPI_SMD_ID = 2;

/* WiFi Tx */
#define RTK_XDSL_SMP_WLANTX_WORK_SIZE	2048
#define RTK_XDSL_SMP_WLANRX_WORK_SIZE	2048
int rtk_xdsl_smp_wlanTx_ipi_enable = 1;
int rtk_xdsl_smp_wlanRx_ipi_enable = 1;
#ifdef CONFIG_RTL_XDSL_WIFI_IPI
atomic_t wlanRx_csd_available = ATOMIC_INIT(0);
atomic_t wlanTx_csd_available = ATOMIC_INIT(0);
rtk_xdsl_smp_wlan_ctrl_t *smpWlanTxCtrl;
spinlock_t rtk_xdsl_smp_wlanTx_lock;
struct list_head rtk_xdsl_smp_wlanTx_free_listHead;
struct list_head rtk_xdsl_smp_wlanTx_used_listHead;
rtk_xdsl_smp_wlan_work_t rtk_xdsl_smp_wlanTx_work[RTK_XDSL_SMP_WLANTX_WORK_SIZE];

/* WiFi Rx */
rtk_xdsl_smp_wlan_ctrl_t *smpWlanRxCtrl;
spinlock_t rtk_xdsl_smp_wlanRx_lock;
struct list_head rtk_xdsl_smp_wlanRx_free_listHead;
struct list_head rtk_xdsl_smp_wlanRx_used_listHead;
rtk_xdsl_smp_wlan_work_t rtk_xdsl_smp_wlanRx_work[RTK_XDSL_SMP_WLANRX_WORK_SIZE];
#endif

extern int32 rtl_xdsl_getAsicNaptIndex(ipaddr_t sip, uint16 sport,ipaddr_t dip, uint16 dport,uint8 protocol,uint8 isUpstream);
extern struct Path_List_Entry * FastPath_Lookup(unsigned int sip, unsigned short sport, unsigned int dip, unsigned short dport, unsigned short protocol);
extern int32 wifi_send_func(void *skb);
__IRAM_FWD
static int wifi_receive_func(struct sk_buff *skb)
{
	skb_push(skb,ETH_HLEN);

#if defined(CONFIG_RTL_SW_FDB_LEARNING)
	if(rtk_xdsl_wlan_hwnat_enable)
	{
		if(!strcmp(skb->dev->name,"wlan0"))
			rtl865x_smac_learning((void *)(skb->data+(ETH_ALEN)),skb->dev,RTL_LANVLANID,RTL_EXTPORT_P1);
		else if(!strcmp(skb->dev->name,"wlan1"))
			rtl865x_smac_learning((void *)(skb->data+(ETH_ALEN)),skb->dev,RTL_LANVLANID,RTL_EXTPORT_P2);
		else
			rtl865x_smac_learning((void *)(skb->data+(ETH_ALEN)),skb->dev,RTL_LANVLANID,RTL_EXTPORT_P3);
	}
#endif

	wifi_send_func((void *)skb);
	return 0;
}

#ifdef CONFIG_RTL_XDSL_WIFI_IPI
__IRAM_FWD
int rtk_xdsl_smp_wlan_rx_dispatch(struct sk_buff *skb)
{
	rtk_xdsl_smp_wlan_work_t *work = NULL;
	unsigned long flags;

	spin_lock_irqsave(&rtk_xdsl_smp_wlanRx_lock, flags);

	work = list_first_entry_or_null(&rtk_xdsl_smp_wlanRx_free_listHead, rtk_xdsl_smp_wlan_work_t, wifi_list);
	if(work == NULL)
	{
		//No avail wifi rx work
		printk("No available rx work queue. skip IPI... (%s %d)\n",__func__,__LINE__);
		dev_kfree_skb_any(skb);
		spin_unlock_irqrestore(&rtk_xdsl_smp_wlanRx_lock, flags);
		return 0;
	}
	else
	{
		list_del(&work->wifi_list);
		work->skb = skb;
		list_add_tail(&work->wifi_list,&rtk_xdsl_smp_wlanRx_used_listHead);
	}

	if(cpu_online(RTK_XDSL_SMP_WIFI_IPI_SMD_ID) && atomic_dec_and_test(&wlanRx_csd_available))
	{
		if(smp_call_function_single_async(RTK_XDSL_SMP_WIFI_IPI_SMD_ID,&smpWlanRxCtrl->rtk_xdsl_wifi_csd))
			printk("smp func not ready!\n");
	}

	spin_unlock_irqrestore(&rtk_xdsl_smp_wlanRx_lock, flags);
	return 1;
}
__IRAM_FWD
int rtk_xdsl_smp_wlan_tx_dispatch(struct sk_buff *skb)
{
	rtk_xdsl_smp_wlan_work_t *work = NULL;
	unsigned long flags;

	spin_lock_irqsave(&rtk_xdsl_smp_wlanTx_lock, flags);
	work = list_first_entry_or_null(&rtk_xdsl_smp_wlanTx_free_listHead, rtk_xdsl_smp_wlan_work_t, wifi_list);
	if(work == NULL)
	{
		//No avail wifi tx work
		dev_kfree_skb_any(skb);
		spin_unlock_irqrestore(&rtk_xdsl_smp_wlanTx_lock, flags);
		return 0;
	}
	else
	{
		list_del(&work->wifi_list);
		work->skb = skb;
		list_add_tail(&work->wifi_list,&rtk_xdsl_smp_wlanTx_used_listHead);
	}

	if(cpu_online(RTK_XDSL_SMP_WIFI_IPI_SMD_ID) && atomic_dec_and_test(&wlanTx_csd_available))
	{
		if(smp_call_function_single_async(RTK_XDSL_SMP_WIFI_IPI_SMD_ID,&smpWlanTxCtrl->rtk_xdsl_wifi_csd))
			printk("smp func not ready!\n");
	}
	spin_unlock_irqrestore(&rtk_xdsl_smp_wlanTx_lock, flags);

	return 0;
}

__IRAM_FWD
void rtk_xdsl_smp_wifi_rx_tasklet(void *info)
{
	rtk_xdsl_smp_wlan_ctrl_t *smpWlanRxCtrl = info;
	tasklet_hi_schedule(&smpWlanRxCtrl->wifi_ipi_tasklet);
	return;
}
__IRAM_FWD
void rtk_xdsl_smp_wifi_rx_cb(unsigned long data)
{
	int cnt = RTK_XDSL_SMP_WLANRX_WORK_SIZE;
	unsigned long flags;
	struct sk_buff *skb;
	rtk_xdsl_smp_wlan_work_t *work;

	spin_lock_irqsave(&rtk_xdsl_smp_wlanRx_lock, flags);
	while(cnt--)
	{
		work = list_first_entry_or_null(&rtk_xdsl_smp_wlanRx_used_listHead,rtk_xdsl_smp_wlan_work_t,wifi_list);
		if(work == NULL)
		{
			break;
		}

		skb = work->skb;
		if(skb)
		{
			wifi_receive_func(skb);
		}

		work->skb = NULL;
		list_del(&work->wifi_list);
		list_add_tail(&work->wifi_list,&rtk_xdsl_smp_wlanRx_free_listHead);
	}

	atomic_set(&wlanRx_csd_available,1);
	spin_unlock_irqrestore(&rtk_xdsl_smp_wlanRx_lock, flags);
	return;

}

__IRAM_FWD
void rtk_xdsl_smp_wifi_tx_tasklet(void *info)
{
	rtk_xdsl_smp_wlan_ctrl_t *smpWlanTxCtrl = info;
	tasklet_hi_schedule(&smpWlanTxCtrl->wifi_ipi_tasklet);
	return;
}
__IRAM_FWD
void rtk_xdsl_smp_wifi_tx_cb(unsigned long data)
{
	int cnt = RTK_XDSL_SMP_WLANTX_WORK_SIZE;
	unsigned long flags;
	struct sk_buff *skb;
	rtk_xdsl_smp_wlan_work_t *work;

	spin_lock_irqsave(&rtk_xdsl_smp_wlanTx_lock, flags);
	while(cnt--)
	{
		struct net_device *dev = NULL;
		work = list_first_entry_or_null(&rtk_xdsl_smp_wlanTx_used_listHead,rtk_xdsl_smp_wlan_work_t,wifi_list);
		if(work == NULL)
			break;

		skb = work->skb;
		if(skb)
			dev = work->skb->dev;

		list_del(&work->wifi_list);
		list_add_tail(&work->wifi_list,&rtk_xdsl_smp_wlanTx_free_listHead);

		if(dev)
			dev->netdev_ops->ndo_start_xmit(skb,dev);
	}

	spin_unlock_irqrestore(&rtk_xdsl_smp_wlanTx_lock, flags);

	atomic_set(&wlanTx_csd_available,1);
	return;

}
#endif
#endif

#ifdef CONFIG_RTL_XDSL_WIFI_HWACCELERATION
__IRAM_FWD
int rtk_xdsl_process_wifi_rx(struct sk_buff *skb)
{
	int ret = 0;
	
	if(!rtk_xdsl_wlan_hwnat_enable)
		return -1;

	if(unlikely(strncmp(skb->dev->name,"wlan",4)))
		return -1;

#if defined(CONFIG_RTL_XDSL_WIFI_IPI)
	if(rtk_xdsl_smp_wlanRx_ipi_enable)
		rtk_xdsl_smp_wlan_rx_dispatch(skb);
	else
#endif
		ret = wifi_receive_func(skb);

	return ret;
}
#endif

#if defined(CONFIG_ETHWAN_MODE_SWITCH)
int ethwan_mode=1;
int ethwan_port=4;
struct device_member {
	struct net_device	*dev;
	uint32			memPort;
};
struct device_member itf_default_mbr[NETDEVICE_MAXNUM];
static int do_etherwan_mode(void);
#endif

#if defined(CONFIG_RTL_SWITCH_NEW_DESCRIPTOR)
#include "rtl819x_swNic.h"
#endif

#define NIC_TX_THRESHOLD	8
#if defined (CONFIG_RTL_IGMP_SNOOPING)
#include <net/rtl/rtl865x_igmpsnooping.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#if defined (CONFIG_RTL_MLD_SNOOPING)
#include <linux/ipv6.h>

#if defined(CONFIG_XDSL_CTRL_PHY)
#include <linux/if_ether.h>
#endif
//int mldSnoopEnabled;
extern int mldSnoopEnabled;
#endif
uint32 nicIgmpModuleIndex=0xFFFFFFFF;
extern int  igmpsnoopenabled;
extern uint32 brIgmpModuleIndex;
#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
extern struct net_bridge *bridge0;
extern uint32 br0SwFwdPortMask;
#endif
#endif

static unsigned int curLinkPortMask=0;
static unsigned int newLinkPortMask=0;
extern int32 global_probe_extPhy;

#define SET_MODULE_OWNER(dev) do { } while (0)

#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
#include <net/rtl/rtl865x_multicast.h>
#endif

#ifdef CONFIG_RTL8196_RTL8366
#include "RTL8366RB_DRIVER/gpio.h"
#include "RTL8366RB_DRIVER/rtl8366rb_apiBasic.h"
#endif

#if (defined(CONFIG_RTL_CUSTOM_PASSTHRU) && !defined(CONFIG_RTL8196_RTL8366)) 
static int oldStatus = 0;
static struct proc_dir_entry *res=NULL;
static char passThru_flag[1];
#endif

#if (defined(CONFIG_RTL_8198))
static struct proc_dir_entry *phyTest_entry=NULL;
#endif

#if defined(CONFIG_RTL_ETH_PRIV_SKB) && (defined(CONFIG_NET_WIRELESS_AGN) || defined(CONFIG_NET_WIRELESS_AG) || defined(CONFIG_WIRELESS))
#include <net/dst.h>
#endif

#if 0
#define DEBUG_ERR printk
#else
#define DEBUG_ERR(format, args...)
#endif

#if defined (CONFIG_RTL_LOCAL_PUBLIC)
#include <net/rtl/rtl865x_localPublic.h>
#endif

#include "../ethctl_implement.h"

#include "rtl.h"

/*Port0 uses RGMII interface to connect to external CPU's MAC*/
//#define PORT0_USE_RGMII_TO_EXTCPU_MAC

#define DBG_VlanConfig
#ifdef DBG_VlanConfig
#define DBG_VlanConfig_PRK( comment ,arg...) DBG_PRK(RTL_DEBUG_LEVEL_VLAN,"[VLAN]",31,40,comment,##arg)
#else
#define DBG_VlanConfig_PRK(format, args...)
#endif

#define DBG_MULTIWAN_API
#ifdef DBG_MULTIWAN_API
#define DBG_MULTIWAN_API_PRK( comment ,arg...) DBG_PRK(RTL_DEBUG_LEVEL_MULTIWAN,"[MULTIWAN]",32,40,comment,##arg)
#else
#define DBG_MULTIWAN_API_PRK(format, args...)
#endif

extern int RTL_HWSTATS_TXPKTS_OVERFLOW[];
extern int RTL_HWSTATS_RXPKTS_OVERFLOW[];
extern int RTL_HWSTATS_TXPKTS[];
extern int RTL_HWSTATS_RXPKTS[];
extern int RTL_HWSTATS_CPU_TXPKTS_OVERFLOW;
extern int RTL_HWSTATS_CPU_RXPKTS_OVERFLOW;
extern int RTL_HWSTATS_CPU_TXPKTS;
extern int RTL_HWSTATS_CPU_RXPKTS;

#if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION)
struct net_device *rtk_xdsl_hwnat_wifiDev[2];
struct dev_priv rtk_xdsl_hwnat_wifiPriv;
struct dev_priv *wifi_priv = &rtk_xdsl_hwnat_wifiPriv;
extern struct list_head rtk_xdsl_hwnat_mbssid_mac_mappingListHead[RTK_XDSL_HWNAT_MBSSID_MAC_MAPPING_BUCKET_SIZE];
#endif

#if defined(CONFIG_RTK_8023AH)
static char efm_8023ah_par_action;
char efm_ifname[IFNAMSIZ];

typedef enum{
	FORWARDING,
	LOOPBACK,
	DROP
}parser_action_type;

struct efm_loopback_stats {
	unsigned long	rx_frames;
	unsigned long	tx_frames;
	unsigned long	rx_octets;
	unsigned long	tx_octets;	
	unsigned long	rx_multicast_frames;
	unsigned long	tx_multicast_frames;
	unsigned long	rx_broadcast_frames;
	unsigned long	tx_broadcast_frames;
	unsigned long	frame_check_seq_errors;
	unsigned long	frames_too_long;
	unsigned long	runts;
};

static struct efm_loopback_stats efm_stats;

int efm_stats_rx(struct sk_buff *skb) {
	//printk("RX:skb->len=%d\n", skb->len);
	efm_stats.rx_frames++;	
	efm_stats.rx_octets += skb->len;	
	if (is_multicast_ether_addr(skb->data))
		efm_stats.rx_multicast_frames++;

	if (is_broadcast_ether_addr(skb->data))
		efm_stats.rx_broadcast_frames++;
	
	return 0;
}

int efm_stats_tx(struct sk_buff *skb) {
	//printk("TX:skb->len=%d\n", skb->len);	
	efm_stats.tx_frames++;	
	efm_stats.tx_octets += skb->len;
	if (is_multicast_ether_addr(skb->data))		
		efm_stats.tx_multicast_frames++;
	
	if (is_broadcast_ether_addr(skb->data))
		efm_stats.tx_broadcast_frames++;
	
	efm_stats.frame_check_seq_errors =0;
	efm_stats.frames_too_long = 0;
	efm_stats.runts = 0;
	return 0;
}
#endif

static int32 __865X_Config;
#if defined(DYNAMIC_ADJUST_TASKLET) || defined(BR_SHORTCUT)
#if 0
static int eth_flag=12; // 0 dynamic tasklet, 1 - disable tasklet, 2 - always tasklet , bit2 - bridge shortcut enabled
#endif
#endif
#if defined(BR_SHORTCUT)
__DRAM_FWD  unsigned char cached_eth_addr[ETHER_ADDR_LEN];
EXPORT_SYMBOL(cached_eth_addr);
__DRAM_FWD  struct net_device *cached_dev;
EXPORT_SYMBOL(cached_dev);
#if defined(CONFIG_WIRELESS_LAN_MODULE)
struct net_device* (*wirelessnet_hook_shortcut)(unsigned char *da) = NULL;
EXPORT_SYMBOL(wirelessnet_hook_shortcut);
#endif //CONFIG_WIRELESS_LAN_MODULE
#endif

#if defined(CONFIG_WIRELESS_LAN_MODULE)
int (*wirelessnet_hook)(void) = NULL;
EXPORT_SYMBOL(wirelessnet_hook);
#endif //CONFIG_WIRELESS_LAN_MODULE

//add poll support when dsl negotiation
#if defined(RX_TASKLET)
struct tasklet_struct *eth_rx_tasklets=NULL;
#endif
int eth_poll; // poll mode flag
//end

#if defined (CONFIG_RTL_UNKOWN_UNICAST_CONTROL)
static rtlMacRecord	macRecord[RTL_MAC_RECORD_NUM];
static uint32	macRecordIdx;
static uint8	lanIfName[NETIF_NUMBER];
static void	rtl_unkownUnicastUpdate(uint8 *mac);
static void rtl_unkownUnicastTimer(unsigned long data);
#endif

#if defined(DYNAMIC_ADJUST_TASKLET) || defined(CONFIG_RTL8186_TR) || defined(RTL8196C_EEE_MAC) || defined(CONFIG_RTL_8198_ESD)
#ifndef CONFIG_RTL8676S
static void one_sec_timer(unsigned long task_priv);
#endif
#endif

//#define CONFIG_RTL_LINKSTATE
#if defined(CONFIG_RTL_LINKSTATE)
static struct timer_list s_timer;
static void linkup_time_handle(unsigned long arg);
static int32 initPortStateCtrl(void);
static void  exitPortStateCtrl(void);
#endif
#if defined(CONFIG_RTL_ETH_PRIV_SKB)
static struct sk_buff *dev_alloc_skb_priv_eth(unsigned int size);
#endif
#if defined(CONFIG_RTL_ETH_PRIV_SKB) || defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
static void init_priv_eth_skb_buf(void);
#endif

#if !defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
__DRAM_FWD static struct ring_que	rx_skb_queue;
int skb_num=0;
#endif//end of !CONFIG_RTL_ETH_PRIV_SKB_ADV

int32 rtl865x_init(void);
int32 rtl865x_config(struct rtl865x_vlanConfig vlanconfig[]);

#ifdef CONFIG_PORT_MIRROR
static inline void switchcore_mirror_pkt(struct sk_buff *skb, const int flag);
#define IN  0x1
#define OUT 0x2
#endif

/* These identify the driver base version and may not be removed. */
MODULE_DESCRIPTION("RealTek RTL-8650 series 10/100 Ethernet driver");
MODULE_LICENSE("GPL");

#if !defined(PORT0_USE_RGMII_TO_EXTCPU_MAC) && defined(CONFIG_RTL8676)
#define RTL867X_PORT0_PATCH
#endif /*PORT0_USE_RGMII_TO_EXTCPU_MAC*/
#ifdef RTL867X_PORT0_PATCH
//#define RTL867X_PORT0_PATCH_BACK_TO_HALF
#ifdef RTL867X_PORT0_PATCH_BACK_TO_HALF
typedef enum{
	NOT_TRIGGERED,
	FORCE_FULL,
	FORCE_HALF,
	ALREADY_FORCE_HALF
}PORT0_STATE;

PORT0_STATE g_port0_state = NOT_TRIGGERED;

void force_full_process(void){
	#define FRAG_SEC_THR 3
	extern int32 rtl8651_returnAsicCounter(uint32 offset); 
	static unsigned int continuous_frag_sec = 0;
	static int pre_frag_cnt = 0;
	int frag_cnt;
		frag_cnt = rtl8651_returnAsicCounter( OFFSET_ETHERSTATSFRAGMEMTS_P0);
		if(continuous_frag_sec > FRAG_SEC_THR){
			printk("go to force_half state\n");
			g_port0_state = FORCE_HALF;
			continuous_frag_sec = 0;
			pre_frag_cnt = 0;
		}
		else{
			if(frag_cnt > pre_frag_cnt){
				//printk("add frag sec %u, %d, %d, %lu\n", continuous_frag_sec, pre_frag_cnt, frag_cnt, jiffies);
				continuous_frag_sec++;
				pre_frag_cnt = frag_cnt;
			}
			else{
				//printk("reset force_full state %u, %d, %d, %lu\n", continuous_frag_sec, pre_frag_cnt, frag_cnt, jiffies);
				continuous_frag_sec = 0;
				pre_frag_cnt = 0;
				//printk("reset force_full state2 %u, %d, %d\n", continuous_frag_sec, pre_frag_cnt, frag_cnt);
			}
		}
}

void force_half_process(void){
	unsigned int regData;
	printk("force_half state\n");

	if(REG32(PCRP0) & (EnablePHYIf)){
		printk("disable port0 phy\n");
		/*	disable phy */
		WRITE_MEM32(PCRP0, ((READ_MEM32(PCRP0))&(~EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP1, ((READ_MEM32(PCRP1))&(~EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP2, ((READ_MEM32(PCRP2))&(~EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP3, ((READ_MEM32(PCRP3))&(~EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP4, ((READ_MEM32(PCRP4))&(~EnablePHYIf)) ); /* Jumbo Frame */
		if (rtl8651_tblAsicDrvPara.externalPHYProperty & RTL8651_TBLASIC_EXTPHYPROPERTY_PORT5_RTL8211B)
		{
			WRITE_MEM32(PCRP5, ((READ_MEM32(PCRP5))&(~EnablePHYIf)) ); /* Jumbo Frame */
		}
		return;
	}

	if(REG32(P0DCR0) == 0 && REG32(P0DCR1) == 0 && REG32(P0DCR2) == 0){
		printk("port0 goes to half duplex\n");
		regData = REG32(PCRP0);
		regData &= ~(ForceDuplex);
		REG32(PCRP0) = regData;
	}

	if((REG32(PSRP0)&(PortStatusDuplex)) == 0){
		printk("go to ALREADY_FORCE_HALF state\n");
		/*	enable phy */
		WRITE_MEM32(PCRP0, ((READ_MEM32(PCRP0))|(EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP1, ((READ_MEM32(PCRP1))|(EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP2, ((READ_MEM32(PCRP2))|(EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP3, ((READ_MEM32(PCRP3))|(EnablePHYIf)) ); /* Jumbo Frame */
		WRITE_MEM32(PCRP4, ((READ_MEM32(PCRP4))|(EnablePHYIf)) ); /* Jumbo Frame */
		if (rtl8651_tblAsicDrvPara.externalPHYProperty & RTL8651_TBLASIC_EXTPHYPROPERTY_PORT5_RTL8211B)
		{
			WRITE_MEM32(PCRP5, ((READ_MEM32(PCRP5))|(EnablePHYIf)) ); /* Jumbo Frame */
		}
		g_port0_state = ALREADY_FORCE_HALF;
	}
}

void port0_patch_state_mechine(void){
	#define POLLING_SEC 2*HZ /*2s*/
	static unsigned long pre_jiffies = 0;
	/*although we can use counter to do this, i use jiffies to 
	create a 2 seconds timer to prevent someone making the timer
	less than 1 second.*/
	if((jiffies - pre_jiffies) >= POLLING_SEC){
		//printk("%s %d\n", __func__, __LINE__);
		switch(g_port0_state){
			case NOT_TRIGGERED:
			case ALREADY_FORCE_HALF:
				break;
			case FORCE_FULL:
				/*calculate counter and deside whether go to FORCE_HALF or not*/
				force_full_process();
				break;
			case FORCE_HALF:
				/*do what FORCE_HALF need to do*/
				force_half_process();
				break;
		}
		pre_jiffies = jiffies;
	}
}
#endif
/*
krammer add this according to " Jim Hsieh/5458 "'s algorithm
Description:
when phy0's link partner link in force mode, we will force to half duplex mode,
in this condition, if there is some traffic in port0's rx, sometimes port0 will die.
so we add this patch, only work when phy0's link state change, if link partner
is link in force mode, we will change our port0 into full duplex.
*/
void port0_force_patch(void){
	unsigned int regData;
	
	if((curLinkPortMask & 0x01) == (newLinkPortMask & 0x01)){
		//no change -> return
		return;
	}
	
	//default disable phy i/f(0xbb804104 b0=0)
	REG32(PCRP0) &= ~(EnablePHYIf);

	//if port#0=link up
	if(newLinkPortMask & 0x01){
		//if AN =0(reg6[0])
		rtl8651_getAsicEthernetPHYReg(rtl8651_tblAsicDrvPara.externalPHYId[0], 6, &regData);
		if((regData & 0x01) == 0){
			rtl8651_getAsicEthernetPHYReg(rtl8651_tblAsicDrvPara.externalPHYId[0], 5, &regData);
			if(regData & 0x080){//if spd = 100M(reg5[7])
				//force MAC 100M Full duplex
				//(set 0xbb804104 b25=1 b24=1 b23=0 b20-19=1 b18=1 b17-16=0x3)
				regData = REG32(PCRP0);
				regData |= (EnForceMode | PollLinkStatus | ForceDuplex | PauseFlowControlEtxErx);
				regData &= ~(ForceLink | ForceSpeedMask);
				regData |= (ForceSpeed100M);
				printk("phy0 link partner is force 100M, we force to 100M Full duplex!regData = 0x%x\n", regData);
				REG32(PCRP0) = regData;
			}
			else if(regData & 0x20){//else spd = 10M(reg5[5])
				//force MAC 10M Full duplex
				//(set 0xbb804104 b25=1 b24=1 b23=0 b20-19=0 b18=1 b17-16=0x3)
				regData = REG32(PCRP0);
				regData |= (EnForceMode | PollLinkStatus | ForceDuplex | PauseFlowControlEtxErx);
				regData &= ~(ForceLink | ForceSpeedMask);
				regData |= (ForceSpeed10M);
				printk("phy0 link partner is force 10M, we force to 10M Full duplex!regData = 0x%x\n", regData);
				REG32(PCRP0) = regData;
			}
			#ifdef RTL867X_PORT0_PATCH_BACK_TO_HALF
			printk("go to force_full state\n");
			g_port0_state = FORCE_FULL;
			#endif
		}
		//enable phy i/f
		//set 0xbb804104 b0=1
		REG32(PCRP0) |= EnablePHYIf;
	}
	else{//if port#0=link down
		//recovery MAC AN setting and disable phy i/f
		//(set 0xbb804104 b25=0 b20-18 0x7 b0=0)
		regData = REG32(PCRP0);
		regData &= ~(EnForceMode | EnablePHYIf);
		regData |= (ForceSpeed1000M | ForceDuplex);
		printk("phy0 link down, we reset phy0!regData = 0x%x\n", regData);
		REG32(PCRP0) = regData;
		#ifdef RTL867X_PORT0_PATCH_BACK_TO_HALF
		g_port0_state = NOT_TRIGGERED;
		#endif
	}
}
#endif

/* Move heavy-weight process here */
#include <linux/kthread.h>
static struct task_struct *nic_event_task;
static DECLARE_WAIT_QUEUE_HEAD(nic_wait);
static u32 event_bits;
#define NIC_EVENT_LINKCHANGE (1<<0) 
static void nic_event(int event) {
	unsigned long flags;
	local_irq_save(flags);
	event_bits |= event;
	wake_up(&nic_wait);
	local_irq_restore(flags);	
}
static int nic_thread(void *__unused)
{
	u32 events;
	unsigned long flags;
	do {
		
		wait_event_interruptible(nic_wait,
				(event_bits != 0) ||
				kthread_should_stop());
		local_irq_save(flags);
		events = event_bits;
		event_bits = 0;
		local_irq_restore(flags);
		
		if (events&NIC_EVENT_LINKCHANGE) {
			#if defined(CONFIG_RTL_LINKCHG_PROCESS) 
			#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER)
			xdslRomeDriver_LinkChange_Process();
			#else
			rtl865x_LinkChange_Process();				
			#endif
			#endif
			#ifdef RTL867X_PORT0_PATCH					
			port0_force_patch();
			#endif			
			curLinkPortMask=newLinkPortMask;
		}
	} while (!kthread_should_stop() || event_bits);

	printk("nic_thread exit!\n");
	return 0;
}

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
static char* multicast_filter_limit = "maximum number of filtered multicast addresses";
module_param (multicast_filter_limit,charp, S_IRUGO);
#else
/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast).
   The RTL chips use a 64 element hash table based on the Ethernet CRC.  */
MODULE_PARM (multicast_filter_limit, "i");
MODULE_PARM_DESC (multicast_filter_limit, "maximum number of filtered multicast addresses");
#endif

#define DRV_NAME		"re865x_nic"
#define PFX			DRV_NAME ": "
#define DRV_VERSION		"0.1"
#define TX_TIMEOUT		(10*HZ)
#define BDINFO_ADDR 0xbe3fc000

#if defined (CONFIG_RTL_PHY_POWER_CTRL)
static int32 rtl865x_initPhyPowerCtrl(void);
#endif

__DRAM_GEN static atomic_t rtl_devOpened;


	

//#define NEXT_DEV(cp)			(cp->dev_next ? cp->dev_next : cp->dev_prev)
//#define NEXT_CP(cp)			((struct dev_priv *)((NEXT_DEV(cp))->priv))
//#define IS_FIRST_DEV(cp)	(NEXT_CP(cp)->opened ? 0 : 1)
//#define GET_IRQ_OWNER(cp) (cp->irq_owner ? cp->dev : NEXT_DEV(cp))

#define MAX_PORT_NUM 9

static unsigned int rxRingSize[RTL865X_SWNIC_RXRING_HW_PKTDESC] = 
	{NUM_RX_PKTHDR_DESC, 
	NUM_RX_PKTHDR_DESC1, 
	NUM_RX_PKTHDR_DESC2, 
	NUM_RX_PKTHDR_DESC3, 
	NUM_RX_PKTHDR_DESC4,
	NUM_RX_PKTHDR_DESC5};

#ifdef CONFIG_RTL_8196D
static unsigned int txRingSize[RTL865X_SWNIC_TXRING_HW_PKTDESC] = 
	{NUM_TX_PKTHDR_DESC, 
	NUM_TX_PKTHDR_DESC1,
	NUM_TX_PKTHDR_DESC2,
	NUM_TX_PKTHDR_DESC3
	};
#else
static unsigned int txRingSize[RTL865X_SWNIC_TXRING_HW_PKTDESC] = 
	{NUM_TX_PKTHDR_DESC, 
	NUM_TX_PKTHDR_DESC1};
#endif

#define VCONFIG_LAN_START		0
#ifdef CONFIG_ETHWAN_MODE_SWITCH
#define VCONFIG_LAN_END			4
#else
#define VCONFIG_LAN_END			3
#endif
//#if defined (CONFIG_RTL_MULTI_LAN_DEV)||defined(CONFIG_RTK_VLAN_SUPPORT)
static  struct rtl865x_vlanConfig packedVlanConfig[NETIF_SW_NUMBER];
//#endif

/*
linux protocol stack netif VS rtl819x driver network interface
the name of ps netif maybe different with driver.
*/
static ps_drv_netif_mapping_t ps_drv_netif_mapping[NETIF_SW_NUMBER];

static unsigned int pvid_per_port[RTL8651_PORT_NUMBER+3]={[0 ... RTL8651_PORT_NUMBER+2]=RTL_LANVLANID};

static struct rtl865x_vlanConfig vlanconfig[] = {
/*      	ifName  W/L      If type		VID	 FID	   Member Port	UntagSet		mtu		MAC Addr	is_slave								*/
/*		=====  ===   =======	===	 ===   =========   =======	====	====================================	*/

/* LAN interface */
#ifdef CONFIG_RTL_MULTI_LAN_DEV
	{ RTL_DRV_LAN_NETIF_NAME,		0,	IF_ETHER,	RTL_LANVLANID,		RTL_LAN_FID,		RTL_LANPORT_MASK_1_DEFAULT,		RTL_LANPORT_MASK_1_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0	},
	{ RTL_DRV_LAN_NETIF_NAME,		0,	IF_ETHER,	RTL_LANVLANID,		RTL_LAN_FID,		RTL_LANPORT_MASK_2_DEFAULT,		RTL_LANPORT_MASK_2_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0	},
	{ RTL_DRV_LAN_NETIF_NAME,		0,	IF_ETHER,	RTL_LANVLANID,		RTL_LAN_FID,		RTL_LANPORT_MASK_3_DEFAULT,		RTL_LANPORT_MASK_3_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0	},
  	{ RTL_DRV_LAN_NETIF_NAME,		0,	IF_ETHER,	RTL_LANVLANID,		RTL_LAN_FID, 	RTL_LANPORT_MASK_4_DEFAULT,		RTL_LANPORT_MASK_4_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0	},
#if defined(CONFIG_ETHWAN_MODE_SWITCH)
	{ RTL_DRV_LAN_NETIF_NAME,		0,	IF_ETHER,	RTL_LANVLANID,		RTL_LAN_FID, 	    RTL_LANPORT_MASK_5_DEFAULT,		RTL_LANPORT_MASK_5_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0	},
#endif
#else
	{ RTL_DRV_LAN_NETIF_NAME,		0,	IF_ETHER,	RTL_LANVLANID,		RTL_LAN_FID, 	RTL_LANPORT_MASK_DEFAULT,		RTL_LANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0	},
#endif	

/* Multi-WAN interface (all vid = 0 by default, it means all multi-wan netif's "name" have not been used yet, "net_device" will be created in smux)
	When you create a new WAN connection , please call rtl_set_wanport_vlanconfig() (or rtl8676_register_Multiwan_dev)
	This function will help you handle the setting of Network interface table, VLAN table and PVID in hw.
*/
#ifdef CONFIG_RTL_MULTI_ETH_WAN
#ifdef CONFIG_ETHWAN
	{ RTL_DRV_ETHWAN0_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0 },
	{ RTL_DRV_ETHWAN1_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_ETHWAN2_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_ETHWAN3_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_ETHWAN4_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_ETHWAN5_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_ETHWAN6_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
#endif

#ifdef CONFIG_PTMWAN
	{ RTL_DRV_PTMWAN0_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0 },
	{ RTL_DRV_PTMWAN1_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN2_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN3_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN4_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN5_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN6_NETIF_NAME,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
#endif

#if defined(CONFIG_ETHWAN)
{ RTL_DRV_ETHWAN0_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0 },
{ RTL_DRV_ETHWAN1_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
{ RTL_DRV_ETHWAN2_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
{ RTL_DRV_ETHWAN3_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
{ RTL_DRV_ETHWAN4_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
{ RTL_DRV_ETHWAN5_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
{ RTL_DRV_ETHWAN6_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
#endif

#if defined(CONFIG_PTMWAN)
	{ RTL_DRV_PTMWAN0_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,		RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,		1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } }, 0, 0 },
	{ RTL_DRV_PTMWAN1_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN2_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN3_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN4_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN5_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
	{ RTL_DRV_PTMWAN6_NETIF_NAME_EXTEND,	1,	IF_ETHER,		0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500,	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 0, 0 },
#endif

/* PPP interface , please use rtl_add_ppp_netif() to create sw_netif (just data structure, not the actual net_device) in driver layer */
#if defined(CONFIG_ETHWAN) || defined(CONFIG_PTMWAN)
	{	RTL_DRV_PPP0_NETIF_NAME,		1,   IF_PPPOE,	0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500, 	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 1, 0 },
	{	RTL_DRV_PPP1_NETIF_NAME,		1,   IF_PPPOE,	0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500, 	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 1, 0 },
	{	RTL_DRV_PPP2_NETIF_NAME,		1,   IF_PPPOE,	0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500, 	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 1, 0 },
	{	RTL_DRV_PPP3_NETIF_NAME,		1,   IF_PPPOE,	0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500, 	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 1, 0 },
	{	RTL_DRV_PPP4_NETIF_NAME,		1,   IF_PPPOE,	0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500, 	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 1, 0 },
	{	RTL_DRV_PPP5_NETIF_NAME,		1,   IF_PPPOE,	0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500, 	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 1, 0 },
	{	RTL_DRV_PPP6_NETIF_NAME,		1,   IF_PPPOE,	0,				RTL_WAN_FID,    	RTL_WANPORT_MASK_DEFAULT,		RTL_WANPORT_MASK_DEFAULT,     	1500, 	{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x91 } }, 1, 0 },
#endif
#endif // of CONFIG_RTL_MULTI_ETH_WAN
	RTL865X_CONFIG_END,
};

#if (defined(CONFIG_PROC_FS) && defined(CONFIG_NET_SCHED) && defined(CONFIG_RTL_LAYERED_DRIVER))||(defined (CONFIG_RTL_HW_QOS_SUPPORT))
//static uint8	netIfName[NETIF_NUMBER][IFNAMSIZ] = {{0}};   /*  br0,nas0,ppp0 (not prototol stack netif name eth0.2 ......)*/
#endif

#if defined(CONFIG_RTL_ETH_PRIV_SKB)
/*	The following structure's field orders was arranged for special purpose, 
	it should NOT be modify	*/
struct priv_skb_buf2 {
	u32 magic;
	void			*buf_pointer;
	/* the below 2 filed MUST together */
	struct list_head	list;	
	unsigned char buf[ETH_SKB_BUF_SIZE];
};

static struct priv_skb_buf2 eth_skb_buf[MAX_ETH_SKB_NUM+1];
__DRAM_FWD static struct list_head eth_skbbuf_list;
__DRAM_FWD int eth_skb_free_num;
EXPORT_SYMBOL(eth_skb_free_num);
extern struct sk_buff *dev_alloc_8190_skb(unsigned char *data, int size);
#endif
#ifdef CONFIG_RTL_ETH_PRIV_SKB_ADV
void rtl865x_free_eth_priv_buf(struct sk_buff *skb, unsigned flag);

struct priv_skb_buf3 {
	struct priv_skb_buf3 *next;
	unsigned char buf[0];
};
static unsigned char eth_skb_hdr[(MAX_ETH_SKB_NUM * SKB_ALIGNED_SIZE) + 16];
static unsigned char eth_skb_buf[(MAX_ETH_SKB_NUM * ETH_SKB_BUF_SIZE) + SMP_CACHE_BYTES];
__DRAM_FWD static struct sk_buff * freeSkbList=NULL;
__DRAM_FWD static struct priv_skb_buf3 *freeBufList=NULL;

__DRAM_FWD int eth_skb_free_num;
EXPORT_SYMBOL(eth_skb_free_num);
#endif//end of CONFIG_RTL_ETH_PRIV_SKB_ADV

#ifdef RX_NAPI
static unsigned int napi_exist=0;
static struct napi_struct rtl_napi;

static struct timer_list txbuff_gc_timer;
#endif
static struct timer_list rtl_hwstats_timer;

#ifdef CONFIG_RTL_SERDES
static struct timer_list CheckSerdes_timer; 
#endif

#if 0
#ifdef CONFIG_POCKET_AP_SUPPORT
int rtl865x_curOpMode=BRIDGE_MODE;
int rtl865x_curOpMode_allLan = 0;
#else
#ifndef CONFIG_RTL_MULTI_ETH_WAN
int rtl865x_curOpMode=GATEWAY_MODE;
int rtl865x_curOpMode_allLan = 0;
#else
int rtl865x_curOpMode=BRIDGE_MODE;
int rtl865x_curOpMode_allLan = 1;
#endif
#endif
#endif

__DRAM_FWD static struct re865x_priv _rtl86xx_dev; 
__DRAM_FWD static  unsigned char portid_index[NETDEVICE_MAXNUM];
__DRAM_FWD static int _rtl86xx_dev_num=0;

#ifdef CONFIG_RTL_STP
static unsigned char STPmac[] = { 1, 0x80, 0xc2, 0,0,0};
#ifdef CONFIG_RTK_MESH
int8 STP_PortDev_Mapping[MAX_RE865X_STP_PORT] ={NO_MAPPING, NO_MAPPING, NO_MAPPING, NO_MAPPING, NO_MAPPING, WLAN_PSEUDO_IF_INDEX, WLAN_MESH_PSEUDO_IF_INDEX};
#else
int8 STP_PortDev_Mapping[MAX_RE865X_STP_PORT] ={NO_MAPPING, NO_MAPPING, NO_MAPPING, NO_MAPPING, NO_MAPPING, WLAN_PSEUDO_IF_INDEX};
#endif
static int re865x_stp_get_pseudodevno(uint32 port_num);
#endif

static int re865x_ioctl (struct net_device *dev, struct ifreq *rq, int cmd);
//static int32 reinit_vlan_configure(struct rtl865x_vlanConfig new_vlanconfig[]);
//static void rtl_print_vlanconfig(struct rtl865x_vlanConfig new_vlanconfig[]);
#if defined(CONFIG_RTK_VLAN_SUPPORT)
static int read_proc_vlan(char *page, char **start, off_t off,int count, int *eof, void *data);
static int write_proc_vlan(struct file *file, const char *buffer,unsigned long count, void *data);
static int32 rtk_vlan_support_read( char *page, char **start, off_t off, int count, int *eof, void *data );
static int32 rtk_vlan_support_write( struct file *filp, const char *buff,unsigned long len, void *data );	
//__DRAM_FWD int rtk_vlan_support_enable;
int rtk_vlan_support_enable;
EXPORT_SYMBOL(rtk_vlan_support_enable);
#endif

#if defined(RTL8196C_EEE_MAC)
extern int eee_enabled;
extern void eee_phy_enable(void);
extern void eee_phy_disable(void);
#ifdef CONFIG_RTL8196C_REVISION_B
#ifndef CONFIG_RTL8676S
static unsigned char prev_port_sts[MAX_PORT_NUMBER] = { 0, 0, 0, 0, 0 };
#endif
#endif
#endif
#if defined(CONFIG_RTL_LOCAL_PUBLIC)
static int32 rtl865x_getPortlistByMac(const unsigned char *mac,uint32 *portlist);
#endif
static inline int rtl_isWanDev(struct dev_priv *cp);

#ifdef CONFIG_RTL8196C_ETH_IOT
uint32 port_link_sts = 0;	// the port which already linked up does not need to check ...
uint32 port_linkpartner_eee = 0;
#endif

#ifdef CONFIG_RTL_8196C_ESD
int _96c_esd_counter = 0;
#endif

#if defined(PATCH_GPIO_FOR_LED)
#define MIB_RX_PKT_CNT	0
#define MIB_TX_PKT_CNT	1

#define PORT_PABCD_BASE	10	// Base of P0~P1 at PABCD
#define P0_PABCD_BIT		10
#define P1_PABCD_BIT		11
#define P2_PABCD_BIT		12
#define P3_PABCD_BIT		13
#define P4_PABCD_BIT		14

#define SUCCESS 0
#define FAILED -1

//#define GPIO_LED_MAX_PACKET_CNT	5000
//#define GPIO_LED_MAX_SCALE			100
#define GPIO_LED_NOBLINK_TIME		(12*HZ/10)	// time more than 1-sec timer interval
//#define GPIO_LED_INTERVAL_TIME		50	// 500ms
#define GPIO_LED_ON_TIME				(4*HZ/100)	// 40ms
#define GPIO_LED_ON					0
#define GPIO_LED_OFF					1
#define GPIO_LINK_STATUS			1
#define GPIO_LINK_STATE_CHANGE 0x80000000

#define GPIO_UINT32_DIFF(a, b)		((a >= b)? (a - b):(0xffffffff - b + a + 1))

struct ctrl_led {
	struct timer_list	LED_Timer;	
	unsigned int			LED_Interval;
	unsigned char		LED_Toggle;
	unsigned char		LED_ToggleStart;
	unsigned int			LED_tx_cnt_log;
	unsigned int			LED_rx_cnt_log;
	unsigned int			LED_tx_cnt;
	unsigned int			LED_rx_cnt;
	unsigned int			link_status;
	unsigned char			blink_once_done;		// 1: blink once done
} led_cb[5];

static int32 rtl819x_getAsicMibCounter(int port, uint32 counter, uint32 *value) 
{
	rtl865x_tblAsicDrv_simpleCounterParam_t simpleCounter;
	rtl8651_getSimpleAsicMIBCounter(port, &simpleCounter);

	switch(counter){
		case MIB_RX_PKT_CNT:
			*value=simpleCounter.rxPkts;
			break;
		case MIB_TX_PKT_CNT:
			*value=simpleCounter.txPkts;
			break;
		default:
			return FAILED;
	}
	return SUCCESS;
}
static void gpio_set_led(int port, int flag){	
	if (flag == GPIO_LED_OFF){
/*		RTL_W32(PABCD_CNR, RTL_R32(PABCD_CNR) & (~((0x1<<port)<<PORT_PABCD_BASE)));	//set GPIO pin
*		RTL_W32(PABCD_DIR, RTL_R32(PABCD_DIR) | ((0x1<<port)<<PORT_PABCD_BASE));//output pin
*/
		RTL_W32(PABCD_DAT, (RTL_R32(PABCD_DAT) | ((0x1<<port)<<PORT_PABCD_BASE)));//set 1
	}
	else{
/*		RTL_W32(PABCD_CNR, RTL_R32(PABCD_CNR) & (~((0x1<<port)<<PORT_PABCD_BASE)));	//set GPIO pin
*		RTL_W32(PABCD_DIR, RTL_R32(PABCD_DIR) | ((0x1<<port)<<PORT_PABCD_BASE));//output pin
*/
		RTL_W32(PABCD_DAT, (RTL_R32(PABCD_DAT) & (~((0x1<<port)<<PORT_PABCD_BASE))));//set 0
	}
}

static void gpio_led_Interval_timeout(unsigned long port)
{
	struct ctrl_led *cb	= &led_cb[port];
	unsigned long flags;

	local_irq_save(flags);

	if (cb->link_status & GPIO_LINK_STATE_CHANGE) {
		cb->link_status &= ~GPIO_LINK_STATE_CHANGE;	
		if (cb->link_status & GPIO_LINK_STATUS)	 
			cb->LED_ToggleStart = GPIO_LED_ON;
		else
			cb->LED_ToggleStart = GPIO_LED_OFF;
		cb->LED_Toggle = cb->LED_ToggleStart;
		gpio_set_led(port, cb->LED_Toggle);
	}
	else {
		if (cb->link_status & GPIO_LINK_STATUS)	 
			gpio_set_led(port, cb->LED_Toggle);
	}

	if (cb->link_status & GPIO_LINK_STATUS)	 {
		if (cb->LED_Toggle == cb->LED_ToggleStart){
			mod_timer(&cb->LED_Timer, jiffies + cb->LED_Interval);
			cb->blink_once_done=1;
		}
		else{
			mod_timer(&cb->LED_Timer, jiffies + GPIO_LED_ON_TIME);
			cb->blink_once_done=0;
		}
		
		cb->LED_Toggle = (cb->LED_Toggle + 1) & 0x1;	//cb->LED_Toggle = (cb->LED_Toggle + 1) % 2;
	}
	local_irq_restore(flags);
}

void calculate_led_interval(int port)
{
	struct ctrl_led *cb	= &led_cb[port];

	unsigned int delta = 0;

	/* calculate counter delta	*/
	delta += GPIO_UINT32_DIFF(cb->LED_tx_cnt, cb->LED_tx_cnt_log);
	delta += GPIO_UINT32_DIFF(cb->LED_rx_cnt, cb->LED_rx_cnt_log);
	cb->LED_tx_cnt_log = cb->LED_tx_cnt;
	cb->LED_rx_cnt_log = cb->LED_rx_cnt;

	/* update interval according to delta	*/
	if (delta == 0) {
		if (cb->LED_Interval == GPIO_LED_NOBLINK_TIME)
			mod_timer(&(cb->LED_Timer), jiffies + cb->LED_Interval);
		else{
			cb->LED_Interval = GPIO_LED_NOBLINK_TIME;
			if(cb->blink_once_done==1){
				mod_timer(&(cb->LED_Timer), jiffies + cb->LED_Interval);
				cb->blink_once_done=0;
			}
		}
	}
	else
	{	
		if(delta>25){		//That is: 200/delta-GPIO_LED_ON_TIME < GPIO_LED_ON_TIME
			cb->LED_Interval = GPIO_LED_ON_TIME;
		}
		else{
			/*	if delta is odd, should be +1 into even.		*/
			/*	just make led blink more stable and smooth.	*/
			if((delta & 0x1) == 1)
				delta++;
			
			cb->LED_Interval=200/delta-GPIO_LED_ON_TIME;		/* rx 1pkt + tx 1pkt => blink one time!	*/

/*			if (cb->LED_Interval < GPIO_LED_ON_TIME)
*				cb->LED_Interval = GPIO_LED_ON_TIME;
*/
		}
	}
}

void update_mib_counter(int port)
{
	uint32 regVal;
	struct ctrl_led *cb	= &led_cb[port];

	regVal=READ_MEM32(PSRP0+(port<<2));
	if((regVal&PortStatusLinkUp)!=0){
		//link up
		if (!(cb->link_status & GPIO_LINK_STATUS)) {
			cb->link_status = GPIO_LINK_STATE_CHANGE | 1;
		}
		rtl819x_getAsicMibCounter(port, MIB_TX_PKT_CNT, (uint32 *)&cb->LED_tx_cnt);
		rtl819x_getAsicMibCounter(port, MIB_RX_PKT_CNT, (uint32 *)&cb->LED_rx_cnt);
	}
	else{
		//link down
		if (cb->link_status & GPIO_LINK_STATUS) {
			cb->link_status = GPIO_LINK_STATE_CHANGE;
		}
	}
}

void init_led_ctrl(int port)
{
	struct ctrl_led *cb	= &led_cb[port];

	RTL_W32(PABCD_CNR, RTL_R32(PABCD_CNR) & (~((0x1<<port)<<PORT_PABCD_BASE)));	//set GPIO pin
	RTL_W32(PABCD_DIR, RTL_R32(PABCD_DIR) | ((0x1<<port)<<PORT_PABCD_BASE));//output pin

	memset(cb, '\0', sizeof(struct ctrl_led));

	update_mib_counter(port);		
	calculate_led_interval(port);	
	cb->link_status |= GPIO_LINK_STATE_CHANGE;

	init_timer(&cb->LED_Timer);
	cb->LED_Timer.expires = jiffies + cb->LED_Interval;
	cb->LED_Timer.data = (unsigned long)port;
	cb->LED_Timer.function = gpio_led_Interval_timeout;	
	mod_timer(&cb->LED_Timer, jiffies + cb->LED_Interval);

	gpio_led_Interval_timeout(port);		
}

void disable_led_ctrl(int port)
{
	struct ctrl_led *cb	= &led_cb[port];
	gpio_set_led(port, GPIO_LED_OFF);

	if (timer_pending(&cb->LED_Timer))	
		del_timer_sync(&cb->LED_Timer);
}
#endif // PATCH_GPIO_FOR_LED

/*
device mapping mainten
*/
#if 0
void rtl_ps_drv_netif_mapping_show(void)
{
	int i;
	printk("linux netif name VS driver netif name mapping:\n");
	for(i = 0; i < NETIF_SW_NUMBER;i++)
	{
		printk("valid(%d),linux netif name(%s) <---->drv netif name(%s)\n",ps_drv_netif_mapping[i].valid,
			ps_drv_netif_mapping[i].ps_netif?ps_drv_netif_mapping[i].ps_netif->name:NULL,ps_drv_netif_mapping[i].drvName);
	}
}
#endif

static int rtl_ps_drv_netif_mapping_init(void)
{
	memset(ps_drv_netif_mapping,0,NETIF_SW_NUMBER * sizeof(ps_drv_netif_mapping_t));	
	return SUCCESS;
}

ps_drv_netif_mapping_t* rtl_get_ps_drv_netif_mapping_by_psdev(struct net_device *dev)
{
	int i;
	for(i = 0; i < NETIF_SW_NUMBER;i++)
	{
		if(ps_drv_netif_mapping[i].valid == 1 && ps_drv_netif_mapping[i].ps_netif == dev)
			return &ps_drv_netif_mapping[i];
	}

	//back compatible,user use br0 to get lan netif
	if(memcmp(dev->name,RTL_PS_BR0_DEV_NAME,strlen(RTL_PS_BR0_DEV_NAME)) == 0)
	{
		for(i = 0; i < NETIF_SW_NUMBER;i++)
		{
			if(ps_drv_netif_mapping[i].valid == 1 && 
				memcmp(ps_drv_netif_mapping[i].drvName,RTL_DRV_LAN_NETIF_NAME,strlen(RTL_DRV_LAN_NETIF_NAME)) == 0)
				return &ps_drv_netif_mapping[i];
		}
	}
	
	return NULL;
}
#if 0

#if defined(CONFIG_RTK_VLAN_SUPPORT) || defined (CONFIG_RTL_MULTI_LAN_DEV)

static struct net_device * rtl_get_psdev_by_ps_drv_netif(char *netifname)
{
	int i;	
	for(i = 0; i < NETIF_SW_NUMBER;i++)
	{
		if(ps_drv_netif_mapping[i].valid == 1 && !strcmp(ps_drv_netif_mapping[i].drvName,netifname))
			return ps_drv_netif_mapping[i].ps_netif;
	}
	return NULL;
}
#endif
#endif

#ifdef CONFIG_RTL8676_Static_ACL
static inline int is_from_not_nicport_under_bridge(struct sk_buff *skb)
{
	/* cannot use the follwing method to retrieve pkt's src mac , it will lead to crash (eth_hdr not initialized, acccess virtual addr)  
	=> something wrong....   bug in our sdk ?  */    
	//unsigned char *pkt_srcmac = eth_hdr(skb)->h_source;
	unsigned char *pkt_srcmac = &skb->data[ETHER_ADDR_LEN];

	ForEachMasterNetif_Declaration  
	/*  1.  this pkt is from other nic port */
	if(skb->src_port == IF_SWITCH)
		return 0;

	/* 2. this pkt is from local netif */
	ForEachMasterNetif_Start
	{
		if(!memcmp(pkt_srcmac,&netif->macAddr.octet,ETHER_ADDR_LEN))
			return 0;
	}
	ForEachMasterNetif_End

	/* other cases : this pkt is from non-nic ports under bridge (ex. wlan0,vc0....)*/

	return 1;
}
#endif
#if 0
static int rtl_add_ps_drv_netif_mapping(struct net_device *dev, const char *name)
{
	int i;	
	
	//duplicate check
	if(rtl_get_ps_drv_netif_mapping_by_psdev(dev) !=NULL)
		return FAILED;
	
	for(i = 0; i < NETIF_SW_NUMBER;i++)
	{
		if(ps_drv_netif_mapping[i].valid == 0)
			break;
	}
	
	if(i == NETIF_SW_NUMBER)
		return FAILED;
	
	ps_drv_netif_mapping[i].ps_netif = dev;
	memcpy(ps_drv_netif_mapping[i].drvName,name,strlen(name));
	ps_drv_netif_mapping[i].valid = 1;
	return SUCCESS;
}
#endif
#if 0
static int rtl_del_ps_drv_netif_mapping(struct net_device *dev)
{
	ps_drv_netif_mapping_t *entry;
	entry = rtl_get_ps_drv_netif_mapping_by_psdev(dev);
	if(entry)
		entry->valid = 0;
	
	return SUCCESS;
}
#endif

/*
 * Disable TX/RX through IO_CMD register
 */
static void rtl8186_stop_hw(struct net_device *dev, struct dev_priv *cp)
{

#if defined(PATCH_GPIO_FOR_LED)
	if (cp->id == RTL_LANVLANID) {
		int port;
		for (port=0; port<RTL8651_PHY_NUMBER; port++)
			disable_led_ctrl(port);
	}
#endif

}

/* Set or clear the multicast filter for this adaptor.
 * This routine is not state sensitive and need not be SMP locked.
 */
static void re865x_set_rx_mode (struct net_device *dev){
/*	Not yet implemented.
	unsigned long flags;
	spin_lock_irqsave (&_rtl86xx_dev.lock, flags);
	spin_unlock_irqrestore (&_rtl86xx_dev.lock, flags);
*/
}

#if defined (CONFIG_RTL_NIC_HWSTATS)
 void  re865x_accumulate_port_stats(uint32 portnum, struct net_device_stats *net_stats)
{	
	uint32 addrOffset_fromP0 =0;	

	if( portnum < 0 ||  portnum > CPU)
			return ;

#ifdef CONFIG_RTL_8367B
    if (rtl8651_tblAsicDrvPara.externalPHYProperty & RTL8676_TBLASIC_EXTPHYPROPERTY_PORT0_RTL8367B) 
    {   
        rtl8367b_accumulate_port_stats(portnum,net_stats);
        return;            
    }

#endif 			

	addrOffset_fromP0 = portnum * MIB_ADDROFFSETBYPORT;
	/* rx_pkt = rx_unicast +rx_multicast + rx_broadcast */
	net_stats->rx_packets += rtl8651_returnAsicCounter( OFFSET_IFINUCASTPKTS_P0 + addrOffset_fromP0 ) ;
	net_stats->rx_packets += rtl8651_returnAsicCounter( OFFSET_ETHERSTATSMULTICASTPKTS_P0 + addrOffset_fromP0 ) ;
	net_stats->rx_packets += rtl8651_returnAsicCounter( OFFSET_ETHERSTATSBROADCASTPKTS_P0 + addrOffset_fromP0 ) ;
	/* tx_pkt = tx_unicast +tx_multicast + tx_broadcast*/
	net_stats->tx_packets += rtl8651_returnAsicCounter( OFFSET_IFOUTUCASTPKTS_P0 + addrOffset_fromP0 ) ;
	net_stats->tx_packets += rtl8651_returnAsicCounter( OFFSET_IFOUTMULTICASTPKTS_P0 + addrOffset_fromP0 ) ;
	net_stats->tx_packets += rtl8651_returnAsicCounter( OFFSET_IFOUTBROADCASTPKTS_P0 + addrOffset_fromP0 ) ;
	
	net_stats->rx_bytes += rtl865xC_returnAsicCounter64( OFFSET_IFINOCTETS_P0 + addrOffset_fromP0 ) ;
	net_stats->tx_bytes += rtl865xC_returnAsicCounter64( OFFSET_IFOUTOCTETS_P0 + addrOffset_fromP0 ) ;
	/*rx_errors = CRC error + Jabber error  + Fragment error*/
	net_stats->rx_errors += rtl8651_returnAsicCounter( OFFSET_DOT3STATSFCSERRORS_P0 + addrOffset_fromP0 ) ;
	net_stats->rx_errors += rtl8651_returnAsicCounter( OFFSET_ETHERSTATSJABBERS_P0 + addrOffset_fromP0 ) ;
	net_stats->rx_errors += rtl8651_returnAsicCounter( OFFSET_ETHERSTATSOVERSIZEPKTS_P0 + addrOffset_fromP0 ) ;
	//OFFSET_DOT1DTPPORTINDISCARDS_P0? 
	//net_stats->rx_dropped += rtl8651_returnAsicCounter( OFFSET_DOT1DTPPORTINDISCARDS_P0 + addrOffset_fromP0 ) ;
	net_stats->tx_dropped += rtl8651_returnAsicCounter( OFFSET_IFOUTDISCARDS + addrOffset_fromP0 ) ;
	net_stats->multicast += rtl8651_returnAsicCounter( OFFSET_ETHERSTATSMULTICASTPKTS_P0 + addrOffset_fromP0 ) ;
  
	net_stats->collisions += rtl8651_returnAsicCounter( OFFSET_ETHERSTATSCOLLISIONS_P0 + addrOffset_fromP0 ) ;
	net_stats->rx_crc_errors += rtl8651_returnAsicCounter( OFFSET_DOT3STATSFCSERRORS_P0 + addrOffset_fromP0 ) ;
	return;
}

void re865x_get_hardwareStats(struct dev_priv  *priv)
{
	uint32 portmask;
	uint32 port;

	portmask = priv->portmask;
	//rx_dropped = priv->net_stats.rx_dropped;
	//memset( &priv->net_stats, 0, sizeof(struct net_device_stats) );
	/* reset counters to 0 */
	priv->net_stats.rx_packets = 0;
	priv->net_stats.tx_packets = 0;
	priv->net_stats.rx_bytes = 0;
	priv->net_stats.tx_bytes = 0;
	priv->net_stats.rx_errors = 0;
	priv->net_stats.tx_dropped = 0;
	priv->net_stats.multicast = 0;
	priv->net_stats.collisions = 0;
	priv->net_stats.rx_crc_errors = 0;
	for( port = 0; port < CPU; port++)
	{
		if((1<<port) & portmask)
		{
			re865x_accumulate_port_stats(port, &priv->net_stats);
		}
			
	}
	return;
}
#endif

static struct net_device_stats *re865x_get_stats(struct net_device *dev){
	struct dev_priv  *dp;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	dp = netdev_priv(dev);
#else
	dp = dev->priv;
#endif
	#if defined (CONFIG_RTL_NIC_HWSTATS)
		re865x_get_hardwareStats(dp);
	#endif
	return &dp->net_stats;
}

static int32 re865x_packVlanConfig(struct rtl865x_vlanConfig vlanConfig1[],  struct rtl865x_vlanConfig vlanConfig2[])
{
	int i, j;
	uint32 vlanCnt=0;
	uint32 found=FALSE;
	
	DBG_VlanConfig_PRK("Enter %s\n",__func__);
	/*get input vlan config entry number*/
	
	memset(vlanConfig2, 0 , NETIF_SW_NUMBER);
	for(i=0; vlanConfig1[i].ifname[0] != '\0'; i++)
	{	
		if(vlanConfig1[i].vid == 0) continue;
		found=FALSE;
		for(j=0;j<vlanCnt;j++)
#if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED)
			/* Multiple WAN with the same vid is allowed */
			if((vlanConfig1[i].isWan == 0) && (vlanConfig1[i].vid == vlanConfig2[j].vid)&&(vlanConfig1[i].is_slave==vlanConfig2[j].is_slave))
#else
			if((vlanConfig1[i].vid == vlanConfig2[j].vid)&&(vlanConfig1[i].is_slave==vlanConfig2[j].is_slave))
#endif
			{
				found=TRUE;	
				break;
			}
		if(found==FALSE){
			vlanConfig2[vlanCnt].vid = vlanConfig1[i].vid;
			vlanConfig2[vlanCnt].is_slave= vlanConfig1[i].is_slave;			
			vlanConfig2[vlanCnt].protocol = vlanConfig1[i].protocol;
			vlanCnt ++;
		}
		
	}

#if 0
	for(i=0; vlanConfig1[i].ifname[0] != '\0'; i++)
	{
		if(vlanConfig1[i].vid != 0)
			vlanCnt++;
	}
#endif

	if((vlanCnt+1) > NETIF_SW_NUMBER)
		printk("ERROR,vlanCnt(%d) > max size %d\n",vlanCnt,NETIF_SW_NUMBER-1);
	
	/*initialize output vlan config*/
	memset(vlanConfig2, 0 , (vlanCnt+1)*sizeof(struct rtl865x_vlanConfig));

	for(i=0; vlanConfig1[i].ifname[0] != '\0'; i++)
	{
		found=FALSE;
		
		if(vlanConfig1[i].vid == 0)
			continue;

		for(j=0; j<vlanCnt; j++)
		{
			if(vlanConfig1[i].if_type != vlanConfig2[j].if_type)
			{
				continue;
			}
			else
			{
				if(vlanConfig1[i].if_type==IF_ETHER)
				{
#if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED)
					/* Multiple WAN can has the same vlan id*/
					if((vlanConfig1[i].isWan == 1) || (vlanConfig1[i].vid!=vlanConfig2[j].vid))
#else
					/*if multiple vlan config has the same vlan id*/
					/*the first one will decide the  real network interface name/asic mtu/hardware address*/
					if(vlanConfig1[i].vid!=vlanConfig2[j].vid)
#endif
					{
						continue;
					}
					else
					{
						found=TRUE;
						break;
					}
	
				}
				else
				{
					/*PPP interface*/
					if(strcmp(vlanConfig1[i].ifname, vlanConfig2[j].ifname))
					{
						continue;
					}
					else
					{
						found=TRUE;
						break;
					}
				}
			}
		}

		if(found==TRUE)
		{
			/*merge port mask*/
			vlanConfig2[j].memPort |=vlanConfig1[i].memPort;
			vlanConfig2[j].untagSet |=vlanConfig1[i].untagSet;
		}	
		else
		{
			/*find an empty entry to store this vlan config*/
			for(j=0; j<vlanCnt; j++)
			{
				if(vlanConfig2[j].vid==0)
				{
					memcpy(&vlanConfig2[j], &vlanConfig1[i], sizeof(struct rtl865x_vlanConfig));
					#ifndef CONFIG_RTL_HW_NAPT
					if(( vlanConfig1[i].if_type==IF_ETHER) && (vlanConfig1[i].isWan==FALSE) )
					{
						/*add cpu port to lan member list*/
						vlanConfig2[j].memPort|=(1<<RTL_CPU_PORT);
						vlanConfig2[j].untagSet|=(1<<RTL_CPU_PORT);
					}					
					#endif
					break;
				}
			}
		}
		
	}

	DBG_VlanConfig_PRK("(%s)---------- vlanConfig1 ------------\n",__func__);
	for(i=0; vlanConfig1[i].ifname[0] != '\0' ; i++)
	{
		DBG_VlanConfig_PRK("name:%7s  isWan:%d  is_slave:%d  vid:%d  port:%03X  proto:%d mac:%02X:%02X:%02X:%02X:%02X:%02X\n"
			,vlanConfig1[i].ifname,vlanConfig1[i].isWan,vlanConfig1[i].is_slave,vlanConfig1[i].vid,vlanConfig1[i].memPort,vlanConfig1[i].protocol
			,vlanConfig1[i].mac.octet[0],vlanConfig1[i].mac.octet[1],vlanConfig1[i].mac.octet[2]
			,vlanConfig1[i].mac.octet[3],vlanConfig1[i].mac.octet[4],vlanConfig1[i].mac.octet[5]);
	}
	DBG_VlanConfig_PRK("(%s)---------- vlanConfig2 ------------\n",__func__);
	for(i=0; vlanConfig2[i].ifname[0] != '\0' ; i++)
	{
		DBG_VlanConfig_PRK("name:%7s  isWan:%d  is_slave:%d  vid:%d  port:%03X  proto:%d mac:%02X:%02X:%02X:%02X:%02X:%02X\n"
			,vlanConfig2[i].ifname,vlanConfig2[i].isWan,vlanConfig2[i].is_slave,vlanConfig2[i].vid,vlanConfig2[i].memPort,vlanConfig1[i].protocol
			,vlanConfig2[i].mac.octet[0],vlanConfig2[i].mac.octet[1],vlanConfig2[i].mac.octet[2]
			,vlanConfig2[i].mac.octet[3],vlanConfig2[i].mac.octet[4],vlanConfig2[i].mac.octet[5]);
	}	
	DBG_VlanConfig_PRK("(%s)-------------------------- ------------\n",__func__);
	DBG_VlanConfig_PRK("Leave %s\n",__func__);
	return SUCCESS;
}

static void rtl865x_disableDevPortForward(struct net_device *dev, struct dev_priv *cp)
{
	int port;
	for(port=0;port<RTL8651_AGGREGATOR_NUMBER;port++)
	{
		if((1<<port) & cp->portmask)
		{
#if 1
			REG32(PCRP0+(port<<2))= ((REG32(PCRP0+(port<<2)))&(~ForceLink));
			REG32(PCRP0+(port<<2))= ((REG32(PCRP0+(port<<2)))&(~EnablePHYIf));
			TOGGLE_BIT_IN_REG_TWICE(PCRP0+(port<<2),EnablePHYIf);
			TOGGLE_BIT_IN_REG_TWICE(PCRP0+(port<<2),ForceLink);
#else
			rtl865xC_setAsicEthernetForceModeRegs(port, TRUE, FALSE, 1, TRUE);
#endif
		}
	}
#ifdef CONFIG_RTL_8196C_ESD
	_96c_esd_counter = 0;		// stop counting
#endif	
}

static void rtl865x_enableDevPortForward(struct net_device *dev, struct dev_priv *cp)
{
	int port;
	for(port=0;port<RTL8651_AGGREGATOR_NUMBER;port++)
	{
		if((1<<port) & cp->portmask)
		{
#if 1
			REG32(PCRP0+(port<<2))= ((REG32(PCRP0+(port<<2)))|(ForceLink));
			REG32(PCRP0+(port<<2))= ((REG32(PCRP0+(port<<2)))|(EnablePHYIf));
			TOGGLE_BIT_IN_REG_TWICE(PCRP0+(port<<2),EnablePHYIf);
			TOGGLE_BIT_IN_REG_TWICE(PCRP0+(port<<2),ForceLink);
#else
			rtl865xC_setAsicEthernetForceModeRegs(port, FALSE, TRUE, 1, TRUE);
			rtl8651_restartAsicEthernetPHYNway(port);
#endif
		}
	}
#ifdef CONFIG_RTL_8196C_ESD
	_96c_esd_counter = 1;		// start counting and check ESD
#endif	
}

static void rtl865x_disableInterrupt(void)
{
	REG32(CPUICR) = 0; 
	REG32(CPUIIMR) = 0;      
}

#ifdef CONFIG_DATA_IN_IMEM
#define ALIGN4(x)	(((x)+3)&(~3))
extern void __iram, __iram_end1;

void * allocBuffFromIMEM(uint32 len)
{
	static uint32 imem_size=0;
	static uint32 imem_offset=0;
	void *pbuff = NULL;

	if (unlikely(0 == imem_size)) {
		imem_size = &__iram_end1 - &__iram;
	}

	if ((len + imem_offset) > imem_size) {
		printk("%s ==========>>> imem overflow(%d/%d offset=%d)!\n", __func__, len, imem_size, imem_offset);
	}
	else {
		printk("%s ==========>>> alloc %d from imem.\n", __func__, len);
		pbuff = (void *)((uint32)&__iram + imem_offset);
		imem_offset += ALIGN4(len);
	}

	return pbuff;
}

void *allocUncachedBuffFromIMEM(uint32 len)
{
	void *pbuff;

	printk("%s alloc %d\n", __func__, len);
	pbuff = allocBuffFromIMEM(len);
	if (pbuff != NULL)
		pbuff = (void *)((uint32)pbuff | UNCACHE_MASK);

	return pbuff;
}
#endif//end of CONFIG_DATA_IN_IMEM

#if !defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
static void rtk_queue_init(struct ring_que *que)
{
	memset(que, 0, sizeof(struct ring_que));
#ifdef CONFIG_DATA_IN_IMEM
	que->ring = (struct sk_buff **)allocBuffFromIMEM(sizeof(struct skb_buff*)*(rtl865x_maxPreAllocRxSkb+1));
	if (NULL == que->ring)
#endif//end of CONFIG_DATA_IN_IMEM
	que->ring = (struct sk_buff **)kmalloc(
		(sizeof(struct skb_buff*)*(rtl865x_maxPreAllocRxSkb+1))
		,GFP_ATOMIC);
	memset(que->ring, 0, (sizeof(struct sk_buff *))*(rtl865x_maxPreAllocRxSkb+1));
	que->qmax = rtl865x_maxPreAllocRxSkb;	
}

static int rtk_queue_tail(struct ring_que *que, struct sk_buff *skb)
{
	int next;
	
	if (que->head == que->qmax)
		next = 0;
	else
		next = que->head + 1;
	
	if (que->qlen >= que->qmax || next == que->tail) {
		return 0;
	}	
	
	que->ring[que->head] = skb;
	que->head = next;
	que->qlen++;

	return 1;
}

static struct sk_buff *rtk_dequeue(struct ring_que *que)
{
	struct sk_buff *skb;

	if (que->qlen <= 0 || que->tail == que->head)
	{
		return NULL;
	}

	skb = que->ring[que->tail];
		
	if (que->tail == que->qmax)
		que->tail  = 0;
	else
		que->tail++;

	que->qlen--;	

	return (struct sk_buff *)skb;
}
#endif//end of !CONFIG_RTL_ETH_PRIV_SKB_ADV

#if 1//defined(CONFIG_RTL_ETH_PRIV_SKB_DEBUG)
#if !defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
int get_buf_in_rx_skb_queue(void)
{
	return rx_skb_queue.qlen;
}
#endif//end of !CONFIG_RTL_ETH_PRIV_SKB_ADV

int get_buf_in_poll(void)
{
#if defined(CONFIG_RTL_ETH_PRIV_SKB)
	return eth_skb_free_num;
#else
	return 0;
#endif
}
#endif

#if !defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
//__MIPS16
__IRAM_FWD
static void refill_rx_skb(void)
{
	struct sk_buff *skb;
	unsigned long flags;

	while (rx_skb_queue.qlen < rtl865x_maxPreAllocRxSkb)
	{
		#if defined(CONFIG_RTL_ETH_PRIV_SKB)
		skb = dev_alloc_skb_priv_eth(CROSS_LAN_MBUF_LEN);
		#else
		skb = dev_alloc_skb(CROSS_LAN_MBUF_LEN);
		#endif

		if (skb == NULL) { 
			DEBUG_ERR("EthDrv: dev_alloc_skb() failed!\n");		
			return;
		}
		skb_reserve(skb, RX_OFFSET);
#ifdef RTK_QUE
		flags = swcore_buf_lock(1);//local_irq_save(flags);
		if (!rtk_queue_tail(&rx_skb_queue, skb))
			dev_kfree_skb_any(skb);
		swcore_buf_unlock(1, flags);//local_irq_restore(flags);
#else		
		__skb_queue_tail(&rx_skb_queue, skb);
#endif
	}
}

//---------------------------------------------------------------------------
static void free_rx_skb(void)
{
	struct sk_buff *skb;
#ifdef CONFIG_RTL_SWITCH_NEW_DESCRIPTOR
	RTL_swNic_freeRxBuf();
#else
	swNic_freeRxBuf();
#endif
	while  (rx_skb_queue.qlen > 0) {
#ifdef RTK_QUE
		skb = rtk_dequeue(&rx_skb_queue);
#else
		skb = __skb_dequeue(&rx_skb_queue);
#endif
		dev_kfree_skb_any(skb);
	}
}

//---------------------------------------------------------------------------
unsigned char *alloc_rx_buf(void **skb, int buflen)
{
	struct sk_buff *new_skb;
	unsigned long flags;

	#ifdef CONFIG_RTL_JUMBO_FRAME
	if(buflen>CROSS_LAN_MBUF_LEN){
		new_skb = dev_alloc_skb(buflen);
		if (new_skb == NULL) {
			DEBUG_ERR("EthDrv: alloc skb failed!\n");
		}
		else
			skb_reserve(new_skb, RX_OFFSET);
	}
	else{
	#endif
		if (rx_skb_queue.qlen == 0) {
			#if defined(CONFIG_RTL_ETH_PRIV_SKB)
			new_skb = dev_alloc_skb_priv_eth(CROSS_LAN_MBUF_LEN);
			#else
			new_skb = dev_alloc_skb(CROSS_LAN_MBUF_LEN);
			#endif
			if (new_skb == NULL) { 
				DEBUG_ERR("EthDrv: alloc skb failed!\n");
			}
			else
				skb_reserve(new_skb, RX_OFFSET);
		}
		else {
			#ifdef RTK_QUE
			flags = swcore_buf_lock(1); //local_irq_save(flags);
			new_skb = rtk_dequeue(&rx_skb_queue);
			swcore_buf_unlock(1, flags); //local_irq_restore(flags);
			#else
			new_skb = __skb_dequeue(&rx_skb_queue);
			#endif
		}
	#ifdef CONFIG_RTL_JUMBO_FRAME
	}
	#endif
	if (new_skb == NULL) 
		return NULL;	
	*skb = new_skb;

	return new_skb->data;		
}

//---------------------------------------------------------------------------
void free_rx_buf(void *skb)
{
	dev_kfree_skb_any((struct sk_buff *)skb);
}
#endif //end of !CONFIG_RTL_ETH_PRIV_SKB_ADV

#if defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
__inline__ unsigned char *get_buf_from_poll(void)
{
	unsigned long flags;
	unsigned char *buf=NULL;

	local_irq_save(flags);
	if ((NULL != freeBufList) && (NULL != freeSkbList))
	{
		buf = freeBufList->buf;
		freeBufList = freeBufList->next;
#ifdef CONFIG_FAST_FORWARDING
		buf += NET_SKB_PAD+RX_OFFSET;
#endif//end of CONFIG_FAST_FORWARDING
	}
	//else
	//	printk("%s get fail.\n", __func__);
	local_irq_restore(flags);
	
	return buf;
}

//---------------------------------------------------------------------------
static void free_rx_skb(void)
{
	swNic_freeRxBuf();
}

#ifdef CONFIG_FAST_FORWARDING
__IRAM
struct sk_buff *FF_Alloc_Skb(unsigned char *data, unsigned int len)
{
	struct sk_buff *skb;
	unsigned long flags;

	local_irq_save(flags);
	
	if (NULL == freeSkbList)
	{
		local_irq_restore(flags);
		//printk("%s freeSkbList is NULL\n", __func__);
		return NULL;
	}
	
	skb = freeSkbList;
	freeSkbList = freeSkbList->prealloc_next;
	local_irq_restore(flags);

	atomic_set(&skb->users, 1);
	/* cloned must be initialized here. */
	skb->cloned = 0;
	skb->head = data-(NET_SKB_PAD+RX_OFFSET);
	skb->data = data;
	skb->len = len;
	skb->data_len = 0;

	skb->src_port = IF_SWITCH;
	skb->prealloc_flags = RETFREEQ_FF|SKB_RET2RXRING;
	skb->prealloc_cb = rtl865x_free_eth_priv_buf;
	
	return skb;
}

inline struct net_device * rtl865x_get_dev_by_pid(uint16 pid)
{
	int idx;

	idx = portid_index[pid];
	return _rtl86xx_dev.dev[idx];
}
#endif//end of CONFIG_FAST_FORWARDING

//---------------------------------------------------------------------------
__IRAM
unsigned char *alloc_rx_buf(void **skb, int buflen)
{
	struct sk_buff *new_skb=NULL;
	unsigned char *data;
	unsigned long flags;

	local_irq_save(flags);
	if ((NULL != freeSkbList) && (NULL != freeBufList))
	{
		data = freeBufList->buf;
		freeBufList = freeBufList->next;
		
		new_skb = freeSkbList;
		freeSkbList = freeSkbList->prealloc_next;
		local_irq_restore(flags);

#ifdef CONFIG_FAST_FORWARDING
		init_skbhdr(new_skb, data, 0, rtl865x_free_eth_priv_buf);
#else
		init_skbhdr(new_skb, data, CROSS_LAN_MBUF_LEN, rtl865x_free_eth_priv_buf);
#endif//end of CONFIG_FAST_FORWARDING
		
		*skb = new_skb;
		return new_skb->data;
	}
	local_irq_restore(flags);

	//if (NULL == freeSkbList)
	//	printk("%s freeSkbList is NULL\n", __func__);
	//if (NULL == freeBufList)
	//	printk("%s freeBufList is NULL\n", __func__);
	return NULL;
}

//---------------------------------------------------------------------------
void free_rx_buf(void *skb)
{
	((struct sk_buff *)skb)->prealloc_flags &= ~SKB_RET2RXRING;
	dev_kfree_skb_any((struct sk_buff *)skb);
}
#endif//end of CONFIG_RTL_ETH_PRIV_SKB_ADV

//---------------------------------------------------------------------------
__IRAM
void tx_done_callback(void *skb)
{
	dev_kfree_skb_any((struct sk_buff *)skb);	
}

#if defined (CONFIG_RTL_IGMP_SNOOPING)		
#if defined (CONFIG_BRIDGE)
extern void br_signal_igmpProxy(void);
#endif

static inline struct iphdr * re865x_getIpv4Header(uint8 *macFrame)
{
	uint8 *ptr;
	struct iphdr *iph=NULL;
	
	ptr=macFrame+12;
	if(*(int16 *)(ptr)==(int16)htons(ETH_P_8021Q))
	{
		ptr=ptr+4;
	}

	if(*(int16 *)(ptr)==(int16)htons(ETH_P_PPP_SES))
	{
		ptr=ptr+8;

		/*it's not ppp over ipv4 packet*/
		if(*(int16 *)(ptr)!=(int16)htons(0x0021))
		{
			return NULL;
		}
		
		iph=(struct iphdr *)(ptr+2);
		return iph;
	}
	else
	{
		/*it's not ipv4 packet*/
		if(*(int16 *)(ptr)!=(int16)htons(ETH_P_IP))
		{
			return NULL;
		}
		
		iph=(struct iphdr *)(ptr+2);

		return iph;
	}

}

#if defined (CONFIG_RTL_MLD_SNOOPING)
static inline struct ipv6hdr* re865x_getIpv6Header(uint8 *macFrame)
{
	uint8 *ptr;
	struct ipv6hdr *ipv6h=NULL;
	
	ptr=macFrame+12;
	if(*(int16 *)(ptr)==(int16)htons(ETH_P_8021Q))
	{
		ptr=ptr+4;
	}

	/*it's not ipv6 packet*/
	if(*(int16 *)(ptr)!=(int16)htons(ETH_P_IPV6))
	{
		return NULL;
	}
	
	ipv6h=(struct ipv6hdr *)(ptr+2);

	return ipv6h;
}
#if 0
#define IPV6_ROUTER_ALTER_OPTION 0x05020000
#define  HOP_BY_HOP_OPTIONS_HEADER 0
#define ROUTING_HEADER 43
#define  FRAGMENT_HEADER 44
#define DESTINATION_OPTION_HEADER 60

#define PIM_PROTOCOL 103
#define MOSPF_PROTOCOL 89
#define TCP_PROTOCOL 6
#define UDP_PROTOCOL 17
#define NO_NEXT_HEADER 59
#define ICMP_PROTOCOL 58

#define MLD_QUERY 130
#define MLDV1_REPORT 131
#define MLDV1_DONE 132
#define MLDV2_REPORT 143

#define IS_IPV6_PIM_ADDR(ipv6addr) ((ipv6addr[0] == 0xFF020000)&&(ipv6addr[1] == 0x00000000)&&(ipv6addr[2] == 0x00000000)&&(ipv6addr[3] ==0x0000000D)) 
#define IS_IPV6_MOSPF_ADDR1(ipv6addr) ((ipv6addr[0] == 0xFF020000)&&(ipv6addr[1] == 0x00000000)&&(ipv6addr[2] == 0x00000000)&&(ipv6addr[3] ==0x00000005)) 		
#define IS_IPV6_MOSPF_ADDR2(ipv6addr) ((ipv6addr[0] == 0xFF020000)&&(ipv6addr[1] == 0x00000000)&&(ipv6addr[2] == 0x00000000)&&(ipv6addr[3] ==0x00000006)) 

int re865x_getIpv6TransportProtocol(struct ipv6hdr* ipv6h)
{
	
	unsigned char *ptr=NULL;
	unsigned char *startPtr=NULL;
	unsigned char *lastPtr=NULL;
	unsigned char nextHeader=0;
	unsigned short extensionHdrLen=0;

	unsigned char  optionDataLen=0;
	unsigned char  optionType=0;
	unsigned int ipv6RAO=0;
		
	if(ipv6h==NULL)
	{
		return -1;
	}
	
	if(ipv6h->version!=6)
	{
		return -1;
	}
	
	startPtr= (unsigned char *)ipv6h;
	lastPtr=startPtr+sizeof(struct ipv6hdr)+(ipv6h->payload_len);
	nextHeader= ipv6h ->nexthdr;
	ptr=startPtr+sizeof(struct ipv6hdr);

	while(ptr<lastPtr)
	{
		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<(startPtr+extensionHdrLen+sizeof(struct ipv6hdr)))
				{
					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 altert 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;
					}
				
				}
				
				break;
			
			case ROUTING_HEADER:
				nextHeader=ptr[0];
				extensionHdrLen=((uint16)(ptr[1])+1)*8;			
                            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;
				if((ptr[0]==MLD_QUERY) ||(ptr[0]==MLDV1_REPORT) ||(ptr[0]==MLDV1_DONE) ||(ptr[0]==MLDV2_REPORT))
				{
					return ICMP_PROTOCOL;
					
				}
				break;
			
			case PIM_PROTOCOL:
				nextHeader=NO_NEXT_HEADER;
				if(IS_IPV6_PIM_ADDR(ipv6h->daddr.s6_addr32))
				{
					return PIM_PROTOCOL;
				}
			
				break;
			
			case MOSPF_PROTOCOL:
				nextHeader=NO_NEXT_HEADER;
				
				if(IS_IPV6_MOSPF_ADDR1(ipv6h->daddr.s6_addr32) || IS_IPV6_MOSPF_ADDR2(ipv6h->daddr.s6_addr32))
				{
					return MOSPF_PROTOCOL;
				}
				break;
				
			case TCP_PROTOCOL:
				nextHeader=NO_NEXT_HEADER;
				return TCP_PROTOCOL;
		
				break;
				
			case UDP_PROTOCOL:
				nextHeader=NO_NEXT_HEADER;
				return UDP_PROTOCOL;
	
				break;	
				
			default:		
				/*not ipv6 multicast protocol*/
				return -1;
				break;
		}
	
	}
	return -1;
}
#endif	
#endif

void rtl865x_igmpSyncLinkStatus(void)
{
	rtl_igmpPortInfo_t portInfo;
	
	portInfo.linkPortMask=newLinkPortMask;
	rtl865x_igmpLinkStatusChangeCallback(nicIgmpModuleIndex, &portInfo);
	
	#if defined (CONFIG_BRIDGE)
	if((newLinkPortMask & (~curLinkPortMask))!=0)
	{	
		/*there is some port linking up*/
		/*notice igmp proxy daemon to send general query to newly link up client*/
		#ifdef CONFIG_RTL8196BU_8186SDK_MP_SPI 
		#else
		br_signal_igmpProxy();
		#endif
	}
	#endif
	
	return;
			
}

#endif	/*	defined (CONFIG_RTL_IGMP_SNOOPING)	*/

#ifdef CONFIG_RTL_MULTI_ETH_WAN /*move from smux.c*/
unsigned int getMemberOfSmuxDevByVid(unsigned int vid)
{
	struct smux_dev_info *dev_info;
	struct net_device *vdev;
	unsigned char name[MAX_IFNAMESIZE];
	rtl865x_getMasterNetifByVid(vid, name);
	vdev = dev_get_by_name(&init_net, name);
	if (!vdev)
		return -1;
	dev_put(vdev);
	dev_info = SMUX_DEV_INFO(vdev);
	return dev_info->member;
}
#endif	/*end of CONFIG_RTL865X_IGMP_SNOOPING*/

static inline int32 rtl_isWanDev(struct dev_priv *cp)
{
#if defined(CONFIG_RTK_VLAN_SUPPORT)
	return (!cp->vlan_setting.is_lan);
#else
#ifndef CONFIG_RTL_MULTI_ETH_WAN
	return (cp->id==RTL_WANVLANID);
#else
	return (cp->id != RTL_LANVLANID);
#endif
#endif
}

#if defined(CONFIG_RTL_CUSTOM_PASSTHRU)
static inline int32 rtl_isPassthruFrame(uint8 *data)
{
	int	ret;

	ret = FAILED;
	if (oldStatus)
	{
		if (oldStatus&IP6_PASSTHRU_MASK)
		{
			if ((*((uint16*)(data+(ETH_ALEN<<1)))==__constant_htons(ETH_P_IPV6)) ||
				((*((uint16*)(data+(ETH_ALEN<<1)))==__constant_htons(ETH_P_8021Q))&&(*((uint16*)(data+(ETH_ALEN<<1)+VLAN_HLEN))==__constant_htons(ETH_P_IPV6))))
			{
				ret = SUCCESS;
			}
		}

		if (oldStatus&PPPOE_PASSTHRU_MASK)
		{
			if (((*((uint16*)(data+(ETH_ALEN<<1)))==__constant_htons(ETH_P_PPP_SES))||(*((uint16*)(data+(ETH_ALEN<<1)))==__constant_htons(ETH_P_PPP_DISC))) ||
				((*((uint16*)(data+(ETH_ALEN<<1)))==__constant_htons(ETH_P_8021Q))&&((*((uint16*)(data+(ETH_ALEN<<1)+VLAN_HLEN))==__constant_htons(ETH_P_PPP_SES))||(*((uint16*)(data+(ETH_ALEN<<1)+VLAN_HLEN))==__constant_htons(ETH_P_PPP_DISC)))))
			{
				ret = SUCCESS;
			}
		}
	}

	return ret;
}
#endif

#if defined(RTL_CPU_QOS_ENABLED)
__DRAM_FWD	static	int	highestPriority;
__DRAM_FWD static 	int	cpuQosHoldLow;
__DRAM_FWD static 	int	totalLowQueueCnt;
struct timer_list	cpuQosTimer;

static rtl_queue_entry	pktQueueByPri[RTL865X_SWNIC_RXRING_MAX_PKTDESC] = {{0}};

__MIPS16 __IRAM_FWD
int rtl_enqueueSkb(rtl_nicRx_info *info)
{
	rtl_queue_entry	*entry;
	
	entry = &pktQueueByPri[info->priority];

	if (info->priority<cpuQosHoldLow)
		totalLowQueueCnt++;

	memcpy(&entry->entry[entry->end], info, sizeof(rtl_nicRx_info));
	entry->cnt++;
	entry->end = (entry->end==(RTL_NIC_QUEUE_LEN-1))?0:(entry->end+1);

	if (entry->cnt==RTL_NIC_QUEUE_LEN)
		return SUCCESS;
	else
		return FAILED;
}

__MIPS16 __IRAM_FWD
int rtl_dequeueSkb(rtl_nicRx_info *info)
{
	rtl_queue_entry	*entry;
	
	entry = &pktQueueByPri[info->priority];

	if(entry->cnt==0)
	{
		return FAILED;
	}
	else
	{
		memcpy(info, &entry->entry[entry->start], sizeof(rtl_nicRx_info));
		entry->cnt--;
		entry->start = (entry->start==(RTL_NIC_QUEUE_LEN-1))?0:(entry->start+1);
		return SUCCESS;
	}
}

static void rtl_cpuQosTimer(unsigned long data)
{
	cpuQosHoldLow = highestPriority;

	init_timer(&cpuQosTimer);
	cpuQosTimer.function = rtl_cpuQosTimer;
}
#endif

#if defined(RTL_CPU_QOS_ENABLED)
__MIPS16 __IRAM_FWD
static inline int32 rtl_processReceivedInfo(rtl_nicRx_info *info, int nicRxRet)
{
	int	ret;
	
	switch(nicRxRet)
	{
		case RTL_NICRX_OK:
			{
				if (highestPriority<info.priority)
				{
					highestPriority = info.priority;
					cpuQosHoldLow = highestPriority;
				}

				if (info.priority==(RTL865X_SWNIC_RXRING_MAX_PKTDESC-1))
				{
					ret = RTL_RX_PROCESS_RETURN_SUCCESS;
				}
				else	if (rtl_enqueueSkb(&info) == SUCCESS)
				{
					rtl_dequeueSkb(&info);
					ret = RTL_RX_PROCESS_RETURN_SUCCESS;
				}
				else
					ret = RTL_RX_PROCESS_RETURN_CONTINUE;

				break;
			}
		case RTL_NICRX_NULL:
			{
				info.priority = highestPriority;
				if (rtl_dequeueSkb(&info)==SUCCESS)
				{
					ret = RTL_RX_PROCESS_RETURN_SUCCESS;
				}
				else if (highestPriority>0)
				{
					highestPriority--;
					if (highestPriority==0)
					{
						mod_timer(&cpuQosTimer, jiffies+RTL_CPUQOS_TIMER_INTERVAL);
					}
					ret = RTL_RX_PROCESS_RETURN_CONTINUE;
				}
				else
				{
					/* highestPriority=0 */
					if (cpuQosHoldLow)
					{
						swNic_flushRxRingByPriority(cpuQosHoldLow);
					}
					ret = RTL_RX_PROCESS_RETURN_BREAK;
				}
				
				break;
			}
		case RTL_NICRX_REPEAT:
			ret = RTL_RX_PROCESS_RETURN_RETRY;
			break;
	}
	return ret;
}
#else	/*	defined(RTL_CPU_QOS_ENABLED)	*/
__MIPS16 __IRAM_FWD
static inline int32 rtl_processReceivedInfo(rtl_nicRx_info *info, int nicRxRet)
{
	int	ret;

	ret = RTL_RX_PROCESS_RETURN_BREAK;
	switch(nicRxRet)
	{
		case RTL_NICRX_OK:
			{
				ret = RTL_RX_PROCESS_RETURN_SUCCESS;
				break;
			}
		case RTL_NICRX_NULL:
			break;
		case RTL_NICRX_REPEAT:
			{
				ret = RTL_RX_PROCESS_RETURN_RETRY;
				break;
			}
	}
	return ret;
}
#endif	/*	defined(RTL_CPU_QOS_ENABLED)	*/

__MIPS16 __IRAM_FWD
static inline int32 rtl_decideRxDevice(rtl_nicRx_info *info)
{
	struct dev_priv	*cp;
	int32			pid, i, ret;
	struct sk_buff 	*skb;
#if defined(CONFIG_RTL_CUSTOM_PASSTHRU) || defined(CONFIG_RTL_STP)
	uint8*			data;
#endif
	#if defined(CONFIG_RTL_STP)
	int32 			dev_no;
	#endif

	pid = info->pid;
	skb = info->input;
#if defined(CONFIG_RTL_CUSTOM_PASSTHRU) || defined(CONFIG_RTL_STP)
	data = skb->data;
#endif

	info->priv = NULL;
	info->isPdev=FALSE;
	ret = FAILED;

#if defined(CONFIG_XDSL_CTRL_PHY_IS_SOC)
	if (*((uint16*)(skb->data+(ETH_ALEN<<1))) != __constant_htons(ETH_P_SLOW)){
		if (info->pid==0){
			pid = info->pid = 5;
		}
	}
#endif

	#if defined(CONFIG_RTL_STP)
	if ((data[0]&0x01) && !memcmp(data, STPmac, 5) && !(data[5] & 0xF0))
	{
		/* It's a BPDU */
		dev_no = re865x_stp_get_pseudodevno(pid);
		if (dev_no != NO_MAPPING)
		{
			info->priv = _rtl86xx_dev.stp_port[dev_no]->priv;
			ret = SUCCESS;
		/*	printk("receieved BPDU packet in nic form %s\n", skb->dev->name);	*/
		}
		else
		{
			dev_kfree_skb_any(skb);
			return FAILED;
		}
	}
	else
	#endif
	{
	#if 0
		for(i = 0; i < _rtl86xx_dev_num; i++)
		{
			cp = ((struct dev_priv *)_rtl86xx_dev.dev[i]->priv);
			//printk("=========%s(%d),cp(%s),i(%d)\n",__FUNCTION__,__LINE__,cp->dev->name,i);
			if(cp && cp->opened && (cp->portmask & (1<<pid)))
			{
				info->priv = cp;
				ret = SUCCESS;
				break;
			}
		}

		//printk("====%s(%d),dev(%s),i(%d)\n",__FUNCTION__,__LINE__,cp->dev->name,i);
		if(_rtl86xx_dev_num==i)
		{
			dev_kfree_skb_any(skb);
			return FAILED;
		}
	#else
		#if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION)
		if(pid == 7)
		{
			struct net_device *thisDev = NULL;
			if(info->rxExtspa == RTL_TXD_EXTSPA1)
			{
				info->pid = RTL_EXTPORT_P1;
				if(rtk_xdsl_hwnat_wifiDev[0])
					thisDev = rtk_xdsl_hwnat_wifiDev[0];
			}
			else if(info->rxExtspa == RTL_TXD_EXTSPA2)
			{
				info->pid = RTL_EXTPORT_P2;
				if(rtk_xdsl_hwnat_wifiDev[1])
					thisDev = rtk_xdsl_hwnat_wifiDev[1];
			}
			else
			{
				rtk_xdsl_hwnat_mbssid_mac_mapping_t *mapping;
				list_for_each_entry(mapping,&rtk_xdsl_hwnat_mbssid_mac_mappingListHead[skb->data[11]], list)
				{
					if(!memcmp(&mapping->macAddr.octet[0],&skb->data[6],ETH_ALEN))
					{
						thisDev = mapping->dev;
						info->pid = RTL_EXTPORT_P3;
						break;
					}
				}
			}
			if(thisDev)
			{
				cp = wifi_priv;
				cp->dev = thisDev;
				info->priv = cp;
				info->vid = RTL_LANVLANID;
				return SUCCESS;
			}
		}
		#endif

		i = portid_index[pid];
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
		cp = netdev_priv(_rtl86xx_dev.dev[i]);
#else
		cp = ((struct dev_priv *)_rtl86xx_dev.dev[i]->priv);
#endif
		if(cp && cp->opened && (cp->portmask & (1<<pid)))
		{
			info->priv = cp;
			ret = SUCCESS;
		}
		else
		{
			dev_kfree_skb_any(skb);
			return FAILED;
		}
	#endif
	#if defined(CONFIG_RTL_CUSTOM_PASSTHRU)
		if (SUCCESS==rtl_isPassthruFrame(data)&&(rtl_isWanDev(cp)==TRUE))
		{
			info->priv = _rtl86xx_dev.pdev->priv;
			info->isPdev=TRUE;	
		}
	#endif
		
	}
	
	return ret;
}

#if defined(BR_SHORTCUT)
__MIPS16 __IRAM_FWD
static inline int32 rtl_processBridgeShortCut(struct sk_buff *skb, struct dev_priv *cp_this)
{
	struct net_device *dev;
	
	if (
		#if 0
		(eth_flag & BIT(2)) &&
		#endif
	#if defined(CONFIG_WIRELESS_LAN_MODULE)
		(wirelessnet_hook_shortcut !=NULL ) && ((dev = wirelessnet_hook_shortcut(skb->data)) != NULL)
	#else
		((dev = get_shortcut_dev(skb->data)) != NULL)
	#endif							
	)
	{
		#if defined(CONFIG_RTL_HARDWARE_NAT)
		if (memcmp(&skb->data[ETH_ALEN], cp_this->dev->dev_addr, ETH_ALEN))
		#endif
		{
			memcpy(cached_eth_addr, &skb->data[ETH_ALEN], ETH_ALEN);
			cached_dev = cp_this->dev;	
		}
		/*skb->dev = dev;*/ /* for performance */
		#if defined(CONFIG_COMPAT_NET_DEV_OPS)
		dev->hard_start_xmit(skb, dev);
		#else
		dev->netdev_ops->ndo_start_xmit(skb,dev);
		#endif
		DEBUG_ERR("[%s][%d]-[%s]\n", __FUNCTION__, __LINE__, skb->dev->name);
		return SUCCESS;
	}
	return FAILED;
}
#endif

/* ysleu:RTL8685P alwalys trap IPv4 with options packets but we just want to flooding these packets for VTUO. */
#if defined(CONFIG_DSL_VTUO)
static inline int _rtl_PreProcessRxToProtocolStack(struct sk_buff *skb, rtl_nicRx_info *info)
{
	int needFlood=0;
	struct iphdr *iph=NULL;
	struct vlan_hdr *vhdr=NULL;
	unsigned int hlen;
	struct sk_buff *skb2;
	
	//printk("%s: proto=0x%x\n", __FUNCTION__, skb->protocol);
	skb2 = skb_clone(skb, GFP_ATOMIC);
	// need to keep srcPort, hard_start_xmit() will use it.
	skb2->srcPort = skb->srcPort;
	hlen = ETH_HLEN;
	if (skb2->protocol == __constant_htons(ETH_P_8021Q)) {
		vhdr = (struct vlan_hdr *)(skb2->data);
		skb2->protocol = vhdr->h_vlan_encapsulated_proto;
		skb_pull(skb2, VLAN_HLEN);
		hlen += VLAN_HLEN;
	}
	
	// vtuo uses hwLookup, and the packet sent from CPU will cause smac learned
	// on cpu port, this will make successive packets be send to wrong interface.
	// It needs to fix hwLookup issue to enable PPPoE session support.
	/*
	if (skb2->protocol == __constant_htons(ETH_P_PPP_SES)) {
		//printk("PPPoE session\n");
		skb_pull(skb2, 6); // pppoe
		hlen += 6;
		skb2->protocol = get_unaligned_be16(skb2->data);
		if (skb2->protocol == 0x21) { // IP
			skb2->protocol = ETH_P_IP;
			skb_pull(skb2, 2); // PPP
			hlen += 2;
		}
	}
	*/
	
	if(skb2->protocol==ETH_P_IP)
	{
		iph = (struct iphdr *)skb2->data;
		
		if(iph->ihl>0x5)
		{
			//IP with option packets
			const unsigned char *dest = eth_hdr(skb2)->h_dest;
			int i;
			rtl865x_netif_local_t *netifTbl;
			
			if(is_multicast_ether_addr(dest))
			{
				//multicast
				//This packet should be flooding.
				needFlood=1;
			}
			else if(is_broadcast_ether_addr(dest))
			{
				//Broadcast: to Protocol Stack
				//return SUCCESS;
			}
			else 
			{
				//Unicast
				extern rtl865x_netif_local_t * getNetifTbl(void);
				int isGatewayMAC=0;
				netifTbl =  getNetifTbl();
				for(i=0; i<NETIF_SW_NUMBER; i++)
				{
					if(!netifTbl[i].valid) continue;
					//Check Dmac==GMAC
					if(!((netifTbl[i].macAddr.octet[0]^dest[0])|(netifTbl[i].macAddr.octet[1]^dest[1])|(netifTbl[i].macAddr.octet[2]^dest[2])|
							(netifTbl[i].macAddr.octet[3]^dest[3])|(netifTbl[i].macAddr.octet[4]^dest[4])|(netifTbl[i].macAddr.octet[5]^dest[5])))
					{
						isGatewayMAC=1;
						break;
					}
				}
				if(!isGatewayMAC) needFlood=1;
			}
		}

		if(needFlood)
		{
#if defined(CONFIG_RTL_IGMP_SNOOPING)
			// go flooding
			skb_push(skb2, hlen);
			skb2->dev->hard_start_xmit(skb2,skb2->dev);
			if(iph && iph->protocol == IPPROTO_IGMP)
			{
				// and go protocol stack.
				return SUCCESS;
			}
			else
			{
				// Just flooding
				return FAILED;
			}
#else
			skb_push(skb2, hlen);
			skb2->dev->hard_start_xmit(skb2,skb2->dev);
			return FAILED;
#endif
		}
	}
	dev_kfree_skb_any(skb2);
	return SUCCESS;
}
#endif

#if !defined(CONFIG_RTL_MULTI_LAN_DEV)
/*
	On Single LAN mode, LAN port flooding is handled by driver due to ACL default trap.
	@ return: 
		SUCCESS -Kernel continue processing
		FAILED - Forwading by driver and stop.
*/
static inline int _rtl_PreProcessRxLanPortForwarding(struct sk_buff *skb)
{
	struct vlan_hdr *vhdr=NULL;
	unsigned int hlen;
	struct sk_buff *skb2;
	const unsigned char *dest = NULL;
	unsigned int txPortMask=0;
	int ret;
	
	if(strncmp(skb->dev->name,"eth0",4))
		return SUCCESS;

	//printk("%s: proto=0x%x\n", __FUNCTION__, skb->protocol);
	dest = eth_hdr(skb)->h_dest;
	ret = _rtl_getPortlistByMac(dest,&txPortMask,RTL_LAN_FID);
	//printk("Get TxPortMask:%x by mac:%pM ret:%d @ %s\n",txPortMask,dest,ret,__func__);
	if(is_broadcast_ether_addr(dest) || is_multicast_ether_addr(dest) || ret==FAILED || (txPortMask&RTL_LANPORT_MASK))
	{
		skb2 = skb_clone(skb, GFP_ATOMIC);

		// need to keep srcPort, hard_start_xmit() will use it.
		skb2->srcPort = skb->srcPort;
		hlen = ETH_HLEN;
		if (skb2->protocol == __constant_htons(ETH_P_8021Q)) {
			vhdr = (struct vlan_hdr *)(skb2->data);
			skb2->protocol = vhdr->h_vlan_encapsulated_proto;
			skb_pull(skb2, VLAN_HLEN);
			hlen += VLAN_HLEN;
		}

		// Tx
		skb_push(skb2, hlen);
		skb2->dev->netdev_ops->ndo_start_xmit(skb2,skb2->dev);
		#if !defined(CONFIG_RTL8685SB) && !defined(CONFIG_REMOTE_ADSL_PHY)
		if(ret==SUCCESS && txPortMask&RTL_LANPORT_MASK)
		{
			//Add H/W forwarding rules and you don't have to send packet to protocol stack.
			rtl8676_add_L2Unicast_hwacc(eth_hdr(skb)->h_source,eth_hdr(skb)->h_dest,skb->dev->name,skb->dev->name);
			return FAILED;
		}
		#endif
	}

	return SUCCESS;
}

#endif

#if defined(CONFIG_XDSL_CTRL_PHY_IS_SOC)
int (*xdsl_ctrl_atmpkt_receive_hook)(struct sk_buff* skb)=NULL;
EXPORT_SYMBOL(xdsl_ctrl_atmpkt_receive_hook);

int (*xdsl_ctrl_ptmpkt_receive_hook)(struct sk_buff* skb)=NULL;
EXPORT_SYMBOL(xdsl_ctrl_ptmpkt_receive_hook);
#endif

#if defined(CONFIG_REMOTE_ADSL_PHY)
extern int rtk_adslphy_eth2sar(struct sk_buff *skb);
#endif


__MIPS16 __IRAM_FWD
static inline void rtl_processRxToProtcolStack(struct sk_buff *skb, struct dev_priv *cp_this, rtl_nicRx_info *info)
{
    // Mason Yu. 
#if defined(CONFIG_RTK_8023AH)    
    //char smac[6], dmac[6];
    char ether_type_8023ah[2] = {0x88, 0x09};
#endif
   
    #if defined(CONFIG_XDSL_CTRL_PHY_IS_SOC)
    if (*((uint16*)(skb->data+(ETH_ALEN<<1))) == __constant_htons(ETH_P_ATMMPOA)){
        if (xdsl_ctrl_atmpkt_receive_hook){
            if ( (*xdsl_ctrl_atmpkt_receive_hook)(skb) )
                return;
        }
    } else if (*((uint16*)(skb->data+(ETH_ALEN<<1))) != __constant_htons(ETH_P_SLOW)){
        if (xdsl_ctrl_ptmpkt_receive_hook){
            if ( (*xdsl_ctrl_ptmpkt_receive_hook)(skb) )
                return;
			else
				skb->protocol = eth_type_trans(skb, skb->dev);
        } else
			skb->protocol = eth_type_trans(skb, skb->dev);
	} 
	else
    #endif 
	
	// Mason Yu.
#if defined(CONFIG_RTK_8023AH)
	if (efm_8023ah_par_action == LOOPBACK) {
		if (memcmp(skb->from_dev->name, efm_ifname, sizeof(efm_ifname)) == 0) {		
			if ( memcmp(skb->data+12, ether_type_8023ah, 2) != 0) {
				#if 0
				memcpy(dmac, skb->data, ETH_ALEN);
				memcpy(smac, skb->data+6, ETH_ALEN);
				memcpy(skb->data, smac, ETH_ALEN);
				memcpy(skb->data+6, dmac, ETH_ALEN);
				#endif
				efm_stats_rx(skb);
				skb->dev = skb->from_dev;	
				skb->from_dev->netdev_ops->ndo_start_xmit(skb,skb->from_dev);
				efm_stats_tx(skb);
				return;
			}
		}
	}
#endif
	
	skb->protocol = eth_type_trans(skb, skb->dev);
	skb->ip_summed = CHECKSUM_NONE;
	//printk("[%s][%d]-skb->dev[%s],proto(0x%x)\n", __FUNCTION__, __LINE__, skb->dev->name,skb->protocol);

	/* ysleu:8685P patch for IP with option frame. */
#if defined(CONFIG_DSL_VTUO)
	if(_rtl_PreProcessRxToProtocolStack(skb,info)!=SUCCESS) {
		dev_kfree_skb_any(skb);
		return;
	}
#endif
 
#if !defined(CONFIG_RTL_MULTI_LAN_DEV)
	// In Single LAN mode, NIC driver need to do LAN ports forwarding due to kernel source device filtering. i,e. Kernel would not forwarding packet from eth0 to eth0.
	#ifndef CONFIG_REMOTE_ADSL_PHY
	if(_rtl_PreProcessRxLanPortForwarding(skb)!=SUCCESS) {
		dev_kfree_skb_any(skb);
		return;
	}
	#endif
#endif

#ifdef CONFIG_RTL_8676HWNAT
	#ifdef CONFIG_RTL_MULTI_LAN_DEV
	skb->vlan_member = cp_this->port_member;
	skb->src_port = IF_SWITCH;
	skb->switch_port = skb->dev->name;	
	#else
	/* For non-MULTI_LAN, we have only eth0, but user program expects eth0.2-5 for IPQoS to work. - andrew */
	do {
		const char *ifnames[] = { ALIASNAME_ELAN_PREFIX "2", ALIASNAME_ELAN_PREFIX "3", ALIASNAME_ELAN_PREFIX "4", ALIASNAME_ELAN_PREFIX "5" };
		skb->vlan_member = cp_this->port_member;
		skb->src_port = IF_SWITCH;
		if ((info->pid >= 1) && (info->pid <= 4))		
			skb->switch_port = (char *)ifnames[info->pid - 1];
		else
			skb->switch_port = skb->dev->name;
		//printk("%s(%d): pid=%d(%s)\n",__FUNCTION__,__LINE__,pid, skb->switch_port);
	} while (0);
	#endif
#endif

#if defined(RX_TASKLET)
	#if defined(CONFIG_RTL_LOCAL_PUBLIC)
	skb->localPublicFlags = 0;
	#endif

	//netif_receive_skb(skb);
	netif_rx(skb);
#else	/*	defined(RX_TASKLET)	*/

	netif_rx(skb);
#endif	/*	defined(RX_TASKLET)	*/
}

#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER) && defined(CONFIG_XDSL_ROMEDRIVER)
enum {
	RE8670_RX_STOP=0,
	RE8670_RX_CONTINUE,
	RE8670_RX_STOP_SKBNOFREE,
	RE8670_RX_END
};

extern int fwdEngine_rx_skb(struct re_private * cp,struct sk_buff * skb,struct rx_info * pRxInfo);
#endif

#if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL)
void (*xdsl_ctrl_nic2sar_hook)(struct sk_buff* skb)=NULL;
EXPORT_SYMBOL(xdsl_ctrl_nic2sar_hook);
#endif

__MIPS16 __IRAM_FWD
static inline void rtl_processRxFrame(rtl_nicRx_info *info)
{
	struct dev_priv	*cp_this;
	struct sk_buff 	*skb;
	uint32			vid;
	uint8			*data;

	cp_this = info->priv;
	skb = info->input;	
	vid = info->vid;
	data = skb->tail = skb->data;
	
	/*	sanity check	*/
	#if 0
	if  ((memcmp(&data[ETH_ALEN], cp_this->dev->dev_addr, ETH_ALEN)==0)||PKTHDR_EXTPORT_MAGIC2==vid||PKTHDR_EXTPORT_MAGIC==vid)// check source mac
	{
		if ((PKTHDR_EXTPORT_MAGIC!=vid)||(info->pid!=PKTHDR_EXTPORT_P3))
		{
			
			cp_this->net_stats.rx_dropped++;
			dev_kfree_skb_any(skb);
			return;
		}
	}	
	#else
	//Kevin, now we only use extPort3 (port8) and check source mac
	if(  vid==PKTHDR_EXTPORT_MAGIC || vid==PKTHDR_EXTPORT_MAGIC2 )
	{
		cp_this->net_stats.rx_dropped++;
        dev_kfree_skb_any(skb);
       	return;
	}
	#endif
	/*	sanity check end 	*/
	
	//skb->len = 0;
	skb_put(skb, info->len);
	#if defined(CONFIG_RTL_CUSTOM_PASSTHRU)
	skb->dev=info->isPdev?_rtl86xx_dev.pdev:info->priv->dev;
	#else
	skb->dev=info->priv->dev;
	#endif

	skb->from_dev = skb->dev;
	//skb->dev=cp_this->dev;

#if defined(CONFIG_NETFILTER_XT_MATCH_PHYPORT)
	skb->srcPhyPort=(uint8)info->pid;
#endif
	//printk("=======%s(%d),cp_this(%s)\n",__FUNCTION__,__LINE__,cp_this->dev->name);
	/*	vlan process (including strip vlan tag)	*/
	#if defined(CONFIG_RTK_VLAN_SUPPORT)
	if (rtk_vlan_support_enable && cp_this->vlan_setting.global_vlan) 
	{
		if (rx_vlan_process(cp_this->dev, &cp_this->vlan_setting, skb))
		{
			cp_this->net_stats.rx_dropped++;
			dev_kfree_skb_any(skb);		
			return;
		}

		#if defined(CONFIG_RTK_VLAN_FOR_CABLE_MODEM)								
		if(rtk_vlan_support_enable == 2)
		{
			DEBUG_ERR("====%s(%d),cp(%s),cp->vlan.id(%d),skb->tag.vid(%d)\n",__FUNCTION__,__LINE__,
				cp_this->dev->name,cp_this->vlan_setting.id,skb->tag.f.pci&0xfff);
			if(cp_this->vlan_setting.vlan && skb->tag.f.tpid == htons(ETH_P_8021Q) && (skb->tag.f.pci & 0xfff) != cp_this->vlan_setting.id)
			{
				if(cp_this->vlan_setting.is_lan == 0)
				{
					struct net_device* vap;
					/*	look up vap	*/
					vap = get_dev_by_vid(skb->tag.f.pci & 0xfff);											
					if(vap)
					{
						
						skb->dev = vap;
						vap->netdev_ops->ndo_start_xmit(skb,vap);
						return;
					}
				}
			}
		}
		#endif
	}
	#endif	/*	defined(CONFIG_RTK_VLAN_SUPPORT)	*/

	#if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION)
	if(!info->rxOrg && info->rxFwd)
	{
		rtk_xdsl_hwnat_mbssid_mac_mapping_t *mapping;
		struct net_device *thisDev = NULL;

		if(info->dpext == RTL_RXD_DPEXT1)
			thisDev = rtk_xdsl_hwnat_wifiDev[0];
		else if(info->dpext == RTL_RXD_DPEXT2)
			thisDev = rtk_xdsl_hwnat_wifiDev[1];
		else if(info->dpext == RTL_RXD_DPEXT3)
		{
			list_for_each_entry(mapping,&rtk_xdsl_hwnat_mbssid_mac_mappingListHead[skb->data[5]], list)
			{
				if(!memcmp(&mapping->macAddr.octet[0],skb->data,ETH_ALEN))
				{
					thisDev = mapping->dev;
					break;
				}
			}
		}
		if(thisDev)
		{
			skb->dev = thisDev;
			#if defined(CONFIG_RTL_XDSL_WIFI_IPI)
			if(rtk_xdsl_smp_wlanTx_ipi_enable)
				rtk_xdsl_smp_wlan_tx_dispatch(skb);
			else
			#endif
			thisDev->netdev_ops->ndo_start_xmit(skb,thisDev);
			return;
		}
	}
	#endif

	#if defined(CONFIG_REMOTE_ADSL_PHY)
        if (*((uint16*)(data+(ETH_ALEN<<1))) == __constant_htons(ETH_P_ATMMPOA))
        {
                rtk_adslphy_eth2sar(skb);
                return ;
        } else
    #endif

    #if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL)
	if (*((uint16*)(data+(ETH_ALEN<<1))) == __constant_htons(ETH_P_ATMMPOA)) 
	{
		vid = *((unsigned short *)(data+(ETH_ALEN<<1)+2));
		// for atm sar data packet
		if (vid & 0x1000){
			if (xdsl_ctrl_nic2sar_hook){
				(*xdsl_ctrl_nic2sar_hook)(skb);
				return;
			}
		}
	}
	#endif		

	if (*((uint16*)(data+(ETH_ALEN<<1))) == __constant_htons(ETH_P_8021Q)) 
	{
		vid = *((unsigned short *)(data+(ETH_ALEN<<1)+2));
		#if	defined(CONFIG_RTL_QOS_8021P_SUPPORT)
		skb->srcVlanPriority = (vid>>13)&0x7;
		#endif
		//skb->mark = ((vid>>13)&0x7)+1;
		//vid &= 0x0fff;
	}
	
	/*	update statistics	*/
	#ifndef CONFIG_RTL_NIC_HWSTATS
	cp_this->net_stats.rx_packets++;	
	cp_this->net_stats.rx_bytes += skb->len;	
	#endif
	cp_this->dev->last_rx = jiffies;
	/*	update statistics end	*/

	/*  Kevin , handle the pkts towarding to extPort*/
	#if defined(CONFIG_RTL_HARDWARE_NAT)	&& defined(CONFIG_RTL8676_Static_ACL)
	if(vid==PKTHDR_EXTPORT_MAGIC3)
	{
		skb->acl_forward_to_extPort=1;
    
	}
	#endif

	#if defined(CONFIG_RTL_SW_FDB_LEARNING)
	//printk("[DEBUG] Learning SMAC:%pM skb->dev:%s @ %s %d\n",data,skb->dev->name,__func__,__LINE__);
	if(strncmp(skb->dev->name,"wlan",4))
		rtl865x_smac_learning((void *)(data+(ETH_ALEN)),skb->dev,vid,info->pid);
	#endif

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST) || defined(CONFIG_RTL_UNKN_UNICAST_STORM_CONTROL) || defined(CONFIG_DSL_VTUO) || !defined(CONFIG_RTL_MULTI_LAN_DEV) || defined(CONFIG_REMOTE_ADSL_PHY)
	skb->srcVlanId=info->vid;
	skb->srcPort=info->pid;
	#endif

#ifdef CONFIG_PORT_MIRROR
	if(IN_NEED_MIR(cp_this->mirror_mode)) {
		switchcore_mirror_pkt(skb, IN);
	}
#endif	

/*romedriver modify Boyce 2014-07-10*/
#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER) && defined(CONFIG_XDSL_ROMEDRIVER)
{
	int retCode;
	struct re_private cp;
	bzero(&cp,sizeof(struct re_private));
	
	cp.dev = cp_this->dev;
	cp.lock=cp_this->lock;
	cp.msg_enable=cp_this->msg_enable;
#if !defined(CONFIG_FAST_FORWARDING)
	cp.rx_tasklets = cp_this->rx_dsr_tasklet;
	cp.tx_tasklets = cp_this->tx_dsr_tasklet;
#endif

/* lose cp	config*/

//	cp.cp_stats
//	cp.default_rxdesc_Mbuf
//	cp.default_rx_desc
//	cp.default_rx_skb
//	cp.dropping_frag
//	cp.frag_skb
//	cp.isr1_status
//	cp.isr_status
//	cp.jumboFrame
//	cp.jumboLength
//	cp.multiWanDev
//	cp.old_rx_Mtail
//	cp.old_tx_Mhqhead
//	cp.old_tx_Mhqtail
//	cp.pdev
//	cp.port2dev
//	cp.port2rxfunc
//	cp.regs
//	cp.ring_dma
//	cp.rxdesc_Mbuf
//	cp.rx_buf_sz
//	cp.rx_config
//	cp.rx_Mring
//	cp.rx_Mtail
//	cp.rx_skb
//	cp.txdesc_Mbuf
//	cp.tx_Mhqhead
//	cp.tx_Mhqring
//	cp.tx_Mhqtail
//	cp.tx_skb
//	cp.wanInterfaceIdx

/* lose cp_this config	*/
//	cp_this->netinit
//	cp_this->net_stats
//	cp_this->opened
//	cp_this->portmask	!!!!!!!!!!
//	cp_this->portnum	!!!!!!!!!!
//	cp_this->port_member!!!!!!!!!!
//	cp_this->vlan_setting;
//	cp_this->vlgrp
//	cp_this->expire_timer;
//	cp_this->id;
//	cp_this->irq_owner;
//	cp_this->link_dsr_tasklet

	retCode = fwdEngine_rx_skb(&cp,skb,&(info->RxInfo));
	//if we receive packet during initial state, goto protocol stack bypass fwdEngine!!
//	printk("fwdEngine_rx_skb retCode = %d \n",retCode);

	if(retCode==RE8670_RX_STOP_SKBNOFREE){	//return without free skb
		return ;
	}
	else if(retCode!=RE8670_RX_CONTINUE /*&& cur->priority!=0*/){
		if(skb){
			__kfree_skb(skb);	
		}
		return;
	}else{ //
//		printk("fwdEngine_rx_skb to rtl_processRxToProtcolStack = %d \n",retCode);
		rtl_processRxToProtcolStack(skb, cp_this, info);
	}
}
#else  //CONFIG_XDSL_LITEROMEDRIVER or original data flow 

	/*	finally successfuly rx to protocol stack	*/
	rtl_processRxToProtcolStack(skb, cp_this, info);
#endif

}

#if defined(CONFIG_RTL_MULTI_WAN)
extern void open_smux_device(char* ifname);
#endif

void open_eth_device(void)
{
	int i;
	for(i = 0; i < _rtl86xx_dev_num; i++)
	{
		if (netif_queue_stopped(_rtl86xx_dev.dev[i])){
			netif_wake_queue(_rtl86xx_dev.dev[i]);
			#if defined(CONFIG_RTL_MULTI_WAN)
			if (_rtl86xx_dev.dev[i]->name[0] == 'n' || _rtl86xx_dev.dev[i]->name[0] == 'p'){
				open_smux_device(_rtl86xx_dev.dev[i]->name);		
			}
			#endif
		}
	}
}

//#if defined(CONFIG_RTL_MULTI_WAN)
//extern void close_smux_device(char* ifname);
//#endif

//void close_eth_device(void)
//{
//	int i;
//	for(i = 0; i < _rtl86xx_dev_num; i++)
//	{
//		netif_stop_queue(_rtl86xx_dev.dev[i]);
//		#if defined(CONFIG_RTL_MULTI_WAN)
//		if (_rtl86xx_dev.dev[i]->name[0] == 'n' || _rtl86xx_dev.dev[i]->name[0] == 'p'){
//			close_smux_device(_rtl86xx_dev.dev[i]->name);		
//		}
//		#endif
//	}
//}

__IRAM
static void rtl_hwstats_Handler(unsigned long arg)
{
	int tx_pkts=0,tx_ucast=0,tx_mcast=0,tx_bcast=0,rx_pkts=0,rx_ucast=0,rx_mcast=0,rx_bcast=0;
	int i;
	uint32 addrOffset_fromP0;
 
	for(i = 0; i < RTL8651_PORT_NUMBER; i++) 
	{

		//tx
		tx_pkts = tx_ucast = tx_mcast = tx_bcast = 0;
		addrOffset_fromP0 = i * MIB_ADDROFFSETBYPORT;
		tx_ucast = rtl8651_returnAsicCounter( OFFSET_IFOUTUCASTPKTS_P0 + addrOffset_fromP0 );
		tx_mcast = rtl8651_returnAsicCounter( OFFSET_IFOUTMULTICASTPKTS_P0 + addrOffset_fromP0 );
		tx_bcast = rtl8651_returnAsicCounter( OFFSET_IFOUTBROADCASTPKTS_P0 + addrOffset_fromP0 );
		tx_pkts = tx_ucast + tx_mcast + tx_bcast;

		if(RTL_HWSTATS_TXPKTS[i]>tx_pkts)
			RTL_HWSTATS_TXPKTS_OVERFLOW[i]++;
		RTL_HWSTATS_TXPKTS[i] = tx_pkts;

		//rx
		rx_pkts = rx_ucast = rx_mcast = rx_bcast = 0;
		rx_ucast = rtl8651_returnAsicCounter( OFFSET_IFINUCASTPKTS_P0 + addrOffset_fromP0 );
		rx_mcast = rtl8651_returnAsicCounter( OFFSET_ETHERSTATSMULTICASTPKTS_P0 + addrOffset_fromP0 );
		rx_bcast = rtl8651_returnAsicCounter( OFFSET_ETHERSTATSBROADCASTPKTS_P0 + addrOffset_fromP0 );
		rx_pkts = rx_ucast + rx_mcast + rx_bcast;

		if(RTL_HWSTATS_RXPKTS[i]>rx_pkts)
			RTL_HWSTATS_RXPKTS_OVERFLOW[i]++;
		RTL_HWSTATS_RXPKTS[i] = rx_pkts;
	}
	addrOffset_fromP0 = RTL8651_CPU_PORTNUMBER * MIB_ADDROFFSETBYPORT;
	tx_ucast = rtl8651_returnAsicCounter( OFFSET_IFOUTUCASTPKTS_P0 + addrOffset_fromP0 );
	tx_mcast = rtl8651_returnAsicCounter( OFFSET_IFOUTMULTICASTPKTS_P0 + addrOffset_fromP0 );
	tx_bcast = rtl8651_returnAsicCounter( OFFSET_IFOUTBROADCASTPKTS_P0 + addrOffset_fromP0 );
	tx_pkts = tx_ucast + tx_mcast + tx_bcast;
	if(RTL_HWSTATS_CPU_TXPKTS>tx_pkts)
		RTL_HWSTATS_CPU_TXPKTS_OVERFLOW++;
	RTL_HWSTATS_CPU_TXPKTS = tx_pkts;

	mod_timer(&rtl_hwstats_timer, jiffies + 3*HZ);
}

#ifdef RX_NAPI
__IRAM
static void nicTx_Done_Handler(unsigned long arg)
{
	int32	idx;

	for(idx=RTL865X_SWNIC_TXRING_MAX_PKTDESC-1;idx>=0;idx--)
	{
		RTL_swNic_txDone(idx); 
	}

	if (avail_txdscp_num(0) > NIC_TX_THRESHOLD){
		open_eth_device();
	}

	mod_timer(&txbuff_gc_timer, jiffies + 30*HZ);
}

__IRAM_FWD
static uint32 rtl_rx(uint32 budget)
{
	int	ret;
	rtl_nicRx_info	info;
	unsigned int rxpktgood=0;

	if(unlikely(eth_poll == 1))
		budget = 1;
	
	while (rxpktgood < budget) 
	{
		#if !RX_ONLY_RING0
		info.priority = RTL_ASSIGN_RX_PRIORITY;
		#endif
		ret = swNic_receive(&info);
		printk("%s(%d):%p=%d\n",__func__,__LINE__,swNic_receive,ret);

#ifdef CONFIG_FAST_FORWARDING
		if (ret == RTL_NICRX_FF)
			goto NEXT;
#endif//end of CONFIG_FAST_FORWARDING
		ret = rtl_processReceivedInfo(&info, ret);

		if (RTL_RX_PROCESS_RETURN_SUCCESS==ret)
		{
			ret = rtl_decideRxDevice(&info);
			if (SUCCESS==ret)
			{
				rtl_processRxFrame(&info);
			}
			else
			{
				continue;
			}
		}
		else if (RTL_RX_PROCESS_RETURN_BREAK==ret)
		{
			break;
		}
		else if (RTL_RX_PROCESS_RETURN_RETRY==ret)
		{
			rxpktgood--;	
		}

#ifdef CONFIG_FAST_FORWARDING
NEXT:
#endif//end of CONFIG_FAST_FORWARDING
		rxpktgood++;
	}

	return rxpktgood;
}

__IRAM_FWD
static int32 rtl_enet_poll_napi(struct napi_struct *napi, int budget)
{
	unsigned int work_done ;
	unsigned long flags;
	unsigned int work_to_do = budget;
	work_done = rtl_rx(work_to_do);	

	if (work_done < work_to_do)
	{
		local_irq_save(flags);
		__napi_complete(napi);
		REG32(CPUIIMR) |= (RX_DONE_IP_ALL|PKTHDR_DESC_RUNOUT_IE_ALL|MBUF_DESC_RUNOUT_IE_ALL);
		local_irq_restore(flags);
	}

	return work_done;
}
#endif

//add poll support when dsl negotiation
void eth_poll_schedule(void)
{
#ifdef RX_TASKLET
	//rtl_rxSetTxDone(FALSE);
	tasklet_hi_schedule(eth_rx_tasklets);
#else
#ifdef RX_NAPI
	if (likely(napi_schedule_prep(&rtl_napi)))
		__napi_schedule(&rtl_napi);
#endif
#endif
}
//end of add

#if defined(CONFIG_RTK_PTM_US_CTRL)
extern int ptm_used_desc_thres;
extern struct net_device *ptmdev;
extern int any_port;

#if defined(CONFIG_RTL_MULTI_WAN)
extern void open_smux_dev_bydev(const struct net_device *dev);
#endif

static inline int isPTMPortCongestion(void) {
	unsigned int regData;

	if (ptmdev && netif_running(ptmdev) && netif_carrier_ok(ptmdev)) {
		//read port 5, reg id 1 (port 5, queue 2)
		regData = READ_MEM32(P0_DCR0+(5<<4)+(1<<2));
		if ((regData&Pn_EQDSCR_MASK)>>Pn_EVEN_OQDSCR_OFFSET > ptm_used_desc_thres) {
			return 1;
		}
	}
	
	return 0;
}

static inline int isLANPortCongestion(int i) {
	unsigned int regData;

	regData = READ_MEM32(P0_DCR0+(i<<4)+(1<<2));
	if ((regData&Pn_EQDSCR_MASK)>>Pn_EVEN_OQDSCR_OFFSET > ptm_used_desc_thres) {
		return 1;
	}	
	
	return 0;
}

static inline int isAnyPortCongestion(void) {
	int ret, p;

	if (!any_port)
		return 0;

	ret = 0;
	for (p = 0; p < 4; p++) {
		ret = isLANPortCongestion(p);
		if (ret)
			break;
	}
	
	return ret;
}

static void try_wake_ptm_dev(void) {
	//wake up PTM dev if it's linkup and the queue is stopped
	if (!isPTMPortCongestion()) {
		if (ptmdev && netif_queue_stopped(ptmdev)) {
			netif_wake_queue(ptmdev);
			#if defined(CONFIG_RTL_MULTI_WAN)
			open_smux_dev_bydev(ptmdev);
			#endif
		}
	}
}

static void try_wake_lan_dev(void) {
	int i;
	for(i = 0; i < 4; i++) {
		if (!isLANPortCongestion(i)) {
			if (_rtl86xx_dev.dev[i] && netif_queue_stopped(_rtl86xx_dev.dev[i])){
				netif_wake_queue(_rtl86xx_dev.dev[i]);
			}
		}
	}	
}
#endif

#if !defined(RX_NAPI)
__IRAM_FWD
static int32 interrupt_dsr_rx(unsigned long task_priv)
{
#ifdef RX_TASKLET
	struct dev_priv *cp = (struct dev_priv *)task_priv;
#endif
	int	ret;
	rtl_nicRx_info	info;
	#ifdef RX_TASKLET
	unsigned long flags;
	#endif
	
	int rx_work;
	if(unlikely(eth_poll==1))
		rx_work = 1;
	else
		rx_work = NUM_RX_PKTHDR_DESC;
	
	while (rx_work--) 
	{
#if defined(CONFIG_RTK_PTM_US_CTRL)
		try_wake_ptm_dev();
		if (any_port)
			try_wake_lan_dev();
#endif
		
		info.priority = RTL_ASSIGN_RX_PRIORITY;	
		ret = RTL_swNic_receive(&info);	
		//printk("%s(%d):%p=%d\n",__func__,__LINE__,swNic_receive,ret);
		
		ret = rtl_processReceivedInfo(&info, ret);

		if (RTL_RX_PROCESS_RETURN_SUCCESS==ret)
		{
			ret = rtl_decideRxDevice(&info);
			if (SUCCESS==ret)
			{
				rtl_processRxFrame(&info);
			}
			else
			{				
				continue;
			}
		}
		else if (RTL_RX_PROCESS_RETURN_BREAK==ret)
		{
			break;
		}
		else if (RTL_RX_PROCESS_RETURN_RETRY==ret)
		{
			rx_work = 0;
			break;
		}
	}

#ifdef RX_TASKLET
	local_irq_save(flags);
	/* if rx tasklet is enabled, then dont need to enable rx interrupt, as driver will polling rx ring to process packet */
	if (!rx_work && eth_poll==0)
		tasklet_schedule(&cp->rx_dsr_tasklet);
	else
		REG32(CPUIIMR) |= (RX_DONE_IP_ALL|PKTHDR_DESC_RUNOUT_IE_ALL|MBUF_DESC_RUNOUT_IE_ALL);
	//krammer: don't clear other isr
	REG32(CPUIISR) = (PKTHDR_DESC_RUNOUT_IE_ALL|MBUF_DESC_RUNOUT_IE_ALL);
	local_irq_restore(flags);
#endif

	return RTL_NICRX_OK;
}
#endif

extern int avail_txdscp_num(int idx);

#if defined(CONFIG_RTL_SWITCH_NEW_DESCRIPTOR)
extern unsigned int New_avail_txdscp_num(int idx);
#endif

#ifndef RX_NAPI
__IRAM_FWD
static void interrupt_dsr_tx(unsigned long task_priv)
{
	int32	idx;
	#ifdef TX_TASKLET
	unsigned long flags;
	#endif

	for(idx=RTL865X_SWNIC_TXRING_MAX_PKTDESC-1;idx>=0;idx--)
	{
		RTL_swNic_txDone(idx); 
	}

#if defined(CONFIG_RTL_SWITCH_NEW_DESCRIPTOR)
	#if defined(CONFIG_RTK_PTM_US_CTRL)
	if (New_avail_txdscp_num(0) > NIC_TX_THRESHOLD && !isPTMPortCongestion() && !isAnyPortCongestion())
	#else
	if (New_avail_txdscp_num(0) > NIC_TX_THRESHOLD)
	#endif
	{
		open_eth_device();
	}
#else
	if (avail_txdscp_num(0) > NIC_TX_THRESHOLD){
		open_eth_device();
	}
#endif

#if !defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
	refill_rx_skb();
#endif//end of !CONFIG_RTL_ETH_PRIV_SKB_ADV
	#ifdef TX_TASKLET
	local_irq_save(flags);
	REG32(CPUIIMR) |= TX_ALL_DONE_IE_ALL; 
	local_irq_restore(flags);
	#endif
}
#endif

#ifdef CONFIG_RTL8196C_ETH_IOT
static int re865x_setPhyGrayCode(void)
{
    uint32 agc, cb0, snr, new_port_link_sts=0, val;
    uint32 DUT_eee, Linkpartner_eee;
    int i;

    /************ Link down check ************/
    for(i=0; i<RTL8651_PHY_NUMBER; i++) {
		if ((REG32(PSRP0 + (i * 4)) & LinkDownEventFlag) != 0){	// !!! LinkDownEventFlag is a read clear bit, so these code must ahead of "Link up check"
				if ((REG32(PSRP0 + (i * 4)) & PortStatusLinkUp) == 0) {
					/*=========== ###01 ===========*/
					extern void set_gray_code_by_port(int);
					set_gray_code_by_port(i);

					/*=========== ###03 ===========*/
					// read DUT eee 100 ability
					rtl8651_setAsicEthernetPHYReg( i, 13, 0x7 );
					rtl8651_setAsicEthernetPHYReg( i, 14, 0x3c );
					rtl8651_setAsicEthernetPHYReg( i, 13, 0x4007 );
					rtl8651_getAsicEthernetPHYReg( i, 14, &DUT_eee );

					// due to the RJ45 cable is plug out now, we can't read the eee 100 ability of link partner.
					// we use the variable port_linkpartner_eee to keep link partner's eee 100 ability after RJ45 cable is plug in.
					if (  ( ((DUT_eee & 0x2) ) == 0)  || ((port_linkpartner_eee & (1<<i)) == 0) )  {
						rtl8651_getAsicEthernetPHYReg( i, 21, &val );
						val = val & ~(0x4000);
						rtl8651_setAsicEthernetPHYReg( i, 21, val );
					}
				}
			}
	}

	/************ Link up check ************/
	for(i=0; i<RTL8651_PHY_NUMBER; i++) {
		if ((REG32(PSRP0 + (i * 4)) & PortStatusLinkUp) != 0) {
			
			if ((port_link_sts & (1<<i)) == 0) {	// the port which already linked up does not need to check ...

				/*=========== ###03 ===========*/
				// read DUT eee 100 ability
				rtl8651_setAsicEthernetPHYReg( i, 13, 0x7 );
				rtl8651_setAsicEthernetPHYReg( i, 14, 0x3c );
				rtl8651_setAsicEthernetPHYReg( i, 13, 0x4007 );
				rtl8651_getAsicEthernetPHYReg( i, 14, &DUT_eee );

				// read Link partner eee 100 ability
				rtl8651_setAsicEthernetPHYReg( i, 13, 0x7 );
				rtl8651_setAsicEthernetPHYReg( i, 14, 0x3d );
				rtl8651_setAsicEthernetPHYReg( i, 13, 0x4007 );
				rtl8651_getAsicEthernetPHYReg( i, 14, &Linkpartner_eee );

				if (  ( ((DUT_eee & 0x2) ) != 0)  && ( ((Linkpartner_eee & 0x2) ) != 0) )  {
					rtl8651_getAsicEthernetPHYReg( i, 21, &snr );
					snr = snr | 0x4000;
					rtl8651_setAsicEthernetPHYReg( i, 21, snr );
				}

				if ( ((Linkpartner_eee & 0x2) ) != 0)
					port_linkpartner_eee |= (1 << i);						
				else
					port_linkpartner_eee &= ~(1 << i);						
				
				/*=========== ###02 ===========*/
				/*
				  1.      reg17 = 0x1f10�Aread reg29, for SNR
				  2.      reg17 =  0x1f11�Aread reg29, for AGC
				  3.      reg17 = 0x1f18�Aread reg29, for cb0
				 */					
				// 1. for SNR
				snr = 0;
				for(val=0; val<3; val++) {
					rtl8651_getAsicEthernetPHYReg(i, 29, &agc);
					snr += agc;
				}
				snr = snr / 3;
			
				// 3. for cb0
				rtl8651_getAsicEthernetPHYReg( i, 17, &val );
				val = (val & 0xfff0) | 0x8;
				rtl8651_setAsicEthernetPHYReg( i, 17, val );					
				rtl8651_getAsicEthernetPHYReg( i, 29, &cb0 );

				// 2. for AGC
				val = (val & 0xfff0) | 0x1;
				rtl8651_setAsicEthernetPHYReg( i, 17, val );
				rtl8651_getAsicEthernetPHYReg( i, 29, &agc );

				// set bit 3~0 to 0x0 for reg 17
				val = val & 0xfff0;
				rtl8651_setAsicEthernetPHYReg( i, 17, val );

				if ( ( (    ((agc & 0x70) >> 4) < 4    ) && ((cb0 & 0x80) != 0) ) || (snr > 4150) ) { // "> 4150" = "< 18dB"
					rtl8651_restartAsicEthernetPHYNway(i);
				}

			}
			
			new_port_link_sts = new_port_link_sts | (1 << i);
		}
	}
	
	port_link_sts = new_port_link_sts;
	
	return SUCCESS;
}
#endif

#if defined (CONFIG_RTL_PHY_PATCH)
#define SNR_THRESHOLD 1000

//db = -(10 * log10(sum/262144));
//18db:SNR_THRESHOLD=4155
//20db:SNR_THRESHOLD=2621
//21db:SNR_THRESHOLD=2082
//22db:SNR_THRESHOLD=1654
//24.18db:SNR_THRESHOLD=1000
#define MAX_RESTART_NWAY_INTERVAL		(60*HZ)		
#define MAX_RESTART_NWAY_CNT		3	

struct rtl_nWayCtrl_s
{
	unsigned long restartNWayTime;
	unsigned long restartNWayCnt;
};

struct rtl_nWayCtrl_s re865x_restartNWayCtrl[RTL8651_PHY_NUMBER];
	
unsigned int re865x_getPhySnr(unsigned int port)
{
	unsigned int sum=0;
	unsigned int j; 
	unsigned int regData;

	if(port >= RTL8651_PHY_NUMBER)
	{
		return -1;
	}
		
	if (REG32(PSRP0 + (port * 4)) & PortStatusLinkUp) 
	{
		for (j=0, sum=0;j<10;j++) 
		{
			rtl8651_getAsicEthernetPHYReg(port, 29, &regData);
			sum += regData;
			mdelay(10);
		}
		sum /= 10;
		return sum;
	}
	
	return 0;
}

#if defined(CONFIG_RTL_8196C)
#if 1
static int re865x_checkPhySnr(void)
{

	unsigned int port;
	unsigned int snr=0;
	unsigned int  val, val2;
	for (port=0; port<RTL8651_PHY_NUMBER; port++)
	{
		if((1<<port) & (newLinkPortMask & (~curLinkPortMask)) )
		{
			snr=re865x_getPhySnr(port);
			//printk("%s:%d, port is %d, snr is %d\n",__FUNCTION__,__LINE__,port,snr);
			 rtl8651_getAsicEthernetPHYReg( port, 17, &val );
			 val = (val & 0xfff0) | 0x8;
			 rtl8651_setAsicEthernetPHYReg(port, 17, val );
			 
			 rtl8651_getAsicEthernetPHYReg( port, 29, &val2 );

			if( ((val2 & 0x80) != 0)|| (snr>4155))
			{
				rtl8651_restartAsicEthernetPHYNway(port);    
			}
			val = val & 0xfff0;
			rtl8651_setAsicEthernetPHYReg( port, 17, val );
			
		}
		
	}
	return 0;

}
#else
static int re865x_checkPhySnr(void)
{

	unsigned int port;
	unsigned int snr=0;

	for (port=0; port<RTL8651_PHY_NUMBER; port++)
	{
		if((1<<port) & (newLinkPortMask & (~curLinkPortMask)))
		{
			snr=re865x_getPhySnr(port);
			//printk("%s:%d, port is %d, snr is %d\n",__FUNCTION__,__LINE__,port,snr);
			if(snr>SNR_THRESHOLD)
			{
				/*poor snr, we should restart n-way*/
				if(re865x_restartNWayCtrl[port].restartNWayTime==0)
				{
					/*last time snr is good*/
					re865x_restartNWayCtrl[port].restartNWayTime=jiffies;
					re865x_restartNWayCtrl[port].restartNWayCnt=1;
			
					rtl8651_restartAsicEthernetPHYNway(port);
					
				}
				else if(time_after(jiffies,re865x_restartNWayCtrl[port].restartNWayTime+MAX_RESTART_NWAY_INTERVAL) )
				{
					/*last time SNR is bad, but interval is long enough, we can take another restart n-way action*/
					re865x_restartNWayCtrl[port].restartNWayTime=jiffies;
					re865x_restartNWayCtrl[port].restartNWayCnt=1;
					rtl8651_restartAsicEthernetPHYNway(port);
					
				}
				else if (re865x_restartNWayCtrl[port].restartNWayCnt>MAX_RESTART_NWAY_CNT)
				{
					/*within restart n-way interval and exceed max try cnt*/
					/*shouldn't do it any more, otherwise it will cause phy link up/down repeatly*/
					//printk("%s:%d,restartNWayCnt>MAX_RESTART_NWAY_CNT,stop do restart n-way\n",__FUNCTION__,__LINE__);
				}
				else
				{
					//printk("%s:%d,restartNWayCnt is %lu\n",__FUNCTION__,__LINE__,re865x_restartNWayCtrl[port].restartNWayCnt);
					re865x_restartNWayCtrl[port].restartNWayCnt++;
					rtl8651_restartAsicEthernetPHYNway(port);
				}
			}
			else
			{
				re865x_restartNWayCtrl[port].restartNWayTime=0;
				re865x_restartNWayCtrl[port].restartNWayCnt=0;
			}
			
		}
		
	}
	return 0;

}
#endif
#endif
#endif

unsigned int rtl865x_getPhysicalPortLinkStatus(void)
{
	unsigned int port_num=0;
	unsigned int linkPortMask=0;
	for(port_num=0;port_num<=RTL8651_PHY_NUMBER;port_num++)
	{
		if((READ_MEM32(PSRP0+(port_num<<2))&PortStatusLinkUp)!=0)
		{
			linkPortMask |= 1<<port_num;
		}
	
	}
	return linkPortMask;
}

#ifdef CONFIG_RTL_8198_ESD
static uint32 phy_reg30[RTL8651_PHY_NUMBER] = { 0, 0, 0, 0, 0};
static int one_second_counter = 0;
static int first_time_read_reg6 = 1;
static int need_to_check_esd2 = 1;

static int diff_more_than_1(uint32 a, uint32 b)
{
	uint32 c;

	if (a==b)
		return 0;
	if (a<b)
		{ c=a; a=b; b=c; }
	if ((a-b) >=2)
		return 1;
	else
		return 0;
}

static int esd_recovery(void)
{
	uint32 val;
	int i;

	for (i=0; i<RTL8651_PHY_NUMBER; i++) {
		/************ Link down  ************/
		if ((REG32(PSRP0 + (i * 4)) & PortStatusLinkUp) == 0) {
			phy_reg30[i] = 0;
		}
		/************ Link up  ************/
		else if (phy_reg30[i] == 0) {
			rtl8651_getAsicEthernetPHYReg( i, 22, &val );
			rtl8651_setAsicEthernetPHYReg( i, 22, ((val & (0xff00)) | 0x17) );
			rtl8651_getAsicEthernetPHYReg( i, 30, &val );
			phy_reg30[i] = BIT(31) | (val & 0xfff);
		}
	}		
	return SUCCESS;
}
#endif

//august: create a function that get net_device by rtl_sw_port
static __inline__ struct net_device* ethtool_get_netdev_by_rtl_sw_port(const struct re865x_priv* _prtl86xx_dev , const unsigned int switch_port)
{
	struct net_device *dev, *ret;
	struct dev_priv	  *dp;
	unsigned int num;

	ret = NULL;
	
	read_lock(&dev_base_lock);
	for_each_netdev(&init_net, dev) {
		//select the switch device
		if(BSP_SW_IRQ != dev->irq)
			continue;

		//list 8676 lan interface 	
		for(num = 0; num < _rtl86xx_dev_num; ++num) {
			//august: compare the ifindex
			if (_prtl86xx_dev->dev[num]->ifindex == dev->ifindex) {
				//august: get priv, use the priv.portmask
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
				dp = netdev_priv(_prtl86xx_dev->dev[num]);
#else
				dp = (struct dev_priv *)_prtl86xx_dev->dev[num]->priv;
#endif
				if(dp->portmask & (1 << switch_port)){
					//dev_hold(dev);
					ret = dev;
					break; //break for(num =0, ...)
				}
			}
		}

		if(ret)
			break;
	}
	read_unlock(&dev_base_lock);
	return ret;
}

static void interrupt_dsr_link(unsigned long task_priv)
{	
	/* Please put heavy weight process to nic_event so we don't occupy bottom-half for too long, Andrew */
	nic_event(NIC_EVENT_LINKCHANGE); 
	
#ifdef CONFIG_RTL_8198_ESD
	esd_recovery();
#endif

#ifdef CONFIG_RTL8196C_ETH_IOT
/*LinkDownEventFlag is a read clear bit, so re865x_setPhyGrayCode() should be call headmost */
		re865x_setPhyGrayCode();
#endif

	newLinkPortMask=rtl865x_getPhysicalPortLinkStatus();	
	
#if defined(CONFIG_RTL_MULTI_LAN_DEV) && defined(CONFIG_RTL_8676HWNAT)
{
	unsigned char LINK_UP_NUM=0; 
	//uint32 sharedON=0,sharedOFF=0, fcON=0,fcOFF=0;
	if(RTL8676_TBLASIC_EXTPHYPROPERTY_PORT0_RTL8367B != global_probe_extPhy)
	{
		int port;
		struct net_device *dev;
		for(port = 0; port < RTL8651_PORT_NUMBER; ++port)
		{
			if((dev = ethtool_get_netdev_by_rtl_sw_port(&_rtl86xx_dev, port)) != NULL)
			{
				// Do NOT update carrier state for PTM(port 5)
				//  here as port-5 always on; Instead, PTM link
				//  state should be sync with dsl link state.
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
				if (((struct dev_priv *)netdev_priv(dev))->portmask == RTL_PTMWANPORT_MASK)
#else
				if (((struct dev_priv *)dev->priv)->portmask == RTL_PTMWANPORT_MASK)
#endif
					continue;
				if((1 << port) & newLinkPortMask){
					netif_carrier_on(dev);
					LINK_UP_NUM++;
				}else{
					netif_carrier_off(dev);
				}	
			}
		}
	}
	#if 0 // andrew, this is unnecessary and incorrect
	if(LINK_UP_NUM<=2){
		//REG32(0xbb804504)=0x00c000d0;
		//REG32(0xbb804508)=0x00a800c0;
		fcON=0xd0;
		fcOFF=0xc0;
		sharedON=0xc0;
		sharedOFF=0xa8;	
		//printk("LINK_UP_NUM<=2");
	}else{
		//REG32(0xbb804504)=0x00A000AC;
		//REG32(0xbb804508)=0x004A0062;
		fcON=0xac;
		fcOFF=0xa0;
		sharedON=0x62;
		sharedOFF=0x4a;			
		//printk("LINK_UP_NUM>=2");
	}
	
	WRITE_MEM32(SBFCR1, (READ_MEM32(SBFCR1) & ~(S_DSC_FCON_MASK | S_DSC_FCOFF_MASK)) | ( fcON<< S_DSC_FCON_OFFSET) | (fcOFF << S_DSC_FCOFF_OFFSET));
	WRITE_MEM32(SBFCR2, (READ_MEM32(SBFCR2) & ~(S_Max_SBuf_FCON_MASK | S_Max_SBuf_FCOFF_MASK)) | (sharedON << S_Max_SBuf_FCON_OFFSET) | (sharedOFF << S_Max_SBuf_FCOFF_OFFSET));
	#endif //if 0 
	
}	
#endif

#if defined(CONFIG_RTL_IGMP_SNOOPING)
		rtl865x_igmpSyncLinkStatus();
#endif	

#if defined(CONFIG_RTL_8196C) && defined(CONFIG_RTL_PHY_PATCH)
		re865x_checkPhySnr();
#endif

#ifdef CONFIG_RTL_NLMSG_PROTOCOL
	rtl_nl_send_lkchg_msg(curLinkPortMask, newLinkPortMask);
#endif

	//curLinkPortMask=newLinkPortMask;

#ifdef LINK_TASKLET		
	//REG32(CPUIIMR) |= (LINK_CHANGE_IP);
#endif
	return;
}

__IRAM_FWD
irqreturn_t interrupt_isr(int irq, void *dev_instance)
{
#if !defined(RX_NAPI) || (defined(CONFIG_RTL_IGMP_SNOOPING)||defined(CONFIG_RTL_LINKCHG_PROCESS) || defined (CONFIG_RTL_PHY_PATCH))
	struct net_device *dev = dev_instance;
	struct dev_priv *cp;
#endif
	unsigned int status;
#if !defined(RX_NAPI) || (defined(CONFIG_RTL_IGMP_SNOOPING)||defined(CONFIG_RTL_LINKCHG_PROCESS) || defined (CONFIG_RTL_PHY_PATCH))
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif
#endif
	status = REG32(CPUIISR);
	REG32(CPUIISR) = status;
	//status &= REG32(CPUIIMR);

	if (status & (RX_DONE_IP_ALL|MBUF_DESC_RUNOUT_IP_ALL|PKTHDR_DESC_RUNOUT_IP_ALL))
	{
		if (unlikely(status & PKTHDR_DESC_RUNOUT_IP_ALL)) {
			SWC_STATS_INC(rx_rbf);
		}
		#if defined(RX_TASKLET)
		REG32(CPUIIMR) &= ~(RX_DONE_IE_ALL | PKTHDR_DESC_RUNOUT_IE_ALL | MBUF_DESC_RUNOUT_IE_ALL); 		
		tasklet_schedule(&cp->rx_dsr_tasklet);
		#else
		#if defined(RX_NAPI)
		if (likely(napi_schedule_prep(&rtl_napi)))
		{
			REG32(CPUIIMR) &= ~(RX_DONE_IE_ALL | TX_ALL_DONE_IE_ALL | PKTHDR_DESC_RUNOUT_IE_ALL | MBUF_DESC_RUNOUT_IE_ALL); 
			__napi_schedule(&rtl_napi);
		}
		#else
		interrupt_dsr_rx((unsigned long)cp);
		#endif
		#endif
	}

	if (status & TX_ALL_DONE_IP_ALL)
	{
  		#if defined(TX_TASKLET)
		REG32(CPUIIMR) &= ~TX_ALL_DONE_IE_ALL; 
		tasklet_schedule(&cp->tx_dsr_tasklet);						
  		#else
		//interrupt_dsr_tx((unsigned long)cp);
 		#endif
	}

	#if	defined(CONFIG_RTL_IGMP_SNOOPING)||defined(CONFIG_RTL_LINKCHG_PROCESS) || defined (CONFIG_RTL_PHY_PATCH)
	if (status & LINK_CHANGE_IP)
	{
  		#ifdef LINK_TASKLET		
  		//REG32(CPUIIMR) &= ~LINK_CHANGE_IP;
		tasklet_schedule(&cp->link_dsr_tasklet);						
  		#else
		interrupt_dsr_link((unsigned long)cp);
  		#endif
	}
	#endif

	#if !defined(RX_TASKLET) || !defined(TX_TASKLET)
	REG32(CPUIISR) = (MBUF_DESC_RUNOUT_IP_ALL|PKTHDR_DESC_RUNOUT_IP_ALL);
	#endif

	return IRQ_HANDLED;
}

static int rtl865x_init_hw(void)
{
	unsigned int mbufRingSize;
	int	i, ret;
	
	#if defined(CONFIG_RTL8685SB)
	if (((REG32(MACCR)>>12)&3)==1)
		REG32(EXTMACCR)|=1; /* set CPort MAC clock to LX speed */
	#endif 

	mbufRingSize = rtl865x_rxSkbPktHdrDescNum;	/* rx ring 0	*/
	for(i=1;i<RTL865X_SWNIC_RXRING_HW_PKTDESC;i++)
	{
		mbufRingSize += rxRingSize[i];
	}

	/* Initialize NIC module */
	ret = RTL_swNic_init(rxRingSize, mbufRingSize, txRingSize, MBUF_LEN);

	if (ret != SUCCESS)
	{
		printk("819x-nic: swNic_init failed!\n");
		return FAILED;
	}
	return SUCCESS;	
}

#ifdef CONFIG_RTL_HARDWARE_NAT		
static void reset_hw_mib_counter(struct net_device *dev)
{
	int i, port;
	int32 totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;

	for(i=0;i<totalVlans;i++)
	{
		if (IF_ETHER!=vlanconfig[i].if_type)
		{
			continue;
		}
		if (!memcmp(vlanconfig[i].mac.octet, dev->dev_addr, 6))
		{
			for (port=0; port<RTL8651_AGGREGATOR_NUMBER; port++)
			{
				if (vlanconfig[i].memPort & (1<<port))
				   WRITE_MEM32(MIB_CONTROL, (1<<port*2) | (1<<(port*2+1)));
				return;
			}
		}
	}
}
#endif

#if defined(DYNAMIC_ADJUST_TASKLET) || defined(CONFIG_RTL8186_TR) || defined(CONFIG_RTL8196C_REVISION_B) || defined(CONFIG_RTL_8198) || defined(RTL8196C_EEE_MAC) || defined(RTL_CPU_QOS_ENABLED)
#ifndef CONFIG_RTL8676S
static void one_sec_timer(unsigned long task_priv)
{
	unsigned long		flags;
	struct dev_priv	*cp;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv((struct net_device *)task_priv);
#else
	cp = ((struct net_device *)task_priv)->priv;
#endif

	spin_lock_irqsave (&cp->lock, flags);

#if defined(PATCH_GPIO_FOR_LED)

//  if (((struct net_device *)task_priv)->name[3] == '0' ) {//
    if(alias_name_are_eq((struct net_device *)task_priv)->name,ALIASNAME_ETH0,ALIASNAME_ELAN_PREFIX)){
		int port;

		for (port=0; port<RTL8651_PHY_NUMBER; port++) {
			update_mib_counter(port);		
			calculate_led_interval(port);
			if (led_cb[port].link_status & GPIO_LINK_STATE_CHANGE) 
			{
				gpio_led_Interval_timeout(port);
			}
		}
	}	
#endif

#if defined(DYNAMIC_ADJUST_TASKLET)
//if (((struct net_device *)task_priv)->name[3] == '0'    // eth0  
if(alias_name_are_eq(((struct net_device *)task_priv)->name,ALIASNAME_ETH0,ALIASNAME_ELAN_PREFIX)
    && rx_pkt_thres > 0 && ((eth_flag&TASKLET_MASK) == 0)){ 
        if (rx_cnt > rx_pkt_thres) {
			if (!rx_tasklet_enabled) {
				rx_tasklet_enabled = 1;
            }							
        }               
        else {
			if (rx_tasklet_enabled) { // tasklet enabled
				rx_tasklet_enabled = 0;
			}						
        }       
        rx_cnt = 0;     
    }   
#endif

#ifdef CONFIG_RTL8186_TR
    cp->tx_avarage = (cp->tx_avarage/10)*7 + (cp->tx_byte_cnt/10)*3;
    if (cp->tx_avarage > cp->tx_peak)
        cp->tx_peak = cp->tx_avarage;
    cp->tx_byte_cnt = 0;
    
    cp->rx_avarage = (cp->rx_avarage/10)*7 + (cp->rx_byte_cnt/10)*3;
    if (cp->rx_avarage > cp->rx_peak)
        cp->rx_peak = cp->rx_avarage;
    cp->rx_byte_cnt = 0;    
#endif

#ifdef CONFIG_RTL8196C_REVISION_B
	if ((REG32(REVR) == RTL8196C_REVISION_A) && (eee_enabled)) {
			int i, curr_sts;
			uint32 reg;
	
			/* 
				prev_port_sts[] = 0, current = 0	:	do nothing
				prev_port_sts[] = 0, current = 1	:	update prev_port_sts[]
				prev_port_sts[] = 1, current = 0	:	update prev_port_sts[], disable EEE
				prev_port_sts[] = 1, current = 1	:	enable EEE if EEE is disabled
			 */
			for (i=0; i<MAX_PORT_NUMBER; i++)
			{
				curr_sts = (REG32(PSRP0 + (i * 4)) & PortStatusLinkUp) >> 4;
	
				if ((prev_port_sts[i] == 1) && (curr_sts == 0)) {
					// disable EEE MAC
					REG32(EEECR) = (REG32(EEECR) & ~(0x1f << (i * 5))) | 
						((EN_P0_FRC_EEE|FRC_P0_EEE_100) << (i * 5));
					//printk("	disable EEE for port %d\n", i);
				}
				else if ((prev_port_sts[i] == 1) && (curr_sts == 1)) {
					reg = REG32(EEECR);
					if ((reg & (1 << (i * 5))) == 0) {
						// enable EEE MAC
						REG32(EEECR) = (reg & ~(0x1f << (i * 5))) | 
							((FRC_P0_EEE_100|EN_P0_TX_EEE|EN_P0_RX_EEE) << (i * 5));
						//printk("	enable EEE for port %d\n", i);
					}
				}			
				prev_port_sts[i] = curr_sts;
			}
			//printk(" %d %d %d %d %d\n", port_sts[0], port_sts[1], port_sts[2], port_sts[3], port_sts[4]);
		}
#endif

#if defined(RTL_CPU_QOS_ENABLED)
	totalLowQueueCnt = 0;
#endif

#ifdef CONFIG_RTL_8198_ESD
#if defined(CONFIG_PRINTK)
#define panic_printk         printk
#endif
	{
	int phy;
	uint32 val;
	if (first_time_read_reg6) {
		first_time_read_reg6 = 0;

		for (phy=0; phy<5; phy++) {
			rtl8651_setAsicEthernetPHYReg( phy, 31, 5  );	
			rtl8651_getAsicEthernetPHYReg(phy, 1, &val);
			rtl8651_setAsicEthernetPHYReg(phy, 1, val | 0x4);

			rtl8651_setAsicEthernetPHYReg(phy, 5, 0xfff6);
			rtl8651_getAsicEthernetPHYReg(phy, 6, &val);
			rtl8651_setAsicEthernetPHYReg( phy, 31, 0  );

		if ((val & 0xff) == 0xFF)
			need_to_check_esd2 = 0;
		}		
	}

	if (++one_second_counter >= 10) {
		for (phy=0; phy<5; phy++)
		{
			if (phy_reg30[phy] != 0) {
				rtl8651_setAsicEthernetPHYReg( phy, 31, 5  );
				rtl8651_setAsicEthernetPHYReg(phy, 5, 0);

				rtl8651_getAsicEthernetPHYReg(phy, 6, &val);
				rtl8651_setAsicEthernetPHYReg( phy, 31, 0  );
	
				if ((val & 0xffff) != 0xAE04)
				{
					panic_printk("  ESD-1\n");
					do {} while(1); // reboot
				}
	
				if (need_to_check_esd2) {
					rtl8651_setAsicEthernetPHYReg( phy, 31, 5  );
					rtl8651_setAsicEthernetPHYReg(phy, 5, 0xfff6);

					rtl8651_getAsicEthernetPHYReg(phy, 6, &val);
					rtl8651_setAsicEthernetPHYReg( phy, 31, 0  );

					if ((val & 0xff) != 0xFC)
					{
						panic_printk("  ESD-2\n");
						do {} while(1); // reboot
					}
				}

				rtl8651_getAsicEthernetPHYReg( phy, 22, &val );
				rtl8651_setAsicEthernetPHYReg( phy, 22, ((val & (0xff00)) | 0x17) );
				rtl8651_getAsicEthernetPHYReg( phy, 30, &val );

				if ((phy_reg30[phy] & 0xfff) != (val & 0xfff)) {
					if (diff_more_than_1((phy_reg30[phy] & 0xf), (val & 0xf)) || 
						diff_more_than_1(((phy_reg30[phy] >> 4) & 0xf), ((val >> 4) & 0xf)) ||
						diff_more_than_1(((phy_reg30[phy] >> 8) & 0xf), ((val >> 8) & 0xf))
						) {
						panic_printk("  ESD-3: old= 0x%x, new= 0x%x\n", phy_reg30[phy] & 0xfff, val & 0xfff);
						do {} while(1); // reboot
					}
					phy_reg30[phy] = BIT(31) | (val & 0xfff);					
				}
			}

		}		
		
		one_second_counter = 0;
	}
	}
#endif

#ifdef CONFIG_RTL_8196C_ESD
#if defined(CONFIG_PRINTK)
#define panic_printk         printk
#endif
	if (_96c_esd_counter) {

		if (++_96c_esd_counter >= 20) {
			extern int is_fault;
	
			if( (RTL_R32(PCRP4) & EnablePHYIf) == 0)
			{
				panic_printk("  ESD reboot...\n");
				is_fault = 1;
			}	
			_96c_esd_counter = 1;
		}
	}
#endif

#ifdef RTL867X_PORT0_PATCH_BACK_TO_HALF
	port0_patch_state_mechine();
#endif

	mod_timer(&cp->expire_timer, jiffies + HZ);

	spin_unlock_irqrestore(&cp->lock, flags);   
}
#endif
#endif

static struct net_device *irqDev=NULL;
static int re865x_open (struct net_device *dev)
{
	struct dev_priv *cp;
	unsigned long flags;
	int rc;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif
	if (cp->opened)
		return SUCCESS;
	
	/*	The first device be opened	*/
	if (atomic_read(&rtl_devOpened)==0)
	{
		/* this is the first open dev */
		spin_lock_irqsave (&cp->lock, flags);

		swcore_init_locks();

#if !defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
		rtk_queue_init(&rx_skb_queue); 
		refill_rx_skb();
#endif//end of !CONFIG_RTL_ETH_PRIV_SKB_ADV
		rc = rtl865x_init_hw();
		spin_unlock_irqrestore(&cp->lock, flags);
		if (rc) {
			printk("rtl865x_init_hw() failed!\n");
			return FAILED;
		}

#if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION)
		memset(wifi_priv,0,sizeof(wifi_priv));
#if defined(CONFIG_RTL_XDSL_WIFI_IPI)
		spin_lock_irqsave(&rtk_xdsl_smp_wlanRx_lock, flags);
		/* WiFi Tx */
		memset(&rtk_xdsl_smp_wlanTx_work[0],0,sizeof(rtk_xdsl_smp_wlan_work_t) * RTK_XDSL_SMP_WLANTX_WORK_SIZE);
		INIT_LIST_HEAD(&rtk_xdsl_smp_wlanTx_free_listHead);
		INIT_LIST_HEAD(&rtk_xdsl_smp_wlanTx_used_listHead);
		if(1)
		{
			int i;
			for(i = 0; i < RTK_XDSL_SMP_WLANTX_WORK_SIZE; i++)
			{
				INIT_LIST_HEAD(&rtk_xdsl_smp_wlanTx_work[i].wifi_list);
				list_add_tail(&rtk_xdsl_smp_wlanTx_work[i].wifi_list,&rtk_xdsl_smp_wlanTx_free_listHead);	
			}
		}
		smpWlanTxCtrl = kmalloc(sizeof(smpWlanTxCtrl), GFP_KERNEL);
		memset(smpWlanTxCtrl, 0,sizeof(smpWlanTxCtrl));
		atomic_set(&wlanTx_csd_available,1);
		smpWlanTxCtrl->rtk_xdsl_wifi_csd.func = rtk_xdsl_smp_wifi_tx_tasklet;
		smpWlanTxCtrl->rtk_xdsl_wifi_csd.info = smpWlanTxCtrl;
		tasklet_init(&smpWlanTxCtrl->wifi_ipi_tasklet, (void *) rtk_xdsl_smp_wifi_tx_cb, (unsigned long)smpWlanTxCtrl);

		/* WiFi Rx */
		memset(&rtk_xdsl_smp_wlanRx_work[0],0,sizeof(rtk_xdsl_smp_wlan_work_t) * RTK_XDSL_SMP_WLANRX_WORK_SIZE);
		INIT_LIST_HEAD(&rtk_xdsl_smp_wlanRx_free_listHead);
		INIT_LIST_HEAD(&rtk_xdsl_smp_wlanRx_used_listHead);
		if(1)
		{
			int i;
			for(i = 0; i < RTK_XDSL_SMP_WLANRX_WORK_SIZE; i++)
			{
				INIT_LIST_HEAD(&rtk_xdsl_smp_wlanRx_work[i].wifi_list);
				list_add_tail(&rtk_xdsl_smp_wlanRx_work[i].wifi_list,&rtk_xdsl_smp_wlanRx_free_listHead);	
			}
		}
		smpWlanRxCtrl = kmalloc(sizeof(smpWlanRxCtrl), GFP_KERNEL);
		memset(smpWlanRxCtrl, 0,sizeof(smpWlanRxCtrl));
		atomic_set(&wlanRx_csd_available,1);
		smpWlanRxCtrl->rtk_xdsl_wifi_csd.func = rtk_xdsl_smp_wifi_rx_tasklet;
		smpWlanRxCtrl->rtk_xdsl_wifi_csd.info = smpWlanRxCtrl;
		tasklet_init(&smpWlanRxCtrl->wifi_ipi_tasklet, (void *) rtk_xdsl_smp_wifi_rx_cb, (unsigned long)smpWlanRxCtrl);
		spin_unlock_irqrestore(&rtk_xdsl_smp_wlanRx_lock, flags);
#endif
#endif

#if defined(RX_TASKLET)
		tasklet_init(&cp->rx_dsr_tasklet, (void *)interrupt_dsr_rx, (unsigned long)cp);
		eth_rx_tasklets = &cp->rx_dsr_tasklet;
#endif

#ifdef TX_TASKLET
		tasklet_init(&cp->tx_dsr_tasklet, interrupt_dsr_tx, (unsigned long)cp);
#endif

#ifdef LINK_TASKLET
		tasklet_init(&cp->link_dsr_tasklet, interrupt_dsr_link, (unsigned long)cp);
#endif

#ifdef CONFIG_RTL_PHY_PATCH
		memset(re865x_restartNWayCtrl,0, sizeof(re865x_restartNWayCtrl));
#endif

#ifndef CONFIG_RTL8676S
		{	
			printk("%s %d, %s\n", __func__, __LINE__, dev->name);
			init_timer(&cp->expire_timer);
			cp->expire_timer.expires = jiffies + HZ;
			cp->expire_timer.data = (unsigned long)dev;
			cp->expire_timer.function = one_sec_timer;
			mod_timer(&cp->expire_timer, jiffies + HZ); 	
		}
#endif

#if defined(RX_NAPI)
		napi_enable(&rtl_napi);
		mod_timer(&txbuff_gc_timer, jiffies + 30*HZ);
#endif
		mod_timer(&rtl_hwstats_timer, jiffies + 10*HZ);

		rc = request_irq(dev->irq, interrupt_isr, IRQF_DISABLED, dev->name, dev);

		if (rc)
		{
			printk("request_irq() error!\n");
			goto err_out_hw;
		}
		irqDev=dev;
		//cp->irq_owner =1;
		rtl865x_start();
	}

	atomic_inc(&rtl_devOpened);
	cp->opened = 1;

#ifdef CONFIG_RTL_HARDWARE_NAT	
	reset_hw_mib_counter(dev);
#endif

	netif_start_queue(dev);

#if 0//krammer move up
#if defined(DYNAMIC_ADJUST_TASKLET) || defined(CONFIG_RTL8186_TR) || defined(CONFIG_RTL8196C_REVISION_B)|| defined(CONFIG_RTL_8198) || defined(RTL8196C_EEE_MAC)
	#if !defined(CONFIG_RTL8186_TR)

//    if (dev->name[3] == '0') //
if(alias_name_are_eq(dev->name,ALIASNAME_ETH0,ALIASNAME_ELAN_PREFIX)||(alias_name_are_eq(dev->name,ALIASNAME_NAS0,ALIASNAME_MWNAS)))
	#endif
	{	
		init_timer(&cp->expire_timer);
		cp->expire_timer.expires = jiffies + HZ;
		cp->expire_timer.data = (unsigned long)dev;
		cp->expire_timer.function = one_sec_timer;
		mod_timer(&cp->expire_timer, jiffies + HZ);		
#ifdef DYNAMIC_ADJUST_TASKLET			
		rx_cnt = 0;
#endif
	}
#endif
#endif

#if 0
#ifdef CONFIG_RTL_LAYERED_DRIVER_L3
	/*FIXME_hyking: should add default route to cpu....*/
	if(rtl865x_curOpMode == GATEWAY_MODE)
#if defined(CONFIG_RTL_PUBLIC_SSID)
		rtl865x_addRoute(0,0,0,RTL_GW_WAN_DEVICE_NAME,0);
#else
#ifndef CONFIG_RTL_MULTI_ETH_WAN
		rtl865x_addRoute(0,0,0,RTL_DRV_WAN0_NETIF_NAME,0);
#endif
#endif
#endif
#endif

	rtl865x_enableDevPortForward( dev, cp);	

	return SUCCESS;
	
err_out_hw:
    rtl8186_stop_hw(dev, cp);
    rtl865x_down();
    return rc;
}

static int re865x_close (struct net_device *dev)
{
	struct dev_priv *cp;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif
//	cp = netdev_priv(dev);
	if (!cp->opened)
		return SUCCESS;

	rtl865x_disableDevPortForward(dev, cp);
	netif_stop_queue(dev);
	rtl8186_stop_hw(dev, cp);

	/* The last opened device	*/
	if (atomic_read(&rtl_devOpened)==1)
	{
		/*	warning:
			1.if we don't reboot,we shouldn't hold switch core from rx/tx, otherwise there will be some problem during change operation mode
			2.only when two devices go down,can we shut down nic interrupt
			3.the interrupt will be re_enable by rtl865x_start()
		*/
		WRITE_MEM32(PCRP0, READ_MEM32(PCRP0) & ~EnablePHYIf);
		WRITE_MEM32(PCRP1, READ_MEM32(PCRP1) & ~EnablePHYIf);
		WRITE_MEM32(PCRP2, READ_MEM32(PCRP2) & ~EnablePHYIf);
		WRITE_MEM32(PCRP3, READ_MEM32(PCRP3) & ~EnablePHYIf);
		WRITE_MEM32(PCRP4, READ_MEM32(PCRP4) & ~EnablePHYIf);
		rtl865x_disableInterrupt();
		
#if defined(RX_NAPI)
		napi_disable(&rtl_napi);
		del_timer(&txbuff_gc_timer);
#endif
		del_timer(&rtl_hwstats_timer);

		//free_irq(dev->irq, GET_IRQ_OWNER(cp));
		//((struct dev_priv *)((GET_IRQ_OWNER(cp))->priv))->irq_owner = 0;
		free_irq(dev->irq, irqDev);
		//((struct dev_priv *)(irqDev->priv))->irq_owner = 0;
		
#ifdef RX_TASKLET
		tasklet_kill(&cp->rx_dsr_tasklet);	
#endif

#ifdef TX_TASKLET
		tasklet_kill(&cp->tx_dsr_tasklet);
#endif

#ifdef LINK_TASKLET
		tasklet_kill(&cp->link_dsr_tasklet);
#endif

#ifdef CONFIG_RTL_PHY_PATCH
		memset(re865x_restartNWayCtrl,0, sizeof(re865x_restartNWayCtrl));
#endif
		free_rx_skb();
	}

	memset(&cp->net_stats, '\0', sizeof(struct net_device_stats));
	atomic_dec(&rtl_devOpened);
	cp->opened = 0;

#if defined(DYNAMIC_ADJUST_TASKLET) || defined(CONFIG_RTL8186_TR) || defined(BR_SHORTCUT) || defined(CONFIG_RTL8196C_REVISION_B) || defined(CONFIG_RTL_8198)
    if (timer_pending(&cp->expire_timer))
        del_timer_sync(&cp->expire_timer);
#endif

#ifdef BR_SHORTCUT
	if (dev == cached_dev)
		cached_dev=NULL;
#endif

#ifdef CONFIG_RTL_HARDWARE_NAT	
	reset_hw_mib_counter(dev);
#endif

	return SUCCESS;
}

#if defined(CONFIG_RTL_STP) || (defined(CONFIG_RTL_CUSTOM_PASSTHRU))
static int re865x_pseudo_open (struct net_device *dev)
{
	struct dev_priv *cp;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif
	//cp = netdev_priv(dev);
	if (cp->opened)
		return SUCCESS;
	
	cp->opened = 1;	
	netif_start_queue(dev);
	return SUCCESS;
}

static int re865x_pseudo_close (struct net_device *dev)
{
	struct dev_priv *cp;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif

	if (!cp->opened)
		return SUCCESS;
	netif_stop_queue(dev);

	memset(&cp->net_stats, '\0', sizeof(struct net_device_stats));
	cp->opened = 0;

#ifdef BR_SHORTCUT
	if (dev == cached_dev)
		cached_dev=NULL;
#endif
	return SUCCESS;
}
#endif

#if defined(CONFIG_RTL_STP)
static int re865x_stp_mapping_init(void)
{
	int i, j, k, totalVlans;
	totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
	
	for (i = 0; i < MAX_RE865X_STP_PORT; i++)
	{
		STP_PortDev_Mapping[i] = NO_MAPPING; 
	}
	
	#ifdef CONFIG_RTK_MESH
	STP_PortDev_Mapping[WLAN_PSEUDO_IF_INDEX] = WLAN_PSEUDO_IF_INDEX;
	STP_PortDev_Mapping[WLAN_MESH_PSEUDO_IF_INDEX] = WLAN_MESH_PSEUDO_IF_INDEX;
	#else
	STP_PortDev_Mapping[MAX_RE865X_STP_PORT -1] = WLAN_PSEUDO_IF_INDEX;
	#endif
	
	j = 0;
	for(k=0;k<totalVlans;k++)
	{
		if (vlanconfig[k].isWan == FALSE)
		{
			#ifdef CONFIG_RTK_MESH
			for(i=0; i< MAX_RE865X_ETH_STP_PORT ; i++)
			#else
			for(i=0; i< MAX_RE865X_STP_PORT-1 ; i++)
			#endif	
			{
				if ( (1<<i) & vlanconfig[k].memPort ) 
				{
					STP_PortDev_Mapping[j] = i;
					j++;
				}
			}
			
			break;
		}
	}

#if 0	
		printk("mapping table is ");
#ifdef CONFIG_RTK_MESH
	for(i=0; i< MAX_RE865X_ETH_STP_PORT ; i++)
#else
	for(i=0; i< MAX_RE865X_STP_PORT-1 ; i++)
#endif		
	{
		printk(" %d ", STP_PortDev_Mapping[i]);
	}
		printk("\n\n");
#endif
	return SUCCESS;
}

static int re865x_stp_mapping_reinit(void)
{
	int i, j, k, totalVlans;
	totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
	
	for (i = 0; i < MAX_RE865X_STP_PORT; i++)
	{
		STP_PortDev_Mapping[i] = NO_MAPPING; 
	}

	#ifdef CONFIG_RTK_MESH
	STP_PortDev_Mapping[WLAN_PSEUDO_IF_INDEX] = WLAN_PSEUDO_IF_INDEX;
	STP_PortDev_Mapping[WLAN_MESH_PSEUDO_IF_INDEX] = WLAN_MESH_PSEUDO_IF_INDEX;
	#else
	STP_PortDev_Mapping[MAX_RE865X_STP_PORT -1] = WLAN_PSEUDO_IF_INDEX;
	#endif

	printk("=======stp port dev mapping reinit=======\n");
	j = 0;
	for(k=0;k<totalVlans;k++)
	{
		if (vlanconfig[k].isWan == FALSE)
		{
			#ifdef CONFIG_RTK_MESH
			for(i=0; i< MAX_RE865X_ETH_STP_PORT; i++)
			#else
			for(i=0; i< MAX_RE865X_STP_PORT-1 ; i++)
			#endif
			{
				if ( (1<<i) & vlanconfig[k].memPort ) 
				{
					STP_PortDev_Mapping[j] = i;
					printk("mapping: lan phycisal [port%d] <====>pseudo [port%d]\n", i, j);
					j++;
				}
			}
			
			break;
		}
	}
	
		printk("mapping table is ");
	#ifdef CONFIG_RTK_MESH
	for(i=0; i< MAX_RE865X_ETH_STP_PORT; i++)
	#else
	for(i=0; i< MAX_RE865X_STP_PORT-1 ; i++)
	#endif	
	{
		printk(" %d ", STP_PortDev_Mapping[i]);
	}
		printk("\n\n");
	return SUCCESS;
}

static int re865x_stp_get_pseudodevno(uint32 port_num)
{
	int i, dev_no;
	for(i=0; i< MAX_RE865X_STP_PORT-1 ; i++)
	{
		if( STP_PortDev_Mapping[i] == port_num)
		{
			dev_no = i;
			return dev_no;
		}		
	}
	return NO_MAPPING;

}

#endif

#if defined(CONFIG_RTL_LOCAL_PUBLIC)
//hyking:this function should move to rtl865x_fdb.c
//implement it at here just for releaae to natami...
//2010-02-22
static int32 rtl865x_getPortlistByMac(const unsigned char *mac,uint32 *portlist)
{
	int32 found = FAILED;
	ether_addr_t *macAddr;
	int32 column;
	rtl865x_tblAsicDrv_l2Param_t	fdbEntry;

	macAddr = (ether_addr_t *)(mac);
	found = rtl865x_Lookup_fdb_entry(0, macAddr, FDB_DYNAMIC, &column, &fdbEntry);
	if(found == SUCCESS)
	{
		if(portlist)
			*portlist = fdbEntry.memberPortMask;
	}

	return found;
	
}
#endif

#if  !defined(CONFIG_RTL_MULTI_LAN_DEV)
int32 _rtl_getPortlistByMac(const unsigned char *mac,uint32 *portlist, int fid)
{
	int32 found = FAILED;
	ether_addr_t *macAddr;
	int32 column;
	rtl865x_tblAsicDrv_l2Param_t	fdbEntry;

	macAddr = (ether_addr_t *)(mac);
	found = rtl865x_Lookup_fdb_entry(fid==-1?RTL_LAN_FID:fid, macAddr, FDB_DYNAMIC, &column, &fdbEntry);
	if(found == SUCCESS)
	{
		if(portlist)
			*portlist = fdbEntry.memberPortMask;
	}
	else if(fid==-1)
	{
		found = rtl865x_Lookup_fdb_entry(RTL_WAN_FID, macAddr, FDB_DYNAMIC, &column, &fdbEntry);
		if(portlist)
			*portlist = fdbEntry.memberPortMask;
	}

	return found;
	
}
#endif

//#if defined(CONFIG_RTK_VLAN_SUPPORT) && defined(CONFIG_RTK_VLAN_FOR_CABLE_MODEM)
struct net_device* re865x_get_netdev_by_name(const char* name)
{
	int i;
	for(i = 0; i < _rtl86xx_dev_num; i++)
	{
		if(strcmp(_rtl86xx_dev.dev[i]->name,name) == 0)
			return _rtl86xx_dev.dev[i];
	}
	return NULL;
}

unsigned int re865x_get_phyportmask_by_name(const char* name)
{
	struct net_device* device = re865x_get_netdev_by_name(name);
	if(device)
	{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
		struct dev_priv	*cp = netdev_priv(device);
#else
		struct dev_priv	*cp = ((struct dev_priv *)device->priv);
#endif
		
		if(cp && cp->opened)
			return cp->portmask;
	}
	
	return 0;
}

//#endif

//__MIPS16
#if defined(CONFIG_RTL_CUSTOM_PASSTHRU)
static inline int rtl_process_passthru_tx(rtl_nicTx_info *txInfo)
{
	struct net_device *dev;
	struct sk_buff *skb = NULL;	
 	struct dev_priv *cp;
	uint16	etherType;
	
	if(oldStatus)
	{
		skb = txInfo->out_skb;
		dev = skb->dev;
		if (dev==_rtl86xx_dev.pdev)
		{
			etherType = *((unsigned short *)(skb->data+(ETH_ALEN<<1)));
			if ((((oldStatus&IP6_PASSTHRU_MASK)&&(*((uint16*)&skb->data[ETH_ALEN<<1])==__constant_htons(ETH_P_IPV6))) ||
			((oldStatus&PPPOE_PASSTHRU_MASK)&&((*((uint16*)&skb->data[ETH_ALEN<<1])==__constant_htons(ETH_P_PPP_SES))||
			(*((uint16*)&skb->data[ETH_ALEN<<1])==__constant_htons(ETH_P_PPP_DISC)))) ))
			{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
				cp = netdev_priv(_rtl86xx_dev.pdev);
#else
				cp = _rtl86xx_dev.pdev->priv;
#endif
				skb->dev=cp->dev;

			}
			else
			{
				dev_kfree_skb_any(skb);
					return FAILED;
			}
		}
	}
	
	return SUCCESS;
}
#endif

#if defined(CONFIG_RTK_VLAN_SUPPORT)
static inline int rtl_process_rtk_vlan_tx(rtl_nicTx_info *txInfo)
{
	struct net_device *dev;
	struct sk_buff *skb = NULL;	
	struct sk_buff *newskb;
	struct dev_priv *cp;
	if(rtk_vlan_support_enable)
	{	
		skb = txInfo->out_skb;
		dev = skb->dev;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
		cp = netdev_priv(dev);
#else
		cp = dev->priv;
#endif
		if (cp->vlan_setting.global_vlan) 
		{
			newskb = NULL;
			if (skb_cloned(skb))
			{
				newskb = skb_copy(skb, GFP_ATOMIC);
				if (newskb == NULL) 
				{
					cp->net_stats.tx_dropped++;
					dev_kfree_skb_any(skb);		
					return FAILED;
				}
				dev_kfree_skb_any(skb);
				skb = newskb;
				txInfo->out_skb = skb;
			}
			
			if (tx_vlan_process(dev, &cp->vlan_setting, skb, 0)) 
			{
				cp->net_stats.tx_dropped++;
				dev_kfree_skb_any(skb);
				return FAILED;				
			}
		}
	}
	return SUCCESS;
}
#endif

static inline int rtl_pstProcess_xmit(struct dev_priv *cp,int len)
{	
#ifndef CONFIG_RTL_NIC_HWSTATS
	cp->net_stats.tx_packets++;		
	cp->net_stats.tx_bytes += len;
#endif	
	cp->dev->trans_start = jiffies;
	return SUCCESS;
}

static inline int rtl_preProcess_xmit(rtl_nicTx_info *txInfo)
{
	int retval = SUCCESS;
	#if defined(CONFIG_RTL_CUSTOM_PASSTHRU)
	retval = rtl_process_passthru_tx(txInfo);
	if(FAILED == retval)
		return retval;
	#endif
	#if defined(CONFIG_RTK_VLAN_SUPPORT)
	retval = rtl_process_rtk_vlan_tx(txInfo);
	if(FAILED == retval)
		return retval;
	#endif
	return retval;
}
			
static inline void rtl_direct_txInfo(uint32 port_mask,rtl_nicTx_info *txInfo)
{
	txInfo->portlist = port_mask & 0x3f;
	txInfo->srcExtPort = 0;		//PKTHDR_EXTPORT_LIST_CPU;
	txInfo->flags = (PKTHDR_USED|PKT_OUTGOING);	
}

static inline void rtl_hwLookup_txInfo(rtl_nicTx_info *txInfo)
{
	txInfo->portlist = RTL8651_CPU_PORT;		/* must be set 0x7 */
	txInfo->srcExtPort = RTL_CPU_PORT - PKTHDR_EXTPORT_P1 + 1 ;
	txInfo->flags = (PKTHDR_USED|PKTHDR_HWLOOKUP|PKTHDR_BRIDGING|PKT_OUTGOING);
}

#if defined(CONFIG_RTK_SKB_PRIO_ASSIGNMENT)
extern int skb_prio2txdesc_map[];
static inline int skb_prio_txRingId_mapping(struct sk_buff *skb) {
	if (skb->priority > 0 && skb->priority <= 15)
		return skb_prio2txdesc_map[skb->priority];
	else if(((skb->mark>>4)&0xf)==0x8) //swqid = 0
		return 1;
	else 
		return 0;
}
#endif

static inline int rtl_fill_txInfo(rtl_nicTx_info *txInfo)
{
	struct sk_buff *skb = txInfo->out_skb;
	struct dev_priv *cp;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(skb->dev);
#else
	cp = skb->dev->priv;
#endif

	if (!txInfo->vid) {
		/* use net_device's vid value as default */
		txInfo->vid = cp->id;
		#ifdef CONFIG_RTL_MULTI_WAN
		/* In RTK's multiWAN(smux), vid is recorded in skb->vlan_tci */
		//if ((skb->dev->priv_flags&IFF_DOMAIN_WAN) && (skb->vlan_tci&VLAN_VID_MASK)) 
		if ((skb->dev->priv_flags&IFF_DOMAIN_WAN) && (skb->vlan_tci&VLAN_TAG_PRESENT)) 
		{
			txInfo->vid = skb->vlan_tci&VLAN_VID_MASK;
		}
		#endif
	}
#if 0
	if(!txInfo->vid)
		return FAILED;
#endif

	#if defined(CONFIG_RTL_HW_QOS_SUPPORT)
	txInfo->priority= 0;
	#endif

#if defined(CONFIG_RTK_SKB_PRIO_ASSIGNMENT)
	txInfo->txIdx = skb_prio_txRingId_mapping(skb);
	if(DumpSwNicTxRx_debug && DumpSwNicTxRx_debug_LIMIT > 0)
		printk("skb->priority:0x%x skb->mark:0x%x txRing:%d @ %s %d\n",skb->priority,skb->mark,txInfo->txIdx,__func__,__LINE__);
#else
	txInfo->txIdx = 0;
#endif	
	if((skb->data[0]&0x01)==0)
	{
#if defined(CONFIG_RTL_MULTI_LAN_DEV) || defined(CONFIG_POCKET_ROUTER_SUPPORT)
		{
#ifdef CONFIG_RTL8676_Static_ACL
			if(is_from_not_nicport_under_bridge(skb))				   
				_rtl865x_addFilterDatabaseEntry(RTL865x_L2_TYPEI, RTL_LAN_FID,	(ether_addr_t *)(&skb->data[ETHER_ADDR_LEN]), FDB_TYPE_FWD, 0x100, FALSE, FALSE);
#endif
			rtl_direct_txInfo(cp->portmask,txInfo);
			#if 0
			if(skb->src_port == 6)
			{
				printk("## srcPort:%d dev:%s @ %s %d\n",skb->src_port,skb->dev->name,__func__,__LINE__);
				rtl_hwLookup_txInfo(txInfo);
			}
			#endif
		}
#elif defined(CONFIG_RTK_VLAN_SUPPORT)
		   if(rtk_vlan_support_enable ==1)
                {
                        /* the pkt must be tx to lan vlan */				
				rtl_direct_txInfo(cp->portmask,txInfo);
                }
		   else
		   {
		#if defined (CONFIG_RTL_LOCAL_PUBLIC)
			//hyking:
			//when hw local public and sw localpublic exist at same time,
			//pkt to sw local public would be trap to cpu by default ACL
			//2010-02-22
			if(rtl_isWanDev(cp)!=TRUE)
			{
				uint32 portlist;				
				//hyking: default acl issue, direct tx now...
				if(rtl865x_getPortlistByMac(skb->data,&portlist) == SUCCESS)
					rtl_direct_txInfo(portlist,txInfo);					
				else
					rtl_direct_txInfo(cp->portmask,txInfo);
					
			}

		#else
			if (rtl_isWanDev(cp)!=TRUE)
			{
				/* the pkt must be tx to lan vlan */
				rtl_hwLookup_txInfo(txInfo);				
			}
			
		#endif			
			else
			{
				rtl_direct_txInfo(cp->portmask,txInfo);
			}
		   }
#else  //(CONFIG_RTL_MULTI_LAN_DEV) ||defined (CONFIG_POCKET_ROUTER_SUPPORT)
		{	/*unicast process*/
		#if defined (CONFIG_RTL_LOCAL_PUBLIC)
			//hyking:
			//when hw local public and sw localpublic exist at same time,
			//pkt to sw local public would be trap to cpu by default ACL
			//2010-02-22
			if(rtl_isWanDev(cp)!=TRUE)
			{
				uint32 portlist;				
				//hyking: default acl issue, direct tx now...
				if(rtl865x_getPortlistByMac(skb->data,&portlist) == SUCCESS)
					rtl_direct_txInfo(portlist,txInfo);					
				else
					rtl_direct_txInfo(cp->portmask,txInfo);
					
			}

		#else
#if 1
			{
				uint32 portlist;				
				if(_rtl_getPortlistByMac(skb->data,&portlist,-1) == SUCCESS)
				{
					rtl_direct_txInfo(portlist,txInfo);
				}
				else
				{
					rtl_direct_txInfo(cp->portmask,txInfo);
				}
			}
#else
			//if (rtl_isWanDev(cp)!=TRUE)
			//{
				rtl_hwLookup_txInfo(txInfo);
			//}
#endif
		#endif
			//else
			//{
			//	rtl_direct_txInfo(cp->portmask,txInfo);
			//}		
		}
#endif
	}
	else
	{
#if 0
		/*multicast process*/
	#if defined (CONFIG_RTL_MULTI_LAN_DEV)
	#ifdef CONFIG_RTL8676_Static_ACL
		if(is_from_not_nicport_under_bridge(skb))
			_rtl865x_addFilterDatabaseEntry(RTL865x_L2_TYPEI, RTL_LAN_FID,	(ether_addr_t *)(&skb->data[ETHER_ADDR_LEN]), FDB_TYPE_FWD, 0x100, FALSE, FALSE);
	#endif
	#else
		_rtl865x_addFilterDatabaseEntry(RTL865x_L2_TYPEI, RTL_LAN_FID,	(ether_addr_t *)(&skb->data[ETHER_ADDR_LEN]), FDB_TYPE_FWD, 0x100, FALSE, FALSE);
	#endif
#endif

#if defined(CONFIG_DSL_VTUO)|| !defined(CONFIG_RTL_MULTI_LAN_DEV) || defined(CONFIG_REMOTE_ADSL_PHY)
	// single LAN tx flooding (filter srcPort)
	if (skb->srcPort >=0 && skb->srcPort <=5)
		rtl_direct_txInfo(cp->portmask & ~(0x1<<skb->srcPort),txInfo);
	else
		rtl_direct_txInfo(cp->portmask,txInfo);
#else
rtl_direct_txInfo(cp->portmask,txInfo);
#endif

#ifdef CONFIG_RTL_STP
		if(!cp->dev->irq)
		{
			//virtual interfaces have no IRQ assigned. We use this to identify STP port interfaces.
			uint8 stpPortNum= cp->dev->name[strlen(cp->dev->name)-1]-'0';
/*			printk("send bpdu packet in nic, port num is %d\n", STP_PortDev_Mapping[stpPortNum]);	*/
			if (STP_PortDev_Mapping[stpPortNum] != NO_MAPPING)
			{
				txInfo->portlist = 1<<STP_PortDev_Mapping[stpPortNum];
			}
			else
			{
				//august: when return FAILED, we will do dev_free_skb in the caller func
				//dev_kfree_skb_any(skb);
				return FAILED;
			}
		}
#endif
	}
	if(txInfo->portlist==0)
	{
		//august: when return FAILED, we will do dev_free_skb in the caller func
		//dev_kfree_skb_any(skb);
		return FAILED;
	}
	return SUCCESS;
}

#ifdef CONFIG_RTL_MULTI_WAN
#define QOS_1P_ENABLE		(1<<8)
static int insert_vlantag(struct sk_buff **skb,unsigned int vid, unsigned int priority)
{
	struct sk_buff *tmpskb;
	unsigned char insert[16];
	u16 *vlan_tci;
	struct ethhdr *eth;
	struct vlan_ethhdr *veth;

	if(skb_cloned(*skb))
	{
		tmpskb = skb_copy(*skb, GFP_ATOMIC);
		dev_kfree_skb_any(*skb);
		if (tmpskb == NULL)
			return 0;
		*skb = tmpskb;
	}

	if (skb_headroom(*skb) < 4) {
		tmpskb = skb_realloc_headroom(*skb, 4);
		dev_kfree_skb_any(*skb);
		if (tmpskb == NULL)
			return 0;
		*skb = tmpskb;
	}

#ifdef CONFIG_E8B
	#ifdef CONFIG_NETFILTER
	if ((*skb)->mark & (1<<16)) { // marked by IPQoS				
		insert[0] = (((*skb)->mark&0x7)<<5) | (priority<<5) | (vid>>8);
	} 
	else if(((*skb)->mark&0x7)>=1)
	{
		insert[0] = ((((*skb)->mark&0x7)-1)<<5) | (priority<<5) | (vid>>8);		
	}
	else 
	#endif
		insert[0] = (priority<<5) | (vid>>8);
#else
	#ifdef CONFIG_NETFILTER
	if ((*skb)->mark&QOS_1P_ENABLE) { // IP_QoS 802.1p remarking
		insert[0] = (((*skb)->mark&0x7)<<5) | (vid>>8);
	}
	else
		insert[0] = (priority<<5) | (vid>>8);
	#else
	insert[0] = vid>>8;
	#endif
#endif
	insert[1] = vid&0xff;
	vlan_tci = (u16 *)insert;
	
	eth = (struct ethhdr *)(*skb)->data;
	if (eth->h_proto != htons(ETH_P_8021Q)) {
		veth = (struct vlan_ethhdr *)skb_push(*skb, VLAN_HLEN);
		/* Move the mac addresses to the beginning of the new header. */
		memmove((*skb)->data, (*skb)->data + VLAN_HLEN, 2 * VLAN_ETH_ALEN);
		/* first, the ethernet type */
		veth->h_vlan_proto = htons(ETH_P_8021Q);
		
		/* now, the TCI */
		veth->h_vlan_TCI = htons(*vlan_tci);
	}
	else {
		veth = (struct vlan_ethhdr *)(*skb)->data;
		/* the TCI */
		veth->h_vlan_TCI = htons(*vlan_tci);
	}

	return 1;
}
#endif

/*static inline*/ int swNic_send_func(struct sk_buff *tx_skb, struct net_device *dev, rtl_nicTx_info *nicTx)
{
	int retval = 0;
	int tx_retry = 3;

	while (--tx_retry) {
#if defined(CONFIG_RTL_SWITCH_NEW_DESCRIPTOR)
	#if (defined(CONFIG_RTL_TSO) || defined(CONFIG_RTL_GSO))
		if( (skb_shinfo(tx_skb)->nr_frags>0) || (skb_shinfo(tx_skb)->gso_size>0) )
		{
			retval = New_swNic_send_tso_sg(tx_skb, tx_skb->data, tx_skb->len, nicTx);
		}
		else
		{
			retval = New_swNic_send((void *)tx_skb, tx_skb->data, tx_skb->len, nicTx);
		}
	#else
		retval = New_swNic_send((void *)tx_skb, tx_skb->data, tx_skb->len, nicTx);
	#endif
#else
		retval = swNic_send((void *)tx_skb, tx_skb->data, tx_skb->len, nicTx);
#endif/*#if defined(CONFIG_RTL_SWITCH_NEW_DESCRIPTOR)*/
		if (retval >= 0)
			break;
		else if (retval == -2){
			netif_stop_queue(dev); //close_eth_device();
			break;
		} else {
#if defined(CONFIG_RTL_SWITCH_NEW_DESCRIPTOR)
			RTL_swNic_txDone(nicTx->txIdx);
#else
			swNic_txDone(nicTx->txIdx);
#endif
		} 
	}

	if (!tx_retry) 
	{
		//cp->net_stats.tx_dropped++;
		dev_kfree_skb_any(tx_skb);
		return 0;
	}

	return 1;
}

#ifdef CONFIG_PORT_MIRROR
static int re865x_start_xmit(struct sk_buff *skb, struct net_device *dev);

//this func is specially used for sar mirror 
static int re865x_mirror_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	int pkt_len;
	struct dev_priv *cp;
	struct sk_buff *tx_skb = skb;

	rtl_nicTx_info	nicTx;
	struct vlan_ethhdr *vhdr = (struct vlan_ethhdr *)(skb->data);

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = (struct dev_priv*)dev->priv; 
#endif
	memset(&nicTx, 0, sizeof(rtl_nicTx_info));

	nicTx.out_skb = skb;

	if((cp->id == 0) || (cp->portmask == 0))
	{
		printk("%s %d: drop skb.\n", __func__, __LINE__);
		dev_kfree_skb_any(tx_skb);
		return 0;
	}

	//fill its vid as we refer it in sw_send
	if (vhdr->h_vlan_proto == htons(ETH_P_8021Q))
		nicTx.vid = (vhdr->h_vlan_TCI & VLAN_VID_MASK);
	else /* untagged mirror packet */
		nicTx.vid = cp->id;
	rtl_direct_txInfo(cp->portmask, &nicTx);
	
	//last check
	if(0 == nicTx.portlist)
	{
		printk("%s %d: drop skb.\n", __func__, __LINE__);
		dev_kfree_skb_any(tx_skb);
		return 0;	
	}

	// Done in _swNic_send()
	//_dma_cache_wback_inv((unsigned long) tx_skb->data, tx_skb->len);

	pkt_len = tx_skb->len;
	if (swNic_send_func(tx_skb, dev, &nicTx)==0)
		return 0;

	rtl_pstProcess_xmit(cp, pkt_len);

    return 0;	
}

void nic_tx_mirror (struct sk_buff *skb)
{
    struct net_device *dev;// = skb->dev;
	struct dev_priv	*dp;// = dev->priv;
	unsigned long flags;

	if(skb->dev)
		dev = skb->dev;
	else
		dev = _rtl86xx_dev.dev[0];

	if(dev->priv_flags & IFF_DOMAIN_WAN)
		dev = _rtl86xx_dev.dev[0];

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	dp = netdev_priv(dev);
#else
	dp = dev->priv;
#endif

	spin_lock_irqsave(&dp->lock, flags);
	re865x_mirror_start_xmit(skb, dev);
	spin_unlock_irqrestore(&dp->lock, flags);
}
#endif

#if defined(CONFIG_XDSL_CTRL_PHY_IS_SOC)
int (*xdsl_ctrl_ptmpkt_xmit_hook)(struct sk_buff* skb)=NULL;
EXPORT_SYMBOL(xdsl_ctrl_ptmpkt_xmit_hook);
#endif

#if defined(CONFIG_RTL_HWNAT_TESTMODEL)
extern int TEST_MODEL_DONOT_DIRECT_TX;
#endif
__IRAM_FWD
#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER) && defined(CONFIG_XDSL_ROMEDRIVER)
#else
//static
#endif
int re865x_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	int retval = FAILED;
	int pkt_len;
	struct dev_priv *cp;
	struct sk_buff *tx_skb=skb;
	rtl_nicTx_info	nicTx;

	#if defined(CONFIG_RTL_HWNAT_TESTMODEL)
	if(TEST_MODEL_DONOT_DIRECT_TX)
	{
			dev_kfree_skb_any(tx_skb);
			return 0;
	}
	#endif
	/* ql I found only need to initialize nicTx.vid here. */
	//memset(&nicTx, 0, sizeof(rtl_nicTx_info));
	nicTx.vid = 0;
#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER)&& defined(CONFIG_XDSL_ROMEDRIVER)
	nicTx.fromRomeDriver=0;
	nicTx.ptxInfo=NULL;
#endif
#if defined(CONFIG_RTL_8685S_HWNAT) //we have highter priority
	nicTx.replaceTxDesc=0; //should be init to zero
#endif

#ifdef CONFIG_RTL867X_VLAN_MAPPING
	if (re_vlan_loaded()) {
		unsigned short vid = 0;
		retval = re_vlan_skb_xmit(skb, &vid);

		if (NET_XMIT_DROP == retval)
			return 0;

		if (vid)
			nicTx.vid = vid;
	}
#endif

	nicTx.out_skb = skb;
	retval = rtl_preProcess_xmit(&nicTx);
	if(FAILED == retval)
		return 0;
	
	tx_skb = nicTx.out_skb;

#if defined(CONFIG_XDSL_CTRL_PHY_IS_SOC)
	if (xdsl_ctrl_ptmpkt_xmit_hook){
		(*xdsl_ctrl_ptmpkt_xmit_hook)(skb);			
	}
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(tx_skb->dev);
#else
	cp = tx_skb->dev->priv;	
#endif

#ifdef CONFIG_RTL_8676HWNAT
#ifndef CONFIG_NEW_PORTMAPPING
#ifndef CONFIG_RTL_MULTI_ETH_WAN
	if(enable_port_mapping){
		if(skb->vlan_member!=0 && skb->vlan_member != cp->port_member){
			dev_kfree_skb_any(tx_skb);
			return 0;
		}
	}
#endif
#endif
#endif

	if(
#ifndef CONFIG_RTL_MULTI_ETH_WAN
		(cp->id==0) || 
#else
		((tx_skb->dev->priv_flags & IFF_DOMAIN_ELAN) && (cp->id==0)) || 
#endif
		(cp->portmask ==0))
	{
		printk("%s %d: drop skb.\n", __func__, __LINE__);
		dev_kfree_skb_any(tx_skb);
		return 0;
	}

	//printk("\n%s on dev %s\n", __func__, tx_skb->dev->name);

//#if defined (CONFIG_RTL_IGMP_SNOOPING) || defined(CONFIG_RTL_HW_PURE_SWITCH)

	retval = rtl_fill_txInfo(&nicTx);
	if(FAILED == retval) {
		printk("%s %d: drop skb.\n", __func__, __LINE__);
		dev_kfree_skb_any(tx_skb);
		return 0;	
	}

#if defined(CONFIG_XDSL_CTRL_PHY_IS_SOC)
	if (nicTx.portlist == RTL_PTMWANPORT_MASK){
		nicTx.portlist = RTL_LANPORT_MASK_1;
		nicTx.vid = RTL_LANVLANID;
	}
#endif

#ifdef RX_NAPI
	RTL_swNic_txDone(nicTx.txIdx);
#endif//end of RX_NAPI
	
#ifdef CONFIG_RTL_MULTI_ETH_WAN
//#ifndef CONFIG_NEW_PORTMAPPING
#if 0
	if (enable_port_mapping)
	{

		int port=0;
		
		if ((tx_skb->from_dev && (tx_skb->from_dev->priv_flags&(IFF_DOMAIN_ELAN|IFF_DOMAIN_WLAN))) &&
			(tx_skb->dev->priv_flags&IFF_DOMAIN_WAN)) {//upstream
			if (tx_skb->from_dev->priv_flags & IFF_DOMAIN_ELAN)
			{
                TOKEN_NUM(tx_skb->from_dev->name,&port);
//				sscanf(tx_skb->from_dev->name, "eth0.%d", &port);                        
				port -= 1;
			}
			else if (tx_skb->from_dev->priv_flags & IFF_DOMAIN_WLAN)
			{
                TOKEN_NUM(tx_skb->from_dev->name,&port);
//				sscanf(tx_skb->from_dev->name, "wlan0-vap%d", &port);
			}
			if (!(tx_skb->vlan_member & (1<<port)))
			{
				printk("upstream packet[%s -> %s] block by port mapping rule (member 0x%x).\n", 
					tx_skb->from_dev->name, tx_skb->dev->name, tx_skb->vlan_member);
				dev_kfree_skb_any(tx_skb);
				return 0;
			}
		}
		else if (tx_skb->from_dev && (tx_skb->from_dev->priv_flags&IFF_DOMAIN_WAN))
		{		  
			if (!smuxDownstreamPortmappingCheck(tx_skb))
			{
				printk("downstream packet[%s -> %s] block by port mapping rule (member 0x%x).\n", 
					tx_skb->from_dev->name, tx_skb->dev->name, tx_skb->vlan_member);
				dev_kfree_skb_any(tx_skb);
				return 0;
			}
		}		
	}
#endif
#endif
	
#ifdef CONFIG_RTL_MULTI_WAN
	if ((RTL_WANVLANID != nicTx.vid) && (RTL_LANVLANID != nicTx.vid) && (RTL_BridgeWANVLANID != nicTx.vid)) {
		if(tx_skb->mark&0x7)
		{
			if (!insert_vlantag(&tx_skb, nicTx.vid, tx_skb->mark&0x7))
				return 0;
		}
		else
		{		
			if (!insert_vlantag(&tx_skb, nicTx.vid, tx_skb->vlan_tci>>13))
				return 0;
		}
	}
	else if(( tx_skb->mark & (1<<7)) && (tx_skb->mark>>20 >0) && (dev->priv_flags & IFF_DOMAIN_WAN )) {
		//nicTx.vid is RTL_WANVLANID  or RTL_LANVLANID or RTL_BridgeWANVLANID
		//means no configured VLAN ID for this WAN interface, so checking mark setting in skb by the QoS/VLAN rules
		//and replace the vid/priority.
		//
		//Mark value define could check subr_qos_3.c in boa	
		
		if (!insert_vlantag(&tx_skb, tx_skb->mark>>20, tx_skb->mark&0x7))
			return 0;
	}
#endif

#ifdef CONFIG_PORT_MIRROR
	if(OUT_NEED_MIR(cp->mirror_mode)) {
		switchcore_mirror_pkt(tx_skb, OUT);
	}
#endif	
	pkt_len = tx_skb->len;
	if (swNic_send_func(tx_skb, dev, &nicTx)==0)
		return 0;

	rtl_pstProcess_xmit(cp, pkt_len);
	//cp->net_stats.tx_packets++;		
	//cp->net_stats.tx_bytes += tx_skb->len;

    return 0;
}

#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER) && defined(CONFIG_XDSL_ROMEDRIVER)

void do_txInfoMask(struct tx_info* pTxInfo, struct tx_info* ptx, struct tx_info* ptxMask){
	if(ptxMask && ptx){
		if(ptxMask->opts1.dw)
		{
			pTxInfo->opts1.dw &= (~ptxMask->opts1.dw);
			pTxInfo->opts1.dw |= (ptx->opts1.dw & ptxMask->opts1.dw);
		}
		if(ptxMask->opts2.dw)
		{
			pTxInfo->opts2.dw &= (~ptxMask->opts2.dw);
			pTxInfo->opts2.dw |= (ptx->opts2.dw & ptxMask->opts2.dw);
		}
		if(ptxMask->opts3.dw)
		{
			pTxInfo->opts3.dw &= (~ptxMask->opts3.dw);
			pTxInfo->opts3.dw |= (ptx->opts3.dw & ptxMask->opts3.dw);
		}
		if(ptxMask->opts4.dw)
		{
			pTxInfo->opts4.dw &= (~ptxMask->opts4.dw);
			pTxInfo->opts4.dw |= (ptx->opts4.dw & ptxMask->opts4.dw);
		}
	}
}

void txinfo_debug(struct tx_info *pTxInfo)
{

		printk("txInfo:\n");
		printk("opts1\t= 0x%08x Own=%d EOR=%d FS=%d LS=%d IPCS=%d L4CS=%d Keep=%d Psel=%d Len=%d\n", pTxInfo->opts1.dw,
			pTxInfo->opts1.bit.own,
			pTxInfo->opts1.bit.eor,
			pTxInfo->opts1.bit.fs,
			pTxInfo->opts1.bit.ls,
			pTxInfo->opts1.bit.ipcs,
			pTxInfo->opts1.bit.l4cs,
			pTxInfo->opts1.bit.keep,
			pTxInfo->opts1.bit.cputag_psel,
			pTxInfo->opts1.bit.data_length
		);
		printk("addr\t= 0x%08x \n", pTxInfo->addr);
		printk("opts2\t= 0x%08x AsPri=%d CpuPri=%d VlanAct=%d Pppoe=%d SidIdx=%d Vlan=%d Pri=%d\n", pTxInfo->opts2.dw,
			pTxInfo->opts2.bit.aspri,			
			pTxInfo->opts2.bit.cputag_pri,
			pTxInfo->opts2.bit.tx_vlan_action,
			pTxInfo->opts2.bit.tx_pppoe_action,
			pTxInfo->opts2.bit.tx_pppoe_idx,
			pTxInfo->opts2.bit.vidh<<8|pTxInfo->opts2.bit.vidl,
			pTxInfo->opts2.bit.prio);
		printk("opts3\t= 0x%08x ExtSpa=%d TxPmsk=0x%x L34Keep=%d PON_SID=%d\n", pTxInfo->opts3.dw,
			pTxInfo->opts3.bit.extspa,
			pTxInfo->opts3.bit.tx_portmask,
			pTxInfo->opts3.bit.l34_keep,
			pTxInfo->opts3.bit.tx_dst_stream_id);

		printk("opts4\t= 0x%08x\n", pTxInfo->opts4.dw);		
	
}

int re865x_send_with_txInfo_and_mask(struct sk_buff *skb, struct tx_info* ptxInfo, int ring_num, struct tx_info* ptxInfoMask)
{

	int retval = FAILED;
	struct net_device *dev;
	struct dev_priv *cp;	
	struct sk_buff *tx_skb = skb;
	rtl_nicTx_info	nicTx;
	
	//apollo
	struct tx_info local_txInfo={{{0}},0,{{0}},{{0}}};
	u32 eor=0;

	int tx_retry = 3;

	//using root dev[0]
	dev= _rtl86xx_dev.dev[0];
	//set skb dev 
	skb->dev = dev;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = (struct dev_priv*)dev->priv;
#endif
	
	/* ql I found only need to initialize nicTx.vid here. */	
	//memset(&nicTx, 0, sizeof(rtl_nicTx_info));	
	nicTx.vid = 0;
	nicTx.ptxInfo=NULL;
	nicTx.out_skb = skb;
	nicTx.fromRomeDriver=0;
	nicTx.flags=0;
#if defined(CONFIG_RTL_8685S_HWNAT) //we have highter priority
	nicTx.replaceTxDesc=0; //should be init to zero
#endif

	if(ptxInfo!=NULL)memcpy(&local_txInfo, ptxInfo, sizeof(struct tx_info));

	//fill re8686 field
	if (skb_shinfo(skb)->nr_frags == 0) {
		u32 len;
//		DEBUG("skb_shinfo(skb)->nr_frags \n");
		len = skb->len;	
		//default setting, always need this
		local_txInfo.addr = (u32)(skb->data);
		local_txInfo.opts1.dw |= (eor | len | DescOwn | FirstFrag |LastFrag | TxCRC | IPCS);

		//plz put tx additional setting into this function
//		tx_additional_setting(skb, dev, &local_txInfo);

		do_txInfoMask(&local_txInfo, ptxInfo, ptxInfoMask);
		nicTx.ptxInfo = &local_txInfo;
		nicTx.fromRomeDriver=1;

/*		
		DEBUG("FROM_FWD[%x] DA=%02x:%02x:%02x:%02x:%02x:%02x SA=%02x:%02x:%02x:%02x:%02x:%02x ethtype=%04x len=%d VlanAct=%d Vlan=%d Pri=%d ExtSpa=%d TxPmsdk=0x%x L34Keep=%x PON_SID=%d\n",
		(u32)skb&0xffff,
		skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5],
		skb->data[6],skb->data[7],skb->data[8],skb->data[9],skb->data[10],skb->data[11],
		(skb->data[12]<<8)|skb->data[13],skb->len,
		local_txInfo.opts2.bit.tx_vlan_action,
		local_txInfo.opts2.bit.vidh<<8|local_txInfo.opts2.bit.vidl,
		local_txInfo.opts2.bit.prio,
		local_txInfo.opts3.bit.extspa,
		local_txInfo.opts3.bit.tx_portmask,
		local_txInfo.opts3.bit.l34_keep,
		local_txInfo.opts3.bit.tx_dst_stream_id);
*/

	}else{
//		DEBUG("%s %d: not support frag xmit now\n", __func__, __LINE__);
		dev_kfree_skb_any(skb);
	}

#ifdef CONFIG_RTL_MULTI_ETH_WAN
		if(((tx_skb->dev->priv_flags & IFF_DOMAIN_ELAN) && (cp->id==0)) || (cp->portmask ==0))
#else
		if((cp->id == 0) || (cp->portmask == 0))
#endif
		{
			DEBUG("%s %d: drop skb.\n", __func__, __LINE__);
			dev_kfree_skb_any(tx_skb);
			return 0;
		}

	retval = rtl_fill_txInfo(&nicTx);
	if(FAILED == retval) {
		printk("%s %d: drop skb.\n", __func__, __LINE__);
		dev_kfree_skb_any(tx_skb);
		return 0;	
	}

	//fill its vid as we refer it in sw_send	
	nicTx.vid = cp->id; 
	rtl_direct_txInfo(cp->portmask, &nicTx);
	//last check	
	if(0 == nicTx.portlist) 
	{		
//		DEBUG("%s %d: drop skb.\n", __func__, __LINE__);
		dev_kfree_skb_any(tx_skb);		
		return 0;		

	}

#ifdef RX_NAPI
	RTL_swNic_txDone(nicTx.txIdx);
#endif//end of RX_NAPI

/*
#ifdef CONFIG_RTL_MULTI_ETH_WAN
#ifndef CONFIG_NEW_PORTMAPPING
		if (enable_port_mapping)
		{
	
			int port=0;
			
			if ((tx_skb->from_dev && (tx_skb->from_dev->priv_flags&(IFF_DOMAIN_ELAN|IFF_DOMAIN_WLAN))) &&
				(tx_skb->dev->priv_flags&IFF_DOMAIN_WAN)) {//upstream
				if (tx_skb->from_dev->priv_flags & IFF_DOMAIN_ELAN)
				{
					TOKEN_NUM(tx_skb->from_dev->name,&port);
	//				sscanf(tx_skb->from_dev->name, "eth0.%d", &port);						 
					port -= 1;
				}
				else if (tx_skb->from_dev->priv_flags & IFF_DOMAIN_WLAN)
				{
					TOKEN_NUM(tx_skb->from_dev->name,&port);
	//				sscanf(tx_skb->from_dev->name, "wlan0-vap%d", &port);
				}
				if (!(tx_skb->vlan_member & (1<<port)))
				{
					printk("upstream packet[%s -> %s] block by port mapping rule (member 0x%x).\n", 
						tx_skb->from_dev->name, tx_skb->dev->name, tx_skb->vlan_member);
					dev_kfree_skb_any(tx_skb);
					return 0;
				}
			}
			else if (tx_skb->from_dev && (tx_skb->from_dev->priv_flags&IFF_DOMAIN_WAN))
			{		  
				if (!smuxDownstreamPortmappingCheck(tx_skb))
				{
					printk("downstream packet[%s -> %s] block by port mapping rule (member 0x%x).\n", 
						tx_skb->from_dev->name, tx_skb->dev->name, tx_skb->vlan_member);
					dev_kfree_skb_any(tx_skb);
					return 0;
				}
			}		
		}
#endif
		
		if ((RTL_WANVLANID != nicTx.vid) && (RTL_LANVLANID != nicTx.vid) && (RTL_BridgeWANVLANID != nicTx.vid)) {
			if (!insert_vlantag(&tx_skb, nicTx.vid))
				return 0;
		}
#endif
*/

	// Done in _swNic_send()
	//_dma_cache_wback_inv((unsigned long) tx_skb->data, tx_skb->len);

//	DEBUG("nicTx.vid=%d  nicTx.protlist=%x nicTx.fromRomeDriver=%d \n",nicTx.vid,nicTx.portlist,nicTx.fromRomeDriver); //check cp correct

	while (--tx_retry && ((retval=swNic_send((void *)tx_skb, tx_skb->data, tx_skb->len, &nicTx)) < 0))	
	{		
#if defined(RX_NAPI)
		if(retval<0){
			DEBUG("swNic_send Error retval=%d\n",retval);
		}
		RTL_swNic_txDone(nicTx.txIdx);	
#else	
		if (retval == -2){
			netif_stop_queue(dev); //close_eth_device();
			DEBUG("!!!!!!!!!!!!!!!!!!!!stop traffic\n");
			break;
		} else {
			RTL_swNic_txDone(nicTx.txIdx);
		} 

#endif

	}	
	
	if (!tx_retry)	
	{		
		//cp->net_stats.tx_dropped++;		
		dev_kfree_skb_any(tx_skb);		
		return 0;	
	}

	wmb();
	rtl_pstProcess_xmit(cp, tx_skb->len);	

	return 0;
}
#endif

/*romedriver Boyce 2014-07-09*/
#ifdef CONFIG_XDSL_NEW_HWNAT_DRIVER
__IRAM_FWD
int re865x_start_xmit_check(struct sk_buff *skb, struct net_device *dev)
{

#if defined(CONFIG_XDSL_ROMEDRIVER)
	// direct tx
//	printk(" skb->dev->name : %s \n",skb->dev->name);
	if(memcmp(skb->dev->name,"wlan1",5)==0 || memcmp(skb->dev->name,"vwlan", 5)==0  ||  memcmp(skb->dev->name,"ptm0", 4)==0  )
//	if(1)
	{
			return re865x_start_xmit(skb,dev);
	}
	else
	{		// send to fwdEngine

			return rtk_rg_fwdEngine_xmit (skb,dev);
	}

	return 0;
#elif defined(CONFIG_XDSL_LITEROMEDRIVER)

	return re865x_start_xmit(skb,dev);
#else
	ERROR("not LITEROMEDRIVER and ROMEDRIVER what' up ");
#endif
	
}
#endif

#ifdef CONFIG_RTL8685_PTM_MII
typedef struct ERB_header 
{
	unsigned char VCE_macaddr[6];
	unsigned char VTU_R_macaddr[6];
	unsigned char length[2];
	
	unsigned char LLC_header[3];
	unsigned char ITU_T[3];
	unsigned char protocol_id[2];

	unsigned char line_id[2];
	unsigned char sync_sumbol_count[2];
	unsigned char segment_code;
}ERB_header_t;

/*
 *	Called by DSL driver to send ERB Ethernet packet in G.Vector mode.
 */
int re865x_send_ERB(char* ERB_data, int ERB_data_len
			,int line_id,int sync_symbol_count,int segment_code,unsigned char* VCE_macaddr)
{
	struct sk_buff *send_skb;
	ERB_header_t* ERB_header_p;
	struct net_device * ptm_dev = dev_get_by_name(&init_net,ALIASNAME_PTM0);
	unsigned short skb_len = ((ERB_data_len+27)>=60)?(ERB_data_len+27):60;
	
	send_skb = dev_alloc_skb(skb_len);
	if(!send_skb) {
		dev_put(ptm_dev);
		return 0;
	}

	skb_put(send_skb,skb_len);
	memset(send_skb->data,0,skb_len);

	/* header */
	ERB_header_p = (ERB_header_t*)(&send_skb->data[0]);	
	memcpy(ERB_header_p->VCE_macaddr,VCE_macaddr,6);	
	memcpy(ERB_header_p->VTU_R_macaddr,ptm_dev->dev_addr,6);
	*(unsigned short *)(&ERB_header_p->length) 	= skb_len-14; // L2 header
	ERB_header_p->LLC_header[0]		= 0xAA;
	ERB_header_p->LLC_header[1]		= 0xAA;
	ERB_header_p->LLC_header[2]		= 0x03;
	ERB_header_p->ITU_T[0]			= 0x00;
	ERB_header_p->ITU_T[1]			= 0x19;
	ERB_header_p->ITU_T[2]			= 0xA7;
	ERB_header_p->protocol_id[0]		= 0x00;
	ERB_header_p->protocol_id[1]		= 0x03;
	*(unsigned short *)(&ERB_header_p->line_id)				=(unsigned short)(line_id&0xFFFF);
	*(unsigned short *)(&ERB_header_p->sync_sumbol_count)	=(unsigned short)(sync_symbol_count&0xFFFF);
	ERB_header_p->segment_code		=segment_code&0xFF;

	/* payload */
	memcpy(&(send_skb->data[27]),ERB_data,ERB_data_len);

	/* assign ptm device */
	send_skb->dev = ptm_dev;

	re865x_start_xmit(send_skb,ptm_dev);

	dev_put(ptm_dev);

	return 0;
}
//#define ERB_DEBUG
#ifdef ERB_DEBUG
extern int erb_debug;
#define erb_printk(fmt, args...) if (erb_debug) printk("[%s] " fmt, __func__, ## args) 
#else
#define erb_printk(fmt, args...) 
#endif

int erb_send_cnt = 0;

#if defined(CONFIG_RTL8685_PTM_MII) && defined(CONFIG_PTM_BONDING_MASTER)
static void dump_slave_erb_pkt(unsigned char *b, int l) {
	unsigned char *byte;
	int i;

	//dump rxbuf
	byte = b;
	
	printk("ERB packet (total: %d bytes):", l);
	for(i = 0; i < l; i++) {
		if (!(i%16)) printk("\n");
		printk("%02x ", byte[i]);		
	}
	printk("\n\n");	
}
#endif

#if defined(CONFIG_PTM_BONDING_MASTER)
int (*xdsl_ctrl_get_slave_ERB_handler)(unsigned char *buf, int *len);
EXPORT_SYMBOL(xdsl_ctrl_get_slave_ERB_handler);

int slave_erb_send_cnt = 0;

int re865x_send_slave_ERB(unsigned char *buf, int len)
{
	struct sk_buff *send_skb_slave;	
	struct net_device * ptm_dev = dev_get_by_name(&init_net, ALIASNAME_PTM0);
	unsigned short skb_len;

	int slave_ERB_packet_len = len;
	unsigned char *slave_ERB_packet = buf;
	ERB_header_t *slave_ERB_header_p;

	if (slave_ERB_packet_len == 0 ) {
		erb_printk("get slave ERB packet failed, slave ERB packet length = 0\n");
		return 0;
	}

	if(ptm_dev == NULL) {
		printk("[%s] get ptm_dev failed!\n", __func__);
		return 0;
	}

	skb_len = (slave_ERB_packet_len >= 60)? slave_ERB_packet_len : 60;
	send_skb_slave = dev_alloc_skb(skb_len); 
	if(!send_skb_slave) {
		dev_put(ptm_dev);
		return 0;
	}

	skb_put(send_skb_slave,skb_len);
	/*replace the Slave's source mac with Master's*/
	slave_ERB_header_p = (ERB_header_t*)slave_ERB_packet;
	memcpy(slave_ERB_header_p->VTU_R_macaddr,ptm_dev->dev_addr,6);	
	memset(send_skb_slave->data, 0, skb_len);
	memcpy(send_skb_slave->data, slave_ERB_packet, slave_ERB_packet_len);

	send_skb_slave->dev = ptm_dev;
	re865x_start_xmit(send_skb_slave,ptm_dev);

	slave_erb_send_cnt++;
	printk("Slave's ERB has been sent!(%d)\n", slave_erb_send_cnt);
	dump_slave_erb_pkt(slave_ERB_packet, slave_ERB_packet_len);
	dev_put(ptm_dev);
	
	return 0;

}
EXPORT_SYMBOL(re865x_send_slave_ERB);

#endif
#endif //CONFIG_PTM_MII

static void re865x_tx_timeout (struct net_device *dev)
{	
	int i;
	struct netdev_queue *txq;
	
	rtlglue_printf("Tx Timeout!!! Can't send packet %s, ", dev->name);
	rtlglue_printf("dev->trans: %lx, ", dev->trans_start);
	for (i = 0; i < dev->num_tx_queues; i++) {
		txq = netdev_get_tx_queue(dev, i);
		rtlglue_printf("txq[%d]->trans: %lx, ", i, txq->trans_start);
	}
	rtlglue_printf("jiffies: %lx\n", jiffies);

#if defined(CONFIG_RTK_PTM_US_CTRL)
	if (netif_queue_stopped(dev)){
		netif_wake_queue(dev);
		if (dev->name[0] == 'n' || dev->name[0] == 'p'){
				open_smux_dev_bydev(dev);	
		}
	}
#endif	
}

#if defined(RTL819X_PRIV_IOCTL_ENABLE)
int rtl819x_get_port_status(int portnum , struct lan_port_status *port_status)
{	
		uint32	regData;
		uint32	data0;

		if( portnum < 0 ||  portnum > CPU)
			return -1;

		regData = READ_MEM32(PSRP0+((portnum)<<2));

		//printk("rtl819x_get_port_status port = %d data=%x\n", portnum,regData); //mark_debug
		data0 = regData & PortStatusLinkUp;		
		if (data0)
			port_status->link =1;
		else
			port_status->link =0;
				
		data0 = regData & PortStatusNWayEnable;
		if (data0)
			port_status->nway=1;
		else
			port_status->nway =0;

		data0 = regData & PortStatusDuplex;
		if (data0)
			port_status->duplex=1;
		else
			port_status->duplex =0;

		data0 = (regData&PortStatusLinkSpeed_MASK)>>PortStatusLinkSpeed_OFFSET;
		port_status->speed = data0 ; // 0 = 10M , 1= 100M , 2=1G ,

	       return 0;	
}

int rtl819x_get_port_stats(int portnum , struct port_statistics *port_stats)
{

	uint32 addrOffset_fromP0 =0;	

	//printk("rtl819x_get_port_stats port = %d \n", portnum); //mark_debug
	if( portnum < 0 ||  portnum > CPU)
			return -1;

	addrOffset_fromP0 = portnum * MIB_ADDROFFSETBYPORT;

#if defined(CONFIG_REMOTE_ADSL_PHY) && defined(CONFIG_RTL8685SB) /*For 8685SB Remote PHY. RX bytes is 64 bit long counter*/
	port_stats->rx_bytes =rtl865xC_returnAsicCounter64( OFFSET_IFINOCTETS_P0 + addrOffset_fromP0 );
#else
	port_stats->rx_bytes =rtl8651_returnAsicCounter( OFFSET_IFINOCTETS_P0 + addrOffset_fromP0 ) ;
#endif	
	port_stats->rx_unipkts= rtl8651_returnAsicCounter( OFFSET_IFINUCASTPKTS_P0 + addrOffset_fromP0 ) ;
	port_stats->rx_mulpkts= rtl8651_returnAsicCounter( OFFSET_ETHERSTATSMULTICASTPKTS_P0 + addrOffset_fromP0 ) ;
	port_stats->rx_bropkts= rtl8651_returnAsicCounter( OFFSET_ETHERSTATSBROADCASTPKTS_P0 + addrOffset_fromP0 ) ;
	port_stats->rx_discard= rtl8651_returnAsicCounter( OFFSET_DOT1DTPPORTINDISCARDS_P0 + addrOffset_fromP0 ) ;
	port_stats->rx_error= (rtl8651_returnAsicCounter( OFFSET_DOT3STATSFCSERRORS_P0 + addrOffset_fromP0 ) +
						 rtl8651_returnAsicCounter( OFFSET_ETHERSTATSJABBERS_P0 + addrOffset_fromP0 ));
#if defined(CONFIG_REMOTE_ADSL_PHY) && defined(CONFIG_RTL8685SB) /*For 8685SB Remote PHY. RX bytes is 64 bit long counter*/
	port_stats->tx_bytes = rtl865xC_returnAsicCounter64( OFFSET_IFOUTOCTETS_P0 + addrOffset_fromP0 );
#else
	port_stats->tx_bytes =rtl8651_returnAsicCounter( OFFSET_IFOUTOCTETS_P0 + addrOffset_fromP0 ) ;
#endif
	port_stats->tx_unipkts= rtl8651_returnAsicCounter( OFFSET_IFOUTUCASTPKTS_P0 + addrOffset_fromP0 ) ;
	port_stats->tx_mulpkts= rtl8651_returnAsicCounter( OFFSET_IFOUTMULTICASTPKTS_P0 + addrOffset_fromP0 ) ;
	port_stats->tx_bropkts= rtl8651_returnAsicCounter( OFFSET_IFOUTBROADCASTPKTS_P0 + addrOffset_fromP0 ) ;
	port_stats->tx_discard= rtl8651_returnAsicCounter( OFFSET_IFOUTDISCARDS + addrOffset_fromP0 ) ;
	port_stats->tx_error= (rtl8651_returnAsicCounter( OFFSET_ETHERSTATSCOLLISIONS_P0 + addrOffset_fromP0 ) +
						 rtl8651_returnAsicCounter( OFFSET_DOT3STATSDEFERREDTRANSMISSIONS_P0 + addrOffset_fromP0 ));
	
	return 0;	
}
#ifdef CONFIG_RTL_HW_QOS_SUPPORT
#if 0
#define DBG_PRINT(fmt, args...) printk(fmt, ##args)
void qos_queue_dbg(struct rtl867x_hwnat_qos_queue qdisc){
	DBG_PRINT("==================================\n");
	switch(qdisc.action) {
		case QDISC_ENABLE:
			DBG_PRINT("[HWNAT]Qdisc Enabled: sp_num=%d wrr_num=%d bandwidth=%d default_queue=%d\n",
				qdisc.sp_num, qdisc.wrr_num, qdisc.bandwidth, qdisc.default_queue);
			break;
		case QDISC_DISABLE:
			DBG_PRINT("[HWNAT]Qdisc Disabled.\n");
			break;
		default:
			break;
	}
	DBG_PRINT("\n");
}

void qos_rule_dbg(struct rtl867x_hwnat_qos_rule rule){
	DBG_PRINT("==================================\n");
	DBG_PRINT("rule type: %u\t q_index: %u\n", rule.rule_type, rule.q_index);
	DBG_PRINT("remark_8021p: 0x%x\t remark_dscp: 0x%x\n", rule.remark_8021p, rule.remark_dscp);
	switch(rule.rule_type){
		case RTL867x_IPQos_Format_srcPort:
			DBG_PRINT("inIfname: %s\n", rule.Format_SRCPort_NETIFNAME);
			break;
		case RTL867x_IPQos_Format_Ethernet:
			break;
		case RTL867x_IPQos_Format_IP:
			DBG_PRINT("sip: 0x%x\t sipMask: 0x%x\n", rule.Format_IP_SIP, rule.Format_IP_SIP_M);
			DBG_PRINT("dip: 0x%x\t dipMask: 0x%x\n", rule.Format_IP_DIP, rule.Format_IP_DIP_M);
			DBG_PRINT("tos: 0x%x\t tosMask: 0x%x\n", rule.Format_IP_TOS, rule.Format_IP_TOS_M);
			DBG_PRINT("ipProto: 0x%x\t ipProtoMask: 0x%x\n", rule.Format_IP_Proto, rule.Format_IP_Proto_M);
			break;
		case RTL867x_IPQos_Format_TCP:
			DBG_PRINT("sip: 0x%x\t sipMask: 0x%x\n", rule.Format_TCP_SIP, rule.Format_TCP_SIP_M);
			DBG_PRINT("dip: 0x%x\t dipMask: 0x%x\n", rule.Format_TCP_DIP, rule.Format_TCP_DIP_M);
			DBG_PRINT("tos: 0x%x\t tosMask: 0x%x\n", rule.Format_TCP_TOS, rule.Format_TCP_TOS_M);
			DBG_PRINT("sPort_start: 0x%x\t sPort_end: 0x%x\n", rule.Format_TCP_SPORT_Sta, rule.Format_TCP_SPORT_End);
			DBG_PRINT("dPort_start: 0x%x\t dPort_end: 0x%x\n", rule.Format_TCP_DPORT_Sta, rule.Format_TCP_DPORT_End);
			break;
		case RTL867x_IPQos_Format_UDP:
			DBG_PRINT("sip: 0x%x\t sipMask: 0x%x\n", rule.Format_UDP_SIP, rule.Format_UDP_SIP_M);
			DBG_PRINT("dip: 0x%x\t dipMask: 0x%x\n", rule.Format_UDP_DIP, rule.Format_UDP_DIP_M);
			DBG_PRINT("tos: 0x%x\t tosMask: 0x%x\n", rule.Format_UDP_TOS, rule.Format_UDP_TOS_M);
			DBG_PRINT("sPort_start: 0x%x\t sPort_end: 0x%x\n", rule.Format_UDP_SPORT_Sta, rule.Format_UDP_SPORT_End);
			DBG_PRINT("dPort_start: 0x%x\t dPort_end: 0x%x\n", rule.Format_UDP_DPORT_Sta, rule.Format_UDP_DPORT_End);
			break;
		case RTL867x_IPQos_Format_8021p:
			DBG_PRINT("vlanTagPri: %u\n", rule.Format_8021P_PRIORITY);
			break;
		default:
			break;
	}
	DBG_PRINT("\n");
}
#else
#define DBG_PRINT(fmt, args...) 
void qos_queue_dbg(struct rtl867x_hwnat_qos_queue qdisc){
}
void qos_rule_dbg(struct rtl867x_hwnat_qos_rule rule){
}
#endif

int RTL_1P_REMARK_ENABLE=0;
int RTL_DSCP_REMARK_ENABLE=0;
static int hwnat_qos_qdisc(struct rtl867x_hwnat_qos_queue qdisc)
{
	switch(qdisc.action) {
		case QDISC_ENABLE:
			rtl8676_IPQos_Enable(qdisc.sp_num, qdisc.wrr_num, qdisc.ceil,
				qdisc.weight, qdisc.default_queue);
			RTL_1P_REMARK_ENABLE=0;
			RTL_DSCP_REMARK_ENABLE=0;
			break;
		case QDISC_DISABLE:
			rtl8676_IPQos_Disable();
			break;
		default:
			break;
	}
	return 0;
}

int hwnat_itf_ioctl(hwnat_ioctl_cmd *cmd)
{
	int i,port=0;
	int ret=0; //ioctl return 0 on success
	struct hwnat_ioctl_itf_cmd *cmdData = NULL;
	unsigned int rate;

	cmdData = (struct hwnat_ioctl_itf_cmd *)cmd->data;

	if((cmdData == NULL) || (cmdData->ifname == NULL))
		return -EFAULT;

	printk("hwnat_itf_ioctl cmd type: %u ifname:%s\n", cmdData->type,cmdData->ifname);

	for(i=0;i<6;i++)
	{
		struct net_device *dev;
		
		dev = _rtl86xx_dev.dev[portid_index[i]];
		if(dev && !strcmp(dev->name,cmdData->ifname))
		{
			port = i;
			break;
		}
	}
	if(i==6) //Could get port index by interface name
		return -EFAULT;
	
	switch(cmdData->type)
	{
		case Set_ShapingRate_CMD:
			printk("\n[%s:%d]Set_ShapingRate_CMD, rate=%d bps\n",__func__,__LINE__,cmdData->u.ShapingRate);
			if (cmdData->u.ShapingRate >= 0)
				rate = (unsigned int)cmdData->u.ShapingRate;
			else  // no shaping
				rate = 0xffffffff;
			if(rtl865x_setItfShapingRate(port,rate)!=SUCCESS)
			{
				ret = -EFAULT;
			}
			break;
		case Get_ShapingRate_CMD:
			if(rtl865x_getItfShapingRate(port,&cmdData->u.ShapingRate)!=SUCCESS)
			{
				ret = -EFAULT;
			}
			else
			{
				printk("\n[%s:%d]Get_ShapingRate_CMD, rate=%d bps\n",__func__,__LINE__,cmdData->u.ShapingRate);
				copy_to_user(&cmd, cmdData, sizeof(struct hwnat_ioctl_qos_cmd));
			}
			break;
		case Set_ShapingBurstSize_CMD:
			printk("\n[%s:%d]Set_ShapingBurstSize_CMD, burstsize=%d bytes\n",__func__,__LINE__,cmdData->u.ShapingBurstSize);
			if(rtl865x_setItfShapingBurstSize(port,cmdData->u.ShapingBurstSize)!=SUCCESS)
			{
				ret = -EFAULT;
			}
			break;
		case Get_ShapingBurstSize_CMD:
			if(rtl865x_getItfShapingBurstSize(port,&cmdData->u.ShapingBurstSize)!=SUCCESS)
			{
				ret = -EFAULT;
			}
			else
			{
				printk("\n[%s:%d]Get_ShapingBurstSize_CMD, burstsize=%d bytes\n",__func__,__LINE__,cmdData->u.ShapingBurstSize);
				copy_to_user(&cmd, cmdData, sizeof(struct hwnat_ioctl_qos_cmd));
			}
			break;
		default:
			ret = -EFAULT;
			break;
	}
	return ret;
}

int hwnat_qos_ioctl(struct hwnat_ioctl_qos_cmd *data){
	struct hwnat_ioctl_qos_cmd cmd;
	int ret=0; //ioctl return 0 on success
	int return_index;
 	if (copy_from_user(&cmd, data, sizeof(struct hwnat_ioctl_qos_cmd)))
	{
		return -EFAULT;
	}
	DBG_PRINT("hwnat_qos_ioctl cmd type: %u\n", cmd.type);
	switch(cmd.type){
		case OUTPUTQ_CMD:
			qos_queue_dbg(cmd.u.qos_queue);
			hwnat_qos_qdisc(cmd.u.qos_queue);
			break;
		case QOSRULE_CMD:
			qos_rule_dbg(cmd.u.qos_rule.qos_rule_pattern);
			if(cmd.u.qos_rule.qos_rule_remark_8021p>0)
				RTL_1P_REMARK_ENABLE=1;
			if(cmd.u.qos_rule.qos_rule_remark_dscp>0)
				RTL_DSCP_REMARK_ENABLE=1;
			rtl865x_Qos_SetRemarking(RTL_1P_REMARK_ENABLE,RTL_DSCP_REMARK_ENABLE);
/* ysleu@20140403: Support Traffic Classification */
#if defined(RTL867X_IPQoS_Support_Traffic_Classification) && (defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB))
			rtl8676_IPQos_AddRule(&cmd.u.qos_rule.qos_rule_pattern
				,cmd.u.qos_rule.qos_rule_queue_index
				,cmd.u.qos_rule.qos_rule_vid
				,cmd.u.qos_rule.qos_rule_remark_8021p-1
				,cmd.u.qos_rule.qos_rule_remark_dscp-1,&return_index);
#else
			rtl8676_IPQos_AddRule(&cmd.u.qos_rule.qos_rule_pattern
				,cmd.u.qos_rule.qos_rule_queue_index
				,cmd.u.qos_rule.qos_rule_remark_8021p-1
				,cmd.u.qos_rule.qos_rule_remark_dscp-1,&return_index);
#endif
			break;
		case OUTPUTQ_BWCTL_CMD:
			printk("\n[%s:%d]OUTPUTQ_BWCTL_CMD, total_bandwidth=%d bps\n",__func__,__LINE__,cmd.qos_total_bandwidth);
			if(cmd.qos_total_bandwidth==0){		
				printk("Total bandwidth set to default value!\n");
				rtl865x_qosSetBandwidth(RTL_WANPORT_MASK,1024*1024*1024);
			}
			else
				rtl865x_qosSetBandwidth(RTL_WANPORT_MASK,cmd.qos_total_bandwidth);
			break;
		default:
			ret = -EFAULT;
			break;
	}
	return ret;
}
#endif // of CONFIG_RTL_HW_QOS_SUPPORT

#if defined(CONFIG_RTL_HARDWARE_NAT) && defined(CONFIG_RTL_LAYERED_DRIVER_L3)
int hwnat_extip_ioctl(struct hwnat_ioctl_extip_cmd *data){
	struct hwnat_ioctl_extip_cmd cmd;
	int ret=0; //ioctl return 0 on success
 	if (copy_from_user(&cmd, data, sizeof(struct hwnat_ioctl_extip_cmd)))
	{
		return -EFAULT;
	}

	switch(cmd.extip_rule.action){
		case ADD_EXTIP_CMD:
			//printk("add ip:%x intf:%s!!!\n", cmd.extip_rule.ip, cmd.extip_rule.name);
			rtl8676_add_napt_extIp(cmd.extip_rule.name, cmd.extip_rule.ip);
			break;
		case DEL_EXTIP_CMD:
			//printk("del ip:%x intf:%s!!!\n", cmd.extip_rule.ip, cmd.extip_rule.name);
			rtl8676_del_napt_extIp(cmd.extip_rule.name, cmd.extip_rule.ip);
			break;
		default:
			ret = -EFAULT;
			break;
	}
	return ret;
}
#endif

int hwnat_ioctl(hwnat_ioctl_cmd *data){
 	hwnat_ioctl_cmd cmd;
	int ret;
 	if (copy_from_user(&cmd, data, sizeof(hwnat_ioctl_cmd)))
	{
		return -EFAULT;
	}
	switch(cmd.type){
#ifdef CONFIG_RTL_HW_QOS_SUPPORT
		case HWNAT_IOCTL_QOS_TYPE:
			ret = hwnat_qos_ioctl((struct hwnat_ioctl_qos_cmd *)cmd.data);
			break;
#endif
#if defined(CONFIG_RTL_HARDWARE_NAT) && defined(CONFIG_RTL_LAYERED_DRIVER_L3)
		case HWNAT_IOCTL_EXTIP_TYPE:
			ret = hwnat_extip_ioctl((struct hwnat_ioctl_extip_cmd*)cmd.data);
			break;
#endif
#ifdef CONFIG_RTL_HW_QOS_SUPPORT
		case HWNAT_IOCTL_ITF_TYPE:
			ret = hwnat_itf_ioctl(&cmd);
			break;
#endif
		default:
			ret = -EFAULT;
			break;
	}
	return ret;
}
 
int re865x_priv_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
{
	int32 rc = 0;
	unsigned long *data_32;
	int portnum=0;
	struct lan_port_status port_status;
	struct port_statistics port_stats;

	data_32 = (unsigned long *)rq->ifr_data;
	if (copy_from_user(&portnum, data_32, 1*sizeof(unsigned long)))
	{
		return -EFAULT;
	}
	
	switch (cmd)
	{
		 case SIOCETHTEST:
			eth_ctl((struct eth_arg *)rq->ifr_data);
			break;
	     case RTL819X_IOCTL_READ_PORT_STATUS:		 	
		 	rc = rtl819x_get_port_status(portnum,&port_status); //portnumber	
		 	if(rc != 0)
				return -EFAULT;
		 	if (copy_to_user((void *)rq->ifr_data, (void *)&port_status, sizeof(struct lan_port_status)))
				return -EFAULT;
		 	break;
	     case RTL819X_IOCTL_READ_PORT_STATS:
		 	rc = rtl819x_get_port_stats(portnum,&port_stats); //portnumber	
		 	if(rc != 0)
				return -EFAULT;			
		 	if (copy_to_user((void *)rq->ifr_data, (void *)&port_stats, sizeof(struct port_statistics)))
				return -EFAULT;
		 	break;
		case RTL819X_IOCTL_HWNAT:
			rc = hwnat_ioctl((hwnat_ioctl_cmd *)rq->ifr_data);
			break;
	     default :
		 	rc = -EOPNOTSUPP;
			break;
	}
	return rc;

}

#endif

//for RTL8676 port mapping
#ifdef CONFIG_RTL_8676HWNAT
void set_port_isolation_member(int port, unsigned int member){
	switch(port){
		case 0:
			WRITE_MEM32(PIR0, (READ_MEM32(PIR0)&(~PI_PORT0_MASK))|(member<<PI_PORT0_OFFSET));
			break;
		case 1:
			WRITE_MEM32(PIR0, (READ_MEM32(PIR0)&(~PI_PORT1_MASK))|(member<<PI_PORT1_OFFSET));
			break;
		case 2:
			WRITE_MEM32(PIR0, (READ_MEM32(PIR0)&(~PI_PORT2_MASK))|(member<<PI_PORT2_OFFSET));
			break;
		case 3:
			WRITE_MEM32(PIR1, (READ_MEM32(PIR1)&(~PI_PORT3_MASK))|(member<<PI_PORT3_OFFSET));
			break;
		case 4:
			WRITE_MEM32(PIR1, (READ_MEM32(PIR1)&(~PI_PORT4_MASK))|(member<<PI_PORT4_OFFSET));
			break;
		case 5:
			WRITE_MEM32(PIR1, (READ_MEM32(PIR1)&(~PI_PORT5_MASK))|(member<<PI_PORT5_OFFSET));
			break;
			
	}
}

int set_port_mapping(struct net_device *dev, struct ifreq *rq, int cmd)
{
#ifndef CONFIG_RTL_MULTI_ETH_WAN
	int totalVlans, i;
#endif	
	struct ifvlan *ifv = rq->ifr_data;

#ifndef CONFIG_RTL_MULTI_ETH_WAN
	if(ifv->enable == 0){
		//printk("clear all port mapping setting\n");
		enable_port_mapping=0;
		WRITE_MEM32(PIR0,(PI_PORT0_MASK|PI_PORT1_MASK|PI_PORT2_MASK));
		WRITE_MEM32(PIR1,(PI_PORT3_MASK|PI_PORT4_MASK|PI_PORT5_MASK));
	}
	else{
		//printk("set port mapping setting for specific port");
		enable_port_mapping=1;
		totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
		for(i=0;i<totalVlans;i++){
			if(vlanconfig[i].memPort == (1<<ifv->port)){
				((struct dev_priv *)(_rtl86xx_dev.dev[i]->priv))->port_member = ifv->member;
				set_port_isolation_member(ifv->port, ifv->member&0x1f);
			}			
		}
	}
#else
	printk("port mapping %s: dev %s member:0x%x\n", ifv->enable?"enable":"disable", dev->name, ifv->member);
	/*
	 * QL port mapping mechanism: all packets are trapped to cpu by default, if it pass through firewall and port mapping rule, then
	 * acl rule will be written to asic table and portmapping will take effect.
	 */
	if ((ifv->enable == 0) && enable_port_mapping)
	{
		enable_port_mapping = 0;
#ifdef CONFIG_RTL_HARDWARE_NAT
		//printk("rtl865x_syncRouteToAsic\n");
		rtl865x_syncRouteToAsic();
#endif
	}
	else if ((ifv->enable == 1) && !enable_port_mapping)
	{
		enable_port_mapping = 1;
#ifdef CONFIG_RTL_HARDWARE_NAT
		/* clear routing asic table. */
				//printk("rtl865x_clearAsicRoutingTable\n");
		rtl865x_clearAsicRoutingTable();
#endif
	}

#ifdef CONFIG_RTL867X_IPTABLES_FAST_PATH
	/* reset fastpath */
	rtl867x_clearFastPathEntry();
#endif

	/* reset hw-acc */
#ifdef CONFIG_RTL_HARDWARE_NAT
	#ifdef CONFIG_RTL8676_Static_ACL
		rtl865x_nat_init();
	#else /* CONFIG_RTL8676_Dynamic_ACL */	
		rtl8676_clean_L34Unicast_hwacc();			
	#endif
#endif

#endif

	/* IPv6 default routing to CPU */
#if defined(CONFIG_RTL_HWNAT_IPv6_SUPPORT)
	{
		rtl8198C_tblAsicDrv_ipv6routingParam_t asic_t;
		memset(&asic_t,0,sizeof(rtl8198C_tblAsicDrv_ipv6routingParam_t));
		asic_t.process = V6L3_PROCESS_S_CPU;
		rtl8198C_setAsicIpv6Routing(V6RT_ASIC_ENTRY_NUM-1, &asic_t);
	}
#endif

	return 0;
}
#endif

#ifdef CONFIG_PORT_MIRROR
static int do_mirror_root(struct net_device *dev, struct portmir *pmr)
{
	struct list_head *lg;
	struct smux_group *smux_grp;
	struct smux_dev_info *sdev = NULL;
	struct dev_priv *cp;
	
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif
	smux_grp = (struct smux_group *)__find_smux_group(dev->name);
	switch (pmr->port_mirror) {
		case -1: /* status */
			printk("%s:\taction=%d, todev=%s\n", dev->name, cp->mirror_mode, cp->monitor_dev?cp->monitor_dev->name:"n/a");
			if (!smux_grp)
				break;
			list_for_each(lg, &smux_grp->virtual_devs){
				sdev = (struct smux_dev_info *)list_entry_smuxdev(lg);
				if (sdev)
					printk("%s:\taction=%d, todev=%s\n", sdev->vdev->name, sdev->port_mirror, sdev->mirror_dev?sdev->mirror_dev->name:"n/a");
			}
			break;
		case -2: /* flush */
			/* flush root */
			cp->mirror_mode = 0;
			cp->monitor_dev = 0;
			if (!smux_grp)
				break;
			/* flush virtual device */
			list_for_each(lg, &smux_grp->virtual_devs){
				sdev = (struct smux_dev_info *)list_entry_smuxdev(lg);
				if (sdev) {
					sdev->port_mirror = 0;
					sdev->mirror_dev = 0;
				}
			}
			break;
		default:
			printk("%s: error command!\n", __FUNCTION__);
	}
	return 0;
}

static int set_real_dev_mirror(struct net_device *dev, struct ifreq *ifr)
{
	struct portmir *pmr;
	struct net_device *todev = NULL;
	struct dev_priv *cp;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif
	pmr = (struct portmir *)ifr->ifr_data;
	
	if (pmr->port_mirror < 0) {
		do_mirror_root(dev, pmr);
		return 0;
	}
	
	// monitor port to receive mirrored traffic: eth0.2, eth0.3 ...
	todev = dev_get_by_name(&init_net, pmr->mir_dev_name);
	if(!todev)
	{
		printk("error lan device!\n");
		return -1;
	}	
	if((todev->priv_flags & IFF_DOMAIN_ELAN) == 0)
	{
		printk("error lan device!\n");
		return -1;
	}
	
	cp->mirror_mode = pmr->port_mirror;
	cp->monitor_dev = todev;
	//printk("monitor_dev: %s; mirred_dev: %s\n", cp->monitor_dev->name, dev->name);

	return 0;
}

static inline void switchcore_mirror_pkt(struct sk_buff *skb, const int flag)
{
	struct dev_priv *cp;
	struct sk_buff *skb2;
	
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(skb->dev);
#else
	cp = skb->dev->priv;
#endif
	if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) 
	{
		// tx to monitor port
		skb2->dev = cp->monitor_dev;
		nic_tx_mirror(skb2);
	}
}
#endif // of CONFIG_PORT_MIRROR

int re865x_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
{
	int32 rc = 0;
	unsigned long *data;
	int32 args[4];
	int32  * pRet;
	#if defined(CONFIG_RTL8186_KB)||defined(CONFIG_RTL8186_GR)
	uint32	*pU32;
	#endif

#ifdef CONFIG_RTL_8676HWNAT
	if(cmd == SIOCSITFGROUP){
		rc = set_port_mapping(dev, rq, cmd);
		return rc;
	}
#endif

#ifdef CONFIG_PORT_MIRROR
#define SIOCPORTMIRROR (SIOCDEVPRIVATE + 0xf) /*ethwan port mirror*/
	if (cmd == SIOCPORTMIRROR) {
		rc = set_real_dev_mirror(dev, rq);
		return rc;
	}
#endif
	if (cmd != SIOCDEVPRIVATE)
	{
		#if defined(RTL819X_PRIV_IOCTL_ENABLE)
		rc = re865x_priv_ioctl(dev,rq,cmd); 
 		return rc;
		#else
 		goto normal;
		#endif
	}

	data = (unsigned long *)rq->ifr_data;

	if (copy_from_user(args, data, 4*sizeof(unsigned long)))
	{
		return -EFAULT;
	}

	switch (args[0])
	{

#ifdef CONFIG_RTL8196_RTL8366
		case RTL8651_IOCTL_GETWANLINKSTATUS:
			{
				uint32 phyNum;
				uint32 linkStatus;

				pRet = (int32 *)args[3];
				*pRet = FAILED;
				rc = SUCCESS;

				phyNum=4;//8366 WAN port
				rc=rtl8366rb_getPHYLinkStatus(phyNum, &linkStatus);

				if(rc==SUCCESS)
				{
					if(linkStatus==1)
					{
						*pRet = SUCCESS;
					}
				}

				break;
			}
#else
		case RTL8651_IOCTL_GETWANLINKSTATUS:
			{
				int i;
				int wanPortMask;
				int32 totalVlans;

				pRet = (int32 *)args[3];
				*pRet = FAILED;
				rc = SUCCESS;

				wanPortMask = 0;
				totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
				for(i=0;i<totalVlans;i++)
				{
					if(vlanconfig[i].isWan==TRUE)
						wanPortMask = vlanconfig[i].memPort;
				}

				if (wanPortMask==0)
				{
					/* no wan port exist */					
					break;
				}

				for(i=0;i<RTL8651_AGGREGATOR_NUMBER;i++)
				{
					if( (1<<i)&wanPortMask )
					{
						if((READ_MEM32(PSRP0+(i<<2))&PortStatusLinkUp)!=0)
						{
							*pRet = SUCCESS;
						}
						break;
					}
				}
				
				break;
			}
#endif
		case RTL8651_IOCTL_GETWANLINKSPEED:
			{
				int i;
				int wanPortMask;
				int32 totalVlans;
					
				pRet = (int32 *)args[3];
				*pRet = FAILED;
				rc = FAILED;

				wanPortMask = 0;
				totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
				for(i=0;i<totalVlans;i++)
				{
					if(vlanconfig[i].isWan==TRUE)
						wanPortMask = vlanconfig[i].memPort;
				}

				if (wanPortMask==0)
				{
					/* no wan port exist */
					break;
				}

				for(i=0;i<RTL8651_AGGREGATOR_NUMBER;i++)
				{
					if( (1<<i)&wanPortMask )
					{
						break;
					}
				}
				
				switch(READ_MEM32(PSRP0 + (i<<2)) & PortStatusLinkSpeed_MASK)
				{
					case PortStatusLinkSpeed10M:
						*pRet = PortStatusLinkSpeed10M;
						rc = SUCCESS;
						break;
					case PortStatusLinkSpeed100M:
						*pRet = PortStatusLinkSpeed100M;
						rc = SUCCESS;
						break;
					case  PortStatusLinkSpeed1000M:
						*pRet = PortStatusLinkSpeed1000M;
						rc = SUCCESS;
						break;
					default:
						break;
				}
				break;
			}
#if defined(CONFIG_RTL8186_KB)|| defined(CONFIG_RTL8186_GR)
		case RTL8651_IOCTL_GETLANLINKSTATUS:
			{
				int i;
				int lanPortMask;
				int32 totalVlans;

				pRet = (int32 *)args[3];
				*pRet = FAILED;
				rc = SUCCESS;

				lanPortMask = 0;
				totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
				for(i=0;i<totalVlans;i++)
				{
					if(vlanconfig[i].isWan==FALSE)
					{
						lanPortMask = vlanconfig[i].memPort;
						if (lanPortMask==0)
						{
							/* no wan port exist */					
							continue;
						}

						for(i=0;i<=RTL8651_PHY_NUMBER;i++)
						{
							if( (1<<i)&lanPortMask )
							{
								if((READ_MEM32(PSRP0+(i<<2))&PortStatusLinkUp)!=0)
								{
									//rtlglue_printf("Lan port i=%d\n",i);//Added for test
									*pRet = SUCCESS;
									return rc;
								}
							}
						}
					}
				}

				break;
			}
#if defined(CONFIG_RTL8186_KB)
		case RTL8651_IOCTL_GETWANTHROUGHPUT:
			{
				static unsigned long last_jiffies = 0;
				static unsigned long last_rxtx = 0;
				int i;
				int32 totalVlans;
				struct dev_priv *cp;
				int32 *throughputLevel;
				unsigned long	diff_jiffies;
				pRet = (int32 *)args[3];

				diff_jiffies = (jiffies-last_jiffies);
				if (diff_jiffies>HZ)
				{
					pU32 = (uint32*)args[1];
					throughputLevel = (uint32*)pU32[0];
					rc = SUCCESS;

					cp = NULL;
					totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
					for(i=0;i<totalVlans;i++)
					{
						if(vlanconfig[i].isWan==TRUE)
						{
						#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
							cp = netdev_priv(_rtl86xx_dev.dev[i]);
						#else
							cp = _rtl86xx_dev.dev[i]->priv;
						#endif
							break;
						}
					}

					if (cp==NULL)
					{
						/* no wan port exist */					
						rc = FAILED;
					}

					for(i=1;i<20;i++)
					{
						if (diff_jiffies < (HZ<<i))
							break;
					}
					/* get the throughput level */
					*throughputLevel = (((cp->net_stats.rx_bytes + cp->net_stats.tx_bytes)-last_rxtx)>>(17+i));

					last_jiffies = jiffies;
					last_rxtx = (cp->net_stats.rx_bytes + cp->net_stats.tx_bytes);
					*pRet = SUCCESS;
				}
				else
				{
					*pRet = FAILED;
				}
			}
#endif
#endif
#ifdef CONFIG_RTL_LAYERED_DRIVER
#if defined(CONFIG_RTL8186_GR)
		case RTL8651_IOCTL_SETWANLINKSTATUS:
			{
				int i;
				int wanPortMask;
				int32 totalVlans;
				int portStatusToSet;
				int forceMode;
				int forceLink;
				int forceLinkSpeed;
				int forceDuplex;
				uint32 regValue;
				uint32 advCapability;
				#define SPEED10M 	0
				#define SPEED100M 	1
				#define SPEED1000M 	2
				
				pRet = (int32 *)args[3];
				*pRet = FAILED;
				rc = SUCCESS;
				
				pU32 = (uint32*)args[1];
				portStatusToSet = *(uint32*)pU32[0];
				
				wanPortMask = 0;
				totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
				for(i=0;i<totalVlans;i++)
				{
					if(vlanconfig[i].isWan==TRUE)
						wanPortMask = vlanconfig[i].memPort;
				}

				if (wanPortMask==0)
				{
					/* no wan port exist */					
					break;
				}

				for(i=0;i<RTL8651_AGGREGATOR_NUMBER;i++)
				{
					if( (1<<i)&wanPortMask )
					{
						/*write register*/
			
						if(HALF_DUPLEX_10M == portStatusToSet)
						{
							forceMode=TRUE;
							forceLink=TRUE;
							forceLinkSpeed=SPEED10M;
							forceDuplex=FALSE;
							advCapability=(1<<HALF_DUPLEX_10M);
						}else if(HALF_DUPLEX_100M == portStatusToSet)
						{
							forceMode=TRUE;
							forceLink=TRUE;
							forceLinkSpeed=SPEED100M;
							forceDuplex=FALSE;
							advCapability=(1<<HALF_DUPLEX_100M);
						}else if(HALF_DUPLEX_1000M == portStatusToSet)
						{
							forceMode=TRUE;
							forceLink=TRUE;
							forceLinkSpeed=SPEED1000M;
							forceDuplex=FALSE;
							advCapability=(1<<HALF_DUPLEX_1000M);
						}else if(DUPLEX_10M == portStatusToSet)
						{
							forceMode=TRUE;
							forceLink=TRUE;
							forceLinkSpeed=SPEED10M;
							forceDuplex=TRUE;
							advCapability=(1<<DUPLEX_10M);
						}else if(DUPLEX_100M == portStatusToSet)
						{
							forceMode=TRUE;
							forceLink=TRUE;
							forceLinkSpeed=SPEED100M;
							forceDuplex=TRUE;
							advCapability=(1<<DUPLEX_100M);
						}else if(DUPLEX_1000M == portStatusToSet)
						{
							forceMode=TRUE;
							forceLink=TRUE;
							forceLinkSpeed=SPEED1000M;
							forceDuplex=TRUE;
							advCapability=(1<<DUPLEX_1000M);
						}else if(PORT_AUTO == portStatusToSet)
						{
							forceMode=FALSE;
							forceLink=TRUE;
							/*all capality*/
							advCapability=(1<<PORT_AUTO);
							
						}else
						{
							forceMode=FALSE;
							forceLink=TRUE;
						}
						rtl865xC_setAsicEthernetForceModeRegs(i, forceMode, forceLink, forceLinkSpeed, forceDuplex);
						
						/*Set PHY Register*/
						rtl8651_setAsicEthernetPHYSpeed(i,forceLinkSpeed);
						rtl8651_setAsicEthernetPHYDuplex(i,forceDuplex);
						rtl8651_setAsicEthernetPHYAutoNeg(i,TRUE);
						rtl8651_setAsicEthernetPHYAdvCapability(i,advCapability);
						rtl8651_restartAsicEthernetPHYNway(i);
						break;
					}
				}
				
				break;
			}
		case RTL8651_IOCTL_GETLANPORTLINKSTATUS:
			{
				int i;
                            int lanPortMask;
                            int32 totalVlans;
				int32 *lanportnum;
				int32 lanPortTypeMask;
				uint32 regVal;
				uint32 portLinkSpeed;
 
                            pRet = (int32 *)args[3];
                            *pRet = FAILED;
                            rc = SUCCESS;

				pU32 = (uint32*)args[1];
				lanportnum = (uint32*)pU32[0];
 
                                lanPortMask = 0;
                                totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
                                for(i=0;i<totalVlans;i++)
                                {
                                        if(vlanconfig[i].isWan==FALSE)
                                        {
                                                lanPortMask = vlanconfig[i].memPort;
                                                if (lanPortMask==0)
                                                {
                                                        /* no wan port exist */
                                                        continue;
                                                }
 
                                                for(i=0;i<=RTL8651_PHY_NUMBER;i++)
                                                {
                                                        if( (1<<i)&lanPortMask )
                                                        {
                                                        	regVal=READ_MEM32(PSRP0+(i<<2));
                                                                if((regVal&PortStatusLinkUp)!=0)
                                                                {
										if(i==(*lanportnum))
										{
                                                                        	*pRet = SUCCESS;
																			
											if((regVal&PortStatusDuplex)!=0)
											{
												lanPortTypeMask=1;
												*pRet |= lanPortTypeMask;
											}

											portLinkSpeed=regVal&PortStatusLinkSpeed_MASK;
											if(PortStatusLinkSpeed100M==portLinkSpeed)
											{
												lanPortTypeMask=4;
												*pRet |= lanPortTypeMask;
											}
											else if(PortStatusLinkSpeed1000M==portLinkSpeed)
											{
												lanPortTypeMask=8;
												*pRet |= lanPortTypeMask;
											}
											else
											{
												lanPortTypeMask=2;
												*pRet |= lanPortTypeMask;
											}
											
                                                                        	return rc;
										}
                                                                }
                                                        }
                                                }
                                        }
                                }
 
                                break;
			}

		case RTL8651_IOCTL_GETWANPORTLINKSTATUS:
			{
				int i;
				int wanPortMask;
				int32 totalVlans;
				int32 wanPortTypeMask;
				uint32 regVal;
				uint32 portLinkSpeed;

				pRet = (int32 *)args[3];
				*pRet = FAILED;
				rc = SUCCESS;

				wanPortMask = 0;
				totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
				for(i=0;i<totalVlans;i++)
				{
					if(vlanconfig[i].isWan==TRUE)
						wanPortMask = vlanconfig[i].memPort;
				}

				if (wanPortMask==0)
				{
					/* no wan port exist */					
					break;
				}

				for(i=0;i<RTL8651_AGGREGATOR_NUMBER;i++)
				{
					if( (1<<i)&wanPortMask )
					{
						/*check phy status link up or down*/
						rtl8651_getAsicEthernetPHYStatus(i,&regVal);
						if(regVal & (1<<2))
						{
							regVal=READ_MEM32(PSRP0+(i<<2));
							if((regVal&PortStatusLinkUp)!=0)
							{
								*pRet = SUCCESS;

								if((regVal&PortStatusDuplex)!=0)
								{
										wanPortTypeMask=1;
										*pRet |= wanPortTypeMask;
								}

								portLinkSpeed=regVal&PortStatusLinkSpeed_MASK;
								if(PortStatusLinkSpeed100M==portLinkSpeed)
								{
										wanPortTypeMask=4;
										*pRet |= wanPortTypeMask;
								}
								else if(PortStatusLinkSpeed1000M==portLinkSpeed)
								{
										wanPortTypeMask=8;
										*pRet |= wanPortTypeMask;
								}
								else
								{
										wanPortTypeMask=2;
										*pRet |= wanPortTypeMask;
								}
							}
						}
						break;
					}
				}
				
				break;
			}
#endif
#endif
		default:
			rc = SUCCESS;
			break;
	}
	
	return rc;
#if !defined(RTL819X_PRIV_IOCTL_ENABLE)
normal:	
#endif
	if (!netif_running(dev))
		return -EINVAL;
	switch (cmd)
        {
	    default:
		rc = -EOPNOTSUPP;
		break;
	}
	return rc;
}

static int rtl865x_set_hwaddr(struct net_device *dev, void *addr)
{
	unsigned long flags;
	int i;
	unsigned char *p;
#if defined (CONFIG_RTL_MULTI_LAN_DEV)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	struct dev_priv *cp = netdev_priv(dev);
#else
	struct dev_priv *cp = dev->priv;
#endif
#endif
#ifdef CONFIG_RTL_MULTI_LAN_DEV
	u32 vid;
#endif

	p = ((struct sockaddr *)addr)->sa_data;
 	local_irq_save(flags);
	for (i = 0; i<ETHER_ADDR_LEN; ++i) {
		dev->dev_addr[i] = p[i];
		dev->broadcast[i] = 0xff;
	}

	//It's unnecessary to sync hw netif table and root device(nas0/ptm0).
	if(dev && (!strcmp(dev->name,ALIASNAME_NAS0) || !strcmp(dev->name,ALIASNAME_PTM0))) {
		local_irq_restore(flags);
		return SUCCESS;
	}

#ifdef CONFIG_RTL_MULTI_LAN_DEV
	vid = cp->id;
#endif
#if defined (CONFIG_RTL_MULTI_LAN_DEV)
	/*find matched vlan config*/
	for(i=0; vlanconfig[i].vid != 0; i++)
	{
		if(	(vlanconfig[i].vid==vid ) && (vlanconfig[i].if_type==IF_ETHER) && 
			(vlanconfig[i].memPort & cp->portmask & ((1<<RTL8651_MAC_NUMBER)-1)))
		{
			break;
		}
	}

	if(vlanconfig[i].vid != 0)
	{
		memcpy(vlanconfig[i].mac.octet, dev->dev_addr, ETHER_ADDR_LEN);
	}
#else
#if defined(CONFIG_RTK_VLAN_SUPPORT)
    TOKEN_NUM(dev->name,&i);
//	i = dev->name[3] - '0';
	if ((i >=0) && (i <5))
		memcpy(vlanconfig[i].mac.octet, dev->dev_addr, ETHER_ADDR_LEN);

#else
	if (!strcmp(dev->name, RTL_PS_LAN_P0_DEV_NAME))
		i = 0;
	else
		i = 1;	
	memcpy(vlanconfig[i].mac.octet, dev->dev_addr, ETHER_ADDR_LEN);
#endif
#endif

#ifdef CONFIG_HARDWARE_NAT_DEBUG
/*2007-12-19*/
	rtlglue_printf("%s:%d:dev->name is %s,__lrconfig[%d].mac is 0x%x:%x:%x:%x:%x:%x\n",__FUNCTION__,__LINE__,dev->name, i, 
	vlanconfig[i].mac.octet[0],vlanconfig[i].mac.octet[1],vlanconfig[i].mac.octet[2],vlanconfig[i].mac.octet[3],vlanconfig[i].mac.octet[4],vlanconfig[i].mac.octet[5]);

	rtlglue_printf("%s:%d:__lrconfig[%d].vid is %d\n",__FUNCTION__,__LINE__,i,vlanconfig[i].vid);
#endif

#ifdef CONFIG_XDSL_NEW_HWNAT_DRIVER
#else
	if(vlanconfig[i].vid != 0)
	{
		rtl865x_netif_t netif;
		memcpy(netif.macAddr.octet,vlanconfig[i].mac.octet,ETHER_ADDR_LEN);
		memcpy(netif.name,vlanconfig[i].ifname,MAX_IFNAMESIZE);
		rtl865x_setNetifMac(&netif);
	}
#endif

	local_irq_restore(flags);
	return SUCCESS;
}

#if defined(CONFIG_RTL8186_LINK_CHANGE)
static int rtl865x_set_link(struct net_device *dev, int enable)
{
	int32 i;
	struct dev_priv *cp;
	int32 portmask;
	int32 totalVlans;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	cp = netdev_priv(dev);
#else
	cp = dev->priv;
#endif
#if defined (CONFIG_RTL_MULTI_LAN_DEV)
	portmask=cp->portmask;
#else
	portmask=0;
	totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;

	for(i=0;i<totalVlans;i++)
	{
		if(vlanconfig[i].vid==cp->id)
		{
			portmask = vlanconfig[i].memPort;
			break;
		}
	}
#endif	
	if (portmask)
	{
		if (enable)
		{
			for(i=0;i<RTL8651_PHY_NUMBER;i++)
			{
				if (portmask & (1<<i))
				{
					rtl865xC_setAsicEthernetForceModeRegs(i, FALSE, TRUE, 1, TRUE);
					rtl8651_restartAsicEthernetPHYNway(i);
				}
			}
		}
		else
		{
			for(i=0;i<RTL8651_PHY_NUMBER;i++)
			{
				if (portmask & (1<<i))
					rtl865xC_setAsicEthernetForceModeRegs(i, TRUE, FALSE, 1, TRUE);
			}
		}
	}

	return SUCCESS;
}
#endif

static int rtl865x_set_mtu(struct net_device *dev, int new_mtu)
{
	unsigned long flags;
	int i;
#if defined (CONFIG_RTL_MULTI_LAN_DEV)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	struct dev_priv *cp = netdev_priv(dev);
#else
	struct dev_priv *cp = dev->priv;
#endif
#endif
#ifdef CONFIG_RTL_MULTI_LAN_DEV
	u32 vid;
#endif

 	local_irq_save(flags);

#ifdef CONFIG_RTL_MULTI_LAN_DEV
#ifndef CONFIG_RTL_MULTI_ETH_WAN
	vid = cp->id;
#else
	if (dev->priv_flags & IFF_DOMAIN_ELAN)
		vid = cp->id;
	else {
		struct net_device *real_dev;
		struct smux_dev_info *dev_info;
		//if(alias_name_is_eq(CMD_CMP,dev->name,ALIASNAME_NAS0) ||
		//	alias_name_is_eq(CMD_CMP,dev->name,ALIASNAME_PTM0))
		if (dev->priv_flags & IFF_RSMUX) // smux real device
		{
			printk("%s on %s.\n", __func__, dev->name);
			dev->mtu = new_mtu;
			local_irq_restore(flags);
			
			return SUCCESS;
		}
		else {	// smux overlap device
			// dev is smux overlap device, reassign cp to real_dev cp
			printk("overlap %s on %s.\n", __func__, dev->name);
			dev_info = SMUX_DEV_INFO(dev);
			real_dev = dev_info->smux_grp->real_dev;
			#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
			cp = netdev_priv(real_dev);
			#else
			cp = real_dev->priv;
			#endif
			vid = (dev_info->vid==-1)?RTL_WANVLANID:(SMUX_DEV_INFO(dev)->vid);
		}
	}
#endif
#endif

#if defined (CONFIG_RTL_MULTI_LAN_DEV)
	/*find matched vlan config*/
	for(i=0; vlanconfig[i].ifname[0] != '\0'; i++)
	{
		if(	(vlanconfig[i].vid==vid) && (vlanconfig[i].if_type==IF_ETHER) && 
			(vlanconfig[i].memPort & cp->portmask & ((1<<RTL8651_MAC_NUMBER)-1)))
		{
			break;
		}
	}
	
	if(vlanconfig[i].vid==0)
	{
		local_irq_restore(flags);
		return FAILED;		
	}
#else
#if defined(CONFIG_RTK_VLAN_SUPPORT)
        TOKEN_NUM(dev->name,&i);
//	i = dev->name[3] - '0';
	if (i>5)
	{
		local_irq_restore(flags);
		return FAILED;		
	}
#else
	if (!strcmp(dev->name, RTL_PS_LAN_P0_DEV_NAME))
		i = 0;
	else
		i = 1;
#endif
#endif
	dev->mtu = new_mtu;	
	vlanconfig[i].mtu=(uint32)new_mtu;

#ifdef CONFIG_XDSL_NEW_HWNAT_DRIVER
#else
	if(vlanconfig[i].vid!=0)
	{
		rtl865x_netif_t netif;
		netif.mtu = vlanconfig[i].mtu;
		memcpy(netif.name,vlanconfig[i].ifname,MAX_IFNAMESIZE);

		rtl865x_setNetifMtu(&netif);
	}
#endif
	#ifdef CONFIG_HARDWARE_NAT_DEBUG
	/*2007-12-19*/
		rtlglue_printf("%s:%d:new_mtu is %d\n",__FUNCTION__,__LINE__,new_mtu);
	#endif

	local_irq_restore(flags);

	return SUCCESS;
}

#if defined(CONFIG_COMPAT_NET_DEV_OPS)
#else
static const struct net_device_ops rtl819x_netdev_ops = {
	.ndo_open		= re865x_open,
	.ndo_stop		= re865x_close,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_set_mac_address 	= rtl865x_set_hwaddr,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	.ndo_set_rx_mode	= re865x_set_rx_mode,
#else
	.ndo_set_multicast_list	= re865x_set_rx_mode,
#endif
	.ndo_get_stats		= re865x_get_stats,
	.ndo_do_ioctl		= re865x_ioctl,
/*romedriver Boyce 2014-07-09*/
#ifdef CONFIG_XDSL_NEW_HWNAT_DRIVER
			.ndo_start_xmit 	= re865x_start_xmit_check,
#else
			.ndo_start_xmit 	= re865x_start_xmit,
#endif

	.ndo_tx_timeout		= re865x_tx_timeout,
#if defined(CP_VLAN_TAG_USED)
	.ndo_vlan_rx_register	= cp_vlan_rx_register,
#endif
	.ndo_change_mtu		= rtl865x_set_mtu,

};
#endif

#if defined (CONFIG_RTL_IGMP_SNOOPING) && defined(CONFIG_RTL_HW_L2_ONLY)

int rtl865x_addAclForIGMPSnooping(void)
{
			int i;
			rtl865x_AclRule_t rule;
#if defined (CONFIG_RTL_MULTI_LAN_DEV) || defined(CONFIG_RTK_VLAN_SUPPORT)
			struct rtl865x_vlanConfig tmpVlanConfig[NETIF_SW_NUMBER];
#endif
			struct rtl865x_vlanConfig *pVlanConfig=NULL;

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

#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)
			re865x_packVlanConfig(vlanconfig, tmpVlanConfig);
			pVlanConfig=tmpVlanConfig;
#else
			pVlanConfig=vlanconfig;
#endif			

			for(i=0; pVlanConfig[i].vid != 0; i++)
			{
				if (IF_ETHER!=pVlanConfig[i].if_type)
				{
					continue;
				}

				rtl865x_regist_aclChain(pVlanConfig[i].ifname, RTL865X_ACL_IGMP_USED,1);

				bzero((void*)&rule,sizeof(rtl865x_AclRule_t));

				rule.ruleType_ = RTL865X_ACL_MAC;
				rule.actionType_ = RTL865X_ACL_TOCPU;
				rule.pktOpApp_ = RTL865X_ACL_ALL_LAYER;
				rule.un_ty.dstMac_.octet[0] = 0x01;
				rule.un_ty.dstMac_.octet[1] = 0x00;
				rule.un_ty.dstMac_.octet[2] = 0x5E;
				rule.un_ty.dstMacMask_.octet[0] = 0xff;					
				rule.un_ty.dstMacMask_.octet[1] = 0xff;
				rule.un_ty.dstMacMask_.octet[2] = 0xff;					
				rtl865x_add_acl(&rule, pVlanConfig[i].ifname, RTL865X_ACL_IGMP_USED,1,0);

			}
	return SUCCESS;
}

int rtl865x_removeAclForIGMPSnooping(void)
{
		int i;
#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)	
		struct rtl865x_vlanConfig tmpVlanConfig[NETIF_SW_NUMBER];
#endif
	
		struct rtl865x_vlanConfig *pVlanConfig=NULL;
		
		if(vlanconfig==NULL)
		{
			return FAILED;
		}
#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)	
		re865x_packVlanConfig(vlanconfig, tmpVlanConfig);
		pVlanConfig=tmpVlanConfig;
#else
		pVlanConfig=vlanconfig;
#endif
	
		for(i=0; pVlanConfig[i].vid != 0; i++)
		{
			if (IF_ETHER!=pVlanConfig[i].if_type)
			{
				continue;
			}
		
			rtl865x_unRegist_aclChain(pVlanConfig[i].ifname, RTL865X_ACL_IGMP_USED);
	
		}
		return SUCCESS;

}

#endif

#if defined (CONFIG_RTL_MLD_SNOOPING) && !defined (CONFIG_RTL_HW_NAPT)
//static int rtl865x_addAclForMldSnooping(struct rtl865x_vlanConfig* vlanConfig)
int rtl865x_addAclForMldSnooping(struct rtl865x_vlanConfig* vlanConfig)
{
	int i;
	#if defined (CONFIG_RTL_MULTI_LAN_DEV) || defined(CONFIG_RTK_VLAN_SUPPORT)
	struct rtl865x_vlanConfig tmpVlanConfig[NETIF_SW_NUMBER];
	#endif
	struct rtl865x_vlanConfig *pVlanConfig=NULL;
	rtl865x_AclRule_t	rule;
	int ret=FAILED;
	
	if(vlanConfig==NULL)
	{
		return FAILED;
	}
	
#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)
	re865x_packVlanConfig(vlanConfig, tmpVlanConfig);
	pVlanConfig=tmpVlanConfig;
#else
	pVlanConfig=vlanConfig;
#endif

	for(i=0; pVlanConfig[i].vid != 0; i++)
	{
		if (IF_ETHER!=pVlanConfig[i].if_type)
		{
			continue;
		}
		
		if(pVlanConfig[i].isWan==0)/*lan config*/
		{
			rtl865x_regist_aclChain(pVlanConfig[i].ifname, RTL865X_ACL_IPV6_USED,2);
			/*ping6 issue*/
			bzero((void*)&rule,sizeof(rtl865x_AclRule_t));
			rule.ruleType_ = RTL865X_ACL_MAC;
			rule.actionType_		= RTL865X_ACL_PERMIT;
			rule.pktOpApp_ 		= RTL865X_ACL_ALL_LAYER;
			rule.un_ty.dstMac_.octet[0]=0x33;
			rule.un_ty.dstMac_.octet[1]=0x33;
			rule.un_ty.dstMac_.octet[2]=0xFF;
	
			rule.un_ty.dstMacMask_.octet[0]=0xFF;
			rule.un_ty.dstMacMask_.octet[1]=0xFF;
			rule.un_ty.dstMacMask_.octet[2]=0xFF;
			
			ret= rtl865x_add_acl(&rule, pVlanConfig[i].ifname, RTL865X_ACL_IPV6_USED,1,0);
			
			/*ipv6 multicast data issue*/
			bzero((void*)&rule,sizeof(rtl865x_AclRule_t));
			rule.ruleType_ = RTL865X_ACL_MAC;
			rule.actionType_		= RTL865X_ACL_TOCPU;
			rule.pktOpApp_ 		= RTL865X_ACL_ALL_LAYER;
			rule.un_ty.dstMac_.octet[0]=0x33;
			rule.un_ty.dstMac_.octet[1]=0x33;
			rule.un_ty.dstMac_.octet[2]=0x00;
			rule.un_ty.dstMac_.octet[3]=0x00;
			rule.un_ty.dstMac_.octet[4]=0x00;
			rule.un_ty.dstMac_.octet[5]=0x00;	
			
			rule.un_ty.dstMacMask_.octet[0]=0xFF;
			rule.un_ty.dstMacMask_.octet[1]=0xFF;
			
			ret= rtl865x_add_acl(&rule, pVlanConfig[i].ifname, RTL865X_ACL_IPV6_USED,1,0);
			
		}
		else/*wan config*/
		{	
			rtl865x_regist_aclChain(pVlanConfig[i].ifname, RTL865X_ACL_IPV6_USED,2);
			/*ipv6 multicast data issue*/
			bzero((void*)&rule,sizeof(rtl865x_AclRule_t));
			rule.ruleType_ = RTL865X_ACL_MAC;
			rule.actionType_		= RTL865X_ACL_TOCPU;
			rule.pktOpApp_ 		= RTL865X_ACL_ALL_LAYER;
			rule.un_ty.dstMac_.octet[0]=0x33;
			rule.un_ty.dstMac_.octet[1]=0x33;
			rule.un_ty.dstMac_.octet[2]=0x00;
			rule.un_ty.dstMac_.octet[3]=0x00;
			rule.un_ty.dstMac_.octet[4]=0x00;
			rule.un_ty.dstMac_.octet[5]=0x00;	
			
			rule.un_ty.dstMacMask_.octet[0]=0xFF;
			rule.un_ty.dstMacMask_.octet[1]=0xFF;
			
			ret= rtl865x_add_acl(&rule, pVlanConfig[i].ifname, RTL865X_ACL_IPV6_USED,1,0);

		}		

		//rtl865x_reConfigDefaultAcl(pVlanConfig[i].ifname);

	}
	
	return SUCCESS;
}

//static int rtl865x_removeAclForMldSnooping(struct rtl865x_vlanConfig* vlanConfig)
int rtl865x_removeAclForMldSnooping(struct rtl865x_vlanConfig* vlanConfig)
{
	int i;
	#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)	
	struct rtl865x_vlanConfig tmpVlanConfig[NETIF_SW_NUMBER];
	#endif

	struct rtl865x_vlanConfig *pVlanConfig=NULL;
	
	if(vlanConfig==NULL)
	{
		return FAILED;
	}
#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)	
	re865x_packVlanConfig(vlanConfig, tmpVlanConfig);
	pVlanConfig=tmpVlanConfig;
#else
	pVlanConfig=vlanConfig;
#endif

	for(i=0; pVlanConfig[i].vid != 0; i++)
	{
		if (IF_ETHER!=pVlanConfig[i].if_type)
		{
			continue;
		}
	
		if(pVlanConfig[i].isWan==0)/*lan config*/
		{
			rtl865x_unRegist_aclChain(pVlanConfig[i].ifname, RTL865X_ACL_IPV6_USED);
		}
		else/*wan config*/
		{	
			rtl865x_unRegist_aclChain(pVlanConfig[i].ifname, RTL865X_ACL_IPV6_USED);
		}

	}
	return SUCCESS;
}
#endif

#if !defined(CONFIG_COMPAT_NET_DEV_OPS) && defined(CONFIG_RTL_CUSTOM_PASSTHRU)
static const struct net_device_ops rtl819x_pseudodev_ops = {
	.ndo_open		= re865x_pseudo_open,
	.ndo_stop		= re865x_pseudo_close,

	.ndo_get_stats	= re865x_get_stats,
	.ndo_do_ioctl		= re865x_ioctl,
#ifdef CONFIG_XDSL_NEW_HWNAT_DRIVER
			.ndo_start_xmit 	= re865x_start_xmit_check,
#else
			.ndo_start_xmit 	= re865x_start_xmit,
#endif

};
#endif

#ifdef PORT0_USE_RGMII_TO_EXTCPU_MAC
static void set_8676_RGMII(void){
	rtl865xC_setAsicEthernetMIIMode(0, LINK_RGMII);
	//rtl865xC_setAsicEthernetMIIMode(0, LINK_MII_MAC);
		
	//set Port 0 Interface Type Configuration to GMII/MII/RGMII interface 
	WRITE_MEM32(PITCR, (READ_MEM32(PITCR)&0xfffffffc)|Port0_TypeCfg_GMII_MII_RGMII);

	rtl865xC_setAsicEthernetRGMIITiming(0, RGMII_TCOMP_2NS, RGMII_RCOMP_2NS);
	WRITE_MEM32(MACCR,(READ_MEM32(MACCR)&0xffffcfff)|0x01<<12);//select 100Mhz system clk
		
	//disable auto-polling
	WRITE_MEM32(PCRP0, (READ_MEM32(PCRP0)&~PollLinkStatus));
	WRITE_MEM32(PCRP1, (READ_MEM32(PCRP1)&~PollLinkStatus));
	WRITE_MEM32(PCRP2, (READ_MEM32(PCRP2)&~PollLinkStatus));
	WRITE_MEM32(PCRP3, (READ_MEM32(PCRP3)&~PollLinkStatus));
	WRITE_MEM32(PCRP4, (READ_MEM32(PCRP4)&~PollLinkStatus));
		
	//set port0 force mode
	//WRITE_MEM32(PCRP0, (READ_MEM32(PCRP0)&(~ForceSpeedMask)&(~ExtPHYID_MASK))|ForceSpeed10M|EnForceMode|ForceDuplex);
	//WRITE_MEM32(PCRP0, (READ_MEM32(PCRP0)&(~ForceSpeedMask)&(~ExtPHYID_MASK))|ForceSpeed100M|EnForceMode|ForceDuplex);
	WRITE_MEM32(PCRP0, (READ_MEM32(PCRP0)&(~ForceSpeedMask)&(~ExtPHYID_MASK))|ForceSpeed1000M|EnForceMode|ForceDuplex);
	mdelay(10);
		
	//port0 force link
	WRITE_MEM32(PCRP0, READ_MEM32(PCRP0)|ForceLink);

	printk("Switch port0 to RGMII mode. \n\r");
}
#endif /*PORT0_USE_RGMII_TO_EXTCPU_MAC*/

#ifdef CONFIG_RTL_SERDES
extern int rtl8685P_setSwitchPort(uint8 PortNum);
static void rtl_SerdesTimer(unsigned long data)
{
	uint8 port=0;

	//printk("[%s] Enter >>>>>>>>>>\n",__func__);

	for(port=0; port<4; port++){
		rtl8685P_setSwitchPort(port);
	}

	mod_timer(&CheckSerdes_timer, jiffies+HZ);
}
#endif

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
static int _re865x_create_net_device(unsigned int vid, unsigned int memPort,char* dev_name, char* mac_addr,unsigned int dev_flags)
#else
static int _re865x_create_net_device(unsigned int vid, unsigned int memPort,char* dev_name, char* mac_addr,unsigned short dev_flags)
#endif
{
	struct net_device *dev;
	struct dev_priv	  *dp;
	int i;

	if(_rtl86xx_dev_num == NETDEVICE_MAXNUM)
	{
		printk("failed to allocate dev %s (there are already exists %d ne_device, reach max allocate limit)", dev_name,NETDEVICE_MAXNUM);
		return FAILED;
	}		

	dev = alloc_etherdev(sizeof(struct dev_priv));
	if (!dev) {
		printk("failed to allocate dev %s", dev_name);
		return FAILED;
	}

	SET_MODULE_OWNER(dev);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
	dp = netdev_priv(dev);
#else
	dp = dev->priv;
#endif
	memset(dp,0,sizeof(*dp));
	dp->dev 		= dev;
	dp->id 			= vid;
	dp->portmask 	= memPort;
	dp->portnum  	= 0;
	for(i=0;i<RTL8651_AGGREGATOR_NUMBER;i++){
		if(dp->portmask & (1<<i))
			dp->portnum++;
	}
	memcpy((char*)dev->dev_addr,mac_addr,ETHER_ADDR_LEN);

	spin_lock_init(&dp->lock);

#if defined(CONFIG_COMPAT_NET_DEV_OPS)
	dev->priv_flags = IFF_DOMAIN_ELAN;
	dev->open = re865x_open;
	dev->stop = re865x_close;
	dev->set_multicast_list = re865x_set_rx_mode;
/*romedriver Boyce normal hooking point 2014-07-09*/
#ifdef CONFIG_XDSL_NEW_HWNAT_DRIVER
		dev->hard_start_xmit = re865x_start_xmit_check;
#else
		dev->hard_start_xmit = re865x_start_xmit;
#endif
	dev->get_stats = re865x_get_stats;
	dev->do_ioctl = re865x_ioctl;
	dev->tx_timeout = re865x_tx_timeout;
	dev->set_mac_address = rtl865x_set_hwaddr;
	dev->change_mtu = rtl865x_set_mtu;
	#if defined(CONFIG_RTL8186_LINK_CHANGE)
	dev->change_link = rtl865x_set_link;
	#endif	
	#ifdef CP_VLAN_TAG_USED
	dev->vlan_rx_register = cp_vlan_rx_register;
	dev->vlan_rx_kill_vid = cp_vlan_rx_kill_vid;
	#endif
#else
	dev->netdev_ops = &rtl819x_netdev_ops;
#endif

	dev->watchdog_timeo = TX_TIMEOUT;
	#ifdef CP_VLAN_TAG_USED
	dev->hw_features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;		
	#endif

	#if (defined(CONFIG_RTL_ETH_TX_SG) || defined(CONFIG_RTL_GSO))
	dev->hw_features |= (NETIF_F_GSO | NETIF_F_SG);
	dev->features |= (NETIF_F_GSO | NETIF_F_SG);
	#endif

	#if (defined(CONFIG_RTL_HW_TX_CSUM))
	dev->hw_features |= NETIF_F_HW_CSUM;
	dev->features |= NETIF_F_HW_CSUM;
	#endif

	#if defined(CONFIG_RTL_TSO)
	dev->hw_features |= (NETIF_F_TSO | NETIF_F_TSO_ECN);
	dev->features |= (NETIF_F_TSO | NETIF_F_TSO_ECN);
	#endif
	
	#ifdef CONFIG_RTL8685_FPGA			
	dev->irq = BSP_NIC100_IRQ;
	#else
	dev->irq = BSP_SW_IRQ;
	#endif

	sprintf(dev->name, dev_name);
	dev->priv_flags = dev_flags;
	#ifdef CONFIG_RTL_MULTI_WAN
	if (dev_flags == IFF_DOMAIN_WAN)
		dev->priv_flags |= IFF_RSMUX; // smux real device
	#endif

	#ifdef CONFIG_RTL_MULTI_LAN_DEV
	rtl_set_ethtool_ops(dev);
	#endif	

#ifdef RX_NAPI
	if (0 == napi_exist) {
		netif_napi_add(dev, &rtl_napi, rtl_enet_poll_napi, 256/*NUM_RX_PKTHDR_DESC*/);
		napi_exist = 1;
	}
#endif
	if(!register_netdev(dev))
	{
		for(i=0;i<RTL8651_AGGREGATOR_NUMBER;i++){
			if(dp->portmask & (1<<i))
				break;
		}
		portid_index[i] = _rtl86xx_dev_num;
		_rtl86xx_dev.dev[_rtl86xx_dev_num++]=dev;
		//  init PTM link state to 'off', it should be sync with dsl link state afterward.
		if (dp->portmask == RTL_PTMWANPORT_MASK || dp->portmask == 0)
			netif_carrier_off(dev);
		rtlglue_printf("%s added. vid=%d Member port 0x%x...\n", dev_name,vid ,memPort );
		return SUCCESS;
	}
	else
	{
		rtlglue_printf("Failed to allocate %s\n",dev_name);
		return FAILED;
	}
}

#if defined(CONFIG_ETHWAN_MODE_SWITCH)
static int32 _rtl_update_pvid(void);

int re865x_restart_ether_port (void)
{
  	int32 i;
	int32 totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
	rtl865x_wanPortMask = RTL_WANPORT_MASK;
	rtl865x_lanPortMask = RTL_LANPORT_MASK;
	
	// Restore default member port for LAN device
	for (i=VCONFIG_LAN_START; i<=VCONFIG_LAN_END; i++) { // lan
		vlanconfig[i].memPort = vlanconfig[i].untagSet = itf_default_mbr[i].memPort;
	}
	
	for(i=0;i<totalVlans;i++)
  	{
  		if (TRUE==vlanconfig[i].isWan)
  		{
  			vlanconfig[i].memPort = vlanconfig[i].untagSet = rtl865x_wanPortMask;
			if(vlanconfig[i].vid != RTL_WANVLANID && vlanconfig[i].vid != RTL_BridgeWANVLANID)
				rtl865x_modVlanPortMember(vlanconfig[i].vid, RTL_WANPORT_MASK, 0);
		}
		else
  		{
  			if (vlanconfig[i].memPort & RTL_ETHWANPORT_MASK) // this device/port is wan port now
 				vlanconfig[i].memPort = vlanconfig[i].untagSet = 0;
		}
	}
	_rtl_update_pvid();
	rtl865x_modVlanPortMember(RTL_BridgeWANVLANID, RTL_LANPORT_MASK|RTL_WANPORT_MASK, RTL_LANPORT_MASK|RTL_WANPORT_MASK);
	rtl865x_modVlanPortMember(RTL_WANVLANID, RTL_WANPORT_MASK, RTL_WANPORT_MASK);
	rtl865x_modVlanPortMember(RTL_LANVLANID, RTL_LANPORT_MASK, RTL_LANPORT_MASK);
	return 0;
}
#endif

#if defined(CONFIG_RTK_8023AH)
static int efm_8023ah_write_proc(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{	  
    //unsigned char chartmp; 
    char str[IFNAMSIZ+2];    
    int par_action=0;
    
    if (count > 1){
      	if (buffer && !copy_from_user(str, buffer, sizeof(str))){
		sscanf(str, "%d:%s", &par_action, efm_ifname);
	    	efm_8023ah_par_action = par_action;				
			printk("efm_8023ah_par_action  : %s, efm_ifname=%s\n", efm_8023ah_par_action ? "loopback" : "Forwarding", efm_ifname);
		// statistics
		if (efm_8023ah_par_action)
			memset(&efm_stats, 0, sizeof(struct efm_loopback_stats));
      	}
	else {
		printk("write fail\n");
		return -EFAULT;
	}
    }

    return count;
}

static int efm_8023ah_read_proc(struct seq_file *f, void *data)
{	  
      seq_printf(f, "efm_8023ah_par_action  : %s\n",  efm_8023ah_par_action ? "loopback" : "Forwarding");

      return 0;
}

static int read_proc_open_efm_8023ah(struct inode *inode, struct file *file) {
    return(single_open(file, efm_8023ah_read_proc, NULL));                                                                                                                                                        
}

static ssize_t write_proc_efm_8023ah(struct file *file, const char __user * userbuf, size_t count, loff_t * off) {                                                                                                
    return efm_8023ah_write_proc(file, userbuf, count, NULL);                                                                                                                                                     
}

static struct file_operations fops_proc_efm_8023ah = {
    .open     = read_proc_open_efm_8023ah,   
    .read     = seq_read,
    .llseek   = seq_lseek,
    .release  = single_release,    
    .write    = write_proc_efm_8023ah,                                                                                                                                                                            
};

// for statistics
static int efm_8023ah_write_proc_stats(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{	  
	return 0;
}

static int efm_8023ah_read_proc_stats(struct seq_file *f, void *data)
{	
	seq_puts(f, "     rxPkt txPkt rxByte txByte rxMulti txMulti rxBcst  txBcst checkSeqErr too_long runts\n");
			      
	seq_printf(f, "%7lu %7lu %7lu %7lu %7lu %7lu %7lu %7lu %7lu %7lu %7lu\n",
		efm_stats.rx_frames, efm_stats.tx_frames,
		efm_stats.rx_octets, efm_stats.tx_octets,
		efm_stats.rx_multicast_frames, efm_stats.tx_multicast_frames,
		efm_stats.rx_broadcast_frames, efm_stats.tx_broadcast_frames,
		efm_stats.frame_check_seq_errors,
		efm_stats.frames_too_long,
		efm_stats.runts);
	
	return 0;
}

static int read_proc_stats_open_efm_8023ah(struct inode *inode, struct file *file) {
    return(single_open(file, efm_8023ah_read_proc_stats, NULL));                                                                                                                                                        
}

static ssize_t write_proc_stats_efm_8023ah(struct file *file, const char __user * userbuf, size_t count, loff_t * off) {                                                                                                
    return efm_8023ah_write_proc_stats(file, userbuf, count, NULL);                                                                                                                                                     
}

static struct file_operations fops_proc_stats_efm_8023ah = {
    .open     = read_proc_stats_open_efm_8023ah,   
    .read     = seq_read,
    .llseek   = seq_lseek,
    .release  = single_release,    
    .write    = write_proc_stats_efm_8023ah,                                                                                                                                                                            
};

extern struct proc_dir_entry *realtek_proc;
static struct proc_dir_entry *efm_8023ah_proc=NULL; // Mason Yu
static struct proc_dir_entry *efm_8023ah_proc_stats=NULL;
#endif

extern int RTL_HWSTATS_RSNC[4][4096];
int  __init re865x_probe (void)
{
/*2007-12-19*/
	int32 i;

	int32 totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
#if defined (CONFIG_RTL_IGMP_SNOOPING)
	int32 retVal;
/*
	int32 igmpInitFlag=FAILED;
	struct rtl_mCastSnoopingGlobalConfig mCastSnoopingGlobalConfig;
*/
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	rtl865x_mCastConfig_t mCastConfig;
	#endif
#endif
#if defined(CONFIG_RTK_VLAN_SUPPORT)
	struct proc_dir_entry *res_stats_root;
	struct proc_dir_entry *res_stats;
	struct proc_dir_entry *rtk_vlan_support_entry;
#endif

#if defined (CONFIG_RTL_LOCAL_PUBLIC)
	struct rtl865x_interface_info ifInfo;
#endif

#if defined(PATCH_GPIO_FOR_LED)
	int port;
#endif
	//WRITE_MEM32(PIN_MUX_SEL_2, 0x7<<21);

	rtlglue_printf("\n\n\nProbing RTL8186 10/100 NIC-kenel stack size order[%d]...\n", THREAD_SIZE_ORDER);
    	REG32(CPUIIMR) = 0x00;
    	REG32(CPUICR) &= ~(TXCMD | RXCMD);
		
	printk("  Tx/Rx/Alloc = %d/%d/%d\n", NUM_TX_PKTHDR_DESC, NUM_RX_PKTHDR_DESC, MAX_PRE_ALLOC_RX_SKB);
	//rxMbufRing=NULL;
	
	/*Initial ASIC table*/
 	#ifdef CONFIG_RTL8198_REVISION_B
 	if (REG32(BSP_REVR) >= BSP_RTL8198_REVISION_B)
	{
	  	REG32(SYS_CLK_MAG)&=(~(SYS_SW_RESET));
		mdelay(300);
		REG32(SYS_CLK_MAG)|=(SYS_SW_RESET);
	}
	else
 	#endif
	FullAndSemiReset();
	
	{
		rtl8651_tblAsic_InitPara_t para;

		memset(&para, 0, sizeof(rtl8651_tblAsic_InitPara_t));

		/*
			For DEMO board layout, RTL865x platform define corresponding PHY setting and PHYID.
		*/
		
		rtl865x_wanPortMask = RTL865X_PORTMASK_UNASIGNED;
		
		INIT_CHECK(rtl865x_initAsicL2(&para));
		
#ifdef CONFIG_RTL_LAYERED_ASIC_DRIVER_L3
		INIT_CHECK(rtl865x_initAsicL3());
#endif
#if defined(CONFIG_RTL_LAYERED_ASIC_DRIVER_L4)
		INIT_CHECK(rtl865x_initAsicL4());
#endif

		/*
			Re-define the wan port according the wan port detection result.
			NOTE:
				There are a very strong assumption that if port5 was giga port, 
				then wan port was port 5.
		*/
		if (RTL865X_PORTMASK_UNASIGNED==rtl865x_wanPortMask)
		{
			/* keep the original mask */
			assert(RTL865X_PORTMASK_UNASIGNED==rtl865x_lanPortMask);
			rtl865x_wanPortMask = RTL_WANPORT_MASK;
			rtl865x_lanPortMask = RTL_LANPORT_MASK;
		}
		else
		{
			/* redefine wan port mask */
			assert(RTL865X_PORTMASK_UNASIGNED!=rtl865x_lanPortMask);
			for(i=0;i<totalVlans;i++)
			{
				if (TRUE==vlanconfig[i].isWan)
				{
					vlanconfig[i].memPort = vlanconfig[i].untagSet = rtl865x_wanPortMask;
				}
				else
				{
					vlanconfig[i].memPort = vlanconfig[i].untagSet = rtl865x_lanPortMask;
				}
			}
		}
#if 1		/*	10/100 & giga use the same pre-allocated skb number */
		/*
			Re-define the pre-allocated skb number according the wan 
			port detection result.
			NOTE:
				There are a very strong assumption that if port1~port4 were
				all giga port, then the sdram was 32M.
		*/
		{
			if (RTL865X_PREALLOC_SKB_UNASIGNED==rtl865x_maxPreAllocRxSkb)
			{
				assert(rtl865x_rxSkbPktHdrDescNum==
					rtl865x_txSkbPktHdrDescNum==
					RTL865X_PREALLOC_SKB_UNASIGNED);

				rtl865x_maxPreAllocRxSkb = MAX_PRE_ALLOC_RX_SKB;
				rtl865x_rxSkbPktHdrDescNum = NUM_RX_PKTHDR_DESC;
				rtl865x_txSkbPktHdrDescNum = NUM_TX_PKTHDR_DESC;
			}
			else
			{
				assert(rtl865x_rxSkbPktHdrDescNum!=RTL865X_PREALLOC_SKB_UNASIGNED);
				assert(rtl865x_txSkbPktHdrDescNum!=RTL865X_PREALLOC_SKB_UNASIGNED);
				/* Assigned value in function of rtl8651_initAsic() */
				rxRingSize[0] = rtl865x_rxSkbPktHdrDescNum;
				txRingSize[0] = rtl865x_txSkbPktHdrDescNum;
			}

			for(i=1;i<RTL865X_SWNIC_RXRING_HW_PKTDESC;i++)
			{
				rtl865x_maxPreAllocRxSkb += rxRingSize[i];
			}
		}
#else
		{
			rtl865x_maxPreAllocRxSkb = MAX_PRE_ALLOC_RX_SKB;
			rtl865x_rxSkbPktHdrDescNum = NUM_RX_PKTHDR_DESC;
			rtl865x_txSkbPktHdrDescNum = NUM_TX_PKTHDR_DESC;
		}
#endif
	}

#ifdef BR_SHORTCUT
	cached_dev=NULL;
#endif
	/*init PHY LED style*/
#if defined(CONFIG_RTL865X_BICOLOR_LED)
	#ifdef BICOLOR_LED_VENDOR_BXXX
	REG32(LEDCR) |= (1 << 19); // 5 ledmode set to 1 for bi-color LED
	REG32(PABCNR) &= ~0x001f0000; /* set port port b-4/3/2/1/0 to gpio */
	REG32(PABDIR) |=  0x001f0000; /* set port port b-4/3/2/1/0 gpio direction-output */
	#else
	//8650B demo board default: Bi-color 5 LED
	WRITE_MEM32(LEDCR, READ_MEM32(LEDCR) | 0x01180000 ); // bi-color LED
	#endif
	/* config LED mode */
	WRITE_MEM32(SWTAA, PORT5_PHY_CONTROL);
	WRITE_MEM32(TCR0, 0x000002C2); //8651 demo board default: 15 LED boards
	WRITE_MEM32(SWTACR, CMD_FORCE | ACTION_START); // force add
#else /* CONFIG_RTL865X_BICOLOR_LED */

	/* config LED mode */
#ifndef PORT0_USE_RGMII_TO_EXTCPU_MAC
	WRITE_MEM32(LEDCR, 0x200000 ); // 15 LED
#endif /*PORT0_USE_RGMII_TO_EXTCPU_MAC*/
	WRITE_MEM32(SWTAA, PORT5_PHY_CONTROL);
	WRITE_MEM32(TCR0, 0x000002C7); //8651 demo board default: 15 LED boards
	WRITE_MEM32(SWTACR, CMD_FORCE | ACTION_START); // force add
#endif /* CONFIG_RTL865X_BICOLOR_LED */

#ifdef CONFIG_HIGH_ACTIVE_SWITCH_LED
	/* Set LEDs to be high-active */
	REG32(DIRECTLCR) |= (1 << 31);
	REG32(BSP_PINMUX1) |= ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4));
#endif

	INIT_CHECK(rtl865x_init());
	
	re865x_packVlanConfig(vlanconfig, packedVlanConfig);	
	INIT_CHECK(rtl865x_config(packedVlanConfig));

#if defined(CONFIG_RTL_SWITCH_NEW_DESCRIPTOR)
    REG32(CPUICR1) = (REG32(CPUICR1) & ~CF_PKT_HDR_TYPE_MASK) | TX_PKTHDR_SHORTCUT_LSO;
#if defined(CONFIG_RTL_JUMBO_FRAME)
    REG32(CPUICR1) |= BIT(CF_RX_GATHER_OFFSET);
#endif
	printk("%s(%d): %08x=%08x\n",__func__,__LINE__,CPUICR1, REG32(CPUICR1));
#if defined(CONFIG_RTL_TSO) || defined(CONFIG_RTL_GSO)
	REG32(CPUICR1) |= BIT(CF_TX_GATHER_OFFSET);
#endif
#endif
	/* create all default VLANs */
//	rtlglue_printf("	creating eth0~eth%d...\n",totalVlans-1 );
#if 0
#if defined (CONFIG_RTL_IGMP_SNOOPING)
	memset(&mCastSnoopingGlobalConfig, 0, sizeof(struct rtl_mCastSnoopingGlobalConfig));
	mCastSnoopingGlobalConfig.maxGroupNum=256;
	mCastSnoopingGlobalConfig.maxSourceNum=300;
	mCastSnoopingGlobalConfig.hashTableSize=64;
	
	mCastSnoopingGlobalConfig.groupMemberAgingTime=260;
	mCastSnoopingGlobalConfig.lastMemberAgingTime=2;
	mCastSnoopingGlobalConfig.querierPresentInterval=260;  
	
	mCastSnoopingGlobalConfig.dvmrpRouterAgingTime=120;
	mCastSnoopingGlobalConfig.mospfRouterAgingTime=120;
	mCastSnoopingGlobalConfig.pimRouterAgingTime=120;
	
	igmpInitFlag=rtl_initMulticastSnooping(mCastSnoopingGlobalConfig);
#endif	
#endif	

	/**************************/
	/*  Create LAN net_device */
	/**************************/
	{
		char lan_devname[IFNAMSIZ];
		#ifdef CONFIG_RTL_MULTI_LAN_DEV
		for(i=VCONFIG_LAN_START; i<=VCONFIG_LAN_END ;i++)
		{
			sprintf(lan_devname, "%s%d",ALIASNAME_ELAN_PREFIX,i+ORIGINATE_NUM);
			_re865x_create_net_device(RTL_LANVLANID,vlanconfig[i].memPort,lan_devname,(char*)(&(vlanconfig[i].mac)),IFF_DOMAIN_ELAN);
			#ifdef CONFIG_ETHWAN_MODE_SWITCH
			// init the default device member port (static table)
			itf_default_mbr[i].memPort = vlanconfig[i].memPort;
			#endif
		}
		#else
		for(i=0;i<totalVlans;i++)
		{
		if (vlanconfig[i].isWan)
			continue;
		sprintf(lan_devname, "%s",ALIASNAME_ETH0);
		_re865x_create_net_device(RTL_LANVLANID,vlanconfig[i].memPort,lan_devname,(char*)(&(vlanconfig[i].mac)),IFF_DOMAIN_ELAN);	
		}
		#endif
	}
	/**************************/
	/*  Create WAN net_device */
	/**************************/
	{
		#if defined(CONFIG_ETHWAN) || defined(CONFIG_PTMWAN) || defined(CONFIG_REMOTE_ADSL_PHY)
		char wan_devname[IFNAMSIZ];
		ether_addr_t wan_mac = { { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } };
		#endif

		#ifdef CONFIG_ETHWAN
		sprintf(wan_devname, ALIASNAME_NAS0);
		_re865x_create_net_device(RTL_WANVLANID,RTL_ETHWANPORT_MASK,wan_devname,(char*)(&wan_mac),IFF_DOMAIN_WAN);
		#endif

		#if defined(CONFIG_PTMWAN) || defined(CONFIG_REMOTE_ADSL_PHY)
		sprintf(wan_devname, ALIASNAME_PTM0);
		_re865x_create_net_device(RTL_WANVLANID,RTL_PTMWANPORT_MASK,wan_devname,(char*)(&wan_mac),IFF_DOMAIN_WAN);
		#endif
	}
	
	#ifdef CONFIG_ETHWAN_MODE_SWITCH
	do_etherwan_mode();
	#endif
	
#if defined (CONFIG_RTL_IGMP_SNOOPING)
	retVal=rtl_registerIgmpSnoopingModule(&nicIgmpModuleIndex);
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	if(retVal==SUCCESS)
	{
		rtl_multicastDeviceInfo_t devInfo;
		memset(&devInfo, 0 , sizeof(rtl_multicastDeviceInfo_t));
		strcpy(devInfo.devName, "eth*");
		for(i=0;i<totalVlans;i++)
		{
			if( vlanconfig[i].if_type==IF_ETHER)
			{
				devInfo.portMask|=vlanconfig[i].memPort;
			}
		}
		devInfo.swPortMask=devInfo.portMask & (~ ((1<<RTL8651_MAC_NUMBER)-1));
		rtl_setIgmpSnoopingModuleDevInfo(nicIgmpModuleIndex, &devInfo);
	}
	#endif
	curLinkPortMask=rtl865x_getPhysicalPortLinkStatus();
	#if defined(CONFIG_RTL_MULTI_LAN_DEV) && defined(CONFIG_RTL_8676HWNAT)
	if(RTL8676_TBLASIC_EXTPHYPROPERTY_PORT0_RTL8367B != global_probe_extPhy)
	{
		int port;
		struct net_device *dev;
		for(port = 0; port < RTL8651_PORT_NUMBER; ++port)
		{
			if((dev = ethtool_get_netdev_by_rtl_sw_port(&_rtl86xx_dev, port)) != NULL)
			{
				// Do NOT update carrier state for PTM(port 5)
				//  here as port-5 always on; Instead, PTM link
				//  state should be sync with dsl link state.
				#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
				if (((struct dev_priv *)netdev_priv(dev))->portmask == RTL_PTMWANPORT_MASK)
				#else
				if (((struct dev_priv *)dev->priv)->portmask == RTL_PTMWANPORT_MASK)
				#endif
					continue;
				if((1 << port) & curLinkPortMask)
					netif_carrier_on(dev);
				else
					netif_carrier_off(dev);
			}
		}
	}
	#endif

	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	memset(&mCastConfig, 0, sizeof(rtl865x_mCastConfig_t));
	for(i=0;i<totalVlans;i++)
	{
		if (TRUE==vlanconfig[i].isWan)
		{
			mCastConfig.externalPortMask |=vlanconfig[i].memPort;
		}
	}
	rtl865x_initMulticast(&mCastConfig);
	#else
	// Trap multicast to CPU for IGMP snooping function (to avoid IGMP forwarding between LAN ports)
	WRITE_MEM32(FFCR, READ_MEM32(FFCR)|IPMltCstCtrl_TrapToCpu);
	#endif
#endif // CONFIG_RTL_IGMP_SNOOPING

#ifdef CONFIG_RTL_STP
	#ifdef CONFIG_RTK_MESH
	printk("Configuration LINUX to process port 0 ~ port %d for Spanning tree process\n", MAX_RE865X_ETH_STP_PORT-1);
	#else
    	printk("Configuration LINUX to process port 0 ~ port %d for Spanning tree process\n", MAX_RE865X_STP_PORT-ORIGINATE_NUM);
//	printk("Configuration LINUX to process port 0 ~ port %d for Spanning tree process\n", MAX_RE865X_STP_PORT-2);
	#endif
	rtl865x_setSpanningEnable(TRUE);
	#ifdef CONFIG_RTK_MESH
	for ( i = 0 ; i < MAX_RE865X_ETH_STP_PORT; i ++ )
	#else
	for ( i = 0 ; i < MAX_RE865X_STP_PORT-1 ; i ++ )
	#endif
	{
		struct net_device *dev;
		struct dev_priv *dp;
		int rc;
		struct re865x_priv *rp;
		
		rp = &_rtl86xx_dev;
		dev = alloc_etherdev(sizeof(struct dev_priv));
		if (!dev){
			rtlglue_printf("failed to allocate dev %d", i);
			return -1;
		}
		strcpy(dev->name, "port%d");	
		memcpy((char*)dev->dev_addr,(char*)(&(vlanconfig[0].mac)),ETHER_ADDR_LEN);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
		dp = netdev_priv(dev);
#else
		dp = dev->priv;
#endif
		memset(dp,0,sizeof(*dp));
		dp->dev = dev;
		dev->open = re865x_pseudo_open;
		dev->stop = re865x_pseudo_close;
		dev->set_multicast_list = NULL;
/*romedriver Boyce 2014-07-09*/
#ifdef CONFIG_XDSL_NEW_HWNAT_DRIVER
		dev->hard_start_xmit = re865x_start_xmit_check;
#else
		dev->hard_start_xmit = re865x_start_xmit;
#endif
		dev->get_stats = re865x_get_stats;
		dev->do_ioctl = re865x_ioctl;
		dev->tx_timeout = NULL;
		dev->watchdog_timeo = TX_TIMEOUT;
		dev->irq = 0;				/* virtual interfaces has no IRQ allocated */
		rc = register_netdev(dev);
		if (rc == 0)
		{
			_rtl86xx_dev.stp_port[i] = dev;
			printk("=> [stp pseudo port%d] done\n", i);
		} else
		{
			printk("=> Failed to register [stp pseudo port%d]", i);
			return -1;
		}
	}

	re865x_stp_mapping_init();

#endif
#if defined(CONFIG_RTL_HW_STP)
	//Initial: disable Realtek Hardware STP
	rtl865x_setSpanningEnable(FALSE);
#endif

	//((struct dev_priv*)((_rtl86xx_dev.dev[0])->priv))->dev_next = _rtl86xx_dev.dev[1];
	//((struct dev_priv*)((_rtl86xx_dev.dev[1])->priv))->dev_prev = _rtl86xx_dev.dev[0];

#if defined(CONFIG_RTL_ETH_PRIV_SKB) || defined(CONFIG_RTL_ETH_PRIV_SKB_ADV)
	init_priv_eth_skb_buf();
#endif

#ifdef RX_NAPI
	init_timer(&txbuff_gc_timer);
	txbuff_gc_timer.function = &nicTx_Done_Handler;
	txbuff_gc_timer.expires = jiffies + 30*HZ;
#endif
	init_timer(&rtl_hwstats_timer);
	rtl_hwstats_timer.function = &rtl_hwstats_Handler;
	rtl_hwstats_timer.expires = jiffies + 3*HZ;

#if (defined(CONFIG_RTL_CUSTOM_PASSTHRU) && !defined(CONFIG_RTL8196_RTL8366)) 
	//cary
	rtl8651_customPassthru_init();
	rtl8651_initStormCtrl();
#endif

#if (defined(CONFIG_RTL_8198))
	// initial proc for phyRegTest
	phyRegTest_init();
#endif

#ifdef CONFIG_RTL_LAYERED_ASIC_DRIVER_L3
#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	rtl8651_setAsicMulticastEnable(TRUE);
#else
	rtl8651_setAsicMulticastEnable(FALSE);
#endif
#endif
#if defined(CONFIG_RTK_VLAN_SUPPORT)

	rtk_vlan_support_enable= 0;
	rtk_vlan_support_entry=create_proc_entry("rtk_vlan_support",0,NULL);
	if (rtk_vlan_support_entry) 
	{
	    rtk_vlan_support_entry->read_proc=rtk_vlan_support_read;
	    rtk_vlan_support_entry->write_proc=rtk_vlan_support_write;
	}
#endif

	// Mason Yu.
#if defined(CONFIG_RTK_8023AH)
	efm_8023ah_proc = proc_create_data("efm_8023ah_par_action", 0644, realtek_proc, &fops_proc_efm_8023ah, NULL);	
	if (!efm_8023ah_proc) {
		printk("Realtek SMUX efm_8023ah_proc, create proc failed!\n");
	}
	
	efm_8023ah_proc_stats = proc_create_data("efm_8023ah_stats", 0644, realtek_proc, &fops_proc_stats_efm_8023ah, NULL);	
	if (!efm_8023ah_proc_stats) {
		printk("Realtek SMUX efm_8023ah_proc_stats, create proc failed!\n");
	}
	memset(&efm_stats, 0, sizeof(struct efm_loopback_stats));
#endif
	
#if defined (CONFIG_RTL_LOCAL_PUBLIC)
	rtl865x_initLocalPublic(NULL);

	memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
#if defined(CONFIG_RTL_PUBLIC_SSID)
	strcpy(ifInfo.ifname,RTL_GW_WAN_DEVICE_NAME);
#else
	strcpy(ifInfo.ifname, RTL_DRV_WAN0_NETIF_NAME);
#endif
	ifInfo.isWan=1;
	for(i=0;i<totalVlans;i++)
	{
		if ((TRUE==vlanconfig[i].isWan) && (vlanconfig[i].if_type==IF_ETHER))
		{
			ifInfo.memPort |= vlanconfig[i].memPort;
			ifInfo.fid=vlanconfig[i].fid;
		}
	}
	rtl865x_setLpIfInfo(&ifInfo);
	
	memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
	strcpy(ifInfo.ifname,RTL_DRV_LAN_NETIF_NAME);
	ifInfo.isWan=0;
	for(i=0;i<totalVlans;i++)
	{
		if ((FALSE==vlanconfig[i].isWan) && (vlanconfig[i].if_type==IF_ETHER))
		{
			ifInfo.memPort|=vlanconfig[i].memPort;
			ifInfo.fid=vlanconfig[i].fid;
		}
	}
	rtl865x_setLpIfInfo(&ifInfo);
#endif
	//rtl_rxTxDoneCnt=0;
	atomic_set(&rtl_devOpened, 0);

#if defined(PATCH_GPIO_FOR_LED)
	for (port=0; port<RTL8651_PHY_NUMBER; port++)
		init_led_ctrl(port);
#endif

#if defined(CONFIG_RTL_LINKSTATE)
	initPortStateCtrl();
#endif

#if 0// defined (CONFIG_RTL_MLD_SNOOPING)
	rtl8651_initMldSnooping();
#endif
#if defined (CONFIG_RTL_PHY_POWER_CTRL)
	rtl865x_initPhyPowerCtrl();
#endif

#ifdef CONFIG_RTL_HW_QOS_SUPPORT
	// init queue mapping (a default queue and a implictly internal high queue)
	#if defined(CONFIG_RTL_8685_8PRIQUE)
	rtl865x_enableQos(8, 0, NULL, NULL, 4);
	#else
	rtl865x_enableQos(4, 0, NULL, NULL, 3);
	#endif
#endif
#if defined(RTL_CPU_QOS_ENABLED)
	highestPriority = 0;
	cpuQosHoldLow = 0;
	totalLowQueueCnt = 0;
	memset(pktQueueByPri, 0, sizeof(rtl_queue_entry)*(RTL865X_SWNIC_RXRING_MAX_PKTDESC));

	init_timer(&cpuQosTimer);
	cpuQosTimer.function = rtl_cpuQosTimer;
#endif

#ifdef CONFIG_RTL_NLMSG_PROTOCOL
	rtl_gbl_rtlmsg_sock_init();
#endif

#ifdef PORT0_USE_RGMII_TO_EXTCPU_MAC
	set_8676_RGMII();
	
	REG32(BSP_MISC_IO_DRIVING) |= BSP_MISC_IO_GPIOA_DrvSel;
	REG32(BSP_RGMII_PAD_CTRL) |= (BSP_RGMII_C_CELLBIAS_DP|BSP_RGMII_C_CELLBIAS_DN | \
			BSP_RGMII_D_CELLBIAS_DP | BSP_RGMII_D_CELLBIAS_DN);
#endif /*PORT0_USE_RGMII_TO_EXTCPU_MAC*/
		
#ifdef CONFIG_RTL_SERDES
	init_timer(&CheckSerdes_timer);
	CheckSerdes_timer.function = rtl_SerdesTimer;
	CheckSerdes_timer.expires = jiffies + HZ;
	add_timer(&CheckSerdes_timer);
#endif

	/* Disable CPU port pause ability 
	 	==> Side effect : WiFi throughput decreased due to the CPU port dropping packet*/
	/* WRITE_MEM32(0xbb804600,READ_MEM32(0xbb804600) & (~(0x3<<5))); */

	nic_event_task = kthread_run(nic_thread, NULL, "kNicEvt");	

	WRITE_MEM32(MACCR1,READ_MEM32(MACCR1) | (0x1<<3));

	for(i=0;i<4096;i++)
	{
		RTL_HWSTATS_RSNC[0][i]=0;
		RTL_HWSTATS_RSNC[1][i]=0;
		RTL_HWSTATS_RSNC[2][i]=0;
		RTL_HWSTATS_RSNC[3][i]=0;
	}
	for(i=0;i<RTL8651_PORT_NUMBER;i++)
	{
		RTL_HWSTATS_TXPKTS_OVERFLOW[i] = 0;
		RTL_HWSTATS_RXPKTS_OVERFLOW[i] = 0;
		RTL_HWSTATS_TXPKTS[i] = 0;
		RTL_HWSTATS_RXPKTS[i] = 0;
	}
#if defined(CONFIG_RTL_HW_NAPT_4KENTRY)
	_setInbound4WayHash(1);
#endif
	if(vlan_passthru_enable)
		rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL,RTL865X_ACLTBL_PERMIT_ALL);
	else
		rtl865x_setDefACLForNetDecisionMiss(RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU,RTL865X_ACLTBL_ALL_TO_CPU);

	return 0;
}

#ifdef CONFIG_RTL_ETH_PRIV_SKB_ADV
__IRAM_GEN void rtl865x_free_eth_priv_buf(struct sk_buff *skb, unsigned free_flag)
{
	struct priv_skb_buf3 *list;
	unsigned long flags;

	//printk("%s prealloc_flags 0x%x free_flag:0x%x\n", __func__, skb->prealloc_flags, free_flag);
	if (free_flag & RETFREEQ_DATA) {
#if defined(DELAY_REFILL_ETH_RX_BUF) && defined(CONFIG_FAST_FORWARDING)
		if (!(skb->prealloc_flags&SKB_RET2RXRING) || (FAILED==return_to_nic_rx_ring(skb->head)))
		{
#endif//end of DELAY_REFILL_ETH_RX_BUF || CONFIG_FAST_FORWARDING
			local_irq_save(flags);
			list = (struct priv_skb_buf3 *)((unsigned int)skb->head - offsetof(struct priv_skb_buf3, buf));
			list->next = freeBufList;
			freeBufList = list;
			local_irq_restore(flags);
#if defined(DELAY_REFILL_ETH_RX_BUF) && defined(CONFIG_FAST_FORWARDING)
		}
#endif//end of DELAY_REFILL_ETH_RX_BUF || CONFIG_FAST_FORWARDING
	}
	
	if (free_flag & RETFREEQ_SKB){
		#if defined(DELAY_REFILL_ETH_RX_BUF) && !defined(CONFIG_FAST_FORWARDING)
		if (!(skb->prealloc_flags&SKB_RET2RXRING) || (FAILED==return_to_nic_rx_ring(skb)))
		#endif//end of DELAY_REFILL_ETH_RX_BUF && !CONFIG_FAST_FORWARDING
		{
			local_irq_save(flags);
			skb->prealloc_next = freeSkbList;
			freeSkbList = skb;
			local_irq_restore(flags);
		}
	}
}

static void init_priv_eth_skb_buf(void)
{
	unsigned char *skb;
	unsigned char *data;
	int i;

	/* XXXX: SMP_CACHE_BYTES must larger than 16*/
	for(i=0, data = 
        (unsigned char *)(SKB_DATA_ALIGN((unsigned long)eth_skb_buf)); i<MAX_ETH_SKB_NUM;
		i++, data += ETH_SKB_BUF_SIZE)
	{
		((struct priv_skb_buf3 *)data)->next = freeBufList;
		freeBufList = (struct priv_skb_buf3 *)data;
	}
	
	for(i = 0, skb = (unsigned char *)
        (((unsigned long)eth_skb_hdr+0xF) & ~0xF); i < MAX_ETH_SKB_NUM;
        i++, skb += SKB_ALIGNED_SIZE)
    {
        ((struct sk_buff *) skb)->prealloc_next = freeSkbList;
        freeSkbList = (struct sk_buff *) skb;
    }
	eth_skb_free_num = i;
}
#endif//end of CONFIG_RTL_ETH_PRIV_SKB_ADV

#if defined(CONFIG_RTL_ETH_PRIV_SKB)

//---------------------------------------------------------------------------
static void init_priv_eth_skb_buf(void)
{
	int i;

	DEBUG_ERR("Init priv skb.\n");
	memset(eth_skb_buf, '\0', sizeof(struct priv_skb_buf2)*(MAX_ETH_SKB_NUM));
	INIT_LIST_HEAD(&eth_skbbuf_list);
	eth_skb_free_num=MAX_ETH_SKB_NUM;

	for (i=0; i<MAX_ETH_SKB_NUM; i++)  {
		eth_skb_buf[i].magic = ETH_MAGIC_CODE;
		eth_skb_buf[i].buf_pointer = (void*)(&eth_skb_buf[i]);
		INIT_LIST_HEAD(&eth_skb_buf[i].list);
		list_add_tail(&eth_skb_buf[i].list, &eth_skbbuf_list);	
	}
}

extern unsigned long swcore_buf_lock(int num);
extern void swcore_buf_unlock(int num, unsigned long flags);

static __inline__ unsigned char *get_buf_from_poll(struct list_head *phead, unsigned int *count)
{
	unsigned long flags;
	unsigned char *buf;
	struct list_head *plist;

	flags = swcore_buf_lock(0); //local_irq_save(flags);
	if (list_empty(phead)) {
		swcore_buf_unlock(0, flags); //local_irq_restore(flags);
		DEBUG_ERR("eth_drv: phead=%X buf is empty now!\n", (unsigned int)phead);
		DEBUG_ERR("free count %d\n", *count);
		return NULL;
	}

	if (*count == 1) {
		swcore_buf_unlock(0, flags); //local_irq_restore(flags);
		DEBUG_ERR("eth_drv: phead=%X under-run!\n", (unsigned int)phead);
		return NULL;
	}

	*count = *count - 1;
	plist = phead->next;
	list_del_init(plist);
	buf = (unsigned char *)((unsigned int)plist + sizeof (struct list_head));
	swcore_buf_unlock(0, flags); //local_irq_restore(flags);
	return buf;
}

static __inline__ void release_buf_to_poll(unsigned char *pbuf, struct list_head	*phead, unsigned int *count)
{
	unsigned long flags;
	struct list_head *plist;

	flags = swcore_buf_lock(0); //local_irq_save(flags);

	*count = *count + 1;
	plist = (struct list_head *)((unsigned int)pbuf - sizeof(struct list_head));
	list_add_tail(plist, phead);
	swcore_buf_unlock(0, flags); //local_irq_restore(flags);
}

__IRAM_GEN void free_rtl865x_eth_priv_buf(unsigned char *head)
{
	#ifdef DELAY_REFILL_ETH_RX_BUF
	if (FAILED==return_to_rx_pkthdr_ring(head)) 
	#endif
	{release_buf_to_poll(head, &eth_skbbuf_list, (unsigned int *)&eth_skb_free_num);}
}

static struct sk_buff *dev_alloc_skb_priv_eth(unsigned int size)
{
	struct sk_buff *skb;
	unsigned char *data; 

	/* first argument is not used */
	data = get_buf_from_poll(&eth_skbbuf_list, (unsigned int *)&eth_skb_free_num);
	if (data == NULL) {
		DEBUG_ERR("eth_drv: priv skb buffer empty!\n");
		return NULL;
	}
	skb = dev_alloc_8190_skb(data, size);
	if (skb == NULL) {
		//free_rtl865x_eth_priv_buf(data);
		release_buf_to_poll(data, &eth_skbbuf_list, (unsigned int *)&eth_skb_free_num);	
		DEBUG_ERR("alloc linux skb buff failed!\n");
		return NULL;
	}

	return skb;
}

int is_rtl865x_eth_priv_buf(unsigned char *head)
{
	unsigned long offset = (unsigned long)(&((struct priv_skb_buf2 *)0)->buf);
	struct priv_skb_buf2 *priv_buf = (struct priv_skb_buf2 *)(((unsigned long)head) - offset);

	if ((priv_buf->magic==ETH_MAGIC_CODE) &&
		(priv_buf->buf_pointer==(void*)(priv_buf))) {
		return 1;	
	}
	else {
		return 0;
	}
}

#if defined(CONFIG_RTL_ETH_PRIV_SKB) && (defined(CONFIG_NET_WIRELESS_AGN) || defined(CONFIG_NET_WIRELESS_AG) || defined(CONFIG_WIRELESS))
struct sk_buff *priv_skb_copy(struct sk_buff *skb)
{
	struct sk_buff *n;
	unsigned long flags;

	if (rx_skb_queue.qlen == 0) {
		n = dev_alloc_skb_priv_eth(CROSS_LAN_MBUF_LEN);
	}
	else {
#ifdef RTK_QUE
		flags = swcore_buf_lock(1); //local_irq_save(flags);
		n = rtk_dequeue(&rx_skb_queue);
		swcore_buf_unlock(1, flags); //local_irq_restore(flags);
#else
		n = __skb_dequeue(&rx_skb_queue);
#endif
	}
	
	if (n == NULL)
	return NULL;

	/* Set the tail pointer and length */
	skb_put(n, skb->len);
	n->csum = skb->csum;
	n->ip_summed = skb->ip_summed;
	memcpy(n->data, skb->data, skb->len);

	copy_skb_header(n, skb);
	return n;
}
EXPORT_SYMBOL(priv_skb_copy);
#endif // defined(CONFIG_NET_WIRELESS_AGN) || defined(CONFIG_NET_WIRELESS_AG)
#endif // CONFIG_RTL_ETH_PRIV_SKB

static void __exit re865x_exit (void)
{

#ifdef RTL865X_DRIVER_DEBUG_FLAG
	rtl865x_proc_debug_cleanup();
#endif

#if defined(CONFIG_PROC_FS) && defined(CONFIG_NET_SCHED) && defined(CONFIG_RTL_LAYERED_DRIVER)
#if defined(CONFIG_RTL_HW_QOS_SUPPORT)
	rtl865x_exitOutputQueue();
#endif
#endif
	
#if defined (CONFIG_RTL_IGMP_SNOOPING)
	rtl_exitMulticastSnooping();
#endif

#if defined(CONFIG_RTL_LINKSTATE)
	exitPortStateCtrl();
#endif
	return;
}

module_init(re865x_probe);
module_exit(re865x_exit);

/*
@func enum RTL_RESULT | rtl865x_init | Initialize light rome driver and RTL865x ASIC.
@rvalue RTL_SUCCESS | Initial success. 
@comm
	Its important to call this API before using the driver. Note taht you can not call this API twice !
*/
int32 rtl865x_init(void)
{
	int32 retval = 0;

	__865X_Config = 0;	

#ifdef CONFIG_RTL8196_RTL8366
	/*	configure 8366 */
	{
		int ret;
		int i;
		rtl8366rb_phyAbility_t phy;

		REG32(PEFGHCNR_REG) = REG32(PEFGHCNR_REG)& (~(1<<11) ); //set byte F GPIO3 = gpio
		REG32(PEFGHDIR_REG) = REG32(PEFGHDIR_REG) | (1<<11);  //0 input, 1 output, set F bit 3 output
		REG32(PEFGHDAT_REG) = REG32(PEFGHDAT_REG) |( (1<<11) ); //F3 GPIO
		mdelay(150);

		ret = smi_init(GPIO_PORT_F, 2, 1);
		ret = rtl8366rb_initChip();
		ret = rtl8366rb_initVlan();
		ret = smi_write(0x0f09, 0x0020);
		ret = smi_write(0x0012, 0xe0ff);

		memset(&phy, 0, sizeof(rtl8366rb_phyAbility_t));
		phy.Full_1000 = 1;
		phy.Full_100 = 1;
		phy.Full_10 = 1;
		phy.Half_100 = 1;
		phy.Half_10 = 1;
		phy.FC = 1;
		phy.AsyFC = 1;
		phy.AutoNegotiation = 1;
		for(i=0;i<5;i++)
		{
			ret = rtl8366rb_setEthernetPHY(i,&phy);
		}
	}

 	 REG32(0xb8010000)=REG32(0xb8010000)&(0x20000000);
        REG32(0xbb80414c)=0x00037d16;
        REG32(0xbb804100)=1;
        REG32(0xbb804104)=0x00E80367;
#endif

/*common*/
	retval = rtl865x_initNetifTable();
	retval = rtl865x_initVlanTable();
#ifdef CONFIG_RTL_LAYERED_DRIVER_ACL
	retval = rtl865x_init_acl();
#endif
	/*l2*/
 #ifdef CONFIG_RTL_LAYERED_DRIVER_L2
	retval = rtl865x_initEventMgr(NULL);	
	retval = rtl865x_layer2_init();
 #endif
 
/*layer3*/
#ifdef CONFIG_RTL_LAYERED_DRIVER_L3
	retval = rtl865x_initIpTable();
	retval = rtl865x_initPppTable();
	retval = rtl865x_initRouteTable();
	retval = rtl865x_initNxtHopTable();
	retval = rtl865x_arp_init();
#if defined(CONFIG_RTL_HWNAT_IPv6_SUPPORT) && !defined(CONFIG_RTL_NEW_FLOW_BASE_HWNAT_DRIVER)
	retval = rtl8198C_initv6RouteTable();
	retval = rtl8198C_initv6NxtHopTable();
	retval = rtl8198C_v6Neigh_init();
	#if defined(CONFIG_RTL_HWNAT_DUAL_STACK_LITE)
	retval = rtl_xdsl_initDSLiteTable();
	#endif
#endif
#endif

/*layer4*/
#if defined(CONFIG_RTL_LAYERED_DRIVER_L4)
	rtl865x_nat_init();
#endif

 	/*queue id & rx ring descriptor mapping*/
 	/*queue id & rx ring descriptor mapping*/
 	REG32(CPUQDM0)=QUEUEID1_RXRING_MAPPING|(QUEUEID0_RXRING_MAPPING<<16);
	REG32(CPUQDM2)=QUEUEID3_RXRING_MAPPING|(QUEUEID2_RXRING_MAPPING<<16);
	REG32(CPUQDM4)=QUEUEID5_RXRING_MAPPING|(QUEUEID4_RXRING_MAPPING<<16);
	#if defined(CONFIG_RTL_8685_8PRIQUE)
	REG32(CPUQDM6)=QUEUEID7_RXRING_MAPPING|(QUEUEID6_RXRING_MAPPING<<16);
	#endif

	rtl8651_setAsicOutputQueueNumber(CPU, 6);

#if defined(CONFIG_RTL_8685S_HWNAT)
	REG32(PQMDCR) = (cf_qmax_dsc_en_mask<<cf_qmax_dsc_en_offset) | (cf_qmax_dsc_clr_mask<<cf_qmax_dsc_clr_offset);
#endif

#ifdef RTL865X_DRIVER_DEBUG_FLAG
	rtl865x_proc_debug_init();
#endif

#if defined(PATCH_GPIO_FOR_LED)
	rtl8651_resetAllAsicMIBCounter();
#endif

	rtl_ps_drv_netif_mapping_init();

	return SUCCESS;
}

/*
@func enum RTL_RESULT | rtl865x_config | Configure light rome driver. Create VLAN and Network interface.
@parm struct rtl865x_vlanConfig * | vlanconfig | 
@rvlaue RTL_SUCCESS | Sucessful configuration.
@rvalue RTL_INVVID | Invalid VID.
@comm
	struct rtl865x_vlanConfig is defined as follows:
	
			ifname:		Layer 3 Network Interface name, eg: eth0, eth1, ppp0...etc,. If it is specified, both layer 2 vlan and layer 3
						netwrok interface are created and bound together. It also can be a NULL value. In this case, only a layer 2 VLAN 
						is created.
			isWan:		1 for WAN interface and 0 for LAN interface in a layer 4 mode.
			if_type:		IF_ETHER sets a network interface to be ETHER type. Instead, IF_PPPOE sets a netwrok to be PPPoE type.
						This field is meaningful only when the ifname is specified.
			vid:			VLAN ID to create a vlan.
			memPort:		VLAN member port.
			untagSet:	VLAN untag Set.
			mtu:			MTU.
			mac:		MAC address of the VLAN or network interface.	
	eg1:

	struct rtl865x_vlanConfig vlanconfig[] = {
		{ 	"eth0",	 1,   IF_ETHER, 	8, 	   1, 	0x01, 		0x01,		1500, 	{ { 0x00, 0x00, 0xda, 0xcc, 0xcc, 0x08 } }	},
		{	"eth1",	 0,   IF_ETHER,	9,	   1,		0x1e,		0x1e,		1500,	{ { 0x00, 0x00, 0xda, 0xcc, 0xcc, 0x09 } }	},

		LRCONFIG_END,
	}
*/

/*    input   :   lan netif name in protocal stack 
       output :   the default vid (pvid) of the port mapping to this netif ,   if  -1 : errors  */
int32 rtl865x_getLANpvid(char* lan_netifname)
{
	struct net_device* lan_dev = re865x_get_netdev_by_name(lan_netifname);

	if(!strncmp(lan_netifname,"wlan",4))
		return RTL_LANVLANID;

	if(lan_dev==NULL)
		return -1;
	else if(!(lan_dev->priv_flags & IFF_DOMAIN_ELAN))
		return -1;
	else
	{
		int i;	
		int port=-1;
		for(i=0;i<RTL8651_AGGREGATOR_NUMBER;i++)
		{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
			if((1<<i) & (((struct dev_priv *)netdev_priv(lan_dev))->portmask) )
#else
			if((1<<i) & (((struct dev_priv *)lan_dev->priv)->portmask) )
#endif
			{
				#if defined (CONFIG_RTL_MULTI_LAN_DEV)
				if(port!=-1)				
					return -1;  /* lan device mapping to more than one nic port ?? */
				port = i;
				#else
				port = i;
				break;
				#endif
			}
		}
		if(port < 0 )
			return -1;
		else
			return pvid_per_port[port];
	}
	return -1;
}
int32 rtl865x_config(struct rtl865x_vlanConfig vlanconfig[])
{
	uint16 pvid;
	int32 i, j=0;
	int32 retval = 0;
	//uint32 valid_port_mask = 0;

	if (!vlanconfig[0].vid)
		return RTL_EINVALIDVLANID;

	INIT_CHECK(rtl8651_setAsicOperationLayer(4));
	WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L2_CHKSUM_ERR);
	WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L3_CHKSUM_ERR);
	WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L4_CHKSUM_ERR);
	
	for(i=0; vlanconfig[i].vid != 0; i++)
	{
		rtl865x_netif_t netif;

		if(vlanconfig[i].memPort == 0)
			continue;

		#if 0
		if(vlanconfig[i].isWan == 1)
		{
			/*wan*/
			valid_port_mask = RTL_WANPORT_MASK;
		}
		else
			valid_port_mask = RTL_LANPORT_MASK |0x100; //port8 
		#endif

//#ifdef CONFIG_RTL_MULTI_ETH_WAN
//		if (vlanconfig[i].isWan == 0)
//			vlanconfig[i].memPort |= RTL_WANPORT_MASK;
//#endif

		/*add vlan*/
#ifdef CONFIG_RTL_MULTI_ETH_WAN
		if (vlanconfig[i].if_type == IF_ETHER) {
#endif
			retval = rtl865x_addVlan(vlanconfig[i].vid);

			if(retval == SUCCESS)
			{
				rtl865x_addVlanPortMember(vlanconfig[i].vid,vlanconfig[i].memPort /*& valid_port_mask*/, vlanconfig[i].untagSet);	
				rtl865x_setVlanFilterDatabase(vlanconfig[i].vid,vlanconfig[i].fid);
			}
#ifdef CONFIG_RTL_MULTI_ETH_WAN
		}
#endif
		/*add network interface*/
		memset(&netif, 0, sizeof(rtl865x_netif_t));
		memcpy(netif.name,vlanconfig[i].ifname,MAX_IFNAMESIZE);
		memcpy(netif.macAddr.octet,vlanconfig[i].mac.octet,ETHER_ADDR_LEN);
		netif.mtu = vlanconfig[i].mtu;
		netif.if_type = vlanconfig[i].if_type;
		netif.vid = vlanconfig[i].vid;
		netif.is_wan = vlanconfig[i].isWan;
		netif.is_slave = vlanconfig[i].is_slave;
#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		netif.enableRoute=1;
#endif
		if(vlanconfig[i].protocol==SMUX_PROTO_BRIDGE)
			netif.if_feature |= IF_FEATURE_IS_BRIDGE;
		retval = rtl865x_addNetif(&netif);
#if defined(CONFIG_RTL_HWNAT_IPv6_SUPPORT)
		{
			rtl865x_netif_local_t *lan_netif = NULL;
			lan_netif = _rtl865x_getSWNetifByName(netif.name);
			if(lan_netif)
				rtl865x_enableNetifRouting(lan_netif,1);
		}
#endif

#ifndef CONFIG_RTL_MULTI_ETH_WAN
		if(netif.is_slave == 1)
#if defined(CONFIG_RTL_PUBLIC_SSID)
			rtl865x_attachMasterNetif(netif.name,RTL_GW_WAN_DEVICE_NAME);
#else
			;//rtl865x_attachMasterNetif(netif.name, RTL_DRV_WAN0_NETIF_NAME);
#endif
#endif
//#if defined(CONFIG_PROC_FS) && defined(CONFIG_NET_SCHED) && defined(CONFIG_RTL_LAYERED_DRIVER)
#if 0
#ifdef CONFIG_RTL_MULTI_ETH_WAN
		if (0==netif.is_slave)
#endif
			memcpy(&netIfName[j++][0], vlanconfig[i].ifname, sizeof(vlanconfig[i].ifname));
#endif

#if defined (CONFIG_RTL_UNKOWN_UNICAST_CONTROL)
		if (vlanconfig[i].isWan==0)
			memcpy(lanIfName, vlanconfig[i].ifname, sizeof(vlanconfig[i].ifname));
#endif
		if(retval != SUCCESS && retval != RTL_EVLANALREADYEXISTS)
			return retval;
		
	}

	/*this is a one-shot config*/
	if ((++__865X_Config) == 1)
	{
		for(i=0; i<RTL8651_PORT_NUMBER + 3; i++) 
		{ 
			/* Set each port's PVID */
			for(j=0,pvid=0; vlanconfig[j].vid != 0; j++)
			{
				if ( (1<<i) & vlanconfig[j].memPort ) 
				{
					pvid = vlanconfig[j].vid;
					break;
				}
			}
			
			if (pvid!=0)	
			{
	#ifdef CONFIG_HARDWARE_NAT_DEBUG
	/*2007-12-19*/
			rtlglue_printf("%s:%d:lrconfig[j].vid is %d,pvid is %d, j is %d,i is %d\n",__FUNCTION__,__LINE__,vlanconfig[j].vid,pvid,j, i);
	#endif
	
			CONFIG_CHECK(rtl8651_setAsicPvid(i, pvid));
	#ifdef CONFIG_RTL_HW_L2_ONLY
			rtl865x_setPortToNetif(vlanconfig[j].ifname,i);
	#endif
			}
		}
	}

	#if defined(CONFIG_RTL_XDSL_WIFI_HWACCELERATION)
		rtl8651_setAsicPvid(RTL_EXTPORT_P1, RTL_LANVLANID);
		rtl8651_setAsicPvid(RTL_EXTPORT_P2, RTL_LANVLANID);
		rtl8651_setAsicPvid(RTL_EXTPORT_P3, RTL_LANVLANID);
	#endif

	#if defined (CONFIG_RTL_HW_QOS_SUPPORT)
	rtl865x_initOutputQueue();  
	#endif

	#if defined (CONFIG_RTL_UNKOWN_UNICAST_CONTROL)
	{
		rtl865x_tblAsicDrv_rateLimitParam_t	asic_rl;
		/* 
	  	* Designer said: The time unit used to achieve rate limit is 1.67s (5/3), hence here we change 
	  	* the time unit to 1 sec.
	  	*/
		bzero(&asic_rl, sizeof(rtl865x_tblAsicDrv_rateLimitParam_t));
		asic_rl.maxToken			= RTL_MAC_REFILL_TOKEN;
		asic_rl.refill_number		= RTL_MAC_REFILL_TOKEN;
		asic_rl.t_intervalUnit		= 1; 
		asic_rl.t_remainUnit		= 1;
		asic_rl.token				= RTL_MAC_REFILL_TOKEN;
		rtl8651_setAsicRateLimitTable(0, &asic_rl);

		macRecordIdx = 0;
		bzero(macRecord, RTL_MAC_RECORD_NUM*sizeof(rtlMacRecord));

		for(i=0;i<RTL_MAC_RECORD_NUM;i++)
		{
			init_timer(&macRecord[i].timer);
			macRecord[i].timer.function = rtl_unkownUnicastTimer;
		}

		WRITE_MEM32(TEACR, (READ_MEM32(TEACR)|EnRateLimitTbAging));
	}
	#endif

#if defined(CONFIG_DSL_VTUO) || defined(CONFIG_REMOTE_ADSL_PHY)
	//VLAN passthrough
	{
		for(i=1;i<VLAN_NUMBER;i++)
		{
			//RTL_LANVLANID is used for untagged flow
			if(i==RTL_LANVLANID) continue;
			rtl865x_addVlan(i);
			rtl865x_addVlanPortMember(i,RTL_LANPORT_MASK,0x0);
		}
	}
#endif
	{
		int vid;
		for ( vid = 0; vid < RTL865XC_VLAN_NUMBER; vid++ ) {
			rtl865x_tblAsicDrv_vlanParam_t vlan;

			//set ASIC vid for those are not created by tagged interface
			if ( rtl8651_getAsicVlan( vid, &vlan ) == FAILED ) {
				vlan.memberPortMask = RTL_WANPORT_MASK|RTL_LANPORT_MASK;
				vlan.fid = 0;
				vlan.untagPortMask = 0;

				if ( rtl8651_setAsicVlan( vid, &vlan ) == FAILED )
					printk("Set vid:%d error!\n",vid);
			}
		}
	}

	return SUCCESS;
}

#if defined (CONFIG_RTL_UNKOWN_UNICAST_CONTROL)
static void rtl_unkownUnicastTimer(unsigned long data)
{
	rtlMacRecord	*record;
	rtl865x_AclRule_t	rule;

	record = (rtlMacRecord*)data;
	bzero((void*)&rule,sizeof(rtl865x_AclRule_t));
	rule.ruleType_ = RTL865X_ACL_MAC;
	rule.pktOpApp_ = RTL865X_ACL_ONLY_L2;
	rule.actionType_ = RTL865X_ACL_DROP_RATE_EXCEED_PPS;
	memset(rule.dstMacMask_.octet, 0xff, ETHER_ADDR_LEN);
	memcpy(rule.un_ty.dstMac_.octet, record->mac, ETHER_ADDR_LEN);
	rtl865x_del_acl(&rule, lanIfName, RTL865X_ACL_SYSTEM_USED);

	bzero(record, sizeof(rtlMacRecord));
	init_timer(&record->timer);
	record->timer.function = rtl_unkownUnicastTimer;
}

static void	rtl_unkownUnicastUpdate(uint8 *mac)
{
	int	idx;
	rtl865x_AclRule_t	rule;

	for(idx=0;idx<RTL_MAC_RECORD_NUM;idx++)
	{
		if (macRecord[idx].enable==0||memcmp(mac, macRecord[idx].mac, ETHER_ADDR_LEN))
			continue;

		/*	The mac has already recorded	*/
		if (macRecord[idx].cnt==RTL_MAC_THRESHOLD||++macRecord[idx].cnt<RTL_MAC_THRESHOLD)
			return;

		break;
	}

	/*	add/del the rules at lan side */
	bzero((void*)&rule,sizeof(rtl865x_AclRule_t));
	rule.ruleType_ = RTL865X_ACL_MAC;
	rule.pktOpApp_ = RTL865X_ACL_ONLY_L2;
	rule.actionType_ = RTL865X_ACL_DROP_RATE_EXCEED_PPS;
	memset(rule.dstMacMask_.octet, 0xff, ETHER_ADDR_LEN);

	if (idx==RTL_MAC_RECORD_NUM)
	{
		if (macRecord[macRecordIdx].enable!=0&&macRecord[macRecordIdx].cnt>RTL_MAC_THRESHOLD)
		{
			memcpy(rule.un_ty.dstMac_.octet, macRecord[macRecordIdx].mac, ETHER_ADDR_LEN);
			rtl865x_del_acl(&rule, lanIfName, RTL865X_ACL_SYSTEM_USED);
			init_timer(&macRecord[macRecordIdx].timer);
			macRecord[macRecordIdx].timer.function = rtl_unkownUnicastTimer;
		}
		else
		{
			macRecord[macRecordIdx].enable = 1;
		}
		macRecord[macRecordIdx].cnt = 0;
		memcpy(macRecord[macRecordIdx].mac, mac, ETHER_ADDR_LEN);
		macRecordIdx = (macRecordIdx+1)&(RTL_MAC_RECORD_NUM-1);
	}
	else
	{
		memcpy(rule.un_ty.dstMac_.octet, mac, ETHER_ADDR_LEN);
		rtl865x_add_acl(&rule, lanIfName, RTL865X_ACL_SYSTEM_USED);
		macRecord[idx].timer.data = (unsigned long)&(macRecord[idx]);
		mod_timer(&macRecord[idx].timer, jiffies+RTL_MAC_TIMEOUT);
	}
}
#endif

#if 0
#if defined (CONFIG_RTL_IGMP_SNOOPING)

static int re865x_reInitIgmpSetting(int mode)
{
	#if defined (CONFIG_RTL_MULTI_LAN_DEV)
	#else
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	rtl_multicastDeviceInfo_t devInfo;
	uint32 externalPortMask=0;
	#endif	
	int32 i;
	int32 totalVlans=((sizeof(vlanconfig))/(sizeof(struct rtl865x_vlanConfig)))-1;
	
	for(i=0; i<totalVlans; i++)
	{
		#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		if (TRUE==vlanconfig[i].isWan)
		{
			externalPortMask |=vlanconfig[i].memPort;
		}
		else
		{
			devInfo.portMask|=vlanconfig[i].memPort ;
		}
		#endif
		
		if(mode==GATEWAY_MODE)
		{
			rtl_setIgmpSnoopingModuleStaticRouterPortMask(nicIgmpModuleIndex, 0);
		}
		else
		{
			rtl_setIgmpSnoopingModuleStaticRouterPortMask(nicIgmpModuleIndex, 0x01);
		}

		rtl_setIgmpSnoopingModuleUnknownMCastFloodMap(nicIgmpModuleIndex, 0x0);
	}
	#endif
	
	#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	rtl865x_reinitMulticast();
	//rtl865x_setMulticastExternalPortMask(externalPortMask);
	rtl865x_setMulticastExternalPortMask(0);
	#endif
	rtl865x_igmpSyncLinkStatus();
	return SUCCESS;
}
#endif
#endif

#if defined (CONFIG_RTL_MULTI_LAN_DEV)
unsigned int rtl865x_getEthDevLinkStatus(struct net_device *dev)
{

	if(dev!=NULL)
	{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
		struct dev_priv *cp = netdev_priv(dev);
#else
		struct dev_priv *cp =dev->priv;
#endif
		//struct dev_priv *cp=netdev_priv(dev);
		return (cp->portmask & rtl865x_getPhysicalPortLinkStatus());
		
	}
	else
	{	
		return 0;
	}
}
#endif

#if 0
static int32 rtl_reinit_hw_table(void)
{
	/*re-init sequence eventmgr->l4->l3->l2->common is to make sure delete asic entry,
	if not following this sequence, 
	some asic entry can't be deleted due to reference count is not zero*/

	/*to-do:in each layer reinit function, memset all software entry to zero, 
	and force to clear all asic entry of own module, 
	then the re-init sequence can be common->l2->l3->l4 */
	/* FullAndSemiReset should not be called here
	  * it will make switch core action totally wrong
        */

	/*event management */
	#ifdef CONFIG_RTL_LAYERED_DRIVER_L2
	rtl865x_reInitEventMgr();
	#endif

	/*l4*/
	#if defined(CONFIG_RTL_LAYERED_DRIVER_L4)
	rtl865x_nat_reinit();
	#endif

	/*l3*/
	#ifdef CONFIG_RTL_LAYERED_DRIVER_L3
	rtl865x_reinitRouteTable();
	rtl865x_reinitNxtHopTable();
	rtl865x_reinitIpTable();	
	rtl865x_reinitPppTable();	
	rtl865x_arp_reinit();
	#endif

	/*l2*/
	#ifdef CONFIG_RTL_LAYERED_DRIVER_L2
	rtl865x_layer2_reinit();
	#endif

	/*common*/	
	rtl865x_reinitNetifTable();	
	rtl865x_reinitVlantable();
	rtl865x_reinit_acl();

	/*queue id & rx ring descriptor mapping*/
 	REG16(CPUQDM0)=QUEUEID0_RXRING_MAPPING;
 	REG16(CPUQDM1)=QUEUEID1_RXRING_MAPPING;
	REG16(CPUQDM2)=QUEUEID2_RXRING_MAPPING;
	REG16(CPUQDM3)=QUEUEID3_RXRING_MAPPING;
	REG16(CPUQDM4)=QUEUEID4_RXRING_MAPPING;
	REG16(CPUQDM5)=QUEUEID5_RXRING_MAPPING;

	WRITE_MEM32(PLITIMR,0);
	
	rtl8651_setAsicOperationLayer(2);	
	
	return SUCCESS;
	
}
#endif
#if 0
#if defined(CONFIG_RTK_VLAN_SUPPORT) || defined (CONFIG_RTL_MULTI_LAN_DEV)
static int rtl_config_perport_perdev_vlanconfig(int mode,int allLan)
{
	struct net_device* netdevice = NULL;
	int config_idx=0;	
	#define Search_dev_error(F) \
		printk("(%s)Error, cannot get the device that netifname is %s\n", __func__,#F)

	/* set LAN  */
	//eth0.2
	strcpy(vlanconfig[config_idx].ifname,RTL_DRV_LAN_P0_NETIF_NAME);
	vlanconfig[config_idx].isWan 		= 0;	
	vlanconfig[config_idx].if_type 		= IF_ETHER;
	vlanconfig[config_idx].vid 			= RTL_LANVLANID;
	vlanconfig[config_idx].fid 			= RTL_LAN_FID;
	vlanconfig[config_idx].memPort 	= RTL_LANPORT_MASK_4;
	vlanconfig[config_idx].untagSet 	= RTL_LANPORT_MASK_4;
	vlanconfig[config_idx].is_slave 		= 0;
	netdevice = rtl_get_psdev_by_ps_drv_netif(vlanconfig[config_idx].ifname);
	if(!netdevice)
	{
		Search_dev_error(vlanconfig[config_idx].ifname);
		return FAILED;
	}
	((struct dev_priv *)netdevice->priv)->portmask = RTL_LANPORT_MASK_4; 
	((struct dev_priv *)netdevice->priv)->portnum = 1;
	config_idx++;

	//eth0.3
	strcpy(vlanconfig[config_idx].ifname,RTL_DRV_LAN_P1_NETIF_NAME);
	vlanconfig[config_idx].isWan 		= 0;	
	vlanconfig[config_idx].if_type 		= IF_ETHER;
	vlanconfig[config_idx].vid 			= RTL_LANVLANID;
	vlanconfig[config_idx].fid 			= RTL_LAN_FID;
	vlanconfig[config_idx].memPort 	= RTL_LANPORT_MASK_3;
	vlanconfig[config_idx].untagSet 	= RTL_LANPORT_MASK_3;
	vlanconfig[config_idx].is_slave 		= 0;
	netdevice = rtl_get_psdev_by_ps_drv_netif(vlanconfig[config_idx].ifname);
	if(!netdevice)
	{
		Search_dev_error(vlanconfig[config_idx].ifname);
		return FAILED;
	}
	((struct dev_priv *)netdevice->priv)->portmask = RTL_LANPORT_MASK_3; 
	((struct dev_priv *)netdevice->priv)->portnum = 1;
	config_idx++;

	//eth0.4
	strcpy(vlanconfig[config_idx].ifname,RTL_DRV_LAN_P2_NETIF_NAME);
	vlanconfig[config_idx].isWan 		= 0;	
	vlanconfig[config_idx].if_type 		= IF_ETHER;
	vlanconfig[config_idx].vid 			= RTL_LANVLANID;
	vlanconfig[config_idx].fid 			= RTL_LAN_FID;
	vlanconfig[config_idx].memPort 	= RTL_LANPORT_MASK_2;
	vlanconfig[config_idx].untagSet 	= RTL_LANPORT_MASK_2;
	vlanconfig[config_idx].is_slave 		= 0;
	netdevice = rtl_get_psdev_by_ps_drv_netif(vlanconfig[config_idx].ifname);
	if(!netdevice)
	{
		Search_dev_error(vlanconfig[config_idx].ifname);
		return FAILED;
	}
	((struct dev_priv *)netdevice->priv)->portmask = RTL_LANPORT_MASK_2; 
	((struct dev_priv *)netdevice->priv)->portnum = 1;
	config_idx++;

	//eth0.5
	strcpy(vlanconfig[config_idx].ifname,RTL_DRV_LAN_P3_NETIF_NAME);
	vlanconfig[config_idx].isWan 		= 0;	
	vlanconfig[config_idx].if_type 		= IF_ETHER;
	vlanconfig[config_idx].vid 			= RTL_LANVLANID;
	vlanconfig[config_idx].fid 			= RTL_LAN_FID;
	vlanconfig[config_idx].memPort 	= RTL_LANPORT_MASK_1;
	vlanconfig[config_idx].untagSet 	= RTL_LANPORT_MASK_1;
	vlanconfig[config_idx].is_slave 		= 0;
	netdevice = rtl_get_psdev_by_ps_drv_netif(vlanconfig[config_idx].ifname);
	if(!netdevice)
	{
		Search_dev_error(vlanconfig[config_idx].ifname);
		return FAILED;
	}
	((struct dev_priv *)netdevice->priv)->portmask = RTL_LANPORT_MASK_1; 
	((struct dev_priv *)netdevice->priv)->portnum = 1;
	config_idx++;

	/* set  WAN */	
	#ifdef CONFIG_RTL_MULTI_ETH_WAN
	//nas0_0 ~ nas0_x
	rtl_adjust_wanport_vlanconfig(vlanconfig,&config_idx);
	#else
	
	//nas0
	strcpy(vlanconfig[config_idx].ifname,RTL_DRV_LAN_P4_NETIF_NAME);
	if(allLan)
		vlanconfig[config_idx].isWan = 0;
	else
		vlanconfig[config_idx].isWan = 1;
	
	vlanconfig[config_idx].if_type 		= IF_ETHER;	

	if((mode == BRIDGE_MODE) || (mode== WISP_MODE))
	{
		vlanconfig[config_idx].vid 			= RTL_BridgeWANVLANID;
		vlanconfig[config_idx].fid 			= RTL_WAN_FID;
		vlanconfig[config_idx].memPort 		= RTL_WANPORT_MASK | RTL_LANPORT_MASK;
		vlanconfig[config_idx].untagSet 		= RTL_WANPORT_MASK | RTL_LANPORT_MASK;
	}
	else
	{
		vlanconfig[config_idx].vid 			= RTL_WANVLANID;
		vlanconfig[config_idx].fid 			= RTL_WAN_FID;
		vlanconfig[config_idx].memPort 		= RTL_WANPORT_MASK;
		vlanconfig[config_idx].untagSet 		= RTL_WANPORT_MASK;
	}
	vlanconfig[config_idx].is_slave 		= 0;
	netdevice = rtl_get_psdev_by_ps_drv_netif(vlanconfig[config_idx].ifname);
	if(!netdevice)
	{
		Search_dev_error(vlanconfig[config_idx].ifname);
		return FAILED;
	}
	((struct dev_priv *)netdevice->priv)->portmask = RTL_WANPORT_MASK; 
	((struct dev_priv *)netdevice->priv)->portnum = 1;
	config_idx++;

	//ppp0
	strcpy(vlanconfig[config_idx].ifname,RTL_DRV_PPP_NETIF_NAME);
	if(allLan)
		vlanconfig[config_idx].isWan = 0;
	else
		vlanconfig[config_idx].isWan = 1;
	
	vlanconfig[config_idx].if_type 		= IF_PPPOE;		
	vlanconfig[config_idx].vid 			= RTL_WANVLANID;
	vlanconfig[config_idx].fid 			= RTL_WAN_FID;
	vlanconfig[config_idx].memPort 		= RTL_WANPORT_MASK;
	vlanconfig[config_idx].untagSet 		= RTL_WANPORT_MASK;
	
	config_idx++;
	#endif

	/* set last entry */
	strcpy(vlanconfig[config_idx].ifname,"");
	vlanconfig[config_idx].vid			= 0;
	vlanconfig[config_idx].fid 			= 0;
	vlanconfig[config_idx].memPort 	= 0;
	vlanconfig[config_idx].untagSet 	= 0;
	
#if 0
	vlanconfig[0].vid = RTL_LANVLANID;
#ifdef CONFIG_RTL_MULTI_ETH_WAN
	vlanconfig[0].isWan = 0;
#endif
	vlanconfig[0].memPort = RTL_LANPORT_MASK_4;
	vlanconfig[0].untagSet= RTL_LANPORT_MASK_4;			
	((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->portmask = RTL_LANPORT_MASK_4; //eth0.2
	//((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->id = RTL_LANVLANID_1; //eth0.2
	((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->portnum = 1;

	if((mode == BRIDGE_MODE) || (mode== WISP_MODE))
	{
		vlanconfig[1] .vid = RTL_LANVLANID; 
		//((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = RTL_LANVLANID; 
	}
	else
	{	
		vlanconfig[1] .vid = RTL_WANVLANID; 
		//((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = RTL_WANVLANID; 
	}		
	if(allLan)
		vlanconfig[1].isWan = 0;
	else
		vlanconfig[1].isWan = 1;
	vlanconfig[1].memPort = RTL_WANPORT_MASK;
	vlanconfig[1].untagSet= RTL_WANPORT_MASK;			
	((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->portmask = RTL_WANPORT_MASK; //nas0
	//((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = RTL_WANVLANID; //nas0
	((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->portnum = 1;
	
	vlanconfig[2] .vid = RTL_LANVLANID;
	vlanconfig[2].memPort = RTL_LANPORT_MASK_3;
	vlanconfig[2].untagSet = RTL_LANPORT_MASK_3;
	((struct dev_priv *)_rtl86xx_dev.dev[2]->priv)->portmask = RTL_LANPORT_MASK_3; //eth0.3
	//((struct dev_priv *)_rtl86xx_dev.dev[2]->priv)->id = RTL_LANVLANID_2; //eth0.3
	((struct dev_priv *)_rtl86xx_dev.dev[2]->priv)->portnum = 1;
	
	vlanconfig[3] .vid = RTL_LANVLANID;
	vlanconfig[3].memPort = RTL_LANPORT_MASK_2;
	vlanconfig[3].untagSet = RTL_LANPORT_MASK_2;
	((struct dev_priv *)_rtl86xx_dev.dev[3]->priv)->portmask = RTL_LANPORT_MASK_2; //eth0.4
	//((struct dev_priv *)_rtl86xx_dev.dev[3]->priv)->id = RTL_LANVLANID_3; //eth0.4
	((struct dev_priv *)_rtl86xx_dev.dev[3]->priv)->portnum = 1;
	
	vlanconfig[4] .vid = RTL_LANVLANID;
	vlanconfig[4].memPort = RTL_LANPORT_MASK_1;
	vlanconfig[4].untagSet = RTL_LANPORT_MASK_1;
	((struct dev_priv *)_rtl86xx_dev.dev[4]->priv)->portmask = RTL_LANPORT_MASK_1; //eth0.5
	//((struct dev_priv *)_rtl86xx_dev.dev[4]->priv)->id = RTL_LANVLANID_4; //eth0.5
	((struct dev_priv *)_rtl86xx_dev.dev[4]->priv)->portnum = 1;
	
	#if defined(CONFIG_8198_PORT5_GMII)
	vlanconfig[5] .vid = RTL_LANVLANID;
	vlanconfig[5].memPort = RTL_LANPORT_MASK_5;
	vlanconfig[5].untagSet = RTL_LANPORT_MASK_5;
	((struct dev_priv *)_rtl86xx_dev.dev[5]->priv)->portmask = RTL_LANPORT_MASK_5;
	//((struct dev_priv *)_rtl86xx_dev.dev[5]->priv)->id = RTL_LANVLANID_5;
	((struct dev_priv *)_rtl86xx_dev.dev[5]->priv)->portnum = 1;
	#endif

	#if defined(CONFIG_8198_PORT5_GMII)
	if(allLan)
		vlanconfig[6].isWan = 0; //ppp0
	else
		vlanconfig[6].isWan = 1;
	#else
	#ifndef CONFIG_RTL_MULTI_ETH_WAN
	if(allLan)
		vlanconfig[5].isWan = 0;
	else
		vlanconfig[5].isWan = 1;
	#else
	for (i=5; i<17; i++) {
		vlanconfig[i].isWan = allLan?0:1;
	}
	#endif
	#endif
	#ifdef CONFIG_RTL_MULTI_ETH_WAN
	rtl_adjust_wanport_vlanconfig(vlanconfig);
	#endif /* CONFIG_RTL_MULTI_ETH_WAN */
#endif /* old version */	
	
	return SUCCESS;
}
#endif
#ifndef CONFIG_RTL_MULTI_LAN_DEV
static int rtl_config_lanwan_dev_vlanconfig(int mode)
{
	/*lan config*/
	{
		vlanconfig[2].vid = 0;
		vlanconfig[3].vid = 0;
		//vlanconfig[4].vid = 0;
		#if defined(CONFIG_8198_PORT5_GMII)
		vlanconfig[5].vid = 0;
		#endif
	}	
	
#if defined (CONFIG_RTL_IVL_SUPPORT)
	#if defined(CONFIG_POCKET_ROUTER_SUPPORT)
		if(rtl865x_curOpMode == GATEWAY_MODE)
		{
			vlanconfig[0].memPort = 0;
			vlanconfig[0].vid=RTL_LANVLANID;
			vlanconfig[0].untagSet = 0;
			((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->portmask = 0; //eth0
			((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->id = RTL_LANVLANID;
			vlanconfig[1].vid = RTL_WANVLANID;
			vlanconfig[1].memPort = RTL_WANPORT_MASK;
			vlanconfig[1].untagSet = RTL_WANPORT_MASK;
			((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->portmask = RTL_WANPORT_MASK; //eth1
			((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = RTL_WANVLANID; //eth1
		}
		else
		{
			vlanconfig[0].memPort = RTL_LANPORT_MASK;
			vlanconfig[0].vid=RTL_LANVLANID;
			vlanconfig[0].untagSet = RTL_LANPORT_MASK;
			((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->portmask = RTL_LANPORT_MASK; //eth0
			((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->id = RTL_LANVLANID;
			
			vlanconfig[1].vid = 0;
			vlanconfig[1].memPort = 0;
			vlanconfig[1].untagSet = 0;
			((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->portmask = 0; //eth1
			((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = RTL_WANVLANID; //eth1
		}
	
	#else //else CONFIG_POCKET_ROUTER_SUPPORT
		vlanconfig[0].memPort = RTL_LANPORT_MASK;
		vlanconfig[0].vid=RTL_LANVLANID;
		vlanconfig[0].untagSet = RTL_LANPORT_MASK;
		((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->portmask = RTL_LANPORT_MASK; //eth0
		((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->id = RTL_LANVLANID;
		
		vlanconfig[1].vid = RTL_WANVLANID;
		vlanconfig[1].memPort = RTL_WANPORT_MASK;
		vlanconfig[1].untagSet = RTL_WANPORT_MASK;
		((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->portmask = RTL_WANPORT_MASK; //eth1
		((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = RTL_WANVLANID; //eth1
	#endif //endif CONFIG_POCKET_ROUTER_SUPPORT
	
#else
	if(rtl865x_curOpMode == BRIDGE_MODE || rtl865x_curOpMode== WISP_MODE)
	{
		vlanconfig[0].memPort = 0x1ff;
		vlanconfig[0].untagSet = 0x1ff;
		vlanconfig[0].vid=RTL_LANVLANID;		
		((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->portmask = 0x1ff; //eth0
		((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->id = RTL_LANVLANID;
		
		vlanconfig[1].vid = 0;
		vlanconfig[1].memPort=0x0;
		vlanconfig[1].untagSet=0x0;
		((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->portmask = 0x0; //eth1
		((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = 0;
	}
	else
	{
		vlanconfig[0].memPort = RTL_LANPORT_MASK;
		vlanconfig[0].vid=RTL_LANVLANID;
		vlanconfig[0].untagSet = RTL_LANPORT_MASK;
		((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->portmask = RTL_LANPORT_MASK; //eth0
		((struct dev_priv *)_rtl86xx_dev.dev[0]->priv)->id = RTL_LANVLANID;
		
		vlanconfig[1].vid = RTL_WANVLANID;
		vlanconfig[1].memPort = RTL_WANPORT_MASK;
		vlanconfig[1].untagSet = RTL_WANPORT_MASK;
		((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->portmask = RTL_WANPORT_MASK; //eth1
		((struct dev_priv *)_rtl86xx_dev.dev[1]->priv)->id = RTL_WANVLANID; //eth1
	}
#endif

	return SUCCESS;
}
#endif
#if	defined (CONFIG_RTL_MULTI_LAN_DEV)
static int rtl_config_multi_lan_dev_vlanconfig(int mode,int allLan)
{	
	rtl_config_perport_perdev_vlanconfig(mode,allLan);
	return SUCCESS;
	
}
#endif
#endif
#if defined(CONFIG_RTK_VLAN_SUPPORT)
static int rtl_config_rtkVlan_vlanconfig(int mode)
{	
	if(!rtk_vlan_support_enable)
		rtl_config_lanwan_dev_vlanconfig(mode);
	else if(rtk_vlan_support_enable == 1)
	{
		rtl_config_perport_perdev_vlanconfig(mode);
	}
	else if(rtk_vlan_support_enable == 2)
		rtl_config_lanwan_dev_vlanconfig(mode);

	return SUCCESS;
}
#endif

#if 0
static int rtl_config_vlanconfig(int mode,int allLan)
{
	#if	defined (CONFIG_RTL_MULTI_LAN_DEV)
		rtl_config_multi_lan_dev_vlanconfig(mode,allLan);
	#else
		#if defined(CONFIG_RTK_VLAN_SUPPORT)
			rtl_config_rtkVlan_vlanconfig(mode);
		#else
			rtl_config_lanwan_dev_vlanconfig(mode);
		#endif
	#endif

	return SUCCESS;
	
}
#endif

#if 0
#ifdef CONFIG_RTL_MULTI_ETH_WAN
static int32 update_vlan_configure(struct rtl865x_vlanConfig new_vlanconfig[])
{
	int32 i;
	struct rtl865x_vlanConfig *pvlanconfig = NULL;
	int32 totalVlans = 0;	
	pvlanconfig = new_vlanconfig;

	/*get input vlan config entry number*/
	for(i=0; pvlanconfig[i].ifname[0] != '\0' ; i++)
	{
		if(pvlanconfig[i].vid != 0)
			totalVlans++;
	}	
	
	//because the new_vlanconfig should be packedVlanConfig
	totalVlans = totalVlans > NETIF_SW_NUMBER? NETIF_SW_NUMBER:totalVlans;

	for(i=0; i<totalVlans; i++)
	{
		if(pvlanconfig[i].vid == 0)
			continue;

		/*add vlan*/
		if(pvlanconfig[i].if_type==IF_ETHER)
		{
			rtl865x_modVlanPortMember(pvlanconfig[i].vid,pvlanconfig[i].memPort, pvlanconfig[i].untagSet);
		}
	}

	rtl865x_setpvid(pvlanconfig);
	#if 0
	for(i=0; i<RTL8651_PORT_NUMBER + 3; i++)
	{
		/* Set each port's PVID */
		for(j=0; pvlanconfig[j].vid != 0; j++)
		{
			if ( pvlanconfig[j].vid == pvid_per_port[i]) 
			{
				break;
			}
		}

		if (pvlanconfig[j].vid) {
			CONFIG_CHECK(rtl8651_setAsicPvid(i,pvid_per_port[i]));
			rtl865x_setPortToNetif(pvlanconfig[j].ifname, i);
		}
	}
	#endif

	return SUCCESS;
}

int32 rtl865x_updateNetifForPortmapping(void)
{
	/*config vlan config*/
	//rtl_config_vlanconfig(rtl865x_curOpMode, rtl865x_curOpMode_allLan);

	if (!vlanconfig[0].vid && !vlanconfig[1].vid )
		return RTL_EINVALIDVLANID;

	re865x_packVlanConfig(vlanconfig, packedVlanConfig);

	update_vlan_configure(packedVlanConfig);

	return SUCCESS;
}
#endif //CONFIG_RTL_MULTI_ETH_WAN
#endif

#ifdef CONFIG_RTL_MULTI_ETH_WAN
static int _rtl_update_vlan_table(int vlan_id)
{
	int i;
	int _the_num_of_this_vid_WAN=0;
	int _has_bridged_WAN=0;

	/*	member ports are always WAN + LAN 	*/
	for(i=0; vlanconfig[i].ifname[0] != '\0' ; i++)
	{
		if( vlanconfig[i].isWan&& vlanconfig[i].is_slave==0 && vlanconfig[i].vid==vlan_id)	
		{
			_the_num_of_this_vid_WAN++;	
			if(vlanconfig[i].protocol==SMUX_PROTO_BRIDGE)			
				_has_bridged_WAN = 1;		
		}
	}

	if(_the_num_of_this_vid_WAN >0 )
	{	
		uint32		memPort;
		uint32		untagSet;
		int 			ret;

		if(_has_bridged_WAN)
		{
			memPort 	= (RTL_LANPORT_MASK | RTL_WANPORT_MASK);
			if(vlan_id==RTL_BridgeWANVLANID)
				untagSet 	= (RTL_LANPORT_MASK | RTL_WANPORT_MASK);
			else
				untagSet 	= RTL_LANPORT_MASK;
		}
		else
		{		
			memPort 	= RTL_WANPORT_MASK;
			if(vlan_id==RTL_WANVLANID)
				untagSet 	= RTL_WANPORT_MASK;
			else
				untagSet 	= 0;
		}
		ret = rtl865x_addVlan(vlan_id);	
		if(ret == SUCCESS)
		{
			/*  this vlan is the first time to setup , set its fid */
			if(rtl865x_setVlanFilterDatabase(vlan_id,RTL_WAN_FID)!=SUCCESS)
			{
				DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
				return FAILED;
			}
		}
		else if(ret!=RTL_EVLANALREADYEXISTS)
		{
			DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
			return FAILED;
		}	

		/*  No matter whether we has setup this vlan table before or not , reset its member/untag ports  */
		if(rtl865x_modVlanPortMember(vlan_id,memPort,untagSet)!=SUCCESS)
		{
			DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
			return FAILED;
		}		
	}
	else
	{
		//For vlan passthru
		#if 1
		uint32          memPort;
		uint32          untagSet;
		memPort = (RTL_LANPORT_MASK | RTL_WANPORT_MASK);
		untagSet = 0;
		if(rtl865x_modVlanPortMember(vlan_id,memPort,untagSet)!=SUCCESS)
		#else
		if(rtl865x_delVlan(vlan_id)!=SUCCESS)
		#endif
		{
			DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
			return FAILED;
		}		
	}

	return SUCCESS;
}

static int32 _rtl_update_pvid(void)
{
	int i,j;
	int bridged_wan_exist = 0;
	int novlantag_bridged_wan_exist = 0;
	int novlantag_routing_wan_exist = 0;

	DBG_MULTIWAN_API_PRK("Enter %s\n",__func__);

	re865x_packVlanConfig(vlanconfig, packedVlanConfig);	

	for(j=0; packedVlanConfig[j].vid != 0; j++)
	{
		 if( packedVlanConfig[j].isWan)
		 {
			if(packedVlanConfig[j].protocol == SMUX_PROTO_BRIDGE)
		 	{
		 		bridged_wan_exist = 1;		
				if(packedVlanConfig[j].vid==RTL_BridgeWANVLANID)
					novlantag_bridged_wan_exist=1;
			}
			else
			{
				if(packedVlanConfig[j].vid==RTL_WANVLANID)
					novlantag_routing_wan_exist=1;
			}
		 }
	}

	DBG_MULTIWAN_API_PRK("(%s) bridged_wan_exist:%d   novlantag_bridged_wan_exist:%d  novlantag_routing_wan_exist:%d \n"
		,__func__,bridged_wan_exist,novlantag_bridged_wan_exist,novlantag_routing_wan_exist);

	for(i=0; i<RTL8651_PORT_NUMBER + 3; i++) 
	{
		uint16 pvid;
		/* wan port*/
		if( (1<<i)&RTL_WANPORT_MASK )
		{				
			/* 
				vlan 7's mbr = WAN + LAN 	(for downstream bridging unicast hwacc)
				vlan 8's mbr = WAN		(for downstream routing unicast hwacc)

				downstream bridging unicast hwacc 
					: vid has to be 7
				downstream IPoE unicast hwacc 
					: vid could be either 7 or 8
				downstream PPPoE unicast hwacc 
					: vid could be either 7 or 8

				downstream bridging multicast hwacc	: vid could be either 7 or 8
				downstream IPoE multicast hwacc	: vid could be either 7 or 8
				downstream PPPoE multicast hwacc	: vid has to be 8 (for DA changes from GMAC to 01:00:5e:xx:xx:xx)

				Hence, Default WAN port's pvid is 8 , but...
					(1) If there existed one bridged WAN, pvid should be 7
					(2) If there existed both bridged and PPPoE WAN, pvid is 7 and set pppoe's pvid = 8		
			*/
			if(novlantag_bridged_wan_exist)
				pvid = RTL_BridgeWANVLANID;
			else if(novlantag_routing_wan_exist)
				pvid = RTL_WANVLANID;					
			else
				pvid = 0;
			
/* //support untag L2 unicast pppoe passthrough hwacc, but l3 pppoe multicast will no hwacc 
			if(novlantag_bridged_wan_exist && novlantag_routing_wan_exist)
				rtl8651_setProtocolBasedVLAN(RTL8651_PBV_RULE_PPPOE_SESSION,i,1,RTL_WANVLANID);
			else
				rtl8651_setProtocolBasedVLAN(RTL8651_PBV_RULE_PPPOE_SESSION,i,0,0);
*/				
		}
		/* lan port */
		else
		{
		#if defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB)
			pvid = RTL_LANVLANID;
		#else
			if(!bridged_wan_exist)
				pvid = RTL_LANVLANID;
			else
			{
				/* if the lan port only  exist in only one bridged WAN, we can set pvid as its vid for upstream hw l2 forwarding*/
				int this_port_exist_in_bridged_wan_num=0;
				int the_first_vid_exist_in_bridged_wan=-1;			

				for(j=0; packedVlanConfig[j].vid != 0; j++)
				{			
					if ( packedVlanConfig[j].isWan && packedVlanConfig[j].memPort != RTL_WANPORT_MASK && (packedVlanConfig[j].memPort&(1<<i)) ) 
					{
						this_port_exist_in_bridged_wan_num ++;			
						if(the_first_vid_exist_in_bridged_wan==-1)
							the_first_vid_exist_in_bridged_wan = packedVlanConfig[j].vid;
					}
				}
					
				if(this_port_exist_in_bridged_wan_num==1)
					pvid = the_first_vid_exist_in_bridged_wan;
				else
					pvid = RTL_LANVLANID;	
			}
		#endif
		}

		/* extport patch*/	
		if(i==RTL_CPU_PORT)	
			pvid = RTL_LANVLANID;

		//set pvid
		CONFIG_CHECK(rtl8651_setAsicPvid(i,pvid));
		pvid_per_port[i]=pvid;
	
		//set port to netif	
		for(j=0; packedVlanConfig[j].vid != 0; j++)		
			if ( packedVlanConfig[j].vid == pvid) 			
				break;		
		rtl865x_setPortToNetif(packedVlanConfig[j].ifname, i);	
	}

	return SUCCESS;
	
}

int rtl_set_wanport_vlanconfig(char *ifname, int proto, int vid, int napt)
{
	int idx=-1;
    int vlan_id;
	rtl865x_netif_t netif;
	int i;

	DBG_MULTIWAN_API_PRK("Enter %s (ifname:%s  protio:%d   vid:%d  napt:%d)\n",__func__,
		ifname,proto,vid,napt);

	/* we has to check whether ifname existed and has not been used yet  
		Otherwise, there will exist two same netif_name in "vlanconfig"
	*/
	for(i=0; vlanconfig[i].ifname[0] != '\0' ; i++)
	{
		if( vlanconfig[i].isWan&& vlanconfig[i].is_slave==0 && !strcmp(vlanconfig[i].ifname,ifname))	
		{
			if(vlanconfig[i].vid==0) /* this wan has not been used */
			{		
				idx = i;
				break;
			}
		}
	}

	if (idx == -1)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}

	/* Note.  the fields except for vid,isWan,proto in "vlanconfig" have no change */
	/* update  vid  in "vlanconfig"  */
	if (vid != -1) {
		vlan_id = vid&VLAN_VID_MASK;
		}
	else {		
		if (proto == SMUX_PROTO_BRIDGE)
			vlan_id = RTL_BridgeWANVLANID;
		else
			vlan_id = RTL_WANVLANID;	
	}
   	 vlanconfig[idx].vid = vlan_id;
	 DBG_MULTIWAN_API_PRK("(%s)vid:%d\n",__func__,vlan_id);

	/* update  proto  in "vlanconfig"  */
	vlanconfig[idx].protocol = proto;

	/* sanity check , this netifname does not exist in sw_netif  */
	if(rtl865x_netifExist(ifname))
	{
		/* BUG !  vlanconfig & sw_neif no sync ??  */
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}	
	
	/* Update VLAN table */
	if(_rtl_update_vlan_table(vlan_id)!=SUCCESS)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}

	/* Update netif table */
	memset(&netif, 0, sizeof(rtl865x_netif_t));
	memcpy(netif.name,ifname,MAX_IFNAMESIZE);
	netif.mtu 		= 1500;
	netif.if_type 		= IF_ETHER;
	netif.vid 			= vlan_id;
	netif.is_wan	= 1;
	if( proto == SMUX_PROTO_BRIDGE )
		netif.if_feature |= IF_FEATURE_IS_BRIDGE;
	if(napt)
		netif.if_feature |= IF_FEATURE_IS_NAPT;

	netif.is_slave 		= 0;
	netif.enableRoute	= 1;
	if(rtl865x_addNetif(&netif)!=SUCCESS)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}
	#if defined(CONFIG_RTL_HWNAT_IPv6_SUPPORT)
	netif.enablev6Route=1;
	#endif
	
#if defined(CONFIG_RTL8196E_IPCHECKSUM_ERROR_PATCH) && defined(CONFIG_RTL_LAYERED_DRIVER_ACL)
	/* Trap the brdiged packet with both vlan and pppoe packet  */
	if(proto == SMUX_PROTO_BRIDGE && vid != -1)
	{
		rtl865x_AclRule_t rule;
		
		DBG_MULTIWAN_API_PRK("(%s) Add ACL to trap the brdiged unicast packet with both vlan and pppoe tag \n",__func__);
		memset(&rule, 0,sizeof(rtl865x_AclRule_t));		
		rule.ruleType_			= RTL865X_ACL_MAC;
		rule.actionType_			= RTL865X_ACL_TOCPU;	
		rule.pktOpApp_ 			= RTL865X_ACL_ALL_LAYER;
		rule.direction_	 		= RTL865X_ACL_INGRESS;			
		rule.un_ty.typeLen_		= 0x8864;
		rule.un_ty.typeLenMask_	= 0xffff;
		if(rtl865x_add_acl(&rule, netif.name, RTL865X_ACL_ICBUG_PATCH,1,1)!=SUCCESS)
		{
			DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
			return FAILED;
		}		
	}
#endif

#if defined(CONFIG_RTL_MULTI_ETH_WAN_VLAN_BASED)
	/* For untagged bridge + untagged route WAN and VLAN-Based setting, 
	   because WAN port pvid = 7, untagged routing WAN down-stream packet will lookup NETIF of vid=7 to check DMAC==GMAC. */
	/* If this vid =7 , use vid 8's mac addr */
	if(vlan_id == RTL_BridgeWANVLANID)
	{
		char vid_8_netif_name[30];
		DBG_MULTIWAN_API_PRK("(%s) this netif's vid =7 , check whether vid 8 existed , use vid 8's mac addr ... \n",__func__);

		rtl865x_getMasterNetifByVid(RTL_WANVLANID,vid_8_netif_name);

		if(strcmp(vid_8_netif_name,""))
		{						
			rtl865x_netif_local_t* netif_vid_8 = _rtl865x_getNetifByName(vid_8_netif_name);
			rtl865x_netif_t netif_set_mac;
			if(netif_vid_8==NULL)
			{
				DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
				return FAILED;
			}

			memcpy(netif_set_mac.macAddr.octet, netif_vid_8->macAddr.octet, ETHER_ADDR_LEN);
			memcpy(netif_set_mac.name, ifname, MAX_IFNAMESIZE);			
			
			if(rtl865x_setNetifMac(&netif_set_mac)!=SUCCESS)
			{
				DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
				return FAILED;
			}
		}				
	}
#endif
	
	/* set pvid */
	_rtl_update_pvid();
	
	return SUCCESS;	
}

/*
	In RTL8676 (it cannot change VID by using ACL rules) , after calling rtl_set_wanport_vlanconfig()
		you should call rtl_set_wanport_portmapping() to set each WAN's binding member ports.
		If the LAN ports binding to more than one Bridged WAN, the LAN port's pvid = 9 (cannot hwacc)

	In RTL8685 , we could use acl to change vid , so we do not care about port-binding relationship
*/	
int rtl_set_wanport_portmapping(char *ifname,unsigned int member)
{
#if !(defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB))
	int idx=-1;
	int i;
	
	DBG_MULTIWAN_API_PRK("Enter %s (ifname:%s  member:0x%X)\n",__func__,
		ifname,member);

	for(i=0; vlanconfig[i].ifname[0] != '\0' ; i++)
	{
		if( vlanconfig[i].isWan&& vlanconfig[i].is_slave==0 && !strcmp(vlanconfig[i].ifname,ifname))	
		{
			if(vlanconfig[i].vid!=0) /* we are really using this wan now */
			{
				idx = i;
				break;
			}
		}
	}		
	if (idx <0)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}	

	DBG_MULTIWAN_API_PRK("(%s) idx = %d  \n",__func__,idx);
	
	vlanconfig[idx].memPort 	= RTL_WANPORT_MASK;
	vlanconfig[idx].untagSet 	= RTL_WANPORT_MASK;
			
	if (vlanconfig[idx].protocol  == SMUX_PROTO_BRIDGE)
	{
		//set membership
		if (member == 0xFFFFFFFF) {
			vlanconfig[idx].memPort |= (RTL_LANPORT_MASK | RTL_WANPORT_MASK);
			vlanconfig[idx].untagSet |= (RTL_LANPORT_MASK | RTL_WANPORT_MASK);
		}
		else {
			vlanconfig[idx].memPort |= (member&RTL_LANPORT_MASK);
			vlanconfig[idx].untagSet |= (member&RTL_LANPORT_MASK);
		}
	}
	if (( vlanconfig[idx].vid != RTL_BridgeWANVLANID && vlanconfig[idx].vid != RTL_WANVLANID)
		#ifdef UNIQUE_MAC_PER_DEV
		&& (vid > 8)
		#endif
	) {
		vlanconfig[idx].untagSet &= (~RTL_WANPORT_MASK);
	}	
	
	/* set pvid */
	_rtl_update_pvid();		
#endif
		
	return SUCCESS;
	
}

int rtl865x_unregisterDev(char *ifname) 
{
	int idx=-1;
	int i;
	int vlan_id=-1;
	
	DBG_MULTIWAN_API_PRK("Enter %s (ifname:%s)\n",__func__,ifname);

	for(i=0; vlanconfig[i].ifname[0] != '\0' ; i++)
	{
		if( vlanconfig[i].isWan&& vlanconfig[i].is_slave==0 && !strcmp(vlanconfig[i].ifname,ifname))	
		{
			//if(vlanconfig[i].vid!=0) /* we are really using this wan now */
			{
				idx = i;
				vlan_id = vlanconfig[i].vid;
				break;
			}
		}
	}		
	if (idx <0)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}

	/* Update vlanconfig (reassign vid back to 0) */
	vlanconfig[idx].isWan 		= 1;	
	vlanconfig[idx].if_type 	= IF_ETHER;
	vlanconfig[idx].vid 		= 0;
	vlanconfig[idx].fid 		= RTL_WAN_FID;
	vlanconfig[idx].memPort 	= RTL_WANPORT_MASK;
	vlanconfig[idx].untagSet 	= RTL_WANPORT_MASK;
	vlanconfig[idx].is_slave 	= 0;
	
	/* sanity check , this netifname exists in sw_netif actually */
	if(!rtl865x_netifExist(ifname))
	{
		/* BUG !  vlanconfig & sw_neif no sync ??  */
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}
	
	/* Update netif table */
	if(rtl865x_delNetif(ifname)!=SUCCESS)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}

	/* Update VLAN table */
	if(_rtl_update_vlan_table(vlan_id)!=SUCCESS)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;	
	}

	/* set pvid */
	_rtl_update_pvid();
	
	return SUCCESS;
	
}

int rtl865x_setNetifMacAddr(char *ifname, unsigned char *addr)
{

	int vid;
	rtl865x_netif_t netif;

	DBG_MULTIWAN_API_PRK("Enter %s (ifname:%s   addr:%02X:%02X:%02X:%02X:%02X:%02X:) \n",__func__,ifname,
		addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]);
	
	memcpy(netif.macAddr.octet, addr, ETHER_ADDR_LEN);
	memcpy(netif.name, ifname, MAX_IFNAMESIZE);
	if(rtl865x_setNetifMac(&netif)!=SUCCESS)
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}	
	
	vid=0;
#if defined(CONFIG_RTL_MULTI_ETH_WAN_VLAN_BASED)
	if(rtl865x_getNetifVid(ifname,&vid)==SUCCESS)
	{
		if(vid == RTL_WANVLANID)
		{
			char vid_7_netif_name[30];
			DBG_MULTIWAN_API_PRK("(%s) this netif's vid =8 ,  set vid 7 either... \n",__func__);

			rtl865x_getMasterNetifByVid(RTL_BridgeWANVLANID,vid_7_netif_name);

			if(strcmp(vid_7_netif_name,""))
			{			
				memcpy(netif.name, vid_7_netif_name, MAX_IFNAMESIZE);
				if(rtl865x_setNetifMac(&netif)!=SUCCESS)
				{
					DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
					return FAILED;
				}
			}			
		}
	}
	else
	{
		DBG_MULTIWAN_API_PRK("Leave %s @ %d \n",__func__,__LINE__);
		return FAILED;
	}
#endif
	
	return SUCCESS;
}
#endif //CONFIG_RTL_8676HWNAT

#ifdef CONFIG_RTL_PPPOE_HWACC
/*
 * FUNC                    : rtl_add_ppp_netif()
 * DESC                    : Only ppp0 exists in netif table now. When we add a new additional pppoe connection, no netif entry is available for it, 
 *                                so we must create a new netif entry for this connection.
 * RETURN VALUE     : int32   SUCCESS or FAIL
 * DATE                    : 2012-02-22 QL
 */
int32 rtl_add_ppp_netif(char *ifname)
{
	struct rtl865x_netif_s netif;
	int idx = -1;
	int i;
	int retval;
	
	if (strncmp(ifname, "ppp", 3))
		return FAILED;

	for (i=0; vlanconfig[i].ifname[0] != '\0'; i++)
	{
		if (vlanconfig[i].isWan && vlanconfig[i].is_slave && !strcmp(vlanconfig[i].ifname, ifname))
		{
			/*QL 2012-3-1 vlanconfg[] has been modified, vid of all ppp entries is set to 0, therefore, ppp0 will not be created in initial time.*/
			//if (0 != vlanconfig[i].vid) {
				idx = i;
				break;
			//}
		}
	}

	if (-1 == idx)
	{
		printk("%s: no vlanconfig entry availble for %s!\n", __func__, ifname);
		return FAILED;
	}

	/*add network interface*/
	memset(&netif, 0, sizeof(rtl865x_netif_t));
	memcpy(netif.name,vlanconfig[idx].ifname,MAX_IFNAMESIZE);
	memcpy(netif.macAddr.octet,vlanconfig[idx].mac.octet,ETHER_ADDR_LEN);
	netif.mtu = vlanconfig[idx].mtu;
	netif.if_type = vlanconfig[idx].if_type;
	netif.vid = vlanconfig[idx].vid;
	netif.is_wan = vlanconfig[idx].isWan;
	netif.is_slave = vlanconfig[idx].is_slave;
	//#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
	netif.enableRoute=1;
	//#endif
	#if defined(CONFIG_RTL_HWNAT_IPv6_SUPPORT)
	netif.enablev6Route=1;
	#endif
	retval = rtl865x_addNetif(&netif);

	return retval;
}
#endif

#if 0
int32 rtl865x_changeOpMode(int mode,int allLan)
{
#if defined (CONFIG_RTL_LOCAL_PUBLIC)
	struct rtl865x_interface_info ifInfo;
#endif

#if defined(CONFIG_RTK_VLAN_SUPPORT)
	if(rtk_vlan_support_enable==0)
	{
		if(mode==rtl865x_curOpMode && allLan==rtl865x_curOpMode_allLan)
		{
			return SUCCESS;
		}
	}
#else
#ifndef CONFIG_RTL_MULTI_ETH_WAN
	if(mode==rtl865x_curOpMode && allLan==rtl865x_curOpMode_allLan)
	{
		printk("(%s)current mode has been ever set to %d (allLan:%d)\n ",__func__,mode,allLan);
		return SUCCESS;
	}
#endif
#endif

	/*config vlan config*/
	//rtl_config_vlanconfig(mode,allLan);

	if (!vlanconfig[0].vid && !vlanconfig[1].vid )
		return RTL_EINVALIDVLANID;

#if defined (CONFIG_RTL_LOCAL_PUBLIC)
	if(mode==GATEWAY_MODE)
	{
		memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
#if defined(CONFIG_RTL_PUBLIC_SSID)
		strcpy(ifInfo.ifname,RTL_GW_WAN_DEVICE_NAME);
#else
		strcpy(ifInfo.ifname, RTL_DRV_WAN0_NETIF_NAME);
#endif
		ifInfo.isWan=1;
		for(i=0;vlanconfig[i].vid!=0; i++)
		{
			if ((TRUE==vlanconfig[i].isWan) && (vlanconfig[i].if_type==IF_ETHER))
			{
				ifInfo.memPort|=vlanconfig[i].memPort;
				ifInfo.fid=vlanconfig[i].fid;
			}
		}
		rtl865x_setLpIfInfo(&ifInfo);

		memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
		strcpy(ifInfo.ifname, RTL_DRV_LAN_NETIF_NAME);
		ifInfo.isWan=0;
		for(i=0;vlanconfig[i].vid!=0;i++)
		{
			if ((FALSE==vlanconfig[i].isWan) && (vlanconfig[i].if_type==IF_ETHER))
			{
				ifInfo.memPort|=vlanconfig[i].memPort;
				ifInfo.fid=vlanconfig[i].fid;
			}
		}
		rtl865x_setLpIfInfo(&ifInfo);
	}
	else if(mode==WISP_MODE)
	{
		memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
		strcpy(ifInfo.ifname, RTL_PS_WLAN0_DEV_NAME);
		ifInfo.isWan=1;
		rtl865x_setLpIfInfo(&ifInfo);

		memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
		strcpy(ifInfo.ifname,RTL_DRV_LAN_NETIF_NAME);
		ifInfo.isWan=0;
		for(i=0;vlanconfig[i].vid!=0;i++)
		{
			if ((FALSE==vlanconfig[i].isWan) && (vlanconfig[i].if_type==IF_ETHER))
			{
				ifInfo.memPort|=vlanconfig[i].memPort;
				ifInfo.fid=vlanconfig[i].fid;
			}
		}
		rtl865x_setLpIfInfo(&ifInfo);
	}
	else 
	{
		memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
		ifInfo.isWan=1;
		rtl865x_setLpIfInfo(&ifInfo);

		memset(&ifInfo, 0 , sizeof(struct rtl865x_interface_info));
		strcpy(ifInfo.ifname, RTL_DRV_LAN_NETIF_NAME);
		ifInfo.isWan=0;
		for(i=0;vlanconfig[i].vid!=0;i++)
		{
			if ((FALSE==vlanconfig[i].isWan) && (vlanconfig[i].if_type==IF_ETHER))
			{
				ifInfo.memPort|=vlanconfig[i].memPort;
				ifInfo.fid=vlanconfig[i].fid;
			}
		}
		rtl865x_setLpIfInfo(&ifInfo);
	}
#endif

#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)
	re865x_packVlanConfig(vlanconfig, packedVlanConfig);
#endif

	/*reinit hw tables*/
	rtl_reinit_hw_table();    

#if defined (CONFIG_RTL_MULTI_LAN_DEV) ||defined(CONFIG_RTK_VLAN_SUPPORT)	
	reinit_vlan_configure(packedVlanConfig);
#else
	reinit_vlan_configure(vlanconfig);
#endif

#if defined (CONFIG_RTL_IGMP_SNOOPING)
	re865x_reInitIgmpSetting(mode);
#if defined (CONFIG_RTL_MLD_SNOOPING)
#if defined(CONFIG_RTK_VLAN_SUPPORT)
	if((mldSnoopEnabled==TRUE)&& (rtk_vlan_support_enable==0))
#else
	if(mldSnoopEnabled==TRUE)
#endif
	{
#if defined (CONFIG_RTL_HARDWARE_NAT)
#if defined (CONFIG_RTL_MULTI_LAN_DEV)
		rtl865x_addAclForMldSnooping(packedVlanConfig);
#else
		rtl865x_addAclForMldSnooping(vlanconfig);
#endif
#endif
	}
#endif
#endif

#if defined (CONFIG_RTL_IVL_SUPPORT)
	if(mode==GATEWAY_MODE)
	{
		WRITE_MEM32( FFCR, READ_MEM32( FFCR ) & ~EN_UNUNICAST_TOCPU );
	}
	else	if((mode==BRIDGE_MODE) ||(mode==WISP_MODE))
	{
		WRITE_MEM32( FFCR, READ_MEM32( FFCR ) | EN_UNUNICAST_TOCPU );
	}
	else
	{
		WRITE_MEM32( FFCR, READ_MEM32( FFCR ) & ~EN_UNUNICAST_TOCPU );
	}
#endif

	/*update current operation mode*/
	rtl865x_curOpMode=mode;
	rtl865x_curOpMode_allLan = allLan;

	//setAsicOperationLayer
#if defined(CONFIG_RTL_HARDWARE_NAT)
	switch(mode)
	{
		case GATEWAY_MODE:
			rtl8651_setAsicOperationLayer(4);
			break;
		case BRIDGE_MODE:
		case WISP_MODE:
			rtl8651_setAsicOperationLayer(3);
			break;
		default:
			rtl8651_setAsicOperationLayer(2);		
	}
#endif

#if 0
#if defined(CONFIG_RTL_LAYERED_DRIVER_L3) && !defined(CONFIG_RTL_MULTI_ETH_WAN)
	//always init the default route...
	if(rtl8651_getAsicOperationLayer() >2)
	{
		rtl865x_addRoute(0,0,0,RTL_DRV_WAN0_NETIF_NAME,0);
	}
#endif
#endif
	//checksum control register
	switch(mode)
	{
		case GATEWAY_MODE:
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L2_CHKSUM_ERR);
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L3_CHKSUM_ERR);
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L4_CHKSUM_ERR);
			break;
		case BRIDGE_MODE:
		case WISP_MODE:
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L2_CHKSUM_ERR);
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)|ALLOW_L3_CHKSUM_ERR);
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)|ALLOW_L4_CHKSUM_ERR);			
			break;			
		default:
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L2_CHKSUM_ERR);
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L3_CHKSUM_ERR);
			WRITE_MEM32(CSCR,READ_MEM32(CSCR)&~ALLOW_L4_CHKSUM_ERR);
	}

	return SUCCESS;
}

static int32 reinit_vlan_configure(struct rtl865x_vlanConfig new_vlanconfig[])
{
//#ifndef CONFIG_RTL_MULTI_ETH_WAN
	
	int32 i;
	//uint32 valid_port_mask = 0;
//#else
	//int32 i, j=0;
//#endif
	struct rtl865x_vlanConfig *pvlanconfig = NULL;
	int32 totalVlans = 0;	
	pvlanconfig = new_vlanconfig;

	/*get input vlan config entry number*/
	for(i=0; pvlanconfig[i].ifname[0] != '\0' ; i++)
	{
		if(pvlanconfig[i].vid != 0)
			totalVlans++;
	}	
	
	//because the new_vlanconfig should be packedVlanConfig
	totalVlans = totalVlans > NETIF_SW_NUMBER? NETIF_SW_NUMBER:totalVlans;

	for(i=0; i<totalVlans; i++)
	{
		rtl865x_netif_t netif;
		
		if(pvlanconfig[i].vid == 0)
			continue;

#if 0
//#ifndef CONFIG_RTL_MULTI_ETH_WAN
		if(pvlanconfig[i].isWan == 1)
		{
			/*wan*/
			valid_port_mask = RTL_WANPORT_MASK;
		}
		else
			valid_port_mask = RTL_LANPORT_MASK | 0x100;
#endif
		/*add vlan*/
		if(pvlanconfig[i].if_type==IF_ETHER)
		{
			rtl865x_addVlan(pvlanconfig[i].vid);
			
#ifdef CONFIG_RTL_MULTI_ETH_WAN
			rtl865x_addVlanPortMember(pvlanconfig[i].vid,pvlanconfig[i].memPort /*& valid_port_mask*/, pvlanconfig[i].untagSet);
#else
			rtl865x_addVlanPortMember(pvlanconfig[i].vid,pvlanconfig[i].memPort /*& valid_port_mask*/);
#endif
			//printk("===> vid %d fid %d\n", pvlanconfig[i].vid, pvlanconfig[i].fid);
			rtl865x_setVlanFilterDatabase(pvlanconfig[i].vid,pvlanconfig[i].fid);
		}

		/*add network interface*/
		memset(&netif, 0, sizeof(rtl865x_netif_t));
		memcpy(netif.name,pvlanconfig[i].ifname,MAX_IFNAMESIZE);
		memcpy(netif.macAddr.octet,pvlanconfig[i].mac.octet,ETHER_ADDR_LEN);
		netif.mtu = pvlanconfig[i].mtu;
		netif.if_type = pvlanconfig[i].if_type;
		netif.vid = pvlanconfig[i].vid;
		netif.is_wan = pvlanconfig[i].isWan;
		netif.is_slave = pvlanconfig[i].is_slave;
		//#if defined (CONFIG_RTL_HARDWARE_MULTICAST)
		netif.enableRoute=1;
		//#endif
		//printk("%s add netif %s\n", __func__, netif.name);
		rtl865x_addNetif(&netif);
		if(netif.is_slave == 1)
#if defined(CONFIG_RTL_PUBLIC_SSID)
			rtl865x_attachMasterNetif(netif.name,RTL_GW_WAN_DEVICE_NAME);
#else
			;//rtl865x_attachMasterNetif(netif.name,RTL_DRV_WAN0_NETIF_NAME);
#endif
#ifdef CONFIG_RTL_MULTI_ETH_WAN
	//#if defined(CONFIG_PROC_FS) && defined(CONFIG_NET_SCHED) && defined(CONFIG_RTL_LAYERED_DRIVER)
	#if 0
		if (0==netif.is_slave)
			memcpy(&netIfName[j++][0], vlanconfig[i].ifname, sizeof(vlanconfig[i].ifname));
#endif
#endif
	}

//#ifdef CONFIG_RTL_MULTI_ETH_WAN
#if 0
	memcpy(netIfNameArray, netIfName, NETIF_NUMBER*IFNAMSIZ);
#endif

#if defined(CONFIG_RTK_VLAN_SUPPORT)
		if(rtk_vlan_support_enable == 1)
			rtl865x_enable_acl(0);
		else 
			rtl865x_enable_acl(1);
#endif

	rtl865x_setpvid(pvlanconfig);
#if 0 /* old version */
#ifndef CONFIG_RTL_MULTI_ETH_WAN
	for(i=0; i<RTL8651_PORT_NUMBER + 3; i++) 
	{ 
		/* Set each port's PVID */
		for(j=0,pvid=0; pvlanconfig[j].vid != 0; j++)
		{
			if ( (1<<i) & pvlanconfig[j].memPort ) 
			{
				pvid = pvlanconfig[j].vid;
				break;
			}
		}
		
		if (pvid!=0)	
		{
			CONFIG_CHECK(rtl8651_setAsicPvid(i,pvid));
			rtl865x_setPortToNetif(pvlanconfig[j].ifname, i);
		}
	}
#else
	for(i=0; i<RTL8651_PORT_NUMBER + 3; i++)
	{
		/* Set each port's PVID */
		for(j=0; pvlanconfig[j].vid != 0; j++)
		{
			if ( pvlanconfig[j].vid == pvid_per_port[i]) 
			{
				break;
			}
		}

		if (pvlanconfig[j].vid) {
			CONFIG_CHECK(rtl8651_setAsicPvid(i,pvid_per_port[i]));
			rtl865x_setPortToNetif(pvlanconfig[j].ifname, i);
		}
	}
#endif
#endif /* old version */

#ifdef CONFIG_RTL_STP
	re865x_stp_mapping_reinit();
#endif

#if defined (CONFIG_RTL_IGMP_SNOOPING) && defined (CONFIG_RTL_HARDWARE_MULTICAST)	
		INIT_CHECK(rtl8651_setAsicOperationLayer(3));
#endif

	return SUCCESS;
}
#endif

#if defined(CONFIG_RTK_VLAN_SUPPORT)
static int32 rtk_vlan_support_read( char *page, char **start, off_t off, int count, int *eof, void *data )
{
	int len;
	len = sprintf(page, "%s %d\n", "rtk_vlan_support_enable:",rtk_vlan_support_enable);

	if (len <= off+count) *eof = 1;
	*start = page + off;
	len -= off;
	if (len>count)
		len = count;
	if (len<0)
	  	len = 0;

	return len;
}

static int32 rtk_vlan_support_write( struct file *filp, const char *buff,unsigned long len, void *data )
{
	char 		tmpbuf[32];	
	int i=0;
	int j=0;
	struct net_device *dev;
	struct dev_priv	  *dp;
	
	if (buff && !copy_from_user(tmpbuf, buff, len))
	{
		tmpbuf[len] = '\0';
		#if defined (CONFIG_RTL_IGMP_SNOOPING) && defined (CONFIG_RTL_MLD_SNOOPING)
		if(mldSnoopEnabled)
		{
			rtl865x_removeAclForMldSnooping(vlanconfig);
		}
		#endif
		if(tmpbuf[0] == '0')
		{
			rtk_vlan_support_enable = 0;
			
			rtl_config_rtkVlan_vlanconfig(rtl865x_curOpMode);
			re865x_packVlanConfig(vlanconfig, packedVlanConfig);	
			rtl_reinit_hw_table();
			reinit_vlan_configure(packedVlanConfig);

			//unknow vlan drop
			REG32(SWTCR0) &= ~(1 << 15);
			
#if defined(CONFIG_RTL_LAYERED_DRIVER_ACL)
			rtl865x_enable_acl(1); //enable acl feature
#endif
		}	
		else if(tmpbuf[0] == '1')
		{
			rtk_vlan_support_enable = 1;				
			
			rtl_config_rtkVlan_vlanconfig(rtl865x_curOpMode);			
			re865x_packVlanConfig(vlanconfig, packedVlanConfig);			
			rtl_reinit_hw_table();
			reinit_vlan_configure(packedVlanConfig);			

			//unknow vid to cpu
			REG32(SWTCR0) |= 1 << 15;
#if defined(CONFIG_RTL_LAYERED_DRIVER_ACL)
			rtl865x_enable_acl(0); //disable acl feature
#endif
		}
#if defined(CONFIG_RTK_VLAN_FOR_CABLE_MODEM)
		else if(tmpbuf[0] == '2')
		{
			rtk_vlan_support_enable = 2;
			//unknow vid to cpu
			REG32(SWTCR0) |= 1 << 15;
		}
#endif
		else
		{
			printk("current support: 0/1/2\n");
			return len;
		}

		/*update dev port number*/
		for(i=0; vlanconfig[i].vid != 0; i++)
		{
			if (IF_ETHER!=vlanconfig[i].if_type)
			{
				continue;
			}
			
			dev=_rtl86xx_dev.dev[i];
			dp = dev->priv;
			dp->portnum  = 0;
			for(j=0;j<RTL8651_AGGREGATOR_NUMBER;j++)
			{
				if(dp->portmask & (1<<j))
					dp->portnum++;
			}	
			
		}
		
	#if defined (CONFIG_RTL_IGMP_SNOOPING)
		re865x_reInitIgmpSetting(rtl865x_curOpMode);
		#if defined (CONFIG_RTL_MLD_SNOOPING)
		if(mldSnoopEnabled && (rtk_vlan_support_enable==0))
		{			
			re865x_packVlanConfig(vlanconfig, packedVlanConfig);
			#ifdef CONFIG_RTL_HARDWARE_NAT
			rtl865x_addAclForMldSnooping(packedVlanConfig);
			#endif
		}
		#endif
	#endif
#ifndef CONFIG_RTL_MULTI_ETH_WAN
		//always init the default route...
		if(rtl8651_getAsicOperationLayer() >2)
		{
			rtl865x_addRoute(0,0,0,RTL_DRV_WAN0_NETIF_NAME,0);
		}
#endif
	}
	return len;
}

static int read_proc_vlan(char *page, char **start, off_t off,
        int count, int *eof, void *data)
{

    struct net_device *dev = (struct net_device *)data;
    struct dev_priv *cp;
    int len;

	cp = dev->priv;
    len = sprintf(page, "gvlan=%d, lan=%d, vlan=%d, tag=%d, vid=%d, priority=%d, cfi=%d\n", 
		cp->vlan_setting.global_vlan, cp->vlan_setting.is_lan, cp->vlan_setting.vlan, cp->vlan_setting.tag, 
		cp->vlan_setting.id, cp->vlan_setting.pri, cp->vlan_setting.cfi);
			
    if (len <= off+count) 
        *eof = 1;      
    *start = page + off;      
    len -= off;      
    if (len > count) 
        len = count;      
    if (len < 0) 
        len = 0;      
    return len; 
}

static int write_proc_vlan(struct file *file, const char *buffer,
              unsigned long count, void *data)
{
	struct net_device *dev = (struct net_device *)data;
	struct dev_priv *cp;
	#define	VLAN_MAX_INPUT_LEN	128
	char *tmp;
	
	tmp = kmalloc(VLAN_MAX_INPUT_LEN, GFP_KERNEL);
	if (count < 2 || tmp==NULL)
	{
		if(tmp)
			kfree(tmp);
		return -EFAULT;
	}

	cp = dev->priv;
	if(rtk_vlan_support_enable == 0)
		goto out;
	if (buffer && !copy_from_user(tmp, buffer, VLAN_MAX_INPUT_LEN))
	{
		int num = sscanf(tmp, "%d %d %d %d %d %d %d", 
			&cp->vlan_setting.global_vlan, &cp->vlan_setting.is_lan, 
			&cp->vlan_setting.vlan, &cp->vlan_setting.tag, 
			&cp->vlan_setting.id, &cp->vlan_setting.pri, 
			&cp->vlan_setting.cfi);
		
		if (num !=  7) {
			printk("invalid vlan parameter!\n");
			goto out;
		}
		#if 0
		printk("===%s(%d), cp->name(%s),global_vlan(%d),is_lan(%d),vlan(%d),tag(%d),id(%d),pri(%d),cfi(%d)",__FUNCTION__,__LINE__,
			cp->dev->name,cp->vlan_setting.global_vlan,cp->vlan_setting.is_lan,cp->vlan_setting.vlan,cp->vlan_setting.tag,
			cp->vlan_setting.id,cp->vlan_setting.pri,cp->vlan_setting.cfi);
		printk("-------------%s(%d),cp->portmask(%d)\n",__FUNCTION__,__LINE__,cp->portmask);
		#endif			
	}
out:
	if(tmp)
		kfree(tmp);
	
	return count;	
}
#endif // CONFIG_RTK_VLAN_SUPPORT

#if (defined(CONFIG_RTL_CUSTOM_PASSTHRU) && !defined(CONFIG_RTL8196_RTL8366)) 

static unsigned long atoi_dec(char *s)
{
	unsigned long k = 0;

	k = 0;
	while (*s != '\0' && *s >= '0' && *s <= '9') {
		k = 10 * k + (*s - '0');
		s++;
	}
	return k;
}

static int custom_Passthru_read_proc(char *page, char **start, off_t off,
		     int count, int *eof, void *data)
{
	int len;	
	len = sprintf(page, "%s\n", passThru_flag);
	if (len <= off+count) 
		*eof = 1;

	*start = page + off;
	len -= off;

	if (len>count) 
		len = count;

	if (len<0) len = 0;

	return len;
}

static int custom_createPseudoDevForPassThru(void)
{
		struct net_device *dev, *wanDev;
		struct dev_priv *dp;
		int rc, i;
		unsigned long		flags;

		wanDev = NULL;
		/*	find wan device first	*/
		for(i=0;i<_rtl86xx_dev_num;i++)
		{
			dp = _rtl86xx_dev.dev[i]->priv;
			if (rtl_isWanDev(dp)==TRUE)
			{
				wanDev = _rtl86xx_dev.dev[i];
				break;
			}
		}

		/*	can't find any wan device, just return	*/
		if (wanDev==NULL)
			return -1;
		
		dev = alloc_etherdev(sizeof(struct dev_priv));
		if (!dev){
			rtlglue_printf("failed to allocate passthru pseudo dev.\n");
			return -1;
		}
		strcpy(dev->name, "peth%d");
		/*	default set lan side mac		*/
		memcpy((char*)dev->dev_addr,(char*)(&(vlanconfig[0].mac)),ETHER_ADDR_LEN);
		dp = dev->priv;
		memset(dp,0,sizeof(*dp));
		dp->dev = wanDev;
#if defined(CONFIG_COMPAT_NET_DEV_OPS)
		dev->open = re865x_pseudo_open;
		dev->stop = re865x_pseudo_close;
		dev->set_multicast_list = NULL;
/*romedriver Boyce 2014-07-09*/
#if CONFIG_XDSL_NEW_HWNAT_DRIVER
		dev->hard_start_xmit = re865x_start_xmit_check;
#else
		dev->hard_start_xmit = re865x_start_xmit;
#endif
		dev->get_stats = re865x_get_stats;
		dev->do_ioctl = re865x_ioctl;
#ifdef CP_VLAN_TAG_USED
		dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
		dev->vlan_rx_register = cp_vlan_rx_register;
		dev->vlan_rx_kill_vid = cp_vlan_rx_kill_vid;
#endif

#else
		dev->netdev_ops = &rtl819x_pseudodev_ops;
#endif

#ifdef CP_VLAN_TAG_USED
		dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;		
#endif
		dev->watchdog_timeo = TX_TIMEOUT;
		
		dev->irq = 0;
		rc = register_netdev(dev);
		if(!rc){
			spin_lock_irqsave (&_rtl86xx_dev.lock, flags);
			_rtl86xx_dev.pdev = dev;
			spin_unlock_irqrestore (&_rtl86xx_dev.lock, flags);
			rtlglue_printf("[%s] added, mapping to [%s]...\n", dev->name, dp->dev->name);
		}else
			rtlglue_printf("Failed to allocate [%s]\n", dev->name);

		return 0;
}

static int custom_Passthru_write_proc(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{
	int flag,i;

#if	!defined(CONFIG_RTL_LAYERED_DRIVER)
	struct rtl865x_vlanConfig  passThruVlanConf = {"passThru",0,IF_ETHER,PASSTHRU_VLAN_ID,0,
		rtl865x_lanPortMask|rtl865x_wanPortMask,
		rtl865x_lanPortMask|rtl865x_wanPortMask,
		1500,{ { 0x00, 0x12, 0x34, 0x56, 0x78, 0x90 } } };
#endif

	if (buffer && !copy_from_user(&passThru_flag, buffer, count))
	{			
		flag=(int)atoi_dec(passThru_flag);		

		if(flag ^ oldStatus)
		{
			//IPv6 PassThru
			if((flag & IP6_PASSTHRU_MASK) ^ (oldStatus & IP6_PASSTHRU_MASK)) 
			{			
				if(flag & IP6_PASSTHRU_MASK)
				{//add		
					for(i=0; i<RTL865XC_PORT_NUMBER; i++)
					{
						rtl8651_setProtocolBasedVLAN(IP6_PASSTHRU_RULEID, i, TRUE, PASSTHRU_VLAN_ID);
					}				
				}
				else
				{//delete
					for(i=0; i<RTL865XC_PORT_NUMBER; i++)
					{
						rtl8651_setProtocolBasedVLAN(IP6_PASSTHRU_RULEID, i, FALSE, PASSTHRU_VLAN_ID);
					}
				}
			}

			//PPPoE PassThru
			if((flag & PPPOE_PASSTHRU_MASK) ^ (oldStatus & PPPOE_PASSTHRU_MASK)) 
			{			
				if(flag & PPPOE_PASSTHRU_MASK)
				{//add				
					for(i=0; i<RTL865XC_PORT_NUMBER; i++)
					{
						rtl8651_setProtocolBasedVLAN(RTL8651_PBV_RULE_PPPOE_CONTROL, i, TRUE, PASSTHRU_VLAN_ID);
						rtl8651_setProtocolBasedVLAN(RTL8651_PBV_RULE_PPPOE_SESSION, i, TRUE, PASSTHRU_VLAN_ID);
					}				
				}
				else
				{//delete
					for(i=0; i<RTL865XC_PORT_NUMBER; i++)
					{
						rtl8651_setProtocolBasedVLAN(RTL8651_PBV_RULE_PPPOE_CONTROL, i, FALSE, PASSTHRU_VLAN_ID);
						rtl8651_setProtocolBasedVLAN(RTL8651_PBV_RULE_PPPOE_SESSION, i, FALSE, PASSTHRU_VLAN_ID);
					}
				}
			}
			
		}

		//passthru vlan
		if(flag==0)
		{
			//To del passthru vlan
			//Do nothing here because change_op_mode reinit vlan already
		}
		else
            {
            		//To add passthru vlan
			rtl865x_addVlan(PASSTHRU_VLAN_ID);

			#if defined(CONFIG_POCKET_ROUTER_SUPPORT)
			rtl865x_addVlanPortMember(PASSTHRU_VLAN_ID, rtl865x_lanPortMask|rtl865x_wanPortMask|0x100, rtl865x_lanPortMask|rtl865x_wanPortMask|0x100);
			#else
			rtl865x_addVlanPortMember(PASSTHRU_VLAN_ID, rtl865x_lanPortMask|rtl865x_wanPortMask, rtl865x_lanPortMask|rtl865x_wanPortMask);
			#endif

            }

		oldStatus=flag;
		return count;
	}
	return -EFAULT;
}

int32 rtl8651_customPassthru_init(void)
{
	oldStatus=0;
	res = create_proc_entry("custom_Passthru", 0, NULL);	
	if(res)
	{
		res->read_proc = custom_Passthru_read_proc;
		res->write_proc = custom_Passthru_write_proc;
	}
	rtl8651_defineProtocolBasedVLAN( IP6_PASSTHRU_RULEID, 0x0, 0x86DD );
	custom_createPseudoDevForPassThru();
	return 0;
}

void __exit rtl8651_customPassthru_exit(void)
{
	if (res) {
		remove_proc_entry("custom_Passthru", res);				
		res = NULL;
	}
}
#define	MULTICAST_STORM_CONTROL	1
#define	BROADCAST_STORM_CONTROL	2

static struct proc_dir_entry *stormCtrlProc=NULL;
static int rtl865x_stormCtrlReadProc(char *page, char **start, off_t off,
		     int count, int *eof, void *data)
{
	int len=0;	
	if (len <= off+count) 
		*eof = 1;

	*start = page + off;
	len -= off;

	if (len>count) 
		len = count;

	if (len<0) len = 0;

	return len;
}

static int rtl865x_stormCtrlWriteProc(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{
	uint32 tmpBuf[32];
	uint32 stormCtrlType=MULTICAST_STORM_CONTROL;
	uint32 enableStormCtrl=FALSE;
	uint32 percentage=0;
	uint32 uintVal;

	if (buffer && !copy_from_user(tmpBuf, buffer, count))
	{
		tmpBuf[count-1]=0;
		uintVal=simple_strtol((const char *)tmpBuf, NULL, 0);
		//printk("%s(%d),tmpBuf=%s,count=%d,uintVal=%d\n",__FUNCTION__,__LINE__,
		//	tmpBuf,count,uintVal);//Added for test
		if(uintVal>100)
		{
			enableStormCtrl=FALSE;
			percentage=0;
		}
		else
		{
			enableStormCtrl=TRUE;
			percentage=uintVal;
		}
		rtl865x_setStormControl(stormCtrlType,enableStormCtrl,percentage);
		return count;
	}
	return -EFAULT;
}

int32 rtl8651_initStormCtrl(void)
{
	stormCtrlProc = create_proc_entry("StormCtrl", 0, NULL);	
	if(stormCtrlProc)
	{
		stormCtrlProc->read_proc = rtl865x_stormCtrlReadProc;
		stormCtrlProc->write_proc = rtl865x_stormCtrlWriteProc;
	}
	
	return 0;
}

void __exit rtl8651_exitStormCtrl(void)
{
	if (stormCtrlProc) {
		remove_proc_entry("StormCtrl", stormCtrlProc);				
		stormCtrlProc = NULL;
	}
}

#endif

#if defined(CONFIG_RTL_8198)
static int32 proc_phyTest_read( char *page, char **start, off_t off, int count, int *eof, void *data )
{
	return 0;
}

static int32 proc_phyTest_write( struct file *filp, const char *buff,unsigned long len1, void *data )
{
	char 		tmpbuf[64];
	uint32	phyId=0, regId=0,iNo=0,iPort=0,len=0,regData=0;
	char		*strptr, *cmd_addr;
	char		*tokptr;
	int32	i=0,port=0;
	int32 	ret=FAILED;
	if (buff && !copy_from_user(tmpbuf, buff, len1)) {
		tmpbuf[len1-1] = '\0';
		strptr=tmpbuf;
		cmd_addr = strsep(&strptr," ");
		if (cmd_addr==NULL)
		{
			goto errout;
		}
	
		if (!memcmp(cmd_addr, "read", 4))
		{

			tokptr = strsep(&strptr," ");
			if (tokptr==NULL)
			{
				goto errout;
			}
			regId=simple_strtol(tokptr, NULL, 0);
			
			ret=rtl8651_getAsicEthernetPHYReg(phyId, regId, &regData); 
			if(ret==SUCCESS)
			{
				printk("read phyId(%d), regId(%d),regData:0x%x\n", phyId, regId, regData);
			}
			else
			{
				printk("error input!\n");
//				goto errout;
			}
		}
		else if (!memcmp(cmd_addr, "test", 4))
		{
			printk("\r\n");
			tokptr = strsep(&strptr," ");
			if (tokptr==NULL)
			{
				goto errout;
			}
			iNo=simple_strtol(tokptr, NULL, 0);
			if(iNo==1)//test mode 1
			{
				
				unsigned int default_val_t1[]={
				4,0x1f,0x0002,
				4,0x13,0xAA00,
				4,0x14,0xAA00,
				4,0x15,0xAA00,
				4,0x16,0xFA00,
				4,0x17,0xAF00
				};
				printk("PHY Test 1 Mode: No\n");
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) |= (EnForceMode);
				len=sizeof(default_val_t1)/sizeof(unsigned int);
				
				for(i=0;i<len;i=i+3) 
				{	                   
				port=default_val_t1[i];                                                    
				rtl8651_setAsicEthernetPHYReg(port, default_val_t1[i+1], default_val_t1[i+2]);
				}
				for(i=0; i<5; i++)
				{
					rtl8651_setAsicEthernetPHYReg(i, 0x1f, 0x0000);
					rtl8651_setAsicEthernetPHYReg(i, 0x09, 0x2E00);
				}		
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) &= ~(EnForceMode);
				printk("Please reset the target after leaving Test Mode\n");
				ret=SUCCESS;
			}
	#if 0
			else if(iNo==2)//test mode 2
			{
				
				unsigned int default_val_t2[]={
				1, 0x1f, 0x0002,
				1, 0x11, 0x5E00,
				1, 0x1f, 0x0000,            
				4, 0x1f, 0x0002,
				4, 0x11, 0x5E00,
				4, 0x1f, 0x0000

				};
				printk("PHY Test 2 Mode: No\n");	
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) |= (EnForceMode);
				len=sizeof(default_val_t2)/sizeof(unsigned int);
				
				for(i=0;i<len;i=i+3) 
				{
				port=default_val_t2[i];                                                    
				rtl8651_setAsicEthernetPHYReg(port, default_val_t2[i+1], default_val_t2[i+2]);
				}
				for(i=0; i<5; i++)
				{
					rtl8651_setAsicEthernetPHYReg(i, 0x1f, 0x0000);
					rtl8651_setAsicEthernetPHYReg(i, 0x09, 0x4E00);
				}		
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) &= ~(EnForceMode);
				printk("Please reset the target after leaving Test Mode\n");
				ret=SUCCESS;
			}
			else if(iNo==3)//test mode 2
			{
				unsigned int default_val_t3[]={
				1, 0x1f, 0x0002,
				1, 0x11, 0x4000,
				};
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) |= (EnForceMode);
				
				printk("PHY Test 3 Mode: No  Port{0~4} Channel{A,B,C,D}\n");
				tokptr = strsep(&strptr," ");
				if (tokptr==NULL)
				{
					goto errout;
				}
				iPort=simple_strtol(tokptr, NULL, 0);
				tokptr = strsep(&strptr," ");
				if (tokptr==NULL)
				{
					goto errout;
				}
				len=sizeof(default_val_t3)/sizeof(unsigned int);
				for(i=0;i<len;i=i+3) 
				{
					port=default_val_t3[i];                                                    
					rtl8651_setAsicEthernetPHYReg(port, default_val_t3[i+1], default_val_t3[i+2]);
				}
			
				switch(*tokptr)
				{
					
					case 'A':
						rtl8651_setAsicEthernetPHYReg(1, 0x10, 0x1100);
						rtl8651_setAsicEthernetPHYReg(1, 0x1f, 0x0000);
						printk("A channel\n");
						
					break;
					case 'B':
						rtl8651_setAsicEthernetPHYReg(1, 0x10, 0x1300);
						rtl8651_setAsicEthernetPHYReg(1, 0x1f, 0x0000);
						printk("B channel\n");
					break;
					case 'C':
						rtl8651_setAsicEthernetPHYReg(1, 0x10, 0x1500);
						rtl8651_setAsicEthernetPHYReg(1, 0x1f, 0x0000);
						printk("C channel\n");
					break;
					case 'D':
						rtl8651_setAsicEthernetPHYReg(1, 0x10, 0x1700);
						rtl8651_setAsicEthernetPHYReg(1, 0x1f, 0x0000);
						printk("D channel\n");
					break;
				}
				rtl8651_setAsicEthernetPHYReg(iPort, 0x1f, 0x0000);
				rtl8651_setAsicEthernetPHYReg(iPort, 0x09, 0x6e00);
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) &= ~(EnForceMode);
				printk("Please reset the target after leaving Test Mode\n");
				ret=SUCCESS;
			}
	#endif
			else if(iNo==4)//test mode 2
			{
				unsigned int default_val_t4[]={
				0, 0x1f, 0x0002,
				0, 0x07, 0x3678,
				0, 0x1f, 0x0000,
				0, 0x09, 0x8e00
				};
				
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) |= (EnForceMode);
				
				len=sizeof(default_val_t4)/sizeof(unsigned int);
				
				tokptr = strsep(&strptr," ");
				if (tokptr==NULL)
				{
					goto errout;
				}
				iPort=simple_strtol(tokptr, NULL, 0);
                                printk("PHY Test 4 Mode: No:%d  Port:%d \n",iNo,iPort);
				for(i=0;i<len;i=i+3) 
				{                                                 
					rtl8651_setAsicEthernetPHYReg(iPort, default_val_t4[i+1], default_val_t4[i+2]);
				}
								
				for(i=0; i<5; i++)
				REG32(PCRP0+i*4) &= ~(EnForceMode);
				printk("Please reset the target after leaving Test Mode\n");
				ret=SUCCESS;
			}
			
#if 0
			tokptr = strsep(&strptr," ");
			if (tokptr==NULL)
			{
				goto errout;
			}
			regId=simple_strtol(tokptr, NULL, 0);

			tokptr = strsep(&strptr," ");
			if (tokptr==NULL)
			{
				goto errout;
			}
			regData=simple_strtol(tokptr, NULL, 0);
		
			ret=rtl8651_setAsicEthernetPHYReg(phyId, regId, regData);
#endif
			if(ret==SUCCESS)
			{
				//printk("Write phyId(%d), regId(%d), regData:0x%x\n", phyId, regId, regData);
			}
			else
			{
				printk("error input!\n");
//				goto errout;
			}
		}
		else
		{
			goto errout;
		}

		return len1;

errout:
		printk("\nTest  format:	\"Test 1~4\"\n");
		printk("PHY Test 1 Mode: No\n");
		//printk("PHY Test 2 Mode: No\n");
		//printk("PHY Test 3 Mode: No  Port{0~4} Channel{A,B,C,D}\n");
		printk("PHY Test 4 Mode: No  Port{0~4} \n");

		return len1;
	}

	return -EFAULT;
}

int32 phyRegTest_init(void)
{
	phyTest_entry= create_proc_entry("phyRegTest", 0, NULL);
	if(phyTest_entry != NULL)
	{
		phyTest_entry->read_proc = proc_phyTest_read;
		phyTest_entry->write_proc = proc_phyTest_write;
	}
	
	return 0;
}

void __exit phyRegTest_exit(void)
{
	if (phyTest_entry)
	{
		remove_proc_entry("phyTest_entry", phyTest_entry);
		phyTest_entry = NULL;
	}
}
#endif

#if defined (CONFIG_RTL_MLD_SNOOPING) && !defined (CONFIG_RTL_HW_NAPT)
void rtl8676_addAclForMldSnooping(void)
{
	rtl865x_addAclForMldSnooping(vlanconfig);
}

void rtl8676_removeAclForMldSnooping(void)
{
	rtl865x_removeAclForMldSnooping(vlanconfig);
}
#endif

#if 0// defined(CONFIG_RTL_MLD_SNOOPING)

static struct proc_dir_entry *mldSnoopingProc=NULL;

static int rtl865x_mldSnoopingReadProc(char *page, char **start, off_t off,
		     int count, int *eof, void *data)
{
	int len;	
	len = sprintf(page, "%s\n", (mldSnoopEnabled==TRUE)?"enable":"disable");
	if (len <= off+count) 
		*eof = 1;

	*start = page + off;
	len -= off;

	if (len>count) 
		len = count;

	if (len<0) len = 0;

	return len;
}

static int rtl865x_mldSnoopingWriteProc(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{
	uint32 tmpBuf[32];
	uint32 uintVal;

	if (buffer && !copy_from_user(tmpBuf, buffer, count))
	{
		tmpBuf[count-1]=0;
		uintVal=simple_strtol((const char *)tmpBuf, NULL, 0);
		if(uintVal!=0)
		{
			if(mldSnoopEnabled==FALSE)
			{
				#ifdef CONFIG_RTL_LAYERED_DRIVER_ACL
				rtl865x_removeAclForMldSnooping(vlanconfig);
				rtl865x_addAclForMldSnooping(vlanconfig);
				#endif
				mldSnoopEnabled=TRUE;
			}
		}
		else
		{
			if(mldSnoopEnabled==TRUE)
			{
				#ifdef CONFIG_RTL_LAYERED_DRIVER_ACL
				rtl865x_removeAclForMldSnooping(vlanconfig);
				#endif
				mldSnoopEnabled=FALSE;
				rtl_flushAllIgmpRecord();	// Mason Yu.
			}
		}
		
		return count;
	}
	return -EFAULT;
}

int32 rtl8651_initMldSnooping(void)
{
	mldSnoopingProc = create_proc_entry("br_mldsnoop", 0, NULL);	
	if(mldSnoopingProc)
	{
		mldSnoopingProc->read_proc = rtl865x_mldSnoopingReadProc;
		mldSnoopingProc->write_proc = rtl865x_mldSnoopingWriteProc;
	}
	 mldSnoopEnabled=FALSE;
	return 0;
}

void __exit rtl8651_exitMldSnoopingCtrl(void)
{
	if (mldSnoopingProc) {
		remove_proc_entry("br_mldsnoop", mldSnoopingProc);				
		mldSnoopingProc = NULL;
	}
}

#endif

#if defined (CONFIG_RTL_PHY_POWER_CTRL)
static struct proc_dir_entry *phyPowerCtrlProc=NULL;
#define PHY_POWER_OFF 0
#define PHY_POWER_ON 1
static int rtl865x_setPhyPowerOff(unsigned int port)
{
	unsigned int offset = port * 4;
	unsigned int pcr = 0;
	unsigned int regData;	
	if(port >=RTL8651_PHY_NUMBER)
	{
		return -1;
	}
	
	/*must set mac force link down first*/	
	pcr=READ_MEM32( PCRP0 + offset );
	pcr |= EnForceMode;
	pcr &= ~ForceLink;	
	WRITE_MEM32( PCRP0 + offset, pcr );
	TOGGLE_BIT_IN_REG_TWICE(PCRP0 + offset,EnForceMode);
	
	rtl8651_getAsicEthernetPHYReg(port, 0, &regData);
	regData=regData |(1<<11);
	rtl8651_setAsicEthernetPHYReg(port, 0, regData);

	return 0;
	
}

static int rtl865x_setPhyPowerOn(unsigned int port)
{	
	unsigned int  offset = port * 4;
	unsigned int pcr = 0;

	unsigned int regData;	
	
	if(port >=RTL8651_PHY_NUMBER)
	{
		return -1;
	}
	
	pcr=READ_MEM32( PCRP0 + offset );
	pcr |= EnForceMode;

	WRITE_MEM32( PCRP0 + offset, pcr );
	TOGGLE_BIT_IN_REG_TWICE(PCRP0 + offset,EnForceMode);
	
	rtl8651_getAsicEthernetPHYReg(port, 0, &regData);
	regData=regData &(~(1<<11));
	rtl8651_setAsicEthernetPHYReg(port, 0, regData);

	pcr &=~EnForceMode;
	WRITE_MEM32( PCRP0 + offset, pcr);
	TOGGLE_BIT_IN_REG_TWICE(PCRP0 + offset,EnForceMode);
	return 0;
	
}

static int rtl865x_phyPowerCtrlReadProc(char *page, char **start, off_t off,
		     int count, int *eof, void *data)
{
	int len=0;	
	int port;
	unsigned int regData;	
	for(port=0;port<RTL8651_PHY_NUMBER;port++)
	{
		rtl8651_getAsicEthernetPHYReg(port, 0, &regData);
		if(regData & (1<<11))
		{
			len += sprintf(page+len, "port[%d] phy is power off\n",port );
		}
		else
		{
			len += sprintf(page+len, "port[%d] phy is power on\n",port );
		}
	}
	
	if (len <= off+count) 
		*eof = 1;

	*start = page + off;
	len -= off;

	if (len>count) 
		len = count;

	if (len<0) len = 0;

	return len;
}

static int rtl865x_phyPowerCtrlWriteProc(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{
	char tmpBuf[256];
	char		*strptr;
	char		*tokptr; 
	unsigned int  portMask;
	unsigned int action;
	int i;
	if (buffer && !copy_from_user(tmpBuf, buffer, count))
	{
		tmpBuf[count-1]=0;
		strptr=tmpBuf;
		
		tokptr = strsep(&strptr," ");
		if (tokptr==NULL)
		{
			goto errOut;
		}
		portMask=simple_strtol(tokptr, NULL, 0);
		
		tokptr = strsep(&strptr," ");
		if (tokptr==NULL)
		{
			goto errOut;
		}
		action=simple_strtol(tokptr, NULL, 0);
		
		for(i=0;i<RTL8651_PHY_NUMBER;i++)
		{
			if((1<<i) &portMask)
			{
				if(action==PHY_POWER_OFF)
				{
					rtl865x_setPhyPowerOff(i);
				}
				else if (action==PHY_POWER_ON)
				{
					rtl865x_setPhyPowerOn(i);
				}
			}
			
		}
		
		return count;
	}

errOut:	
	return -EFAULT;
}

static int32 rtl865x_initPhyPowerCtrl(void)
{
	phyPowerCtrlProc = create_proc_entry("phyPower", 0, NULL);	
	if(phyPowerCtrlProc)
	{
		phyPowerCtrlProc->read_proc = rtl865x_phyPowerCtrlReadProc;
		phyPowerCtrlProc->write_proc = rtl865x_phyPowerCtrlWriteProc;
	}

	return 0;
}

void __exit rtl865x_exitPhyPowerCtrl(void)
{
	if (phyPowerCtrlProc) {
		remove_proc_entry("phyPower", phyPowerCtrlProc);				
		phyPowerCtrlProc = NULL;
	}
}

#endif

#if defined(CONFIG_RTL_LINKSTATE)

static struct proc_dir_entry *portStateProc=NULL;
static unsigned int linkUpTime[RTL8651_PHY_NUMBER] = {0,0,0,0,0};

static void init_linkup_time(void)
{
	init_timer(&s_timer);
	s_timer.function = &linkup_time_handle;
	s_timer.expires = jiffies + HZ;
	add_timer(&s_timer);
}

static void linkup_time_handle(unsigned long arg)
{
	int port;
	uint32	regData;
	uint32	data0;
	mod_timer(&s_timer,jiffies +HZ);
	for(port=PHY0;port<PHY5;port++)
	{
		regData = READ_MEM32(PSRP0+((port)<<2));
		data0 = regData & PortStatusLinkUp;
		if (data0)
		{
			linkUpTime[port]++;
		}		
		else
		{
			linkUpTime[port]=0;
		}
	}
}

static int32 port_state_read_proc( char *page, char **start, off_t off, int count, int *eof, void *data )
{
	int		len;
	uint32	regData;
	uint32	data0;
	int		port;

	len = sprintf(page, "Port Link State:\n");

	for(port=PHY0;port<PHY5;port++)
	{
		regData = READ_MEM32(PSRP0+((port)<<2));

		//if (port==CPU)
			//len += sprintf(page+len, "CPUPort ");
		//else
		len += sprintf(page+len, "Port[%d]:", port);
		data0 = regData & PortStatusLinkUp;
		
		if (data0)
		{
			len += sprintf(page+len, "LinkUp|");
		}
		else
		{
			len += sprintf(page+len, "LinkDown\n");
			continue;
		}
		//data0 = regData & PortStatusNWayEnable;
		//len += sprintf(page+len, "NWay Mode %s\n", data0?"Enabled":"Disabled");
		data0 = regData & PortStatusRXPAUSE;
		len += sprintf(page+len, "RXPause:%s|", data0?"Enable":"Disable");
		data0 = regData & PortStatusTXPAUSE;
		len += sprintf(page+len, "TXPause:%s|", data0?"Enable":"Disable");
		data0 = regData & PortStatusDuplex;
		len += sprintf(page+len, "Duplex:%s|", data0?"Enable":"Disable");
		data0 = (regData&PortStatusLinkSpeed_MASK)>>PortStatusLinkSpeed_OFFSET;
		len += sprintf(page+len, "Speed:%s|", data0==PortStatusLinkSpeed100M?"100M":
			(data0==PortStatusLinkSpeed1000M?"1G":
				(data0==PortStatusLinkSpeed10M?"10M":"Unkown")));
		len += sprintf(page+len, "UpTime:%ds\n", linkUpTime[port]);
	}
	
	return len;
}

static int32 port_state_write_proc( struct file *filp, const char *buff,unsigned long len, void *data )
{
	return len;
}

static int32 initPortStateCtrl(void)
{
	portStateProc = create_proc_entry("portState", 0, NULL);	
	if(portStateProc)
	{
		portStateProc->read_proc = port_state_read_proc;
		portStateProc->write_proc = port_state_write_proc;
	}
	memset(linkUpTime,0,sizeof(linkUpTime));
	init_linkup_time();
	
	return 0;
}

static void  exitPortStateCtrl(void)
{
	if (portStateProc) {
		remove_proc_entry("portState", portStateProc);				
		portStateProc = NULL;
	}
	del_timer(&s_timer);
}	
#endif	

#if defined(CONFIG_ETHWAN_MODE_SWITCH)
static int prev_ethwan_port=-1;

struct net_device *get_landev_by_default_port(int port_id)
{
	int i, j;
	char lan_devname[IFNAMSIZ];
	uint32 target_memPort;
	
	target_memPort = (1<<port_id);
	#if 0
	for (i=VCONFIG_LAN_START; i<=VCONFIG_LAN_END; i++) {
		printk("vlanconfig[%d].memPort=0x%x\n", i, vlanconfig[i].memPort);
	}
	#endif
	for (i=VCONFIG_LAN_START; i<=VCONFIG_LAN_END; i++) {
		if (itf_default_mbr[i].memPort == target_memPort) {
			sprintf(lan_devname, "%s%d",ALIASNAME_ELAN_PREFIX,i+ORIGINATE_NUM);
			//printk("lan_devname = %s\n", lan_devname);
			for (j=0; j<_rtl86xx_dev_num; j++) {
				if (strcmp(_rtl86xx_dev.dev[j]->name, lan_devname)==0)
					return _rtl86xx_dev.dev[j];
			}
		}
	}
	return 0;
}

static int do_etherwan_mode(void)
{
	struct net_device *lan_dev=NULL;
	int i;
	struct dev_priv	*cp;
	
	//printk("%s: ethwan_mode=%d ethwan_port=%d prev_ethwan_port=%d\n", __FUNCTION__, ethwan_mode, ethwan_port, prev_ethwan_port);
	if (!ethwan_mode){  //as LAN interface
		if (prev_ethwan_port >= 0) {
			lan_dev = get_landev_by_default_port(prev_ethwan_port);
			if (lan_dev == NULL) {
				printk("Cannot find lan device by port %d\n", prev_ethwan_port);
				return -EFAULT;
			}
			
			printk("enable %s:\n",lan_dev->name);
			// check link state
			if((1 << prev_ethwan_port) & newLinkPortMask){
				netif_carrier_on(lan_dev);
			}else{
				netif_carrier_off(lan_dev);
			}
		}
		
		for (i = 0; i < _rtl86xx_dev_num; i++) {
			#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
			cp = netdev_priv(_rtl86xx_dev.dev[i]);
			#else
			cp = _rtl86xx_dev.dev[i]->priv;
			#endif
			if (strcmp(_rtl86xx_dev.dev[i]->name, ALIASNAME_NAS0)==0){
				//disable portmask
				cp->portmask = 0x00;
				
				printk("disable %s:\n", ALIASNAME_NAS0);
				netif_carrier_off(_rtl86xx_dev.dev[i]);
			}
			
			if (lan_dev && strcmp(_rtl86xx_dev.dev[i]->name, lan_dev->name)==0){
				cp->portmask = (1<<prev_ethwan_port);
				//ethwan port id index change to lanif id index
				portid_index[prev_ethwan_port] = i;
			}
		}
		
		re865x_restart_ether_port();
		prev_ethwan_port = -1;

	} else {	//as WAN interface

		printk("lan port %d change to ethwan port\n", ethwan_port);

		// Reset prev_ethwan_port as Lan device
		if (prev_ethwan_port!=ethwan_port && prev_ethwan_port >= 0) {
			lan_dev = get_landev_by_default_port(prev_ethwan_port);
			if (lan_dev == NULL) {
				printk("Cannot find lan device by port %d\n", prev_ethwan_port);
				return -EFAULT;
			}
			printk("enable previous port %s\n",lan_dev->name);
          		// check link state
			if((1 << prev_ethwan_port) & newLinkPortMask){
				netif_carrier_on(lan_dev);
			}else{
				netif_carrier_off(lan_dev);
			}
			
			for (i = 0; i < _rtl86xx_dev_num; i++) {
				//cp = ((struct dev_priv *)_rtl86xx_dev.dev[i]->priv);
				#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
				cp = netdev_priv(_rtl86xx_dev.dev[i]);
				#else
				cp = _rtl86xx_dev.dev[i]->priv;
				#endif
                	
				if (strcmp(_rtl86xx_dev.dev[i]->name, lan_dev->name)==0){
					cp->portmask = (1<<prev_ethwan_port);
					//ethwan port id index change to lanif id index
					portid_index[prev_ethwan_port] = i;
				}
			}
		}
		
		// Disable ethwan_port mapped Lan device and set this port as Wan device
		lan_dev = get_landev_by_default_port(ethwan_port);
		if (lan_dev == NULL) {
			printk("Cannot find lan device by port %d\n", ethwan_port);
			return -EFAULT;
		}
		printk("disable %s\n",lan_dev->name);
		netif_carrier_off(lan_dev);
		
		printk("enable %s:\n", ALIASNAME_NAS0);
		for (i = 0; i < _rtl86xx_dev_num; i++) {
			#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
			cp = netdev_priv(_rtl86xx_dev.dev[i]);
			#else
			cp = _rtl86xx_dev.dev[i]->priv;
			#endif
			if (strcmp(_rtl86xx_dev.dev[i]->name, ALIASNAME_NAS0)==0){
				// check link state
				if((1 << ethwan_port) & newLinkPortMask){
					netif_carrier_on(_rtl86xx_dev.dev[i]);
				}else{
					netif_carrier_off(_rtl86xx_dev.dev[i]);
				}
				//disable portmask
				cp->portmask = (1<<ethwan_port);
				//ethwan port id index change to lanif id index
				portid_index[ethwan_port] = i;
			}

			if (strcmp(_rtl86xx_dev.dev[i]->name, lan_dev->name)==0){
				cp->portmask = 0x00;
			}
		}
		re865x_restart_ether_port();
		prev_ethwan_port = ethwan_port;
	}
	return 0;
}

int etherwan_mode_write(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{
 	unsigned char chartmp[8];
 	int port_num;

	if (count > 1){	
		//prev_ethwan_port = ethwan_port;
	      	if (buffer && !copy_from_user(chartmp, buffer, sizeof(chartmp))){
			chartmp[sizeof(chartmp)-1] = '\0';
      		} else {
			return -EFAULT;
		}

		sscanf(chartmp, "%d %d", &ethwan_mode, &port_num);
		//ethwan_mode = chartmp[0] - '0';
		printk("etherwan mode  : %s\n", ethwan_mode ? "Enable" :"Disable");
		if (ethwan_mode!=0) {
			ethwan_port = port_num;
		}

		do_etherwan_mode();
	}
	else{
		printk("write fail\n");		
		return -EFAULT;
	}
	return count;
}
#endif