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);
		}
	}
}