/**************************************************************************************** * dda_txrx.c * Linux atm module implementation. * * 2006 (c) Texas Instruments Inc. * * 08/15/06 MK CQ10847: Fixed available_tx_bufs allocation for all channels. * 08/15/06 MK CQ10842: Fixed display of SAR Failed to send packet retval error msg * in multi pvcs test. * 9/15/2006 AV Enabling the free-ing of the skbs in the loop-back mode. * 10/6/2006 EP Fixing in-correct cache invalidation in the DDA_atm_send function. * 10/9/2006 AV Adding Tasklet mode support for Tx and Rx. * 10/25/2006 AV Changing the Tx and Rx tasklets to use the Service Max from DDC structure, * not from a channel. Fixing file header comments. * 10/31/2006 AV Consolidated the calls for the DDC_sar_handle_tx_process() and DDC_sar_handle_rx_process(). * 12/07/2006 EP Fixed TurboDSL to work in both BE and LE mode. * 01/31/2007 EP Enabled passing packets with NULL dev for PPPoA connection *********************************************************************************************/ /* All the kernel include files should be included from here. */ #include "dda_ti_dsl.h" //AV_26 #define __NO__VOICE_PATCH__ #define DDA_atm_kfree_skb(x) dev_kfree_skb(x) /* prototypes */ //AV_26 static int DDA_atm_queue_packet_to_sar (void *vcc1, void *skb1, int dmachan); /* * * Turbo DSL Implementaion * * Zhicheng Tang ztang@ti.com * * 2003 (c) Texas Instruments Inc. * */ /* defines and variables */ #define RFC2684_BRIDGED_HDR_SIZE 10 unsigned char LLC_BRIDGED_HEADER_2684[RFC2684_BRIDGED_HDR_SIZE] = {0xAA, 0xAA, 0x03, 0x00, 0x80, 0xC2, 0x00, 0x07, 0x00, 0x00}; #define RFC2684_ROUTED_HDR_SIZE 6 unsigned char LLC_ROUTED_HEADER_2684[RFC2684_ROUTED_HDR_SIZE] = {0xAA, 0xAA, 0x03, 0x00, 0x00, 0x00}; #define PPP_LLC_HDR_SIZE 4 unsigned char PPP_LLC_HEADER[PPP_LLC_HDR_SIZE] = {0xFE, 0xFE, 0x03, 0xCF}; /* struct definition */ enum { AAL5_ENCAP_PPP_LLC, AAL5_ENCAP_PPP_VCMUX, AAL5_ENCAP_RFC2684_LLC_BRIDGED, AAL5_ENCAP_RFC2684_LLC_ROUTED }; /* Etherent header */ typedef struct _turbodsl_ether_header { unsigned char dst_mac_addr[6]; unsigned char src_mac_addr[6]; unsigned short ether_type; } turbodsl_ether_header_t; /* Ip header define */ typedef struct _turbodsl_ip_header { unsigned short vit; unsigned short total_length; unsigned short ip_id; unsigned char flag; /* bit 0 = 0, bit1 = don't fragment, bit2=more frag */ unsigned char fragment_offset; /* offset include remaining 5 bits above, which make it 13 bits */ unsigned char time_to_live; unsigned char protocol; unsigned short checksum; unsigned int src_ip; unsigned int dst_ip; } turbodsl_ip_header_t; /* Arp packet define */ typedef struct _turbodsl_arp_header { unsigned short hardware_type; unsigned short protocol_type; unsigned char h_len; unsigned char p_len; unsigned short operation ; unsigned char snd_hw_address[6]; unsigned char snd_pt_address[4]; unsigned char dst_hw_address[6]; unsigned char dst_pt_address[4]; } turbodsl_arp_header_t; #define FIN_FLAG 1 #define SYN_FLAG 1<<1 #define RST_FLAG 1<<2 #define PSH_FLAG 1<<3 #define ACK_FLAG 1<<4 #define URG_FLAG 1<<5 typedef struct _turbodsl_tcp_header { unsigned short src_port; unsigned short dst_port; unsigned int seq_num; unsigned int ack_num; unsigned char offset; /* only bits 4-7 are for offset */ unsigned char flags; /* bits: 0-FIN, 1-SYN, 2-RST, 3-PSH, 4-ACK, 5-URG */ unsigned short windows; unsigned short checksum; unsigned short urgent_ptr; } turbodsl_tcp_header_t; /* TurboDSL function declarations. */ __inline__ static int turbodsl_check_aal5_encap_type(unsigned char *pData); static int turbodsl_check_priority_type(unsigned char *pData); /* ATM device operations */ #ifdef CPATM_TASKLET_MODE void DDA_atm_handle_tasklet (unsigned long data); struct tasklet_struct DDA_atm_tasklet; #endif int __guDbgLevel = 0; //static spinlock_t chan_init_lock; /*************************************************************************** * Turbo DSL Implementaion * * Zhicheng Tang ztang@ti.com * * 2003 (c) Texas Instruments Inc. * * Function: turbodsl_check_aal5_encap_type * Descripation: Determine AAL5 Encapsulation type * Input: * unsigned char *pData, AAL5 Packet buffer pointer ****************************************************************************/ __inline__ static int turbodsl_check_aal5_encap_type(unsigned char *pData) { if(!DDA_atm_memcmp(pData, LLC_BRIDGED_HEADER_2684, 6)) return AAL5_ENCAP_RFC2684_LLC_BRIDGED; if(!DDA_atm_memcmp(pData, LLC_ROUTED_HEADER_2684, 6)) return AAL5_ENCAP_RFC2684_LLC_ROUTED; if(!DDA_atm_memcmp(pData, PPP_LLC_HEADER, sizeof(PPP_LLC_HDR_SIZE))) return AAL5_ENCAP_PPP_LLC; return AAL5_ENCAP_PPP_VCMUX; } /*************************************************************************** * Turbo DSL Implementaion * * Zhicheng Tang ztang@ti.com * * 2003 (c) Texas Instruments Inc. * * Function: turbodsl_check_priority_type * Descripation: Determine AAL5 Encapsulation type * Input: * unsigned char *pData, AAL5 Packet buffer pointer. * short vpi, VPI. * int vci, VCI ****************************************************************************/ static int turbodsl_check_priority_type(unsigned char *pData) { unsigned char *pP; turbodsl_ip_header_t *pIp; turbodsl_tcp_header_t *pTcp; dgprintf(2, "turbodsl_check_priority_type ==>\n"); pP = pData; switch(turbodsl_check_aal5_encap_type(pData)) { case AAL5_ENCAP_RFC2684_LLC_BRIDGED: pP += RFC2684_BRIDGED_HDR_SIZE; //skip off aal5 encap pP += 12; //skip of mac address if(((*pP != 0x88) || (*(pP+1) != 0x64)) && ((*pP != 0x08) || (*(pP+1) != 0x00))) { //Not an IP packet return 1; } pP +=2; //skip ether type if((*pP == 0x88) && (*(pP+1) == 0x64)) { pP += 6; } break; case AAL5_ENCAP_RFC2684_LLC_ROUTED: pP += RFC2684_ROUTED_HDR_SIZE; //skip of encap pP += 2; //skip ether type break; case AAL5_ENCAP_PPP_LLC: pP += sizeof(PPP_LLC_HEADER); if((*pP == 0xff) && (*(pP+1) == 0x03)) //ppp hdlc header pP += 2; break; case AAL5_ENCAP_PPP_VCMUX: if((*pP == 0xff) && (*(pP+1) == 0x03)) //ppp hdlc header pP += 2; break; default: return 1; } pIp = (turbodsl_ip_header_t *)pP; if(pIp->vit != BYTE_SWAP16(0x0045)) { //Not a IP packet return 1; } if(pIp->protocol != 0x06) { //not tcp packet return 1; } pTcp = (turbodsl_tcp_header_t *)(pP + sizeof(turbodsl_ip_header_t)); if((pTcp->flags & ACK_FLAG) && ((BYTE_SWAP16(((pIp->total_length>>8)&0x00FF) | ((pIp->total_length<<8)&0xFF00))) <= 40)) { return 0; } return 1; } /************************************************************ NEED FN HEADER ************************************************************/ int DDA_atm_send (struct atm_vcc *vcc, struct sk_buff *skb) { Tn7AtmPrivate *priv; int bret; int chan; dgprintf (4, "DDA_atm_send()\n"); #ifdef TIATM_INST_SUPP psp_trace_par (ATM_DRV_PKT_TX_ENTER, skb->len); #endif priv = (Tn7AtmPrivate *) vcc->dev->dev_data; /* * add vcc field in skb for clip inATMARP fix */ ATM_SKB (skb)->vcc = vcc; /* * check for dsl line connection */ //DDA_printf("%s: Called with skb %p and len = %d \n", __FUNCTION__, skb, skb->len); if (priv->dslPriv->lConnected != 1 || priv->xmitStop == 1) { dgprintf (4, "dsl line down\n"); if (vcc->pop) { vcc->pop (vcc, skb); } else { //AV_26 //DDA_atm_kfree_skb (skb); dev_kfree_skb(skb); } //AV_26 priv->stats->tx_errors++; return 1; } /* * send over clear eoc */ if (vcc->vpi == CLEAR_EOC_VPI && vcc->vci == CLEAR_EOC_VCI) { bret = DDC_dsl_clear_eoc_send (priv->dslPriv, (unsigned char *) skb->data, skb->len, 0); if (vcc->pop) { vcc->pop (vcc, skb); } else { //AV_26 //DDA_atm_kfree_skb (skb); dev_kfree_skb(skb); } } else // send over atm channel { /* * check whether PVC is closing */ chan = DDC_atm_lut_find (priv->dslPriv, vcc->vpi, vcc->vci); /* set br2684 dev pointer */ //priv->dslPriv->lut[chan].net_device = skb->dev; if (chan == ATM_NO_DMA_CHAN || priv->dslPriv->lut[chan].bClosing == 1) { dgprintf (4, "can find sar channel\n"); netif_stop_queue(skb->dev); if (vcc->pop) { vcc->pop (vcc, skb); } else { //AV_26 //DDA_atm_kfree_skb (skb); dev_kfree_skb(skb); } priv->stats->tx_errors++; return 1; } /*** Viren: Added the 3rd parameter to the function to avoid calling the function again ***/ bret = DDA_atm_queue_packet_to_sar (vcc, skb, chan); } #ifdef TIATM_INST_SUPP psp_trace_par (ATM_DRV_PKT_TX_EXIT, bret); #endif return bret; } /************************************************************ NEED FN HEADER ************************************************************/ static int DDA_atm_queue_packet_to_sar (void *vcc1, void *skb1, int dmachan) { struct atm_vcc *vcc; struct sk_buff *skb; int priority = 0; Tn7AtmPrivate *priv; int retval; int flag = 0; vcc = (struct atm_vcc *) vcc1; skb = (struct sk_buff *) skb1; priv = (Tn7AtmPrivate *) vcc->dev->dev_data; dgprintf (4, "vcc->vci=%d\n", vcc->vci); // turbo dsl TCP ack check if (priv->dslPriv->bTurboDsl) priority = turbodsl_check_priority_type (skb->data); #ifdef LINUX_2_4 /*EP20061207: only for Linux 2.4*/ // skb priority check if (priority != 0) { if ((skb->cb[47]) >> 1) priority = 1; else priority = 0; } #endif if (skb->dev != NULL) { if(priv->available_tx_bufs[dmachan].counter <= 0) { netif_stop_queue(skb->dev); flag = 1; #ifndef NO_EP_SAR if (vcc->pop) vcc->pop (vcc, skb); else DDA_atm_kfree_skb (skb); priv->stats->tx_dropped++; return -1; #endif } atomic_dec (&priv->available_tx_bufs[dmachan]); } #if 0 /*EP20070131: enable passing packets with NULL dev for PPPoA connection*/ else { printk("%s: Got NULL dev pointer in skb 0x%p \n",__FUNCTION__, skb); if (vcc->pop) vcc->pop (vcc, skb); else DDA_atm_kfree_skb (skb); return -1; } #endif if ((retval = DDC_sar_send_packet (priv->dslPriv, dmachan, skb, skb->data, skb->len, priority))) { //dgprintf (1, "failed to send packet\n"); //printk("%s: SAR Failed to send packet retval = %#x\n", __FUNCTION__, retval); if (vcc->pop) vcc->pop (vcc, skb); else DDA_atm_kfree_skb (skb); /* * PANKAJ: In case of error we need to wakeup the queue again. */ //if ((skb->dev != NULL) /*&& (EnableQoS)*/) if ((skb->dev != NULL) && (flag)) { netif_wake_queue(skb->dev); flag = 0; } priv->stats->tx_dropped++; } return retval; } /* functions needed by SAR HAL */ /************************************************************ NEED FN HEADER ************************************************************/ int DDA_atm_send_complete (void *skb1) { Tn7AtmPrivate *priv; struct sk_buff *skb; struct atm_vcc *vcc; static unsigned int ledticks; int chan; dgprintf (4, "DDA_atm_send_complete()\n"); #ifdef TIATM_INST_SUPP psp_trace (ATM_DRV_PKT_TX_COMPLETE_ENTER); #endif skb = (struct sk_buff *) skb1; vcc = ATM_SKB (skb)->vcc; if (vcc) { dgprintf (4, "vcc->vci=%d\n", vcc->vci); priv = (Tn7AtmPrivate *) vcc->dev->dev_data; chan = DDC_atm_lut_find (priv->dslPriv, vcc->vpi, vcc->vci); if (chan != ATM_NO_DMA_CHAN && priv->dslPriv->lut[chan].bClosing != 1) { if (vcc->pop) { dgprintf (5, "free packet\n"); vcc->pop (vcc, skb); } else { //DDA_atm_kfree_skb (skb); dev_kfree_skb_any(skb); } atomic_inc(&priv->available_tx_bufs[chan]); } else { //DDA_atm_kfree_skb (skb); dev_kfree_skb_any(skb); } } //AV_26 //priv->stats.tx_packets++; //priv->stats->tx_packets++; //AV:Add when we do device stats. atomic_inc (&vcc->stats->tx); //AV_26 //#if 0 #if defined (CONFIG_LED_MODULE) || defined (CONFIG_MIPS_AVALANCHE_COLORED_LED) if ((ledticks != jiffies) && (hnd_LED_0)) { ledticks = jiffies; TN7DSL_LED_ACTION(hnd_LED_0, DDC_MOD_ADSL, DEF_ADSL_ACTIVITY); } #endif //#endif /* * track number of buffer used */ dgprintf (4, "DDA_atm_send_complete() done\n"); /* * PANKAJ: Once the packet has been transmitted and the interrupt * acknowledged the SAR driver is free to take another packet for * transmission. */ //AV_26 Probably Not needed //#if 0 if (skb->dev != NULL) netif_wake_queue (skb->dev); //#endif #ifdef TIATM_INST_SUPP psp_trace_par (ATM_DRV_PKT_TX_COMPLETE_EXIT, skb->len); #endif dgprintf (4, "DDA_atm_send_complete() done\n"); return 0; } /************************************************************ NEED FN HEADER ************************************************************/ void *DDA_atm_allocate_rx_skb (void *os_dev, void **os_receive_info, unsigned int size, void *vcc) { struct sk_buff *skb; struct atm_vcc *vcc1; dgprintf (4, "DDA_atm_allocate_rx_skb size=%d\n", size); size = ((size + 3) & 0xfffffffc); //AV_26 Changing for 8.1_B&F #ifdef TI_STATIC_ALLOCATIONS skb = ti_alloc_skb (size, GFP_ATOMIC); #else skb = dev_alloc_skb (size); #endif if (skb == NULL) { dgprintf (4, "rx allocate skb failed\n"); return NULL; } *os_receive_info = (void *) skb; vcc1 = (struct atm_vcc *) vcc; ATM_SKB (skb)->vcc = vcc1; /* Possible condition for OAM channel */ if (vcc1 != NULL) { if (vcc1->qos.txtp.icr == 1) // hack to have this field indicating // it is pppoa channel. { skb_reserve (skb, 2); // reserve 2 bytes for pppoa no copy } //AV_26 if(!skb->sk) //bk //skb->sk = vcc1->sk; skb->sk = &vcc1->sk; //end bk //printk("%s: vcc->sk is 0x%p \n", __FUNCTION__, vcc1->sk); } else { //AV_26 //printk("%s: vcc is NULL \n", __FUNCTION__); } return (skb->data); } /************************************************************ NEED FN HEADER ************************************************************/ void DDA_atm_free_rx_skb (void *skb, void *pmem) { dgprintf (4, "DDA_atm_free_rx_skb\n"); DDA_atm_kfree_skb ((struct sk_buff *) skb); } /************************************************************ NEED FN HEADER ************************************************************/ int DDA_atm_receive (void *os_dev, int ch, unsigned int packet_size, void *os_receive_info, void *data) { Tn7AtmPrivate *priv; struct atm_dev *dev; struct sk_buff *skb; struct atm_vcc *vcc; static unsigned int ledticks; unsigned int read_u; dgprintf (4, "DDA_atm_receive()\n"); //printk("%s: Recieved %d bytes of data \n" , __FUNCTION__, packet_size); #ifdef TIATM_INST_SUPP psp_trace_par (ATM_DRV_PKT_RX_ENTER, packet_size); #endif dev = (struct atm_dev *) os_dev; priv = (Tn7AtmPrivate *) dev->dev_data; if (priv->dslPriv->lut[ch].ready == 0) { printk ("%s: channel not ready %d\n", __FUNCTION__, ch); priv->stats->rx_errors++; return 1; } vcc = (struct atm_vcc *) priv->dslPriv->lut[ch].vcc; if (vcc == NULL) { printk ("vcc=Null"); priv->stats->rx_errors++; #ifdef TIATM_INST_SUPP psp_trace_2_par (ATM_DRV_PKT_RX_EXIT, -1, -1); #endif return 1; } /* * assume no fragment packet for now */ skb = (struct sk_buff *) os_receive_info; if (skb == NULL) { dgprintf (1, "received empty skb.\n"); priv->stats->rx_errors++; #ifdef TIATM_INST_SUPP psp_trace_2_par (ATM_DRV_PKT_RX_EXIT, -1, -2); #endif return 1; } /* * see skbuff->cb definition in include/linux/skbuff.h */ ATM_SKB (skb)->vcc = vcc; skb->len = packet_size; //AV_26 /* printk ("skb:[0x%p]:0x%x pdu_len: 0x%04x\n", skb, skb->len, packet_size); printk ("data location: 0x%x, 0x%x\n", (unsigned int) skb->data, (unsigned int) data); */ /* * skb_trim(skb,skb->len); */ /* * skb size is incorrect for large packets > 1428 bytes ?? */ __skb_trim (skb, skb->len); /* change to correct > 1500 ping when * firewall is on */ dgprintf (3, "pushing the skb...\n"); //AV_26 skb->stamp.tv_sec = xtime.tv_sec; skb->stamp.tv_usec = xtime.tv_nsec; //Sorry //xadump ((unsigned char *) skb->data, skb->len, 5); if (atm_charge (vcc, skb->truesize) == 0) { //if(!(priv->stats->rx_dropped++ % 1024)) { dgprintf("Receive buffers saturated for %d.%d.%d - PDU dropped drp = %d\n", vcc->itf, vcc->vci, vcc->vpi, (priv->stats->rx_dropped)); } #ifdef UR8_SAR //AV Temp. read_u = atomic_read(&skb->users); if (read_u == 1) dev_kfree_skb_any(skb); else { DDA_printf("%s: skb 0x%p is not free to be freed users = %d\n", __FUNCTION__, skb, read_u); //DDA_atm_kfree_skb(skb); dev_kfree_skb_any(skb); } #endif #ifdef TIATM_INST_SUPP psp_trace_2_par (ATM_DRV_PKT_RX_EXIT, -1, -3); #endif return 1; } /* * pass it up to kernel networking layer and update stats */ vcc->push (vcc, skb); #if 0 //AV Waiting for Datapump read_u = atomic_read(&skb->users); if (read_u == 1) DDA_atm_kfree_skb (skb); else { DDA_printf("%s: skb 0x%p is not free to be freed users = %d\n", __FUNCTION__, skb, read_u); DDA_atm_kfree_skb(skb); } #endif /* * Update receive packet stats */ //AV_26 //priv->stats.rx_packets++; //priv->stats->rx_packets++; //atomic_inc (&vcc->stats->rx); //AV_26 //#if 0 #if defined (CONFIG_LED_MODULE) || defined (CONFIG_MIPS_AVALANCHE_COLORED_LED) if ((ledticks != jiffies) && (hnd_LED_0)) { ledticks = jiffies; TN7DSL_LED_ACTION(hnd_LED_0, DDC_MOD_ADSL, DEF_ADSL_ACTIVITY); } #endif //#endif //AV_26 /* printk ("(a) Receiving:vpi/vci[%d/%d] chan_id: %d skb len:0x%x skb truesize:0x%x\n", vcc->vpi, vcc->vci, ch, skb->len, skb->truesize); */ #ifdef TIATM_INST_SUPP psp_trace_2_par (ATM_DRV_PKT_RX_EXIT, 0, skb->truesize); #endif return 0; } #ifdef TI_DSL_TASKLET_MODE void cpsar_handle_tx_tasklet (unsigned long data) { Tn7AtmPrivate *priv = (Tn7AtmPrivate *) data; Bool isEOQ = FALSE; int more; /* Process packets - Call the DDC packet processing function */ unsigned int tx_queue = priv->sar_tx_irq - CPPI4_MIN_TX_INT_LINE; #ifdef TIATM_INST_SUPP psp_trace(ATM_DRV_TASKLET_ENTER); #endif isEOQ = DDC_sar_handle_tx_process (priv->dslPriv, priv->sar_tx_irq, &more); /* If more packets reschedule the tasklet or call pktProcessEnd */ if (isEOQ) { ddc_sar_do_tx_eoi(priv->dslPriv->SarDev, tx_queue); } else { printk("%s: Not Doing EoI Reschduling tasklet Handled %d packets \n\n",__FUNCTION__, more); tasklet_schedule (&priv->tx_tasklet); } #ifdef TIATM_INST_SUPP psp_trace_par(ATM_DRV_TASKLET_EXIT, isEOQ); #endif } #endif #ifdef TI_DSL_TASKLET_MODE void cpsar_handle_rx_tasklet (unsigned long data) { Tn7AtmPrivate *priv = (Tn7AtmPrivate *) data; Bool isEOQ = FALSE; int more; /* Process packets - Call the DDC packet processing function */ unsigned int rx_queue = priv->sar_rx_irq - CPPI4_MIN_RX_INT_LINE; #ifdef TIATM_INST_SUPP psp_trace(ATM_DRV_TASKLET_ENTER); #endif isEOQ = DDC_sar_handle_rx_process (priv->dslPriv, priv->sar_rx_irq, &more); /* If more packets reschedule the tasklet or call pktProcessEnd */ if (isEOQ) { ddc_sar_do_rx_eoi(priv->dslPriv->SarDev, rx_queue); } else { //printk("%s: Not Doing EoI Reschduling tasklet Handled %d packets \n\n",__FUNCTION__, pktsServiced); tasklet_schedule (&priv->rx_tasklet); } #ifdef TIATM_INST_SUPP psp_trace_par(ATM_DRV_TASKLET_EXIT, isEOQ); #endif } #endif