static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev) { struct rmnet_private *p = netdev_priv(dev); struct xmd_ch_info *info = p->ch; int ret; #if defined (RMNET_CRITICAL_DEBUG) dynadbg_module(DYNADBG_CRIT|DYNADBG_TX,"\nRMNET[%d]: %d>\n",info->chno, skb->len); printk("\nRMNET[%d]: %d>\n",info->chno, skb->len); #endif if((skb->len - RMNET_ETH_HDR_SIZE) <= 0) { #ifdef CONFIG_MSM_RMNET_DEBUG dynadbg_module(DYNADBG_DEBUG|DYNADBG_TX,"\nrmnet: Got only header for ch %d, return\n", info->chno); // printk("\nrmnet: Got only header for ch %d, return\n", info->chno); #endif ret = NETDEV_TX_OK; dev_kfree_skb_irq(skb); goto quit_xmit; } if ((ret = mux_net_write(info->chno, (void *)((char *) skb->data + RMNET_ETH_HDR_SIZE), skb->len - RMNET_ETH_HDR_SIZE)) != 0) { if(ret == -ENOMEM) { ret = NETDEV_TX_BUSY; #ifdef CONFIG_MSM_RMNET_DEBUG dynadbg_module(DYNADBG_DEBUG|DYNADBG_TX,"\nrmnet: Cannot alloc mem, so returning busy for ch %d\n", info->chno); printk("\nrmnet: Cannot alloc mem, so returning busy for ch %d\n", // info->chno); #endif goto quit_xmit; } else if(ret == -EBUSY) {
static int rmnet_open(struct net_device *dev) { struct rmnet_private *p = netdev_priv(dev); struct xmd_ch_info *ch = p->ch; dev->flags&=~IFF_MULTICAST; dev->flags&=~IFF_BROADCAST; dynadbg_module(DYNADBG_WARN|DYNADBG_OPEN_CLOSE,"rmnet_open()\n"); if (p->ch) { ch->chno = xmd_ch_open(ch, xmd_net_notify); if (ch->chno < 0) { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_ERR|DYNADBG_OPEN_CLOSE,"error opening channel\n"); // printk("error opening channel\n"); #endif ch->chno = 0; return -ENODEV; } } netif_start_queue(dev); return 0; }
static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev) { struct rmnet_private *p = netdev_priv(dev); struct xmd_ch_info *info = p->ch; int ret; #if defined (RMNET_CRITICAL_DEBUG) dynadbg_module(DYNADBG_CRIT|DYNADBG_TX,"\nRMNET[%d]: %d>\n",info->chno, skb->len); // printk("\nRMNET[%d]: %d>\n",info->chno, skb->len); #endif if((skb->len - RMNET_ETH_HDR_SIZE) <= 0) { #ifdef CONFIG_MSM_RMNET_DEBUG dynadbg_module(DYNADBG_DEBUG|DYNADBG_TX,"\nrmnet: Got only header for ch %d, return\n", info->chno); // printk("\nrmnet: Got only header for ch %d, return\n", info->chno); #endif ret = NETDEV_TX_OK; dev_kfree_skb_irq(skb); goto quit_xmit; } if ((ret = xmd_ch_write (info->chno, (void *)((char *) skb->data + RMNET_ETH_HDR_SIZE), skb->len - RMNET_ETH_HDR_SIZE)) != 0) { if(ret == -ENOMEM) { ret = NETDEV_TX_BUSY; #ifdef CONFIG_MSM_RMNET_DEBUG dynadbg_module(DYNADBG_DEBUG|DYNADBG_TX,"\nrmnet: Cannot alloc mem, so returning busy for ch %d\n", info->chno); // printk("\nrmnet: Cannot alloc mem, so returning busy for ch %d\n", // info->chno); #endif goto quit_xmit; } else if(ret == -EBUSY) { netif_stop_queue(dev); rmnet_ch_block_info[info->chno].dev = dev; rmnet_ch_block_info[info->chno].blocked = 1; #ifdef CONFIG_MSM_RMNET_DEBUG dynadbg_module(DYNADBG_DEBUG|DYNADBG_TX,"\nrmnet: Stopping queue for ch %d\n", info->chno); // printk("\nrmnet: Stopping queue for ch %d\n", info->chno); #endif ret = NETDEV_TX_BUSY; goto quit_xmit; } } else { if (count_this_packet(skb->data, skb->len)) { p->stats.tx_packets++; p->stats.tx_bytes += skb->len; #ifdef CONFIG_MSM_RMNET_DEBUG p->wakeups_xmit += rmnet_cause_wakeup(p); #endif } } ret = NETDEV_TX_OK; dev_kfree_skb_irq(skb); quit_xmit: return ret; }
static int rmnet_stop(struct net_device *dev) { struct rmnet_private *p = netdev_priv(dev); struct xmd_ch_info *info = p->ch; dynadbg_module(DYNADBG_WARN|DYNADBG_OPEN_CLOSE,"rmnet_stop()\n"); netif_stop_queue(dev); xmd_ch_close(info->chno); return 0; }
void rmnet_restart_queue(int chno) { if(rmnet_ch_block_info[chno].blocked) { rmnet_ch_block_info[chno].blocked = 0; netif_wake_queue(rmnet_ch_block_info[chno].dev); #ifdef CONFIG_MSM_RMNET_DEBUG dynadbg_module(DYNADBG_DEBUG|DYNADBG_INIT_EXIT,"rmnet: FIFO free so unblocking rmnet %d queue\n", chno); printk("rmnet: FIFO free so unblocking rmnet %d queue\n", chno); #endif } }
static void rmnet_tx_timeout(struct net_device *dev) { int chno; dynadbg_module(DYNADBG_WARN|DYNADBG_TX,"rmnet_tx_timeout()\n"); for(chno=0; chno < 16; chno++) { if(rmnet_ch_block_info[chno].dev == dev) { rmnet_restart_queue(chno); break; } } }
/* Called in wq context */ void xmd_net_notify(int chno) { int i; struct net_device *dev = NULL; void *buf = NULL; int tot_sz = 0; struct rmnet_private *p = NULL; struct xmd_ch_info *info = NULL; for (i=0; i<ARRAY_SIZE(rmnet_channels); i++) { if (rmnet_channels[i].chno == chno) { dev = (struct net_device *)rmnet_channels[i].priv; break; } } if (!dev) { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify: No device \n"); printk("xmd_net_notify: No device \n"); #endif return; } p = netdev_priv(dev); if (!p) return; info = p->ch; if (!info) return; /* contains the full data read from hsi channel.*/ buf = netdatabuffer; tot_sz = data_size; if (!buf) { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify: No buf recvd from ch:%d \n", info->chno); printk("xmd_net_notify: No buf recvd from ch:%d tot_sz :%d\n", info->chno,tot_sz); #endif return; } #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: total size read = %d from ch:%d \n", tot_sz, info->chno); printk("xmd_net_notify: total size read = %d from ch:%d \n", tot_sz, info->chno); #endif switch (past_packet.state) { case RMNET_FULL_PACKET: /* no need to do anything */ break; case RMNET_PARTIAL_PACKET: { void *ip_hdr = (void *)past_packet.buf; int sz; int copy_size; #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: past partial packet\n"); printk("xmd_net_notify: past partial packet\n"); #endif if (past_packet.type == RMNET_IPV4_VER) { sz = ntohs(((struct iphdr*) ip_hdr)->tot_len); } else if (past_packet.type == RMNET_IPV6_VER) { sz = ntohs(((struct ipv6hdr*) ip_hdr)->payload_len) + sizeof(struct ipv6hdr); } else { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify: Invalid past version (data), %d\n", past_packet.type); printk("xmd_net_notify: Invalid past version (data), %d\n", past_packet.type); #endif rmnet_reset_pastpacket_info(); return; } if(sz > RMNET_DATAT_SIZE) { printk(KERN_INFO"rmnet partial packet size too large, sz=%d,can not handle\n",sz); rmnet_reset_pastpacket_info(); return; } copy_size = sz - past_packet.size; /* if read size if > then copy size, copy full packet.*/ if (tot_sz >= copy_size) { memcpy(past_packet.buf + past_packet.size,buf,copy_size); } else { /* copy whatever read if read size < packet size.*/ memcpy(past_packet.buf + past_packet.size,buf,tot_sz); #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"\nxmd_net_notify: RMNET_PARTIAL_PACKET. past size = %d," " total size = %d\n", past_packet.size, tot_sz); printk("\nxmd_net_notify: RMNET_PARTIAL_PACKET. past size = %d," " total size = %d\n", past_packet.size, tot_sz); #endif past_packet.size += tot_sz; return; } xmd_trans_packet(dev,past_packet.type,(void*)past_packet.buf,sz); #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: pushed reassembled data packet to tcpip," " sz = %d\n", sz); printk("xmd_net_notify: pushed reassembled data packet to tcpip," " sz = %d\n", sz); #endif buf = buf + copy_size; tot_sz = tot_sz - copy_size; past_packet.state = RMNET_FULL_PACKET; } break; case RMNET_PARTIAL_HEADER: { void *ip_hdr = (void *)past_packet.buf; int sz; int copy_size; int hdr_size = 0; #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: past partial header packet\n"); printk("xmd_net_notify: past partial header packet\n"); #endif if (past_packet.type == RMNET_IPV4_VER) hdr_size = sizeof(struct iphdr); else if (past_packet.type == RMNET_IPV6_VER) hdr_size = sizeof(struct ipv6hdr); else { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify: Invalid past version (hdr), %d\n", past_packet.type); printk("xmd_net_notify: Invalid past version (hdr), %d\n", past_packet.type); #endif past_packet.state = RMNET_FULL_PACKET; return; } copy_size = hdr_size - past_packet.size; if(tot_sz >= copy_size) { memcpy(past_packet.buf + past_packet.size,buf,copy_size); } else { /* copy whatever read if read size < packet size. */ memcpy(past_packet.buf + past_packet.size,buf,tot_sz); #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: Still partial header \n"); printk("xmd_net_notify: Still partial header \n"); #endif past_packet.size += tot_sz; return; } buf = buf + copy_size; tot_sz = tot_sz - copy_size; past_packet.size = past_packet.size + copy_size; if (past_packet.type == RMNET_IPV4_VER) { sz = ntohs(((struct iphdr*) ip_hdr)->tot_len); } else if (past_packet.type == RMNET_IPV6_VER) { sz = ntohs(((struct ipv6hdr*) ip_hdr)->payload_len) + sizeof(struct ipv6hdr); } else { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify: Invalid past version, %d\n", past_packet.type); printk("xmd_net_notify: Invalid past version, %d\n", past_packet.type); #endif past_packet.state = RMNET_FULL_PACKET; return; } copy_size = sz - past_packet.size; /* if read size if > then copy size, copy full packet. */ if (tot_sz >= copy_size) { memcpy(past_packet.buf + past_packet.size,buf,copy_size); } else { /* copy whatever read if read size < packet size.*/ memcpy(past_packet.buf + past_packet.size,buf,tot_sz); #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"\nxmd_net_notify: RMNET_PARTIAL_HEADER. past size = %d," " total size = %d\n", past_packet.size, tot_sz); printk("\nxmd_net_notify: RMNET_PARTIAL_HEADER. past size = %d," " total size = %d\n", past_packet.size, tot_sz); #endif past_packet.size += tot_sz; past_packet.state = RMNET_PARTIAL_PACKET; return; } xmd_trans_packet(dev,past_packet.type,(void *)past_packet.buf,sz); buf = buf + copy_size; tot_sz = tot_sz - copy_size; } break; default: #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify: Invalid past state %d\n", (int)past_packet.state); printk("xmd_net_notify: Invalid past state %d\n", (int)past_packet.state); #endif past_packet.state = RMNET_FULL_PACKET; break; } while (tot_sz) { int hdr_size = 0; int ver = 0; void *ip_hdr = (void *)buf; int data_sz = 0; #if defined(__BIG_ENDIAN_BITFIELD) ver = ((char *)buf)[0] & 0x0F; #elif defined(__LITTLE_ENDIAN_BITFIELD) ver = (((char *)buf)[0] & 0xF0) >> 4; #endif #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: ver = 0x%x, total size : %d \n", ver, tot_sz); printk("xmd_net_notify: ver = 0x%x, total size : %d \n", ver, tot_sz); #endif if (ver == RMNET_IPV4_VER) { hdr_size = sizeof(struct iphdr); } else if (ver == RMNET_IPV6_VER) { hdr_size = sizeof(struct ipv6hdr); } else { #if defined (RMNET_ERR) if( ( (dynamic_debug_mask)&((DYNADBG_DEBUG)|(DYNADBG_RX)|(DYNADBG_XMD_NET_EN)|(DYNADBG_GLOBAL_EN)) ) == \ ((DYNADBG_DEBUG)|(DYNADBG_RX)|(DYNADBG_XMD_NET_EN)|(DYNADBG_GLOBAL_EN)) ){ void *ip_hdr = (char*)(buf+4); int tmp_sz = ntohs(((struct iphdr*) ip_hdr)->tot_len); printk("xmd_net_notify: Invalid version, 0x%x\n", ver); printk("xmd_net_notify: Few bytes of pkt : 0x%x 0x%x 0x%x 0x%x " "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", ((char *)buf)[0], ((char *)buf)[1], ((char *)buf)[2], ((char *)buf)[3], ((char *)buf)[4], ((char *)buf)[5], ((char *)buf)[6], ((char *)buf)[7], ((char *)buf)[8], ((char *)buf)[9], ((char *)buf)[10]); printk("xmd_net_notify: Current packet size = %d," "ip packet size = %d\n",tot_sz,tmp_sz); } #endif past_packet.state = RMNET_FULL_PACKET; break; } if (tot_sz < 6) { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify:buf size insufficient to decode pkt length\n"); printk("xmd_net_notify:buf size insufficient to decode pkt length\n"); #endif past_packet.state = RMNET_FULL_PACKET; return; } if (tot_sz < hdr_size) { past_packet.state = RMNET_PARTIAL_HEADER; past_packet.size = tot_sz; memcpy(past_packet.buf, buf, tot_sz); past_packet.type = ver; #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: partial header packet copied locally," " sz = %d\n", tot_sz); printk("xmd_net_notify: partial header packet copied locally," " sz = %d\n", tot_sz); #endif return; } if (ver == RMNET_IPV4_VER) { data_sz = ntohs(((struct iphdr*) ip_hdr)->tot_len); if (data_sz < sizeof(struct iphdr)){ printk(KERN_INFO"rmnet receive ipv4 pkt too small, data_sz=%d,can not handle\n",data_sz); rmnet_reset_pastpacket_info(); return; } } else if (ver == RMNET_IPV6_VER) { data_sz = ntohs(((struct ipv6hdr*) ip_hdr)->payload_len) + sizeof(struct ipv6hdr); } else { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_RX,"xmd_net_notify: data sz check -- " "Invalid version, %d\n",ver); printk("xmd_net_notify: data sz check -- " "Invalid version, %d\n",ver); #endif past_packet.state = RMNET_FULL_PACKET; break; } if(data_sz > RMNET_DATAT_SIZE) { printk(KERN_INFO"rmnet full packet size too large, sz=%d,can not handle\n",data_sz); rmnet_reset_pastpacket_info(); return; } #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: data size = %d\n", data_sz); printk("xmd_net_notify: data size = %d\n", data_sz); #endif if (tot_sz < data_sz) { past_packet.state = RMNET_PARTIAL_PACKET; past_packet.size = tot_sz; memcpy(past_packet.buf, buf, tot_sz); past_packet.type = ver; #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: partial data packet copied locally," " sz = %d\n", tot_sz); printk("xmd_net_notify: partial data packet copied locally," " sz = %d\n", tot_sz); #endif return; } xmd_trans_packet(dev, ver, buf, data_sz); #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: pushed full data packet to tcpip, " "sz = %d\n", data_sz); printk("xmd_net_notify: pushed full data packet to tcpip, " "sz = %d\n", data_sz); #endif tot_sz = tot_sz - data_sz; buf = buf + data_sz; #if defined (RMNET_DEBUG) dynadbg_module(DYNADBG_DEBUG|DYNADBG_RX,"xmd_net_notify: looping for another packet" " tot_sz = %d\n", tot_sz); printk("xmd_net_notify: looping for another packet" " tot_sz = %d\n", tot_sz); #endif } past_packet.state = RMNET_FULL_PACKET; }
/*give the packet to TCP/IP*/ static void xmd_trans_packet( struct net_device *dev, int type, void *buf, int sz) { struct rmnet_private *p = netdev_priv(dev); struct sk_buff *skb; void *ptr = NULL; sz += RMNET_ETH_HDR_SIZE; #if defined (RMNET_CRITICAL_DEBUG) dynadbg_module(DYNADBG_CRIT|DYNADBG_TX,"\nRMNET: %d<\n",sz); printk("\nRMNETsend to tcp/ip : %d<\n",sz); #endif if (sz > (RMNET_MTU_SIZE + RMNET_ETH_HDR_SIZE)) { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_TX,"xmd_trans_packet() discarding %d pkt len\n", sz); printk("xmd_trans_packet() discarding %d pkt len\n", sz); #endif ptr = 0; return; } else { skb = dev_alloc_skb(sz + NET_IP_ALIGN); if (skb == NULL) { #if defined (RMNET_ERR) dynadbg_module(DYNADBG_WARN|DYNADBG_TX,"xmd_trans_packet() cannot allocate skb\n"); printk("xmd_trans_packet() cannot allocate skb\n"); #endif return; } else { skb->dev = dev; skb_reserve(skb, NET_IP_ALIGN); ptr = skb_put(skb, sz); wake_lock_timeout(&p->wake_lock, HZ / 2); /* adding ethernet header */ { /* struct ethhdr eth_hdr = {0xB6,0x91,0x24,0xa8,0x14,0x72,0xb6, 0x91,0x24,0xa8,0x14,0x72,0x08,0x0};*/ char temp[] = {0xB6,0x91,0x24,0xa8,0x14,0x72,0xb6,0x91,0x24, 0xa8,0x14,0x72,0x08,0x0}; struct ethhdr *eth_hdr = (struct ethhdr *) temp; if (type == RMNET_IPV6_VER) { eth_hdr->h_proto = 0x08DD; eth_hdr->h_proto = htons(eth_hdr->h_proto); } memcpy((void *)eth_hdr->h_dest, (void*)dev->dev_addr, sizeof(eth_hdr->h_dest)); memcpy((void *)ptr, (void *)eth_hdr, sizeof(struct ethhdr)); } memcpy(ptr + RMNET_ETH_HDR_SIZE, buf, sz - RMNET_ETH_HDR_SIZE); skb->protocol = eth_type_trans(skb, dev); if (count_this_packet(ptr, skb->len)) { #ifdef CONFIG_MSM_RMNET_DEBUG p->wakeups_rcv += rmnet_cause_wakeup(p); #endif p->stats.rx_packets++; p->stats.rx_bytes += skb->len; } netif_rx(skb); wake_unlock(&p->wake_lock); } } }