#include "hasbani.h"
#define EOPEN 1
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) /* not sure about the 0 */
#define rtnl_LOCK()	rtnl_lock()
#define rtnl_UNLOCK()	rtnl_unlock()
#else
#define rtnl_LOCK()	/* nop */
#define rtnl_UNLOCK()	/* nop */
#endif

/**********debugger**********/
void hsb_show_mem(unsigned char * data, int len)
{
	int i;
	for(i=0;i<len;i++)
		printk("%02x ",(unsigned int)data[i]);
	printk("\n");
}
void hsb_skb_dump(struct sk_buff*skb)
{
	int i;
	int j = 0;
	printk("Number of bytes: %d\nData:\n",skb->len);
	for(i=0;i<skb->len;i++)
	{
		if( j%16 == 0 )
		{	
			printk("\n");
			printk("%03d :", j );
			
		}
		printk("%02x ",skb->data[i]);
		j++;
	}
	printk("\n");
}
/*************************************/
/*************added for packed mode***********/
#define HSB_SND_BUF_LEN		1024*3
#define HSB_SND_BUF_MAX_LEN 	5		//HSB_SND_BUF_LEN-128 
static char * hsb_snd_buf;
int hsb_snd_buf_index=0;



#define HSB_USB_RECV_SIZE	3096
static char hsb_eth_rcv_pool[HSB_USB_RECV_SIZE+1000];
static char hsb_eth_tmp_pool[1514];	//larger than an ethernet packet.
int hsb_eth_rcv_pool_index=0;




extern int hsb_usb_cmd_init(struct hsb_usb_device* phsb_device);
extern void hsb_usb_cmd_stop(struct hsb_usb_device* phsb_device);
extern unsigned char BlFirmwareInitialize
(
	struct hsb_usb_device*	DeviceObject
);

int hsb_eth_open(struct net_device *dev)
{	
	struct hsb_eth_priv_data* tmp_priv=(struct hsb_eth_priv_data*)dev->priv; 
	struct hsb_usb_device* udev = tmp_priv->usb_dev;
	
	if (!dev && !udev)
		return -1;
		
	if( udev->DevType != DEV_HASBANI)
	{
		if ( !udev->bDownload )
			return -EAGAIN;
	
	}
	if( !udev->bSetMac )
		return -EAGAIN;
		
	if(hsb_usb_start(dev)!=0) 
		return -EOPEN; /* EOPEN is created by sanborn*/


//Cleaning.....
hsb_snd_buf_index=0;
hsb_eth_rcv_pool_index=0;
//end


	if((hsb_snd_buf=kmalloc(HSB_SND_BUF_LEN,GFP_KERNEL))==0)
		return -1;
//	hsb_snd_buf_index=0;

	netif_start_queue ( dev );
		
	MOD_INC_USE_COUNT;
	return 0;
}


int hsb_eth_stop(struct net_device* dev)
{
//	struct hsb_eth_priv_data* tmp_priv=(struct hsb_eth_priv_data*)dev->priv; 
//	struct hsb_usb_device* udev = tmp_priv->usb_dev;

//	hsb_usb_cmd_stop(udev);
	
	netif_stop_queue (dev );
	
	hsb_usb_stop(dev);

//Cleaning.....
hsb_snd_buf_index=0;
hsb_eth_rcv_pool_index=0;
//end

	kfree(hsb_snd_buf);
	hsb_snd_buf=0;
	MOD_DEC_USE_COUNT;
	return 0;
}
struct net_device_stats* hsb_eth_get_stats(struct net_device* dev)
{
	struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
	return pstats;
}

int hsb_eth_tx1(struct sk_buff* skb,struct net_device* dev)
{
	
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
        struct sk_buff* new;
	
	struct hsb_eth_priv_data* tmp_priv=(struct hsb_eth_priv_data*)dev->priv; 
	struct hsb_usb_device* udev = tmp_priv->usb_dev;
	__u16 * pcount=(__u16*)(hsb_snd_buf+hsb_snd_buf_index);
	
	unsigned char zeromarker = 0;
	
	if (!dev && !udev)
	{
		kfree_skb( skb );
		return 0;
	}
	
	if ( !udev->bSetMac && ( udev->DevType != DEV_HASBANI) )
	{
		kfree_skb( skb );
		return 0;
	}
	
	*pcount=skb->len;	//length,
	hsb_snd_buf_index+=2;	//skip length.
	memcpy(hsb_snd_buf+hsb_snd_buf_index,skb->data,skb->len);
        pstats->tx_bytes+=skb->len;
	pstats->tx_packets++;
	
	hsb_snd_buf_index+=skb->len;
	//Make even. 
	if(hsb_snd_buf_index%2)
		hsb_snd_buf_index++;
	
	if(hsb_snd_buf_index>=HSB_SND_BUF_MAX_LEN)
	{	//Send this packet:
	
		zeromarker = ( 0 == (hsb_snd_buf_index % 64) );
		
		if( zeromarker )
			new=dev_alloc_skb(hsb_snd_buf_index+2);
		else
			new=dev_alloc_skb(hsb_snd_buf_index);
			
		if(!new)
			return -ENOMEM;
		kfree_skb(skb);
		skb = 0;
                memcpy(skb_put(new,hsb_snd_buf_index),hsb_snd_buf,hsb_snd_buf_index);
                
		if( zeromarker )
			memset(skb_put(new,2),0,2*sizeof(char));
		//For traffic control
		netif_stop_queue(dev);

  	        hsb_usb_send(dev,new);
		hsb_snd_buf_index=0;
	}
	
	if( skb )
		kfree_skb(skb);
		
	return 0;
}




/*********************************************/
int hsb_eth_tx2(struct sk_buff* skb,struct net_device* dev)
{
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
        int retval=0;
        int length;
        struct sk_buff* new;

	struct hsb_eth_priv_data* tmp_priv=(struct hsb_eth_priv_data*)dev->priv; 
	struct hsb_usb_device* udev = tmp_priv->usb_dev;
	
	if (!dev && !udev)
	{
		kfree_skb( skb );
		return 0;
	}
	
	if ( !udev->bSetMac && ( udev->DevType != DEV_HASBANI) )
	{
		kfree_skb( skb );
		return 0;
	}

        dev->trans_start=jiffies;
        length=(skb->len)%64;
        if(length==63)
        {
                new=dev_alloc_skb(skb->len+2);
                if(!new)
                        return -ENOMEM;
                memcpy(skb_put(new,skb->len),skb->data,skb->len);
                new->data[skb->len]=0x00;
                new->data[skb->len+1]=0x02;
                skb_put(new,2);
                kfree_skb(skb);
        }
        else
         {
                new=dev_alloc_skb(skb->len+1);
                if(!new)
                      return -ENOMEM;
                memcpy(skb_put(new,skb->len),skb->data,skb->len);
                new->data[skb->len]=0x01;
                skb_put(new,1);
                kfree_skb(skb);
        }

	//For traffic control
	netif_stop_queue(dev);

        hsb_usb_send(dev,new);
        pstats->tx_bytes+=skb->len;
        pstats->tx_packets++;
        return retval;

}
/***************************************/
extern unsigned char bPackingMode;
int hsb_eth_tx(struct sk_buff* skb,struct net_device* dev)
{
	int ret = 0;

	if ( bPackingMode )
		ret = hsb_eth_tx1(skb, dev);
	else
		ret = hsb_eth_tx2 (skb, dev);
	
	return ret;
}

/* this function will be provided to USB layer */


static char hsb_tmp_ether_head[]=
{
0x0,0x30,0xcd,0x00,0x05,0x04,	//dest
0x82,0x30,0xcd,0x0, 0x7,0xc,	//src
};


/********************packed mode***************
void hsb_eth_rx1(struct net_device *dev,struct sk_buff *skb)
{
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
	struct sk_buff* new;

	if(skb->len==0)
		return;
	skb->dev=(struct net_device*) dev;
	skb->protocol=eth_type_trans(skb,dev);
	skb->ip_summed=CHECKSUM_UNNECESSARY;  
	pstats->rx_packets++;

	new=dev_alloc_skb(skb->len+12);

	if(!new)
		return ;
	memcpy(skb_put(new,12),hsb_tmp_ether_head,12);
	memcpy(skb_put(new,skb->len),skb->data,skb->len);
	kfree_skb(skb);

	new->dev=(struct net_device*) dev;
	new->protocol=eth_type_trans(new,dev);
        new->ip_summed=CHECKSUM_UNNECESSARY;  

	netif_rx(new);
	return;
}
************************************************************************/

/***********************************************
void hsb_eth_rx1(struct net_device *dev,struct sk_buff *skb)
{
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
	struct sk_buff* new;
	__u16 i=0,*pi=(__u16*)hsb_eth_rcv_pool;
	__u16 rem=0;
	
	unsigned char lastpacketcomplete;
	int length = 0;

	if( skb->len & 1 )
	{
		kfree_skb(skb);
		return;
	}
	
	lastpacketcomplete = ( 0 != (skb->len % 64) );
	length = skb->len;
	
	memcpy(hsb_eth_rcv_pool+hsb_eth_rcv_pool_index,skb->data,skb->len);
	hsb_eth_rcv_pool_index+=skb->len;
	
	i=*pi;
	
//	printk("\n\n.........i=%d\n\n", i );
	while(length!=0)
	{
		//Modified by Shen Wei 20020626
		
		// misalignment found? Ignore the rest of the buffer	
		if (i < 14 || i > 1514 || length < 0)
		{
			hsb_eth_rcv_pool_index = 0;
			break;
		}
		//End

//		if(hsb_eth_rcv_pool_index<i+2)//If the remainder is not a full eth. packet
//		{
//		//	hsb_eth_rcv_pool_index = 0;
//			break;
//		}
//
		if( i > length ) 
		{
			if( lastpacketcomplete )
			{
				hsb_eth_rcv_pool_index = 0;
				break;
			}
			
//			hsb_eth_rcv_pool_index = length;


			i = length-2;
	#ifdef USB_DEBUG1
	printk("hsb_eth_rx1:............skb->len=0x%x\n", skb->len);
	hsb_skb_dump(skb);
	printk("hsb_eth_rx1: End.........i=%d, length=%d\n", i, length);
	
	#endif					
//			break;
		}
			
		new=dev_alloc_skb(i);
		if(new)
		{
			memcpy(skb_put(new,i),((char*)pi)+2,i);
	
			
			new->dev=(struct net_device*) dev;
			new->protocol=eth_type_trans(new,dev);
		        new->ip_summed=CHECKSUM_UNNECESSARY;  
			pstats->rx_packets++;
			pstats->rx_bytes+=i;
			
			netif_rx(new);
			
		}
		//Make even
		if(i%2)
			i++;
			
		//Update counters
		
		length -= i+2;
		
		hsb_eth_rcv_pool_index-=i+2;
		
		if(hsb_eth_rcv_pool_index==0)
			break;
		
		pi=(__u16*)(((char*)pi)+2+i);
		i=*pi;
	}
	

	hsb_eth_rcv_pool_index=0;	
	//Move data to front.
//	if(hsb_eth_rcv_pool_index==0||i==0)
//		hsb_eth_rcv_pool_index=0;
//	else
//	{
//		memcpy(hsb_eth_tmp_pool,(char *)pi,hsb_eth_rcv_pool_index);
//		memcpy(hsb_eth_rcv_pool,hsb_eth_tmp_pool,hsb_eth_rcv_pool_index);
//	}
//
	kfree_skb(skb);
	return;
}
**********************************/
/*****************************************
void hsb_eth_rx1(struct net_device *dev,struct sk_buff *skb)
{
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
	struct sk_buff* new;
	
	struct hsb_eth_priv_data* tmp_priv=(struct hsb_eth_priv_data*)dev->priv;
	struct hsb_usb_device* udev = tmp_priv->usb_dev;
	
	unsigned char lastpacketcomplete;
	int length = 0;
	__u16 ethlen = 0;
	char * pdata = skb->data;

	if( skb->len & 1 )
	{
		kfree_skb(skb);
		return;
	}
	
	if(!udev->bSetMac && (udev->DevType != DEV_HASBANI))
	{
	    kfree_skb(skb);
	    return;
	}
	
	lastpacketcomplete = ( 0 != (skb->len % 64) );
	length = skb->len;
	ethlen = (pdata[0]&0xff)|((pdata[1]<<8)&0xff00);
	while(length>0)
	{
		if (ethlen < 14 || ethlen > 1514)		
			break;

		if(ethlen > length)
		{
			if(lastpacketcomplete)
				break;
			ethlen = length;
		}
		new=dev_alloc_skb( ethlen );
		if(new)
		{
			memcpy(skb_put(new,ethlen),pdata+2,ethlen );
	
			new->dev=(struct net_device*) dev;
			new->protocol=eth_type_trans(new,dev);
		        new->ip_summed=CHECKSUM_UNNECESSARY;  
			pstats->rx_packets++;
			pstats->rx_bytes+=ethlen;
			
			netif_rx(new);
			
		}
		else
			break;
		ethlen += 1 & ethlen;
		length -= ethlen+2;
		if(length == 0)
			break;
		
		pdata += ethlen+2;			
		
	}
	
	if(skb)
		kfree_skb(skb);
	return;
}

**************************************************/

void hsb_eth_rx1(struct net_device *dev,struct sk_buff *skb)
{
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
	struct sk_buff* new;
	
	struct hsb_eth_priv_data *tmp_priv=(struct hsb_eth_priv_data*)dev->priv;
	struct hsb_usb_device *udev = tmp_priv->usb_dev;
	
	static __u16 packetresidue = 0;
	unsigned char lastpacketcomplete;
	int length = 0;
	__u16 ethlen = 0;
	char * pdata = skb->data;

	if( skb->len & 1 )
	{
		kfree_skb(skb);
		return;
	}

	if(!udev->bSetMac && (udev->DevType != DEV_HASBANI))
	{
	    kfree_skb(skb);
	    return;
	}	
	
	if(hsb_eth_rcv_pool_index != 0)
	{
	    if(skb->len < packetresidue)
	    {
		hsb_eth_rcv_pool_index = 0;
		packetresidue = 0;
		kfree_skb(skb);
		return;
	    }
	    
	    ethlen = hsb_eth_rcv_pool_index + packetresidue;
	    memmove(hsb_eth_rcv_pool + hsb_eth_rcv_pool_index, skb->data, packetresidue);
	    new = dev_alloc_skb(ethlen);
	    if(new)
	    {
		memmove(skb_put(new, ethlen), hsb_eth_rcv_pool, ethlen);
		
		#ifdef USB_DEBUG1
		printk("ethlen=%d, packetresidue=%d, index=%d\n", 
		    ethlen, packetresidue, hsb_eth_rcv_pool_index);
		printk("skb->len=%d\n", skb->len);
		    
		hsb_skb_dump(new);
		#endif
		
		new->dev = (struct net_device *)dev;
		new->protocol = eth_type_trans(new, dev);
		new->ip_summed = CHECKSUM_UNNECESSARY;
		
		pstats->rx_packets++;
		pstats->rx_bytes += ethlen;
		
		netif_rx(new);
	    }
	    
	    hsb_eth_rcv_pool_index = 0;
	    packetresidue = 0;
	    
	    kfree_skb(skb);
	    return;	
	}
	
	lastpacketcomplete = ( 0 != (skb->len % 64) );
	length = skb->len;
	ethlen = (pdata[0]&0xff)|((pdata[1]<<8)&0xff00);
//	hsb_skb_dump(skb);
	while(length>0)
	{
		if (ethlen < 14 || ethlen > 1514)		
		{
//			CmCommandSend(udev, CM_REQUEST_PACKING_MODE, 1);
//			printk("Break here..ethlen=%d..\n", ethlen);
//			hsb_skb_dump(skb);
			break;
		}
		
		
		if(ethlen > length)
		{
			if(lastpacketcomplete)
				break;
		//	ethlen = length;
		    packetresidue = ethlen - length + 2;
		    memmove(hsb_eth_rcv_pool, pdata + 2, length - 2);
		    hsb_eth_rcv_pool_index = length - 2;
		    
		    length = 0;
		    continue;
		}
		new=dev_alloc_skb( ethlen );
		if(new)
		{
			memcpy(skb_put(new,ethlen),pdata+2,ethlen );
	
			new->dev=(struct net_device*) dev;
			new->protocol=eth_type_trans(new,dev);
		        new->ip_summed=CHECKSUM_UNNECESSARY;  
			pstats->rx_packets++;
			pstats->rx_bytes+=ethlen;
			
			netif_rx(new);
			
		}
		else
			break;
		ethlen += 1 & ethlen;
		length -= ethlen+2;
		if(length == 0)
			break;
		
		pdata += ethlen+2;
//		printk("pdata[0] =0x%x, pdata[1]=0x%x, 2=0x%x\n", pdata[0], pdata[1],pdata[2]);			
		ethlen = pdata[1];
		ethlen = (ethlen << 8) | (pdata[0]&0xff);		
	}
	
	if(skb)
		kfree_skb(skb);
	return;
}



void hsb_eth_rx2(struct net_device *dev,struct sk_buff *skb)
{
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
	int length = skb->len;
	
	#ifdef USB_DEBUG1
	int i = 0;
	unsigned char debug=0;
	#endif
	
	
	struct sk_buff* new;
	char * pdata = skb->data;
	__u16 iplen;
	__u16 flag =0;
	unsigned char isarp = 0;
	
	flag = skb->data[12]<<8 | skb->data[13];
	if( flag == 0x0806 )
	{
		isarp = 1;
		skb->dev=(struct net_device*) dev;
		skb->protocol=eth_type_trans(skb,dev);
		skb->ip_summed=CHECKSUM_UNNECESSARY;  
		pstats->rx_packets++;
		pstats->rx_bytes+=skb->len;
	
		netif_rx(skb);	
		
		return;	
	}
	
	
	iplen = ((skb->data[16]<<8)&0xff00) | ((skb->data[17])&0xff);
	iplen += 14;

	#ifdef USB_DEBUG1
	if(length > iplen )
		debug = 1;
	#endif
		
	while( length > 0 )
	{
	
		if( iplen < 14 || iplen > 1514 )
		{
			#ifdef USB_DEBUG1
			printk("Break here!................iplen=%d\n", iplen);
			#endif
			break;
		}	
		new=dev_alloc_skb( iplen );
		if(new)
		{
			memcpy(skb_put(new,iplen),pdata,iplen );
	
	
	#ifdef USB_DEBUG1
	if ( debug ){//length > iplen ){
	if( i== 0 )
	{
		printk("hsb_eth_rx2:......skb->len=%d, new->len=%d\n", skb->len, new->len);
		hsb_skb_dump(skb);
	}
	printk("\n..............................i=%d\n", i);
	hsb_skb_dump(new);
	printk("hsb_eth_rx2: End......skb->len=%d, new->len=%d\n", skb->len, new->len);
	printk("......................iplen= %d\n", iplen);
	i++;
	}
	#endif		
			
			new->dev=(struct net_device*) dev;
			new->protocol=eth_type_trans(new,dev);
		        new->ip_summed=CHECKSUM_UNNECESSARY;  
			pstats->rx_packets++;
			pstats->rx_bytes+=iplen;
			
			netif_rx(new);
			
		}		
		else
			break;
		length -= iplen;
		if(length == 0)	break;
		
		pdata += iplen;
		
		iplen = ((pdata[16]<<8)&0xff00) | (0xff & pdata[17]);
	#ifdef USB_DEBUG1
	if(debug )
	{
		printk("pdata[16]=%0x, pdata[17]=%0x, iplen=%d length=%d\n", pdata[16], pdata[17], iplen, length);
		printk("skb->data=%p, pdata=%p\n", skb->data, pdata);
	}
	#endif	
		iplen += 14;
		
		
	
	}

	kfree_skb(skb);

	return;
}
/*******************************************************/

/**********************************
void hsb_eth_rx2(struct net_device *dev,struct sk_buff *skb)
{
        struct net_device_stats* pstats=&(((struct hsb_eth_priv_data*)(dev->priv))->net_stats);
	

//	if(skb->len==0)
//		return;
	if(skb->len < 14 || skb->len > 1514)
	{
	#ifdef USB_DEBUG1
	printk("hsb_eth_rx2:............skb->len=0x%x\n", skb->len);
	hsb_skb_dump(skb);
	printk("hsb_eth_rx2: End........skb->len=%d\n", skb->len);
	
	#endif				
		kfree_skb(skb);
		return;
	}
	skb->dev=(struct net_device*) dev;
	skb->protocol=eth_type_trans(skb,dev);
	skb->ip_summed=CHECKSUM_UNNECESSARY;  
	pstats->rx_packets++;
	pstats->rx_bytes+=skb->len;
	
	netif_rx(skb);
	return;
}
**************************************************************/

void hsb_eth_rx(struct net_device *dev,struct sk_buff *skb)
{
	
	if ( bPackingMode )
		hsb_eth_rx1(dev, skb);
	else
		hsb_eth_rx2 (dev, skb);	
	
	return;
}

int hsb_eth_startdevice(struct hsb_usb_device* usb_dev)
{
	struct hsb_eth_priv_data *tmp_priv;
	usb_dev->hsb_eth_dev=(struct net_device*)kmalloc(sizeof(struct net_device),GFP_KERNEL);
	memset(usb_dev->hsb_eth_dev,0,sizeof(struct net_device));
	strcpy(usb_dev->hsb_eth_dev->name,"hsb0");
	ether_setup(usb_dev->hsb_eth_dev);
	memcpy(usb_dev->hsb_eth_dev->dev_addr,hsb_tmp_ether_head,6);	/* ?? */
	usb_dev->hsb_eth_dev->priv=kmalloc(sizeof(struct hsb_eth_priv_data),GFP_KERNEL);
        if(!usb_dev->hsb_eth_dev->priv)
                return -ENOMEM;

	tmp_priv=(struct hsb_eth_priv_data*)(usb_dev->hsb_eth_dev->priv);
        memset(usb_dev->hsb_eth_dev->priv,0,sizeof(struct hsb_eth_priv_data));
	tmp_priv->usb_dev=usb_dev;
	
	usb_dev->hsb_eth_dev->open=hsb_eth_open;
	usb_dev->hsb_eth_dev->stop=hsb_eth_stop;
	usb_dev->hsb_eth_dev->get_stats=hsb_eth_get_stats;
	usb_dev->hsb_eth_dev->hard_start_xmit=hsb_eth_tx;
	//usb_dev->hsb_eth_dev->flags|=IFF_NOARP;
	rtnl_LOCK();
	register_netdevice(usb_dev->hsb_eth_dev);
	rtnl_UNLOCK();
	return 0;
}

void hsb_eth_stopdevice(struct hsb_usb_device *usb_dev)
{
	kfree(usb_dev->hsb_eth_dev->priv);
	rtnl_LOCK();
	unregister_netdevice(usb_dev->hsb_eth_dev);
	rtnl_UNLOCK();
	kfree(usb_dev->hsb_eth_dev);
	usb_dev->hsb_eth_dev=0;
}

void hsb_eth_set_mac_addr(struct hsb_usb_device* usb_dev, char* mac,int len)
{
	memcpy(usb_dev->hsb_eth_dev->dev_addr,mac,len);	/* ?? */
	memcpy(hsb_tmp_ether_head,mac,len);
}
