/***************************************************************************
 * File Name    : smux.c
 * Description  : smux mean server mux.
 ***************************************************************************/
#include <asm/uaccess.h>
#include <linux/capability.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/in.h>
#include <linux/init.h>
#include <linux/rtnetlink.h>
#include <linux/notifier.h>
#ifdef CONFIG_IP_MROUTE
#include <linux/inetdevice.h>
#endif
#include <linux/if_vlan.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/notifier.h>


#include <net/rtl/rtl_nic.h>
#include <net/rtl/rtl867x_hwnat_api.h>

#include <linux/if_smux.h> /* must be after if_vlan.h so no redef error */
#include "../net/bridge/br_private.h"

static char vlan_passthru = 1;

#ifdef CONFIG_RTL8685_PTM_MII
#include "8685_ptm/ptm_mii_sw_wanif.h"

#ifdef USE_PTM_MII_WANIF
static int is_ptmdev(struct net_device *vdev)
{
	int ret=0;
	struct smux_dev_info *dev_info;

	if(vdev==NULL) return ret;

	dev_info = SMUX_DEV_INFO(vdev);
	if(dev_info && 
		dev_info->smux_grp &&
		dev_info->smux_grp->real_dev)
	{
		char *p=dev_info->smux_grp->real_dev->name;
		//printk( "%s: vdev=%s, real_dev=%s\n", __FUNCTION__, vdev->name, p );
		if(strcmp(p,ALIASNAME_PTM0)==0)
			ret=1;
	}
	return ret;
}
#endif /*USE_PTM_MII_WANIF*/
#endif /*CONFIG_RTL8685_PTM_MII*/

#ifdef CONFIG_PORT_MIRROR
extern void nic_tx_mirror (struct sk_buff *skb);
static inline void smux_mirror_pkt(struct sk_buff *skb, 
			const struct smux_dev_info *dev_info, const int flag);

#define IN  0x1
#define OUT 0x2
#endif

#ifdef DEBUG
#define DPRINTK(format, args...) printk(KERN_DEBUG "SMUX: " format, ##args)
#else
#define DPRINTK(format, args...)
#endif

//extern unsigned int pvid_per_port[RTL8651_PORT_NUMBER+3];

//#define UNIQUE_MAC_PER_DEV
#undef UNIQUE_MAC_PER_DEV

/***************************************************************************
                         Global variables 
 ***************************************************************************/
#define SET_MODULE_OWNER(dev) do { } while (0)

static struct list_head SMUX_IGNORE_HEAD;
static struct smux_ignore_entry {
	int proto;
	struct list_head smuxIgnoreList;
};
typedef struct smux_ignore_entry SMUX_IGN_ENTRY;

static DEFINE_RWLOCK(smux_lock);

static LIST_HEAD(smux_grp_devs);

static int smux_device_event(struct notifier_block *, unsigned long, void *);

static struct notifier_block smux_notifier_block = {
	.notifier_call = smux_device_event,
};

/* 
 * return value: 
 *	0: NOT match any rule
 *	1: match smux ignore rule
 */
int smux_ignore_check(struct sk_buff *skb){
	SMUX_IGN_ENTRY *pEntry,*next;
	list_for_each_entry_safe(pEntry, next, &SMUX_IGNORE_HEAD, smuxIgnoreList){
		if(eth_hdr(skb)->h_proto == pEntry->proto){
//			printk("[%s:%d] found Commserver Broadcast packet\n", __FUNCTION__, __LINE__);
			return 1;
		}
	}
	return 0;
}

#ifdef UNIQUE_MAC_PER_DEV
unsigned char wan_dev_def_vid[9]={[0 ... 8]=0};//index 0 is reserved


/*
 * return value: -1 : FAIL
 */
int allocSmuxDevVid(void)
{
	int i;
	
	for (i=1; i<9; i++)
	{
		if (!wan_dev_def_vid[i])
			break;
	}
	if (i<9)
		return i;

	return -1;
}

int freeSmuxDevVid(int vid)
{
	if ((vid >= 9) || (vid <= 0))
		return -1;
	
	wan_dev_def_vid[vid] = 0;

	return 0;
}
#endif

/***************************************************************************
                         Function Definisions
 ***************************************************************************/
static inline struct smux_group *list_entry_smuxgrp(const struct list_head *le)
{
	return list_entry(le, struct smux_group, smux_grp_devs);
}

/***************************************************************************
 * Function Name: __find_smux_group
 * Description  : returns the smux group of interfaces/devices from list
 * Returns      : struct smux_group.
 ***************************************************************************/
struct smux_group *__find_smux_group(const char *ifname)
{
	struct list_head *lh;
	struct smux_group *smux_grp;
	struct smux_group *ret_smux = NULL;

	read_lock(&smux_lock);
	list_for_each(lh, &smux_grp_devs) {
		smux_grp = (struct smux_group *)list_entry_smuxgrp(lh);
		if (!strncmp(smux_grp->real_dev->name, ifname, IFNAMSIZ)) {
			ret_smux = smux_grp;
			break;
		}
	}
	read_unlock(&smux_lock);

	return ret_smux;
} /* __find_smux_group */

inline struct smux_dev_info *list_entry_smuxdev(const struct list_head *le)
{
  return list_entry(le, struct smux_dev_info, list);
}


void open_smux_dev_bydev(const struct net_device *dev)
{
	struct list_head *lg, *lh;
	struct smux_group *smux_grp;
	struct smux_dev_info * sdev = NULL;

	read_lock(&smux_lock);
	list_for_each(lh, &smux_grp_devs){
		smux_grp = (struct smux_group *)list_entry_smuxgrp(lh);
		if (smux_grp->real_dev == dev) {
			list_for_each(lg, &smux_grp->virtual_devs){
				sdev = list_entry_smuxdev(lg);				
				netif_wake_queue(sdev->vdev);
			}
			break;
		}
	}
	read_unlock(&smux_lock);

} /* open_smux_device */


void open_smux_device(char *ifname)
{
	struct list_head *lg, *lh;
	struct smux_group *smux_grp;
	struct smux_dev_info * sdev = NULL;

	read_lock(&smux_lock);
	list_for_each(lh, &smux_grp_devs){
		smux_grp = (struct smux_group *)list_entry_smuxgrp(lh);
		if (!strncmp(smux_grp->real_dev->name, ifname, IFNAMSIZ)){
			list_for_each(lg, &smux_grp->virtual_devs){
				sdev = list_entry_smuxdev(lg);
				netif_wake_queue(sdev->vdev);
			}
			break;
		}
	}
	read_unlock(&smux_lock);

} /* open_smux_device */


void close_smux_device(char *ifname)
{
	struct list_head *lg, *lh;
	struct smux_group *smux_grp;
	struct smux_dev_info * sdev = NULL;

	read_lock(&smux_lock);
	list_for_each(lh, &smux_grp_devs){
		smux_grp = (struct smux_group *)list_entry_smuxgrp(lh);
		if (!strncmp(smux_grp->real_dev->name, ifname, IFNAMSIZ)){
			list_for_each(lg, &smux_grp->virtual_devs){
				sdev = list_entry_smuxdev(lg);
				netif_stop_queue(sdev->vdev);
			}
			break;
		}
	}
	read_unlock(&smux_lock);

} /* close_smux_device */

/***************************************************************************
 * Function Name: __find_smux_in_smux_group
 * Description  : returns the smux device from smux group of devices 
 * Returns      : struct net_device
 ***************************************************************************/
static struct net_device *__find_smux_in_smux_group(
                                     struct smux_group *smux_grp, 
                                     const char *ifname)
{
	struct list_head *lh;
	struct smux_dev_info * sdev = NULL;
	struct net_device    * ret_dev = NULL;

	read_lock(&smux_lock);
	list_for_each(lh, &smux_grp->virtual_devs) {
		sdev = list_entry_smuxdev(lh);
		if(!strncmp(sdev->vdev->name, ifname, IFNAMSIZ)) {
			ret_dev = sdev->vdev;
			break;
		}
	}
	read_unlock(&smux_lock);

	return ret_dev;
} /* __find_smux_in_smux_group */

static struct sk_buff *smux_eat_vlan(struct sk_buff *skb, struct smux_dev_info *info)
{
	struct vlan_hdr *vhdr=NULL;
	struct net_bridge_port *p;		

	if ((skb->dev->priv_flags & IFF_VSMUX) && (skb->protocol ==  __constant_htons(ETH_P_8021Q))) {
		skb = skb_vlan_untag(skb);
		if (skb) {
			skb->vlan_tci = 0;
		}
	}
	return skb;
}

#if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED)
/*
 *	Return Value:
 *	0: smux device not accept this packet
 *	1: smux device accept this packet
 */
static inline int smux_check_mac(struct smux_dev_info *dev_info, struct sk_buff *skb)
{
	struct net_device *vdev;
	
	vdev = dev_info->vdev;

	if(!memcmp(eth_hdr(skb)->h_dest,vdev->dev_addr,ETH_ALEN))
		return 1;
	
	return 0;
}

#else

/*
 *	Return Value:
 *	0: smux device not accept this packet
 *	1: smux device accept this packet
 */
static inline int smux_check_vlan(struct smux_dev_info *dev_info, struct sk_buff *skb)
{
	struct net_device *vdev;
	unsigned short tpid_check = skb->protocol;	
	unsigned short vid = 0;
	struct vlan_hdr *vhdr=NULL;	
	int is_vlan_pkt = 0;
	
	if(tpid_check == __constant_htons(ETH_P_8021Q)){
		vhdr = (struct vlan_hdr *)(skb->data);
		vid = (vhdr->h_vlan_TCI)&VLAN_VID_MASK;
		is_vlan_pkt = 1;
	}
	
	vdev = dev_info->vdev;
	
	if (vdev->priv_flags&IFF_VSMUX) { // tagged wan
		if (!is_vlan_pkt) // untagged packet
			return 0;
		if (vid!=dev_info->vid)
			return 0;
	}
	else { // untagged wan
		if (is_vlan_pkt){ // tagged packet
			if (vlan_passthru){ // vlan passthrough on untagged bridged wan
				if (dev_info->proto!=SMUX_PROTO_BRIDGE && !dev_info->brpppoe)
					return 0;
			} else {
				return 0;
			}
		}
	}
	return 1;
}
#endif

#if defined(CONFIG_RTK_8023AH) || defined(CONFIG_RTK_Y1731)
static inline int smux_recv_eoam(struct sk_buff *skb)
{
	struct vlan_hdr *vhdr;
	
	if (skb->protocol ==  cpu_to_be16(ETH_P_8021Q)) {
		vhdr = (struct vlan_hdr *)skb->data;		
		while (vhdr->h_vlan_encapsulated_proto == cpu_to_be16(ETH_P_8021Q))
		{
			vhdr = vhdr+1;
		}
			
		if (vhdr->h_vlan_encapsulated_proto == htons(0x8809) || vhdr->h_vlan_encapsulated_proto == htons(0x8902)) {
			// ignore vlan, set skb->protocol so __netif_receive_skb_core() will not do skb_vlan_untag().
			vlan_set_encap_proto(skb, vhdr);
		}		
	}
	if (skb->protocol ==  htons(0x8809) || skb->protocol ==  htons(0x8902))
		return 1;
	return 0;
}
#endif

/***************************************************************************
 * Function Name: smux_pkt_recv
 * Description  : packet recv routine for all smux devices from real dev.
 * Returns      : 0 on Success, 1 on drop, 2 on ignore
 ***************************************************************************/
int smux_pkt_recv(struct sk_buff *skb, struct net_device *dev)
{
	struct smux_group *grp;
	unsigned char *dstAddr;
	struct sk_buff *skb2;
	struct smux_dev_info *dev_info;
	struct smux_dev_info *dev_info_first;
	struct list_head *lh;
	struct net_device *vdev;
	struct vlan_hdr *vhdr=NULL;
	unsigned short protocol=skb->protocol;
	int isTxDone = 0;


	if(!dev) {
		dev_kfree_skb(skb);
		return 1;
	}

	if(smux_ignore_check(skb)){
//		printk("[%s:%d] match smux ignore rule, exit smux.\n", __FUNCTION__, __LINE__);
		return 2;
	}

#if defined(CONFIG_RTK_8023AH) || defined(CONFIG_RTK_Y1731)
	// Ethernet OAM packet, not processed by smux
	if (smux_recv_eoam(skb))
		return 2; // ignore and pass to eoam
#endif

	grp = __find_smux_group(dev->name);
	if(!grp) {
		dev_kfree_skb(skb);
		return 1;
	}

	dstAddr = eth_hdr(skb)->h_dest;

	if (protocol ==  __constant_htons(ETH_P_8021Q)) {
		vhdr = (struct vlan_hdr *)(skb->data);
		protocol = vhdr->h_vlan_encapsulated_proto;
	}

	read_lock(&smux_lock);
	/* Multicast Traffic will go on all intf.*/
	if (dstAddr[0] & 1)
	{

		dev_info_first = NULL;
		/* multicast or broadcast frames */
		list_for_each(lh, &grp->virtual_devs)
		{
			dev_info = list_entry_smuxdev(lh);
			vdev = dev_info->vdev;
		
#if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED)
#else
			if (!smux_check_vlan(dev_info, skb))
				continue;
#endif

			// do not check protocol here, it will be checked in linux protocol stack
			#if 0
			if (((dev_info->proto == SMUX_PROTO_PPPOE) && (protocol != htons(ETH_P_PPP_DISC)) && (protocol != htons(ETH_P_PPP_SES))) ||
				((dev_info->proto == SMUX_PROTO_IPOE) && ((protocol == htons(ETH_P_PPP_DISC)) || (protocol == htons(ETH_P_PPP_SES)))))
			{
				DPRINTK("TRACE %d: packet dropped on RX dev %s\n", __LINE__, vdev->name);
				continue;
			}
			#endif
			
			if(!dev_info_first) {
				dev_info_first = dev_info;
				continue;
			}
	
#ifdef	CONFIG_PORT_MIRROR
			if(IN_NEED_MIR(dev_info->port_mirror))
			{
				smux_mirror_pkt(skb, dev_info, IN);
			}
#endif
			skb2 = skb_clone(skb, GFP_ATOMIC);
			if (skb2) {
				dev_info->stats.rx_packets++;
				dev_info->stats.rx_bytes += skb2->len;
				skb2->dev = vdev;
				skb2->from_dev = vdev;
				//skb2->pkt_type = PACKET_HOST;
	
				if (smux_eat_vlan(skb2, dev_info))
					netif_rx(skb2);
			}
		}

		if (!dev_info_first) {
			dev_kfree_skb(skb);
			read_unlock(&smux_lock);
			return 1;
		}
		else {
			dev_info_first->stats.rx_packets++;
			dev_info_first->stats.rx_bytes += skb->len; 
			skb->dev = dev_info_first->vdev;
			skb->from_dev = dev_info_first->vdev;
			//skb->pkt_type = PACKET_HOST;
			
#ifdef	CONFIG_PORT_MIRROR
			if(IN_NEED_MIR(dev_info->port_mirror))
			{
				smux_mirror_pkt(skb, dev_info, IN);
			}
#endif

			if (smux_eat_vlan(skb, dev_info_first))
				netif_rx(skb);
		}	
		isTxDone = 1;		
	}
	else /* route Traffic.*/
	{
		#ifndef UNIQUE_MAC_PER_DEV
		dev_info_first = NULL;
		#endif
		/* Routing Interface Traffic : check dst mac */
		list_for_each(lh, &grp->virtual_devs)
		{
			dev_info = list_entry_smuxdev(lh);
			if (dev_info->proto == SMUX_PROTO_BRIDGE)
				continue;
			
			vdev = dev_info->vdev;
#if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED)
			if (!smux_check_mac(dev_info, skb))
				continue;
#else	
			if (!smux_check_vlan(dev_info, skb))
				continue;
#endif

			#if 0
			if (((dev_info->proto == SMUX_PROTO_PPPOE) && (protocol != htons(ETH_P_PPP_DISC)) && (protocol != htons(ETH_P_PPP_SES))) ||
				((dev_info->proto == SMUX_PROTO_IPOE) && ((protocol == htons(ETH_P_PPP_DISC)) || (protocol == htons(ETH_P_PPP_SES)))))
			{
				DPRINTK("TRACE %d: packet dropped on RX dev %s\n", __LINE__, vdev->name);
				continue;
			}
			#endif

			#ifndef UNIQUE_MAC_PER_DEV
			if (!memcmp(dstAddr, vdev->dev_addr, ETH_ALEN))
			{
				if(!dev_info_first) {
					dev_info_first = dev_info;
					continue;
				}
				
				skb2 = skb_copy(skb, GFP_ATOMIC);
				skb2->dev = vdev;
				skb2->from_dev = vdev;
				dev_info->stats.rx_packets++;
				dev_info->stats.rx_bytes += skb2->len;
				skb2->pkt_type = PACKET_HOST;
				//printk("(route) receive from %s\n", vdev->name);
#ifdef	CONFIG_PORT_MIRROR
				if(IN_NEED_MIR(dev_info->port_mirror))
				{
					smux_mirror_pkt(skb, dev_info, IN);
				}
#endif			
				if (smux_eat_vlan(skb2, dev_info))
					netif_rx(skb2);

				isTxDone = 1;
			}
			#else
			if (!memcmp(dstAddr, vdev->dev_addr, ETH_ALEN))
			{
				skb->dev = vdev;
				skb->from_dev = vdev;
				dev_info->stats.rx_packets++;
				dev_info->stats.rx_bytes += skb->len;
				skb->pkt_type = PACKET_HOST;
				//printk("(route) receive from %s\n", vdev->name);

#ifdef	CONFIG_PORT_MIRROR
				if(IN_NEED_MIR(dev_info->port_mirror))
				{
					smux_mirror_pkt(skb, dev_info, IN);
				}
#endif
				if (smux_eat_vlan(skb, dev_info))
					netif_rx(skb);

				isTxDone = 1;
				break;
			}
			#endif
		}

		#ifndef UNIQUE_MAC_PER_DEV
		if (dev_info_first) {
			dev_info_first->stats.rx_packets++;
			dev_info_first->stats.rx_bytes += skb->len; 
			skb->dev = dev_info_first->vdev;
			skb->from_dev = dev_info_first->vdev;
			skb->pkt_type = PACKET_HOST;
			//printk("(route) receive from %s\n", dev_info_first->vdev->name);
#ifdef	CONFIG_PORT_MIRROR
			if(IN_NEED_MIR(dev_info_first->port_mirror))
			{
				smux_mirror_pkt(skb, dev_info_first, IN);
			}
#endif	
			if (smux_eat_vlan(skb, dev_info_first))
				netif_rx(skb);
			
			isTxDone = 1;
		}
		#endif
	}

	if(isTxDone != 1) 
	{
		/* Bridging Interface Traffic */
		list_for_each(lh, &grp->virtual_devs)
		{
			dev_info = list_entry_smuxdev(lh);
			if (dev_info->proto != SMUX_PROTO_BRIDGE && !dev_info->brpppoe)
				continue;
			
			vdev = dev_info->vdev;

#if defined(CONFIG_RTL_MULTI_ETH_WAN_MAC_BASED)
#else
			if (!smux_check_vlan(dev_info, skb))
				continue;
#endif
			
			if (vdev->promiscuity)
			{
				skb->dev = vdev;
				skb->from_dev = vdev;
				dev_info->stats.rx_packets++;
				dev_info->stats.rx_bytes += skb->len; 
				skb->pkt_type = PACKET_OTHERHOST;
				//printk("(bridge) receive from %s\n", vdev->name);
#ifdef	CONFIG_PORT_MIRROR
				if(IN_NEED_MIR(dev_info->port_mirror))
				{
					smux_mirror_pkt(skb, dev_info, IN);
				}
#endif
				if (smux_eat_vlan(skb, dev_info))
					netif_rx(skb);
				isTxDone = 1;
				break;
			}
		}
	}
	read_unlock(&smux_lock);

	if(!isTxDone) {
		DPRINTK("dropping packet that has wrong dest. on RX dev %s\n", dev->name);
		dev_kfree_skb(skb);
		return 1;
	}

	return 0;
} /* smux_pkt_recv */
/*Start:add by caoxiafei cKF24361 20100426 for fastpath*/
/***************************************************************************
 * Function Name: getSmuxDev
 * Description  : fetch smux devices according to real dev.
 * Returns      : 0 on fail
 ***************************************************************************/
struct net_device * getSmuxDev(struct sk_buff *skb)
{
	struct smux_group *grp;
	unsigned char *dstAddr;
	struct smux_dev_info *dev_info;
	struct list_head *lh;
	struct net_device *vdev;

	grp = __find_smux_group(skb->dev->name);
	if (!grp)
		return NULL;
	dstAddr = eth_hdr(skb)->h_dest;
	if (dstAddr[0]&0x01)
		return NULL;
	read_lock(&smux_lock);
	list_for_each(lh, &grp->virtual_devs)
	{
		dev_info = list_entry_smuxdev(lh);
		vdev = dev_info->vdev;
		if (ether_addr_equal(dstAddr, vdev->dev_addr)) {
			skb->dev = vdev;
			read_unlock(&smux_lock);
			return vdev;
		}
	}

	read_unlock(&smux_lock);
	return NULL;
}
/*End:add by caoxiafei cKF24361 20100426 for fastpath*/

/***************************************************************************
 * Function Name: smux_dev_hard_start_xmit
 * Description  : xmit routine for all smux devices on real dev.
 * Returns      : 0 on Success
 ***************************************************************************/
__IRAM
int smux_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct net_device_stats *stats = smux_dev_get_stats(dev);
	struct smux_dev_info *dev_info;
	struct netdev_queue *txq;

	dev_info = SMUX_DEV_INFO(dev);
	skb->dev = dev_info->smux_grp->real_dev;
	if (-1 == dev_info->vid) {
		#if CONFIG_RTL_8676HWNAT
		if (dev_info->proto == SMUX_PROTO_BRIDGE)
			skb->vlan_tci = RTL_BridgeWANVLANID;
		else
			skb->vlan_tci = RTL_WANVLANID;
		#else
		skb->vlan_tci = 0;
		#endif
	}
	else
	{
		if(dev_info->m_1p==0)
			skb->vlan_tci = (dev_info->vid&VLAN_VID_MASK)|VLAN_TAG_PRESENT;
		else
		{
			skb->vlan_tci = ((dev_info->vid&VLAN_VID_MASK)|((dev_info->m_1p-1)<<13))|VLAN_TAG_PRESENT;
		}
			
	}	
	skb->vlan_member = dev_info->member;
#ifdef	CONFIG_PORT_MIRROR
	if((OUT_NEED_MIR(dev_info->port_mirror)))
	{
		smux_mirror_pkt(skb, dev_info, OUT);
	}
#endif
	//printk("%s,%d::dev_info->member: %x\n",__func__,__LINE__,skb->vlan_member);
	if  (!netif_queue_stopped(skb->dev)) {
		stats->tx_packets++; 
		stats->tx_bytes += skb->len;

		//update real dev's txq trans
		txq = netdev_pick_tx(skb->dev, skb, NULL);
		txq->trans_start = jiffies;

	        skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
	} else {
		netif_stop_queue(dev);
		dev_kfree_skb_any(skb);
		stats->tx_dropped++;
	}

	//dev_queue_xmit(skb);

	return 0;
} /* smux_dev_hard_start_xmit */

/***************************************************************************
 * Function Name: smux_dev_open
 * Description  : 
 * Returns      : 0 on Success
 ***************************************************************************/
int smux_dev_open(struct net_device *vdev)
{
	if (!(SMUX_DEV_INFO(vdev)->smux_grp->real_dev->flags & IFF_UP))
		return -ENETDOWN;

	return 0;
} /* smux_dev_open */

/***************************************************************************
 * Function Name: smux_dev_stop
 * Description  : 
 * Returns      : 0 on Success
 ***************************************************************************/
int smux_dev_stop(struct net_device *dev)
{
	return 0;
} /* smux_dev_stop */

/***************************************************************************
 * Function Name: smux_dev_set_mac_address
 * Description  : sets the mac for devs
 * Returns      : 0 on Success
 ***************************************************************************/
int smux_dev_set_mac_address(struct net_device *dev, 
                             void *addr_struct_p)
{
	struct sockaddr *addr = (struct sockaddr *)(addr_struct_p);
	int i, flgs;
#if 0
#ifdef CONFIG_RTL_8676HWNAT
	rtl865x_netif_t netif;
#endif
#endif
#ifdef UNIQUE_MAC_PER_DEV
	struct smux_group *grp = NULL;
	struct smux_dev_info *vdev_info = NULL;
	struct list_head *lh;
#endif

	if (netif_running(dev))
		return -EBUSY;

	memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
	memset(dev->broadcast, 0xff, ETH_ALEN);

#if defined(CONFIG_RTL_HW_NAPT) && defined(CONFIG_RTL_FLOW_BASE_HWNAT)
	//memcpy(netif.macAddr.octet,addr->sa_data,ETHER_ADDR_LEN);
	//memcpy(netif.name,dev->name,MAX_IFNAMESIZE);
	//rtl865x_setNetifMac(&netif);
	rtl8676_set_Multiwan_NetifMacAddr(dev->name, addr->sa_data);
#endif
#if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF)
	if(is_ptmdev(dev))
		ptm_mii_update_wanif_macaddr(dev->name, addr->sa_data);
#endif /*USE_PTM_MII_WANIF*/
	return 0;
#if 0	
#ifdef UNIQUE_MAC_PER_DEV

grp = __find_smux_group(ALIASNAME_NAS0);
//grp = __find_smux_group("nas0");
	if (!grp)
		return -EADDRNOTAVAIL;
	if (list_empty(&grp->virtual_devs))
	{
		return -EADDRNOTAVAIL;
	}
	list_for_each(lh, &grp->virtual_devs)
	{
		vdev_info = list_entry_smuxdev(lh);
		if (vdev_info->vdev == dev)
			continue;
		
		if (!memcmp(vdev_info->vdev->dev_addr, addr->sa_data, ETH_ALEN))
		{
        	return -EADDRNOTAVAIL;
		}
	}
#endif

	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);

	printk("%s: Setting MAC address to ", dev->name);
	for (i = 0; i < 6; i++)
		printk(" %2.2x", dev->dev_addr[i]);
	printk(".\n");

	if (memcmp(SMUX_DEV_INFO(dev)->smux_grp->real_dev->dev_addr, dev->dev_addr, dev->addr_len) != 0) {
		if (!(SMUX_DEV_INFO(dev)->smux_grp->real_dev->flags & IFF_PROMISC)) {
			flgs = SMUX_DEV_INFO(dev)->smux_grp->real_dev->flags;

			/* Increment our in-use promiscuity counter */
			dev_set_promiscuity(SMUX_DEV_INFO(dev)->smux_grp->real_dev, 1);

			/* Make PROMISC visible to the user. */
			flgs |= IFF_PROMISC;
			printk("SMUX (%s):  Setting underlying device (%s) to promiscious mode.\n",
				dev->name, SMUX_DEV_INFO(dev)->smux_grp->real_dev->name);
			dev_change_flags(SMUX_DEV_INFO(dev)->smux_grp->real_dev, flgs);
		}
	} else {
		printk("SMUX (%s):  Underlying device (%s) has same MAC, not checking promiscious mode.\n",
			dev->name, SMUX_DEV_INFO(dev)->smux_grp->real_dev->name);
	}


	SMUX_DEV_INFO(dev)->smux_grp->real_dev->netdev_ops->ndo_set_mac_address(dev, addr_struct_p);

	return 0;
#endif
} /* smux_dev_set_mac_address */

#ifdef CONFIG_PORT_MIRROR
static int smux_do_mirror(struct net_device *dev, struct portmir *pmr)
{
	struct smux_dev_info *dev_info = SMUX_DEV_INFO(dev);
	
	switch (pmr->port_mirror) {
		case -1: /* status */
			printk("%s:\taction=%d, todev=%s\n", dev->name, dev_info->port_mirror, dev_info->mirror_dev?dev_info->mirror_dev->name:"n/a");
			break;
		case -2: /* flush */
			dev_info->port_mirror = 0;
			dev_info->mirror_dev = 0;
			break;
		default:
			printk("%s: error command!\n", __FUNCTION__);
	}
	return 0;
}
#endif

/***************************************************************************
 * Function Name: smux_dev_ioctl
 * Description  : handles device related ioctls
 * Returns      : 0 on Success
 ***************************************************************************/
int smux_dev_ioctl(struct net_device *vdev, struct ifreq *ifr, int cmd)
{
	struct net_device *real_dev = SMUX_DEV_INFO(vdev)->smux_grp->real_dev;
	struct ifreq ifrr;
	int err = -EOPNOTSUPP;
#ifdef CONFIG_RTL_8676HWNAT		
	extern int set_port_mapping(struct net_device *dev, struct ifreq *rq, int cmd);
#endif

	strncpy(ifrr.ifr_name, real_dev->name, IFNAMSIZ);
	ifrr.ifr_ifru = ifr->ifr_ifru;

	//printk("%s %d cmd 0x%x (dev:%s)\n", __func__, __LINE__, cmd,vdev->name);
	switch(cmd) {
		case SIOCGMIIPHY:
		case SIOCGMIIREG:
		case SIOCSMIIREG:
			if (real_dev->netdev_ops->ndo_do_ioctl && netif_device_present(real_dev))
			err = real_dev->netdev_ops->ndo_do_ioctl(real_dev, &ifrr, cmd);
			break;

		case SIOCETHTOOL:
			err = dev_ethtool(&init_net, &ifrr);

			if (!err)
				ifr->ifr_ifru = ifrr.ifr_ifru;
			break;

#ifdef CONFIG_PORT_MIRROR
#define SIOCPORTMIRROR (SIOCDEVPRIVATE + 0xf) /*ethwan port mirror*/
		case SIOCPORTMIRROR:
		{
			struct portmir *pmr;
			struct smux_dev_info *dev_info = SMUX_DEV_INFO(vdev);
			struct net_device *dev = NULL;

			pmr = (struct portmir *)ifr->ifr_data;
			if (pmr->port_mirror < 0) {
				smux_do_mirror(vdev, pmr);
				err = 0;
				break;
			}

			dev = dev_get_by_name(&init_net, pmr->mir_dev_name);
			if(!dev)
			{
				printk("error lan device!\n");
				break;
			}	
			if((dev->priv_flags & IFF_DOMAIN_ELAN) == 0)
			{
				printk("error lan device!\n");
				break;
			}
			dev_info->port_mirror = pmr->port_mirror;
			dev_info->mirror_dev  = dev;

			err = 0;
			break;
		}
#endif
			
		case SIOCSITFGROUP:
			{
				struct ifvlan *ifvl;
				struct smux_dev_info *dev_info = SMUX_DEV_INFO(vdev);

				ifvl = (struct ifvlan *)ifr->ifr_data;
				if (ifvl->enable)
					dev_info->member = ifvl->member;
				else
					dev_info->member = 0xFFFFFFFF;
				printk("%s %d set portmapping  (dev:%s  member:0x%X) \n", __func__, __LINE__,vdev->name,dev_info->member);
#ifdef CONFIG_RTL_8676HWNAT			
				err = set_port_mapping(vdev, ifr, cmd);
#endif
				#if defined(CONFIG_RTL_HW_NAPT) && defined(CONFIG_RTL_FLOW_BASE_HWNAT)
				rtl8676_update_portmapping_Multiwan_dev(vdev->name,dev_info->member);
				#endif
			}
			//rtl865x_updateNetifForPortmapping();
          
			return err;
			
#if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF)
		case SIOCSPTMQMAP:
			{
				unsigned char *p;

				p=ifr->ifr_data;
				printk( "SIOCSPTMQMAP, dev=%s, %x\n", vdev->name, (unsigned int)p );
				if(p!=NULL) printk( "path=%u, qmap=%u%u%u%u%u%u%u%u\n",
							p[0],p[1],p[2],p[3],p[4]
							,p[5],p[6],p[7],p[8]);
				if(is_ptmdev(vdev) && p!=NULL)
					err=ptm_mii_update_qmap( vdev->name, p[0], &p[1] );
				return err;
			}
#endif /*USE_PTM_MII_WANIF*/
	}

	return err;
} /* smux_dev_ioctl */

/***************************************************************************
 * Function Name: smux_dev_change_mtu
 * Description  : changes mtu for dev
 * Returns      : 0 on Success
 ***************************************************************************/
int smux_dev_change_mtu(struct net_device *vdev, int new_mtu)
{
	//MTU should be larger than real device.
	if (SMUX_DEV_INFO(vdev)->smux_grp->real_dev->mtu < new_mtu)
		return -ERANGE;

	//vdev->mtu = new_mtu;
	SMUX_DEV_INFO(vdev)->smux_grp->real_dev->netdev_ops->ndo_change_mtu(vdev, new_mtu);

	return 0;
}

/***************************************************************************
 * Function Name: smux_setup
 * Description  : inits device api
 * Returns      : None
 ***************************************************************************/
static void smux_setup(struct net_device *new_dev)
{
	SET_MODULE_OWNER(new_dev);

	/* Make this thing known as a SMUX device */
	new_dev->priv_flags |= IFF_OSMUX;

	new_dev->tx_queue_len = 0;

#ifdef CONFIG_COMPAT_NET_DEV_OPS
	/* set up method calls */
	new_dev->change_mtu = smux_dev_change_mtu;
	new_dev->open = smux_dev_open;
	new_dev->stop = smux_dev_stop;
	new_dev->set_mac_address = smux_dev_set_mac_address;
	/*new_dev->set_multicast_list = smux_dev_set_multicast_list; TODO: */
	new_dev->destructor = free_netdev;
	new_dev->do_ioctl = smux_dev_ioctl;
	new_dev->get_stats = smux_dev_get_stats;
#endif
} /* smux_setup */

/***************************************************************************
 * Function Name: smux_transfer_operstate
 * Description  : updates the operstate of overlay device 
 * Returns      : None.
 ***************************************************************************/
static void smux_transfer_operstate(const struct net_device *rdev, 
                                    struct net_device *vdev)
{

	if (rdev->operstate == IF_OPER_DORMANT)
		netif_dormant_on(vdev);
	else
		netif_dormant_off(vdev);

	if (netif_carrier_ok(rdev)) {
		if (!netif_carrier_ok(vdev))
			netif_carrier_on(vdev);
	} else {
		if (netif_carrier_ok(vdev))
			netif_carrier_off(vdev);
	}
} /* smux_transfer_operstate */

static const struct ethtool_ops smux_ethtool_ops = {
	.get_link = ethtool_op_get_link,
};

#if defined(CONFIG_COMPAT_NET_DEV_OPS)
#else
static const struct net_device_ops smux_netdev_ops = {
	.ndo_open		= smux_dev_open,
	.ndo_stop		= smux_dev_stop,
	.ndo_validate_addr	= eth_validate_addr,
	.ndo_set_mac_address 	= smux_dev_set_mac_address,
	.ndo_do_ioctl		= smux_dev_ioctl,
	.ndo_start_xmit		= smux_dev_hard_start_xmit,
	.ndo_change_mtu		= smux_dev_change_mtu,
	.ndo_get_stats		= smux_dev_get_stats,
};
#endif

/***************************************************************************
 * Function Name: smux_register_device
 * Description  : regists new overlay device on real device & registers for 
                  packet handlers depending on the protocol types
 * Returns      : 0 on Success
 ***************************************************************************/
static struct net_device *smux_register_device(const char *rifname,
					       const char *nifname, int smux_proto, int vid, int napt, int brpppoe)
{
	struct net_device *new_dev = NULL;
	struct net_device *real_dev = NULL; 
	struct smux_group *grp = NULL;
	struct smux_dev_info *vdev_info = NULL;
	//int    mac_reused = 0;
	//unsigned char LSB=0;
	//struct list_head *lh;

	real_dev = dev_get_by_name(&init_net, rifname);
	if (!real_dev) {
		goto real_dev_invalid;
	}

	if (!(real_dev->flags & IFF_UP)) {
		goto real_dev_invalid;
	}

	new_dev = alloc_netdev(sizeof(struct smux_dev_info), nifname, NET_NAME_USER, smux_setup);
	if (new_dev == NULL)
	{
		printk("netdev alloc failure\n");
		goto new_dev_invalid;
	}
	else {
		new_dev->priv = netdev_priv(new_dev);
	}
	
	//dev->netdev_ops = &rtl819x_netdev_ops;
	ether_setup(new_dev);
	if (vid != -1)
		new_dev->priv_flags |= IFF_VSMUX;

	new_dev->flags &= ~IFF_UP;
	new_dev->flags &= ~IFF_MULTICAST;
	new_dev->priv_flags |= IFF_DOMAIN_WAN;

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

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

	#if defined(CONFIG_RTL_TSO)
	new_dev->features |= (NETIF_F_TSO | NETIF_F_TSO_ECN);
	#endif
	#else
	new_dev->features = real_dev->features;
	#endif

	real_dev->priv_flags |= IFF_RSMUX;

	new_dev->state = (real_dev->state & 
                    ((1<<__LINK_STATE_NOCARRIER) |
                     (1<<__LINK_STATE_DORMANT))) |
                     (1<<__LINK_STATE_PRESENT);

	new_dev->mtu = real_dev->mtu;
	new_dev->type = real_dev->type;
	new_dev->hard_header_len = real_dev->hard_header_len;
#ifdef CONFIG_COMPAT_NET_DEV_OPS
	new_dev->hard_start_xmit = smux_dev_hard_start_xmit;
	new_dev->set_mac_address = smux_dev_set_mac_address;
#else
	new_dev->netdev_ops = &smux_netdev_ops;
#endif
	new_dev->ethtool_ops = &smux_ethtool_ops;

	/* find smux group name. if not found create all new smux group */
	grp = __find_smux_group(rifname);
	if (!grp) {
		grp = kzalloc(sizeof(struct smux_group), GFP_KERNEL);

		if(grp) {
			INIT_LIST_HEAD(&grp->virtual_devs);
			INIT_LIST_HEAD(&grp->smux_grp_devs);

			grp->real_dev = real_dev;

			write_lock_irq(&smux_lock);
			list_add_tail(&grp->smux_grp_devs, &smux_grp_devs);
			write_unlock_irq(&smux_lock);
		}
		else {
			printk("%s %d create smux group failed\n", __func__, __LINE__);
			free_netdev(new_dev);
			new_dev = NULL;
		}
	}

	if(grp && new_dev) {
		/* Assign default mac to bridge so that we can add it to linux bridge */
		if(smux_proto == SMUX_PROTO_BRIDGE) 
		{
			memcpy( new_dev->dev_addr, "\xFE\xFF\xFF\xFF\xFF\xFF", ETH_ALEN );
		}
		else 
		{
		#ifdef UNIQUE_MAC_PER_DEV
			if (list_empty(&grp->virtual_devs))
			{
				memcpy(new_dev->dev_addr, real_dev->dev_addr, ETH_ALEN);
			}
			else
			{
				list_for_each(lh, &grp->virtual_devs)
				{
					vdev_info = list_entry_smuxdev(lh);
					if (!memcmp(real_dev->dev_addr, vdev_info->vdev->dev_addr, ETH_ALEN))
					{
		            	mac_reused = 1;
					}
					if (LSB < vdev_info->vdev->dev_addr[5])
						LSB = vdev_info->vdev->dev_addr[5];
				}

				memcpy(new_dev->dev_addr, real_dev->dev_addr, ETH_ALEN);
				if (mac_reused)
				{
					//generate new mac address, real_addr mac addr increased by 1.
					new_dev->dev_addr[5] = LSB+1;
				}
			}
		#else
			memcpy(new_dev->dev_addr, real_dev->dev_addr, ETH_ALEN);
		#endif
		}
	}

	if(grp && new_dev) {
		struct net_device *ret_dev;
		/*find new smux in smux group if it does not exit create one*/
		if(NULL == (ret_dev=__find_smux_in_smux_group(grp, nifname))) {
			vdev_info = SMUX_DEV_INFO(new_dev);
			if (!vdev_info) {
				free_netdev(new_dev);
                        	new_dev = NULL;
				printk("%s(%d) vdev_info is NULL\n", __func__, __LINE__);	
			}
			else {
				memset(vdev_info, 0, sizeof(struct smux_dev_info));
				//m_1p : 0~8, 0 is meaning disable
				if(vid>=0)
					vdev_info->m_1p=vid>>13;
				else
					vdev_info->m_1p=0;			
				
				vdev_info->smux_grp = grp;
				vdev_info->vdev = new_dev;
				vdev_info->proto = smux_proto;
				#ifdef UNIQUE_MAC_PER_DEV
				if ((vid == -1) && (smux_proto != SMUX_PROTO_BRIDGE)) {			
					if ((vid = allocSmuxDevVid()) == -1)
						printk("fatal error, too many wan interface created.\n");
				}
				#endif
				if(vid!=-1)
					vdev_info->vid = (vid&VLAN_VID_MASK);
				else
					vdev_info->vid = vid;
				
				vdev_info->napt = napt;
				vdev_info->brpppoe = brpppoe;
				vdev_info->member = 0xFFFFFFFF;	//init membership to include all interface.
#ifdef CONFIG_PORT_MIRROR
				vdev_info->port_mirror = 0;
				vdev_info->mirror_dev = NULL;
#endif			
				if(smux_proto == SMUX_PROTO_BRIDGE) {
					new_dev->promiscuity = 1;
				}
				else if(smux_proto == SMUX_PROTO_IPOE) {
					new_dev->flags |= IFF_MULTICAST;
				}

				if (register_netdev(new_dev)) {
					printk("register_netdev failed\n");
					//list_del(&vdev_info->list);
					free_netdev(new_dev);
					new_dev = NULL;
				}
				else {
					INIT_LIST_HEAD(&vdev_info->list);
					write_lock_irq(&smux_lock);
					/* To avoid vlan-tagged packet recognize incorrectly vlan-untagged WAN device due to vlanpassthu,
						we add vlan-tagged WAN device after list head and vlan-untagged WAN device to list tail. */
					if(vid!=-1)
						list_add(&vdev_info->list, &grp->virtual_devs);
					else
						list_add_tail(&vdev_info->list, &grp->virtual_devs);
					write_unlock_irq(&smux_lock);
					smux_transfer_operstate(real_dev, new_dev);
				}
				#ifdef CONFIG_RTL_HW_NAPT
				if(new_dev)
				{
					if(smux_proto != SMUX_PROTO_BRIDGE && brpppoe)
					{
						char ifname_extend[IFNAMSIZ];
						strcpy(ifname_extend,vdev_info->vdev->name);
						strcat(ifname_extend,"_B");
						#if defined(CONFIG_RTL_FLOW_BASE_HWNAT) && !defined(CONFIG_RTL_EVENT_TRIGGER_HWNAT)
						rtl8676_register_Multiwan_dev(vdev_info->vdev->name, smux_proto, vid, napt);
						rtl8676_register_Multiwan_dev(ifname_extend, SMUX_PROTO_BRIDGE, vid, napt);	
						#endif
					}
					else
					{
#if defined(CONFIG_RTL_FLOW_BASE_HWNAT)	&& !defined(CONFIG_RTL_EVENT_TRIGGER_HWNAT)
						rtl8676_register_Multiwan_dev(vdev_info->vdev->name, smux_proto, vid, napt);
#endif
					}	
				}
				#endif
				#if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF)
				if(new_dev)
				{
					if(is_ptmdev(new_dev))
						ptm_mii_register_wanif( new_dev->name, new_dev->dev_addr, -1, vdev_info->vid, vdev_info->proto==SMUX_PROTO_BRIDGE );
				}	
				#endif /*USE_PTM_MII_WANIF*/

			}
		}
		else {
			printk("device %s already exist.\n", nifname);
			free_netdev(new_dev);
			new_dev = ret_dev;
		}

	}

	return new_dev;

real_dev_invalid:
new_dev_invalid:

	return NULL;
} /* smux_register_device */

/***************************************************************************
 * Function Name: smux_unregister_device
 * Description  : unregisters the smux devices along with releasing mem.
 * Returns      : 0 on Success
 ***************************************************************************/
static int smux_unregister_device(const char* vifname)
{
	struct net_device *vdev = NULL;
	struct net_device *real_dev = NULL;
	int ret;
	struct smux_dev_info *dev_info;
	ret = -EINVAL;

	vdev = dev_get_by_name(&init_net, vifname);

	if(vdev) {
		printk("%s remove smux dev %s\n", __func__, vifname);
		/* remove related acl rule */
		#if 0
		#ifdef CONFIG_RTL8676_Dynamic_ACL
		rtl865x_acl_control_delete_all_by_netif(vdev->name);
		#endif
		rtl865x_delNetif(vdev->name);
		#endif

		dev_info = SMUX_DEV_INFO(vdev);
		#if defined(CONFIG_RTL_HW_NAPT) && !defined(CONFIG_RTL_EVENT_TRIGGER_HWNAT)
		rtl8676_unregister_Multiwan_dev(vdev->name);
		{
			char ifname_extend[IFNAMSIZ];
			strcpy(ifname_extend,vdev->name);
			strcat(ifname_extend,"_B");
			if(rtl865x_netifExist(ifname_extend))
				rtl865x_unregisterDev(ifname_extend);
		}
		#endif
		#if defined(CONFIG_RTL8685_PTM_MII) && defined(USE_PTM_MII_WANIF)
		if(is_ptmdev(vdev))
			ptm_mii_unregister_wanif( vdev->name );
		#endif /*USE_PTM_MII_WANIF*/

		#ifdef UNIQUE_MAC_PER_DEV
		freeSmuxDevVid(dev_info->vid);
		#endif
		real_dev = dev_info->smux_grp->real_dev;

		write_lock_irq(&smux_lock);
		list_del(&dev_info->list);
		write_unlock_irq(&smux_lock);

		if (list_empty(&dev_info->smux_grp->virtual_devs)) {
			write_lock_irq(&smux_lock);
			list_del(&dev_info->smux_grp->smux_grp_devs);
			write_unlock_irq(&smux_lock);

			kfree(dev_info->smux_grp);
		}

		dev_put(vdev);
		unregister_netdev(vdev);
		free_netdev(vdev);

		synchronize_net();
		dev_put(real_dev); 

		ret = 0;
	}

	return ret;
} /* smux_unregister_device */

/***************************************************************************
 * Function Name: smux_device_event
 * Description  : handles real device events to update overlay devs. status
 * Returns      : 0 on Success
 ***************************************************************************/
static int smux_device_event(struct notifier_block *unused, 
                             unsigned long event, 
                             void *ptr)
{
	struct net_device *rdev = netdev_notifier_info_to_dev(ptr);
	struct smux_group *grp = __find_smux_group(rdev->name);
	int flgs;
	struct list_head *lh;
	struct list_head *lhp;
	struct smux_dev_info *dev_info;


	if (!grp)
		goto out;

	switch (event) {
		case NETDEV_CHANGE:

			/* Propagate real device state to overlay devices */
			read_lock(&smux_lock);
			list_for_each(lh, &grp->virtual_devs) {
				dev_info = list_entry_smuxdev(lh);
				if(dev_info) {
					smux_transfer_operstate(rdev, dev_info->vdev);
				}
			}
			read_unlock(&smux_lock);
			break;

		case NETDEV_DOWN:

			/* Put all Overlay devices for this dev in the down state too.*/
			read_lock(&smux_lock);
			list_for_each(lh, &grp->virtual_devs) {
				dev_info = list_entry_smuxdev(lh);
				if(dev_info) {
					flgs = dev_info->vdev->flags;

					if (!(flgs & IFF_UP))
						continue;

					dev_change_flags(dev_info->vdev, flgs & ~IFF_UP);
				}
			}
			read_unlock(&smux_lock);
			break;

		case NETDEV_UP:

			/* Put all Overlay devices for this dev in the up state too.  */
			read_lock(&smux_lock);
			list_for_each(lh, &grp->virtual_devs) {
				dev_info = list_entry_smuxdev(lh);
				if(dev_info) {
					smux_transfer_operstate(rdev, dev_info->vdev);
					flgs = dev_info->vdev->flags;

					if (flgs & IFF_UP)
						continue;

					dev_change_flags(dev_info->vdev, flgs & IFF_UP);
				}
			}
			read_unlock(&smux_lock);
			break;

		case NETDEV_UNREGISTER:
			
			/* Delete all Overlay devices for this dev. */
			write_lock_irq(&smux_lock);
			list_for_each_safe(lh, lhp, &grp->virtual_devs) {
				dev_info = list_entry_smuxdev(lh);
				if(dev_info) {
					/* delete by l67530 for cpu0 when reboot system. HG551c.2010/12/07 */
					//list_del(&dev_info->list);
					smux_unregister_device(dev_info->vdev->name);
				}
			}
			write_unlock_irq(&smux_lock);
			break;
		}

out:
  return NOTIFY_DONE;
} /* smux_device_event */

/***************************************************************************
 * Function Name: smux_ioctl_handler
 * Description  : ioctl handler for user apps
 * Returns      : 0 on Success
 ***************************************************************************/
static int smux_ioctl_handler(void __user *arg)
{
	int err = 0;
	struct smux_ioctl_args args;
	SMUX_IGN_ENTRY *pEntry,*next, tmpEntry;

	if (copy_from_user(&args, arg, sizeof(struct smux_ioctl_args)))
		return -EFAULT;

	args.rsmux_ifname[IFNAMSIZ-1] = 0;
	args.osmux_ifname[IFNAMSIZ-1] = 0;

	switch (args.cmd) {
		case ADD_SMUX_CMD:
			if (!capable(CAP_NET_ADMIN))
				return -EPERM;
			if(smux_register_device(args.rsmux_ifname, args.osmux_ifname, args.proto, args.vid, args.napt, args.brpppoe)) {
				err = 0;
			} else {
				err = -EINVAL;
			}
			break;

		case REM_SMUX_CMD:
			if (!capable(CAP_NET_ADMIN))
				return -EPERM;
			err = smux_unregister_device(args.u.ifname);
			break;
		case IGN_SMUX_CMD:
			if(args.vid == 0){
				/* dump ignore list */
				list_for_each_entry_safe(pEntry, next, &SMUX_IGNORE_HEAD, smuxIgnoreList){
					printk("    proto=0x%x\n", pEntry->proto);
				}
			}else if((args.vid == 1) || (args.vid == 2)){
				/* 1: add ignore list */
				/* 2: del ignore list */
				if(!list_empty(&SMUX_IGNORE_HEAD)){
					list_for_each_entry_safe(pEntry, next, &SMUX_IGNORE_HEAD, smuxIgnoreList){
						if(pEntry->proto == args.proto){
							if(args.vid == 2){
								list_del(&pEntry->smuxIgnoreList);
								kfree(pEntry);
							}
							break;
						}
					}
				}

				if(args.vid == 1){
					pEntry = (SMUX_IGN_ENTRY*)kmalloc(sizeof(SMUX_IGN_ENTRY), GFP_KERNEL);	//it should free when del from list
					memset(pEntry, 0, sizeof(SMUX_IGN_ENTRY));
					if(args.proto <= 0xFFFF){
						pEntry->proto = args.proto;
						list_add_tail(&pEntry->smuxIgnoreList, &SMUX_IGNORE_HEAD);
					}
				}
			}
			break;
		default:
			printk("%s: Unknown SMUX CMD: %x \n",
				__FUNCTION__, args.cmd);
			return -EINVAL;
	}

	return err;
} /* smux_ioctl_handler */

static int vlan_passthru_write_proc(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{	  
    unsigned char chartmp; 
	  
    if (count > 1){
      	if (buffer && !copy_from_user(&chartmp, buffer, 1)){
	    	vlan_passthru = chartmp - '0';				
			printk("vlan passthru  : %s\n", vlan_passthru ? "Enable" : "Disable");	
      	}
	} else {
		printk("write fail\n");
		return -EFAULT;
	}

	return count;
}

static int vlan_passthru_read_proc(struct seq_file *f, void *data)
{	  
      seq_printf(f, "vlan passthru  : %s\n",  vlan_passthru ? "Enable" : "Disable");

      return 0;
}

static int read_proc_open_vlan_passthru(struct inode *inode, struct file *file) {
    return(single_open(file, vlan_passthru_read_proc, NULL));                                                                                                                                                        
}

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

static struct file_operations fops_proc_vlan_passthru = {
    .open     = read_proc_open_vlan_passthru,   
    .read     = seq_read,
    .llseek   = seq_lseek,
    .release  = single_release,    
    .write    = write_proc_vlan_passthru,                                                                                                                                                                            
};

extern struct proc_dir_entry *realtek_proc;
static struct proc_dir_entry *vlan_passthru_proc=NULL;

static int initVlanPassThruProc(void)
{
	vlan_passthru_proc = proc_create_data("vlan_passthru", 0644, realtek_proc, &fops_proc_vlan_passthru, NULL);	
	if (!vlan_passthru_proc) {
		printk("Realtek SMUX VLan PassThru, create proc failed!\n");
	}

	return 0;
}

/***************************************************************************
 * Function Name: smux_drv_init
 * Description  : Initialization of smux driver
 * Returns      : struct net_device
 ***************************************************************************/
static int __init smux_drv_init(void)
{
	register_netdevice_notifier(&smux_notifier_block);
	smux_ioctl_set(smux_ioctl_handler);
	initVlanPassThruProc();

	INIT_LIST_HEAD(&SMUX_IGNORE_HEAD);
	return 0;
} /* smux_drv_init */

/***************************************************************************
 * Function Name: smux_cleanup_devices
 * Description  : cleans up all the smux devices and releases memory on exit
 * Returns      : None
 ***************************************************************************/
static void __exit smux_cleanup_devices(void)
{
	struct net_device *dev;
	struct list_head *lh;
	struct list_head *lhp;
	struct smux_dev_info *dev_info;
	struct smux_group *grp;


	/* clean up all the smux devices */
	rtnl_lock();
	for_each_netdev(&init_net, dev)
	{
		if (dev->priv_flags & IFF_OSMUX) {
			dev_info = SMUX_DEV_INFO(dev);
			write_lock_irq(&smux_lock);
			list_del(&dev_info->list);
			write_unlock_irq(&smux_lock);
			unregister_netdevice(dev);
		}
	}
	rtnl_unlock();

	/* cleanup all smux groups  */
	write_lock_irq(&smux_lock);
	list_for_each_safe(lh, lhp, &smux_grp_devs) {
		grp = list_entry_smuxgrp(lh);
		if(grp) {
			list_del(&grp->virtual_devs);
		}
	}
	write_unlock_irq(&smux_lock);
} /* smux_cleanup_devices */

/***************************************************************************
 * Function Name: smux_drv_exit
 * Description  : smux module clean routine
 * Returns      : None
 ***************************************************************************/
static void __exit smux_drv_exit(void)
{
	smux_ioctl_set(NULL);

	/* Un-register us from receiving netdevice events */
	unregister_netdevice_notifier(&smux_notifier_block);
	smux_cleanup_devices();
	synchronize_net();
} /* smux_drv_exit */

struct net_device* getSmuxRealDev(struct net_device *vdev)
{
	struct smux_dev_info *dev_info;

	if(vdev==NULL) 
		return NULL;

	dev_info = SMUX_DEV_INFO(vdev);
	if(dev_info && 
		dev_info->smux_grp &&
		dev_info->smux_grp->real_dev)
	{
		return dev_info->smux_grp->real_dev;
	}
	return NULL;
}

int getVidOfSmuxDev(char *name)
{
	struct smux_dev_info *dev_info;
	struct net_device *vdev;


	vdev = dev_get_by_name(&init_net, name);
	if (!vdev)
		return -1;

	dev_put(vdev);
	
	dev_info = SMUX_DEV_INFO(vdev);

	if (dev_info->vid > 0 && dev_info->vid < 4096)
	{
		return dev_info->vid;
	}
	else 
	{
		if (dev_info->proto == SMUX_PROTO_BRIDGE)
			return RTL_BridgeWANVLANID;
		else
			return RTL_WANVLANID;
	}
}

#if 0 /*move to rtl_nic.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

/*attention: curridx>=11 is reserved for ppp interface*/
#if 0
int getVlanconfigIdx(struct rtl865x_vlanConfig *vconfig, int *curridx, int vid)
#endif
#if 0
int getVlanconfigIdx(struct rtl865x_vlanConfig *vconfig, int vid)
{
	unsigned int i;
	
	#if 0
	if (vconfig[1].vid && (vid == vconfig[1].vid))//get
	{
		if (*curridx == 0)
			*curridx = 1;
		return 1;
	}

	for (i=5; (i<=*curridx) && (i<=10); i++)
	{
		if (vconfig[i].vid && (vconfig[i].vid == vid))//get
			return i;
	}

	*curridx = (*curridx==1)?5:(*curridx+1);
	
	return (*curridx);
	#else 
	for(i=0; vconfig[i].ifname[0] != '\0' ; i++)
	{
		//if( vconfig[i].vid && vconfig[i].isWan==1 && vconfig[i].is_slave==0)
		if( vconfig[i].vid && vconfig[i].is_slave==0)
		{
			if(vconfig[i].vid == vid)		
				return i;			
		}
	}
	for(i=0; vconfig[i].ifname[0] != '\0' ; i++)
	{
		//if( !vconfig[i].vid && vconfig[i].isWan==1 && vconfig[i].is_slave==0)	
		if( !vconfig[i].vid && vconfig[i].is_slave==0)	
			return i;
	}
	return -1;
	#endif
}
#endif
/* vconfig: vlanconfig */
/*
 * BRIDGE: vlan enable      vid=CUSTOM_VAL
 *               vlan disable      vid=RTL_LANVLANID
 * ROUTE:  vlan enable      vid=CUSTOM_VAL
 *               vlan disable      vid=RTL_LANVLANID
 */
#if 0
void rtl_adjust_wanport_vlanconfig(struct rtl865x_vlanConfig *vconfig,int *curr_idx)
{
	struct smux_group *grp;
	struct list_head *lh;
	struct smux_dev_info * sdev = NULL;
	//int vid=1;
	/* vlanconfig index 1,5-10 is for wan interface. */
	#if 0
	int idx=0, curridx=0;
	#else
	int idx=0;
	#endif

#ifdef  CONFIG_RTL_ALIASNAME
    	grp = __find_smux_group(ALIASNAME_NAS0);
#else
	grp = __find_smux_group("nas0");
#endif	
	if (!grp)
		return;

	/*init vconfig firstly */
	#if 0
	vconfig[1].vid = 0;
	vconfig[1].untagSet = RTL_WANPORT_MASK;
	vconfig[1].memPort = RTL_WANPORT_MASK;
	for (idx=5; idx<11; idx++)
	{
		vconfig[idx].vid = 0;
		vconfig[idx].untagSet = RTL_WANPORT_MASK;
		vconfig[idx].memPort = RTL_WANPORT_MASK;
	}
	#else


	
	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN0_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_ETHER;
	vconfig[*curr_idx].vid 		= 0;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 0;
	(*curr_idx)++;

	

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN1_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_ETHER;
	vconfig[*curr_idx].vid 		= 0;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 0;
	(*curr_idx)++;

	

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN2_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_ETHER;
	vconfig[*curr_idx].vid 		= 0;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 0;
	(*curr_idx)++;

	

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN3_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_ETHER;
	vconfig[*curr_idx].vid 		= 0;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 0;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN4_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_ETHER;
	vconfig[*curr_idx].vid 		= 0;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 0;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN5_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_ETHER;
	vconfig[*curr_idx].vid 		= 0;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 0;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_WAN6_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_ETHER;
	vconfig[*curr_idx].vid 		= 0;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 0;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP0_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_PPPOE;
	vconfig[*curr_idx].vid 		= RTL_WANVLANID;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 1;
	(*curr_idx)++;

	

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP1_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_PPPOE;
	vconfig[*curr_idx].vid 		= RTL_WANVLANID;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 1;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP2_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_PPPOE;
	vconfig[*curr_idx].vid 		= RTL_WANVLANID;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 1;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP3_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_PPPOE;
	vconfig[*curr_idx].vid 		= RTL_WANVLANID;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 1;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP4_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_PPPOE;
	vconfig[*curr_idx].vid 		= RTL_WANVLANID;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 1;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP5_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_PPPOE;
	vconfig[*curr_idx].vid 		= RTL_WANVLANID;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 1;
	(*curr_idx)++;

	//strcpy(vconfig[*curr_idx].ifname,RTL_DRV_PPP6_NETIF_NAME);
	vconfig[*curr_idx].isWan 		= 1;	
	vconfig[*curr_idx].if_type 		= IF_PPPOE;
	vconfig[*curr_idx].vid 		= RTL_WANVLANID;
	vconfig[*curr_idx].fid 		= RTL_WAN_FID;
	vconfig[*curr_idx].memPort 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].untagSet 	= RTL_WANPORT_MASK;
	vconfig[*curr_idx].is_slave 	= 1;
	(*curr_idx)++;
	
	#endif
	//for (i=0; i<RTL8651_PORT_NUMBER+3; i++)
	//	pvid_per_port[i]=RTL_LANVLANID;	
	
	read_lock(&smux_lock);
	list_for_each(lh, &grp->virtual_devs)
	{
		sdev = list_entry_smuxdev(lh);
		
		if (sdev->vid != -1) {
			idx = getVlanconfigIdx(vconfig, sdev->vid);
			if (idx <0)
				break;
			vconfig[idx].vid = sdev->vid;
			#ifdef UNIQUE_MAC_PER_DEV
			if (sdev->vid > 8)
			#endif
				//vconfig[idx].untagSet = 0;
		}
		else {
			if (sdev->proto == SMUX_PROTO_BRIDGE)
				idx = getVlanconfigIdx(vconfig, RTL_BridgeWANVLANID);
			else
				idx = getVlanconfigIdx(vconfig, RTL_WANVLANID);
			if (idx <0)
				break;

			if (sdev->proto == SMUX_PROTO_BRIDGE)
				vconfig[idx].vid = RTL_BridgeWANVLANID;
			else
				vconfig[idx].vid = RTL_WANVLANID;
	
		}
		
		//skip interface name set if this vconfig[idx] had been set to a route intf.
		if((vconfig[idx].memPort!=RTL_WANPORT_MASK) || (vconfig[idx].untagSet!=0) || (sdev->proto != SMUX_PROTO_BRIDGE)){
		strcpy(vconfig[idx].ifname, sdev->vdev->name);
		memcpy(vconfig[idx].mac.octet, sdev->vdev->dev_addr, ETH_ALEN);
		}
		
		if (sdev->proto == SMUX_PROTO_BRIDGE)
		{
			//vconfig[idx].fid = RTL_WAN_FID;
			
			//set membership
			if (sdev->member == 0xFFFFFFFF) {
				vconfig[idx].memPort |= (RTL_LANPORT_MASK | RTL_WANPORT_MASK);
				vconfig[idx].untagSet |= (RTL_LANPORT_MASK | RTL_WANPORT_MASK);
			}
			else {
				vconfig[idx].memPort |= ((sdev->member&RTL_LANPORT_MASK)|0x100);
				vconfig[idx].untagSet |= ((sdev->member&RTL_LANPORT_MASK)|0x100);
				//for (i=1; i<RTL8651_PORT_NUMBER; i++) {
				//	if (vconfig[idx].memPort & (1<<i))
				//		pvid_per_port[i] = vconfig[idx].vid;
				//}
			}
		}
		else{
			//set napt
			vconfig[idx].isWan = sdev->napt;
		}

		if ((sdev->vid != -1)
			#ifdef UNIQUE_MAC_PER_DEV
			&& (sdev->vid > 8)
			#endif
			) {
			vconfig[idx].untagSet &= (~RTL_WANPORT_MASK);
		}
	}
	read_unlock(&smux_lock);
}
#endif


int smuxUpstreamPortmappingCheck(struct sk_buff *skb)
{
	struct net_device *from, *to;
	struct smux_dev_info *dev_info,*dev_info2;
	unsigned int member, port;

	from = skb->from_dev;
	to = skb->dev;

	dev_info = SMUX_DEV_INFO(to);
		dev_info2 = SMUX_DEV_INFO(from);
	member = dev_info->member;
	
    if (from->priv_flags & IFF_DOMAIN_ELAN)
	{
		//sscanf(from->name, "eth0.%d", &port);
		TOKEN_NUM(from->name,&port);
		port -= 1;
	}
	else if (from->priv_flags & IFF_DOMAIN_WLAN)
	{
	    TOKEN_NUM(from->name,&port);
//		sscanf(from->name, "wlan0-vap%d", &port);
	}      

	else {
		//printk("%s not from lan dev.\n", __func__);
		return 0;
	}

	if (member & (1<<port)) {
		return 1;
	}
	else {
		//printk("%s skb block in %s\n", __func__, to->name);
		return 0;
	}
}

int smuxDownstreamPortmappingCheck(struct sk_buff *skb)
{
	struct net_device *from, *to;
	struct smux_dev_info *dev_info,*dev_info2;
	unsigned int member, port;

	from = skb->from_dev;
	to = skb->dev;

    
	dev_info = SMUX_DEV_INFO(from);
		dev_info2 = SMUX_DEV_INFO(to);
	//member=skb->vlan_member;
	member = dev_info->member;

    if (to->priv_flags & IFF_DOMAIN_ELAN)
	{
		//sscanf(to->name, "eth0.%d", &port);
		TOKEN_NUM(to->name,&port);
		port -= 1;
	}
	else if (to->priv_flags & IFF_DOMAIN_WLAN)
	{
	    TOKEN_NUM(to->name,&port);
//		sscanf(to->name, "wlan0-vap%d", &port);
	}    

	else {
	//	printk("%s not from lan dev.\n", __func__);
		return 0;
	}

	if (member & (1<<port)) {
	//printk("%s %d from %s to %s member0x%x  member & (1<<port)=true    \n", __func__, __LINE__, from->name, to->name, member);
		return 1;
	}


	//printk("%s %d from %s to %s member0x%x\n", __func__, __LINE__, from->name, to->name, member);

	return 0;
}
#if 0
int smuxDevMacUpdate(unsigned char *pmac)
{
	struct smux_group *grp;
	struct list_head *lh;
	struct smux_dev_info * sdev = NULL;
	//int i;


	grp = __find_smux_group("nas0");
	if (!grp)
		return;

	read_lock(&smux_lock);
	list_for_each(lh, &grp->virtual_devs)
	{
		sdev = list_entry_smuxdev(lh);
		
		if (sdev->proto != SMUX_PROTO_BRIDGE)
		{
			smux_dev_set_mac_address(sdev->vdev, pmac);
		}
	}
	read_unlock(&smux_lock);
}
#endif

#ifdef CONFIG_PORT_MIRROR
#define QOS_1P_ENABLE		(1<<8)
static inline struct sk_buff *smux_mirror_tx_vlan(struct sk_buff *skb, const struct smux_dev_info *dev_info, unsigned int vid, unsigned int priority)
{
	struct sk_buff *skb2, *tmpskb;
	unsigned char insert[16];
	u16 *vlan_tci;
	//struct ethhdr *eth;
	struct vlan_ethhdr *veth;
	
	skb2 = skb_copy(skb, GFP_ATOMIC);
	
	if (skb_headroom(skb2) < 4) {
		tmpskb = skb_realloc_headroom(skb2, 4);
		dev_kfree_skb_any(skb2);
		if (tmpskb == NULL)
			return 0;
		skb2 = tmpskb;
	}
	
	skb2->dev = dev_info->mirror_dev;
	if (skb2->mark&QOS_1P_ENABLE) { // IP_QoS 802.1p remarking
		insert[0] = ((skb2->mark&0x7)<<5) | (vid>>8);
	}
	else
		insert[0] = (priority<<5) | (vid>>8);
	insert[1] = vid&0xff;
	vlan_tci = (u16 *)insert;
	
	veth = (struct vlan_ethhdr *)skb_push(skb2, VLAN_HLEN);
	/* Move the mac addresses to the beginning of the new header. */
	memmove(skb2->data, skb2->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);
	return skb2;
}

static inline void smux_mirror_pkt(struct sk_buff *skb, 
					const struct smux_dev_info *dev_info, const int flag)
{
	struct sk_buff *skb2=0;
	//AUG_DBG("the dev_info->port_mirror is %d\n", dev_info->port_mirror);
	if ((OUT == flag) && (skb->vlan_tci&VLAN_TAG_PRESENT)) { // insert vlan tag
		skb2 = smux_mirror_tx_vlan(skb, dev_info, skb->vlan_tci&VLAN_VID_MASK, (skb->vlan_tci>>VLAN_PRIO_SHIFT)&0x07);
	}
	else
	if ((skb2 = skb_clone(skb, GFP_ATOMIC)) != NULL) 
	{		
		skb2->dev = dev_info->mirror_dev;

		if(IN == flag)
			skb_push(skb2, ETH_HLEN);
	}
	//AUG_DBG("the dev_info->mirror_dev is %s\n", dev_info->mirror_dev->name);
	if (skb2)
		nic_tx_mirror(skb2);
}
#endif
module_init(smux_drv_init);
module_exit(smux_drv_exit);

EXPORT_SYMBOL(smux_pkt_recv);