/***************************************************************************
 * File Name    : rtk_rg_wmux.c
 * Description  : wmux mean wan mux.
 ***************************************************************************/
 
#include <linux/ip.h>
#include <linux/rtnetlink.h>
#include <linux/ppp_defs.h>
#include <linux/if_pppox.h>
#include <net/rtl/rtl_alias.h>
#include <rtk_rg_internal.h>

//#ifdef CONFIG_RG_DEBUG
#include <rtk_rg_debug.h>
//#endif

#include "rtk_rg_wmux.h"


/***************************************************************************
                         Global variables 
 ***************************************************************************/
static DEFINE_RWLOCK(wmux_lock);

static LIST_HEAD(total_wmux_grp_devs_head);

static struct notifier_block wmux_notifier_block;/* = {
	.notifier_call = wmux_device_event,
};*/

/***************************************************************************
                         Function Definisions
 ***************************************************************************/
static inline struct wmux_group *list_entry_wmuxgrp(const struct list_head *le)
{
	return list_entry(le, struct wmux_group, grp_list);
}

/***************************************************************************
 * Function Name: __find_wmux_group
 * Description  : returns the wmux group of interfaces/devices from list
 * Returns      : struct wmux_group.
 ***************************************************************************/
static struct wmux_group *__find_wmux_group(const char *ifname)
{
	struct list_head *lh;
	struct wmux_group *wmux_grp;
	struct wmux_group *ret_wmux = NULL;

	read_lock(&wmux_lock);
	list_for_each(lh, &total_wmux_grp_devs_head) {
		wmux_grp = (struct wmux_group *)list_entry_wmuxgrp(lh);
		if (wmux_grp && !strncmp(wmux_grp->real_dev->name, ifname, IFNAMSIZ)) {
			ret_wmux = wmux_grp;
			break;
		}
	}
	read_unlock(&wmux_lock);

	return ret_wmux;
} /* __find_wmux_group */

static inline struct wmux_dev_info *list_entry_wmuxdev(const struct list_head *le)
{
  return list_entry(le, struct wmux_dev_info, wdev_list);
}

/***************************************************************************
 * Function Name: __find_wmux_in_wmux_group
 * Description  : returns the wmux device from wmux group of devices 
 * Returns      : struct net_device
 ***************************************************************************/
static struct net_device *__find_wmux_in_wmux_group(
                                     struct wmux_group *wmux_grp, 
                                     const char *ifname)
{
	struct list_head *lh;
	struct wmux_dev_info * wdev = NULL;
	struct net_device    * ret_dev = NULL;

	read_lock(&wmux_lock);
	list_for_each(lh, &wmux_grp->virtual_devs_head) {
		wdev = list_entry_wmuxdev(lh);
		if(wdev && !strncmp(wdev->vdev->name, ifname, IFNAMSIZ)) {
			ret_dev = wdev->vdev;
			break;
		}
	}
	read_unlock(&wmux_lock);

	return ret_dev;
} /* __find_wmux_in_wmux_group */

static inline struct sk_buff *wmux_reorder_header(struct sk_buff *skb, struct vlan_hdr *vhdr, unsigned short vlan_tci, unsigned char keep_order)
{
	skb_pull_rcsum(skb, VLAN_HLEN);
		
	if(keep_order)	//move pointer only
	{
		skb->vlan_tci =(vlan_tci & VLAN_VID_MASK)+1;
		skb->mark =((vlan_tci >> 13)& 0x7)+1;						  
		skb->protocol=vhdr->h_vlan_encapsulated_proto;
		skb_reset_network_header(skb);
	}
	else	//memove to remove tag in skb
	{
		if(skb_cow(skb, skb_headroom(skb)) < 0)
			return NULL;
		skb->protocol=vhdr->h_vlan_encapsulated_proto;
		memmove(skb->data - ETH_HLEN, skb->data - VLAN_ETH_HLEN, 12);
		skb->mac_header += VLAN_HLEN;
	}

	return skb;
}



void _wmux_debug_recv(struct sk_buff *skb)
{
	char buf[64];
	snprintf(buf,64,"WMUX[%x] rx_dump:len=%d, dev=%s, proto=%04x",(unsigned int)skb->data&0xffff,skb->len,skb->dev->name,skb->protocol);
	if(rg_kernel.debug_level&RTK_RG_DEBUG_LEVEL_WMUX)
	{
		int32 show=1;
		if(rg_kernel.filter_level&RTK_RG_DEBUG_LEVEL_WMUX)
		{	
			//show=_rtk_rg_trace_filter_compare(rg_db.pktHdr->skb,rg_db.pktHdr);
			show = rg_kernel.tracefilterShow;
		}	
		if(show)
			memDump(skb->data,skb->len,buf);
	}


	if(rg_db.systemGlobal.psRxMirrorToPort0)
	{
		skb->data-=14;
		skb->len+=14;
		_rtk_rg_psRxMirrorToPort0(skb,skb->dev);
		skb->data+=14;
		skb->len-=14;
	}	
	netif_rx(skb);
}

/***************************************************************************
 * Function Name: wmux_pkt_recv
 * Description  : packet recv routine for all wmux devices from real dev.
 * Returns      : 0 on Success
 ***************************************************************************/
int wmux_pkt_recv(struct sk_buff *skb, struct net_device *dev)
{
	unsigned char *dstAddr;
	struct sk_buff *skb2=NULL;
	struct iphdr *network_header;  // ip header struct
	struct wmux_group *grp;
	struct wmux_dev_info *dev_info;
	struct wmux_dev_info *dev_info_first;
	struct list_head *lh;
	unsigned short protocol,ppp_protocol=0;
	unsigned short vlan_tci=0;
	struct vlan_hdr *vhdr=NULL;
	rtk_rg_ipStaticInfo_t *p_wanStaticInfo=NULL;
	char isTxDone = 0;
	char l3Choosed = 0;

	if(!dev) 
	{
		WMUX("null dev....free skb!");
		_rtk_rg_dev_kfree_skb_any(skb);
		return RE8670_RX_STOP_SKBNOFREE;	//the SKB had been sended or droped, kfree is no need
	}

	grp = __find_wmux_group(dev->name);
	if(!grp) 
	{
		WMUX("%s not wmux device...continue to protocol stack",dev->name);	
		WMUX("skb->protocol is %x, vlan_tci is %d",skb->protocol,skb->vlan_tci);
		WMUX("skb->dev is %s, from_dev is %s",skb->dev->name,skb->from_dev->name);
		WMUX("skb->switch_port is %s, mark is %x",skb->switch_port,skb->mark);
		return RE8670_RX_CONTINUE;
	}

	//dump_packet(skb->data,skb->len,"wmux skb1");

	skb->protocol = eth_type_trans (skb, dev);
	if(skb->protocol==htons(ETH_P_802_2))
	{
		WMUX("the ethernet type can't be recognized(maybe 802.2 LLC)...continue to protocol stack");
		return RE8670_RX_CONTINUE;
	}
	dstAddr = eth_hdr(skb)->h_dest;
	//dump_packet(skb->data,skb->len,"wmux skb2");
	//WMUX("enter=================>protocol is %x\n", skb->protocol);
	
	read_lock(&wmux_lock);

	protocol=skb->protocol;
	WMUX("protocol is %x",protocol);
	skb_reset_network_header(skb);
	if(skb->protocol == htons(ETH_P_8021Q)) 
	{
		vhdr = (struct vlan_hdr *)skb->data;
		vlan_tci = ntohs(vhdr->h_vlan_TCI);
		protocol=vhdr->h_vlan_encapsulated_proto;
		WMUX("h_vlan_encapsulated_proto is 0x%x, h_vlan_TCI is 0x%x", protocol, vlan_tci);
		skb_set_network_header(skb,VLAN_HLEN);
	}		
	//WMUX("skb->data[0] is %x",skb->data[0]);
	//WMUX("dstAddr[0] is %x",dstAddr[0]);
	/* Multicast Traffic will go on all intf.*/
	if(dstAddr[0] & 1)
	{
		WMUX("Multicast to all VLAN-matched devices...");
		dev_info_first = NULL;

		list_for_each(lh, &grp->virtual_devs_head)
		{
			dev_info = list_entry_wmuxdev(lh);

			//if dev's VLAN don't match, pass
			if (((vlan_tci&VLAN_VID_MASK) && !(dev_info->vid>=0)) ||
				((dev_info->vid>=0) && (!(vlan_tci&VLAN_VID_MASK) || ((vlan_tci&VLAN_VID_MASK)!=dev_info->vid))))
	  			continue;

			//if dev PPPoE but packet is, pass
			if (((dev_info->proto == RTK_RG_PPPoE) && (protocol != htons(ETH_P_PPP_DISC)) && (protocol != htons(ETH_P_PPP_SES))) ||
				((dev_info->proto != RTK_RG_PPPoE) && ((protocol == htons(ETH_P_PPP_DISC)) || (protocol == htons(ETH_P_PPP_SES)))))
			{
				WMUX("packet dropped on RX dev %s", dev_info->vdev->name);
				continue;
			}



			//WMUX("intf_idx=%d isWan=%d ingressPort=%d WanNetdevPort=%d",dev_info->intf_idx,rg_db.systemGlobal.interfaceInfo[dev_info->intf_idx].storedInfo.is_wan,rg_db.pktHdr->ingressPort,rg_db.systemGlobal.interfaceInfo[dev_info->intf_idx].storedInfo.wan_intf.wan_intf_conf.wan_port_idx);
			if(rg_db.systemGlobal.interfaceInfo[dev_info->intf_idx].storedInfo.is_wan==1)
			{
				if(rg_db.pktHdr->ingressPort!= rg_db.systemGlobal.interfaceInfo[dev_info->intf_idx].storedInfo.wan_intf.wan_intf_conf.wan_port_idx)
				{
					WMUX("wanDevice %s isn't binding at port %d, don't flood to this net_dev.",dev_info->vdev->name,rg_db.pktHdr->ingressPort);
					continue;
				}
			}

			//keep first dev_info, find next
			if(!dev_info_first) 
			{
				dev_info_first = dev_info;
				continue;
			}			
	
			skb2 = _rtk_rg_skb_clone(skb,GFP_ATOMIC);
			dev_info->stats.rx_packets++;
			dev_info->stats.rx_bytes += skb2->len;
			skb2->dev = dev_info->vdev;
			skb2->from_dev = dev_info->vdev;

			//based on proto and flag to decide VLAN tag order
			if(dev_info->proto != RTK_RG_BRIDGE && vhdr!=NULL)	//bridge don't change order and don't move pointer
			{
				if(wmux_reorder_header(skb2,vhdr,vlan_tci,dev_info->keep_order)==NULL)
					goto errout;
			}

			WMUX("send to %s(copy)!",skb2->dev->name);
			if(rg_db.systemGlobal.fwdStatistic)
				rg_db.systemGlobal.statistic.perPortCnt_ToPS[rg_db.pktHdr->ingressPort]++;			
			_wmux_debug_recv(skb2);

			
		}

		if (!dev_info_first) 	//no any matched VLAN interface
		{
			//_rtk_rg_dev_kfree_skb_any(skb);
			WMUX("send to %s!",skb->dev->name);	
			_wmux_debug_recv(skb);			
		}
		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;
			
			//based on proto and flag to decide VLAN tag order
			if(dev_info_first->proto != RTK_RG_BRIDGE && vhdr != NULL)	//bridge don't change order and don't move pointer
			{
				WMUX("reorder! keep is %d",dev_info_first->keep_order);
				if(wmux_reorder_header(skb,vhdr,vlan_tci,dev_info_first->keep_order)==NULL)
					goto errout;
			}

			WMUX("skb->protocol is %x",skb->protocol);
			WMUX("send to %s!",skb->dev->name);	
			_wmux_debug_recv(skb);
		}	
		isTxDone = 1;		
	}
	else /* route Traffic.*/
	{
		WMUX("check matched interface...");
		dev_info_first = NULL;

		list_for_each(lh, &grp->virtual_devs_head)
		{
			dev_info = list_entry_wmuxdev(lh);

			//if dev is bridge, pass
			//if (dev_info->proto == RTK_RG_BRIDGE)
				//continue;

			//if dev's VLAN don't match, pass
			if (((vlan_tci&VLAN_VID_MASK) && !(dev_info->vid>=0)) ||
				((dev_info->vid>=0) && (!(vlan_tci&VLAN_VID_MASK) || ((vlan_tci&VLAN_VID_MASK)!=dev_info->vid))))
				continue;

			//if dev not PPPoE but packet is, pass
			if (((dev_info->proto == RTK_RG_PPPoE) && (protocol != htons(ETH_P_PPP_DISC)) && (protocol != htons(ETH_P_PPP_SES))) ||
				((dev_info->proto != RTK_RG_PPPoE) && ((protocol == htons(ETH_P_PPP_DISC)) || (protocol == htons(ETH_P_PPP_SES)))))
			{
				WMUX("packet bypassed on RX dev %s",dev_info->vdev->name);
				continue;
			}

			//20141226LUKE: keep first vlan-matching L2 WAN, if no L34 match, we choose this WAN!!
			if((!dev_info_first&&!l3Choosed)&&(dev_info->proto==RTK_RG_BRIDGE))dev_info_first = dev_info;
			if((dev_info->proto!=RTK_RG_BRIDGE)&&(!memcmp(dstAddr, dev_info->vdev->dev_addr, ETH_ALEN)))
			{
				WMUX("Routing or NAPT Wan Interface[%d]",dev_info->intf_idx);
				p_wanStaticInfo=rg_db.systemGlobal.interfaceInfo[dev_info->intf_idx].p_wanStaticInfo;
				//20141226LUKE: keep first routing WAN
				//20150129LUKE: store all matching routing WAN number
				//if((p_wanStaticInfo)&&(!p_wanStaticInfo->napt_enable))
				if((p_wanStaticInfo)&&((!p_wanStaticInfo->napt_enable)|| ( (p_wanStaticInfo->ip_version==IPVER_V6ONLY || p_wanStaticInfo->ip_version==IPVER_V4V6)&&(protocol==0x86dd)/*ipv6 always routing*/)    ))
				{
					WMUX("l3Choosed",l3Choosed);
					if(!(l3Choosed++))
						dev_info_first = dev_info;
					else
						dev_info_first = NULL;	//multi-match in L3

					if(dev_info_first!=NULL){
						WMUX("dev_info_first = intf[%d]",dev_info_first->intf_idx);
					}
						
					continue;
				}

				WMUX("more intf match VLAN+MAC...compare IP for NAPT");
				//we are more interfaces match VLAN+MAC, so compare IP address to find out which one to use
				if(protocol == htons(ETH_P_PPP_SES)) 
				{
					if(ppp_protocol == 0)
					{
						skb_set_network_header(skb,skb_network_header(skb)-skb->data+sizeof(struct pppoe_hdr)+2);
						ppp_protocol = *((unsigned short *)(skb_network_header(skb)-2));
					}
					WMUX("the protocol of PPPOE is %x",ppp_protocol);
					if (ppp_protocol != htons(PPP_IP))	//if the packet is not ip, check next intf
						continue;
				}
				//else if (protocol != htons(ETH_P_IP))	//if the packet is not ip, check next intf
				else if (!((protocol == htons(ETH_P_IP))||(protocol == htons(ETH_P_ARP))))	//if the packet is not ip/arp, check next intf
					continue;

				if(protocol == htons(ETH_P_IP))
				{
					network_header = ip_hdr(skb);
					if((p_wanStaticInfo)&&(p_wanStaticInfo->ip_addr!=ntohl(network_header->daddr)))	//IP different
						continue;
				}
				else if(protocol == htons(ETH_P_ARP))
				{
					WMUX("ARP packet DIP=%x WAN=%x",rg_db.pktHdr->ipv4Dip,p_wanStaticInfo->ip_addr);
					if((p_wanStaticInfo)&&(p_wanStaticInfo->ip_addr!=rg_db.pktHdr->ipv4Dip)) //IP different
						continue;

				}

				//L4 MATCH IP!!
				skb->dev = dev_info->vdev;
				skb->from_dev = dev_info->vdev;
				dev_info->stats.rx_packets++;
				dev_info->stats.rx_bytes += skb->len;
				skb->pkt_type = PACKET_HOST;
				//rtlglue_printf("(route) receive from %s\n", vdev->name);

				//based on proto and flag to decide VLAN tag order
				if(dev_info->proto!=RTK_RG_BRIDGE && vhdr!=NULL)
				{
					if(wmux_reorder_header(skb,vhdr,vlan_tci,dev_info->keep_order)==NULL)
						goto errout;
				}

				WMUX("send to %s!",skb->dev->name);
				_wmux_debug_recv(skb);

				isTxDone = 1;
				dev_info_first=NULL;	//since this interface match IP, dev_info_first should not match!
				break;
			}
		}

		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;
			//rtlglue_printf("(route) receive from %s\n", dev_info_first->vdev->name);

			//based on proto and flag to decide VLAN tag order
			if(dev_info_first->proto!=RTK_RG_BRIDGE && vhdr!=NULL)
			{
				if(wmux_reorder_header(skb,vhdr,vlan_tci,dev_info_first->keep_order)==NULL)
					goto errout;
			}

			WMUX("send to %s!",skb->dev->name);
			_wmux_debug_recv(skb);
			
			isTxDone = 1;
		}
	}

	if(!isTxDone)
	{
		dev_info_first=NULL;
		WMUX("do flooding...");
		list_for_each(lh, &grp->virtual_devs_head)
		{
			dev_info = list_entry_wmuxdev(lh);
			//if (dev_info->proto != RTK_RG_BRIDGE)
				//continue;

			//if dev's VLAN don't match, pass
			if (((vlan_tci&VLAN_VID_MASK) && !(dev_info->vid>=0)) ||
				((dev_info->vid>=0) && (!(vlan_tci&VLAN_VID_MASK) || ((vlan_tci&VLAN_VID_MASK)!=dev_info->vid))))
				continue;

			if(l3Choosed &&((dev_info->proto==RTK_RG_BRIDGE)||((p_wanStaticInfo)&&(p_wanStaticInfo->napt_enable))||(memcmp(dstAddr, dev_info->vdev->dev_addr, ETH_ALEN))))
				continue;

			//keep first dev_info, find next
			if(!dev_info_first) 
			{
				dev_info_first = dev_info;
				continue;
			}

			skb2 = _rtk_rg_skb_clone(skb,GFP_ATOMIC);
			skb2->dev = dev_info->vdev;
			skb2->from_dev = dev_info->vdev;
			dev_info->stats.rx_packets++;
			dev_info->stats.rx_bytes += skb2->len; 
			//skb2->pkt_type = PACKET_OTHERHOST;
			skb2->pkt_type = PACKET_HOST;

			//bridge don't change order and don't move pointer
			//based on proto and flag to decide VLAN tag order
			if(dev_info->proto!=RTK_RG_BRIDGE && vhdr!=NULL)
			{
				if(wmux_reorder_header(skb2,vhdr,vlan_tci,dev_info->keep_order)==NULL)
					goto errout;
			}

			WMUX("send to %s(copy)!",skb2->dev->name);
			if(rg_db.systemGlobal.fwdStatistic)
				rg_db.systemGlobal.statistic.perPortCnt_ToPS[rg_db.pktHdr->ingressPort]++;			
			_wmux_debug_recv(skb2);

			
		}

		if (!dev_info_first) 	//no any matched VLAN Bridge interface
		{
			//_rtk_rg_dev_kfree_skb_any(skb);

			WMUX("send to %s!",skb->dev->name);			
			_wmux_debug_recv(skb);			
		}
		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_OTHERHOST;
			skb->pkt_type = PACKET_HOST;

			//bridge don't change order and don't move pointer
			//based on proto and flag to decide VLAN tag order
			if(dev_info_first->proto!=RTK_RG_BRIDGE && vhdr!=NULL)
			{
				if(wmux_reorder_header(skb,vhdr,vlan_tci,dev_info_first->keep_order)==NULL)
					goto errout;
			}

			WMUX("send to %s!",skb->dev->name);			
			_wmux_debug_recv(skb);
		}
		isTxDone = 1;
	}

errout:
	if(!isTxDone) 
	{
		WMUX("dropping packet that has wrong dest. on RX dev %s", dev->name);
		if(skb)_rtk_rg_dev_kfree_skb_any(skb);
		if(skb2)_rtk_rg_dev_kfree_skb_any(skb2);
	}

	return RE8670_RX_STOP_SKBNOFREE;	//the SKB had been sended or droped, kfree is no need
} /* wmux_pkt_recv */

/***************************************************************************
 * Function Name: wmux_dev_hard_start_xmit
 * Description  : xmit routine for all wmux devices on real dev.
 * Returns      : 0 on Success
 ***************************************************************************/
int wmux_dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
	struct net_device_stats *stats = wmux_dev_get_stats(dev);
	struct wmux_dev_info *dev_info;

	if(stats){
		stats->tx_packets++; 
		stats->tx_bytes += skb->len;
	}

	dev_info = WMUX_DEV_INFO(dev);
	skb->dev = dev_info->wmux_grp->real_dev;

	WMUX("WMUX_TX[%x]: from interface: %s",(unsigned int)skb->data&0xffff,dev->name);
	
	if (-1 == dev_info->vid) {
		skb->vlan_tci = 0;
	}
	else
	{

		WMUX("skb->dev is %s, vid is %d  pri=%d, proto is %s",skb->dev->name,dev_info->vid,dev_info->m_1p,dev_info->proto==RTK_RG_STATIC?"STATIC":dev_info->proto==RTK_RG_DHCP?"DHCP":dev_info->proto==RTK_RG_PPPoE?"PPPoE":"BRIDGE");
		if(dev_info->proto != RTK_RG_BRIDGE)
		{
#ifdef CONFIG_DEFAULTS_KERNEL_3_18
			if(dev_info->m_1p)
			{
				if(vlan_insert_tag_set_proto(skb,htons(ETH_P_8021Q),(dev_info->vid&VLAN_VID_MASK)|((dev_info->m_1p-1)<<13))==NULL)
				{
					WMUX("error when add cvlan tag\n");
					return 0;
				}			
			}
			else
			{
				if(vlan_insert_tag_set_proto(skb,htons(ETH_P_8021Q),(dev_info->vid&VLAN_VID_MASK))==NULL)
				{
					WMUX("error when add cvlan tag\n");
					return 0;
				}			
			}

#else
			//PATCH20131216:for fwdEngine, we need wmux to remarking ctag directly, not put in skb->vlan_tci
			if(vlan_put_tag(skb,(dev_info->vid&VLAN_VID_MASK))==NULL)
			{
				WMUX("error when add cvlan tag\n");
				return 0;
			}
			if(dev_info->m_1p)
			{
				if (skb->dev->features & NETIF_F_HW_VLAN_TX)
				{
					skb->vlan_tci |= ((dev_info->m_1p-1)<<13);
				}
				else
				{
					struct vlan_ethhdr *veth = (struct vlan_ethhdr *)skb->data;
					veth->h_vlan_TCI |=((dev_info->m_1p-1)<<13);
				}
			}
#endif

		}

	}	
	skb->vlan_member = dev_info->member;
	WMUX("%s,%d::dev_info->member: %x\n",__func__,__LINE__,skb->vlan_member);



#ifdef CONFIG_DEFAULTS_KERNEL_3_18
//	wmux_netdev_ops.ndo_start_xmit(skb, skb->dev);
	skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
#else
	skb->dev->hard_start_xmit(skb, skb->dev);
#endif



	//dev_queue_xmit(skb);

	return 0;
} /* wmux_dev_hard_start_xmit */


struct net_device_stats *wmux_dev_get_stats(struct net_device *dev)
{
	struct wmux_dev_info *dev_info=WMUX_DEV_INFO(dev);
	if(dev_info)
		return &(dev_info->stats);
	return NULL;
}


/***************************************************************************
 * Function Name: wmux_dev_open
 * Description  : 
 * Returns      : 0 on Success
 ***************************************************************************/
int wmux_dev_open(struct net_device *vdev)
{
	struct wmux_dev_info *dev_info=WMUX_DEV_INFO(vdev);
	if (dev_info && !(dev_info->wmux_grp->real_dev->flags & IFF_UP))
		return -ENETDOWN;

	return 0;
} /* wmux_dev_open */

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

/***************************************************************************
 * Function Name: wmux_dev_set_mac_address
 * Description  : sets the mac for devs
 * Returns      : 0 on Success
 ***************************************************************************/
int wmux_dev_set_mac_address(struct net_device *dev, 
                             void *addr_struct_p)
{
	struct sockaddr *addr = (struct sockaddr *)(addr_struct_p);

#if 0
#ifdef CONFIG_RTL_8676HWNAT
	rtl865x_netif_t netif;
#endif
#endif
#ifdef UNIQUE_MAC_PER_DEV
	struct wmux_group *grp = NULL;
	struct wmux_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);

	return 0;
} /* wmux_dev_set_mac_address */

/***************************************************************************
 * Function Name: wmux_dev_change_mtu
 * Description  : changes mtu for dev
 * Returns      : 0 on Success
 ***************************************************************************/
int wmux_dev_change_mtu(struct net_device *vdev, int new_mtu)
{
	struct wmux_dev_info *dev_info=WMUX_DEV_INFO(vdev);
#if 0
	const struct net_device_ops *ops = WMUX_DEV_INFO(vdev)->wmux_grp->real_dev->netdev_ops;
#endif
	
	//MTU should be smaller than real device.
	if (dev_info && dev_info->wmux_grp->real_dev->mtu < new_mtu)
		return -ERANGE;

	vdev->mtu = new_mtu;
//20151124LUKE: for multi-WAN device, every virtual device should not change real device's mtu. They could change themselves only.
#if 0
#ifdef CONFIG_DEFAULTS_KERNEL_3_18
	//if((WMUX_DEV_INFO(vdev)->wmux_grp->real_dev->netdev_ops->ndo_change_mtu) == NULL)printk("\n\nERROR!!!real_dev %s don't register ndo_change_mtu.....\n\n",WMUX_DEV_INFO(vdev)->wmux_grp->real_dev->name)
	if (ops->ndo_change_mtu)
		return ops->ndo_change_mtu(vdev, new_mtu);
#else
	WMUX_DEV_INFO(vdev)->wmux_grp->real_dev->change_mtu(vdev, new_mtu);
#endif
#endif
	return 0;
}

/***************************************************************************
 * Function Name: wmux_setup
 * Description  : inits device api
 * Returns      : None
 ***************************************************************************/
static void wmux_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_DEFAULTS_KERNEL_3_18
	new_dev->destructor = free_netdev;	
#else
	new_dev->get_stats = wmux_dev_get_stats;
#ifdef CONFIG_COMPAT_NET_DEV_OPS
	/* set up method calls */
	new_dev->change_mtu = wmux_dev_change_mtu;
	new_dev->open = wmux_dev_open;
	new_dev->stop = wmux_dev_stop;
	new_dev->set_mac_address = wmux_dev_set_mac_address;
	new_dev->destructor = free_netdev;
	//new_dev->do_ioctl = wmux_dev_ioctl;
#endif

#endif

} /* wmux_setup */

/***************************************************************************
 * Function Name: wmux_transfer_operstate
 * Description  : updates the operstate of overlay device 
 * Returns      : None.
 ***************************************************************************/
static void wmux_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);
	}
} /* wmux_transfer_operstate */

/***************************************************************************
 * Function Name: wmux_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 *wmux_register_device(const char *rifname, int intf_idx)
{
	struct net_device *new_dev = NULL;
	struct net_device *real_dev = NULL; 
	struct wmux_group *grp = NULL;
	struct wmux_dev_info *vdev_info = NULL;
	char nifname[IFNAMSIZ]={'\0'};
	int wmux_proto;
	int vid=-1;

	//check if interface had been created
	if(!rg_db.systemGlobal.interfaceInfo[intf_idx].valid)
		goto new_dev_invalid;

	if(rg_db.systemGlobal.interfaceInfo[intf_idx].storedInfo.wan_intf.wan_intf_conf.egress_vlan_tag_on)
		vid=rg_db.systemGlobal.interfaceInfo[intf_idx].storedInfo.wan_intf.wan_intf_conf.egress_vlan_id;

	wmux_proto=rg_db.systemGlobal.interfaceInfo[intf_idx].storedInfo.wan_intf.wan_intf_conf.wan_type;

	snprintf(nifname,IFNAMSIZ,"%s_%d",rifname,intf_idx);

	WMUX("enter register device\n");
	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;
	}
#ifdef CONFIG_RTL_MULTI_ETH_WAN
#if !defined(CONFIG_OPENWRT_RG)
	real_dev->priv_flags &= ~IFF_RSMUX;	//disable SMUX when WMUX is registered.
#endif
#endif

#ifdef CONFIG_DEFAULTS_KERNEL_3_18
	new_dev = alloc_netdev(sizeof(struct wmux_dev_info), nifname, NET_NAME_UNKNOWN, wmux_setup);

#else
	new_dev = alloc_netdev(sizeof(struct wmux_dev_info), nifname, wmux_setup);
#endif

	if (new_dev == NULL)
	{
		WMUX("netdev alloc failure\n");
		goto new_dev_invalid;
	}

	//dev->netdev_ops = &rtl819x_netdev_ops;
	ether_setup(new_dev);
	//if (vid != -1)
		//new_dev->priv_flags |= IFF_VWMUX;

	new_dev->flags &= ~IFF_UP;
	new_dev->flags &= ~IFF_MULTICAST;
	new_dev->priv_flags |= IFF_DOMAIN_WAN;
	//new_dev->priv_flags |= IFF_DOMAIN_ELAN;
	//real_dev->priv_flags |= IFF_RWMUX;

	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 = wmux_dev_hard_start_xmit;
	new_dev->set_mac_address = wmux_dev_set_mac_address;
#else
	new_dev->netdev_ops = &wmux_netdev_ops;
#endif
#ifdef CONFIG_DEFAULTS_KERNEL_3_18
	new_dev->ethtool_ops = &wmux_ethtool_ops;
#endif

	/* find wmux group name. if not found create all new wmux group */
	grp = __find_wmux_group(rifname);
	if (!grp) 
	{
		WMUX("allocate new group for %s..\n",rifname);
		grp = kzalloc(sizeof(struct wmux_group), GFP_KERNEL);

		if(grp) 
		{
			INIT_LIST_HEAD(&grp->virtual_devs_head);
			INIT_LIST_HEAD(&grp->grp_list);

			grp->real_dev = real_dev;

			write_lock_irq(&wmux_lock);
			list_add_tail(&grp->grp_list, &total_wmux_grp_devs_head);
			write_unlock_irq(&wmux_lock);
		}
		else 
		{
			free_netdev(new_dev);
			new_dev = NULL;
		}
	}

	if(grp && new_dev) {
		#if 0
		/* Assign default mac to bridge so that we can add it to linux bridge */
		if(wmux_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_head))
			{
				memcpy(new_dev->dev_addr, real_dev->dev_addr, ETH_ALEN);
			}
			else
			{
				list_for_each(lh, &grp->virtual_devs_head)
				{
					vdev_info = list_entry_wmuxdev(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
		}
		#else
        char landev_ifname[16]="";
		struct net_device *landev;
		sprintf(landev_ifname, "%s%d",ALIASNAME_ELAN_PREFIX,ORIGINATE_NUM);
//		const char landev_ifname[16]="eth0.2";
		landev = dev_get_by_name(&init_net, landev_ifname);
		if (landev) {
			memcpy(new_dev->dev_addr, landev->dev_addr, ETH_ALEN);
			dev_put(landev);
		}
		else
			WMUX("eth0.2 not created.\n");
		#endif
	}
  
	if(grp && new_dev) 
	{
		struct net_device *ret_dev;
		/*find new wmux in wmux group if it does not exit create one*/
		if(NULL == (ret_dev=__find_wmux_in_wmux_group(grp, nifname))) 
		{
			WMUX("create new wmux in group..\n");
			vdev_info = WMUX_DEV_INFO(new_dev);
			memset(vdev_info, 0, sizeof(struct wmux_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->wmux_grp = grp;
			vdev_info->vdev = new_dev;
			vdev_info->proto = wmux_proto;
			vdev_info->intf_idx = intf_idx;
			
			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.

			if(wmux_proto == RTK_RG_BRIDGE) 
			{
				new_dev->promiscuity = 1;
			}
			else /*if(wmux_proto == SMUX_PROTO_IPOE)*/
			{
				new_dev->flags |= IFF_MULTICAST;
			}

			if (register_netdev(new_dev)) 
			{
				WMUX("register_netdev failed\n");
				//list_del(&vdev_info->wdev_list);
				free_netdev(new_dev);
				new_dev = NULL;
			}
			else 
			{
				WMUX("success!\n");
				INIT_LIST_HEAD(&vdev_info->wdev_list);
				write_lock_irq(&wmux_lock);
				list_add_tail(&vdev_info->wdev_list, &grp->virtual_devs_head);
				write_unlock_irq(&wmux_lock);
				wmux_transfer_operstate(real_dev, new_dev);
			}
		}
		else 
		{
			WMUX("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;
} /* wmux_register_device */

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

	vdev = dev_get_by_name(&init_net, vifname);

	if(vdev) 
	{
		WMUX("remove wmux dev %s\n",vifname);

		dev_info = WMUX_DEV_INFO(vdev);
		real_dev = dev_info->wmux_grp->real_dev;

		//remove from group's virtual_devs_head
		write_lock_irq(&wmux_lock);
		list_del(&dev_info->wdev_list);
		write_unlock_irq(&wmux_lock);

		if (list_empty(&dev_info->wmux_grp->virtual_devs_head)) 
		{
			//if group is empty, remove from total_wmux_grp_devs_head and free it
			write_lock_irq(&wmux_lock);
			list_del(&dev_info->wmux_grp->grp_list);
			write_unlock_irq(&wmux_lock);

			kfree(dev_info->wmux_grp);
		}

		dev_put(vdev);
		unregister_netdev(vdev);

		synchronize_net();
		dev_put(real_dev); 

		ret = 0;
	}

	return ret;
} /* wmux_unregister_device */

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


	if (!grp)
		goto out;

	switch (event) {
		case NETDEV_CHANGE:

			/* Propagate real device state to overlay devices */
			read_lock(&wmux_lock);
			list_for_each(lh, &grp->virtual_devs_head) {
				dev_info = list_entry_wmuxdev(lh);
				if(dev_info) {
					wmux_transfer_operstate(rdev, dev_info->vdev);
				}
			}
			read_unlock(&wmux_lock);
			break;

		case NETDEV_DOWN:

			/* Put all Overlay devices for this dev in the down state too.*/
			read_lock(&wmux_lock);
			list_for_each(lh, &grp->virtual_devs_head) {
				dev_info = list_entry_wmuxdev(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(&wmux_lock);
			break;

		case NETDEV_UP:

			/* Put all Overlay devices for this dev in the up state too.  */
			read_lock(&wmux_lock);
			list_for_each(lh, &grp->virtual_devs_head) {
				dev_info = list_entry_wmuxdev(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(&wmux_lock);
			break;

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

out:
  return NOTIFY_DONE;
} /* wmux_device_event */

/***************************************************************************
 * Function Name: wmux_drv_init
 * Description  : Initialization of wmux driver
 * Returns      : struct net_device
 ***************************************************************************/
int wmux_drv_init(void)
{
	wmux_notifier_block.notifier_call = wmux_device_event;
	register_netdevice_notifier(&wmux_notifier_block);
	//wmux_ioctl_set(wmux_ioctl_handler);

	return 0;
} /* wmux_drv_init */

/***************************************************************************
 * Function Name: wmux_cleanup_devices
 * Description  : cleans up all the wmux devices and releases memory on exit
 * Returns      : None
 ***************************************************************************/
void wmux_cleanup_devices(void)
{
	struct net_device *dev;
	struct list_head *lh,*lhp;
	struct wmux_dev_info *dev_info;
	struct wmux_group *grp;

	/* clean up all the wmux virtual devices and group */
	for_each_netdev(&init_net, dev)
	{
		grp=__find_wmux_group(dev->name);	//find real_dev
		if(grp)
		{
			list_for_each_safe(lh, lhp, &grp->virtual_devs_head)	//find virtual_dev in grp
			{
				dev_info = list_entry_wmuxdev(lh);
				if(dev_info)wmux_unregister_device(dev_info->vdev->name);
			}
		}
	}
} /* wmux_cleanup_devices */

/***************************************************************************
 * Function Name: wmux_drv_exit
 * Description  : wmux module clean routine
 * Returns      : None
 ***************************************************************************/
void wmux_drv_exit(void)
{
	/* Un-register us from receiving netdevice events */
	unregister_netdevice_notifier(&wmux_notifier_block);

	wmux_cleanup_devices();
	
	synchronize_net();
} /* wmux_drv_exit */

/***************************************************************************
                         Proc system Function Definisions
 ***************************************************************************/
//Read Functions
int _rtk_rg_wmux_init_read(struct seq_file *s, void *v)
{
	int len=0;
	//cat /proc/rg/wmux_init
	__wmux_init_usage();
	return len;
}
int _rtk_rg_wmux_add_read(struct seq_file *s, void *v)
{
	int len=0;
	//cat /proc/rg/wmux_add
	__wmux_add_usage();
	return len;
}
int _rtk_rg_wmux_del_read(struct seq_file *s, void *v)
{
	int len=0;
	//cat /proc/rg/wmux_del
	__wmux_del_usage();
	return len;
}
int _rtk_rg_wmux_flag_read(struct seq_file *s, void *v)
{
	int len=0;
	//cat /proc/rg/wmux_flag
	__wmux_flag_usage();
	return len;
}
int _rtk_rg_wmux_info(struct seq_file *s, void *v)
{
	struct net_device *dev;
	struct list_head *lh;
	struct list_head *lhp;
	struct wmux_dev_info *dev_info;
	struct wmux_group *grp;
	int len=0;
	
	//cat /proc/rg/wmux_dump
	//show all wmux settings
	PROC_PRINTF("WMUX virtual devices:\n");
	for_each_netdev(&init_net, dev)
	{
		grp=__find_wmux_group(dev->name);	//find real_dev
		if(grp)
		{
			PROC_PRINTF("{%s}\n",dev->name);
			PROC_PRINTF("=========================================================\n");
			list_for_each_safe(lh, lhp, &grp->virtual_devs_head)	//find virtual_dev in grp
			{
				dev_info = list_entry_wmuxdev(lh);
				if(dev_info){
					if(dev_info->vid<0)
					{
						PROC_PRINTF("%s\tvlan:[untag] order_type=%s\n",dev_info->vdev->name,
							dev_info->proto==RTK_RG_BRIDGE?"[BRIDGE],KEEP,REORDER":dev_info->keep_order==1?"BRIDGE,[KEEP],REORDER":"BRIDGE,KEEP,[REORDER]");
					}
					else
					{
						PROC_PRINTF("%s\tvlan:[%d] order_type=%s\n",dev_info->vdev->name,dev_info->vid,
							dev_info->proto==RTK_RG_BRIDGE?"[BRIDGE],KEEP,REORDER":dev_info->keep_order==1?"BRIDGE,[KEEP],REORDER":"BRIDGE,KEEP,[REORDER]");
					}
					PROC_PRINTF("\tRX_PktCnt:%ld RX_ByteCnt=%ld\n",dev_info->stats.rx_packets,dev_info->stats.rx_bytes);
					PROC_PRINTF("\tTX_PktCnt:%ld TX_ByteCnt:%ld\n",dev_info->stats.tx_packets,dev_info->stats.tx_bytes);
				}
			}
			PROC_PRINTF("=========================================================\n");
		}
	}

	return len;
}

//Write Functions
int _rtk_rg_wmux_init_write(struct file *file, const char *buff, unsigned long len, void *data)
{
	char 		tmpbuf[64]={'\0'};
	char		*strptr;
	unsigned int init;
	char		*tokptr;

	//echo 1 >wmux_init
	if (buff && !copy_from_user(tmpbuf, buff, len)) {
		//tmpbuf[len] = '\0';
		strptr=tmpbuf;
		tokptr = strsep(&strptr," ");
		if (tokptr==NULL)
			goto errout;
		
		init = simple_strtol(tokptr, NULL, 0);
		if(init!=1)
			goto errout;
		
		//do init
		wmux_cleanup_devices();
	}
	else
	{
		struct seq_file *s=NULL;//used by PROC_PRINTF
		// char *buf=NULL; //used by PROC_PRINTF
		// int len=0;//used by PROC_PRINTF
errout:		
		__wmux_init_usage();
	}

	return len;
}

int _rtk_rg_wmux_add_write(struct file *file, const char *buff, unsigned long len, void *data)
{
	char 		tmpbuf[64]={'\0'};
	char		*strptr;
	unsigned int intf_idx;
	char		*tokptr=NULL,*rifname=NULL;

	//echo nas0 1 >wmux_add
	if (buff && !copy_from_user(tmpbuf, buff, len)) {
		//tmpbuf[len] = '\0';
		strptr=tmpbuf;
		rifname = strsep(&strptr," ");
		if (rifname==NULL)
			goto errout;

		tokptr = strsep(&strptr," ");
		if (tokptr==NULL)
			goto errout;
		
		intf_idx = simple_strtol(tokptr, NULL, 0);
		if(intf_idx > MAX_NETIF_SW_TABLE_SIZE)
			goto errout;
		
		if(wmux_register_device(rifname,intf_idx)==NULL)
			goto errout;
	}
	else
	{
		struct seq_file *s=NULL;//used by PROC_PRINTF
		// char *buf=NULL; //used by PROC_PRINTF
		// int len=0;//used by PROC_PRINTF
errout:
		__wmux_add_usage();
	}

	return len;
}

int _rtk_rg_wmux_del_write(struct file *file, const char *buff, unsigned long len, void *data)
{
	char 		tmpbuf[64]={'\0'};
	char		*strptr;
	char		*vifname=NULL;
	char		vif_not_exist=1;
	char		vif_name_length=0;
	struct net_device 		*dev;
	struct list_head 		*lh,*lhp;
	struct wmux_dev_info 	*dev_info;
	struct wmux_group 		*grp;

	//echo nas0_1 >wmux_del
	if (buff && !copy_from_user(tmpbuf, buff, len)) {
		//tmpbuf[len] = '\0';
		strptr=tmpbuf;
		vifname = strsep(&strptr," ");
		vifname = strsep(&vifname,"\n");
		if (vifname==NULL)
			goto errout;
		vif_name_length=strnlen(vifname,IFNAMSIZ);
		if(vif_name_length>IFNAMSIZ)
			goto errout;
		
		/* clean up wmux virtual device */
		rtnl_lock();
		for_each_netdev(&init_net, dev)
		{
			grp=__find_wmux_group(dev->name);	//find real_dev
			if(grp)
			{
				list_for_each_safe(lh, lhp, &grp->virtual_devs_head)	//find virtual_dev in grp
				{
					dev_info = list_entry_wmuxdev(lh);
					//compare the name and its length
					if(strnlen(dev_info->vdev->name,IFNAMSIZ)==vif_name_length && 
						!memcmp(dev_info->vdev->name,vifname,vif_name_length))
					{
						vif_not_exist=0;
						break;
					}
				}
			}
			if(!vif_not_exist)
				break;
		}
		rtnl_unlock();
		
		if(vif_not_exist)
		{
			rtlglue_printf("%s doesn't exist or not WMUX device.\n",vifname);
			goto errout;
		}
		
		if(wmux_unregister_device(vifname))
		 	goto errout;
	}
	else
	{
		struct seq_file *s=NULL;//used by PROC_PRINTF
		// char *buf=NULL; //used by PROC_PRINTF
		// int len=0;//used by PROC_PRINTF
errout:
		__wmux_del_usage();
	}
	return len;
}

int _rtk_rg_wmux_flag_write(struct file *file, const char *buff, unsigned long len, void *data)
{
	char 		tmpbuf[64]={'\0'};
	char		*strptr;
	char		*vifname=NULL,*tokptr=NULL;
	char		vif_not_exist=1;
	char		vif_name_length=0;
	char		keep_flag=0;
	struct net_device 		*dev;
	struct list_head 		*lh,*lhp;
	struct wmux_dev_info 	*dev_info;
	struct wmux_group 		*grp;

	//echo nas0_1 1 >wmux_flag
	if (buff && !copy_from_user(tmpbuf, buff, len)) {
		//tmpbuf[len] = '\0';
		strptr=tmpbuf;
		vifname = strsep(&strptr," ");
		if (vifname==NULL)
			goto errout;
		vif_name_length=strnlen(vifname,64);
		if(vif_name_length>IFNAMSIZ)
			goto errout;
		
		tokptr = strsep(&strptr," ");
		if (tokptr==NULL)
			goto errout;
		keep_flag = simple_strtol(tokptr, NULL, 0);
		if(keep_flag != 0 && keep_flag != 1)
			goto errout;
		
		/* set flag to wmux virtual device */
		rtnl_lock();
		for_each_netdev(&init_net, dev)
		{
			grp=__find_wmux_group(dev->name);	//find real_dev
			if(grp)
			{
				list_for_each_safe(lh, lhp, &grp->virtual_devs_head)	//find virtual_dev in grp
				{
					dev_info = list_entry_wmuxdev(lh);
					//compare the name and its length
					if(strnlen(dev_info->vdev->name,IFNAMSIZ)==vif_name_length && 
						!memcmp(dev_info->vdev->name,vifname,vif_name_length))
					{
						write_lock_irq(&wmux_lock);
						dev_info->keep_order=keep_flag;
						write_unlock_irq(&wmux_lock);
						vif_not_exist=0;
						break;
					}
				}
			}
			if(!vif_not_exist)
				break;
		}
		rtnl_unlock();
		
		if(vif_not_exist)
			goto errout;
	}
	else
	{
		struct seq_file *s=NULL; //used by PROC_PRINTF
		// char *buf=NULL; //used by PROC_PRINTF
		// int len=0;//used by PROC_PRINTF
errout:
		__wmux_flag_usage();
	}

	return len;
}