/*
* ----------------------------------------------------------------
* Copyright c                  Realtek Semiconductor Corporation, 2002  
* All rights reserved.
* 
* $Header: /usr/local/dslrepos/linux-2.6.30/drivers/net/rtl819x/rtl865xc_swNic.c,v 1.25 2013/01/24 09:52:57 knight_peng Exp $
*
* Abstract: Switch core polling mode NIC driver source code.
*
* $Author: knight_peng $
*
* $Log: rtl865xc_swNic.c,v $
* Revision 1.25  2013/01/24 09:52:57  knight_peng
* fix RX_info->len in case of receive huge packet which consume more than one mbuff.
*
* Revision 1.24  2013/01/14 06:29:32  knight_peng
* patch for huge packet that need more than one mbuf in a pkthdr
*
* Revision 1.23  2012/11/19 08:55:45  ikevin362
* setup tx desc. when xmit packet with vlan tag
*
* Revision 1.22  2012/11/15 08:57:13  ikevin362
* RTL8685 support
*
* Revision 1.21  2012/05/23 03:31:42  czpinging
* fix compiler error
*
* Revision 1.20  2012/05/22 04:07:46  czpinging
* refill priority in descriptor
*
* Revision 1.19  2012/05/21 08:47:44  czpinging
* refill priority in descriptork
*
* Revision 1.18  2012/04/23 09:19:27  ikevin362
* skb_debug when enable dump_swNicTxRx_pkt
*
* Revision 1.17  2012/03/09 13:28:23  kaohj
* invalidate cache before DMA
*
* Revision 1.16  2012/02/21 08:49:53  tylo
* add debug by Kevin
*
* Revision 1.14  2011/12/09 08:37:56  cathy
* enable debug for rx and remove trap_by_ingress_acl
*
* Revision 1.13  2011/11/17 07:47:26  czpinging
* (Redef)8367b
*
* Revision 1.12  2011/11/17 03:37:34  czpinging
* Add RL6000 testing function
*
* Revision 1.11  2011/11/16 14:09:09  czpinging
* Add RL6000 testing function
*
* Revision 1.10  2011/11/04 10:44:09  kaohj
* Fix compile error, use DELAY_REFILL_ETH_RX_BUF
*
* Revision 1.9  2011/07/29 14:18:35  ql
* nothing change
*
* Revision 1.8  2011/07/25 07:01:29  ql
* add debug info.
*
* Revision 1.7  2011/07/22 08:01:15  ikevin362
* if the pkt is only toward to extPort, reset vid to a magic number
*
* Revision 1.6  2011/07/11 09:04:56  cathy
* avoid free the same skb in swNic_txDone
*
* Revision 1.5  2011/06/27 14:12:45  ikevin362
* 1. print the reason when trap to cpu  2.mark the packet which is trapped by ingress acl rule
*
* Revision 1.4  2011/06/20 12:07:05  tylo
* fix unknown unicast rx issue
*
* Revision 1.3  2011/06/13 10:27:49  ikevin362
* add trap2cpu pkt debug
*
* Revision 1.2  2011/04/11 12:45:18  tylo
* update hw nat driver from AP team
*
* Revision 1.11  2008/04/11 10:49:14  bo_zhao
* * restore the original cache flush
*
* Revision 1.10  2008/04/11 10:12:38  bo_zhao
* *: swap nic drive to 8186 style
*
* Revision 1.6  2008/02/22 05:31:52  joeylin
* set one VLAN group for Bridge/WISP mode, and fix the issue:
* WAN port PC can not ping br0 (192.168.1.254) in Bridge/WISP mode
*
* Revision 1.5  2008/02/15 09:52:46  forrest
* 1. Add hardware accelerated PPTP processing. 2. Fine tune some hardware NAT to be compatible to hardware accelerated PPTP.
*
* Revision 1.4  2007/12/08 08:24:26  davidhsu
* Adjust tx desc size. Hide error message
*
* Revision 1.3  2007/12/04 12:00:18  joeylin
* add hardware NAT feature
*
* Revision 1.2  2007/11/11 02:51:24  davidhsu
* Fix the bug that do not fre rx skb in rx descriptor when driver is shutdown
*
* Revision 1.1.1.1  2007/08/06 10:04:52  root
* Initial import source to CVS
*
* Revision 1.11  2007/03/27 12:51:07  michaelhuang
* +: add function swNic_send_portmbr for FT2
*
*
*
* ---------------------------------------------------------------
*/

#include <net/rtl/rtl_types.h>
#include <net/rtl/rtl_glue.h>
#include <net/rtl/rtl_nic.h>
#include "common/rtl_errno.h"
#include "AsicDriver/asicRegs.h"
#include "rtl865xc_swNic.h"
#include "common/mbuf.h"
#include "AsicDriver/rtl865x_asicCom.h"
#include "AsicDriver/rtl865x_asicL2.h"

#include <linux/skbuff.h>
#include "AsicDriver/rtl865xC_hs.h"
#ifdef	CONFIG_RTL865X_ROMEPERF
#include "romeperf.h"
#endif
#include <linux/netdevice.h>
#ifdef CONFIG_FAST_FORWARDING
#include "../brg_shortcut.h"
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <linux/etherdevice.h>
#endif//end of CONFIG_FAST_FORWARDING

int DumpTrapCPUHs_debug = 0;
int DumpTrapCPUHs_debug_LIMIT = 0;
int DumpTrapCPUpkt_debug = 0;
int DumpTrapCPUpkt_debug_LIMIT = 0;
int DumpSwNicTxRx_debug = 0;
int DumpSwNicTxRx_debug_LIMIT = 0;
int DumpERBpacket_debug = 0;
#if defined(CONFIG_RTL_8685S_HWNAT)
	char pkt_data[2048];
	int DumpSwNicTxRx_debug_pkt=0;
#endif

extern void (*_dma_cache_wback_inv)(unsigned long start, unsigned long size);
extern void tx_done_callback(void *skb);

#ifdef CONFIG_DATA_IN_IMEM
extern void *allocUncachedBuffFromIMEM(uint32 len);
#endif
#ifdef CONFIG_RTL_ETH_PRIV_SKB_ADV
extern void rtl865x_free_eth_priv_buf(struct sk_buff *skb, unsigned flag);
extern unsigned char *get_buf_from_poll(void);
#endif//end of CONFIG_RTL_ETH_PRIV_SKB_ADV

/* RX Ring */
static uint32*  rxPkthdrRing[RTL865X_SWNIC_RXRING_HW_PKTDESC];                 /* Point to the starting address of RX pkt Hdr Ring */
static dma_addr_t rxPkthdrRing_addr[RTL865X_SWNIC_RXRING_HW_PKTDESC];

__DRAM_FWD static uint32   rxPkthdrRingCnt[RTL865X_SWNIC_RXRING_HW_PKTDESC];              /* Total pkt count for each Rx descriptor Ring */
__DRAM_FWD static uint32   rxPkthdrRefillThreshold[RTL865X_SWNIC_RXRING_HW_PKTDESC];              /* Ether refill threshold for each Rx descriptor Ring */

/* TX Ring */
static uint32*  txPkthdrRing[RTL865X_SWNIC_TXRING_HW_PKTDESC];             /* Point to the starting address of TX pkt Hdr Ring */
static dma_addr_t txPkthdrRing_addr[RTL865X_SWNIC_TXRING_HW_PKTDESC];

#if defined(CONFIG_RTL8196C_REVISION_B)
__DRAM_FWD static uint32	rtl_chip_version;
static uint32*  txPkthdrRing_backup[RTL865X_SWNIC_TXRING_HW_PKTDESC];             /* Point to the starting address of TX pkt Hdr Ring */
static dma_addr_t  txPkthdrRing_backup_addr[RTL865X_SWNIC_TXRING_HW_PKTDESC];
#endif

__DRAM_FWD static uint32   txPkthdrRingCnt[RTL865X_SWNIC_TXRING_HW_PKTDESC];          /* Total pkt count for each Tx descriptor Ring */

#define txPktHdrRingFull(idx)   (((txPkthdrRingFreeIndex[idx] + 1) & (txPkthdrRingMaxIndex[idx])) == (txPkthdrRingDoneIndex[idx]))

/* Mbuf */
static uint32* rxMbufRing;                                                     /* Point to the starting address of MBUF Ring */
static dma_addr_t rxMbufRing_addr;

__DRAM_FWD static uint32  rxMbufRingCnt;                                                  /* Total MBUF count */

__DRAM_FWD uint32  size_of_cluster;

/* descriptor ring tracing pointers */
__DRAM_FWD static int32   currRxPkthdrDescIndex[RTL865X_SWNIC_RXRING_HW_PKTDESC];      /* Rx pkthdr descriptor to be handled by CPU */
__DRAM_FWD static int32   currRxMbufDescIndex;        /* Rx mbuf descriptor to be handled by CPU */

__DRAM_FWD static int32   currTxPkthdrDescIndex[RTL865X_SWNIC_TXRING_HW_PKTDESC];      /* Tx pkthdr descriptor to be handled by CPU */
__DRAM_FWD static int32 txPktDoneDescIndex[RTL865X_SWNIC_TXRING_HW_PKTDESC];

/* Rx hs table */
void dump_hstbl(void)
{
        hsb_param_t *hsb_r, dummy_hsb_r;
        hsa_param_t *hsa_r, dummy_hsa_r;
        ipaddr_t addr;
        char addr_s[100];

        hsb_r = &dummy_hsb_r;
        hsa_r = &dummy_hsa_r;
        memset((void*)hsb_r,0,sizeof(hsa_param_t));
        memset((void*)hsa_r,0,sizeof(hsa_param_t));

        rtl865xC_virtualMacGetHsb( hsb_r );
        {
                printk("HSB(");
                printk("\ttype:%d",hsb_r->type);

                printk("\tspa:%d",hsb_r->spa);
                printk("\tlen:%d",hsb_r->len);
                printk("\tvid :%d\n",hsb_r->vid);
                printk("\tpppoe:%d",hsb_r->pppoeif);

                /* Protocol contents */
                printk("\ttagif:%d\tpppoeId:%d",hsb_r->tagif,hsb_r->pppoeid);
                printk("\tethrtype:0x%04x\n",hsb_r->ethtype);
                printk("\tllc_other:%d\tsnap:%d\n",hsb_r->llcothr,hsb_r->snap);
                printk("\tda:%02x-%02x-%02x-%02x-%02x-%02x",hsb_r->da[0],hsb_r->da[1],hsb_r->da[2],hsb_r->da[3],hsb_r->da[4],hsb_r->da[5]);
                printk("\tsa:%02x-%02x-%02x-%02x-%02x-%02x\n",hsb_r->sa[0],hsb_r->sa[1],hsb_r->sa[2],hsb_r->sa[3],hsb_r->sa[4],hsb_r->sa[5]);

                addr = ntohl( hsb_r->sip);
                inet_ntoa_r(addr, addr_s);
                printk("\tsip:%s(hex:%08x)   ",addr_s,hsb_r->sip);
                printk("\tsprt:%d (hex:%x)\n ",(int)hsb_r->sprt,hsb_r->sprt);
                addr  = ntohl(hsb_r->dip);
                inet_ntoa_r(addr, addr_s);
                printk("\tdip:%s(hex:%08x) ",addr_s,hsb_r->dip);;
                printk("\tdprt:%d(hex:%08x)\n",hsb_r->dprt,hsb_r->dprt);

                printk("\tipptl:%d,",(int)hsb_r->ipptl);
                printk("\tipflg:%d,",hsb_r->ipfg);
                printk("\tiptos:%d,",hsb_r->iptos);
                printk("\ttcpflg:%d\n",hsb_r->tcpfg);

                printk("\tdirtx:%d,",hsb_r->dirtx);
                printk("\tprtnmat:%d",hsb_r->patmatch);

                printk("\tudp_nocs:%d",hsb_r->udpnocs);
                printk("\tttlst:0x%x\n",hsb_r->ttlst);

                printk("\thp:%d",hsb_r->hiprior);
                printk("\tl3csok:%d\tl4csok:%d\tipfragif:%d\n",hsb_r->l3csok,hsb_r->l4csok,hsb_r->ipfo0_n);

                printk("\textspa:%d",hsb_r->extspa);
#if defined(CONFIG_RTL_8685S_HWNAT)
		printk("\turlmch:%d\n",hsb_r->urlmch);


		printk("\t ipv4_opt:%d",hsb_r->ipv4_opt);
		printk("\t cpuTagIf:%d",hsb_r->cpuTagIf);
		printk("\t v6Ext:%d\n",hsb_r->v6Ext);
		printk("\t v6fragmOffs:%d",hsb_r->v6fragmOffs);	
		printk("\t v6Flag:%d",hsb_r->v6Flag);	
		printk("\t qpri:%d\n",hsb_r->qpri);	
		printk("\t ptpPkt:%d",hsb_r->ptpPkt);	
		printk("\t ptpVer:%d",hsb_r->ptpVer);	
		printk("\t ptpTyp:%d",hsb_r->ptpTyp);	
		printk("\t ipVerFirst:%d\n",hsb_r->ipVerFirst);	

		printk("\t Sipv6:\t%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n"
		                ,hsb_r->v6Sip.s6_addr32[0]>>16,hsb_r->v6Sip.s6_addr32[0]&0xFFFF
		        		,hsb_r->v6Sip.s6_addr32[1]>>16,hsb_r->v6Sip.s6_addr32[1]&0xFFFF
		        		,hsb_r->v6Sip.s6_addr32[2]>>16,hsb_r->v6Sip.s6_addr32[2]&0xFFFF
		        		,hsb_r->v6Sip.s6_addr32[3]>>16,hsb_r->v6Sip.s6_addr32[3]&0xFFFF);		

		printk("\t Dipv6:\t%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n"
						,hsb_r->v6Dip.s6_addr32[0]>>16,hsb_r->v6Dip.s6_addr32[0]&0xFFFF
						,hsb_r->v6Dip.s6_addr32[1]>>16,hsb_r->v6Dip.s6_addr32[1]&0xFFFF
						,hsb_r->v6Dip.s6_addr32[2]>>16,hsb_r->v6Dip.s6_addr32[2]&0xFFFF
						,hsb_r->v6Dip.s6_addr32[3]>>16,hsb_r->v6Dip.s6_addr32[3]&0xFFFF);	
		
		printk("\t v6HopLimit:%d",hsb_r->v6HopLimit);
		printk("\t v6TafficClass:%d",hsb_r->v6TafficClass);
		printk("\t v6FlowLabel:%d",hsb_r->v6FlowLabel);
		printk("\t v6NxtHdr:%d\n",hsb_r->v6NxtHdr);
		printk("\t v4PktHdr:%d",hsb_r->v4PktHdr);
		printk("\t v6PktHdr:%d",hsb_r->v6PktHdr);
		printk("\t innerIpLen:%d",hsb_r->innerIpLen);
		if(IS_AFTER_RL6405)
		{
			printk("\t tunnelLen:%d\n",hsb_r->tunnelLen);
			printk("\t l3ChecksumOfflad:%d l4ChecksumOfflad:%d  innIpHeaderLen:%d \n\t swredPktFlag:%d  swredPktFlag:%d",
				hsb_r->l3ChecksumOfflad,hsb_r->l4ChecksumOfflad,hsb_r->innIpHeaderLen,hsb_r->swredPktFlag,hsb_r->swredPktType);
			printk("  l3Checksum:%d l4Checksum:%d \n)\n",
							hsb_r->l3Checksum,hsb_r->l4Checksum);
		}
		else

		{
			printk("\t tunnelLen:%d\n)\n",hsb_r->tunnelLen);
		}	


#else /* CONFIG_RTL_8685S_HWNAT */
		printk("\turlmch:%d\n)\n",hsb_r->urlmch);

#endif /* CONFIG_RTL_8685S_HWNAT */
	}
		
	rtl865xC_virtualMacGetHsa( hsa_r );
	{
		printk(("HSA("));
		printk("\tmac:%02x-%02x-%02x-%02x-%02x-%02x\n",hsa_r->nhmac[0],hsa_r->nhmac[1],hsa_r->nhmac[2],hsa_r->nhmac[3],hsa_r->nhmac[4],hsa_r->nhmac[5]);

		addr =ntohl( hsa_r->trip);
		inet_ntoa_r(addr, addr_s);
		printk("\ttrip:%s(hex:%08x)",addr_s,hsa_r->trip);	
		printk("\tprt:%d\tipmcast:%d\n",hsa_r->port,hsa_r->ipmcastr);
		printk("\tl3cs:%d",hsa_r->l3csdt);
		printk("\tl4cs:%d",hsa_r->l4csdt);
		printk("\tInternal NETIF:%d",hsa_r->egif);
		printk("\tl2tr:%d,\n ",hsa_r->l2tr);
		printk("\tl34tr:%d",hsa_r->l34tr);
		printk("\tdirtx:%d",hsa_r->dirtxo);
		printk("\ttype:%d",hsa_r->typeo);
		printk("\tsnapo:%d",hsa_r->snapo);
		printk("\twhy2cpu 0x%x (%d)\n",hsa_r->why2cpu,hsa_r->why2cpu);
		printk("\tpppif:%d",hsa_r->pppoeifo);
		printk("\tpppid:%d",hsa_r->pppidx);
		printk("\tttl_1:0x%x",hsa_r->ttl_1if);
		printk("\tdpc:%d,",hsa_r->dpc);

		printk("\tleno:%d(0x%x)\n",hsa_r->leno,hsa_r->leno);

		printk("\tl3CrcOk:%d",hsa_r->l3csoko);
		printk("\tl4CrcOk:%d",hsa_r->l4csoko);
		printk("\tfrag:%d",hsa_r->frag);
		printk("\tlastFrag:%d\n",hsa_r->lastfrag);



		printk("\tsvid:0x%x",hsa_r->svid);
		printk("\tdvid:%d(0x%x)",hsa_r->dvid,hsa_r->dvid);
		printk("\tdestination interface :%d\n",hsa_r->difid);
		printk("\trxtag:%d",hsa_r->rxtag);
		printk("\tdvtag:0x%x",hsa_r->dvtag);
		printk("\tspa:%d",hsa_r->spao);
		printk("\tdpext:0x%x\thwfwrd:%d\n",hsa_r->dpext,hsa_r->hwfwrd);
		printk("\tspcp:%d",hsa_r->spcp);
		printk("\tpriority:%d",hsa_r->priority);
		
		printk("\tdp:0x%x\n",hsa_r->dp);

#if defined(CONFIG_RTL_8685S_HWNAT)
		printk("\t mirrort:%d",hsa_r->mirrort);
		printk("\t v4First:%d",hsa_r->v4First);
		printk("\t cpuTag:%d\n",hsa_r->cpuTag);
		printk("\t ptpPkt:%d",hsa_r->ptpPkt);
		printk("\t ptpV2:%d",hsa_r->ptpV2);
		printk("\t ptpType:%d\n",hsa_r->ptpType);
		printk("\t routeModeDstPort:%d",hsa_r->routeModeDstPort);
		printk("\t dPri:%d",hsa_r->dPri);
		printk("\t ipMdf:%d\n",hsa_r->ipMdf);

		printk("\t Sipv6(6rd/dslite):\t%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n"
						,hsa_r->sip.s6_addr32[0]>>16,hsa_r->sip.s6_addr32[0]&0xFFFF
						,hsa_r->sip.s6_addr32[1]>>16,hsa_r->sip.s6_addr32[1]&0xFFFF
						,hsa_r->sip.s6_addr32[2]>>16,hsa_r->sip.s6_addr32[2]&0xFFFF
						,hsa_r->sip.s6_addr32[3]>>16,hsa_r->sip.s6_addr32[3]&0xFFFF);

		printk("\t Dipv6(6rd/dslite):\t%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n"
						,hsa_r->dip.s6_addr32[0]>>16,hsa_r->dip.s6_addr32[0]&0xFFFF
						,hsa_r->dip.s6_addr32[1]>>16,hsa_r->dip.s6_addr32[1]&0xFFFF
						,hsa_r->dip.s6_addr32[2]>>16,hsa_r->dip.s6_addr32[2]&0xFFFF
						,hsa_r->dip.s6_addr32[3]>>16,hsa_r->dip.s6_addr32[3]&0xFFFF);

		printk("\t ipLen:%d",hsa_r->ipLen);
		printk("\t v4Id:%d",hsa_r->v4Id);
		printk("\t v4pkt:%d",hsa_r->v4pkt);
		printk("\t v6pkt:%d\n",hsa_r->v6pkt);
		printk("\t tunnelLen:%d",hsa_r->tunnelLen);
		printk("\t v6Multicast:%d",hsa_r->v6Multicast);
		printk("\t addipPri:%d\n",hsa_r->addipPri);
		
		if(IS_AFTER_RL6405)
		{
			printk("\t l3ChecksumOfflad:%d  l4ChecksumOfflad:%d  innIpHeaderLen:%d  \n\t swredPktFlag:%d  swredPktType:%d \n"							,hsa_r->l3ChecksumOfflad,hsa_r->l4ChecksumOfflad,hsa_r->innIpHeaderLen,hsa_r->swredPktFlag,hsa_r->swredPktType);
		}

#else /* CONFIG_RTL_8685S_HWNAT */
		printk("\t fragpkt:%d",hsa_r->fragpkt);
#endif /* CONFIG_RTL_8685S_HWNAT */

		
		printk(")\n");
	}

	return; 
}

/* debug counters */
//__DRAM_FWD static int32   rxPktCounter;
//__DRAM_FWD static int32   txPktCounter;

#ifdef DELAY_REFILL_ETH_RX_BUF
__DRAM_FWD static int32   rxDescReadyForHwIndex[RTL865X_SWNIC_RXRING_HW_PKTDESC];
#endif

__DRAM_FWD static uint8 extPortMaskToPortNum[_RTL865XB_EXTPORTMASKS+1] =
{
	0, 1, 2, 0, 3, 0, 0, 0
};

#if defined(CONFIG_RTL_PROC_DEBUG)||defined(CONFIG_RTL_DEBUG_TOOL)
__DRAM_FWD unsigned int rx_noBuffer_cnt;
__DRAM_FWD unsigned int tx_ringFull_cnt;
__DRAM_FWD unsigned int tx_drop_cnt;
#endif

/* locks for Tx/Rx descriptor rings */
//#define DEBUG_LOCKS	1
#define CPU_NONE -1

#ifdef DEBUG_LOCKS
struct swcore_locks_counter_s {
	u64 cntr_wait_total;
	u32 cntr_wait_high;
	u32 cntr_lock;
	u32 cntr_unlock;
	u32 cntr_contention;
};

#define DEFINE_SWCORE_LOCKS_ARRAY_VAR(name, size) \
	static spinlock_t name##_locks[size]; \
	static int name##_locks_owners[size]; \
	DEFINE_PER_CPU(struct swcore_locks_counter_s, name##_lock_counters);
	
#define DEFINE_SWCORE_LOCKS_ARRAY_FUNC(name, size) \
	unsigned long swcore_##name##_lock(int ring) { \
		unsigned long flags = 0; \
		u32 t0, t1, diff;	\
		struct swcore_locks_counter_s *cntr = &per_cpu(name##_lock_counters, smp_processor_id()); \
		if (name##_locks_owners[ring]==CPU_NONE) \
			name##_locks_owners[ring] = smp_processor_id(); \
		else \
			cntr->cntr_contention++; \
		t0 = read_c0_count(); \
		spin_lock_irqsave(&name##_locks[ring], flags); \
		t1 = read_c0_count(); \
		diff = t1 - t0;\
		cntr->cntr_wait_total += diff; \
		if (unlikely(diff > cntr->cntr_wait_high)) \
			cntr->cntr_wait_high = diff; \
		cntr->cntr_lock++; \
		return flags; \
	} \
	void swcore_##name##_unlock(int ring, unsigned long flags) { \
		struct swcore_locks_counter_s *cntr = &per_cpu(name##_lock_counters, smp_processor_id()); \
		name##_locks_owners[ring] = CPU_NONE;	\
		spin_unlock_irqrestore(&name##_locks[ring], flags);	\
		cntr->cntr_unlock++;	\
	}
#else 
#define DEFINE_SWCORE_LOCKS_ARRAY_VAR(name, size) \
	static spinlock_t name##_locks[size] __attribute__((__aligned__(32)));
	
#define DEFINE_SWCORE_LOCKS_ARRAY_FUNC(name, size) \
	unsigned long swcore_##name##_lock(int ring) { \
		unsigned long flags = 0; \
		spin_lock_irqsave(&name##_locks[ring], flags); \
		return flags; \
	} \
	void swcore_##name##_unlock(int ring, unsigned long flags) { \
		spin_unlock_irqrestore(&name##_locks[ring], flags);	\
	}
		
#endif //DEBUG_LOCKS	
	
#define DECLARE_SWCORE_LOCK_ARRAY(name, size) \
	DEFINE_SWCORE_LOCKS_ARRAY_VAR(name,size) \
	DEFINE_SWCORE_LOCKS_ARRAY_FUNC(name, size)

DECLARE_SWCORE_LOCK_ARRAY(tx,RTL865X_SWNIC_TXRING_HW_PKTDESC);
DECLARE_SWCORE_LOCK_ARRAY(rx,RTL865X_SWNIC_RXRING_HW_PKTDESC);
DECLARE_SWCORE_LOCK_ARRAY(buf,2); 	// 0 - protects eth_skbbuf_list
									// 1 - protects rx_skb_queue

#ifdef DEBUG_LOCKS									
static int print_lock_counter(struct seq_file* s, struct swcore_locks_counter_s *cntrs) {	
	return seq_printf(s, "%u/%u %u %u %llu\n", cntrs->cntr_lock, cntrs->cntr_unlock, cntrs->cntr_contention, cntrs->cntr_wait_high, cntrs->cntr_wait_total);
}

static int locks_read(struct seq_file* s, void* v)
{	
	int cpu;
	
	struct swcore_locks_counter_s *cntrs;	
	
	seq_printf(s, "\nTxLock: \n");
	for_each_possible_cpu(cpu) {		
		cntrs = &per_cpu(tx_lock_counters, cpu);
		seq_printf(s, "cpu%d ",cpu);
		print_lock_counter(s, cntrs);
	}
	seq_printf(s, "\n");

	//seq_printf(s, "Rx:");
	//for (i=0;i<RTL865X_SWNIC_RXRING_HW_PKTDESC;i++)
	//	seq_printf(s, "%u ", cntr_rx_locks[i]);
	
	seq_printf(s, "\nRxLock: \n");
	for_each_possible_cpu(cpu) {		
		cntrs = &per_cpu(rx_lock_counters, cpu);
		seq_printf(s, "cpu%d ",cpu);
		print_lock_counter(s, cntrs);
	}
	seq_printf(s, "\nBufLock: \n");
	for_each_possible_cpu(cpu) {		
		cntrs = &per_cpu(buf_lock_counters, cpu);
		seq_printf(s, "cpu%d ",cpu);
		print_lock_counter(s, cntrs);
	}
	seq_printf(s, "\n");
	return 0;
}

static int locks_single_open(struct inode *inode, struct file *file)
{
	return(single_open(file, locks_read, NULL));
}

static ssize_t locks_single_write(struct file *filp, const char __user *buff,size_t len, loff_t *data)
{
	return len;
}

static struct file_operations locks_proc_fops = {
	.open           = locks_single_open,
	.write          = locks_single_write,
	.read           = seq_read,
	.llseek         = seq_lseek,
	.release        = single_release,
};

#endif

void swcore_init_locks(void) {
	int i;
	for (i=0; i<RTL865X_SWNIC_TXRING_HW_PKTDESC;i++) {		
		spin_lock_init(&tx_locks[i]);
		#ifdef DEBUG_LOCKS
		tx_locks_owners[i] = CPU_NONE;
		#endif 
	}
	
	for (i=0; i<RTL865X_SWNIC_RXRING_HW_PKTDESC;i++) {		
		spin_lock_init(&rx_locks[i]);
		#ifdef DEBUG_LOCKS
		rx_locks_owners[i] = CPU_NONE;
		#endif
	}
	for (i=0; i<2;i++) {
		spin_lock_init(&buf_locks[i]);
	}

	#ifdef DEBUG_LOCKS
	do {
		extern struct proc_dir_entry *rtl865x_proc_dir;
		proc_create_data("locks",0,rtl865x_proc_dir,&locks_proc_fops,NULL);
	} while (0);
	#endif
}

//__DRAM_FWD atomic_t lock_tx_tail = ATOMIC_INIT(0);
#ifdef RX_NAPI
__DRAM_FWD static volatile unsigned int mitigation_factor=0;
//__DRAM_FWD static volatile unsigned int fast_reclaim=0;
#endif

static void skb_debug(const char* data)
{
#define NUM2PRINT 100
	int i;
	for (i=0; i<NUM2PRINT; i++) 
	{
		printk("%02X  ",data[i]&0xFF);
		if(i%16==15)
			printk("\n");
		else if(i%8==7)
			printk("  ");		
	}
	printk("\n");
}

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

static void skb_debug_len(const char* data,uint32 len)
{
	int i;
	for (i=0; i<len; i++) 
	{
		printk("%02X  ",data[i]&0xFF);
		if(i%16==15)
			printk("\n");
		else if(i%8==7)
			printk("  ");		
	}
	printk("\n");
}


/*	WE MUST do simple parse packet for  pPkthdr->ph_type/ph_vlanTagged/ph_LLCTagged/ph_pppeTagged/l3v4v6HdrFlag */
extern int32 rtk_pppoe_getIdx(int32 sessionId);
void simplePacketParser(void* pData,uint32 packetLen,rtl_nicTx_info *nicTx,struct rtl_pktHdr * pPkthdr)
{
	
	uint32 offset=12;
	uint32 protocol=0;

	if(  *((uint16*)(((uint8*)pData)+offset)) ==htons(0x8100)  ){
		pPkthdr->ph_vlanTagged=1;
		offset+=4;
	}

	
	if(  *((uint16*)(((uint8*)pData)+offset)) ==htons(0x8863)  || *((uint16*)(((uint8*)pData)+offset)) ==htons(0x8864)){
		int32 pppoeIdx=-1;
		pPkthdr->ph_pppeTagged=1;
		pppoeIdx =rtk_pppoe_getIdx( *((uint16*)(((uint8*)pData)+offset+4)));
		if(pppoeIdx >=0){
			pPkthdr->ph_pppoeIdx=pppoeIdx;
		}else{
			printk("error can't get pppoeid(%d) idx %s :%d\n",*((uint16*)(((uint8*)pData)+offset+4)),__func__,__LINE__);
		}
		offset+=8;
	}	

	
	if(  *((uint16*)(((uint8*)pData)+offset)) ==htons(0x0800)  || *((uint16*)(((uint8*)pData)+offset)) ==htons(0x0021)){
		protocol=((uint8*)pData)[offset+11];
		switch(protocol){
			case 0x29: 	//v6rd packet 
				#if defined(CONFIG_RTL_8685S_HWNAT)
				if(IS_AFTER_RL6405)
				{
					pPkthdr->l3v4v6HdrFlag=v4FIRST;
					pPkthdr->l3v4v6HdrFlag|=v4HDR_FLAG;
				}
				#endif
				break;		
			default:
				break;
		}

	}else if (*((uint16*)(((uint8*)pData)+offset)) ==htons(0x86dd)  || *((uint16*)(((uint8*)pData)+offset)) ==htons(0x0057)){
		protocol=((uint8*)pData)[offset+8];

		switch(protocol){
			case 4: 	//IP in IP ds-lite packet
				#if defined(CONFIG_RTL_8685S_HWNAT)
				if(IS_AFTER_RL6405)
				{
					pPkthdr->l3v4v6HdrFlag=v4HDR_FLAG;
					pPkthdr->l3v4v6HdrFlag|=v6HDR_FLAG;
				}
				break;
				#endif
			case 6: 	//upper level, TCP
			case 17:	//upper level, UDP
			case 41:	//IPv6 Header, for Tunnel
			case 43:	//Routing Header
			case 60:	//Destionation Options Header
			case 59:	//No Next Header
				break;			
			default:
				break;
		}

	}

	pPkthdr->ph_type=PKTHDR_ETHERNET;

	if(protocol==0x6){//TCP
		pPkthdr->ph_type=PKTHDR_TCP;
	}else if(protocol==0x11){//UDP
		pPkthdr->ph_type=PKTHDR_UDP;		
	}else if(protocol==0x1){//ICMP
		pPkthdr->ph_type=PKTHDR_ICMP;
	}else if(protocol==0x2){ //IGMP
		pPkthdr->ph_type=PKTHDR_IGMP;
	}


}


void romeDriver_refillTxDesc(void* pData,uint32 packetLen,rtl_nicTx_info *nicTx,struct rtl_pktHdr * pPkthdr)
{

	struct tx_info* ptxInfo;
	char* ptr_pPkthdr_debug;
	uint32 L234Change=0;
	int32 i;
	int32 debug_p=0;
	//init para
	ptxInfo=nicTx->ptxInfo;

	//FOR TX
	if(nicTx->ptxInfo !=NULL && nicTx->fromRomeDriver ==1){

#if 0  //for debug
		if(*((uint16*)(((uint8*)pData)+12+8))  == 0xc021)
		{
			debug_p=1;
		}
#endif

		//need reset to a default value 0
		pPkthdr->ph_pppeTagged=0;
		pPkthdr->ph_vlanTagged=0;
		pPkthdr->ph_LLCTagged=0;
		pPkthdr->ph_flags &=~(CSUM_IP);
		pPkthdr->ph_flags &=~(CSUM_TCPUDP);
		pPkthdr->ph_flags &=~(PKTHDR_PPPOE_AUTOADD);
		pPkthdr->ph_txCVlanTagAutoAdd=0;
#if defined(CONFIG_RTL_8685S_HWNAT)
		pPkthdr->l3v4v6HdrFlag=0;
#endif
		
		if(ptxInfo->opts1.bit.ipcs  || ptxInfo->opts1.bit.l4cs || ptxInfo->opts2.bit.tx_vlan_action ||  ptxInfo->opts2.bit.tx_pppoe_action){
			L234Change=TRUE;
		}
		

		if(L234Change){

			/*parsing packet first*/
			simplePacketParser(pData,packetLen,nicTx,pPkthdr);
			
			switch(ptxInfo->opts2.bit.tx_vlan_action){

				case 0x2: //remove tag	-> untag
					pPkthdr->ph_txCVlanTagAutoAdd=0;
//					printk("remove vlan tag	-> untag\n");
				break;
				case 0x3: //remarking (add/modify)
//					printk("ori pPkthdr->ph_vlanId =%d	vlan=%d \n",pPkthdr->ph_vlanId,((ptxInfo->opts2.bit.vidh << 8) | (ptxInfo->opts2.bit.vidl)));
					pPkthdr->ph_vlanId			=((ptxInfo->opts2.bit.vidh << 8) | (ptxInfo->opts2.bit.vidl));
					pPkthdr->ph_txPriority		=((ptxInfo->opts2.bit.prio)) ; //for 802.1p
					pPkthdr->_flags2._tx._tx_qid =((ptxInfo->opts2.bit.prio)) ; //for 802.1p
					pPkthdr->ph_txCVlanTagAutoAdd =  ptxInfo->opts3.bit.tx_portmask &0x3f ; //portmask p0-p5 
				break;
				case 0x0: //no-action
//					printk("valn no-action\n");
					if(pPkthdr->ph_vlanTagged){ //nic always do action add/remove/modify, so need modify to ori vlan 
						pPkthdr->ph_vlanId=  (((((uint8*)pData)[14])&0xf)<<8)| (((uint8*)pData)[15]);
						pPkthdr->ph_txPriority =  (((uint8*)pData)[14]) >>5 ;
					}
				break;
				default:
					printk("default ptxInfo->opts2.bit.tx_vlan_action=%d \n ",ptxInfo->opts2.bit.tx_vlan_action);
				break;
			}


			switch(ptxInfo->opts2.bit.tx_pppoe_action){
				case 0x2: //remove pppoe tag	-> untag			
					printk("remove pppoe tag	-> untag\n");
					pPkthdr->ph_flags &=~(PKTHDR_PPPOE_AUTOADD);
				break;
				case 0x3: //remarking
					printk("pppoe remarking to pppoeIdx=%d\n",ptxInfo->opts2.bit.tx_pppoe_idx);
					pPkthdr->ph_flags |=(PKTHDR_PPPOE_AUTOADD);
					pPkthdr->ph_pppoeIdx=ptxInfo->opts2.bit.tx_pppoe_idx;
				break;
				case 0x0: //no-action
//					printk("pppoe no-action  pppoeIdx=%d \n",pPkthdr->ph_pppoeIdx);
					if(pPkthdr->ph_pppeTagged){ //should keep pppoe
						pPkthdr->ph_flags |=(PKTHDR_PPPOE_AUTOADD);
					}
				break;
				default:
					printk("default ptxInfo->opts2.bit.tx_vlan_action=%d \n ",ptxInfo->opts2.bit.tx_vlan_action);
				break;

			}

			if(ptxInfo->opts1.bit.ipcs)
				pPkthdr->ph_flags|=CSUM_IP;
			if(ptxInfo->opts1.bit.l4cs)
				pPkthdr->ph_flags|= CSUM_TCPUDP;
			
		}
		
		pPkthdr->ph_portlist		=ptxInfo->opts3.bit.tx_portmask;

//============================= unsure setting ======================================
//			pPkthdr->ph_len 			=ptxInfo->opts1.bit.data_length ;		//mask it we using skb len here 
//			pPkthdr->ph_queueId 		//Bit[14:12]: the CPU Queue priority for direct Tx	 
//			pPkthdr->ph_srcExtPortNum	=ptxInfo->opts3.bit.extspa; 	//unsure
/*
#define PKTHDR_ETHERNET      0
#define PKTHDR_PPTP          1
#define PKTHDR_IP            2
#define PKTHDR_ICMP          3
#define PKTHDR_IGMP          4
#define PKTHDR_TCP           5
#define PKTHDR_UDP           6
*/
//		printk("pPkthdr->ph_type =%d ",pPkthdr->ph_type);

//			pPkthdr->ph_LLCTagged
//			pPkthdr->ph_pppeTagged		=ptxInfo->opts2.bit.tx_pppoe_action ;	//unsure
//			pPkthdr->ph_pppoeIdx		=ptxInfo->opts2.bit.tx_pppoe_idx ; //FIXME: pppoe same as vlan action
//			pPkthdr->ph_linkID			//for wifi
//			pPkthdr->ph_flags			//PPPoETAG/L3CS/l4CS /HW LOOKUP
		/*
		865x
		Bit[7]: Reserved
				Bit[6]: CPU port ( no work )
				Bit[5-0]: Port5-Port0 
		*/
	//	pPkthdr->ph_tx_qid

//============================= unsure setting ======================================

		if((DumpSwNicTxRx_debug && DumpSwNicTxRx_debug_LIMIT>0)|| debug_p && ){  //debug use	
			ptr_pPkthdr_debug = (char*)pPkthdr;

			printk("opts1.bit.ipcs=%d  opts1.bit.l4cs=%d  opts2.bit.tx_vlan_action=%d opts2.bit.tx_pppoe_action=%d \n",
				ptxInfo->opts1.bit.ipcs  , ptxInfo->opts1.bit.l4cs , ptxInfo->opts2.bit.tx_vlan_action ,ptxInfo->opts2.bit.tx_pppoe_action);
			printk("TxPortmask:0x%x packetlen=%d %s %s\n",pPkthdr->ph_portlist,pPkthdr->ph_len,(pPkthdr->ph_flags&CSUM_IP)>0?"L3ChkOffload":"",(pPkthdr->ph_flags&CSUM_TCPUDP)>0?"L4ChkOffload":"" );
			printk("ph_vlanTagged=%d ph_txCVlanTagAutoAdd=%x  ph_vlanId=%d  ph_txPriority=%d \n",pPkthdr->ph_vlanTagged,pPkthdr->ph_txCVlanTagAutoAdd,pPkthdr->ph_vlanId,pPkthdr->ph_txPriority);
			printk("ph_pppeTagged=%d %s  ph_pppoeIdx=%d \n",pPkthdr->ph_pppeTagged,(pPkthdr->ph_flags&PKTHDR_PPPOE_AUTOADD)>0?"PPPoEAutoAdd":"DelPPPoE",pPkthdr->ph_pppoeIdx);
#if defined(CONFIG_RTL_8685S_HWNAT)
			printk("%s  %s  %s \n",(pPkthdr->l3v4v6HdrFlag &v4FIRST)>0?"v4FIRST":"",(pPkthdr->l3v4v6HdrFlag &v4HDR_FLAG)>0?"v4HDR_FLAG":"",(pPkthdr->l3v4v6HdrFlag &v6HDR_FLAG)>0?"v6HDR_FLAG":"");
#endif
			printk("(%s)--------pPkthdr-----------------\n",__func__);
			for(i=0;i<20;i++)
				printk("%02X  ",ptr_pPkthdr_debug[i]&0xFF);
			printk("\n(%s)--------packet content -----------------\n",__func__);
			skb_debug_len((char*)pData,pPkthdr->ph_len);	
			printk("\n------------------------------------------\n");
			DumpSwNicTxRx_debug_LIMIT--;
		}


		

	}

}

#endif


/*************************************************************************
*   FUNCTION                                                              
*       swNic_intHandler                                         
*                                                                         
*   DESCRIPTION                                                           
*       This function is the handler of NIC interrupts
*                                                                         
*   INPUTS                                                                
*       intPending      Pending interrupt sources.
*                                                                         
*   OUTPUTS                                                               
*       None
*************************************************************************/
void swNic_intHandler(uint32 intPending) {return;}
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__MIPS16 __IRAM_FWD 
#endif
inline int32 rtl8651_rxPktPreprocess(void *pkt, unsigned int *vid)
{
	struct rtl_pktHdr *m_pkthdr = (struct rtl_pktHdr *)pkt;
	uint32 srcPortNum;

	srcPortNum = m_pkthdr->ph_portlist&0x7;

	/* ph_vlanId:
	 * RX: Destination VLAN ID(after routing).
	 * TX: Source Destination VLAN ID select
	 */
	*vid = m_pkthdr->ph_vlanId;
	#if 0
	if (srcPortNum >= RTL8651_CPU_PORT)
	{        
		if (m_pkthdr->ph_extPortList == 0)
		{
			/* No any destination ( extension port or CPU) : ASIC's BUG */
			return FAILED;
		}else if ((m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_CPU) == 0)// to extension port
		{
			/*
				if dest Ext port 0x1 => to dst ext port 1 => from src port 1+5=6
				if dest Ext port 0x2 => to dst ext port 2 => from src port 2+5=7
				if dest Ext port 0x4 => to dst ext port 3 => from src port 3+5=8
			*/
			srcPortNum = extPortMaskToPortNum[m_pkthdr->ph_extPortList]+RTL8651_PORT_NUMBER-1;
			m_pkthdr->ph_portlist = srcPortNum;//now ph_portlist assign tx destinatino port mask
#if	defined(CONFIG_RTL_HARDWARE_NAT)&&(defined(CONFIG_RTL8192SE)||defined(CONFIG_RTL8192CD))
			*vid = PKTHDR_EXTPORT_MAGIC;
#endif
		}else//to cpu port
		{
			/* has CPU bit, pkt is original pkt from port 6~8 */
			srcPortNum = m_pkthdr->ph_srcExtPortNum + RTL8651_PORT_NUMBER - 1;
			m_pkthdr->ph_portlist = srcPortNum;
#if	defined(CONFIG_RTL_HARDWARE_NAT)&&(defined(CONFIG_RTL8192SE)||defined(CONFIG_RTL8192CD))
			*vid = PKTHDR_EXTPORT_MAGIC2;
#endif
		}        
	}
       else
	{
#ifndef CONFIG_RTL_8676HWNAT
		/* otherwise, pkt is rcvd from PHY */
		m_pkthdr->ph_srcExtPortNum = 0;
		if((m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_CPU) == 0)//to extension port or phy
		{	/* No CPU bit, only dest ext mbr port... */
			/*
				if dest Ext port 0x1 => to dst ext port 1 => from src port 1+5=6
				if dest Ext port 0x2 => to dst ext port 2 => from src port 2+5=7
				if dest Ext port 0x4 => to dst ext port 3 => from src port 3+5=8
			*/
			if(m_pkthdr->ph_extPortList&&0!=extPortMaskToPortNum[m_pkthdr->ph_extPortList])
			{
				/* redefine src port number */
				srcPortNum = extPortMaskToPortNum[m_pkthdr->ph_extPortList] + RTL8651_PORT_NUMBER - 1;
				m_pkthdr->ph_portlist = srcPortNum;
				#if	defined(CONFIG_RTL_HARDWARE_NAT)&&(defined(CONFIG_RTL8192SE)||defined(CONFIG_RTL8192CD))
				*vid = PKTHDR_EXTPORT_MAGIC;
				#endif
			}
		}
#endif
	}		
	#endif
	//Kevin, if the pkt is only toward to extPort, reset vid to a magic number
	if (srcPortNum < RTL8651_CPU_PORT)
	{
		/* No CPU bit, only dest ext mbr port... */
		if( !(m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_CPU) && (m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_P0))
			*vid = PKTHDR_EXTPORT_MAGIC;	

		if( !(m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_CPU) && (m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_P1))
			*vid = PKTHDR_EXTPORT_MAGIC2;	

		if( !(m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_CPU) && (m_pkthdr->ph_extPortList & PKTHDR_EXTPORTMASK_P2))
			*vid = PKTHDR_EXTPORT_MAGIC3;		

		return SUCCESS;
	}
	else 
		return FAILED;	
}

#ifdef DELAY_REFILL_ETH_RX_BUF
static inline int __return_to_rxing_check(int ringIdx)
{
	int ret;
	//unsigned long flags;
	//flags = swcore_buf_lock(1); //local_irq_save(flags);
	ret = ((rxPkthdrRingCnt[ringIdx]!=0) && (rxDescReadyForHwIndex[ringIdx] != currRxPkthdrDescIndex[ringIdx]))? 1:0;
	//swcore_buf_unlock(1,flags); //local_irq_restore(flags);
	return ret;	
}
static inline int __buffer_reuse(int ringIdx) 
{
	int index1,index2,gap;
	//unsigned long flags;
	//flags = swcore_buf_lock(1); //local_irq_save(flags);
	index1 = rxDescReadyForHwIndex[ringIdx];
	index2 = currRxPkthdrDescIndex[ringIdx]+1;
	gap = (index2 > index1) ? (index2 - index1) : (index2 + rxPkthdrRingCnt[ringIdx] - index1);
	
	if ((rxPkthdrRingCnt[ringIdx] - gap) < (rxPkthdrRefillThreshold[ringIdx]))
	{
		//swcore_buf_unlock(1,flags); //local_irq_restore(flags);
		return 1;
	}
	else
	{
		//swcore_buf_unlock(1,flags); //local_irq_restore(flags);
		return 0;
	}
}

static inline void set_RxPkthdrRing_OwnBit(uint32 rxRingIdx)
{
	unsigned long flags;	
	flags = swcore_buf_lock(1);
	rxPkthdrRing[rxRingIdx][rxDescReadyForHwIndex[rxRingIdx]] |= DESC_SWCORE_OWNED;
	
	if ( ++rxDescReadyForHwIndex[rxRingIdx] == rxPkthdrRingCnt[rxRingIdx] )
		rxDescReadyForHwIndex[rxRingIdx] = 0;
	swcore_buf_unlock(1,flags);
}

#ifndef CONFIG_FAST_FORWARDING
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__IRAM_FWD 
#endif
static void release_pkthdr(struct sk_buff  *skb, int idx)
{
	struct rtl_pktHdr *pReadyForHw;
	struct rtl_mBuf *mbuf;
	uint32 mbufIndex;
	unsigned long flags;

	_dma_cache_wback_inv((unsigned long)skb->head, MBUF_LEN);
	local_irq_save(flags);
	pReadyForHw = (struct rtl_pktHdr *)(rxPkthdrRing[idx][rxDescReadyForHwIndex[idx]] & 
						~(DESC_OWNED_BIT | DESC_WRAP));
	mbufIndex = ((uint32)(pReadyForHw->ph_mbuf) - (rxMbufRing[0] & ~(DESC_OWNED_BIT | DESC_WRAP))) /
					(sizeof(struct rtl_mBuf));
	
	pReadyForHw->ph_mbuf->m_data = skb->data;
	pReadyForHw->ph_mbuf->m_extbuf = skb->data;
	pReadyForHw->ph_mbuf->skb = skb;

	//patch for huge packets which need more than one mbuf in a pktHdr
	//the skb in first mbuf will drop in protocol stack, the other mbufs will set ownerbit here for reuse
	mbuf = pReadyForHw->ph_mbuf->m_next;
	rxMbufRing[mbufIndex] |= DESC_SWCORE_OWNED;
	while(mbuf != NULL){
		mbufIndex = ((uint32)mbuf - (rxMbufRing[0] & ~(DESC_OWNED_BIT | DESC_WRAP))) /
					(sizeof(struct rtl_mBuf));
		mbuf = mbuf->m_next;
		rxMbufRing[mbufIndex] |= DESC_SWCORE_OWNED;
	}
	//end of patch
	
	set_RxPkthdrRing_OwnBit(idx);
	local_irq_restore(flags);
}
#else
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__IRAM_FWD 
#endif
static void release_pkthdr(unsigned char *data, int idx)
{
	struct rtl_pktHdr *pReadyForHw;
	struct rtl_mBuf *mbuf;
	uint32 mbufIndex;
	unsigned long flags;

	//_dma_cache_wback_inv((unsigned long)skb->head, skb->truesize);
	_dma_cache_inv((unsigned long)data, MBUF_LEN);
	local_irq_save(flags);
	pReadyForHw = (struct rtl_pktHdr *)(rxPkthdrRing[idx][rxDescReadyForHwIndex[idx]] & 
						~(DESC_OWNED_BIT | DESC_WRAP));
	mbufIndex = ((uint32)(pReadyForHw->ph_mbuf) - (rxMbufRing[0] & ~(DESC_OWNED_BIT | DESC_WRAP))) /
					(sizeof(struct rtl_mBuf));
	
	pReadyForHw->ph_mbuf->m_data = data;
	pReadyForHw->ph_mbuf->m_extbuf = data;

	//patch for huge packets which need more than one mbuf in a pktHdr
	//the skb in first mbuf will drop in protocol stack, the other mbufs will set ownerbit here for reuse
	mbuf = pReadyForHw->ph_mbuf->m_next;
	rxMbufRing[mbufIndex] |= DESC_SWCORE_OWNED;
	while(mbuf != NULL){
		mbufIndex = ((uint32)mbuf - (rxMbufRing[0] & ~(DESC_OWNED_BIT | DESC_WRAP))) /
					(sizeof(struct rtl_mBuf));
		mbuf = mbuf->m_next;
		rxMbufRing[mbufIndex] |= DESC_SWCORE_OWNED;
	}
	//end of patch
	
	set_RxPkthdrRing_OwnBit(idx);
	local_irq_restore(flags);
}
#endif//end of CONFIG_FAST_FORWARDING

#if defined(CONFIG_RTL_ETH_PRIV_SKB)
/*
	return value: 1 ==> success, returned to rx pkt hdr desc
	return value: 0 ==> failed, no return ==> release to priv skb buf pool
 */	
extern struct sk_buff *dev_alloc_8190_skb(unsigned char *data, int size);
int return_to_rx_pkthdr_ring(unsigned char *head) 
{
	struct sk_buff *skb;
	int ret, i;
	unsigned long flags;

	ret=FAILED;

	//local_irq_save(flags);
	for(i = RTL865X_SWNIC_RXRING_MAX_PKTDESC -1; i >= 0; i--)
	{
		flags = swcore_rx_lock(i);
		if (__return_to_rxing_check(i)) {

			skb = dev_alloc_8190_skb(head, CROSS_LAN_MBUF_LEN);
			if (skb == NULL) {
				swcore_rx_unlock(i, flags);
				goto _ret1;
			}

			skb_reserve(skb, RX_OFFSET);
			release_pkthdr(skb, i);
			ret = SUCCESS;
			swcore_rx_unlock(i, flags);
			break;
		}
		swcore_rx_unlock(i, flags);
	}

_ret1:
	//local_irq_restore(flags);
	return ret;
}
#endif//end of CONFIG_RTL_ETH_PRIV_SKB

#ifdef CONFIG_RTL_ETH_PRIV_SKB_ADV
#ifdef CONFIG_FAST_FORWARDING
__IRAM_GEN
int return_to_nic_rx_ring(unsigned char *data) 
{
	int ret=FAILED, i;
	unsigned long flags;

	for(i = RTL865X_SWNIC_RXRING_MAX_PKTDESC -1; i >= 0; i--)
	{
		flags = swcore_rx_lock(i);
		if (__return_to_rxing_check(i))
		{
			data += NET_SKB_PAD+RX_OFFSET;
			release_pkthdr(data, i);
			ret = SUCCESS;
			swcore_rx_unlock(i, flags);
			break;
		}
		swcore_rx_unlock(i, flags);
	}

	return ret;
}
#else
__IRAM_GEN
int return_to_nic_rx_ring(struct sk_buff *skb) 
{
	int ret=FAILED, i;
	unsigned long flags;

	for(i = RTL865X_SWNIC_RXRING_MAX_PKTDESC -1; i >= 0; i--)
	{
		flags = swcore_rx_lock(i);
		if (__return_to_rxing_check(i))
		{
			unsigned char *data;
			data = get_buf_from_poll();
			if (NULL == data) {
				swcore_rx_unlock(i, flags);
				return FAILED;
			}
			
			init_skbhdr(skb, data, CROSS_LAN_MBUF_LEN, rtl865x_free_eth_priv_buf);
			release_pkthdr(skb, i);
			ret = SUCCESS;
			swcore_rx_unlock(i, flags);
			break;
		}
		swcore_rx_unlock(i, flags);
	}

	return ret;
}
#endif//end of CONFIG_FAST_FORWARDING
#endif//end of CONFIG_RTL_ETH_PRIV_SKB_ADV

#else //DELAY_REFILL_ETH_RX_BUF
static void release_pkthdr(struct sk_buff  *skb, int idx)
{}
#endif //DELAY_REFILL_ETH_RX_BUF

/*	It's the caller's responsibility to make sure "rxRingIdx" and 
*	"currRxPktDescIdx" NOT NULL, since the callee never check
*	sanity of the parameters, in order to speed up.
*/
#if RX_ONLY_RING0
static inline int32 swNic_getRxringIdx(void)
{
	if ((rxPkthdrRing[0][currRxPkthdrDescIndex[0]] & DESC_OWNED_BIT) == DESC_RISC_OWNED)
	{
		return SUCCESS;
	}
	return FAILED;
}
#else
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__MIPS16 __IRAM_FWD
#endif
static inline int32 swNic_getRxringIdx(uint32 *rxRingIdx, uint32 *currRxPktDescIdx,uint32 policy, unsigned long *rxLockFlags)
{
	int32	i;
	int32	priority;
	unsigned long flags;


	priority = policy;	
	for(i = RTL865X_SWNIC_RXRING_MAX_PKTDESC -1; i >= priority; i--)
	{
		if(rxPkthdrRingCnt[i] == 0)
			continue;
		
		flags = swcore_rx_lock(i); 
		*rxLockFlags = flags;
		
		if((rxPkthdrRing[i][currRxPkthdrDescIndex[i]] & DESC_OWNED_BIT) == DESC_RISC_OWNED)
		{
			*rxRingIdx = i;
			*currRxPktDescIdx = currRxPkthdrDescIndex[i];			
			return SUCCESS;
		}
		swcore_rx_unlock(i, flags);
	}

	return FAILED;
}
#endif
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__IRAM_FWD
#endif
static int __swNic_increaseRxIdx(int rxRingIdx)
{
	//unsigned long	flags;
	//int32		nextIdx;

	//local_irq_save(flags);
        if ( ++currRxPkthdrDescIndex[rxRingIdx] == rxPkthdrRingCnt[rxRingIdx] )
            currRxPkthdrDescIndex[rxRingIdx] = 0;

	#if 0
	if (currRxPkthdrDescIndex[rxRingIdx]+1 == rxPkthdrRingCnt[rxRingIdx])
		nextIdx = 0;
	else
		nextIdx = currRxPkthdrDescIndex[rxRingIdx]+1;
	#endif
	//local_irq_restore(flags);

	return SUCCESS;
}

#if defined(CONFIG_RTL_ETH_PRIV_SKB_DEBUG)
int get_nic_txRing_buf(void)
{
	int txCnt = 0;
	int i,j;
	struct rtl_pktHdr *pPkthdr;
	for(i = RTL865X_SWNIC_TXRING_MAX_PKTDESC -1; i >= 0; i--)
	{
		if(txPkthdrRingCnt[i] == 0)
			continue;

		for(j = 0; j <txPkthdrRingCnt[i]; j++)
		{
			
			pPkthdr = (struct rtl_pktHdr *) ((int32) txPkthdrRing[i][j]& ~(DESC_OWNED_BIT | DESC_WRAP));

			if(pPkthdr->ph_mbuf->skb)
			{
				if(is_rtl865x_eth_priv_buf(((struct sk_buff *)pPkthdr->ph_mbuf->skb)->head))
					txCnt++;
			}
		}		
	}
	
	return txCnt;
}

int get_nic_rxRing_buf(void)
{
	int rxCnt = 0;
	int i,j;
	struct rtl_pktHdr *pPkthdr;
	for(i = RTL865X_SWNIC_RXRING_MAX_PKTDESC -1; i >= 0; i--)
	{
		if(rxPkthdrRingCnt[i] == 0)
			continue;

		for(j = 0; j < rxPkthdrRingCnt[i]; j++)
		{
			{
				pPkthdr = (struct rtl_pktHdr *) (rxPkthdrRing[i][j] & ~(DESC_OWNED_BIT | DESC_WRAP));
				if(pPkthdr->ph_mbuf->skb)
				{
					if(is_rtl865x_eth_priv_buf(((struct sk_buff *)pPkthdr->ph_mbuf->skb)->head))
						rxCnt++;
				}
			}
		}
	}

	return rxCnt;
}
#endif

int32 swNic_flushRxRingByPriority(int priority)
{
	int32	i;
	struct rtl_pktHdr * pPkthdr;
	void *skb;
	unsigned long flags;
	unsigned long rxlock_flags;

	#if defined(CONFIG_RTL865X_WTDOG)
	REG32(WDTCNR) |=  WDTCLR; /* reset watchdog timer */
	#endif
	local_irq_save(flags);
	for(i = priority -1; i >= 0; i--)
	{
		if(rxPkthdrRingCnt[i] == 0)
			continue;
		
		rxlock_flags = swcore_rx_lock(i); 
		while((rxPkthdrRing[i][currRxPkthdrDescIndex[i]] & DESC_OWNED_BIT) == DESC_RISC_OWNED)
		{
			pPkthdr = (struct rtl_pktHdr *) (rxPkthdrRing[i][currRxPkthdrDescIndex[i]] & ~(DESC_OWNED_BIT | DESC_WRAP));
			skb = pPkthdr->ph_mbuf->skb;
			release_pkthdr(skb, i);
			__swNic_increaseRxIdx(i);
		}
		swcore_rx_unlock(i, rxlock_flags);
	}
	local_irq_restore(flags);
	REG32(CPUIISR) = (MBUF_DESC_RUNOUT_IP_ALL|PKTHDR_DESC_RUNOUT_IP_ALL);
	return SUCCESS;
}

#if defined(CONFIG_RTL_HWNAT_TESTMODEL)
extern int TEST_MODEL_DONOT_DIRECT_TX;
#endif
/*************************************************************************
*   FUNCTION                                                              
*       swNic_receive                                         
*                                                                         
*   DESCRIPTION                                                           
*       This function reads one packet from rx descriptors, and return the 
*       previous read one to the switch core. This mechanism is based on 
*       the assumption that packets are read only when the handling 
*       previous read one is done.
*                                                                         
*   INPUTS                                                                
*       None
*                                                                         
*   OUTPUTS                                                               
*       None
*************************************************************************/
//__MIPS16
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__IRAM_FWD
#endif
int32 swNic_receive(rtl_nicRx_info *info)
{
	/*QL 20110615 start: MTU is 1522, so one mbuf is sufficient to receive a packet. 
	 * don't care the case that consecutive mbuf occupied by one packet.
	 */
	struct rtl_pktHdr * pPkthdr;
	#ifndef DELAY_REFILL_ETH_RX_BUF
	struct rtl_mBuf *mbuf;
	uint32 mbufIndex;
	#endif
	unsigned char *buf;
	void *skb=NULL;
	uint32 rxRingIdx=0;
	uint32 currRxPktDescIdx=0;
	int32 retval;
	//unsigned long flags;
	unsigned long rxlock_flags = 0;
	#if defined(CONFIG_RTL_HARDWARE_NAT)
	uint32	vid;
	#endif
	
	int i;
	char* ptr_pPkthdr_debug;
	if(DumpSwNicTxRx_debug && DumpSwNicTxRx_debug_LIMIT>0)
		printk("Enter %s\n",__func__);
get_next:	
	 /* Check OWN bit of descriptors */
	 //local_irq_save(flags);
	 #if RX_ONLY_RING0
	 retval = swNic_getRxringIdx();
	 currRxPktDescIdx=currRxPkthdrDescIndex[0];
	 #else
	 retval = swNic_getRxringIdx(&rxRingIdx,&currRxPktDescIdx,info->priority, &rxlock_flags);
	 #endif
	 //local_irq_restore(flags);

	 /* Check OWN bit of descriptors */
	if (retval == SUCCESS ) 
	{
	#if !RX_ONLY_RING0
		info->priority = rxRingIdx;
	#endif
		/* Fetch pkthdr */
		pPkthdr = (struct rtl_pktHdr *) (rxPkthdrRing[rxRingIdx][currRxPktDescIdx] & ~(DESC_OWNED_BIT | DESC_WRAP));    

		/* Increment counter */
		//rxPktCounter++;

		/*	checksum error drop it	*/
		if (unlikely((pPkthdr->ph_flags & (CSUM_TCPUDP_OK | CSUM_IP_OK)) != (CSUM_TCPUDP_OK | CSUM_IP_OK)))
		{
			printk("%s %d checksum error.\n", __func__, __LINE__);
			buf = NULL;
			#ifdef DELAY_REFILL_ETH_RX_BUF
			goto release1;
			#else
			goto release;
			#endif
		}

		if(unlikely(DumpTrapCPUpkt_debug) && DumpTrapCPUpkt_debug_LIMIT>0)
		{
			printk("rxring_idx=%d     priority:%d   from port%d  vid=%d  reason:0x%X (",rxRingIdx,pPkthdr->ph_rxPriority,pPkthdr->ph_portlist&0x7,pPkthdr->ph_vlanId,pPkthdr->ph_reason);
			if(pPkthdr->ph_reason >> 9 == 0)
			{
				printk("gerneral purpose");
			}
			else if(pPkthdr->ph_reason >> 9 == 1)
			{
				printk("trap by ingresss acl no. %d",pPkthdr->ph_reason & 0x7f);
			}
			else if(pPkthdr->ph_reason >> 9 == 2)
			{
				printk("trap by egresss acl no. %d",pPkthdr->ph_reason & 0x7f);
			}
			else if(pPkthdr->ph_reason >> 9 == 3)
			{
				printk("protocal parsing failed");
			}
			else
			{
				printk("no reason ?? ");
			}
			printk(")\n");
			DumpTrapCPUpkt_debug_LIMIT--;
		}
		if(unlikely(DumpTrapCPUHs_debug) && DumpTrapCPUHs_debug_LIMIT>0)
		{
			dump_hstbl();
			DumpTrapCPUHs_debug_LIMIT--;
		}

#if defined(CONFIG_RTL_HARDWARE_NAT)
		if (rtl8651_rxPktPreprocess(pPkthdr, &vid) != SUCCESS)
		{
			buf = NULL;
		}
		else
		{
#ifdef CONFIG_FAST_FORWARDING
			buf = get_buf_from_poll();
#else
			buf = alloc_rx_buf(&skb, size_of_cluster);
#endif//end of CONFIG_FAST_FORWARDING
		}
		info->vid = vid;
#else
		/*
		 * vid is assigned in rtl8651_rxPktPreprocess() 
		 * do not update it when CONFIG_RTL_HARDWARE_NAT is defined
		 */
		info->vid=pPkthdr->ph_vlanId;
#ifdef CONFIG_FAST_FORWARDING
		buf = get_buf_from_poll();
#else
		buf = alloc_rx_buf(&skb, size_of_cluster);
#endif//end of CONFIG_FAST_FORWARDING
#endif//end of CONFIG_RTL_HARDWARE_NAT

		info->pid=pPkthdr->ph_portlist&0x7;
		
	#ifdef CONFIG_PTMWAN
		if(unlikely(DumpERBpacket_debug))
		{
			unsigned char* data = ((struct sk_buff*)pPkthdr->ph_mbuf->skb)->data;
			
			if( data[14]==0xAA && data[15]==0xAA && data[16]==0x03 &&
				data[17]==0x00 && data[18]==0x19 && data[19]==0xA7 &&
				data[20]==0x00 && data[21]==0x03)
			{
				printk("-------- ERB packet content -----------------\n");
				skb_debug(((struct sk_buff*)pPkthdr->ph_mbuf->skb)->data);	
				printk("\n------------------------------------------\n");
			}
		}
	#endif
		if(unlikely(DumpSwNicTxRx_debug) && DumpSwNicTxRx_debug_LIMIT>0)
		{
			ptr_pPkthdr_debug = (char*)pPkthdr;
			printk("(%s)info->vid : %d   info->pid :%d  \n",__func__,info->vid,info->pid);
			printk("(%s)--------pPkthdr-----------------\n",__func__);
			for(i=0;i<20;i++)
				printk("%02X  ",ptr_pPkthdr_debug[i]&0xFF);
			printk("\n(%s)--------packet content -----------------\n",__func__);
#ifdef CONFIG_FAST_FORWARDING
			skb_debug(pPkthdr->ph_mbuf->m_data);
#else
			skb_debug(((struct sk_buff*)pPkthdr->ph_mbuf->skb)->data);	
#endif
			printk("\n------------------------------------------\n");
			DumpSwNicTxRx_debug_LIMIT--;
		}
		
#if defined(CONFIG_RTL_HWNAT_TESTMODEL)
			memcpy(&pkt_data ,((struct sk_buff*)pPkthdr->ph_mbuf->skb)->data ,pPkthdr->ph_mbuf->m_len);
#endif

		if (buf)
		{
			#ifdef CONFIG_FAST_FORWARDING
			info->data = pPkthdr->ph_mbuf->m_data;
			#else
			info->input = pPkthdr->ph_mbuf->skb;
			#endif//end of CONFIG_FAST_FORWARDING
			//info->len = pPkthdr->ph_len - 4;
			//patch for huge packet which consume more than one mbuf
			info->len = pPkthdr->ph_mbuf->m_len - 4;

			#ifdef DELAY_REFILL_ETH_RX_BUF
			#ifdef CONFIG_FAST_FORWARDING
			release_pkthdr(buf, rxRingIdx);
			#else
			release_pkthdr(skb, rxRingIdx);
			#endif//end of CONFIG_FAST_FORWARDING
			#else
			pPkthdr->ph_mbuf->m_data = pPkthdr->ph_mbuf->m_extbuf = buf;
			pPkthdr->ph_mbuf->skb = skb;
			#endif
			#ifndef RX_NAPI
			REG32(CPUIISR) = (MBUF_DESC_RUNOUT_IP_ALL|PKTHDR_DESC_RUNOUT_IP_ALL);
			#endif
		}
#ifdef DELAY_REFILL_ETH_RX_BUF
		else if (!__buffer_reuse(rxRingIdx)) {
			#ifdef CONFIG_FAST_FORWARDING
			info->data = pPkthdr->ph_mbuf->m_data;
			#else
			info->input = pPkthdr->ph_mbuf->skb;
			#endif//end of CONFIG_FAST_FORWARDING
			//info->len = pPkthdr->ph_len - 4;
			//patch for huge packet which consume more than one mbuf
            info->len = pPkthdr->ph_mbuf->m_len - 4;
			#if defined(CONFIG_RTL_ETH_PRIV_SKB_DEBUG)
			pPkthdr->ph_mbuf->skb = NULL;
			#endif
			#ifdef CONFIG_FAST_FORWARDING
			buf = (unsigned char *)info->data;
			#else
			buf = (unsigned char *)info->input; // just only for "if (buf == NULL)" below
			#endif//end of CONFIG_FAST_FORWARDING
		}
		else {
			swcore_rx_unlock(rxRingIdx, rxlock_flags);
			return RTL_NICRX_REPEAT;
release1:
			skb = pPkthdr->ph_mbuf->skb;
			release_pkthdr(skb, rxRingIdx);
			skb = NULL;
		}		
#else
release:
		//patch for huge packets which need more than one mbuf in a pktHdr 
		//set all mbuf's ownerbit for reuse
		mbuf = pPkthdr->ph_mbuf;
		while(mbuf != NULL){
			mbufIndex = ((uint32)mbuf - (rxMbufRing[0] & ~(DESC_OWNED_BIT | DESC_WRAP))) /
					(sizeof(struct rtl_mBuf));
			mbuf = mbuf->m_next;
			rxMbufRing[mbufIndex] |= DESC_SWCORE_OWNED;
		}
		//end of patch
		
		rxPkthdrRing[rxRingIdx][currRxPkthdrDescIndex[rxRingIdx]] |= DESC_SWCORE_OWNED;
#endif

        /* Increment index */
		if (__swNic_increaseRxIdx(rxRingIdx)!=SUCCESS) {
			swcore_rx_unlock(rxRingIdx, rxlock_flags);
			return RTL_NICRX_REPEAT;
		}
		
		swcore_rx_unlock(rxRingIdx, rxlock_flags);

		if (buf == NULL)
			goto get_next;

#ifdef CONFIG_FAST_FORWARDING
		info->input = (void *)FF_Alloc_Skb((unsigned char *)info->data, info->len);
		skb = info->input;

		/* FIXME: device addr of nas0 is fixed to 00:12:34:56:78:90, it is not the real addr of br0, so it is incorrect to 
		 *     use nas0 addr for downstream briding decision.
		 */
		((struct sk_buff *)skb)->dev = (struct net_device *)rtl865x_get_dev_by_pid(info->pid);

		if (compare_ether_addr(info->data, ((struct sk_buff *)skb)->dev->dev_addr))
		{
			int dir=DIR_LAN;

			/*QL why I set pkt_type here? because I will use this field to check if it is a brdging packet in har_start_xmit function, 
			 * we only need invalidate and write back cache for ethernet header.
			 */
			((struct sk_buff *)skb)->pkt_type = PACKET_OTHERHOST;

			if (RTL_WANPORT_MASK & (1<<info->pid))
				dir = DIR_WAN;
			
			if (brgFastForwarding((struct sk_buff *)skb, dir))
				return RTL_NICRX_FF;
		}

		skb_reset_mac_header((struct sk_buff *)skb);
		((struct sk_buff *)skb)->protocol = ((unsigned short *)(((struct sk_buff *)skb)->data))[6];
		((struct sk_buff *)skb)->pkt_type = PACKET_HOST;
		skb_pull((struct sk_buff *)skb, ETH_HLEN);
		((struct sk_buff *)skb)->dst = NULL;
		
		if (NET_RX_SUCCESS == rteFastForwarding((struct sk_buff *)skb))
			return RTL_NICRX_FF;

		if (((struct sk_buff *)skb)->dst){
			dst_release(((struct sk_buff *)skb)->dst);
			((struct sk_buff *)skb)->dst = NULL;
		}

		skb_push((struct sk_buff *)skb, ETH_HLEN);
		
		init_skbhdr((struct sk_buff *)skb, ((struct sk_buff *)skb)->head, 0, rtl865x_free_eth_priv_buf);
#endif//end of CONFIG_FAST_FORWARDING


		/*romedriver modify transfer here Boyce 2014-07-10*/
#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER) &&  defined(CONFIG_XDSL_ROMEDRIVER)
		bzero(&(info->RxInfo),sizeof(struct rx_info));

/* opts1  */
//		info->RxInfo.opts1.bit.own = pPkthdr->;
//		info->RxInfo.opts1.bit.eor = pPkthdr->;
//		info->RxInfo.opts1.bit.fs = pPkthdr->;
//		info->RxInfo.opts1.bit.ls = pPkthdr->;
//		info->RxInfo.opts1.bit.crcerr =  //8685 no crcErr field
//		info->RxInfo.opts1.bit.ipv4csf = !(pPkthdr->ph_flags & CSUM_IP_OK );  //8685 when set indicates checksum correct ,and 6266 reverse
//		info->RxInfo.opts1.bit.l4csf = !(pPkthdr->ph_flags & CSUM_TCPUDP_OK); //8685 when set indicates checksum correct ,and 6266 reverse
//		info->RxInfo.opts1.bit.rcdf = pPkthdr->;
//		info->RxInfo.opts1.bit.ipfrag = pPkthdr->;
//		info->RxInfo.opts1.bit.pppoetag = pPkthdr->;
//		info->RxInfo.opts1.bit.rwt = pPkthdr->;
//		info->RxInfo.opts1.bit.pkttype =pPkthdr->ph_type;
//		info->RxInfo.opts1.bit.l3routing = pPkthdr->;
//		info->RxInfo.opts1.bit.origformat = pPkthdr->;
//		info->RxInfo.opts1.bit.pctrl = pPkthdr->;
//		info->RxInfo.opts1.bit.data_length = pPkthdr->ph_len;
/* addr  */
//		info->RxInfo.addr = 
/* opts2  */
//		info->RxInfo.opts2.bit.cputag = 
//		info->RxInfo.opts2.bit.ptp_in_cpu_tag_exist = 
//		info->RxInfo.opts2.bit.svlan_tag_exist = 
//		info->RxInfo.opts2.bit.rsvd_2 = 
//**xx	info->RxInfo.opts2.bit.pon_stream_id =						//fwdEngine use pon_stream_id,but 8685 no gpon 
//		info->RxInfo.opts2.bit.rsvd_1 = 
		info->RxInfo.opts2.bit.ctagva = pPkthdr->ph_vlanTagged;
		info->RxInfo.opts2.bit.cvlan_tag =	pPkthdr->ph_vlanId & (pPkthdr->ph_txPriority << 12);		/*	VIDH: The high 4 bits of a 12-bit VLAN ID.
																											VIDL: The low 8 bits of a 12-bit VLAN ID.
																											PRIO: 3-bit 8-level priority.
																											*/
/* opts3  */
		info->RxInfo.opts3.bit.src_port_num =((pPkthdr->ph_portlist)&0x7);	 //Bit[2-0]: source port number
		info->RxInfo.opts3.bit.dst_port_mask =		(((pPkthdr->ph_extPortList)&0x7)<<1) |	(((pPkthdr->ph_extPortList)>>3)&0x1);		
		info->RxInfo.opts3.bit.reason =  255;  /* set a useless reason to slow path*/
//		info->RxInfo.opts3.bit.internal_priority
//		info->RxInfo.opts3.bit.ext_port_ttl_1
//**xx	info->RxInfo.opts3.bit.rsvd
		
#endif



		return RTL_NICRX_OK;
	}
	else {
		if (skb)
		{
			free_rx_buf(skb);
		}

		return RTL_NICRX_NULL;
	}
}

/*************************************************************************
*   FUNCTION                                                              
*       swNic_send                                         
*                                                                         
*   DESCRIPTION                                                           
*       This function writes one packet to tx descriptors, and waits until 
*       the packet is successfully sent.
*                                                                         
*   INPUTS                                                                
*       None
*                                                                         
*   OUTPUTS                                                               
*       None
*************************************************************************/

#if 0
#ifdef CONFIG_RTL_8367B

/************************************
*	const variable defination
*************************************/
#define	RTL_BridgeWANVLANID		7 /* WAN vid (bridged, default no vlan tag)*/
#define	RTL_WANVLANID			8 /* WAN vid (routed,   default no vlan tag)*/
#define	RTL_LANVLANID			9 /* LAN vid  (default no vlan tag) */
#endif
#endif

int avail_txdscp_num(int idx)
{
	return ((txPktDoneDescIndex[idx] - currTxPkthdrDescIndex[idx] - 1) % txPkthdrRingCnt[idx]);
}

static int qos_mark2prio_enable = 1;
static int qos_mark2prio_count = 0;
static struct qos_mark2prio_s qos_mark2prio[4];

static inline int sw_qos_count_mark2prio(void) {
	int i, count = 0;
	for (i=0; i<ARRAY_SIZE(qos_mark2prio); i++) {
		if (qos_mark2prio[i].en)
			count++;
	}
	return count;
}

void sw_qos_mark2prio_set_enable(int en) {
	en = !!en;
	if (qos_mark2prio_enable == en)
		return;
	qos_mark2prio_enable = en;
	#if 0
	if (en) {
		REG32(MACCR1) |= (1<<3); /* CPU decides QID */		
		REG32(PSRP6_RW) &= ~(3 << 5); /* disable CPU Tx/Rx PAUSE */
	} else {
		REG32(MACCR1) &= ~(1<<3); 		
		REG32(PSRP6_RW) |= (3 << 5);
	}
	#endif
}

void sw_qos_mark2prio_get_enable(int *en) {
	*en = qos_mark2prio_enable;
}

int sw_qos_add_mark2prio(u32 mark, u32 mask, int prio) {
	int i;
	
	if ((prio > 5) || (prio < 0))
		return -1;
	for (i=0; i<ARRAY_SIZE(qos_mark2prio); i++) {
		if (!qos_mark2prio[i].en)
			break;
	}
	if (i >= ARRAY_SIZE(qos_mark2prio))
		return -1;
	qos_mark2prio[i].en   = 1;
	qos_mark2prio[i].mark = (mark & mask);
	qos_mark2prio[i].mask = mask;
	qos_mark2prio[i].prio = prio;
	qos_mark2prio_count = sw_qos_count_mark2prio();
	return 0;
}

int sw_qos_del_mark2prio(int index) {

	if ((index >= ARRAY_SIZE(qos_mark2prio)) || (index < 0))
		return -1;
	qos_mark2prio[index].en   = 0;
	qos_mark2prio_count = sw_qos_count_mark2prio();
	return 0;
}

int sw_qos_get_mark2prio(int index, struct qos_mark2prio_s *m) {
	if ((index >= ARRAY_SIZE(qos_mark2prio)) || (index < 0))
		return -1;	
	memcpy(m, &qos_mark2prio[index], sizeof(struct qos_mark2prio_s));
	return 0;
}

//int prio2mark_debug = 0;
static inline int mark_select_queue(struct sk_buff *skb, rtl_nicTx_info *txinfo) {
#if defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB)
	int i; 	
	struct qos_mark2prio_s *qos;
	if ((!qos_mark2prio_enable) || (0==qos_mark2prio_count))
		return 0;
	//if (unlikely(prio2mark_debug)) printk("%s(%d): skb=%p mark=%x\n",__func__,__LINE__,skb,skb->mark);
	for (i=0; i<ARRAY_SIZE(qos_mark2prio); i++) {
		qos = &qos_mark2prio[i];
		if (!qos->en) 
			continue;
		if ((skb->mark & qos->mask) != qos->mark)
			continue;		
		if (qos->prio)
			txinfo->txIdx = 1;
		//if (unlikely(prio2mark_debug)) printk("%s(%d): skb=%p qid=%d txIdx=%d\n",__func__,__LINE__,skb,qos->prio,txinfo->txIdx);
		return qos->prio;
	}
#endif 	
	return 0;
}

#ifdef CONFIG_RTL_HW_QOS_SUPPORT
//S/W QUEUE
#define RTL_IPQOS_SWQUEUE_HIGH		0
#define RTL_IPQOS_SWQUEUE_LOW		5
#define RTL_IPQOS_SWQUEUE_DEFAULT	3

extern int rtl865x_get_priority_by_swqid(int swqid);
static inline void skb_set_qid(struct sk_buff *skb, struct rtl_pktHdr *hdr) {
#if defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB)
	int asicPri;
        int swqid=RTL_IPQOS_SWQUEUE_DEFAULT;
	if(!qos_mark2prio_enable)
	{
                asicPri = rtl865x_get_priority_by_swqid(RTL_IPQOS_SWQUEUE_DEFAULT);
		if(DumpTrapCPUpkt_debug)
			printk("Set tx priority:%d by default.\n",asicPri);
	}
	else if(skb->priority)
	{
		switch(skb->priority)
		{
			case 8: /* Proprietary used for VOIP traffic) */
                        case 7: /* TC_PRIO_CONTROL(e.g. PPPoE) */
                                asicPri = rtl865x_get_priority_by_swqid(RTL_IPQOS_SWQUEUE_HIGH);
                                break;
			default:
                                asicPri = rtl865x_get_priority_by_swqid(RTL_IPQOS_SWQUEUE_DEFAULT);
				break;
		}
		if(DumpTrapCPUpkt_debug)
			printk("Set tx priority:%d by skb priority %d.\n",asicPri,skb->priority);
	}
	else
	{
		swqid = (skb->mark>>3)&0x7;
		asicPri = swqid?rtl865x_get_priority_by_swqid(swqid):rtl865x_get_priority_by_swqid(RTL_IPQOS_SWQUEUE_DEFAULT);

		if(DumpTrapCPUpkt_debug)
			printk("Set tx priority:%d by skb mark 0x%x.\n",asicPri,skb->mark);
	}
	if(asicPri == -1)
                asicPri = rtl865x_get_priority_by_swqid(RTL_IPQOS_SWQUEUE_DEFAULT);

	hdr->_flags2._tx._tx_qid = asicPri;
	return;
#endif 	
}
#endif // of CONFIG_RTL_HW_QOS_SUPPORT

static inline void mark_set_qid(struct sk_buff *skb, struct rtl_pktHdr *hdr, int prio) {
#if defined(CONFIG_RTL8685) || defined(CONFIG_RTL8685S) || defined(CONFIG_RTL8685SB)
		hdr->_flags2._tx._tx_qid = prio;
#endif 	
}


#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__IRAM_FWD  
#endif
inline int32 _swNic_send(void *skb, void * output, uint32 len,rtl_nicTx_info *nicTx)
{
	struct rtl_pktHdr * pPkthdr;
	char* ptr_pPkthdr_debug;
	int i;	
	int next_index, curr, avail;
	char txFull = 0;
	
	if(DumpSwNicTxRx_debug && DumpSwNicTxRx_debug_LIMIT>0)
		printk("Enter %s  (nicTx->txIdx:%d)\n",__func__,nicTx->txIdx);

	//prio = mark_select_queue(skb, nicTx);
	curr = currTxPkthdrDescIndex[nicTx->txIdx];
	next_index = ((curr+1)==txPkthdrRingCnt[nicTx->txIdx]) ? 0 : (curr+1);
	
	avail = avail_txdscp_num(nicTx->txIdx);
	if (avail<=8) {		
		txFull = 1;
	if (avail <= 0)	{
		return -1;
	}
	}
	currTxPkthdrDescIndex[nicTx->txIdx] = next_index;

#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER)
	/* Pad small packets and add CRC */
	if ( len < 60 )
	{	
		/*To avoid padding random content . reset padding bytes to zero Boyce 2015-08-07*/
		memset( ((uint8*)output)+len ,0x00,64-len);
		len = 64;
	}
	else
	{
		len += 4;
	}
#endif

	
	// Kaohj -- invalidate cache before DMA
#if defined(CONFIG_RTL_8676HWNAT) && defined(CONFIG_RTL_MULTI_LAN_DEV) && defined(CONFIG_FAST_FORWARDING)
	/* ql: I think only ethernet header will be modified for bridge mode, so invalidate ethernet header is enough */
	if ((((struct sk_buff *)skb)->src_port == IF_SWITCH) && (((struct sk_buff *)skb)->pkt_type == PACKET_OTHERHOST))
		dma_cache_wback_inv((unsigned long) output, (14)/*ethernet header*/);
	else
#endif
		dma_cache_wback_inv((unsigned long) output, len);
	/* Fetch packet header from Tx ring */
	pPkthdr = (struct rtl_pktHdr *) ((int32) txPkthdrRing[nicTx->txIdx][curr] 
                                                & ~(DESC_OWNED_BIT | DESC_WRAP));
	ptr_pPkthdr_debug = (char*)pPkthdr;

	/* Pad small packets and add CRC */
#if !defined(CONFIG_XDSL_NEW_HWNAT_DRIVER)
	if ( len < 60 )
	{	
		len = 64;
	}
	else
		len += 4;
#endif


	if((RTL_WANVLANID != nicTx->vid) && (RTL_LANVLANID != nicTx->vid) && (RTL_BridgeWANVLANID != nicTx->vid))
	{
		pPkthdr->ph_vlanTagged	= 1;
		pPkthdr->ph_flags2		= nicTx->portlist&0x3f;
	}
	else
	{		
		pPkthdr->ph_vlanTagged	= 0;
		pPkthdr->ph_flags2		= 0;
	}

#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER)
/*we need init other value avoid tx using previous pkthdr Boyce 2015-05-13*/
	pPkthdr->ph_LLCTagged=0;
	pPkthdr->ph_pppeTagged=0;
#if defined(CONFIG_RTL_8685S_HWNAT)
	pPkthdr->l3v4v6HdrFlag=0;
	pPkthdr->v6HdrLen=0;
#endif
#endif

	pPkthdr->ph_mbuf->m_len  = len;
	pPkthdr->ph_mbuf->m_extsize = len;
	pPkthdr->ph_mbuf->skb = skb;
	pPkthdr->ph_len = len;
	
	pPkthdr->ph_vlanId = nicTx->vid;
	pPkthdr->ph_portlist = nicTx->portlist&0x3f;
	pPkthdr->ph_srcExtPortNum = nicTx->srcExtPort;
	pPkthdr->ph_flags = nicTx->flags;
#if	defined(CONFIG_RTL_HW_QOS_SUPPORT)
	pPkthdr->ph_txPriority = (((char*)output)[14]&0xFF)>>5;
#endif
#ifdef CONFIG_RTL_HW_QOS_SUPPORT
	skb_set_qid(skb,pPkthdr);
#endif
	//printk("%s ph_vlanId=%d ph_portlist=0x%x ph_flags=0x%x\n", __func__, pPkthdr->ph_vlanId, pPkthdr->ph_portlist, pPkthdr->ph_flags);

    /* Set cluster pointer to buffer */		
	pPkthdr->ph_mbuf->m_data    = (output);
	pPkthdr->ph_mbuf->m_extbuf = (output);

/*
RomeDriver's tx_info  has higher priority ,for fwdEngine only (function re865x_send_with_txInfo_and_mask)
*/
#if defined(CONFIG_XDSL_NEW_HWNAT_DRIVER) &&defined(CONFIG_XDSL_ROMEDRIVER)

	romeDriver_refillTxDesc(output,len,nicTx,pPkthdr);

#endif  //CONFIG_XDSL_NEW_HWNAT_DRIVER



#if defined(CONFIG_RTL_8685S_HWNAT) //we have hightest priority
		if(nicTx->replaceTxDesc){
			pPkthdr->ph_type = nicTx->pktType;
			pPkthdr->ph_vlanTagged = nicTx->vlanTagged;
			pPkthdr->ph_LLCTagged = nicTx->llcTagged;
			pPkthdr->ph_pppeTagged = nicTx->pppeTagged;
			pPkthdr->ph_pppoeIdx = nicTx->pppoeIdx;
			pPkthdr->_flags2._tx._txCVlanTagAutoAdd = nicTx->txCVlanTagAutoAdd;
			pPkthdr->ph_txPriority = nicTx->_priority;
			pPkthdr->_flags2._tx._tx_qid = nicTx->_queueId;
			pPkthdr->ph_queueId = nicTx->CPUqueueId;
			pPkthdr->v6HdrLen = nicTx->v6HdrLen;
			pPkthdr->l3v4v6HdrFlag =nicTx->l3v4v6HdrFlag;
		}
#endif


	
	if(DumpSwNicTxRx_debug && DumpSwNicTxRx_debug_LIMIT>0)
	{
		printk("(%s)  pPkthdr->ph_vlanId=%d   pPkthdr->ph_portlist=0x%X    pPkthdr->ph_srcExtPortNum=0x%X \n",__func__
			,pPkthdr->ph_vlanId,pPkthdr->ph_portlist,pPkthdr->ph_srcExtPortNum);
		printk("(%s)--------pPkthdr-----------------\n",__func__);
		for(i=0;i<20;i++)
			printk("%02X  ",ptr_pPkthdr_debug[i]&0xFF);
		printk("\n(%s)--------packet content -----------------\n",__func__);
		skb_debug((char*)output);	
		printk("\n------------------------------------------\n");
		DumpSwNicTxRx_debug_LIMIT--;
	}
	
	/* Give descriptor to switch core */
	txPkthdrRing[nicTx->txIdx][curr] |= DESC_SWCORE_OWNED;
#if 0
	memDump((void*)output, 64, "TX");
	printk("index %d address 0x%p, 0x%x 0x%p.\n", curr, &txPkthdrRing[nicTx->txIdx][curr], (*(volatile uint32 *)&txPkthdrRing[nicTx->txIdx][ret]), pPkthdr);
	printk("Flags 0x%x proto 0x%x portlist 0x%x vid %d extPort %d srcExtPort %d len %d.\n", 
		pPkthdr->ph_flags, pPkthdr->ph_proto, pPkthdr->ph_portlist, pPkthdr->ph_vlanId, 
		pPkthdr->ph_extPortList, pPkthdr->ph_srcExtPortNum, pPkthdr->ph_len);
#endif
#ifndef CONFIG_DESC_IN_SRAM
	/* fix bug: OCP memory access is slower than lx register operation*/
	next_index = *(volatile unsigned int *)(&txPkthdrRing[nicTx->txIdx][curr]);
#endif

	mb();
	
	/* Set TXFD bit to start send */
	REG32(CPUICR) |= TXFD;

	if (txFull)
		return -2;
	else
		return curr;
}
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__IRAM_FWD
#endif
inline int32 swNic_send(void *skb, void * output, uint32 len,rtl_nicTx_info *nicTx)
{
	int	ret;
	unsigned long flags;

	flags = swcore_tx_lock(nicTx->txIdx);
	ret = _swNic_send(skb, output, len, nicTx);
	swcore_tx_unlock(nicTx->txIdx, flags);
	return ret;
}

#if NUM_TX_PKTHDR_DESC>512
#define HALF_TX_PKTHDR_DESC	256
#else
#define HALF_TX_PKTHDR_DESC	(NUM_TX_PKTHDR_DESC/2)
#endif
#if defined(CONFIG_DEFAULTS_KERNEL_2_6) 
__IRAM_FWD
#endif
int32 swNic_txDone(int idx)
{
	struct rtl_pktHdr	*pPkthdr;
	int				free_num;
	unsigned long flags;

	/* it seems swNic_txDone will never reentrance, so dont need to lock it */
	//if (atomic_read(&lock_tx_tail))
	//	return 0;
	//atomic_set(&lock_tx_tail, 1);
#if 0
#ifdef RX_NAPI
	mitigation_factor++;
	if ((mitigation_factor & (HALF_TX_PKTHDR_DESC-1))  && !fast_reclaim)
		return 0;
#endif
#endif
	
	//local_irq_save(flags);
	flags = swcore_tx_lock(idx);
#if 0
#ifdef RX_NAPI
	fast_reclaim = 0;
#endif
#endif
	free_num = 0;
	{
		while (txPktDoneDescIndex[idx] != currTxPkthdrDescIndex[idx]) {		
			if ( (*(volatile uint32 *)&txPkthdrRing[idx][txPktDoneDescIndex[idx]] 
				& DESC_OWNED_BIT) == DESC_RISC_OWNED )
			{
				#ifdef CONFIG_RTL8196C_REVISION_B
				if (rtl_chip_version == RTL8196C_REVISION_A)
					txPkthdrRing[idx][txPktDoneDescIndex[idx]] =txPkthdrRing_backup[idx][txPktDoneDescIndex[idx]] ;
				#endif

				pPkthdr = (struct rtl_pktHdr *) ((int32) txPkthdrRing[idx][txPktDoneDescIndex[idx]] 
					& ~(DESC_OWNED_BIT | DESC_WRAP));

				if (pPkthdr->ph_mbuf->skb)
				{
					tx_done_callback(pPkthdr->ph_mbuf->skb);
					pPkthdr->ph_mbuf->skb = NULL;
				}
				
				if (++txPktDoneDescIndex[idx] == txPkthdrRingCnt[idx])
					txPktDoneDescIndex[idx] = 0;

				free_num++;
			}
			else
				break;
		}
	}

	//local_irq_restore(flags);
	swcore_tx_unlock(idx, flags);
	//atomic_set(&lock_tx_tail, 0);
	return free_num;	
}

#ifdef  CONFIG_RTL865X_MODEL_TEST_FT2
int32 swNic_send_portmbr(void * output, uint32 len, uint32 portmbr)
{
    struct rtl_pktHdr * pPkthdr;
    uint8 pktbuf[2048];
    uint8* pktbuf_alligned = (uint8*) (( (uint32) pktbuf & 0xfffffffc) | 0xa0000000);

    /* Copy Packet Content */
    memcpy(pktbuf_alligned, output, len);

    ASSERT_CSP( ((int32) txPkthdrRing[0][currTxPkthdrDescIndex] & DESC_OWNED_BIT) == DESC_RISC_OWNED );

    /* Fetch packet header from Tx ring */
    pPkthdr = (struct rtl_pktHdr *) ((int32) txPkthdrRing[0][currTxPkthdrDescIndex] 
                                                & ~(DESC_OWNED_BIT | DESC_WRAP));

    /* Pad small packets and add CRC */
    if ( len < 60 )
        pPkthdr->ph_len = 64;
    else
        pPkthdr->ph_len = len + 4;

    pPkthdr->ph_mbuf->m_len = pPkthdr->ph_len;
    pPkthdr->ph_mbuf->m_extsize = pPkthdr->ph_len;

    /* Set cluster pointer to buffer */
    pPkthdr->ph_mbuf->m_data = pktbuf_alligned;
    pPkthdr->ph_mbuf->m_extbuf = pktbuf_alligned;

    /* Set destination port */
    pPkthdr->ph_portlist = portmbr;

    /* Give descriptor to switch core */
    txPkthdrRing[0][currTxPkthdrDescIndex] |= DESC_SWCORE_OWNED;

    /* Set TXFD bit to start send */
    REG32(CPUICR) |= TXFD;
    
    /* Wait until packet is successfully sent */
#if 1    
    while ( (*(volatile uint32 *)&txPkthdrRing[0][currTxPkthdrDescIndex] 
                    & DESC_OWNED_BIT) == DESC_SWCORE_OWNED );
#endif    
    txPktCounter++;
    
    if ( ++currTxPkthdrDescIndex == txPkthdrRingCnt[0] )
        currTxPkthdrDescIndex = 0;

    return 0;
}
#endif


void swNic_freeRxBuf(void)
{
	int idx, i;
	struct rtl_pktHdr * pPkthdr;

	for(i=RTL865X_SWNIC_RXRING_MAX_PKTDESC-1; i >= 0 ; i--)
	{
		for (idx=0; idx<rxPkthdrRingCnt[i]; idx++)
		{
			if (!((rxPkthdrRing[i][idx] & DESC_OWNED_BIT) == DESC_RISC_OWNED)) {
				pPkthdr = (struct rtl_pktHdr *) (rxPkthdrRing[i][idx] & 
					~(DESC_OWNED_BIT | DESC_WRAP));    

				/*if(pPkthdr == NULL || pPkthdr->ph_mbuf == NULL)
				*	continue;
				*/
				if (pPkthdr->ph_mbuf->skb)
				{
					free_rx_buf(pPkthdr->ph_mbuf->skb);
					pPkthdr->ph_mbuf->skb = NULL;
				}

				pPkthdr->ph_mbuf->m_data = NULL;
				pPkthdr->ph_mbuf->m_extbuf = NULL;
		    	}
	    	}
	}
}

//#pragma ghs section text=default
/*************************************************************************
*   FUNCTION                                                              
*       swNic_init                                         
*                                                                         
*   DESCRIPTION                                                           
*       This function initializes descriptors and data structures.
*                                                                         
*   INPUTS                                                                
*       userNeedRxPkthdrRingCnt[RTL865X_SWNIC_RXRING_HW_PKTDESC] :
*          Number of Rx pkthdr descriptors of each ring.
*       userNeedRxMbufRingCnt :
*          Number of Tx mbuf descriptors.
*       userNeedTxPkthdrRingCnt[RTL865X_SWNIC_TXRING_HW_PKTDESC] :
*          Number of Tx pkthdr descriptors of each ring.
*       clusterSize :
*          Size of cluster.
*                                                                         
*   OUTPUTS                                                               
*       Status.
*************************************************************************/

int32 swNic_init(uint32 userNeedRxPkthdrRingCnt[RTL865X_SWNIC_RXRING_HW_PKTDESC],
                 uint32 userNeedRxMbufRingCnt,
                 uint32 userNeedTxPkthdrRingCnt[RTL865X_SWNIC_TXRING_HW_PKTDESC],
                 uint32 clusterSize)
{
	uint32 i, j, k;
	static uint32 totalRxPkthdrRingCnt = 0, totalTxPkthdrRingCnt = 0;
	static struct rtl_pktHdr *pPkthdrList_start;
	static struct rtl_mBuf *pMbufList_start;		
	struct rtl_pktHdr *pPkthdrList;
	struct rtl_mBuf *pMbufList;
	struct rtl_pktHdr * pPkthdr;
	struct rtl_mBuf * pMbuf;

	dma_addr_t pPkthdrList_start_addr, pMbufList_start_addr;
	
	/* init const array for rx pre-process	*/
	extPortMaskToPortNum[0] = 0;
	extPortMaskToPortNum[1] = 1;
	extPortMaskToPortNum[2] = 2;
	extPortMaskToPortNum[3] = 0;
	extPortMaskToPortNum[4] = 3;
	extPortMaskToPortNum[5] = 0;
	extPortMaskToPortNum[6] = 0;
	extPortMaskToPortNum[7] = 0;
	
	rxPkthdrRefillThreshold[0] = ETH_REFILL_THRESHOLD;
	rxPkthdrRefillThreshold[1] = ETH_REFILL_THRESHOLD1;
	rxPkthdrRefillThreshold[2] = ETH_REFILL_THRESHOLD2;
	rxPkthdrRefillThreshold[3] = ETH_REFILL_THRESHOLD3;
	rxPkthdrRefillThreshold[4] = ETH_REFILL_THRESHOLD4;
	rxPkthdrRefillThreshold[5] = ETH_REFILL_THRESHOLD5;

	#if defined(CONFIG_RTL8196C_REVISION_B)
	rtl_chip_version = REG32(REVR);
	#endif
	
	if (rxMbufRing == NULL)
	{ 
#ifdef CONFIG_DESC_IN_SRAM
		//unsigned char* s_begin = (unsigned char*)kmalloc(8*1024, GFP_KERNEL);
		unsigned char* s_begin = (unsigned char*)0xAF000000;
		unsigned int sram_off=0;

		s_begin = (unsigned char*)((unsigned int)s_begin | UNCACHE_MASK);
		
		//use segment 0 8K SRAM here.
		REG32(0xb8004000) = (((unsigned int )s_begin)&(0x1ffffffe))|1;
		REG32(0xb8004004) = 0x06;
		REG32(0xB8004008) = 0;
		REG32(0xb8001300) = (((unsigned int )s_begin)&(0x1ffffffe))|1;
		REG32(0xb8001304) = 0x06;
		memset((unsigned char*)s_begin, 0, 8*1024);
#endif
		size_of_cluster = clusterSize;

		/* Allocate Rx descriptors of rings */
		for (i = 0; i < RTL865X_SWNIC_RXRING_HW_PKTDESC; i++) {   
			rxPkthdrRingCnt[i] = userNeedRxPkthdrRingCnt[i];//rxRingSize[]
			if (rxPkthdrRingCnt[i] == 0)
			{
				rxPkthdrRing[i] = NULL;
				continue;
			}

#ifdef CONFIG_DESC_IN_SRAM
			if ((0 == i) && ((sram_off + (rxPkthdrRingCnt[i] * sizeof(uint32 *))) <= 0x2000)) {
				rxPkthdrRing[i] = (uint32 *)s_begin;
				sram_off += (rxPkthdrRingCnt[i] * sizeof(uint32 *));
			}
			else
#endif//end of CONFIG_DESC_IN_SRAM
#ifdef CONFIG_DATA_IN_IMEM
			rxPkthdrRing[i] = (uint32 *)allocUncachedBuffFromIMEM(rxPkthdrRingCnt[i] * sizeof(uint32 *));
			if (NULL == rxPkthdrRing[i])
#endif//end of CONFIG_DATA_IN_IMEM
				rxPkthdrRing[i] = dma_alloc_coherent(NULL, rxPkthdrRingCnt[i] * sizeof(uint32), &rxPkthdrRing_addr[i],GFP_KERNEL);
			ASSERT_CSP( (uint32) rxPkthdrRing[i] & 0x0fffffff );

			totalRxPkthdrRingCnt += rxPkthdrRingCnt[i];
		}

		if (totalRxPkthdrRingCnt == 0)
			return EINVAL;

		/* Allocate Tx descriptors of rings */
		for (i = 0; i < RTL865X_SWNIC_TXRING_HW_PKTDESC; i++) {    
			txPkthdrRingCnt[i] = userNeedTxPkthdrRingCnt[i];//txRingSize[]

			if (txPkthdrRingCnt[i] == 0)
			{
				txPkthdrRing[i] = NULL;
				continue;
			}

#ifdef CONFIG_DESC_IN_SRAM
			if ((0 == i) && ((sram_off + (txPkthdrRingCnt[i] * sizeof(uint32 *))) <= 0x2000)) {
				txPkthdrRing[i] = (uint32 *)(s_begin + sram_off);
				sram_off += (txPkthdrRingCnt[i] * sizeof(uint32 *));
			}
			else
#endif//end of CONFIG_DESC_IN_SRAM
#ifdef CONFIG_DATA_IN_IMEM
			txPkthdrRing[i] = (uint32 *)allocUncachedBuffFromIMEM(txPkthdrRingCnt[i] * sizeof(uint32 *));
			if (NULL == txPkthdrRing[i])
#endif//end of CONFIG_DATA_IN_IMEM
			txPkthdrRing[i] = dma_alloc_coherent(NULL, txPkthdrRingCnt[i] * sizeof(uint32), &txPkthdrRing_addr[i], GFP_KERNEL);
			#ifdef CONFIG_RTL8196C_REVISION_B
			if (rtl_chip_version == RTL8196C_REVISION_A) {
#ifdef CONFIG_DESC_IN_SRAM
				if ((0 == i) && ((sram_off + (txPkthdrRingCnt[i] * sizeof(uint32 *))) <= 0x2000)) {
					txPkthdrRing_backup[i] = (uint32 *)(s_begin + sram_off);
					sram_off += (txPkthdrRingCnt[i] * sizeof(uint32 *));
				}
				else
#endif//end of CONFIG_DESC_IN_SRAM
			#ifdef CONFIG_DATA_IN_IMEM
				txPkthdrRing_backup[i] = (uint32 *)allocUncachedBuffFromIMEM(txPkthdrRingCnt[i] * sizeof(uint32));
				if (NULL == txPkthdrRing_backup[i])
			#endif//end of CONFIG_DATA_IN_IMEM
				txPkthdrRing_backup[i] = dma_alloc_coherent(NULL, txPkthdrRingCnt[i] * sizeof(uint32), &txPkthdrRing_backup_addr[i], GFP_KERNEL);
			}
			#endif

			ASSERT_CSP( (uint32) txPkthdrRing[i] & 0x0fffffff );

			totalTxPkthdrRingCnt += txPkthdrRingCnt[i];
		}

		if (totalTxPkthdrRingCnt == 0)
			return EINVAL;

		/* Allocate MBuf descriptors of rings */
		rxMbufRingCnt = userNeedRxMbufRingCnt;

		if (userNeedRxMbufRingCnt == 0)
			return EINVAL;

#ifdef CONFIG_DESC_IN_SRAM
		if ((sram_off + (rxMbufRingCnt * sizeof(uint32*))) <= 0x2000) {
			rxMbufRing = (uint32 *)(s_begin + sram_off);
			sram_off += (rxMbufRingCnt * sizeof(uint32*));
		}
		else
#endif//end of CONFIG_DESC_IN_SRAM
#ifdef CONFIG_DATA_IN_IMEM
		rxMbufRing = (uint32 *)allocUncachedBuffFromIMEM(rxMbufRingCnt * sizeof(uint32*));
		if (NULL == rxMbufRing)
#endif//end of CONFIG_DATA_IN_IMEM
		rxMbufRing = dma_alloc_coherent(NULL, rxMbufRingCnt * sizeof(uint32), &rxMbufRing_addr, GFP_KERNEL);
		ASSERT_CSP( (uint32) rxMbufRing & 0x0fffffff );

		/* Allocate pkthdr */
#ifdef CONFIG_DATA_IN_IMEM
		pPkthdrList_start = (struct rtl_pktHdr *)allocUncachedBuffFromIMEM(
								(totalRxPkthdrRingCnt + totalTxPkthdrRingCnt) * sizeof(struct rtl_pktHdr));
		if (NULL == pPkthdrList_start)
#endif//end of CONFIG_DATA_IN_IMEM
		pPkthdrList_start = dma_alloc_coherent(NULL, \
			(totalRxPkthdrRingCnt + totalTxPkthdrRingCnt) * sizeof(struct rtl_pktHdr), &pPkthdrList_start_addr, GFP_KERNEL);
		ASSERT_CSP( (uint32) pPkthdrList_start & 0x0fffffff );

		/* Allocate mbufs */
#ifdef CONFIG_DATA_IN_IMEM
		pMbufList_start = (struct rtl_mBuf *)allocUncachedBuffFromIMEM(
							(rxMbufRingCnt + totalTxPkthdrRingCnt) * sizeof(struct rtl_mBuf));
		if (NULL == pMbufList_start)
#endif//end of CONFIG_DATA_IN_IMEM
		pMbufList_start = dma_alloc_coherent(NULL, \
			(rxMbufRingCnt + totalTxPkthdrRingCnt) * sizeof(struct rtl_mBuf), &pMbufList_start_addr, GFP_KERNEL);
		ASSERT_CSP( (uint32) pMbufList_start & 0x0fffffff );

#ifdef CONFIG_DESC_IN_SRAM
		printk("==============>>> SRAM used 0x%x\n", sram_off);
#endif//end of CONFIG_DESC_IN_SRAM
	}

	/* Initialize interrupt statistics counter */
	//rxPktCounter = txPktCounter = 0;

	/* Initialize index of Tx pkthdr descriptor */
	for (i=0;i<RTL865X_SWNIC_TXRING_HW_PKTDESC;i++)
	{
		currTxPkthdrDescIndex[i] = 0;
		txPktDoneDescIndex[i]=0;
	}

	pPkthdrList = pPkthdrList_start;
	pMbufList = pMbufList_start;

	/* Initialize Tx packet header descriptors */
	for (i = 0; i < RTL865X_SWNIC_TXRING_HW_PKTDESC; i++)
	{
		for (j = 0; j < txPkthdrRingCnt[i]; j++)
		{
			/* Dequeue pkthdr and mbuf */
			pPkthdr = pPkthdrList++;
			pMbuf = pMbufList++;

			bzero((void *) pPkthdr, sizeof(struct rtl_pktHdr));
			bzero((void *) pMbuf, sizeof(struct rtl_mBuf));

			pPkthdr->ph_mbuf = pMbuf;
			pPkthdr->ph_len = 0;
			pPkthdr->ph_flags = PKTHDR_USED | PKT_OUTGOING;
			pPkthdr->ph_type = PKTHDR_ETHERNET;
			pPkthdr->ph_portlist = 0;

			pMbuf->m_next = NULL;
			pMbuf->m_pkthdr = pPkthdr;
			pMbuf->m_flags = MBUF_USED | MBUF_EXT | MBUF_PKTHDR | MBUF_EOR;
			pMbuf->m_data = NULL;
			pMbuf->m_extbuf = NULL;
			pMbuf->m_extsize = 0;

			txPkthdrRing[i][j] = (int32) pPkthdr | DESC_RISC_OWNED;
			#ifdef CONFIG_RTL8196C_REVISION_B
			if (rtl_chip_version == RTL8196C_REVISION_A)
				txPkthdrRing_backup[i][j]=(int32) pPkthdr | DESC_RISC_OWNED;
			#endif
		}

		if(txPkthdrRingCnt[i] > 0)
		{
			/* Set wrap bit of the last descriptor */
			txPkthdrRing[i][txPkthdrRingCnt[i] - 1] |= DESC_WRAP;
			#ifdef CONFIG_RTL8196C_REVISION_B
			if (rtl_chip_version == RTL8196C_REVISION_A)
				txPkthdrRing_backup[i][txPkthdrRingCnt[i] - 1] |= DESC_WRAP;
			#endif
		}

	}

	/* Fill Tx packet header FDP */
	REG32(CPUTPDCR0) = (uint32) txPkthdrRing[0];
	REG32(CPUTPDCR1) = (uint32) txPkthdrRing[1];

#ifdef CONFIG_RTL_8196D
	/* Fill Tx packet header FDP */
	REG32(CPUTPDCR2) = (uint32) txPkthdrRing[2];
	REG32(CPUTPDCR3) = (uint32) txPkthdrRing[3];
#endif

	/* Initialize Rx packet header descriptors */
	k = 0;

	for (i = 0; i < RTL865X_SWNIC_RXRING_HW_PKTDESC; i++)
	{
		for (j = 0; j < rxPkthdrRingCnt[i]; j++)
		{
			/* Dequeue pkthdr and mbuf */
			pPkthdr = pPkthdrList++;
			pMbuf = pMbufList++;

			bzero((void *) pPkthdr, sizeof(struct rtl_pktHdr));
			bzero((void *) pMbuf, sizeof(struct rtl_mBuf));

			/* Setup pkthdr and mbuf */
			pPkthdr->ph_mbuf = pMbuf;
			pPkthdr->ph_len = 0;
			pPkthdr->ph_flags = PKTHDR_USED | PKT_INCOMING;
			pPkthdr->ph_type = PKTHDR_ETHERNET;
			pPkthdr->ph_portlist = 0;
			pMbuf->m_next = NULL;
			pMbuf->m_pkthdr = pPkthdr;
			pMbuf->m_len = 0;
			pMbuf->m_flags = MBUF_USED | MBUF_EXT | MBUF_PKTHDR | MBUF_EOR;
			pMbuf->m_extsize = size_of_cluster;
			#ifdef CONFIG_FAST_FORWARDING
			pMbuf->m_data = pMbuf->m_extbuf = get_buf_from_poll();;
			#else
			pMbuf->m_data = pMbuf->m_extbuf = alloc_rx_buf(&pPkthdr->ph_mbuf->skb, size_of_cluster);
			#endif//end of CONFIG_FAST_FORWARDING

			/* Setup descriptors */
			rxPkthdrRing[i][j] = (int32) pPkthdr | DESC_SWCORE_OWNED;
			rxMbufRing[k++] = (int32) pMbuf | DESC_SWCORE_OWNED;
		}

		/* Initialize index of current Rx pkthdr descriptor */
		currRxPkthdrDescIndex[i] = 0;

		/* Initialize index of current Rx Mbuf descriptor */
		currRxMbufDescIndex = 0;

		/* Set wrap bit of the last descriptor */
		if(rxPkthdrRingCnt[i] > 0)
			rxPkthdrRing[i][rxPkthdrRingCnt[i] - 1] |= DESC_WRAP;

		#ifdef DELAY_REFILL_ETH_RX_BUF
		rxDescReadyForHwIndex[i] = 0;
		#endif
	}

	rxMbufRing[rxMbufRingCnt - 1] |= DESC_WRAP;

	/* Fill Rx packet header FDP */
	REG32(CPURPDCR0) = (uint32) rxPkthdrRing[0];
	REG32(CPURPDCR1) = (uint32) rxPkthdrRing[1];
	REG32(CPURPDCR2) = (uint32) rxPkthdrRing[2];
	REG32(CPURPDCR3) = (uint32) rxPkthdrRing[3];
	REG32(CPURPDCR4) = (uint32) rxPkthdrRing[4];
	REG32(CPURPDCR5) = (uint32) rxPkthdrRing[5];

	REG32(CPURMDCR0) = (uint32) rxMbufRing;

	return SUCCESS;
}


#ifdef FAT_CODE
/*************************************************************************
*   FUNCTION                                                              
*       swNic_resetDescriptors                                         
*                                                                         
*   DESCRIPTION                                                           
*       This function resets descriptors.
*                                                                         
*   INPUTS                                                                
*       None.
*                                                                         
*   OUTPUTS                                                               
*       None.
*************************************************************************/
void swNic_resetDescriptors(void)
{
    /* Disable Tx/Rx and reset all descriptors */
    REG32(CPUICR) &= ~(TXCMD | RXCMD);
    return;
}
#endif//FAT_CODE

#if 	defined(CONFIG_RTL_PROC_DEBUG)
/*	dump the rx ring info	*/
int32	rtl_dumpRxRing(void)
{
	int	idx, cnt;
	struct rtl_pktHdr * pPkthdr;

	for(idx=0;idx<RTL865X_SWNIC_RXRING_HW_PKTDESC;idx++)
	{
		printk("**********************************************\nRxRing%d: cnt %d\n",
			idx, rxPkthdrRingCnt[idx]);

		/*	skip the null rx ring */
		if (rxPkthdrRingCnt[idx]==0)
			continue;

		/*	dump all the pkt header	*/
		for(cnt=0;cnt<rxPkthdrRingCnt[idx];cnt++)
		{
			pPkthdr = (struct rtl_pktHdr *) (rxPkthdrRing[idx][cnt] & ~(DESC_OWNED_BIT | DESC_WRAP));
			
				
			printk("  idx[%d]: 0x%p-->mbuf[0x%p],skb[0x%p]%s%s%s%s\n",  cnt, pPkthdr, pPkthdr->ph_mbuf, pPkthdr->ph_mbuf->skb,
				(rxPkthdrRing[idx][cnt]&DESC_OWNED_BIT)==DESC_RISC_OWNED?" :CPU":" :SWCORE", 
				(rxPkthdrRing[idx][cnt]&DESC_WRAP)!=0?" :WRAP":"",
				cnt==currRxPkthdrDescIndex[idx]?"  <===currIdx":"",
				#ifdef DELAY_REFILL_ETH_RX_BUF
				cnt ==rxDescReadyForHwIndex[idx]?" <===readyForHw":
				#endif
				"");
				
		}
	}
	return SUCCESS;
}

/*	dump the tx ring info	*/
int32	rtl_dumpTxRing(void)
{
	int	idx, cnt;
	struct rtl_pktHdr * pPkthdr = NULL;

	for(idx=0;idx<RTL865X_SWNIC_TXRING_HW_PKTDESC;idx++)
	{
		printk("**********************************************\nTxRing%d: cnt %d\n",
			idx, txPkthdrRingCnt[idx]);

		/*	skip the null rx ring */
		if (txPkthdrRingCnt[idx]==0)
			continue;

		/*	dump all the pkt header	*/
		for(cnt=0;cnt<txPkthdrRingCnt[idx];cnt++)
		{
 #ifdef CONFIG_RTL8196C_REVISION_B
		  if (rtl_chip_version == RTL8196C_REVISION_A)
			pPkthdr = (struct rtl_pktHdr *) (txPkthdrRing_backup[idx][cnt] & ~(DESC_OWNED_BIT | DESC_WRAP));
		  else
#endif
			pPkthdr = (struct rtl_pktHdr *) (txPkthdrRing[idx][cnt] & ~(DESC_OWNED_BIT | DESC_WRAP));

			printk("  idx[%d]: 0x%p-->mbuf[0x%p],skb[0x%p]%s%s%s%s\n",  cnt, pPkthdr, pPkthdr->ph_mbuf, pPkthdr->ph_mbuf->skb, 
				(txPkthdrRing[idx][cnt]&DESC_OWNED_BIT)==DESC_RISC_OWNED?" :CPU":" :SWCORE", 
				(txPkthdrRing[idx][cnt]&DESC_WRAP)!=0?" :WRAP":"",
				cnt==currTxPkthdrDescIndex[idx]?"  <===currIdx":"", 
				cnt==txPktDoneDescIndex[idx]?"  <===txDoneIdx":"");
		}
	}
	return SUCCESS;
}

/*	dump the tx ring info	*/
int32	rtl_dumpMbufRing(void)
{
	int	idx;
	struct rtl_mBuf *mbuf;

	idx = 0;
	printk("**********************************************\nMbufRing:\n");
	while(1)
	{
		mbuf = (struct rtl_mBuf *)(rxMbufRing[idx] & ~(DESC_OWNED_BIT | DESC_WRAP));
		printk("mbuf[%d]: 0x%p: ==> pkthdr[0x%p] ==> skb[0x%p]%s%s%s\n", idx, mbuf, mbuf->m_pkthdr, 
				mbuf->skb, 
				(rxMbufRing[idx]&DESC_OWNED_BIT)==DESC_RISC_OWNED?" :CPU":" :SWCORE", 
				(rxMbufRing[idx]&DESC_WRAP)==DESC_ENG_OWNED?" :WRAP":"",
				idx==currRxMbufDescIndex?"  <===currIdx":"");
			if ((rxMbufRing[idx]&DESC_WRAP)!=0)
				break;
			idx++;
	}
	return SUCCESS;
}
#endif