/*      @doc RTL_LAYEREDDRV_API

        @module rtl865x_outputQueue.c - RTL865x Home gateway controller Layered driver API documentation       |
        This document explains the API interface of the table driver module. Functions with rtl865x prefix
        are external functions.
        @normal Hyking Liu (Hyking_liu@realsil.com.cn) <date>

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

        @head3 List of Symbols |
        Here is a list of all functions and variables in this module.
        
        @index | RTL_LAYEREDDRV_API
*/
#include <linux/version.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
#include <linux/kconfig.h>
#else
#include <linux/config.h>
#include <common/rtl8651_tblDrvProto.h>
#endif
#include <net/rtl/rtl_types.h>
#include <net/rtl/rtl_glue.h>
#include <common/rtl865x_eventMgr.h>
#include <common/rtl865x_vlan.h>
#include <net/rtl/rtl865x_netif.h>
#include <common/rtl865x_netif_local.h>
#include <net/rtl/rtl865x_outputQueue.h>
//#include "assert.h"
//#include "rtl_utils.h"
#include <common/rtl_errno.h>
#if defined (CONFIG_RTL_LOCAL_PUBLIC)
#include <l3Driver/rtl865x_localPublic.h>
#endif

#ifdef CONFIG_RTL_LAYERED_ASIC_DRIVER
#include <AsicDriver/asicRegs.h>
#include <AsicDriver/rtl865x_asicCom.h>
#include <AsicDriver/rtl865x_asicL2.h>
#else
#include <AsicDriver/asicRegs.h>
#include <AsicDriver/rtl8651_tblAsicDrv.h>
#endif

#include "../../../../net/bridge/br_private.h"



/* Warning ! If you want to create more than 4 output queues , you have to decrese Per-Queue physical length gap Register (0xBB80-4500)
     In order to let pkts enqueue into higher queues (Queue4 , 5) */
//(0xBB80-45D8)PQPLGR	Per-Queue physical length gap Register  

struct list_head rtl865x_qosRuleHead;


static uint8    priorityDecisionArray[] = {	1,		/* port base */
						2,		/* 802.1p base */ 
						1,		/* dscp base */                   
						8,		/* acl base */    
						4		/* nat base */
					};


typedef struct aclpriority_mapping_data_s
{
	uint8 valid;
	uint8 Qid;
	uint8 swQid;
	uint32 remark_8021p;
	uint32 remark_dscp;
	uint32 ref_count;
} aclpriority_mapping_data_t;

static aclpriority_mapping_data_t 	upstream_priority_mapping[TOTAL_VLAN_PRIORITY_NUM];
int32 						upstream_priority_default 	=-1;
int32 						upstream_udp_priority_default 	=-1;
#ifdef CONFIG_RTL_ADV_FAST_PATH
int32								upstream_default_swQid 		=-1;
#else
static int32 						upstream_default_swQid 		=-1;
#endif
static uint32 						upstream_hwQid2swQid_Mapping[RTL8651_OUTPUTQUEUE_SIZE] = {0};

int rtl865x_get_priority_by_hwqid(int hwqid)
{
	int i;
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
	{
		if(upstream_priority_mapping[i].valid && upstream_priority_mapping[i].Qid == hwqid)
			return i;
	}
	return -1;
}

int rtl865x_get_priority_by_swqid(int swqid)
{
        int i;
        for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
        {
                if(upstream_priority_mapping[i].valid && upstream_priority_mapping[i].swQid == swqid)
                        return i;
        }
        return -1;
}

#if defined(CONFIG_RTL_PROC_DEBUG)
static void rtl865x_show_QosAcl(rtl865x_qos_rule_t	*qosRule)
{
	rtl865x_showACL_DataField(&qosRule->acl_rule_data,qosRule->acl_rule_data_format,0);
	
	printk("\t[index %02d] Priority: %d	sw_Qid:0x%08X    Remark_8021p: 0x%08X   Remark_DSCP: 0x%08X \n"
		,qosRule->index ,  qosRule->priority, qosRule->swQid, qosRule->remark_8021p, qosRule->remark_dscp);

}
int32 rtl865x_show_allQosAcl(void)
{
	int index=0;
	rtl865x_qos_rule_t	*qosRule;
	list_for_each_entry(qosRule,&rtl865x_qosRuleHead,qos_rule_list)
	{
		printk("[%d] ",index);
		rtl865x_show_QosAcl(qosRule);
		printk("\n");
		index++;
	}	
	return SUCCESS;

}
void rtl865x_qosShowDebugInfo(void)
{
	int i;
	
	printk("Queue hwIdx <----> swIdx : ");
	for(i=0;i<RTL8651_OUTPUTQUEUE_SIZE;i++)
		printk("[HwQid_%d]0x%08X  ",i,upstream_hwQid2swQid_Mapping[i]);
	printk("\n");

	printk("acl priority <----> \tQueue hwIdx  \tQueue swIdx \tRemark_802.1p \tRemark_DSCP  \tref_count\n");
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
        {
                char str_remark_8021p[10]="-",str_remark_dscp[10]="-";
                if(upstream_priority_mapping[i].valid && (upstream_priority_mapping[i].remark_8021p>=0) && (upstream_priority_mapping[i].remark_8021p<=7))
                        sprintf(str_remark_8021p,"%d",upstream_priority_mapping[i].remark_8021p);
                if(upstream_priority_mapping[i].valid && (upstream_priority_mapping[i].remark_dscp>=0) && (upstream_priority_mapping[i].remark_dscp<=63))
                        sprintf(str_remark_dscp,"%d",upstream_priority_mapping[i].remark_dscp);
                printk("%d[%s] \t\t\t%d \t\t\t%d \t\t%s \t\t%s \t\t%d\n"
		,i,upstream_priority_mapping[i].valid?"Y":"N"
		,upstream_priority_mapping[i].Qid
		,upstream_priority_mapping[i].swQid
                ,str_remark_8021p
                ,str_remark_dscp
		,upstream_priority_mapping[i].ref_count);
        }

	printk("def acl priority : %d\n",upstream_priority_default);
	
}

#endif

int32 rtl865x_setItfShapingRate(uint32 port, uint32 bps)
{
	uint32	qid,apr,pprTime,burstSize,asicBandwidth;
	DBG_OutputQueue_PRK("Enter %s (memberPort=%d , bps = %d)\n",__func__,port,bps);
	
	rtl865xC_lockSWCore();
	rtl865x_qosSetBandwidth((0x1<<port),bps);
	asicBandwidth = bps>>EGRESS_BANDWIDTH_GRANULARITY_BITLEN;
	if(asicBandwidth>0)
		asicBandwidth--;

#if defined(CONFIG_RTL_8685_8PRIQUE)
	for(qid=0;qid<8;qid++)
#else
	for(qid=0;qid<6;qid++)
#endif
	{
		rtl8651_getAsicQueueRate(port,qid,&pprTime,&burstSize,&apr);
		rtl8651_setAsicQueueRate(port,qid,pprTime,burstSize,asicBandwidth);
	}
	//rtl865xC_waitForOutputQueueEmpty();
	//rtl8651_resetAsicOutputQueue();
	rtl865xC_unLockSWCore();
	return SUCCESS;
}

int32 rtl865x_getItfShapingRate(uint32 port, uint32 *bps)
{
	uint32	apr,pprTime,burstSize;
	DBG_OutputQueue_PRK("Enter %s (memberPort=%d )\n",__func__,port);
	
	rtl865xC_lockSWCore();
	rtl8651_getAsicQueueRate(port,0,&pprTime,&burstSize,&apr);
	*bps = apr<<EGRESS_BANDWIDTH_GRANULARITY_BITLEN;
	if(*bps>0x40000000)
		*bps = 1024*1024*1024;	//1G bps
	else
		*bps += (1<<EGRESS_BANDWIDTH_GRANULARITY_BITLEN);
	//rtl865xC_waitForOutputQueueEmpty();
	//rtl8651_resetAsicOutputQueue();
	rtl865xC_unLockSWCore();
	DBG_OutputQueue_PRK("Get bps=%d bps\n",*bps);
	return SUCCESS;
}

int32 rtl865x_setItfShapingBurstSize(uint32 port, uint32 burstSize)
{
	uint32	qid,apr,pprTime,L1;
	DBG_OutputQueue_PRK("Enter %s (memberPort=%d , burstSize = %d)\n",__func__,port,burstSize);
	
	// asic burstSize unit = 1Kbyes
	burstSize = (burstSize!=0)?((burstSize-1)>>10)+1:0;
	if (burstSize > 0xff) // overflow, truncate to 0xff
		burstSize = 0xff;
	rtl865xC_lockSWCore();
#if defined(CONFIG_RTL_8685_8PRIQUE)
	for(qid=0;qid<8;qid++)
#else
	for(qid=0;qid<6;qid++)
#endif
	{
		rtl8651_getAsicQueueRate(port,qid,&pprTime,&L1,&apr);
		rtl8651_setAsicQueueRate(port,qid,pprTime,burstSize,apr);
	}
	//rtl865xC_waitForOutputQueueEmpty();
	//rtl8651_resetAsicOutputQueue();
	rtl865xC_unLockSWCore();
	return SUCCESS;
}

int32 rtl865x_getItfShapingBurstSize(uint32 port, uint32 *burstSize)
{
	uint32	apr,pprTime;
	DBG_OutputQueue_PRK("Enter %s (memberPort=%d )\n",__func__,port);
	
	rtl865xC_lockSWCore();
	rtl8651_getAsicQueueRate(port,0,&pprTime,burstSize,&apr);
	// Kbyte to byte
	*burstSize = (*burstSize<<10);
	//rtl865xC_waitForOutputQueueEmpty();
	//rtl8651_resetAsicOutputQueue();
	rtl865xC_unLockSWCore();
	DBG_OutputQueue_PRK("Get burstSize=%d byte\n",*burstSize);
	return SUCCESS;
}

/* Set 8676 switch's port egress bandwidth limit */
int32 rtl865x_qosSetBandwidth(uint32 memberPort, uint32 bps)
{
	uint32	port;
	DBG_OutputQueue_PRK("Enter %s (memberPort=0x%X , bps = %d)\n",__func__,memberPort,bps);
	
	rtl865xC_lockSWCore();
	for(port=0;port<=CPU;port++)
	{
		if(((1<<port)&memberPort)==0)
			continue;
		rtl8651_setAsicPortEgressBandwidth(port, bps);
	}
	//rtl865xC_waitForOutputQueueEmpty();
	//rtl8651_resetAsicOutputQueue();
	rtl865xC_unLockSWCore();
	return SUCCESS;
}

int32 rtl865x_qosFlushBandwidth(uint32 memberPort)
{

	uint32	port;	
	DBG_OutputQueue_PRK("Enter %s(memberPort:0x%X)\n",__func__,memberPort);

	rtl865xC_lockSWCore();
	for(port=0;port<=CPU;port++)
	{
		if(((1<<port)&memberPort)==0)
			continue;
		
		rtl8651_setAsicPortEgressBandwidth(port, -1);

		//To flush wan port Ingress bandwidth limit
//		if(strcmp(netIf->name,"eth1")==0)
		{
			rtl8651_setAsicPortIngressBandwidth(port,0);
		}
	}


	
	//rtl865xC_waitForOutputQueueEmpty();
	//rtl8651_resetAsicOutputQueue();
	rtl865xC_unLockSWCore();
	return SUCCESS;
}

/* Get hw_Qid */
int32 _rtl865x_qosQIDMappingGet( uint32 sw_Qidx)
{
	int	i;

	for(i=0; i < RTL8651_OUTPUTQUEUE_SIZE; i++)
	{
		if (upstream_hwQid2swQid_Mapping[i] == sw_Qidx)
		{
			return i;
		}
	}

	return -1;
}

/* rtl_setup_voip_rx_qos */
int voip_qos_setup_ruleIndex[4] = {-1,-1,-1,-1};
int rtl_setup_voip_rx_qos(int enable, int rtp_port_start, int rtp_port_end)
{
	rtl867x_hwnat_qos_rule_t qosRule;
	int idx;
	int priority;

	if(enable != 0 && enable != 1)
		return FAILED;

	if(rtp_port_start < 0 || rtp_port_start > 65536 || rtp_port_end < 0 || rtp_port_end > 65536 || rtp_port_start > rtp_port_end)
		return FAILED;

	if(voip_qos_setup_ruleIndex[0] > 0)
		rtl865x_qosDelRule(voip_qos_setup_ruleIndex[0]);
	if(voip_qos_setup_ruleIndex[1] > 0)
		rtl865x_qosDelRule(voip_qos_setup_ruleIndex[1]);
	if(voip_qos_setup_ruleIndex[2] > 0)
		rtl865x_qosDelRule(voip_qos_setup_ruleIndex[2]);
	if(voip_qos_setup_ruleIndex[3] > 0)
		rtl865x_qosDelRule(voip_qos_setup_ruleIndex[3]);
	if(enable)
	{
		memset(&qosRule,0,sizeof(qosRule));
		qosRule.rule_type = RTL867x_IPQos_Format_UDP;
		qosRule.match_field.L4._sport_start = rtp_port_start;
		qosRule.match_field.L4._sport_end = rtp_port_end;
		qosRule.match_field.L4._dport_start = 1025;
		qosRule.match_field.L4._dport_end = 65535;
		rtl865x_qosAddRule(&qosRule,0,-1,-1,-1,&idx);
		voip_qos_setup_ruleIndex[0] = idx;

		memset(&qosRule,0,sizeof(qosRule));
		qosRule.rule_type = RTL867x_IPQos_Format_UDP;
		qosRule.match_field.L4._sport_start = 1025;
		qosRule.match_field.L4._sport_end = 65535;
		qosRule.match_field.L4._dport_start = rtp_port_start;
		qosRule.match_field.L4._dport_end = rtp_port_end;
		rtl865x_qosAddRule(&qosRule,0,-1,-1,-1,&idx);
		voip_qos_setup_ruleIndex[1] = idx;

		memset(&qosRule,0,sizeof(qosRule));
		qosRule.rule_type = RTL867x_IPQos_Format_UDP;
		qosRule.match_field.L4._sport_start = rtp_port_start;
		qosRule.match_field.L4._sport_end = rtp_port_end;
		qosRule.match_field.L4._dport_start = 1025;
		qosRule.match_field.L4._dport_end = 65535;
		rtl865x_qosAddRule(&qosRule,0,-1,-1,-1,&idx);
		voip_qos_setup_ruleIndex[2] = idx;

		memset(&qosRule,0,sizeof(qosRule));
		qosRule.rule_type = RTL867x_IPQos_Format_UDP;
		qosRule.match_field.L4._sport_start = 1025;
		qosRule.match_field.L4._sport_end = 65535;
		qosRule.match_field.L4._dport_start = rtp_port_start;
		qosRule.match_field.L4._dport_end = rtp_port_end;
		rtl865x_qosAddRule(&qosRule,0,-1,-1,-1,&idx);
		voip_qos_setup_ruleIndex[3] = idx;

		priority = rtl865x_get_priority_by_swqid(0);
		//printk("priority:%d\n",priority);
		if(priority > 0)
		{
			int val;
			rtl8651_setAsicCPUPriorityToQIDMappingTable(CPU,priority,UPSTREAM_Quene_NUM-1);
			rtl8651_setAsicCPUPriorityToQIDMappingTable(EXT1,priority,UPSTREAM_Quene_NUM-1);
			rtl8651_setAsicCPUPriorityToQIDMappingTable(EXT2,priority,UPSTREAM_Quene_NUM-1);
			rtl8651_setAsicCPUPriorityToQIDMappingTable(EXT3,priority,UPSTREAM_Quene_NUM-1);

			val = REG32(CPUQDM4);
			val &= 0xffff0000;
			val |= (UPSTREAM_Quene_NUM-1)<<12 | (UPSTREAM_Quene_NUM-1)<<8 | (UPSTREAM_Quene_NUM-1)<<4 | (UPSTREAM_Quene_NUM-1);
			REG32(CPUQDM4) = val;

			//printk("reg:%x val:%x\n",reg,val);
		}
	}
	return SUCCESS;
}




static int _rtl865x_qosQIDMappingSet(int32 Qidmapping[RTL8651_OUTPUTQUEUE_SIZE])
{		
	memcpy(upstream_hwQid2swQid_Mapping,Qidmapping,RTL8651_OUTPUTQUEUE_SIZE*sizeof(int));	
	return SUCCESS;
}


static void _rtl865x_qosQIDMappingClear(void)
{
	
	int i;		
	for(i=0;i<RTL8651_OUTPUTQUEUE_SIZE;i++)
		upstream_hwQid2swQid_Mapping[i] = Queue_NOT_Create;	
	
}

/* Note. it need not to lock 0412 switch core before use this funcion */
static int _rtl865x_qosPriorityMappingTakeEffect(void)
{	
	int32  i;	

	/* 1. set priotiy  <---> queue_id  */		
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)		
	{		
		if(rtl8651_setAsicPriorityToQIDMappingTable(UPSTREAM_Quene_NUM, i, upstream_priority_mapping[i].Qid)==FAILED)
		{
			DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
			return FAILED;
		}			
	}	

	/* 2. set remark info */
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)		
	{
		if(rtl8651_SetPortRemark(i,upstream_priority_mapping[i].remark_8021p,upstream_priority_mapping[i].remark_dscp)==FAILED)
		{
			DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
			return FAILED;
		}
	}

	/* 3. set CPU priority <---> queue_id */
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
	{
		rtl8651_setAsicCPUPriorityToQIDMappingTable(CPU, i, upstream_priority_mapping[i].Qid);
		rtl8651_setAsicCPUPriorityToQIDMappingTable(CPU+1, i, upstream_priority_mapping[i].Qid);
		rtl8651_setAsicCPUPriorityToQIDMappingTable(CPU+2, i, upstream_priority_mapping[i].Qid);
		rtl8651_setAsicCPUPriorityToQIDMappingTable(CPU+3, i, upstream_priority_mapping[i].Qid);
		rtl8651_setAsicCPUPriorityToQIDMappingTable(CPU+4, i, upstream_priority_mapping[i].Qid);
	}

	return SUCCESS;
	
}

extern int RTL_DEBUG_LEVEL;
void rtl865x_qosPriorityMappingDeRef(uint32 priority)	
{

	#ifdef DBG_OutputQueue
	DBG_OutputQueue_PRK("Enter %s(priority:%d)\n",__func__,priority);
	if(0) // (RTL_DEBUG_LEVEL & RTL_DEBUG_LEVEL_OUTPUTQUEUE)
	{
		DBG_OutputQueue_PRK("--  dump stack --\n");
		dump_stack();
		DBG_OutputQueue_PRK("------------------------ --\n");
	}
	#endif
	

	if(priority<0 || priority>=TOTAL_VLAN_PRIORITY_NUM)
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return;	
	}
		
	upstream_priority_mapping[priority].ref_count--;
	if(upstream_priority_mapping[priority].ref_count==0)
	{
		int i;
		memset(&upstream_priority_mapping[priority], 0,sizeof(aclpriority_mapping_data_t));
		for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
			upstream_priority_mapping[i].remark_8021p = upstream_priority_mapping[i].remark_dscp = -1;
		if(_rtl865x_qosPriorityMappingTakeEffect()!=SUCCESS)
		{
			printk("%s@ %d(return FAILED)\n",__func__,__LINE__);		
			DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);		
		}
	}		

}

/* return ...     -1:   no avaliable acl priority mapping   others: acl priority 
	Note. At the same time ,ref_count ++ */
int rtl865x_qosPriorityMappingGet(uint32 sw_Qidx,uint32 remark_8021p,uint32 remark_dscp)
{
	int32  i,empty_idx,hw_queue_idx;	

	DBG_OutputQueue_PRK("Enter %s (sw_Qidx:%d   remark_8021p:%d  remark_dscp:%d )\n"
		,__func__,sw_Qidx,remark_8021p,remark_dscp); 
	
	hw_queue_idx = _rtl865x_qosQIDMappingGet(sw_Qidx);
	if(hw_queue_idx==-1)
	{
		DBG_OutputQueue_PRK("Leave %s @ %d\n",__func__,__LINE__);	
		return -1;	
	}
	
	DBG_OutputQueue_PRK("(%s)  hw_queue_idx=%d \n",__func__,hw_queue_idx);	
	
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
	{
		if( upstream_priority_mapping[i].valid 
			&& upstream_priority_mapping[i].Qid 			== hw_queue_idx
                        && (upstream_priority_mapping[i].remark_8021p == remark_8021p || upstream_priority_mapping[i].remark_8021p == -1 || remark_8021p == -1)
                        && (upstream_priority_mapping[i].remark_dscp == remark_dscp || upstream_priority_mapping[i].remark_dscp == -1 || remark_dscp == -1))
		{
                        if(remark_8021p != -1)
                                upstream_priority_mapping[i].remark_8021p = remark_8021p;
                        if(remark_dscp != -1)
                                upstream_priority_mapping[i].remark_dscp = remark_dscp;
                        if(remark_8021p != -1 || remark_dscp != -1)
                                _rtl865x_qosPriorityMappingTakeEffect();
			upstream_priority_mapping[i].ref_count++;			
			DBG_OutputQueue_PRK("Leave %s (return existed priority %d, ref:%d)\n",__func__,i,upstream_priority_mapping[i].ref_count);
			return i;
		}
	}

	/* cannot find out matching entry, create the new one*/
	empty_idx = -1;
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
	{
		if(upstream_priority_mapping[i].valid==0)
		{
			empty_idx = i;
			break;
		}
	}
	if(empty_idx==-1)
	{
		DBG_OutputQueue_PRK("Leave %s (return %d)\n",__func__,-1);
		return -1; /* no empty entry */
	}

	upstream_priority_mapping[empty_idx].valid 			= 1	;
	upstream_priority_mapping[empty_idx].Qid 			= hw_queue_idx;
	upstream_priority_mapping[empty_idx].swQid 			= sw_Qidx;
	upstream_priority_mapping[empty_idx].remark_8021p 	= remark_8021p;
	upstream_priority_mapping[empty_idx].remark_dscp 	= remark_dscp;
	upstream_priority_mapping[empty_idx].ref_count		= 1	;

	/*  let new acl_priority mapping take effect */
	/*  mapping between priority and Qid need not lock hw. (change queue number or bandwith need it ! ) */
	if(_rtl865x_qosPriorityMappingTakeEffect()!=SUCCESS)
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return FAILED;	
	}	
	DBG_OutputQueue_PRK("Leave %s (return new priority %d, ref:%d)\n",__func__,i,upstream_priority_mapping[i].ref_count);
	return empty_idx;
	
}

static int my_gcd(int numA, int numB)
{
	int	tmp;
	int	divisor;

	if (numA<numB)
	{
		tmp = numA;
		numA = numB;
		numB = tmp;
	}

	divisor = numA%numB;
	while(divisor)
	{
		numA = numB;
		numB = divisor;
		divisor = numA%numB;
	}

	return numB;
}

static void _rtl865x_qosArrangeQueue(int ceil[],int rate[],uint32 bands_num)
{	
	int	i; 	

	#ifdef DBG_OutputQueue
	DBG_OutputQueue_PRK("Enter %s\n",__func__);		
	DBG_OutputQueue_PRK("(%s)------------Before--------------------\n",__func__);
	for(i=0; i<bands_num; i++)
		DBG_OutputQueue_PRK("(%s) [Queue %d] bandwidth:%d(bits)  ceil:%d\n",__func__,i,ceil==NULL?0:ceil[i],rate[i]);
	DBG_OutputQueue_PRK("-------------------------------------------------------------\n");
	#endif


	
	/* bandwidth
		only cna be set as 0~255 ,  if the maxBandwidth>255 ..... 

		 ex. bandwidth_0 = 3  , bandwidth_1 = 254 , bandwidth_2 = 257
		 we shift right these value until  the maxBandwidth/divisor <= 255   */
	
	do
	{
		int	divisor;
		int	maxBandwidth;
		
		for(i=0; i<bands_num; i++)
		{		
			if(rate[i]==0)
				rate[i] = 1;			
		}

		/* calculate gcd */
		divisor = rate[0];
		for(i=1; i<bands_num; i++)				
			divisor = my_gcd(rate[i], divisor);		
		
		for(i=0; i<bands_num; i++)				
			rate[i] = rate[i]/divisor;		

		/* maxBandwidth has to <= 128*/
		maxBandwidth = 0;
		for(i=0; i<bands_num; i++)
		{		
			if(maxBandwidth<rate[i])
				maxBandwidth = rate[i];		
		}
		if(maxBandwidth<EGRESS_WFQ_MAX_RATIO)
			break;

		/* start right shift .. */
		for(i=0; i<bands_num; i++)				
			rate[i] = rate[i]>>1 ;		
		
	}while(1);	

	#ifdef DBG_OutputQueue	
	DBG_OutputQueue_PRK("(%s)------------After--------------------\n",__func__);
	for(i=0; i<bands_num; i++)
		DBG_OutputQueue_PRK("(%s) [Queue %d] bandwidth:%d(bits) ceil:%d\n",__func__,i,ceil==NULL?0:ceil[i],rate[i]);
	DBG_OutputQueue_PRK("-------------------------------------------------------------\n");
	DBG_OutputQueue_PRK("Leave %s\n",__func__);
	#endif

	
}

static int _rtl865x_qos_remark(int enable_8021p,int enable_dscp)
{
	int ipqos_8676_port_mask=0;
	int port_idx;

#ifdef CONFIG_RTL_8367B
	if (rtl8651_tblAsicDrvPara.externalPHYProperty & RTL8676_TBLASIC_EXTPHYPROPERTY_PORT0_RTL8367B) 
		ipqos_8676_port_mask = 0x1; /* p0 router mode => port 0*/
	else
#endif
	{
		int i;
		for(i=PHY0;i<=CPU;i++)
		{			
			if(((1<<i)&RTL_WANPORT_MASK)==0)
				continue;
			ipqos_8676_port_mask |= (1<<i);			
		}
	}

	for(port_idx=PHY0;port_idx<=CPU;port_idx++)
	{
		if( (1<<port_idx)&ipqos_8676_port_mask )
		{
			if(enable_8021p)
				rtl8651_EnablePortRemark_8021p(port_idx);
			else
				rtl8651_DisablePortRemark_8021p(port_idx);

			if(enable_dscp)
				rtl8651_EnablePortRemark_dscp(port_idx);
			else
				rtl8651_DisablePortRemark_dscp(port_idx);
		}
	}

	return SUCCESS;		
}

static int _rtl865x_qos_setQueueInfo(int sp_queue_num, int wrr_queue_num,int queue_number,int ceil[],int rate[])
{

	int ipqos_8676_port_mask=0;
	int port_idx,queue_idx;

	if(sp_queue_num+wrr_queue_num >queue_number)
		return FAILED;
		
	/*   Start  to set Queue scheduling ..  
		8676 embeded switch 	: set queue's number /  set queue's scheduling info / enable remark

		ex. if sp_queue_num=1 , wrr_queue_num=2   queue_number=4

			hw_qid : 0   1   2   3   4   5 
			sw_qid:  1   2   -1   0  -1   -1

			we only use the first 4 hw Queues 
			set hw_Queue 0 & 1 as WRR type
			set the remainging hw_Queue 2 & 3 as SP type (we will not use hw_Queue 2)
	*/		

#ifdef CONFIG_RTL_8367B
	if (rtl8651_tblAsicDrvPara.externalPHYProperty & RTL8676_TBLASIC_EXTPHYPROPERTY_PORT0_RTL8367B) 
		ipqos_8676_port_mask = 0x1; /* p0 router mode => port 0*/
	else
#endif
	{
		int i;
		for(i=PHY0;i<=CPU;i++)
		{			
			//if(((1<<i)&RTL_WANPORT_MASK)==0)
			//cxy 2017-4-1: support downstream qos
			if(((1<<i)&(RTL_WANPORT_MASK|RTL_LANPORT_MASK))==0)
				continue;
			ipqos_8676_port_mask |= (1<<i);			
		}
	}
	DBG_OutputQueue_PRK("(%s) ipqos_8676_port_mask : 0x%X \n",__func__,ipqos_8676_port_mask);

	rtl865xC_lockSWCore(); 
	for(port_idx=PHY0;port_idx<=CPU;port_idx++)
	{
		if( (1<<port_idx)&ipqos_8676_port_mask )
		{
			/* 1.1  set queue's number  */				
			//Don't touch H/W queue number setting in runtime
			//rtl8651_setAsicOutputQueueNumber(port_idx, queue_number);					

			/* 1.2  set queue's scheduling info  */
			for (queue_idx=0;queue_idx<queue_number;queue_idx++)
			{
				if(queue_idx < wrr_queue_num) /* wrr */
				{
					int asicBandwidth;					
					#if defined(CONFIG_RTL_8685S_HWNAT)
					if(ceil)
						asicBandwidth = ceil[queue_idx]/1000;
					else
						asicBandwidth = 1000000; //1Gbps
					#else
					/*	Egress bandwidth granularity was 64Kbps	*/
					asicBandwidth = ((ceil[queue_idx])>>(EGRESS_BANDWIDTH_GRANULARITY_BITLEN)) - 1;
					if ((ceil[queue_idx])&(1<<(EGRESS_BANDWIDTH_GRANULARITY_BITLEN-1)))
						asicBandwidth += 1;	
					#endif
				
					/*  HTB's ceil==>rtl8651_setAsicQueueRate ,    HTB's rate==>rtl8651_setAsicQueueWeight  */		
					rtl8651_setAsicQueueRate(port_idx, queue_idx, 
						0>>PPR_OFFSET, 
						L1_MASK>>L1_OFFSET, 
						asicBandwidth);			
					rtl8651_setAsicQueueWeight(port_idx, queue_idx, WFQ_PRIO, rate[queue_idx]-1);					
				}
				else /* sp */
				{					
#if 0 //Keep original setting
					rtl8651_setAsicQueueRate(port_idx, queue_idx, 
						PPR_MASK>>PPR_OFFSET, 
						L1_MASK>>L1_OFFSET, 
						APR_MASK>>APR_OFFSET);
#endif
					rtl8651_setAsicQueueWeight(port_idx, queue_idx, STR_PRIO, 0);					
				}				
			}	
			if(wrr_queue_num==0) //Disable WRR, recovery queue bandwidth setting
			{
				int asicBandwidth = 1024*1024*1024; //1G bps
				asicBandwidth = (asicBandwidth>>(EGRESS_BANDWIDTH_GRANULARITY_BITLEN))-1;
				//Setup Queue weight & rate
#if defined(CONFIG_RTL_8685_8PRIQUE)
				for(queue_idx=0;queue_idx<8;queue_idx++)
#else
				for(queue_idx=0;queue_idx<6;queue_idx++)
#endif
				{
					rtl8651_setAsicQueueRate(port_idx, queue_idx, 
						0>>PPR_OFFSET, 
						L1_MASK>>L1_OFFSET, 
						asicBandwidth);			
					rtl8651_setAsicQueueWeight(port_idx, queue_idx, STR_PRIO, 0);
				}
			}
		}		
	}
	//rtl865xC_waitForOutputQueueEmpty();
	//rtl8651_resetAsicOutputQueue();
	rtl865xC_unLockSWCore();
	return SUCCESS;
}



int32 rtl865x_enableQos(int sp_queue_num, int wrr_queue_num, int ceil[],int rate[],int32 default_sw_qid)
{

	int total_queue_num = sp_queue_num + wrr_queue_num;
	int Qidmapping[RTL8651_OUTPUTQUEUE_SIZE];
	int i;

	DBG_OutputQueue_PRK("Enter %s (sp_queue_num:%d   wrr_queue_num:%d   default_sw_qid:%d)\n"
		,__func__,sp_queue_num,wrr_queue_num,default_sw_qid);

	/* 1. check queue number */
	if (sp_queue_num<0 || wrr_queue_num<0 )
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED) \n",__func__,__LINE__);
		return FAILED;	
	}
	
	if( total_queue_num<1 || total_queue_num>(UPSTREAM_Quene_NUM-1)/* High/Low queue are both reserved */)
	{
		printk("(%s)Warning!! 0412 switch only support 1~%d outputqueues in upstream (Your input is %d) \n"
			,__func__,UPSTREAM_Quene_NUM,total_queue_num);
		return FAILED;	
	}
	
	if (default_sw_qid<0 || default_sw_qid>=total_queue_num)
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED) \n",__func__,__LINE__);
		return FAILED;	
	}

	/* 2.  Re-adjust qosinfo's para  */
	if(wrr_queue_num>0)
	{
		if(rate==NULL)
		{
			DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED) \n",__func__,__LINE__);
		return FAILED;	
		}

		_rtl865x_qosArrangeQueue(ceil,rate,wrr_queue_num);
	}

	/* 3.  set QueueID mapping
		ex. if we create 4 queues (2 is priority , 2 is wrr )
			sw_qid ==>	0 : the highest priority 
						1 : the second priority 
						2 : 1st wrr queue
						3 : 2nd wrr queue
			
			hw_qid : 0   1   2   3   4   5 
                        sw_qid:  3   4   -   2   1   0

		ex. if we create 3 queues (1 is priority , 2 is wrr )
			sw_qid ==>	0 : the highest priority 					
						1 : 1st wrr queue
						2 : 2nd wrr queue
			
			hw_qid : 0   1   2   3   4   5 
                        sw_qid:  2   3   -   -   1   0
	*/	
	for(i=0;i<RTL8651_OUTPUTQUEUE_SIZE;i++)
		Qidmapping[i] = Queue_NOT_Create;

	memset(&upstream_priority_mapping[0],0,sizeof(aclpriority_mapping_data_t)*TOTAL_VLAN_PRIORITY_NUM);
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
		upstream_priority_mapping[i].remark_8021p = upstream_priority_mapping[i].remark_dscp = -1;

	/* Reserved 1 high queue */
	Qidmapping[UPSTREAM_Quene_NUM-1] = 0; // S/W Qid[0] mapping to H/W hight queue
	
        for(i=0;i<sp_queue_num;i++)
                Qidmapping[UPSTREAM_Quene_NUM-1-(i+1)] = i+1; /* Start from S/W queue 0 and reserve the highest queue for nonabandonable packets */

	for(i=0;i<wrr_queue_num;i++)
                Qidmapping[i+1] = sp_queue_num+i+1;

	/* Reserved 1 low queue */
//	Qidmapping[wrr_queue_num] = sp_queue_num+wrr_queue_num+1; // S/W Qid N+1 mapping H/W lowest queue 0

	if(_rtl865x_qosQIDMappingSet(Qidmapping)!=SUCCESS)
	{			
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return FAILED;
	}

	/*  4. Start  to set Queue scheduling ..  */
	if(_rtl865x_qos_setQueueInfo(sp_queue_num,wrr_queue_num,UPSTREAM_Quene_NUM,ceil,rate)!=SUCCESS)
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return FAILED;
	}

	/*  5. Disable 802.1p/DSCP remarking by default */
	if(_rtl865x_qos_remark(0,0)!=SUCCESS)
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return FAILED;
	}

	/*  6. set default acl priority mapping  */
	upstream_default_swQid = default_sw_qid;
	upstream_priority_default = rtl865x_qosPriorityMappingGet(default_sw_qid,-1,-1);
	if(upstream_priority_default==-1)
	{			
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return FAILED;
	}
	/* Init. reserved high/low queue to priority mapping */
//	rtl865x_qosPriorityMappingGet(0,-1,-1);
	rtl865x_qosPriorityMappingGet(sp_queue_num+wrr_queue_num+1,-1,-1);
	#if defined(CONFIG_RTL_8685_8PRIQUE)
	for(i=0;i<8;i++)
	#else
	for(i=0;i<6;i++)
	#endif
	{
		rtl8651_setAsicQueueFlowControlConfigureRegister(5,i,1);
		rtl8651_setAsicQueueFlowControlConfigureRegister(6,i,1);
	}
#if 0
	rtl8651_setAsicQueueFlowControlConfigureRegister(5,wrr_queue_num,0); //port 5 lowest queue flow control disable
	rtl8651_setAsicQueueFlowControlConfigureRegister(6,wrr_queue_num,0); //port 6 lowest queue flow control disable
#endif
	
	/*  7. init priority setting except fot ACL based in hw 
		8676 :	(1) 802.1p based (for 802.1p rules)
				(2) port based	(for default priority)
	*/	
	rtl8651_flushAsicDot1qAbsolutelyPriority();	
	for(i=0; i<RTL8651_PORT_NUMBER + 3; i++)
		rtl8651_setAsicPortDefaultPriority(i,upstream_priority_default);

	
	/*  8.  set  priority  between different qos based 
		8676 	: NAPT-based > ACL-based >802.1p based >port based
	*/
	rtl8651_setAsicPriorityDecision(priorityDecisionArray[PORT_BASE], 
	priorityDecisionArray[D1P_BASE], priorityDecisionArray[DSCP_BASE], 
	priorityDecisionArray[ACL_BASE], priorityDecisionArray[NAT_BASE]);	

	DBG_OutputQueue_PRK("Leave %s\n",__func__);
	
	return SUCCESS;	
}


/* Create a complete 8676 ACL Rule from 8676 SW QoS Rule */
static rtl865x_AclRule_t* __rtl865x_qosCreateACLrule(rtl865x_qos_rule_t* input_rule)
{
	rtl865x_AclRule_t*	AclRule;	
	AclRule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
	if(!AclRule)
		return NULL;
	
	memset(AclRule,0,sizeof(rtl865x_AclRule_t));	
	AclRule->actionType_ 	= RTL865X_ACL_PRIORITY;
	AclRule->pktOpApp_  = RTL865X_ACL_ALL_LAYER;
	AclRule->direction_ 	= RTL865X_ACL_INGRESS; 
	AclRule->priority_ 	= input_rule->priority;
	AclRule->ruleType_	= input_rule->acl_rule_data_format;
	memcpy(&AclRule->un_ty,&input_rule->acl_rule_data,sizeof(rtl865x_AclRuleData_t));

	return AclRule;
}

/* It inspects acl rule
	(NOT including  q_index and reamaking value ) 
*/
static int8 __rtl865x_sameQosRule(rtl865x_qos_rule_t *rule1, rtl865x_qos_rule_t *rule2)
{
	if(rtl865x_sameAclRuleDataField(&rule1->acl_rule_data,&rule2->acl_rule_data,rule1->acl_rule_data_format,rule2->acl_rule_data_format)==FALSE)		
		return FALSE;	
	else
	{
		if((rule1->priority != rule2->priority) || (rule1->swQid != rule2->swQid) || (rule1->remark_8021p != rule2->remark_8021p) || (rule1->remark_dscp != rule2->remark_dscp))		
			return FALSE;		
		else
			return TRUE;
	}		
}



/*  	
	FUNC 	: _rtl865x_qosAddRule()
	USAGE	: Add a new sw_qos entry into sw_qos_list , and add asic acl rule 
		
	Note.  
		1. It is caller's respnsibility to promise that all fileds  (except for linking list) in input_rule has been assigned correctly
		2. You have to get priority first. (Ref priority)	
*/

static int _rtl865x_qosAddRule(rtl865x_qos_rule_t* input_rule)
{
	rtl865x_qos_rule_t* rule_entry;	
	int rtn=0;

	/* dupliate check */
	list_for_each_entry(rule_entry,&rtl865x_qosRuleHead,qos_rule_list)
	{       
	    if(__rtl865x_sameQosRule(rule_entry,input_rule)==TRUE)
	    	return FAILED;	    
	}		

	/* ysleu@20140403: Support Traffic Classification */
#if defined(RTL867X_IPQoS_Support_Traffic_Classification) && (defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB))
	if(input_rule->vid)
	{
		rtl865x_AclRule_t*	aclRule;	
		aclRule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
		if(!aclRule)
			return FAILED;
		
		memset(aclRule,0,sizeof(rtl865x_AclRule_t));	
		aclRule->actionType_	= RTL865X_ACL_VID;
		aclRule->pktOpApp_	= RTL865X_ACL_ALL_LAYER;
		aclRule->direction_ 	= RTL865X_ACL_INGRESS;
		//FIXME: lose fid
#if defined(CONFIG_RTL_8685S_HWNAT)
		//aclRule->fid_
#endif
		aclRule->vid_ = input_rule->vid;
		aclRule->ruleType_	= input_rule->acl_rule_data_format;
		memcpy(&aclRule->un_ty,&input_rule->acl_rule_data,sizeof(rtl865x_AclRuleData_t));

		if(input_rule->outIfname[0]!='\0')											
			rtn=rtl865x_add_acl(aclRule, input_rule->outIfname, RTL865X_ACL_QOS_USED0,1,1);
		else
			rtn=rtl865x_add_acl(aclRule, NULL, RTL865X_ACL_QOS_USED0,1,1);
		if(rtn!=SUCCESS)	
		{
			DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
			kfree(aclRule);
			return FAILED;
		}
		kfree(aclRule); /* rtl865x_add_acl() will memcpy the rule */
	}
#endif

	if(input_rule->acl_rule_data_format!=RTL865X_ACL_802D1P && input_rule->acl_rule_data_format!=RTL865X_ACL_DSTINTF_1PREMARK)
	{
		rtl865x_AclRule_t*	aclRule;
		aclRule = __rtl865x_qosCreateACLrule(input_rule);
		if(!aclRule)
			return FAILED;

		if(input_rule->outIfname[0]!='\0')											
			rtn=rtl865x_add_acl(aclRule, input_rule->outIfname, RTL865X_ACL_QOS_USED0,1,1);
		else
			rtn=rtl865x_add_acl(aclRule, NULL, RTL865X_ACL_QOS_USED0,1,1);
		if(rtn!=SUCCESS)	
		{
			DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
			kfree(aclRule);
			return FAILED;
		}
		kfree(aclRule); /* rtl865x_add_acl() will memcpy the rule */
	}
	else	
	{
		if(rtl8651_setAsicDot1qAbsolutelyPriority(input_rule->acl_rule_data.VLANTAG.vlanTagPri, input_rule->priority)!=SUCCESS)
		{
			DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
			return FAILED;
		}
	}

	INIT_LIST_HEAD(&input_rule->qos_rule_list);
	list_add_tail(&input_rule->qos_rule_list , &rtl865x_qosRuleHead);
	
	return SUCCESS;
}
/*  	
	FUNC 	: _rtl865x_qosDelRule()
	USAGE	: Remove some specific sw_qos entry from sw_qos_list , and delete asic acl rule 
		
	Note. I will help you deref the prioity
*/
static void _rtl865x_qosDelRule(rtl865x_qos_rule_t* input_rule)
{		

	#ifdef DBG_QosRule
	DBG_QosRule_PRK("Enter %s\n",__func__);	
	DBG_QosRule_PRK("-------    deleted rule----------\n");
	rtl865x_show_QosAcl(input_rule);
	DBG_QosRule_PRK("----------------------------------\n");
	#endif	

/* ysleu@20140403: Support Traffic Classification */
#if defined(RTL867X_IPQoS_Support_Traffic_Classification) && (defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB))
	if(input_rule->vid)
	{
		rtl865x_AclRule_t*	aclRule;

		aclRule = kmalloc(sizeof(rtl865x_AclRule_t), GFP_ATOMIC);
		if(!aclRule)
			return ;
		
		memset(aclRule,0,sizeof(rtl865x_AclRule_t));	
		aclRule->actionType_	= RTL865X_ACL_VID;
		aclRule->pktOpApp_	= RTL865X_ACL_ALL_LAYER;
		aclRule->direction_ 	= RTL865X_ACL_INGRESS; 
		//FIXME:lose fid
#if defined(CONFIG_RTL_8685S_HWNAT)
		//aclRule->fid_
#endif
		aclRule->vid_ = input_rule->vid;
		aclRule->ruleType_	= input_rule->acl_rule_data_format;
		memcpy(&aclRule->un_ty,&input_rule->acl_rule_data,sizeof(rtl865x_AclRuleData_t));

		if(!aclRule)
			return;
		if(input_rule->outIfname[0]!='\0')
			rtl865x_del_acl(aclRule, input_rule->outIfname, RTL865X_ACL_QOS_USED0);
		else	
			rtl865x_del_acl(aclRule, NULL, RTL865X_ACL_QOS_USED0);
		kfree(aclRule);
	}
#endif

	if(input_rule->acl_rule_data_format!=RTL865X_ACL_802D1P && input_rule->acl_rule_data_format!=RTL865X_ACL_DSTINTF_1PREMARK)
	{
		rtl865x_AclRule_t*	aclRule;
		aclRule = __rtl865x_qosCreateACLrule(input_rule);
		if(!aclRule)
			return;
		if(input_rule->outIfname[0]!='\0')
			rtl865x_del_acl(aclRule, input_rule->outIfname, RTL865X_ACL_QOS_USED0);
		else	
			rtl865x_del_acl(aclRule, NULL, RTL865X_ACL_QOS_USED0);
		kfree(aclRule);
	}
	else
		rtl8651_setAsicDot1qAbsolutelyPriority(input_rule->acl_rule_data.VLANTAG.vlanTagPri, 0);

	rtl865x_qosPriorityMappingDeRef(input_rule->priority);
	list_del(&input_rule->qos_rule_list);
	kfree(input_rule);

	DBG_QosRule_PRK("Leave %s\n",__func__);	
}

/* Convert RTL867x IPQoS Format to 6239 acl format 
	Note. It only write un_ty and ruleType_
*/
static int _rtl865x_qosConvertDataForamt
	(rtl867x_hwnat_qos_rule_t* input_rule,rtl865x_AclRuleData_t* output_ruleData, int* output_ruleDataFormat)
{

	switch(input_rule->rule_type)
	{
		case RTL867x_IPQos_Format_Ethernet:
			*output_ruleDataFormat	= RTL865X_ACL_MAC;
			break;
		case RTL867x_IPQos_Format_IP:
			*output_ruleDataFormat	= RTL865X_ACL_IP;
			break;
		case RTL867x_IPQos_Format_IP_Range:
			*output_ruleDataFormat	= RTL865X_ACL_IP_RANGE;
			break;
		case RTL867x_IPQos_Format_TCP:
			*output_ruleDataFormat	= RTL865X_ACL_TCP;
			break;
		case RTL867x_IPQos_Format_TCP_Range:
			*output_ruleDataFormat	= RTL865X_ACL_TCP_IPRANGE;
			break;
		case RTL867x_IPQos_Format_UDP:
			*output_ruleDataFormat	= RTL865X_ACL_UDP;
			break;
		case RTL867x_IPQos_Format_UDP_Range:
			*output_ruleDataFormat	= RTL865X_ACL_UDP_IPRANGE;
			break;
		case RTL867x_IPQos_Format_8021p:
			*output_ruleDataFormat	= RTL865X_ACL_802D1P;
			break;
		case RTL867x_IPQos_Format_srcPort_setMask:
		case RTL867x_IPQos_Format_srcPort:
		case RTL867x_IPQos_Format_srcFilter:
			*output_ruleDataFormat	= RTL865X_ACL_SRCFILTER;
			break;
		case RTL867x_IPQos_Format_srcFilter_Range:
			*output_ruleDataFormat	= RTL865X_ACL_SRCFILTER_IPRANGE;
			break;
		case RTL867x_IPQos_Format_dstIntf_1pRemark:
			*output_ruleDataFormat	= RTL865X_ACL_DSTINTF_1PREMARK;
			break;
		default: /* unknow type or not supported */
			DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
			return FAILED;
	}


	switch(input_rule->rule_type)
	{
		case RTL867x_IPQos_Format_Ethernet:			
			output_ruleData->MAC._dstMac		= input_rule->match_field.L2._dstMac;
			output_ruleData->MAC._dstMacMask	= input_rule->match_field.L2._dstMacMask;
			output_ruleData->MAC._srcMac			= input_rule->match_field.L2._srcMac;
			output_ruleData->MAC._srcMacMask	= input_rule->match_field.L2._srcMacMask;
			output_ruleData->MAC._typeLen		= input_rule->match_field.L2._ethType;
			output_ruleData->MAC._typeLenMask	= input_rule->match_field.L2._ethTypeMask;
			break;


		case RTL867x_IPQos_Format_TCP:
		case RTL867x_IPQos_Format_TCP_Range:
			if(input_rule->match_field.L4._sport_start==0
				&& input_rule->match_field.L4._sport_end==0)
			{
				output_ruleData->L3L4.is.tcp._srcPortUpperBound = 0xFFFF;
				output_ruleData->L3L4.is.tcp._srcPortLowerBound = 0x0000;
			}
			else
			{
				output_ruleData->L3L4.is.tcp._srcPortUpperBound = input_rule->match_field.L4._sport_end;
				output_ruleData->L3L4.is.tcp._srcPortLowerBound = input_rule->match_field.L4._sport_start;
			}
			
			if(input_rule->match_field.L4._dport_start==0
				&& input_rule->match_field.L4._dport_end==0)
			{
				output_ruleData->L3L4.is.tcp._dstPortUpperBound = 0xFFFF;
				output_ruleData->L3L4.is.tcp._dstPortLowerBound = 0x0000;
			}
			else
			{
				output_ruleData->L3L4.is.tcp._dstPortUpperBound = input_rule->match_field.L4._dport_end;
				output_ruleData->L3L4.is.tcp._dstPortLowerBound = input_rule->match_field.L4._dport_start;
			}
			goto finish_port;
		case RTL867x_IPQos_Format_UDP:			
		case RTL867x_IPQos_Format_UDP_Range:
			if(input_rule->match_field.L4._sport_start==0
				&& input_rule->match_field.L4._sport_end==0)
			{
				output_ruleData->L3L4.is.udp._srcPortUpperBound = 0xFFFF;
				output_ruleData->L3L4.is.udp._srcPortLowerBound = 0x0000;
			}
			else
			{
				output_ruleData->L3L4.is.udp._srcPortUpperBound = input_rule->match_field.L4._sport_end;
				output_ruleData->L3L4.is.udp._srcPortLowerBound = input_rule->match_field.L4._sport_start;
			}
			
			if(input_rule->match_field.L4._dport_start==0
				&& input_rule->match_field.L4._dport_end==0)
			{
				output_ruleData->L3L4.is.udp._dstPortUpperBound = 0xFFFF;
				output_ruleData->L3L4.is.udp._dstPortLowerBound = 0x0000;
			}
			else
			{
				output_ruleData->L3L4.is.udp._dstPortUpperBound = input_rule->match_field.L4._dport_end;
				output_ruleData->L3L4.is.udp._dstPortLowerBound = input_rule->match_field.L4._dport_start;
			}	
			goto finish_port;
		case RTL867x_IPQos_Format_IP:
		case RTL867x_IPQos_Format_IP_Range:
		finish_port:			
		if(input_rule->rule_type == RTL867x_IPQos_Format_IP 
			|| input_rule->rule_type == RTL867x_IPQos_Format_TCP
			|| input_rule->rule_type == RTL867x_IPQos_Format_UDP)
		{
			output_ruleData->L3L4._srcIpAddr		= input_rule->match_field.L3.ip.mask._sip;
			output_ruleData->L3L4._srcIpAddrMask	= input_rule->match_field.L3.ip.mask._sipMask;
			output_ruleData->L3L4._dstIpAddr		= input_rule->match_field.L3.ip.mask._dip;
			output_ruleData->L3L4._dstIpAddrMask	= input_rule->match_field.L3.ip.mask._dipMask;
		}
		else //RTL867x_IPQos_Format_IP_Range , RTL867x_IPQos_Format_TCP_RANGE , RTL867x_IPQos_Format_UDP_Range
		{
			/*Hyking:Asic use Addr to srore Upper address
				and use Mask to store Lower address */
			if(input_rule->match_field.L3.ip.range._sip_start==0
				&& input_rule->match_field.L3.ip.range._sip_end==0)
			{
				output_ruleData->L3L4._srcIpAddr		= 0xFFFFFFFF;
				output_ruleData->L3L4._srcIpAddrMask	= 0x00000000;
			}
			else
			{
				output_ruleData->L3L4._srcIpAddr		= input_rule->match_field.L3.ip.range._sip_end;
				output_ruleData->L3L4._srcIpAddrMask	= input_rule->match_field.L3.ip.range._sip_start;
			}

			if(input_rule->match_field.L3.ip.range._dip_start==0
				&& input_rule->match_field.L3.ip.range._dip_end==0)
			{
				output_ruleData->L3L4._dstIpAddr		= 0xFFFFFFFF;
				output_ruleData->L3L4._dstIpAddrMask	= 0x00000000;
			}
			else
			{
				output_ruleData->L3L4._dstIpAddr		= input_rule->match_field.L3.ip.range._dip_end;
				output_ruleData->L3L4._dstIpAddrMask	= input_rule->match_field.L3.ip.range._dip_start;
			}
		}	

			output_ruleData->L3L4._tos			= input_rule->match_field.L3._tos;
			output_ruleData->L3L4._tosMask		= input_rule->match_field.L3._tosMask;	
		
		if(input_rule->rule_type == RTL867x_IPQos_Format_IP 
			|| input_rule->rule_type == RTL867x_IPQos_Format_IP_Range)
		{
			output_ruleData->L3L4.is.ip._proto		= input_rule->match_field.L3._ipProto;
			output_ruleData->L3L4.is.ip._protoMask	= input_rule->match_field.L3._ipProtoMask;
		}
			break;

		case RTL867x_IPQos_Format_8021p:
			output_ruleData->VLANTAG.vlanTagPri = input_rule->match_field.L2._8021p;
			break;

		case RTL867x_IPQos_Format_srcPort:
		{	
			struct net_device *dev = re865x_get_netdev_by_name(input_rule->match_field.PHY._lan_netifname);
			DBG_QosRule_PRK("(%s) %s\n",__func__,input_rule->match_field.PHY._lan_netifname);
			
			/*  check whether this interface is  under br0 */
			if(dev)
			{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
				struct net_bridge_port *br_port = br_port_get_rcu(dev);
				if(br_port && !strcmp(br_port_get_rcu(dev)->br->dev->name,RTL_DRV_LAN_NETIF_NAME))
#else
				struct net_bridge_port *br_port = dev->br_port;
				if(br_port && !strcmp(dev->br_port->br->dev->name,RTL_DRV_LAN_NETIF_NAME))
#endif
				{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0)
					output_ruleData->SRCFILTER._srcPort = ((struct dev_priv *)netdev_priv(dev))->portmask;  
#else
					output_ruleData->SRCFILTER._srcPort = ((struct dev_priv *)dev->priv)->portmask;  
#endif
#if 0
					output_ruleData->SRCFILTER._srcPortUpperBound	= 0xFFFF;
					output_ruleData->SRCFILTER._srcPortLowerBound	= 0x0000;
					output_ruleData->SRCFILTER._ignoreL4=1;
#else
					output_ruleData->SRCFILTER._ignoreL3L4=1;
#endif
				}
				else	
				{
					DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
					return FAILED;
				}		
			}
			else		
			{
				DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
				return FAILED;
			}
			
			break;
		}

		case RTL867x_IPQos_Format_srcFilter:	
		case RTL867x_IPQos_Format_srcFilter_Range:
		{
			output_ruleData->SRCFILTER._srcMac				= input_rule->match_field.L2._srcMac;
			output_ruleData->SRCFILTER._srcMacMask			= input_rule->match_field.L2._srcMacMask;
			output_ruleData->SRCFILTER._srcVlanIdx				= input_rule->match_field.L2._vlanid;
			output_ruleData->SRCFILTER._srcVlanIdxMask			= input_rule->match_field.L2._vlanidMask;

			if(input_rule->rule_type == RTL867x_IPQos_Format_srcFilter)
			{
				output_ruleData->SRCFILTER._srcIpAddr			= input_rule->match_field.L3.ip.mask._sip;
				output_ruleData->SRCFILTER._srcIpAddrMask		= input_rule->match_field.L3.ip.mask._sipMask;
			}
			else //RTL867x_IPQos_Format_srcFilter_Range
			{
				if(input_rule->match_field.L3.ip.range._sip_start==0
					&& input_rule->match_field.L3.ip.range._sip_end==0)
				{
					output_ruleData->SRCFILTER._srcIpAddr			= 0xFFFFFFFF;
					output_ruleData->SRCFILTER._srcIpAddrMask		= 0x00000000;
				}
				else
				{
					output_ruleData->SRCFILTER._srcIpAddr			= input_rule->match_field.L3.ip.range._sip_end;
					output_ruleData->SRCFILTER._srcIpAddrMask		= input_rule->match_field.L3.ip.range._sip_start;
				}
			}

			if(input_rule->match_field.L4._sport_start==0
				&& input_rule->match_field.L4._sport_end==0)
			{
				output_ruleData->SRCFILTER._srcPortUpperBound	= 0xFFFF;
				output_ruleData->SRCFILTER._srcPortLowerBound	= 0x0000;
			}
			else
			{
				output_ruleData->SRCFILTER._srcPortUpperBound	= input_rule->match_field.L4._sport_end;
				output_ruleData->SRCFILTER._srcPortLowerBound	= input_rule->match_field.L4._sport_start;
			}

			if(input_rule->match_field.L4._sport_start==0 && input_rule->match_field.L4._sport_end==0
				&& input_rule->match_field.L3.ip.mask._sip ==0 && input_rule->match_field.L3.ip.mask._sipMask ==0
				&& input_rule->match_field.L3.ip.range._sip_end ==0 && input_rule->match_field.L3.ip.range._sip_start ==0)
				output_ruleData->SRCFILTER._ignoreL3L4 = 1;

			else if(input_rule->match_field.L4._sport_start==0 && input_rule->match_field.L4._sport_end==0)
				output_ruleData->SRCFILTER._ignoreL4 = 1;

			break;	
		}
		case RTL867x_IPQos_Format_srcPort_setMask:
		{	
			output_ruleData->SRCFILTER._srcPort = input_rule->match_field.PHY._lan_portmask;  
			output_ruleData->SRCFILTER._srcPortUpperBound	= 0xFFFF;
			output_ruleData->SRCFILTER._srcPortLowerBound	= 0x0000;		
			output_ruleData->SRCFILTER._ignoreL4=1;
			break;
		}	
		case RTL867x_IPQos_Format_dstIntf_1pRemark:
			output_ruleData->VLANTAG.vlanTagPri = input_rule->match_field.L2._8021p;
			break;
		default: // unknow type
			DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
			return FAILED;
	}	
	return SUCCESS;
}
	

/* Note.
	1. if q_index , remark_8021p , remark_dscp <0 :	it means that ...
		(1) using the default setting (q_index = default_qid  ,  remark = 0 ) if it is a new qos rule
		(2) keep the original setting if there exists the same qos rule
	2. This function will copy the data in input_qos_rule , the caller can free input_qos_rule after calling function
*/
/* ysleu@20140403: Support Traffic Classification */
#if defined(RTL867X_IPQoS_Support_Traffic_Classification) && (defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB))
int32 rtl865x_qosAddRule(rtl867x_hwnat_qos_rule_t *input_qos_rule ,int q_index ,int vid,int remark_8021p ,int remark_dscp ,int* return_index)
#else
int32 rtl865x_qosAddRule(rtl867x_hwnat_qos_rule_t *input_qos_rule ,int q_index ,int remark_8021p ,int remark_dscp ,int* return_index)
#endif
{	
	rtl865x_qos_rule_t*	qosRule_entry;
	rtl865x_qos_rule_t*	qosRule_same = NULL ;
	rtl865x_qos_rule_t*	new_qosRule = NULL;
	int priority;
	unsigned int	candidate_index;
	
	DBG_QosRule_PRK("Enter %s\n",__func__);	

	/* Step1. Create a new sw ipqos rule entry  */
	new_qosRule = kmalloc(sizeof(rtl865x_qos_rule_t), GFP_ATOMIC);
	if(!new_qosRule)
		goto add_fail;
	memset(new_qosRule,0,sizeof(rtl865x_qos_rule_t));	

	
	/* Step2. convert input rule to 8676 acl rule */
	if(_rtl865x_qosConvertDataForamt(input_qos_rule,&new_qosRule->acl_rule_data,&new_qosRule->acl_rule_data_format)!=SUCCESS)
	{
		DBG_QosRule_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		goto add_fail;
	}	
	
	#ifdef DBG_QosRule
	DBG_QosRule_PRK("(%s)----------- After converting-----------------\n",__func__);
	rtl865x_showACL_DataField(&new_qosRule->acl_rule_data,new_qosRule->acl_rule_data_format,0);
	#endif	


	/* Step3. Setup IPQoS policy  (At the same time, if we found the same qos rule, delete older one) */	
	
	/* 	Step3.1  Qid , 802.1p / DSCP remark 
			
			if user input q_index , remark_8021p , remark_dscp <0 :	it means that ...
			(1) using the default setting (q_index = default_qid  ,  remark = 0 ) if it is a new qos rule
			(2) keep the original setting if there exists the same qos rule
	*/	
	list_for_each_entry(qosRule_entry,&rtl865x_qosRuleHead,qos_rule_list)
	{		
		if(rtl865x_sameAclRuleDataField(&new_qosRule->acl_rule_data,&qosRule_entry->acl_rule_data
										,new_qosRule->acl_rule_data_format,qosRule_entry->acl_rule_data_format)==TRUE)
		{
			qosRule_same = qosRule_entry;
			break;
		}
	}	

	if(qosRule_same) /* there exists the same qos rule */
	{
		DBG_QosRule_PRK("(%s %d)there exists the same qos rule \n",__func__,__LINE__);	

		/* Update Qid , 802.1p / DSCP remark  */
		new_qosRule->swQid 			= ((q_index>=0)?			q_index: 		qosRule_same->swQid);
		new_qosRule->remark_8021p 	= ((remark_8021p>=0)?	remark_8021p: 	qosRule_same->remark_8021p);
		new_qosRule->remark_dscp 	= ((remark_dscp>=0)?		remark_dscp: 	qosRule_same->remark_dscp);
/* ysleu@20140403: Support Traffic Classification */
#if defined(RTL867X_IPQoS_Support_Traffic_Classification) && (defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB))
		new_qosRule->vid 			= ((vid>=0)?	vid: 	qosRule_same->vid);
#endif

		/* Delete the older rule */
		_rtl865x_qosDelRule(qosRule_same);		
	
	}
	else /* new qos rule */
	{
		/* New  Qid , 802.1p / DSCP remark  */
		new_qosRule->swQid			= ((q_index>=0)?			q_index: 		upstream_default_swQid);
		new_qosRule->remark_8021p	= remark_8021p;
		new_qosRule->remark_dscp	= remark_dscp;
/* ysleu@20140403: Support Traffic Classification */
#if defined(RTL867X_IPQoS_Support_Traffic_Classification) && (defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB))
		new_qosRule->vid			= ((vid>=0)?	vid: 	0);
#endif
	}

	/* 	Step3.2  Get acl priority */
	priority = rtl865x_qosPriorityMappingGet(new_qosRule->swQid,new_qosRule->remark_8021p,new_qosRule->remark_dscp);
	if(priority==-1)
	{
		DBG_QosRule_PRK("Hardware priority exceeds! Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		goto add_fail;
	}	
	else
		new_qosRule->priority 	= priority;		
	memcpy(new_qosRule->outIfname,input_qos_rule->outIfname,sizeof(input_qos_rule->outIfname));

	/* Step 4. find an available unique index */
	candidate_index=0;
	while(1)
	{		
		int 	findout = 1;			
		list_for_each_entry(qosRule_entry,&rtl865x_qosRuleHead,qos_rule_list)
		{
			if(candidate_index==qosRule_entry->index)
			{
				findout = 0;
				break;
			}
		}

		if(findout)
		{
			new_qosRule->index = candidate_index;
			break;
		}
		else		
			candidate_index++;
		
	}	
	
	/* Step 5. Finally, start to add acl rule*/	
	if(_rtl865x_qosAddRule(new_qosRule)!=SUCCESS)	
		goto add_fail_deref_prio;

	*return_index = new_qosRule->index;
	DBG_QosRule_PRK("Leave %s\n",__func__);	
	return SUCCESS;

add_fail_deref_prio:
	rtl865x_qosPriorityMappingDeRef(priority);
add_fail:
	if(new_qosRule)
		kfree(new_qosRule);
	DBG_QosRule_PRK("Leave %s @ %d\n",__func__,__LINE__);	
	return FAILED;
}

int32 rtl865x_qosDelRule(int input_index)
{
	rtl865x_qos_rule_t	*qosRule_entry;
	rtl865x_qos_rule_t	*qosRule_nxt;

	DBG_QosRule_PRK("Enter %s\n",__func__);
	
	list_for_each_entry_safe(qosRule_entry, qosRule_nxt,&rtl865x_qosRuleHead,qos_rule_list) {	
		if(qosRule_entry->index == input_index)
		{
			_rtl865x_qosDelRule(qosRule_entry);
			break;
		}
	}

	return SUCCESS;
}

int32 rtl865x_qosFlushRule(void)
{
	rtl865x_qos_rule_t	*qosRule_entry;
	rtl865x_qos_rule_t	*qosRule_nxt;

	DBG_QosRule_PRK("Enter %s\n",__func__);
	
	list_for_each_entry_safe(qosRule_entry, qosRule_nxt,&rtl865x_qosRuleHead,qos_rule_list) {		 
		_rtl865x_qosDelRule(qosRule_entry);
	}

	
	return SUCCESS;
}

int32 rtl865x_Qos_SetRemarking(int enable_8021p,int enable_dscp)
{
	DBG_OutputQueue_PRK("Enter %s  (enable_8021p:%d  enable_dscp:%d)\n",__func__,enable_8021p,enable_dscp);
	return _rtl865x_qos_remark(enable_8021p,enable_dscp);
}


int32 rtl865x_closeQos(void)
{
	
	uint32	i;

	DBG_OutputQueue_PRK("Enter %s\n",__func__);
	/* 1.  clear Qid  mapping and acl priotiy mapping */
	_rtl865x_qosQIDMappingClear();
	upstream_default_swQid = -1;

	/*  2. flush all sw qos rules */	
	rtl865x_qosFlushRule();
	rtl865x_raiseEvent(EVENT_FLUSH_QOSRULE, NULL); /* inform napt table  to cancel its IPQos*/
	/*  3. clear default acl priority */
	rtl865x_qosPriorityMappingDeRef(upstream_priority_default);
	rtl865x_qosPriorityMappingDeRef(UPSTREAM_Quene_NUM-1);
	
	/* 4. check whether all ref count reaches zero ? */
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
	{
                if( upstream_priority_mapping[i].valid)
                        rtl865x_qosPriorityMappingDeRef(i);

		if( upstream_priority_mapping[i].valid)
		{			
			printk("(%s)!!!!BUG!!!!  @%d (return %d, ref:%d)\n",__func__,__LINE__,i,upstream_priority_mapping[i].ref_count);		
		}
	}

	
	/*  5. Start  to set Queue scheduling .. (back to number 1)  */
	if(_rtl865x_qos_setQueueInfo(1,0,1,NULL,NULL)!=SUCCESS)
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return FAILED;
	}

	/*  6. Disable 802.1p/DSCP remarking */
	if(_rtl865x_qos_remark(0,0)!=SUCCESS)
	{
		DBG_OutputQueue_PRK("Leave %s@ %d(return FAILED)\n",__func__,__LINE__);
		return FAILED;
	}

	DBG_OutputQueue_PRK("Leave %s\n",__func__);
	return SUCCESS;
}

/* When enable hwQos  ....*/
int __init rtl865x_initOutputQueue(void)
{
	int	i;

	DBG_OutputQueue_PRK("Enter %s\n",__func__);	

	#if 0
	/* register Qos chain in ACL */
	{
		ForEachMasterNetif_Declaration
		ForEachMasterNetif_Start
			rtl865x_regist_aclChain(netif->name, RTL865X_ACL_QOS_USED0 ,8);
			rtl865x_regist_aclChain(netif->name, RTL865X_ACL_QOS_USED1 ,0);
		ForEachMasterNetif_End		
	}	
	#endif

	/* Init some sw_data (sw_priority_mapping , sw_qosrule .. ) */
	upstream_priority_default = -1;
	memset(upstream_priority_mapping, 0, TOTAL_VLAN_PRIORITY_NUM*sizeof(aclpriority_mapping_data_t));
	for(i=0;i<TOTAL_VLAN_PRIORITY_NUM;i++)
		upstream_priority_mapping[i].remark_8021p = upstream_priority_mapping[i].remark_dscp = -1;
	
	for(i=0;i<RTL8651_OUTPUTQUEUE_SIZE;i++)
	{			
		upstream_hwQid2swQid_Mapping[i] = Queue_NOT_Create;			
	}	

	INIT_LIST_HEAD(&rtl865x_qosRuleHead);

	DBG_OutputQueue_PRK("Leave %s\n",__func__);
	
	return SUCCESS;
}

void __exit rtl865x_exitOutputQueue(void)
{
	ForEachMasterNetif_Declaration
	ForEachMasterNetif_Start
		rtl865x_unRegist_aclChain(netif->name, RTL865X_ACL_QOS_USED0);
		//rtl865x_unRegist_aclChain(netif->name, RTL865X_ACL_QOS_USED1);
	ForEachMasterNetif_End

	rtl865x_qosFlushBandwidth(0xFF);	
	rtl865x_closeQos();
}