#include <linux/module.h> #include <linux/kernel.h> #include <linux/compiler.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/atmdev.h> #include <linux/atm.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/ethtool.h> #include <linux/mii.h> #include <linux/if_vlan.h> #include <linux/crc32.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <linux/atmbr2684.h> #include "ra8670.h" #include "ra_debug.h" #include "../drivers/net/ethernet/realtek/re830x.h" //UINT16 mantissa[0x10]= { // 0x005, 0x016, 0x028, 0x03C, // 0x051, 0x068, 0x080, 0x09B, // 0x0B8, 0x0D8, 0x0FA, 0x120, // 0x14B, 0x179, 0x1AE, 0x1E8}; //UINT32 CRC32TBL[0x100]; //UINT16 CRC10TBL[0x100]; #ifdef CONFIG_RTL8672_ATM_QoS #define SAR_INTERPOLATE 0 #else #define SAR_INTERPOLATE 1 #define IC8671G #endif #define SAR_CHECK_RC 1 #define SAR_CHECK_RC_NEW 0 // new methold of check_cr //tylo, for linux2.6 porting extern int sar_irq; #define __SWAP_DATA #define __SWAP int cr2reg(int pcr); int sar_send (struct atm_vcc *vcc,struct sk_buff *skb); extern BOOL GetLinkSpeed(char *Rate); #ifdef CONFIG_RTL8672_ATM_QoS #define MAX_PCR_AUTO_CH -1 void Check_Line_Rate_PCR(sar_private *cp, int ch_no); #endif extern void skb_oamaal5_debug(const struct sk_buff *skb, int enable, int flag); volatile long unsigned int lock_txbuff = 0; #if 0 static int xxx = 0; static char yyy[20000]; void scout(char ch) { return; yyy[xxx++] = ch; if ( xxx == (sizeof(yyy)-1) ) { yyy[xxx] = '\0'; // null terminated printk("\n\n"); for( xxx = 0; yyy[xxx]; xxx++ ) printk("%c",yyy[xxx]); for(;;); } } #endif /*-------------------------------- Routines ---------------------------------*/ #if 0 struct sk_buff *get_skb_from_pool(INT8 ch_no){ struct sk_buff *skb=NULL; INT8 i=cp->vcc[ch_no].skb_pool_get; if(cp->vcc[ch_no].skb_pool[i]!=(UINT32)NULL){ skb=(struct sk_buff *)cp->vcc[ch_no].skb_pool[i]; cp->vcc[ch_no].skb_pool[i] = (UINT32)NULL; i++; i%=SAR_RX_DESC_NUM; cp->vcc[ch_no].skb_pool_get = i; } return skb; } void refill_skb_pool(INT8 ch_no){ struct sk_buff *skb; INT8 put=cp->vcc[ch_no].skb_pool_put, get=cp->vcc[ch_no].skb_pool_get; while((put != get)||(cp->vcc[ch_no].skb_pool[get]==(UINT32)NULL)){ skb=dev_alloc_skb(SAR_RX_Buffer_Size); if(skb!=(struct sk_buff *)NULL) cp->vcc[ch_no].skb_pool[put]=(UINT32)skb; else break; put++; put%=SAR_RX_DESC_NUM; cp->vcc[ch_no].skb_pool_put = put; } return; } void init_skb_pool(INT8 ch_no){ int i; struct sk_buff *skb; cp->vcc[ch_no].skb_pool_put = 0; cp->vcc[ch_no].skb_pool_get = 0; for(i=0;i<SAR_RX_DESC_NUM;i++){ skb=dev_alloc_skb(SAR_RX_Buffer_Size); if(skb!=(struct sk_buff *)NULL) cp->vcc[ch_no].skb_pool[i]=(UINT32)skb; else break; } i%=SAR_RX_DESC_NUM; cp->vcc[ch_no].skb_pool_put=i; return; } #endif /* move to ra8670.c struct VPIVCI{ int vpi; int vci; }; struct VPIVCI Possible_PVC[]={{0,35},{8,35},{0,43},{0,51},{0,59},{8,43},{8,51},{8,59},{0,8},{0,9}, {1,0},{2,0},{3,0},{4,0},{5,0},{6,0},{7,0},{8,0},{9,0},{10,0}, {0,0},{0,0},{0,0},{0,0},{0,0},{5,35},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0},{8,35},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0},{0,35},{0,0},{0,0},{0,0},{0,0}, {0,0},{0,0},{0,0},{0,0},{0,0},{4,35},{0,0},{0,0},{0,0},{0,0},}; */ void Alloc_desc(sar_private *cp) { UINT32 *tmp; INT32 size; #if 0 /* TV Channels : 16 byte x 64 descriptors x Enable_VC_CNT channel, +256 for 256 byte alignment */ size=sizeof(TV_CMD_DESC)*SAR_TX_RING_SIZE*Enable_VC_CNT+Desc_Align; tmp=kmalloc(size, GFP_KERNEL); cp->pTVDescBuf=(char *)tmp; cp->TVDesc=(TV_CMD_DESC *) (((UINT32)((UINT32)tmp+Desc_Align)&0x0FFFFF00)|Uncache_Mask); memset(cp->TVDesc, 0 , size); /* RV Channels : 16 byte x 64 descriptors x Enable_VC_CNT channel, +256 for 256 byte alignment */ size=sizeof(RV_CMD_DESC)*SAR_RX_RING_SIZE*Enable_VC_CNT+Desc_Align; tmp=kmalloc(size, GFP_KERNEL); cp->pRVDescBuf=(char *)tmp; cp->RVDesc=(RV_STS_DESC *) (((UINT32)((UINT32)tmp+Desc_Align)&0xFFFFFF00)|Uncache_Mask ); memset(cp->RVDesc, 0 , size); /* TO Channel : 16 byte x 64 descriptors x 1 channel, +256 for 256 byte alignment */ size=sizeof(TO_CMD_DESC)*SAR_TX_RING_SIZE+Desc_Align; tmp=kmalloc(size, GFP_KERNEL); cp->pTODescBuf=(char *)tmp; cp->TODesc=(TO_CMD_DESC *) (((UINT32)((UINT32)tmp+Desc_Align)&0xFFFFFF00)|Uncache_Mask ); memset(cp->TODesc, 0 ,size); /* RO Channel : 16 byte x 64 descriptors x 1 channel, +256 for 256 byte alignment */ size=sizeof(RO_CMD_DESC)*SAR_RX_RING_SIZE+Desc_Align; tmp=kmalloc(size, GFP_KERNEL); cp->pRODescBuf=(char *)tmp; cp->RODesc=(RO_STS_DESC *) (((UINT32)((UINT32)tmp+Desc_Align)&0xFFFFFF00)|Uncache_Mask ); memset(cp->RODesc, 0 , size); #else /* TV Channels : 16 byte x 64 descriptors x (Enable_VC_CNT+1) channel, +256 for 256 byte alignment */ size=sizeof(TV_CMD_DESC)*SAR_TX_RING_SIZE*(Enable_VC_CNT+1)+Desc_Align; tmp=kmalloc(size, GFP_KERNEL); cp->pTVDescBuf=(char *)tmp; cp->TVDesc=(TV_CMD_DESC *) (((UINT32)((UINT32)tmp+Desc_Align)&0x0FFFFF00)|Uncache_Mask); memset(cp->pTVDescBuf, 0 , size); /* TO Channel : 16 byte x 64 descriptors x 1 channel, +256 for 256 byte alignment */ cp->pTODescBuf=(char *)NULL; cp->TODesc=(TO_CMD_DESC *) (&cp->TVDesc[OAM_CH_NO*SAR_TX_RING_SIZE]); /* RV Channels : 16 byte x 64 descriptors x (Enable_VC_CNT+1) channel, +256 for 256 byte alignment */ // Mason Yu. Recieve packet from OAM ch1(not cho) for AutoHunt size=sizeof(RV_CMD_DESC)*SAR_RX_RING_SIZE*(Enable_VC_CNT+NUMBER_OF_OAM_CH)+Desc_Align; tmp=kmalloc(size, GFP_KERNEL); cp->pRVDescBuf=(char *)tmp; cp->RVDesc=(RV_STS_DESC *) (((UINT32)((UINT32)tmp+Desc_Align)&0xFFFFFF00)|Uncache_Mask ); memset(cp->pRVDescBuf, 0 , size); /* RO Channel : 16 byte x 64 descriptors x 1 channel, +256 for 256 byte alignment */ cp->pRODescBuf=(char *)NULL; cp->RODesc=(RO_STS_DESC *) (&cp->RVDesc[OAM_CH_NO*SAR_RX_RING_SIZE]); #endif } void Free_desc(sar_private *cp) { if (cp->pTVDescBuf) kfree(cp->pTVDescBuf); if (cp->pTODescBuf) kfree(cp->pTODescBuf); if (cp->pRVDescBuf) kfree(cp->pRVDescBuf); if (cp->pRODescBuf) kfree(cp->pRODescBuf); } void Init_reg(sar_private *cp) { int i; sar_atm_vcc *vcc; static int first_init=1; //vcc = kmalloc(sizeof(sar_atm_vcc)*(Enable_VC_CNT+1), GFP_KERNEL); //5/22/04' hrchen, bug, extra malloc /* Set Register Address */ for (i=0;i<Enable_VC_CNT;i++){ vcc=&cp->vcc[i]; memset(&vcc->TV, 0, sizeof(TV_Channel)); memset(&vcc->RV, 0, sizeof(RV_Channel)); if (first_init) { //only reset stat information once memset(&vcc->stat, 0, sizeof(ch_stat)); //printk("reset\n"); }; memset(&vcc->QoS, 0, sizeof(Traffic_Manage)); vcc->TV.CtlSts = PCR_Period; vcc->TV.SCR = SCR_Period<<16; vcc->TV.CtlSts_Addr = TV_Ctrl_Addr + sizeof(TV_CMD_DESC)*i; vcc->TV.FDP_Addr = TV_FDP_Addr + sizeof(TV_CMD_DESC)*i; vcc->TV.SCR_Addr = TV_SCR_Addr + sizeof(TV_CMD_DESC)*i; vcc->TV.HDR_Addr = TV_HDR_Addr + sizeof(TV_CMD_DESC)*i; #ifdef CONFIG_RTL8672_ATM_QoS vcc->TV.QoS_PCR_Addr = TV_QoS_PCR_Addr + sizeof(TV_CMD_DESC)*i; vcc->TV.QoS_SCR_Addr = TV_QoS_SCR_Addr + sizeof(TV_CMD_DESC)*i; vcc->TV.QoS_SCR1_Addr = TV_QoS_SCR1_Addr+ sizeof(TV_CMD_DESC)*i; #endif #if 0 // 8672 migration vcc->RV.CtlSts_Addr = RV_Ctrl_Addr + sizeof(RV_CMD_DESC)*i; vcc->RV.FDP_Addr = RV_FDP_Addr + sizeof(RV_CMD_DESC)*i; vcc->RV.CKS_Addr = RV_CKS_Addr + sizeof(RV_CMD_DESC)*i; vcc->RV.HDR_Addr = RV_HDR_Addr + sizeof(RV_CMD_DESC)*i; #else /* pkta disabled mode..*/ vcc->RV.CtlSts_Addr = RV_Ctrl_Addr + 16*i; // 8672 from 4 to 5 sizeof(RV_CMD_DESC)*i; vcc->RV.FDP_Addr = RV_FDP_Addr + 16*i; // 8672 from 4 to 5 sizeof(RV_CMD_DESC)*i; vcc->RV.CKS_Addr = RV_CKS_Addr + 16*i; // 8672 from 4 to 5 sizeof(RV_CMD_DESC)*i; vcc->RV.HDR_Addr = RV_HDR_Addr + 16*i; // 8672 from 4 to 5 sizeof(RV_CMD_DESC)*i; #endif vcc->TV.SegmentCRC = 0xFFFFFFFF; vcc->RV.SegmentCRC = 0xFFFFFFFF; vcc->TBE_Flag = TRUE; vcc->created = VC_NOT_CREATED; } first_init=0; cp->vcc[OAM_CH_NO].TV.CtlSts_Addr = TO_Ctrl_Addr; cp->vcc[OAM_CH_NO].TV.FDP_Addr = TO_FDP_Addr; cp->vcc[OAM_CH_NO].RV.CtlSts_Addr = RO_Ctrl_Addr; cp->vcc[OAM_CH_NO].RV.FDP_Addr = RO_FDP_Addr; cp->vcc[OAM_CH_NO].TV.CtlSts = 0; //for reset rx CDOI cp->vcc[OAM_CH_NO].RV.CtlSts = 0; //for reset tx CDOI // Mason Yu. Recieve packet from OAM ch1(not cho) for AutoHunt #ifdef CONFIG_RTL8672 cp->vcc[OAM_CH_NO+1].RV.CtlSts_Addr = RO1_Ctrl_Addr; cp->vcc[OAM_CH_NO+1].RV.FDP_Addr = RO1_FDP_Addr; cp->vcc[OAM_CH_NO+1].RV.CtlSts = 0; //for reset tx CDOI #endif cp->CNFG_Reg = 0x00000000; cp->STS_Reg = 0x00000000; cp->TDFI_Reg = 0x00000000; cp->TBEI_Reg = 0x00000000; cp->RTOI_Reg = 0x00000000; cp->RDAI_Reg = 0x00000000; cp->RBFI_Reg = 0x00000000; cp->tx_channel_on =0x00000000; cp->rx_channel_on =0x00000000; /* Assing FDP to SAR Register */ for(i=0;i<Enable_VC_CNT;i++){ cp->vcc[i].TV.FDP = (UINT32)(cp->TVDesc + i*SAR_TX_RING_SIZE); cp->vcc[i].RV.FDP = (UINT32)(cp->RVDesc + i*SAR_RX_RING_SIZE); cp->vcc[i].TV_Desc = (TV_CMD_DESC *)(cp->TVDesc + i*SAR_TX_RING_SIZE); cp->vcc[i].RV_Desc = (RV_STS_DESC *)(cp->RVDesc + i*SAR_RX_RING_SIZE); } cp->vcc[OAM_CH_NO].TV.FDP = (UINT32)cp->TODesc; cp->vcc[OAM_CH_NO].RV.FDP = (UINT32)cp->RODesc; cp->vcc[OAM_CH_NO].TO_Desc = (TO_CMD_DESC *)cp->TODesc; cp->vcc[OAM_CH_NO].RO_Desc = (RO_STS_DESC *)cp->RODesc; reg(cp->vcc[0].TV.FDP_Addr) = (UINT32)cp->TVDesc; reg(cp->vcc[0].RV.FDP_Addr) = (UINT32)cp->RVDesc; reg(cp->vcc[OAM_CH_NO].TV.FDP_Addr) = (UINT32)cp->TODesc; reg(cp->vcc[OAM_CH_NO].RV.FDP_Addr) = (UINT32)cp->RODesc; // Mason Yu. Recieve packet from OAM ch1(not cho) for AutoHunt #ifdef CONFIG_RTL8672 cp->vcc[OAM_CH_NO+1].RV.FDP = (UINT32)(cp->RODesc+ SAR_RX_RING_SIZE); // 8672 RO1 support cp->vcc[OAM_CH_NO+1].RO_Desc = (RO_STS_DESC *)(cp->RODesc+ SAR_RX_RING_SIZE);// 8672 RO1 support reg(cp->vcc[OAM_CH_NO+1].RV.FDP_Addr) = (UINT32)(cp->RODesc+ SAR_RX_RING_SIZE); // 8672 RO1 support #endif reg(cp->vcc[0].TV.CtlSts_Addr) = cp->vcc[0].TV.CtlSts; reg(cp->vcc[0].TV.SCR_Addr) = cp->vcc[0].TV.SCR; } #if 0 void SetCRCTbl(void ) { UINT32 i,tmp; UINT8 j; for (i=0;i<0x0100;i++) { tmp = i<<0x18; for (j=0;j<8;j++){ if ((tmp & 0x80000000)!=0) tmp = (tmp<<1)^CRC32_POLY; else tmp = tmp<<1; }; CRC32TBL[i]=tmp ; }; } void GenCRC10Table(void) { UINT16 Crc10Accum; int i, j; for (i = 0; i < 256; i++) { Crc10Accum = ((UINT16) i<<2); for (j = 0; j < 8; j++) { if ((Crc10Accum <<= 1) & 0x400) Crc10Accum ^= CRC10_POLY ; } CRC10TBL[i] = Crc10Accum; } } #endif void Enable_Sachem_Loopback(void){ /*REG16(0xb8000c44)=0x4100;*/ /* in FPGA */ #if 0 Idle(200000); REG16(0xb8600c44)=0xBE03; /* in ASIC */ Idle(200000); REG16(0xb8600032)=0x1800; Idle(200000); REG16(0xb8600162)=0x0000; Idle(200000); #else // 8672 Sachem loopback on ATM port 0 Idle(200000); REG16(0xb8600c44)=0x1D00; Idle(200000); REG16(0xb8600032)=0x0800; Idle(200000); REG16(0xb8600c00)=0x2200; Idle(200000); REG16(0xb8600c20)=0xC600; Idle(200000); #endif } void Enable_Sachem_Utopia(void){ Idle(200000); REG16(0xb8000c44)=0xBE03; /* in ASIC */ Idle(200000); } __SWAP void Clear_TBE(int8 i, int8 CDOI) { #if 0// 8672 enable TBE.. mark 8671 old fashion uint32 tmp; tmp=*(unsigned int *)(0xb8300000+0x10*i); //sar_dev->vcc[i].TV.CtlSts |=TBEC; sar_dev->vcc[i].TV.CtlSts &= 0xFF00FFFF; sar_dev->vcc[i].TV.CtlSts |= ((uint32)CDOI)<<CDOI_Offset ; //tmp=sar_dev->vcc[i].TV.CtlSts; sar_dev->vcc[i].TV.CtlSts &=0x9fffffff; sar_dev->vcc[i].TV.CtlSts |= (tmp&0x60000000); /* Next Time we may not want to clear TBE */ //sar_dev->vcc[i].TV.CtlSts &= (~TBEC); #endif if(sar_dev){ // printk("clear CH %d : %x = %x\n", i, sar_dev->vcc[i].TV.CtlSts_Addr, sar_dev->vcc[i].TV.CtlSts|TBEC); WriteD_(sar_dev->vcc[i].TV.CtlSts_Addr, sar_dev->vcc[i].TV.CtlSts|TBEC); } return; } __SWAP void Clear_RBF(sar_private *cp, int8 i, int8 CDOI) { uint32 tmp=0; cp->vcc[i].RV.CtlSts |=RBFC; /* cp->vcc[i].RV.CtlSts &= 0xFF03FFFF; cp->vcc[i].RV.CtlSts |= ((uint32)CDOI)<<CDOI_Offset ; */ tmp=cp->vcc[i].RV.CtlSts; /* Next Time we may not want to clear RBF */ cp->vcc[i].RV.CtlSts &= (~RBFC); WriteD_(cp->vcc[i].RV.CtlSts_Addr, tmp); WriteD_(cp->vcc[i].RV.CtlSts_Addr, tmp); return; } void Cell_Forced_Dropped(sar_private *cp, int8 i){ uint32 tmp=0; cp->vcc[i].RV.CtlSts |=CFD; cp->vcc[i].RV.CtlSts |=RBFC; tmp=cp->vcc[i].RV.CtlSts; /* Next Time we may not want to clear RBF and drop cell */ cp->vcc[i].RV.CtlSts &= (~RBFC); cp->vcc[i].RV.CtlSts &= (~CFD); WriteD_(cp->vcc[i].RV.CtlSts_Addr, tmp); return; } #if 1 //HW CDOI has bug, check desc to fix it __SWAP int GetTxCDOI(sar_private *pDrvCtrl, int ch_no) { int currCDOI; int i=0; //return ((int)((REG32(pDrvCtrl->vcc[ch_no].TV.CtlSts_Addr)&CDOI_Mask)>>CDOI_Offset)&0x3F); currCDOI = pDrvCtrl->vcc[ch_no].TV.desc_pf; while (!((pDrvCtrl->TVDesc[ch_no*SAR_TX_RING_SIZE+currCDOI].CMD)&OWN)) { currCDOI=(currCDOI+1)&(SAR_TX_DESC_NUM-1); if ((i++)>=SAR_TX_DESC_NUM) { //every tx desc wait to send pkt break; }; }; return currCDOI; } #else //HW CDOI has bug, check desc to fix it __SWAP int GetTxCDOI(sar_private *pDrvCtrl, int ch_no) { int currCDOI, tmp; int i=0; currCDOI = pDrvCtrl->vcc[ch_no].TV.desc_pf; tmp= (currCDOI-1)&(SAR_TX_DESC_NUM-1); if (!((pDrvCtrl->TVDesc[ch_no*SAR_TX_RING_SIZE+tmp].CMD)&OWN)) return currCDOI; while (!((pDrvCtrl->TVDesc[ch_no*SAR_TX_RING_SIZE+currCDOI].CMD)&OWN)) { currCDOI=(currCDOI+1)&(SAR_TX_DESC_NUM-1); if ((i++)>=SAR_TX_DESC_NUM) { //every tx desc wait to send pkt break; }; }; return currCDOI; } #endif //HW CDOI has bug, check desc to fix it int8 GetRxCDOI(sar_private *pDrvCtrl, int8 ch_no) { int8 currCDOI; int i=0; //return ((int8)((REG32(pDrvCtrl->vcc[ch_no].RV.CtlSts_Addr)&CDOI_Mask)>>CDOI_Offset)&0x3F); currCDOI = pDrvCtrl->vcc[ch_no].RV.desc_pr; while (!((pDrvCtrl->RVDesc[ch_no*SAR_RX_RING_SIZE+currCDOI].STS)&OWN)) { currCDOI=((currCDOI+1)>=SAR_RX_DESC_NUM)?0:(currCDOI+1); if ((i++)>=SAR_RX_DESC_NUM) { //every desc rx a pkt break; }; }; return currCDOI; } void Reset_Sar(void){ reg(Test_Reg_0)=0x00000001; Idle(200000); reg(Test_Reg_0)=0x00000000; Idle(200000); /*reg(Test_Reg_1)=0x00000000;*/ } void Idle(int32 period){ int32 i; for(i=0;i<(period);i++) asm("#sll\t$0, $0, 1\t\t\t# nop\n\t"); return; } void Dump(uint32 Buffer, int32 size){ int k; if(size%4) size=size+4-(size%4); Buffer=Buffer&0xFFFFFFFC; if ((Buffer&0xF0000000)==0x80000000) Buffer |= Uncache_Mask; printk("Address : Data"); for(k=0;k<(size/4);k++){ if ((k%4)==0) { printk ("\n"); printk("%08X : ",Buffer+k*4); } printk("%08X ", reg(Buffer+k*4)); } printk("\n"); } void Search(uint32 pattern){ uint32 i; printk("search pattern = 0x%08X\n", pattern); for(i=0;i<0x00FFFFFF;i=i+4){ if(reg(0xA0000000+i)==pattern) printk("--------->Match Address=0x%08X\n", 0xA0000000+i); if(!(i&0x000FFFFF)) printk("Now Scan Address=0x%08X\n", 0xA0000000+i); } return; } void SetVpiVci(sar_private *cp, uint8 VPI, uint16 VCI,int8 ch_no){ uint32 HDR=0x00000000; REG32(0xb8303010) |= 0xff; HDR |= ((((uint32) VPI)<< 20) | (((uint32) VCI) << 4)); /* set VPI VCI value to corresponding channel, GFC is always set to 0 */ cp->vcc[ch_no].TV.HDR = HDR; WriteD_(cp->vcc[ch_no].TV.HDR_Addr, cp->vcc[ch_no].TV.HDR); cp->vcc[ch_no].RV.HDR = HDR; WriteD_(cp->vcc[ch_no].RV.HDR_Addr, cp->vcc[ch_no].RV.HDR); cp->vcc[ch_no].vpi = VPI; cp->vcc[ch_no].vci = VCI; REG32(0xb8303010) &= (~0xff); return; } //for ATM QoS mantisa value #ifndef IC8671G static int mantissa_CR[16]={ 0x005, 0x016, 0x028, 0x03C, 0x051, 0x068, 0x080, 0x09B, 0x0B8, 0x0D8, 0x0FA, 0x120, 0x14B, 0x179, 0x1AE, 0x1E8 }; #endif #define PRECISE_NUM 36 struct qos_rate_struct{ unsigned short set_rate; unsigned short actual_rate; }; static struct qos_rate_struct qos_rates[PRECISE_NUM]= {{12,10},{23,20},{46,40},{78,70},{112,100},{160,143},{224,200},{256,250}, {342,301},{396,345},{447,400},{482,449},{511,500},{596,547},{676,598},{748,653}, {800,699},{844,744},{892,800},{936,859},{964,901},{996,955},{1020,1000},{1200,1103}, {1360,1207},{1488,1306},{1600,1406},{1696,1505},{1776,1600},{1856,1706},{1920,1802}, {1983,1910},{2032,2000},{2176,2098},{2240,2133},{2688,2415}}; int reg2cr(int rate){ int mantisa, exp; unsigned int result; exp = (rate&0x00001E00)>>9; mantisa = rate&0x000001FF; result=((512+mantisa)<<exp)>>9; //printk("result:%d %x\n",result,cr2reg(result)); return result; } int check_precise(int rate){ int i; for(i=0;i<PRECISE_NUM;i++){ if(rate==qos_rates[i].actual_rate) return qos_rates[i].set_rate; } return 0; } int findProperRate(int which,int rate){ //which => 1:find upper_rate 0:find lower_rate int i; // out-of-bound conditions. if (rate <= qos_rates[0].actual_rate) { if (0==which) // lower return 1; else return qos_rates[0].set_rate; } else if (rate >= qos_rates[PRECISE_NUM-1].actual_rate) { return qos_rates[PRECISE_NUM-1].set_rate; } for(i=0;i<PRECISE_NUM-1;i++){ //printk("rate:%d %d %d\n",rate,qos_rates[i].actual_rate,qos_rates[i+1].actual_rate); if((rate>=qos_rates[i].actual_rate) && (rate<qos_rates[i+1].actual_rate)){ if (rate == qos_rates[i].actual_rate) { return qos_rates[i].set_rate; } if(which==1)//return upperCR return qos_rates[i+1].set_rate; else//return lowerCR return qos_rates[i].set_rate; } } printk("FIND proper RATE ERROR %d!!!!\n",rate); return 0; } int findActualRate(int which,int rate){ int i; // out-of-bound conditions. if (rate <= qos_rates[0].actual_rate) { if (0==which) // lower return 1; else return qos_rates[0].actual_rate; } else if (rate >= qos_rates[PRECISE_NUM-1].actual_rate) { return qos_rates[PRECISE_NUM-1].actual_rate; } for(i=0;i<PRECISE_NUM-1;i++){ //printk("rate:%d %d %d\n",rate,qos_rates[i].actual_rate,qos_rates[i+1].actual_rate); if((rate>qos_rates[i].actual_rate) && (rate<qos_rates[i+1].actual_rate)){ if(which==1)//return upperCR return qos_rates[i+1].actual_rate; else//return lowerCR return qos_rates[i].actual_rate; } } printk("FIND actual RATE ERROR %d!!!!\n",rate); return 0; } #ifndef IC8671G static int remap_mantisa(int rate) { int mantisa, exp, i; exp = (rate&0x00001E00)>>9; mantisa = rate&0x000001FF; for (i=0;i<16;i++) { if (mantisa>mantissa_CR[i]) continue; break; }; if (i==16) { //next exp level exp++; if (exp>=0x10) { //overflow case exp=0x0F; return ( (exp<<9)|mantissa_CR[15] ); } else return ( (exp<<9)|mantissa_CR[0] ); } else return ( (exp<<9)|mantissa_CR[i] ); } #endif int exp2int(int rate) { int mantisa, exp; exp = (rate&0x00001E00)>>9; mantisa = rate&0x000001FF; return ( (1<<exp)+(1<<exp)*mantisa/512 ); } int get_QoSlowerValue(int upperCR, int lowerCR, int rate, int factor) { int value; int upperVal, lowerVal, rateVal; upperVal = exp2int(upperCR); lowerVal = exp2int(lowerCR); rateVal = exp2int(rate); if ((upperVal == lowerVal)||(upperVal == rateVal)||(lowerVal == rateVal)) { value = -1; //the range is too small } else { value = (upperVal - rateVal)*factor/(upperVal - lowerVal); }; return value; } void set_QoSParameter(sar_private *cp, int8 ch_no, int qos_type, int type, int rate) { #ifndef IC8671G//add for 2.6 and old QoS method int mantisa, exp, j; #else int org_rate,set_rate; #endif int upperCR, lowerCR; int actual_upperCR, actual_lowerCR; cp->vcc[ch_no].QoSinterpolateEnable = 0; cp->vcc[ch_no].numberCR = 0; #ifdef IC8671G org_rate=reg2cr(rate); set_rate=check_precise(org_rate); if(set_rate){ if (type == 0) { cp->vcc[ch_no].QoSinterpolateEnable &= ~1; SetPCR(cp, ch_no, cr2reg(set_rate)); } else SetSCR(cp, ch_no, cr2reg(set_rate)); return; } upperCR=cr2reg(findProperRate(1,org_rate)); lowerCR=cr2reg(findProperRate(0,org_rate)); actual_upperCR=cr2reg(findActualRate(1,org_rate)); actual_lowerCR=cr2reg(findActualRate(0,org_rate)); //printk("upper:%d lower:%d\n",findProperRate(1,org_rate),findProperRate(0,org_rate)); //printk("upper:%d lower:%d\n",findActualRate(1,org_rate),findActualRate(0,org_rate)); #else if ((rate==remap_mantisa(rate))&&(qos_type==QoS_CBR)) { //perfect case for CBR, HW CR match //no need PCR interpolate cp->vcc[ch_no].QoSinterpolateEnable &= ~1; //tell timer to stop interpolate PCR of the channel SetPCR(cp, ch_no, rate); /* Set Peak Cell Rate */ return; } //get cell rate upper & lower bound exp = (rate&0x00001E00)>>9; mantisa = rate&0x000001FF; for (j=0;j<16;j++) { if (mantisa>mantissa_CR[j]) continue; break; }; if (j==16) { upperCR = ( ((exp+1)<<9)|mantissa_CR[0] ); lowerCR = ( (exp<<9)|mantissa_CR[15] ); } else if (j==0) { upperCR = ( (exp<<9)|mantissa_CR[0]); lowerCR = ( ((exp-1)<<9)|mantissa_CR[15]); } else { upperCR = ( (exp<<9)|mantissa_CR[j]); lowerCR = ( (exp<<9)|mantissa_CR[j-1]); }; actual_upperCR=upperCR; actual_lowerCR=lowerCR; #endif //if(cp->vcc[ch_no].hwPRIO==3){ // lowerCR=upperCR; // actual_lowerCR=actual_upperCR; //} //get cell rate swap ratio, the swap resolution is depend on system ticks(100Hz) if (type==0) { //set PCR cp->vcc[ch_no].hwCR[UPPER_PCR] = upperCR; cp->vcc[ch_no].hwCR[LOWER_PCR] = lowerCR; cp->vcc[ch_no].hwCRtickData[LOWER_PCR] = get_QoSlowerValue(actual_upperCR, actual_lowerCR, rate, HZ); if (cp->vcc[ch_no].hwCRtickData[LOWER_PCR]==-1) { if (qos_type==QoS_CBR) { //range too small, do not swap, set to upper bound SetPCR(cp, ch_no, upperCR); return; } else { cp->vcc[ch_no].hwCRtickData[LOWER_PCR] = 0; } }; cp->vcc[ch_no].hwCRtickData[UPPER_PCR] = HZ - cp->vcc[ch_no].hwCRtickData[LOWER_PCR]; cp->vcc[ch_no].hwCRtick[UPPER_PCR] = 0; cp->vcc[ch_no].hwCRtick[LOWER_PCR] = 0; cp->vcc[ch_no].QoSinterpolateEnable |= 1; //tell timer to start interpolate PCR of the channel cp->vcc[ch_no].numberCR = 2; } else { //set SCR cp->vcc[ch_no].hwCR[UPPER_SCR] = upperCR; cp->vcc[ch_no].hwCR[LOWER_SCR] = lowerCR; cp->vcc[ch_no].hwCRtickData[LOWER_SCR] = get_QoSlowerValue(actual_upperCR, actual_lowerCR, rate, HZ); if (cp->vcc[ch_no].hwCRtickData[LOWER_SCR]==-1) { //range too small, do not swap, set to upper bound cp->vcc[ch_no].hwCRtickData[LOWER_SCR] = 0; } cp->vcc[ch_no].hwCRtickData[UPPER_SCR] = HZ - cp->vcc[ch_no].hwCRtickData[LOWER_SCR]; cp->vcc[ch_no].hwCRtick[UPPER_SCR] = 0; cp->vcc[ch_no].hwCRtick[LOWER_SCR] = 0; cp->vcc[ch_no].QoSinterpolateEnable |= 2; //tell timer to start interpolate SCR of the channel cp->vcc[ch_no].numberCR = 4; // andrew, disable PCR swapping cp->vcc[ch_no].hwCRtickData[UPPER_PCR] = 0; cp->vcc[ch_no].hwCRtickData[LOWER_PCR] = 0; }; } void SetQoS(sar_private *cp, int8 i, int8 QoS) { /* Set Scheduler Option */ uint32 SSL = 0 ; SSL |= (((uint32) QoS)<< 29); cp->vcc[i].QoS.Type=QoS; cp->vcc[i].TV.CtlSts &= 0x9FFFFFFF; cp->vcc[i].TV.CtlSts |= SSL; WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); } //#define CR_OFFSET 1 #define CR_OFFSET 2 // andrew, change to 2 so PCR960,SCR959 case will not lost cell. char PrioChannelNum[5]={0,0,0,0,0}; #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif void SetQoSInterpolate(sar_private *cp, int8 i, struct SAR_IOCTL_CFG *cfg) { #if SAR_INTERPOLATE int pkt_exp=1,j; #ifndef IC8671G int val, timeRatioPCR; #endif #endif //cli(); local_irq_disable(); //2.6 migration SetQoS(cp, i, cfg->QoS.Type); /* Set Scheduler Option */ SetPCR(cp, i, cfg->QoS.PCR); SetSCR(cp, i, cfg->QoS.SCR); SetMBS(cp, i, cfg->QoS.MBS); #if SAR_INTERPOLATE SetQoS(cp, i, cfg->QoS.Type); /* Set Scheduler Option */ SetPCR(cp, i, cfg->QoS.PCR+CR_OFFSET); SetSCR(cp, i, 0); SetMBS(cp, i, 0); //calculate (cells per second*512) if(cfg->QoS.Type==QoS_CBR){ for(j=0;j<((cfg->QoS.PCR&0x00001E00)>>9);j++){ pkt_exp*=2; } cp->vcc[i].hwCellPerSecond=pkt_exp*(512+(cfg->QoS.PCR&0x000001FF)); //printk("channel:%d cells per second:%d\n",i,cp->vcc[i].hwCellPerSecond); cp->vcc[i].hwPRIO=1; PrioChannelNum[cp->vcc[i].hwPRIO]++; cp->vcc[i].hwTxCellCount=0; } else if(cfg->QoS.Type==QoS_rtVBR ||cfg->QoS.Type==QoS_nrtVBR){ for(j=0;j<((cfg->QoS.PCR&0x00001E00)>>9);j++){ pkt_exp*=2; } cp->vcc[i].hwCellPerSecond=pkt_exp*(512+(cfg->QoS.PCR&0x000001FF)); //printk("channel:%d cells per second:%d\n",i,cp->vcc[i].hwCellPerSecond); if(cfg->QoS.Type==QoS_rtVBR) cp->vcc[i].hwPRIO=2; else cp->vcc[i].hwPRIO=3; PrioChannelNum[cp->vcc[i].hwPRIO]++; cp->vcc[i].hwTxCellCount=0; } else{ for(j=0;j<((cfg->QoS.PCR&0x00001E00)>>9);j++){ pkt_exp*=2; } cp->vcc[i].hwCellPerSecond=pkt_exp*(512+(cfg->QoS.PCR&0x000001FF)); //cp->vcc[i].hwCellPerSecond=0; cp->vcc[i].hwPRIO=4; PrioChannelNum[cp->vcc[i].hwPRIO]++; cp->vcc[i].hwTxCellCount=0; } switch (cfg->QoS.Type) { case QoS_rtVBR: case QoS_nrtVBR: //0921 shlee cfg->QoS.Type &= 1; //remap priority SetQoS(cp, i, cfg->QoS.Type); /* Set Scheduler Option */ SetMBS(cp, i, cfg->QoS.MBS); //0921 shlee set_QoSParameter(cp, i, QoS_rtVBR, 0, cfg->QoS.PCR+CR_OFFSET); set_QoSParameter(cp, i, QoS_rtVBR, 1, cfg->QoS.SCR+CR_OFFSET); SetSCR(cp, i, cp->vcc[i].hwCR[UPPER_SCR]); //0922 shlee #ifndef IC8671G //recalculate time ratio val = exp2int(cfg->QoS.SCR); if ( cfg->QoS.MBS>= val) //use PCR to send all the time timeRatioPCR = 100; else { timeRatioPCR = cfg->QoS.MBS*100/val+1; if (timeRatioPCR>100) timeRatioPCR=100; } cp->vcc[i].hwCRtickData[LOWER_SCR] = cp->vcc[i].hwCRtickData[LOWER_SCR]*(100-timeRatioPCR)/100; cp->vcc[i].hwCRtickData[UPPER_SCR] = cp->vcc[i].hwCRtickData[UPPER_SCR]*(100-timeRatioPCR)/100; cp->vcc[i].hwCRtickData[LOWER_PCR] = cp->vcc[i].hwCRtickData[LOWER_PCR]*timeRatioPCR/100; cp->vcc[i].hwCRtickData[UPPER_PCR] = 100 - cp->vcc[i].hwCRtickData[LOWER_PCR] - cp->vcc[i].hwCRtickData[LOWER_SCR] - cp->vcc[i].hwCRtickData[UPPER_SCR]; #endif break; case QoS_CBR: set_QoSParameter(cp, i, QoS_CBR, 0, cfg->QoS.PCR+CR_OFFSET); break; case QoS_UBR: set_QoSParameter(cp, i, QoS_CBR, 0, cfg->QoS.PCR+CR_OFFSET); default: break; }; #endif //sti(); local_irq_enable(); // 2.6 migration return; } char use_low_rate=0; char use_low_rate_UBR=0; char ubr_low_rate=0; void swapCR(sar_private *cp, int ch_no) { sar_atm_vcc *vcc_dev; int idx; vcc_dev = &cp->vcc[ch_no]; if (vcc_dev->numberCR==0) return; idx=vcc_dev->creditCR; if (!(vcc_dev->hwCRtick[idx])) { ////timeout, change to next CR do { idx=(idx+1)%(vcc_dev->numberCR); } while (!(vcc_dev->hwCRtickData[idx])); if (vcc_dev->hwCRtickData[idx]) vcc_dev->hwCRtick[idx] = vcc_dev->hwCRtickData[idx]-1; if(use_low_rate && vcc_dev->hwPRIO==2) SetPCR(cp, ch_no, vcc_dev->hwCR_low[idx]); //traffic more than line rate, decrease else{ if(vcc_dev->hwPRIO!=4 || ubr_low_rate==0) //not UBR or UBR normal mode { //0921 shlee if(vcc_dev->hwPRIO == 2 || vcc_dev->hwPRIO==3) { //0921 shlee for rtVBR //SetSCR(cp, ch_no, vcc_dev->hwCR[UPPER_SCR]); //SetPCR(cp, ch_no, vcc_dev->hwCR[idx]); // andrew, interpolate using SCR so we can have burst SetPCR(cp, ch_no, vcc_dev->hwCR[UPPER_PCR]); SetSCR(cp, ch_no, vcc_dev->hwCR[idx]); } else { //0921 shlee SetPCR(cp, ch_no, vcc_dev->hwCR[idx]); } } //0921 shlee } vcc_dev->creditCR = idx; //printk("CCR%d:%d %08x %d\n", ch_no, jiffies, vcc_dev->hwCR[idx], vcc_dev->hwCRtickData[idx]); } else vcc_dev->hwCRtick[idx]--; } extern int total_pvc_number; int CurrentUBR=0; extern char checkRateEnable; extern T_LinkSpeed LINE_rate; char rtvbr_improve=0; #define rtvbr_margin 30 #define ubr_margin 10 #if 0// SAR_CHECK_RC_NEW static unsigned int rate_UBR = 1; #define NUM_HISTORY 16 static unsigned int rate_history[NUM_HISTORY]; static int rate_index = 0, debugMsg = 0; void check_CR(sar_private *cp){ sar_atm_vcc *vcc_dev; int totalAvaiable = 0, totalActualUse = 0, nrtVBRActualUse = 0; int totalUsable = 0, targetRate; int totalUBR = 0, totalHwCR = 0; unsigned int pool = 0; int i; if (!debugMsg) { debugMsg = 1; } printk("**New Check CR\n"); //get line rate if((sar_dev==NULL || checkRateEnable==0) || (PrioChannelNum[4] == 0)) goto THE_END; totalAvaiable=LINE_rate.upstreamRate*1000/(53*8); if ((totalAvaiable==0) ) goto THE_END; // compute the actual usage of (CBR+rtVBR+nrtVBR) //printk("==>"); for (i=0;i<total_pvc_number;i++) { //printk("(%d,%d,%d)",i, cp->vcc[i].hwPRIO, cp->vcc[i].hwTxCellCount); if(cp->vcc[i].hwPRIO<=3) totalActualUse+=cp->vcc[i].hwTxCellCount; if(cp->vcc[i].hwPRIO==3) nrtVBRActualUse+=cp->vcc[i].hwTxCellCount; else if (cp->vcc[i].hwPRIO==4) totalUBR+=cp->vcc[i].hwCellPerSecond; //totalHwCR +=cp->vcc[i].hwCellPerSecond; } //printk("\n\n"); // covert from /500ms to /1sec. totalActualUse /= 256; nrtVBRActualUse /=256; totalUBR /=512; // total configured rate. totalHwCR /=512; // Other priority already exceed to limit, set UBR to lowest possible rate. if (totalActualUse >= totalAvaiable) { targetRate = 1; } else { totalUsable = totalAvaiable - totalActualUse; targetRate = totalUsable / PrioChannelNum[4]; //targetRate = (targetRate < cp->vcc[i].hwCellPerSecond/512) ? targetRate : (cp->vcc[i].hwCellPerSecond/512); } if (nrtVBRActualUse && (totalUsable <= totalUBR)) { ubr_low_rate = 1; // disable swapping. printk("LowR %d, %d, %d\n", nrtVBRActualUse, totalUsable, totalUBR); } else { ubr_low_rate = 0; // enable rate swapping. } #if 1 // adaptive increase UBR rate; if (targetRate >= rate_UBR) { rate_UBR = (rate_UBR + targetRate * 5) / 6; } else { rate_UBR = targetRate; } #endif //printk("[%d %d %d %d %d]\n", totalAvaiable, totalActualUse, targetRate, rate_UBR, nrtVBRActualUse); // debug only #if 0 rate_history[rate_index % NUM_HISTORY] = rate_UBR; rate_index ++; if ((rate_index % NUM_HISTORY) == 0) { printk("index %d\n", rate_index); printk("%d %d %d %d %d %d %d %d\n", rate_history[0], rate_history[1], rate_history[2], rate_history[3], rate_history[4], rate_history[5], rate_history[6], rate_history[7]); printk("%d %d %d %d %d %d %d %d\n", rate_history[8], rate_history[9], rate_history[10], rate_history[11], rate_history[12], rate_history[13], rate_history[14], rate_history[15]); } #endif //pool = totalUsable; for(i=0;i<total_pvc_number;i++){ unsigned int setRate, vcRate; if(cp->vcc[i].hwPRIO!=4) continue; vcRate = (cp->vcc[i].hwCellPerSecond/512); if (rate_UBR > vcRate) { pool += (rate_UBR - vcRate); // save # of unused bandwidth setRate = vcRate; } else { setRate = rate_UBR; } //printk("rate = (%d, %d) %d\n", i, cp->vcc[i].hwCellPerSecond, setRate); SetPCR(cp, i, cr2reg ( findProperRate(0, setRate) )); }; // distribute unused bandwidth to UBR for(i=0;i<total_pvc_number;i++){ unsigned int setRate, vcRate; if (pool == 0) break; if(cp->vcc[i].hwPRIO!=4) continue; vcRate = (cp->vcc[i].hwCellPerSecond/512); if (vcRate < rate_UBR) continue; if ( (vcRate - rate_UBR) > pool) { setRate = rate_UBR + pool; pool = 0; } else { setRate = vcRate; pool -= (vcRate - rate_UBR); } SetPCR(cp, i, cr2reg ( findProperRate(0, setRate) )); }; THE_END: // reset the counter for (i=0;i<total_pvc_number;i++) { cp->vcc[i].hwTxCellCount=0; } } #endif #ifdef IC8671G void check_CR(sar_private *cp){ // sar_atm_vcc *vcc_dev; int Hw_UpRate,Hw_UpRate2,tx_cell; // int HwThroughput1=0,HwThroughput2=0; int HwThroughput2=0; int i;//,j,k; //int mantisa, exp, j; // int lowerCR,adjust_ubr, adjust_total_load=0; int adjust_ubr, adjust_total_load=0; // int rtvbr_send=0,nrtVBR_send=0,cbr_send=0; int nrtVBR_send=0; // unsigned int adjust_rtVBR; int lowest_ubr=0; int reserveHwRate; //rate reserved after cbr & rt-vbr int Hw_UpRate_rtvbr; int proper_rate; //get line rate if(sar_dev==NULL || checkRateEnable==0) return; tx_cell=LINE_rate.upstreamRate*1000/(53*8); if (tx_cell==0) return; Hw_UpRate=tx_cell*512/2; //line rate, check every 500ms reserveHwRate=Hw_UpRate*2; //total cells that upstream can support Hw_UpRate2=Hw_UpRate; Hw_UpRate_rtvbr=Hw_UpRate; //printk("US:%d\n",rate.upstreamRate); //for nrt-VBR and UBR for (i=0;i<total_pvc_number;i++) { HwThroughput2+=cp->vcc[i].hwTxCellCount; if(cp->vcc[i].hwPRIO==3) nrtVBR_send+=cp->vcc[i].hwTxCellCount; } //printk("HwThroughput2:%d Hw_UpRate2:%d nrtVBR:%d\n",HwThroughput2,Hw_UpRate2,nrtVBR_send); if(((HwThroughput2 > Hw_UpRate2) || ubr_low_rate==1) && nrtVBR_send >0 && PrioChannelNum[4]>0){ //decrease UBR rate if(use_low_rate_UBR==0){ //decrease ubr rate from now on //printk("Decrease UBR rate!\n"); for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==4){ //set to low rate adjust_ubr=cr2reg(1); CurrentUBR=1; //printk("set ubr %d %x\n",i,adjust_ubr); SetPCR(cp,i,adjust_ubr); } } use_low_rate_UBR=1; ubr_low_rate=1; } else{ //adjust ubr rate //Hw_UpRate2*=2; //printk("Adjust UBR rate!\n"); for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO<4){ adjust_total_load+=cp->vcc[i].hwTxCellCount; }else{ if(lowest_ubr==0) lowest_ubr=cp->vcc[i].hwCellPerSecond; else if(cp->vcc[i].hwCellPerSecond<lowest_ubr) lowest_ubr=cp->vcc[i].hwCellPerSecond; } } //printk("adjust_total_load:%d\n",adjust_total_load); Hw_UpRate2=(Hw_UpRate2-adjust_total_load)*2; if(Hw_UpRate2<=0) Hw_UpRate2=0; CurrentUBR=((Hw_UpRate2/PrioChannelNum[4])/512+CurrentUBR)/2; if(CurrentUBR<1) CurrentUBR=1; if(CurrentUBR>40){ proper_rate=findProperRate(0, CurrentUBR - ubr_margin); //adjust_ubr=findActualRate(0, proper_rate); adjust_ubr= cr2reg(proper_rate); } else{ adjust_ubr= cr2reg(1); } for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==4){ //printk("adjust ubr channel %d %x\n",i,adjust_ubr); SetPCR(cp,i,adjust_ubr); } } //SetPCR(cp,i,adjust_ubr); } } else if(ubr_low_rate==1 && nrtVBR_send==0) { //printk("Normal UBR Rate!\n"); //UBR normal rate for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==4){ CurrentUBR=cp->vcc[i].hwCellPerSecond/512; adjust_ubr=cr2reg(cp->vcc[i].hwCellPerSecond/512); //printk("channel %d ubr normal rate! %x\n",i,adjust_ubr); SetPCR(cp,i,adjust_ubr); } } use_low_rate_UBR=0; ubr_low_rate=0; } for (i=0;i<total_pvc_number;i++) { cp->vcc[i].hwTxCellCount=0; } } #else #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif void check_CR(sar_private *cp){ int Hw_UpRate,Hw_UpRate2,tx_cell; int HwThroughput1=0,HwThroughput2=0; int i; int mantisa, exp, j; int lowerCR,adjust_ubr, adjust_total_load=0; int rtvbr_send=0,nrtVBR_send=0,cbr_send=0; int adjust_rtVBR; int lowest_ubr=0; int reserveHwRate; //rate reserved after cbr & rt-vbr int Hw_UpRate_rtvbr; //get line rate if(sar_dev==NULL || checkRateEnable==0) return; tx_cell=LINE_rate.upstreamRate*1000/(53*8); if (tx_cell==0) return; Hw_UpRate=tx_cell*512/2; //line rate, check every 500ms reserveHwRate=Hw_UpRate*2; //total cells that upstream can support Hw_UpRate2=Hw_UpRate; Hw_UpRate_rtvbr=Hw_UpRate; //printk("US:%d\n",rate.upstreamRate); //check pkts received in CBR,rt-VBR channels for (i=0;i<total_pvc_number;i++) { if(cp->vcc[i].hwPRIO==1 || cp->vcc[i].hwPRIO==2){ HwThroughput1+=cp->vcc[i].hwTxCellCount; reserveHwRate-=cp->vcc[i].hwCellPerSecond;//for rt-vbr issue } if(cp->vcc[i].hwPRIO==1) cbr_send+=cp->vcc[i].hwTxCellCount; if(cp->vcc[i].hwPRIO==2) rtvbr_send+=cp->vcc[i].hwTxCellCount; } if(rtvbr_improve==0 && use_low_rate==1){ //improve rtvbr rate Hw_UpRate_rtvbr*=2; Hw_UpRate_rtvbr=Hw_UpRate_rtvbr-(cbr_send*2)-rtvbr_margin; if(Hw_UpRate_rtvbr<=0) Hw_UpRate_rtvbr=1; adjust_rtVBR=cr2reg(Hw_UpRate_rtvbr/(PrioChannelNum[2]*512)-1); exp = (adjust_rtVBR&0x00001E00)>>9; mantisa = adjust_rtVBR&0x000001FF; for (j=0;j<16;j++) { if (mantisa>mantissa_CR[j]) continue; break; }; if (j==16) { lowerCR = ( (exp<<9)|mantissa_CR[15] ); } else if (j==0) { lowerCR = ( ((exp-1)<<9)|mantissa_CR[15]); } else { lowerCR = ( (exp<<9)|mantissa_CR[j-1]); }; for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==2){//rt-VBR cp->vcc[i].hwCR_low[0]=lowerCR; cp->vcc[i].hwCR_low[1]=lowerCR; cp->vcc[i].hwCR_low[2]=lowerCR; cp->vcc[i].hwCR_low[3]=lowerCR; //printk("improve rtvbr set channel %d to %x\n",i,lowerCR); } } rtvbr_improve=1; } if(HwThroughput1 > Hw_UpRate && use_low_rate==0 && cbr_send>0 && PrioChannelNum[2]!=0 && reserveHwRate<0){ //if reserveHwRate>0, no need to decrease PCR for rt-vbr //printk("decrease VBR rate!!HwThroughput:%d HW_UpRate:%d\n",HwThroughput1,Hw_UpRate); //decrease rt-VBR rate Hw_UpRate*=2; //for 1 sec. for (i=0;i<total_pvc_number;i++) { if(cp->vcc[i].hwPRIO==1)//CBR Hw_UpRate-=cp->vcc[i].hwCellPerSecond; } //share throughput to rt-VBR //printk("adjust cell rate:%d\n",Hw_UpRate); if(Hw_UpRate<=0) Hw_UpRate=10; adjust_rtVBR=cr2reg(Hw_UpRate/(PrioChannelNum[2]*512)-1); //get cell rate upper & lower bound exp = (adjust_rtVBR&0x00001E00)>>9; mantisa = adjust_rtVBR&0x000001FF; for (j=0;j<16;j++) { if (mantisa>mantissa_CR[j]) continue; break; }; if (j==16) { lowerCR = ( (exp<<9)|mantissa_CR[15] ); } else if (j==0) { lowerCR = ( ((exp-1)<<9)|mantissa_CR[15]); } else { lowerCR = ( (exp<<9)|mantissa_CR[j-1]); }; for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==2){//rt-VBR cp->vcc[i].hwCR_low[0]=lowerCR; cp->vcc[i].hwCR_low[1]=lowerCR; cp->vcc[i].hwCR_low[2]=lowerCR; cp->vcc[i].hwCR_low[3]=lowerCR; //printk("CR1 set channel %d to %x\n",i,lowerCR); } } use_low_rate=1; }else if(cbr_send==0){ rtvbr_improve=0; use_low_rate=0; } //for nrt-VBR and UBR for (i=0;i<total_pvc_number;i++) { HwThroughput2+=cp->vcc[i].hwTxCellCount; if(cp->vcc[i].hwPRIO==3) nrtVBR_send+=cp->vcc[i].hwTxCellCount; } //printk("HwThroughput2:%d Hw_UpRate2:%d nrtVBR:%d\n",HwThroughput2,Hw_UpRate2,nrtVBR_send); if(((HwThroughput2 > Hw_UpRate2) || ubr_low_rate==1) && nrtVBR_send >0 && PrioChannelNum[4]>0){ //decrease UBR rate if(use_low_rate_UBR==0){ //decrease ubr rate from now on //printk("Decrease UBR rate!\n"); for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==4){ if(use_low_rate==1) adjust_ubr=cr2reg(5); else adjust_ubr=cr2reg(cp->vcc[i].hwCellPerSecond/(512*16)); CurrentUBR=cp->vcc[i].hwCellPerSecond/(512*16); //printk("set ubr %d %x\n",i,adjust_ubr); SetPCR(cp,i,adjust_ubr); } } use_low_rate_UBR=1; ubr_low_rate=1; } else{ //adjust ubr rate //Hw_UpRate2*=2; //printk("Adjust UBR rate!\n"); for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO<4){ adjust_total_load+=cp->vcc[i].hwTxCellCount; }else{ if(lowest_ubr==0) lowest_ubr=cp->vcc[i].hwCellPerSecond; else if(cp->vcc[i].hwCellPerSecond<lowest_ubr) lowest_ubr=cp->vcc[i].hwCellPerSecond; } } //printk("adjust_total_load:%d\n",adjust_total_load); Hw_UpRate2=(Hw_UpRate2-adjust_total_load)*2; if(Hw_UpRate2<=0) Hw_UpRate2=5; CurrentUBR=((Hw_UpRate2/PrioChannelNum[4])/512+CurrentUBR)/2; if(CurrentUBR<lowest_ubr/(512*16)) CurrentUBR=lowest_ubr/(512*16); if(use_low_rate==1) adjust_ubr= cr2reg(5); else adjust_ubr= cr2reg(CurrentUBR - ubr_margin); for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==4){ //printk("adjust ubr channel %d %x\n",i,adjust_ubr); SetPCR(cp,i,adjust_ubr); } } //SetPCR(cp,i,adjust_ubr); } } else if(ubr_low_rate==1 && nrtVBR_send==0) { //printk("Normal UBR Rate!\n"); //UBR normal rate for(i=0;i<total_pvc_number;i++){ if(cp->vcc[i].hwPRIO==4){ CurrentUBR=cp->vcc[i].hwCellPerSecond/512; adjust_ubr=cr2reg(cp->vcc[i].hwCellPerSecond/512); //printk("channel %d ubr normal rate! %x\n",i,adjust_ubr); SetPCR(cp,i,adjust_ubr); } } use_low_rate_UBR=0; ubr_low_rate=0; } for (i=0;i<total_pvc_number;i++) { cp->vcc[i].hwTxCellCount=0; } } #endif //call by timer interrupt to interpolate Cell Rate int Int_Count=0; //for small pkt int check_small_pkt=0; int sec_check=0; int sec_pkt=0; extern int rx_small_pkt; int change_BitSwap=0; extern void EnterHeavyTraffic(void); extern void ExitHeavyTraffic(void); #ifdef CONFIG_RTL867X_PACKET_PROCESSOR extern unsigned int SAR_TDFI_get_and_clear(void); extern void clearSARvtxBuffer(int port, struct net_device *dev); #endif #if defined( CONFIG_EXT_SWITCH) && !defined(CONFIG_ETHWAN) static int nicLinkStatCheckCnt=0; #endif void ATM_QoS_Interpolate(void) { int i=0; unsigned int TDFI_Value; if (sar_dev==NULL) return; #ifdef FAST_ROUTE_Test // Kaohj -- pass frame up each tick for MAC learning // called every tick fast_frame_up = 1; #endif #ifdef CONFIG_RTL867X_PACKET_PROCESSOR if ((TDFI_Value = SAR_TDFI_get_and_clear())) { for(i=0;i<Enable_VC_CNT ;i++){ if(!(TDFI_Value&((unsigned int)0x00000001<<i))) continue; clearSARvtxBuffer(i, sar_dev->vcc[i].dev); } } if ((TDFI_Value = reg(SAR_TDFI_Addr))) { reg(SAR_TDFI_Addr) = (TDFI_Value & ((UINT32)0x00000001<<HW_OAM_CH_NO)); // write to clear TDFI reg if (sar_dev->TDFI_Reg&((UINT32)0x00000001<<HW_OAM_CH_NO)) ClearTxBuffer(sar_dev, OAM_CH_NO); } #else if ((TDFI_Value = reg(SAR_TDFI_Addr))) { reg(SAR_TDFI_Addr) = TDFI_Value; // write to clear TDFI reg for(i=0;i<Enable_VC_CNT ;i++){ if(!(TDFI_Value&((unsigned int)0x00000001<<i))) continue; ClearTxBuffer(sar_dev, i); } if (sar_dev->TDFI_Reg&((UINT32)0x00000001<<HW_OAM_CH_NO)) ClearTxBuffer(sar_dev, OAM_CH_NO); } #endif #if defined( CONFIG_EXT_SWITCH) && !defined(CONFIG_ETHWAN) if (nicLinkStatCheckCnt++ >= 50 )//check link status per port every 500ms { static unsigned char portstatus[SW_PORT_NUM]={0}; unsigned short regValue; static unsigned int swport=0; extern int virt2phy[5]; extern unsigned char eth_close; extern int sw_link_stable_times[SW_PORT_NUM]; extern int sw_link_checked[SW_PORT_NUM]; extern unsigned int swtype; extern struct timer_list sw_check_timer; extern void miiar_read(unsigned char phyaddr, unsigned char regaddr, unsigned short *value); nicLinkStatCheckCnt = 0; if (eth_close) goto OMITNICLINKCHECK; if (0x5988 == swtype) { miiar_read(virt2phy[swport], 1, ®Value); //If the link had ever failed, this bit will be 0 until after reading this bit again. miiar_read(virt2phy[swport], 1, ®Value); if (((regValue>>2)&0x01) != portstatus[swport]) { if ((regValue>>2)&0x01) printk("port%d is link up\n", swport); else printk("port%d is link down\n", swport); portstatus[swport] = ((regValue>>2)&0x01); sw_link_stable_times[i]=0; sw_link_checked[i]=0; if(!timer_pending(&sw_check_timer)){ mod_timer(&sw_check_timer, jiffies + 1*HZ); } } } if (++swport >= SW_PORT_NUM) swport = 0; } OMITNICLINKCHECK: #endif #if SAR_INTERPOLATE #if SAR_CHECK_RC if(Int_Count==50){ //check every 0.5 sec. check_CR(sar_dev); Int_Count=0; } else{ Int_Count++; } #endif for (i=0;i<total_pvc_number;i++) { if (sar_dev->vcc[i].QoSinterpolateEnable) swapCR(sar_dev, i); }; #endif #if 1 if(check_small_pkt==10){ //if rx 4000 small pkts in 0.5 min, disable bitswap if(rx_small_pkt>800){ if( !change_BitSwap ){ //if(tr069_pid) tr069_reduce_nice(); EnterHeavyTraffic(); change_BitSwap=1; } } if(sec_check==10){ if(sec_pkt<1000) { if( change_BitSwap ){ //if(tr069_pid) tr069_increase_nice(); ExitHeavyTraffic(); change_BitSwap=0; } } sec_check = 0; sec_pkt = 0; } else { sec_check++; sec_pkt+=rx_small_pkt; } rx_small_pkt=0; check_small_pkt=0; } else{ check_small_pkt++; } #endif } #if 0 //def CONFIG_VDSL /*ATM over VDSL test*/ /*gDecidedMode: MODE_VDSL2=0x100000*/ extern int gDecidedMode; #endif /*CONFIG_VDSL*/ void SetPCR(sar_private *cp, int8 i, uint16 PCR) { /* Set Peak Cell Rate */ cp->vcc[i].TV.CtlSts &= 0xFFFF0000; #ifndef CONFIG_RTL8672_ATM_QoS cp->vcc[i].QoS.PCR=PCR; cp->vcc[i].TV.CtlSts |= (uint32)PCR; #else cp->vcc[i].QoS.PCR=PCR; cp->vcc[i].TV.CtlSts |= PCR_Period; #if 0 //def CONFIG_VDSL if(gDecidedMode&0x100000) /*MODE_VDSL2*/ WriteD_(cp->vcc[i].TV.QoS_PCR_Addr, (uint32)0x800); else #endif /*CONFIG_VDSL*/ //Set PCR in Check_Line_Rate_PCR() //WriteD_(cp->vcc[i].TV.QoS_PCR_Addr, (uint32)QoS_CLK*PCR_Period/PCR); Check_Line_Rate_PCR(cp, i); #endif //printk("cp->vcc[i].TV.CtlSts %x\n", cp->vcc[i].TV.CtlSts); WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); return; } void SetSCR(sar_private *cp, int8 i, uint16 SCR) { REG32(0xb8303010) |= 0xff; /* Set Sustainable Cell Rate */ cp->vcc[i].TV.SCR &= 0x0000FFFF; #ifndef CONFIG_RTL8672_ATM_QoS cp->vcc[i].QoS.SCR=SCR; cp->vcc[i].TV.SCR |= ((uint32)SCR)<<16; #else cp->vcc[i].QoS.SCR=SCR_Period; cp->vcc[i].TV.SCR |= SCR_Period<<16; if(SCR) //don't have to set SCR value in CBR,UBR mode WriteD_(cp->vcc[i].TV.QoS_SCR_Addr, (uint32)QoS_CLK*SCR_Period/SCR); #endif //printk("cp->vcc[i].TV.SCR %x\n", cp->vcc[i].TV.SCR); WriteD_(cp->vcc[i].TV.SCR_Addr, cp->vcc[i].TV.SCR); REG32(0xb8303010) &= (~0xff); return; } #ifdef CONFIG_RTL8672_ATM_QoS void SetMBS(sar_private *cp, int8 i, uint16 MBS) #else void SetMBS(sar_private *cp, int8 i, uint8 MBS) #endif { REG32(0xb8303010) |= 0xff; /* Set Max Burst Size */ cp->vcc[i].QoS.MBS=MBS; #ifndef CONFIG_RTL8672_ATM_QoS cp->vcc[i].TV.SCR &= 0xFFFF00FF; cp->vcc[i].TV.SCR |= ((uint32)MBS)<<8; //printk("cp->vcc[i].TV.SCR %x\n", cp->vcc[i].TV.SCR); WriteD_(cp->vcc[i].TV.SCR_Addr, cp->vcc[i].TV.SCR); #else /* RTL8672 corner case: the function of ATM QoS will fail if MBS <=1 */ if( MBS <= 1) MBS = 2; WriteD_(cp->vcc[i].TV.QoS_SCR1_Addr, (uint32)MBS); #endif REG32(0xb8303010) &= (~0xff); return; } uint8 GetCRD(sar_private *cp, int8 i) { uint8 CRD; REG32(0xb8303010) |= 0xff; /* Set Cell Credit */ cp->vcc[i].TV.SCR=ReadD_(cp->vcc[i].TV.SCR_Addr); CRD = (uint8)(cp->vcc[i].TV.SCR&0x000000FF); cp->vcc[i].QoS.CRD=CRD; REG32(0xb8303010) &= (~0xff); return CRD; } uint32 ReadD_ (uint32 address){ if(!address){ return 0; } address = address & 0xFFFFFFFC; if ((address&0xF0000000)==0x80000000) address |= Uncache_Mask; return reg(address); } void WriteD_ (uint32 address, uint32 data){ if(address){ address = address & 0xFFFFFFFC; if ((address&0xF0000000)==0x80000000) address |= Uncache_Mask; reg(address)=data; } return; } void Set1 (uint32 address, int8 index ){ if ((address&0xF0000000)==0x80000000) address += 0x20000000; address &= 0xFFFFFFFC; reg(address) = (reg(address)|(((uint32)0x00000001) << index)); return; } void Reset1 (uint32 address, int8 index ){ if ((address&0xF0000000)==0x80000000) address += 0x20000000; address &= 0x0FFFFFFC; reg(address) &= (0xFFFFFFFF^(((uint32)0x00000001) << index)); return; } uint8 Test1 (uint32 address, int8 index ){ if ((address&0xF0000000)==0x80000000) address += 0x20000000; address &= 0xFFFFFFFC; if(reg(address)&(((uint32)0x00000001) << index)) return TRUE; else return FALSE; } int32 S2i(uint8 * str_P) { uint32 val; if ( (str_P[0] == '0') && (str_P[1] == 'x') ) { str_P += 2; for (val = 0; *str_P; str_P++) { val *= 16; if ( '0' <= *str_P && *str_P <= '9' ) val += *str_P - '0'; else if ( 'a' <= *str_P && *str_P <= 'f' ) val += *str_P - 'a' + 10; else if ( 'A' <= *str_P && *str_P <= 'F' ) val += *str_P - 'A' + 10; else break; } } else { for (val = 0; *str_P; str_P++) { val *= 10; if ( '0' <= *str_P && *str_P <= '9' ) val += *str_P - '0'; else break; } } return val; } /*-------------------------------------------- Enable/Disable Control routines used by commands ---------------------------------------------*/ void Enable_IERBF(sar_private *cp){ cp->CNFG_Reg |=IERBF; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_IERBF(sar_private *cp){ cp->CNFG_Reg &=(~IERBF); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Set_RDA(sar_private *cp, int8 RdaThr, int8 RdaTimer){ cp->CNFG_Reg &= 0xF000FFFF; cp->CNFG_Reg |= (((uint32)RdaThr)<<RDATHR_Offset)&0x07000000; cp->CNFG_Reg |= (((uint32)RdaTimer)<<RDATIMER_Offset)&0x003F0000; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_IS8671G_1(sar_private *cp) { printk("Enable 8671G 1 function\n"); cp->CNFG_Reg |= IS8671G_1; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_IS8671G_0(sar_private *cp) { printk("Enable 8671 0 function\n"); cp->CNFG_Reg |= IS8671G_0; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_IS8672_10(sar_private *cp) { printk("Enable 8672 function \n"); // cp->CNFG_Reg |= (0x11<<Is8672_10_Offset); cp->CNFG_Reg |= 0x30000000; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_IERDA(sar_private *cp){ cp->CNFG_Reg |=IERDA; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_IERDA(sar_private *cp){ cp->CNFG_Reg &=(~IERDA); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_IERTO(sar_private *cp){ cp->CNFG_Reg |=IERTO; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_IERTO(sar_private *cp){ cp->CNFG_Reg &=(~IERTO); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_IETBE(sar_private *cp){ cp->CNFG_Reg |=IETBE; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_IETBE(sar_private *cp){ cp->CNFG_Reg &=(~IETBE); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_IETDF(sar_private *cp){ cp->CNFG_Reg |=IETDF; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_IETDF(sar_private *cp){ cp->CNFG_Reg &=(~IETDF); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_LoopBack(sar_private *cp) { cp->CNFG_Reg |=UFLB; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_LoopBack(sar_private *cp) { cp->CNFG_Reg &=(~UFLB); //for performance tune test //cp->CNFG_Reg |=UFLB; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_SAR(sar_private *cp) { cp->CNFG_Reg |=SAREN; // 120210 -- Transmit utopia clk to sachem when SAR Disabled #if !defined(CONFIG_RTL8681_SAR_ARCH) && !defined(CONFIG_RTL8685_SAR_ARCH) cp->CNFG_Reg |=UTO_CLK_MASK; #endif WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_SAR(sar_private *cp) { cp->CNFG_Reg &=(~SAREN); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_QoS_Starvation(sar_private *cp) { cp->CNFG_Reg |=QoS_Starvation; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_QoS_Starvation(sar_private *cp) { cp->CNFG_Reg &=(~QoS_Starvation); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Enable_ATRLREN(sar_private *cp, int8 i){ cp->vcc[i].RV.CtlSts |=ATRLREN; WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } void Disable_ATRLREN(sar_private *cp, int8 i){ cp->vcc[i].RV.CtlSts &= (~ATRLREN); WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } void Enable_AAL5(sar_private *cp, int8 i) { cp->vcc[i].RV.CtlSts |=AAL5; WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } void Enable_Raw(sar_private *cp, int8 i) { cp->vcc[i].RV.CtlSts &=(~AAL5); WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } void Enable_tx_ch(sar_private *cp, int8 i) { cp->vcc[i].TV.CtlSts |=CHEN; cp->tx_channel_on|=(1<<i); WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); return; } void Enable_rx_ch(sar_private *cp, int8 i) { cp->vcc[i].RV.CtlSts |=CHEN; cp->rx_channel_on|=(1<<i); WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } void Disable_tx_ch(sar_private *cp, int8 i) { cp->vcc[i].TV.CtlSts &=(~CHEN); cp->tx_channel_on&=(~(1<<i)); WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); return; } void Disable_rx_ch(sar_private *cp, int8 i) { cp->vcc[i].RV.CtlSts &=(~CHEN); cp->rx_channel_on&=(~(1<<i)); WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } //0922 shlee for rtl8671 (G) void Enable_QoS_Priority(sar_private *cp, int8 i) { cp->vcc[i].TV.CtlSts |=QosPriority; WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); return; } void Disable_QoS_Priority(sar_private *cp, int8 i) { cp->vcc[i].TV.CtlSts &= (~QosPriority); WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); return; } void Enable_HP_CHTx(sar_private *cp, int8 i) { cp->vcc[i].TV.CtlSts |=HpCHTx; WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); return; } void Disable_HP_CHTx(sar_private *cp, int8 i) { cp->vcc[i].TV.CtlSts &=(~HpCHTx); WriteD_(cp->vcc[i].TV.CtlSts_Addr, cp->vcc[i].TV.CtlSts); return; } //0922 rtl8671(G) void Enable_Word_Insert(sar_private *cp, int8 i) { cp->vcc[i].RV.CKS |=WIEN; WriteD_(cp->vcc[i].RV.CKS_Addr, cp->vcc[i].RV.CKS); return; } void Set_L2Encap(sar_private *cp, int8 i, int8 encap) { cp->vcc[i].RV.CKS &= ~(1<<L2Encap_Offset); cp->vcc[i].RV.CKS |= encap<<L2Encap_Offset; WriteD_(cp->vcc[i].RV.CKS_Addr, cp->vcc[i].RV.CKS); // printk("%s: ch: %d encap %d write %x to reg %x\n", __func__,i, encap, cp->vcc[i].RV.CKS, cp->vcc[i].RV.CKS_Addr); } void Set_SARhdr(sar_private *cp, int8 i, int8 mode) { UINT32 val; val = cp->vcc[i].RV.CKS; val &= 0xffffe3ff; val |= mode<<SARhdr_Offset; WriteD_(cp->vcc[i].RV.CKS_Addr, val); cp->vcc[i].RV.CKS = val; // printk("%s: ch:%d mode %d write %x to reg %x\n", __func__, i,mode,val, cp->vcc[i].RV.CKS_Addr); } void Write_IP_Parser(sar_private *cp, int8 i, int8 en, int8 offset) { UINT32 val; val = cp->vcc[i].RV.CKS; val &= ~(0x3F); val |= ((en << IEPEN_Offset) | (offset)); WriteD_(cp->vcc[i].RV.CKS_Addr, val); cp->vcc[i].RV.CKS = val; // printk("%s: write %x to reg %x\n", __func__, val, cp->vcc[i].RV.CKS_Addr); } void Disable_Word_Insert(sar_private *cp, int8 i) { cp->vcc[i].RV.CKS &= (~WIEN); WriteD_(cp->vcc[i].RV.CKS_Addr, cp->vcc[i].RV.CKS); return; } void Enable_Drop_NonOAM(sar_private *cp, int8 i) { cp->vcc[i].RV.CtlSts |=DNOAMEN; WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } void Disable_Drop_NonOAM(sar_private *cp, int8 i) { cp->vcc[i].RV.CtlSts &= (DNOAMEN ^ 0xFFFFFFFF); WriteD_(cp->vcc[i].RV.CtlSts_Addr, cp->vcc[i].RV.CtlSts); return; } #if defined(CONFIG_RTL8681_SAR_ARCH) || defined(CONFIG_RTL8685_SAR_ARCH) void Enable_IEASMTICK(sar_private *cp){ printk("Enable IEASMTICK function \n"); cp->CNFG_Reg |=IEASMTICK; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Disable_IEASMTICK(sar_private *cp){ printk("Disable IEASMTICK function \n"); cp->CNFG_Reg &=(~IEASMTICK); WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Set_TSTMP(sar_private *cp, int16 asm_dvsr, int16 tstmp_dvsr) { uint32 val=0; val = (asm_dvsr << 16) |tstmp_dvsr; reg(SAR_TSTMP) = val; return; } void Enable_BOND_Link(sar_private *cp, int8 i) { uint32 val; val = reg(SAR_LINK_STS); val |= (1<<i) | (1<<(i+8)); /* TX/RX direction */ reg(SAR_LINK_STS) = val; return; } void Set_BOND_Line(sar_private *cp, int8 line) { uint32 val; val = reg(SAR_BOND_CFG); val &= ~( 0x7 << Total_links_shift); val |= ((line+1) << Total_links_shift) ; /* Total line + 1 */ reg(SAR_BOND_CFG) = val; return; } void Set_Dual_Map(sar_private *cp, int8 ch_no, int8 type) { uint32 val; val = reg(SAR_DUAL_MAP); //if (cp->vcc[ch_no].created == VC_CREATED && cp->vcc[ch_no].dev){ val &= ~(1<<ch_no); val |= (type<<ch_no); //}else{ // val &= ~(1<<ch_no); // val |=0; //} reg(SAR_DUAL_MAP) = val; return; } void Set_Bond_WFQ(sar_private *cp, int8 link, int8 weight) { uint32 val; if( link < 4){ val = reg(SAR_BOND_WFQ0); val &= ~((0xff) << (link*WFQ_Width)); val |= (weight) << (link*WFQ_Width); reg(SAR_BOND_WFQ0) = val; }else{ val = reg(SAR_BOND_WFQ1); val &= ~((0xff) << (link*WFQ_Width)); val |= (weight) << (link*WFQ_Width); reg(SAR_BOND_WFQ1) = val; } return; } void Enable_Dummy(sar_private *cp) { uint32 val; val = reg(SAR_BOND_CFG); val |= Dummy_En; reg(SAR_BOND_CFG) = val; return; } void Set_Bond_Dummy(sar_private *cp, uint32 result) { uint32 val; val = reg(SAR_DUMMY_CFG); val = result; reg(SAR_DUMMY_CFG) = val; return; } #endif #if 0 //not use anymore void Set_QoS_Int(sar_private *cp){ cp->CNFG_Reg &= (~QCLKSEL) ; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } void Set_QoS_Ext(sar_private *cp, int clk_offset){ cp->CNFG_Reg |= QCLKSEL; cp->CNFG_Reg |= (clk_offset&0x000F)<<QCLKOFFSET_Offset; WriteD_(SAR_CNFG_Addr, cp->CNFG_Reg); return; } #endif #if 0 BOOL ClearRxBuffer(sar_private *cp, int8 ch_no){ int8 j, i; if(cp->vcc[ch_no].RV.desc_pr==cp->vcc[ch_no].RV.desc_pc){ printk("channel-%d-Rx Buffer Bull with desc_pr == desc_pc\n",ch_no); return FALSE; } cp->vcc[ch_no].RV.desc_pc=cp->vcc[ch_no].RV.desc_pa; /* Read Descriptor and Allocate the memory */ j=cp->vcc[ch_no].RV.desc_pa; i=(cp->vcc[ch_no].RV.desc_pr+SAR_RX_DESC_NUM-1)%SAR_RX_DESC_NUM; while(j!=i){ if (ch_no==OAM_CH_NO) { if(!(cp->RODesc[j].STS&OWN_32)) { /* Restore LEN and CMD field */ cp->RODesc[j].LEN = cp->vcc[ch_no].RV.buffer_size; cp->RODesc[j].STS|=0x8000; cp->RODesc[j].STS&=0xC000; /* EOR and OWN bit */ j++; j%=SAR_RX_DESC_NUM; } else break; } else { if(!(cp->RVDesc[ch_no*SAR_RX_RING_SIZE+j].STS&OWN_32)){ /* Free Memory for this descriptor */ /* free(tmpRV_DESC->START_ADDR); */ /* Allocate new buffer for this descriptor, and modify the address to non-cacheable area */ /* cp->RVDesc[ch_no*SAR_RX_RING_SIZE+j].START_ADDR=(uint32)(malloc(Rx_Side))|Uncache_Mask;*/ /* Restore LEN and CMD field */ cp->RVDesc[ch_no*SAR_RX_RING_SIZE+j].LEN = SAR_RX_Buffer_Size; cp->RVDesc[ch_no*SAR_RX_RING_SIZE+j].STS|=0x8000; cp->RVDesc[ch_no*SAR_RX_RING_SIZE+j].STS&=0xC000; /* EOR and OWN bit */ j++; j%=SAR_RX_DESC_NUM; } else break; }; } cp->vcc[ch_no].RV.desc_pa=j; return TRUE; } #endif //extern int sar_loopback_cnt[8]; extern void open_smux_device(char *ifname); #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif /*__IRAM */BOOL ClearTxBuffer(sar_private *cp, int8 ch_no){ int j=0; TV_CMD_DESC *TVDesc; struct sk_buff *skb; //UINT32 encap_mode; struct atm_vcc *atmvcc; if (test_and_set_bit(0, &lock_txbuff)) return 0; #ifdef CONFIG_FLOW_CTRL int pw; pw = sar_dev->vcc[ch_no].TV.desc_pw; #endif// CONFIG_FLOW_CTRL j=cp->vcc[ch_no].TV.desc_pf; /*cp->vcc[ch_no].TV.desc_pc=(uint8)((REG32(cp->vcc[ch_no].TV.CtlSts_Addr)&CDOI_Mask)>>18); */ /* Now For 1483 only */ //encap_mode = cp->vcc[ch_no].rfc*2+cp->vcc[ch_no].framing; TVDesc=&cp->TVDesc[ch_no*SAR_TX_RING_SIZE+j]; skb = (struct sk_buff *)cp->vcc[ch_no].tx_buf[j]; //while((j!=cp->vcc[ch_no].TV.desc_pw)&&(!(TVDesc->CMD&OWN))){ while((!(TVDesc->CMD&OWN))&&skb){ cp->vcc[ch_no].stat.tx_desc_ok_cnt++; //sar_loopback_cnt[ch_no]++; cp->vcc[ch_no].stat.tx_byte_cnt += TVDesc->LEN; //cp->vcc[ch_no].stat.tx_cell_cnt += ((TVDesc->LEN%48==0) ? (TVDesc->LEN/48):(TVDesc->LEN/48+1)); //cp->net_stats.tx_bytes += TVDesc->LEN; /* if skb exists and this descriptor is not a 1483 header, free this mblk */ #ifdef INS_DESC_HEADER //if ((skb!=NULL)&&(TVDesc->START_ADDR!=(UINT32)(&FrameHeader_1483[encap_mode]))){ #else //if (skb!=NULL){ #endif //if (netif_msg_tx_done(cp)) // printk(KERN_DEBUG "%s: tx ch %d done, desc_pf=%d\n", cp->dev->type, ch_no, j); //if (skb!=NULL){ if (ch_no!=OAM_CH_NO) { atmvcc = (struct atm_vcc *)cp->vcc[ch_no].dev_data; if (atmvcc->pop) atmvcc->pop(atmvcc,skb); else dev_kfree_skb_irq(skb); //rtl8671_putPreAlloc(skb); if (cp->QoS_Tx_Credit) cp->vcc[ch_no].creditQoSTx++; } else { //skb of OAM_CH comes from driver itself, so we don't need to pop it for br2684 kfree(skb); }; cp->vcc[ch_no].tx_buf[j]=(UINT32)NULL; cp->vcc[ch_no].stat.tx_buf_free++; //}; if(TVDesc->CMD&LS){/* one packet has been successfully transmmited */ cp->vcc[ch_no].stat.tx_pkt_ok_cnt++; //cp->net_stats.tx_packets++; } //TVDesc->RSVD1=0x00000000; //TVDesc->START_ADDR=0x00000000; //j++; j%=SAR_TX_DESC_NUM; j=(j+1)&(SAR_TX_DESC_NUM-1); //TVDesc=&cp->TVDesc[ch_no*SAR_TX_RING_SIZE+j]; TVDesc= (j!=0)?(TVDesc+1):(&cp->TVDesc[ch_no*SAR_TX_RING_SIZE]); skb = (struct sk_buff *)cp->vcc[ch_no].tx_buf[j]; } cp->vcc[ch_no].TV.desc_pf=j; //wake queue if(cp->vcc[ch_no].dev!=NULL){ int avail_desc; avail_desc = (cp->vcc[ch_no].TV.desc_pf- 1 - cp->vcc[ch_no].TV.desc_pw + SAR_TX_DESC_NUM)%SAR_TX_DESC_NUM; if (netif_queue_stopped(cp->vcc[ch_no].dev)){ if (avail_desc >= SAR_TX_THRESHOLD) { //printk("start queue\n"); netif_wake_queue(cp->vcc[ch_no].dev); #if defined(CONFIG_RTL_MULTI_PVC_WAN) open_smux_device(cp->vcc[ch_no].dev->name); #endif } } } #if 0 if (netif_queue_stopped(cp->dev) && (((cp->vcc[ch_no].TV.desc_pf-cp->vcc[ch_no].TV.desc_pw)%SAR_TX_DESC_NUM) > (MAX_SKB_FRAGS + 1))) netif_wake_queue(cp->dev); #endif #ifdef CONFIG_FLOW_CTRL if(FCCtrl && FCEnable){ if (1 == FCFlag) { if (((j -1 -pw + SAR_TX_DESC_NUM)&(SAR_TX_DESC_NUM-1)) >= FC_high) {//used descriptor num. FCFlag = 0; REG32(0xB9800044)=0x0000000f; } } } #endif if (!test_and_clear_bit(0, &lock_txbuff)) { printk("[%s] Error! Clear lock_txbuff before set!!\n", __func__); } return TRUE; } #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif void FreeVcBuff(sar_private *cp, int8 ch_no){ int i; struct sk_buff *skb; if (ch_no>(Enable_VC_CNT+1)) return; /* initialize TX Descriptors: clear CMD and set EOR*/ //for(i=0;i<SAR_TX_DESC_NUM;i++){ // if(i==(SAR_TX_DESC_NUM-1)) // cp->TVDesc[ch_no*SAR_TX_RING_SIZE+i].CMD=0x4000; // else /* EOR */ // cp->TVDesc[ch_no*SAR_TX_RING_SIZE+i].CMD=0x0000; // //} /* Free RX Descriptors */ for(i=0;i<SAR_TX_DESC_NUM;i++){ if(ch_no==OAM_CH_NO){ if(cp->vcc[ch_no].tx_buf[i] !=(u32)NULL) kfree((void *)(cp->vcc[ch_no].tx_buf[i])); cp->vcc[ch_no].tx_buf[i]=(UINT32)NULL; /* Clear OWN and EOR bit */ cp->TODesc[i].CMD=0x0000; cp->TODesc[i].START_ADDR = 0; }else{ skb=(struct sk_buff *)cp->vcc[ch_no].tx_buf[i]; if(skb!=(struct sk_buff *)NULL) { if (ATM_SKB(skb)->vcc->pop) ATM_SKB(skb)->vcc->pop(ATM_SKB(skb)->vcc,skb); else dev_kfree_skb(skb); } /* Clear this skb */ cp->vcc[ch_no].tx_buf[i]=(UINT32)NULL; cp->TVDesc[ch_no*SAR_TX_RING_SIZE+i].CMD=0x0000; } } for(i=0;i<SAR_RX_DESC_NUM;i++){ if(ch_no==OAM_CH_NO){ if(cp->vcc[ch_no].rx_buf[i] !=(u32)NULL) kfree((void *)(cp->vcc[ch_no].rx_buf[i])); //5/22/04' hrchen, bug TVDesc is replaced by RVDesc cp->vcc[ch_no].rx_buf[i]=(UINT32)NULL; #ifdef CONFIG_RTL8672 // andrew, free memory on OAM ch1 as well if(cp->vcc[ch_no+1].rx_buf[i] !=(u32)NULL) kfree((void *)(cp->vcc[ch_no+1].rx_buf[i])); cp->vcc[ch_no+1].rx_buf[i]=(UINT32)NULL; #endif /* Clear OWN and EOR bit */ cp->RODesc[i].STS=0x0000; cp->RODesc[i].START_ADDR = 0; }else{ skb=(struct sk_buff *)cp->vcc[ch_no].rx_buf[i]; if(skb!=(struct sk_buff *)NULL) dev_kfree_skb(skb); /* Clear this skb */ cp->vcc[ch_no].rx_buf[i]=(UINT32)NULL; cp->RVDesc[ch_no*SAR_RX_RING_SIZE+i].STS=0x0000; } } cp->vcc[ch_no].RV.desc_pr =0; cp->vcc[ch_no].RV.desc_pc =0; cp->vcc[ch_no].RV.desc_pa =0; cp->vcc[ch_no].TV.desc_pf =0; cp->vcc[ch_no].TV.desc_pc =0; cp->vcc[ch_no].TV.desc_pw =0; #ifdef CONFIG_RTL8672 if(ch_no==OAM_CH_NO){ // init outband ch1 cp->vcc[ch_no+1].RV.desc_pr =0; cp->vcc[ch_no+1].RV.desc_pc =0; cp->vcc[ch_no+1].RV.desc_pa =0; cp->vcc[ch_no+1].TV.desc_pf =0; cp->vcc[ch_no+1].TV.desc_pc =0; cp->vcc[ch_no+1].TV.desc_pw =0; } #endif return; } void AllocVcBuff(sar_private *cp, int8 ch_no){ int i; struct sk_buff *skb=NULL; UINT32 *ptr; /* save buffer size to this channel (using same size) */ if(ch_no==OAM_CH_NO) cp->vcc[ch_no].TV.buffer_size=SAR_OAM_Buffer_Size; else cp->vcc[ch_no].TV.buffer_size=SAR_TX_Buffer_Size; if(ch_no==OAM_CH_NO) cp->vcc[ch_no].RV.buffer_size=SAR_OAM_Buffer_Size; else cp->vcc[ch_no].RV.buffer_size=SAR_RX_Buffer_Size; if (ch_no>(Enable_VC_CNT+1)) return; if ((ch_no!=OAM_CH_NO)&&((per_vc_desc_number<1)||(per_vc_desc_number>SAR_RX_DESC_HI_LIMIT))) { printk(KERN_ERR ":Execute /bin/sarctl pvcnumber <num> before create PVC\n"); return; }; /* initialize TX Descriptors: clear CMD and set EOR*/ for(i=0;i<SAR_TX_DESC_NUM;i++){ if(ch_no==OAM_CH_NO){ if(i==(SAR_TX_DESC_NUM-1)) cp->TODesc[i].CMD=0x4000; else /* EOR */ cp->TODesc[i].CMD=0x0000; cp->TODesc[i].START_ADDR = 0; } else { if(i==(SAR_TX_DESC_NUM-1)) cp->TVDesc[ch_no*SAR_TX_RING_SIZE+i].CMD=0x4000; else /* EOR */ cp->TVDesc[ch_no*SAR_TX_RING_SIZE+i].CMD=0x0000; } /* clear skb */ cp->vcc[ch_no].tx_buf[i]=(UINT32)NULL; } /* initialize RX Descriptors */ for(i=0;i<SAR_RX_DESC_NUM;i++){ if(ch_no==OAM_CH_NO){ ptr = (UINT32*)kmalloc(SAR_OAM_Buffer_Size, GFP_ATOMIC); if(ptr==(u32*)NULL) goto free_desc; cp->RODesc[i].LEN = SAR_OAM_Buffer_Size; cp->RODesc[i].START_ADDR = (u32)ptr|Uncache_Mask; cp->vcc[ch_no].rx_buf[i] = (UINT32)ptr; /* set OWN and EOR bit */ if(i==(SAR_RX_DESC_NUM-1)) cp->RODesc[i].STS=0xC000; else cp->RODesc[i].STS=0x8000; cp->vcc[ch_no].RV.desc_pa ++; cp->vcc[ch_no].RV.desc_pa %= SAR_RX_DESC_NUM; // Mason Yu. Recieve packet from OAM ch1(not cho) for AutoHunt #ifdef CONFIG_RTL8672 //8672 for ro1 ch, allocate SAR_RX_Buffer_Size length buffer for normal packet ptr = (UINT32*)kmalloc(SAR_OAM_Buffer_Size, GFP_ATOMIC); if(ptr == (u32*)NULL) goto free_desc; cp->RODesc[i+SAR_RX_RING_SIZE].LEN = SAR_OAM_Buffer_Size; cp->RODesc[i+SAR_RX_RING_SIZE].START_ADDR = (u32)ptr|Uncache_Mask; cp->vcc[ch_no+1].rx_buf[i] = (UINT32)ptr; /* set OWN and EOR bit */ if(i==(SAR_RX_DESC_NUM-1)) cp->RODesc[i+SAR_RX_RING_SIZE].STS=0xC000; else cp->RODesc[i+SAR_RX_RING_SIZE].STS=0x8000; cp->vcc[ch_no+1].RV.desc_pa ++; cp->vcc[ch_no+1].RV.desc_pa %= SAR_RX_DESC_NUM; #endif }else{ /* Get a Buffer */ if(i < per_vc_desc_number){ #ifdef CONFIG_SAR_SHARE_PRIV_SKB_WITH_ETH skb = dev_alloc_skb_priv_eth(CROSS_LAN_MBUF_LEN); #else skb = dev_alloc_skb(SAR_RX_Buffer_Size); #endif //CONFIG_SAR_SHARE_PRIV_SKB_WITH_ETH if (skb==NULL) { printk(KERN_ERR ":Error Could Not Get A Buffer..\n"); goto free_desc; } else cp->vcc[ch_no].stat.rx_buf_alloc++; } /* allocate memory for RX descriptors */ cp->RVDesc[ch_no*SAR_RX_RING_SIZE+i].LEN=SAR_RX_Buffer_Size; if(i < per_vc_desc_number) cp->RVDesc[ch_no*SAR_RX_RING_SIZE+i].START_ADDR=(u32)skb->data|Uncache_Mask; /* record this skb */ if(i < per_vc_desc_number) cp->vcc[ch_no].rx_buf[i]=(UINT32)skb; else cp->vcc[ch_no].rx_buf[i]=(UINT32)NULL; /* set OWN and EOR bit */ if(i < per_vc_desc_number) cp->RVDesc[ch_no*SAR_RX_RING_SIZE+i].STS=0x8000; if(i==(SAR_RX_DESC_NUM-1)) cp->RVDesc[ch_no*SAR_RX_RING_SIZE+i].STS |= 0x4000; cp->vcc[ch_no].RV.desc_pa ++; cp->vcc[ch_no].RV.desc_pa %= SAR_RX_DESC_NUM; } } if(cp->CNFG_Reg&IERBF) cp->vcc[ch_no].RV.desc_pa =(cp->vcc[ch_no].RV.desc_pa + SAR_RX_DESC_NUM -1) % SAR_RX_DESC_NUM; return; free_desc: /* !!! free ring */ FreeVcBuff(cp, ch_no); return; } int cr2reg(int pcr) { int k, e, m, pow2, reg; k = pcr; e=0; while (k>1) { k = k/2; e++; } //printf("pcr=%d, e=%d\n", pcr,e); pow2 = 1; for (k = 1; k <= e; k++) pow2*=2; //printf("pow2=%d\n", pow2); //m = ((pcr/pow2)-1)*512; k = 0; while (pcr >= pow2) { pcr -= pow2; k++; } m = (k-1)*512 + pcr*512/pow2; //printf("m=%d\n", m); reg = (e<<9 | m ); //printf("reg=%d\n", reg); return reg; } #if 0 void adjust_ATM_QoS(sar_atm_vcc *vcc_dev) { if (vcc_dev->QoS.Type&0x02) { //VBR type int credit_num; credit_num=ReadD_(vcc_dev->TV.SCR_Addr)&0x00FF; if (credit_num >= vcc_dev->SCR_Credit_hi) { //normal credit number if (vcc_dev->write_flag) { WriteD_(vcc_dev->TV.SCR_Addr, vcc_dev->TV.SCR); vcc_dev->write_flag = 0; printk("NormSCR %08x\n", vcc_dev->TV.SCR); }; } else if (credit_num <= vcc_dev->SCR_Credit_low) { //credit is not enough, use Safe SCR to increase credit if (!vcc_dev->write_flag) { WriteD_(vcc_dev->TV.SCR_Addr, vcc_dev->Safe_SCR); vcc_dev->write_flag = 1; printk("SafeSCR %08x\n", vcc_dev->Safe_SCR); }; } }; } #endif //deprecated after applying maximum PCR limitation mechanism #ifdef SW_QOS_STARV_PREVENTION /*update channel information record of each QoS type*/ static void UpdateStarvationInfo(sar_private *cp) { unsigned long flags; int chno; spin_lock_irqsave(&cp->txlock, flags); memset(cp->starv, 0 , sizeof(struct ch_qos_type_info)*QoS_type_num); for (chno=0; chno < Enable_VC_CNT; chno++) { if (!cp->vcc[chno].created) continue; cp->starv[(int)cp->vcc[chno].QoS.Type].cnt++; cp->starv[(int)cp->vcc[chno].QoS.Type].mask |= (1 << chno); cp->vcc[chno].starv_drop_flag = 0; } spin_unlock_irqrestore (&cp->txlock, flags); return; } #endif void CreateVC(sar_private *cp, struct SAR_IOCTL_CFG *cfg, int reset_stat_info){ int8 ch_no, encap_mode; ch_no = cfg->ch_no; cp->vcc[ch_no].ch_no = cfg->ch_no; cp->vcc[ch_no].vpi = cfg->vpi; cp->vcc[ch_no].vci = cfg->vci; cp->vcc[ch_no].rfc = cfg->rfc; cp->vcc[ch_no].framing = cfg->framing; //cp->vcc[ch_no].loopback = cfg->loopback; memcpy(&cp->vcc[ch_no].QoS, &cfg->QoS, sizeof(Traffic_Manage)); if(cp->vcc[ch_no].created == VC_NOT_CREATED){ AllocVcBuff(cp, ch_no); if (reset_stat_info) memset(&cp->vcc[ch_no].stat, 0, sizeof(ch_stat)); } SetVpiVci(cp, cfg->vpi, cfg->vci, ch_no); /* Set Vpi, Vci to Rx */ // 8672 set each CH Rx/Tx FDP if(ch_no!=0&&ch_no!=OAM_CH_NO) { reg(cp->vcc[ch_no].TV.FDP_Addr) = (unsigned int)&cp->TVDesc[ch_no*SAR_TX_RING_SIZE]; reg(cp->vcc[ch_no].RV.FDP_Addr) = (unsigned int)&cp->RVDesc[ch_no*SAR_RX_RING_SIZE]; } Enable_AAL5(cp, ch_no); Enable_ATRLREN(cp, ch_no); #if 1//SAR_INTERPOLATE SetQoSInterpolate(cp, ch_no, cfg); /* Set cell rate interpolation */ //printk("ch_no%d, (%x/%x) (%x/%x)\n", ch_no, cp->vcc[ch_no].hwCRtickData[LOWER_SCR], cp->vcc[ch_no].hwCRtickData[UPPER_SCR], // cp->vcc[ch_no].hwCRtickData[LOWER_PCR], cp->vcc[ch_no].hwCRtickData[UPPER_PCR]); #endif printk("create: ch%d (%d/%d) %d,%d\n", ch_no, cp->vcc[ch_no].vpi, cp->vcc[ch_no].vci, cfg->QoS.PCR, cfg->QoS.SCR); //for ATM Tx Credit if (total_pvc_number==1) { //only 1 PVC cp->QoS_Tx_Credit = 0; } else { switch (cp->vcc[ch_no].QoS.Type) { case QoS_CBR: cp->vcc[ch_no].creditQoSTx = QoS_CREDIT_HI; cp->QoS_Tx_Credit = 1; break; case QoS_nrtVBR: case QoS_rtVBR: cp->vcc[ch_no].creditQoSTx = QoS_CREDIT_MID; cp->QoS_Tx_Credit = 1; break; case QoS_UBR: default: cp->vcc[ch_no].creditQoSTx = QoS_CREDIT_LOW; break; }; }; /* Now For 1483 only */ encap_mode = cp->vcc[ch_no].rfc*2+cp->vcc[ch_no].framing; /* if 1483 Bridge Mode, header(10)+Eth(14) = 24 makes the TCP/IP just on the 4-byte boundary, Word Insertion is not needed */ if(cp->vcc[ch_no].rfc == RFC1483_BRIDGED) { Write_IP_Parser(cp, ch_no, 1, 0); // enable eth parse from 0 offset Disable_Word_Insert(cp, ch_no); } #ifdef CONFIG_ATM_CLIP else if(cp->vcc[ch_no].rfc == RFC1483_ROUTED || cp->vcc[ch_no].rfc == RFC1577) // Jenny, IPoA #else else if(cp->vcc[ch_no].rfc == RFC1483_ROUTED) #endif { Write_IP_Parser(cp, ch_no, 1, 0); // enable eth parse from 0 offset Disable_Word_Insert(cp, ch_no); } else if (cp->vcc[ch_no].rfc == RFC2364) { #ifdef CONFIG_RTL867X_PACKET_PROCESSOR Write_IP_Parser(cp, ch_no, 1, 0); #else Enable_Word_Insert(cp, ch_no); Write_IP_Parser(cp, ch_no, 1, 0); // enable eth parse from 0 offset #endif } else if (cp->vcc[ch_no].rfc == RFC2516) { Write_IP_Parser(cp, ch_no, 1, 0); // enable eth parse from 0 offset Disable_Word_Insert(cp, ch_no); } #ifndef SAR_CRC_GEN cp->vcc[ch_no].TV.Ether_Offset_Value=No_Offset; #else cp->vcc[ch_no].TV.Ether_Offset_Value=FrameHeaderSize_1483[encap_mode]; #endif cp->vcc[ch_no].TV.Ether_Offset_Value&=ETHNT_OFFSET_MSK; //Enable_rx_ch(cp, ch_no); //Enable_tx_ch(cp, ch_no); cp->vcc[ch_no].created = VC_CREATED; cp->vcc[ch_no].dev=NULL; //deprecated after applying maximum PCR limitation mechanism #ifdef SW_QOS_STARV_PREVENTION cp->vcc[ch_no].last_pkt_send_jiffies = 0; UpdateStarvationInfo(cp); #endif return; } void DeleteVC(sar_private *cp, struct SAR_IOCTL_CFG *cfg){ int8 ch_no; ch_no = cfg->ch_no; if (ch_no == OAM_CH_NO) { //ql -- set push to null cp->vcc[OAM_CH_NO].dev_data = NULL; return; } // stop hw first Disable_rx_ch(cp, ch_no); Disable_tx_ch(cp, ch_no); cp->vcc[ch_no].ch_no = cfg->ch_no; //cp->vcc[ch_no].vpi = cfg->vpi; //cp->vcc[ch_no].vci = cfg->vci; //cp->vcc[ch_no].rfc = cfg->rfc; //cp->vcc[ch_no].framing = cfg->framing; //memcpy(&cp->vcc[ch_no].QoS, &cfg->QoS, sizeof(Traffic_Manage)); //move here to tell sar_rx abadoning rx action.... jim cp->vcc[ch_no].created = VC_NOT_CREATED; FreeVcBuff(cp, ch_no); SetVpiVci(cp, 0, 0, ch_no); /* Set Vpi, Vci to Rx */ //Enable_AAL5(ch_no); //Disable_ATRLREN(ch_no); //SetQoS(ch_no, 0); /* Set Scheduler Option */ //SetPCR(ch_no, 0); /* Set Peak Cell Rate */ /* Now For 1483 only */ //encap_mode = cp->vcc[ch_no].rfc*2+cp->vcc[ch_no].framing; /* if 1483 Bridge Mode, header(10)+Eth(14) = 24 makes the TCP/IP just on the 4-byte boundary, Word Insertion is not needed */ //if(cp->vcc[ch_no].rfc == RFC1483_BRIDGED) // Disable_Word_Insert(ch_no); //else if(cp->vcc[ch_no].rfc == RFC1483_ROUTED) // Enable_Word_Insert(ch_no); // #ifndef SAR_CRC_GEN // cp->vcc[ch_no].TV.Ether_Offset_Value=No_Offset; //#else // cp->vcc[ch_no].TV.Ether_Offset_Value=FrameHeaderSize_1483[encap_mode]; //#endif // cp->vcc[ch_no].TV.Ether_Offset_Value&=ETHNT_OFFSET_MSK; //cp->vcc[ch_no].created = VC_NOT_CREATED; cp->vcc[ch_no].dev=NULL; //deprecated after applying maximum PCR limitation mechanism #ifdef SW_QOS_STARV_PREVENTION UpdateStarvationInfo(cp); #endif return; } #if 0 static const unsigned char ppp_term[] = { // 0xfe, // 0xfe, // 0x03, // 0xcf, 0xc0, 0x21, 0x05, /* PPP TERM-REQ */ 0x00, /* Identifier */ 0x00, 0x04 }; static const unsigned char pppllc[] = { 0xFE, 0xFE, 0x03, 0xCF, 0xC0, 0x21 }; #define LLC_LEN (4) #define TERM_LEN (6) #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif void sendTERM(void) { sar_private *cp = sar_dev; struct atm_vcc *vcc; int i; for (i=0; i<Enable_VC_CNT; i++) { if (cp->vcc[i].created==VC_CREATED) { // fake PPPoA TERM-REQ struct sk_buff *skb; if ((skb = dev_alloc_skb(sizeof (struct sk_buff)))==NULL) return; vcc = cp->vcc[i].dev_data; memcpy(skb->data, ppp_term, TERM_LEN); memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN); ATM_SKB(skb)->vcc = vcc; atomic_set(&skb->users, 0); // printk("sendTERM: %d/%d send skb->data=%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", cp->vcc[i].vpi, cp->vcc[i].vci // , skb->data[0], skb->data[1], skb->data[2], skb->data[3], skb->data[4], skb->data[5], skb->data[6], skb->data[7], skb->data[8], skb->data[9]); //atomic_add(skb->truesize, &vcc->tx_inuse); //ATM_SKB(skb)->iovcnt = 0; ATM_SKB(skb)->atm_options = vcc->atm_options; // printk("atm_skb(%p)->vcc(%p)->dev(%p)\n", skb, vcc, vcc->dev); skb->len = TERM_LEN + LLC_LEN; sar_send(vcc, skb); dev_kfree_skb_any(skb); } } } #endif #ifdef CONFIG_RTL8672_ATM_QoS /* * Check the maximum PCR under linked line rate, if the configured PCR is more than the maximum line rate PCR, * set the PCR to the maximum line rate PCR instead of the configured one. */ void Check_Line_Rate_PCR(sar_private *cp, int ch_no) { int lineRatePCR; uint32 minPCRRegVal, configPCRRegVal; GetLinkSpeed((char *)&LINE_rate); lineRatePCR = LINE_rate.upstreamRate*1000/(53*8); if (lineRatePCR == 0) { printk("[%s] Unable to get line rate PCR, skip the checking ...\n", __func__); return; } printk("[%s] line rate: %d kbps, line rate PCR: %d\n", __func__, (int)LINE_rate.upstreamRate, lineRatePCR); //caculate the actual reg val minPCRRegVal = (uint32)QoS_CLK*PCR_Period/lineRatePCR; printk("[%s] QoS_CLK: %d, PCR_Period: %d, Min PCR Reg Val: 0x%08X\n", __func__, QoS_CLK, PCR_Period, minPCRRegVal); //enable reading for other channels REG32(0xb8303010) |= 0xff; if (ch_no == MAX_PCR_AUTO_CH) { for (ch_no = 0; ch_no < Enable_VC_CNT; ch_no++) { if (cp->vcc[ch_no].created == VC_CREATED) { configPCRRegVal = (uint32)QoS_CLK*PCR_Period/cp->vcc[ch_no].QoS.PCR; printk("[%s] CH %d Config PCR Reg Val: 0x%08X, config PCR: %d\n", __func__, ch_no, configPCRRegVal, cp->vcc[ch_no].QoS.PCR); WriteD_(cp->vcc[ch_no].TV.QoS_PCR_Addr, ((configPCRRegVal < minPCRRegVal)? minPCRRegVal : configPCRRegVal)); printk("[%s] CH %d PCR Reg Val after check: 0x%08X\n", __func__, ch_no, ReadD_(cp->vcc[ch_no].TV.QoS_PCR_Addr)); } } } else { configPCRRegVal = (uint32)QoS_CLK*PCR_Period/cp->vcc[ch_no].QoS.PCR; printk("[%s] CH %d Config PCR Reg Val: 0x%08X, config PCR: %d\n", __func__, ch_no, configPCRRegVal, cp->vcc[ch_no].QoS.PCR); WriteD_(cp->vcc[ch_no].TV.QoS_PCR_Addr, ((configPCRRegVal < minPCRRegVal)? minPCRRegVal : configPCRRegVal)); printk("[%s] CH %d PCR Reg Val after check: 0x%08X\n", __func__, ch_no, ReadD_(cp->vcc[ch_no].TV.QoS_PCR_Addr)); } //restore the state of SAR Test[1] register REG32(0xb8303010) &= ~(0xff); } #endif #if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL) void (*xdsl_ctrl_event_notify_hook)(int event) = NULL; EXPORT_SYMBOL(xdsl_ctrl_event_notify_hook); #endif //called by 867x_sar/xdsl_api.c: AdslLinkUp() void AdslLinkUp_SAR(void){ sar_private *cp = sar_dev; struct net_device *ndev; struct atm_dev *atm_dev; #ifndef CONFIG_RTL8672_ATM_QoS GetLinkSpeed((char *)&LINE_rate); #endif #ifdef CONFIG_FLOW_CTRL //enable flow control FCEnable=1; REG32(0xB9800044)=0x0f; #endif //Check(set) line rate maximum PCR before enabling SAR #ifdef CONFIG_RTL8672_ATM_QoS Check_Line_Rate_PCR(cp, MAX_PCR_AUTO_CH); #endif //CONFIG_RTL8685_SAR_PATCH_ONOFF printk( "Enable_SAR\n" ); Enable_SAR(cp); atm_dev = (struct atm_dev *)cp->dev; /* Found signal again, notify atmdevice */ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_FOUND); ndev = dev_get_by_name(&init_net, "atm0"); if (ndev) { netif_carrier_on(ndev); dev_put(ndev); } #if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL) if (xdsl_ctrl_event_notify_hook){ (*xdsl_ctrl_event_notify_hook)(1); } #endif } //called by 867x_sar/xdsl_api.c: AdslLinkDown() void AdslLinkDown_SAR(void){ sar_private *cp = sar_dev; struct net_device *ndev; struct atm_dev *atm_dev; #ifdef CONFIG_FLOW_CTRL //disable flow control FCEnable=0; REG32(0xB9800044)=0x0f; #endif atm_dev = (struct atm_dev *)cp->dev; /* Loss of signal, notify atmdevice */ atm_dev_signal_change(atm_dev, ATM_PHY_SIG_LOST); ndev = dev_get_by_name(&init_net, "atm0"); if (ndev) { netif_carrier_off(ndev); dev_put(ndev); } //CONFIG_RTL8685_SAR_PATCH_ONOFF printk( "Disable_SAR\n" ); Disable_SAR(cp); #if defined(CONFIG_XDSL_CTRL_PHY_IS_DSL) if (xdsl_ctrl_event_notify_hook){ (*xdsl_ctrl_event_notify_hook)(0); } #endif } #if 0 void flush_tx_desc(sar_private *cp){ int i,j; TV_CMD_DESC *TVDesc; struct sk_buff *skb; for(i=0;i<Enable_VC_CNT;i++){ if(cp->vcc[i].created==VC_CREATED){ for(j=0;j<SAR_TX_DESC_NUM;j++){ TVDesc=&cp->TVDesc[i*SAR_TX_RING_SIZE+j]; skb = (struct sk_buff *)cp->vcc[i].tx_buf[j]; if(skb!=(struct sk_buff *)NULL) dev_kfree_skb_irq(skb); cp->vcc[i].tx_buf[j]=(UINT32)NULL; TVDesc->RSVD1=0x00000000; TVDesc->START_ADDR=0x00000000; TVDesc->CMD=0x00000000; } cp->vcc[i].TV.desc_pf =0; cp->vcc[i].TV.desc_pc =0; cp->vcc[i].TV.desc_pw =0; Disable_tx_ch(i); Enable_tx_ch(i); } } //for OB channel if(cp->vcc[OAM_CH_NO].created==VC_CREATED){ for(j=0;j<SAR_TX_DESC_NUM;j++){ TVDesc=&cp->TVDesc[OAM_CH_NO*SAR_TX_RING_SIZE+j]; skb = (struct sk_buff *)cp->vcc[OAM_CH_NO].tx_buf[j]; if(skb!=(struct sk_buff *)NULL) dev_kfree_skb_irq(skb); cp->vcc[OAM_CH_NO].tx_buf[j]=(UINT32)NULL; TVDesc->RSVD1=0x00000000; TVDesc->START_ADDR=0x00000000; TVDesc->CMD=0x00000000; } cp->vcc[OAM_CH_NO].TV.desc_pf =0; cp->vcc[OAM_CH_NO].TV.desc_pc =0; cp->vcc[OAM_CH_NO].TV.desc_pw =0; Disable_tx_ch(OAM_CH_NO); Enable_tx_ch(OAM_CH_NO); } } #endif #ifdef LoopBack_Test uint16 ipcsum(uint16 *ptr, uint32 len, uint16 resid) { uint32 csum = resid; uint32 odd = 0; if(len & 1) odd = 1; len = len >> 1; for(;len > 0 ; len--,ptr++) csum += ntohs(*ptr); if(odd) csum += (*((uint8 *)ptr) <<8) & 0xff00; /* take care of 1's complement */ while(csum >> 16) csum = (csum & 0xffff) + (csum >> 16); if(csum == 0xffff) csum = 0; return((uint16)csum); } void exchange_mac(uint8 *buf){ char tmpMac[6]; memcpy(tmpMac, buf, 6); memcpy(buf, buf+6, 6); memcpy(buf+6, tmpMac, 6); return; } INT16 Lying_Engine(uint8 *buf, INT16 len){ char broadcast[6]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; char remoteMac[6]={0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; char tmpMac[6], tempIPMac[10], tempIP[4]; UINT16 *Type, *Opcode, *csum, *Frag, fragment; UINT8 *Protocol, *ICMP_Type; Type =(UINT16 *)(buf + 12); Opcode =(UINT16 *)(buf + 20); Protocol = (UINT8 *)(buf + 23); ICMP_Type = (UINT8 *)(buf + 34); Frag= (UINT16 *)(buf+ 20); fragment = (UINT16)*Frag & 0x1FFF; if(!memcmp(buf, broadcast, 6)){ /* Broadcast Packet */ if(!memcmp(buf+28, buf+38, 4)) /* Initial ARP, don't lie */ return len; if((*Type==0x0806)&&(*Opcode==0x0001)){ /* this is a ARP Request */ /* handle Ehternet Header - Mac Address */ memcpy(tmpMac, buf+6, 6); memcpy(buf+6, remoteMac, 6); memcpy(buf, tmpMac, 6); /* change IP Opcode to reply */ *Opcode = 0x0002; /* ARP IP and Mac Address */ memcpy(tempIPMac, buf+22, 10); memcpy(tempIP, buf+38, 4); memcpy(buf+32, tempIPMac, 10); memcpy(buf+22, remoteMac, 6); memcpy(buf+28, tempIP, 4); /* memset(buf+42, 0x20, 18); */ return (len+18); } }else { if((*Type==0x0800)&&(*Protocol==0x01)&&(*ICMP_Type==0x08)){ /* this is a ICMP Ping Request */ /* handle Ehternet Header - exchange Mac Address */ memcpy(tmpMac, buf, 6); memcpy(buf, buf+6, 6); memcpy(buf+6, tmpMac, 6); /* ip check sum */ csum = (UINT16 *)(buf + 24); *csum = 0; *csum = (~ipcsum((UINT16 *)(buf+14), 20, 0)); /* exchange IP Address */ memcpy(tempIP, buf+26, 4); memcpy(buf+26, buf+30, 4); memcpy(buf+30, tempIP, 4); /* ICMP header */ *ICMP_Type = 0x00; /* ICMP checksum */ csum = (UINT16 *)(buf + 36); /* *csum = 0;*/ *csum += 0x0800;/*(~ipcsum((UINT16 *)(buf+34), len-34, 0));*/ }else if ((*Type==0x0800)&&(*Protocol==0x01)&&(fragment!=0)){ /* this is a ICMP Ping Request, fragment packet */ /* handle Ehternet Header - exchange Mac Address */ memcpy(tmpMac, buf, 6); memcpy(buf, buf+6, 6); memcpy(buf+6, tmpMac, 6); /* ip check sum */ csum = (UINT16 *)(buf + 24); *csum = 0; *csum = (~ipcsum((UINT16 *)(buf+14), 20, 0)); /* exchange IP Address */ memcpy(tempIP, buf+26, 4); memcpy(buf+26, buf+30, 4); memcpy(buf+30, tempIP, 4); } if(len<64) len=64; return len; } /* exchange_mac(buf); */ return len; } #endif /////////////////////////////////////////////for OAM #ifdef ATM_OAM extern int OAMFSendACell(unsigned char *pCell, int flagOAM); /* OAM to SAR driver interface */ #define EMPTY 0 #define OCCUPY 1 #ifndef NULL #define NULL 0 #endif #define ATM_OAM_TRACE printk #define ATM_OAM_DEBUG printk OAMF5 OAMF5_info; static const unsigned char f5_llid_all_0[] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; static const unsigned char f5_llid_all_1[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; #ifdef CONFIG_SMP spinlock_t oamf5_lock; #define OAMF5_DEFINE_FLAG(x) unsigned long x #define OAMF5_LOCK(x) spin_lock_irqsave(&oamf5_lock, x) #define OAMF5_UNLOCK(x) spin_unlock_irqrestore(&oamf5_lock, x) #else #define OAMF5_DEFINE_FLAG(x) #define OAMF5_LOCK(x) #define OAMF5_UNLOCK(x) #endif /* OAM F5 */ unsigned long OAMF5GetTimeStamp(void) { //#if RTOS == VXWORKS return (unsigned long)(jiffies*(1000/HZ)); //#endif } __SWAP int OAMF5StartTimer(OAMF5Timer* timer, VOIDFUNCPTR pFunc) { if(timer->occupied == OCCUPY) { ATM_OAM_TRACE("OAMF5StartTimer (timer->occupied == OCCUPY)\n"); return 0; } init_timer(&timer->timer); timer->timer.expires = jiffies + (timer->ulTimeout * HZ)/1000; timer->timer.function = pFunc; timer->timer.data = (unsigned long)(timer); add_timer(&timer->timer); timer->occupied = OCCUPY; ATM_OAM_DEBUG("OAMF5StartTimer ok\n"); return 1; } __SWAP int OAMF5StopTimer(OAMF5Timer* timer) { if(timer->occupied == EMPTY) { ATM_OAM_TRACE("OAMF5StopTimer (timer->occupied == EMPTY)\n"); return 0; } del_timer(&timer->timer); timer->occupied = EMPTY; ATM_OAM_DEBUG("OAMF5StopTimer ok\n"); return 1; } void OAMF5Init(unsigned short vpi, unsigned short vci, OAMF5 *pOAMF5) { int i, j; //unsigned char gfc = 0, clp = 0, ucPTI; pOAMF5->VPI = vpi; pOAMF5->VCI = vci; /* OAM FM --LB */ pOAMF5->OAMF5lFaultLBState = FAULT_LB_IDLE; // loopback state: idle pOAMF5->OAMF5ulFaultLBTag = 0; // loopback correlation tag for (i = 0; i < OAM_LB_LLID_SIZE; i++) { pOAMF5->OAMF5ucCPID[i] = 0xFF; // connection point ID } // initialize LB table for TMN transmission only for (i = 0; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { pOAMF5->OAMF5LBList[i].tag = 0; for (j = 0; j < OAM_LB_LLID_SIZE; j++) { pOAMF5->OAMF5LBList[i].locationID[j] = 0; } pOAMF5->OAMF5LBList[i].scope = 0; pOAMF5->OAMF5LBList[i].count = 0; pOAMF5->OAMF5LBList[i].status = FAULT_LB_IDLE; // initialize LB timer pOAMF5->OAMF5LBList[i].timer.pOAM = (void *)pOAMF5; pOAMF5->OAMF5LBList[i].timer.OAMFunctionType = FAULT_LB; pOAMF5->OAMF5LBList[i].timer.OAMUserData = i; pOAMF5->OAMF5LBList[i].timer.oneShot = 1; pOAMF5->OAMF5LBList[i].timer.occupied = EMPTY; pOAMF5->OAMF5LBList[i].timer.ulTimeout = OAM_LB_WAIT_TIME; } for (i = 0; i < LB_ALL_NODE_SIZE; i++) { for (j = 0; j < OAM_LB_LLID_SIZE; j++) { pOAMF5->LBRXList[i].locationID[j] = 0; } pOAMF5->LBRXList[i].status = EMPTY; pOAMF5->LBRXList[i].sur_des_count = 0; } pOAMF5->OAMF5ulFaultLBTxGoodCell = 0; pOAMF5->OAMF5ulFaultLBRxGoodCell = 0; ATM_OAM_DEBUG("ATM OAM F5 initialized.\n"); } void OAMF5ChangeState(OAMF5 *pOAMF5) { int i; OAMF5_LB_INFO *pLBInfo = NULL; // FM LB state for (i = 0, pLBInfo = &pOAMF5->OAMF5LBList[0]; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if(pLBInfo[i].status == FAULT_LB_WAITING) return; if (i == 0) i = LB_ALL_NODE_SIZE-1; } pOAMF5->OAMF5lFaultLBState = FAULT_LB_IDLE; ATM_OAM_DEBUG("OAMF5ChangeState >> IDLE\n"); } __SWAP int OAMF5SetVpiVci(unsigned short vpi, unsigned short vci, OAMF5 *pOAMF5) { if (pOAMF5->OAMF5lFaultLBState != FAULT_LB_IDLE) return 0; pOAMF5->VPI = vpi; pOAMF5->VCI = vci; return 1; } ///////////////////////////////////////////////////////////////////////////// // // CAtmOAMF5::SetLBID() // // Input : ucIDType -- 0: all connection point with location ID enable // 1: specified by TNM (telecommunication network management) // country code & network ID (4 bytes) + operator specific information (11 bytes) // 2: specified by TNM (telecommunication network management) // country code + network ID (4 bytes) // 3: specified by TNM (telecommunication network management) // partial NSAP based coding structure // 0x6A: no loopback // 0xFF: endpoint // others: reserved for future use // pDesID: pointer of the destination ID array // pID: pointer of the specified location ID array // // Output : None // // Author : Network Group // // History : // // Design Notes: Prepare loopback ID information for transmission path // I.610, Table 5, 10.2.3 // ///////////////////////////////////////////////////////////////////////////// __SWAP void OAMF5SetLBID(unsigned char ucIDType, unsigned char *pDesID, unsigned char *pID) { int i; switch (ucIDType) { case 0: // all 0's: all CP with location ID option enable case 0x6A: // all 0x6A: not specify CP, no loopback is perform case 0xFF: // all 1's: enpoint for (i = 0; i < OAM_LB_LLID_SIZE; i++) pDesID[i] = ucIDType; break; case 2: pDesID[0] = ucIDType; // specified by telecommunication network management (TNM) for (i = 0; i < 4; i++) pDesID[i+1] = pID[i]; for (; i < 15; i++) pDesID[i+1] = 0x6A; break; case 1: case 3: pDesID[0] = ucIDType; // specified by telecommunication network management (TNM) for (i = 0; i < OAM_LB_LLID_SIZE-1; i++) pDesID[i+1] = pID[i]; break; } } __SWAP int OAMF5TMNSetCPID(unsigned char ucLocationType, unsigned char *pCPID, OAMF5 *pOAMF5) { if (pOAMF5->OAMF5lFaultLBState != FAULT_LB_IDLE) return 0; OAMF5SetLBID(ucLocationType, pOAMF5->OAMF5ucCPID, pCPID); return 1; } ///////////////////////////////////////////////////////////////////////////// // // CAtmOAMF5::SendACell() // // Input : pointer of a cell, channel (ATM0/ATM1) // // Output : 0/1 // // Author : Network Group // // History : // // Design Notes: Send a OAM cell to queue // ///////////////////////////////////////////////////////////////////////////// __SWAP int OAMF5SendACell(unsigned char *pOamCell) { unsigned char *dupCell; ATM_OAM_DEBUG("Into the OAMF5SendACell\n"); dupCell = (unsigned char *) kmalloc(OAM_CELL_SIZE, GFP_KERNEL); if(dupCell == NULL) { ATM_OAM_TRACE("OAM malloc fail!\n"); return 0; } memcpy(dupCell, pOamCell, OAM_CELL_SIZE); #if 0 { unsigned int i; for(i=0;i<52;i+=13) { ATM_OAM_DEBUG("Cell(%d) %.02x %.02x %.02x %.02x %.02x %.02x %.02x %.02x %.02x %.02x %.02x %.02x %.02x\n" ,i ,pOamCell[i] ,pOamCell[i+1] ,pOamCell[i+2] ,pOamCell[i+3] ,pOamCell[i+4] ,pOamCell[i+5] ,pOamCell[i+6] ,pOamCell[i+7] ,pOamCell[i+8] ,pOamCell[i+9] ,pOamCell[i+10] ,pOamCell[i+11],pOamCell[i+12]); } } #endif return OAMFSendACell(dupCell, 1); } ///////////////////////////////////////////////////////////////////////////// // // CAtmOAMF5::GenLBCell() // // Input : pointer of OAMF5_LB_INFO // // Output : NONE // // Author : Network Group // // History : // // Design Notes: Generate OAM fault management loopback cell for transmission // (excluding HEC and CRC10) // ///////////////////////////////////////////////////////////////////////////// __SWAP void OAMF5GenLBCell(OAMF5_LB_INFO *pLBInfo, OAMF5 *pOAMF5) { unsigned char ucPTI = (pLBInfo->scope)? 5 : 4; unsigned char gfc = 0, clp = 0; int i; ATM_OAM_DEBUG("Into the OAMF5GenLBCell\n"); // generate cell header & payload pLBInfo->cell[OAM_FORMAT_H1] = (unsigned char) ((pOAMF5->VPI >> 4) & 0x0F) | ((gfc << 4) & 0xF0); pLBInfo->cell[OAM_FORMAT_H2] = (unsigned char) (((pOAMF5->VPI << 4) & 0x00F0) | (pOAMF5->VCI >> 12)); pLBInfo->cell[OAM_FORMAT_H3] = (unsigned char) (pOAMF5->VCI >> 4); pLBInfo->cell[OAM_FORMAT_H4] = (unsigned char) ((pOAMF5->VCI << 4) & 0x00F0) | ((ucPTI << 1) & 0xE) | (clp & 1); pLBInfo->cell[OAM_FORMAT_TYPE] = FAULT_LB; // OAM type & function pLBInfo->cell[OAM_FORMAT_LB_INDICATION] = 1; // loopback indication bit pLBInfo->cell[OAM_FORMAT_LB_TAG] = (unsigned char)(pLBInfo->tag >> 24 & 0xFF); // loopback correlation tag pLBInfo->cell[OAM_FORMAT_LB_TAG+1] = (unsigned char)(pLBInfo->tag >> 16 & 0xFF); // loopback correlation tag pLBInfo->cell[OAM_FORMAT_LB_TAG+2] = (unsigned char)(pLBInfo->tag >> 8 & 0xFF); // loopback correlation tag pLBInfo->cell[OAM_FORMAT_LB_TAG+3] = (unsigned char)(pLBInfo->tag & 0xFF); // loopback correlation tag for (i = 0; i < OAM_LB_LLID_SIZE; i++) { pLBInfo->cell[OAM_FORMAT_LB_LLID+i] = pLBInfo->locationID[i]; // loopback location ID pLBInfo->cell[OAM_FORMAT_LB_SID+i] = pOAMF5->OAMF5ucCPID[i]; // loopback source ID } ATM_OAM_DEBUG("The Loopback ID ="); for (i = 0; i < OAM_LB_SRCID_SIZE; i++) ATM_OAM_DEBUG("%.02x", pLBInfo->locationID[i]); ATM_OAM_DEBUG("\n"); ATM_OAM_DEBUG("The Source ID ="); for (i = 0; i < OAM_LB_SRCID_SIZE; i++) ATM_OAM_DEBUG("%.02x", pOAMF5->OAMF5ucCPID[i]); ATM_OAM_DEBUG("\n"); for (i = 0; i < OAM_LB_UNUSE; i++) pLBInfo->cell[OAM_FORMAT_LB_UNUSE+i] = 0x6A; ATM_OAM_DEBUG("Leave the OAMF5GenLBCell\n"); } __SWAP OAMF5_LB_INFO *OAMF5FindLBInfo(unsigned long tag, OAMF5 *pOAMF5) { int i; for (i = 0; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if (tag == pOAMF5->OAMF5LBList[i].tag && pOAMF5->OAMF5LBList[i].status != FAULT_LB_IDLE) return &(pOAMF5->OAMF5LBList[i]); if (i == 0) i = LB_ALL_NODE_SIZE-1; } return NULL; } __SWAP OAMF5_LBRX_INFO *OAMF5FindLBRXInfo(OAMF5 *pOAMF5) { return &(pOAMF5->LBRXList[0]); } //void OAMF5TMNLBTimeoutFunc(struct timer_list *id, int arg) void OAMF5TMNLBTimeoutFunc(unsigned long arg) { OAMF5Timer *pTimer; OAMF5 *pOAMF5; OAMF5_DEFINE_FLAG(flags); ATM_OAM_DEBUG("Into the OAMF5TMNLBTimeoutFunc %x\n", (unsigned int)arg); OAMF5_LOCK(flags); pTimer = (OAMF5Timer*) arg; if(pTimer) { OAMF5_LB_INFO *pLBInfo = NULL; //int i; pOAMF5 = pTimer->pOAM; pTimer->occupied = EMPTY; if(pTimer->OAMFunctionType != FAULT_LB) { ATM_OAM_TRACE("OAMF5TMNLBTimeoutFunc (pTimer->OAMFunctionType != FAULT_LB)\n"); OAMF5_UNLOCK(flags); return; } pLBInfo = &pOAMF5->OAMF5LBList[pTimer->OAMUserData]; // double check if(&(pLBInfo->timer) != pTimer) { ATM_OAM_TRACE("OAMF5TMNLBTimeoutFunc (&(pLBInfo->timer) != pTimer)\n"); OAMF5_UNLOCK(flags); return; } // timestamp pLBInfo->rtt = OAMF5GetTimeStamp() - pLBInfo->timestamp; // return to idle state pLBInfo->status = FAULT_LB_STOP; OAMF5ChangeState(pOAMF5); } OAMF5_UNLOCK(flags); ATM_OAM_DEBUG("Leave the OAMF5TMNLBTimeoutFunc\n"); } ///////////////////////////////////////////////////////////////////////////// // // CAtmOAMF5::TMNTxLBStart() // // Input : Scope (1/0:end/segment), ATM channel, location ID // // Output : fail (0)/OK (1) // // Author : Network Group // // History : // // Design Notes: Generate an OAM fault management loopback cell // Send out to PHY layer // Start waiting timer and adjust internal state // return true -- cell is ready to be sent // return false -- no cell available // ///////////////////////////////////////////////////////////////////////////// __SWAP int OAMF5TMNTxLBStart(unsigned char Scope, unsigned char *pLocID, unsigned long *Tag, OAMF5 *pOAMF5) { OAMF5_LB_INFO *pLBInfo = NULL; int i, j; OAMF5_DEFINE_FLAG(flags); ATM_OAM_DEBUG("Into the OAMF5TMNTxLBStart\n"); OAMF5_LOCK(flags); // search for an empty OAMF5_LB_INFO from table if (!pLocID[0]) { if (pOAMF5->OAMF5LBList[0].status == FAULT_LB_WAITING) { ATM_OAM_TRACE("OAMF5TMNTxLBStart (pOAMF5->OAMF5LBList[0].status == FAULT_LB_WAITING)\n"); OAMF5_UNLOCK(flags); return 0; } pLBInfo = &pOAMF5->OAMF5LBList[0]; // clean up all-0 table for (i = 0; i < LB_ALL_NODE_SIZE; i++) { pLBInfo[i].all0_status = FAULT_LB_IDLE; pLBInfo[i].count = 0; pLBInfo[i].tag = 0; for (j = 0; j < OAM_LB_LLID_SIZE; j++) pLBInfo[i].locationID[j] = 0; pLBInfo[i].timestamp = 0; pLBInfo[i].rtt = 0; } } else { for (i = LB_ALL_NODE_SIZE; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if (pOAMF5->OAMF5LBList[i].status == FAULT_LB_IDLE) { pLBInfo = &pOAMF5->OAMF5LBList[i]; break; } } // if no more IDLE list, pick a STOP list if(pLBInfo == NULL) { unsigned int timestamp = 0xFFFFFFFF; for (i = LB_ALL_NODE_SIZE; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if ((pOAMF5->OAMF5LBList[i].status == FAULT_LB_STOP) && (pOAMF5->OAMF5LBList[i].timestamp < timestamp)){ pLBInfo = &pOAMF5->OAMF5LBList[i]; timestamp = pOAMF5->OAMF5LBList[i].timestamp; } } } } if (pLBInfo == NULL){ ATM_OAM_TRACE("OAMF5TMNTxLBStart (pLBInfo == NULL)\n"); OAMF5_UNLOCK(flags); return 0; } // prepare LB information // AtomicSet(&(pLBInfo->status), FAULT_LB_WAITING); // loopback state: waiting pLBInfo->status = FAULT_LB_WAITING; // AtomicIncrement((long *)&OAMF5ulFaultLBTag); pOAMF5->OAMF5ulFaultLBTag++; pLBInfo->tag = pOAMF5->OAMF5ulFaultLBTag-1; pLBInfo->scope = Scope; pLBInfo->count = 0; for (i = 0; i < OAM_LB_LLID_SIZE; i++) pLBInfo->locationID[i] = pLocID[i]; OAMF5GenLBCell(pLBInfo, pOAMF5); // generate OAM LB cell OAMF5SendACell(pLBInfo->cell); // Send out a LB OAM cell pOAMF5->OAMF5ulFaultLBTxGoodCell++; // adjust internal state if (pOAMF5->OAMF5lFaultLBState != FAULT_LB_WAITING) pOAMF5->OAMF5lFaultLBState = FAULT_LB_WAITING; // timestamp pLBInfo->timestamp = OAMF5GetTimeStamp(); pLBInfo->rtt = 0; // start timer if(OAMF5StartTimer(&(pLBInfo->timer), (VOIDFUNCPTR )OAMF5TMNLBTimeoutFunc) == 0) { pLBInfo->status = FAULT_LB_IDLE; OAMF5ChangeState(pOAMF5); } *Tag = pLBInfo->tag; OAMF5_UNLOCK(flags); ATM_OAM_DEBUG("Leave the OAMF5TMNTxLBStart\n"); return 1; } ///////////////////////////////////////////////////////////////////////////// // // CAtmOAMF5::TMNTxLBStop() // // Input : Tag // // Output : fail (0)/OK (1) // // Author : Network Group // // History : // // Design Notes: Stop generating an OAM fault management loopback cell // Cancel timer and adjust internal state // ///////////////////////////////////////////////////////////////////////////// __SWAP int OAMF5TMNTxLBStop(unsigned long Tag, OAMF5 *pOAMF5) { OAMF5_LB_INFO *pLBInfo; //OAMF5Timer *pLBTimer = NULL; OAMF5_LBRX_INFO *pLBRXInfo; int i, j; OAMF5_DEFINE_FLAG(flags); OAMF5_LOCK(flags); ATM_OAM_DEBUG("Into the OAMF5TMNTxLBStop\n"); if ((pLBInfo = OAMF5FindLBInfo(Tag, pOAMF5)) == NULL) { OAMF5_UNLOCK(flags); return 0; } // clear OAM FM LB Rx List pLBRXInfo = &(pOAMF5->LBRXList[0]); for (i = 0; i < LB_ALL_NODE_SIZE; i++) { for (j = 0; j < OAM_LB_LLID_SIZE; j++) { pLBRXInfo[i].locationID[j] = 0; } pLBRXInfo[i].status = EMPTY; pLBRXInfo[i].sur_des_count = 0; } // stop timer OAMF5StopTimer(&(pLBInfo->timer)); // return to idle state pLBInfo->status = FAULT_LB_IDLE; OAMF5ChangeState(pOAMF5); OAMF5_UNLOCK(flags); ATM_OAM_DEBUG("Out the OAMF5TMNTxLBStop\n"); return 1; } ///////////////////////////////////////////////////////////////////////////// // // CAtmOAMF5::ProcessRxLBCell() // // Input : pointer of a cell received // // Output : false/true (discard/loopback) // // Author : Network Group // // History : // // Design Notes: Verify current loopback state // Verify OAM data // Please refer to figure C.1/I.610 // ///////////////////////////////////////////////////////////////////////////// __SWAP int OAMF5ProcessRxLBCell(unsigned char *pOamCell, OAMF5 *pOAMF5) { unsigned long PTI = (pOamCell[OAM_FORMAT_H4] & 0xE); unsigned char ucLBIndicator = (pOamCell[OAM_FORMAT_LB_INDICATION] & 1); int i=0, j; // assumed CRC-10 is correct, ASIC will handle CRC-10 ATM_OAM_DEBUG("Into the OAMF5ProcessRxLBCell\n"); // handle FM LB request if ((PTI == 8 || PTI == 0xA) && ucLBIndicator) { // segment/end-to-end OAM F5 FM LB // it's segment endpoint or connection endpoint char Match1 = FALSE, Match2 = FALSE, Match3 = FALSE; // verify LLID if (pOamCell[OAM_FORMAT_LB_LLID] == 0x6A) { ATM_OAM_DEBUG("OAMF5ProcessRxLBCell LLID 6A\n"); return 0; // no loopback } ATM_OAM_DEBUG("The rcvd Loopback ID ="); for (j = 0; j < OAM_LB_SRCID_SIZE; j++) ATM_OAM_DEBUG("%x", pOamCell[OAM_FORMAT_LB_LLID+j]); ATM_OAM_DEBUG("\n"); if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], pOAMF5->OAMF5ucCPID, OAM_LB_LLID_SIZE) == 0) Match1 = TRUE; // match connection point ID else if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], f5_llid_all_0, OAM_LB_LLID_SIZE) == 0) Match2 = TRUE; // all connection point ID else if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], f5_llid_all_1, OAM_LB_LLID_SIZE) == 0) Match3 = TRUE; // end-to-end loopback if (PTI == 0xA) // is this end-to-end LB, ???? Match2 = FALSE; if (Match1 || Match2 || Match3) { // Loop cell back char SidFound = FALSE; OAMF5_LBRX_INFO *pLBRXInfo; pLBRXInfo = &(pOAMF5->LBRXList[0]); // reset loopback indication pOamCell[OAM_FORMAT_LB_INDICATION] &= 0xFE; // set LLID memcpy(&pOamCell[OAM_FORMAT_LB_LLID], pOAMF5->OAMF5ucCPID, OAM_LB_LLID_SIZE); // save source ID // ITEX's implementation is save LLID, I think they are wrong. by Dick Tam for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if((pLBRXInfo[i].status == OCCUPY) && (memcmp(&pOamCell[OAM_FORMAT_LB_SID], pLBRXInfo[i].locationID, OAM_LB_SRCID_SIZE) == 0)) { SidFound = TRUE; pLBRXInfo[i].sur_des_count++; break; } } if(!SidFound) { for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if(pLBRXInfo[i].status == EMPTY) { pLBRXInfo[i].status = OCCUPY; memcpy(pLBRXInfo[i].locationID, &pOamCell[OAM_FORMAT_LB_SID], OAM_LB_SRCID_SIZE); pLBRXInfo[i].sur_des_count++; break; } } } // debug message for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if (pLBRXInfo[i].sur_des_count != 0) { ATM_OAM_DEBUG("The Reomte ID ="); for (j = 0; j < OAM_LB_SRCID_SIZE; j++) ATM_OAM_DEBUG("%x", pLBRXInfo[i].locationID[j]); ATM_OAM_DEBUG(", The Rx Remote Count = %d\n", (unsigned int)pLBRXInfo[i].sur_des_count); } } // Do not have to change cell header // cell is ready to be sent out at the moment return 1; } } // handle FM LB response if (pOAMF5->OAMF5lFaultLBState == FAULT_LB_WAITING) { if ((PTI == 8 || PTI == 0xA) && (!ucLBIndicator)){ OAMF5_LB_INFO *pLBInfo = NULL; unsigned long LBTag; // verify loopback source ID if (memcmp(&pOamCell[OAM_FORMAT_LB_SID], pOAMF5->OAMF5ucCPID, OAM_LB_SRCID_SIZE) != 0) return 0; // verify correlation tag value LBTag = (unsigned long)(pOamCell[OAM_FORMAT_LB_TAG]); LBTag = (LBTag << 8) | ((unsigned long)(pOamCell[OAM_FORMAT_LB_TAG+1])); LBTag = (LBTag << 8) | ((unsigned long)(pOamCell[OAM_FORMAT_LB_TAG+2])); LBTag = (LBTag << 8) | ((unsigned long)(pOamCell[OAM_FORMAT_LB_TAG+3])); if ((pLBInfo = OAMF5FindLBInfo(LBTag, pOAMF5)) != NULL) { // loopback successful if (pLBInfo->status == FAULT_LB_WAITING) { if (pLBInfo == &pOAMF5->OAMF5LBList[0]) { // all zero LB, don't stop timer // wait 5 second to collect all response // save LLID to pLBInfo->locationID, handle counter for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if (pLBInfo[i].all0_status == FAULT_LB_WAITING) { // match LLID if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], pLBInfo[i].locationID, OAM_LB_LLID_SIZE) == 0) { pLBInfo[i].count++; return 0; } } else if (pLBInfo[i].all0_status == FAULT_LB_IDLE) { // save LLID pLBInfo[i].all0_status = FAULT_LB_WAITING; pLBInfo[i].count++; memcpy(pLBInfo[i].locationID, &pOamCell[OAM_FORMAT_LB_LLID], OAM_LB_LLID_SIZE); // timestamp pLBInfo[i].rtt = OAMF5GetTimeStamp() - pLBInfo->timestamp; return 0; } } // more than LB_ALL_NODE_SIZE reponse, how to handle it ? } else { pLBInfo->count++; // timestamp pLBInfo->rtt = OAMF5GetTimeStamp() - pLBInfo->timestamp; // stop timer OAMF5StopTimer(&(pLBInfo->timer)); // save LLID memcpy(pLBInfo[i].locationID, &pOamCell[OAM_FORMAT_LB_LLID], OAM_LB_LLID_SIZE); // return to idle state pLBInfo->status = FAULT_LB_STOP; OAMF5ChangeState(pOAMF5); } // Should tell TMN that LB successes. // TODO ... // (or TMN may monitor Variable ulFaultLBRxGoodCell instead.) } } } } ATM_OAM_DEBUG("Leave the OAMF5ProcessRxLBCell\n"); return 0; } ///////////////////////////////////////////////////////////////////////////// // // OAMRxF5Cell // // Input : pointer of an OAM cell // // Output : None // // Author : Network Group // // History : // // Design Notes: OAM F5 handler routine // ///////////////////////////////////////////////////////////////////////////// __SWAP void OAMRxF5Cell(unsigned char *pOamCell, OAMF5 *pOAMF5) { OAMF5_DEFINE_FLAG(flags); ATM_OAM_DEBUG("Into the RxOAMF5Cell %d\n", pOamCell[OAM_FORMAT_TYPE]); cell_debug(pOamCell, debug_obcell, 0); #if 0 { unsigned int i; for(i=0;i<52;i+=13) { ATM_OAM_DEBUG("Cell(%d) %x %x %x %x %x %x %x %x %x %x %x %x %x\n" ,i ,pOamCell[i] ,pOamCell[i+1] ,pOamCell[i+2] ,pOamCell[i+3] ,pOamCell[i+4] ,pOamCell[i+5] ,pOamCell[i+6] ,pOamCell[i+7] ,pOamCell[i+8] ,pOamCell[i+9] ,pOamCell[i+10] ,pOamCell[i+11],pOamCell[i+12]); } } #endif OAMF5_LOCK(flags); switch (pOamCell[OAM_FORMAT_TYPE]) { #if 0 case FAULT_AIS: case FAULT_RDI: case FAULT_CC: case PERFORMANCE_FPM: case PERFORMANCE_BACKWARD_REPORT: case ACT_DEACT_FPM: case ACT_DEACT_FPM_BR: case ACT_DEACT_CC: case APS_GROUP_PROTECT: case APS_INDIVIDUAL_PROTECT: case SYSTEM_MANAGEMENT: #endif case FAULT_LB: if (pOAMF5 != NULL) { if (OAMF5ProcessRxLBCell(pOamCell, pOAMF5)) { OAMF5SendACell(pOamCell); // send LB cell out pOAMF5->OAMF5ulFaultLBTxGoodCell++; } pOAMF5->OAMF5ulFaultLBRxGoodCell++; } break; default: break; } OAMF5_UNLOCK(flags); ATM_OAM_DEBUG("Leave the OAMRxF5Cell\n"); } /* OAM F4 */ OAMF4 OAMF4_info; #define OAMF4GetTimeStamp OAMF5GetTimeStamp #define OAMF4StartTimer OAMF5StartTimer #define OAMF4StopTimer OAMF5StopTimer #define OAMF4SetLBID OAMF5SetLBID #define OAMF4SendACell OAMF5SendACell void OAMF4Init(unsigned short vpi, unsigned short vci, OAMF4 *pOAMF4) { int i, j; //unsigned char gfc = 0, clp = 0, ucPTI; pOAMF4->VPI = vpi; pOAMF4->VCI = vci; /* OAM FM --LB */ pOAMF4->OAMF4lFaultLBState = FAULT_LB_IDLE; // loopback state: idle pOAMF4->OAMF4ulFaultLBTag = 0; // loopback correlation tag for (i = 0; i < OAM_LB_LLID_SIZE; i++) { pOAMF4->OAMF4ucCPID[i] = 0xFF; // connection point ID } // initialize LB table for TMN transmission only for (i = 0; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { pOAMF4->OAMF4LBList[i].tag = 0; for (j = 0; j < OAM_LB_LLID_SIZE; j++) { pOAMF4->OAMF4LBList[i].locationID[j] = 0; } pOAMF4->OAMF4LBList[i].scope = 0; pOAMF4->OAMF4LBList[i].count = 0; pOAMF4->OAMF4LBList[i].status = FAULT_LB_IDLE; // initialize LB timer pOAMF4->OAMF4LBList[i].timer.pOAM = (void *)pOAMF4; pOAMF4->OAMF4LBList[i].timer.OAMFunctionType = FAULT_LB; pOAMF4->OAMF4LBList[i].timer.OAMUserData = i; pOAMF4->OAMF4LBList[i].timer.oneShot = 1; pOAMF4->OAMF4LBList[i].timer.occupied = EMPTY; pOAMF4->OAMF4LBList[i].timer.ulTimeout = OAM_LB_WAIT_TIME; } for (i = 0; i < LB_ALL_NODE_SIZE; i++) { for (j = 0; j < OAM_LB_LLID_SIZE; j++) { pOAMF4->LBRXList[i].locationID[j] = 0; } pOAMF4->LBRXList[i].status = EMPTY; pOAMF4->LBRXList[i].sur_des_count = 0; } pOAMF4->OAMF4ulFaultLBTxGoodCell = 0; pOAMF4->OAMF4ulFaultLBRxGoodCell = 0; ATM_OAM_DEBUG("ATM OAM F4 initialized.\n"); } void OAMF4ChangeState(OAMF4 *pOAMF4) { int i; OAMF4_LB_INFO *pLBInfo = NULL; // FM LB state for (i = 0, pLBInfo = &pOAMF4->OAMF4LBList[0]; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if(pLBInfo[i].status == FAULT_LB_WAITING) return; if (i == 0) i = LB_ALL_NODE_SIZE-1; } pOAMF4->OAMF4lFaultLBState = FAULT_LB_IDLE; ATM_OAM_DEBUG("OAMF4ChangeState >> IDLE\n"); } __SWAP int OAMF4SetVpiVci(unsigned short vpi, unsigned short vci, OAMF4 *pOAMF4) { if (pOAMF4->OAMF4lFaultLBState != FAULT_LB_IDLE) return 0; pOAMF4->VPI = vpi; pOAMF4->VCI = vci; return 1; } __SWAP int OAMF4TMNSetCPID(unsigned char ucLocationType, unsigned char *pCPID, OAMF4 *pOAMF4) { if (pOAMF4->OAMF4lFaultLBState != FAULT_LB_IDLE) return 0; OAMF5SetLBID(ucLocationType, pOAMF4->OAMF4ucCPID, pCPID); return 1; } __SWAP void OAMF4GenLBCell(OAMF5_LB_INFO *pLBInfo, OAMF4 *pOAMF4) { unsigned long VCI = (pLBInfo->scope)? 4 : 3; unsigned char gfc = 0, clp = 0, ucPTI = 0; int i; ATM_OAM_DEBUG("Into the OAMF4GenLBCell\n"); // generate cell header & payload pLBInfo->cell[OAM_FORMAT_H1] = (unsigned char) ((pOAMF4->VPI >> 4) & 0x0F) | ((gfc << 4) & 0xF0); pLBInfo->cell[OAM_FORMAT_H2] = (unsigned char) (((pOAMF4->VPI << 4) & 0x00F0) | (VCI >> 12)); pLBInfo->cell[OAM_FORMAT_H3] = (unsigned char) (VCI >> 4); pLBInfo->cell[OAM_FORMAT_H4] = (unsigned char) ((VCI << 4) & 0x00F0) | ((ucPTI << 1) & 0xE) | (clp & 1); pLBInfo->cell[OAM_FORMAT_TYPE] = FAULT_LB; // OAM type & function pLBInfo->cell[OAM_FORMAT_LB_INDICATION] = 1; // loopback indication bit pLBInfo->cell[OAM_FORMAT_LB_TAG] = (unsigned char)(pLBInfo->tag >> 24 & 0xFF); // loopback correlation tag pLBInfo->cell[OAM_FORMAT_LB_TAG+1] = (unsigned char)(pLBInfo->tag >> 16 & 0xFF); // loopback correlation tag pLBInfo->cell[OAM_FORMAT_LB_TAG+2] = (unsigned char)(pLBInfo->tag >> 8 & 0xFF); // loopback correlation tag pLBInfo->cell[OAM_FORMAT_LB_TAG+3] = (unsigned char)(pLBInfo->tag & 0xFF); // loopback correlation tag for (i = 0; i < OAM_LB_LLID_SIZE; i++) { pLBInfo->cell[OAM_FORMAT_LB_LLID+i] = pLBInfo->locationID[i]; // loopback location ID pLBInfo->cell[OAM_FORMAT_LB_SID+i] = pOAMF4->OAMF4ucCPID[i]; // loopback source ID } for (i = 0; i < OAM_LB_UNUSE; i++) pLBInfo->cell[OAM_FORMAT_LB_UNUSE+i] = 0x6A; ATM_OAM_DEBUG("Leave the OAMF4GenLBCell\n"); } __SWAP OAMF4_LB_INFO *OAMF4FindLBInfo(unsigned long tag, OAMF4 *pOAMF4) { int i; for (i = 0; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if (tag == pOAMF4->OAMF4LBList[i].tag && pOAMF4->OAMF4LBList[i].status != FAULT_LB_IDLE) return &(pOAMF4->OAMF4LBList[i]); if (i == 0) i = LB_ALL_NODE_SIZE-1; } return NULL; } __SWAP OAMF4_LBRX_INFO *OAMF4FindLBRXInfo(OAMF4 *pOAMF4) { return &(pOAMF4->LBRXList[0]); } //void OAMF4TMNLBTimeoutFunc(struct timer_list *id, int arg) void OAMF4TMNLBTimeoutFunc(unsigned long arg) { OAMF5Timer *pTimer; OAMF4 *pOAMF4; OAMF5_DEFINE_FLAG(flags); ATM_OAM_DEBUG("Into the OAMF4TMNLBTimeoutFunc %x\n",(unsigned int) arg); OAMF5_LOCK(flags); pTimer = (OAMF4Timer*) arg; if(pTimer) { OAMF4_LB_INFO *pLBInfo = NULL; //int i; pOAMF4 = pTimer->pOAM; pTimer->occupied = EMPTY; if(pTimer->OAMFunctionType != FAULT_LB) { ATM_OAM_TRACE("OAMF4TMNLBTimeoutFunc (pTimer->OAMFunctionType != FAULT_LB)\n"); OAMF5_UNLOCK(flags); return; } pLBInfo = &pOAMF4->OAMF4LBList[pTimer->OAMUserData]; // double check if(&(pLBInfo->timer) != pTimer) { ATM_OAM_TRACE("OAMF4TMNLBTimeoutFunc (&(pLBInfo->timer) != pTimer)\n"); OAMF5_UNLOCK(flags); return; } // timestamp pLBInfo->rtt = OAMF4GetTimeStamp() - pLBInfo->timestamp; // return to idle state pLBInfo->status = FAULT_LB_STOP; OAMF4ChangeState(pOAMF4); } OAMF5_UNLOCK(flags); ATM_OAM_DEBUG("Leave the OAMF4TMNLBTimeoutFunc\n"); } __SWAP int OAMF4TMNTxLBStart(unsigned char Scope, unsigned char *pLocID, unsigned long *Tag, OAMF4 *pOAMF4) { OAMF5_LB_INFO *pLBInfo = NULL; int i, j; OAMF5_DEFINE_FLAG(flags); ATM_OAM_DEBUG("Into the OAMF4TMNTxLBStart\n"); OAMF5_LOCK(flags); // search for an empty OAMF4_LB_INFO from table if (!pLocID[0]) { if (pOAMF4->OAMF4LBList[0].status == FAULT_LB_WAITING) { ATM_OAM_TRACE("OAMF4TMNTxLBStart (pOAMF4->OAMF4LBList[0].status == FAULT_LB_WAITING)\n"); OAMF5_UNLOCK(flags); return 0; } pLBInfo = &pOAMF4->OAMF4LBList[0]; // clean up all-0 table for (i = 0; i < LB_ALL_NODE_SIZE; i++) { pLBInfo[i].all0_status = FAULT_LB_IDLE; pLBInfo[i].count = 0; pLBInfo[i].tag = 0; for (j = 0; j < OAM_LB_LLID_SIZE; j++) pLBInfo[i].locationID[j] = 0; pLBInfo[i].timestamp = 0; pLBInfo[i].rtt = 0; } } else { for (i = LB_ALL_NODE_SIZE; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if (pOAMF4->OAMF4LBList[i].status == FAULT_LB_IDLE) { pLBInfo = &pOAMF4->OAMF4LBList[i]; break; } } // if no more IDLE list, pick a STOP list if(pLBInfo == NULL) { unsigned int timestamp = 0xFFFFFFFF; for (i = LB_ALL_NODE_SIZE; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if ((pOAMF4->OAMF4LBList[i].status == FAULT_LB_STOP) && (pOAMF4->OAMF4LBList[i].timestamp < timestamp)){ pLBInfo = &pOAMF4->OAMF4LBList[i]; timestamp = pOAMF4->OAMF4LBList[i].timestamp; } } } } if (pLBInfo == NULL){ ATM_OAM_TRACE("OAMF4TMNTxLBStart (pLBInfo == NULL)\n"); OAMF5_UNLOCK(flags); return 0; } // prepare LB information // AtomicSet(&(pLBInfo->status), FAULT_LB_WAITING); // loopback state: waiting pLBInfo->status = FAULT_LB_WAITING; // AtomicIncrement((long *)&OAMF5ulFaultLBTag); pOAMF4->OAMF4ulFaultLBTag++; pLBInfo->tag = pOAMF4->OAMF4ulFaultLBTag-1; pLBInfo->scope = Scope; pLBInfo->count = 0; for (i = 0; i < OAM_LB_LLID_SIZE; i++) pLBInfo->locationID[i] = pLocID[i]; OAMF4GenLBCell(pLBInfo, pOAMF4); // generate OAM LB cell OAMF4SendACell(pLBInfo->cell); // Send out a LB OAM cell pOAMF4->OAMF4ulFaultLBTxGoodCell++; // adjust internal state if (pOAMF4->OAMF4lFaultLBState != FAULT_LB_WAITING) pOAMF4->OAMF4lFaultLBState = FAULT_LB_WAITING; // timestamp pLBInfo->timestamp = OAMF4GetTimeStamp(); pLBInfo->rtt = 0; // start timer if(OAMF4StartTimer(&(pLBInfo->timer), (VOIDFUNCPTR )OAMF4TMNLBTimeoutFunc) == 0) { pLBInfo->status = FAULT_LB_IDLE; OAMF4ChangeState(pOAMF4); } *Tag = pLBInfo->tag; OAMF5_UNLOCK(flags); ATM_OAM_DEBUG("Leave the OAMF4TMNTxLBStart\n"); return 1; } __SWAP int OAMF4TMNTxLBStop(unsigned long Tag, OAMF4 *pOAMF4) { OAMF4_LB_INFO *pLBInfo; //OAMF4Timer *pLBTimer = NULL; OAMF4_LBRX_INFO *pLBRXInfo; int i, j; OAMF5_DEFINE_FLAG(flags); OAMF5_LOCK(flags); ATM_OAM_DEBUG("Into the OAMF4TMNTxLBStop\n"); if ((pLBInfo = OAMF4FindLBInfo(Tag, pOAMF4)) == NULL) { OAMF5_UNLOCK(flags); return 0; } // clear OAM FM LB Rx List pLBRXInfo = &(pOAMF4->LBRXList[0]); for (i = 0; i < LB_ALL_NODE_SIZE; i++) { for (j = 0; j < OAM_LB_LLID_SIZE; j++) { pLBRXInfo[i].locationID[j] = 0; } pLBRXInfo[i].status = EMPTY; pLBRXInfo[i].sur_des_count = 0; } // stop timer OAMF4StopTimer(&(pLBInfo->timer)); // return to idle state pLBInfo->status = FAULT_LB_IDLE; OAMF4ChangeState(pOAMF4); ATM_OAM_DEBUG("Out the OAMF4TMNTxLBStop\n"); OAMF5_UNLOCK(flags); return 1; } __SWAP unsigned short get_vci(unsigned char *pCell) { return (((((unsigned short) pCell[1]) << 12) & (0xf000)) | ((((unsigned short) pCell[2]) << 4) & (0x0ff0)) | ((((unsigned short) pCell[3]) >> 4) & (0x000f))); } __SWAP int OAMF4ProcessRxLBCell(unsigned char *pOamCell, OAMF4 *pOAMF4) { unsigned long VCI = get_vci(pOamCell); unsigned char ucLBIndicator = (pOamCell[OAM_FORMAT_LB_INDICATION] & 1); int i=0, j; // assumed CRC-10 is correct, ASIC will handle CRC-10 ATM_OAM_DEBUG("Into the OAMF4ProcessRxLBCell\n"); // handle FM LB request if ((VCI == 3 || VCI == 4) && ucLBIndicator){ // segment/end-to-end OAM F5 FM LB // it's segment endpoint or connection endpoint char Match1 = FALSE, Match2 = FALSE, Match3 = FALSE; // verify LLID if (pOamCell[OAM_FORMAT_LB_LLID] == 0x6A) { ATM_OAM_DEBUG("OAMF4ProcessRxLBCell LLID 6A\n"); return 0; // no loopback } if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], pOAMF4->OAMF4ucCPID, OAM_LB_LLID_SIZE) == 0) Match1 = TRUE; // match connection point ID else if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], f5_llid_all_0, OAM_LB_LLID_SIZE) == 0) Match2 = TRUE; // all connection point ID else if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], f5_llid_all_1, OAM_LB_LLID_SIZE) == 0) Match3 = TRUE; // end-to-end loopback if (VCI == 4) // is this end-to-end LB Match2 = FALSE; if (Match1 || Match2 || Match3) { // Loop cell back char SidFound = FALSE; OAMF5_LBRX_INFO *pLBRXInfo; pLBRXInfo = &(pOAMF4->LBRXList[0]); // reset loopback indication pOamCell[OAM_FORMAT_LB_INDICATION] &= 0xFE; // set LLID memcpy(&pOamCell[OAM_FORMAT_LB_LLID], pOAMF4->OAMF4ucCPID, OAM_LB_LLID_SIZE); // save source ID // ITEX's implementation is save LLID, I think they are wrong. by Dick Tam for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if((pLBRXInfo[i].status == OCCUPY) && (memcmp(&pOamCell[OAM_FORMAT_LB_SID], pLBRXInfo[i].locationID, OAM_LB_SRCID_SIZE) == 0)) { SidFound = TRUE; pLBRXInfo[i].sur_des_count++; break; } } if(!SidFound) { for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if(pLBRXInfo[i].status == EMPTY) { pLBRXInfo[i].status = OCCUPY; memcpy(pLBRXInfo[i].locationID, &pOamCell[OAM_FORMAT_LB_SID], OAM_LB_SRCID_SIZE); pLBRXInfo[i].sur_des_count++; break; } } } // debug message for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if (pLBRXInfo[i].sur_des_count != 0) { ATM_OAM_DEBUG("The Reomte ID ="); for (j = 0; j < OAM_LB_SRCID_SIZE; j++) ATM_OAM_DEBUG("%x", pLBRXInfo[i].locationID[j]); ATM_OAM_DEBUG(", The Rx Remote Count = %d\n", (unsigned int)pLBRXInfo[i].sur_des_count); } } // Do not have to change cell header // cell is ready to be sent out at the moment return 1; } } // handle FM LB response if (pOAMF4->OAMF4lFaultLBState == FAULT_LB_WAITING) { if ((VCI == 3 || VCI == 4) && (!ucLBIndicator)){ OAMF4_LB_INFO *pLBInfo = NULL; unsigned long LBTag; // verify loopback source ID if (memcmp(&pOamCell[OAM_FORMAT_LB_SID], pOAMF4->OAMF4ucCPID, OAM_LB_SRCID_SIZE) != 0) return 0; // verify correlation tag value LBTag = (unsigned long)(pOamCell[OAM_FORMAT_LB_TAG]); LBTag = (LBTag << 8) | ((unsigned long)(pOamCell[OAM_FORMAT_LB_TAG+1])); LBTag = (LBTag << 8) | ((unsigned long)(pOamCell[OAM_FORMAT_LB_TAG+2])); LBTag = (LBTag << 8) | ((unsigned long)(pOamCell[OAM_FORMAT_LB_TAG+3])); if ((pLBInfo = OAMF4FindLBInfo(LBTag, pOAMF4)) != NULL) { // loopback successful if (pLBInfo->status == FAULT_LB_WAITING) { if (pLBInfo == &pOAMF4->OAMF4LBList[0]) { // all zero LB, don't stop timer // wait 5 second to collect all response // save LLID to pLBInfo->locationID, handle counter for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if (pLBInfo[i].all0_status == FAULT_LB_WAITING) { // match LLID if (memcmp(&pOamCell[OAM_FORMAT_LB_LLID], pLBInfo[i].locationID, OAM_LB_LLID_SIZE) == 0) { pLBInfo[i].count++; return 0; } } else if (pLBInfo[i].all0_status == FAULT_LB_IDLE) { // save LLID pLBInfo[i].all0_status = FAULT_LB_WAITING; pLBInfo[i].count++; memcpy(pLBInfo[i].locationID, &pOamCell[OAM_FORMAT_LB_LLID], OAM_LB_LLID_SIZE); // timestamp pLBInfo[i].rtt = OAMF4GetTimeStamp() - pLBInfo->timestamp; return 0; } } // more than LB_ALL_NODE_SIZE reponse, how to handle it ? } else { pLBInfo->count++; // timestamp pLBInfo->rtt = OAMF4GetTimeStamp() - pLBInfo->timestamp; // stop timer OAMF4StopTimer(&(pLBInfo->timer)); // save LLID memcpy(pLBInfo[i].locationID, &pOamCell[OAM_FORMAT_LB_LLID], OAM_LB_LLID_SIZE); // return to idle state pLBInfo->status = FAULT_LB_STOP; OAMF4ChangeState(pOAMF4); } // Should tell TMN that LB successes. // TODO ... // (or TMN may monitor Variable ulFaultLBRxGoodCell instead.) } } } } ATM_OAM_DEBUG("Leave the OAMF4ProcessRxLBCell\n"); return 0; } __SWAP void OAMRxF4Cell(unsigned char *pOamCell, OAMF4 *pOAMF4) { OAMF5_DEFINE_FLAG(flags); ATM_OAM_DEBUG("Into the RxOAMF4Cell %d\n", pOamCell[OAM_FORMAT_TYPE]); cell_debug(pOamCell, debug_obcell, 0); #if 0 { unsigned int i; for(i=0;i<52;i+=13) { ATM_OAM_DEBUG("Cell(%d) %x %x %x %x %x %x %x %x %x %x %x %x %x\n" ,i ,pOamCell[i] ,pOamCell[i+1] ,pOamCell[i+2] ,pOamCell[i+3] ,pOamCell[i+4] ,pOamCell[i+5] ,pOamCell[i+6] ,pOamCell[i+7] ,pOamCell[i+8] ,pOamCell[i+9] ,pOamCell[i+10] ,pOamCell[i+11],pOamCell[i+12]); } } #endif OAMF5_LOCK(flags); switch (pOamCell[OAM_FORMAT_TYPE]) { #if 0 case FAULT_AIS: case FAULT_RDI: case FAULT_CC: case PERFORMANCE_FPM: case PERFORMANCE_BACKWARD_REPORT: case ACT_DEACT_FPM: case ACT_DEACT_FPM_BR: case ACT_DEACT_CC: case APS_GROUP_PROTECT: case APS_INDIVIDUAL_PROTECT: case SYSTEM_MANAGEMENT: #endif case FAULT_LB: if (pOAMF4 != NULL) { if (OAMF4ProcessRxLBCell(pOamCell, pOAMF4)) { OAMF4SendACell(pOamCell); // send LB cell out pOAMF4->OAMF4ulFaultLBTxGoodCell++; } pOAMF4->OAMF4ulFaultLBRxGoodCell++; } break; default: break; } OAMF5_UNLOCK(flags); ATM_OAM_DEBUG("Leave the OAMRxF4Cell\n"); } ///////////////////////////////////////////////////////////////////////////// // // SetAtmOAMCpid // // Input : pointer of ATMOAMLBID structure // // Output : fail (0)/OK (1) // // Author : Realtek CN SD2 // ///////////////////////////////////////////////////////////////////////////// __SWAP int SetAtmOAMCpid(ATMOAMLBID *pATMOAMLBID) { /* F5 */ if(OAMF5SetVpiVci(pATMOAMLBID->vpi, pATMOAMLBID->vci, &OAMF5_info) == 0) return 0; if(OAMF5TMNSetCPID(pATMOAMLBID->LocID[0], &(pATMOAMLBID->LocID[1]), &OAMF5_info) == 0) return 0; if(OAMF4SetVpiVci(pATMOAMLBID->vpi, pATMOAMLBID->vci, &OAMF4_info) == 0) return 0; if(OAMF4TMNSetCPID(pATMOAMLBID->LocID[0], &(pATMOAMLBID->LocID[1]), &OAMF4_info) == 0) return 0; return 1; } ///////////////////////////////////////////////////////////////////////////// // // StartAtmOAMLoopBack // // Input : pointer of ATMOAMLBReq structure // // Output : fail (0)/OK (1) // // Author : Realtek CN SD2 // ///////////////////////////////////////////////////////////////////////////// __SWAP int StartAtmOAMLoopBack(ATMOAMLBReq *pATMOAMLBReq) { int i; if((pATMOAMLBReq->vci == 3) || (pATMOAMLBReq->vci == 4)) { // F4 LB if (pATMOAMLBReq->vpi != 0 || pATMOAMLBReq->vci != 0) if(OAMF4SetVpiVci(pATMOAMLBReq->vpi, pATMOAMLBReq->vci, &OAMF4_info) == 0) return 0; for (i=0; i<16; i++) { if (pATMOAMLBReq->SrcID[i] != 0) { if (OAMF4TMNSetCPID(pATMOAMLBReq->SrcID[0], &pATMOAMLBReq->SrcID[1], &OAMF4_info) == 0) return 0; break; } } return OAMF4TMNTxLBStart(pATMOAMLBReq->Scope, pATMOAMLBReq->LocID, &(pATMOAMLBReq->Tag), &OAMF4_info); } else { // F5 LB if (pATMOAMLBReq->vpi != 0 || pATMOAMLBReq->vci != 0) if(OAMF5SetVpiVci(pATMOAMLBReq->vpi, pATMOAMLBReq->vci, &OAMF5_info) == 0) return 0; for (i=0; i<16; i++) { if (pATMOAMLBReq->SrcID[i] != 0) { if (OAMF5TMNSetCPID(pATMOAMLBReq->SrcID[0], &pATMOAMLBReq->SrcID[1], &OAMF5_info) == 0) return 0; break; } } return OAMF5TMNTxLBStart(pATMOAMLBReq->Scope, pATMOAMLBReq->LocID, &(pATMOAMLBReq->Tag), &OAMF5_info); } } ///////////////////////////////////////////////////////////////////////////// // // StopAtmOAMLoopBack // // Input : pointer of ATMOAMLBReq structure // // Output : fail (0)/OK (1) // // Author : Realtek CN SD2 // ///////////////////////////////////////////////////////////////////////////// __SWAP int StopAtmOAMLoopBack(ATMOAMLBReq *pATMOAMLBReq) { if((pATMOAMLBReq->vci == 3) || (pATMOAMLBReq->vci == 4)) { // F4 LB return OAMF4TMNTxLBStop(pATMOAMLBReq->Tag, &OAMF4_info); } else { // F5 LB return OAMF5TMNTxLBStop(pATMOAMLBReq->Tag, &OAMF5_info); } } ///////////////////////////////////////////////////////////////////////////// // // GetAtmOAMLoopBackStatus // // Input : pointer of ATMOAMLBState structure // // Output : fail (0)/OK (1) // // Author : Realtek CN SD2 // ///////////////////////////////////////////////////////////////////////////// __SWAP int GetAtmOAMLoopBackStatus(ATMOAMLBState *pATMOAMLBState) { OAMF5_DEFINE_FLAG(flags); if((pATMOAMLBState->vci == 3) || (pATMOAMLBState->vci == 4)) { // F4 LB OAMF4_LB_INFO* LBInfo; OAMF5_LOCK(flags); LBInfo = OAMF4FindLBInfo(pATMOAMLBState->Tag, &OAMF4_info); if(LBInfo == NULL) { OAMF5_UNLOCK(flags); return 0; } if(LBInfo == OAMF4_info.OAMF4LBList) { unsigned int i; for(i=0; i<6; i++) { memcpy(pATMOAMLBState->LocID[i], LBInfo[i].locationID, OAM_LB_LLID_SIZE); pATMOAMLBState->count[i] = LBInfo[i].count; pATMOAMLBState->rtt[i] = LBInfo[i].rtt; pATMOAMLBState->status[i] = LBInfo[i].all0_status; } } else { memset(pATMOAMLBState->LocID, 0x00, 6*16*sizeof(unsigned char)); memset(pATMOAMLBState->count, 0x00, 6*sizeof(unsigned long)); memset(pATMOAMLBState->rtt, 0x00, 6*sizeof(unsigned long)); memset(pATMOAMLBState->status, 0x00, 6*sizeof(long)); memcpy(pATMOAMLBState->LocID[0], LBInfo->locationID, OAM_LB_LLID_SIZE); pATMOAMLBState->count[0] = LBInfo->count; pATMOAMLBState->rtt[0] = LBInfo->rtt; pATMOAMLBState->status[0] = LBInfo->status; } OAMF5_UNLOCK(flags); return 1; } else { // F5 LB OAMF5_LB_INFO* LBInfo; OAMF5_LOCK(flags); LBInfo = OAMF5FindLBInfo(pATMOAMLBState->Tag, &OAMF5_info); if(LBInfo == NULL) { OAMF5_UNLOCK(flags); return 0; } if(LBInfo == OAMF5_info.OAMF5LBList) { unsigned int i; for(i=0; i<6; i++) { memcpy(pATMOAMLBState->LocID[i], LBInfo[i].locationID, OAM_LB_LLID_SIZE); pATMOAMLBState->count[i] = LBInfo[i].count; pATMOAMLBState->rtt[i] = LBInfo[i].rtt; pATMOAMLBState->status[i] = LBInfo[i].all0_status; } } else { memset(pATMOAMLBState->LocID, 0x00, 6*16*sizeof(unsigned char)); memset(pATMOAMLBState->count, 0x00, 6*sizeof(unsigned long)); memset(pATMOAMLBState->rtt, 0x00, 6*sizeof(unsigned long)); memset(pATMOAMLBState->status, 0x00, 6*sizeof(long)); memcpy(pATMOAMLBState->LocID[0], LBInfo->locationID, OAM_LB_LLID_SIZE); pATMOAMLBState->count[0] = LBInfo->count; pATMOAMLBState->rtt[0] = LBInfo->rtt; pATMOAMLBState->status[0] = LBInfo->status; } OAMF5_UNLOCK(flags); return 1; } } ///////////////////////////////////////////////////////////////////////////// // // GetAtmOAMLoopBackStatusFE // // Input : pointer of ATMOAMLBRXState structure // // Output : fail (0)/OK (1) // // Author : Realtek CN SD2 // ///////////////////////////////////////////////////////////////////////////// __SWAP int GetAtmOAMLoopBackStatusFE(ATMOAMLBRXState *pATMOAMLBRXState) { OAMF5_DEFINE_FLAG(flags); if((pATMOAMLBRXState->vci == 3) || (pATMOAMLBRXState->vci == 4)) { // F4 LB unsigned int i; OAMF5_LOCK(flags); for(i=0; i<LB_ALL_NODE_SIZE; i++) { memcpy(pATMOAMLBRXState->LocID[i], OAMF4_info.LBRXList[i].locationID, OAM_LB_LLID_SIZE); pATMOAMLBRXState->count[i] = OAMF4_info.LBRXList[i].sur_des_count; pATMOAMLBRXState->status[i] = OAMF4_info.LBRXList[i].status; } OAMF5_UNLOCK(flags); return 1; } else { // F5 LB unsigned int i; OAMF5_LOCK(flags); for(i=0; i<LB_ALL_NODE_SIZE; i++) { memcpy(pATMOAMLBRXState->LocID[i], OAMF5_info.LBRXList[i].locationID, OAM_LB_LLID_SIZE); pATMOAMLBRXState->count[i] = OAMF5_info.LBRXList[i].sur_des_count; pATMOAMLBRXState->status[i] = OAMF5_info.LBRXList[i].status; } OAMF5_UNLOCK(flags); return 1; } } #ifdef ATM_OAM_TEST /* For Test only */ static unsigned long current_tag; void OAMTestInit(void) { ATMOAMLBID lbid; unsigned int i; lbid.vpi = 5; lbid.vci = 35; for(i=0; i<16; i++) { lbid.LocID[i] = i+1; } SetAtmOAMCpid(&lbid); } void OAMF4LbTest(void) { ATMOAMLBReq req; req.vpi = 0; req.vci = 3; req.Scope = 1; req.LocID[0] = 0x86; req.LocID[1] = 0x70; req.LocID[2] = 0x86; req.LocID[3] = 0x70; req.LocID[4] = 0x86; req.LocID[5] = 0x70; req.LocID[6] = 0x86; req.LocID[7] = 0x70; req.LocID[8] = 0x86; req.LocID[9] = 0x70; req.LocID[10] = 0x86; req.LocID[11] = 0x70; req.LocID[12] = 0x86; req.LocID[13] = 0x70; req.LocID[14] = 0x86; req.LocID[15] = 0x70; StartAtmOAMLoopBack(&req); current_tag = req.Tag; ATM_OAM_DEBUG("Tag %u\n", (unsigned int)req.Tag); } //0=>fail, 1=>success __SWAP int OAMF5LbTest(ATMOAMLBReq *pucPtr) { if (0==StartAtmOAMLoopBack(pucPtr)) { printk("OAMF5LbTest no responce! Test stop.\n"); return 0; }; current_tag = pucPtr->Tag; ATM_OAM_DEBUG("Tag %u\n", (unsigned int)pucPtr->Tag); return 1; } void OAMF5StopLbTest(void); static OAMF5Timer oamf5lbtest_timer; __SWAP void OAMF5LbTestTimer(ATMOAMLBReq *pucPtr) { if (0==OAMF5LbTest(pucPtr)) { OAMF5StopLbTest(); return; }; // start timer for next OAM Tx init_timer (&oamf5lbtest_timer.timer); oamf5lbtest_timer.timer.expires = jiffies + oamf5lbtest_timer.ulTimeout; oamf5lbtest_timer.timer.function = (void (*)(unsigned long))OAMF5LbTestTimer; oamf5lbtest_timer.timer.data = (unsigned long)(pucPtr); add_timer(&oamf5lbtest_timer.timer); oamf5lbtest_timer.occupied = OCCUPY; } __SWAP void OAMF5TMNAllStop(OAMF5 *pOAMF5) { int i; for (i = 0; i < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; i++) { if (pOAMF5->OAMF5LBList[i].status != FAULT_LB_IDLE) { // stop timer OAMF5StopTimer(&(pOAMF5->OAMF5LBList[i].timer)); }; if (i == 0) i = LB_ALL_NODE_SIZE-1; } return; } static ATMOAMLBReq gblpucPtr; __SWAP void OAMF5ContiLbTest(ATMOAMLBReq *pucPtr) { if(oamf5lbtest_timer.occupied == OCCUPY) { ATM_OAM_TRACE("OAMF5ContiLbTest (oamf5lbtest_timer.occupied == OCCUPY)\n"); return; } oamf5lbtest_timer.ulTimeout = (300 * HZ) / 1000; memcpy(&gblpucPtr, pucPtr, sizeof(ATMOAMLBReq)); OAMF5TMNAllStop(&OAMF5_info); OAMF5Init(pucPtr->vpi, pucPtr->vci, &OAMF5_info); OAMF5LbTestTimer(&gblpucPtr); } __SWAP void OAMF5StopLbTest(void) { if(oamf5lbtest_timer.occupied == EMPTY) return; OAMF5TMNAllStop(&OAMF5_info); del_timer(&oamf5lbtest_timer.timer); oamf5lbtest_timer.occupied = EMPTY; } #if 0 void OAMF4LbTestTimer(struct timer_list *id, int arg) { struct itimerspec time_value; if (timer_create(CLOCK_REALTIME, NULL, &oamf5lbtest_timer) == -1) { ATM_OAM_DEBUG("OAMTest8Timer timer_create Fail\n"); return; } if (timer_connect(oamf5lbtest_timer, (VOIDFUNCPTR)OAMF4LbTestTimer, (int) NULL) == -1) { ATM_OAM_DEBUG("OAMTest8Timer timer_connect Fail\n"); return; } bzero((char*)&time_value, sizeof(struct itimerspec)); time_value.it_value.tv_sec = 1; time_value.it_value.tv_nsec = 0; if (timer_settime(oamf5lbtest_timer, 0, &time_value, NULL) == -1) { ATM_OAM_DEBUG("OAMTest8Timer timer_settime Fail\n"); return; } OAMF4LbTest(); } void OAMF4ContiLbTest(void) { OAMF4LbTestTimer(0, 0); } void OAMF5Dump(void) { // ITEX_ATM_OAM_STATUS int i, j, k; OAMF5_LB_INFO *pLBInfo = NULL; OAMF5_LBRX_INFO *pLBRXInfo = NULL; for (k = 0; k < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; k++) { pLBInfo = &(OAMF5_info.OAMF5LBList[k]); ATM_OAM_TRACE("[FM LB Info] Tag:%d, status:%d, count:%d, timestamp:%u ,rtt:%u, LLID:" ,pLBInfo->tag ,pLBInfo->status ,pLBInfo->count ,pLBInfo->timestamp ,pLBInfo->rtt); for (j = 0; j < OAM_LB_LLID_SIZE; j++) ATM_OAM_TRACE("%x", pLBInfo->locationID[j]); ATM_OAM_TRACE("\n"); } if ((pLBRXInfo = OAMF5FindLBRXInfo(&OAMF5_info)) != NULL) { for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if (pLBRXInfo[i].sur_des_count != 0) { ATM_OAM_TRACE("The Reomte ID ="); for (j = 0; j < OAM_LB_LLID_SIZE; j++) ATM_OAM_TRACE("%x", pLBRXInfo[i].locationID[j]); ATM_OAM_TRACE(", The Rx Remote Count = %d\n", pLBRXInfo[i].sur_des_count); } } } } void OAMF4Dump(void) { // ITEX_ATM_OAM_STATUS int i, j, k; OAMF4_LB_INFO *pLBInfo = NULL; OAMF4_LBRX_INFO *pLBRXInfo = NULL; for (k = 0; k < LB_ALL_NODE_SIZE+LB_TABLE_SIZE; k++) { pLBInfo = &(OAMF4_info.OAMF4LBList[k]); ATM_OAM_TRACE("[FM LB Info] Tag:%d, status:%d, count:%d, timestamp:%u ,rtt:%u, LLID:" ,pLBInfo->tag ,pLBInfo->status ,pLBInfo->count ,pLBInfo->timestamp ,pLBInfo->rtt); for (j = 0; j < OAM_LB_LLID_SIZE; j++) ATM_OAM_TRACE("%x", pLBInfo->locationID[j]); ATM_OAM_TRACE("\n"); } if ((pLBRXInfo = OAMF4FindLBRXInfo(&OAMF4_info)) != NULL) { for (i = 0; i < LB_ALL_NODE_SIZE; i++) { if (pLBRXInfo[i].sur_des_count != 0) { ATM_OAM_TRACE("The Reomte ID ="); for (j = 0; j < OAM_LB_LLID_SIZE; j++) ATM_OAM_TRACE("%x", pLBRXInfo[i].locationID[j]); ATM_OAM_TRACE(", The Rx Remote Count = %d\n", pLBRXInfo[i].sur_des_count); } } } } #endif #endif // ATM_OAM_TEST #ifdef REMOTE_MANAGEMENT_ENABLE #define CRC32(c,crc) (crc32tab[((size_t)(crc>>24) ^ (c)) & 0xff] ^ (((crc) << 8))) static unsigned long crc32tab[256] = { 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L }; __SWAP static unsigned long calc_crc(char *mem, int len, unsigned initial) { unsigned crc; crc = initial; for(;len;mem++,len--) { crc = CRC32(*mem, crc); } return(crc); } #define crc32sar( crc, mem, len) calc_crc(mem, len, crc) #define ATM_HDR_GFC_SHIFT 28 #define ATM_HDR_VPI_SHIFT 20 #define ATM_HDR_VCI_SHIFT 4 #define ATM_HDR_PTI_SHIFT 1 //Soft SAR for Remote Management Channel 0/16 //Encode AAL5 __SWAP #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif struct sk_buff *sarlib_encode_aal5 (struct sk_buff *skb) { int length, pdu_length; unsigned char *trailer; unsigned char *pad; uint crc = 0xffffffff; //ATM_OAM_TRACE("sarlib_encode_aal5 (0x%08x) called\n", skb); /* determine aal5 length */ pdu_length = skb->len; length = ((pdu_length + 8 + 47) / 48) * 48; if ( skb_tailroom(skb) < (length - pdu_length) ) { struct sk_buff *out; /* get new skb */ out = dev_alloc_skb( SAR_TX_Buffer_Size ); if (!out) return NULL; //ATM_OAM_TRACE("out->data = 0x%08x\n", out->data); //ATM_OAM_TRACE("sarlib_encode_aal5 pdu length %d, allocated length %d\n", skb->len, length); memcpy (out->data, skb->data, skb->len); skb_put(out, skb->len); dev_kfree_skb_irq(skb); skb = out; } //ATM_OAM_TRACE("skb->data = 0x%08x\n", skb->data); /* note end of pdu and add length */ pad = skb_put(skb, length - pdu_length); trailer = skb->tail - 8; //ATM_OAM_TRACE("trailer = 0x%08x\n", trailer); /* zero padding space */ memset(pad, 0, length - pdu_length-8); /* add trailer */ *trailer++ = (unsigned char) 0; /* UU = 0 */ *trailer++ = (unsigned char) 0; /* CPI = 0 */ *trailer++ = (unsigned char) (pdu_length >> 8); *trailer++ = (unsigned char) (pdu_length & 0xff); //xprintfk("CRC on %08xh, len %d\n",skb->data, length-4); crc = ~crc32sar(crc, skb->data, length-4); *trailer++ = (unsigned char) (crc >> 24); *trailer++ = (unsigned char) (crc >> 16); *trailer++ = (unsigned char) (crc >> 8); *trailer++ = (unsigned char) (crc & 0xff); //ATM_OAM_TRACE("sarlib_encode_aal5 return 0x%08x (length %d)\n", skb, skb->len); return skb; } #ifdef RM_TEST #include <asm/checksum.h> unsigned short gInUDPPort=0; #endif //Out Band Channel AAL5 Tx //return 0: fail; 1:success __SWAP #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif int OutBandAAL5Tx(unsigned short vpi, unsigned short vci, struct sk_buff *skb) { struct sk_buff *aal5_skb; int i, ret=1; char *pCell; unsigned char pti, gfc; unsigned int atmHeader; if (skb==NULL) { ATM_OAM_TRACE("Remote Management: NULL tx packet\n"); return 0; }; #ifdef RM_TEST { //add llc/mac/IP/UDP header struct sk_buff *testskb; unsigned char *tail; unsigned short *ptr; char mac[14]={0x00, 0x50, 0xba, 0x0f, 0x8a, 0xf2, 0x00, 0xe0, 0x4c, 0x86, 0x70, 0x01, 0x08, 0x00}; char iphdr[20]={0x45, 0x00, 0x00, 0x00, //IP data gram size 0xf7, 0x70, 0x00, 0x00, 0x80, 0x11, 0x00, 0x00, //IP checksum 0xc0, 0xa8, 0x01, 0x01, //SIP 0xc0, 0xa8, 0x01, 0x96, //DIP }; testskb = dev_alloc_skb( SAR_TX_Buffer_Size ); tail=testskb->data; skb_put(testskb, 10); memcpy(tail, FrameHeader_1483[0], 10); tail+=10; //MAC skb_put(testskb, 14); memcpy(tail, mac, 14); tail+=14; //IP skb_put(testskb, sizeof(iphdr)); *((unsigned short*)&iphdr[2])=20+8+skb->len; //length *((unsigned short*)&iphdr[10])=ip_compute_csum(iphdr, 20); //IP checksum memcpy(tail, iphdr, sizeof(iphdr)); tail+=sizeof(iphdr); //UDP skb_put(testskb, 8); ptr=(unsigned short *)tail; *(ptr++)=161; //sport=161 *(ptr++)=gInUDPPort; //dport *(ptr++)=8+skb->len; //length *ptr=0; tail+=8; //snmp skb_put(testskb, skb->len); memcpy(tail, skb->data, skb->len); //*(ptr)=csum_tcpudp_magic(); //UDP checksum dev_kfree_skb_irq(skb); skb = testskb; vpi = 1; vci = 39; /*for (i=0;i<testskb->len;i++) { if ((i&0x0f)==0) printk("\n"); printk("%02X ", testskb->data[i]); }; printk("\n"); */ } #endif if ((aal5_skb=sarlib_encode_aal5(skb))==NULL) return 0; skb_oamaal5_debug(aal5_skb, debug_obaal5, 1); //segment & tx gfc=0; pti=0; //cell header atmHeader = ((unsigned long)gfc << ATM_HDR_GFC_SHIFT) | ((unsigned long)vpi << ATM_HDR_VPI_SHIFT) | ((unsigned long)vci << ATM_HDR_VCI_SHIFT) | ((unsigned long)pti << ATM_HDR_PTI_SHIFT) ; //Enable_LoopBack(sar_dev); for (i=0;i<(aal5_skb->len/48);i++) { pCell=kmalloc(OAM_CELL_SIZE, GFP_KERNEL); if (pCell==NULL) { ret = 0; break; } if (i==(aal5_skb->len/48)-1) //set pti bit in last cell atmHeader|=0x2; memcpy(&pCell[0], &atmHeader, 4); //data memcpy(&pCell[4], (aal5_skb->data+i*48), 48); //printk("AAL5 TX %d/%d:\n", vpi, vci); //Dump(pCell, 52); if (OAMFSendACell(pCell, 0)==0) { //send fail ret = 0; break; }; }; dev_kfree_skb_irq(aal5_skb); //Disable_LoopBack(sar_dev); return ret; } //Out Band Channel AAL5 Rx //remember to add 10 bytes for llc header when skb is created //return 0: fail, crc or size mismatch, drop the skb; // 1: current assemble success; // 2: full packet assemble success struct sk_buff *outband_rx_skb=NULL; __SWAP #ifdef CONFIG_RTL867X_KERNEL_MIPS16_DRIVERS_ATM __NOMIPS16 #endif int OutBandAAL5Rx(struct atm_vcc *atm_vcc, char *rx_bfr) { unsigned int atmHeader; int length; uint crc = 0xffffffff; unsigned char *bfr=rx_bfr, *tail; unsigned int upushAddr; if (bfr==NULL) { ATM_OAM_TRACE("Remote Management: NULL rx packet buffer\n"); goto drop_rx_bfr; }; if (outband_rx_skb==NULL) { outband_rx_skb = dev_alloc_skb( SAR_RX_Buffer_Size ); //create a new skb for next packet if (outband_rx_skb==NULL) { ATM_OAM_TRACE("Remote Management: Allocate rx skb fail\n"); goto drop_rx_bfr; }; }; //printk("AAL5 RX:\n"); //Dump(rx_bfr, 52); //cell header memcpy(&atmHeader, bfr, 4); bfr+=4; //skip cell header //Assemble cell if ((outband_rx_skb->len+48)>SAR_RX_Buffer_Size) { //size too large ATM_OAM_TRACE("Remote Management: packet size error %d\n", (outband_rx_skb->len+48)); goto drop_rx_bfr; }; //Assemble cell tail=skb_put(outband_rx_skb, 48); memcpy(tail, bfr, 48); if (!(atmHeader&2)) { //normal cell //kfree(rx_bfr); return 1; }; //last cell crc = crc32sar(crc, outband_rx_skb->data, outband_rx_skb->len)^0xc704dd7b; if (crc) { //crc error ATM_OAM_TRACE("Remote Management: packet CRC error 0x%08x\n", crc); goto drop_rx_bfr; }; //remove AAL5 trailer length = ((bfr[42]&0x00ff)<<8)|(bfr[43]&0x00ff); skb_trim(outband_rx_skb, length); #ifdef RM_TEST if (*((unsigned short*)&outband_rx_skb->data[0x2e])==161) { gInUDPPort = *((unsigned short*)&outband_rx_skb->data[0x2c]); } else { //drop non SNMP packet goto drop_rx_bfr; }; //remove llc/MAC/IP/UDP header skb_pull(outband_rx_skb, 52); #endif skb_oamaal5_debug(outband_rx_skb, debug_obaal5, 0); // end of our responsability upushAddr = (unsigned int)atm_vcc->push; if (((0xff000000 & upushAddr)== 0x80000000) && !(upushAddr & 0x03)) atm_vcc->push(atm_vcc, outband_rx_skb); else { printk("fatal error occur:%s %d atmvcc->push:0x%x\n", __FILE__, __LINE__, upushAddr); goto drop_rx_bfr; } outband_rx_skb = NULL; //create a new skb for next packet //ATM_OAM_TRACE("Remote Management: get rx packet\n"); //kfree(rx_bfr); return 2; drop_rx_bfr: if (outband_rx_skb) { dev_kfree_skb_irq(outband_rx_skb); outband_rx_skb = NULL; }; //if (rx_bfr) // kfree(rx_bfr); return 0; } #endif //REMOTE_MANAGEMENT_ENABLE //Soft SAR for Remote Management Channel 0/16 #endif // ATM_OAM int get_wlan_member(void); void set_wlan_vid(int vid); void pp_ifgroup_setup() { extern int rtl8672_l2flush(void); #ifdef CONFIG_RTL867X_PACKET_PROCESSOR #ifdef CONFIG_EXT_SWITCH sar_private *cp = sar_dev; if(enable_port_mapping){ int ch_no,k; extern struct r8305_struc rtl8305_info; extern void set_intf_vid(int ch,int vlanid); for(ch_no=0;ch_no<total_pvc_number;ch_no++){ struct atm_vcc * atm_vcc = cp->vcc[ch_no].dev_data; for (k=0; k<SW_PORT_NUM; k++) { if (atm_vcc->ifgrp.member == rtl8305_info.vlan[k].member) { set_intf_vid(ch_no,rtl8305_info.vlan[k].vid); break; } } for (k=0; k<SW_PORT_NUM; k++) { if (get_wlan_member() == rtl8305_info.vlan[k].member){ set_intf_vid(15,rtl8305_info.vlan[k].vid); set_wlan_vid(rtl8305_info.vlan[k].vid); break; } } } } #endif rtl8672_l2flush(); #endif }